| 252 | } |
| 253 | |
| 254 | static parseIncludeComponent (component: string, gitData: GitData): ParsedComponent { |
| 255 | assert(!component.includes("://"), `This GitLab CI configuration is invalid: component: \`${component}\` should not contain protocol`); |
| 256 | const pattern = /(?<domain>[^/:\s]+)(:(?<port>\d+))?\/(?<projectPath>.+)\/(?<componentName>[^@]+)@(?<ref>.+)/; // https://regexr.com/7v7hm |
| 257 | const gitRemoteMatch = pattern.exec(component); |
| 258 | 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}`); |
| 259 | const {domain, projectPath, port, componentName, ref} = gitRemoteMatch.groups; |
| 260 | const isLocalComponent = projectPath === `${gitData.remote.group}/${gitData.remote.project}` && ref === gitData.commit.SHA; |
| 261 | return { |
| 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}^{}`)) ?? |