diff --git a/.github/workflows/bumpy-check.yaml b/.github/workflows/bumpy-check.yaml new file mode 100644 index 00000000..d71cbf71 --- /dev/null +++ b/.github/workflows/bumpy-check.yaml @@ -0,0 +1,55 @@ +name: Bumpy check + +# Posts/updates the release-plan comment on every PR (including forks). +# +# Uses `pull_request_target` so the comment can be posted on fork PRs (a plain +# `pull_request` gives forks a read-only token, so `pull-requests: write` would +# be ineffective). Because that runs with the base repo's elevated token, the PR +# code is treated as untrusted DATA only: +# - the base branch is checked out at the root (trusted bunfig.toml / lockfile +# + the base package.json we read the pinned bumpy version from); +# - the PR head is checked out into ./pr, isolated and never executed; +# - bumpy is run from the root (so the PR's bunfig.toml/.npmrc can't redirect +# package resolution) and only reads the PR files via `--cwd ./pr`. +# ⚠️ DO NOT bun install / npm install / run any script from ./pr ⚠️ +on: pull_request_target + +permissions: + pull-requests: write + contents: read + +concurrency: + group: bumpy-check-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + check: + runs-on: ubuntu-latest + steps: + # Trusted base checkout at the root: provides the bunfig.toml / lockfile + # that govern how `bunx` below resolves and runs bumpy, plus the base + # package.json we read the pinned bumpy version from. + - uses: actions/checkout@v7 + with: + ref: main + persist-credentials: false + + - uses: oven-sh/setup-bun@v2 + + # Untrusted PR head, isolated in ./pr — read as data, never executed. + - uses: actions/checkout@v7 + with: + ref: ${{ github.event.pull_request.head.sha }} + path: pr + persist-credentials: false + + # Resolve bumpy from the base package.json (trusted) and run it from the + # root against the PR files. The version is read straight into the bunx + # invocation (never written to $GITHUB_ENV) so there is no env-injection + # sink even though this is a pull_request_target workflow. + - name: Bumpy release-plan check + run: | + VERSION=$(jq -r '.devDependencies["@varlock/bumpy"] // .dependencies["@varlock/bumpy"]' package.json | sed 's/[\^~]//') + bunx "@varlock/bumpy@$VERSION" ci check --cwd ./pr + env: + GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index e1fb8503..7d927280 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -47,6 +47,7 @@ jobs: runs-on: ubuntu-latest outputs: mode: ${{ steps.plan.outputs.mode }} + bumpy-version: ${{ steps.bumpy-version.outputs.version }} includes-varlock: ${{ contains(fromJSON(steps.plan.outputs.json).packageNames, 'varlock') }} includes-vscode: ${{ contains(fromJSON(steps.plan.outputs.json).packageNames, 'env-spec-language') }} macos-cache-key: ${{ steps.keys.outputs.macos-cache-key }} @@ -57,8 +58,15 @@ jobs: - uses: actions/checkout@v7 - name: Setup Bun uses: oven-sh/setup-bun@v2 + # Pin bumpy to the version committed on main so every release job runs the + # exact version this repo declares (this job does not `bun install`). + - id: bumpy-version + name: Resolve bumpy version + run: | + VERSION=$(jq -r '.devDependencies["@varlock/bumpy"] // .dependencies["@varlock/bumpy"]' package.json | sed 's/[\^~]//') + echo "version=$VERSION" >> "$GITHUB_OUTPUT" - id: plan - run: bunx @varlock/bumpy ci plan + run: bunx "@varlock/bumpy@${{ steps.bumpy-version.outputs.version }}" ci plan env: GH_TOKEN: ${{ github.token }} @@ -127,6 +135,32 @@ jobs: echo "cache-hit=false" >> "$GITHUB_OUTPUT" fi + # Open/update the "version packages" PR. This is the non-publish path: when + # bump files are pending on main, bumpy computes versions + changelogs and + # opens (or refreshes) the release PR. No build/native-binary work is needed — + # publishing happens later, when that PR merges and `plan` reports `publish`. + version-pr: + needs: plan + if: needs.plan.outputs.mode == 'version-pr' + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@v7 + with: + fetch-depth: 0 + # Push the version branch with BUMPY_GH_TOKEN (below) rather than the + # default token, so PR workflows trigger on the version branch. + persist-credentials: false + - name: Setup Bun + uses: oven-sh/setup-bun@v2 + - name: Create / update version PR + run: bunx "@varlock/bumpy@${{ needs.plan.outputs.bumpy-version }}" ci release --expect-mode version-pr + env: + BUMPY_GH_TOKEN: ${{ secrets.BUMPY_GH_TOKEN }} + GH_TOKEN: ${{ github.token }} + # Build + sign the macOS native binary. Skipped on a cache hit — the previously # notarized bundle is reused and re-verified (see verify-native-macos) instead. build-native-macos: @@ -198,10 +232,13 @@ jobs: secrets: OP_CI_TOKEN: ${{ secrets.OP_CI_TOKEN }} + # Publish path: native binaries are ready (built this run or restored from + # cache), so build the npm packages and publish. Gated on `mode == 'publish'`; + # the version-PR path is handled by the `version-pr` job above. release: name: Release needs: [plan, notarize-native-macos, verify-native-macos, build-native-rust] - if: always() && !failure() && !cancelled() + if: always() && !failure() && !cancelled() && needs.plan.outputs.mode == 'publish' runs-on: ubuntu-latest permissions: id-token: write # Required for OIDC @@ -363,8 +400,8 @@ jobs: tenant-id: ${{ env.AZURE_TENANT_ID }} allow-no-subscriptions: true - - name: Create Release PR / Publish - run: bunx @varlock/bumpy ci release + - name: Publish packages + run: bunx "@varlock/bumpy@${{ needs.plan.outputs.bumpy-version }}" ci release --expect-mode publish env: # BUMPY_GH_TOKEN is a bot user PAT used by bumpy to push the version branch and create PRs # so that PR workflows (binary-release, etc.) will be triggered diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 9413c3a9..4467383f 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -10,7 +10,6 @@ concurrency: permissions: contents: read - pull-requests: write jobs: build-and-test: @@ -41,12 +40,9 @@ jobs: - name: Enable turborepo build cache uses: rharkor/caching-for-turbo@56219402aacc0d06b650d898c222996dbc1191ec # v2.3.14 - # ------------------------------------------------------------ - - name: Bumpy release plan check - if: github.event_name == 'pull_request' - run: bunx @varlock/bumpy ci check - env: - GH_TOKEN: ${{ github.token }} + # Note: the bumpy release-plan comment now lives in its own hardened + # workflow (.github/workflows/bumpy-check.yaml) so it can post on fork PRs + # without running PR-defined code with write permissions. # lint, build, tests --------------------------------- - name: ESLint diff --git a/bun.lock b/bun.lock index 621cbbfb..2431ebe3 100644 --- a/bun.lock +++ b/bun.lock @@ -12,7 +12,7 @@ "@types/node": "catalog:", "@typescript-eslint/eslint-plugin": "^8.56.1", "@typescript-eslint/parser": "^8.56.1", - "@varlock/bumpy": "^1.10.2", + "@varlock/bumpy": "^1.17.0", "@varlock/tsconfig": "workspace:*", "eslint": "^10.0.2", "eslint-plugin-es-x": "^9.5.0", @@ -1437,7 +1437,7 @@ "@varlock/bitwarden-plugin": ["@varlock/bitwarden-plugin@workspace:packages/plugins/bitwarden"], - "@varlock/bumpy": ["@varlock/bumpy@1.10.2", "", { "bin": { "bumpy": "dist/cli.mjs" } }, "sha512-y590dPGSvIkrHtk+ILWzBBj8UdtNMkRCXl7qRM3lwOOeC7M3m/hTgQoATP43u/plPui76tTDCvoclwEsAAephQ=="], + "@varlock/bumpy": ["@varlock/bumpy@1.17.0", "", { "bin": { "bumpy": "dist/cli.mjs" } }, "sha512-EunYq1RGBCTWFoaakcNdgHhw+ncJ0y4NtbrELtwhnTHcy1g+wVbOJ1/0I7wg898LUFqo+/kAlZq6wfudA8KRyA=="], "@varlock/ci-env-info": ["@varlock/ci-env-info@workspace:packages/ci-env-info"], diff --git a/bunfig.toml b/bunfig.toml index 28bc58a4..5d32e3c9 100644 --- a/bunfig.toml +++ b/bunfig.toml @@ -2,6 +2,7 @@ env = false [install] minimumReleaseAge = 86400 # 1 day +minimumReleaseAgeExcludes = ["@varlock/bumpy"] [install.security] scanner = "@socketsecurity/bun-security-scanner" \ No newline at end of file diff --git a/package.json b/package.json index cd2e56f7..31a74282 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,7 @@ "@types/node": "catalog:", "@typescript-eslint/eslint-plugin": "^8.56.1", "@typescript-eslint/parser": "^8.56.1", - "@varlock/bumpy": "^1.10.2", + "@varlock/bumpy": "^1.17.0", "@varlock/tsconfig": "workspace:*", "eslint": "^10.0.2", "eslint-plugin-es-x": "^9.5.0",