diff --git a/.github/dependabot.yml b/.github/dependabot.yml index e45e826b2..04d77299a 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -10,3 +10,62 @@ updates: cooldown: default-days: 5 open-pull-requests-limit: 5 + + # Root Bun workspace. Uses the `bun` ecosystem (Dependabot >= v1.2.5) + # rather than `npm`, so bun.lock and Bun-specific manifest features + # (including `catalog:`) are parsed by the correct updater. Groups + # keep transitive churn bundled so the PR queue stays reviewable. + - package-ecosystem: "bun" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "09:00" + timezone: "Asia/Shanghai" + cooldown: + default-days: 7 + open-pull-requests-limit: 5 + groups: + electron: + patterns: + - "electron" + - "electron-*" + - "@electron-forge/*" + - "@electron/*" + typescript-tooling: + patterns: + - "typescript" + - "@types/*" + - "@tsconfig/*" + - "ts-*" + - "tslib" + turbo-build: + patterns: + - "turbo" + - "@turbo/*" + - "vite" + - "@vitejs/*" + - "vitest" + - "@vitest/*" + testing: + patterns: + - "playwright" + - "@playwright/*" + - "@testing-library/*" + opencode: + patterns: + - "@opencode-ai/*" + production-minor-patch: + patterns: + - "*" + dependency-type: "production" + update-types: + - "minor" + - "patch" + development-minor-patch: + patterns: + - "*" + dependency-type: "development" + update-types: + - "minor" + - "patch" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 025575d80..c7682f22a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: outputs: docs_only: ${{ steps.filter.outputs.docs_only }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # actions/checkout@v6 with: fetch-depth: 0 persist-credentials: false @@ -92,11 +92,11 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 20 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # actions/checkout@v6 with: persist-credentials: false - - uses: actions/setup-node@v6.3.0 + - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # actions/setup-node@v6.3.0 with: node-version: "24" @@ -104,14 +104,14 @@ jobs: with: bun-version: "1.3.11" - - uses: actions/cache@v5 + - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 with: path: ~/.bun/install/cache key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }} restore-keys: | bun-${{ runner.os }}- - - uses: actions/cache@v5 + - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 with: path: node_modules/.cache/turbo key: turbo-${{ runner.os }}-${{ hashFiles('turbo.json', '**/package.json', 'bun.lock') }}-${{ github.sha }} @@ -133,11 +133,11 @@ jobs: contents: read checks: write steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # actions/checkout@v6 with: persist-credentials: false - - uses: actions/setup-node@v6.3.0 + - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # actions/setup-node@v6.3.0 with: node-version: "24" @@ -145,14 +145,14 @@ jobs: with: bun-version: "1.3.11" - - uses: actions/cache@v5 + - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 with: path: ~/.bun/install/cache key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }} restore-keys: | bun-${{ runner.os }}- - - uses: actions/cache@v5 + - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 with: path: node_modules/.cache/turbo key: turbo-${{ runner.os }}-${{ hashFiles('turbo.json', '**/package.json', 'bun.lock') }}-${{ github.sha }} @@ -182,7 +182,7 @@ jobs: - name: Upload unit artifacts if: always() - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # actions/upload-artifact@v7 with: name: unit-${{ github.run_attempt }} include-hidden-files: true @@ -190,6 +190,11 @@ jobs: retention-days: 7 path: packages/*/.artifacts/unit/junit.xml + # Aggregator for the `dev` branch ruleset. The required check on GitHub + # is `ci / check`; every new job added above MUST be listed in `needs:` + # below, otherwise its failure will not block merge. If this list grows + # past ~5 entries, consider switching to `re-actors/alls-green` for + # dynamic aggregation (tracked in issue #54). check: if: always() needs: diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index ef0aeaee0..be6fcae1e 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,3 +1,9 @@ +# This workflow is a required merge check on `dev`. The exact ruleset +# entry to register is `codeql / analyze-js-ts`. PawWork runs user- +# authored skills and accesses the OS keychain; solo-dev code review is +# not a substitute for a JS/TS security scanner. Keep it in its own file +# (not folded into ci.yml) so security failures are legible in isolation. +# Decision recorded in issue #54 (D1). name: codeql on: diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 000000000..9c2bc9e9a --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,41 @@ +name: dependency-review + +on: + pull_request: + branches: [dev] + +concurrency: + group: dependency-review-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + pull-requests: write + +jobs: + review: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # actions/checkout@v6 + with: + persist-credentials: false + + # Non-blocking during the initial rollout: `warn-only` makes policy + # findings (CVEs, license violations) surface as a comment without + # failing the job, while still failing loudly on action-level + # misconfiguration (auth, parser, network). GitHub supports bun.lock + # directly, but the `catalog:` field and workspace resolution are the + # real unknowns on this repo. Once a PR run shows the dep graph is + # parsed correctly, drop warn-only, set fail-on-severity: high, and + # add this job to the dev ruleset. Tracked in #54. + # + # Skipped on fork PRs: GITHUB_TOKEN is read-only, so the + # comment-summary-in-pr write would fail or silently no-op under + # warn-only. Mirrors the fork guard on the junit publisher in ci.yml. + - if: > + github.event.pull_request.head.repo.full_name == github.repository + uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # actions/dependency-review-action@v4.9.0 + with: + comment-summary-in-pr: always + warn-only: true diff --git a/.github/workflows/desktop-smoke.yml b/.github/workflows/desktop-smoke.yml index 90c5327f6..4485143f4 100644 --- a/.github/workflows/desktop-smoke.yml +++ b/.github/workflows/desktop-smoke.yml @@ -20,7 +20,7 @@ jobs: outputs: docs_only: ${{ steps.filter.outputs.docs_only }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # actions/checkout@v6 with: fetch-depth: 0 persist-credentials: false @@ -92,11 +92,11 @@ jobs: runs-on: macos-14 timeout-minutes: 30 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # actions/checkout@v6 with: persist-credentials: false - - uses: actions/setup-node@v6.3.0 + - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # actions/setup-node@v6.3.0 with: node-version: "24" @@ -104,14 +104,14 @@ jobs: with: bun-version: "1.3.11" - - uses: actions/cache@v5 + - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 with: path: ~/.bun/install/cache key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }} restore-keys: | bun-${{ runner.os }}- - - uses: actions/cache@v5 + - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 with: path: | ~/Library/Caches/electron @@ -185,6 +185,9 @@ jobs: grep -q "Signature=adhoc" /tmp/pawwork-codesign.txt working-directory: packages/desktop-electron + # Aggregator for the `dev` branch ruleset. The required check on GitHub + # is `desktop-smoke / check`; every new job added above MUST be listed + # in `needs:` below, otherwise its failure will not block merge. check: if: always() needs: diff --git a/.github/workflows/e2e-artifacts.yml b/.github/workflows/e2e-artifacts.yml index a8a01ab4f..d86131a90 100644 --- a/.github/workflows/e2e-artifacts.yml +++ b/.github/workflows/e2e-artifacts.yml @@ -1,3 +1,9 @@ +# This workflow is intentionally non-blocking: it uploads Playwright +# artifacts on PRs for manual inspection but never fails the PR. Do NOT +# add `e2e-artifacts / e2e-artifacts` to the `dev` ruleset as a required +# check -- `continue-on-error: true` on the job means the check reports +# success even when tests fail, which would make the gate lie about +# every PR forever. Decision recorded in issue #54. name: e2e-artifacts on: @@ -30,11 +36,11 @@ jobs: timeout-minutes: 30 continue-on-error: true steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # actions/checkout@v6 with: persist-credentials: false - - uses: actions/setup-node@v6.3.0 + - uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # actions/setup-node@v6.3.0 with: node-version: "24" @@ -42,14 +48,14 @@ jobs: with: bun-version: "1.3.11" - - uses: actions/cache@v5 + - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 with: path: ~/.bun/install/cache key: bun-${{ runner.os }}-${{ hashFiles('bun.lock') }} restore-keys: | bun-${{ runner.os }}- - - uses: actions/cache@v5 + - uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # actions/cache@v5 with: path: ${{ github.workspace }}/.playwright-browsers key: playwright-${{ runner.os }}-${{ hashFiles('packages/app/package.json', 'bun.lock') }} @@ -81,7 +87,7 @@ jobs: - name: Upload e2e artifacts if: always() - uses: actions/upload-artifact@v7 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # actions/upload-artifact@v7 with: name: e2e-artifacts-linux-${{ github.run_attempt }} if-no-files-found: ignore diff --git a/bun.lock b/bun.lock index cca971c02..04f0a253d 100644 --- a/bun.lock +++ b/bun.lock @@ -18,7 +18,6 @@ "@types/mime-types": "3.0.1", "@typescript/native-preview": "catalog:", "glob": "13.0.5", - "husky": "9.1.7", "prettier": "3.6.2", "semver": "^7.6.0", "sst": "3.18.10", @@ -2332,8 +2331,6 @@ "human-signals": ["human-signals@5.0.0", "", {}, "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ=="], - "husky": ["husky@9.1.7", "", { "bin": { "husky": "bin.js" } }, "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="], - "iconv-corefoundation": ["iconv-corefoundation@1.1.7", "", { "dependencies": { "cli-truncate": "^2.1.0", "node-addon-api": "^1.6.3" }, "os": "darwin" }, "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ=="], "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], diff --git a/package.json b/package.json index a4b34b66e..bf8ef540e 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,6 @@ "dev:storybook": "bun --cwd packages/storybook storybook", "typecheck": "bun turbo typecheck", "postinstall": "bun run --cwd packages/opencode fix-node-pty", - "prepare": "husky", "random": "echo 'Random script'", "hello": "echo 'Hello World!'", "test": "echo 'do not run tests from root' && exit 1" @@ -81,7 +80,6 @@ "@types/mime-types": "3.0.1", "@typescript/native-preview": "catalog:", "glob": "13.0.5", - "husky": "9.1.7", "prettier": "3.6.2", "semver": "^7.6.0", "sst": "3.18.10", diff --git a/packages/opencode/test/config/e2e-artifacts-workflow.test.ts b/packages/opencode/test/config/e2e-artifacts-workflow.test.ts index 5e115f8c0..5d0b3cd39 100644 --- a/packages/opencode/test/config/e2e-artifacts-workflow.test.ts +++ b/packages/opencode/test/config/e2e-artifacts-workflow.test.ts @@ -37,14 +37,14 @@ describe("e2e artifacts workflow", () => { expect(parsed.permissions).toEqual({ contents: "read" }) expect(job?.["runs-on"]).toBe("ubuntu-latest") expect(job?.["continue-on-error"]).toBe(true) - expect(checkoutStep?.uses).toBe("actions/checkout@v6") + expect(checkoutStep?.uses).toBe("actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd") expect(checkoutStep?.with).toEqual({ "persist-credentials": false }) expect(bunStep?.uses).toBe("oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6") expect(installBrowsersStep?.run).toBe("bunx playwright install --with-deps chromium") expect(runStep?.run).toContain("bun --cwd packages/app test:e2e:local:smoke") expect(warnStep?.if).toBe("failure()") expect(warnStep?.run).toContain("::warning::") - expect(uploadStep?.uses).toBe("actions/upload-artifact@v7") + expect(uploadStep?.uses).toBe("actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a") expect(uploadStep?.with?.name).toBe("e2e-artifacts-linux-${{ github.run_attempt }}") expect(uploadStep?.with?.["if-no-files-found"]).toBe("ignore") expect(uploadStep?.with?.["retention-days"]).toBe(7) diff --git a/packages/opencode/test/github/ci-workflow.test.ts b/packages/opencode/test/github/ci-workflow.test.ts index 7d1bccdd3..ae3f237cb 100644 --- a/packages/opencode/test/github/ci-workflow.test.ts +++ b/packages/opencode/test/github/ci-workflow.test.ts @@ -23,9 +23,9 @@ describe("ci workflow", () => { expect(parsed.name).toBe("ci") expect(parsed.permissions).toEqual({ contents: "read" }) - expect(changesCheckoutStep?.uses).toBe("actions/checkout@v6") - expect(typecheckCheckoutStep?.uses).toBe("actions/checkout@v6") - expect(unitCheckoutStep?.uses).toBe("actions/checkout@v6") + expect(changesCheckoutStep?.uses).toBe("actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd") + expect(typecheckCheckoutStep?.uses).toBe("actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd") + expect(unitCheckoutStep?.uses).toBe("actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd") expect(changesCheckoutStep?.with).toEqual({ "fetch-depth": 0, @@ -38,7 +38,7 @@ describe("ci workflow", () => { expect(unitCheckoutStep?.with).toEqual({ "persist-credentials": false }) expect(unitBunStep?.uses).toBe("oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6") expect(junitStep?.uses).toBe("mikepenz/action-junit-report@bccf2e31636835cf0874589931c4116687171386") - expect(uploadArtifactsStep?.uses).toBe("actions/upload-artifact@v7") + expect(uploadArtifactsStep?.uses).toBe("actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a") expect(workflow).not.toContain("pull_request_target") expect(workflow).not.toContain("persist-credentials: true") diff --git a/packages/opencode/test/github/desktop-smoke-workflow.test.ts b/packages/opencode/test/github/desktop-smoke-workflow.test.ts index 4f9302d5d..0ee217feb 100644 --- a/packages/opencode/test/github/desktop-smoke-workflow.test.ts +++ b/packages/opencode/test/github/desktop-smoke-workflow.test.ts @@ -28,8 +28,8 @@ describe("desktop smoke workflow", () => { expect(parsed.on?.workflow_dispatch).toEqual(null) expect(parsed.permissions).toEqual({ contents: "read" }) expect(Object.keys(jobs).sort()).toEqual(["changes", "check", "smoke-macos-arm64"]) - expect(changesCheckoutStep?.uses).toBe("actions/checkout@v6") - expect(smokeCheckoutStep?.uses).toBe("actions/checkout@v6") + expect(changesCheckoutStep?.uses).toBe("actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd") + expect(smokeCheckoutStep?.uses).toBe("actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd") expect(changes?.outputs).toEqual({ docs_only: "${{ steps.filter.outputs.docs_only }}" }) expect(changesCheckoutStep?.with).toEqual({