Dev.to · 6 min read

Your CI should cut the release, not your laptop

Your CI should cut the release, not your laptop

This one covers November 3 to 10. The week was about one question: can an AI agent on a throwaway cloud machine ship the way I do from my desk? Most of the answer landed in my workflow kit. The rest went into a Windows bug class in a CLI I maintain, and into a new private tool that designs shirts with exactly one language-model call. A cloud session hit three walls on the way to a release workkit is my workflow kit for Claude Code: GitHub issues as the work list, labels as the pipeline, hooks that enforce the rules. On my Mac, "ship" means a work commit, a release commit, a push to main, green CI, then a GitHub release. Then I ran the same loop in a Claude Code cloud session, and it stopped halfway. It hit three walls: The branch. The session is pinned to its own claude/ branch and may not push anywhere else. So the commits never reached main, and CI, which runs on pull requests and on pushes to main, never started. GraphQL. The session's GitHub proxy answered REST calls but refused a GraphQL check with a 403. Releases. The proxy refuses every release write. I probed a tag push later: also a 403. The first two were plumbing. Version 0.91 ships cloud work through one pull request carrying the work commit and the release commit, merged with a merge commit, so the CHANGELOG's commit links still resolve on main. The kit's gh shim already answered issue commands over REST in the cloud; now it answers gh pr and gh release view the same way. The third wall changed the design. My first answer was a queue: a cloud ship leaves its release waiting, and the next ship from a machine cuts every version that is missing one. It worked, but it meant two paths, and a release that waited on me opening a laptop. So in 0.92, no machine makes the release. CI does. Each repo's checks.yml is now a short caller the kit owns, pointing at one reusable workflow in the kit. Its release job runs on a push to the default branch. It reads every commit the push brought, finds each chore(release): x.y.z subject with no release yet, builds the notes from the CHANGELOG at that commit with the old cut's own code, and creates the vx.y.z release there. Because it reads the whole pushed range, a pull request merge whose second parent holds the release commit counts too. workkit ship cut now only waits for the release to appear. That is a read, and the cloud allows reads. One detail I did not see coming: a release created with the Actions token starts no other workflow. So after it creates one, the job dispatches every workflow in the repo that listens for releases and accepts a manual run, and it warns about any that does not. Why it matters: a laptop and a cloud agent now release through the same door, and neither one creates the release anymore. One more piece made cloud runs honest. A suite can declare the commands it needs with needs('hugo', 'ffmpeg'). In a cloud session missing one, the suite is skipped with a line naming the tool; anywhere else, it fails. workkit tools prints the list, so a cloud setup knows what to install. Quoting is a loop you never win node-power-user (npu) is my npm wrapper; it routes installs through Socket Firewall. It had two Windows bugs this week, and the second taught me something. The first was a hang. In Git Bash's terminal, the command npu ran through the firewall got a stdin pipe that never closed, so it waited forever. Now it gets npu's stdin only when that is a terminal or a redirected file. The second was arguments. Version 2.3.1 fixed a path with a backslash, tilde or space by double-quoting it for cmd.exe instead of single-quoting it. About five hours later I threw that approach out. Any quoting scheme is a guess about how cmd.exe will read %VAR%, &, |, and inner quotes, and every guess has an edge. Version 2.3.2 stops building a shell string at all. npu spawns the binary with an argument array, so each argument arrives exactly as typed. The same class was hiding in npu sync, which commits and pushes for you. It ran git commit as one shell string, so a commit message with a quote, a $, a backtick or a & could run a command. Version 2.3.3 hands git an argument list instead. workkit got the same kind of fix on Windows the same week. Every program its Node code starts now goes through one launcher: gh through the shim, bash through Git Bash and never WSL, no argument globbing. Why it matters: escaping fixes one input. Not having a shell in the path fixes the class. The model picks, the scripts finish The private story this week is a new design engine for print-on-demand shirts. It has no public page yet, so no link. It landed as one commit of 96 files. It runs as a short pipeline: research a keyword, plan a collection, generate the artwork, finish the print files, build a review gallery. Exactly one language-model call happens, in the plan step. It picks the subjects, a style, a brand name and all the listing copy in one reply, which is checked before anything is saved: every product exists, the copy fits its limits, the keyword came from the research and is not a trademark hit. A bad reply gets one retry with the errors attached; a second stops the run with every error listed. Then an image model draws one picture per design, with no text in it. Everything after that is deterministic. The finish step strips the background, snaps the colors to a small flat palette, traces the result to SVG, and composes the print file on a page sized to the product's canvas. Any words on a shirt are typeset by a template, never drawn by the model, so the spelling is exact. A trademark register the check cannot reach reads unchecked, never clear. Uploads stay by hand. Why it matters: the model does the part that needs taste. The scripts do the part that has to be right every time, and they can be tested like any other code. Small but satisfying Deploys that carry only the diff. omega 0.57.0 shipped. Its deploy snapshot is now parented on the deploy ref's remote tip and pushed without force, so a deploy push carries only what changed since the last one. A copy button with no JavaScript. The same release makes any element with data-omega-copy a copy control, and its llms.txt is now an llmstxt.org-style brand summary. Next week I want to watch a cloud session take a real release all the way through the new CI job. If you let an agent ship software, who creates the release in your setup: the agent, a person, or CI? Comments are open.

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More AI & Machine Learning News