Files
computer-playbook/cli/administration/deploy/development/deploy/run.py
T
kevinveenbirkenbachandClaude Opus 5 a37039f0cb fix(web-app-docs): name the replayed role instead of taking the first one
The instructions test brings up a machine per role, so it can only ever
replay one guide. Which one it picked was a side effect of ordering: the
lookup returned the first invokable peer the round listed, and on a CI row
that is the role under test only because the row's apps happen to read
<role>,web-app-docs.

It now reads APP_ID, which the deploy workflow already sets to the row's app
and which the swarm routine already documents as the app under test. A round
that carries no APP_ID, or one whose app publishes no Production block, draws
an invokable peer at random instead, so a local round covers a different
guide each time rather than always the first one the inventory lists.

Ansible runs inside the infinito container rather than on the runner, and
that exec passes an explicit allowlist, so the name arrives only where it is
forwarded. Two seams carry it: _FORWARDED_ENV_KEYS, which covers CI and the
local initialize and reinstall verbs, and the update verb's own docker exec,
which assembles its arguments without that CLI.

The pin is tolerant rather than strict. APP_ID is set on every deploy row and
not only on documented ones, so refusing an app that publishes no guide would
let a generic variable abort a deploy.

Measured: the draw is observed over 200 rounds rather than assumed, and the
forwarding is negative-controlled, removing the key and watching the new test
fail on that key alone.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-28 09:16:29 +02:00

95 lines
2.3 KiB
Python

"""Execute one deploy pass inside the infinito container.
:func:`_run_deploy` is the SPOT for how a single
``cli.administration.deploy.dedicated`` invocation is assembled: the
inventory/password file layout, the ansible CLI flags, the ``-e`` var
encoding, and which host environment variables are forwarded into the
container.
"""
from __future__ import annotations
import json
import os
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from collections.abc import Mapping
_FORWARDED_ENV_KEYS = (
"APP_ID",
"GH_TOKEN",
"GITHUB_TOKEN",
"GITHUB_REPOSITORY_OWNER",
"GITHUB_REPOSITORY",
"INFINITO_IMAGE_TAG",
"INFINITO_GHCR_MIRROR_PREFIX",
)
def _run_deploy(
compose,
*,
deploy_ids: list[str],
debug: bool,
passthrough: list[str],
inventory_dir: str,
container_name: str,
extra_ansible_vars: Mapping[str, Any] | None = None,
) -> int:
inv_root = str(inventory_dir).rstrip("/")
inv_file = f"{inv_root}/devices.yml"
pw_file = f"{inv_root}/.password"
cmd = [
"python3",
"-m",
"cli.administration.deploy.dedicated",
inv_file,
"-p",
pw_file,
"-vv",
"--assert",
"true",
"--diff",
"--id",
*deploy_ids,
]
if debug:
cmd.insert(cmd.index("--diff") + 1, "--debug")
if extra_ansible_vars:
for key, value in extra_ansible_vars.items():
cmd.extend(["-e", f"{key}={json.dumps(value)}"])
if passthrough:
cmd.extend(passthrough)
extra_env: dict[str, str] = {
"ANSIBLE_FORCE_COLOR": "1",
"PY_COLORS": "1",
"TERM": "xterm-256color",
}
services_disabled = os.environ.get("disable", "")
if services_disabled:
extra_env["disable"] = services_disabled
ansible_log_path = (
os.environ.get("ANSIBLE_LOG_PATH") or "/tmp/infinito-deploy.log" # noqa: S108 - fixed fallback log path inside the ephemeral CI container
)
extra_env["ANSIBLE_LOG_PATH"] = ansible_log_path
for key in _FORWARDED_ENV_KEYS:
val = os.environ.get(key)
if val:
extra_env[key] = val
r = compose.exec(
cmd,
check=False,
live=True,
extra_env=extra_env,
)
return int(r.returncode)