Files
computer-playbook/cli/administration/deploy/ci/trigger/__main__.py
T
kevinveenbirkenbachandClaude Opus 5 0eb04b2239 feat(ci): retrigger failed roles on the distros the source run swept
The retrigger sent only whitelist and priority, so entry-manual.yml applied its
own distros default of 'debian'. A run that swept five distros came back as one,
and a failure that only reproduces on centos returned green with nothing fixed.

The dispatch inputs are not recoverable from the API - it answers inputs: null
for a finished workflow_dispatch - so they are read back from the run title.
The title format is not copied here: run_name.py parses the workflow's own
run-name declaration and derives the literals framing an input, and the tests
build their titles through the same helper instead of spelling one out.

Known gap: a run from another entry point carries an unrelated title and yields
no distros, leaving the retrigger on the workflow default. Job step names carry
the list for every entry point and arrive in the same gh call, so that is where
to look when it matters.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 02:13:31 +02:00

132 lines
4.2 KiB
Python

"""Trigger the manual CI run (entry-manual.yml) for the current branch.
entry-manual.yml reads the "__ALL__" whitelist sentinel as "force a full
deploy across all roles".
"""
from __future__ import annotations
import argparse
import sys
from cli.administration.deploy.ci import runs
_WORKFLOW = "entry-manual.yml"
_ALL = "__ALL__"
def main(argv: list[str] | None = None) -> int:
p = argparse.ArgumentParser(
prog="infinito administration deploy ci trigger",
description=(
"Dispatch the manual CI workflow for the branch you are on. "
"Default: trigger every role. With --failed: the roles that "
"failed in the last run form the priority line and the full "
"run follows once they are green. With --apps: an explicit "
"role list as whitelist."
),
)
group = p.add_mutually_exclusive_group()
group.add_argument(
"--failed",
nargs="?",
const="total",
default=None,
choices=("total", "swarm", "compose", "docker", "host"),
metavar="{total,swarm,compose,host}",
help=(
"Re-trigger roles that were not green in the last run as the "
"priority line; the remaining roles run after they succeed. "
"Optional scope: 'total' (default; failed in any mode), "
"'swarm', 'compose', or 'host'."
),
)
group.add_argument(
"--apps",
default=None,
metavar='"app1 app2 ..."',
help="Explicit space-separated role ids to trigger.",
)
p.add_argument(
"--run",
default=None,
metavar="URL|ID",
help=(
"Run URL or bare run id whose results determine the --failed apps "
"(a bare id resolves against the current branch's repo). Default: "
"the latest deploy run on the current branch."
),
)
p.add_argument(
"--strict",
action="store_true",
help=(
"With --failed: re-trigger only roles with a hard failure (❌), "
"not cancelled/aborted (🚫) or still-running (⏳)."
),
)
args = p.parse_args(argv)
if args.strict and args.failed is None:
p.error("--strict only applies with --failed")
branch = runs.current_branch()
repo = runs.resolve_repo()
whitelist = ""
priority = ""
distros = ""
if args.apps is not None:
apps = " ".join(args.apps.split())
if not apps:
p.error("--apps was empty")
whitelist = apps
elif args.failed is not None:
scope = "docker" if args.failed == "compose" else args.failed
if args.run:
if args.run.isdigit():
source = runs.fetch_run(args.run, repo=repo)
else:
source = runs.fetch_run(
runs.run_id_from_url(args.run), repo=runs.slug_from_url(args.run)
)
jobs = source.get("jobs", [])
title = source.get("displayTitle", "")
else:
run = runs.find_last_deploy_run(branch, repo=repo)
if run is None:
print(
f"No CI run with deploy jobs found on {repo}@{branch}.",
file=sys.stderr,
)
return 1
jobs = run["_jobs"]
title = run.get("displayTitle", "")
failed = runs.failed_roles(
runs.parse_role_statuses(jobs), scope, strict=args.strict
)
if not failed:
print(f"Nothing failed ({args.failed}) in that run; not triggering.")
return 0
priority = " ".join(failed)
distros = runs.distros_from_title(title)
else:
whitelist = _ALL
if priority:
label = f"priority {priority}, then the remaining roles"
elif whitelist == _ALL:
label = "all roles"
else:
label = whitelist
on_distros = f" on {distros}" if distros else ""
print(f"Triggering {_WORKFLOW} on {repo}@{branch} for: {label}{on_distros}")
runs.dispatch_workflow(
_WORKFLOW, branch, whitelist, priority=priority, distros=distros, repo=repo
)
print("Dispatched. Watch with: infinito administration deploy ci status")
return 0
if __name__ == "__main__":
raise SystemExit(main())