feat(mumble F2-14c): drop cc-ci compose.host-ports.yml fork; deploy 0.2.0 base minimally, add native host-ports on upgrade-to-latest via new UPGRADE_EXTRA_ENV harness hook + COMPOSE_FILE-aware READY_PROBE/install skip

This commit is contained in:
autonomic-bot
2026-05-31 05:07:44 +00:00
parent e3720bedf3
commit 4bf9e1d43d
8 changed files with 101 additions and 80 deletions

View File

@ -81,8 +81,8 @@ def recipe_checkout(recipe: str, version: str) -> None:
path = os.path.expanduser(f"~/.abra/recipes/{recipe}")
# -f (force): the version-pinning checkout must yield the EXACT ref tree. Without it, a cc-ci
# install_steps-provided overlay (e.g. mumble's compose.host-ports.yml, copied into a version that
# predates it) is an UNTRACKED file that collides with the same path TRACKED in a later ref, and
# install_steps-provided overlay (e.g. discourse's compose.ccci.yml, copied into the pinned base)
# is an UNTRACKED file that collides with the same path TRACKED in a later ref, and
# `git checkout <ref>` aborts ("untracked working tree files would be overwritten"). Force resolves
# it by writing the ref's tracked version. Safe: we never want local recipe-tree state preserved
# across a version switch (and chaos deploys re-provide the overlay via install_steps when needed).
@ -137,6 +137,25 @@ def env_set(domain: str, key: str, value: str) -> None:
fh.write("\n".join(out) + "\n")
def env_get(domain: str, key: str) -> str | None:
"""Read a key from the app's .env (last uncommented assignment wins). None if absent. Symmetric
with env_set; abra has no getter. Strips surrounding quotes from the value."""
import os
import re
path = os.path.expanduser(f"~/.abra/servers/default/{domain}.env")
if not os.path.exists(path):
return None
pat = re.compile(rf"^\s*{re.escape(key)}=(.*)$")
val = None
with open(path) as fh:
for ln in fh.read().splitlines():
m = pat.match(ln)
if m:
val = m.group(1).strip().strip('"').strip("'")
return val
def secret_generate(domain: str, timeout: int = 300) -> None:
# -m avoids the TTY/table (ioctl) path; output (which contains the generated values) is
# captured by _run and never logged. -C -o keep the recipe at the PR checkout (without -o it

View File

@ -18,7 +18,7 @@ import socket
import ssl
import time
from . import lifecycle
from . import abra, lifecycle
# A recipe is backup-capable iff a compose file carries a truthy backupbot.backup label.
_BACKUPBOT_RE = re.compile(r"backupbot\.backup\b[^\n]*\btrue\b", re.IGNORECASE)
@ -244,6 +244,17 @@ def perform_upgrade(
before = lifecycle.deployed_identity(domain)
if head_ref:
lifecycle.recipe_checkout_ref(recipe, head_ref)
# UPGRADE_EXTRA_ENV (F2-14c): a recipe may need different app .env for the upgrade-TARGET deploy
# than for the base — e.g. mumble's `compose.host-ports.yml` overlay exists ONLY in the newer
# (target) version, so the base deploys minimally WITHOUT it and the upgrade adds it to COMPOSE_FILE
# here, after the PR-head checkout (which ships the overlay) and before the chaos redeploy that
# picks up the new .env. Dict or callable(domain)->dict. No-op for recipes without it.
upgrade_env = meta.get("UPGRADE_EXTRA_ENV") or {}
if callable(upgrade_env):
upgrade_env = upgrade_env(domain) or {}
for k, v in upgrade_env.items():
print(f" upgrade-env: {k}={v}", flush=True)
abra.env_set(domain, k, v)
# HQ1: warm the NEW-version image set before the chaos redeploy (the head_ref checkout's pinned
# tags) so a pull failure is a clear pre-deploy error and convergence isn't pull-bound.
lifecycle.prepull_images(recipe, domain)

View File

@ -231,10 +231,10 @@ def deploy_app(
flush=True,
)
chaos = True
# A recipe may force a chaos base deploy via recipe_meta CHAOS_BASE_DEPLOY=True when cc-ci adds
# an untracked compose overlay to the recipe checkout (e.g. mumble's host-ports.yml, provided
# by install_steps for older versions that predate it). The untracked file makes abra's
# pinned-deploy clean-tree check FATA ('has locally unstaged changes'); chaos skips lint +
# A recipe may force a chaos base deploy via recipe_meta CHAOS_BASE_DEPLOY=True when an
# install_steps hook adds an untracked compose overlay to the recipe checkout (e.g. discourse's
# compose.ccci.yml, provided by install_steps for the pinned base). The untracked file makes
# abra's pinned-deploy clean-tree check FATA ('has locally unstaged changes'); chaos skips lint +
# the clean-tree gate and deploys the EXPLICITLY-checked-out pinned version (we already ran
# recipe_checkout(version) above) — NOT latest. Same mechanism as the lightweight-tag branch.
elif _recipe_meta_flag(recipe, "CHAOS_BASE_DEPLOY"):

View File

@ -194,7 +194,7 @@ def _load_meta(recipe: str) -> dict:
ns: dict = {}
with open(path) as fh:
exec(compile(fh.read(), path, "exec"), ns) # noqa: S102 (trusted, in-repo)
for k in list(meta) + ["BACKUP_CAPABLE", "SKIP_GENERIC", "OIDC_AT_INSTALL", "READY_PROBE", "UPGRADE_BASE_VERSION", "BACKUP_VERIFY"]:
for k in list(meta) + ["BACKUP_CAPABLE", "SKIP_GENERIC", "OIDC_AT_INSTALL", "READY_PROBE", "UPGRADE_BASE_VERSION", "BACKUP_VERIFY", "UPGRADE_EXTRA_ENV"]:
if k in ns:
meta[k] = ns[k]
return meta