Skip to content

Repository layout

Three crates, two fixture trees, and one rule about GitHub Actions. This page is the map for someone who has just cloned the repository and wants to change something.

pixi-sandbox/
├── crates/
│ ├── pixi-sandbox-core/ the library: wire format, sharding, verification
│ ├── pixi-sandbox-git/ git behind a trait: real git + an in-memory mock
│ └── pixi-sandbox/ the CLI — and, under tests/fixtures/, the test fixtures
├── docs/ this site (Astro + Starlight → GitHub Pages), a bun workspace member
├── .github/ workflows plus verified release/setup/publish Actions
├── .pixi-sandbox.toml reviewed bundle/platform plan for release-driven publishing
├── .convoco/ why convoco is deliberately absent
├── package.json the bun workspace: docs plus repo-wide devDependencies
├── pixi.toml environments and every task a human, hook or CI job runs
└── Cargo.toml workspace: members, shared dependency versions
crate responsibility depends on
pixi-sandbox-core manifest.json (schema 1) and its validation, sharding (record_file, materialise, atomic join_parts), embedded helper-tool pins, publish-plan schema, verification incl. the ELF linkage check serde, sha2, thiserror, walkdir, toml
pixi-sandbox-git GitProtocol: publish, fetch_into, branch_exists, remote_size. ShellGit (real git, swappable Runner) and FakeGit (in-memory mock) thiserror
pixi-sandbox the CLI (pack, publish, restore, unpack, doctor, plan, tools) and the test fixtures core, git, clap, anyhow

The split is deliberate: core is the part that must be right (it decides what reaches a user’s disk), git is the part that touches a remote (the part you cannot unit-test against reality), and the CLI orchestrates verified staging plus external helper tools. All three are pure Rust, and the static release binaries are the portable bootstrap an airlock restores from.

Adding a crate is three steps: cargo new --lib crates/<name>, add it to members in the root Cargo.toml, and give it description plus license.workspace = true — cargo deny check fails on a crate without a licence, and it rejects a bare path dependency (crates that depend on a sibling must state version next to path). If the crate is part of the published package, add it to crates/pixi-sandbox/Cargo.toml as well; the conda package (cargo install) builds from the workspace, so a missing member shows up as a package that builds but does not run.

The fixtures: tests never point at this repository

Section titled “The fixtures: tests never point at this repository”
crates/pixi-sandbox/tests/fixtures/
├── demo-project/ a complete pixi project definition — the *input* to pack
└── transport/ a synthetic, already-packed payload — the *input* to doctor/publish/restore

This repository is the tool’s own development environment: its default environment contains pixi-pack, and its lockfile resolves hundreds of MiB. A test pointed at the repository root would therefore test the developer’s machine — passing where a fresh clone fails, slow, and blind to the bugs that only appear on a plain project. So:

  • demo-project/ has no pixi-pack/pixi-unpack dependency, and a test asserts that;
  • transport/ is a committed 15 KB payload with real digests, including a blob split into .partNNN parts, so the verification path runs without pixi, a packer or a network;
  • tests copy a fixture into a temp directory before writing to it;
  • a test that walks out of its crate from CARGO_MANIFEST_DIR fails the suite.

Measured bonus: the fixture project is packable without installing its environment — pixi-pack resolves from pixi.lock — so pack-integration tests need network but not a multi-GiB local install (485 blobs / 100.6 MiB / 2.2 s).

GitHub Actions: source-driven commands stay one line

Section titled “GitHub Actions: source-driven commands stay one line”

Source-driven commands in .github/workflows/ are pixi run <task>. If one needs more than one line, it becomes a task in pixi.toml — not inline bash. The release-driven publisher uses auditable composite actions in bash/pwsh with checksum verification:

  • the command CI runs is the command you run locally, so a red gate reproduces in one paste;
  • logic is diffable and shell-independent (the Windows runner defaults to pwsh);
  • the workflow reads like the list of gates it is.
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
- uses: prefix-dev/setup-pixi@ba3bb36eb2066252b2363392b7739741bb777659 # v0.8.1
with: { environments: default, cache: true }
- run: pixi run sandbox-pack . default "$TRANSPORT" linux-64 # pack + vendor + pinned tools
- run: pixi run sandbox-doctor "$TRANSPORT" # verify every blob, write nothing
- run: pixi run sandbox-publish "$BRANCH" origin "$TRANSPORT" # one orphan commit, force-pushed

The unified publish-sandbox.yml workflow (replaces publish-sandbox + publish-sandboxes) validates .pixi-sandbox.toml via plan --json, then for each native bundle/platform calls setup-pixi-sandbox to verify a release and publish-pixi-sandbox composite to pack/doctor/publish. All third-party workflow dependencies are full-SHA pins; release-label comments are informational only.

Paths come from the environment: pixi tasks expand $VAR but do not support ${VAR:-default}, so the local tasks carry literal defaults and the ci-* tasks read SANDBOX_PROJECT, SANDBOX_ENVS, SANDBOX_TRANSPORT, SANDBOX_PLATFORM, SANDBOX_BRANCH, SANDBOX_REMOTE, SANDBOX_SELF_BIN, SANDBOX_CARGO_VENDOR and SANDBOX_PROOF_OUT — all set in the workflow’s env: block.

.pixi/, target/, vendor/, .pixi-sandbox/, node_modules/, docs/dist/ and .sandbox-*. The payload lives on the sandbox branch; the crate sources are vendored on demand; a fixture that grows past a quarter of a MiB fails a test.