5.2 KiB
Build networking, cache and CI
Build networking and cache
The runtime uses an official Python 3.14 Alpine 3.24 image pinned by digest,
locked Python dependencies, and font-noto-cjk for Chinese rendering. APK
repository signatures and offline wheel SHA256 checks remain enabled.
Production uses scripts/build-verify.sh; CI uses scripts/ci-compose-verify.py.
Both build the same Dockerfile with BuildKit and docker compose build --pull.
ALPINE_MIRROR accepts a mirror host (without scheme or path), defaulting to
dl-cdn.alpinelinux.org. For mainland-China builds:
. ./deploy/build-cn.env
./scripts/build-verify.sh
CI uses this profile; the Gitea ALPINE_MIRROR repository variable can override
it. APK upgrades available packages during a fresh dependency-layer build.
Use docker compose build --pull --no-cache to refresh OS packages explicitly.
Layer order is pinned base → APK/fonts → requirements → Python dependencies
→ application source → metadata. BuildKit caches pip downloads separately for
musl and architecture. Optional build-cache/wheels/ must contain compatible
musllinux wheels and their SHA256 manifest; empty-cache builds work normally.
Do not reuse Debian/manylinux wheels on Alpine. Caches are disposable builder
resources and are not runtime mounts or dependencies.
CI with a host Docker socket
Run python scripts/ci-compose-verify.py after loading the build mirror profile.
The script derives a temporary Compose document from production settings, with
an absolute client-side build context, a UUID project name, a unique CI image
tag, and project-scoped data / config named volumes. It removes production
bind mounts, the fixed container name, host ports, and the production .env
reference. A fresh test token, mock driver, and disabled device writes are
explicitly configured. Docker initializes empty named volumes from the image's
owned /data and /config directories; the service remains UID/GID 10333.
Build contexts are sent by the Docker client, so the job workspace can remain
inside the job container. Runtime storage is managed by the daemon; no matching
NAS host /workspace directory is needed. HTTP readiness and the unmodified
nine-endpoint smoke script run inside the service with compose exec -T.
Restart is followed by another readiness check and the same smoke test.
A finally block removes the test containers, network, volumes, and unique
image tag on success or failure. The workflow records the private temporary
Compose path in GITHUB_ENV and repeats cleanup in an always() step, covering
normal cancellation/interruption. Failed cleanup retains the manifest so the
fallback can retry. Abrupt host/daemon loss or a forcibly terminated entire job
can prevent both cleanup paths; no CI script can guarantee cleanup in that case.
No global prune or production resource removal is performed. Keep builder
caches: only the run-specific image tag is removed.
CI Job pip indexes versus Docker build indexes
The Job bootstrap runs python scripts/ci-install.py before Ruff/mypy/tests.
It needs only the Python standard library, so it runs before PyYAML or tooling
is installed. CI_PIP_INDEX_URL and CI_PIP_FALLBACK_INDEX_URL are optional
Gitea repository variables. Defaults are the TUNA HTTPS PyPI simple endpoint
and official https://pypi.org/simple. Each is attempted separately; the
fallback repeats the unchanged editable .[test] installation, including
PEP 517 build isolation and setuptools>=75. No simultaneous extra index is
used, no build dependency is removed, and both failures fail CI.
Logs show inherited index/config/proxy hosts and the selected index host for
each attempt, with URL credentials and query strings removed. Bootstrap
disables inherited pip config files via PIP_CONFIG_FILE=/dev/null, explicitly
sets the selected index, and clears no-index/extra-index/find-links, insecure
trusted-host, and no-build-isolation environment settings. Constraints, TLS
certificate environment settings, and proxy settings remain in effect.
These CI_PIP_* settings apply only to the Job's pip install. They are not
passed into Docker build arguments or persisted into the production image.
The Dockerfile keeps its own cached/offline dependency installation and logs
the Docker pip index host for online installs. The Alpine mirror variable affects
APK only and are loaded later in the Docker verification step.
If dependency installation fails, the always() cleanup step still executes. It skips the Python/PyYAML cleanup script when no CI Compose manifest exists, as no Docker verification resources have been created at that point.
Verified prebuilt production image
When the production host cannot reach the registry, build the complete Dockerfile in a trusted build environment and transfer it with docker image save / docker image load. Supply the immutable sha256: image ID privately as FRAME_FLOW_PREBUILT_IMAGE_ID to scripts/server-release.py. The expected version/commit tag must already exist. The script checks the ID, version and revision labels, then verifies runtime source and license hashes in an isolated container without production mounts before switching the service. Existing storage checks, backups, post-deployment verification and rollback remain in effect.