mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-09 12:17:13 +00:00
The three CI entry workflows drop the "⚡ CI:" prefix for one emoji each - 🕹️ Manual, 🔀 Pull Request, 📤 Push - completing the trigger triad by symbol. The manual run-name loses its prose prefix and separator dots; the segments now frame themselves: 🕹️ for the dispatch, 🐧 before the distro sweep, ⭐ for the priority line, 🎯 for a verbatim whitelist, 🔀 for diff derivation. before: Manual CI triggered: debian arch; web-app-x ⭐; diff-derived (origin/main) after: 🕹️ 🐧 debian arch ⭐ web-app-x 🔀 diff (origin/main) Dropping the dots removes the fixed literal after inputs.distros that utils/github/run_name.py cut the distro list at - the list is itself space-separated, and the next segment starts with a branch-dependent emoji. value_from_title therefore derives the terminator dynamically when the framing literal is whitespace-only: _openings_after collects what any later template segment can render first (the leading literal of each quoted string in later expressions, format placeholders stripped, plus later inter-expression literals) and cuts at the earliest occurrence. The SPOT property holds: the openings are read from the workflow's own run-name, so a future emoji change flows into the parser without touching it. Titles from before this change no longer match the template and yield '' - the documented unknown-title behaviour, so a retrigger of a pre-rename run falls back to the workflow's distro default instead of guessing. The dispatch CLI is unaffected (it targets entry-manual.yml by file name), job names and the orchestrator are untouched, and no workflow_run trigger references the old names. Required status checks configured against the old workflow names in repository settings are the one surface this repo cannot see - re-check them after the rename lands. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
151 lines
5.5 KiB
Python
151 lines
5.5 KiB
Python
from __future__ import annotations
|
|
|
|
import io
|
|
import unittest
|
|
from contextlib import redirect_stdout
|
|
from unittest import mock
|
|
|
|
from cli.administration.deploy.ci import runs
|
|
from cli.administration.deploy.ci.trigger import __main__ as trigger
|
|
from tests.utils.ci_job_names import deploy_job_name
|
|
from utils.github import run_name
|
|
|
|
|
|
def _job(mode: str, app: str, conclusion: str) -> dict:
|
|
return {
|
|
"name": deploy_job_name(mode, app, "0,1"),
|
|
"status": "completed",
|
|
"conclusion": conclusion,
|
|
}
|
|
|
|
|
|
_JOBS = [
|
|
_job("docker", "web-app-x", "success"),
|
|
_job("swarm", "web-app-x", "failure"),
|
|
_job("docker", "web-app-y", "failure"),
|
|
_job("swarm", "web-app-y", "success"),
|
|
]
|
|
|
|
_RUN_URL = "https://github.com/o/r/actions/runs/55" # nocheck: url
|
|
_SOURCE_RUN = {
|
|
"jobs": _JOBS,
|
|
"displayTitle": run_name.title_with(
|
|
"distros", "arch centos", "🔀 diff (origin/main)"
|
|
),
|
|
}
|
|
|
|
|
|
class TestTriggerMain(unittest.TestCase):
|
|
def _run(self, argv: list[str], run: dict | None = None) -> tuple[int, list]:
|
|
calls: list[tuple] = []
|
|
buf = io.StringIO()
|
|
with (
|
|
mock.patch.object(runs, "current_branch", return_value="feature/x"),
|
|
mock.patch.object(runs, "resolve_repo", return_value="o/r"),
|
|
mock.patch.object(runs, "find_last_deploy_run", return_value=run),
|
|
mock.patch.object(
|
|
runs,
|
|
"dispatch_workflow",
|
|
side_effect=lambda wf, ref, wl="", priority="", distros="", repo=None: (
|
|
calls.append((wf, ref, wl, priority, distros, repo))
|
|
),
|
|
),
|
|
redirect_stdout(buf),
|
|
):
|
|
rc = trigger.main(argv)
|
|
return rc, calls
|
|
|
|
def test_default_triggers_all(self) -> None:
|
|
rc, calls = self._run([])
|
|
self.assertEqual(rc, 0)
|
|
self.assertEqual(
|
|
calls, [("entry-manual.yml", "feature/x", "__ALL__", "", "", "o/r")]
|
|
)
|
|
|
|
def test_apps_explicit_list(self) -> None:
|
|
rc, calls = self._run(["--apps", "web-app-a web-app-b"])
|
|
self.assertEqual(rc, 0)
|
|
self.assertEqual(calls[0][2], "web-app-a web-app-b")
|
|
self.assertEqual(calls[0][3], "")
|
|
|
|
def test_failed_total_sends_priority_without_whitelist(self) -> None:
|
|
rc, calls = self._run(["--failed"], run={"_jobs": _JOBS})
|
|
self.assertEqual(rc, 0)
|
|
self.assertEqual(calls[0][2], "")
|
|
self.assertEqual(calls[0][3], "web-app-x web-app-y")
|
|
|
|
def test_failed_swarm_scope(self) -> None:
|
|
_rc, calls = self._run(["--failed", "swarm"], run={"_jobs": _JOBS})
|
|
self.assertEqual(calls[0][3], "web-app-x")
|
|
|
|
def test_failed_compose_scope(self) -> None:
|
|
_rc, calls = self._run(["--failed", "compose"], run={"_jobs": _JOBS})
|
|
self.assertEqual(calls[0][3], "web-app-y")
|
|
|
|
def test_failed_nothing_does_not_dispatch(self) -> None:
|
|
green = [
|
|
_job("docker", "web-app-x", "success"),
|
|
_job("swarm", "web-app-x", "success"),
|
|
]
|
|
rc, calls = self._run(["--failed"], run={"_jobs": green})
|
|
self.assertEqual(rc, 0)
|
|
self.assertEqual(calls, [])
|
|
|
|
def test_failed_with_run_url_uses_that_run(self) -> None:
|
|
calls: list = []
|
|
with (
|
|
mock.patch.object(runs, "current_branch", return_value="feature/x"),
|
|
mock.patch.object(runs, "resolve_repo", return_value="o/r"),
|
|
mock.patch.object(runs, "fetch_run", return_value=_SOURCE_RUN) as fetch,
|
|
mock.patch.object(runs, "find_last_deploy_run") as find_last,
|
|
mock.patch.object(
|
|
runs,
|
|
"dispatch_workflow",
|
|
side_effect=lambda wf, ref, wl="", priority="", distros="", repo=None: (
|
|
calls.append((priority, distros))
|
|
),
|
|
),
|
|
redirect_stdout(io.StringIO()),
|
|
):
|
|
rc = trigger.main(["--failed", "--run", _RUN_URL])
|
|
self.assertEqual(rc, 0)
|
|
self.assertEqual(calls[0], ("web-app-x web-app-y", "arch centos"))
|
|
fetch.assert_called_once()
|
|
find_last.assert_not_called()
|
|
|
|
def test_failed_with_bare_run_id_resolves_against_branch_repo(self) -> None:
|
|
calls: list = []
|
|
with (
|
|
mock.patch.object(runs, "current_branch", return_value="feature/x"),
|
|
mock.patch.object(runs, "resolve_repo", return_value="o/r"),
|
|
mock.patch.object(runs, "fetch_run", return_value=_SOURCE_RUN) as fetch,
|
|
mock.patch.object(runs, "find_last_deploy_run") as find_last,
|
|
mock.patch.object(
|
|
runs,
|
|
"dispatch_workflow",
|
|
side_effect=lambda wf, ref, wl="", priority="", distros="", repo=None: (
|
|
calls.append((priority, distros))
|
|
),
|
|
),
|
|
redirect_stdout(io.StringIO()),
|
|
):
|
|
rc = trigger.main(["--failed", "--run", "55"])
|
|
self.assertEqual(rc, 0)
|
|
self.assertEqual(calls[0], ("web-app-x web-app-y", "arch centos"))
|
|
fetch.assert_called_once_with("55", repo="o/r")
|
|
find_last.assert_not_called()
|
|
|
|
def test_failed_no_run_found(self) -> None:
|
|
rc, calls = self._run(["--failed"], run=None)
|
|
self.assertEqual(rc, 1)
|
|
self.assertEqual(calls, [])
|
|
|
|
def test_apps_and_failed_mutually_exclusive(self) -> None:
|
|
with self.assertRaises(SystemExit) as ctx, redirect_stdout(io.StringIO()):
|
|
trigger.main(["--failed", "--apps", "x"])
|
|
self.assertEqual(ctx.exception.code, 2)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|