Skip to content

ci: replace the two-stage release flow with release-please - #96

Merged
mogita merged 4 commits into
mainfrom
chore/release-please
Sep 15, 2026
Merged

mogita merged 4 commits into
mainfrom
chore/release-please

Conversation

@mogita

@mogita mogita commented Sep 15, 2026

Copy link
Copy Markdown
Contributor

Ticket

CHA-2963

Problem

initiate_release.yml opened a release-NN branch with a hand-typed version, release.yml published whatever that branch name said, and publish-new-version.yml was a manual fallback. Nothing tested the tree before it was tagged and pushed to Maven Central.

Solution

Deletes all three workflows. release-please keeps the bump in a Release PR, and the suite runs before the tag.

  • release-please-config.json / .release-please-manifest.json, simple strategy, seeded at 10.1.1. No last-release-sha: 10.1.1 is reachable from main.
  • include-v-in-tag: false keeps tags bare (10.1.2, not v10.1.2). The key is singular; include-v-in-tags is dropped as unknown.
  • gradle.properties uses the generic updater's block form. A .properties file takes a trailing comment as part of the value, so the markers bracket the line.
  • detect finds the merged Release PR waiting to be tagged; the suite gates the release half.
  • The ci.yml job moves into a reusable run_tests.yml that the release gate also calls.
  • workflow_dispatch takes publish_tag, replacing publish-new-version.yml.
  • pr_title.yml added.
  • CONTRIBUTING.md rewritten for the new flow.

CHANGELOG.md is unchanged: it already comes from standard-version, which writes the format release-please writes.

How to verify

Dry run against a throwaway branch with one fix: commit, now deleted:

  • proposes 10.1.2 from base 10.1.1
  • gradle.properties 10.1.1 -> 10.1.2, markers intact
  • CHANGELOG.md section inserted at the top
  • compare link 10.1.1...10.1.2, no v

actionlint clean on all four workflows.

Expected on merge: no Release PR, because ci: is a hidden type.

Review instructions

PR titles did not previously decide anything in this repo; the version was typed into initiate_release. From here a non-conventional title ships nothing, so pr_title.yml changes how contributors work. Worth announcing when this lands.

initiate_release.yml opened a release-NN branch with a hand-typed version and
release.yml published whatever that branch name said, with publish-new-version.yml
as a manual fallback. All three are gone. release-please keeps the bump in a
reviewable Release PR, and the suite runs before the tag.

This is the one repo where the version was never derived from PR titles, so
pr_title.yml is a behaviour change rather than an alignment: titles now decide the
version, and a non-conventional title ships nothing.

gradle.properties is updated through the generic updater's block form. A
.properties file takes a trailing comment as part of the value, so the marker
brackets the line instead of sitting on it, and configparser still reads 10.1.1.

Tags here have no v prefix, which include-v-in-tag: false preserves. The key is
singular; the plural is silently dropped as an unknown key. No last-release-sha is
needed, since 10.1.1 is reachable from main, and CHANGELOG.md is already in the
format release-please writes, having come from standard-version.

The ci.yml job moves into a reusable run_tests.yml that the release gate also
calls. ci.yml was already pull_request-only, so no duplicate-run change here.
Extracting the job into run_tests.yml dropped environment: ci from it.
STREAM_API_SECRET, STREAM_API_KEY and STREAM_BASE_URL all live in that
environment rather than at repo level, so secrets: inherit had nothing to pass
and the call failed before any step ran with "Secret STREAM_API_SECRET is
required, but not provided".

The secret is also declared required: false, because an environment secret is
resolved by the job at runtime rather than handed over at call time.
main requires a status check called '🧪 Test & lint', which was this job's name
before the extraction. Renaming it to Build meant that context would never report
again, so every PR would block, including the Release PR release-please opens.

A reusable workflow reports as '<caller job id> / <callee job name>', so the check
becomes 'ci / 🧪 Test & lint' and the required context still has to be updated from
the bare name at merge time. Keeping the name makes that a one-word change.
@mogita

mogita commented Sep 15, 2026

Copy link
Copy Markdown
Contributor Author

One thing this PR cannot do from the diff, and it has to happen at merge time.

main requires a status check named 🧪 Test & lint. That was the ci.yml job name; a reusable workflow reports as <caller job id> / <callee job name>, so from this branch it is ci / 🧪 Test & lint. The bare name will never report again, so leaving the protection as-is blocks every future PR, including the Release PR release-please opens.

