1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | Object.defineProperty(exports, "__esModule", { value: true });
|
18 | exports.save = exports.CachingProjectLoader = void 0;
|
19 | const shutdown_1 = require("@atomist/automation-client/lib/internal/util/shutdown");
|
20 | const logger_1 = require("@atomist/automation-client/lib/util/logger");
|
21 | const fs = require("fs-extra");
|
22 | const sha = require("sha-regex");
|
23 | const util_1 = require("util");
|
24 | const cloningProjectLoader_1 = require("./cloningProjectLoader");
|
25 | const cacheKey_1 = require("./support/cacheKey");
|
26 | const LruCache_1 = require("./support/LruCache");
|
27 |
|
28 |
|
29 |
|
30 | class CachingProjectLoader {
|
31 | constructor(delegate = cloningProjectLoader_1.CloningProjectLoader, maxEntries = 20) {
|
32 | this.delegate = delegate;
|
33 | this.deleteOnExit = [];
|
34 | this.cache = new LruCache_1.LruCache(maxEntries, p => this.cleanUp(p.baseDir, "eviction"));
|
35 | shutdown_1.registerShutdownHook(async () => {
|
36 | if (this.deleteOnExit.length > 0) {
|
37 | logger_1.logger.debug("Deleting cached projects");
|
38 | }
|
39 | await Promise.all(this.deleteOnExit.map(p => this.cleanUp(p, "shutdown")));
|
40 | return 0;
|
41 | }, 10000, `deleting cached projects`);
|
42 | }
|
43 | async doWithProject(params, action) {
|
44 |
|
45 | if (!params.readOnly) {
|
46 | logger_1.logger.debug("Forcing fresh clone for non readonly use of '%j'", params.id);
|
47 | return this.saveAndRunAction(this.delegate, params, action);
|
48 | }
|
49 |
|
50 | if (!sha({ exact: true }).test(params.id.sha)) {
|
51 | logger_1.logger.debug("Forcing fresh clone for branch use of '%j'", params.id);
|
52 | return this.saveAndRunAction(this.delegate, params, action);
|
53 | }
|
54 | logger_1.logger.debug("Attempting to reuse clone for readonly use of '%j'", params.id);
|
55 | const key = cacheKey_1.cacheKey(params);
|
56 | let project = this.cache.get(key);
|
57 | if (!!project) {
|
58 |
|
59 | try {
|
60 | await util_1.promisify(fs.access)(project.baseDir);
|
61 | }
|
62 | catch (_a) {
|
63 | this.cache.evict(key);
|
64 | project = undefined;
|
65 | }
|
66 | }
|
67 | if (!project) {
|
68 | project = await save(this.delegate, params);
|
69 | logger_1.logger.debug("Caching project '%j' at '%s'", project.id, project.baseDir);
|
70 | this.cache.put(key, project);
|
71 | }
|
72 | logger_1.logger.debug("About to invoke action. Cache stats: %j", this.cache.stats);
|
73 | return action(project);
|
74 | }
|
75 | |
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 | async saveAndRunAction(delegate, params, action) {
|
82 | const p = await save(delegate, params);
|
83 | if (params.context && params.context.lifecycle) {
|
84 | params.context.lifecycle.registerDisposable(async () => this.cleanUp(p.baseDir, "disposal"));
|
85 | }
|
86 | else {
|
87 |
|
88 | setTimeout(async () => this.cleanUp(p.baseDir, "timeout"), 10000).unref();
|
89 |
|
90 | this.deleteOnExit.push(p.baseDir);
|
91 | }
|
92 | return action(p);
|
93 | }
|
94 | |
95 |
|
96 |
|
97 |
|
98 |
|
99 | async cleanUp(dir, reason) {
|
100 | if (dir && await fs.pathExists(dir)) {
|
101 | if (reason === "timeout") {
|
102 | logger_1.logger.debug(`Deleting project '%s' because a timeout passed`, dir);
|
103 | }
|
104 | else {
|
105 | logger_1.logger.debug(`Deleting project '%s' because %s was triggered`, dir, reason);
|
106 | }
|
107 | try {
|
108 | await fs.remove(dir);
|
109 | const ix = this.deleteOnExit.indexOf(dir);
|
110 | if (ix >= 0) {
|
111 | this.deleteOnExit.slice(ix, 1);
|
112 | }
|
113 | }
|
114 | catch (err) {
|
115 | logger_1.logger.warn(err);
|
116 | }
|
117 | }
|
118 | }
|
119 | }
|
120 | exports.CachingProjectLoader = CachingProjectLoader;
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 | function save(pl, params) {
|
127 | let p;
|
128 | return pl.doWithProject(params, async (loaded) => {
|
129 | p = loaded;
|
130 | }).then(() => p);
|
131 | }
|
132 | exports.save = save;
|
133 |
|
\ | No newline at end of file |