Skip to content

ci: release the package with release-please - #44

Draft
aliaksandr-kazarez wants to merge 8 commits into
chore/release-0.1.0-alpha.1from
ci/release-please
Draft

aliaksandr-kazarez wants to merge 8 commits into
chore/release-0.1.0-alpha.1from
ci/release-please

Conversation

@aliaksandr-kazarez

@aliaksandr-kazarez aliaksandr-kazarez commented Sep 17, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds the release pipeline, modelled on session-replay-flutter. It stacks on #50 (the 0.1.0-alpha.1 release), which sits on main. On every push to main the workflow runs the unit-test matrix, then amplitude/actions/release-please-action opens or updates a release PR from conventional commits. When a release PR merges, the same action creates the GitHub release and its tag. SPM has no registry, so the tag is the publish. The release tags ruleset makes a v* tag immutable once created, so the workflow is arranged to get each tag right the first time.

The action is not googleapis/release-please. It reimplements it (release-please-action/index.js, 654 lines). It has no bootstrap-sha and no Release-As, and it finds the commits to release by comparing against the tag for the manifest version. With no tag it compares main...main and proposes nothing. So the first release, v0.1.0-alpha.1, is tagged by hand from #50's merge, which carries the constant and the changelog. This PR starts the manifest at 0.1.0-alpha.1 to match. Its first run on main compares against that tag, finds only this ci: commit, and proposes nothing. After that, the action takes over: feat, fix and breaking changes all bump alpha.N to alpha.N+1.

Verify before tagging. The release job needs a verify job that calls unit-test.yml, so a tag is only cut on a commit whose iOS/macOS/tvOS matrix is green. unit-test.yml and lint.yml drop their push: main triggers. The matrix still runs once per merge, inside verify. Lint on main gated nothing and ran on a billed larger runner.

Version drift check. The action's file updater silently skips a file whose x-release-please-version marker is gone, which would ship a tag reporting a different version on every event. lint.yml now fails when .release-please-manifest.json and StreamingAnalyticsInfo.version disagree. Lint is a required check, so a drifted release PR cannot merge. README.md and the four docs/ pages join extra-files; #50 adds their markers.

Credentials, per the security review.

  • GITHUB_TOKEN gets nothing (permissions: {}), except contents: read for verify. Every write goes through the App token.
  • The App token is narrowed to contents: write and pull-requests: write. Labelling a PR needs nothing more.
  • The release job runs in a release environment that only main can deploy to. The App key is therefore released only to runs on main, not to a workflow any collaborator pushes on a branch.
  • Pinned: create-github-app-token at bcd2ba49 (v3.2.0), the release action at 820754f (v0), runner ubuntu-24.04, 10-minute timeout. Runs queue rather than cancel, because a run killed between the tag and the release would leave a tag no one can move.

Needs before merging: the App installed on this repo, and SDK_RELEASE_BOT_APP_ID / SDK_RELEASE_BOT_PRIVATE_KEY added as secrets of the release environment. Without them, the token step fails on every push to main.

Graduating to 1.0.0. In one PR, remove versioning, prerelease and bump-minor-pre-major, and title it as a breaking change, for example feat(sdk)!: graduate to 1.0.0. The semantic-title check rejects an unscoped feat!:.

Validation

  • Rebased onto chore: release 0.1.0-alpha.1 #50 at c5f6fce: the manifest and constant are both 0.1.0-alpha.1, and every workflow file parses. Before the rebase, at 61ad843 on main, all checks were green. The version check proved itself at d8896a0, failing on manifest 0.1.0-alpha.1 != StreamingAnalyticsInfo.version 0.0.0.
  • I simulated the action's own code at 8091f292 (its bumpVersion and updateGenericFile) with this config:
    • From 0.1.0-alpha.1, feat, fix and feat(sdk)! each give 0.1.0-alpha.2.
    • With the graduation keys removed, feat(sdk)! gives 1.0.0, feat gives 0.2.0 and fix gives 0.1.1.
    • Over chore: release 0.1.0-alpha.1 #50's files, the updater rewrote only the marked version strings.
  • I ran the version check locally in three cases. Matching versions pass, a drifted constant fails, and a missing constant fails with a distinct message.
  • Not exercised: release-please.yml runs only on pushes to main. The verify → release chain, the environment and the narrowed token are first proven by the merge of this PR.

Checklist

  • Does your PR title have the correct title format?
  • Does your PR have a breaking change?: No. Only CI and release configuration change; there is no source or public API change.

Prepared with Claude. LLMs make mistakes — please verify before acting.

