diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml new file mode 100644 index 0000000..a282eab --- /dev/null +++ b/.github/workflows/preview.yml @@ -0,0 +1,30 @@ +name: Preview + +# Builds the per-package preview container (npm artifact + smoke test of the +# exact files pi loads) and pushes it to GHCR on main. +on: + pull_request: + push: + branches: [main] + +permissions: + contents: read + packages: write + +jobs: + preview: + runs-on: ubuntu-latest + timeout-minutes: 20 + steps: + - uses: actions/checkout@v7 + - name: Build preview image + run: docker build -f preview/Dockerfile -t ghcr.io/groeponline/pi-tools-preview:sha-${GITHUB_SHA} . + - name: Run preview smoke + run: docker run --rm ghcr.io/groeponline/pi-tools-preview:sha-${GITHUB_SHA} + - name: Push preview image (main only) + if: github.ref == 'refs/heads/main' + run: | + echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin + docker tag ghcr.io/groeponline/pi-tools-preview:sha-${GITHUB_SHA} ghcr.io/groeponline/pi-tools-preview:main + docker push ghcr.io/groeponline/pi-tools-preview:sha-${GITHUB_SHA} + docker push ghcr.io/groeponline/pi-tools-preview:main diff --git a/packages/pi-tools/src/tgrep.ts b/packages/pi-tools/src/tgrep.ts index e40003c..6a14c77 100644 --- a/packages/pi-tools/src/tgrep.ts +++ b/packages/pi-tools/src/tgrep.ts @@ -143,7 +143,17 @@ async function defaultExec( stdout?: string; stderr?: string; message?: string; + killed?: boolean; }; + // execFile reports a budget timeout as killed:true with code null, so it + // would otherwise fall through to the generic failure below. User aborts + // are already handled above; a kill here means the time budget ran out. + if (execError.killed) { + const budget = opts.timeoutMs ?? TGREP_TIME_BUDGET_MS; + throw new Error( + `tgrep timed out after ${budget}ms; narrow with fileType/glob or raise tgrepTimeBudgetMs`, + ); + } if (execError.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER") return { exit: 0, stdout: execError.stdout ?? "", stderr: execError.stderr ?? "" }; if (typeof execError.code === "number") diff --git a/packages/pi-tools/test/tgrep.test.ts b/packages/pi-tools/test/tgrep.test.ts index 2f18162..8e03d0d 100644 --- a/packages/pi-tools/test/tgrep.test.ts +++ b/packages/pi-tools/test/tgrep.test.ts @@ -381,6 +381,15 @@ describe("runTgrep", () => { await expect(running).rejects.toThrow("Operation aborted"); }); + test("maps a time-budget timeout to a narrowing hint", async () => { + await expect( + runTgrep(process.execPath, ["-e", "setTimeout(() => {}, 30000)"], { + cwd: "/tmp", + timeoutMs: 100, + }), + ).rejects.toThrow("timed out after 100ms"); + }); + test("forwards a custom time budget to the executor", async () => { let seen: number | undefined; await runTgrep( diff --git a/preview/Dockerfile b/preview/Dockerfile new file mode 100644 index 0000000..51c9914 --- /dev/null +++ b/preview/Dockerfile @@ -0,0 +1,20 @@ +# Preview image for @groeponline/pi-tools. +# Packs the npm artifact and smoke-tests the exact files pi loads, +# inside a clean container. (Build context is the repo root; only the +# packages/pi-tools subpackage is packed.) +# +# docker build -f preview/Dockerfile -t ghcr.io/groeponline/pi-tools-preview:local . +# docker run --rm ghcr.io/groeponline/pi-tools-preview:local +FROM node:22 AS build +WORKDIR /src/packages/pi-tools +COPY packages/pi-tools/package.json ./ +RUN npm install --ignore-scripts --no-audit --no-fund +COPY packages/pi-tools/ ./ +RUN mkdir -p /out && npm pack --pack-destination /out + +FROM node:22-slim AS preview +WORKDIR /preview +COPY --from=build /out/*.tgz ./pkg.tgz +COPY preview/smoke.sh ./smoke.sh +RUN npm install ./pkg.tgz --prefix ./pkg --no-audit --no-fund && sh ./smoke.sh ./pkg +CMD ["sh", "./smoke.sh", "./pkg"] diff --git a/preview/smoke.sh b/preview/smoke.sh new file mode 100644 index 0000000..4924716 --- /dev/null +++ b/preview/smoke.sh @@ -0,0 +1,9 @@ +#!/bin/sh +# Smoke-test the installed preview package: the exact entries pi loads must +# exist in the shipped artifact. +set -eu +PREFIX="${1:-./pkg}" +PKG="$PREFIX/node_modules/@groeponline/pi-tools" +fail() { echo "preview FAIL: $1" >&2; exit 1; } +test -f "$PKG/src/index.ts" || fail "missing pi extension entry src/index.ts" +echo "preview OK: @groeponline/pi-tools (src/index.ts present)"