mirror of
https://github.com/razzant/ouroboros.git
synced 2026-10-03 04:07:04 +00:00
optimize dockerfile and add base image and russian trusted certs
This commit is contained in:
parent
feea544273
commit
4fabeadfbc
9 changed files with 185 additions and 58 deletions
9
.github/workflows/ci.yml
vendored
9
.github/workflows/ci.yml
vendored
|
|
@ -42,6 +42,7 @@ on:
|
|||
- 'build_linux.sh'
|
||||
- 'build_windows.ps1'
|
||||
- 'Dockerfile'
|
||||
- 'docker/**'
|
||||
- 'scripts/**'
|
||||
- 'devtools/**'
|
||||
- 'packaging/**'
|
||||
|
|
@ -532,7 +533,9 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
- name: Build Docker image
|
||||
id: docker_build
|
||||
run: docker build -t ouroboros-web:test .
|
||||
run: |
|
||||
docker build -f docker/Dockerfile.base -t ouroboros-base:local .
|
||||
docker build -t ouroboros-web:test .
|
||||
- uses: ./.github/actions/setup-python-env
|
||||
id: setup_python
|
||||
- name: Install UI smoke browser binaries
|
||||
|
|
@ -561,7 +564,9 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build Docker image
|
||||
run: docker build -t ouroboros-web:test .
|
||||
run: |
|
||||
docker build -f docker/Dockerfile.base -t ouroboros-base:local .
|
||||
docker build -t ouroboros-web:test .
|
||||
- name: Run portable detail tests in Docker
|
||||
run: |
|
||||
docker run --rm --entrypoint sh -e OUROBOROS_EXPECT_HEADLESS_SHELL=1 ouroboros-web:test -c \
|
||||
|
|
|
|||
42
Dockerfile
42
Dockerfile
|
|
@ -1,39 +1,31 @@
|
|||
# Ouroboros — Docker image for web UI runtime
|
||||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Ouroboros — application image for web UI runtime
|
||||
# Usage:
|
||||
# docker build -f docker/Dockerfile.base -t ouroboros-base:local .
|
||||
# docker build -t ouroboros-web .
|
||||
# docker run --rm -p 8765:8765 ouroboros-web
|
||||
|
||||
FROM ghcr.io/astral-sh/uv:0.12.1 AS uv
|
||||
FROM python:3.10-slim
|
||||
ARG OUROBOROS_BASE_IMAGE=ouroboros-base:local
|
||||
FROM ${OUROBOROS_BASE_IMAGE}
|
||||
|
||||
COPY --from=uv /uv /uvx /bin/
|
||||
|
||||
# System dependencies (git + Playwright/Chromium native libs installed via playwright install-deps)
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
git \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Working directory
|
||||
ENV APP_HOME=/app
|
||||
# Application environment
|
||||
ENV APP_HOME=/app \
|
||||
UV_LINK_MODE=copy \
|
||||
UV_COMPILE_BYTECODE=1 \
|
||||
UV_PROJECT_ENVIRONMENT=/opt/venv \
|
||||
PATH="/opt/venv/bin:$PATH"
|
||||
WORKDIR ${APP_HOME}
|
||||
|
||||
# Resolve only from the reviewed lock. Keeping dependencies in their own layer
|
||||
# lets source edits reuse the expensive Python package and browser downloads.
|
||||
ENV UV_LINK_MODE=copy \
|
||||
UV_COMPILE_BYTECODE=1 \
|
||||
PATH="/app/.venv/bin:$PATH"
|
||||
# Install locked project dependencies separately so source edits reuse this layer.
|
||||
COPY pyproject.toml uv.lock ./
|
||||
RUN uv sync --locked --no-dev --extra browser --no-install-project
|
||||
|
||||
# Install all Playwright native system dependencies for Chromium/WebKit (authoritative list from Playwright)
|
||||
RUN python3 -m playwright install-deps chromium webkit
|
||||
|
||||
# Install Playwright Chromium/WebKit browser binaries so browser tools work out of the box
|
||||
RUN PLAYWRIGHT_BROWSERS_PATH=0 python3 -m playwright install chromium webkit
|
||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||
uv sync --locked --no-dev --extra browser --no-install-project
|
||||
|
||||
# Copy application
|
||||
COPY . .
|
||||
RUN uv sync --locked --no-dev --extra browser --no-editable
|
||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||
uv sync --locked --no-dev --extra browser --no-editable
|
||||
|
||||
# Default environment
|
||||
ENV OUROBOROS_SERVER_HOST=0.0.0.0 \
|
||||
|
|
|
|||
|
|
@ -346,6 +346,7 @@ uv export --locked --no-dev --extra browser --no-emit-project --no-hashes --no-a
|
|||
### Docker
|
||||
|
||||
```bash
|
||||
docker build -f docker/Dockerfile.base -t ouroboros-base:local .
|
||||
docker build -t ouroboros-web .
|
||||
docker run --rm -p 8765:8765 \
|
||||
-e OUROBOROS_NETWORK_PASSWORD='choose-a-password' \
|
||||
|
|
@ -354,7 +355,10 @@ docker run --rm -p 8765:8765 \
|
|||
ouroboros-web
|
||||
```
|
||||
|
||||
Docker runs the web runtime, not the native desktop shell. It bundles Chromium and WebKit support; use [`docs/DEPLOYMENT.md`](docs/DEPLOYMENT.md) for network and container policy.
|
||||
The base image contains only runtime prerequisites: Playwright, Chromium/WebKit,
|
||||
their system libraries, Git, and trusted certificates from `docker/certs`.
|
||||
The application image owns the project environment and locked dependencies. Docker runs
|
||||
the web runtime, not the native desktop shell; use [`docs/DEPLOYMENT.md`](docs/DEPLOYMENT.md) for network and container policy.
|
||||
|
||||
### Release tag prerequisite
|
||||
|
||||
|
|
|
|||
28
docker/Dockerfile.base
Normal file
28
docker/Dockerfile.base
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# syntax=docker/dockerfile:1
|
||||
|
||||
# Ouroboros runtime-dependency base. Rebuild when this file or docker/certs changes:
|
||||
# docker build -f docker/Dockerfile.base -t ouroboros-base:local .
|
||||
|
||||
ARG UV_IMAGE=ghcr.io/astral-sh/uv:0.12.1
|
||||
ARG PYTHON_IMAGE=python:3.10-slim
|
||||
|
||||
FROM ${UV_IMAGE} AS uv
|
||||
FROM ${PYTHON_IMAGE}
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
ARG PLAYWRIGHT_VERSION=1.62.0
|
||||
|
||||
COPY --from=uv /uv /uvx /bin/
|
||||
COPY docker/certs/ /usr/local/share/ca-certificates/
|
||||
ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright \
|
||||
UV_SYSTEM_CERTS=true
|
||||
|
||||
RUN --mount=type=cache,target=/root/.cache/uv \
|
||||
--mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||
apt-get update \
|
||||
&& apt-get install -y --no-install-recommends ca-certificates git \
|
||||
&& update-ca-certificates \
|
||||
&& uv pip install --system "playwright==${PLAYWRIGHT_VERSION}" \
|
||||
&& python3 -m playwright install-deps chromium webkit \
|
||||
&& python3 -m playwright install chromium webkit
|
||||
4
docker/Dockerfile.base.dockerignore
Normal file
4
docker/Dockerfile.base.dockerignore
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
**
|
||||
!docker/
|
||||
!docker/certs/
|
||||
!docker/certs/*.crt
|
||||
33
docker/certs/russian-trusted-root-ca.crt
Normal file
33
docker/certs/russian-trusted-root-ca.crt
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIFwjCCA6qgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwcDELMAkGA1UEBhMCUlUx
|
||||
PzA9BgNVBAoMNlRoZSBNaW5pc3RyeSBvZiBEaWdpdGFsIERldmVsb3BtZW50IGFu
|
||||
ZCBDb21tdW5pY2F0aW9uczEgMB4GA1UEAwwXUnVzc2lhbiBUcnVzdGVkIFJvb3Qg
|
||||
Q0EwHhcNMjIwMzAxMjEwNDE1WhcNMzIwMjI3MjEwNDE1WjBwMQswCQYDVQQGEwJS
|
||||
VTE/MD0GA1UECgw2VGhlIE1pbmlzdHJ5IG9mIERpZ2l0YWwgRGV2ZWxvcG1lbnQg
|
||||
YW5kIENvbW11bmljYXRpb25zMSAwHgYDVQQDDBdSdXNzaWFuIFRydXN0ZWQgUm9v
|
||||
dCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMfFOZ8pUAL3+r2n
|
||||
qqE0Zp52selXsKGFYoG0GM5bwz1bSFtCt+AZQMhkWQheI3poZAToYJu69pHLKS6Q
|
||||
XBiwBC1cvzYmUYKMYZC7jE5YhEU2bSL0mX7NaMxMDmH2/NwuOVRj8OImVa5s1F4U
|
||||
zn4Kv3PFlDBjjSjXKVY9kmjUBsXQrIHeaqmUIsPIlNWUnimXS0I0abExqkbdrXbX
|
||||
YwCOXhOO2pDUx3ckmJlCMUGacUTnylyQW2VsJIyIGA8V0xzdaeUXg0VZ6ZmNUr5Y
|
||||
Ber/EAOLPb8NYpsAhJe2mXjMB/J9HNsoFMBFJ0lLOT/+dQvjbdRZoOT8eqJpWnVD
|
||||
U+QL/qEZnz57N88OWM3rabJkRNdU/Z7x5SFIM9FrqtN8xewsiBWBI0K6XFuOBOTD
|
||||
4V08o4TzJ8+Ccq5XlCUW2L48pZNCYuBDfBh7FxkB7qDgGDiaftEkZZfApRg2E+M9
|
||||
G8wkNKTPLDc4wH0FDTijhgxR3Y4PiS1HL2Zhw7bD3CbslmEGgfnnZojNkJtcLeBH
|
||||
BLa52/dSwNU4WWLubaYSiAmA9IUMX1/RpfpxOxd4Ykmhz97oFbUaDJFipIggx5sX
|
||||
ePAlkTdWnv+RWBxlJwMQ25oEHmRguNYf4Zr/Rxr9cS93Y+mdXIZaBEE0KS2iLRqa
|
||||
OiWBki9IMQU4phqPOBAaG7A+eP8PAgMBAAGjZjBkMB0GA1UdDgQWBBTh0YHlzlpf
|
||||
BKrS6badZrHF+qwshzAfBgNVHSMEGDAWgBTh0YHlzlpfBKrS6badZrHF+qwshzAS
|
||||
BgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF
|
||||
AAOCAgEAALIY1wkilt/urfEVM5vKzr6utOeDWCUczmWX/RX4ljpRdgF+5fAIS4vH
|
||||
tmXkqpSCOVeWUrJV9QvZn6L227ZwuE15cWi8DCDal3Ue90WgAJJZMfTshN4OI8cq
|
||||
W9E4EG9wglbEtMnObHlms8F3CHmrw3k6KmUkWGoa+/ENmcVl68u/cMRl1JbW2bM+
|
||||
/3A+SAg2c6iPDlehczKx2oa95QW0SkPPWGuNA/CE8CpyANIhu9XFrj3RQ3EqeRcS
|
||||
AQQod1RNuHpfETLU/A2gMmvn/w/sx7TB3W5BPs6rprOA37tutPq9u6FTZOcG1Oqj
|
||||
C/B7yTqgI7rbyvox7DEXoX7rIiEqyNNUguTk/u3SZ4VXE2kmxdmSh3TQvybfbnXV
|
||||
4JbCZVaqiZraqc7oZMnRoWrXRG3ztbnbes/9qhRGI7PqXqeKJBztxRTEVj8ONs1d
|
||||
WN5szTwaPIvhkhO3CO5ErU2rVdUr89wKpNXbBODFKRtgxUT70YpmJ46VVaqdAhOZ
|
||||
D9EUUn4YaeLaS8AjSF/h7UkjOibNc4qVDiPP+rkehFWM66PVnP1Msh93tc+taIfC
|
||||
EYVMxjh8zNbFuoc7fzvvrFILLe7ifvEIUqSVIC/AzplM/Jxw7buXFeGP1qVCBEHq
|
||||
391d/9RAfaZ12zkwFsl+IKwE/OZxW8AHa9i1p4GO0YSNuczzEm4=
|
||||
-----END CERTIFICATE-----
|
||||
41
docker/certs/russian-trusted-sub-ca.crt
Normal file
41
docker/certs/russian-trusted-sub-ca.crt
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIHQjCCBSqgAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwcDELMAkGA1UEBhMCUlUx
|
||||
PzA9BgNVBAoMNlRoZSBNaW5pc3RyeSBvZiBEaWdpdGFsIERldmVsb3BtZW50IGFu
|
||||
ZCBDb21tdW5pY2F0aW9uczEgMB4GA1UEAwwXUnVzc2lhbiBUcnVzdGVkIFJvb3Qg
|
||||
Q0EwHhcNMjIwMzAyMTEyNTE5WhcNMjcwMzA2MTEyNTE5WjBvMQswCQYDVQQGEwJS
|
||||
VTE/MD0GA1UECgw2VGhlIE1pbmlzdHJ5IG9mIERpZ2l0YWwgRGV2ZWxvcG1lbnQg
|
||||
YW5kIENvbW11bmljYXRpb25zMR8wHQYDVQQDDBZSdXNzaWFuIFRydXN0ZWQgU3Vi
|
||||
IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9YPqBKOk19NFymrE
|
||||
wehzrhBEgT2atLezpduB24mQ7CiOa/HVpFCDRZzdxqlh8drku408/tTmWzlNH/br
|
||||
HuQhZ/miWKOf35lpKzjyBd6TPM23uAfJvEOQ2/dnKGGJbsUo1/udKSvxQwVHpVv3
|
||||
S80OlluKfhWPDEXQpgyFqIzPoxIQTLZ0deirZwMVHarZ5u8HqHetRuAtmO2ZDGQn
|
||||
vVOJYAjls+Hiueq7Lj7Oce7CQsTwVZeP+XQx28PAaEZ3y6sQEt6rL06ddpSdoTMp
|
||||
BnCqTbxW+eWMyjkIn6t9GBtUV45yB1EkHNnj2Ex4GwCiN9T84QQjKSr+8f0psGrZ
|
||||
vPbCbQAwNFJjisLixnjlGPLKa5vOmNwIh/LAyUW5DjpkCx004LPDuqPpFsKXNKpa
|
||||
L2Dm6uc0x4Jo5m+gUTVORB6hOSzWnWDj2GWfomLzzyjG81DRGFBpco/O93zecsIN
|
||||
3SL2Ysjpq1zdoS01CMYxie//9zWvYwzI25/OZigtnpCIrcd2j1Y6dMUFQAzAtHE+
|
||||
qsXflSL8HIS+IJEFIQobLlYhHkoE3avgNx5jlu+OLYe0dF0Ykx1PGNjbwqvTX37R
|
||||
Cn32NMjlotW2QcGEZhDKj+3urZizp5xdTPZitA+aEjZM/Ni71VOdiOP0igbw6asZ
|
||||
2fxdozZ1TnSSYNYvNATwthNmZysCAwEAAaOCAeUwggHhMBIGA1UdEwEB/wQIMAYB
|
||||
Af8CAQAwDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTR4XENCy2BTm6KSo9MI7NM
|
||||
XqtpCzAfBgNVHSMEGDAWgBTh0YHlzlpfBKrS6badZrHF+qwshzCBxwYIKwYBBQUH
|
||||
AQEEgbowgbcwOwYIKwYBBQUHMAKGL2h0dHA6Ly9yb3N0ZWxlY29tLnJ1L2NkcC9y
|
||||
b290Y2Ffc3NsX3JzYTIwMjIuY3J0MDsGCCsGAQUFBzAChi9odHRwOi8vY29tcGFu
|
||||
eS5ydC5ydS9jZHAvcm9vdGNhX3NzbF9yc2EyMDIyLmNydDA7BggrBgEFBQcwAoYv
|
||||
aHR0cDovL3JlZXN0ci1wa2kucnUvY2RwL3Jvb3RjYV9zc2xfcnNhMjAyMi5jcnQw
|
||||
gbAGA1UdHwSBqDCBpTA1oDOgMYYvaHR0cDovL3Jvc3RlbGVjb20ucnUvY2RwL3Jv
|
||||
b3RjYV9zc2xfcnNhMjAyMi5jcmwwNaAzoDGGL2h0dHA6Ly9jb21wYW55LnJ0LnJ1
|
||||
L2NkcC9yb290Y2Ffc3NsX3JzYTIwMjIuY3JsMDWgM6Axhi9odHRwOi8vcmVlc3Ry
|
||||
LXBraS5ydS9jZHAvcm9vdGNhX3NzbF9yc2EyMDIyLmNybDANBgkqhkiG9w0BAQsF
|
||||
AAOCAgEARBVzZls79AdiSCpar15dA5Hr/rrT4WbrOfzlpI+xrLeRPrUG6eUWIW4v
|
||||
Sui1yx3iqGLCjPcKb+HOTwoRMbI6ytP/ndp3TlYua2advYBEhSvjs+4vDZNwXr/D
|
||||
anbwIWdurZmViQRBDFebpkvnIvru/RpWud/5r624Wp8voZMRtj/cm6aI9LtvBfT9
|
||||
cfzhOaexI/99c14dyiuk1+6QhdwKaCRTc1mdfNQmnfWNRbfWhWBlK3h4GGE9JK33
|
||||
Gk8ZS8DMrkdAh0xby4xAQ/mSWAfWrBmfzlOqGyoB1U47WTOeqNbWkkoAP2ys94+s
|
||||
Jg4NTkiDVtXRF6nr6fYi0bSOvOFg0IQrMXO2Y8gyg9ARdPJwKtvWX8VPADCYMiWH
|
||||
h4n8bZokIrImVKLDQKHY4jCsND2HHdJfnrdL2YJw1qFskNO4cSNmZydw0Wkgjv9k
|
||||
F+KxqrDKlB8MZu2Hclph6v/CZ0fQ9YuE8/lsHZ0Qc2HyiSMnvjgK5fDc3TD4fa8F
|
||||
E8gMNurM+kV8PT8LNIM+4Zs+LKEV8nqRWBaxkIVJGekkVKO8xDBOG/aN62AZKHOe
|
||||
GcyIdu7yNMMRihGVZCYr8rYiJoKiOzDqOkPkLOPdhtVlgnhowzHDxMHND/E2WA5p
|
||||
ZHuNM/m0TXt2wTTPL7JH2YC0gPz/BvvSzjksgzU5rLbRyUKQkgU=
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -153,7 +153,7 @@ or Intent/Scope checklists are.
|
|||
| 6 | New tool added? | `get_tools()` exports it, its schema description says WHEN to choose it (each profile receives its visible schema set every round, so the schema is the SSOT of the per-tool contract; `prompts/SYSTEM.md` is the cross-tool selection policy and mentions a tool only when the change alters that policy, never as a catalog entry; mechanism documentation lives in ARCHITECTURE/DEVELOPMENT), the handler signature matches the declared schema, and (if it mutates repo state) it is routed through the reviewed commit path rather than ad-hoc `run_command`. Also add an explicit entry in `ouroboros/safety.py::TOOL_POLICY` (`POLICY_SKIP` for trusted built-ins, `POLICY_CHECK` for opaque or outward-facing ones) — the `test_tool_policy_covers_all_builtin_tools` invariant will fail otherwise, and without an entry the tool falls through to `DEFAULT_POLICY = check` and pays a light-model LLM call per invocation. |
|
||||
| 7 | Tests green before first `commit_reviewed`? | Run `pytest -x` on the narrowest relevant target(s) you can name before the first `preflight_review` / `commit_reviewed` attempt. Size gates no longer block locally: they live in the official-CI-only `size_ratchet` pytest lane (manifest exactness plus the pairwise base-vs-tip shrink-only transition), and local surfaces (`check_worktree_readiness`, `codebase_health`) surface the same `validate_size_ratchet` findings as "official CI will enforce" warnings. When a size warning appears — or a new `.py` file lands under `ouroboros/` or `supervisor/` — run `pytest tests/ -m size_ratchet` and `scripts/regenerate_size_ratchet.py` locally to preview and fix what official CI would reject. A red test suite before the first commit attempt has caused repeated $2-5 blocked-review cycles. |
|
||||
| 8 | Adding a `README.md` version row? | BIBLE.md P9 hard cap: ≤ 2 major, ≤ 5 minor, ≤ 5 patch visible entries. Categories are mutually exclusive: major = `X.0.0` (minor=0, patch=0); minor = `X.Y.0` (patch=0, Y≠0); patch = all other `X.Y.Z` (Z≠0). Count existing rows in the category you are adding to. Easy check: `run_command(["python", "-c", "import sys; from ouroboros.tools.release_sync import check_history_limit; warns=check_history_limit(open('README.md').read()); print(warns or 'OK')"])` — if it prints warnings, trim the oldest row in the over-limit category **in the same edit** before committing. |
|
||||
| 9 | Changing any of `build.sh`, `build_linux.sh`, `build_windows.ps1`, `Dockerfile`, or `ouroboros/tools/browser.py`? | Cross-surface doc sync is mandatory. Check ALL of: `README.md` Install section (Linux native-lib caveat), `README.md` Build section (per-platform instructions), `docs/ARCHITECTURE.md` browser tools paragraph, WebKit/mobile verification notes, and inline comments in the touched build script. Any one of these being stale has blocked review twice. Verify before staging. |
|
||||
| 9 | Changing any of `build.sh`, `build_linux.sh`, `build_windows.ps1`, `Dockerfile`, `docker/Dockerfile.base`, or `ouroboros/tools/browser.py`? | Cross-surface doc sync is mandatory. Check ALL of: `README.md` Install section (Linux native-lib caveat), `README.md` Build section (per-platform instructions), `docs/ARCHITECTURE.md` browser tools paragraph, WebKit/mobile verification notes, and inline comments in the touched build script. Any one of these being stale has blocked review twice. Verify before staging. |
|
||||
| 10 | Changing `ouroboros/tools/commit_gate.py`? | Coupled surfaces that MUST be updated atomically in the same commit: (a) `claude_advisory_review.py::get_tools()` tool description for `preflight_review` and `review_status`; (b) `claude_advisory_review.py::_next_step_guidance()` strings; (c) `docs/DEVELOPMENT.md` Review & Commit Protocol section; (d) the `prompts/SYSTEM.md` Self-Modification section IF the commit-gate rule it states changed. Missing any one has blocked review. |
|
||||
| 11 | Changing VERSION + pyproject.toml? | Ordering matters: (1) write `VERSION`, `pyproject.toml`, `uv.lock`, `web/package.json` and `web/modules/api_types.js` first; (2) then write the `README.md` badge + changelog row + download links, both install pages' download links, and the `docs/ARCHITECTURE.md` header; (3) then run `pytest`. Never interleave — updating README before VERSION means `test_version_in_readme` will catch a stale badge. |
|
||||
| 12 | Writing or editing any JS file under `web/modules/`? | New or changed static inline visual properties are blocked: inspect the diff for added/changed `style=""` markup and `.style.<property>` assignments, and use CSS classes/tokens plus `classList`/`hidden` instead. Unchanged legacy hits are debt, not a blocker. A dynamic measured value may update a narrowly named CSS custom property when that is the actual runtime data flow. |
|
||||
|
|
|
|||
|
|
@ -544,28 +544,48 @@ class TestDockerignore:
|
|||
|
||||
|
||||
class TestDockerfile:
|
||||
"""Dockerfile must install Playwright Chromium/WebKit binaries so browser tools work
|
||||
out of the box in the container without additional setup."""
|
||||
"""The Docker base must bundle Playwright Chromium/WebKit for child images."""
|
||||
|
||||
def test_application_image_uses_dependency_base(self):
|
||||
src = _read("Dockerfile")
|
||||
assert "ARG OUROBOROS_BASE_IMAGE=ouroboros-base:local" in src
|
||||
assert "FROM ${OUROBOROS_BASE_IMAGE}" in src
|
||||
|
||||
def test_application_owns_project_environment(self):
|
||||
app = _read("Dockerfile")
|
||||
base = _read("docker/Dockerfile.base")
|
||||
assert "UV_PROJECT_ENVIRONMENT=/opt/venv" in app
|
||||
assert 'PATH="/opt/venv/bin:$PATH"' in app
|
||||
assert "uv sync --locked --no-dev --extra browser --no-install-project" in app
|
||||
assert "UV_PROJECT_ENVIRONMENT" not in base
|
||||
assert "uv sync" not in base
|
||||
|
||||
def test_base_installs_project_certificates(self):
|
||||
src = _read("docker/Dockerfile.base")
|
||||
assert "COPY docker/certs/ /usr/local/share/ca-certificates/" in src
|
||||
assert "update-ca-certificates" in src
|
||||
|
||||
def test_playwright_install_chromium_present(self):
|
||||
src = _read("Dockerfile")
|
||||
src = _read("docker/Dockerfile.base")
|
||||
assert "playwright install chromium webkit" in src, (
|
||||
"Dockerfile must call 'playwright install chromium webkit' to bundle the browsers"
|
||||
)
|
||||
|
||||
def test_playwright_browsers_path_zero_set(self):
|
||||
src = _read("Dockerfile")
|
||||
assert "PLAYWRIGHT_BROWSERS_PATH=0" in src, (
|
||||
"Dockerfile must set PLAYWRIGHT_BROWSERS_PATH=0 so Chromium installs "
|
||||
"inside the pip package tree (not into a user cache that won't survive "
|
||||
"image layer boundaries)"
|
||||
)
|
||||
def test_playwright_uses_shared_browser_path(self):
|
||||
src = _read("docker/Dockerfile.base")
|
||||
assert "PLAYWRIGHT_BROWSERS_PATH=/ms-playwright" in src
|
||||
|
||||
def test_base_playwright_version_matches_lock(self):
|
||||
src = _read("docker/Dockerfile.base")
|
||||
match = re.search(r'\[\[package\]\]\nname = "playwright"\nversion = "([^"]+)"', _read("uv.lock"))
|
||||
assert match is not None
|
||||
assert f"PLAYWRIGHT_VERSION={match.group(1)}" in src
|
||||
|
||||
def test_playwright_install_deps_present(self):
|
||||
"""Dockerfile must use 'playwright install-deps chromium webkit' (the authoritative
|
||||
Playwright dependency resolver) rather than a hand-curated apt library list.
|
||||
This ensures all runtime native libs required by Chromium/WebKit are present."""
|
||||
src = _read("Dockerfile")
|
||||
src = _read("docker/Dockerfile.base")
|
||||
assert "playwright install-deps chromium webkit" in src, (
|
||||
"Dockerfile must call 'playwright install-deps chromium webkit' to install all "
|
||||
"native system libraries required by Chromium/WebKit via Playwright's authoritative "
|
||||
|
|
@ -575,7 +595,7 @@ class TestDockerfile:
|
|||
def test_install_deps_before_install_chromium(self):
|
||||
"""Native system dependencies must be installed BEFORE the Chromium binary
|
||||
is downloaded, so the binary can find its runtime libraries on first launch."""
|
||||
src = _read("Dockerfile")
|
||||
src = _read("docker/Dockerfile.base")
|
||||
deps_pos = src.find("playwright install-deps chromium webkit")
|
||||
src.find("playwright install chromium webkit")
|
||||
# binary_pos must not match the install-deps line itself
|
||||
|
|
@ -588,26 +608,26 @@ class TestDockerfile:
|
|||
"playwright install-deps must appear BEFORE playwright install chromium webkit in Dockerfile"
|
||||
)
|
||||
|
||||
def test_uv_sync_before_playwright_install_deps(self):
|
||||
"""uv sync must appear BEFORE playwright install-deps chromium webkit — the
|
||||
def test_playwright_package_before_playwright_install_deps(self):
|
||||
"""uv pip must install Playwright BEFORE playwright install-deps — the
|
||||
playwright Python package must be importable when install-deps runs."""
|
||||
src = _read("Dockerfile")
|
||||
sync_pos = src.find("uv sync")
|
||||
src = _read("docker/Dockerfile.base")
|
||||
install_pos = src.find("uv pip install --system")
|
||||
deps_pos = src.find("playwright install-deps chromium webkit")
|
||||
assert sync_pos != -1, "uv sync step not found in Dockerfile"
|
||||
assert install_pos != -1, "Playwright package install not found in Dockerfile"
|
||||
assert deps_pos != -1, "playwright install-deps chromium webkit not found in Dockerfile"
|
||||
assert sync_pos < deps_pos, (
|
||||
"uv sync must appear BEFORE playwright install-deps chromium webkit in Dockerfile "
|
||||
f"(sync at char {sync_pos}, install-deps at {deps_pos})"
|
||||
assert install_pos < deps_pos, (
|
||||
"Playwright package must be installed before playwright install-deps "
|
||||
f"(install at char {install_pos}, install-deps at {deps_pos})"
|
||||
)
|
||||
|
||||
def test_uv_sync_before_all_playwright_invocations(self):
|
||||
"""uv sync must appear BEFORE every ``python3 -m playwright ...`` invocation
|
||||
def test_playwright_package_before_all_playwright_invocations(self):
|
||||
"""uv pip must install Playwright before every ``python3 -m playwright`` invocation
|
||||
in the Dockerfile — both ``install-deps`` and ``install chromium webkit``.
|
||||
If *any* playwright invocation precedes dependency sync, ModuleNotFoundError occurs."""
|
||||
src = _read("Dockerfile")
|
||||
sync_pos = src.find("uv sync")
|
||||
assert sync_pos != -1, "uv sync step not found in Dockerfile"
|
||||
Otherwise the module invocation raises ModuleNotFoundError."""
|
||||
src = _read("docker/Dockerfile.base")
|
||||
install_pos = src.find("uv pip install --system")
|
||||
assert install_pos != -1, "Playwright package install not found in Dockerfile"
|
||||
|
||||
import re as _re
|
||||
playwright_invocations = [
|
||||
|
|
@ -616,9 +636,9 @@ class TestDockerfile:
|
|||
assert playwright_invocations, "No 'python3 -m playwright' invocations found in Dockerfile"
|
||||
|
||||
earliest_playwright = min(playwright_invocations)
|
||||
assert sync_pos < earliest_playwright, (
|
||||
"uv sync must appear BEFORE the earliest 'python3 -m playwright' invocation "
|
||||
f"in the Dockerfile (sync at char {sync_pos}, earliest playwright at {earliest_playwright}). "
|
||||
assert install_pos < earliest_playwright, (
|
||||
"Playwright package install must appear before its first module invocation "
|
||||
f"(install at char {install_pos}, earliest playwright at {earliest_playwright}). "
|
||||
f"Found {len(playwright_invocations)} playwright invocation(s) at positions: "
|
||||
f"{playwright_invocations}"
|
||||
)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue