fix(CI): stop re-uploading release assets to already-published releases - #18
Merged
Merged
Conversation
ci.yml already builds and publishes every binary when a version tag is pushed. release-assets.yml's `release: published` trigger then fired again and tried to rebuild and re-upload the same assets, which GitHub now rejects with "Cannot upload asset ... to an immutable release" once a release is published. Trim release-assets.yml down to just the README link update it's actually needed for.
almostintuitive
added a commit
that referenced
this pull request
Aug 27, 2026
## Problem `curl -fsSL .../install.sh | bash` installs a **9-byte text file containing `Not Found`** as `/usr/local/bin/aperiodic`: ``` $ file /usr/local/bin/aperiodic /usr/local/bin/aperiodic: ASCII text, with no line terminators $ aperiodic --version /usr/local/bin/aperiodic: line 1: Not: command not found ``` Every release since **v1.0.4 has shipped with zero binaries**, so `releases/latest/download/aperiodic-darwin-arm64` 404s: | tag | assets | |---|---| | v1.0.5 | none | | v1.0.4 | none | | v1.0.3 | 6 binaries | ## Root cause Three failures compounding: 1. **`ci.yml`'s release job has never run.** `bump.yml` pushes the tag with the default `GITHUB_TOKEN`, and GitHub deliberately does not trigger workflows from `GITHUB_TOKEN` events. Every `ci.yml` run in this repo's history is `event: push` on `main` or `pull_request` — not one tag push. 2. **Immutable releases reject post-publish uploads.** With no automated release, tags were published by hand, firing `release-assets.yml` on `release: published`. That's too late — [run 32262281202](https://github.com/aperiodic-io/cli/actions/runs/32262281202) built all six binaries and then died on: ``` Cannot upload asset aperiodic-linux-arm64 to an immutable release. GitHub only allows asset uploads before a release is published. ``` #18 responded by deleting the upload logic rather than moving it before publication, so nothing has uploaded binaries since. 3. **`install.sh` failed silently.** It used `curl -L` without `-f`, so curl exits `0` on a 404 and writes the error body to disk. The script then `chmod +x`'d it and moved it into `/usr/local/bin`. ## Changes **`bump.yml` now owns the entire release.** Tag → build all six targets → attach them to a **draft** release (permitted under immutability) → verify the asset count → publish. Keeping it in one workflow sidesteps the `GITHUB_TOKEN` trigger problem entirely, with no PAT required. The asset-count check means a partial upload fails the run instead of quietly publishing an empty release — the exact failure mode that went unnoticed for two releases. **`ci.yml`'s `release` job is removed.** It could never fire from a `GITHUB_TOKEN` tag push, and on a manual tag push it would now race `bump.yml` and fail on immutability regardless. **`install.sh` fails loudly.** `curl -fL`, plus explicit rejection of empty and HTML/error-page responses, and temp-file cleanup on every exit path. `INSTALL_DIR` is now overridable, which makes the happy path testable without touching the system. ## PagerDuty Adds a `notify-failure` job, on `ubuntu-latest` to match this repo's runners. A silent release failure is precisely what this bug was. ## Verification `install.sh` against the currently-broken `latest` — now exits 1 and leaves the system untouched: ``` Error: Failed to download binary from https://github.com/.../aperiodic-darwin-arm64 The release may not exist, or it may not have assets for darwin-arm64. EXIT CODE: 1 ``` Happy path against `v1.0.3`, which still has assets: ``` $ INSTALL_DIR=/tmp/testbin bash install.sh v1.0.3 Installation complete. $ file /tmp/testbin/aperiodic /tmp/testbin/aperiodic: Mach-O 64-bit executable arm64 ``` `actionlint` clean across all workflows. `go build ./...` passes; `go test ./...` gives 33 passed / 5 failed, all five being pre-existing integration tests gated on `APERIODIC_API_KEY`, which CI supplies. ## Follow-ups (not in this PR) - **v1.0.4 and v1.0.5 stay assetless** — immutable releases can't be amended. The next `bump` run produces the first working release; until then `install.sh` correctly refuses rather than installing garbage. - **`release-assets.yml`** (the README link updater) triggers on `release: published`. Since `bump.yml` publishes via `GITHUB_TOKEN`, it won't fire either — same root cause as #1, worth folding into `bump.yml` separately. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ci.yml'sreleasejob already builds every OS/arch binary and publishes the GitHub release on tag push.release-assets.ymlalso triggers onrelease: published, rebuilding the same binaries and trying to upload them again — which GitHub now rejects once a release is published ("Cannot upload asset ... to an immutable release"), as seen in run #32262281202.release-assets.ymlthat isn't redundant is the README release-link update (added in feature(CI): update release workflow to refresh README asset links #15), so this trims the workflow down to just that.Test plan
workflow_dispatchwith an existing tag) and confirmrelease-assets.ymlonly updates README links, without attempting a duplicate asset uploadci.yml's release job still publishes assets successfully