Skip to content
Merged
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
50 changes: 50 additions & 0 deletions planning/releases/0.5.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# semvertag 0.5.0 — `tag --dry-run`

**Minor release shipping `semvertag tag --dry-run` and a new docs home.** `--dry-run` computes the bump and prints the planned tag without pushing — useful for previewing the next release locally and for CI smoke tests that need to exercise the composite action side-effect-free. The flag is purely additive; default behavior is unchanged.

If you don't use the CLI directly and rely on the `modern-python/semvertag@v0` action, this release is wire-compatible — the action's `dry-run` input lands in a follow-up (0.6.0).

## What landed

- **`semvertag tag --dry-run`** — new CLI flag. When set, the use case computes the bump and emits `status="dry_run"` with `bump` / `tag` populated, but never calls `provider.create_tag`. The three other early-return statuses (`no_tags`, `already_tagged`, the strategy's `no_*_commit`) are untouched — they don't push anything, so dry-run has no effect on those paths. ([PR #15](https://github.com/modern-python/semvertag/pull/15))
- **`status` field gains the `dry_run` value.** `RunResult.status` is already plain `str`, so no type widening — just a new well-known string alongside the existing `created`, `no_tags`, `already_tagged`, `no_*_commit` enum.
- **Human-readable output renders dry-run distinctly:** `Dry run: would create tag X on commit Y (strategy: ..., bump: ...)` mirrors the `created` format with the verb phrase swapped.
- **Docs site moves to https://semvertag.modern-python.org.** mkdocs now auto-deploys to GitHub Pages on every push to `main` that touches `docs/`, `mkdocs.yml`, or the workflow itself. The previous `semvertag.readthedocs.io` URL is no longer maintained. ([PR #14](https://github.com/modern-python/semvertag/pull/14))
- **CI action pins bumped to current majors:** `actions/checkout@v6`, `extractions/setup-just@v4`, `astral-sh/setup-uv@v8.2.0`. Mechanical change, no behavior difference for consumers. ([PR #14](https://github.com/modern-python/semvertag/pull/14))

## Why dry-run

Two motivations:

1. **Preview the next bump locally.** Run `uvx 'semvertag>=0.5.0' tag --dry-run --json` against any repo to see what tag the next release would be, without committing to it.
2. **Make CI smoke tests structurally safe.** This repo's `action-smoke` job (in `ci.yml`) has historically run the composite action against the real `main` with `permissions: contents: write`, and when main's HEAD was an untagged `feat/`/`bugfix/` merge, the smoke test pushed a real release tag from a PR's CI run. PR #14 surfaced this concretely (the smoke test minted `0.4.1` and `0.5.0` on different commits from PR runs). `--dry-run` is the CLI half of the fix; the composite action wiring lands in 0.6.0.

## Usage

```sh
# Preview only — does not push:
uvx 'semvertag>=0.5.0' tag --dry-run

# JSON envelope for programmatic consumers:
uvx 'semvertag>=0.5.0' tag --dry-run --json
# {"schema_version":"1.0","strategy":"branch-prefix","bump":"minor",
# "status":"dry_run","tag":"0.6.0","commit":"abc1234...","reason":null}
```

When the latest commit already has a tag, `--dry-run` doesn't change anything — the run reports `status=already_tagged` exactly as a normal run would.

## Breaking changes

None. `--dry-run` is opt-in; the kwarg defaults to `False`; existing call sites and consumers see no change.

## CLI version floor (action.yml)

The `modern-python/semvertag@v0` action still pins `'semvertag>=0.3.1,<1'` after this release. The floor will bump to `>=0.5.0,<1` when the action exposes a `dry-run` input in 0.6.0.

## See also

- Spec: `planning/specs/2026-06-09-dry-run-flag-design.md`
- Implementation plan: `planning/plans/2026-06-09-dry-run-flag.md`
- Dry-run feature: [PR #15](https://github.com/modern-python/semvertag/pull/15)
- Docs site auto-deploy: [PR #14](https://github.com/modern-python/semvertag/pull/14)
- Composite action follow-up (the second half of the smell fix): 0.6.0 — see `planning/releases/0.6.0.md`
62 changes: 62 additions & 0 deletions planning/releases/0.6.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# semvertag 0.6.0 — `dry-run` on the composite action

**Minor release exposing `--dry-run` through `modern-python/semvertag@v0` and removing the action-smoke side-effect smell.** Pure action + CI change — the CLI is unchanged from 0.5.0 (no Python source touched). Consumers who don't need the new input can keep using `@v0` exactly as before; the input is opt-in.

If you don't use the composite action and only consume `semvertag` directly via `uvx`, you can skip this release — there's no CLI change.

## What landed

- **`dry-run` input on `action.yml`** (default `'false'`). When `true`, the composite passes `--dry-run` to `semvertag tag`; semvertag short-circuits before `provider.create_tag` and the existing case block normalizes `status="dry_run"` to the public `status="no-bump"`. The action's `bump` / `tag` outputs reflect what *would* have happened. ([PR #16](https://github.com/modern-python/semvertag/pull/16))
- **`action-smoke` (this repo's CI) becomes structurally side-effect-free.** Two layers of protection:
- Layer 1: `with: { dry-run: 'true' }` on `uses: ./` — semvertag never attempts a push.
- Layer 2: `permissions: contents: write` removed — even a future regression that bypassed dry-run would 403 on the API.
- **Assertion reduced** from two lines (`status == no-bump` AND `bump == none`) to one (`status == no-bump`). Under dry-run, `bump` is unstable (reflects the would-be value) — the old check would fail on any PR where main HEAD was an untagged `feat/`/`bugfix/` merge. Status normalization is the real wiring contract; the single check catches a regression loudly (a unwired dry-run would surface as `created`).
- **CLI version floor in `action.yml` bumps** from `>=0.3.1,<1` to `>=0.5.0,<1` (the release that ships `--dry-run`).
- **`docs/providers/github.md` gets a `## Preview the next bump` section** with both the action-input example and the equivalent local `uvx` invocation.

## Usage

```yaml
- id: semvertag
uses: modern-python/semvertag@v0
with:
dry-run: true
- if: steps.semvertag.outputs.bump != 'none'
run: echo "Next release would be ${{ steps.semvertag.outputs.tag }} (${{ steps.semvertag.outputs.bump }} bump)"
```

Under `dry-run: true`, the action's `status` output is `no-bump` (no real tag was pushed). The `bump` and `tag` outputs reflect what *would* have happened, so a downstream conditional like the one above can branch on the planned bump.

## Why

PR #14 (in 0.5.0) surfaced the smell: `action-smoke` ran semvertag against the real `main` with `contents: write`, and when main HEAD wasn't already tagged, the smoke test pushed a real release tag from a PR's CI run. 0.5.0 shipped the CLI half (`--dry-run`); 0.6.0 wires it through the composite and switches the smoke job to use it. After this release:

- `action-smoke` is provably side-effect-free regardless of main HEAD's tag state.
- The brittle "main HEAD is always already-tagged" assumption is gone — the test asserts the dry-run wiring, not an external invariant.
- Any consumer who wants to preview the next bump from a workflow can opt in via the same input.

## Breaking changes

None. `dry-run` defaults to `'false'`; existing `uses: modern-python/semvertag@v0` invocations behave identically to 0.5.x.

The version floor bump (`>=0.3.1,<1` → `>=0.5.0,<1`) is internal to the action — consumers who pin `@v0` don't see it directly. If anyone was somehow consuming `action.yml` outside the `@v0` pin and pinning an old `semvertag` themselves, they'd need to upgrade to 0.5.0+. No known consumer pattern matches this.

## Release procedure

`tag-major.yml` automatically updates the floating `v0` tag on release-published, so the steps from 0.4.0's manual bootstrap are no longer needed.

```sh
gh release create 0.6.0 \
--title '0.6.0 — dry-run on the composite action' \
--notes-file planning/releases/0.6.0.md
```

`publish.yml` fires on release creation and pushes 0.6.0 to PyPI. `tag-major.yml` fires in parallel and force-updates `v0` to point at the 0.6.0 commit.

## See also

- Spec: `planning/specs/2026-06-09-action-yml-dry-run-design.md`
- Implementation plan: `planning/plans/2026-06-09-action-yml-dry-run.md`
- PR: [#16](https://github.com/modern-python/semvertag/pull/16)
- Predecessor (CLI half): [PR #15](https://github.com/modern-python/semvertag/pull/15) — semvertag 0.5.0
- Original smell surfacing: [PR #14](https://github.com/modern-python/semvertag/pull/14) — mkdocs deploy + the action-smoke side-effect discovery