Committed profiles and retained evidence
The original localci command runs one command in your
current checkout. It remains available. Use
localci validate when checks must run from committed source
and produce retained evidence. Use localci report to
publish that record later. Validation needs neither GitHub credentials
nor network access when the project's declared commands and Docker image
are already available.
Declare a small ordered profile in pyproject.toml:
[tool.localci.execution.profile]
backend = "host"
[[tool.localci.execution.profile.phases]]
name = "tests"
command = ["python", "-m", "unittest"]
timeout_seconds = 120
[[tool.localci.execution.profile.phases]]
name = "build"
command = ["make", "bundle"]
timeout_seconds = 120
[[tool.localci.execution.profile.artifacts]]
name = "bundle"
path = "source/dist/bundle.tar"
required = true
phase = "build"
[tool.localci.execution.reporting.contexts]
tests = "Local checks / tests"
build = "Local checks / bundle"Host commands inherit the operator's environment and explicitly
configured profile.settings.environment values. They run in
the disposable snapshot, not the working checkout. Arrange the required
tools and dependencies first. LocalCI does not install dependencies,
copy ignored files, or create worktrees.
For Docker, set backend = "docker" and declare an
immutable image:
[tool.localci.execution.runner]
image = "sha256:<64 lowercase hexadecimal characters>"
[tool.localci.execution.profile.settings.environment]
EXAMPLE_SETTING = "value"Replace the placeholder with a local image ID. Docker is optional,
and LocalCI never installs it or pulls/builds an image. It checks the
exact image identity, uses --pull=never,
--init, dropped capabilities and
no-new-privileges, mounts the writable snapshot at
/workspace, and defaults to offline networking. Each phase
can explicitly choose network = "bridge" instead of
"none". Containers receive only the declared environment
values. Host variables are not implicitly forwarded. Keep credentials
out of configuration and argv, since both belong to the retained
execution record.
Large profiles can use a separate committed file:
[tool.localci]
execution_config = "ci/execution.toml"That file contains [runner], [profile],
[[profile.phases]], [[profile.artifacts]], and
[reporting.contexts], without the
tool.localci.execution prefix.
localci validate --config ci/execution.toml selects it
directly. The selected commit supplies the configuration, even if the
working checkout has different edits.
localci validate HEAD
localci validate <commit> --evidence /path/to/new-empty-directory --json
localci validate HEAD --phase build--phase chooses an ordered subset. Repeat it for several
phases. --timeout changes deadlines for this invocation.
With exactly one selected phase, --command accepts a
repeated argv override. The resulting record contains the actual
selection, commands, deadlines, settings and overrides. Python callers
can additionally pass explicit environment overrides to
execution.validate. A successful isolated build is
distinguishable from complete validation.
Each required artifact is checked after its declared producing phase.
Paths are relative to the evidence directory; snapshot outputs begin
with source/. Artifact paths and symlinks must stay within
the evidence directory. Execution stops after a failed, timed-out, or
interrupted phase. Later phases are recorded as unexecuted, with no exit
code or log. The phase deadline covers output streaming, including a
silent process or a child that keeps stdout open.
The evidence directory retains source.tar,
source/, individual phase logs, and
result.json, with owner-only record and log permissions.
Records use format = "localci.execution" and
version = 1. They contain source commit, tree and archive
checksum; runner version and image; the effective profile; phase
outcomes, elapsed times and log checksums; and artifact checksums.
success means the requested profile completed, including
required artifacts. It does not imply deployment or authorize another
project's full validation gate.
Checksums detect changed retained bytes. They are not signatures against an operator rewriting the entire record. Project consumers decide which execution profile is sufficient for deployment or accepted-merge reuse.
To report retained results, supply the intended published commit:
localci report /path/to/evidence --sha <full-commit> --repository acme/widget
localci report /path/to/evidence --sha <full-commit> --comment --issue 123Before posting, LocalCI checks retained bytes against their checksums, reproduces the source archive from Git, verifies the selected committed configuration and actual overrides, and compares the requested repository/commit with the record. The caller resolves the intended published branch or PR head and supplies that SHA. LocalCI does not infer deployment or merge policy. Source/image/profile identities cannot be changed silently, and phases omitted from a partial run are reported as unexecuted failures. A reporting failure does not rewrite the execution record, so reporting can be retried without rerunning checks.
GitHub setup and credential behavior are the same as the original command. Validation exits 0 for requested-profile success, 1 for failed checks/artifacts, 2 for configuration/setup failures, and 130 for interruption. Reporting exits 3 when publication fails. JSON validation prints one result locator on stdout and sends execution output to stderr. Simple single-command users retain their existing command, dirty-checkout behavior, and exit-code guarantees.
One command and one bundle
A project does not need a phase for every check. Declare one phase that invokes its existing validation-and-build script, and one required artifact produced by that script. This uses one container, one log, and one status context. The script can print individual command timings and must exit nonzero if any required check fails or the build cannot complete. LocalCI does not interpret test skips; projects that require particular tests to execute must enforce that in the script.
Consuming evidence from Python
The public integration functions are execution.validate,
evidence.read, evidence.verify_source, and
execution_reporting.report. They use Path
arguments. validate returns the evidence directory even
when checks fail; inspect the verified record before accepting its
artifact. Configuration and handled Git errors raise
LocalCIError (from localci.errors). Filesystem
and source archive operations can also raise their underlying Python
exceptions. Reporting does not rerun validation.
For example, a project adapter can supply the selected commit to its build:
from pathlib import Path
from localci import evidence, execution
root = Path.cwd()
sha = selected_full_commit_sha # Selected by the project's adapter.
directory = execution.validate(
root,
sha,
overrides={"environment": {"BUILD_SHA": sha}},
)
record = evidence.verify_source(directory, root)
if not record["success"]:
raise RuntimeError(f"Validation failed; inspect {directory}")The environment override is forwarded to host or Docker execution and
retained in record["profile"]["overrides"]. It is public
evidence, so use it only for non-secret values. The CLI does not expose
environment overrides; use the Python API when a project needs them.
Before deploying, a consumer must additionally require its intended full commit, image, effective profile, reporting contexts, and allowed overrides. A successful subset or overridden command is not necessarily the consumer's required gate. Verify any identity embedded in the artifact too. LocalCI verifies what ran; the consumer decides which runs it accepts. Do not relabel a candidate's record or bundle with a merge commit's SHA. Keep deployment receipts separate from the execution record, and reuse verified artifact bytes for deployment retries.
evidence.read(directory) verifies the v1 record and
retained checksums. evidence.verify_source(directory, root)
additionally binds those bytes and the effective configuration to the
local Git history. The source commit must remain available locally.
Checksums do not authenticate the operator who created them.
Choosing the execution mode
The original localci command is appropriate when checks
need the checkout's Git metadata, existing virtual environment, or other
locally prepared files. It remains the small adoption path for projects
that already have one check command. Committed validation is an
additional choice, not a migration requirement.
validate extracts git archive: it has no
.git directory, ignored virtual environments, or populated
submodules. Archive attributes can exclude files, and Git LFS content is
not hydrated by LocalCI. Make the declared command work with that source
snapshot and prepared image, or keep using checkout execution. LocalCI
does not install Git, gh, test tools, or project
dependencies in an image. Tests that need Git should create their own
disposable fixtures, with Git already available in the declared
execution environment.