Skip to content

Release automation with release-please

The CommonGrants monorepo publishes four packages — @common-grants/core, @common-grants/cli, and @common-grants/sdk to npm, and common-grants-sdk to PyPI. ADR 0010 chose Changesets to version and release them independently.

In practice, the Changesets-based workflow developed several recurring problems:

  • Contributors must remember to author a .changeset/*.md file for every release-worthy change; a forgotten file ships the change silently unversioned.
  • The version-bump workflow runs the moment any changeset lands on main, so the team cannot stack several merged changes into one release.
  • The bump commit is pushed directly to main by CI, which requires keeping branch protections disabled.
  • Changesets is a Node-only tool: the Python SDK needs a vestigial package.json and a shell script that greps changeset files to decide the Poetry version bump.
  • Publishing is a separate, manually triggered step that someone must remember to run after each version bump.

This ADR supersedes ADR 0010’s choice of Changesets.

We will adopt release-please in manifest mode for the four published packages. Version bumps and changelog entries are derived from the conventional-commit history on main (the squashed PR titles). Each package gets its own Release PR that accumulates changes until the team merges it; merging tags the release, creates the GitHub release, and triggers publishing automatically.

  • The commit history is the release metadata — there is no separate file for contributors to forget.
  • Multiple merged changes stack in an open Release PR and ship only when it is merged, so the team controls release timing.
  • Version bumps land through a normal PR, so branch protections on main can be re-enabled.
  • The Python SDK uses release-type: python against the real pyproject.toml; the vestigial package.json and shell-parsing step are removed.
  • Merging a Release PR publishes automatically — no separate manual deploy step to remember.
  • Release notes are generated from PR titles, reading like a changelog rather than a list of changeset summaries.
  • Conventional-commit PR titles become mandatory and release-facing; a mistyped title puts a change in the wrong category (or omits it entirely), so a CI title check is required.
  • Contributors must know which commit types map to which version bumps.
  • Commits that do not follow the conventional format are invisible to release-please, so changelog entries for the period before adoption may under-report.
  • A commit’s type applies to every package whose files it touches, so a PR spanning lib/core/ and lib/ts-sdk/ opens a Release PR for both at that type’s level. That is usually correct — both packages changed — but a PR needs splitting when the packages warrant different bumps, such as a feat in one alongside a mechanical regeneration in the other. See PR title format.
  • No release step that depends on contributors remembering a separate artifact.
  • The team controls when releases ship, independent of when changes merge.
  • Branch protections on main stay enabled.
  • First-class support for the Python package.
  • Automated changelogs, tags, and GitHub releases.
  • Publishing triggered by the release step itself.
  • Option 1: Keep the current Changesets workflow.
  • Option 2: Keep Changesets, but adopt the official changesets/action Version-PR mode.
  • Option 3: Adopt release-please in manifest mode.
  • ✅ Criterion met
  • ❌ Criterion not met
  • 🟡 Partially met or unsure
Criteria Option 1 Option 2 Option 3
No forgettable release artifact
Team-controlled release timing
Branch protections stay enabled
First-class Python support
Automated changelogs and releases 🟡 🟡
Publish triggered by release step
No new contributor conventions

Option 1: Keep the current Changesets workflow

Section titled “Option 1: Keep the current Changesets workflow”
  • Summary: The status quo. Contributors author .changeset/*.md files; a CI workflow detects them on main, bumps versions (via pnpm changeset version for Node, a shell script + Poetry for Python), commits directly to main, and tags. Publishing is a separate manually dispatched workflow per package.
  • Pros
    • No migration work; the team already knows the flow.
    • Bump type is declared explicitly per change, not inferred from commit type.
  • Cons
    • A forgotten changeset ships a change unversioned, with no structural backstop.
    • Versions bump as soon as one changeset lands — no batching.
    • CI pushes to main force branch protections off.
    • Python support is bolted on (vestigial package.json, shell parsing).
    • Publishing requires a separate manual step.

Option 2: Changesets with the official Version-PR mode

Section titled “Option 2: Changesets with the official Version-PR mode”
  • Summary: Replace the custom bump workflow with the official changesets/action, which maintains a “Version Packages” PR that accumulates pending changesets. Merging that PR applies the bumps and can trigger publishing.
  • Pros
    • Batching and team-controlled timing, like release-please.
    • Bumps land via a PR, so branch protections can be re-enabled.
    • Contributor-facing workflow (authoring changesets) is unchanged.
  • Cons
    • The forgotten-changeset failure mode is unchanged — it is inherent to the design.
    • Python stays second-class: the vestigial package.json and custom bump script remain.
    • Still a single combined Version PR for all Node packages rather than one per package.

Option 3: Adopt release-please in manifest mode

Section titled “Option 3: Adopt release-please in manifest mode”
  • Summary: A GitHub workflow runs release-please on every push to main. It parses conventional commits, attributes them to packages by the paths they touch, and maintains one Release PR per package (separate-pull-requests). Merging a Release PR bumps the version (package.json or pyproject.toml), updates the changelog, tags with the existing name@version format, creates the GitHub release, and fans out to the per-package publish workflows.
  • Common workflows:
    1. Regular change: Merge a PR titled fix(ts-sdk): ... or feat(core): ...; the affected package’s Release PR is opened or updated automatically.
    2. Stacking changes: Merge several PRs; they accumulate in the open Release PR until the team merges it.
    3. Release: Merge the Release PR; tagging, GitHub release, and npm/PyPI publish happen automatically.
    4. Merge without release-worthy changes: chore:/ci:/test: commits do not open Release PRs (unless marked breaking with !).
  • Pros
    • No forgettable artifact; every squashed commit is parsed.
    • Per-package Release PRs give batching and independent release timing.
    • Native Python release type; the version is edited only in pyproject.toml (release-please mirrors it into common_grants_sdk/__init__.py on each release).
    • Bumps via PR, so branch protections can stay enabled.
    • Existing name@version tag format is preserved exactly.
  • Cons
    • Requires conventional-commit PR titles, enforced by a CI check.
    • Bump types are inferred from commit types, which contributors must learn.
    • Non-conventional commits are invisible to changelogs.