Skip to content

Commit 43ad834

Browse files
committed
Lazy component.{version,effectiveRef,sha}
1 parent a6aeb0a commit 43ad834

1 file changed

Lines changed: 82 additions & 76 deletions

File tree

src/parser-includes.ts

Lines changed: 82 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -26,18 +26,31 @@ type ParserIncludesInitOptions = {
2626
};
2727

2828
type ParsedComponent = {
29+
_cache: {
30+
version: string | null | undefined;
31+
effectiveRef: string | undefined;
32+
sha: string | undefined;
33+
};
34+
gitData: GitData;
2935
domain: string;
3036
port: string;
3137
projectPath: string;
3238
componentPath: string;
33-
effectiveRef: string;
3439
name: string;
3540
reference: string;
36-
version: string | undefined;
41+
version: string | null;
42+
effectiveRef: string;
3743
sha: string;
3844
isLocal: boolean;
3945
};
4046

47+
type GitRemoteInfoContext = {
48+
gitData: GitData;
49+
domain: string;
50+
port: string;
51+
projectPath: string;
52+
};
53+
4154
export class ParserIncludes {
4255
private static count: number = 0;
4356

@@ -100,7 +113,7 @@ export class ParserIncludes {
100113
} else if (value["remote"]) {
101114
promises.push(this.downloadIncludeRemote(cwd, stateDir, value["remote"], fetchIncludes, writeStreams));
102115
} else if (value["component"]) {
103-
const component = this.parseIncludeComponent(value["component"], gitData, opts);
116+
const component = this.parseIncludeComponent(value["component"], gitData);
104117
componentParseCache.set(index, component);
105118
if (!component.isLocal)
106119
{
@@ -238,40 +251,74 @@ export class ParserIncludes {
238251
};
239252
}
240253

241-
static parseIncludeComponent (component: string, gitData: GitData, opts: ParserIncludesInitOptions): ParsedComponent {
254+
static parseIncludeComponent (component: string, gitData: GitData): ParsedComponent {
242255
assert(!component.includes("://"), `This GitLab CI configuration is invalid: component: \`${component}\` should not contain protocol`);
243256
const pattern = /(?<domain>[^/:\s]+)(:(?<port>\d+))?\/(?<projectPath>.+)\/(?<componentName>[^@]+)@(?<ref>.+)/; // https://regexr.com/7v7hm
244257
const gitRemoteMatch = pattern.exec(component);
245-
246258
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}`);
247-
248-
const {domain, projectPath, port} = gitRemoteMatch.groups;
249-
const reference = gitRemoteMatch.groups["ref"];
250-
const isLocalComponent = projectPath === `${gitData.remote.group}/${gitData.remote.project}` && reference === gitData.commit.SHA;
251-
const context = {
252-
isLocal: isLocalComponent,
253-
projectPath: projectPath,
254-
remote: gitData.remote,
255-
commit: gitData.commit,
256-
domain: domain,
257-
port: port,
258-
component: component,
259-
cwd: opts.cwd,
260-
};
261-
const name = gitRemoteMatch.groups["componentName"];
262-
const version = getComponentVersion(context, reference);
263-
const effectiveRef = version ?? reference;
264-
const sha = getComponentSha(context, effectiveRef);
259+
const {domain, projectPath, port, componentName, ref} = gitRemoteMatch.groups;
260+
const isLocalComponent = projectPath === `${gitData.remote.group}/${gitData.remote.project}` && ref === gitData.commit.SHA;
265261
return {
266-
domain: domain,
267-
port: port,
268-
projectPath: projectPath,
269-
componentPath: `templates/${name}`,
270-
effectiveRef: effectiveRef,
271-
name: name,
272-
reference: reference,
273-
sha: sha,
274-
version: version,
262+
_cache: {
263+
version: undefined,
264+
effectiveRef: undefined,
265+
sha: undefined,
266+
},
267+
gitData,
268+
domain,
269+
port,
270+
projectPath,
271+
componentPath: `templates/${componentName}`,
272+
name: componentName,
273+
reference: ref,
274+
get version () {
275+
if (this._cache.version === undefined) {
276+
if (this.isLocal) {
277+
this._cache.version = this.gitData.commit.SHA;
278+
} else {
279+
const semanticVersionRangesPattern = /^\d+(\.\d+)?$/;
280+
if (this.reference == "~latest" || semanticVersionRangesPattern.test(this.reference)) {
281+
// https://docs.gitlab.com/ci/components/#semantic-version-ranges
282+
const stdout = getGitRemoteInfo(this, "--tags");
283+
const tags = stdout.split("\n").map(line => line.split("\t")[1].split("/")[2]);
284+
const version = resolveSemanticVersionRange(this.reference, tags);
285+
assert(version, `This GitLab CI configuration is invalid: component: \`${this.name}\` - The reference (${this.reference}) is invalid`);
286+
this._cache.version = version;
287+
} else {
288+
this._cache.version = null;
289+
}
290+
}
291+
}
292+
return this._cache.version;
293+
},
294+
get effectiveRef () {
295+
if (this._cache.effectiveRef === undefined) {
296+
this._cache.effectiveRef = this.version ?? this.reference;
297+
}
298+
return this._cache.effectiveRef;
299+
},
300+
get sha () {
301+
if (this._cache.sha === undefined) {
302+
if (this.isLocal) {
303+
this._cache.sha = this.gitData.commit.SHA;
304+
} else if (/^[0-9a-f]{40}$/.test(this.effectiveRef)) {
305+
// effectiveRef may already be a sha, if so return it directly
306+
this._cache.sha = this.effectiveRef;
307+
} else {
308+
const stdout = getGitRemoteInfo(this);
309+
const lines = stdout.split("\n");
310+
// annotated tags: prefer the deref'd commit sha (refs/tags/x^{})
311+
const match = lines.find(line => line.endsWith(`refs/tags/${this.effectiveRef}^{}`)) ??
312+
lines.find(line =>
313+
line.endsWith(`refs/tags/${this.effectiveRef}`) ||
314+
line.endsWith(`refs/heads/${this.effectiveRef}`),
315+
);
316+
assert(match, `Could not resolve commit SHA for ${this.effectiveRef} in ${this.projectPath}`);
317+
this._cache.sha = match.split("\t")[0];
318+
}
319+
}
320+
return this._cache.sha;
321+
},
275322
isLocal: isLocalComponent,
276323
};
277324
}
@@ -459,53 +506,12 @@ export async function resolveIncludeLocal (pattern: string, cwd: string) {
459506
return repoFiles.filter((f: any) => re2js.matches(f));
460507
}
461508

