mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-07 16:11:46 +02:00
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Remote OpenCode and ACPX runs need a provider pack from the application build. > - Cloud now builds its application image from the standard production image. > - The provider pack was added only to the legacy cloud image target. > - The standard image therefore cannot supply the pack to downstream Cloud images. > - This pull request adds the pack to the production image and lets the cloud target inherit it. > - Remote runs can then use the pack that matches the application source commit. ## Linked Issues or Issue Description Refs #13827. Refs #14024. The standard production image does not include the remote provider pack. Downstream Cloud images inherit that omission. Remote OpenCode and ACPX runs fail with `runner_remote_provider_artifact_incompatible` and ask for `PAPERCLIP_RUNNER_REMOTE_PROVIDER_PACK_PATH`. ## What Changed - Build and copy the provider pack into the standard production image. - Set the pack path and check that an unprivileged user can read its artifacts and execute Node. - Let the legacy cloud target inherit the pack from production. - Add regression checks for production packaging and cloud inheritance. - Document stamped image behavior and the default pack path. ## Verification - The 12 focused Docker stamp and provider-pack reuse tests pass on commit `4a11f8d52aead55f85527c8e82c6d7f2644ce0da`. - The new packaging regression failed against the old Dockerfile and passed with the fix. - On the current commit, `pnpm build` and `pnpm -r typecheck` pass. All seven standard-image contract tests also pass. - The current-head CI build, typecheck, test, browser, and native Runner checks passed. The local full suite hit one chat-channel assertion failure; that exact test passed in isolation. The remaining local run was stopped after CI completed to avoid duplicating its full suite. An earlier run on the pre-rebase base had a heartbeat comment batching timeout; the external chat wait integration suite passed all 142 tests in isolation. - [The stamped preview image build passed](https://github.com/paperclipai/paperclip/actions/runs/36885002850/job/110446106393), including the production-stage provider pack build, copy, and unprivileged artifact readability/executable check. Publication, compatibility validation, and deployment of this exact commit to a staging QA instance passed. - Reproduced the exact missing-pack error on an existing staging image with Paperclip Runner, ACPX, and Claude in a remote Daytona computer. The legacy Claude adapter succeeds with the same account and computer. After deploying this commit, the same native task succeeded: it computed `5050` with a real remote shell command, wrote a proof file, read it back in a separate call, uploaded the file as a deliverable, and completed the task. The uploaded file contents and Done state persisted after a page reload. The run trace confirms Paperclip Runner, ACPX, and Claude. The first run took 2m 59s, including approximately 97s of remote artifact preparation. A second native run read the unchanged file from the prior run and completed successfully. Its startup took about 120s; this verifies repeated execution and file persistence, not fast provider-pack reuse. ## Risks - Stamped standard images now include the provider pack and its build cost. A pack build failure now fails the production image build. - Unstamped local builds still skip pack generation. Setting the path alone does not create a pack. - No database, provider authentication, or runner verification rules change. ## Model Used OpenAI Codex, GPT-6. The exact serving model identifier and context window are not exposed in this session. Capabilities used: repository inspection, code editing, shell verification, and browser testing. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [ ] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge Co-authored-by: Paperclip <noreply@paperclip.ing>
391 lines
17 KiB
Markdown
391 lines
17 KiB
Markdown
# Docker Quickstart
|
|
|
|
Run Paperclip in Docker without installing Node or pnpm locally.
|
|
|
|
All commands below assume you are in the **project root** (the directory containing `package.json`), not inside `docker/`.
|
|
|
|
## Building the image
|
|
|
|
```sh
|
|
docker build -t paperclip-local .
|
|
```
|
|
|
|
The Dockerfile installs common agent tools (`git`, `gh`, `curl`, `wget`, `ripgrep`, `python3`) and the Claude, Codex, and OpenCode CLIs.
|
|
|
|
Build arguments:
|
|
|
|
| Arg | Default | Purpose |
|
|
|-----|---------|---------|
|
|
| `USER_UID` | `1000` | UID for the container `node` user (match your host UID to avoid permission issues on bind mounts) |
|
|
| `USER_GID` | `1000` | GID for the container `node` group |
|
|
| `CLI_TOOLS_CACHE_EPOCH` | empty | Refresh the CLI-install layer; CI supplies the current ISO week |
|
|
| `PAPERCLIP_BUILD_VERSION` | empty | Runtime version when Git metadata is unavailable |
|
|
| `PAPERCLIP_BUILD_COMMIT` | empty | Source commit written into the server build stamp and runtime environment |
|
|
|
|
Changing the build version or commit preserves the CLI-install cache. The
|
|
tool layer refreshes when its weekly epoch, base image, installation command,
|
|
or earlier build inputs change. Local builds can set a new epoch explicitly
|
|
to refresh tools without clearing the entire build cache.
|
|
|
|
```sh
|
|
docker build -t paperclip-local \
|
|
--build-arg USER_UID=$(id -u) --build-arg USER_GID=$(id -g) .
|
|
```
|
|
|
|
## Standard images and downstream composition
|
|
|
|
The Docker workflow publishes the standard `production` target for Linux AMD64
|
|
and ARM64. Canonical master pushes also publish
|
|
`ghcr.io/paperclipai/paperclip:sha-<FULL_SHA>` and a GitHub/Sigstore attestation
|
|
for its immutable multi-platform digest. Downstream services can compose their
|
|
own images from this public base without rebuilding Core.
|
|
|
|
Stamped standard images include the build-owned remote provider pack at
|
|
`/opt/paperclip-runner/provider-pack` and set
|
|
`PAPERCLIP_RUNNER_REMOTE_PROVIDER_PACK_PATH` to that directory. Downstream
|
|
compositions and the explicit `cloud` target inherit both. The pack is required
|
|
on the controller for remote OpenCode and ACPX runs, even when the sandbox has
|
|
preinstalled providers; setting the environment variable alone cannot repair an
|
|
image that omits the pack. Rebuild and redeploy from a standard image containing
|
|
the pack if runs fail with `runner_remote_provider_artifact_incompatible`.
|
|
|
|
Local Docker builds must supply a full `PAPERCLIP_BUILD_COMMIT` to include a
|
|
qualified pack. Unstamped builds remain usable for local adapters and skip pack
|
|
generation. The pack stays root-owned and readable by remapped runtime UIDs.
|
|
|
|
The legacy recurring public `-cloud` publisher is retired. Master pushes,
|
|
release tags, and manual `Docker` dispatches no longer build that variant.
|
|
Existing `-cloud` tags and digests remain in the registry for rollback; their
|
|
release-channel aliases no longer advance. This change deletes no images,
|
|
cache tags, or migrator artifacts.
|
|
|
|
The `cloud` Dockerfile target remains available for explicit
|
|
[preview builds](preview-release-artifacts.md). Those requests still publish a
|
|
full-SHA `-cloud` tag when needed. They do not advance a release channel or
|
|
replace downstream private composition.
|
|
|
|
A published image alone does not prove source tests or migration compatibility.
|
|
Downstream deployment tooling must verify [source proof](cloud-build-readiness.md),
|
|
the standard image attestation, the exact-source migrator, and its own composed
|
|
image before rollout. Resolve immutable digests instead of deploying mutable tags.
|
|
|
|
## One-liner (build + run)
|
|
|
|
```sh
|
|
docker build -t paperclip-local . && \
|
|
docker run --name paperclip \
|
|
-p 3100:3100 \
|
|
-e HOST=0.0.0.0 \
|
|
-e PAPERCLIP_HOME=/paperclip \
|
|
-e BETTER_AUTH_SECRET=$(openssl rand -hex 32) \
|
|
-e PAPERCLIP_TOOL_ACTION_SIGNING_SECRET=$(openssl rand -hex 32) \
|
|
-v "$(pwd)/data/docker-paperclip:/paperclip" \
|
|
paperclip-local
|
|
```
|
|
|
|
Open: `http://localhost:3100`
|
|
|
|
Data persistence:
|
|
|
|
- Embedded PostgreSQL data
|
|
- uploaded assets
|
|
- local secrets key
|
|
- local agent workspace data
|
|
|
|
All persisted under your bind mount (`./data/docker-paperclip` in the example above).
|
|
|
|
## Docker Compose
|
|
|
|
### Quickstart (embedded SQLite)
|
|
|
|
Single container, no external database. Data persists via a bind mount.
|
|
|
|
```sh
|
|
BETTER_AUTH_SECRET=$(openssl rand -hex 32) \
|
|
PAPERCLIP_TOOL_ACTION_SIGNING_SECRET=$(openssl rand -hex 32) \
|
|
docker compose -f docker/docker-compose.quickstart.yml up --build
|
|
```
|
|
|
|
Defaults:
|
|
|
|
- host port: `3100`
|
|
- persistent data dir: `./data/docker-paperclip`
|
|
|
|
Optional overrides:
|
|
|
|
```sh
|
|
PAPERCLIP_PORT=3200 PAPERCLIP_DATA_DIR=../data/pc \
|
|
docker compose -f docker/docker-compose.quickstart.yml up --build
|
|
```
|
|
|
|
**Note:** `PAPERCLIP_DATA_DIR` is resolved relative to the compose file (`docker/`), so `../data/pc` maps to `data/pc` in the project root.
|
|
|
|
If you change host port or use a non-local domain, set `PAPERCLIP_PUBLIC_URL` to the external URL you will use in browser/auth flows.
|
|
|
|
Pass `OPENAI_API_KEY` and/or `ANTHROPIC_API_KEY` to enable local adapter runs.
|
|
|
|
### Full stack (with PostgreSQL)
|
|
|
|
Paperclip server + PostgreSQL 17. The database is health-checked before the server starts.
|
|
|
|
```sh
|
|
BETTER_AUTH_SECRET=$(openssl rand -hex 32) \
|
|
docker compose -f docker/docker-compose.yml up --build
|
|
```
|
|
|
|
PostgreSQL data persists in a named Docker volume (`pgdata`). Paperclip data persists in `paperclip-data`.
|
|
|
|
### Untrusted PR review
|
|
|
|
Isolated container for reviewing untrusted pull requests with Codex or Claude, without exposing your host machine. See `doc/UNTRUSTED-PR-REVIEW.md` for the full workflow.
|
|
|
|
```sh
|
|
docker compose -f docker/docker-compose.untrusted-review.yml build
|
|
docker compose -f docker/docker-compose.untrusted-review.yml run --rm --service-ports review
|
|
```
|
|
|
|
## Authenticated Compose (Single Public URL)
|
|
|
|
For authenticated deployments, set one canonical public URL and let Paperclip derive auth/callback defaults:
|
|
|
|
```yaml
|
|
services:
|
|
paperclip:
|
|
environment:
|
|
PAPERCLIP_DEPLOYMENT_MODE: authenticated
|
|
PAPERCLIP_DEPLOYMENT_EXPOSURE: private
|
|
PAPERCLIP_PUBLIC_URL: https://desk.koker.net
|
|
```
|
|
|
|
`PAPERCLIP_PUBLIC_URL` is used as the primary source for:
|
|
|
|
- auth public base URL
|
|
- Better Auth base URL defaults
|
|
- bootstrap invite URL defaults
|
|
- hostname allowlist defaults (hostname extracted from URL)
|
|
|
|
For fresh `authenticated/private` Docker or appliance-style installs, the first
|
|
admin can now be claimed entirely from the browser after sign-in. Open the
|
|
Paperclip URL, sign in or create an account, then choose `Claim this instance`
|
|
on the setup screen. This browser claim is disabled for `authenticated/public`;
|
|
public deployments should run the high-entropy CLI invite fallback instead:
|
|
|
|
```sh
|
|
pnpm paperclipai auth bootstrap-ceo
|
|
```
|
|
|
|
Granular overrides remain available if needed (`PAPERCLIP_AUTH_PUBLIC_BASE_URL`, `BETTER_AUTH_URL`, `BETTER_AUTH_TRUSTED_ORIGINS`, `PAPERCLIP_ALLOWED_HOSTNAMES`).
|
|
|
|
Set `PAPERCLIP_ALLOWED_HOSTNAMES` explicitly only when you need additional hostnames beyond the public URL host (for example Tailscale/LAN aliases or multiple private hostnames).
|
|
|
|
### Optional Vercel Connect credentials
|
|
|
|
Vercel Connect's backend integration is retained for controlled testing and
|
|
existing Vercel-backed connections, but its new-connection UI is currently
|
|
withheld from **Apps → Browse**. Setting
|
|
`PAPERCLIP_VERCEL_CONNECT_ENABLED=true` does not expose a customer-facing setup
|
|
entry. Native provider setup screens remain unchanged. Vercel-hosted deployments use the
|
|
workload OIDC token Vercel injects. Other hosted and self-hosted deployments
|
|
can provide `PAPERCLIP_VERCEL_CONNECT_ACCESS_TOKEN` as a deployment bootstrap
|
|
secret only when that token type is accepted by the live Connect API:
|
|
|
|
```yaml
|
|
services:
|
|
paperclip:
|
|
environment:
|
|
PAPERCLIP_VERCEL_CONNECT_ENABLED: "true"
|
|
PAPERCLIP_VERCEL_CONNECT_ACCESS_TOKEN: ${PAPERCLIP_VERCEL_CONNECT_ACCESS_TOKEN}
|
|
```
|
|
|
|
Do not save that access token in a company secret or connection config. It is
|
|
instance bootstrap authority for the operator-selected Vercel account. A token's
|
|
long expiry and broad Vercel scope do not prove Connect compatibility; validate
|
|
it with connector metadata before rollout. Workload OIDC takes precedence when
|
|
both authorities are present. Turning the feature flag off hides new
|
|
Vercel-backed setup; existing connections keep resolving while workload OIDC or
|
|
the bootstrap token remains available. Missing or invalid authority fails
|
|
closed. See the [Vercel Connect operator guide](./connections/VERCEL-CONNECT.md).
|
|
|
|
## Claude + Codex Local Adapters in Docker
|
|
|
|
The image pre-installs:
|
|
|
|
- `claude` (Anthropic Claude Code CLI)
|
|
- `codex` (OpenAI Codex CLI)
|
|
|
|
If you want local adapter runs inside the container, pass API keys when starting the container:
|
|
|
|
```sh
|
|
docker run --name paperclip \
|
|
-p 3100:3100 \
|
|
-e HOST=0.0.0.0 \
|
|
-e PAPERCLIP_HOME=/paperclip \
|
|
-e OPENAI_API_KEY=... \
|
|
-e ANTHROPIC_API_KEY=... \
|
|
-v "$(pwd)/data/docker-paperclip:/paperclip" \
|
|
paperclip-local
|
|
```
|
|
|
|
Notes:
|
|
|
|
- Without API keys, the app still runs normally.
|
|
- Adapter environment checks in Paperclip will surface missing auth/CLI prerequisites.
|
|
|
|
## Podman Quadlet (systemd)
|
|
|
|
The `docker/quadlet/` directory contains unit files to run Paperclip + PostgreSQL as systemd services via Podman Quadlet.
|
|
|
|
| File | Purpose |
|
|
|------|---------|
|
|
| `docker/quadlet/paperclip.pod` | Pod definition — groups containers into a shared network namespace |
|
|
| `docker/quadlet/paperclip.container` | Paperclip server — joins the pod, connects to Postgres at `127.0.0.1` |
|
|
| `docker/quadlet/paperclip-db.container` | PostgreSQL 17 — joins the pod, health-checked |
|
|
|
|
### Setup
|
|
|
|
1. Build the image (see above).
|
|
|
|
2. Copy quadlet files to your systemd directory:
|
|
|
|
```sh
|
|
# Rootless (recommended)
|
|
cp docker/quadlet/*.pod docker/quadlet/*.container \
|
|
~/.config/containers/systemd/
|
|
|
|
# Or rootful
|
|
sudo cp docker/quadlet/*.pod docker/quadlet/*.container \
|
|
/etc/containers/systemd/
|
|
```
|
|
|
|
3. Create a secrets env file (keep out of version control):
|
|
|
|
```sh
|
|
cat > ~/.config/containers/systemd/paperclip.env <<EOL
|
|
BETTER_AUTH_SECRET=$(openssl rand -hex 32)
|
|
PAPERCLIP_TOOL_ACTION_SIGNING_SECRET=$(openssl rand -hex 32)
|
|
POSTGRES_USER=paperclip
|
|
POSTGRES_PASSWORD=paperclip
|
|
POSTGRES_DB=paperclip
|
|
DATABASE_URL=postgres://paperclip:paperclip@127.0.0.1:5432/paperclip
|
|
# OPENAI_API_KEY=sk-...
|
|
# ANTHROPIC_API_KEY=sk-...
|
|
EOL
|
|
```
|
|
|
|
4. Create the data directory and start:
|
|
|
|
```sh
|
|
mkdir -p ~/.local/share/paperclip
|
|
systemctl --user daemon-reload
|
|
systemctl --user start paperclip-pod
|
|
```
|
|
|
|
### Quadlet management
|
|
|
|
```sh
|
|
journalctl --user -u paperclip -f # App logs
|
|
journalctl --user -u paperclip-db -f # DB logs
|
|
systemctl --user status paperclip-pod # Pod status
|
|
systemctl --user restart paperclip-pod # Restart all
|
|
systemctl --user stop paperclip-pod # Stop all
|
|
```
|
|
|
|
### Quadlet notes
|
|
|
|
- **First boot**: Unlike Docker Compose's `condition: service_healthy`, Quadlet's `After=` only waits for the DB unit to *start*, not for PostgreSQL to be ready. On a cold first boot you may see one or two restart attempts in `journalctl --user -u paperclip` while PostgreSQL initialises — this is expected and resolves automatically via `Restart=on-failure`.
|
|
- Containers in a pod share `localhost`, so Paperclip reaches Postgres at `127.0.0.1:5432`.
|
|
- PostgreSQL data persists in the `paperclip-pgdata` named volume.
|
|
- Paperclip data persists at `~/.local/share/paperclip`.
|
|
- For rootful quadlet deployment, remove `%h` prefixes and use absolute paths.
|
|
|
|
## Onboard Smoke Test (Ubuntu + npm only)
|
|
|
|
Use this when you want to mimic a fresh machine that only has Ubuntu + npm and verify:
|
|
|
|
- `npx paperclipai onboard --yes` completes
|
|
- the server binds to `0.0.0.0:3100` so host access works
|
|
- onboard/run banners and startup logs are visible in your terminal
|
|
|
|
Build + run:
|
|
|
|
```sh
|
|
./scripts/docker-onboard-smoke.sh
|
|
```
|
|
|
|
Open: `http://localhost:3131` (default smoke host port)
|
|
|
|
Useful overrides:
|
|
|
|
```sh
|
|
HOST_PORT=3200 PAPERCLIPAI_VERSION=latest ./scripts/docker-onboard-smoke.sh
|
|
PAPERCLIP_DEPLOYMENT_MODE=authenticated PAPERCLIP_DEPLOYMENT_EXPOSURE=private ./scripts/docker-onboard-smoke.sh
|
|
SMOKE_DETACH=true SMOKE_METADATA_FILE=/tmp/paperclip-smoke.env PAPERCLIPAI_VERSION=latest ./scripts/docker-onboard-smoke.sh
|
|
```
|
|
|
|
Notes:
|
|
|
|
- Persistent data is mounted at `./data/docker-onboard-smoke` by default.
|
|
- Container runtime user id defaults to your local `id -u` so the mounted data dir stays writable while avoiding root runtime.
|
|
- Smoke script defaults to `authenticated/private` mode so `HOST=0.0.0.0` can be exposed to the host.
|
|
- Smoke script defaults host port to `3131` to avoid conflicts with local Paperclip on `3100`.
|
|
- Smoke script also defaults `PAPERCLIP_PUBLIC_URL` to `http://localhost:<HOST_PORT>` so bootstrap invite URLs and auth callbacks use the reachable host port instead of the container's internal `3100`.
|
|
- In authenticated mode, the smoke script defaults `SMOKE_AUTO_BOOTSTRAP=true` and drives the real bootstrap path automatically: it signs up a real user, runs `paperclipai auth bootstrap-ceo` inside the container to mint a real bootstrap invite, accepts that invite over HTTP, and verifies board session access.
|
|
- Run the script in the foreground to watch the onboarding flow; stop with `Ctrl+C` after validation.
|
|
- Set `SMOKE_DETACH=true` to leave the container running for automation and optionally write shell-ready metadata to `SMOKE_METADATA_FILE`.
|
|
- Set `SMOKE_CONTAINER_NAME` to fix the container's name up front. Automation that has to collect diagnostics when the script *fails* needs a name it already knows, rather than one it can only read back out of a successful run. Defaults to the image name.
|
|
- The container's logs are dumped to `SMOKE_LOG_FILE` (default `$TMPDIR/<container name>.log`) before the script tears the container down, so a run that never became ready still leaves its logs behind.
|
|
- The image definition is in `docker/Dockerfile.onboard-smoke`.
|
|
|
|
## General Notes
|
|
|
|
- The `docker-entrypoint.sh` adjusts the container `node` user UID/GID at startup to match the values passed via `USER_UID`/`USER_GID`, avoiding permission issues on bind-mounted volumes.
|
|
- Paperclip data persists via Docker volumes/bind mounts (compose) or at `~/.local/share/paperclip` (quadlet).
|
|
|
|
## Native Runner build cache
|
|
|
|
The image compiles the native Runner in `runner-build`, before copying the
|
|
application source. A pinned `cargo-chef` generates a dependency recipe in
|
|
`runner-plan`. The separate `runner-deps` stage compiles that recipe with the
|
|
package-owned Rust compiler. Both the dependency build and the real binary use
|
|
the release profile and locked Cargo dependencies. The recipe stage never
|
|
modifies source in the checkout.
|
|
|
|
Changes to Rust source or embedded protocol inputs rebuild the real binary but
|
|
can reuse compiled dependencies when the recipe is unchanged. Dependency
|
|
manifests, the Cargo lockfile, target metadata, or compiler changes invalidate
|
|
the relevant cache. Ordinary server or UI changes can reuse the entire native
|
|
build through the existing registry cache (`mode=max`). Each platform gets its
|
|
own native build; no cross-architecture binary is reused. No additional GitHub
|
|
Actions cache is created. A cold build also installs the recipe generator and
|
|
compiles dependencies, so the savings apply after those layers are available.
|
|
|
|
Cloud builds import one registry cache: the first available full-SHA cache in
|
|
the current commit's ten-entry first-parent ancestry, with the legacy cache
|
|
as a final fallback. Each build still exports its own SHA cache with
|
|
`mode=max`. In fresh-builder checks, importing several historical manifests
|
|
missed native layers that a single matching manifest reused. The selector
|
|
inspects metadata after Docker login, stops at the first available cache, and
|
|
permits a cold build if no cache can be read.
|
|
|
|
The application build inherits that stage and still runs the normal server
|
|
build, including Cargo, binary staging, and generated-contract checks. Rust
|
|
input file times are normalized in both stages so fresh checkouts do not force
|
|
Cargo to rebuild unchanged source. Changes made by build scripts still reach
|
|
Cargo's normal validation. The final application copy excludes Cargo's target
|
|
directory as before. Cache misses only cost compilation time.
|
|
|
|
Pull requests that change the Dockerfile, Docker ignore rules, or Runner native
|
|
inputs also build the isolated `runner-build` target in `Docker Runner check`.
|
|
The check runs `bash scripts/check-docker-runner-cache.sh` against a disposable
|
|
copy of tracked source and the actual Docker ignore rules. It compiles a baseline
|
|
and exports a local cache, removes that builder, changes a Rust metadata constant,
|
|
and rebuilds on a fresh builder using only the exported cache. It requires a
|
|
cached dependency build, an unchanged dependency recipe, and changed metadata
|
|
from the real binary. It also verifies that a dependency declaration change
|
|
alters the recipe. The probe exports small metadata results instead of importing
|
|
a large test image into the Docker daemon. Temporary builders and cache files
|
|
are removed afterward. It catches missing embedded inputs before the post-merge
|
|
build. It uses a GitHub-hosted runner with read-only repository access and never
|
|
publishes images or registry caches. Allow up to 20 minutes for its cold build and
|
|
source rebuild.
|