lc build
Build a containerized project's system-layer image, and commit it to the repository.
Build the project's system-layer image, and commit it. Containerized projects only: a project
containerizes by declaring a [tool.lightcone.image] table in pyproject.toml, and on a direct
project this verb just says so and exits.
Synopsis
lc build [OPTIONS]Idempotent: an image that is already built and committed is left alone.
What the image is
The image is the system layer only: the declared base (digest-pinned, or the default), the
declared apt packages, and the pinned Python interpreter. Your analysis environment is not in it
(recipes' Python packages come from the project's lock, synced into the container at run time) and
neither is lc itself. That is what makes "editing code never rebuilds the image" structural: no
project file enters the build context at all.
The declaration is a closed set of keys, hashed into the image's identity:
[tool.lightcone.image]
base = "docker.io/library/debian@sha256:..." # optional; default pinned by lc
apt-install = ["libfftw3-dev"] # optional
run-commands = ["curl -L ... | tar xz"] # optional, the bounded escape
env = { OMP_NUM_THREADS = "1" } # optionalThe archive is the store
lc build saves the built image into the repository, as .datalad/environments/<tag>/image, a
docker-archive committed through git-annex, so the exact bytes travel with the project: a clone
obtains them with a fetch, no registry and no credentials involved. Execution always pins the
image's content id, never a tag, so nothing can substitute a different image under the same name.
The archive records the architecture it was built for, and a host that can't execute that architecture is refused up front: build where the architecture matches the machines that will run recipes (on NERSC, a login node).
Requirements
- A clean tree: the image commit must not sweep your staged edits in, and the tag derives from the committed declaration.
- A build-capable runtime:
podman-hpc,podman, ordocker(detected in that order; nothing to configure).
lc materialize also builds as a preflight when the committed declaration has no image yet,
announcing it first; lc build exists so you can pay the minutes when you choose to.
Options
| Option | Default | Effect |
|---|---|---|
--json | off | Emit the result as JSON on stdout. |
The JSON result
{
"mode": "containerized",
"tag": "lc-env-1a2b3c4d5e6f7a8b",
"id": "sha256:...",
"archive": ".datalad/environments/lc-env-1a2b3c4d5e6f7a8b/image",
"action": "built"
}action is "built" when this invocation built and committed the image, "present" when it was
already there. On a direct project the result is just {"mode": "direct"}.
Examples
lc build # build + commit, or confirm it's already there
lc build --json # the machine-readable form