Skip to content

ci: ship an actual binary, replacing the release that never released - #151

Merged
rmems merged 2 commits into
mainfrom
ci/rust-binary-release
Sep 13, 2026
Merged

rmems merged 2 commits into
mainfrom
ci/rust-binary-release

Conversation

@rmems

@rmems rmems commented Sep 7, 2026 •

Copy link
Copy Markdown
Owner

User description

The defect

Every job in release.yml was gated, directly or transitively, on a check-python-pkg step testing for python/Cargo.toml. That check has reported exists=false for the whole life of the workflow, so build-wheels, build-sdist, publish-pypi and github-release were skipped on every run — while the workflow itself reported success.

Both tags cut so far shipped nothing:

$ gh release view v0.1.0 --json assets
{"assets":[]}
$ gh release view v0.2.0 --json assets
{"assets":[]}

Two releases, zero artifacts, green checks.

There is no Python package to publish any more, so the PyPI path and its id-token: write permission are deleted rather than repaired.

What ships now

The CLI, built for five targets on native runners — no cross toolchain or linker configuration involved. This repository is public, so the ARM and Intel-macOS runners are free.

runner target
ubuntu-latest x86_64-unknown-linux-gnu
ubuntu-24.04-arm aarch64-unknown-linux-gnu
macos-15-intel x86_64-apple-darwin
macos-latest aarch64-apple-darwin
windows-latest x86_64-pc-windows-msvc

Two design points worth calling out

workflow_dispatch builds every target and stops; only a v* tag also publishes. A release workflow whose sole trigger is a tag is one whose first real test is a release you cannot take back. The dispatch path makes it exercisable — and is how I would suggest smoke-testing this before the next tag.

The binary name is read from cargo metadata, not hardcoded, so this survives the wh -> writ rename in #145 with no edit. Confirmed against both trees:

main                 -> wh
chore/rename-to-writ -> writ

Failing loudly this time

  • --locked — a release builds from the committed Cargo.lock, never a lockfile silently updated on the runner
  • if-no-files-found: error and fail_on_unmatched_files: true — a silently empty release now fails instead of reporting green
  • one SHA256SUMS generated on a single runner rather than per-target (macOS has shasum, Linux has sha256sum)

Verification

I ran the workflow's own package step verbatim against this workspace:

Finished `release` profile [optimized] target(s) in 7.97s

wh-x86_64-unknown-linux-gnu/
wh-x86_64-unknown-linux-gnu/wh
wh-x86_64-unknown-linux-gnu/README.md
wh-x86_64-unknown-linux-gnu/LICENSE

829K  dist/wh-x86_64-unknown-linux-gnu.tar.gz
e7b20114aa4a9d2218933daf8eb1ad0fd163fa3a474d53d0e483d847446c1320  dist/wh-x86_64-unknown-linux-gnu.tar.gz

$ ./dist/wh-x86_64-unknown-linux-gnu/wh --version
wh 0.2.0

actionlint is clean. It caught the retired macos-13 runner label while I was writing this — a fair advertisement for #149.

Honest limit: the tag-triggered release job cannot be fully exercised without pushing a tag. The build matrix — which is where all the real work is — runs on workflow_dispatch and is fully testable now.

Closes #41

🤖 Generated with Claude Code

https://claude.ai/code/session_016yFLnKN9zQA7uzgpYxTrjX


CodeAnt-AI Description

Ship usable multi-platform CLI binaries from every release

What Changed

  • Releases now build and publish CLI archives for Linux, macOS, and Windows across five native targets instead of skipping all release artifacts.
  • Manual runs build every target for inspection without publishing a release; version tags build and publish the archives.
  • Each archive includes the binary, README, and license, with a single SHA256 checksum file for verification.
  • Failed or empty packaging steps now stop the release instead of reporting success without downloadable files.
  • The unused Python package and PyPI publishing path have been removed.

Impact

✅ Downloadable CLI binaries for five operating systems and architectures
✅ Test releases before creating a version tag
✅ Verifiable release archives with SHA256 checksums

💡 Usage Guide

Checking Your Pull Request

Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.

Talking to CodeAnt AI

Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:

@codeant-ai ask: Your question here

This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.

Example

@codeant-ai ask: Can you suggest a safer alternative to storing this secret?

Preserve Org Learnings with CodeAnt

You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:

@codeant-ai: Your feedback here

This helps CodeAnt AI learn and adapt to your team's coding style and standards.

Example

@codeant-ai: Do not flag unused imports.

Retrigger review

Ask CodeAnt AI to review the PR again, by typing:

