diff --git a/scripts/artifact-publish-bun-runtime.test.mjs b/scripts/artifact-publish-bun-runtime.test.mjs new file mode 100644 index 00000000..6fb24760 --- /dev/null +++ b/scripts/artifact-publish-bun-runtime.test.mjs @@ -0,0 +1,14 @@ +import assert from "node:assert/strict"; +import { readFile } from "node:fs/promises"; +import test from "node:test"; + +test("bun-command artifact Dockerfile installs native Bun binary without npm postinstall", async () => { + const source = await readFile("scripts/artifact-publish.mjs", "utf8"); + assert.match(source, /@oven\/bun-linux-x64@1\.3\.13/u); + assert.match(source, /@oven\/bun-linux-aarch64@1\.3\.13/u); + assert.match(source, /--ignore-scripts "\$bun_pkg"/u); + assert.match(source, /ln -sf "\/app\/node_modules\/\\\$\{bun_pkg%@\*\}\/bin\/bun" \/usr\/local\/bin\/bun/u); + assert.doesNotMatch(source, /npm install --no-save --omit=dev --include=optional --ignore-scripts bun@/u); + assert.match(source, /hwlab-artifact-runtime-check-failed:\$1/u); + assert.match(source, /fail bun-version/u); +}); diff --git a/scripts/artifact-publish.mjs b/scripts/artifact-publish.mjs index 04a05047..ca1e28a1 100644 --- a/scripts/artifact-publish.mjs +++ b/scripts/artifact-publish.mjs @@ -1210,7 +1210,8 @@ if (runtimeKind === "node-command" && entrypoint) { } function dockerfile(baseImage, port, labels = []) { - const dependencyTimingScript = `started_at=$(node -e "console.log(Date.now())"); status=succeeded; if [ -d /opt/hwlab-node-runtime-base/node_modules ]; then cp -a /opt/hwlab-node-runtime-base/node_modules ./node_modules; fi && if [ -f package-lock.json ]; then npm install --omit=dev --include=optional --ignore-scripts; else npm install --omit=dev --include=optional --ignore-scripts; fi && if [ "\${HWLAB_ARTIFACT_KIND:-}" = "bun-command" ]; then npm install --no-save --omit=dev --include=optional --ignore-scripts bun@1.3.13 && test -x /app/node_modules/.bin/bun && ln -sf /app/node_modules/.bin/bun /usr/local/bin/bun; fi && codex_version="$(node -p "require('./node_modules/@openai/codex/package.json').version")" && case "$(uname -m)" in x86_64|amd64) test -e node_modules/@openai/codex-linux-x64 || npm install --omit=dev --include=optional --ignore-scripts "@openai/codex-linux-x64@npm:@openai/codex@\${codex_version}-linux-x64" ;; aarch64|arm64) test -e node_modules/@openai/codex-linux-arm64 || npm install --omit=dev --include=optional --ignore-scripts "@openai/codex-linux-arm64@npm:@openai/codex@\${codex_version}-linux-arm64" ;; esac || status=failed; finished_at=$(node -e "console.log(Date.now())"); node -e "console.log(JSON.stringify({event:'g14-cicd-build-step-timing',stage:'dependency-install',status:process.argv[1],durationMs:Number(process.argv[3])-Number(process.argv[2]),source:'dockerfile-run'}))" "$status" "$started_at" "$finished_at"; test "$status" = succeeded`; + const dependencyTimingScript = `started_at=$(node -e "console.log(Date.now())"); status=succeeded; if [ -d /opt/hwlab-node-runtime-base/node_modules ]; then cp -a /opt/hwlab-node-runtime-base/node_modules ./node_modules; fi && if [ -f package-lock.json ]; then npm install --omit=dev --include=optional --ignore-scripts; else npm install --omit=dev --include=optional --ignore-scripts; fi && if [ "\${HWLAB_ARTIFACT_KIND:-}" = "bun-command" ]; then bun_pkg=""; case "$(uname -m)" in x86_64|amd64) bun_pkg="@oven/bun-linux-x64@1.3.13" ;; aarch64|arm64) bun_pkg="@oven/bun-linux-aarch64@1.3.13" ;; *) echo "unsupported bun-command architecture: $(uname -m)" >&2; false ;; esac && npm install --no-save --omit=dev --include=optional --ignore-scripts "$bun_pkg" && test -x "node_modules/\${bun_pkg%@*}/bin/bun" && ln -sf "/app/node_modules/\${bun_pkg%@*}/bin/bun" /usr/local/bin/bun; fi && codex_version="$(node -p "require('./node_modules/@openai/codex/package.json').version")" && case "$(uname -m)" in x86_64|amd64) test -e node_modules/@openai/codex-linux-x64 || npm install --omit=dev --include=optional --ignore-scripts "@openai/codex-linux-x64@npm:@openai/codex@\${codex_version}-linux-x64" ;; aarch64|arm64) test -e node_modules/@openai/codex-linux-arm64 || npm install --omit=dev --include=optional --ignore-scripts "@openai/codex-linux-arm64@npm:@openai/codex@\${codex_version}-linux-arm64" ;; esac || status=failed; finished_at=$(node -e "console.log(Date.now())"); node -e "console.log(JSON.stringify({event:'g14-cicd-build-step-timing',stage:'dependency-install',status:process.argv[1],durationMs:Number(process.argv[3])-Number(process.argv[2]),source:'dockerfile-run'}))" "$status" "$started_at" "$finished_at"; test "$status" = succeeded`; + const runtimeReadinessScript = `set -eu; fail() { echo "hwlab-artifact-runtime-check-failed:$1" >&2; exit 1; }; mkdir -p /workspace /codex-home; rm -rf /workspace/hwlab; ln -s /app /workspace/hwlab; if [ "\${HWLAB_ARTIFACT_KIND:-}" = "bun-command" ]; then test -x /usr/local/bin/bun || fail bun-missing; /usr/local/bin/bun --version >/tmp/hwlab-bun-version.txt || fail bun-version; fi; chmod -R a+rwX /app /workspace /codex-home || fail chmod; test -x /app/node_modules/.bin/codex || fail codex-bin-missing; /app/node_modules/.bin/codex --version >/tmp/hwlab-codex-version.txt || fail codex-version; test -x /app/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/codex/codex || test -x /app/node_modules/@openai/codex-linux-arm64/vendor/aarch64-unknown-linux-musl/codex/codex || fail codex-native-missing`; return [ `FROM ${baseImage}`, "WORKDIR /app", @@ -1236,7 +1237,7 @@ function dockerfile(baseImage, port, labels = []) { "COPY tools ./tools", "COPY skills ./skills", "COPY deploy ./deploy", - "RUN mkdir -p /workspace /codex-home && rm -rf /workspace/hwlab && ln -s /app /workspace/hwlab && if [ \"${HWLAB_ARTIFACT_KIND:-}\" = \"bun-command\" ]; then test -x /usr/local/bin/bun && /usr/local/bin/bun --version >/tmp/hwlab-bun-version.txt; fi && chmod -R a+rwX /app /workspace /codex-home && test -x /app/node_modules/.bin/codex && /app/node_modules/.bin/codex --version >/tmp/hwlab-codex-version.txt && (test -x /app/node_modules/@openai/codex-linux-x64/vendor/x86_64-unknown-linux-musl/codex/codex || test -x /app/node_modules/@openai/codex-linux-arm64/vendor/aarch64-unknown-linux-musl/codex/codex)", + `RUN ${runtimeReadinessScript}`, `RUN node -e "const fs=require('node:fs'); fs.writeFileSync('/usr/local/bin/hwlab-dev-artifact-runtime.mjs', Buffer.from('${runtimeScriptBase64()}', 'base64')); fs.chmodSync('/usr/local/bin/hwlab-dev-artifact-runtime.mjs', 0o755);"`, "ARG HWLAB_ENVIRONMENT", "ARG HWLAB_SERVICE_ID", diff --git a/scripts/g14-gitops-render.mjs b/scripts/g14-gitops-render.mjs index 86d38c83..1e225882 100644 --- a/scripts/g14-gitops-render.mjs +++ b/scripts/g14-gitops-render.mjs @@ -1152,7 +1152,7 @@ NODE fi rm -rf /workspace/service-work/repo mkdir -p /workspace/service-work/repo -cp -a /workspace/source/repo/. /workspace/service-work/repo/ +tar -C /workspace/source/repo -cf - . | tar -C /workspace/service-work/repo -xo -f - cd /workspace/service-work/repo export HWLAB_CI_ARTIFACT_RUN_ID="$(context.pipelineRun.name)-$(params.service-id)" export HWLAB_CI_ARTIFACT_RUN_OWNER="tekton" diff --git a/scripts/g14-gitops-render.test.ts b/scripts/g14-gitops-render.test.ts index e6c114d0..4d76a995 100644 --- a/scripts/g14-gitops-render.test.ts +++ b/scripts/g14-gitops-render.test.ts @@ -29,6 +29,7 @@ test("v02 render follows TypeScript runtime checks and does not self-patch boots const pipeline = await readFile(path.join(outDir, "tekton-v02", "pipeline.yaml"), "utf8"); const pipelineJson = JSON.parse(pipeline); assert.match(pipeline, /127\.0\.0\.1:5000\/hwlab\/hwlab-ci-node-tools:node22-alpine-bun-v1/u); + assert.match(pipeline, /tar -C \/workspace\/source\/repo -cf - \. \| tar -C \/workspace\/service-work\/repo -xo -f -/u); assert.match(pipeline, /node scripts\/run-bun\.mjs test cmd\/hwlab-codex-api-responses-forwarder\/main\.test\.ts/u); assert.doesNotMatch(pipeline, /cmd\/hwlab-codex-api-responses-forwarder\/main\.mjs/u); assert.match(pipeline, /process\.exitCode = 1/u);