Validate and build in CI
The published Apizr 0.4.3 release provides apizr ci, a GitHub
composite Action, and canonical GitLab component source. Final 0.4.3 is
available from PyPI; the immutable v0.4.3 Action tag selects the released source. The GitLab component is source-only:
hosted runtime and catalog publication have not been performed. Qualification
installs the exact original candidate wheel.
One static command
With the exact Apizr core installed, any CI system can run:
apizr ci check --project apizr.toml --authorize-project-analysis --output-dir .apizr-ci
apizr ci build-rest --project apizr.toml --authorize-project-analysis --output-dir .apizr-ci-rest
apizr ci build-mcp --project apizr.toml --authorize-project-analysis --output-dir .apizr-ci-mcp
Each invocation validates the project, runs local doctor checks, assesses repository readiness and plans exposure using the existing compiler. Build modes additionally render the selected REST or MCP bundle. They never import the project, invoke its functions, install application dependencies, start a server or acquire Git sources. Checkout belongs to the consuming forge workflow.
The project must declare an exposure policy; its readiness and exposure policies
must allow the intended execution mode. This increment renders direct bundles.
For example, direct generation requires execution.modes: ["direct"] in the
readiness policy and execution.allowed: ["direct"] in the exposure policy.
A readiness refusal anywhere in the scanned repository remains a refusal even
when an exposure plan for a selected subset exists.
Exactly one analysis authority is required:
--authorize-project-analysiscreates an in-memory existing operator policy granting onlysource.analyzefor the exact canonical local project root.--operator-policy PATHuses that existing policy exactly, without rewriting it.
Neither option is implicit. Do not commit or rewrite the machine-local
.apizr/operator.json generated by apizr init for CI. No CI-specific permission
is introduced. No registry, signing, delivery, GitHub or GitLab credential is needed.
Results and refusal
The absent or empty output directory receives an atomically published tree. Its parent must exist; symlinked output roots/parents, traversal and nonempty trees are refused. Repeated runs need separate output roots or an explicit consumer cleanup of its own prior artifacts. Apizr never cleans a workspace.
| Artifact | Meaning |
|---|---|
result.json |
Strict canonical apizr.ci-result/v1, digests, state and artifact inventory |
doctor.json |
Portable local configuration diagnostics |
readiness.json |
Existing canonical repository readiness report, once available |
exposure-plan.json |
Existing canonical exposure plan, only when valid |
bundle/ |
Build modes: generated files and the existing REST/MCP bundle manifest |
Valid refusals retain available evidence. Invalid arguments or an unreadable project can fail before a tree can be produced. Exit codes are 0 success, 1 business refusal, 2 invalid configuration/invocation, 130 SIGINT cancellation; SIGTERM propagates normally. Diagnostics are fixed and do not echo input contents.
Portable artifacts omit timestamps, provider identity, absolute runner paths, operator-policy contents and environment values. Build bundles necessarily contain the explicitly selected source/resources: choose artifact visibility appropriately. The result inventory excludes its own JSON to avoid a recursive digest. Limits are 4,096 retained files including the result, 16 MiB per artifact and 64 MiB total; existing scan/graph/bundle limits still apply.
Use bounded source roots such as [scan] source_roots = ["src"], or explicitly
exclude generated output directories in your scan policy. With source_roots =
["."], a previous bundle inside the project is additional source input. CI does
not silently alter your scan policy. Identical source/configuration produces
identical bytes across repeat runs and both wrappers.
GitHub Actions
The root action.yml is a real composite Action. This example uses the
published stable v0.4.3 tag:
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: Alien6-Studio/outerspace-apizr@v0.4.3
id: apizr
with:
operation: check
project: apizr.toml
authorize-analysis: "true"
apizr-version: "0.4.3"
python-version: "3.14"
output-dir: .apizr-ci
Use the immutable release tag shared with the Python release, or pin its exact
commit SHA. There are no moving major/minor Action tags. The Action reference and
apizr-version are separate identities; choose matching releases deliberately.
The published v0.4.3 Action and component source default to 0.4.3.
The matching coordinated packages are available from PyPI.
| Input | Default / behavior |
|---|---|
operation |
check; also build-rest, build-mcp |
project |
apizr.toml, already checked out |
authorize-analysis |
false; explicit "true" opts in |
operator-policy |
Empty; mutually exclusive with the opt-in |
output-dir |
.apizr-ci; bounded workspace-relative path |
python-version |
3.14; supported minors 3.11, 3.12, 3.13, 3.14 |
apizr-version |
Exact core version only |
The Action uses immutable actions/setup-python v7.0.0 commit
5fda3b95a4ea91299a34e894583c3862153e4b97, creates an isolated environment and
installs exactly outerspace-apizr==<apizr-version> from normal PyPI. It installs
no plugins and strips token/environment values from the apizr ci subprocess.
There is no arbitrary arguments/script input and no GitHub API call.
Outputs result and artifacts are workspace-relative paths; apizr-version
is the verified installed version and state is success or refused when a
corresponding tree was produced. Consumers explicitly choose artifact upload and
retention. The Action itself performs neither checkout nor artifact upload.
For repository qualification only, paired wheel-path / wheel-sha256 inputs
select a regular local candidate wheel matching the Action's qualification version.
The v0.4.3 Action accepts exactly 0.4.3 for qualification; historical Action tags
retain their original version policies. The Action checks the SHA-256, embedded core
distribution/version and installed CLI version, using a snapshot of the original
verified bytes. URLs, symlinks and mismatched identities are rejected. This is not
the normal public installation route.
GitLab component source
templates/apizr.yml is the canonical, self-contained component source. It is
implemented and structurally validated, but not published on GitLab or in
the Component Catalog. No hosted GitLab pipeline has qualified it because no
GitLab component project is authorized. GitHub source is not a GitLab component
registry. Creating a project, mirror, token or catalog release is separate work.
This is syntax only, with a placeholder project on the consumer's GitLab instance; it is not a live component URL:
include:
- component: $CI_SERVER_FQDN/<alien6-component-project>/apizr@0.4.3
inputs:
job-name: apizr-check
stage: test
operation: check
project: apizr.toml
authorize-analysis: true
apizr-version: "0.4.3"
output-dir: .apizr-ci
The source targets GitLab 18.7+. It uses a spec:inputs header separated by
---, typed boolean authorization and operation options, with bounded regexes
for job name, stage, version, image and artifact directory. spec:component
was audited (generally available in 18.7); this component does not require that
optional context or infer a PyPI version from a component reference.
The generated job installs the exact core from normal PyPI and runs the same
apizr ci command. Input values enter variables with expand: false and are
passed as quoted arguments. The YAML needs no helper from the component project,
no third-party component, plugin, Docker installation, Node installation or API.
Its default image is the previously reviewed Linux amd64 Python 3.14.7 image:
python@sha256:51dafde81dbdb6ebde285137a295cf18a47ca95234fe388a343719cb97305b3d
An explicit python-image override must also be digest-pinned and contain Python
3.11–3.14 plus venv. Accepting an override does not qualify that image or another
architecture. job-name starts with apizr-; multiple jobs can use distinct names
and .apizr-ci-<suffix> output directories. The consumer supplies an existing stage.
Native artifacts archive only the explicit .apizr-ci or .apizr-ci-<suffix>
tree on success, for seven days. A failed invocation must not upload pre-existing
content from an output conflict. Refusal evidence remains local to the job;
there is no automatic upload on failure.
Qualification references
The Forge integrations workflow exercises the real local composite (uses: ./)
on Linux Python 3.11–3.14 with original coordinated wheel bytes. Installed-core
proofs outside checkout compare API, CLI and Action output bytes. Tests also cover
refusals, cancellation, portable results, bounds and shell-injection sentinels.
GitLab qualification validates both YAML documents against the vendored official
schema at commit 4dc642ade3597f7a8ae8d93091cc49b36f74a08d (SHA-256
ca545816e585b0b2e17cb89a8782d1e93260d215c28c8ebd1427fb19e8fcea8b), checks
input interpolation and executes the shell argument construction for parity.
This is structural/source qualification, not a hosted GitLab runtime claim.
Official contracts: GitHub composite metadata, GitHub input security, GitLab components, GitLab inputs and GitLab YAML.