Skip to content

Restructure release workflow for atomic publish: gate release on signing success #777

@IvanMurzak

Description

@IvanMurzak

Summary

The release pipeline currently creates the GitHub Release before several finalization steps run. If any of those steps fails (notably OpenUPM signing), the release is already public and the failure produces a misleading false-positive: a published release whose assets are incomplete or unsigned. We want to restructure the workflow into a proper "build everything → atomically publish" shape, where the GitHub Release is created only when every asset (signed UPM package included) is already prepared and ready to attach.

Problem

In .github/workflows/release.yml today:

  • release-unity-plugin is the job that creates the GitHub Release + tag (softprops/action-gh-release@v2). It runs after tests.
  • sign-and-publish-upm (added in ci: sign OpenUPM package via Unity UPM CLI on release #776) has needs: release-unity-plugin and runs after the release exists. It is marked continue-on-error: true, so if signing fails or the secrets are missing, the workflow still reports green, but the release that's now public is unsigned.
  • Release notes (the body) are generated inside release-unity-plugin's own steps (Generate release description writes release.md from git log), so the notes are tightly coupled to the release-creation step.

This shape has two concrete problems:

  1. Signing is a soft afterthought. A signing failure leaves a public, unsigned release in place. The new OpenUPM `trackingMode: githubRelease` consumer would pick up that unsigned release.
  2. Release notes can't be inspected before publication. They're only materialized as a step output during the release-creation job — there's no chance to gather/verify them earlier.

Proposed shape

Restructure the workflow so the GitHub Release is the last thing that happens, gated on every artifact already being prepared:

  1. prepare-release-notes (new) — runs early in parallel with tests. Generates release.md exactly as the current Generate release description step does. Uploads it as a workflow artifact.
  2. build-signed-upm-package (new — extracted from the current sign-and-publish-upm) — runs in parallel with tests/builds. Verifies signing secrets, installs Unity UPM CLI, runs `upm pack --organization-id`, verifies the archive contains `package/.attestation.p7m` and that its basename begins with `com.ivanmurzak.unity.mcp-`. Uploads the signed `.tgz` as a workflow artifact.
  3. release-unity-plugin (modified — the atomic publish point) — needs: is extended to include both new jobs and the existing build-unity-installer / build-and-zip-mcp-server. Downloads all three asset artifacts (.unitypackage, server .zips, signed .tgz) and the release-notes artifact. Creates tag + release with the pre-built notes and uploads every asset in the same softprops/action-gh-release@v2 call (or via per-asset upload steps inside this job — the key requirement is that the tag/release is only created once all assets are on disk in this job).
  4. publish-unity-installer / publish-mcp-server / sign-and-publish-upm as separate post-release jobs — these collapse away. If we keep them as separate jobs for runner/scheduling reasons, they must needs: the release-unity-plugin job and only do the upload (the build/sign work already happened upstream), but in that case the upload failing still strands a release with incomplete assets — so prefer folding the uploads into release-unity-plugin itself.

Hard-gate signing (decision)

The current `continue-on-error: true` design was added so missing secrets wouldn't break releases. That trade-off is being reversed for this restructure: signing is now a hard gate. If `UPM_SERVICE_ACCOUNT_KEY_ID` / `UPM_SERVICE_ACCOUNT_KEY_SECRET` / `UPM_ORG_ID` are missing, the `build-signed-upm-package` job fails fast with a clear error explaining how to configure them (see `docs/openupm-signing.md`), and the release pipeline fails as a whole. No release is created. Forces secret configuration before the next release ships.

Update `docs/openupm-signing.md` to reflect that signing is now blocking, and remove the soft-fail language.

Acceptance criteria

  • build-signed-upm-package job exists and produces a signed .tgz artifact whose name is fixed/predictable (e.g. signed-upm-package). Runs in parallel with tests; does not depend on release-unity-plugin.
  • prepare-release-notes job exists and produces a release-notes artifact containing release.md. Runs in parallel with tests; does not depend on release-unity-plugin.
  • release-unity-plugin needs: includes build-signed-upm-package, prepare-release-notes, plus the existing test/build dependencies. The release/tag is created only when ALL prerequisite jobs (including signing) succeeded.
  • All release assets (.unitypackage, server .zips, signed .tgz) are uploaded as part of the same atomic release-creation step inside release-unity-plugin (no post-release upload jobs that can strand a release with missing assets).
  • The sign-and-publish-upm post-release job is removed (its signing work moved to build-signed-upm-package, its upload work folded into release-unity-plugin).
  • Missing UPM signing secrets cause the pipeline to fail fast with a clear error message; no GitHub Release is created.
  • docs/openupm-signing.md is updated to reflect that signing is blocking and to remove the soft-fail language.
  • All existing checks still pass on PR CI.

Context links

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions