Conversation
One binary is both the interactive CLI and (with LINEAR_CLI_DAEMON=true) the daemon - macOS Apple Silicon, Linux x86_64, Windows x86_64. exqlite's NIF cross-compilation verified empirically (built and ran the linux_x86_64 target under podman/QEMU, confirmed it created a real SQLite db on disk) rather than assumed - see documents/phase-8-plan.adoc. macOS Intel intentionally not targeted.
## Changes - Wraps the already-built Linux x86_64 Burrito binary rather than a separate Elixir build stage - one build artifact, reused - Lives in oci/ (not the repo root) since we may want more than one variant later - ci/build_image.sh already searches oci/Containerfile as a fallback location, so no script changes were needed ## Tests - Built locally with ci/build_image.sh + podman (--platform linux/amd64, this dev machine is arm64) - Ran the built image with LINEAR_CLI_DAEMON=true, confirmed it stays up rather than crashing
## Changes - mix test + mix format --check-formatted, against a pinned OTP 29.0.3 / Elixir 1.20.3 toolchain - Wires up the existing (previously unwired) ci/conventional_commits.sh as its own job - Triggered on pull_request and workflow_call, so release.yaml can gate on it ## Tests - Validated YAML syntax (ruby -ryaml) - mix test (174 passed) and mix format --check-formatted run clean locally against the same toolchain pin
## Changes - release.yaml: on push to main, gated on the ci.yaml validate workflow, runs googleapis/release-please-action@v5 against .release-please-config.json/.release-please-manifest.json - When release-please creates a release, calls burrito-release.yaml with the new tag - burrito-release.yaml: builds all 3 Burrito targets in one job (erlef/setup-beam pinned to OTP 29.0.3/Elixir 1.20.3, mlugg/setup-zig pinned to 0.16.0), uploads the binaries to the GitHub release via gh release upload - burrito-release.yaml also builds the linux_x86_64 target again for the container job (parallel, not serialized - no artifact sharing needed) and publishes it to ghcr.io via the existing ci/build_image.sh + ci/publish.sh ## Tests - Validated YAML syntax (ruby -ryaml) for both files - Action versions/inputs (release-please-action, setup-beam, setup-zig, actions/checkout, actions/cache) verified against their real action.yml/tags via the GitHub API, not guessed - The actual release-please PR/GitHub release flow can only be verified by a real push to main once RELEASE_PLEASE_TOKEN is set up (confirmed already present as an org secret)
There was a problem hiding this comment.
Pull request overview
This PR implements “Phase 8” packaging/releasing/CI so the project can ship cross-platform standalone binaries (Burrito), publish a daemon container image to GHCR, and manage versions/releases via release-please, with CI validation gating releases.
Changes:
- Add Burrito release configuration (multi-target) and wire
release-pleaseversion tracking intoapp/mix.exs+ new repo-root release metadata files. - Add CI workflows: validation (
mix test, format, conventional commits) plus release orchestration and artifact publishing (binaries + container image). - Harden container build metadata by avoiding embedding raw git remote URLs into OCI image labels, and add a daemon container
Containerfilethat wraps the Burrito Linux binary.
Reviewed changes
Copilot reviewed 12 out of 13 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| oci/Containerfile | Defines the daemon container image by copying in the prebuilt Linux Burrito binary. |
| documents/phase-8-plan.adoc | Adds the Phase 8 design/verification write-up. |
| ci/build_image.sh | Prevents credential leakage by deriving the OCI URL label from parsed owner/repo instead of the raw remote URL. |
| app/mix.exs | Adds release-please version markers, Burrito release targets, and the Burrito dependency. |
| app/mix.lock | Locks Burrito and its transitive deps. |
| app/.gitignore | Ignores Burrito output directory (burrito_out/). |
| AGENTS.md | Adds Phase 8 plan to the agent plan index. |
| .version.txt | Introduces the version file tracked by release-please. |
| .release-please-manifest.json | Adds the release-please manifest mapping for the root package. |
| .release-please-config.json | Configures release-please (simple release type, changelog, extra-file mix.exs updating). |
| .github/workflows/ci.yaml | Adds CI validation workflow (tests, format, conventional commits). |
| .github/workflows/release.yaml | Adds release-please workflow gated on CI. |
| .github/workflows/burrito-release.yaml | Adds publishing workflow to build/upload Burrito targets and build/push GHCR image. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
pull_request's default checkout ref is GitHub's synthetic refs/pull/N/merge test-merge commit (subject "Merge <sha> into <sha>"), not the PR branch tip - that commit was failing conventional-commit validation on every PR, and neither rebasing the branch nor loosening the validator would fix it (GitHub regenerates that ref fresh on every run, regardless of branch history). Check out github.event.pull_request.head.sha instead, falling back to github.sha for non-PR triggers.
git init --bare's HEAD symref follows the runner's ambient init.defaultBranch config (falls back to git's legacy "master" if unset). The test only ever creates/pushes "main", so on a runner without init.defaultBranch=main set (e.g. GitHub Actions, unlike this machine), origin's HEAD dangled at a "master" that never exists, and git ls-remote --symref reported nothing - failing default_branch/0's test in CI while passing locally. Set origin's HEAD explicitly instead of relying on ambient config, matching what a real git host does when you configure a repo's default branch. Verified by reproducing the failure locally with GIT_CONFIG_GLOBAL=/dev/null before this fix, and confirming all 8 git_test.exs tests pass under those same config-free conditions after it.
- burrito-release.yaml: install Zig 0.16.0 in the `container` job too - it runs the same `mix release`/Burrito build as the `burrito` job, which already has this step; without it the build fails - build_image.sh: org.opencontainers.image.source now points to $image_url/tree/$revision instead of a string embedding $USER@$HOSTNAME - closes an info leak and satisfies the OCI spec's requirement that this label be a URL - build_image.sh: drop the conflicting org.opencontainers.image.version --label (set to the raw tag) - oci/Containerfile already sets it correctly from $APP_VERSION via the build-arg; the CLI-level label was silently overriding that with the wrong value
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
Phase 8: makes this thing actually releasable. Cross-platform standalone
binaries via Burrito (one binary is both the interactive CLI and, with
LINEAR_CLI_DAEMON=true, the daemon), a container image for the daemon,and
release-please-driven versioning - matching the conventions alreadyused by
linear-cli(the Ruby sibling) and the user's other FOSSprojects. Full design writeup and verification notes in
documents/phase-8-plan.adoc.Changes
docs: add the phase 8 plan doc (decisions, verification notes, sequencing)fix(ci): stop leaking git remote credentials into container image labels -ci/build_image.shwas embedding the raw (possibly credential-bearing)git remote get-url originoutput directly into theorg.opencontainers.image.urllabel; rebuilt from the already-parsed owner/repo path insteadfeat(release): track the version viarelease-please-.version.txt+.release-please-config.json/-manifest.json,app/mix.exs's version field wired as a generic extra-file viax-release-please-start-version/-endmarkersfeat(release): add the real Burrito multi-target release config - macOS Apple Silicon, Linux x86_64, Windows x86_64, one binary for both the CLI and the daemonfeat: addoci/Containerfilewrapping the Linux x86_64 Burrito binary (not a separate Elixir build stage) - lives inoci/, not the repo root, since there may be more than one variant laterci: add a validate workflow -mix test,mix format --check-formatted, and the existing (previously unwired)ci/conventional_commits.shci: add therelease-please+ Burrito/container publish pipeline - on push tomain, gated on the validate workflow,release-please-actionopens/merges release PRs; when it cuts a release, a second workflow builds every Burrito target plus the container image, attaches binaries to the GitHub release, and pushes the image toghcr.ioVerified, not assumed
0.16.0- its own README says0.15.2. Found by hitting the version-check error directly.exqlite's NIF (viaecto_sqlite3) survives cross-compilation. Built a real Burrito release targetinglinux_x86_64from this macOS arm64 host; the build log showed Burrito cross-compiling it for real (CC/LDinvocation, not a mismatched precompiled fetch:Going to recompile NIF for cross-build: exqlite -> x86_64-linux...Successfully re-built exqlite for x86_64-linux!). Ran the produced binary inside apodmancontainer (--platform linux/amd64, QEMU-emulated) withLINEAR_CLI_DAEMON=true- it booted, stayed alive, and created a real~/.linear_cli/oban.dbon disk.custom_erts:(used briefly while chasing a not-yet-catalogued local OTP patch version) silently skips Burrito's musl-runtime-embedding step for Linux targets - verified by readingdeps/burrito/lib/steps/fetch/fetch_musl.ex. Not used in the real pipeline, which pins a specific, catalogued OTP version (29.0.3) instead.googleapis/release-please-action,erlef/setup-beam,mlugg/setup-zig,actions/checkout,actions/cache) was checked against its realaction.yml/tags via the GitHub API, not guessed.Tests
mix test: 174 passed (1 doctest, 173 tests), against the pinned OTP 29.0.3 / Elixir 1.20.3 toolchain used in CImix format --check-formatted: cleanruby -ryaml).release-please-config.json/.release-please-manifest.jsonvalidated as JSONlinear_cliBurrito release for thelinux_x86_64target locally and ran itci/build_image.sh+ podman (--platform linux/amd64, since this dev machine is arm64) and ran it, confirmingLINEAR_CLI_DAEMON=trueboots correctly inside the actual imageci/build_image.shcorrectly discoversoci/Containerfilevia its existing search order, with no script changes neededrelease-pleasePR/GitHub-release flow itself and the full CI pipeline can only be verified by a real push tomain-RELEASE_PLEASE_TOKENis confirmed already present as an org secret, so this should Just Work once merged