Фундамент тиража 10-common (эпик ORCH-10): платформа разворачивается на
новой инфре без правки кода — только env/конфиг. Каждый дефолт = боевому
значению (пустой .env => поведение 1:1, kill-switch-природа, NFR-2);
STAGE_TRANSITIONS/QG_CHECKS/check_*/machine-verdict/схема БД не тронуты.
- config: agent_home_dir / agent_git_name / git_email_domain / staging_port
(ADR-001 D2/D4); код-блокеры A1-A4 закрыты: plane_sync ссылки из
gitea_public_url+gitea_owner, launcher - единый agent_git_env() (x2 места),
self_deploy/post_deploy - HOME+домен из Settings (имена системных акторов -
платформенные литералы)
- image_freshness: staging_port из конфига + fail-closed guard
staging_port == прод-порт -> отказ ДО ssh/build (инвариант ORCH-058 AC-9
стал исполняемым); REPO= передаётся хуку явно обоими инвокерами (D7)
- SELF_HOSTING_REPO - нормативная платформенная константа (D3, пин-тест)
- compose: полная ${VAR:-default}-интерполяция (реестр B, карта D6); группа
ORCH-040 uid/gid/HOME/маунты двигается согласованно (build.args APP_*);
group_add "МИНА 1" сохранён x3; оба app-сервиса с явным command:
- Dockerfile: ARG APP_UID/APP_GID/APP_USER/APP_HOME (CMD exec-form 8500
сознательно не тронут - D5); deploy-hook: REPO="${REPO:-...}" (D1 реестра)
- секреты: stdlib scripts/gen_secrets.py (token_hex(32); печать по умолчанию;
--write никогда не перезаписывает существующий .env молча, exit=2;
перезапись только --force); .env.example дополнен до полноты ключей старта
- доки: новый docs/operations/REPLICATION.md (карта env, чек-лист секретов,
smoke-процедура с PASS/FAIL, границы 10-common/Lite/Bundled), INFRA.md,
README, CLAUDE.md, CHANGELOG
- анти-регресс: tests/test_no_host_hardcodes.py (tokenize-сканер запрещённых
литералов, config-модули - структурное исключение, allowlist пуст,
негативная самопроверка) + test_host_config_keys / test_infra_parametrization
/ test_secrets_gen / test_replication_smoke; согласованные структурные
правки test_orch040_compose (судит резолв дефолтов) и
test_deploy_hook_rollback_sim (REPO через env-override = контракт D7)
Полный регресс: 1764 passed.
Refs: ORCH-101
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
66 lines
3.8 KiB
Docker
66 lines
3.8 KiB
Docker
FROM python:3.12-slim
|
|
# ORCH-058 (Strategy B): stamp the image with the git commit it was built from so
|
|
# the deploy hook can fail-close if a stale staging image would be promoted to prod
|
|
# (INV-FRESH). Passed at build time via `--build-arg GIT_SHA=<sha>` (the staging
|
|
# rebuild in check_staging_image_fresh / the --build-staging hook mode supplies it).
|
|
# Without the build-arg the label is empty -> the hook treats it as a mismatch
|
|
# (fail-closed). The OCI-standard key is read by `docker image inspect`.
|
|
ARG GIT_SHA=""
|
|
LABEL org.opencontainers.image.revision=$GIT_SHA
|
|
WORKDIR /app
|
|
RUN apt-get update -qq && apt-get install -y -qq openssh-client git curl ca-certificates && rm -rf /var/lib/apt/lists/*
|
|
# git operations run as root over bind-mounted /repos (may be owned by host uid) -> trust it.
|
|
RUN git config --system --add safe.directory '*'
|
|
# ORCH-022: pinned gitleaks static Go binary for the offline secret-scan sub-gate
|
|
# (07-infra I-1). Baked into the image (NOT a pip package): the gate runs INSIDE the
|
|
# orchestrator container over a per-task worktree. Pinned release => deterministic
|
|
# rules; gitleaks needs no network so the "a secret always blocks" guarantee (BR-2)
|
|
# is independent of internet access. Multi-arch aware (amd64/arm64).
|
|
ARG GITLEAKS_VERSION=8.18.4
|
|
RUN set -eux; \
|
|
arch="$(dpkg --print-architecture)"; \
|
|
case "$arch" in \
|
|
amd64) gl_arch="x64" ;; \
|
|
arm64) gl_arch="arm64" ;; \
|
|
*) echo "unsupported arch: $arch" >&2; exit 1 ;; \
|
|
esac; \
|
|
curl -fsSL -o /tmp/gitleaks.tar.gz \
|
|
"https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_${gl_arch}.tar.gz"; \
|
|
tar -xzf /tmp/gitleaks.tar.gz -C /usr/local/bin gitleaks; \
|
|
chmod +x /usr/local/bin/gitleaks; \
|
|
rm -f /tmp/gitleaks.tar.gz; \
|
|
gitleaks version
|
|
# ORCH-58: compose runs the container as uid:gid 1000:1000 (ORCH-40), but the base
|
|
# image has no passwd entry for uid 1000 -> ssh/whoami fail with
|
|
# "No user exists for uid 1000" (rc=255), breaking the detached self-deploy ssh
|
|
# launch (ORCH-36 Phase B). Create a real user 1000 with a home dir so getpwuid()
|
|
# resolves and ssh can start.
|
|
# ORCH-101 (D5): uid/gid/home/username are build ARGs (defaults = current prod
|
|
# values); compose build.args wires APP_UID/APP_GID/APP_HOME from the SAME env
|
|
# vars as the runtime user: and the mount targets, so the ORCH-040 group
|
|
# (uid/gid/HOME/mounts/useradd) moves coherently. APP_USER is passwd cosmetics
|
|
# (the ENTRY matters for getpwuid/ssh, not the name) — Dockerfile-default only.
|
|
ARG APP_UID=1000
|
|
ARG APP_GID=1000
|
|
ARG APP_USER=slin
|
|
ARG APP_HOME=/home/slin
|
|
RUN groupadd -g ${APP_GID} app && useradd -u ${APP_UID} -g ${APP_GID} -m -d ${APP_HOME} -s /bin/bash ${APP_USER}
|
|
COPY requirements.txt .
|
|
RUN pip install --no-cache-dir -r requirements.txt
|
|
COPY src/ ./src/
|
|
# ORCH-021: do NOT `COPY data/ ./data/`. `data/` is gitignored (SQLite DB dir) and
|
|
# is provided at runtime as a bind-mount volume (`./data:/app/data`, see
|
|
# docker-compose.yml) which shadows anything baked into the image — so the COPY was
|
|
# dead weight. Worse, the ORCH-058 staging rebuild (`check_staging_image_fresh`)
|
|
# builds with the task *worktree* as the docker build context; a fresh worktree never
|
|
# contains the untracked `data/`, so `COPY data/` failed `docker build` with exit 1
|
|
# and bounced the task off `deploy-staging`. We just ensure the mountpoint exists.
|
|
RUN mkdir -p /app/data
|
|
ENV PYTHONPATH=/app
|
|
# ORCH-101 (D5): CMD deliberately stays exec-form with the documented 8500
|
|
# default — an ARG cannot reach a runtime CMD, and a shell-form CMD would break
|
|
# the verified `init: true` + exec-form PID-1/signal semantics (B-2). The prod
|
|
# port is parametrised on the compose layer (`command:` with
|
|
# ${ORCH_DEPLOY_PROD_TARGET_PORT:-8500}), which overrides this CMD.
|
|
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0", "--port", "8500"]
|