fix(hwlab): cache yaml in node tools image (#808)
Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
@@ -152,6 +152,8 @@ targets:
|
||||
- COPY --from=python-runtime /usr/local /usr/local
|
||||
- COPY --from=docker-cli-runtime /usr/local/bin/docker /usr/local/bin/docker
|
||||
- RUN ln -sf /usr/local/bin/bun /usr/local/bin/bunx
|
||||
- ENV NODE_PATH=/usr/local/lib/node_modules
|
||||
- RUN npm install -g --omit=dev --ignore-scripts --no-audit --no-fund yaml@2.8.3 && NODE_PATH=/usr/local/lib/node_modules node -e "require.resolve('yaml')"
|
||||
- RUN node --version && npm --version && bun --version && git --version && python3 --version && docker --version && ssh -V
|
||||
buildArgs:
|
||||
NODE_IMAGE: docker.io/library/node:22-bookworm-slim
|
||||
|
||||
@@ -4948,60 +4948,18 @@ function nodeRuntimePipelinePostprocessScript(): string[] {
|
||||
"};",
|
||||
"const stepEnv = { ...proxyEnv, ...dockerProxyEnv, ...(overlay.stepEnv || {}) };",
|
||||
"function prepareSourceDependencyScript() {",
|
||||
" const registry = String(overlay.npmRegistry || 'https://registry.npmjs.org/');",
|
||||
" const timeoutSeconds = Math.max(15, Math.ceil(Number(overlay.npmFetchTimeoutMs || 120000) / 1000));",
|
||||
" const retryCount = Math.max(0, Math.floor(Number(overlay.npmRetries || 3)));",
|
||||
" return `prepare_source_dependencies_started_ms=\"$(ci_now_ms)\"",
|
||||
"node <<'NODE_UNIDESK_YAML_DEPENDENCY'",
|
||||
"const { spawnSync } = require('node:child_process');",
|
||||
"const fs = require('node:fs');",
|
||||
"const os = require('node:os');",
|
||||
"const path = require('node:path');",
|
||||
"const registry = ${JSON.stringify(registry)};",
|
||||
"const timeoutMs = ${JSON.stringify(timeoutSeconds * 1000)};",
|
||||
"const timeoutSeconds = ${JSON.stringify(timeoutSeconds)};",
|
||||
"const retryCount = ${JSON.stringify(retryCount)};",
|
||||
"const dependency = 'yaml';",
|
||||
"const version = '2.8.3';",
|
||||
"function emit(status, extra = {}) { console.error(JSON.stringify({ event: 'prepare-source-dependencies', status, dependency, ...extra })); }",
|
||||
"function hasYaml() { try { require.resolve('yaml'); return true; } catch { return false; } }",
|
||||
"function run(command, args) {",
|
||||
" const result = spawnSync(command, args, { stdio: 'inherit', env: process.env, timeout: timeoutMs });",
|
||||
" if (result.error) console.error(JSON.stringify({ event: 'prepare-source-dependencies', status: 'command-error', command, error: result.error.message }));",
|
||||
" return result.status === 0;",
|
||||
"try {",
|
||||
" const resolved = require.resolve('yaml');",
|
||||
" emit('cached', { manager: 'tools-image', resolved });",
|
||||
" process.exit(0);",
|
||||
"} catch (error) {",
|
||||
" emit('failed', { reason: 'tools-image-missing-yaml', message: error && error.message ? error.message : String(error) });",
|
||||
" process.exit(34);",
|
||||
"}",
|
||||
"function tailNpmLog() {",
|
||||
" const dir = path.join(process.env.HOME || '/tmp', '.npm', '_logs');",
|
||||
" if (!fs.existsSync(dir)) return;",
|
||||
" const files = fs.readdirSync(dir).filter((name) => name.includes('debug') && name.endsWith('.log')).sort();",
|
||||
" const file = files[files.length - 1];",
|
||||
" if (!file) return;",
|
||||
" const full = path.join(dir, file);",
|
||||
" console.error(JSON.stringify({ event: 'prepare-source-dependencies', status: 'npm-debug-log', path: full }));",
|
||||
" const lines = fs.readFileSync(full, 'utf8').split(String.fromCharCode(10));",
|
||||
" console.error(lines.slice(-80).join(String.fromCharCode(10)));",
|
||||
"}",
|
||||
"if (hasYaml()) { emit('cached'); process.exit(0); }",
|
||||
"fs.mkdirSync('node_modules/yaml', { recursive: true });",
|
||||
"let registryBase = registry;",
|
||||
"while (registryBase.endsWith('/')) registryBase = registryBase.slice(0, -1);",
|
||||
"const tarball = registryBase + '/yaml/-/yaml-' + version + '.tgz';",
|
||||
"const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'hwlab-yaml-'));",
|
||||
"const tgz = path.join(tmpDir, 'yaml.tgz');",
|
||||
"if (run('curl', ['-fsSL', '--retry', String(retryCount), '--connect-timeout', '10', '--max-time', String(timeoutSeconds), '-o', tgz, tarball]) && run('tar', ['-xzf', tgz, '-C', 'node_modules/yaml', '--strip-components=1'])) emit('installed', { manager: 'tarball' });",
|
||||
"else emit('tarball-failed', { manager: 'tarball' });",
|
||||
"fs.rmSync(tmpDir, { recursive: true, force: true });",
|
||||
"if (!hasYaml()) {",
|
||||
" fs.rmSync('node_modules/yaml', { recursive: true, force: true });",
|
||||
" if (run('bun', ['add', '--no-save', '--ignore-scripts', '--registry', registry, 'yaml@' + version]) && hasYaml()) emit('installed', { manager: 'bun' });",
|
||||
" else emit('bun-failed', { manager: 'bun' });",
|
||||
"}",
|
||||
"if (!hasYaml()) {",
|
||||
" fs.rmSync('node_modules/yaml', { recursive: true, force: true });",
|
||||
" if (run('npm', ['install', '--package-lock=false', '--no-save', '--ignore-scripts', '--no-audit', '--no-fund', '--omit=dev', '--registry', registry, 'yaml@' + version]) && hasYaml()) emit('installed', { manager: 'npm' });",
|
||||
" else { emit('npm-failed', { manager: 'npm' }); tailNpmLog(); process.exit(34); }",
|
||||
"}",
|
||||
"if (!hasYaml()) { emit('failed', { reason: 'unresolved' }); process.exit(34); }",
|
||||
"NODE_UNIDESK_YAML_DEPENDENCY",
|
||||
"ci_timing_emit prepare-source-dependencies succeeded \"$prepare_source_dependencies_started_ms\"`;",
|
||||
"}",
|
||||
@@ -5902,6 +5860,7 @@ function nodeRuntimePipelinePostprocessScript(): string[] {
|
||||
"if (!structured && !changed && !text.includes(`--gitops-root ${quotedRoot}`) && !text.includes(`--gitops-root ${escapedQuotedRoot}`)) { throw new Error(`generated pipeline missing expected gitops-render invocation in ${pipelinePath}`); }",
|
||||
"if (text.includes('prepare_source_dependencies_started_ms=\"$(ci_now_ms)\"') && text.includes('npm ci --ignore-scripts --no-audit --prefer-offline')) { throw new Error(`generated pipeline still uses full npm ci prepare-source dependency install in ${pipelinePath}`); }",
|
||||
"if (text.includes('prepare_source_dependencies_started_ms=\"$(ci_now_ms)\"') && !text.includes('NODE_UNIDESK_YAML_DEPENDENCY')) { throw new Error(`generated pipeline missing UniDesk yaml dependency install in ${pipelinePath}`); }",
|
||||
"if (text.includes('/yaml/-/yaml-') || text.includes('bun add --no-save --ignore-scripts') || text.includes('npm install --package-lock=false --no-save')) { throw new Error(`generated pipeline still downloads yaml during prepare-source in ${pipelinePath}`); }",
|
||||
"if (text.includes('npm run gitops:ts:check')) { throw new Error(`generated pipeline still uses npm gitops:ts:check gate in ${pipelinePath}`); }",
|
||||
"fs.writeFileSync(pipelinePath, text);",
|
||||
"patchGitMirrorTransportYaml();",
|
||||
|
||||
Reference in New Issue
Block a user