Skip to content

Rework release into a reusable workflow with one job per run kind#678

Merged
ptrdom merged 3 commits into
mainfrom
release-workflow-merge
Jun 30, 2026
Merged

Rework release into a reusable workflow with one job per run kind#678
ptrdom merged 3 commits into
mainfrom
release-workflow-merge

Conversation

@ptrdom

@ptrdom ptrdom commented Jun 30, 2026

Copy link
Copy Markdown
Owner

Replaces the release-prepare.yml + release.yml split with a single pipeline that also dry-runs the full publish graph on every PR, then fans it out into one job per run kind via a reusable workflow.

Structure

release.yml is orchestration only. A determine job decides what the run is and outputs kind; four thin caller jobs gate on it and delegate to the shared reusable workflow:

  • RELEASEworkflow_dispatch, first attempt: bump example versions, commit, tag, push, then publish.
  • RELEASE_RETRYworkflow_dispatch whose v$VERSION tag is already on origin from a prior attempt: skip bump/push, check out the existing tag and republish, so a release whose ci-release failed on an intermittent error can be re-run.
  • DRY_RUNpull_request: run the full ci-release graph with an ephemeral GPG key and dummy credentials, assembling the bundle locally (CI_SONATYPE_RELEASE=sonaBundle) instead of uploading. Needs no secrets, so it works on fork PRs.
  • SNAPSHOT — push to main: publish a snapshot.

_run.yml (workflow_call, input kind) holds the entire pipeline once — checkout, credentials, sbt setup, bump/tag, push, ci-release — gated on inputs.kind. No pipeline logic is duplicated; the four jobs are a few lines each. Each run kind shows up as its own job in the Actions UI.

Why

The old two-workflow split existed only so a tag push via the GitHub App token would trigger release.yml. Publishing inline removes that need, so the tags: trigger is dropped to avoid double-publishing.

The commit and tag are pushed before ci-release: a release promoted without its tag pushed (an intermittent push failure after a successful publish) is worse than a pushed tag whose publish failed and can simply be re-run.

The dry run exercises exampleVersionWrite/exampleVersionCheck, signing, and the ci-release task graph on every PR without touching Sonatype.

Permissions

Deny-all default (permissions: {}) with contents: read on every job. No job needs write: the bump-commit and tag are pushed to main with the GitHub App token, whose ability to push comes from the App installation and its bypass entry on the main ruleset, not from GITHUB_TOKEN. The App is still required — it is the sole bypass actor on the protected main ruleset, which the default github-actions[bot] is not.

Validation

The DRY_RUN path is confirmed green on CI: the ephemeral %no-protection key signs every artifact (.asc produced) and sonaBundle assembles the bundle into local staging with no upload. The RELEASE / RELEASE_RETRY / SNAPSHOT paths can only be exercised by their real triggers.

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com

ptrdom and others added 2 commits June 30, 2026 19:57
Fold release-prepare.yml into release.yml and drive every step from a
single RUN_KIND decided up front:

  RELEASE       - workflow_dispatch, first attempt: bump examples, tag,
                  push, then publish.
  RELEASE_RETRY - workflow_dispatch whose tag is already on origin: skip
                  bump/push and republish the tag, so a release whose
                  ci-release failed on an intermittent error can be re-run.
  DRY_RUN       - pull_request: run the full ci-release graph with an
                  ephemeral signing key and dummy credentials, assembling
                  the bundle locally (sonaBundle) instead of uploading.
  SNAPSHOT      - push to main: publish a snapshot.

The old two-workflow split existed only so a tag push via the GitHub App
token would trigger release.yml; publishing inline removes that need, so
the tags: trigger is dropped to avoid double-publishing. The commit and
tag are pushed before ci-release: a release promoted without its tag
pushed is worse than a pushed tag whose publish can simply be re-run.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extract the shared publish pipeline into _publish.yml (workflow_call) and
turn release.yml into orchestration: a determine job outputs the run kind,
then one thin caller job per kind (release, release-retry, dry-run,
snapshot) selects the reusable workflow. No pipeline logic is duplicated;
kind-specific steps stay single-sourced in _publish.yml, gated on
inputs.kind.

Each kind is now its own job in the UI and gets least-privilege
permissions: a deny-all default with contents: read on every job. The
GitHub App token still pushes the bump commit and tag to main (it is the
sole bypass actor on the main ruleset), so no job needs contents: write.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ptrdom ptrdom changed the title Merge release workflows into one RUN_KIND-keyed workflow Rework release into a reusable workflow with one job per run kind Jun 30, 2026
'Publish' overstated it: the dry-run kind assembles a bundle locally and
never uploads. Name the shared pipeline after what it is -- one run of the
release for a given kind -- as _run.yml (name 'Release run', job 'run').

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ptrdom ptrdom enabled auto-merge (squash) June 30, 2026 18:11
@ptrdom ptrdom merged commit c5b690e into main Jun 30, 2026
15 of 25 checks passed
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