Skip to content

chore: standardize release process#209

Merged
tylerjroach merged 8 commits into
masterfrom
release/standardize-release-process
May 20, 2026
Merged

chore: standardize release process#209
tylerjroach merged 8 commits into
masterfrom
release/standardize-release-process

Conversation

@tylerjroach
Copy link
Copy Markdown
Contributor

Summary

Standardizes this repo's release process to match the rest of the Mixpanel SDK fleet, per the SDK Release Process Standardization Effort.

After merge, releases follow a uniform two-step ceremony:

  1. Run the Prepare Release workflow (prepare-release.yml) — opens a release PR with the version bump, changelog section, and README header line updated.
  2. Push the release tag — release-upm.yml validates, gates on the release GitHub environment for human approval, and publishes (where applicable for this ecosystem).

What's added

  • .github/modules.json — single source of truth for module config (paths, tag prefixes, package names)
  • .github/workflows/prepare-release.yml — manual dispatch, opens release PR
  • .github/workflows/release-upm.yml — tag-triggered publish workflow
  • .github/workflows/pr-title-check.yml — conventional-commit enforcement (regex built from modules.json)
  • .github/scripts/generate-changelog.sh — shared changelog generator (verbatim from mixpanel-android)
  • README version-header line on each module's README, seeded with the current latest tag
  • CHANGELOG.md per module where missing
  • Single module analytics (com.mixpanel.unity UPM package)
  • Removes the legacy .github/workflows/release.yml (heinrichreimer/github-changelog-generator-action) — fully superseded by the new tag-triggered release-upm.yml
  • UPM consumers install via git URL, so there is no registry upload. The publish workflow attaches the committed Examples.unitypackage and Tests.unitypackage as GitHub release assets
  • .unitypackage files are NOT rebuilt by CI (Unity license required). If a release includes Examples/Tests changes, maintainers must rebuild the .unitypackage locally before tagging — runbook documents this
  • The existing scripts/release.py is now superseded but intentionally left in place; can be deprecated in a follow-up. CLAUDE.md and .github/copilot-instructions.md still reference the old release flow and would benefit from a follow-up update

How releases work after this lands

Full ceremony, one-time setup, and PR title conventions: Unity Release Runbook

One-time setup before the first release

The runbook lists the full setup. The work that requires repo-admin access (cannot be done in this PR):

  • Create a GitHub Environment named release with required reviewer(s)
  • Branch protection on the default branch (require PR review + the new Validate PR title check)
  • Tag rulesets (restrict creation/update/deletion of release tags)
  • Workflow permissions: enable Read and write permissions and Allow GitHub Actions to create and approve pull requests (needed by prepare-release.yml's gh pr create)

Notes

  • This is a WIP draft to be reviewed alongside the equivalent PRs in the other SDK repos. The Flutter and React Native ports are tracked at mixpanel-flutter#238, mixpanel-flutter-session-replay#30, mixpanel-react-native#408, mixpanel-react-native-session-replay#64.
  • This PR does not change any source code or build behavior — only adds release infrastructure.
  • The standardization commit is followed by a small fix: commit correcting a jq tag-resolution snippet (the same bug exists in the WIP Flutter/RN PRs and should be back-ported there).

Adds the shared release infrastructure (prepare-release, release-<ecosystem>,
pr-title-check workflows + modules.json + generate-changelog.sh) so this
repo's release flow matches the other Mixpanel SDK repositories.

See: https://www.notion.so/348e0ba925628029af63c779caa835f9
The previous form `select($t | startswith(.value.tag_prefix))` rebinds the
context inside `select` so `.value` is no longer the entry — jq errors with
"Cannot index string with string \"value\"" and every tag push fails.

Bind the prefix first via `.value.tag_prefix as $p` so the comparison runs
against the captured variable.
The legacy scripts/release.py bumped both package.json and the
`MixpanelUnityVersion` constant in Mixpanel/MixpanelAPI.cs (the value the
SDK reports as `$lib_version` on every event sent to Mixpanel). The first
pass of the standardized prepare-release workflow only bumped package.json,
so the C# constant would lag behind every release and the SDK would report
a stale version to Mixpanel.

Mirror the Flutter PR's `version_files` mechanism: list extra files in
modules.json, do a literal `OLD_VERSION -> VERSION` replace in the prepare
workflow, and refuse to publish in release-upm.yml if any version_files
entry contains a semver-shaped string other than the tag version.

For mixpanel-unity, version_files is `["Mixpanel/MixpanelAPI.cs"]` —
verified that file contains exactly one X.Y.Z literal so the strict
validation works.
…ease flow

The standardization PR replaces scripts/release.py and the legacy
.github/workflows/release.yml with a two-step prepare-release +
release-upm flow. Update both AI-agent context files so future automated
edits don't fall back to the old script and the deleted workflow.

Both docs now point at the Unity Release Runbook on Notion as the
canonical reference.
The script is fully replaced by the prepare-release.yml + release-upm.yml
workflows. Leaving it in tree just gives contributors and AI agents a
second, broken way to cut a release. Remove the script, its .meta
sidecar, the now-empty scripts/ directory, and the AGENTS.md reference
that still pointed at it. Also fix a stale line in CLAUDE.md that
suggested rebuilding .unitypackage assets via "scripts under scripts/" —
the script never did that; .unitypackage exports come from the Unity
Editor's Assets → Export Package… menu.
Aligns the changelog-section extraction with the deployed mixpanel-android
release workflow, which uses a sed range. The Python regex implementation
was an accident of port-time authorship; sed is the proven approach in the
gold-standard Maven Central pipeline.

Uses `\@...@` as the sed address delimiter so tags containing `/` (e.g.
`openfeature/v0.1.0`) don't conflict with the default `/`. Behavior is
otherwise preserved: file-based release_notes.md output, fallback to
"Release $TAG" placeholder when the section is missing or empty, and the
two-step structure for log visibility in the workflow run.
Aligns with the Android fleet's convention of allowing `feat(all): ...`,
`fix(all): ...`, `chore(all): ...` for cross-cutting changes that should
appear in every module's changelog. The shared generate-changelog.sh
already matches `all` (it was copied verbatim from mixpanel-android), so
this regex change is the only piece needed to make the end-to-end flow
accept `all`-scoped PR titles.

For single-module repos, `feat(all): foo` is functionally equivalent to
`feat(<only-module>): foo` — kept for fleet-wide consistency.
@tylerjroach tylerjroach force-pushed the release/standardize-release-process branch from c8fad23 to e6ef2b8 Compare May 14, 2026 17:15
@tylerjroach tylerjroach marked this pull request as ready for review May 19, 2026 13:29
@tylerjroach tylerjroach requested review from a team and rahul-mixpanel May 19, 2026 13:29
Mirrors the same fix applied to mixpanel-flutter and mixpanel-react-native.

Previously the generator required every commit to be scoped to either
`<module>` or `all` — meaning a perfectly valid bare-titled PR like
`feat: add foo` would silently drop out of the changelog despite passing
the pr-title-check workflow (which accepts bare titles).

Split the rule so it matches pr-title-check semantics:

- feat / fix: bare, scoped to the current module, or scoped to `all`
- chore: explicit scope required — bare `chore:` is the convention for
  changes intentionally hidden from the changelog (release prep, CI
  tweaks, lockfile bumps, internal docs)

Other-module scopes (e.g. `feat(other):`) remain excluded from this
module's changelog, as before.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@tylerjroach tylerjroach merged commit e609432 into master May 20, 2026
1 check 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.

2 participants