refactor(env/spot): forbid .get() defaults + cache namespace rename

Three new lint tests close gaps in the INFINITO_* SPOT contract:
* tests/lint/filesystem/env/test_default_env_keys_handled.py
  Every default.env key must be wired through a handler.
* tests/lint/filesystem/env/test_used_keys_declared.py
  Every INFINITO_* in .sh/.py must live in env/{default,ci}.env or
  a handler; per-line `# nocheck:` suppresses runtime-only refs.
* tests/lint/filesystem/python/test_no_infinito_get_defaults.py
  Forbid ANY default in `.get("INFINITO_*", ...)` -- including the
  empty-string fallback (which silently masks an unset key). The
  nocheck check walks every physical line the call spans so an
  autoformat-split call still honours the marker.

Cache namespace flattens to a single prefix:
  INFINITO_PACKAGE_CACHE_*  -> INFINITO_CACHE_PACKAGE_*
  INFINITO_REGISTRY_CACHE_* -> INFINITO_CACHE_REGISTRY_*
The five previously driver-injected paths (APT_LIST, NPMRC,
PIP_CONF, REGISTRY_PROXY_CONF, FRONTEND_CA_FILE) move into
env/default.env and passthrough.STATIC_KEYS; the
`cache_env_overrides()` helper and its two call sites are gone
along with the test-fixture override for FRONTEND_CA_DIR.

SPOT-strict rewrites:
  - 19 sites: `something.get("INFINITO_X", "")` -> subscript
    `something["INFINITO_X"]` (loud KeyError when env loader did
    not run; all keys live in env/default.env).
  - 2 handler-internal bootstrap reads suppressed with
    `# nocheck: handler-bootstrap-read`.
  - 30 runtime-only / driver-injected / test-fixture references
    annotated with `# nocheck: <reason>` per-line.
  - Unit tests that previously removed SPOT-guaranteed keys via
    `{k: v ... if k \!= KEY}` or `clear=True` switch to setting
    the key to "" explicitly, matching the new contract.

Side fixes:
  - INFINITO_DNS_DOMAIN + INFINITO_DNS_HOSTS_FILE move from
    shell-side defaults in common.sh to env/default.env and
    passthrough.STATIC_KEYS.
  - dns_systemd_is_operational() drops the dead
    INFINITO_DNS_FORCE_HOSTS_FALLBACK override branch (the
    variable was never set anywhere).
  - INFINITO_WORKER_FETCH switches from `os.environ.get(KEY, "1")`
    to `os.environ[KEY]`.
  - scripts/tests/code/wrapper.sh exec args move into a bash
    array so `# nocheck:` on INFINITO_TEST_TYPE can live on its
    own element line (SC2215 fix).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 4.7 committed 2026-05-20 22:26:16 +02:00
1 parent 22cf6f38f1
commit eb4bcd23a8
53 files changed
+666 -267

No files matched your search

