| 403 | } |
| 404 | |
| 405 | static async downloadIncludeComponent (opts: ParserIncludesInitOptions, project: string, ref: string, componentName: string): Promise<void> { |
| 406 | const {cwd, stateDir, gitData, fetchIncludes, writeStreams} = opts; |
| 407 | const remote = gitData.remote; |
| 408 | const files = [`${componentName}.yml`, `${componentName}/template.yml`]; |
| 409 | let tmpDir = null; |
| 410 | try { |
| 411 | const target = `${stateDir}/includes/${remote.host}/${project}/${ref}`; |
| 412 | |
| 413 | if (!fetchIncludes && (await fs.pathExists(`${cwd}/${target}/${files[0]}`) || await fs.pathExists(`${cwd}/${target}/${files[1]}`))) return; |
| 414 | const time = process.hrtime(); |
| 415 | |
| 416 | if (remote.schema.startsWith("http")) { |
| 417 | const ext = "tmp-" + Math.random(); |
| 418 | await fs.mkdirp(path.dirname(`${cwd}/${target}/templates`)); |
| 419 | tmpDir = `${cwd}/${target}.${ext}`; |
| 420 | |
| 421 | const gitCloneBranch = (ref === "HEAD") ? "" : `--branch ${ref}`; |
| 422 | await Utils.bashMulti([ |
| 423 | `cd ${cwd}/${stateDir}`, |
| 424 | `git clone ${gitCloneBranch} -n --depth=1 --filter=tree:0 ${remote.schema}://${remote.host}:${remote.port}/${project}.git ${tmpDir}`, |
| 425 | `cd ${tmpDir}`, |
| 426 | `git sparse-checkout set --no-cone ${files[0]} ${files[1]}`, |
| 427 | "git checkout", |
| 428 | `cd ${cwd}/${stateDir}`, |
| 429 | `mkdir -p ${tmpDir}/templates`, // create templates subdir (if it doesn't exist), as the check out may not create it |
| 430 | `cp -r ${tmpDir}/templates ${cwd}/${target}`, |
| 431 | ], cwd); |
| 432 | } else { |
| 433 | // git archive fails if the paths do not exist, to work around this we use a wildcard "templates/component*.yml" |
| 434 | // this resolves to either "templates/component.yml" or "templates/component/template.yml" |
| 435 | // if both exist "templates/component.yml" will be pulled |
| 436 | // Drawback: also pulls all other .yml files from templates/component/ directory |
| 437 | const componentWildcard = `${componentName}*.yml`; |
| 438 | await fs.mkdirp(`${cwd}/${target}`); |
| 439 | await Utils.bash(`set -eou pipefail; git archive --remote=ssh://git@${remote.host}:${remote.port}/${project}.git ${ref} ${componentWildcard} | tar -f - -xC ${target}/`, cwd); |
| 440 | } |
| 441 | writeStreams.stderr(chalk`{grey downloaded ${project} ${ref} ${componentName} in ${prettyHrtime(process.hrtime(time))}}\n`); |
| 442 | } catch (e) { |
| 443 | throw new AssertionError({message: `Component include could not be fetched { project: ${project}, ref: ${ref}, file: ${files} }\n${e}`}); |
| 444 | } finally { |
| 445 | if (tmpDir !== null) { |
| 446 | // always cleanup temporary directory (if created) |
| 447 | await fs.rm(tmpDir, {recursive: true, force: true}); |
| 448 | } |
| 449 | } |
| 450 | } |
| 451 | |
| 452 | static readonly memoLocalRepoFiles = (() => { |
| 453 | const cache = new Map<string, string[]>(); |