Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .bumpy/ci-mode-flag.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@varlock/bumpy': minor
---

Add `--mode` flag to `bumpy ci release` for asserting the detected release mode (`version-pr` or `publish`). Enables split-job release workflows where each job fails loudly if the runtime state doesn't match what the job expects. Refactored `ReleaseOptions` to rename the existing `mode` field to `autoPublish: boolean` and add `assertMode`. `--mode` and `--auto-publish` cannot be combined.
81 changes: 64 additions & 17 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,20 @@ concurrency:
cancel-in-progress: false

jobs:
release:
# Detect what `ci release` would do and gate downstream jobs accordingly.
# Runs with no write permissions and no publish credentials.
plan:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
id-token: write # required for npm trusted publishing (OIDC)
contents: read
outputs:
mode: ${{ steps.plan.outputs.mode }}
packages: ${{ steps.plan.outputs.packages }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: oven-sh/setup-bun@v2
# Node.js (npm) is needed for npm publish
- uses: actions/setup-node@v6
with:
node-version: latest
- run: bun install

# --- You wont need this part ---
Expand All @@ -32,21 +31,69 @@ jobs:
- run: bun install
# -------------------------------

# 🐸 Plan first — detects mode and caches the result for ci release
# Outputs: mode (version-pr|publish|nothing), packages (comma-separated), json (full plan)
# 🐸 Outputs: mode (version-pr|publish|nothing), packages (comma-separated), json (full plan)
- id: plan
run: bunx @varlock/bumpy ci plan
env:
GH_TOKEN: ${{ github.token }}

# Example: conditionally run expensive steps only when publishing
# In your project, this is where you'd put build/compile/test steps
# that are only needed before a publish (not when updating the version PR)
- if: steps.plan.outputs.mode == 'publish'
run: echo "📦 Publish mode — packages to release:" && echo "${{ steps.plan.outputs.packages }}"
# Creates/updates the Version Packages PR. No publish credentials — never sees
# id-token or npm secrets, so a malicious commit to main can't ride this job to 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@v6
with:
fetch-depth: 0
- uses: oven-sh/setup-bun@v2
- run: bun install

# Creates/updates release PR when PRs merge to main, publishes packages when release PR is merged
- run: bunx @varlock/bumpy ci release
# --- You wont need this part ---
- run: bun run --filter @varlock/bumpy build
- run: bun install
# -------------------------------

- run: bunx @varlock/bumpy ci release --mode version-pr
env:
GH_TOKEN: ${{ github.token }}
BUMPY_GH_TOKEN: ${{ secrets.BUMPY_GH_TOKEN }} # <- PAT so that version PR triggers CI

# Publishes packages. Scoped to the `publish` environment — pin the npm trusted
# publisher to this environment name on npmjs.com so that an OIDC token requested
# from any other job (or a rogue workflow file) will be rejected by npm.
publish:
needs: plan
if: needs.plan.outputs.mode == 'publish'
runs-on: ubuntu-latest
environment: publish
permissions:
contents: write
id-token: write # required for npm trusted publishing (OIDC)
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: oven-sh/setup-bun@v2
# Node.js (npm) is needed for npm publish
- uses: actions/setup-node@v6
with:
node-version: latest
- run: bun install

# --- You wont need this part ---
- run: bun run --filter @varlock/bumpy build
- run: bun install
# -------------------------------

- run: echo "📦 Publishing packages:" && echo "${{ needs.plan.outputs.packages }}"

- run: bunx @varlock/bumpy ci release --mode publish
env:
GH_TOKEN: ${{ github.token }}
# We dont use the default GH token so that further workflows can be triggred by GH release events
BUMPY_GH_TOKEN: ${{ secrets.BUMPY_GH_TOKEN }}
13 changes: 7 additions & 6 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,19 +223,20 @@ CI command for releases. Has two modes:

**Version PR mode (default):** If pending bump files exist, creates or updates a "Version Packages" PR with all version bumps and changelog updates. If the current push is the Version Packages PR being merged, publishes the new versions, creates git tags, and creates GitHub releases.

**Auto-publish mode (`--auto-publish`):** Versions and publishes directly on merge without an intermediate PR.
**Auto-publish mode (`--auto-publish`):** Versions and publishes directly on merge without an intermediate PR. **Not recommended** — you lose the review/preview step on version bumps, and the job needs both PR-writing and publish credentials at once, which defeats the security split between version-PR and publish jobs.

```bash
bumpy ci release
bumpy ci release --auto-publish
bumpy ci release --auto-publish --tag beta
```

| Flag | Description |
| ----------------- | ---------------------------------------------------------- |
| `--auto-publish` | Version + publish directly instead of creating a PR |
| `--tag <tag>` | npm dist-tag (for `--auto-publish`) |
| `--branch <name>` | Version PR branch name (default: `bumpy/version-packages`) |
| Flag | Description |
| ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--mode <mode>` | Assert detected mode: `version-pr` or `publish`. Errors if the detected mode differs. Use to gate split-job workflows so a job can't silently fall into the wrong path. |
| `--auto-publish` | Version + publish directly instead of creating a PR |
| `--tag <tag>` | npm dist-tag (for `--auto-publish`) |
| `--branch <name>` | Version PR branch name (default: `bumpy/version-packages`) |

Requires `GH_TOKEN`. When `BUMPY_GH_TOKEN` is set, it is automatically used to push the version branch and create/edit the PR so that PR workflows trigger (see [GitHub Actions setup](github-actions.md#token-setup)).

Expand Down
Loading