Installation¶
pixi-sbom is a pixi extension: a standalone
executable named pixi-sbom. Pixi finds any pixi-<name> binary on PATH (or in its own global bin directory) and
runs it when you type pixi <name>. There is no plugin registration; installing the binary is the whole setup.
Methods¶
| Method | Command |
|---|---|
| pixi global (recommended) | pixi global install pixi-sbom |
| Prebuilt binary | Download pixi-sbom-<version>-<platform>.tar.gz (or .zip on Windows) from the releases page, which has every version, check its provenance (below), and put pixi-sbom on your PATH |
| PyPI | uvx pixi-sbom runs it without installing; uv tool install pixi-sbom or pip install pixi-sbom installs it. The wheels hold the same binary as the release archives (no Python code), for Linux (glibc and musl) x86_64/aarch64, macOS x86_64/arm64 and Windows x86_64, from 1.7.0 on |
| From source | pixi run build in a clone, then copy target/release/pixi-sbom to ~/.pixi/bin/ |
Check it is picked up:
pixi-sbom --help and pixi sbom --help are equivalent; the binary can be run directly without pixi.
Checking where a binary came from¶
Each release archive has a .sha256 and a signed .sigstore.json beside it. They answer different questions. The
checksum says the file did not change between the release page and your disk. It says nothing about who made the
file — anyone can publish a binary and a correct checksum of it.
The provenance attestation is the one worth running. It is a Sigstore-signed statement that this exact archive was built by this repository's release workflow, from a named commit, in a named workflow run. Releases carry one from 0.11.0 onward; earlier archives have a checksum and nothing else.
The PyPI wheels are attested the same way, from 1.7.0 on: gh attestation verify pixi_sbom-<version>-py3-none-<platform>.whl
--repo aesir-oss/pixi-sbom checks a downloaded wheel (pip download pixi-sbom --no-deps fetches one), and PyPI shows
its own publish attestation beside each file.
Loaded digest sha256:... for file://pixi-sbom-<version>-linux-64.tar.gz
Loaded 1 attestation from GitHub API
The following policy criteria will be enforced:
- Predicate type must match:................ https://slsa.dev/provenance/v1
- Source Repository Owner URI must match:... https://github.com/aesir-oss
- Source Repository URI must match:......... https://github.com/aesir-oss/pixi-sbom
✓ Verification succeeded!
That needs the GitHub CLI and reaches api.github.com to fetch the statement. On a
machine that cannot, pass the bundle that ships with the archive and the check runs entirely offline:
gh attestation verify pixi-sbom-<version>-linux-64.tar.gz \
--bundle pixi-sbom-<version>-linux-64.tar.gz.sigstore.json \
--repo aesir-oss/pixi-sbom
The strict form¶
--repo alone accepts an archive signed by any workflow in this repository, from any branch. To also require that
it came from the release workflow, run on the tag of the version you downloaded, on a GitHub-hosted runner:
gh attestation verify pixi-sbom-<version>-linux-64.tar.gz --repo aesir-oss/pixi-sbom \
--signer-workflow aesir-oss/pixi-sbom/.github/workflows/release-artifacts.yml \
--source-ref refs/tags/v<version> \
--deny-self-hosted-runners
Older releases were built, and signed, in the repositories pixi-sbom lived in before: 1.12.0 to 1.13.1 in
aesir-oss/pixi-ext-contrib (tagged pixi-sbom-v<version> there), up to 1.11.x in millsks/pixi-sbom. Their releases
here carry each archive's signed bundle (<archive>.sigstore.json, from 0.11.0 on), so verify against the bundle and
name the repository that built it. This checks the signature offline and does not need that repository to exist:
gh attestation verify pixi-sbom-v1.12.0-linux-64.tar.gz \
--bundle pixi-sbom-v1.12.0-linux-64.tar.gz.sigstore.json \
--repo aesir-oss/pixi-ext-contrib \
--signer-workflow aesir-oss/pixi-ext-contrib/.github/workflows/release-artifacts.yml \
--source-ref refs/tags/pixi-sbom-v1.12.0
For 1.11.x and older, use --repo millsks/pixi-sbom,
--signer-workflow millsks/pixi-sbom/.github/workflows/release-artifacts.yml and --source-ref refs/tags/v<version>.
| Flag | Refuses |
|---|---|
--signer-workflow |
An archive signed by any other workflow, such as a CI job that also builds binaries |
--source-ref |
One built from a branch, or from a different release's tag |
--deny-self-hosted-runners |
One built on a machine outside GitHub's hosted runners |
It combines with --bundle for the offline check. The strict form holds from 0.12.0 onward. The 0.11.0 archives
were signed by release.yml on refs/heads/main, before the build moved to the tag-triggered workflow (#218), so for
0.11.0 the plain --repo check is as far as verification goes.
The conda-forge package is deliberately not attested: the feedstock builds on conda-forge's infrastructure rather than ours, so the provenance there would be theirs to make. The binaries this project builds and hands out are the ones covered.
Not on crates.io any more. pixi-sbom is not published to crates.io after 1.13.0 (#106): cargo install pixi-sbom
and cargo binstall pixi-sbom stay at 1.13.0. Use pixi global install pixi-sbom, uv tool install pixi-sbom (or
pip), or a release archive.
Shell completion¶
pixi-sbom completes its flags, the values they take (--report <TAB> lists the report kinds,
--format <TAB> the formats) and file paths. Flags and values are listed alphabetically in every shell, so the
--fail-on-… gates sit together. Add the line for your shell to its startup file:
| Shell | Startup file | Line |
|---|---|---|
| bash | ~/.bashrc (~/.bash_profile on macOS) |
eval "$(PIXI_SBOM_COMPLETE=bash pixi-sbom)" |
| zsh | ~/.zshrc |
source <(PIXI_SBOM_COMPLETE=zsh pixi-sbom) |
| fish | ~/.config/fish/config.fish |
PIXI_SBOM_COMPLETE=fish pixi-sbom \| source |
| PowerShell | $PROFILE |
$env:PIXI_SBOM_COMPLETE = "powershell"; pixi-sbom \| Out-String \| Invoke-Expression; Remove-Item Env:\PIXI_SBOM_COMPLETE |
| elvish | ~/.config/elvish/rc.elv |
eval (E:PIXI_SBOM_COMPLETE=elvish pixi-sbom \| slurp) |
For bash, eval rather than source <(…): macOS's own bash is 3.2, where source reading a
process substitution gets nothing and completion silently stays off. The line asks the binary for
its registration each time a shell starts, so it always matches the
installed version; don't save its output to a file. Pressing Tab runs pixi-sbom briefly to ask
for suggestions. That run reads no lockfile, writes nothing and makes no network request, and
PIXI_SBOM_COMPLETE is set only for it, so every other run writes its SBOM as usual.
pixi sbom, with a space¶
pixi's own completion knows only pixi's built-in commands: it does not hand an extension's
arguments to the extension (#424 has the tests,
and prefix-dev/pixi#7225 asks pixi to). The
mechanism above is the one pixi would use to forward them, so if it does, pixi sbom completes
with no change here.
Until then, a short addition to your shell's startup file hands pixi sbom … to pixi-sbom's
completion. Load it after pixi's completion (pixi completion --shell <shell>) and pixi-sbom's
line above. Each one is run in that shell by CI on every change, next to pixi's own completion,
which keeps working.
# Complete `pixi sbom ...` with pixi-sbom's own completion. Load after pixi's and pixi-sbom's.
_pixi_with_sbom() {
if (( COMP_CWORD >= 2 )) && [[ ${COMP_WORDS[1]} == sbom ]]; then
local spec fn old=${#COMP_LINE}
spec=$(complete -p pixi-sbom 2>/dev/null) || return 0
fn=${spec##*-F }; fn=${fn%% *}
COMP_WORDS=(pixi-sbom "${COMP_WORDS[@]:2}"); (( COMP_CWORD-- ))
COMP_LINE="pixi-sbom ${COMP_LINE#*sbom }"; (( COMP_POINT -= old - ${#COMP_LINE} ))
"$fn" pixi-sbom "${COMP_WORDS[COMP_CWORD]}" "${COMP_WORDS[COMP_CWORD-1]}"
else
_pixi "$@"
fi
}
# -o nosort keeps pixi-sbom's order, and needs bash 4.4 or later (macOS's /bin/bash is 3.2).
if (( BASH_VERSINFO[0] > 4 || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] >= 4) )); then
complete -o bashdefault -o default -o nosort -F _pixi_with_sbom pixi
else
complete -o bashdefault -o default -F _pixi_with_sbom pixi
fi
# Complete `pixi sbom ...` with pixi-sbom's own completion. Replaces the usual
# `pixi completion --shell powershell | Out-String | Invoke-Expression` line, because a command
# has one native completer: pixi's is kept and called for everything that is not `pixi sbom`.
$s = (pixi completion --shell powershell | Out-String) -replace "Register-ArgumentCompleter -Native -CommandName 'pixi' -ScriptBlock", '$global:PixiCompleter ='
Invoke-Expression $s
Register-ArgumentCompleter -Native -CommandName pixi -ScriptBlock {
param($w, $ast, $pos)
$e = $ast.CommandElements
if ($e.Count -ge 2 -and $e[1].Extent.Text -eq 'sbom' -and $pos -gt $e[1].Extent.EndOffset) {
$o = $ast.Extent.StartOffset
$line = $ast.Extent.Text.Substring(0, [math]::Min($pos - $o, $ast.Extent.Text.Length)).PadRight($pos - $o)
$inner = 'pixi-sbom' + $line.Substring($e[1].Extent.EndOffset - $o)
(TabExpansion2 -inputScript $inner -cursorColumn $inner.Length).CompletionMatches
} else { & $global:PixiCompleter $w $ast $pos }
}
PowerShell allows one completer per command, so this replaces the usual pixi completion line
and calls pixi's completer for everything that is not pixi sbom. It edits the text of pixi's
script, and stops working if pixi changes that line.
Two limits: sbom is not added to the list pixi <TAB> offers, and pixi options placed before
sbom (pixi -v sbom …) are not handled.
Upgrading¶
pixi global update pixi-sbom follows the conda-forge feedstock, which tracks releases within a day or two; a
downloaded binary is replaced by the next one from the releases page. pixi sbom --version prints the running
version, and every release is listed in the changelog.
Next: run it, use it in CI, or see which format to pick.