@codeant-ai: review

Check Your Repository Health

To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.


Summary by cubic

Fixes the release workflow so releases actually ship a binary; previously every job was gated on a Python package check that always failed, leaving both v0.1.0 and v0.2.0 with zero artifacts and green checks.

  • Builds the CLI for five targets on native runners and publishes .tar.gz archives plus a SHA256SUMS file on v* tag pushes.
  • workflow_dispatch runs the full build matrix but stops before publishing, so the workflow can be exercised without cutting a release.
  • Deletes the PyPI publishing path and its id-token: write permission; there is no Python package anymore.
  • Releases now fail loudly: --locked, if-no-files-found: error, fail_on_unmatched_files: true, and explicit checks that README.md and LICENSE exist before packaging.
  • The binary name is read from cargo metadata, so the upcoming wh → writ rename needs no workflow edit.

Written for commit 16414c7. Summary will update on new commits.

Review in cubic

Every job in release.yml was gated, directly or transitively, on a
`check-python-pkg` step testing for `python/Cargo.toml`. That check has
reported `exists=false` for the whole life of the workflow, so
`build-wheels`, `build-sdist`, `publish-pypi` and `github-release` were
skipped on every run -- while the workflow reported success. Both tags
cut so far shipped nothing: `gh release view` reports an empty `assets`
array for v0.1.0 and for v0.2.0.

Two releases, zero artifacts, green checks. Replaced with a workflow
that builds the CLI for five targets and attaches them to the release.

There is no Python package to publish any more, so the PyPI path and its
`id-token: write` permission are deleted rather than repaired.

Targets are built on native runners -- ubuntu-latest, ubuntu-24.04-arm,
macos-15-intel, macos-latest, windows-latest -- so no `cross` toolchain
or linker configuration is involved. This repository is public, so the
ARM and Intel-macOS runners are free.

Two design points worth stating:

`workflow_dispatch` builds every target and stops; only a `v*` tag also
publishes. A release workflow whose sole trigger is a tag is one whose
first real test is a release you cannot take back. The dispatch path
makes it exercisable.