When merging, update the required context on main:

gh api -X PATCH repos/GetStream/stream-sdk-java/branches/main/protection/required_status_checks \
  -f "contexts[]=ci / 🧪 Test & lint"

The job keeps its original name so this is a prefix change rather than a rename. enforce_admins is false, so an admin can merge before this lands, but the Release PR is opened by github-actions[bot] and cannot bypass.

None of the other five repos needed this: py, ruby, php and net have no required status checks, and go was already on release-please.

@mogita mogita left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Reviewed at 1ef6336.

The shape is right: the irreversible half is gated behind the suite, the two release-please halves are separate calls, and the three legacy workflows are deleted in the same commit that lands the config, which is what stops release-please--branches--main from tripping the old startsWith(head_ref, 'release-') trigger.

2 findings, both inline: 1 must fix (ci.yml:14, the required status check), 1 should fix (release.yml:29, Release PR stalls during a release run).

Checked and fine, no action needed:

  • include-v-in-tag is the real key name (schemas/config.json:89 in googleapis/release-please).
  • The simple strategy's version.txt update is createIfMissing: false (src/strategies/simple.ts), so the Release PR will not add a stray version.txt.
  • release_created and tag_name are set unprefixed for the root path, so steps.release.outputs.* resolves (release-please-action src/index.ts:178).
  • The repo already has allow_merge_commit: false and squash_merge_commit_title: PR_TITLE, so the linted PR title really is the commit subject release-please reads. Without both, a feat!: title silently downgrades to a minor.

Verdict: comment. The must-fix is a repo setting that has to move at merge time, not a change to this diff.

Comment thread .github/workflows/ci.yml
run: |
./gradlew spotlessCheck
./gradlew build --info
uses: ./.github/workflows/run_tests.yml

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[Must Fix] Renaming this job locks main.

Branch protection on main requires the status check 🧪 Test & lint:

evidence
$ gh api repos/GetStream/stream-sdk-java/branches/main/protection
"checks": [{"context": "🧪 Test & lint", "app_id": 15368}]

$ gh api repos/GetStream/stream-sdk-java/commits/1ef6336/check-runs
ci / Build	completed	success

$ grep -rn "Test & lint" .github/
(no match)

A reusable-workflow call reports as <caller job id> / <called job name>, so the check is now ci / Build and nothing produces the old name again.

Why it matters: once this merges, every PR into main waits on a required check that can never report, including release-please's Release PR. Releases then need an admin bypass every time.

Fix: update the required contexts to ci / Build in the same window as the merge. Worth adding 👮 Conventional PR title while you are in there, since it is the only thing between a non-conventional title and a silent no-release.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed the other way round, in fbb81df: the job keeps name: 🧪 Test & lint, so the reusable call reports as ci / 🧪 Test & lint and the required context becomes a prefix change instead of a rename. Confirmed on this head, the check reports under that name.

Merge-time command, with the title gate added as you suggested (strict has to be repeated, the PATCH replaces the whole object):

gh api -X PATCH repos/GetStream/stream-sdk-java/branches/main/protection/required_status_checks \
  -F strict=true \
  -f "contexts[]=ci / 🧪 Test & lint" \
  -f "contexts[]=👮 Conventional PR title"

release-pr:
name: Release PR
needs: detect
if: needs.detect.outputs.pending != 'true'

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[Should Fix] A PR merged while a release is running never reaches a Release PR.

pending is true from the moment the Release PR is merged until the release job flips its label, and the tests job in between is allowed 30 minutes. Every push in that window skips this job, and nothing re-runs the PR half after release succeeds, so those commits sit unreleased until the next unrelated push to main.

Fix: run the PR half once more after the tag lands, for example a job with needs: release and if: needs.release.outputs.release_created == 'true' calling googleapis/release-please-action@v4 with skip-github-release: true.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed in 30419f4: a release-pr-followup job with needs: release and if: needs.release.outputs.release_created == 'true', calling the action with skip-github-release: true, so the reversible half runs once more after the tag exists.

A push merged while a release run is in flight is gated out of release-pr by the pending label, and nothing re-runs that half once the tag exists, so those commits sat unreleased until the next unrelated push to main.
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