@aliaksandr-kazarez
aliaksandr-kazarez changed the base branch from main to feat/library-version September 18, 2026 04:16
@aliaksandr-kazarez
aliaksandr-kazarez force-pushed the ci/release-please branch 2 times, most recently from a6282da to 5c59167 Compare September 18, 2026 07:32
@aliaksandr-kazarez
aliaksandr-kazarez marked this pull request as ready for review September 18, 2026 15:52
@aliaksandr-kazarez
aliaksandr-kazarez requested a balanced review from Copilot September 18, 2026 15:52

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

The first-version behavior, graduation instructions, and mutable privileged action reference need correction.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Adds automated prerelease versioning, changelog generation, tagging, and GitHub Releases via release-please.

Changes:

  • Configures alpha releases and Swift version-file updates.
  • Adds manifest-based version tracking.
  • Adds a GitHub App-authenticated release workflow.
File summaries
File Description
release-please-config.json Defines prerelease and version-update behavior.
.release-please-manifest.json Initializes package version tracking.
.github/workflows/release-please.yml Runs release-please on main.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 3
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread .github/workflows/release-please.yml Resolved
Comment thread .github/workflows/release-please.yml Resolved
Comment thread release-please-config.json Resolved
@aliaksandr-kazarez
aliaksandr-kazarez force-pushed the ci/release-please branch 2 times, most recently from 66e8b8d to 8089fc5 Compare September 18, 2026 16:48
@aliaksandr-kazarez
aliaksandr-kazarez marked this pull request as draft September 18, 2026 22:53
Base automatically changed from feat/library-version to main September 18, 2026 22:54
@aliaksandr-kazarez
aliaksandr-kazarez removed this pull request from stack #47 September 21, 2026 19:12
@aliaksandr-kazarez
aliaksandr-kazarez requested a balanced review from Copilot September 22, 2026 22:39

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

Release authentication and the initial history cutoff remain unresolved, preventing a reliable first release.

Get a fresh assessment by requesting another Copilot review.

Review effort: Balanced
Findings: 1 High severity · 1 Medium severity

Open (2)
Resolved since last review (3)

Comment on lines +65 to +66
app-id: ${{ secrets.SDK_RELEASE_BOT_APP_ID }}
private-key: ${{ secrets.SDK_RELEASE_BOT_PRIVATE_KEY }}
@@ -0,0 +1,22 @@
{
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
"packages": {
@aliaksandr-kazarez
aliaksandr-kazarez marked this pull request as ready for review September 22, 2026 22:56

@jlmitra-ampl jlmitra-ampl left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Reviewed for least privilege. The pattern matches session-replay-flutter, and it's tighter in several places (SHA pins, the verify gate, queued concurrency). Three permission-scoping comments are inline.

Two repo-settings items sit outside this diff; I'll raise them separately:

  • The release tags ruleset blocks delete, update and force-push on v*, but it doesn't restrict creation. Anyone with write access, or any workflow token with contents: write, can create a release tag. For SPM that is a publish, and it can't be undone. Suggest adding a creation rule with the release App as the only bypass.
  • Actions settings: switch the default workflow permission to read, and turn off "Allow GitHub Actions to create and approve pull requests".

Unrelated to permissions: the # v0 comment on 8091f292 is 5 commits behind the current v0 tag (820754f). Those commits don't touch release-please-action, but the comment is misleading.

Comment thread .github/workflows/release-please.yml Outdated
Comment on lines +41 to +43
permissions:
contents: write
pull-requests: write

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Least privilege: GITHUB_TOKEN doesn't need write here. Every write in this workflow goes through the App token. release-please-action only falls back to github.token when token is empty, and the token step fails before that can happen. So these two grants go unused. They also flow into the verify job. unit-test.yml's own contents: read caps them there, but only by accident. (This came over from session-replay-flutter, which has the same over-grant.)

Suggest denying everything at the top level and granting per job:

Suggested change
permissions:
contents: write
pull-requests: write
permissions: {}

and then:

  verify:
    permissions:
      contents: read
    uses: ./.github/workflows/unit-test.yml

  release-please:
    permissions: {}

Comment on lines +63 to +66
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ secrets.SDK_RELEASE_BOT_APP_ID }}
private-key: ${{ secrets.SDK_RELEASE_BOT_PRIVATE_KEY }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Narrow the App token. With no permission-* inputs, the token gets every permission the App's installation has. That matters most if we use the org-wide SDK release bot. release-please only needs contents and pull requests. It's already limited to this repo by default, since owner/repositories are unset.

Suggested change
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ secrets.SDK_RELEASE_BOT_APP_ID }}
private-key: ${{ secrets.SDK_RELEASE_BOT_PRIVATE_KEY }}
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
with:
app-id: ${{ secrets.SDK_RELEASE_BOT_APP_ID }}
private-key: ${{ secrets.SDK_RELEASE_BOT_PRIVATE_KEY }}
permission-contents: write
permission-pull-requests: write

