Skip to content

Commit 1c3a1fd

Browse files
committed
Use some in-memory cache for git ls-remote identical calls
1 parent cfc7aaf commit 1c3a1fd

1 file changed

Lines changed: 26 additions & 18 deletions

File tree

src/parser-includes.ts

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ type GitRemoteInfoContext = {
5353

5454
export class ParserIncludes {
5555
private static count: number = 0;
56+
private static gitRemoteInfoCache: Record<string, string> = {};
5657

5758
static resetCount (): void {
5859
this.count = 0;
@@ -114,13 +115,13 @@ export class ParserIncludes {
114115
promises.push(this.downloadIncludeRemote(cwd, stateDir, value["remote"], fetchIncludes, writeStreams));
115116
} else if (value["component"]) {
116117
const component = this.parseIncludeComponent(value["component"], gitData);
117-
componentParseCache.set(index, component);
118-
if (!component.isLocal)
119-
{
120-
promises.push(this.downloadIncludeComponent(opts, component.projectPath, component.effectiveRef, component.componentPath));
121-
}
118+
promises.push((async (componentParseCache: Map<number, ParsedComponent>, component: ParsedComponent, opts: ParserIncludesInitOptions) => {
119+
if (!component.isLocal) {
120+
await this.downloadIncludeComponent(opts, component.projectPath, component.effectiveRef, component.componentPath);
121+
}
122+
componentParseCache.set(index, component);
123+
})(componentParseCache, component, opts));
122124
}
123-
124125
}
125126

126127
await Promise.all(promises);
@@ -251,13 +252,30 @@ export class ParserIncludes {
251252
};
252253
}
253254

255+
static getGitRemoteInfo (ctx: GitRemoteInfoContext, ...args: string[]): string {
256+
const cmdArgs = ["git", "ls-remote", ...args];
257+
if (ctx.gitData.remote.schema == "git" || ctx.gitData.remote.schema == "ssh") {
258+
cmdArgs.push(`git@${ctx.domain}:${ctx.projectPath}`);
259+
} else {
260+
cmdArgs.push(`${ctx.gitData.remote.schema}://${ctx.domain}:${ctx.port ?? 443}/${ctx.projectPath}.git`);
261+
}
262+
const cmdStr = cmdArgs.join(" ");
263+
let info = this.gitRemoteInfoCache[cmdStr];
264+
if (!(cmdStr in this.gitRemoteInfoCache)) {
265+
info = Utils.syncSpawn(cmdArgs).stdout;
266+
this.gitRemoteInfoCache[cmdStr] = info;
267+
}
268+
return info;
269+
};
270+
254271
static parseIncludeComponent (component: string, gitData: GitData): ParsedComponent {
255272
assert(!component.includes("://"), `This GitLab CI configuration is invalid: component: \`${component}\` should not contain protocol`);
256273
const pattern = /(?<domain>[^/:\s]+)(:(?<port>\d+))?\/(?<projectPath>.+)\/(?<componentName>[^@]+)@(?<ref>.+)/; // https://regexr.com/7v7hm
257274
const gitRemoteMatch = pattern.exec(component);
258275
if (gitRemoteMatch?.groups == null) throw new Error(`This is a bug, please create a github issue if this is something you're expecting to work. input: ${component}`);
259276
const {domain, projectPath, port, componentName, ref} = gitRemoteMatch.groups;
260277
const isLocalComponent = projectPath === `${gitData.remote.group}/${gitData.remote.project}` && ref === gitData.commit.SHA;
278+
const parserIncludes = this; // eslint-disable-line @typescript-eslint/no-this-alias
261279
return {
262280
_cache: {
263281
version: undefined,
@@ -279,7 +297,7 @@ export class ParserIncludes {
279297
const semanticVersionRangesPattern = /^\d+(\.\d+)?$/;
280298
if (this.reference == "~latest" || semanticVersionRangesPattern.test(this.reference)) {
281299
// https://docs.gitlab.com/ci/components/#semantic-version-ranges
282-
const stdout = getGitRemoteInfo(this, "--tags");
300+
const stdout = parserIncludes.getGitRemoteInfo(this, "--tags");
283301
const tags = stdout.split("\n").map(line => line.split("\t")[1].split("/")[2]);
284302
const version = resolveSemanticVersionRange(this.reference, tags);
285303
assert(version, `This GitLab CI configuration is invalid: component: \`${this.name}\` - The reference (${this.reference}) is invalid`);
@@ -305,7 +323,7 @@ export class ParserIncludes {
305323
// effectiveRef may already be a sha, if so return it directly
306324
this._cache.sha = this.effectiveRef;
307325
} else {
308-
const stdout = getGitRemoteInfo(this);
326+
const stdout = parserIncludes.getGitRemoteInfo(this);
309327
const lines = stdout.split("\n");
310328
// annotated tags: prefer the deref'd commit sha (refs/tags/x^{})
311329
const match = lines.find(line => line.endsWith(`refs/tags/${this.effectiveRef}^{}`)) ??
@@ -505,13 +523,3 @@ export async function resolveIncludeLocal (pattern: string, cwd: string) {
505523
const re2js = RE2JS.compile(`^${pattern}`);
506524
return repoFiles.filter((f: any) => re2js.matches(f));
507525
}
508-
509-
export function getGitRemoteInfo (ctx: GitRemoteInfoContext, ...args: string[]) {
510-
const cmdArgs = ["git", "ls-remote", ...args];
511-
if (ctx.gitData.remote.schema == "git" || ctx.gitData.remote.schema == "ssh") {
512-
cmdArgs.push(`git@${ctx.domain}:${ctx.projectPath}`);
513-
} else {
514-
cmdArgs.push(`${ctx.gitData.remote.schema}://${ctx.domain}:${ctx.port ?? 443}/${ctx.projectPath}.git`);
515-
}
516-
return Utils.syncSpawn(cmdArgs).stdout;
517-
}

0 commit comments

Comments
 (0)