Skip to content
This repository was archived by the owner on Aug 8, 2025. It is now read-only.

Commit e707a0f

Browse files
committed
Cleanup clones in CachingProjectLoader
fixes #482
1 parent 4bdd76c commit e707a0f

File tree

2 files changed

+32
-12
lines changed

2 files changed

+32
-12
lines changed

src/api-helper/project/CachingProjectLoader.ts

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@
1616

1717
import { logger } from "@atomist/automation-client";
1818
import { GitProject } from "@atomist/automation-client/project/git/GitProject";
19-
import * as fs from "fs";
19+
import * as fs from "fs-extra";
2020
import { promisify } from "util";
21-
import { ProjectLoader, ProjectLoadingParameters, WithLoadedProject } from "../../spi/project/ProjectLoader";
21+
import {
22+
ProjectLoader,
23+
ProjectLoadingParameters,
24+
WithLoadedProject,
25+
} from "../../spi/project/ProjectLoader";
2226
import { CloningProjectLoader } from "./cloningProjectLoader";
2327
import { cacheKeyForSha } from "./support/cacheKey";
2428
import { LruCache } from "./support/LruCache";
@@ -33,12 +37,16 @@ export class CachingProjectLoader implements ProjectLoader {
3337

3438
public async doWithProject<T>(params: ProjectLoadingParameters, action: WithLoadedProject<T>): Promise<T> {
3539
if (!params.readOnly) {
36-
logger.info("CachingProjectLoader: Forcing fresh clone for non readonly use of %j", params.id);
40+
logger.info("Forcing fresh clone for non readonly use of '%j'", params.id);
3741
const p = await save(this.delegate, params);
38-
return action(p);
42+
return action(p)
43+
.then(result => {
44+
cleanUp(p);
45+
return result;
46+
});
3947
}
4048

41-
logger.debug("CachingProjectLoader: Hoping to reuse clone for readonly use of %j", params.id);
49+
logger.debug("Attempting to reuse clone for readonly use of '%j'", params.id);
4250
const key = cacheKeyForSha(params.id);
4351
let project = this.cache.get(key);
4452
if (!!project) {
@@ -47,27 +55,37 @@ export class CachingProjectLoader implements ProjectLoader {
4755
await promisify(fs.access)(project.baseDir);
4856
} catch {
4957
this.cache.evict(key);
50-
logger.warn("CachingProjectLoader: Invalid cache entry %s", key);
58+
logger.warn("Invalid cache entry '%s'", key);
5159
project = undefined;
5260
}
5361
}
5462

5563
if (!project) {
5664
project = await save(this.delegate, params);
57-
logger.info("Caching project %j", project.id);
65+
logger.info("Caching project '%j'", project.id);
5866
this.cache.put(key, project);
5967
}
6068

61-
logger.debug("CachingProjectLoader: About to invoke action. Cache stats: %j", this.cache.stats);
69+
logger.debug("About to invoke action. Cache stats: %j", this.cache.stats);
6270
return action(project);
6371
}
6472

6573
constructor(
6674
private readonly delegate: ProjectLoader = CloningProjectLoader,
6775
maxEntries: number = 20) {
68-
this.cache = new LruCache<GitProject>(maxEntries);
76+
this.cache = new LruCache<GitProject>(maxEntries, cleanUp);
6977
}
78+
}
7079

80+
function cleanUp(p: GitProject): void {
81+
logger.debug(`Evicting project '%j'`, p.id);
82+
if (p.baseDir && fs.accessSync(p.baseDir)) {
83+
try {
84+
fs.removeSync(p.baseDir);
85+
} catch (err) {
86+
logger.warn(err);
87+
}
88+
}
7189
}
7290

7391
export function save(pl: ProjectLoader, params: ProjectLoadingParameters): Promise<GitProject> {

src/api-helper/project/support/LruCache.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ export class LruCache<T> implements SimpleCache<T> {
2727

2828
private readonly values: Map<string, T> = new Map<string, T>();
2929

30-
constructor(private readonly maxEntries: number = 200) {
30+
constructor(private readonly maxEntries: number = 200,
31+
private readonly evictCallback: (t: T) => void = () => { /** intentionally left empty */}) {
3132
}
3233

3334
get stats() {
@@ -42,7 +43,7 @@ export class LruCache<T> implements SimpleCache<T> {
4243
++this.hits;
4344
// Peek the entry, re-insert for LRU strategy
4445
entry = this.values.get(key);
45-
this.values.delete(key);
46+
this.evict(key);
4647
this.values.set(key, entry);
4748
}
4849
return entry;
@@ -52,12 +53,13 @@ export class LruCache<T> implements SimpleCache<T> {
5253
if (this.values.size >= this.maxEntries) {
5354
// least-recently used cache eviction strategy
5455
const keyToDelete = this.values.keys().next().value;
55-
this.values.delete(keyToDelete);
56+
this.evict(keyToDelete);
5657
}
5758
this.values.set(key, value);
5859
}
5960

6061
public evict(key: string) {
62+
this.evictCallback(this.values.get(key));
6163
return this.values.delete(key);
6264
}
6365

0 commit comments

Comments
 (0)