mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-21 04:10:47 +02:00
feat: componentize GStack 2 runtime and release integrity
This commit is contained in:
@@ -1,8 +1,12 @@
|
||||
name: Workflow Lint
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
actionlint:
|
||||
runs-on: ubicloud-standard-8
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: rhysd/actionlint@v1.7.11
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
- uses: rhysd/actionlint@393031adb9afb225ee52ae2ccd7a5af5525e03e8 # v1.7.11
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
name: Build CI Image
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
# Rebuild weekly (Monday 6am UTC) to pick up CLI updates
|
||||
schedule:
|
||||
@@ -20,18 +23,18 @@ jobs:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
# Copy lockfile + package.json into Docker build context
|
||||
- run: cp package.json bun.lock .github/docker/
|
||||
|
||||
- uses: docker/login-action@v3
|
||||
- uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- uses: docker/build-push-action@v6
|
||||
- uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
|
||||
with:
|
||||
context: .github/docker
|
||||
file: .github/docker/Dockerfile.ci
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
name: Periodic Evals
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 6 * * 1' # Monday 6 AM UTC
|
||||
@@ -22,12 +25,12 @@ jobs:
|
||||
outputs:
|
||||
image-tag: ${{ steps.meta.outputs.tag }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- id: meta
|
||||
run: echo "tag=${{ env.IMAGE }}:${{ hashFiles('.github/docker/Dockerfile.ci', 'package.json', 'bun.lock') }}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- uses: docker/login-action@v3
|
||||
- uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@@ -46,7 +49,7 @@ jobs:
|
||||
run: cp package.json bun.lock .github/docker/
|
||||
|
||||
- if: steps.check.outputs.exists == 'false'
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
|
||||
with:
|
||||
context: .github/docker
|
||||
file: .github/docker/Dockerfile.ci
|
||||
@@ -88,7 +91,7 @@ jobs:
|
||||
- name: e2e-gemini
|
||||
file: test/gemini-e2e.test.ts
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -126,7 +129,7 @@ jobs:
|
||||
|
||||
- name: Upload eval results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: eval-periodic-${{ matrix.suite.name }}
|
||||
path: ~/.gstack-dev/evals/*.json
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
name: E2E Evals
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
@@ -15,6 +18,8 @@ env:
|
||||
jobs:
|
||||
# Build Docker image with pre-baked toolchain (cached — only rebuilds on Dockerfile/lockfile change)
|
||||
build-image:
|
||||
# Paid secrets and package-write credentials never run on fork code.
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: ubicloud-standard-8
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -22,12 +27,12 @@ jobs:
|
||||
outputs:
|
||||
image-tag: ${{ steps.meta.outputs.tag }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- id: meta
|
||||
run: echo "tag=${{ env.IMAGE }}:${{ hashFiles('.github/docker/Dockerfile.ci', 'package.json', 'bun.lock') }}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- uses: docker/login-action@v3
|
||||
- uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9 # v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
@@ -46,7 +51,7 @@ jobs:
|
||||
run: cp package.json bun.lock .github/docker/
|
||||
|
||||
- if: steps.check.outputs.exists == 'false'
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
|
||||
with:
|
||||
context: .github/docker
|
||||
file: .github/docker/Dockerfile.ci
|
||||
@@ -56,6 +61,7 @@ jobs:
|
||||
${{ env.IMAGE }}:latest
|
||||
|
||||
evals:
|
||||
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
|
||||
runs-on: ${{ matrix.suite.runner || 'ubicloud-standard-8' }}
|
||||
needs: build-image
|
||||
container:
|
||||
@@ -105,7 +111,7 @@ jobs:
|
||||
file: test/skill-e2e-office-hours-auto-mode.test.ts test/skill-e2e-plan-mode-no-op.test.ts
|
||||
timeout: 35
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -254,7 +260,7 @@ jobs:
|
||||
|
||||
- name: Upload eval results
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: eval-${{ matrix.suite.name }}
|
||||
path: ~/.gstack-dev/evals/*.json
|
||||
@@ -263,7 +269,7 @@ jobs:
|
||||
report:
|
||||
runs-on: ubicloud-standard-8
|
||||
needs: evals
|
||||
if: always() && github.event_name == 'pull_request'
|
||||
if: always() && github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
|
||||
timeout-minutes: 5
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -275,12 +281,12 @@ jobs:
|
||||
# early and never hit it, which is why this stayed hidden). See #1802 CI fix.
|
||||
issues: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Download all eval artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
pattern: eval-*
|
||||
path: /tmp/eval-results
|
||||
|
||||
@@ -30,7 +30,7 @@ jobs:
|
||||
bun-version: 1.3.14
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version: 22
|
||||
node-version: 22.23.1
|
||||
- name: Configure isolated test identity
|
||||
run: |
|
||||
git config --global user.email "gstack2-ci@example.invalid"
|
||||
@@ -56,10 +56,27 @@ jobs:
|
||||
bun-version: 1.3.14
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version: 22
|
||||
node-version: 22.23.1
|
||||
- run: bun install --frozen-lockfile
|
||||
- run: bun run test:gstack2:install
|
||||
|
||||
installed-first-use:
|
||||
name: Installed six-skill first use
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
||||
with:
|
||||
bun-version: 1.3.14
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
- run: bun install --frozen-lockfile --ignore-scripts
|
||||
- run: bun run gen:gstack2
|
||||
- name: Prove consent-first capability setup continues after approval
|
||||
run: bun test --timeout 30000 test/gstack2-installation.test.ts test/gstack2-skill-ux.test.ts
|
||||
|
||||
dev-container:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 25
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
name: make-pdf copy-paste gate
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
@@ -36,11 +39,11 @@ jobs:
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
||||
with:
|
||||
bun-version: latest
|
||||
bun-version: 1.3.14
|
||||
|
||||
- name: Install dependencies
|
||||
run: bun install --frozen-lockfile
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
name: PR Title Sync
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# WHY pull_request_target (not pull_request): the default GITHUB_TOKEN is
|
||||
# READ-ONLY on fork PRs under `pull_request`, so the title-sync backstop could
|
||||
# never `gh pr edit` a fork/agent PR. `pull_request_target` runs in the base-repo
|
||||
@@ -39,7 +42,7 @@ jobs:
|
||||
steps:
|
||||
# Base repo only — trusted infra (the rewrite helper). No PR-head checkout.
|
||||
- name: Checkout base repo (trusted)
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
name: Release quality gate
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
quality:
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
||||
with:
|
||||
bun-version: 1.3.14
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
|
||||
- name: Install frozen dependencies
|
||||
run: bun install --frozen-lockfile --ignore-scripts
|
||||
|
||||
- name: Release and package contract tests
|
||||
run: bun run check:release
|
||||
|
||||
- name: Scan changed production text for credentials
|
||||
env:
|
||||
BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
|
||||
HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if ! git cat-file -e "${BASE_SHA}^{commit}" 2>/dev/null; then
|
||||
BASE_SHA=$(git rev-parse HEAD^)
|
||||
fi
|
||||
git diff --unified=0 --no-color "$BASE_SHA" "$HEAD_SHA" -- \
|
||||
. \
|
||||
':(exclude)test/**' \
|
||||
':(exclude)evals/**' \
|
||||
':(exclude)outputs/**' \
|
||||
':(exclude)lib/diagram-render/dist/**' \
|
||||
':(exclude)skills/*/references/support/**' \
|
||||
':(exclude)docs/gstack-2/BACKLOG-MAP.json' \
|
||||
| node .github/scripts/gate-secret-scan.mjs
|
||||
|
||||
- name: Pack, install, and invoke the npm runtime-control package
|
||||
run: node .github/scripts/smoke-packed-package.mjs "$GITHUB_WORKSPACE"
|
||||
|
||||
- name: Gate critical dependency advisories
|
||||
run: bun audit --audit-level=critical
|
||||
|
||||
- name: Syntax-check runtime JavaScript
|
||||
run: find runtime -type f \( -name '*.js' -o -name '*.mjs' \) -print0 | xargs -0 -n1 node --check
|
||||
|
||||
- name: Install ShellCheck
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y shellcheck=0.9.0-1
|
||||
shellcheck --version
|
||||
|
||||
- name: ShellCheck release and setup boundaries
|
||||
run: >-
|
||||
shellcheck --severity=error
|
||||
setup
|
||||
scripts/build.sh
|
||||
scripts/write-version-files.sh
|
||||
scripts/gstack2/runtime-install-smoke.sh
|
||||
browse/scripts/build-node-server.sh
|
||||
@@ -0,0 +1,198 @@
|
||||
name: Release runtime artifacts
|
||||
|
||||
on:
|
||||
push:
|
||||
tags: [v2.0.0]
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: runtime-release-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build ${{ matrix.target }}
|
||||
permissions:
|
||||
contents: read
|
||||
id-token: write
|
||||
attestations: write
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- os: macos-15
|
||||
target: darwin-arm64
|
||||
capabilities: browser,browser-visible,design,pdf,diagram,ios
|
||||
- os: macos-15-intel
|
||||
target: darwin-x64
|
||||
capabilities: browser,browser-visible,design,pdf,diagram,ios
|
||||
- os: ubuntu-24.04-arm
|
||||
target: linux-arm64
|
||||
capabilities: browser,browser-visible,design,pdf,diagram
|
||||
- os: ubuntu-24.04
|
||||
target: linux-x64
|
||||
capabilities: browser,browser-visible,design,pdf,diagram
|
||||
- os: windows-11-arm
|
||||
target: windows-arm64
|
||||
capabilities: browser,browser-visible,design,pdf,diagram
|
||||
- os: windows-2025
|
||||
target: windows-x64
|
||||
capabilities: browser,browser-visible,design,pdf,diagram
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 35
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
||||
with:
|
||||
bun-version: 1.3.14
|
||||
- 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
|
||||
|
||||
- name: Build and stage the complete managed runtime
|
||||
env:
|
||||
GSTACK_HOME: ${{ runner.temp }}/gstack-release-home
|
||||
TARGET: ${{ matrix.target }}
|
||||
CAPABILITIES: ${{ matrix.capabilities }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
node runtime/install.js \
|
||||
--source "$GITHUB_WORKSPACE" \
|
||||
--home "$GSTACK_HOME" \
|
||||
--version 2.0.0 \
|
||||
--install-now \
|
||||
--yes \
|
||||
--capabilities "$CAPABILITIES"
|
||||
active_slot=$(node -e 'const fs=require("fs"),p=process.argv[1];const v=JSON.parse(fs.readFileSync(p,"utf8")).current;if(typeof v!=="string"||!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(v))process.exit(1);process.stdout.write(v)' "$GSTACK_HOME/versions/current.json")
|
||||
active="$GSTACK_HOME/versions/$active_slot"
|
||||
test -f "$active/.gstack-bundle.json"
|
||||
case "$TARGET" in
|
||||
windows-*) managed_bun_rel=".gstack-runtime-tools/bun.exe" ;;
|
||||
*) managed_bun_rel=".gstack-runtime-tools/bun" ;;
|
||||
esac
|
||||
managed_bun="$active/$managed_bun_rel"
|
||||
test -f "$managed_bun"
|
||||
test "$("$managed_bun" --version)" = "1.3.14"
|
||||
test -f "$active/runtime/licenses/BUN-LICENSE-1.3.14.md"
|
||||
test -f "$active/runtime/licenses/BUN-SOURCE.md"
|
||||
node -e 'const fs=require("fs"),c=require("crypto"),root=process.argv[1],rel=process.argv[2];const m=JSON.parse(fs.readFileSync(root+"/.gstack-bundle.json","utf8"));if(m.tools?.bun?.path!==rel||m.tools?.bun?.version!=="1.3.14")process.exit(1);const license=fs.readFileSync(root+"/runtime/licenses/BUN-LICENSE-1.3.14.md");if(c.createHash("sha256").update(license).digest("hex")!=="2cb858b2db8fc793bca2093489c5bc8eee615d002cc4924254904044c27a0afa")process.exit(1)' "$active" "$managed_bun_rel"
|
||||
test -d "$active/.gstack-runtime-browsers"
|
||||
(
|
||||
cd "$active"
|
||||
PLAYWRIGHT_BROWSERS_PATH="$active/.gstack-runtime-browsers" \
|
||||
node --input-type=module --eval \
|
||||
'const { chromium } = await import("./node_modules/playwright/index.mjs"); for (const options of [{ headless: true }, { headless: true, channel: "chromium" }]) { const browser = await chromium.launch(options); await browser.close(); }'
|
||||
)
|
||||
node_command=$(node -p 'process.execPath')
|
||||
host_bun=$(command -v bun)
|
||||
host_bun_dir=$(cd "$(dirname "$host_bun")" && pwd -P)
|
||||
clean_path=""
|
||||
IFS=: read -r -a path_parts <<< "$PATH"
|
||||
for part in "${path_parts[@]}"; do
|
||||
physical=$(cd "$part" 2>/dev/null && pwd -P || printf '%s' "$part")
|
||||
if [ "$physical" != "$host_bun_dir" ]; then
|
||||
clean_path="${clean_path:+$clean_path:}$part"
|
||||
fi
|
||||
done
|
||||
if (PATH="$clean_path"; command -v bun >/dev/null 2>&1); then
|
||||
echo "Host-global Bun remained available after removing setup-bun from PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
test "$(PATH="$clean_path" GSTACK_NODE="$node_command" "$GSTACK_HOME/bin/bun" --version)" = "1.3.14"
|
||||
browser_cleanup() {
|
||||
PATH="$clean_path" GSTACK_NODE="$node_command" BROWSE_PARENT_PID=0 \
|
||||
"$GSTACK_HOME/bin/browse" stop >/dev/null 2>&1 || true
|
||||
}
|
||||
trap browser_cleanup EXIT
|
||||
PATH="$clean_path" GSTACK_NODE="$node_command" BROWSE_PARENT_PID=0 \
|
||||
"$GSTACK_HOME/bin/browse" goto about:blank
|
||||
PATH="$clean_path" GSTACK_NODE="$node_command" BROWSE_PARENT_PID=0 \
|
||||
"$GSTACK_HOME/bin/browse" status
|
||||
browser_cleanup
|
||||
trap - EXIT
|
||||
stage="$RUNNER_TEMP/runtime-components"
|
||||
mkdir -p "$stage" "$GITHUB_WORKSPACE/release-output"
|
||||
node .github/scripts/stage-runtime-components.mjs "$active" "$stage"
|
||||
for component_dir in "$stage"/*; do
|
||||
test -d "$component_dir" || continue
|
||||
component=$(basename "$component_dir")
|
||||
archive="$GITHUB_WORKSPACE/release-output/gstack-runtime-2.0.0-$TARGET-$component.tar.gz"
|
||||
tar -czf "$archive" -C "$component_dir" gstack
|
||||
node -e 'const fs=require("fs"),c=require("crypto"),p=process.argv[1];const b=fs.readFileSync(p);fs.writeFileSync(p+".sha256",c.createHash("sha256").update(b).digest("hex")+" "+require("path").basename(p)+"\n")' "$archive"
|
||||
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:
|
||||
subject-path: release-output/*.tar.gz
|
||||
|
||||
- name: Upload signed archive
|
||||
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
|
||||
with:
|
||||
name: runtime-${{ matrix.target }}
|
||||
path: release-output/*
|
||||
if-no-files-found: error
|
||||
retention-days: 14
|
||||
|
||||
manifest:
|
||||
name: Assemble manifest and GitHub Release
|
||||
needs: build
|
||||
runs-on: ubuntu-24.04
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
permissions:
|
||||
contents: write
|
||||
id-token: write
|
||||
attestations: write
|
||||
steps:
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
|
||||
with:
|
||||
pattern: runtime-*
|
||||
path: release-output
|
||||
merge-multiple: true
|
||||
- uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0
|
||||
|
||||
- name: Create strict six-target manifest
|
||||
run: node .github/scripts/create-runtime-release-manifest.mjs release-output "$GITHUB_REPOSITORY" 2.0.0
|
||||
|
||||
- name: Checksum and keyless-sign manifest
|
||||
run: |
|
||||
set -euo pipefail
|
||||
cd release-output
|
||||
sha256sum gstack-runtime-manifest.json > gstack-runtime-manifest.json.sha256
|
||||
cosign sign-blob --yes --bundle gstack-runtime-manifest.json.sigstore.json gstack-runtime-manifest.json
|
||||
shell: bash
|
||||
|
||||
- name: Attest manifest provenance
|
||||
uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2
|
||||
with:
|
||||
subject-path: release-output/gstack-runtime-manifest.json
|
||||
|
||||
- name: Publish immutable release assets
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
gh release create "$GITHUB_REF_NAME" \
|
||||
--verify-tag \
|
||||
--title "GStack runtime 2.0.0" \
|
||||
--notes "Signed optional runtime artifacts for the six portable GStack skills." \
|
||||
release-output/*
|
||||
shell: bash
|
||||
@@ -1,11 +1,15 @@
|
||||
name: Skill Docs Freshness
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
check-freshness:
|
||||
runs-on: ubicloud-standard-8
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
||||
- run: bun install
|
||||
- name: Check Claude host freshness
|
||||
run: bun run gen:skill-docs
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
name: Version Gate
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
@@ -20,13 +23,13 @@ jobs:
|
||||
pull-requests: read
|
||||
steps:
|
||||
- name: Checkout PR head
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
|
||||
- name: Setup Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
||||
|
||||
- name: Read versions
|
||||
id: versions
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
name: Windows Free Tests
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# Curated subset of the free test suite that runs on a paid faster Windows runner.
|
||||
#
|
||||
# Codex's v1.18.0.0 review flagged that the existing evals.yml workflow uses
|
||||
@@ -39,11 +42,11 @@ jobs:
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- uses: oven-sh/setup-bun@v1
|
||||
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
||||
with:
|
||||
bun-version: latest
|
||||
bun-version: 1.3.14
|
||||
|
||||
- name: Configure git identity (required by tests that init temp repos)
|
||||
run: |
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
name: Windows Setup E2E
|
||||
|
||||
# End-to-end fresh-install gate for Windows. Runs `./setup` on a clean
|
||||
# windows-latest checkout and asserts the build completes, binaries
|
||||
# resolve via find-browse, and the gstack-paths state root resolves
|
||||
# cleanly. Catches Bun shell-parser regressions in package.json's build
|
||||
# chain (#1538, #1537, #1530, #1457, #1561) before they reach users.
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
# End-to-end optional-runtime gate for Windows. It first proves dry-run is
|
||||
# non-mutating, then performs an explicit browser-capability install, checks
|
||||
# doctor and the native artifact, and uninstalls. Agent Skills host placement
|
||||
# is deliberately outside this workflow and is covered by gstack2-gate.yml.
|
||||
#
|
||||
# Separate from windows-free-tests.yml because that one runs a curated
|
||||
# unit-test subset; this one exercises the install path itself.
|
||||
@@ -19,6 +21,7 @@ on:
|
||||
- 'scripts/build.sh'
|
||||
- 'scripts/write-version-files.sh'
|
||||
- 'setup'
|
||||
- 'runtime/**'
|
||||
- 'browse/src/cli.ts'
|
||||
- 'browse/src/find-browse.ts'
|
||||
- 'bin/gstack-paths'
|
||||
@@ -35,11 +38,18 @@ jobs:
|
||||
timeout-minutes: 15
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
|
||||
|
||||
- uses: oven-sh/setup-bun@v1
|
||||
- name: Configure isolated runtime home
|
||||
run: echo "GSTACK_HOME=$RUNNER_TEMP/gstack-setup-e2e" >> "$GITHUB_ENV"
|
||||
shell: bash
|
||||
|
||||
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
|
||||
with:
|
||||
bun-version: latest
|
||||
bun-version: 1.3.14
|
||||
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
|
||||
with:
|
||||
node-version: 22.23.1
|
||||
|
||||
- name: Configure git identity
|
||||
run: |
|
||||
@@ -52,45 +62,34 @@ jobs:
|
||||
run: bun install --frozen-lockfile
|
||||
shell: bash
|
||||
|
||||
- name: Run bun run build (the previously-broken path)
|
||||
# This is the regression gate. Bun's Windows shell parser rejected
|
||||
# multiple constructs the old inline build chain used; the wave
|
||||
# moved the build to scripts/build.sh. If this step fails on
|
||||
# Windows, the build chain regressed.
|
||||
run: bun run build
|
||||
shell: bash
|
||||
env:
|
||||
GSTACK_SKIP_PLAYWRIGHT: '1'
|
||||
|
||||
- name: Verify binaries exist (with .exe extension on Windows)
|
||||
- name: Preview without mutating state
|
||||
run: |
|
||||
set -e
|
||||
test -f browse/dist/browse.exe || test -f browse/dist/browse || (echo "MISSING: browse" && exit 1)
|
||||
test -f browse/dist/find-browse.exe || test -f browse/dist/find-browse || (echo "MISSING: find-browse" && exit 1)
|
||||
test -f design/dist/design.exe || test -f design/dist/design || (echo "MISSING: design" && exit 1)
|
||||
test -f bin/gstack-global-discover.exe || test -f bin/gstack-global-discover || (echo "MISSING: gstack-global-discover" && exit 1)
|
||||
echo "All binaries present"
|
||||
bash ./setup --dry-run --capabilities browser
|
||||
test ! -e "$GSTACK_HOME" || (echo "dry-run mutated GSTACK_HOME" && exit 1)
|
||||
shell: bash
|
||||
|
||||
- name: Verify find-browse resolves to the .exe variant
|
||||
- name: Explicitly install the browser capability
|
||||
run: |
|
||||
set -e
|
||||
OUT=$(bun browse/src/find-browse.ts 2>&1) || true
|
||||
echo "find-browse output: $OUT"
|
||||
# On Windows, find-browse should successfully resolve to a binary,
|
||||
# whether or not it has the .exe extension on disk. Empty output
|
||||
# or "not found" means the .exe extension resolver regressed.
|
||||
echo "$OUT" | grep -qE '(browse\.exe|browse)$' || (echo "find-browse failed to resolve binary on Windows" && exit 1)
|
||||
bash ./setup --install-now --yes --capabilities browser
|
||||
test -f "$GSTACK_HOME/versions/current.json"
|
||||
test -f "$GSTACK_HOME/bin/gstack.cmd"
|
||||
shell: bash
|
||||
|
||||
- name: Verify gstack-paths state root resolves
|
||||
- name: Verify doctor and installed native browser
|
||||
run: |
|
||||
set -e
|
||||
eval "$(bash bin/gstack-paths)"
|
||||
test -n "$GSTACK_STATE_ROOT" || (echo "GSTACK_STATE_ROOT empty" && exit 1)
|
||||
test -n "$PLAN_ROOT" || (echo "PLAN_ROOT empty" && exit 1)
|
||||
test -n "$TMP_ROOT" || (echo "TMP_ROOT empty" && exit 1)
|
||||
echo "GSTACK_STATE_ROOT=$GSTACK_STATE_ROOT"
|
||||
echo "PLAN_ROOT=$PLAN_ROOT"
|
||||
echo "TMP_ROOT=$TMP_ROOT"
|
||||
node runtime/cli.js doctor --json > doctor.json
|
||||
node -e 'const r=require("./doctor.json");if(!r.checks.some(x=>x.id==="managed-runtime"&&x.status==="pass"))process.exit(1);if(!r.checks.some(x=>x.id==="capability:browser"&&x.status==="pass"))process.exit(1)'
|
||||
browser=$(node runtime/cli.js runtime path browse/dist/browse.exe)
|
||||
test -f "$browser"
|
||||
shell: bash
|
||||
|
||||
- name: Uninstall the runtime and preserve user state
|
||||
run: |
|
||||
set -e
|
||||
node runtime/cli.js uninstall
|
||||
test ! -e "$GSTACK_HOME/versions/current.json"
|
||||
test -d "$GSTACK_HOME/projects"
|
||||
shell: bash
|
||||
|
||||
Reference in New Issue
Block a user