This repository was archived by the owner on Jul 24, 2026. It is now read-only.
feat(nix): self-contained flake + nix flake check as CI - #87
Merged
schickling merged 2 commits intoJul 20, 2026
Conversation
convoy had no flake and no CI. This adds both, and they are the same thing:
`nix flake check` is the gate.
The flake is thin and self-contained — nixpkgs, flake-utils, and the two
sibling packages from their own flakes. It builds from `self`, so it packages
whatever revision it ships with rather than a pinned fetchFromGitHub.
Why a flake check and not a Node workflow: convoy depends on smalltalk and pty
via `file:../sibling`, so a plain `npm ci` in CI needs those repos checked out
next to convoy. Flake inputs solve that properly — the sibling packages come
from their own flakes, pinned in flake.lock, and the sibling node_modules links
are expressed declaratively as an npm-name -> store-path attrset.
Checks (all run a REAL built binary, not a source tree):
- help — `convoy --help`
- completions — generates bash/zsh/fish, asserts each is non-empty, that fish
still binds to `convoy`, and that each shell accepts its own
generated script (`-n`). This is where zsh actually gets
syntax-checked; it is often absent from a dev machine.
- typecheck — convoy's own `tsc --noEmit`, against the same sibling deps.
The vitest suite is deliberately not wired up: parts of it shell out to
`git worktree` and probe the host, which the sandbox cannot provide.
Completions are generated at build time from the just-built binary and
installed via installShellCompletion, so what ships can never drift from
src/command-table.ts.
The smalltalk input points at the branch of compoundingtech/smalltalk#104 (the
PR adding smalltalk's flake) because that flake does not exist on its main yet;
there is a TODO to repoint once it merges.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T1xLDkqUDCYUMMADdMqVgP
agent-session-id: 312caff5-3274-4d97-baa4-8ff06ab03fc5
agent-tool: Claude Code
agent-tool-version: 2.1.215
agent-model: claude-opus-4-8
agent-runtime-profile: /nix/store/acr8a3l2v366jgmwiq8xdrhgz1py0db5-coding-agent-runtime-profile/share/coding-agents/profile.json
agent-skills-manifest: /nix/store/sj1v5j91h8v8d1w9lca4040302lwrd6v-agent-skills-corpus/share/agent-skills/manifest.json
tooling-profile: dotfiles@unknown-dirty
The suite in src/completions.test.ts is what keeps the command table, argv dispatch, and the generated scripts from drifting — but nothing ran it automatically. It is sandbox-safe (it only spawns `convoy` and reads src/cli.ts), unlike the rest of the vitest suite, which shells out to `git worktree` and probes the host. bash, zsh, and fish are in the check's inputs so its per-shell syntax assertions actually run instead of skipping. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T1xLDkqUDCYUMMADdMqVgP agent-session-id: 312caff5-3274-4d97-baa4-8ff06ab03fc5 agent-tool: Claude Code agent-tool-version: 2.1.215 agent-model: claude-opus-4-8 agent-runtime-profile: /nix/store/acr8a3l2v366jgmwiq8xdrhgz1py0db5-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/sj1v5j91h8v8d1w9lca4040302lwrd6v-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@unknown-dirty
schickling
marked this pull request as ready for review
July 20, 2026 19:35
schickling
merged commit Jul 20, 2026
f90e2d1
into
schickling-assistant/2026-07-20-completions
1 check passed
schickling-assistant
added a commit
that referenced
this pull request
Jul 21, 2026
…ent (#87) (#91) * feat(nix): self-contained flake + `nix flake check` as CI convoy had no flake and no CI. This adds both, and they are the same thing: `nix flake check` is the gate. The flake is thin and self-contained — nixpkgs, flake-utils, and the two sibling packages from their own flakes. It builds from `self`, so it packages whatever revision it ships with rather than a pinned fetchFromGitHub. Why a flake check and not a Node workflow: convoy depends on smalltalk and pty via `file:../sibling`, so a plain `npm ci` in CI needs those repos checked out next to convoy. Flake inputs solve that properly — the sibling packages come from their own flakes, pinned in flake.lock, and the sibling node_modules links are expressed declaratively as an npm-name -> store-path attrset. Checks (all run a REAL built binary, not a source tree): - help — `convoy --help` - completions — generates bash/zsh/fish, asserts each is non-empty, that fish still binds to `convoy`, and that each shell accepts its own generated script (`-n`). This is where zsh actually gets syntax-checked; it is often absent from a dev machine. - typecheck — convoy's own `tsc --noEmit`, against the same sibling deps. The vitest suite is deliberately not wired up: parts of it shell out to `git worktree` and probe the host, which the sandbox cannot provide. Completions are generated at build time from the just-built binary and installed via installShellCompletion, so what ships can never drift from src/command-table.ts. The smalltalk input points at the branch of compoundingtech/smalltalk#104 (the PR adding smalltalk's flake) because that flake does not exist on its main yet; there is a TODO to repoint once it merges. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T1xLDkqUDCYUMMADdMqVgP agent-session-id: 312caff5-3274-4d97-baa4-8ff06ab03fc5 agent-tool: Claude Code agent-tool-version: 2.1.215 agent-model: claude-opus-4-8 agent-runtime-profile: /nix/store/acr8a3l2v366jgmwiq8xdrhgz1py0db5-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/sj1v5j91h8v8d1w9lca4040302lwrd6v-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@unknown-dirty * feat(nix): run the completions parity suite as a flake check The suite in src/completions.test.ts is what keeps the command table, argv dispatch, and the generated scripts from drifting — but nothing ran it automatically. It is sandbox-safe (it only spawns `convoy` and reads src/cli.ts), unlike the rest of the vitest suite, which shells out to `git worktree` and probes the host. bash, zsh, and fish are in the check's inputs so its per-shell syntax assertions actually run instead of skipping. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T1xLDkqUDCYUMMADdMqVgP agent-session-id: 312caff5-3274-4d97-baa4-8ff06ab03fc5 agent-tool: Claude Code agent-tool-version: 2.1.215 agent-model: claude-opus-4-8 agent-runtime-profile: /nix/store/acr8a3l2v366jgmwiq8xdrhgz1py0db5-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/sj1v5j91h8v8d1w9lca4040302lwrd6v-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@unknown-dirty * fix(nix): repoint the smalltalk input at main compoundingtech/smalltalk#104 has merged, so smalltalk's flake now exists on main and the PR-branch pin is no longer needed. Drops the TODO that asked for exactly this. The pty input already pointed at plain `github:compoundingtech/pty` (main) and carried no TODO, so it is left untouched. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T1xLDkqUDCYUMMADdMqVgP agent-session-id: 312caff5-3274-4d97-baa4-8ff06ab03fc5 agent-tool: Claude Code agent-tool-version: 2.1.215 agent-model: claude-opus-4-8 agent-runtime-profile: /nix/store/acr8a3l2v366jgmwiq8xdrhgz1py0db5-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/sj1v5j91h8v8d1w9lca4040302lwrd6v-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@unknown-dirty * fix(nix): lock smalltalk onto the post-#105 main tip The flake pinned smalltalk at ec24ac4, whose npmDepsHash was stale -- smalltalk main was broken standalone, so every `nix flake check` here failed on an input it does not own. smalltalk#105 fixed that hash on main; this moves the lock onto that merge commit (850266f). Only the smalltalk node moves. Verified locally: `nix build .#default` succeeds, `nix flake check` is green on all four checks (help, completions, parity, typecheck), and the built `convoy --help` and `convoy completions fish` both work. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T1xLDkqUDCYUMMADdMqVgP agent-session-id: 312caff5-3274-4d97-baa4-8ff06ab03fc5 agent-tool: Claude Code agent-tool-version: 2.1.215 agent-model: claude-opus-4-8 agent-runtime-profile: /nix/store/acr8a3l2v366jgmwiq8xdrhgz1py0db5-coding-agent-runtime-profile/share/coding-agents/profile.json agent-skills-manifest: /nix/store/sj1v5j91h8v8d1w9lca4040302lwrd6v-agent-skills-corpus/share/agent-skills/manifest.json tooling-profile: dotfiles@unknown-dirty --------- Co-authored-by: Claude Opus 4.8 <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 subscribe to this conversation on GitHub.
Already have an account?
Sign in.
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.
What
A self-contained
flake.nix, and convoy's first CI — which is the same thing:nix flake checkis the gate.The flake
Thin and self-contained. Inputs:
nixpkgs,flake-utils,ptyandsmalltalkfrom their own flakes (both withnixpkgsfollowed, andsmalltalk.inputs.ptyfollowed so there is exactly one pty in the closure). No first-party tooling deps — it builds standalone from a bare checkout.self, so it packages the revision it ships with rather than a pinnedfetchFromGitHub.package.json(0.2.0-ts.0) — a release bump needs no edit here.npm-name -> store-pathattrset, rendered intonode_modulesby a sharedlinkSiblingshelper, instead of ad-hocln -slines. The names are@compoundingtech/ptyand@compoundingtech/smalltalk, matchingpackage.json/package-lock.jsonafter 795b9be —coordis gone entirely.installShellCompletion, so what ships can never drift fromsrc/command-table.ts.meta.mainProgram = "convoy".# TODO(rust):markers on the three things a Rust rewrite deletes:npmDepsHash, the sibling-symlink block, and the strip-types bin shim.Why flake-check-as-CI, not a Node workflow
convoy depends on smalltalk and pty via
file:../sibling. A plainnpm ciin CI needs both repos checked out next to convoy — there is no clean way for a GitHub workflow to arrange that. Flake inputs solve it properly: the sibling packages come from their own flakes, pinned inflake.lock..github/workflows/nix.ymlis modeled on pty's existing one (PR + push to main,ubuntu-latest,DeterminateSystems/determinate-nix-action@v3) but runsnix flake checkrather thannix build, so the checks actually gate rather than only the package building.Checks — all against a real built binary
helpconvoy --helpruns from the installed binarycompletionsconvoy, and that each shell accepts its own generated script (bash -n/zsh -n/fish -n)typechecknpm run typecheck(tsc --noEmit), against the same sibling deps the package linksparitysrc/completions.test.ts— the suite that keeps the command table, argv dispatch, and the generated scripts from drifting. Nothing ran it automatically before thischecks.completionsis where zsh actually gets syntax-checked — it is often absent from a dev machine, so the unit test in #85 skips it locally. Here it is guaranteed.Gap, stated honestly
The full vitest suite is not wired up as a check — only the completions parity file. Parts of the rest shell out to
git worktreeand probe the host, which the Nix sandbox cannot provide, so forcing it in would mean either a red check or carving the suite up, neither of which belongs here. Sonix flake checkcovers build + help + completions + typecheck + the parity suite, and the remainingnpm testcoverage stays a local step. Worth revisiting by splitting the host-dependent tests behind a marker.Verification (observed, on x86_64-linux)
nix build .#default— succeeds.npmDepsHash = "sha256-RCsVWvgt0853fww5A89GDDd9OR2aexf23dAnn7EDq1M=", computed by letting the build fail on a placeholder and taking the reportedgot:value.nix flake check— passes:checks.help,checks.typecheck,checks.completions,checks.parityall green. The parity check's log shows 31 passed, with the bash/zsh/fish syntax assertions actually running rather than skipping../result/bin/convoy --help— prints the subcommand listing../result/bin/convoy --version—0.2.0-ts.0../result/bin/convoy completions fish— 186 lines; bash/zsh also emit and pass their-ncheck inside the sandbox.share/bash-completion/completions/,share/fish/vendor_completions.d/,share/zsh/site-functions/.nix flake checkwas only run for the host system; the other platforms are evaluated but not built (standardeachDefaultSystembehavior).