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
9 changes: 9 additions & 0 deletions .bumpy/snapshot-releases.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@varlock/bumpy': minor
---

Add snapshot releases — transient, one-off preview publishes for private packages (the private-registry counterpart to pkg.pr.new).

`bumpy publish --snapshot <name>` computes the pending release plan, derives a unique prerelease version per package (e.g. `1.4.0-pr-123-a1b2c3d`), exact-pins in-plan internal deps, publishes to a non-`latest` dist-tag (default: the snapshot name), then restores the working tree. It never consumes bump files, writes changelogs, commits, creates git tags, or makes GitHub releases. `bumpy ci release --snapshot <name>` runs the whole thing and, on a PR, posts/updates a comment with the published versions and install instructions. Requires pending bump files; mutually exclusive with `--channel`.

Version uniqueness is configurable via the new `snapshot.versionStrategy` option: `"sha"` (default — `<target>-<name>-<short-sha>`, idempotent per commit so re-runs skip) or `"timestamp"`. Consumers install via the dist-tag regardless, so the exact version string is just an implementation detail.
24 changes: 16 additions & 8 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,18 +91,22 @@ bumpy publish
bumpy publish --dry-run
bumpy publish --tag beta
bumpy publish --filter "@myorg/*"
bumpy publish --snapshot pr-123
```

| Flag | Description |
| ------------------ | ------------------------------------------------------------ |
| `--dry-run` | Preview what would be published without actually doing it |
| `--tag <tag>` | npm dist-tag (e.g., `next`, `beta`) |
| `--no-push` | Skip pushing git tags to the remote |
| `--filter <names>` | Only publish matching packages (supports globs) |
| `--channel <name>` | Channel override (default: inferred from the current branch) |
| Flag | Description |
| ------------------- | ---------------------------------------------------------------------------------------------------- |
| `--dry-run` | Preview what would be published without actually doing it |
| `--tag <tag>` | npm dist-tag (e.g., `next`, `beta`) |
| `--no-push` | Skip pushing git tags to the remote |
| `--filter <names>` | Only publish matching packages (supports globs) |
| `--channel <name>` | Channel override (default: inferred from the current branch) |
| `--snapshot <name>` | Publish a transient [snapshot](snapshots.md#snapshot-releases) (mutually exclusive with `--channel`) |

On a [prerelease channel](prereleases.md) branch, publish derives prerelease versions (targets from the cycle's bump files, counters from the registry), transiently writes them into the working tree so pack/build see them, publishes the whole cycle to the channel's dist-tag with exact-pinned inter-cycle deps, then restores the files. Nothing version-shaped is ever committed.

With `--snapshot <name>`, publish derives a throwaway prerelease version per pending package (e.g. `1.4.0-pr-123-a1b2c3d`), publishes them to a non-`latest` dist-tag (default: the snapshot name), then restores the working tree. It never consumes bump files, writes changelogs, commits, tags, or creates GitHub releases — it's the private-registry counterpart to [pkg.pr.new](https://pkg.pr.new). Requires pending bump files. See [Snapshot releases](snapshots.md#snapshot-releases).

**How bumpy detects unpublished packages:**

1. Custom `checkPublished` command (if configured per-package — see [`allowCustomCommands`](./configuration.md#custom-commands-and-allowcustomcommands))
Expand Down Expand Up @@ -239,18 +243,22 @@ CI command for releases. Has two modes:

**Auto-publish mode (`--auto-publish`):** Versions and publishes directly on merge without an intermediate PR. **Not recommended** — you lose the version-PR preview/review gate, so every merge to main with a bump file ships immediately. It's also incompatible with the [split-job workflow](github-actions.md#release-workflow-recommended-split-jobs) (since both paths happen in one run). The credential surface itself is the same as a single-job non-auto-publish workflow — the cost here is purely the loss of the preview gate.

**Snapshot mode (`--snapshot <name>`):** Publishes a transient [snapshot](snapshots.md#snapshot-releases) and, on a PR, posts/updates a comment with the published versions and install instructions. A single self-contained step — no version-PR/publish split, no branch routing — so it can run from any branch (typically a feature PR). Incompatible with `--expect-mode` and `--auto-publish`.

```bash
bumpy ci release
bumpy ci release --auto-publish
bumpy ci release --auto-publish --tag beta
bumpy ci release --snapshot pr-123
```

| Flag | Description |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--expect-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`) |
| `--tag <tag>` | npm dist-tag (for `--auto-publish`, or the snapshot dist-tag) |
| `--branch <name>` | Version PR branch name (default: `bumpy/version-packages`) |
| `--snapshot <name>` | Publish a transient [snapshot](snapshots.md#snapshot-releases) and comment install instructions on the PR |

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
65 changes: 44 additions & 21 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,35 @@ Bumpy is configured via `.bumpy/_config.json`, created by `bumpy init`. Per-pack

## Global config (`.bumpy/_config.json`)

| Option | Type | Default | Description |
| ---------------------------- | -------------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------ |
| `baseBranch` | `string` | `"main"` | Branch used for release comparisons |
| `access` | `"public" \| "restricted"` | `"public"` | Default npm publish access level |
| `changelog` | `false \| string \| [string, options]` | `"default"` | Changelog formatter — `"default"`, `"github"`, path to a custom formatter, or `false` to disable |
| `fixed` | `string[][]` | `[]` | Package groups that always bump together to the same version |
| `linked` | `string[][]` | `[]` | Package groups that share the highest bump level |
| `ignore` | `string[]` | `[]` | Package name globs to exclude from versioning |
| `include` | `string[]` | `[]` | Package name globs to explicitly include (overrides `ignore` and `privatePackages`) |
| `privatePackages` | `{ version, tag }` | `{ version: false, tag: false }` | Whether to version and/or create git tags for private packages |
| `updateInternalDependencies` | `"patch" \| "minor" \| "out-of-range"` | `"out-of-range"` | When to update internal dependency version ranges |
| `dependencyBumpRules` | `object` | see below | Controls how bumps propagate through dependency types |
| `versionCommitMessage` | `string` | — | Customize the version commit message (see below) |
| `changedFilePatterns` | `string[]` | `["**"]` | Glob patterns to filter which changed files count toward marking a package as changed |
| `ignoredPackageJsonFields` | `string[]` | `["devDependencies"]` | `package.json` fields whose change alone doesn't require a bump file (see below) |
| `publish` | `object` | see below | Publishing pipeline config |
| `gitUser` | `{ name, email }` | bumpy-bot | Git identity for CI commits |
| `versionPr` | `{ title, branch, preamble }` | see below | Customize the version PR |
| `allowCustomCommands` | `boolean \| string[]` | `false` | Allow per-package custom commands from `package.json` (see below) |
| `packages` | `object` | `{}` | Per-package config overrides (keyed by package name) |
| `channels` | `object` | `{}` | Prerelease channels, keyed by channel name (see below) |
| Option | Type | Default | Description |
| ---------------------------- | -------------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------ |
| `baseBranch` | `string` | `"main"` | Branch used for release comparisons |
| `access` | `"public" \| "restricted"` | `"public"` | Default npm publish access level |
| `changelog` | `false \| string \| [string, options]` | `"default"` | Changelog formatter — `"default"`, `"github"`, path to a custom formatter, or `false` to disable |
| `fixed` | `string[][]` | `[]` | Package groups that always bump together to the same version |
| `linked` | `string[][]` | `[]` | Package groups that share the highest bump level |
| `ignore` | `string[]` | `[]` | Package name globs to exclude from versioning |
| `include` | `string[]` | `[]` | Package name globs to explicitly include (overrides `ignore` and `privatePackages`) |
| `privatePackages` | `{ version, tag }` | `{ version: false, tag: false }` | Whether to version and/or create git tags for `"private": true` packages (never published — see below) |
| `updateInternalDependencies` | `"patch" \| "minor" \| "out-of-range"` | `"out-of-range"` | When to update internal dependency version ranges |
| `dependencyBumpRules` | `object` | see below | Controls how bumps propagate through dependency types |
| `versionCommitMessage` | `string` | — | Customize the version commit message (see below) |
| `changedFilePatterns` | `string[]` | `["**"]` | Glob patterns to filter which changed files count toward marking a package as changed |
| `ignoredPackageJsonFields` | `string[]` | `["devDependencies"]` | `package.json` fields whose change alone doesn't require a bump file (see below) |
| `publish` | `object` | see below | Publishing pipeline config |
| `gitUser` | `{ name, email }` | bumpy-bot | Git identity for CI commits |
| `versionPr` | `{ title, branch, preamble }` | see below | Customize the version PR |
| `allowCustomCommands` | `boolean \| string[]` | `false` | Allow per-package custom commands from `package.json` (see below) |
| `packages` | `object` | `{}` | Per-package config overrides (keyed by package name) |
| `channels` | `object` | `{}` | Prerelease channels, keyed by channel name (see below) |
| `snapshot` | `{ versionStrategy }` | `{ versionStrategy: "sha" }` | Snapshot release settings — how snapshot versions are made unique (see below) |

### Private packages and private registries

These are two different things, and bumpy treats them differently:

- **Publishing to a private registry** (scoped package + `access: "restricted"` and/or a `registry`, _without_ `"private": true`) works like any other publish — bumpy versions, publishes, tags, and snapshots them normally. This is the recommended setup for private/internal packages. See [Publishing to a private registry](snapshots.md#publishing-to-a-private-registry).
- **`"private": true` in `package.json`** is npm's "never publish" marker (`npm publish` refuses it). bumpy never publishes these. `privatePackages` only controls whether they're _versioned_ (`version`) and _git-tagged_ (`tag`) — not published. Use this for apps and internal tooling you want bumpy to bump but never ship to a registry.

### Change detection and `package.json` fields

Expand Down Expand Up @@ -142,6 +150,21 @@ The `channels` object maps long-lived branches to prerelease lines. See [prerele

Channel names become `.bumpy/<name>/` subdirectories (holding bump files that shipped on the channel), so they must be filesystem-safe and can't start with `_` or collide with reserved entries.

### Snapshot releases

The `snapshot` object configures one-off transient previews published with `bumpy publish --snapshot <name>`. See [snapshots.md → Snapshot releases](snapshots.md#snapshot-releases) for the full workflow.

```jsonc
{
"snapshot": {
// How snapshot versions are made unique (consumers install via the tag regardless):
// "sha" → 1.4.0-pr-123-a1b2c3d (short git SHA; idempotent per commit; default)
// "timestamp" → 1.4.0-pr-123-20260623123456 (always unique)
"versionStrategy": "sha",
},
}
```

## Per-package config

Per-package settings can be defined in two places:
Expand Down
Loading