From e06c8ca4cff194ac51ffde820c211a587e0fdde2 Mon Sep 17 00:00:00 2001 From: Yuhan Lei Date: Sat, 30 May 2026 01:01:08 +0800 Subject: [PATCH 1/7] ci: remove unused perf Playwright browser cache --- .github/workflows/perf-probe-baseline.yml | 11 ----------- packages/app/src/testing/perf-workflow.test.ts | 7 ++++--- .../opencode/test/github/perf-probe-workflow.test.ts | 9 ++++----- 3 files changed, 8 insertions(+), 19 deletions(-) diff --git a/.github/workflows/perf-probe-baseline.yml b/.github/workflows/perf-probe-baseline.yml index cfee1490e..440a217f5 100644 --- a/.github/workflows/perf-probe-baseline.yml +++ b/.github/workflows/perf-probe-baseline.yml @@ -20,9 +20,6 @@ on: permissions: contents: read -env: - PLAYWRIGHT_BROWSERS_PATH: ${{ github.workspace }}/.playwright-browsers - jobs: perf-probe-baseline: runs-on: ubuntu-latest @@ -112,14 +109,6 @@ jobs: restore-keys: | bun-${{ runner.os }}- - - uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 - if: steps.compute-range.outputs.skipped != 'true' - with: - path: ${{ github.workspace }}/.playwright-browsers - key: playwright-${{ runner.os }}-${{ hashFiles('head/packages/app/package.json', 'head/bun.lock', 'base/packages/app/package.json', 'base/bun.lock') }} - restore-keys: | - playwright-${{ runner.os }}- - - name: Install head dependencies if: steps.compute-range.outputs.skipped != 'true' working-directory: head diff --git a/packages/app/src/testing/perf-workflow.test.ts b/packages/app/src/testing/perf-workflow.test.ts index 0d796c94e..846750f50 100644 --- a/packages/app/src/testing/perf-workflow.test.ts +++ b/packages/app/src/testing/perf-workflow.test.ts @@ -5,9 +5,9 @@ const normalizeLineEndings = (text: string) => text.replace(/\r\n?/g, "\n") describe("perf workflow contract", () => { test("matches workflow snippets after Windows line-ending checkout", () => { - const workflow = normalizeLineEndings("restore-keys: |\r\n playwright-${{ runner.os }}-") + const workflow = normalizeLineEndings("restore-keys: |\r\n bun-${{ runner.os }}-") - expect(workflow).toContain("restore-keys: |\n playwright-${{ runner.os }}-") + expect(workflow).toContain("restore-keys: |\n bun-${{ runner.os }}-") }) test("keeps default gate broad and low-end gate scoped", async () => { @@ -28,7 +28,8 @@ describe("perf workflow contract", () => { expect(workflow).toContain('PLAYWRIGHT_VIDEO: "off"') expect(workflow).not.toContain("playwright install --with-deps chromium") expect(workflow).toContain("actions/cache/restore@") - expect(workflow).toContain("restore-keys: |\n playwright-${{ runner.os }}-") + expect(workflow).not.toContain("PLAYWRIGHT_BROWSERS_PATH") + expect(workflow).not.toContain(".playwright-browsers") expect(workflow).toContain("perf-base-combined.json") expect(workflow).toContain("perf-head-combined.json") expect(workflow).toContain("perf-comment.md") diff --git a/packages/opencode/test/github/perf-probe-workflow.test.ts b/packages/opencode/test/github/perf-probe-workflow.test.ts index 917dde7c6..8bc89e34e 100644 --- a/packages/opencode/test/github/perf-probe-workflow.test.ts +++ b/packages/opencode/test/github/perf-probe-workflow.test.ts @@ -21,11 +21,10 @@ describe("perf probe baseline workflow", () => { expect(parsed.name).toBe("perf-probe-baseline") expect(parsed.permissions).toEqual({ contents: "read" }) - expect(cacheSteps.map((step) => step.uses)).toEqual([pinned.cacheRestore, pinned.cacheRestore]) - expect(cacheSteps.map((step) => step.with?.path)).toEqual([ - "~/.bun/install/cache", - "${{ github.workspace }}/.playwright-browsers", - ]) + expect(cacheSteps.map((step) => step.uses)).toEqual([pinned.cacheRestore]) + expect(cacheSteps.map((step) => step.with?.path)).toEqual(["~/.bun/install/cache"]) + expect(parsed.env).not.toHaveProperty("PLAYWRIGHT_BROWSERS_PATH") + expect(workflow).not.toContain(".playwright-browsers") expect(workflow).not.toContain("issues: write") expect(workflow).not.toContain("pull-requests: write") expect(workflow).not.toContain("actions/github-script") From c542a4e93973946c24c48b3bcf6f504d7b2de6e2 Mon Sep 17 00:00:00 2001 From: Yuhan Lei Date: Sat, 30 May 2026 01:02:35 +0800 Subject: [PATCH 2/7] ci: add dependency audit checks --- .github/workflows/dependency-review.yml | 11 +--- .github/workflows/dev-dep-audit.yml | 36 +++++++++++ .../github/dependency-review-workflow.test.ts | 64 +++++++++++++++++++ 3 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/dev-dep-audit.yml create mode 100644 packages/opencode/test/github/dependency-review-workflow.test.ts diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 752471431..0b6e82825 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -13,7 +13,7 @@ permissions: pull-requests: read jobs: - review: + dependency-review: runs-on: ubuntu-latest timeout-minutes: 10 steps: @@ -21,13 +21,8 @@ jobs: with: persist-credentials: false - # Advisory check: `warn-only: true` surfaces CVE/license findings - # in the Actions log but never fails the PR. Paired with `never` - # for comment-summary-in-pr so the action needs no write access - # and runs on fork PRs too (exactly where advisory coverage is - # most useful). Upgrading to fail-on-severity: high is a separate - # decision. - uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # actions/dependency-review-action@v5.0.0 with: comment-summary-in-pr: never - warn-only: true + fail-on-severity: high + fail-on-scopes: runtime,unknown diff --git a/.github/workflows/dev-dep-audit.yml b/.github/workflows/dev-dep-audit.yml new file mode 100644 index 000000000..41bb29bb1 --- /dev/null +++ b/.github/workflows/dev-dep-audit.yml @@ -0,0 +1,36 @@ +name: dev-dep-audit + +on: + pull_request: + branches: [dev] + +concurrency: + group: dev-dep-audit-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + dev-dep-audit: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # actions/checkout@v6 + with: + persist-credentials: false + + - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # actions/setup-node@v6.4.0 + with: + node-version: "24" + + - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # oven-sh/setup-bun@v2 + with: + # Load-bearing for `bun audit` exit semantics; do not bump without re-verifying advisory exit codes. + bun-version: "1.3.14" + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Run Bun audit + run: bun audit --audit-level=high diff --git a/packages/opencode/test/github/dependency-review-workflow.test.ts b/packages/opencode/test/github/dependency-review-workflow.test.ts new file mode 100644 index 000000000..866801bec --- /dev/null +++ b/packages/opencode/test/github/dependency-review-workflow.test.ts @@ -0,0 +1,64 @@ +import { describe, expect, test } from "bun:test" +import path from "node:path" +import { fileURLToPath } from "node:url" +import { parseWorkflow } from "./workflow-parser" + +const repoRoot = fileURLToPath(new URL("../../../..", import.meta.url)) +const workflowsRoot = path.join(repoRoot, ".github", "workflows") + +const pinned = { + checkout: "actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd", + dependencyReview: "actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294", + setupBun: "oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6", + setupNode: "actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e", +} + +describe("dependency review workflow", () => { + test("uses the required dependency-review check name and fails high runtime advisories", () => { + const parsed = parseWorkflow(path.join(workflowsRoot, "dependency-review.yml")) + const job = parsed.jobs?.["dependency-review"] + const steps = job?.steps ?? [] + const checkout = steps.find((step) => step.uses?.startsWith("actions/checkout@")) + const review = steps.find((step) => step.uses?.startsWith("actions/dependency-review-action@")) + + expect(parsed.name).toBe("dependency-review") + expect(parsed.permissions).toEqual({ contents: "read", "pull-requests": "read" }) + expect(Object.keys(parsed.jobs ?? {})).toEqual(["dependency-review"]) + expect(job?.["runs-on"]).toBe("ubuntu-latest") + expect(job?.["timeout-minutes"]).toBe(10) + expect(checkout?.uses).toBe(pinned.checkout) + expect(checkout?.with?.["persist-credentials"]).toBe(false) + expect(review?.uses).toBe(pinned.dependencyReview) + expect(review?.with).toEqual({ + "comment-summary-in-pr": "never", + "fail-on-scopes": "runtime,unknown", + "fail-on-severity": "high", + }) + }) + + test("runs the dev dependency audit check on every pull request", () => { + const parsed = parseWorkflow(path.join(workflowsRoot, "dev-dep-audit.yml")) + const job = parsed.jobs?.["dev-dep-audit"] + const steps = job?.steps ?? [] + const checkout = steps.find((step) => step.uses?.startsWith("actions/checkout@")) + const setupNode = steps.find((step) => step.uses?.startsWith("actions/setup-node@")) + const setupBun = steps.find((step) => step.uses?.startsWith("oven-sh/setup-bun@")) + const install = steps.find((step) => step.name === "Install dependencies") + const audit = steps.find((step) => step.name === "Run Bun audit") + + expect(parsed.name).toBe("dev-dep-audit") + expect(parsed.on?.pull_request).toEqual({ branches: ["dev"] }) + expect(parsed.permissions).toEqual({ contents: "read" }) + expect(Object.keys(parsed.jobs ?? {})).toEqual(["dev-dep-audit"]) + expect(job?.["runs-on"]).toBe("ubuntu-latest") + expect(job?.["timeout-minutes"]).toBe(10) + expect(checkout?.uses).toBe(pinned.checkout) + expect(checkout?.with?.["persist-credentials"]).toBe(false) + expect(setupNode?.uses).toBe(pinned.setupNode) + expect(setupNode?.with).toEqual({ "node-version": "24" }) + expect(setupBun?.uses).toBe(pinned.setupBun) + expect(setupBun?.with).toEqual({ "bun-version": "1.3.14" }) + expect(install?.run).toBe("bun install --frozen-lockfile") + expect(audit?.run).toBe("bun audit --audit-level=high") + }) +}) From 3f4141ffce8533821f53e6a1b64a40b77f41f2b6 Mon Sep 17 00:00:00 2001 From: Yuhan Lei Date: Sat, 30 May 2026 01:07:40 +0800 Subject: [PATCH 3/7] ci: pin Bun audit runtime --- .github/workflows/build.yml | 3 +- .github/workflows/ci.yml | 21 ++++--- .github/workflows/desktop-smoke.yml | 3 +- .github/workflows/e2e-artifacts.yml | 3 +- .github/workflows/officecli-bump.yml | 3 +- .github/workflows/perf-probe-baseline.yml | 3 +- .github/workflows/windows-advisory.yml | 3 +- package.json | 2 +- .../test/github/bun-version-workflow.test.ts | 59 +++++++++++++++++++ .../github/officecli-bump-workflow.test.ts | 2 +- 10 files changed, 87 insertions(+), 15 deletions(-) create mode 100644 packages/opencode/test/github/bun-version-workflow.test.ts diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f970b9a9d..845d27736 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -256,7 +256,8 @@ jobs: - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 with: - bun-version: "1.3.13" + # Load-bearing for `bun audit` exit semantics; do not bump without re-verifying advisory exit codes. + bun-version: "1.3.14" # Load-bearing: `bun install` runs `trustedDependencies` # postinstalls from the root package.json (electron, node-pty, diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 170de8c8e..d5a67096d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -125,7 +125,8 @@ jobs: - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # oven-sh/setup-bun@v2 with: - bun-version: "1.3.13" + # Load-bearing for `bun audit` exit semantics; do not bump without re-verifying advisory exit codes. + bun-version: "1.3.14" - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 with: @@ -169,7 +170,8 @@ jobs: - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # oven-sh/setup-bun@v2 with: - bun-version: "1.3.13" + # Load-bearing for `bun audit` exit semantics; do not bump without re-verifying advisory exit codes. + bun-version: "1.3.14" - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 with: @@ -256,7 +258,8 @@ jobs: - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # oven-sh/setup-bun@v2 with: - bun-version: "1.3.13" + # Load-bearing for `bun audit` exit semantics; do not bump without re-verifying advisory exit codes. + bun-version: "1.3.14" - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 with: @@ -300,7 +303,8 @@ jobs: - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # oven-sh/setup-bun@v2 with: - bun-version: "1.3.13" + # Load-bearing for `bun audit` exit semantics; do not bump without re-verifying advisory exit codes. + bun-version: "1.3.14" - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 with: @@ -371,7 +375,8 @@ jobs: - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # oven-sh/setup-bun@v2 with: - bun-version: "1.3.13" + # Load-bearing for `bun audit` exit semantics; do not bump without re-verifying advisory exit codes. + bun-version: "1.3.14" - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 with: @@ -442,7 +447,8 @@ jobs: - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # oven-sh/setup-bun@v2 with: - bun-version: "1.3.13" + # Load-bearing for `bun audit` exit semantics; do not bump without re-verifying advisory exit codes. + bun-version: "1.3.14" - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 with: @@ -513,7 +519,8 @@ jobs: - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # oven-sh/setup-bun@v2 with: - bun-version: "1.3.13" + # Load-bearing for `bun audit` exit semantics; do not bump without re-verifying advisory exit codes. + bun-version: "1.3.14" - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 with: diff --git a/.github/workflows/desktop-smoke.yml b/.github/workflows/desktop-smoke.yml index 2e152608b..4be6c26fe 100644 --- a/.github/workflows/desktop-smoke.yml +++ b/.github/workflows/desktop-smoke.yml @@ -125,7 +125,8 @@ jobs: - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # oven-sh/setup-bun@v2 with: - bun-version: "1.3.13" + # Load-bearing for `bun audit` exit semantics; do not bump without re-verifying advisory exit codes. + bun-version: "1.3.14" - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 with: diff --git a/.github/workflows/e2e-artifacts.yml b/.github/workflows/e2e-artifacts.yml index 324f8cd42..b302bc87c 100644 --- a/.github/workflows/e2e-artifacts.yml +++ b/.github/workflows/e2e-artifacts.yml @@ -132,7 +132,8 @@ jobs: - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # oven-sh/setup-bun@v2 with: - bun-version: "1.3.13" + # Load-bearing for `bun audit` exit semantics; do not bump without re-verifying advisory exit codes. + bun-version: "1.3.14" - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 with: diff --git a/.github/workflows/officecli-bump.yml b/.github/workflows/officecli-bump.yml index 912be7ff9..ecd1a53e9 100644 --- a/.github/workflows/officecli-bump.yml +++ b/.github/workflows/officecli-bump.yml @@ -35,7 +35,8 @@ jobs: - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # oven-sh/setup-bun@v2 with: - bun-version: "1.3.13" + # Load-bearing for `bun audit` exit semantics; do not bump without re-verifying advisory exit codes. + bun-version: "1.3.14" - name: Install dependencies run: bun install --frozen-lockfile diff --git a/.github/workflows/perf-probe-baseline.yml b/.github/workflows/perf-probe-baseline.yml index 440a217f5..933b6c574 100644 --- a/.github/workflows/perf-probe-baseline.yml +++ b/.github/workflows/perf-probe-baseline.yml @@ -99,7 +99,8 @@ jobs: - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # oven-sh/setup-bun@v2 if: steps.compute-range.outputs.skipped != 'true' with: - bun-version: "1.3.13" + # Load-bearing for `bun audit` exit semantics; do not bump without re-verifying advisory exit codes. + bun-version: "1.3.14" - uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 if: steps.compute-range.outputs.skipped != 'true' diff --git a/.github/workflows/windows-advisory.yml b/.github/workflows/windows-advisory.yml index bacce202d..8be78f1bf 100644 --- a/.github/workflows/windows-advisory.yml +++ b/.github/workflows/windows-advisory.yml @@ -225,7 +225,8 @@ jobs: - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # oven-sh/setup-bun@v2 with: - bun-version: "1.3.13" + # Load-bearing for `bun audit` exit semantics; do not bump without re-verifying advisory exit codes. + bun-version: "1.3.14" - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 with: diff --git a/package.json b/package.json index c7188b112..44dae66b6 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Desktop AI workstation for knowledge workers", "private": true, "type": "module", - "packageManager": "bun@1.3.13", + "packageManager": "bun@1.3.14", "scripts": { "dev": "bun run dev:desktop", "dev:desktop": "bun --cwd packages/desktop-electron dev", diff --git a/packages/opencode/test/github/bun-version-workflow.test.ts b/packages/opencode/test/github/bun-version-workflow.test.ts new file mode 100644 index 000000000..9eabe5c68 --- /dev/null +++ b/packages/opencode/test/github/bun-version-workflow.test.ts @@ -0,0 +1,59 @@ +import { describe, expect, test } from "bun:test" +import fs from "node:fs" +import path from "node:path" +import { fileURLToPath } from "node:url" + +const repoRoot = fileURLToPath(new URL("../../../..", import.meta.url)) +const workflowsRoot = path.join(repoRoot, ".github", "workflows") +const expectedBunVersion = "1.3.14" +const auditComment = "Load-bearing for `bun audit` exit semantics" + +describe("GitHub workflow Bun version pin", () => { + test("keeps every setup-bun runtime on the audit-verified Bun version", () => { + const packageJson = JSON.parse(fs.readFileSync(path.join(repoRoot, "package.json"), "utf8")) as { + packageManager?: string + } + expect(packageJson.packageManager).toBe(`bun@${expectedBunVersion}`) + + const workflowFiles = fs + .readdirSync(workflowsRoot) + .filter((name) => name.endsWith(".yml")) + .sort() + .map((name) => path.join(workflowsRoot, name)) + + const setupBunPins: string[] = [] + const missingComments: string[] = [] + + for (const workflowPath of workflowFiles) { + const lines = fs.readFileSync(workflowPath, "utf8").split(/\r?\n/) + for (const [index, line] of lines.entries()) { + if (!line.includes("bun-version:")) continue + + const relativePath = path.relative(repoRoot, workflowPath) + setupBunPins.push(`${relativePath}:${index + 1}:${line.trim()}`) + const previousLines = lines.slice(Math.max(0, index - 3), index).join("\n") + if (!previousLines.includes(auditComment)) { + missingComments.push(`${relativePath}:${index + 1}`) + } + } + } + + expect(setupBunPins.map((entry) => entry.replace(/:\d+:/, ":line:"))).toEqual([ + ".github/workflows/build.yml:line:bun-version: \"1.3.14\"", + ".github/workflows/ci.yml:line:bun-version: \"1.3.14\"", + ".github/workflows/ci.yml:line:bun-version: \"1.3.14\"", + ".github/workflows/ci.yml:line:bun-version: \"1.3.14\"", + ".github/workflows/ci.yml:line:bun-version: \"1.3.14\"", + ".github/workflows/ci.yml:line:bun-version: \"1.3.14\"", + ".github/workflows/ci.yml:line:bun-version: \"1.3.14\"", + ".github/workflows/ci.yml:line:bun-version: \"1.3.14\"", + ".github/workflows/desktop-smoke.yml:line:bun-version: \"1.3.14\"", + ".github/workflows/dev-dep-audit.yml:line:bun-version: \"1.3.14\"", + ".github/workflows/e2e-artifacts.yml:line:bun-version: \"1.3.14\"", + ".github/workflows/officecli-bump.yml:line:bun-version: \"1.3.14\"", + ".github/workflows/perf-probe-baseline.yml:line:bun-version: \"1.3.14\"", + ".github/workflows/windows-advisory.yml:line:bun-version: \"1.3.14\"", + ]) + expect(missingComments).toEqual([]) + }) +}) diff --git a/packages/opencode/test/github/officecli-bump-workflow.test.ts b/packages/opencode/test/github/officecli-bump-workflow.test.ts index ffa675d5c..af185a84b 100644 --- a/packages/opencode/test/github/officecli-bump-workflow.test.ts +++ b/packages/opencode/test/github/officecli-bump-workflow.test.ts @@ -37,7 +37,7 @@ describe("officecli bump workflow", () => { expect(setupNode?.uses).toBe("actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e") expect(setupNode?.with).toEqual({ "node-version": "24" }) expect(setupBun?.uses).toBe("oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6") - expect(setupBun?.with).toEqual({ "bun-version": "1.3.13" }) + expect(setupBun?.with).toEqual({ "bun-version": "1.3.14" }) expect(workflow).not.toContain("persist-credentials: true") expect(workflow).toContain("gh api repos/iOfficeAI/OfficeCLI/releases/latest") From a71eaeb2ff9deefd93481c094fb1f322c865701d Mon Sep 17 00:00:00 2001 From: Yuhan Lei Date: Sat, 30 May 2026 01:11:15 +0800 Subject: [PATCH 4/7] test: include workflow env contract type --- packages/opencode/test/github/workflow-parser.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/opencode/test/github/workflow-parser.ts b/packages/opencode/test/github/workflow-parser.ts index e8ce625f1..ce4f9700c 100644 --- a/packages/opencode/test/github/workflow-parser.ts +++ b/packages/opencode/test/github/workflow-parser.ts @@ -49,6 +49,7 @@ export type Workflow = { group?: string "cancel-in-progress"?: boolean | string } + env?: Record on?: Record permissions?: Record jobs?: Record From f99749c34cb88dcf6a12b4d5dedd135ff49c9534 Mon Sep 17 00:00:00 2001 From: Yuhan Lei Date: Sat, 30 May 2026 01:27:02 +0800 Subject: [PATCH 5/7] ci: keep Bun audit advisory --- .github/workflows/dev-dep-audit.yml | 1 + packages/opencode/test/github/dependency-review-workflow.test.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/dev-dep-audit.yml b/.github/workflows/dev-dep-audit.yml index 41bb29bb1..48b2f0f3a 100644 --- a/.github/workflows/dev-dep-audit.yml +++ b/.github/workflows/dev-dep-audit.yml @@ -33,4 +33,5 @@ jobs: run: bun install --frozen-lockfile - name: Run Bun audit + continue-on-error: true run: bun audit --audit-level=high diff --git a/packages/opencode/test/github/dependency-review-workflow.test.ts b/packages/opencode/test/github/dependency-review-workflow.test.ts index 866801bec..af0b84ae8 100644 --- a/packages/opencode/test/github/dependency-review-workflow.test.ts +++ b/packages/opencode/test/github/dependency-review-workflow.test.ts @@ -59,6 +59,7 @@ describe("dependency review workflow", () => { expect(setupBun?.uses).toBe(pinned.setupBun) expect(setupBun?.with).toEqual({ "bun-version": "1.3.14" }) expect(install?.run).toBe("bun install --frozen-lockfile") + expect(audit?.["continue-on-error"]).toBe(true) expect(audit?.run).toBe("bun audit --audit-level=high") }) }) From 69e62f5be2c0312441ac7fa5d83d3125d1ed8881 Mon Sep 17 00:00:00 2001 From: Yuhan Lei Date: Sat, 30 May 2026 01:34:28 +0800 Subject: [PATCH 6/7] test: require setup-bun version pins --- .../test/github/bun-version-workflow.test.ts | 65 ++++++++++++++----- 1 file changed, 48 insertions(+), 17 deletions(-) diff --git a/packages/opencode/test/github/bun-version-workflow.test.ts b/packages/opencode/test/github/bun-version-workflow.test.ts index 9eabe5c68..30e4362e9 100644 --- a/packages/opencode/test/github/bun-version-workflow.test.ts +++ b/packages/opencode/test/github/bun-version-workflow.test.ts @@ -2,13 +2,43 @@ import { describe, expect, test } from "bun:test" import fs from "node:fs" import path from "node:path" import { fileURLToPath } from "node:url" +import { parseWorkflow, type Workflow } from "./workflow-parser" const repoRoot = fileURLToPath(new URL("../../../..", import.meta.url)) const workflowsRoot = path.join(repoRoot, ".github", "workflows") const expectedBunVersion = "1.3.14" const auditComment = "Load-bearing for `bun audit` exit semantics" +function collectSetupBunPins(workflow: Workflow, relativePath: string) { + const pins: string[] = [] + + for (const [jobName, job] of Object.entries(workflow.jobs ?? {})) { + for (const [stepIndex, step] of (job.steps ?? []).entries()) { + if (!step.uses?.startsWith("oven-sh/setup-bun@")) continue + + const bunVersion = step.with?.["bun-version"] ?? "" + pins.push(`${relativePath}:${jobName}:step-${stepIndex + 1}:bun-version: ${JSON.stringify(bunVersion)}`) + } + } + + return pins +} + describe("GitHub workflow Bun version pin", () => { + test("detects setup-bun steps that omit bun-version", () => { + const workflow: Workflow = { + jobs: { + unpinned: { + steps: [{ uses: "oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6" }], + }, + }, + } + + expect(collectSetupBunPins(workflow, "synthetic.yml")).toEqual([ + "synthetic.yml:unpinned:step-1:bun-version: \"\"", + ]) + }) + test("keeps every setup-bun runtime on the audit-verified Bun version", () => { const packageJson = JSON.parse(fs.readFileSync(path.join(repoRoot, "package.json"), "utf8")) as { packageManager?: string @@ -25,12 +55,13 @@ describe("GitHub workflow Bun version pin", () => { const missingComments: string[] = [] for (const workflowPath of workflowFiles) { + const relativePath = path.relative(repoRoot, workflowPath) + setupBunPins.push(...collectSetupBunPins(parseWorkflow(workflowPath), relativePath)) + const lines = fs.readFileSync(workflowPath, "utf8").split(/\r?\n/) for (const [index, line] of lines.entries()) { if (!line.includes("bun-version:")) continue - const relativePath = path.relative(repoRoot, workflowPath) - setupBunPins.push(`${relativePath}:${index + 1}:${line.trim()}`) const previousLines = lines.slice(Math.max(0, index - 3), index).join("\n") if (!previousLines.includes(auditComment)) { missingComments.push(`${relativePath}:${index + 1}`) @@ -38,21 +69,21 @@ describe("GitHub workflow Bun version pin", () => { } } - expect(setupBunPins.map((entry) => entry.replace(/:\d+:/, ":line:"))).toEqual([ - ".github/workflows/build.yml:line:bun-version: \"1.3.14\"", - ".github/workflows/ci.yml:line:bun-version: \"1.3.14\"", - ".github/workflows/ci.yml:line:bun-version: \"1.3.14\"", - ".github/workflows/ci.yml:line:bun-version: \"1.3.14\"", - ".github/workflows/ci.yml:line:bun-version: \"1.3.14\"", - ".github/workflows/ci.yml:line:bun-version: \"1.3.14\"", - ".github/workflows/ci.yml:line:bun-version: \"1.3.14\"", - ".github/workflows/ci.yml:line:bun-version: \"1.3.14\"", - ".github/workflows/desktop-smoke.yml:line:bun-version: \"1.3.14\"", - ".github/workflows/dev-dep-audit.yml:line:bun-version: \"1.3.14\"", - ".github/workflows/e2e-artifacts.yml:line:bun-version: \"1.3.14\"", - ".github/workflows/officecli-bump.yml:line:bun-version: \"1.3.14\"", - ".github/workflows/perf-probe-baseline.yml:line:bun-version: \"1.3.14\"", - ".github/workflows/windows-advisory.yml:line:bun-version: \"1.3.14\"", + expect(setupBunPins).toEqual([ + ".github/workflows/build.yml:build-electron:step-5:bun-version: \"1.3.14\"", + ".github/workflows/ci.yml:typecheck:step-3:bun-version: \"1.3.14\"", + ".github/workflows/ci.yml:lint:step-3:bun-version: \"1.3.14\"", + ".github/workflows/ci.yml:frontend-architecture:step-5:bun-version: \"1.3.14\"", + ".github/workflows/ci.yml:unit-app:step-3:bun-version: \"1.3.14\"", + ".github/workflows/ci.yml:unit-ui:step-3:bun-version: \"1.3.14\"", + ".github/workflows/ci.yml:unit-opencode:step-3:bun-version: \"1.3.14\"", + ".github/workflows/ci.yml:unit-desktop:step-3:bun-version: \"1.3.14\"", + ".github/workflows/desktop-smoke.yml:smoke-macos-arm64:step-3:bun-version: \"1.3.14\"", + ".github/workflows/dev-dep-audit.yml:dev-dep-audit:step-3:bun-version: \"1.3.14\"", + ".github/workflows/e2e-artifacts.yml:e2e-artifacts:step-3:bun-version: \"1.3.14\"", + ".github/workflows/officecli-bump.yml:officecli-bump:step-3:bun-version: \"1.3.14\"", + ".github/workflows/perf-probe-baseline.yml:perf-probe-baseline:step-7:bun-version: \"1.3.14\"", + ".github/workflows/windows-advisory.yml:unit-windows:step-3:bun-version: \"1.3.14\"", ]) expect(missingComments).toEqual([]) }) From 42bc2aa989efe5bf10d65e90179e29475e6e9247 Mon Sep 17 00:00:00 2001 From: Yuhan Lei Date: Sat, 30 May 2026 02:07:33 +0800 Subject: [PATCH 7/7] ci: fail dev dependency audit advisories --- .github/workflows/dev-dep-audit.yml | 1 - .../opencode/test/github/dependency-review-workflow.test.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/dev-dep-audit.yml b/.github/workflows/dev-dep-audit.yml index 48b2f0f3a..41bb29bb1 100644 --- a/.github/workflows/dev-dep-audit.yml +++ b/.github/workflows/dev-dep-audit.yml @@ -33,5 +33,4 @@ jobs: run: bun install --frozen-lockfile - name: Run Bun audit - continue-on-error: true run: bun audit --audit-level=high diff --git a/packages/opencode/test/github/dependency-review-workflow.test.ts b/packages/opencode/test/github/dependency-review-workflow.test.ts index af0b84ae8..e53a3909f 100644 --- a/packages/opencode/test/github/dependency-review-workflow.test.ts +++ b/packages/opencode/test/github/dependency-review-workflow.test.ts @@ -59,7 +59,7 @@ describe("dependency review workflow", () => { expect(setupBun?.uses).toBe(pinned.setupBun) expect(setupBun?.with).toEqual({ "bun-version": "1.3.14" }) expect(install?.run).toBe("bun install --frozen-lockfile") - expect(audit?.["continue-on-error"]).toBe(true) + expect(audit?.["continue-on-error"]).toBeUndefined() expect(audit?.run).toBe("bun audit --audit-level=high") }) })