+14 -14
View File
@@ -37,13 +37,13 @@ jobs:
# post-prune in-run re-pulls: large enough to fit one deploy's
# in-use image set (rpardini) plus pip/npm/apt artefacts (Nexus),
# leaving ~62g headroom for Docker layer storage on /mnt.
INFINITO_REGISTRY_CACHE_MAX_SIZE: 8g
INFINITO_REGISTRY_CACHE_HOST_PATH: /mnt/cache/registry/mirror
INFINITO_REGISTRY_CACHE_CA_HOST_PATH: /mnt/cache/registry/ca
INFINITO_PACKAGE_CACHE_BLOBSTORE_MAX: 4g
INFINITO_PACKAGE_CACHE_HEAP: 1g
INFINITO_PACKAGE_CACHE_DIRECT_MEM: 1g
INFINITO_PACKAGE_CACHE_HOST_PATH: /mnt/cache/package/data
INFINITO_CACHE_REGISTRY_MAX_SIZE: 8g
INFINITO_CACHE_REGISTRY_HOST_PATH: /mnt/cache/registry/mirror
INFINITO_CACHE_REGISTRY_CA_HOST_PATH: /mnt/cache/registry/ca
INFINITO_CACHE_PACKAGE_BLOBSTORE_MAX: 4g
INFINITO_CACHE_PACKAGE_HEAP: 1g
INFINITO_CACHE_PACKAGE_DIRECT_MEM: 1g
INFINITO_CACHE_PACKAGE_HOST_PATH: /mnt/cache/package/data
steps:
- name: Checkout repository
@@ -69,13 +69,13 @@ jobs:
-e INFINITO_PULL_POLICY=never \
-e INFINITO_NO_BUILD=0 \
-e NIX_CONFIG \
-e INFINITO_REGISTRY_CACHE_MAX_SIZE \
-e INFINITO_REGISTRY_CACHE_HOST_PATH \
-e INFINITO_REGISTRY_CACHE_CA_HOST_PATH \
-e INFINITO_PACKAGE_CACHE_BLOBSTORE_MAX \
-e INFINITO_PACKAGE_CACHE_HEAP \
-e INFINITO_PACKAGE_CACHE_DIRECT_MEM \
-e INFINITO_PACKAGE_CACHE_HOST_PATH \
-e INFINITO_CACHE_REGISTRY_MAX_SIZE \
-e INFINITO_CACHE_REGISTRY_HOST_PATH \
-e INFINITO_CACHE_REGISTRY_CA_HOST_PATH \
-e INFINITO_CACHE_PACKAGE_BLOBSTORE_MAX \
-e INFINITO_CACHE_PACKAGE_HEAP \
-e INFINITO_CACHE_PACKAGE_DIRECT_MEM \
-e INFINITO_CACHE_PACKAGE_HOST_PATH \
--network host \
-v "${HOST_WORKDIR}:${HOST_WORKDIR}" \
-v "/mnt/docker:/var/lib/docker" \
@@ -377,9 +377,7 @@ def main() -> int:
help="Container mode: run, start, stop, exec, remove.",
)
parser.add_argument(
"--image", default=os.environ.get("INFINITO_IMAGE", "infinito:latest")
)
parser.add_argument("--image", default=os.environ.get("INFINITO_IMAGE"))
parser.add_argument("--build", action="store_true")
parser.add_argument("--rebuild", action="store_true")
parser.add_argument("--no-cache", action="store_true")
@@ -30,32 +30,9 @@ def compose_file_args() -> list[str]:
return out
def cache_env_overrides() -> dict[str, str]:
"""Env vars consumed strictly by compose/cache.override.yml."""
from .profile import Profile
if not Profile().registry_cache_active():
return {}
ca_dir = os.environ.get("INFINITO_PACKAGE_CACHE_FRONTEND_CA_DIR", "").strip()
if not ca_dir:
raise SystemExit(
"INFINITO_PACKAGE_CACHE_FRONTEND_CA_DIR is not set. Run "
"'make dotenv' (or source scripts/meta/env/load.sh) before "
"invoking cli.administration.deploy.development."
)
return {
"INFINITO_REGISTRY_CACHE_PROXY_CONF": "./compose/registry-cache/proxy.conf",
"INFINITO_PACKAGE_CACHE_PIP_CONF": "./compose/package-cache/pip.conf",
"INFINITO_PACKAGE_CACHE_NPMRC": "./compose/package-cache/npmrc",
"INFINITO_PACKAGE_CACHE_APT_LIST": "./compose/package-cache/apt.list",
"INFINITO_PACKAGE_CACHE_FRONTEND_CA_FILE": f"{ca_dir}/ca.crt",
}
def resolve_distro() -> str:
"""Return INFINITO_DISTRO; raise SystemExit if missing or invalid."""
distro = os.environ.get("INFINITO_DISTRO", "").strip()
distro = os.environ["INFINITO_DISTRO"].strip()
if not distro:
raise SystemExit(
"INFINITO_DISTRO is not set. Run 'make dotenv' (or source scripts/meta/env/load.sh) "
@@ -72,7 +49,7 @@ def resolve_distro() -> str:
def resolve_container() -> str:
"""Return INFINITO_CONTAINER; raise SystemExit if unset."""
container = os.environ.get("INFINITO_CONTAINER", "").strip()
container = os.environ["INFINITO_CONTAINER"].strip()
if not container:
raise SystemExit(
"INFINITO_CONTAINER is not set. Run 'make dotenv' (or source scripts/meta/env/load.sh) "
@@ -5,7 +5,7 @@ import subprocess
import time
from typing import TYPE_CHECKING
from .common import cache_env_overrides, compose_file_args
from .common import compose_file_args
from .coredns import CoreDNSCorefileRenderer
from .network import detect_outer_network_mtu
from .proc import run_streaming
@@ -42,7 +42,6 @@ class Compose:
check=True,
)
env["INFINITO_IMAGE"] = result.stdout.strip()
env.update(cache_env_overrides())
return env
def run(
@@ -192,7 +191,7 @@ class Compose:
print(">>> env:", {k: env.get(k) for k in keys})
print(">>> NIX_CONFIG:", "<set>" if env.get("NIX_CONFIG") else "<empty>")
no_build = env.get("INFINITO_NO_BUILD", "0") == "1"
no_build = env["INFINITO_NO_BUILD"] == "1"
# Compose env-file precedence: later files override earlier ones.
args = ["--env-file", "env/ci.env"]
@@ -23,7 +23,7 @@ if TYPE_CHECKING:
def _env_variant() -> int | None:
raw = os.environ.get("INFINITO_VARIANT", "").strip()
raw = os.environ["INFINITO_VARIANT"].strip()
if not raw:
return None
try:
@@ -35,7 +35,7 @@ def _env_variant() -> int | None:
def _env_full_cycle() -> bool:
return os.environ.get("INFINITO_FULL_CYCLE", "").strip().lower() == "true"
return os.environ["INFINITO_FULL_CYCLE"].strip().lower() == "true"
def _run_deploy(
@@ -84,7 +84,7 @@ def _run_deploy(
"PY_COLORS": "1",
"TERM": "xterm-256color",
}
services_disabled = os.environ.get("INFINITO_SERVICES_DISABLED", "")
services_disabled = os.environ["INFINITO_SERVICES_DISABLED"]
if services_disabled:
extra_env["INFINITO_SERVICES_DISABLED"] = services_disabled
@@ -216,7 +216,7 @@ def handler(args: argparse.Namespace) -> int:
# Remove any app IDs that were disabled via INFINITO_SERVICES_DISABLED so the
# deploy list stays consistent with the inventory created by init.
raw_disabled = os.environ.get("INFINITO_SERVICES_DISABLED", "").strip()
raw_disabled = os.environ["INFINITO_SERVICES_DISABLED"].strip()
disabled_app_ids: set[str] = set()
if raw_disabled:
services = parse_services_disabled(raw_disabled)
@@ -7,7 +7,7 @@ from pathlib import Path
from typing import TYPE_CHECKING
from . import PROJECT_ROOT
from .common import cache_env_overrides, compose_file_args, resolve_distro
from .common import compose_file_args, resolve_distro
from .profile import Profile
if TYPE_CHECKING:
@@ -20,7 +20,6 @@ CI_DOCKER_ROOT_STR = str(CI_DOCKER_ROOT)
def _base_env(*, distro: str) -> dict[str, str]:
env = dict(os.environ)
env["INFINITO_DISTRO"] = distro
env.update(cache_env_overrides())
return env
@@ -45,7 +44,7 @@ def _cleanup_docker_root() -> None:
print(f">>> Not on GitHub - No bind volumes will be deleted: {docker_root}")
return
docker_root_env = os.environ.get("INFINITO_DOCKER_VOLUME", "").strip().rstrip("/")
docker_root_env = os.environ["INFINITO_DOCKER_VOLUME"].strip().rstrip("/")
if docker_root_env and docker_root_env != CI_DOCKER_ROOT_STR:
raise RuntimeError(
@@ -51,7 +51,7 @@ def handler(args: argparse.Namespace) -> int:
raise SystemExit("exec requires a command (e.g. exec -- sh -lc 'whoami')")
extra_env: dict[str, str] = {}
services_disabled = os.environ.get("INFINITO_SERVICES_DISABLED", "")
services_disabled = os.environ["INFINITO_SERVICES_DISABLED"]
if services_disabled:
extra_env["INFINITO_SERVICES_DISABLED"] = services_disabled
@@ -82,7 +82,7 @@ def add_parser(sub: argparse._SubParsersAction) -> None:
def _env_variant() -> int | None:
raw = os.environ.get("INFINITO_VARIANT", "").strip()
raw = os.environ["INFINITO_VARIANT"].strip()
if not raw:
return None
try:
@@ -117,7 +117,7 @@ def handler(args: argparse.Namespace) -> int:
# the inventory step strip it back out — leaving the round's
# include list inconsistent with what the inventory actually
# contains.
raw_disabled = os.environ.get("INFINITO_SERVICES_DISABLED", "").strip()
raw_disabled = os.environ["INFINITO_SERVICES_DISABLED"].strip()
disabled_app_ids: set[str] = set()
if raw_disabled:
services = parse_services_disabled(raw_disabled)
@@ -159,7 +159,7 @@ def handler(args: argparse.Namespace) -> int:
raise SystemExit(f"--variant: {exc}") from exc
runtime = os.environ.get("RUNTIME") or detect_runtime()
services_disabled = os.environ.get("INFINITO_SERVICES_DISABLED", "")
services_disabled = os.environ["INFINITO_SERVICES_DISABLED"]
for _round_index, inv_dir, round_variants, include_R, _purge_set in plan:
round_include = tuple(
role for role in include_R if role not in disabled_app_ids
@@ -19,7 +19,7 @@ def detect_outer_network_mtu(
daemon_config_path: Path = DOCKER_DAEMON_CONFIG_PATH,
) -> str | None:
current_env = os.environ if env is None else env
explicit_mtu = current_env.get("INFINITO_OUTER_NETWORK_MTU", "").strip()
explicit_mtu = current_env["INFINITO_OUTER_NETWORK_MTU"].strip()
if explicit_mtu:
return explicit_mtu
+1 -1
View File
@@ -24,7 +24,7 @@ def add_parser(sub: argparse._SubParsersAction) -> None:
def _maybe_build_missing() -> int:
# CI: never build (image is pulled). Local: build if missing.
if os.environ.get("INFINITO_NO_BUILD", "0") == "1":
if os.environ["INFINITO_NO_BUILD"] == "1":
return 0
build_args = argparse.Namespace(
@@ -235,7 +235,7 @@ def apply_services_disabled_from_env(
inventory_file: Path | None = None,
) -> None:
"""Read INFINITO_SERVICES_DISABLED from the environment and apply to host_vars and inventory."""
raw = os.environ.get("INFINITO_SERVICES_DISABLED", "").strip()
raw = os.environ["INFINITO_SERVICES_DISABLED"].strip()
if not raw:
return
services = parse_services_disabled(raw)
@@ -326,7 +326,7 @@ def assert_services_disabled_inventory_consistency_from_env(
roles_dir: Path,
) -> None:
"""Fail fast when INFINITO_SERVICES_DISABLED conflicts with the existing inventory."""
raw = os.environ.get("INFINITO_SERVICES_DISABLED", "").strip()
raw = os.environ["INFINITO_SERVICES_DISABLED"].strip()
if not raw:
return
+2 -1
View File
@@ -65,7 +65,8 @@ services:
volumes:
- /sys/fs/cgroup:/sys/fs/cgroup:rw
- .:/opt/src/infinito
- ${INFINITO_DOCKER_VOLUME:?Run 'make dotenv' to generate the .env single source of truth}:${INFINITO_DOCKER_MOUNT:?Run 'make dotenv' to generate the .env single source of truth}
- ${INFINITO_DOCKER_VOLUME:?Run 'make dotenv' to generate the .env single source of truth}:${INFINITO_DOCKER_MOUNT:?Run 'make dotenv' to generate the .env
single source of truth}
- infinito:/var/lib/infinito/
- letsencrypt:/etc/letsencrypt/
+27 -27
View File
@@ -9,17 +9,17 @@ services:
environment:
ENABLE_MANIFEST_CACHE: "true"
AUTH_REGISTRIES: ""
CACHE_MAX_SIZE: "${INFINITO_REGISTRY_CACHE_MAX_SIZE:?Run 'make dotenv' to generate the .env single source of truth}"
CACHE_MAX_SIZE: "${INFINITO_CACHE_REGISTRY_MAX_SIZE:?Run 'make dotenv' to generate the .env single source of truth}"
# Avoid 502s on hosts with broken IPv6 outbound.
DISABLE_IPV6: "true"
volumes:
- type: bind
source: ${INFINITO_REGISTRY_CACHE_HOST_PATH:?Run 'make dotenv' to generate the .env single source of truth}
source: ${INFINITO_CACHE_REGISTRY_HOST_PATH:?Run 'make dotenv' to generate the .env single source of truth}
target: /docker_mirror_cache
bind:
create_host_path: true
- type: bind
source: ${INFINITO_REGISTRY_CACHE_CA_HOST_PATH:?Run 'make dotenv' to generate the .env single source of truth}
source: ${INFINITO_CACHE_REGISTRY_CA_HOST_PATH:?Run 'make dotenv' to generate the .env single source of truth}
target: /ca
bind:
create_host_path: true
@@ -39,19 +39,19 @@ services:
# Run as root: fresh bind-mount is root:root and Nexus aborts otherwise.
user: "0:0"
environment:
INSTALL4J_ADD_VM_PARAMS: "-Xms${INFINITO_PACKAGE_CACHE_HEAP:?Run 'make dotenv' to generate the .env single source of truth} -Xmx${INFINITO_PACKAGE_CACHE_HEAP:?Run
'make dotenv' to generate the .env single source of truth} -XX:MaxDirectMemorySize=${INFINITO_PACKAGE_CACHE_DIRECT_MEM:?Run 'make dotenv' to generate the
INSTALL4J_ADD_VM_PARAMS: "-Xms${INFINITO_CACHE_PACKAGE_HEAP:?Run 'make dotenv' to generate the .env single source of truth} -Xmx${INFINITO_CACHE_PACKAGE_HEAP:?Run
'make dotenv' to generate the .env single source of truth} -XX:MaxDirectMemorySize=${INFINITO_CACHE_PACKAGE_DIRECT_MEM:?Run 'make dotenv' to generate the
.env single source of truth}"
# Random-password mode leaves no readable secret for the bootstrap.
NEXUS_SECURITY_RANDOMPASSWORD: "false"
volumes:
- type: bind
source: ${INFINITO_PACKAGE_CACHE_HOST_PATH:?Run 'make dotenv' to generate the .env single source of truth}
source: ${INFINITO_CACHE_PACKAGE_HOST_PATH:?Run 'make dotenv' to generate the .env single source of truth}
target: /nexus-data
bind:
create_host_path: true
ports:
- "${INFINITO_BIND_IP:?Run 'make dotenv' to generate the .env single source of truth}:${INFINITO_PACKAGE_CACHE_PORT:?Run 'make dotenv' to generate the .env
- "${INFINITO_BIND_IP:?Run 'make dotenv' to generate the .env single source of truth}:${INFINITO_CACHE_PACKAGE_PORT:?Run 'make dotenv' to generate the .env
single source of truth}:8081"
healthcheck:
test:
@@ -71,13 +71,13 @@ services:
condition: service_healthy
volumes:
- type: bind
source: ${INFINITO_PACKAGE_CACHE_FRONTEND_CA_DIR:?Run 'make dotenv' to generate the .env single source of truth}
source: ${INFINITO_CACHE_PACKAGE_FRONTEND_CA_DIR:?Run 'make dotenv' to generate the .env single source of truth}
target: /etc/nginx/ca
read_only: true
bind:
create_host_path: true
- type: bind
source: ${INFINITO_PACKAGE_CACHE_FRONTEND_CERTS_DIR:?Run 'make dotenv' to generate the .env single source of truth}
source: ${INFINITO_CACHE_PACKAGE_FRONTEND_CERTS_DIR:?Run 'make dotenv' to generate the .env single source of truth}
target: /etc/nginx/certs
read_only: true
bind:
@@ -85,7 +85,7 @@ services:
- ./compose/package-cache-frontend/upstreams.conf:/etc/nginx/conf.d/upstreams.conf:ro
networks:
default:
ipv4_address: ${INFINITO_PACKAGE_CACHE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}
ipv4_address: ${INFINITO_CACHE_PACKAGE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}
healthcheck:
test:
- CMD-SHELL
@@ -108,32 +108,32 @@ services:
condition: service_healthy
required: false
environment:
INFINITO_PACKAGE_CACHE_FRONTEND_IP: ${INFINITO_PACKAGE_CACHE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}
INFINITO_CACHE_PACKAGE_FRONTEND_IP: ${INFINITO_CACHE_PACKAGE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}
extra_hosts:
- "pypi.org:${INFINITO_PACKAGE_CACHE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}"
- "files.pythonhosted.org:${INFINITO_PACKAGE_CACHE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}"
- "registry.npmjs.org:${INFINITO_PACKAGE_CACHE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}"
- "rubygems.org:${INFINITO_PACKAGE_CACHE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}"
- "index.rubygems.org:${INFINITO_PACKAGE_CACHE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}"
- "repo.packagist.org:${INFINITO_PACKAGE_CACHE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}"
- "proxy.golang.org:${INFINITO_PACKAGE_CACHE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}"
- "dl-cdn.alpinelinux.org:${INFINITO_PACKAGE_CACHE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}"
- "raw.githubusercontent.com:${INFINITO_PACKAGE_CACHE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}"
- "codeload.github.com:${INFINITO_PACKAGE_CACHE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}"
- "pypi.org:${INFINITO_CACHE_PACKAGE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}"
- "files.pythonhosted.org:${INFINITO_CACHE_PACKAGE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}"
- "registry.npmjs.org:${INFINITO_CACHE_PACKAGE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}"
- "rubygems.org:${INFINITO_CACHE_PACKAGE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}"
- "index.rubygems.org:${INFINITO_CACHE_PACKAGE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}"
- "repo.packagist.org:${INFINITO_CACHE_PACKAGE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}"
- "proxy.golang.org:${INFINITO_CACHE_PACKAGE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}"
- "dl-cdn.alpinelinux.org:${INFINITO_CACHE_PACKAGE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}"
- "raw.githubusercontent.com:${INFINITO_CACHE_PACKAGE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}"
- "codeload.github.com:${INFINITO_CACHE_PACKAGE_FRONTEND_IP:?Run 'make dotenv' to generate the .env single source of truth}"
volumes:
- type: bind
source: ${INFINITO_REGISTRY_CACHE_CA_HOST_PATH:?Run 'make dotenv' to generate the .env single source of truth}
source: ${INFINITO_CACHE_REGISTRY_CA_HOST_PATH:?Run 'make dotenv' to generate the .env single source of truth}
target: /opt/registry-cache-ca
read_only: true
bind:
create_host_path: true
- ${INFINITO_REGISTRY_CACHE_PROXY_CONF:?Set by 'cli deploy development up' / 'make up' (driver-injected, not produced by 'make
- ${INFINITO_CACHE_REGISTRY_PROXY_CONF:?Set by 'cli deploy development up' / 'make up' (driver-injected, not produced by 'make
dotenv')}:/etc/systemd/system/docker.service.d/registry-cache-proxy.conf:ro
- ./scripts/docker/cache/registry-ca.sh:/usr/local/bin/registry-cache-ca.sh:ro
- ${INFINITO_PACKAGE_CACHE_PIP_CONF:?Set by 'cli deploy development up' / 'make up' (driver-injected, not produced by 'make dotenv')}:/etc/pip.conf:ro
- ${INFINITO_PACKAGE_CACHE_NPMRC:?Set by 'cli deploy development up' / 'make up' (driver-injected, not produced by 'make dotenv')}:/root/.npmrc:ro
- ${INFINITO_PACKAGE_CACHE_APT_LIST:?Set by 'cli deploy development up' / 'make up' (driver-injected, not produced by 'make
- ${INFINITO_CACHE_PACKAGE_PIP_CONF:?Set by 'cli deploy development up' / 'make up' (driver-injected, not produced by 'make dotenv')}:/etc/pip.conf:ro
- ${INFINITO_CACHE_PACKAGE_NPMRC:?Set by 'cli deploy development up' / 'make up' (driver-injected, not produced by 'make dotenv')}:/root/.npmrc:ro
- ${INFINITO_CACHE_PACKAGE_APT_LIST:?Set by 'cli deploy development up' / 'make up' (driver-injected, not produced by 'make
dotenv')}:/etc/apt/sources.list.d/package-cache.list:ro
- ${INFINITO_PACKAGE_CACHE_FRONTEND_CA_FILE:?Set by 'cli deploy development up' / 'make up' (driver-injected, not produced by 'make
- ${INFINITO_CACHE_PACKAGE_FRONTEND_CA_FILE:?Set by 'cli deploy development up' / 'make up' (driver-injected, not produced by 'make
dotenv')}:/opt/package-frontend-ca.crt:ro
- ./scripts/docker/cache/package-frontend-ca.sh:/usr/local/bin/package-frontend-ca.sh:ro
+18 -18
View File
@@ -66,24 +66,24 @@ The env-var contracts each service expects strictly via `${VAR:?…}` (consumed
| Variable | Purpose |
|---|---|
| `INFINITO_REGISTRY_CACHE_HOST_PATH` | Host path bind-mounted into `registry-cache` for blob/manifest persistence. |
| `INFINITO_REGISTRY_CACHE_CA_HOST_PATH` | Host path holding the proxy MITM CA bundle. Mounted writable into `registry-cache`, read-only into `infinito`. |
| `INFINITO_REGISTRY_CACHE_MAX_SIZE` | Maximum on-disk size. Computed by [infinito_registry_cache_max_size.py](../../../../utils/env/handlers/infinito_registry_cache_max_size.py) as half the free disk at the cache path, floor `1g`, fallback `2g`. |
| `INFINITO_REGISTRY_CACHE_PROXY_CONF` | Bind source for the systemd drop-in inside `infinito`. Set by the dev tooling to the real `proxy.conf` under the `cache` profile. Driver-injected; not produced by `make dotenv`. |
| `INFINITO_PACKAGE_CACHE_HOST_PATH` | Host path bind-mounted at `/nexus-data`. |
| `INFINITO_PACKAGE_CACHE_HEAP` | JVM heap (`-Xms`/`-Xmx`). Computed as half free RAM, capped `2g`, floor `1g`. |
| `INFINITO_PACKAGE_CACHE_DIRECT_MEM` | `MaxDirectMemorySize`. Mirrors `INFINITO_PACKAGE_CACHE_HEAP`. |
| `INFINITO_PACKAGE_CACHE_BLOBSTORE_MAX` | Soft quota for the default blobstore. Computed as half free disk at the cache path, floor `2g`. |
| `INFINITO_PACKAGE_CACHE_MAX_AGE_MIN` | Cache freshness window in minutes, applied to every Nexus proxy repo (`contentMaxAge`/`metadataMaxAge`/negative-cache TTL). Stays strictly below the 7-day `Valid-Until` window Debian / Ubuntu generate in their apt `Release` files, so `apt-get update` never aborts with "Release file ... is expired". |
| `INFINITO_PACKAGE_CACHE_ADMIN_PASSWORD` | Target value for the rotated Nexus admin password. Default is a stable per-host hash. |
| `INFINITO_PACKAGE_CACHE_PORT` | Host-side port mapped to Nexus REST/UI. Bound to `${INFINITO_BIND_IP}` only. |
| `INFINITO_PACKAGE_CACHE_PIP_CONF` | Bind source for `/etc/pip.conf`. Driver-injected by `make up` under the `cache` profile. |
| `INFINITO_PACKAGE_CACHE_NPMRC` | Bind source for `/root/.npmrc`. Driver-injected. |
| `INFINITO_PACKAGE_CACHE_APT_LIST` | Bind source for `/etc/apt/sources.list.d/package-cache.list`. Driver-injected. |
| `INFINITO_PACKAGE_CACHE_FRONTEND_CA_DIR` | Host directory for the frontend CA. |
| `INFINITO_PACKAGE_CACHE_FRONTEND_CERTS_DIR` | Host directory for per-hostname leaf certs. |
| `INFINITO_PACKAGE_CACHE_FRONTEND_IP` | Static IPv4 address for the frontend on the compose default network. |
| `INFINITO_PACKAGE_CACHE_FRONTEND_CA_FILE` | Bind source for `/opt/package-frontend-ca.crt` inside the runner. Driver-injected. |
| `INFINITO_CACHE_REGISTRY_HOST_PATH` | Host path bind-mounted into `registry-cache` for blob/manifest persistence. |
| `INFINITO_CACHE_REGISTRY_CA_HOST_PATH` | Host path holding the proxy MITM CA bundle. Mounted writable into `registry-cache`, read-only into `infinito`. |
| `INFINITO_CACHE_REGISTRY_MAX_SIZE` | Maximum on-disk size. Computed by [infinito_registry_cache_max_size.py](../../../../utils/env/handlers/infinito_registry_cache_max_size.py) as half the free disk at the cache path, floor `1g`, fallback `2g`. |
| `INFINITO_CACHE_REGISTRY_PROXY_CONF` | Bind source for the systemd drop-in inside `infinito`. Set by the dev tooling to the real `proxy.conf` under the `cache` profile. Driver-injected; not produced by `make dotenv`. |
| `INFINITO_CACHE_PACKAGE_HOST_PATH` | Host path bind-mounted at `/nexus-data`. |
| `INFINITO_CACHE_PACKAGE_HEAP` | JVM heap (`-Xms`/`-Xmx`). Computed as half free RAM, capped `2g`, floor `1g`. |
| `INFINITO_CACHE_PACKAGE_DIRECT_MEM` | `MaxDirectMemorySize`. Mirrors `INFINITO_CACHE_PACKAGE_HEAP`. |
| `INFINITO_CACHE_PACKAGE_BLOBSTORE_MAX` | Soft quota for the default blobstore. Computed as half free disk at the cache path, floor `2g`. |
| `INFINITO_CACHE_PACKAGE_MAX_AGE_MIN` | Cache freshness window in minutes, applied to every Nexus proxy repo (`contentMaxAge`/`metadataMaxAge`/negative-cache TTL). Stays strictly below the 7-day `Valid-Until` window Debian / Ubuntu generate in their apt `Release` files, so `apt-get update` never aborts with "Release file ... is expired". |
| `INFINITO_CACHE_PACKAGE_ADMIN_PASSWORD` | Target value for the rotated Nexus admin password. Default is a stable per-host hash. |
| `INFINITO_CACHE_PACKAGE_PORT` | Host-side port mapped to Nexus REST/UI. Bound to `${INFINITO_BIND_IP}` only. |
| `INFINITO_CACHE_PACKAGE_PIP_CONF` | Bind source for `/etc/pip.conf`. Driver-injected by `make up` under the `cache` profile. |
| `INFINITO_CACHE_PACKAGE_NPMRC` | Bind source for `/root/.npmrc`. Driver-injected. |
| `INFINITO_CACHE_PACKAGE_APT_LIST` | Bind source for `/etc/apt/sources.list.d/package-cache.list`. Driver-injected. |
| `INFINITO_CACHE_PACKAGE_FRONTEND_CA_DIR` | Host directory for the frontend CA. |
| `INFINITO_CACHE_PACKAGE_FRONTEND_CERTS_DIR` | Host directory for per-hostname leaf certs. |
| `INFINITO_CACHE_PACKAGE_FRONTEND_IP` | Static IPv4 address for the frontend on the compose default network. |
| `INFINITO_CACHE_PACKAGE_FRONTEND_CA_FILE` | Bind source for `/opt/package-frontend-ca.crt` inside the runner. Driver-injected. |
### Networking
+4 -4
View File
@@ -47,7 +47,7 @@ The `cache` decision is exposed via `Profile.registry_cache_active()` in [profil
- The runner's `infinito` service receives:
- bind-mounts for the registry-cache CA, the package-cache client snippets (`pip.conf`, `npmrc`, `apt.list`), and the frontend CA file
- `extra_hosts` entries DNS-hijacking the HTTPS upstream hostnames to the frontend's static IP
- `INFINITO_PACKAGE_CACHE_FRONTEND_IP` env var for the inner compose wrapper
- `INFINITO_CACHE_PACKAGE_FRONTEND_IP` env var for the inner compose wrapper
- Cert generation, Nexus repo bootstrap, and runner trust-store install run from [compose.py](../../../cli/administration/deploy/development/compose.py).
When inactive, the override is omitted: cache services do not exist, the runner has no cache mounts or DNS-hijack, package managers go direct to upstream.
@@ -80,17 +80,17 @@ The runner's `compose` wrapper at [roles/sys-svc-compose/files/compose.py](../..
| `compose.yml` | always |
| `compose.override.yml` | when present (role provides it) |
| `compose.ca.override.yml` | when present (TLS self-signed CA-inject runs) |
| `compose.cache.override.yml` | generated on the fly when `INFINITO_PACKAGE_CACHE_FRONTEND_IP` is set; emits `build.extra_hosts` for every service that has a `build:` key |
| `compose.cache.override.yml` | generated on the fly when `INFINITO_CACHE_PACKAGE_FRONTEND_IP` is set; emits `build.extra_hosts` for every service that has a `build:` key |
[pull.py](../../../roles/sys-svc-compose/files/pull.py) delegates to the same wrapper so `pull` and `build --pull` operations see the identical `-f` set.
## Environment Variables 🌳
The host-side env-vars (`INFINITO_REGISTRY_CACHE_*`, `INFINITO_PACKAGE_CACHE_*`) are declared in [env/default.env](../../../env/default.env) and the dynamic ones (cache sizes from `df`/`/proc/meminfo`, sha256 admin password) are computed by [utils/env/builder.py](../../../utils/env/builder.py). `make dotenv` writes them into `.env`, which Compose auto-loads and which `BASH_ENV` makes available to every Makefile recipe through [scripts/meta/env/load.sh](../../../scripts/meta/env/load.sh). See [variables.md](variables.md).
The host-side env-vars (`INFINITO_CACHE_REGISTRY_*`, `INFINITO_CACHE_PACKAGE_*`) are declared in [env/default.env](../../../env/default.env) and the dynamic ones (cache sizes from `df`/`/proc/meminfo`, sha256 admin password) are computed by [utils/env/builder.py](../../../utils/env/builder.py). `make dotenv` writes them into `.env`, which Compose auto-loads and which `BASH_ENV` makes available to every Makefile recipe through [scripts/meta/env/load.sh](../../../scripts/meta/env/load.sh). See [variables.md](variables.md).
Per-variable defaults and purposes are in [compose.yml.md](../artefact/files/compose.yml.md) under the cache section.
`INFINITO_PACKAGE_CACHE_MAX_AGE_MIN` (default `8640` = 6 days) controls how long every Nexus proxy repo holds an upstream response before revalidating. The bootstrap helper applies it as `contentMaxAge`, `metadataMaxAge`, and `negativeCache.timeToLive`. The default is intentionally one day below the 7-day `Valid-Until` window Debian and Ubuntu generate in their apt `Release` files, so Nexus always refreshes the manifest before `apt-get update` would see it expired. Raise it for slow-moving upstreams when staleness is preferable to upstream load; lower it further for fast-moving feeds (`apt-debian-security`, branch-pinned tarballs) when staleness becomes visible.
`INFINITO_CACHE_PACKAGE_MAX_AGE_MIN` (default `8640` = 6 days) controls how long every Nexus proxy repo holds an upstream response before revalidating. The bootstrap helper applies it as `contentMaxAge`, `metadataMaxAge`, and `negativeCache.timeToLive`. The default is intentionally one day below the 7-day `Valid-Until` window Debian and Ubuntu generate in their apt `Release` files, so Nexus always refreshes the manifest before `apt-get update` would see it expired. Raise it for slow-moving upstreams when staleness is preferable to upstream load; lower it further for fast-moving feeds (`apt-debian-security`, branch-pinned tarballs) when staleness becomes visible.
## Operations 🛠️
+1 -1
View File
@@ -34,7 +34,7 @@ Already-set env vars take precedence over `default.env` defaults.
A handful of variables are documented next to their consumer because they cannot have a static default:
- **Driver-injected** by `make up` and the dev CLI (cache override paths like `INFINITO_REGISTRY_CACHE_PROXY_CONF`, the package-cache config paths, the frontend CA file).
- **Driver-injected** by `make up` and the dev CLI (cache override paths like `INFINITO_CACHE_REGISTRY_PROXY_CONF`, the package-cache config paths, the frontend CA file).
See [common.py](../../../cli/administration/deploy/development/common.py) and [compose.yml.md](../artefact/files/compose.yml.md).
- **Computed at container start** for `INFINITO_PATH`, set by [entry.sh](../../../scripts/docker/entry.sh).
- **Persisted on the deploy target** for `INFINITO_VERSION`, written by the [sys-version](../../../roles/sys-version/) role.
@@ -93,10 +93,10 @@ Therefore:
### Env defaults
- [ ] `scripts/meta/env/cache/package.sh` exists and exports
`INFINITO_PACKAGE_CACHE_HOST_PATH`, `INFINITO_PACKAGE_CACHE_HEAP`,
`INFINITO_PACKAGE_CACHE_DIRECT_MEM`,
`INFINITO_PACKAGE_CACHE_BLOBSTORE_MAX`, and
`INFINITO_PACKAGE_CACHE_ADMIN_PASSWORD`.
`INFINITO_CACHE_PACKAGE_HOST_PATH`, `INFINITO_CACHE_PACKAGE_HEAP`,
`INFINITO_CACHE_PACKAGE_DIRECT_MEM`,
`INFINITO_CACHE_PACKAGE_BLOBSTORE_MAX`, and
`INFINITO_CACHE_PACKAGE_ADMIN_PASSWORD`.
- [ ] `scripts/meta/env/cache/package.sh` is sourced by
`scripts/meta/env/all.sh`.
- [ ] `compose/cache.override.yml` consumes every package-cache env
@@ -109,11 +109,11 @@ Therefore:
(repeated runs MUST NOT error on already-existing repos), and is
invoked once on first start under the `cache` profile.
- [ ] On first start, the auto-generated Nexus admin password is
rotated to the value of `INFINITO_PACKAGE_CACHE_ADMIN_PASSWORD`.
rotated to the value of `INFINITO_CACHE_PACKAGE_ADMIN_PASSWORD`.
- [ ] On first start, the proxy repositories listed in
[cache.md](../contributing/environment/cache.md) are created.
- [ ] The blobstore `default` is created with quota
`INFINITO_PACKAGE_CACHE_BLOBSTORE_MAX`.
`INFINITO_CACHE_PACKAGE_BLOBSTORE_MAX`.
### Client wiring
@@ -196,7 +196,7 @@ These commits are not per-role fixes but address the underlying environment so s
| --- | --- | --- |
| `c6affc96f` | New entity-keyed purge primitive `scripts/container/purge/entity/network.sh` removes the per-entity default Docker network when `compose down` left it behind with the wrong / empty `com.docker.compose.network` label. `scripts/container/purge/apps.sh` runs the primitive per entity and then issues a single bare `docker network prune -f` after the entity loop. | C9b (gitea), C12 (matrix); any future role hitting the same orphan-network signature. |
| `6d61e3ca4` | Source-able helper `scripts/tests/deploy/local/utils/cache-retry.sh` wraps the deploy command in each local deploy entrypoint (`fresh-purged-app.sh`, `fresh-kept-app.sh`, `fresh-kept-all.sh`, `reuse-kept-app.sh`, `reuse-kept-all.sh`). Detects the stale-apt signature `Release file ... is expired` / `Valid-Until ... expired` in the wrapped command's output, runs `make cache-clean` + `docker builder prune -af` + `docker image prune -af`, then re-runs the command exactly once. | Defensive fallback for "apt Release file ... is expired" Docker-build failures during local iteration. |
| `e58f71987` | Default `INFINITO_PACKAGE_CACHE_MAX_AGE_MIN` lowered 129600 → 8640 (90 days → 6 days). Nexus now re-validates every cached `Release` against upstream before apt would see it expired (Debian/Ubuntu emit `Valid-Until` 7 days from generation). Applies to every Nexus proxy repo via `contentMaxAge` / `metadataMaxAge` / `negativeCache.timeToLive`. | Prevents the underlying stale-apt failure from reaching the deploy in the first place; the cache-retry helper from `6d61e3ca4` is the secondary defence. |
| `e58f71987` | Default `INFINITO_CACHE_PACKAGE_MAX_AGE_MIN` lowered 129600 → 8640 (90 days → 6 days). Nexus now re-validates every cached `Release` against upstream before apt would see it expired (Debian/Ubuntu emit `Valid-Until` 7 days from generation). Applies to every Nexus proxy repo via `contentMaxAge` / `metadataMaxAge` / `negativeCache.timeToLive`. | Prevents the underlying stale-apt failure from reaching the deploy in the first place; the cache-retry helper from `6d61e3ca4` is the secondary defence. |
## CI Run 25890032686 follow-up snapshot
+48 -27
View File
@@ -22,6 +22,48 @@ INFINITO_BIND_IP=127.0.0.1
# Comma-separated inventory bundles consumed by 'make deploy-bundles' / 'make redeploy-bundles'.
INFINITO_BUNDLES=
# Bind-mounted apt sources.list fragment for the package-cache frontend.
INFINITO_CACHE_PACKAGE_APT_LIST=./compose/package-cache/apt.list
# Host directory holding the package-cache frontend CA materials.
INFINITO_CACHE_PACKAGE_FRONTEND_CA_DIR=/var/cache/infinito/core/cache/package/frontend/ca
# CA certificate file produced under INFINITO_CACHE_PACKAGE_FRONTEND_CA_DIR.
INFINITO_CACHE_PACKAGE_FRONTEND_CA_FILE=/var/cache/infinito/core/cache/package/frontend/ca/ca.crt
# Host directory holding the package-cache frontend TLS certificates.
INFINITO_CACHE_PACKAGE_FRONTEND_CERTS_DIR=/var/cache/infinito/core/cache/package/frontend/certs
# Init image used by the package-cache frontend cert-gen wrapper to run cert-gen.sh.
INFINITO_CACHE_PACKAGE_FRONTEND_INIT_IMAGE=alpine:3.20
# Static IPv4 address the package-cache frontend takes on the docker bridge.
INFINITO_CACHE_PACKAGE_FRONTEND_IP=172.30.0.4
# Host directory backing the Nexus 3 package-cache blobstore.
INFINITO_CACHE_PACKAGE_HOST_PATH=/var/cache/infinito/core/cache/package/data
# Cache freshness window (minutes) Nexus tolerates before re-validating upstream. 8640 = 6 days (below the 7-day Debian Valid-Until).
INFINITO_CACHE_PACKAGE_MAX_AGE_MIN=8640
# Bind-mounted .npmrc fragment for the package-cache frontend.
INFINITO_CACHE_PACKAGE_NPMRC=./compose/package-cache/npmrc
# Bind-mounted pip.conf fragment for the package-cache frontend.
INFINITO_CACHE_PACKAGE_PIP_CONF=./compose/package-cache/pip.conf
# Host port the Nexus 3 package cache binds to on BIND_IP.
INFINITO_CACHE_PACKAGE_PORT=8081
# Host directory holding the registry-cache CA materials.
INFINITO_CACHE_REGISTRY_CA_HOST_PATH=/var/cache/infinito/core/cache/registry/ca
# Host directory backing the docker registry-mirror cache (bind mount).
INFINITO_CACHE_REGISTRY_HOST_PATH=/var/cache/infinito/core/cache/registry/mirror
# Bind-mounted registry-cache proxy configuration consumed by the cache container.
INFINITO_CACHE_REGISTRY_PROXY_CONF=./compose/registry-cache/proxy.conf
# One-shot command 'make exec INFINITO_CMD=...' runs inside the dev container (empty -> interactive shell).
INFINITO_CMD=
@@ -49,6 +91,12 @@ INFINITO_DISTRO=debian
# Space-separated list of distros the build/test matrix iterates over.
INFINITO_DISTROS="arch debian ubuntu fedora centos"
# Domain the host-side DNS-fallback / dnsmasq config wires up (consumed by scripts/system/network/dns/).
INFINITO_DNS_DOMAIN=infinito.example
# Path to the hosts file the DNS-fallback installer writes the BEGIN/END block into.
INFINITO_DNS_HOSTS_FILE=/etc/hosts
# Mount point inside the infinito container where INFINITO_DOCKER_VOLUME lands.
INFINITO_DOCKER_MOUNT=/var/lib/docker
@@ -88,36 +136,9 @@ INFINITO_NO_BUILD=1
# MTU of the compose stack's bridge network; 1500 matches most hosts.
INFINITO_OUTER_NETWORK_MTU=1500
# Host directory holding the package-cache frontend CA materials.
INFINITO_PACKAGE_CACHE_FRONTEND_CA_DIR=/var/cache/infinito/core/cache/package/frontend/ca
# Host directory holding the package-cache frontend TLS certificates.
INFINITO_PACKAGE_CACHE_FRONTEND_CERTS_DIR=/var/cache/infinito/core/cache/package/frontend/certs
# Init image used by the package-cache frontend cert-gen wrapper to run cert-gen.sh.
INFINITO_PACKAGE_CACHE_FRONTEND_INIT_IMAGE=alpine:3.20
# Static IPv4 address the package-cache frontend takes on the docker bridge.
INFINITO_PACKAGE_CACHE_FRONTEND_IP=172.30.0.4
# Host directory backing the Nexus 3 package-cache blobstore.
INFINITO_PACKAGE_CACHE_HOST_PATH=/var/cache/infinito/core/cache/package/data
# Cache freshness window (minutes) Nexus tolerates before re-validating upstream. 8640 = 6 days (below the 7-day Debian Valid-Until).
INFINITO_PACKAGE_CACHE_MAX_AGE_MIN=8640
# Host port the Nexus 3 package cache binds to on BIND_IP.
INFINITO_PACKAGE_CACHE_PORT=8081
# Docker pull policy compose applies; generator overrides to `always` on GHA.
INFINITO_PULL_POLICY=never
# Host directory holding the registry-cache CA materials.
INFINITO_REGISTRY_CACHE_CA_HOST_PATH=/var/cache/infinito/core/cache/registry/ca
# Host directory backing the docker registry-mirror cache (bind mount).
INFINITO_REGISTRY_CACHE_HOST_PATH=/var/cache/infinito/core/cache/registry/mirror
# True when running under nektos/act locally; generator overrides via runtime detection.
INFINITO_RUNNING_ON_ACT=false
+1 -1
View File
@@ -89,7 +89,7 @@ _CACHE_HTTP_HOSTNAMES = (
def generate_cache_override(project_dir: Path, base_compose: Path) -> Path | None:
"""Emit transient build.extra_hosts override when cache profile active."""
cache_ip = (os.environ.get("INFINITO_PACKAGE_CACHE_FRONTEND_IP") or "").strip()
cache_ip = (os.environ.get("INFINITO_CACHE_PACKAGE_FRONTEND_IP") or "").strip()
if not cache_ip or not base_compose.is_file():
return None
+1 -1
View File
@@ -447,7 +447,7 @@ def main() -> int:
return 2
if cmd == "run":
no_ca = os.environ.get("INFINITO_CONTAINER_NO_CA", "").lower() in {
no_ca = os.environ["INFINITO_CONTAINER_NO_CA"].lower() in {
"1",
"true",
"yes",
+6 -6
View File
@@ -5,8 +5,8 @@
set -euo pipefail
: "${INFINITO_PACKAGE_CACHE_FRONTEND_CA_DIR:?Source scripts/meta/env/load.sh first}"
: "${INFINITO_PACKAGE_CACHE_FRONTEND_CERTS_DIR:?Source scripts/meta/env/load.sh first}"
: "${INFINITO_CACHE_PACKAGE_FRONTEND_CA_DIR:?Source scripts/meta/env/load.sh first}"
: "${INFINITO_CACHE_PACKAGE_FRONTEND_CERTS_DIR:?Source scripts/meta/env/load.sh first}"
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
INNER_SCRIPT="${REPO_ROOT}/compose/package-cache-frontend/cert-gen.sh"
@@ -16,12 +16,12 @@ if [[ ! -r "${INNER_SCRIPT}" ]]; then
exit 1
fi
: "${INFINITO_PACKAGE_CACHE_FRONTEND_INIT_IMAGE:?Source scripts/meta/env/load.sh first}"
ALPINE_IMAGE="${INFINITO_PACKAGE_CACHE_FRONTEND_INIT_IMAGE}"
: "${INFINITO_CACHE_PACKAGE_FRONTEND_INIT_IMAGE:?Source scripts/meta/env/load.sh first}"
ALPINE_IMAGE="${INFINITO_CACHE_PACKAGE_FRONTEND_INIT_IMAGE}"
exec docker run --rm \
-v "${INFINITO_PACKAGE_CACHE_FRONTEND_CA_DIR}:/ca" \
-v "${INFINITO_PACKAGE_CACHE_FRONTEND_CERTS_DIR}:/certs" \
-v "${INFINITO_CACHE_PACKAGE_FRONTEND_CA_DIR}:/ca" \
-v "${INFINITO_CACHE_PACKAGE_FRONTEND_CERTS_DIR}:/certs" \
-v "${INNER_SCRIPT}:/work/cert-gen.sh:ro" \
--entrypoint /bin/sh \
"${ALPINE_IMAGE}" \
+12 -12
View File
@@ -5,12 +5,12 @@
set -euo pipefail
: "${INFINITO_PACKAGE_CACHE_HOST_PATH:?Source scripts/meta/env/load.sh first}"
: "${INFINITO_PACKAGE_CACHE_ADMIN_PASSWORD:?Source scripts/meta/env/load.sh first}"
: "${INFINITO_PACKAGE_CACHE_BLOBSTORE_MAX:?Source scripts/meta/env/load.sh first}"
: "${INFINITO_PACKAGE_CACHE_MAX_AGE_MIN:?Source scripts/meta/env/load.sh first}"
: "${INFINITO_CACHE_PACKAGE_HOST_PATH:?Source scripts/meta/env/load.sh first}"
: "${INFINITO_CACHE_PACKAGE_ADMIN_PASSWORD:?Source scripts/meta/env/load.sh first}"
: "${INFINITO_CACHE_PACKAGE_BLOBSTORE_MAX:?Source scripts/meta/env/load.sh first}"
: "${INFINITO_CACHE_PACKAGE_MAX_AGE_MIN:?Source scripts/meta/env/load.sh first}"
CACHE_MAX_AGE_MIN="${INFINITO_PACKAGE_CACHE_MAX_AGE_MIN}"
CACHE_MAX_AGE_MIN="${INFINITO_CACHE_PACKAGE_MAX_AGE_MIN}"
# REST goes via `docker exec curl` to bypass sandbox network isolation.
PKGCACHE_CONTAINER="infinito-package-cache"
@@ -39,7 +39,7 @@ rotate_admin_password() {
ADMIN_USER="admin"
if docker exec "${PKGCACHE_CONTAINER}" test -f "${BOOTSTRAP_DONE_FILE}"; then
log "Already bootstrapped; using stored admin password"
ADMIN_PASS="${INFINITO_PACKAGE_CACHE_ADMIN_PASSWORD}"
ADMIN_PASS="${INFINITO_CACHE_PACKAGE_ADMIN_PASSWORD}"
return 0
fi
@@ -47,24 +47,24 @@ rotate_admin_password() {
if ! nexus_curl -fsS -u "admin:${NEXUS_DEFAULT_PASSWORD}" \
-H "Content-Type: text/plain" \
-X PUT "${NEXUS_REST}/v1/security/users/admin/change-password" \
--data-binary "${INFINITO_PACKAGE_CACHE_ADMIN_PASSWORD}"; then
--data-binary "${INFINITO_CACHE_PACKAGE_ADMIN_PASSWORD}"; then
# Recover from a partially-completed previous bootstrap.
if nexus_curl -fsS -o /dev/null -u "admin:${INFINITO_PACKAGE_CACHE_ADMIN_PASSWORD}" \
if nexus_curl -fsS -o /dev/null -u "admin:${INFINITO_CACHE_PACKAGE_ADMIN_PASSWORD}" \
"${NEXUS_REST}/v1/repositories" >/dev/null 2>&1; then
log "Default rotation rejected but configured password authenticates; assuming previous bootstrap, marking done"
else
log "Cannot authenticate with default '${NEXUS_DEFAULT_PASSWORD}' nor with configured INFINITO_PACKAGE_CACHE_ADMIN_PASSWORD."
log "Wipe ${INFINITO_PACKAGE_CACHE_HOST_PATH} (e.g. via 'make cache-clean') and retry."
log "Cannot authenticate with default '${NEXUS_DEFAULT_PASSWORD}' nor with configured INFINITO_CACHE_PACKAGE_ADMIN_PASSWORD."
log "Wipe ${INFINITO_CACHE_PACKAGE_HOST_PATH} (e.g. via 'make cache-clean') and retry."
return 1
fi
fi
docker exec "${PKGCACHE_CONTAINER}" touch "${BOOTSTRAP_DONE_FILE}"
ADMIN_PASS="${INFINITO_PACKAGE_CACHE_ADMIN_PASSWORD}"
ADMIN_PASS="${INFINITO_CACHE_PACKAGE_ADMIN_PASSWORD}"
}
ensure_blobstore() {
# Nexus quota expects MB; convert from "Ng".
local quota_gb="${INFINITO_PACKAGE_CACHE_BLOBSTORE_MAX%g}"
local quota_gb="${INFINITO_CACHE_PACKAGE_BLOBSTORE_MAX%g}"
local quota_mb=$((quota_gb * 1024))
local payload
payload="$(printf '{"name":"default","path":"default","softQuota":{"type":"spaceUsedQuota","limit":%d}}' "${quota_mb}")"
+7 -7
View File
@@ -10,7 +10,7 @@ fi
# Compute dynamically if not provided from outside
echo "[docker-infinito] before pkgmgr path"
INFINITO_PATH="$(pkgmgr path infinito)"
echo "[docker-infinito] after pkgmgr path: ${INFINITO_PATH}"
echo "[docker-infinito] after pkgmgr path: ${INFINITO_PATH}" # nocheck: container-bootstrap
INFINITO_SRC_DIR="/opt/src/infinito"
export INFINITO_PATH
@@ -18,13 +18,13 @@ export INFINITO_SRC_DIR
run_local_build() {
echo "[docker-infinito] Build enabled (INFINITO_COMPILE=1)"
echo "[docker-infinito] Using ${INFINITO_PATH} as working directory"
echo "[docker-infinito] Using ${INFINITO_PATH} as working directory" # nocheck: container-bootstrap
mkdir -p "${INFINITO_PATH}"
cd "${INFINITO_PATH}"
mkdir -p "${INFINITO_PATH}" # nocheck: container-bootstrap
cd "${INFINITO_PATH}" # nocheck: container-bootstrap
echo "[docker-infinito] Copy ${INFINITO_SRC_DIR} to ${INFINITO_PATH}..."
rsync -a --delete --chown=root:root --exclude='.git' "${INFINITO_SRC_DIR}/" "${INFINITO_PATH}/"
echo "[docker-infinito] Copy ${INFINITO_SRC_DIR} to ${INFINITO_PATH}..." # nocheck: container-bootstrap
rsync -a --delete --chown=root:root --exclude='.git' "${INFINITO_SRC_DIR}/" "${INFINITO_PATH}/" # nocheck: container-bootstrap
echo "[docker-infinito] Reinstall via 'make install'..."
make install
@@ -51,6 +51,6 @@ if [[ $# -eq 0 ]]; then
echo "[docker-infinito] No arguments provided. Showing infinito help..."
exec infinito --help
else
cd "${INFINITO_PATH}"
cd "${INFINITO_PATH}" # nocheck: container-bootstrap
exec "$@"
fi
+2 -2
View File
@@ -30,10 +30,10 @@
# short-circuit here when we see it, so subprocesses skip the load
# entirely during generation.
if [[ "${INFINITO_ENV_LOADED:-}" == "1" ]]; then
if [[ "${INFINITO_ENV_LOADED:-}" == "1" ]]; then # nocheck: env-loader-internal-guard
return 0
fi
if [[ "${INFINITO_ENV_GENERATING:-}" == "1" ]]; then
if [[ "${INFINITO_ENV_GENERATING:-}" == "1" ]]; then # nocheck: env-loader-internal-guard
return 0
fi
+3 -3
View File
@@ -37,9 +37,9 @@ if docker ps --format '{{.Names}}' | grep -qE '^infinito-(registry|package)-cach
fi
PATHS=(
"${INFINITO_REGISTRY_CACHE_HOST_PATH}"
"${INFINITO_REGISTRY_CACHE_CA_HOST_PATH}"
"${INFINITO_PACKAGE_CACHE_HOST_PATH}"
"${INFINITO_CACHE_REGISTRY_HOST_PATH}"
"${INFINITO_CACHE_REGISTRY_CA_HOST_PATH}"
"${INFINITO_CACHE_PACKAGE_HOST_PATH}"
)
# Remove each cache path individually (keeps the parent /var/cache/
+4 -7
View File
@@ -2,8 +2,8 @@
DNS_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DNS_PROJECT_ROOT="$(cd "${DNS_SCRIPT_DIR}/../../../.." && pwd)"
DNS_DOMAIN="${INFINITO_DNS_DOMAIN:-infinito.example}" # nocheck: host-dns-override -- run with sudo outside load.sh context; tweak the host's /etc/hosts only
DNS_HOSTS_FILE="${INFINITO_DNS_HOSTS_FILE:-/etc/hosts}" # nocheck: host-dns-override -- run with sudo outside load.sh context; tweak the host's /etc/hosts only
DNS_DOMAIN="${INFINITO_DNS_DOMAIN}"
DNS_HOSTS_FILE="${INFINITO_DNS_HOSTS_FILE}"
DNS_HOSTS_BLOCK_BEGIN="# BEGIN infinito-dns-fallback"
DNS_HOSTS_BLOCK_END="# END infinito-dns-fallback"
DNS_HOSTS_GENERATOR="${DNS_PROJECT_ROOT}/cli/meta/domains/__main__.py"
@@ -19,9 +19,6 @@ DNS_NM_DNSMASQ_CONF="${DNS_NM_DNSMASQ_DIR}/${DNS_DOMAIN}.conf"
DNS_SYS_DNSMASQ_CONF="/etc/dnsmasq.d/${DNS_DOMAIN}.conf"
dns_systemd_is_operational() {
if [[ "${INFINITO_DNS_FORCE_HOSTS_FALLBACK:-0}" == "1" ]]; then # nocheck: host-dns-override -- run with sudo outside load.sh context
return 1
fi
command -v systemctl >/dev/null 2>&1 && [[ -d /run/systemd/system ]]
}
@@ -87,8 +84,8 @@ dns_read_generated_hosts_fallback_entries() {
}
dns_read_hosts_fallback_entries() {
if [[ -n "${INFINITO_DNS_HOSTS:-}" ]]; then
dns_read_hosts_fallback_entries_from_raw "${INFINITO_DNS_HOSTS}"
if [[ -n "${INFINITO_DNS_HOSTS:-}" ]]; then # nocheck: infinito-dns-hosts-optional
dns_read_hosts_fallback_entries_from_raw "${INFINITO_DNS_HOSTS}" # nocheck: infinito-dns-hosts-optional
return
fi
+2 -2
View File
@@ -6,7 +6,7 @@
set -euo pipefail
: "${INFINITO_TEST_PATTERN:?INFINITO_TEST_PATTERN must be set}"
: "${INFINITO_TEST_TYPE:?INFINITO_TEST_TYPE must be set}"
: "${INFINITO_TEST_TYPE:?INFINITO_TEST_TYPE must be set}" # nocheck: makefile-supplied
NIX_CONFIG_EFFECTIVE="$(
printf "%s\n%s\n" \
@@ -25,4 +25,4 @@ if [ -n "${PYTHON:-}" ]; then
fi
make setup
"${PYTHON:-python3}" -m unittest discover -s "tests/${INFINITO_TEST_TYPE}" -t . -p "${INFINITO_TEST_PATTERN}"
"${PYTHON:-python3}" -m unittest discover -s "tests/${INFINITO_TEST_TYPE}" -t . -p "${INFINITO_TEST_PATTERN}" # nocheck: makefile-supplied
+18 -11
View File
@@ -19,7 +19,7 @@ source scripts/meta/env/load.sh
: "${INFINITO_TEST_PATTERN:?INFINITO_TEST_PATTERN must be set}"
: "${INFINITO_TEST_RUNNER:?INFINITO_TEST_RUNNER must be set}"
: "${INFINITO_TEST_TYPE:?INFINITO_TEST_TYPE must be set}"
: "${INFINITO_TEST_TYPE:?INFINITO_TEST_TYPE must be set}" # nocheck: makefile-supplied
RUN_SCRIPT_IN_CONTAINER="${INFINITO_SRC_DIR}/scripts/tests/code/run.sh"
@@ -28,7 +28,7 @@ docker)
# INFINITO_DISTRO is sourced (with a `debian` default) from
# scripts/meta/env/load.sh above, so no explicit check here.
echo "============================================================"
echo ">>> Running ${INFINITO_TEST_TYPE^^} tests in ${INFINITO_DISTRO} container (compose stack)"
echo ">>> Running ${INFINITO_TEST_TYPE^^} tests in ${INFINITO_DISTRO} container (compose stack)" # nocheck: makefile-supplied
echo "============================================================"
# Auto-bring-up: if the `infinito` container is not running, kick off
@@ -48,23 +48,30 @@ docker)
# every INFINITO_* key from the bind-mounted .env into the test
# runner's environment (notably INFINITO_WORKER_FETCH, otherwise the
# external URL probe collapses to 1 worker).
# Per-invocation env passed through docker exec. Built as a bash
# array so the inline `# nocheck:` markers between elements stay
# syntactically valid (a backslash-continued line cannot host an
# inline comment).
exec_env_args=(
-e ACT="${ACT:-}"
-e BASH_ENV="${INFINITO_SRC_DIR}/scripts/meta/env/load.sh"
-e GITHUB_ACTIONS="${GITHUB_ACTIONS:-}"
-e GITHUB_REPOSITORY="${GITHUB_REPOSITORY:-}"
-e GITHUB_REPOSITORY_OWNER="${GITHUB_REPOSITORY_OWNER:-}"
-e GITHUB_SHA="${GITHUB_SHA:-}"
-e INFINITO_TEST_PATTERN="${INFINITO_TEST_PATTERN}"
-e INFINITO_TEST_TYPE="${INFINITO_TEST_TYPE}" # nocheck: makefile-supplied
)
INFINITO_DISTRO="${INFINITO_DISTRO}" \
docker compose --env-file env/ci.env --env-file .env --profile ci exec -T \
-e ACT="${ACT:-}" \
-e BASH_ENV="${INFINITO_SRC_DIR}/scripts/meta/env/load.sh" \
-e GITHUB_ACTIONS="${GITHUB_ACTIONS:-}" \
-e GITHUB_REPOSITORY="${GITHUB_REPOSITORY:-}" \
-e GITHUB_REPOSITORY_OWNER="${GITHUB_REPOSITORY_OWNER:-}" \
-e GITHUB_SHA="${GITHUB_SHA:-}" \
-e INFINITO_TEST_PATTERN="${INFINITO_TEST_PATTERN}" \
-e INFINITO_TEST_TYPE="${INFINITO_TEST_TYPE}" \
"${exec_env_args[@]}" \
--workdir "${INFINITO_SRC_DIR}" \
infinito \
bash --login "${RUN_SCRIPT_IN_CONTAINER}"
;;
host)
echo "============================================================"
echo ">>> Running ${INFINITO_TEST_TYPE^^} tests on host"
echo ">>> Running ${INFINITO_TEST_TYPE^^} tests on host" # nocheck: makefile-supplied
echo "============================================================"
exec bash --login "${RUN_SCRIPT}"
;;
@@ -18,7 +18,7 @@ _cache_retry_nexus_invalidate() {
local rest="http://127.0.0.1:8081/service/rest/v1/repositories/${repo}/invalidate-cache"
local code
code="$(docker exec infinito-package-cache curl -sS -o /dev/null -w '%{http_code}' \
-u "admin:${INFINITO_PACKAGE_CACHE_ADMIN_PASSWORD}" \
-u "admin:${INFINITO_CACHE_PACKAGE_ADMIN_PASSWORD}" \
-X POST "${rest}" 2>/dev/null || echo 000)"
if [[ "${code}" =~ ^2[0-9][0-9]$ ]]; then
echo "[cache-retry] invalidate-cache OK (${code}): ${repo}"
@@ -30,7 +30,7 @@ _cache_retry_nexus_invalidate() {
_cache_retry_list_apt_proxy_repos() {
local json
json="$(docker exec infinito-package-cache curl -sS \
-u "admin:${INFINITO_PACKAGE_CACHE_ADMIN_PASSWORD}" \
-u "admin:${INFINITO_CACHE_PACKAGE_ADMIN_PASSWORD}" \
"http://127.0.0.1:8081/service/rest/v1/repositories" 2>/dev/null)" || return 1
[[ -z "${json}" ]] && return 1
printf '%s' "${json}" |
@@ -16,7 +16,7 @@ set -euo pipefail
cd /opt/src/infinito
: "${INFINITO_INVENTORY_FILE:?INFINITO_INVENTORY_FILE must be set}"
: "${INFINITO_INVENTORY_PASSWORD_FILE:?INFINITO_INVENTORY_PASSWORD_FILE must be set}"
: "${INFINITO_INVENTORY_PASSWORD_FILE:?INFINITO_INVENTORY_PASSWORD_FILE must be set}" # nocheck: driver-injected-per-invocation
: "${INFINITO_LIMIT_HOST:?INFINITO_LIMIT_HOST must be set}"
: "${INFINITO_DEBUG:?INFINITO_DEBUG must be set}"
@@ -29,7 +29,7 @@ cmd=(infinito administration deploy dedicated "${INFINITO_INVENTORY_FILE}"
-l "${INFINITO_LIMIT_HOST}"
--diff
-vv
--password-file "${INFINITO_INVENTORY_PASSWORD_FILE}"
--password-file "${INFINITO_INVENTORY_PASSWORD_FILE}" # nocheck: driver-injected-per-invocation
-e ASYNC_ENABLED=false
-e SYS_SERVICE_ALL_ENABLED=false
-e SYS_SERVICE_DEFAULT_STATE=started
+2 -2
View File
@@ -213,9 +213,9 @@ probe_caches() {
# Ansible-driven runner-side traffic alone does NOT exercise.
probe_did_inner_build() {
local frontend_ip
frontend_ip="${INFINITO_PACKAGE_CACHE_FRONTEND_IP:-}"
frontend_ip="${INFINITO_CACHE_PACKAGE_FRONTEND_IP:-}"
if [[ -z "${frontend_ip}" ]]; then
echo "[probe-did] INFINITO_PACKAGE_CACHE_FRONTEND_IP unset; skipping DiD probe"
echo "[probe-did] INFINITO_CACHE_PACKAGE_FRONTEND_IP unset; skipping DiD probe"
return 0
fi
+1 -1
View File
@@ -82,7 +82,7 @@ _WARNING_STATUS_CODES = {
}
_REQUEST_TIMEOUT_SECONDS = 10
_USER_AGENT = "infinito-nexus-url-reachability-check"
_MAX_WORKERS = int(os.environ.get("INFINITO_WORKER_FETCH", "1"))
_MAX_WORKERS = int(os.environ["INFINITO_WORKER_FETCH"])
# Hard ceiling for the whole probe loop. After this elapses, any probe
# still in flight is marked as a Timeout warning so the test never hangs
# indefinitely on a slow / trickling server. 5h30m matches the longest
+14 -14
View File
@@ -85,20 +85,20 @@ EXPECTED_BASELINE_KEYS = frozenset(
"PIP",
"PYTHONPATH",
"INVENTORY_VARS_FILE",
"INFINITO_REGISTRY_CACHE_HOST_PATH",
"INFINITO_REGISTRY_CACHE_CA_HOST_PATH",
"INFINITO_REGISTRY_CACHE_MAX_SIZE",
"INFINITO_PACKAGE_CACHE_HOST_PATH",
"INFINITO_PACKAGE_CACHE_PORT",
"INFINITO_PACKAGE_CACHE_HEAP",
"INFINITO_PACKAGE_CACHE_DIRECT_MEM",
"INFINITO_PACKAGE_CACHE_BLOBSTORE_MAX",
"INFINITO_PACKAGE_CACHE_ADMIN_PASSWORD",
"INFINITO_PACKAGE_CACHE_FRONTEND_CA_DIR",
"INFINITO_PACKAGE_CACHE_FRONTEND_CERTS_DIR",
"INFINITO_PACKAGE_CACHE_FRONTEND_IP",
"INFINITO_PACKAGE_CACHE_FRONTEND_INIT_IMAGE",
"INFINITO_PACKAGE_CACHE_MAX_AGE_MIN",
"INFINITO_CACHE_REGISTRY_HOST_PATH",
"INFINITO_CACHE_REGISTRY_CA_HOST_PATH",
"INFINITO_CACHE_REGISTRY_MAX_SIZE",
"INFINITO_CACHE_PACKAGE_HOST_PATH",
"INFINITO_CACHE_PACKAGE_PORT",
"INFINITO_CACHE_PACKAGE_HEAP",
"INFINITO_CACHE_PACKAGE_DIRECT_MEM",
"INFINITO_CACHE_PACKAGE_BLOBSTORE_MAX",
"INFINITO_CACHE_PACKAGE_ADMIN_PASSWORD",
"INFINITO_CACHE_PACKAGE_FRONTEND_CA_DIR",
"INFINITO_CACHE_PACKAGE_FRONTEND_CERTS_DIR",
"INFINITO_CACHE_PACKAGE_FRONTEND_IP",
"INFINITO_CACHE_PACKAGE_FRONTEND_INIT_IMAGE",
"INFINITO_CACHE_PACKAGE_MAX_AGE_MIN",
"INFINITO_IMAGE_TAG",
}
)
@@ -0,0 +1,80 @@
"""Every key in env/default.env must be wired through a handler builder.
env/default.env is the SPOT for static defaults. The handler builders
under ``utils/env/handlers/`` are responsible for applying each entry
as a ``setdefault`` on the EnvBuilder. If a key lives in default.env
but no handler references it, the generated ``.env`` will silently
miss the default and downstream consumers crash with an unbound
variable.
"""
from __future__ import annotations
import re
import unittest
from typing import TYPE_CHECKING
from utils.cache.files import read_text
from . import PROJECT_ROOT
if TYPE_CHECKING:
from pathlib import Path
_KEY_RE = re.compile(r"^\s*(?P<key>INFINITO_[A-Z0-9_]+)\s*=")
_HANDLER_LITERAL_RE = re.compile(r"[\"'](?P<key>INFINITO_[A-Z0-9_]+)[\"']")
def _default_env_keys(path: Path) -> set[str]:
return {
match.group("key")
for line in read_text(str(path)).splitlines()
if (match := _KEY_RE.match(line)) is not None
}
def _handler_referenced_keys(handlers_dir: Path) -> set[str]:
keys: set[str] = set()
for module in sorted(handlers_dir.glob("*.py")):
if module.name == "__init__.py":
continue
text = read_text(str(module))
for match in _HANDLER_LITERAL_RE.finditer(text):
keys.add(match.group("key"))
return keys
class TestDefaultEnvKeysHandled(unittest.TestCase):
def test_every_default_env_key_has_a_handler(self) -> None:
default_env_path = PROJECT_ROOT / "env" / "default.env"
handlers_dir = PROJECT_ROOT / "utils" / "env" / "handlers"
self.assertTrue(default_env_path.is_file(), "env/default.env not found")
self.assertTrue(handlers_dir.is_dir(), "utils/env/handlers/ not found")
declared = _default_env_keys(default_env_path)
self.assertTrue(declared, "env/default.env has no INFINITO_* entries")
referenced = _handler_referenced_keys(handlers_dir)
missing = sorted(declared - referenced)
if not missing:
return
lines = [
f"INFINITO_* keys in env/default.env are not wired through any handler "
f"({len(missing)} unhandled):",
"",
"env/default.env is the SPOT for static defaults; each key MUST be "
"applied via setdefault by a handler in utils/env/handlers/ "
"(typically the static-passthrough handler). Add the key to the "
"handler's STATIC_KEYS / KEY constant so `make dotenv` materialises "
"the default into the generated .env.",
"",
"Unhandled keys:",
]
lines.extend(f" {key}" for key in missing)
self.fail("\n".join(lines))
if __name__ == "__main__":
unittest.main()
+168
View File
@@ -0,0 +1,168 @@
"""Every INFINITO_* used in `.sh` / `.py` must be declared as a default.
Defaults live in ``env/default.env`` (the SPOT for static values) and
in the handler modules under ``utils/env/handlers/`` (which apply
dynamic defaults via ``setdefault``). Together they form the registry
of valid INFINITO_* keys.
This test scans every shell and Python source file for INFINITO_*
references and fails when a referenced key has no corresponding entry
in either the static file or a handler module. Such a key would reach
the consumer unset, and a bare read would crash with KeyError / set -u.
Allowed reference shapes (sampled, not exhaustive):
* shell: ``${INFINITO_VAR}``, ``${INFINITO_VAR:?msg}``, ``${INFINITO_VAR:-}``
* python: ``os.environ["INFINITO_VAR"]``, ``os.environ.get("INFINITO_VAR")``,
``env.get("INFINITO_VAR")``, etc.
Suppress per line with a same-line ``# nocheck: <reason>`` marker. Use
it only for keys that legitimately stay registry-less (e.g. pure
bootstrap one-shots).
"""
from __future__ import annotations
import re
import unittest
from dataclasses import dataclass
from typing import TYPE_CHECKING
from utils.cache.files import iter_non_ignored_files, read_text
from . import PROJECT_ROOT
if TYPE_CHECKING:
from pathlib import Path
_SH_REF_RE = re.compile(r"\$\{?(?P<key>INFINITO_[A-Z0-9_]+)")
_PY_LITERAL_RE = re.compile(r"[\"'](?P<key>INFINITO_[A-Z0-9_]+)[\"']")
_DEFAULT_ENV_KEY_RE = re.compile(r"^\s*(?P<key>INFINITO_[A-Z0-9_]+)\s*=")
_HANDLER_LITERAL_RE = re.compile(r"[\"'](?P<key>INFINITO_[A-Z0-9_]+)[\"']")
_NOCHECK_RE = re.compile(r"#\s*nocheck\b")
# These trees define the registry / document it; references in them
# describe the rule, they do not consume the keys.
_EXCLUDED_PREFIXES = (
"tests/lint/",
"utils/env/handlers/",
"env/",
)
@dataclass(frozen=True)
class _Reference:
file: str
line_no: int
key: str
def _registered_keys() -> set[str]:
keys: set[str] = set()
env_dir = PROJECT_ROOT / "env"
for env_file in sorted(env_dir.glob("*.env")):
for line in read_text(str(env_file)).splitlines():
match = _DEFAULT_ENV_KEY_RE.match(line)
if match is not None:
keys.add(match.group("key"))
handlers_dir = PROJECT_ROOT / "utils" / "env" / "handlers"
for module in sorted(handlers_dir.glob("*.py")):
if module.name == "__init__.py":
continue
text = read_text(str(module))
for match in _HANDLER_LITERAL_RE.finditer(text):
keys.add(match.group("key"))
return keys
def _strip_comment(line: str) -> str:
"""Drop a trailing `# ...` comment so INFINITO_* names mentioned only in
prose do not register as code references. Crude: a `#` inside a quoted
string also truncates, which is fine since INFINITO_* references inside
quoted strings stay intact in the kept prefix."""
idx = line.find("#")
return line if idx < 0 else line[:idx]
def _scan_with(path: Path, pattern: re.Pattern[str]) -> list[_Reference]:
rel = path.relative_to(PROJECT_ROOT).as_posix()
out: list[_Reference] = []
for idx, line in enumerate(read_text(str(path)).splitlines(), start=1):
if _NOCHECK_RE.search(line):
continue
code = _strip_comment(line)
out.extend(_Reference(rel, idx, m.group("key")) for m in pattern.finditer(code))
return out
def _scan_shell(path: Path) -> list[_Reference]:
return _scan_with(path, _SH_REF_RE)
def _scan_python(path: Path) -> list[_Reference]:
return _scan_with(path, _PY_LITERAL_RE)
def _scan_targets() -> tuple[list[Path], list[Path]]:
sh: list[Path] = []
py: list[Path] = []
root_str = str(PROJECT_ROOT)
for absolute in iter_non_ignored_files(root=root_str):
rel = absolute.removeprefix(root_str + "/")
if any(rel.startswith(prefix) for prefix in _EXCLUDED_PREFIXES):
continue
if rel.endswith(".sh"):
sh.append(PROJECT_ROOT / rel)
elif rel.endswith(".py"):
py.append(PROJECT_ROOT / rel)
return sh, py
class TestUsedKeysDeclared(unittest.TestCase):
def test_every_referenced_infinito_key_is_registered(self) -> None:
registered = _registered_keys()
self.assertTrue(registered, "no INFINITO_* keys found in the registry")
shell_files, python_files = _scan_targets()
self.assertTrue(shell_files or python_files, "no .sh / .py files to scan")
violations: list[_Reference] = []
for path in shell_files:
violations.extend(
ref for ref in _scan_shell(path) if ref.key not in registered
)
for path in python_files:
violations.extend(
ref for ref in _scan_python(path) if ref.key not in registered
)
if not violations:
return
unique_keys = sorted({v.key for v in violations})
grouped: dict[str, list[_Reference]] = {}
for v in violations:
grouped.setdefault(v.key, []).append(v)
lines = [
f"INFINITO_* keys referenced in code without a registry entry "
f"({len(unique_keys)} unknown key(s) across {len(violations)} use site(s)):",
"",
"Add a default to env/default.env (and to the static-passthrough handler's "
"STATIC_KEYS) for static values, or write a dedicated handler under "
"utils/env/handlers/ that sets the key via eb.setdefault. "
"Suppress per line with `# nocheck: <reason>` when the key legitimately "
"stays registry-less.",
"",
"Unregistered keys:",
]
for key in unique_keys:
lines.append(f" {key}:")
lines.extend(f" {ref.file}:{ref.line_no}" for ref in grouped[key])
self.fail("\n".join(lines))
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,144 @@
"""Lint guard: `.py` files MUST NOT declare INFINITO_* defaults in `.get(KEY, default)`.
The single source of truth for INFINITO_* defaults is ``env/default.env``
(materialised into ``.env`` by ``make dotenv`` and applied as
``setdefault`` by the handler builders in ``utils/env/handlers/``).
Hardcoded fallbacks in Python contradict that contract -- if the
default.env value drifts, the Python copy silently keeps the old one.
Allowed:
* Bare read: ``os.environ.get("INFINITO_VAR")`` (returns ``None`` when unset)
* Required-loud: ``os.environ["INFINITO_VAR"]`` (raises ``KeyError`` when unset)
Forbidden:
* Any default at all, including the empty string:
``os.environ.get("INFINITO_VAR", "")``,
``os.environ.get("INFINITO_VAR", "1")``,
``env.get("INFINITO_VAR", DEFAULT)``, etc.
Suppress per line with a same-line ``# nocheck: <reason>`` marker.
Use it only when the call genuinely cannot consume ``env/default.env``
(e.g. bootstrap code that runs before the env loader).
"""
from __future__ import annotations
import ast
import re
import unittest
from dataclasses import dataclass
from typing import TYPE_CHECKING
from utils.cache.files import iter_non_ignored_files, read_text
from . import PROJECT_ROOT
if TYPE_CHECKING:
from pathlib import Path
_INFINITO_KEY_RE = re.compile(r"^INFINITO_[A-Z0-9_]+$")
_NOCHECK_RE = re.compile(r"#\s*nocheck\b")
@dataclass(frozen=True)
class _Violation:
file: str
line_no: int
key: str
default_repr: str
def _scan_file(path: Path) -> list[_Violation]:
rel = path.relative_to(PROJECT_ROOT).as_posix()
try:
text = read_text(str(path))
tree = ast.parse(text, filename=rel)
except (OSError, UnicodeDecodeError, SyntaxError):
return []
raw_lines = text.splitlines()
violations: list[_Violation] = []
for node in ast.walk(tree):
if not isinstance(node, ast.Call):
continue
if not (isinstance(node.func, ast.Attribute) and node.func.attr == "get"):
continue
if len(node.args) < 2:
continue
key_node = node.args[0]
if not (isinstance(key_node, ast.Constant) and isinstance(key_node.value, str)):
continue
if not _INFINITO_KEY_RE.match(key_node.value):
continue
default_node = node.args[1]
# Allow per-line `# nocheck:` suppression on any physical line the
# call spans (autoformat may have split a one-liner across the
# opening paren, the args, and the closing paren).
line_no = node.lineno
end_line_no = getattr(node, "end_lineno", line_no) or line_no
if any(
1 <= n <= len(raw_lines) and _NOCHECK_RE.search(raw_lines[n - 1])
for n in range(line_no, end_line_no + 1)
):
continue
violations.append(
_Violation(
rel,
line_no,
key_node.value,
ast.unparse(default_node),
)
)
return violations
def _scan_targets() -> list[Path]:
return [
PROJECT_ROOT / rel
for rel in iter_non_ignored_files(root=str(PROJECT_ROOT))
if rel.endswith(".py")
]
class TestPythonNoInfinitoGetDefaults(unittest.TestCase):
def test_python_files_dont_declare_infinito_get_defaults(self) -> None:
targets = _scan_targets()
self.assertTrue(targets, "no .py files found to scan")
violations: list[_Violation] = []
for path in targets:
violations.extend(_scan_file(path))
if not violations:
return
grouped: dict[str, list[_Violation]] = {}
for v in violations:
grouped.setdefault(v.file, []).append(v)
lines = [
f"INFINITO_* defaults declared in `.get(KEY, default)` "
f"({len(violations)} violations across {len(grouped)} file(s)):",
"",
"INFINITO_* defaults belong in env/default.env (SPOT); the "
"handler builders in utils/env/handlers/ then apply them via "
"setdefault. Use `os.environ.get(KEY)` (None when unset) or "
"`os.environ[KEY]` (loud KeyError) instead -- the empty-string "
"fallback is also forbidden because it silently masks an unset "
"key. Suppress per line with `# nocheck: <reason>`.",
"",
"Offenders:",
]
for f, vs in sorted(grouped.items()):
lines.append(f" {f}:")
lines.extend(
f" line {v.line_no}: {v.key} default={v.default_repr}" for v in vs
)
self.fail("\n".join(lines))
if __name__ == "__main__":
unittest.main()
@@ -144,7 +144,6 @@ class TestComposeUpRetries(unittest.TestCase):
)
self.assertEqual(env["INFINITO_DISTRO"], "arch")
self.assertNotIn("COMPOSE_PROFILES", env)
self.assertNotIn("INFINITO_REGISTRY_CACHE_PROXY_CONF", env)
@patch.dict(
os.environ,
@@ -153,8 +152,6 @@ class TestComposeUpRetries(unittest.TestCase):
"GITHUB_ACTIONS": "",
"INFINITO_RUNNING_ON_GITHUB": "",
"CI": "",
# Tests bypass BASH_ENV; set explicitly so cache_env_overrides() passes.
"INFINITO_PACKAGE_CACHE_FRONTEND_CA_DIR": "/var/cache/infinito/test/ca",
},
clear=False,
)
@@ -189,11 +186,13 @@ class TestComposeUpRetries(unittest.TestCase):
],
)
self.assertEqual(
env["INFINITO_REGISTRY_CACHE_PROXY_CONF"],
env["INFINITO_CACHE_REGISTRY_PROXY_CONF"], # nocheck: test-fixture
"./compose/registry-cache/proxy.conf",
)
self.assertTrue(
env["INFINITO_PACKAGE_CACHE_FRONTEND_CA_FILE"].endswith("/ca.crt")
env["INFINITO_CACHE_PACKAGE_FRONTEND_CA_FILE"].endswith(
"/ca.crt"
) # nocheck: test-fixture
)
@patch.dict(
@@ -267,8 +266,6 @@ class TestComposeUpRetries(unittest.TestCase):
"CI": "",
"GITHUB_ACTIONS": "",
"INFINITO_RUNNING_ON_GITHUB": "",
# Tests bypass BASH_ENV; set explicitly so cache_env_overrides() passes.
"INFINITO_PACKAGE_CACHE_FRONTEND_CA_DIR": "/var/cache/infinito/test/ca",
},
clear=False,
)
@@ -48,7 +48,7 @@ class TestDevelopmentExec(unittest.TestCase):
compose = unittest.mock.Mock()
compose.exec.return_value.returncode = 0
env = {k: v for k, v in os.environ.items() if k != "INFINITO_SERVICES_DISABLED"}
env = {**os.environ, "INFINITO_SERVICES_DISABLED": ""}
with (
unittest.mock.patch(
"cli.administration.deploy.development.exec.make_compose",
@@ -71,7 +71,7 @@ class TestDevelopmentExec(unittest.TestCase):
compose = unittest.mock.Mock()
compose.exec.return_value.returncode = 0
env = {k: v for k, v in os.environ.items() if k != "INFINITO_SERVICES_DISABLED"}
env = {**os.environ, "INFINITO_SERVICES_DISABLED": ""}
with (
unittest.mock.patch(
"cli.administration.deploy.development.exec.make_compose",
@@ -103,7 +103,7 @@ class TestDevelopmentExec(unittest.TestCase):
compose = unittest.mock.Mock()
compose.exec.return_value.returncode = 0
env = {k: v for k, v in os.environ.items() if k != "INFINITO_SERVICES_DISABLED"}
env = {**os.environ, "INFINITO_SERVICES_DISABLED": ""}
with (
unittest.mock.patch(
"cli.administration.deploy.development.exec.make_compose",
@@ -12,7 +12,7 @@ class TestDetectOuterNetworkMtu(unittest.TestCase):
def test_prefers_explicit_outer_network_mtu_env(self) -> None:
self.assertEqual(detect_outer_network_mtu(), "1300")
@patch.dict(os.environ, {}, clear=True)
@patch.dict(os.environ, {"INFINITO_OUTER_NETWORK_MTU": ""}, clear=True)
@patch("cli.administration.deploy.development.network.Path.exists", autospec=True)
@patch(
"cli.administration.deploy.development.network.Path.read_text", autospec=True
@@ -27,7 +27,7 @@ class TestDetectOuterNetworkMtu(unittest.TestCase):
self.assertEqual(detect_outer_network_mtu(), "1400")
@patch.dict(os.environ, {}, clear=True)
@patch.dict(os.environ, {"INFINITO_OUTER_NETWORK_MTU": ""}, clear=True)
@patch("cli.administration.deploy.development.network.Path.exists", autospec=True)
@patch(
"cli.administration.deploy.development.network.Path.read_text", autospec=True
@@ -400,7 +400,7 @@ class TestApplyServicesDisabledFromEnv(unittest.TestCase):
def test_no_op_when_env_not_set(self):
self._write({"applications": {}})
env = {k: v for k, v in os.environ.items() if k != "INFINITO_SERVICES_DISABLED"}
env = {**os.environ, "INFINITO_SERVICES_DISABLED": ""}
with unittest.mock.patch.dict(os.environ, env, clear=True):
apply_services_disabled_from_env(self.host_vars, roles_dir=self.roles_dir)
self.assertEqual(self._read(), {"applications": {}})
@@ -20,7 +20,7 @@ def _resolved_repo_root() -> Path:
(used by some CI fixtures); otherwise the canonical ``PROJECT_ROOT``
constant from the package ``__init__.py`` is returned.
"""
env = os.environ.get("INFINITO_REPO_ROOT")
env = os.environ.get("INFINITO_REPO_ROOT") # nocheck: test-fixture
if env:
return Path(env).expanduser().resolve()
return PROJECT_ROOT
@@ -42,7 +42,7 @@ class TestComposeCaInject(unittest.TestCase):
"!",
"grep",
"-qF",
"INFINITO_TAIGA_AUTH_CONFIG",
"INFINITO_TAIGA_AUTH_CONFIG", # nocheck: test-fixture
"/taiga-back/settings/config.py",
";",
"then",
@@ -501,7 +501,7 @@ class TestComposeCaInject(unittest.TestCase):
"!",
"grep",
"-qF",
"INFINITO_TAIGA_AUTH_CONFIG",
"INFINITO_TAIGA_AUTH_CONFIG", # nocheck: test-fixture
"/taiga-back/settings/config.py",
";",
"then",
@@ -31,7 +31,7 @@ class TestInfinitoComposeWrapper(unittest.TestCase):
def setUp(self):
# Disable the wrapper's cache-override branch; tests use mocked is_file.
self._env_patch = patch.dict(
os.environ, {"INFINITO_PACKAGE_CACHE_FRONTEND_IP": ""}, clear=False
os.environ, {"INFINITO_CACHE_PACKAGE_FRONTEND_IP": ""}, clear=False
)
self._env_patch.start()
+3 -1
View File
@@ -20,7 +20,9 @@ COMMENT = (
def apply(eb: EnvBuilder, ctx: BuildContext) -> None:
if not ctx.on_gha:
return
repo_name = os.environ.get("INFINITO_IMAGE_REPOSITORY", "").strip()
repo_name = os.environ.get(
"INFINITO_IMAGE_REPOSITORY", ""
).strip() # nocheck: handler-bootstrap-read
if not repo_name:
repo_name = run_helper(
["bash", "scripts/meta/resolve/repository/name.sh"],
+3 -1
View File
@@ -27,7 +27,9 @@ HOST_VARS_FILE_COMMENT = (
def apply(eb: EnvBuilder, ctx: BuildContext) -> None:
inventory_dir = os.environ.get("INFINITO_INVENTORY_DIR", "").strip()
inventory_dir = os.environ.get(
"INFINITO_INVENTORY_DIR", ""
).strip() # nocheck: handler-bootstrap-read
if not inventory_dir:
inventory_dir = run_helper(
["bash", "scripts/inventory/resolve.sh"],
@@ -1,4 +1,4 @@
"""INFINITO_PACKAGE_CACHE_ADMIN_PASSWORD: stable per-host Nexus admin
"""INFINITO_CACHE_PACKAGE_ADMIN_PASSWORD: stable per-host Nexus admin
password: sha256(host_path:hostname), truncated to 32 hex chars."""
from __future__ import annotations
@@ -12,7 +12,7 @@ from utils.env.runtime import hostname
if TYPE_CHECKING:
from utils.env.builder import BuildContext, EnvBuilder
KEY = "INFINITO_PACKAGE_CACHE_ADMIN_PASSWORD"
KEY = "INFINITO_CACHE_PACKAGE_ADMIN_PASSWORD"
COMMENT = (
"Stable per-host Nexus admin password: sha256(host_path:hostname), "
"truncated to 32 hex chars."
@@ -24,6 +24,6 @@ def apply(eb: EnvBuilder, ctx: BuildContext) -> None:
if existing_pw:
eb.set(KEY, existing_pw, comment=COMMENT)
return
seed = f"{eb.get('INFINITO_PACKAGE_CACHE_HOST_PATH')}:{hostname()}"
seed = f"{eb.get('INFINITO_CACHE_PACKAGE_HOST_PATH')}:{hostname()}"
digest = hashlib.sha256(seed.encode("utf-8")).hexdigest()[:32]
eb.set(KEY, digest, comment=COMMENT)
+5 -5
View File
@@ -1,5 +1,5 @@
"""INFINITO_PACKAGE_CACHE_BLOBSTORE_MAX: Nexus blobstore quota (half of
free disk at INFINITO_PACKAGE_CACHE_HOST_PATH, floor 2g)."""
"""INFINITO_CACHE_PACKAGE_BLOBSTORE_MAX: Nexus blobstore quota (half of
free disk at INFINITO_CACHE_PACKAGE_HOST_PATH, floor 2g)."""
from __future__ import annotations
@@ -11,10 +11,10 @@ from utils.env.runtime import df_avail_gb
if TYPE_CHECKING:
from utils.env.builder import BuildContext, EnvBuilder
KEY = "INFINITO_PACKAGE_CACHE_BLOBSTORE_MAX"
KEY = "INFINITO_CACHE_PACKAGE_BLOBSTORE_MAX"
COMMENT = (
"Nexus blobstore quota (half of free disk at "
"INFINITO_PACKAGE_CACHE_HOST_PATH, floor 2g)."
"INFINITO_CACHE_PACKAGE_HOST_PATH, floor 2g)."
)
@@ -23,5 +23,5 @@ def apply(eb: EnvBuilder, ctx: BuildContext) -> None:
if existing_blob:
eb.set(KEY, existing_blob, comment=COMMENT)
return
avail = df_avail_gb(eb.get("INFINITO_PACKAGE_CACHE_HOST_PATH")) or 4
avail = df_avail_gb(eb.get("INFINITO_CACHE_PACKAGE_HOST_PATH")) or 4
eb.set(KEY, f"{max(avail // 2, 2)}g", comment=COMMENT)
+5 -5
View File
@@ -1,5 +1,5 @@
"""INFINITO_PACKAGE_CACHE_DIRECT_MEM: Nexus direct-memory budget; tied to
INFINITO_PACKAGE_CACHE_HEAP."""
"""INFINITO_CACHE_PACKAGE_DIRECT_MEM: Nexus direct-memory budget; tied to
INFINITO_CACHE_PACKAGE_HEAP."""
from __future__ import annotations
@@ -8,9 +8,9 @@ from typing import TYPE_CHECKING
if TYPE_CHECKING:
from utils.env.builder import BuildContext, EnvBuilder
KEY = "INFINITO_PACKAGE_CACHE_DIRECT_MEM"
COMMENT = "Nexus direct-memory budget; tied to INFINITO_PACKAGE_CACHE_HEAP."
KEY = "INFINITO_CACHE_PACKAGE_DIRECT_MEM"
COMMENT = "Nexus direct-memory budget; tied to INFINITO_CACHE_PACKAGE_HEAP."
def apply(eb: EnvBuilder, ctx: BuildContext) -> None:
eb.setdefault(KEY, eb.get("INFINITO_PACKAGE_CACHE_HEAP"), comment=COMMENT)
eb.setdefault(KEY, eb.get("INFINITO_CACHE_PACKAGE_HEAP"), comment=COMMENT)
+2 -2
View File
@@ -1,4 +1,4 @@
"""INFINITO_PACKAGE_CACHE_HEAP: Nexus JVM heap size (half of MemAvailable,
"""INFINITO_CACHE_PACKAGE_HEAP: Nexus JVM heap size (half of MemAvailable,
cap 2g, floor 1g)."""
from __future__ import annotations
@@ -11,7 +11,7 @@ from utils.env.runtime import mem_available_mb
if TYPE_CHECKING:
from utils.env.builder import BuildContext, EnvBuilder
KEY = "INFINITO_PACKAGE_CACHE_HEAP"
KEY = "INFINITO_CACHE_PACKAGE_HEAP"
COMMENT = "Nexus JVM heap size (half of MemAvailable, cap 2g, floor 1g)."
+5 -5
View File
@@ -1,5 +1,5 @@
"""INFINITO_REGISTRY_CACHE_MAX_SIZE: registry-cache soft cap (half of
free disk at INFINITO_REGISTRY_CACHE_HOST_PATH, floor 1g)."""
"""INFINITO_CACHE_REGISTRY_MAX_SIZE: registry-cache soft cap (half of
free disk at INFINITO_CACHE_REGISTRY_HOST_PATH, floor 1g)."""
from __future__ import annotations
@@ -11,15 +11,15 @@ from utils.env.runtime import df_avail_gb
if TYPE_CHECKING:
from utils.env.builder import BuildContext, EnvBuilder
KEY = "INFINITO_REGISTRY_CACHE_MAX_SIZE"
KEY = "INFINITO_CACHE_REGISTRY_MAX_SIZE"
COMMENT = (
"Registry-cache soft cap (half of free disk at "
"INFINITO_REGISTRY_CACHE_HOST_PATH, floor 1g)."
"INFINITO_CACHE_REGISTRY_HOST_PATH, floor 1g)."
)
def apply(eb: EnvBuilder, ctx: BuildContext) -> None:
host_path = eb.get("INFINITO_REGISTRY_CACHE_HOST_PATH")
host_path = eb.get("INFINITO_CACHE_REGISTRY_HOST_PATH")
if not host_path:
return
existing = os.environ.get(KEY, "").strip()
+16 -9
View File
@@ -15,6 +15,8 @@ STATIC_KEYS = (
"INFINITO_SUBNET",
"INFINITO_GATEWAY",
"INFINITO_DISTRO",
"INFINITO_DNS_DOMAIN",
"INFINITO_DNS_HOSTS_FILE",
"INFINITO_CONTAINER",
"INFINITO_CONTAINER_NO_CA",
"INFINITO_RUNNING_ON_ACT",
@@ -50,15 +52,20 @@ STATIC_KEYS = (
"INFINITO_VENV_NAME",
"PYTHONPATH",
"INVENTORY_VARS_FILE",
"INFINITO_REGISTRY_CACHE_HOST_PATH",
"INFINITO_REGISTRY_CACHE_CA_HOST_PATH",
"INFINITO_PACKAGE_CACHE_HOST_PATH",
"INFINITO_PACKAGE_CACHE_PORT",
"INFINITO_PACKAGE_CACHE_FRONTEND_CA_DIR",
"INFINITO_PACKAGE_CACHE_FRONTEND_CERTS_DIR",
"INFINITO_PACKAGE_CACHE_FRONTEND_IP",
"INFINITO_PACKAGE_CACHE_FRONTEND_INIT_IMAGE",
"INFINITO_PACKAGE_CACHE_MAX_AGE_MIN",
"INFINITO_CACHE_REGISTRY_HOST_PATH",
"INFINITO_CACHE_REGISTRY_CA_HOST_PATH",
"INFINITO_CACHE_PACKAGE_HOST_PATH",
"INFINITO_CACHE_PACKAGE_PORT",
"INFINITO_CACHE_PACKAGE_APT_LIST",
"INFINITO_CACHE_PACKAGE_FRONTEND_CA_DIR",
"INFINITO_CACHE_PACKAGE_FRONTEND_CA_FILE",
"INFINITO_CACHE_PACKAGE_FRONTEND_CERTS_DIR",
"INFINITO_CACHE_PACKAGE_FRONTEND_IP",
"INFINITO_CACHE_PACKAGE_FRONTEND_INIT_IMAGE",
"INFINITO_CACHE_PACKAGE_MAX_AGE_MIN",
"INFINITO_CACHE_PACKAGE_NPMRC",
"INFINITO_CACHE_PACKAGE_PIP_CONF",
"INFINITO_CACHE_REGISTRY_PROXY_CONF",
"INFINITO_IMAGE_TAG",
)
+1 -1
View File
@@ -67,4 +67,4 @@ def latest_semver(tags: list[str], depth: int, flavor: str = "") -> str | None:
def resolve_max_fetch_workers() -> int:
return int(os.environ.get("INFINITO_WORKER_FETCH", "1"))
return int(os.environ["INFINITO_WORKER_FETCH"])