Skip to content

Draft GitHub Release with installer assets on tag push#14

Merged
lukaskroepfl merged 2 commits into
mainfrom
ci/draft-release-on-tag
May 7, 2026
Merged

Draft GitHub Release with installer assets on tag push#14
lukaskroepfl merged 2 commits into
mainfrom
ci/draft-release-on-tag

Conversation

@webzherd
Copy link
Copy Markdown
Contributor

@webzherd webzherd commented May 6, 2026

Summary

Adds a draft-release job to pack.yml that runs after both pack jobs on v* tag pushes, downloads their workflow artifacts, and creates a draft GitHub Release with each installer file attached as a downloadable asset, plus a SHA256SUMS checksum file:

  • bitmovin-vX.Y.Z-<sha>-{linux,darwin,win32}-*.tar.gz / .tar.xz
  • bitmovin-vX.Y.Z-<sha>-x64.exe (Windows NSIS)
  • bitmovin-vX.Y.Z-<sha>-{x64,arm64}.pkg (macOS, signed with Developer ID Installer)
  • SHA256SUMS — basenames + sha256 hashes, sorted with LC_ALL=C for determinism

Drafts are only visible to maintainers; clicking "Publish release" in the Releases UI flips visibility. Workflow artifacts continue to upload for short-term inspection (30-day retention) on non-tag runs.

Pre-release CI gate

A ci-passed job (only runs on v* tag pushes) queries the GitHub API for the latest build-and-test check-runs on the tagged SHA and fails if any are not success. draft-release needs: it, so tagging a red commit produces no release. Empty / missing CI run case is handled with an explicit error message rather than passing through.

Permissions

Workflow-level is contents: read (default closed for any future job). Only draft-release re-grants contents: write for the release create. Pack jobs and ci-passed inherit the read default.

Other

actions/download-artifact@v4 uses pattern: bitmovin-cli-* so future unrelated artifacts (e.g. source maps, debug symbols) don't silently get pulled into releases.

Known limitation (deliberate, follow-up)

macOS Sequoia / Sonoma (14+) blocks unnotarized .pkg installers at install time even when properly signed ("Apple could not verify ... is free of malware"). Notarization needs a Developer ID Application codesign of the bundled node binary plus an App Store Connect API key for xcrun notarytool — that's a separate PR (and an org-side .p8 key procurement step).

In the meantime, draft releases produced by this workflow are useful for internal testing; do not publish a release publicly until the notarization PR lands (or accept that mac users will see the install-time block).

Test plan

