Skip to content

Building SIPI from Source Code

SIPI's build system is Bazel (orchestrating every C/C++ target, the OCI Docker image, and the Rust e2e + smoke tests). Nix's role is reduced to provisioning a dev shell with bazelisk + the host tools rules_foreign_cc and the kakadu_archive repository_rule need on PATH.

All build commands are wrapped by recipes in the root justfile. Run just to see the full list. Every CI step invokes one of these recipes — there are no inline bazel ... calls in any workflow.

Quick start

nix develop                                    # bazelisk + host tools on PATH
just bazel-build                               # bazel build --stamp //src/cli:sipi
./bazel-bin/src/cli/sipi server --config config/sipi.localdev-config.lua

just run chains the two: it depends on bazel-build and starts sipi with the localdev config in one step.

For Bazel concepts, common commands, --config= flags, and querying the build graph, see Building with Bazel.

For dev-shell setup (Determinate Nix install, what the shell provides), see Nix dev shell.

Prerequisites

Kakadu (JPEG 2000)

SIPI uses Kakadu, a proprietary JPEG 2000 toolkit licensed separately. The archive is published as a release asset on the private dasch-swiss/dsp-ci-assets repo and is fetched at build time by a custom Bazel kakadu_archive repository_rule (bazel/kakadu.bzl) that shells out to gh release download.

gh auth login    # one-time; needs dasch-swiss org membership
just bazel-build # the repository_rule fetches Kakadu on first build

CI passes GH_TOKEN: ${{ secrets.DASCHBOT_PAT }} on every Bazel step so the repository_rule resolves there too. See Kakadu setup for details and the version-bump procedure.

Adobe ICC Color Profiles

SIPI uses Adobe ICC color profiles, which are downloaded automatically by the Bazel build. The user is responsible for reading and agreeing with Adobe's license conditions, which are specified in Color Profile EULA.pdf at the repo root.

Building a Docker image

The Docker image is built by Bazel rules_oci (//src:image). There is no Dockerfilesrc/BUILD.bazel is the single source of truth. A running Docker daemon is still required for docker load / docker push, but docker buildx is not used (multi-arch manifest assembly happens via crane index append on a coordinator job).

just bazel-docker-build-arm64  # or -amd64; loads daschswiss/sipi:latest
just bazel-test-smoke          # builds //src:image and probes the loaded image

Per-arch builds (used by CI)

just bazel-docker-build-amd64           # build + load amd64 as daschswiss/sipi:latest
just bazel-docker-build-arm64           # build + load arm64
just bazel-docker-extract-debug amd64   # surface sipi-amd64.debug for sentry-cli
just bazel-docker-push-amd64            # push as :v<version>-amd64 + :latest-amd64
just bazel-docker-push-arm64            # push as :v<version>-arm64 + :latest-arm64
just bazel-docker-publish-manifest      # crane index append → daschswiss/sipi:v<version>

Each per-arch CI runner builds + pushes only its matching architecture (target_compatible_with rejects cross-arch invocations). A coordinator job runs crane index append to stitch the two pushed digests into a multi-arch manifest at daschswiss/sipi:v<version>.

Cross-platform builds

The full test matrix runs on macOS-aarch64, linux-x86_64, and linux-aarch64. CI exercises every variant on every platform; a green CI run verifies macOS as well as Linux.

For Linux-target builds from a macOS host, the simplest path is OrbStack or any other lightweight Linux VM with the dev shell available inside it (nix develop from a shared workdir). Native cross-compilation via toolchains_llvm's sysroot machinery is on the post-launch roadmap (out of scope for the Y → Y+7 migration).

All just targets

Run just with no arguments to see the live list. Key target groups:

Target Description
bazel-build [*FLAGS] bazel build --stamp //src/cli:sipi (fastbuild; pass -c opt/--config=asan etc.)
bazel-test [*FLAGS] bazel test //test/unit/... //test/approval/... //test/e2e-rust/... (no coverage)
bazel-coverage [*FLAGS] Same target set, instrumented; emits combined lcov for Codecov
bazel-test-unit bazel test //test/unit/...
bazel-test-approval bazel test //test/approval:approvaltests
bazel-test-e2e [*FLAGS] All Rust e2e rust_test targets
bazel-test-smoke [*FLAGS] Docker smoke test (consumes Bazel-built image tarball)
bazel-build-sanitized [*FLAGS] bazel build --config=asan --config=ubsan //src/cli:sipi
bazel-build-fuzz [*FLAGS] libFuzzer harness (linux-x86_64 in CI, darwin-aarch64 local)
bazel-run-fuzz corpus duration [seed] Run libFuzzer harness against a corpus
bazel-docker-build-{amd64,arm64} Build + load per-arch image as daschswiss/sipi:latest
bazel-docker-push-{amd64,arm64} Push to daschswiss/sipi:{latest,v<version>}-${arch}
bazel-docker-publish-manifest crane index append → multi-arch manifest at daschswiss/sipi:v<version>
bazel-docker-extract-debug arch Build :sipi_debug_layout, surface sipi-<arch>.debug
run just bazel-build then run sipi with the dev config
valgrind just bazel-build then run sipi under Valgrind
fuzz-corpus-update Download CI fuzz corpus and merge into the seed corpus
shttps-context-check Advisory grep that enforces SIPI → shttps boundary
docs-build Build documentation site (mkdocs build)
docs-serve Serve documentation locally for preview

Documentation

just docs-build                # build documentation site
just docs-serve                # serve documentation locally for preview