Skip to content

ci(release): add release-please for stable releases (two-lane flow)#78

Merged
pofallon merged 1 commit into
mainfrom
ci/release-please
Jul 25, 2026
Merged

ci(release): add release-please for stable releases (two-lane flow)#78
pofallon merged 1 commit into
mainfrom
ci/release-please

Conversation

@pofallon

Copy link
Copy Markdown
Contributor

Summary

Adds release-please to automate stable releases from Conventional Commits, while leaving the proven tag-driven publish path (release.yml) completely unchanged. First of two PRs; a follow-up adds a release skill that drives the local pre-PyPI validation and the RC lane.

The two lanes (now in CONTRIBUTING.md)

  • Stable → release-please maintains a chore(main): release X.Y.Z PR that bumps pyproject.toml + CHANGELOG.md. Merging it tags vX.Y.Z, which triggers the existing release.yml (PyPI + Docker). No manual bump or tag.
  • Pre-release (RC/beta) → still manual: bump to X.Y.Zrc1, push vX.Y.Z-rcN, release.yml publishes a prerelease. release-please stays out.

Files

  • release-please-config.json + .release-please-manifest.jsonrelease-type: python, package-name: remo-cli, tags vX.Y.Z, seeded at 2.2.0.
  • .github/workflows/release-please.yml — runs on push to main.
  • CHANGELOG.md — seed; release-please maintains it from 2.3.0 onward.
  • CONTRIBUTING.md — documents both lanes + the local "test before PyPI" gate.

⚠️ Reviewer notes — needs a live run + two setup items

CI infra can't be fully exercised locally (actionlint + JSON/YAML parse all pass):

  1. RELEASE_PLEASE_TOKEN secret required. A tag created with the default GITHUB_TOKEN does not trigger on: push: tags, so publish wouldn't fire. Add a PAT (contents + pull-requests write) as repo secret RELEASE_PLEASE_TOKEN. (This is why release.yml is untouched rather than a reusable-workflow refactor — that route has locally-untestable gotchas: docker/metadata-action can't derive semver from a non-tag ref; OIDC environment across workflow_call.)
  2. Action pinning. googleapis/release-please-action@v4 is a floating tag (SHA unresolvable offline); pin to @<sha> # v4 to match repo convention.
  3. uv.lock drift. release-please won't run uv lock; uv.lock's project version can lag pyproject.toml (doesn't affect uv build). Noted in CONTRIBUTING.

Validation: actionlint clean across all workflows; config/manifest JSON valid. First real behavior (release PR, then publish-on-merge) can only be confirmed on main with the PAT set — recommend verifying the RC lane first.

🤖 Generated with Claude Code

…ne flow

Automate stable releases from Conventional Commits while keeping the proven
tag-driven publish path untouched:

- release-please-config.json / .release-please-manifest.json: python release
  type, package remo-cli, tags vX.Y.Z (no component prefix), seeded at 2.2.0.
- .github/workflows/release-please.yml: on push to main, maintain a release PR
  (bump pyproject.toml + CHANGELOG.md); merging it tags vX.Y.Z, which triggers
  the existing release.yml (PyPI + Docker). Requires a RELEASE_PLEASE_TOKEN PAT
  so the created tag fires the tag-driven workflow (GITHUB_TOKEN tags don't).
- CHANGELOG.md: seed; maintained by release-please from 2.3.0 onward.
- CONTRIBUTING.md: document the two lanes (stable = release-please release PR;
  RC/beta = manual vX.Y.Z-rcN tag) plus the local "test before PyPI" gate, and
  list release-please.yml in the workflows table.

release.yml is deliberately unchanged: the RC lane and any manual tag still
publish exactly as before. Chosen over a reusable-workflow refactor because the
latter has locally-untestable gotchas (metadata-action semver from a non-tag
ref; OIDC environment across workflow_call).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HAPB5GA4NVUgKFsVFVJDxN
@pofallon
pofallon merged commit d471330 into main Jul 25, 2026
10 of 11 checks passed
@pofallon
pofallon deleted the ci/release-please branch July 25, 2026 00:23
pofallon added a commit that referenced this pull request Jul 25, 2026
…s, no PyPI) (#79)

* feat(skill): add /release skill to operationalize the two-lane release flow

A user-invocable skill that drives remo's release process with a mandatory
local "test before PyPI" gate and approval-before-publish guardrails:

- stable lane: locate the release-please "chore(main): release X.Y.Z" PR,
  review the bump + CHANGELOG, run the validation gate on the PR head, then
  merge only on explicit approval (release-please tags -> release.yml publishes).
- rc lane: compute X.Y.ZrcN, bump pyproject, run the validation gate, then
  either hand off a local wheel (no PyPI) or, only on explicit approval, tag
  vX.Y.Z-rcN and publish a prerelease.
- Step V validation gate: full pytest + uv build + throwaway-venv smoke that the
  wheel's --version matches, before any tag/merge/publish.

Guardrails: never push a tag/commit or merge a release PR without explicit
in-turn approval; require a clean, up-to-date main; prefer local-only testing.

Pairs with the release-please integration (#78).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HAPB5GA4NVUgKFsVFVJDxN

* ci(dev-build): manual-dispatch workflow to publish RC/dev wheels as run artifacts

Build an installable wheel in clean CI and upload it as a run artifact you can
download and test on another machine — no git tag, no PyPI, no reserved version:

- .github/workflows/dev-build.yml: workflow_dispatch with an optional `version`
  input. Stamps pyproject.toml with the base version plus a PEP 440 LOCAL
  segment (+g<shortsha>), which PyPI rejects outright — so a dev build can never
  leak to PyPI and every build is uniquely identifiable. Uploads dist/* as the
  `remo-wheel` artifact (30-day retention). release.yml is untouched.
    gh workflow run dev-build.yml [-f version=2.3.0rc1]
    gh run download <run-id> -n remo-wheel && uv tool install --force ./remo_cli-*.whl
- /release skill: add a "CI dev build (cross-machine, no PyPI)" outcome to the
  RC lane alongside local-only and publish-to-PyPI.

Version stamping validated locally (packaging.Version): blank -> X.Y.Z.dev<run>,
label -> X.Y.ZrcN, each with a +g<sha> local segment. actionlint clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HAPB5GA4NVUgKFsVFVJDxN

---------

Co-authored-by: Paul O'Fallon <505519+pofallon@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant