CI resource preparation
Execution trace
2. Prepare resources under the job owner
scripts/ci/prepare.mjs:prepareFile, scripts/ci/prepare.mjs:ensureK3dCluster
Static Checks restores the documentation pnpm store before installation. On cache misses, it saves the store after Pagefind succeeds. Baseline part 1 and browser part 1 only restore it. Keys include OS, architecture, Node.js, pnpm, and lockfile hash. Prefix matches can cross lockfiles. Frozen installation fetches missing packages.
First Agent Smoke and run-ci-lane use setup-ci-tools to restore yq and k3d
downloads outside PATH. On Linux, scripts/ci/setup-tools.sh selects the
checked-in platform checksum. scripts/ci/download-pinned.sh verifies installed
or cached bytes in private staging before atomic publication with mode 0755.
Missing or corrupt entries use pinned downloads; cache outages do not fail setup. Hits preserve cached bytes.
PostgreSQL, image and Kubernetes profiles share OS-, architecture- and
installer-source-keyed caches. Direct callers can reuse verified installed bytes.
For packaging and shared-runtime artifact consumers using run-ci-lane,
scripts/ci/setup-registry-transport.sh:verify_skopeo reuses working PATH tools;
packaging requires version 1.13.3. Missing, broken or mismatched tools trigger
APT update and installation from configured repositories with signature checks.
Version probes allow 10 seconds plus 5 seconds before forced termination;
each APT command allows 120 plus 10 seconds, three acquisition retries and
20-second HTTP/HTTPS timeouts. Sudo is non-interactive. Failed updates prevent
installation. APT output and failure status are preserved.
The final probe must pass.
scripts/ci/image-pull.ts:pullImage logs each failed pull attempt before deciding
whether to retry, including the final attempt. It reports elapsed time, timeout status and up to eight layer states from captured output tails.
The summary includes only known Docker phases and hexadecimal layer IDs;
it omits unmatched output and leading partial lines at the tail cutoff.
The First Agent smoke adapter forwards both captured streams to this parser.
Diagnostic emission is best effort and cannot replace the terminal pull error.
Docker suppresses numeric progress on non-terminal
output, so these states provide partial evidence, not the current transfer phase. If no progress matches, the
summary says so. Diagnostics change no timeouts, backoff, concurrency or digest verification, and establish neither registry health nor stall causes.
The PR advisory OpenShell job uses Ubuntu 24.04 for Landlock and selects packaged Google Chrome through OCC_TEST_BROWSER_EXECUTABLE. The OpenShell manifest lists this optional input so test children receive it. Before image builds, scripts/ci/setup-tools.sh:install_browser_dependencies checks a headless sandboxed renderer and logs its version; errors fail setup. The browser inherits only PATH, with private HOME and TMPDIR. Ubuntu’s existing AppArmor profile permits its user namespaces. Without an override, Playwright uses its default headless shell.
For lanes declaring both optional immutable image inputs,
scripts/ci/prepare.mjs:validateLaneInputsBeforeSideEffects rejects partial
Gateway/Agent pairs before creating state. prepareK3dRuntimeImages imports the
selected artifact or builds source when both are absent; selected pairs retain their immutable references.
Controller build selection is independent.
scripts/ci/prepare.mjs:imageBuildArgs restores read-only caches, including
k3d-otel. prepareLane primes selected sourceBuildFiles;
each OpenShell lane builds its stack. Tests inherit builder and epoch,
not cache credentials; installers still build source.
openshell-build-inputs resolves the integration-main controller base for
all/openshell builds, including Local Setup. openshell-image-cache requires
caller-source exports and successful cleanup before admission. Strict export
errors propagate after sibling settlement; standalone exports remain best effort.
Queued writers remain unordered by commit; mutable caches permit misses.
imageCacheObservation counts steps/imports for Gateway Routing, Slack Live,
Production TUI, k3d-otel and OpenShell, including alternate sources.
It deduplicates step IDs; import lines do not prove hits. Loads may reuse probes.
Image lanes, First Agent Smoke, Docker Model Runtime, Gateway Routing and
Production TUI fetch artifacts by repository/run/artifact IDs with actions: read.
Docker Model Runtime requires an artifact ID on producer success. Failure or
skip selects standalone builds. Local import cancels three minutes before the
lane deadline. Gateway Routing and default
Production TUI share that deadline. Full and focused runs wait for the producer
before controller preparation and runtime import. integration-main snapshots
the Gateway/Agent pair; consumers reject incomplete mode, pair or artifact ID
before setup. Focused explicit pairs skip the producer. Artifacts cannot accompany
Gateway inputs or TUI upgrade candidates; conflicts fail before allocation.
loadRuntimeArtifact verifies archive, source/run/attempt, platform, OCI
index/manifest/config digests and loaded image. Docker image IDs can equal config digests. buildRuntimeImages records ownership before import, rejects
alternate sources and import fallback, and sets aliases. Startup skips Buildx;
packaging/source builds retain it. Envoy and seccomp stay separate.
First Agent Smoke's prepareRuntimeArtifactLayout verifies compressed and
uncompressed layer digests; Buildx builds its trust layer from the verified
platform manifest digest.
Before tagging or import, the CLI records image ownership in private
RUNNER_TEMP state, excluded from uploads. Database tests use the limited
application role and fresh migrated databases; Agent revision worker tests use
migrated template copies (prepareFile({ template })). Failure and Kubernetes
database names satisfy admission guards.
Ordinary lanes select loopback k3d contexts and digest-pinned Kubernetes 1.35 K3s;
readiness rejects other server versions. OpenShell keeps its separate K3s pin.
External images use approved registry digests and the selected platform. All
images receive run-owned references at their imported platform manifest digests.
Preparation pins OCI or Docker manifests against kubelet garbage collection.
Every node must report the expected containerd digest and an exact CRI
digest-list entry with boolean pinned: true. Manual imports are excluded.
TUI upgrades require both candidate and baseline controller/runtime digests before allocation. Preparation imports all four, rejects candidates resolving to their baselines, and records source and imported identities. File preparation checks that selection and passes imported references to tests. Otherwise, preparation builds the controller and imports the selected runtime artifact or builds source. Failures enter cleanup, which checks k3d inventory before and after deletion, then matching Docker containers, networks and volumes. Invalid inventory or possible survivors retain state.
The retired CLI-only installed repository lane is absent from the manifest:
assertLane rejects it before allocation; its preparation below is unreachable.
scripts/ci/prepare.mjs:retainRuntimeProvenance compares platform-selected host
and CRI config digests. scripts/ci/runtime-provenance.ts:readImageCodexProvenance
reads bounded metadata; results and diagnostics retain only their preparation's evidence.
Missing metadata records unavailable; image policy is unchanged.
Extraction journals IDs before removal. Cleanup removes containers and anonymous volumes. Journal and cleanup errors fail preparation; unresolved resources retain receipts.
scripts/ci/prepare.mjs:ensurePostgresServer journals exclusive bootstrap-directory
and SQL-copy ownership. It refuses existing paths and retires failed directory
records. Its container-readable migrations/init-local.sql copy is selected by
OPENCLAW_CI_POSTGRES_INIT_SQL.
Docker assigns the loopback port. Preparation rejects missing, non-loopback and
55432 mappings before connecting. Failed discovery retains pending 0 and
prevents database preparation. Mixed fixture lanes start PostgreSQL before k3d.
Model lanes start k3d first and attach PostgreSQL before port discovery: Docker
can reassign automatic ports on attachment. Tests retain that attachment;
reverse-order cleanup removes PostgreSQL first.
scripts/ci/cleanup.ts:cleanupComposePostgres validates the directory against state.
Unconfirmed ownership preserves contents/state. Directory confirmation alone permits
empty-directory removal; confirmed SQL is removed after Compose teardown. Unexpected
contents or teardown failure retain state. Subnet exhaustion preserves the original
error and planned resource and prints owned cleanup.
prepareFile registers child identities.
prepareLane atomically publishes complete journals without replacement. Cleanup opens regular files,
verifying identity, lane and path. Tests require fresh registered children.
Worker death remains recoverable; mismatches, symlinks and failures preserve
journals and parent references.
For hosted repository QA, scripts/ci/qa-matrix-credentials.mjs reads the selected
reviewed registry file only when a repository scenario is selected. It copies
that metadata, the selected private App key and approved endpoint CIDRs into one
private input directory under RUNNER_TEMP. Both fixtures pass that directory to
the repository consumer and independent App-backed observer. The observer mints
short-lived tokens for the selected repository; its credentials stay outside
Agents. It checks run ownership before closing a pull request or deleting a branch
with an exact-SHA lease. Invalid file input or an isolated target mismatch fails
before the runner receives credential paths.
For hosted qa-matrix jobs, scripts/ci/lane-images.ts:imageInventory
records tagged or digest-pinned image references and their IDs in private
lane-images state before tests. If the inventory fails, preparation warns and
records no baseline. Workstation runs record none. During cleanup,
restoreLaneImages attempts to restore changed or missing baseline tags matching
the QA launcher patterns and remove newly observed matching tags. Cleanup does
not target other references. A successful final inventory reports added, changed,
and missing references. If the first cleanup inventory fails, cleanup warns and
skips both restoration and removal. If the final inventory fails, cleanup warns
and reports added, changed, and missing as null: the final store
state is unknown. Tag and inventory failures warn rather than fail the job.
A returned resource is removed from state; invalid state or ownership still
fails cleanup. These results do not prove
the runner accepted a shared-store commit.
Each Kubernetes fixture lane uses a server and worker with a shared local-path
mount. Preparation validates the server's route source to the worker Pod CIDR and
exports its /32 as OCC_TEST_KUBERNETES_PLUGIN_STATUS_PROXY_CIDRS. It verifies
the imported digest alias on both nodes. A DaemonSet keeps the image active on both
nodes until cluster cleanup and exposes no Service. The status suite schedules the
runtime on the worker to test proxy ingress across nodes.
scripts/ci/k3d-fixture-storage.ts:buildFixtureImage reports separate engine-version and
fixture-build stages. The engine probe uses the existing image-command
limit (30 seconds by default); the fixture build has a 30-minute cap to allow slow builds. These are execution limits, not
expected durations. Both commands respect the remaining lane deadline less
the test reserve, recalculated before each command. Without a valid lane deadline,
the finite command limits still apply. Unsupported timer values are rejected.
An exhausted budget reports the stage that could not start. On POSIX, timeouts
terminate the command's owned process group, including local descendants. The
shared scripts/ci/cleanup-command.ts supervisor anchors the group ID through
its last signal, including when preparation receives SIGINT cancellation; after
it exits, preparation sends no further group signals. Windows retains direct-child
termination. A failed build reports up to eight numbered FROM, RUN or COPY steps
and their reported completion states from the last 32,768 characters of each captured stream.
The step summary omits arguments, container output and arbitrary error text. This partial
summary identifies steps in the fixture Dockerfile; it does not establish the
current build phase or cause of a stall. The timeout keeps its concise stage error
and original cause. The owned image tag is recorded before
building and becomes ready only after success, so failed preparation remains
available to cleanup.
Preparation checks the storage controller before fixture setup, then restarts it after image registration and requires the replacement to become ready before publishing test inputs. Per-file cleanup repeats the health check while preserving database cleanup. Failures report the last 30 lines of storage-controller logs, Pod scheduling conditions, and node pressure/taints outside the sanitized test reporter; they do not include tenant workloads or complete Pod specifications.
The runtime image recipe pins compatible OpenClaw, Codex-plugin and Slack-plugin releases together with the Codex app-server version required by the plugin. Image startup smoke verifies fresh-home plugin loading, actual app-server initialization, and nested Codex home ownership for generated images and credential files before credentialed tests. Routing additionally requires the Gateway identity-scope contract; embedded continuity requires outgoing media to remain visible through history and artifact APIs across Pod replacement. Image builds alone prove none of these outcomes.
scripts/ci/prepare.mjs:reuseEngineImage asks BuildKit whether the restored cache
resolves to an image already in the engine; a cache miss builds it. In CI, the
probe and subsequent build share the remaining CI_RUNNER_DEADLINE_MS budget,
reserving three minutes for preparation and tests. Exhaustion reports Image build out of lane time, starts no further build, and leaves owned-tag removal to
job cleanup. Local runs and main's cache warmer have no lane deadline. The warmer
gives the OpenClaw build and final runtime assembly one shared 180-minute budget;
controller builds retain 20 minutes. Each export has a separate 20-minute limit
within the 210-minute job. These are cancellation limits, not expected durations;
cold Node compilation exceeded the former 20-minute build limit.
For dedicated Codex preparation (and installed repository-credential preparation, once that lane is available again), each owned node supplies its actual RuntimeDefault syscall profile from a restricted probe Pod. In the same command path, preparation first verifies that RuntimeDefault denies the pinned Codex Bubblewrap sandbox, then preserves that baseline, adds the version-pinned Bubblewrap calls, installs the resulting Localhost profile, verifies its hash and effective OCI policy, and requires actual sandbox execution through the profile. A missing profile must prevent container creation. The selected relative profile path is passed to the live fixture as runtime.codexSeccompProfile; only the dedicated Codex container uses it. Node profile files belong to the disposable cluster, and temporary probe resources are cleaned before model tests. The live fixture checks the effective configured model before paid model turns. OpenShell continues to own containment for its provider-created Harness.
scripts/ci/repository-credentials.ts:prepareRepositoryCredentials journals each
tag before building it. A valid immutable Docker image ID moves the tag to ready.
The Repository Credentials lane writes the service, client, and qualification IDs
and source commit to a mode-0600 receipt. Production Observability builds the
service for k3d import and verification on each node.
scripts/ci/prepare.mjs:prepareFileWithState checks the file's manifest
requirements before calling scripts/ci/repository-credentials.ts:prepareRepositoryCredentialsFile.
The helper extracts the delivered gh in a container with no network and a
read-only root. It checks the binary's pinned version before returning its path
for the test environment. Invalid image IDs, source commits, and binary versions
produce fixed errors without the command output. File cleanup removes the extracted
binary. Lane cleanup removes its journaled image tags.
The suite map owns fixed selection flags, required input names, and the resources each lane needs. Preparation consumes those descriptors instead of maintaining parallel lane lists. External model, ChatGPT and Slack credentials come only from the selected protected environment. Missing selected inputs fail rather than turning the lane into a skipped success.
Routing preparation installs pinned Gateway API, cert-manager v1.18.4, and Envoy Gateway v1.6.7 controllers and creates a private test CA. It imports immutable controller and runtime references so OCC can reach Envoy through its ClusterIP Service.
OpenShell preparation uses digest-pinned K3s v1.36.4 with runc, installs
matching kubectl and Agent Sandbox resources, obtains the OpenShell CLI and chart,
and imports gateway, sandbox, and supervisor images. During cluster creation,
k3d mounts the private admission file read-only on the server. The file exempts
only the selected RuntimeClass. It exempts no namespaces or users. The API must reject a violating ordinary Pod in a restricted
namespace and admit it with the selected class. A smoke Pod checks that the class
runs. The full lane checks filesystem, privilege, and binary-scoped Layer 7
network policy enforcement by the supervisor. The Harness and supervisor receive
neither SYS_PTRACE nor DAC_READ_SEARCH.
Logging preparation starts an owned OpenTelemetry Collector and passes JSONL
evidence to selected tests. The setup-test-docker action
replaces the runner's daemon with Docker 29.9.2 for Collector and Docker Model
Runtime jobs. This version supports fluentd-write-timeout. The action uses the
pinned official Docker setup action and connects the CLI, Compose, and Driver to
its socket at /var/run/docker.sock. Other jobs keep the runner daemon.
See Full Integration delivery status
for hosted proof and remaining coverage.
The OpenShell test owns management port-forwards throughout the test. Teardown stops the worker, drains forwards, then attempts app, database, namespace and directory cleanup even after an earlier failure. Forward shutdown allows two seconds after SIGTERM, then sends SIGKILL if necessary and waits two more seconds. Cleanup errors fail the test.
