Skip to content

[FEATURE] Branching strategy Phase 2.B - release trigger and versioning docs#557

Draft
John McCall (lowlydba) wants to merge 11 commits into
mainfrom
533-devops-branching-strategy---phase-2b---release-versions
Draft

[FEATURE] Branching strategy Phase 2.B - release trigger and versioning docs#557
John McCall (lowlydba) wants to merge 11 commits into
mainfrom
533-devops-branching-strategy---phase-2b---release-versions

Conversation

@lowlydba

@lowlydba John McCall (lowlydba) commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Phase 2.B of the branching strategy (#533): per-package release detection on main, plus the versioning and contributor docs. Publishing to PyPI is Phase 3 (#509).

Full design and rationale: docs/versioning.md and CONTRIBUTING.md.

What this adds

  • Per-package releases.
    • Each package under packages/* versions independently and gets its own release series, tagged <package>-v<major>.<minor>.0.
    • Since consumers pin only overture-schema, this stays invisible downstream in our current usage patterns.
  • release-trigger workflow.
    • On merge to main, cuts a published GitHub Release for each package whose major.minor bumped.
    • Patch-only merges go to internal CodeArtifact, not PyPI.
  • Changelog via towncrier 🔔
    • Towncrier is essentially antsibull (the original inspiration for this changelog approach), minus the Ansible parts.
    • TL;DR - you add changelog fragments for each PR (changelog.d/), then before cutting a release run a command that merges all fragments into a changelog, which then feeds release notes.

Decisions

Resolves the TBDs from #533/#509 and this PR's review feedback:

  1. ✅ Per-package releases, not a single umbrella tag.
  2. ✅ Patch merges publish to CodeArtifact only; PyPI is reserved for major.minor bumps.
  3. towncrier fragments over manual notes or auto-generated changelogs.
  4. ⌛ Published release on merge; the human approval gate lives at the Phase 3 PyPI publish (GitHub Environment approval), not a draft release.
  5. ❌ No semantic-release / release-please; they conflict with the human-owns-major/minor model from Phase 2.A.

Notes

Closes #533

Detects an umbrella overture-schema major/minor bump on main and creates
a draft GitHub Release at v<major>.<minor>.0. Publishing the draft is the
only trigger for the public PyPI publish (Phase 3).

Decisions locked: patch builds stay CodeArtifact-only; release notes are
authored manually at release time; only the umbrella package bump cuts a
release.

Closes #533

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Signed-off-by: John McCall <john@overturemaps.org>
@github-actions

github-actions Bot commented Jul 14, 2026

Copy link
Copy Markdown

🗺️ Schema reference docs preview is live!

🌍 Preview https://staging.overturemaps.org/schema/pr/557/schema/index.html
🕐 Updated Jul 21, 2026 21:04 UTC
📝 Commit 69bbe1d
🔧 env SCHEMA_PREVIEW true

Note

♻️ This preview updates automatically with each push to this PR.

…ases

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Signed-off-by: John McCall <john@overturemaps.org>
…ft release tag

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Signed-off-by: John McCall <john@overturemaps.org>
@lowlydba
John McCall (lowlydba) marked this pull request as ready for review July 14, 2026 14:54
Copilot AI review requested due to automatic review settings July 14, 2026 14:54
@lowlydba
John McCall (lowlydba) requested a review from a team as a code owner July 14, 2026 14:54

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Phase 2.B release-event detection on main and documents the versioning/release process so Phase 3 publish workflows can rely on a clear trigger and scheme.

Changes:

  • Adds a release-trigger GitHub Actions workflow that detects umbrella overture-schema <major>.<minor> bumps on main and creates a draft GitHub Release/tag.
  • Adds docs/versioning.md describing version ownership (human vs CI), trigger→destination behavior, and the release process.
  • Updates CONTRIBUTING.md with a release-trigger section, a versioning quick reference, and Phase status updates.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
.github/workflows/release-trigger.yaml New workflow that detects umbrella major/minor bumps and creates a draft GitHub Release/tag to gate public releases.
docs/versioning.md New documentation for version scheme, CI version behavior, and the release process.
CONTRIBUTING.md Adds contributor-facing guidance and references to the new versioning/release process docs.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread .github/workflows/release-trigger.yaml Outdated
Comment thread .github/workflows/release-trigger.yaml Outdated
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Signed-off-by: John McCall <john@overturemaps.org>
@vcschapp

Victor Schappert (vcschapp) commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

👀 Trying to come back up to speed on this.

John McCall (@lowlydba) Unblocking this is my main priority for this week. It'll take me some time to come back up to speed and also do a bit of progressive work aimed setting this process into the overall versioning strategy, but I'm very much on it this week.

@vcschapp

Copy link
Copy Markdown
Collaborator

This is my current understanding of the state. (Same diagram I just posted on the parent tracking issue #509, but with changes marking the deviations from plan that are specific to this PR. (Note: Diagram is LLM-generated.)

2026-07-20-Overture-schema-devops-branching-strategy-PR-557-phase-2 B

Let me know if anything should be corrected.

@vcschapp

Copy link
Copy Markdown
Collaborator

Thanks for this John McCall (@lowlydba). The progress so far is awesome, and only really being held back by slow reviewers.

Posting my conceptual level thoughts answering items listed in the description below. I'll do one comment per point. Once we're aligned on conceptual should be quick to do the concrete review.

@vcschapp

Copy link
Copy Markdown
Collaborator
  1. Patch destination: CodeArtifact only (Option B in [Devops] Branching Strategy - Phase 3 #509). Publishing a GitHub Release is the sole trigger for public PyPI. Keeps the public release signal curated; every main merge is not a public event.

I'd rather have a version number change in pyproject.toml (with a mandatory attached change log file) be the trigger for automated releasing. It keeps the entire release management as part of the code versioning and PR review process and gives more opportunities for automation.

@vcschapp

Victor Schappert (vcschapp) commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator
  1. Umbrella-only release trigger (deviation from "any package"). The issues said any package's major/minor bump cuts a release, but a single v<major>.<minor>.0 tag can't represent two packages bumping to different versions — e.g. a cli 0.1→0.2 bump would have cut a misleading v0.2.0. Only the umbrella overture-schema bump now cuts a release; its version names the tag. Escape hatch: a non-core package needing public PyPI immediately bumps the umbrella minor in the same PR (one line, and honest — the umbrella distribution's contents changed). Otherwise it rides the next umbrella release.

Our intention for the package architecture is to version each package independently.

I suspect this change relates to Alex's issue #558; and possibly also to the issue of how to maintain separate GitHub releases and tags for different packages within a monorepo.

IMO neither of these issues should be blockers for independently versioning the different packages.

Alex's issue #558

I shouldn't think this would be a huge problem for Alex Iannicelli (@atiannicelli) and the data pipeline team because they should only need to depend on overture-schema and worry about the singular version number of that package, but I'm still hoping to hear a reply back from Alex on the issue.

Maintaining separate releases and tags

Up to now, we've only had a single "sequence" of GitHub releases and release tags that version the schema as a whole.

# Release title Tag Date
1. Preview Release v0.4.0 2023-06-15
2. Preview Release v0.5.0 2023-10-18
3. Schema v0.6.0 release v0.6.0 2023-11-14
4. Schema v0.7.0 release v0.7.0 2023-12-13
5. Schema v0.8.0 release v0.8.0 2024-02-14
6. Schema v0.9.0 release v0.9.0 2024-03-12
7. Schema v0.10.0-beta release v0.10.0-beta 2024-04-17
8. Schema v0.11.0-beta release v0.11.0-beta 2024-05-15
9. v0.12.0-beta v0.12.0-beta 2024-06-12
10. v1.0.0 v1.0.0 2024-07-17
11. v1.1.0 v1.1.0 2024-08-19
12. v1.2.0 v1.2.0 2024-10-15
13. v1.3.0 v1.3.0 2024-11-08
14. v1.4.0 v1.4.0 2024-12-17
15. v1.5.0 v1.5.0 2025-01-22
16. v1.6.0 v1.6.0 2025-02-19
17. v1.7.0 v1.7.0 2025-03-19
18. v1.8.0 v1.8.0 2025-04-02
19. v1.9.0 v1.9.0 2025-05-21
20. v1.10.0 v1.10.0 2025-06-25
21. v1.11.0 v1.11.0 2025-07-23
22. v1.12.0 v1.12.0 2025-09-18
23. v1.13.0 v1.13.0 2025-10-17
24. v1.14.0 v1.14.0 2025-11-19
25. v1.15.0 v1.15.0 2025-12-17
26. v1.16.0 v1.16.0 2026-02-12
27. v1.17.0 v1.17.0 2026-05-14

At first glance, it may seem like versioning multiple packages in a monorepo doesn't work well within the GitHub release paradigm, but in practice it's very doable: we just need one tag-and-release "series" for every package.

There are different ways we could do this, but here's a scheme I think would work well:

  • Tag name = package name with -<SEMVER> suffix.
    • e.g., overture-schema-vA.B.C for the overture-schema package
    • e.g., overture-schema-system-vD.E.F for the overture-schema-system package
  • Release title = package name in backticks, space,
    • e.g. overture-schema vA.B.C
    • e.g. overture-schema-system vD.E.F.

@vcschapp

Copy link
Copy Markdown
Collaborator
  1. Changelog: manual notes at release time. No fragment files or auto-generation machinery. Evaluated per-PR fragments (antsibull pattern) and GitHub auto-generated notes; rejected for friction/quality respectively.

Agreed that GitHub auto-generated notes have terrible quality and won't work—and they moreover probably wouldn't work for per-package releases.

I very much like the idea of every PR coming with an antsibull-style changelog fragment for each PR, enforced with a GH action, enabling the final release notes to be fully auto-generated.

The process would be essentially that every PR that touches one of the Python package trees would require a changelog fragment under that tree, e.g.:

packages/
   overture-schema/
      changelog/
          fragments/
              - 535-add-provider-resource-and-version-to-sources.yaml
              - ...

This would allow the releases to be fully auto-generated from the version bumps and should also be helpful in addressing the somewhat frequent complaint that the content of an upcoming schema release is opaque/difficult to dosciver.

John McCall (@lowlydba) You mentioned this being too much friction. Can you elaborate?

@vcschapp

Copy link
Copy Markdown
Collaborator
  1. Draft-release human gate. The bump merge creates a draft release; a maintainer writes notes and clicks Publish, and only that release: published event fires the Phase 3 publish. Pairs the manual-notes decision with a natural approval gate — no accidental public releases.

This ends up being added friction - to create a release we now need the authoritative version bump PRs and then still have to manually go retrieve the changes and write them up as notes.

Per-package versioning can also produce many release drafts so there's added work here (one release draft/package) and potential to miss something.

If we want a simpler manual release gate "just to make sure" we could have the GH action a "review issue" mid-run and require a designated approver to indicate "approve" or some such on it before the publish actions are taken?

@lowlydba
John McCall (lowlydba) marked this pull request as draft July 21, 2026 18:54
…changelog

- release-trigger: detect per-package major.minor bumps on main and cut a
  published GitHub Release per package (tag `<package>-v<major>.<minor>.0`,
  notes extracted from that package's CHANGELOG.md; umbrella marked Latest)
- require-changelog-fragment: new PR check enforcing a changelog update on any
  major.minor bump (accepts a towncrier fragment or a built CHANGELOG.md)
- towncrier: centralize [tool.towncrier] in root pyproject.toml; per-package
  changelog.d/ with a DRY README that defers to docs/versioning.md (no .gitkeep)
- docs/versioning.md: Diataxis rewrite (reference/how-to/why) with a TOC
- CONTRIBUTING.md: lean guiding-light rewrite (217 -> 91 lines); richer,
  workflow-accurate gitGraphs
- publish-python-packages: add contents: read for reusable-workflow checkout

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Signed-off-by: John McCall <john@overturemaps.org>
Wrap each release path (main->patch, main->minor, vnext->major) in a
collapsible <details> section with its own workflow-accurate gitGraph.
Drop the branch-protection bullet and reduce the CI notes to authoritative
links to the vnext-compat and pr-advisory workflows, which comment their
own fix steps inline.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Signed-off-by: John McCall <john@overturemaps.org>
… a PR

Add how/when each release path reaches consumers (patch rides the next
release to PyPI via the umbrella; minor and major publish to PyPI on merge).
Reword the Opening a PR CI notes to point at the checks without narrating.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Signed-off-by: John McCall <john@overturemaps.org>
Move the inline detect heredoc and the extract_notes heredoc out of
release-trigger.yaml into .github/workflows/scripts/detect_version_bumps.py
and extract_release_notes.py, matching the existing package-versions.py
convention. The workflow steps now just invoke them, so the Python is
lint-covered, testable, and free of heredoc quoting. Behavior is unchanged.

Also vary the CONTRIBUTING release diagrams across distinct packages
(places/base/transportation themes) and note that a no-bump merge still
publishes a patch to internal CodeArtifact.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Signed-off-by: John McCall <john@overturemaps.org>
… any package change

Extract the release-creation logic into a reusable composite action,
.github/actions/create-package-release, with a typed input contract and a
dry-run mode. release-trigger now runs as two jobs: detect emits the bumped
packages as a JSON step output, and a fail-fast:false matrix job calls the
action once per package, so one bad CHANGELOG cannot block sibling releases.
extract_release_notes.py moves into the action as an internal detail, and
detect_version_bumps.py emits JSON (and uses pathlib for portability).

Broaden the changelog policy: require-changelog-fragment now flags any package
with changed files (excluding its own changelog.d/ and CHANGELOG.md) that lacks
a fragment, not just major/minor bumps. Update docs/versioning.md and
CONTRIBUTING.md to match, and clarify that a no-bump merge still publishes a
patch to internal CodeArtifact that is consumable immediately.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Signed-off-by: John McCall <john@overturemaps.org>
@lowlydba

John McCall (lowlydba) commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author
  1. I'd rather have a version number change in pyproject.toml (with a mandatory attached change log file) be the trigger for automated releasing.

👍🏻 Fully aligned, and that is what is now implemented.

The release trigger is a <major>.<minor> change in a package's pyproject.toml; a new Changelog fragment verification check makes the attached towncrier changelog fragment mandatory.

Release management stays entirely inside code versioning + PR review, with no out-of-band "publish" action.

@lowlydba

Copy link
Copy Markdown
Contributor Author
  1. Our intention for the package architecture is to version each package independently ... Tag name = package name with -<SEMVER> suffix ... Release title = package name in backticks, space, <SEMVER>.

Adopted as described. release-trigger now detects a bump in any package (not just the umbrella) and cuts one published Release per bumped package:

  • tag <package>-v<semver> (e.g. overture-schema-v1.18.0, overture-schema-system-v0.2.0)
  • title `<package>` <semver> (e.g. `overture-schema` 1.18.0)

The umbrella overture-schema release is flagged Latest; the others are not. Historical bare vX.Y.Z tags are left as a one-time discontinuity. One nuance to confirm: the title carries the bare semver (1.18.0), while the v prefix lives only in the tag. Happy to add v to the title too if you prefer it to match your example exactly.

#558 (pipeline consumers depending only on the umbrella) is tracked separately and is not a blocker here.

@lowlydba

John McCall (lowlydba) commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author
  1. ... a change log file per PR per affected package ... antsibull-style ... auto-generate release notes. Can you elaborate on the friction you saw here?

Agreed on the pattern, and it is in. To your friction question: the friction I flagged earlier was more or less a gut check from my past experiences. Personally, although sometimes it annoys me, I think this approach produces really high quality release notes - Lets Go!

Two notes on how it landed:

  • Tooling: we use towncrier rather than antsibull. Same per-PR-fragment model, but it is well-maintained, Python-native, and assembles notes with a single towncrier build at bump time.
  • Fragment location: packages/<pkg>/changelog.d/<id>.<type>.md (towncrier's convention) instead of changelog/fragments/*.yaml.
    • I didn't look into customizing the locale of these, for lack of motivation, but probably customizable if there are feelings contrary
  • Scope: broadened to require a fragment on any change to a package, not just version bumps, so the changelog is always complete (patch/internal work uses the misc type).
    • This felt like the spirit of the approach, because even patch changes can ruin production ;)

Config is centralized in the root pyproject.toml; per-package builds pass --config pyproject.toml --dir packages/<pkg>. Light usage docs are in docs/versioning.md and the changelog.d/README.md keepers.

@lowlydba

John McCall (lowlydba) commented Jul 21, 2026

Copy link
Copy Markdown
Contributor Author
  1. ... the draft release gate felt like friction. What if instead there were a review issue mid-run with a designated approver?

Agreed the draft-release gate was friction, so we dropped it. The release is published directly by the workflow with auto-assembled notes, so there is no manual note-writing and no per-package draft to babysit.

For the human checkpoint you are after, we can use a GitHub Environment approval (native required-reviewers gate) on the Phase 3 PyPI publish job rather than a mid-run review issue. Same designated-approver semantics, less machinery, and it sits at the real public-publish boundary instead of on every internal release. Tracked in #509.

…strategy---phase-2b---release-versions

# Conflicts:
#	.github/workflows/publish-python-packages.yaml

Signed-off-by: John McCall <john@overturemaps.org>
…with any-change policy

The new overture-schema-pyspark package (merged from main) lacked a
changelog.d/ keeper. Add one so the fragment check and towncrier treat it
like the other packages. Also correct the stale 'per user-facing change'
line in all keepers to match the any-change policy in docs/versioning.md.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Signed-off-by: John McCall <john@overturemaps.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Devops] Branching Strategy - Phase 2.B - Release Versions

3 participants