Back to Devbox

Release the Devbox CLI

.agents/skills/release-devbox/SKILL.md

0.18.07.5 KB
Original Source

Release the Devbox CLI

scripts/release.ts owns every mechanical step and the order they run in. Your job is the judgment: read what's shipping, recommend a version, write the title and notes, and help the user pick how to release.

Two entry points, both of which walk the whole pipeline:

CommandWhat it does
devbox run draft-releaseBuilds the release, leaves it as a draft for review
devbox run publish-releaseBuilds and takes it live, or finishes an existing draft

Read-only helpers: devbox run release-changes and devbox run release-status.

1. Look at what would ship

bash
devbox run release-changes

This prints the commits since the last release grouped by kind (breaking / features / fixes / other), the recommended version, and whether flake.nix needs bumping. Read it before saying anything about the release.

The recommendation follows devbox's own history: it's pre-1.0, so breaking changes take a minor bump (0.17.5 โ†’ 0.18.0) and everything else takes a patch bump โ€” 0.17.4 shipped new features as a patch. Say what the script recommends and why, but sanity-check it against the actual diff. A release that removes whole subsystems deserves a minor bump even if nobody wrote feat!:.

2. Ask how they want to release

Present the three options and let the user choose โ€” don't assume:

  1. Draft first (devbox run draft-release) โ€” builds everything and stops at a draft. Right when the notes need review, or when the release is being coordinated with an announcement. This is the safe default.
  2. Publish directly (devbox run publish-release) โ€” same pipeline, then takes it live. Right for routine patch releases.
  3. Publish an existing draft (devbox run publish-release) โ€” if a draft is already sitting there, the command lists it and offers to finish it. Check with devbox run release-status first.

3. Write the title and notes

The script prompts for these in $EDITOR when a human drives it. When you're driving, write them yourself and pass them as flags โ€” that's the point of doing this through an agent.

Get the raw material from GitHub's generated changelog:

bash
gh api repos/jetify-com/devbox/releases/generate-notes \
  -f tag_name=<version> -f previous_tag_name=<prev> -f target_commitish=main --jq '.body'

That's a dump of PR titles. Rewrite it into user-facing notes in the house style that 0.17.4 established:

markdown
## What's Changed

### ๐Ÿ’ฅ Breaking Changes

* **What was removed** โ€” what users must do instead, by @author ([#1234](https://github.com/jetify-com/devbox/pull/1234))

### โœจ New Features

* **Short bold lead-in** โ€” what changed and why a user cares, by @author ([#1234](...))

### ๐Ÿ› Bug Fixes

* Plain one-liners for small fixes, by @author ([#1234](...)).
* **Group related fixes** โ€” combine several PRs into one bullet when they share
  a root cause ([#1234](...)) and ([#1235](...)), by @author.

### ๐Ÿงน Maintenance

* Dependency bumps, CI work, docs. Group aggressively; nobody reads this section
  line by line.

## New Contributors
* @newperson made their first contribution in https://github.com/jetify-com/devbox/pull/1234

**Full Changelog**: https://github.com/jetify-com/devbox/compare/<prev>...<version>

Rules that matter:

  • Lead with impact, not the commit subject. "Shells in paths with spaces now work" beats "quote the shellrc source guard".
  • Breaking changes go first and say what to do instead. For a release that removes features, this is the whole story โ€” say plainly what's gone and what replaces it, or that nothing does.
  • Only include sections with content. Drop the ones that are empty.
  • Keep attribution. Every bullet ends with by @author and its PR link.
  • Escape backticks as \`. goreleaser interpolates the body into the Discord announcement template and bare backticks break it.

For the title, default to the bare version (0.18.0). A short theme suffix is fine when the release has one: 0.18.0 โ€” Devbox goes fully local.

Show the user the title and full notes and get explicit sign-off before running anything. That's the last checkpoint before it goes live.

4. Run it

Save the notes to .context/release-notes-<version>.md, then:

bash
node scripts/release.ts --draft \
  --version 0.18.0 \
  --title "0.18.0" \
  --notes-file .context/release-notes-0.18.0.md \
  --yes

Swap --draft for --publish to go live. Notes on driving it as an agent:

  • --yes is required when you run it. Its confirmation prompts need a TTY, and your shell doesn't have one โ€” without --yes it stops with "this step needs an answer but stdin is not a terminal". Only pass it after the user has signed off; it's skipping their checkpoint, not adding one.
  • It blocks for ~15 minutes waiting on cli-release. Run it in the background and check back, rather than letting the call time out.
  • It's resumable. Every step is idempotent, so if it fails partway, fix the cause and re-run the same command โ€” it picks up where it left off rather than duplicating work.

If flake.nix needs bumping, the script stops and tells you to open a PR. That bump has to merge into main before the tag is pushed, otherwise the tagged commit ships the wrong version string. Do that, wait for the merge, then re-run.

Why the order is what it is

Don't work around these; they're why the script exists.

  • Draft before tag. goreleaser runs with use_existing_draft and attaches its artifacts to whatever draft it finds, keeping the body. With no draft it creates one whose body is a bare list of commit SHAs โ€” that's what shipped as the 0.17.5 release notes.
  • Publish after the build. docker-image-release fires on the release event and immediately downloads the release tarballs to bake into the image. Publishing before goreleaser uploads them fails the Docker build. That's exactly what happened on 0.17.3 and 0.17.5, both published from the GitHub UI, which creates the tag and publishes in one action.
  • Publish from local credentials, not CI. GitHub doesn't trigger workflows from events raised by GITHUB_TOKEN. That's why CI-created edge releases never trigger docker-image-release, and why publishing can't just be a CI step.

If something is stuck

devbox run release-status reports the tag, draft, asset count, cli-release result and the current flake.nix version in one shot.

SymptomCauseFix
cli-release never startedTag push didn't landRe-run the same command
cli-release failed in testsRed main (see below)Fix the test, re-run
Draft has 0 assetsgoreleaser didn't find the draftConfirm the draft exists, re-run
Published but installer serves the old versioncli-post-release failed or is still runninggh run list --workflow=cli-post-release.yml
Docker build failedPublished before assets uploadedRe-run docker-image-release via workflow_dispatch with the tag

Known breakage

Check these are still true before blaming the release itself:

  • main has been red since 2026-07-02. The macOS zig-hello-world example test fails (error running script "run_test"). build.zig uses the pre-Zig-0.12 API (.root_source_file = .{ .path = ... }) while devbox.lock pins zig 0.11.0. This blocks all releases โ€” cli-release gates on the test suite โ€” and has already killed seven consecutive weekly edge builds.
  • flake.nix drifts. It sat at 0.17.3 through both the 0.17.4 and 0.17.5 releases. Preflight catches this now.