The binary name is read from `cargo metadata` rather than hardcoded, so
this survives the wh -> writ rename (#145) with no edit. Confirmed
against both trees: metadata yields `wh` on main and `writ` on the
rename branch.

Also: `--locked` so a release builds from the committed Cargo.lock,
`if-no-files-found: error` and `fail_on_unmatched_files: true` so a
silently empty release fails loudly this time, and a single SHA256SUMS
generated on one runner rather than per-target (macOS has `shasum`,
Linux has `sha256sum`).

Verified locally, running the workflow's own package step verbatim:
`cargo build --release --locked` succeeds; the archive contains the
binary plus README and LICENSE; sha256sum produces a valid digest; and
the packaged binary runs (reports `wh 0.2.0`). actionlint is clean -- it
caught the retired `macos-13` label during authoring, which is a fair
advertisement for #149.

Closes #41

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016yFLnKN9zQA7uzgpYxTrjX
@codeant-ai

codeant-ai Bot commented Sep 7, 2026 •

Copy link
Copy Markdown

🤖 CodeAnt AI — Review Status

Status Commit Started (UTC) Finished (UTC)
✅ Reviewed your PR d047344 Sep 07, 2026 · 07:23 07:25

@codeant-ai

codeant-ai Bot commented Sep 7, 2026

Copy link
Copy Markdown

Thanks for using CodeAnt! 🎉

We're free for open-source projects. if you're enjoying it, help us grow by sharing.

Share on X ·
Reddit ·
LinkedIn

@coderabbitai

coderabbitai Bot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 42 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Team

Run ID: 1b37ae57-2a61-47bf-a620-7bf36a02bf9f

📥 Commits

Reviewing files that changed from the base of the PR and between 00d6989 and 16414c7.

📒 Files selected for processing (1)
  • .github/workflows/release.yml

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codeant-ai codeant-ai Bot added the size:L This PR changes 100-499 lines, ignoring generated files label Sep 7, 2026
codescene-access[bot]

This comment was marked as outdated.

@amazon-q-developer amazon-q-developer Bot 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.

This PR successfully fixes the broken release workflow that was silently skipping all jobs. The replacement Rust binary release workflow is well-structured with good error handling and testability via workflow_dispatch.

Critical Issues Found:

  1. Missing jq on Windows runners will cause builds to fail
  2. Missing error handling for README.md/LICENSE files could cause silent failures

Both issues have actionable fixes provided. Once addressed, this workflow should successfully ship binary releases for all five target platforms.


You can now have the agent implement changes and create commits directly on your pull request's source branch. Simply comment with /q followed by your request in natural language to ask the agent to make changes.

staging="${bin}-${TARGET}"
mkdir -p "dist/${staging}"
cp "target/release/${bin}${ext}" "dist/${staging}/"
cp README.md LICENSE "dist/${staging}/"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Add error handling for missing README.md or LICENSE files. The cp command will fail if these files don't exist, causing the workflow to fail silently without a clear error message.

Suggested change
cp README.md LICENSE "dist/${staging}/"
if [ ! -f README.md ] || [ ! -f LICENSE ]; then
echo "::error::README.md or LICENSE not found"
exit 1
fi
cp README.md LICENSE "dist/${staging}/"

- name: Build
run: cargo build --release --locked

# The binary name is read from cargo metadata rather than hardcoded, so

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛑 Dependency Missing: The packaging script relies on jq to parse cargo metadata, but jq is not installed by default on GitHub's windows-latest runner. This will cause the Windows build to fail.

Suggested change
# The binary name is read from cargo metadata rather than hardcoded, so
# jq is required for parsing cargo metadata in the Package step below
- name: Install jq
if: runner.os == 'Windows'
run: choco install jq -y
# The binary name is read from cargo metadata rather than hardcoded, so

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

@codecov

codecov Bot commented Sep 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Addresses both Amazon Q review comments on this PR.

Accepted: an explicit check for README.md and LICENSE before packaging.
`set -euo pipefail` already aborted the step if either were missing, so
the failure was never silent -- but it surfaced as `cp: cannot stat ...`
buried in the log. Naming the two files a rename could plausibly break
makes the failure explain itself on the run summary.

Rejected, with evidence: the claim that `jq` is not installed on
windows-latest and would fail the Windows build. `jq` is preinstalled on
all three GitHub runner images, and this step is `shell: bash`, so one
script runs everywhere. This was verified rather than assumed before the
comment was filed -- dispatching this workflow on the branch built
x86_64-pc-windows-msvc successfully and produced a 679,896-byte
artifact, alongside the other four targets. Adding `choco install jq`
would install a second copy of a tool already on PATH and add ~30s to
every Windows release build.

The reasoning is recorded in the workflow itself rather than only in a
review thread, so the next reader does not re-litigate it.

actionlint clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016yFLnKN9zQA7uzgpYxTrjX

@codescene-access codescene-access Bot 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.

No application code in the PR — skipped Code Health checks.

See analysis details in CodeScene

Quality Gate Profile: Pay Down Tech Debt
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.

@rmems

rmems commented Sep 7, 2026

Copy link
Copy Markdown
Owner Author

Amazon Q — both findings already handled in the file, rejecting

release.yml:129 — missing README.md / LICENSE

The suggested guard is already there, immediately above the cp:

          for required in README.md LICENSE; do
            if [ ! -f "$required" ]; then
              echo "::error::${required} not found at the repository root; the release archive expects it"
              exit 1
            fi
          done

with a comment giving the same reasoning the finding does — set -e alone would abort, but with cp: cannot stat ... buried in the log instead of surfaced as a run annotation.

One correction: the finding says the workflow would "fail silently". It would not. The step runs set -euo pipefail, so a failed cp exits non-zero and fails the job loudly. The reason to name the files explicitly is legibility, not silence.

release.yml:86 — jq missing on windows-latest

Not correct, and this one was checked empirically rather than reasoned about. jq is preinstalled on all three GitHub runner images, Windows included, and the step is shell: bash so one script runs everywhere. The file records the evidence:

Verified rather than assumed: dispatching this workflow on the branch built x86_64-pc-windows-msvc successfully and produced a 679,896-byte artifact.

A Windows job that reached the packaging step, ran jq against cargo metadata, and emitted an artifact is direct disproof of "this will cause the Windows build to fail". Adding choco install jq would spend ~30s per Windows run installing something already present.

No changes made. Both suggestions were already implemented or refuted before review.

🤖 Claude Opus 5

@rmems rmems assigned rmems and Claude Sep 8, 2026
@rmems rmems added the CI/CD label Sep 8, 2026
@rmems
rmems merged commit f89ccff into main Sep 13, 2026
21 checks passed
@linear-code

linear-code Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

LIM-1130

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI/CD size:L This PR changes 100-499 lines, ignoring generated files

Projects

Development

Successfully merging this pull request may close these issues.

[Ship] Rust-only binary release — replace the never-running PyO3 workflow

2 participants