If labelling the release PR fails (autorelease: pending), add permission-issues: write.


release-please:
needs: verify
runs-on: ubuntu-24.04

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Keep the App key behind an environment. Put the App ID and private key in a release environment that only main can deploy to (Settings → Environments → Deployment branches: main). This repo is public and its default workflow permission is write. Any collaborator can open a pull request from a branch in this repo that adds a pull_request workflow, and that workflow can read repo-level secrets. Environment secrets are only released to jobs running on main. This applies directly if we go the repo-scoped App route, as session-replay-flutter does. If we stay on the org secrets instead, they should be limited to selected repositories.

Suggested change
runs-on: ubuntu-24.04
runs-on: ubuntu-24.04
environment: release

@jlmitra-ampl jlmitra-ampl left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

approving conditionally pending addressing comments

@aliaksandr-kazarez aliaksandr-kazarez mentioned this pull request Sep 23, 2026
1 task done
@aliaksandr-kazarez
aliaksandr-kazarez changed the base branch from main to chore/release-0.1.0-alpha.1 September 26, 2026 01:03
aliaksandr-kazarez and others added 2 commits September 25, 2026 23:22
Adds the release pipeline. Modelled on session-replay-flutter, which runs the
same setup on prerelease versions today.

* release-please opens a version PR from conventional commits; merging it tags
  the repo and cuts the GitHub release
* alpha prereleases via versioning "prerelease" + prerelease-type "alpha", so
  the first release is v0.0.1-alpha.1
* release-type "simple" because release-please has no Swift updater; files
  carrying a version go in extra-files with an x-release-please-version marker
* no publish job: SPM resolves from the git tag, so tagging is publishing, and
  this SDK ships no podspec

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
f8d387b6 was v3.0.0. bcd2ba49 is v3.2.0, the current v3 tag and the pin
16 other amplitude workflows already use. Same major, so the app-id and
private-key inputs are unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
aliaksandr-kazarez and others added 5 commits September 25, 2026 23:22
Matches what #48 established for the other workflows, with one
inversion: cancel-in-progress is false. Cancelling a release run between
the tag and the GitHub Release would strand a tag that the release tags
ruleset forbids moving or deleting.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Tagging is publishing for SPM, and the release tags ruleset makes a tag
immutable the moment it is created. Two gaps closed ahead of the first one.

release-please ran as a second workflow on the same push as the tests, so
it could tag a commit whose matrix was still running or already red. It
now calls unit-test.yml as a `verify` job and needs it. unit-test.yml
drops its own push trigger so the matrix still runs exactly once on main.

Nothing asserted that the tag and the version constant agree, though
release-please writes both from the same number, and its generic updater
skips a file that lost its annotation without a warning. A drift would
ship events reporting a version that does not exist, permanently.
lint.yml now compares the manifest against the constant; it is already a
required check, so the release PR cannot merge with a drift.

lint.yml also drops its push trigger. After the merge it gated nothing,
and it runs on a larger runner, which is billed even on a public repo.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Security review on the release workflow.

GITHUB_TOKEN no longer gets contents and pull-requests write. Every write
goes through the App token, so the workflow denies everything and grants
verify only the contents read its checkout needs. The App token is
narrowed to contents and pull requests; labelling a PR needs nothing
more. The job runs in a `release` environment so the App key is released
only to runs on main, not to any branch a collaborator pushes.

The action is re-pinned to 820754f, the current v0. Nothing in the five
commits between is loaded by the release-please action.

The amplitude/actions action reimplements release-please and supports
neither bootstrap-sha nor Release-As. The manifest therefore starts at
0.1.0-alpha.1, the release tagged by hand in #50, and the graduation
steps now use a breaking-change title instead of a Release-As footer.
prerelease-type and bump-patch-for-minor-pre-major are dropped: from an
existing alpha the action bumps the alpha counter whatever they say.

The README and docs pages join extra-files, so their version strings
follow each release.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With the manifest at 0.1.0-alpha.1 and the constant still 0.0.0 on main,
the version check held this PR until #50 merged, and #50 is blocked on
taxonomy. At 0.0.0 the two agree again, and merging this first is
harmless: the action looks for a v0.0.0 tag, finds none, and proposes
nothing. #50 now carries the manifest bump alongside the constant.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This PR now sits above #50, which moves the version constant to
0.1.0-alpha.1, so the manifest follows it and the version check passes.
v0.1.0-alpha.1 is tagged by hand from #50's merge, and the action's first
comparison runs from that tag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in main (#51) and the release-notes fix from #50.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@aliaksandr-kazarez
aliaksandr-kazarez marked this pull request as draft October 15, 2026 22:54
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.

3 participants