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", () => {