Verified end-to-end on this branch:

  • Pushed v0.0.0-release-test.3; all 4 jobs green (ci-passed, pack-tarballs-and-windows, pack-macos, draft-release)
  • ci-passed correctly read the build-and-test conclusions on the tagged SHA: ["success","success"] → ✓
  • Draft release created with 14 assets: 5 .tar.gz + 5 .tar.xz + 1 .exe + 2 .pkg + SHA256SUMS
  • SHA256SUMS content verified: 13 lines, sorted alphabetically by basename, valid sha256 hex
  • All .pkg files pass pkgutil --check-signature (verified earlier in pack-macos's verify step)
  • Test draft + tag cleaned up

Out of scope (follow-ups)

  1. macOS notarization — Developer ID Application codesigning of bundled binaries + xcrun notarytool submit + xcrun stapler validate. Needs App Store Connect API key (.p8).
  2. npm publish on tag — needs NPM_TOKEN secret. README install instructions in same change.
  3. Debian .deb via oclif pack deb.
  4. Authenticode signing for .exe (Windows SmartScreen).

🤖 Generated with Claude Code

Adds a third workflow job that runs after both pack jobs on `v*`
tag pushes, downloads their artifacts, and creates a draft GitHub
Release with each installer file (tarballs, .exe, .pkg) attached as
a downloadable asset.

Drafts are invisible to non-maintainers; "Publish release" in the
Releases UI flips visibility once the contents are reviewed.

Workflow artifacts continue to be uploaded for shorter-term
inspection (auto-expire in 30 days). The release job is gated on
tag pushes only — `workflow_dispatch` runs still produce just
artifacts.

Adds `permissions: contents: write` at workflow level for the
Release create. workflow_dispatch and tag-push are the only triggers,
both requiring maintainer access; PRs from forks cannot reach this.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@webzherd webzherd requested a review from lukaskroepfl May 6, 2026 12:42
@lukaskroepfl
Copy link
Copy Markdown
Member

Clean split, exactly the shape I'd hoped for after #11. Three things worth tightening before this comes out of draft:

  1. No gate between CI and release. draft-release runs whenever both pack jobs succeed on a v* tag, but nothing here checks that ci.yml (lint, type, tests) was green on the tagged commit. Tagging a red commit silently produces a draft with installers attached, and someone hitting "Publish release" in the UI gets no signal that CI is broken. Two reasonable approaches:

    • Switch the workflow trigger to workflow_run on ci.yml succeeded with a tag filter, so this only runs after CI is green.
    • Add an explicit ci-passed gating job (calls the reusable test workflow or asserts the latest CI run for the SHA succeeded) that draft-release needs:.
  2. permissions: contents: write is workflow-level — too broad. Only draft-release needs it. The pack jobs are read-only. Per least-privilege, scope it to the job: drop the workflow-level block and add permissions: { contents: write } inside the draft-release job. Bonus: explicitly set permissions: { contents: read } at the workflow level so future jobs default closed.

  3. No SHA256SUMS asset. For a binary distribution this is table stakes — users who download a .pkg or .tar.gz from a Release have no way to verify the bytes match what CI produced without a checksum file shipped alongside. Cheap to add: a step before softprops/action-gh-release that does (cd artifacts && find . -type f -name '*.tar.*' -o -name '*.exe' -o -name '*.pkg' | xargs sha256sum > SHA256SUMS), then include artifacts/SHA256SUMS in the files: list. Especially load-bearing while notarization is still pending — a checksum is the next-best authenticity signal users have.

Smaller note

actions/download-artifact@v4 with no name: downloads every artifact in the run. That's correct for today's two artifacts, but means any future upload-artifact (e.g. source maps, debug symbols) gets silently attached to releases. Either name them explicitly, or accept that and document it in a comment.

Approving once (1) and (2) are addressed; (3) is strongly recommended but not strictly blocking.

Review feedback from #14:

1. Add a `ci-passed` job that fails if ci.yml's build-and-test checks
   on the tagged commit weren't all green. `draft-release` now
   `needs:` ci-passed, so tagging a red commit produces no release
   even if the pack jobs themselves succeed.

2. Tighten permissions. Workflow-level is now `contents: read`
   (default closed for any future job), and only `draft-release`
   re-grants `contents: write` for the release create. Pack jobs
   inherit the read default.

3. Generate a `SHA256SUMS` file from all release assets and attach
   it. Lets downloaders verify bytes match what CI produced — the
   next-best authenticity signal until macOS notarization lands.

Plus the smaller note: download-artifact now uses
`pattern: bitmovin-cli-*` so future unrelated artifacts (debug
symbols, source maps) don't silently get pulled into releases.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@webzherd webzherd marked this pull request as ready for review May 7, 2026 08:12
Copy link
Copy Markdown
Member

@lukaskroepfl lukaskroepfl left a comment

Choose a reason for hiding this comment

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

All three addressed cleanly:

  1. CI gate — new ci-passed job queries the check-runs API for build-and-test* on the tagged commit and fails loud if any didn't succeed (or none ran at all). draft-release now needs: [ci-passed, ...]. The empty-array check is the right belt-and-suspenders against "tag pushed but CI never fired."
  2. Permissions scoped to the job — workflow-level contents: read, job-level contents: write only on draft-release. Exactly the least-privilege shape.
  3. SHA256SUMS asset — sorted with LC_ALL=C, format compatible with sha256sum --check. Useful authenticity signal while notarization is still pending.

Bonus: pattern: bitmovin-cli-* on download-artifact closes the smaller note about future artifact additions silently slipping into releases.

One thing to keep in mind for later (not blocking): the ci-passed job hard-couples to ci.yml's job name (build-and-test*). If that job is ever renamed it'll fail loud, which is fine — just worth a comment in pack.yml noting why the prefix matters.

Approving.

@lukaskroepfl lukaskroepfl merged commit bdba7c2 into main May 7, 2026
7 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.

2 participants