diff --git a/.github/workflows/release-artifacts.yml b/.github/workflows/release-artifacts.yml index bb4c7b14d..b6aab910d 100644 --- a/.github/workflows/release-artifacts.yml +++ b/.github/workflows/release-artifacts.yml @@ -53,8 +53,6 @@ jobs: - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: 22.23.1 - - uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0 - - name: Install frozen dependencies run: bun install --frozen-lockfile --ignore-scripts shell: bash @@ -114,8 +112,9 @@ jobs: "$GSTACK_HOME/bin/browse" stop >/dev/null 2>&1 || true } trap browser_cleanup EXIT + smoke_url=$(node -e 'const fs=require("fs"),p=require("path").join(process.env.RUNNER_TEMP,"gstack-runtime-smoke.html");fs.writeFileSync(p,"GStack runtime smoke\n");process.stdout.write(require("url").pathToFileURL(p).href)') PATH="$clean_path" GSTACK_NODE="$node_command" BROWSE_PARENT_PID=0 \ - "$GSTACK_HOME/bin/browse" goto about:blank + "$GSTACK_HOME/bin/browse" goto "$smoke_url" PATH="$clean_path" GSTACK_NODE="$node_command" BROWSE_PARENT_PID=0 \ "$GSTACK_HOME/bin/browse" status browser_cleanup @@ -132,14 +131,6 @@ jobs: done shell: bash - - name: Keyless-sign component archives - run: | - set -euo pipefail - for archive in release-output/*.tar.gz; do - cosign sign-blob --yes --bundle "$archive.sigstore.json" "$archive" - done - shell: bash - - name: Attest component archive provenance uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2 with: @@ -171,6 +162,14 @@ jobs: merge-multiple: true - uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0 + - name: Keyless-sign component archives + run: | + set -euo pipefail + for archive in release-output/*.tar.gz; do + cosign sign-blob --yes --bundle "$archive.sigstore.json" "$archive" + done + shell: bash + - name: Create strict six-target manifest run: node .github/scripts/create-runtime-release-manifest.mjs release-output "$GITHUB_REPOSITORY" 2.0.0 "$GITHUB_REF_NAME" diff --git a/runtime/runtime-bootstrap.mjs b/runtime/runtime-bootstrap.mjs index 9419f2a87..8270ed190 100644 --- a/runtime/runtime-bootstrap.mjs +++ b/runtime/runtime-bootstrap.mjs @@ -16,7 +16,7 @@ export const BOOTSTRAP_RUNTIME_VERSION = "2.0.0"; // Keep the runtime compatibility version separate from the immutable release // channel. Release candidates carry the 2.0.0 runtime contract while letting // fresh-machine production journeys run before the stable v2.0.0 tag exists. -export const BOOTSTRAP_RELEASE_TAG = "v2.0.0-rc.1"; +export const BOOTSTRAP_RELEASE_TAG = "v2.0.0-rc.2"; export const OFFICIAL_MANIFEST_URL = `https://github.com/time-attack/gstack/releases/download/${BOOTSTRAP_RELEASE_TAG}/gstack-runtime-manifest.json`; const CAPABILITIES = new Set(["browser", "browser-visible", "design", "pdf", "diagram", "ios"]); diff --git a/skills/debug/references/support/runtime-bootstrap.mjs b/skills/debug/references/support/runtime-bootstrap.mjs index 9419f2a87..8270ed190 100644 --- a/skills/debug/references/support/runtime-bootstrap.mjs +++ b/skills/debug/references/support/runtime-bootstrap.mjs @@ -16,7 +16,7 @@ export const BOOTSTRAP_RUNTIME_VERSION = "2.0.0"; // Keep the runtime compatibility version separate from the immutable release // channel. Release candidates carry the 2.0.0 runtime contract while letting // fresh-machine production journeys run before the stable v2.0.0 tag exists. -export const BOOTSTRAP_RELEASE_TAG = "v2.0.0-rc.1"; +export const BOOTSTRAP_RELEASE_TAG = "v2.0.0-rc.2"; export const OFFICIAL_MANIFEST_URL = `https://github.com/time-attack/gstack/releases/download/${BOOTSTRAP_RELEASE_TAG}/gstack-runtime-manifest.json`; const CAPABILITIES = new Set(["browser", "browser-visible", "design", "pdf", "diagram", "ios"]); diff --git a/skills/design/references/support/runtime-bootstrap.mjs b/skills/design/references/support/runtime-bootstrap.mjs index 9419f2a87..8270ed190 100644 --- a/skills/design/references/support/runtime-bootstrap.mjs +++ b/skills/design/references/support/runtime-bootstrap.mjs @@ -16,7 +16,7 @@ export const BOOTSTRAP_RUNTIME_VERSION = "2.0.0"; // Keep the runtime compatibility version separate from the immutable release // channel. Release candidates carry the 2.0.0 runtime contract while letting // fresh-machine production journeys run before the stable v2.0.0 tag exists. -export const BOOTSTRAP_RELEASE_TAG = "v2.0.0-rc.1"; +export const BOOTSTRAP_RELEASE_TAG = "v2.0.0-rc.2"; export const OFFICIAL_MANIFEST_URL = `https://github.com/time-attack/gstack/releases/download/${BOOTSTRAP_RELEASE_TAG}/gstack-runtime-manifest.json`; const CAPABILITIES = new Set(["browser", "browser-visible", "design", "pdf", "diagram", "ios"]); diff --git a/skills/plan/references/support/runtime-bootstrap.mjs b/skills/plan/references/support/runtime-bootstrap.mjs index 9419f2a87..8270ed190 100644 --- a/skills/plan/references/support/runtime-bootstrap.mjs +++ b/skills/plan/references/support/runtime-bootstrap.mjs @@ -16,7 +16,7 @@ export const BOOTSTRAP_RUNTIME_VERSION = "2.0.0"; // Keep the runtime compatibility version separate from the immutable release // channel. Release candidates carry the 2.0.0 runtime contract while letting // fresh-machine production journeys run before the stable v2.0.0 tag exists. -export const BOOTSTRAP_RELEASE_TAG = "v2.0.0-rc.1"; +export const BOOTSTRAP_RELEASE_TAG = "v2.0.0-rc.2"; export const OFFICIAL_MANIFEST_URL = `https://github.com/time-attack/gstack/releases/download/${BOOTSTRAP_RELEASE_TAG}/gstack-runtime-manifest.json`; const CAPABILITIES = new Set(["browser", "browser-visible", "design", "pdf", "diagram", "ios"]); diff --git a/skills/qa/references/support/runtime-bootstrap.mjs b/skills/qa/references/support/runtime-bootstrap.mjs index 9419f2a87..8270ed190 100644 --- a/skills/qa/references/support/runtime-bootstrap.mjs +++ b/skills/qa/references/support/runtime-bootstrap.mjs @@ -16,7 +16,7 @@ export const BOOTSTRAP_RUNTIME_VERSION = "2.0.0"; // Keep the runtime compatibility version separate from the immutable release // channel. Release candidates carry the 2.0.0 runtime contract while letting // fresh-machine production journeys run before the stable v2.0.0 tag exists. -export const BOOTSTRAP_RELEASE_TAG = "v2.0.0-rc.1"; +export const BOOTSTRAP_RELEASE_TAG = "v2.0.0-rc.2"; export const OFFICIAL_MANIFEST_URL = `https://github.com/time-attack/gstack/releases/download/${BOOTSTRAP_RELEASE_TAG}/gstack-runtime-manifest.json`; const CAPABILITIES = new Set(["browser", "browser-visible", "design", "pdf", "diagram", "ios"]); diff --git a/skills/review/references/support/runtime-bootstrap.mjs b/skills/review/references/support/runtime-bootstrap.mjs index 9419f2a87..8270ed190 100644 --- a/skills/review/references/support/runtime-bootstrap.mjs +++ b/skills/review/references/support/runtime-bootstrap.mjs @@ -16,7 +16,7 @@ export const BOOTSTRAP_RUNTIME_VERSION = "2.0.0"; // Keep the runtime compatibility version separate from the immutable release // channel. Release candidates carry the 2.0.0 runtime contract while letting // fresh-machine production journeys run before the stable v2.0.0 tag exists. -export const BOOTSTRAP_RELEASE_TAG = "v2.0.0-rc.1"; +export const BOOTSTRAP_RELEASE_TAG = "v2.0.0-rc.2"; export const OFFICIAL_MANIFEST_URL = `https://github.com/time-attack/gstack/releases/download/${BOOTSTRAP_RELEASE_TAG}/gstack-runtime-manifest.json`; const CAPABILITIES = new Set(["browser", "browser-visible", "design", "pdf", "diagram", "ios"]); diff --git a/skills/ship/references/support/runtime-bootstrap.mjs b/skills/ship/references/support/runtime-bootstrap.mjs index 9419f2a87..8270ed190 100644 --- a/skills/ship/references/support/runtime-bootstrap.mjs +++ b/skills/ship/references/support/runtime-bootstrap.mjs @@ -16,7 +16,7 @@ export const BOOTSTRAP_RUNTIME_VERSION = "2.0.0"; // Keep the runtime compatibility version separate from the immutable release // channel. Release candidates carry the 2.0.0 runtime contract while letting // fresh-machine production journeys run before the stable v2.0.0 tag exists. -export const BOOTSTRAP_RELEASE_TAG = "v2.0.0-rc.1"; +export const BOOTSTRAP_RELEASE_TAG = "v2.0.0-rc.2"; export const OFFICIAL_MANIFEST_URL = `https://github.com/time-attack/gstack/releases/download/${BOOTSTRAP_RELEASE_TAG}/gstack-runtime-manifest.json`; const CAPABILITIES = new Set(["browser", "browser-visible", "design", "pdf", "diagram", "ios"]); diff --git a/test/gstack2-runtime-release-channel.test.ts b/test/gstack2-runtime-release-channel.test.ts index 53628ede0..964424ad3 100644 --- a/test/gstack2-runtime-release-channel.test.ts +++ b/test/gstack2-runtime-release-channel.test.ts @@ -57,10 +57,18 @@ describe("GStack runtime release channel", () => { test("release workflow publishes both RC and stable tags through the same signed manifest path", async () => { const workflow = await fs.readFile(WORKFLOW, "utf8"); + const buildSection = workflow.slice(workflow.indexOf(" build:"), workflow.indexOf("\n manifest:")); + const manifestSection = workflow.slice(workflow.indexOf("\n manifest:")); expect(workflow).toContain("v2.0.0-rc.*"); expect(workflow).toContain('2.0.0 "$GITHUB_REF_NAME"'); expect(workflow).toContain("PRERELEASE_FLAG:"); expect(workflow).toContain("--prerelease"); expect(workflow).toContain('gh release create "$GITHUB_REF_NAME"'); + expect(workflow).toContain("pathToFileURL(p).href"); + expect(workflow).not.toContain("goto about:blank"); + expect(buildSection).not.toContain("sigstore/cosign-installer"); + expect(manifestSection).toContain("sigstore/cosign-installer"); + expect(manifestSection.indexOf("Keyless-sign component archives")) + .toBeLessThan(manifestSection.indexOf("Create strict six-target manifest")); }); }); diff --git a/test/release-hardening.test.ts b/test/release-hardening.test.ts index 529683957..bde6c9351 100644 --- a/test/release-hardening.test.ts +++ b/test/release-hardening.test.ts @@ -53,14 +53,20 @@ describe("release and CI hardening", () => { expect(workflow).toContain("versions/current.json"); expect(workflow).not.toContain('active="$GSTACK_HOME/versions/2.0.0"'); expect(workflow).toContain(".gstack-runtime-browsers"); - expect(workflow).toContain('chromium.launch({ headless: true, channel: "chromium" })'); + // Exercise both the bundled browser and the explicit Chromium channel. Keep + // this semantic: the workflow intentionally loops over launch options so a + // harmless refactor does not invalidate release hardening. + expect(workflow).toMatch(/for \(const options of \[\{ headless: true \}, \{ headless: true, channel: ["']chromium["'] \}\]\)/); + expect(workflow).toContain("chromium.launch(options)"); + expect(workflow).toContain("await browser.close()"); expect(workflow).not.toContain("--with-deps"); expect(workflow).toContain(".gstack-runtime-tools/bun"); expect(workflow).toContain('"$GSTACK_HOME/bin/bun" --version'); expect(workflow).toContain("BUN-LICENSE-1.3.14.md"); expect(workflow).toContain("command -v bun"); expect(workflow).toContain("GSTACK_NODE=\"$node_command\""); - expect(workflow).toContain("goto about:blank"); + expect(workflow).toContain("pathToFileURL(p).href"); + expect(workflow).not.toContain("goto about:blank"); const manifest = read(".github/scripts/create-runtime-release-manifest.mjs"); expect(manifest).toContain("bytes: stat.size"); expect(manifest).toContain('certificateOidcIssuer: "https://token.actions.githubusercontent.com"'); @@ -80,7 +86,8 @@ describe("release and CI hardening", () => { expect(installer).toContain('entry(managedBunRelativePath(), "managed-bun", true)'); const browser = read("browse/src/cli.ts"); expect(browser).toContain("Every installed/compiled client must use the adjacent Node-compatible daemon"); - expect(browser).toContain("if (IS_COMPILED && !NODE_SERVER_SCRIPT)"); + expect(browser).toContain("export function resolveServerLaunchTarget("); + expect(browser).toContain("server-node.mjs not found. Rebuild the managed browser runtime"); }); test("Windows setup lane installs, doctors, and uninstalls rather than only building", () => {