462-
export function getGitRemoteInfo (ctx: any, ...args: string[]) {
509+
export function getGitRemoteInfo (ctx: GitRemoteInfoContext, ...args: string[]) {
463510
const cmdArgs = ["git", "ls-remote", ...args];
464-
if (ctx.remote.schema == "git" || ctx.remote.schema == "ssh") {
511+
if (ctx.gitData.remote.schema == "git" || ctx.gitData.remote.schema == "ssh") {
465512
cmdArgs.push(`git@${ctx.domain}:${ctx.projectPath}`);
466513
} else {
467-
cmdArgs.push(`${ctx.remote.schema}://${ctx.domain}:${ctx.port ?? 443}/${ctx.projectPath}.git`);
514+
cmdArgs.push(`${ctx.gitData.remote.schema}://${ctx.domain}:${ctx.port ?? 443}/${ctx.projectPath}.git`);
468515
}
469516
return Utils.syncSpawn(cmdArgs).stdout;
470517
}
471-
472-
export function getComponentVersion (ctx: any, reference: string) {
473-
if (!ctx.isLocal) {
474-
const semanticVersionRangesPattern = /^\d+(\.\d+)?$/;
475-
if (reference == "~latest" || semanticVersionRangesPattern.test(reference)) {
476-
// https://docs.gitlab.com/ci/components/#semantic-version-ranges
477-
const stdout = getGitRemoteInfo(ctx, "--tags");
478-
const tags = stdout
479-
.split("\n")
480-
.map((line) => {
481-
return line
482-
.split("\t")[1]
483-
.split("/")[2];
484-
});
485-
const version = resolveSemanticVersionRange(reference, tags);
486-
assert(version, `This GitLab CI configuration is invalid: component: \`${ctx.component}\` - The reference (${reference}) is invalid`);
487-
return version;
488-
}
489-
} else {
490-
return ctx.commit.SHA;
491-
}
492-
}
493-
494-
export function getComponentSha (ctx: any, effectiveRef: string) {
495-
if (ctx.isLocal) {
496-
return ctx.commit.SHA;
497-
}
498-
// effectiveRef may already be a sha, if so return it directly
499-
if (/^[0-9a-f]{40}$/.test(effectiveRef)) {
500-
return effectiveRef;
501-
}
502-
const stdout = getGitRemoteInfo(ctx);
503-
const lines = stdout.split("\n");
504-
// annotated tags: prefer the deref'd commit sha (refs/tags/x^{})
505-
const match = lines.find(line => line.endsWith(`refs/tags/${effectiveRef}^{}`))
506-
?? lines.find(line =>
507-
line.endsWith(`refs/tags/${effectiveRef}`) ||
508-
line.endsWith(`refs/heads/${effectiveRef}`));
509-
assert(match, `Could not resolve commit SHA for ${effectiveRef} in ${ctx.projectPath}`);
510-
return match.split("\t")[0];
511-
}

0 commit comments

Comments
 (0)