Skip to content

CI Publishing

Run pixi-sandbox init, review the generated .github/workflows/publish-sandbox.yml, and commit it with pixi-sandbox.toml. The project-owned workflow installs pixi-sandbox from https://prefix.dev/archont561/archont561, computes the native matrix with plan, then calls pack, doctor --verify, and publish directly. No pixi-sandbox composite Action or reusable workflow is involved.

Regenerate after changing the init contract and validate the workflow with actionlint. Existing projects migrating from v0.3.x should replace root/setup/publish Action calls and reusable-workflow calls with the newly generated file; immutable old refs remain available for historical jobs.

Every real repository wants to change something about the generated workflow: a paths: allowlist so an unrelated change does not repack and force-push the transport, tighter permissions:, a concurrency: guard against two publishes racing, timeout-minutes: so a hung pack does not hold a runner for six hours, or a pinned pixi-version:/disabled cache on setup-pixi. Older guidance was to hand-edit the generated file for these and re-apply the same five edits after every init --force — with nothing confirming the re-applied edit still matched what the current generator would produce.

That ritual is retired. An optional [workflow] table in pixi-sandbox.toml carries this policy instead, and init renders it straight into the workflow — no hand edits, ever:

[workflow]
push_paths = ["pixi-sandbox.toml", "pixi.toml", "pixi.lock"]
permissions = true
pixi_version = "0.81.0"
setup_pixi_cache = true
[workflow.concurrency]
group = "publish-sandbox"
cancel_in_progress = false
[workflow.timeouts]
plan = 15
publish = 60

See the Configuration Reference for every field and its default. No [workflow] table at all keeps rendering the historical, unconditional template — nothing changes for an existing project until it opts in. Run pixi-sandbox init again any time policy changes (or let the scheduled upgrade job below do it): the output always matches exactly what the config describes, which is also what makes the upgrade job’s own regenerated pull requests trustworthy — they come from the same renderer this local init does, with no local edit left to disagree with it.

The workflow’s “Download released pixi-sandbox” step always downloads the standalone pixi-sandbox-<target> asset and its SHA256SUMS manifest from this project’s own repository (github.com/Archont561/pixi-sandbox/releases/download/v<version>) — never from the consumer repository the workflow runs in. The downloaded bytes are checksum-verified against SHA256SUMS before chmod +x/execution in every leg; a mismatch fails the job.

Regenerate if you generated this workflow with v0.4.3, v0.4.4, or v0.5.0. Those versions built the download URL from $GITHUB_REPOSITORY (the consumer’s own repository), which publishes no such release assets, so the step failed with curl: (22) (a 404) before anything was packed (issue #80). Re-run pixi-sandbox init --force with a patched CLI to regenerate the workflow against the fixed template; no other part of the generated file changes.

A new pixi-sandbox release leaves every consumer’s generated files silently stale until someone notices and re-runs init by hand. The generated workflow also contains an upgrade job that closes that gap without ever floating a production pin:

  • Weekly, on its own (schedule:), it bootstraps the standalone binary your workflow is currently pinned to (the same checksum-verified download the publish job uses), asks that exact binary to self-update to the latest release, and runs init --check with the updated binary against the exact paths, branch, and config this project was generated with.
  • On demand (workflow_dispatch, leaving the new upgrade input blank runs an ordinary publish exactly as before; naming an exact version there — 1.2.3 — makes the job self-update to that exact release instead of latest). This is also how you roll back: name an older released version to propose downgrading the generated files to it.

If init --check finds no drift, the job stops — nothing is written, nothing is proposed. If it finds drift, the updated binary re-runs init for real and the job prepares a pull request containing only the regenerated workflow, relock workflow, and launcher. It never pushes to main and never touches pixi-sandbox.toml — config is reviewed data, and a config whose schema the updated binary no longer understands is reported as a finding in that same init --check output for a human to act on, never edited automatically.

GitHub’s built-in GITHUB_TOKEN cannot create or update files under .github/workflows/, even when the job grants contents: write and pull-requests: write. To let the generated job open its PR automatically, add a consumer secret named PIXI_SANDBOX_UPGRADE_TOKEN containing a PAT or GitHub App token with permission to write workflow files. The upgrade job uses that token for checkout, the branch push, and gh pr create; it does not use it to push main.

The secret is optional. Without it, or when GitHub refuses the configured token, the scheduled job stays green and writes an actionable error and step summary naming the missing Workflows: write permission. It uploads pixi-sandbox-upgrade-artifacts, including a git apply-able patch for the exact regenerated files, so a maintainer can apply and review the upgrade manually. This fallback preserves exact version pins and does not bypass the human review gate. There is no separate upgrade workflow to configure: the generated publisher owns the versioned paths and keeps the upgrade and publish lanes mutually exclusive.

Review the diff like any other dependency bump — it is, after all, exactly the file init --force would produce locally with the same binary — then merge it. Because a github.token commit or merge starts no on: push workflow (the same trap the relock workflow documents), merging this PR does not by itself trigger a new publish. Dispatch it explicitly:

Terminal window
gh workflow run .github/workflows/publish-sandbox.yml --ref main

After that run, the matrix’s bootstrap step downloads the exact new release named in the merged PIXI_SANDBOX_VERSION, and pack/doctor --verify/publish/--self-bin all use that one verified binary — the published transport’s manifest names that exact version. No production step ever resolves latest; latest only ever decided what the proposed pull request contained.

This repository’s own publisher is unaffected by any of this: it builds pixi-sandbox from source and never installs or self-updates a released CLI, so there is nothing here for it to depend on.

Pipeline diagnostics and transport preservation

Section titled “Pipeline diagnostics and transport preservation”

Publish failures inside the generated workflow (install → pack → doctor → publish) do not require access to the live Actions log CDN to diagnose:

  • Durable diagnostics artifact: every publish leg records bounded execution logs into $RUNNER_TEMP/pixi-sandbox-logs/ (with --log-file for pack, doctor, and publish) and uploads them via actions/upload-artifact as publish-diagnostics-<platform> with 7-day retention.
  • Secondary GitHub failure surface: if any phase fails, the step formats an actionable failure report directly into $GITHUB_STEP_SUMMARY naming the failed phase, target branch, the uploaded diagnostic artifact name, and a bounded 50-line excerpt without leaking tokens or secrets.
  • Transport preservation: failures during install, pack, or doctor halt before publish is called. A rejected push or publish error never commits over or corrupts an existing healthy orphan transport snapshot on the remote branch, leaving the existing transport byte-identical and fetchable.
  • Separation of concerns: diagnostic logs remain in runner temporary storage and are uploaded as standard workflow artifacts; they are never committed into or published over the transport branch.