diff --git a/avrea_cli/commands/job.py b/avrea_cli/commands/job.py index 8dbed78f..3eaee387 100644 --- a/avrea_cli/commands/job.py +++ b/avrea_cli/commands/job.py @@ -65,6 +65,8 @@ _JOB_LIST_FIELDS = make_schema( "job_id", + "ci_provider", + "run_id", "platform_job_id", "job_name", "state", @@ -103,6 +105,14 @@ def _hdr_cell(label: str, width: int) -> str: return click.style(f"{label:{width}s}", fg=DIM_FG, underline=True) +def _job_ci_provider(job: dict[str, Any]) -> str: + if provider := job.get("ci_provider"): + return str(provider) + # Older control revisions omit ci_provider. Source platform still + # distinguishes external jobs during a rolling deploy. + return {"github": "github_actions", "gitlab": "gitlab_ci"}.get(job.get("platform", "github"), "unknown") + + def _print_jobs_table(jobs_data: list[dict[str, Any]], *, console_url: str = "", slug: str = "") -> None: """Sectioned-table renderer shared by `avr job list` and `avr job watch`. @@ -114,7 +124,9 @@ def _print_jobs_table(jobs_data: list[dict[str, Any]], *, console_url: str = "", truncation, ISO timestamps) when stdout isn't a TTY — the standard scriptability convention.""" if is_piped(): - print_piped_header(["status", "job_name", "repository", "on_avrea", "job_id", "duration_seconds", "created_at"]) + print_piped_header( + ["status", "job_name", "repository", "on_avrea", "job_id", "duration_seconds", "created_at", "ci_provider"] + ) for j in jobs_data: state = j.get("state", "unknown") conclusion = j.get("conclusion") @@ -128,6 +140,7 @@ def _print_jobs_table(jobs_data: list[dict[str, Any]], *, console_url: str = "", j.get("job_id", ""), j.get("duration_seconds"), j.get("created_at", ""), + _job_ci_provider(j), ] ) return @@ -137,7 +150,7 @@ def _print_jobs_table(jobs_data: list[dict[str, Any]], *, console_url: str = "", click.echo( f" {_hdr_cell('NAME', w['name'])}{s}{_hdr_cell('REPOSITORY', w['repo'])}{s}" f"{_hdr_cell('STATUS', w['status'])}{s}{_hdr_cell('ON', w['on'])}{s}" - f"{_hdr_cell('AGE', w['age'])}{s}{_hdr_cell('ID', w['id'])}" + f"{_hdr_cell('AGE', w['age'])}{s}{_hdr_cell('ID', w['id'])}{s}CI" ) for j in jobs_data: state = j.get("state", "unknown") @@ -168,7 +181,7 @@ def _print_jobs_table(jobs_data: list[dict[str, Any]], *, console_url: str = "", f"{click.style(repo, dim=True)}{s}" f"{status_cell}{s}{on_cell}{s}" f"{click.style(age, dim=True)}{s}" - f"{job_id_cell}" + f"{job_id_cell}{s}{_job_ci_provider(j)}" ) @@ -248,7 +261,7 @@ def job_list( \b JSON FIELDS - completed_at, conclusion, created_at, duration_seconds, platform_job_id, + ci_provider, run_id, completed_at, conclusion, created_at, duration_seconds, platform_job_id, platform_run_id, job_id, job_name, labels, repository, repository_id, running_on_avrea, started_at, state """ @@ -501,7 +514,7 @@ def job_view( \b JSON FIELDS - completed_at, conclusion, created_at, duration_seconds, platform_job_id, + ci_provider, run_id, completed_at, conclusion, created_at, duration_seconds, platform_job_id, platform_run_id, job_id, job_name, labels, repository, repository_id, running_on_avrea, started_at, state, steps, workflow_run """ @@ -551,7 +564,11 @@ def job_view( run_id_full = "" if wf_run: run_id_full = wf_run.get("run_id", "") - run_display = f"#{wf_run.get('run_number', '?')} ({short_id(run_id_full)})" + run_display = ( + f"#{wf_run['run_number']} ({short_id(run_id_full)})" + if wf_run.get("run_number") is not None + else run_id_full + ) repo_full = job_data.get("repository_full_name", "-") repo_id = job_data.get("repository_id") @@ -575,6 +592,7 @@ def job_view( format_key_value( { "Job": job_cell, + "CI": _job_ci_provider(job_data), "Name": job_data.get("job_name", "-"), "Repository": repo_cell, "Run": run_cell, diff --git a/avrea_cli/commands/run.py b/avrea_cli/commands/run.py index 332b51a9..187c6bf1 100644 --- a/avrea_cli/commands/run.py +++ b/avrea_cli/commands/run.py @@ -83,6 +83,9 @@ _RUN_LIST_FIELDS = make_schema( "run_id", + "ci_provider", + "workflow_name", + "workflow_path", "platform_run_id", "display_title", "status", @@ -134,7 +137,7 @@ def _split_status_values(values: tuple[str, ...]) -> tuple[list[str], list[str]] @click.group(cls=GhGroup) @click.pass_context def run(ctx): - """View and manage GitHub workflow runs.""" + """View and manage CI workflow runs.""" ensure_ctx(ctx) @@ -225,7 +228,7 @@ def run_list( \b JSON FIELDS - conclusion, created_at, display_title, duration_seconds, event, + ci_provider, workflow_name, workflow_path, conclusion, created_at, display_title, duration_seconds, event, head_branch, head_sha, platform_run_id, repository, run_attempt, run_id, run_number, status, triggering_actor, updated_at, workflow, workflow_id """ @@ -314,7 +317,17 @@ def run_list( # Tab-separated output for cut/awk/grep, with a header row so scripts # can index by column name rather than memorizing positional order. print_piped_header( - ["status", "title", "workflow", "branch", "event", "run_id", "duration_seconds", "created_at"] + [ + "status", + "title", + "workflow", + "branch", + "event", + "run_id", + "duration_seconds", + "created_at", + "ci_provider", + ] ) for r in runs: status = r.get("conclusion") or r.get("status") or "unknown" @@ -322,12 +335,13 @@ def run_list( [ status, r.get("display_title", ""), - (r.get("workflow") or {}).get("name", ""), + (r.get("workflow") or {}).get("name") or r.get("workflow_name") or "", r.get("head_branch") or "", r.get("event", ""), r.get("run_id", ""), r.get("duration_seconds"), r.get("created_at", ""), + r.get("ci_provider", "github_actions"), ] ) return @@ -341,7 +355,7 @@ def _hdr(label: str, width: int) -> str: click.echo( f" {_hdr('TITLE', W['title'])}{s}{_hdr('WORKFLOW', W['wf'])}{s}{_hdr('BRANCH', W['branch'])}{s}" f"{_hdr('EVENT', W['event'])}{s}{_hdr('ID', W['id'])}{s}" - f"{_hdr('ELAPSED', W['elapsed'])}{s}{_hdr('AGE', W['age'])}" + f"{_hdr('ELAPSED', W['elapsed'])}{s}{_hdr('AGE', W['age'])}{s}CI" ) # OSC 8 link context — slug lookup is best-effort, one extra API call. links_enabled = ctx.obj.get("links_enabled", False) @@ -350,7 +364,8 @@ def _hdr(label: str, width: int) -> str: for r in runs: ind = status_indicator(r.get("status", "unknown"), r.get("conclusion")) title = f"{_truncate(r.get('display_title', ''), W['title'] - 2):{W['title']}s}" - wf = f"{_truncate((r.get('workflow') or {}).get('name', ''), W['wf'] - 2):{W['wf']}s}" + workflow_name = (r.get("workflow") or {}).get("name") or r.get("workflow_name") or "" + wf = f"{_truncate(workflow_name, W['wf'] - 2):{W['wf']}s}" head_branch = r.get("head_branch") or "" br = f"{_truncate(head_branch, W['branch'] - 2):{W['branch']}s}" event = f"{r.get('event', ''):{W['event']}s}" @@ -369,14 +384,14 @@ def _hdr(label: str, width: int) -> str: title_cell = hyperlink(title, url) wf_cell = hyperlink(wf_cell, url) r_id_cell = hyperlink(r_id_cell, url) - if links_enabled and head_branch and repo_full and "/" in repo_full: + if links_enabled and r.get("ci_provider") != "avrea_ci" and head_branch and repo_full and "/" in repo_full: br_cell = hyperlink(br_cell, f"https://github.com/{repo_full}/tree/{head_branch}") click.echo( f"{ind} {title_cell}{s}" f"{wf_cell}{s}" f"{br_cell}{s}" f"{event}{s}{r_id_cell}{s}" - f"{elapsed}{s}{click.style(age, dim=True)}" + f"{elapsed}{s}{click.style(age, dim=True)}{s}{r.get('ci_provider', 'github_actions')}" ) next_cursor = (response.get("pagination") or {}).get("next_cursor") @@ -568,18 +583,18 @@ def run_view( \b Examples: avr run view - avr run view run-abc123 + avr run view run-019e5a2e474e71c297e452279d4bcdea avr run view 123456789 avr run view https://github.com/acme/widgets/actions/runs/123456789 - avr run view run-abc123 --steps - avr run view run-abc123 --log-failed - avr run view run-abc123 --job Build - avr run view run-abc123 --json conclusion,jobs - avr run view run-abc123 --json '*' --jq '.jobs[].job_name' + avr run view run-019e5a2e474e71c297e452279d4bcdea --steps + avr run view run-019e5a2e474e71c297e452279d4bcdea --log-failed + avr run view run-019e5a2e474e71c297e452279d4bcdea --job Build + avr run view run-019e5a2e474e71c297e452279d4bcdea --json conclusion,jobs + avr run view run-019e5a2e474e71c297e452279d4bcdea --json '*' --jq '.jobs[].job_name' \b JSON FIELDS - conclusion, created_at, display_title, duration_seconds, event, + ci_provider, workflow_name, workflow_path, conclusion, created_at, display_title, duration_seconds, event, head_branch, head_sha, jobs, platform_run_id, repository, run_attempt, run_id, run_number, status, triggering_actor, updated_at, workflow, workflow_id @@ -668,19 +683,20 @@ def run_view( actor_data = run_data.get("triggering_actor") or {} actor = actor_data.get("login") or actor_data.get("platform_login") or "-" wf_data = run_data.get("workflow") or {} - wf_name = wf_data.get("name", "") - wf_path = (wf_data.get("path") or "").rsplit("/", 1)[-1] # e.g. ".github/workflows/build.yml" → "build.yml" + wf_name = wf_data.get("name") or run_data.get("workflow_name") or "" + wf_path = (wf_data.get("path") or run_data.get("workflow_path") or "").rsplit("/", 1)[ + -1 + ] # e.g. ".github/workflows/build.yml" → "build.yml" workflow = f"{wf_name} ({wf_path})" if wf_name and wf_path else wf_name run_number = run_data.get("run_number", "?") run_attempt = run_data.get("run_attempt", 1) - # OSC 8 link context. Github repos always have an `org/name` shape; - # gate platform-specific links on `/` so a future non-github repo - # skips them gracefully. + # Native runs may use any source host; the run response does not carry + # a canonical source URL, so only external GitHub runs get source links. links_enabled = ctx.obj.get("links_enabled", False) link_console_url = get_console_url(config.public_api_url) if links_enabled else "" link_slug = get_org_slug(client, org_id) if links_enabled else "" - is_github = repo and "/" in repo + is_github = run_data.get("ci_provider") != "avrea_ci" and repo and "/" in repo title_text = run_data.get("display_title", "-") branch_cell = branch @@ -710,7 +726,8 @@ def run_view( "Event": run_data.get("event", "-"), "Actor": actor, "Duration": format_duration(run_data.get("duration_seconds")), - "Run": f"#{run_number} (attempt {run_attempt})", + "Run": f"#{run_number} (attempt {run_attempt})" if run_number is not None else run_id, + "CI": run_data.get("ci_provider", "github_actions"), "Workflow": workflow_cell, "Repository": repo_cell, } @@ -785,7 +802,7 @@ def run_diagnose(ctx, run: str, org_id: str | None, json_output: bool) -> None: \b Examples: - avr run diagnose run-abc123 + avr run diagnose run-019e5a2e474e71c297e452279d4bcdea avr run diagnose 123456789 --json avr run diagnose https://github.com/acme/widgets/actions/runs/123456789 """ @@ -1176,8 +1193,8 @@ def run_watch( avr run watch avr run watch --repo acme/web avr run watch --repo a/x --repo b/y - avr run watch run-abc123 --exit-status - avr run watch run-abc123 --ndjson | jq -c . + avr run watch run-019e5a2e474e71c297e452279d4bcdea --exit-status + avr run watch run-019e5a2e474e71c297e452279d4bcdea --ndjson | jq -c . """ client: ApiClient = ctx.obj["client"] config: CliConfig = ctx.obj["config"] @@ -1330,9 +1347,9 @@ def run_rerun(ctx, run: str, org_id, failed: bool, yes: bool): \b Examples: - avr run rerun run-abc123 - avr run rerun run-abc123 --failed - avr run rerun run-abc123 --yes + avr run rerun run-019e5a2e474e71c297e452279d4bcdea + avr run rerun run-019e5a2e474e71c297e452279d4bcdea --failed + avr run rerun run-019e5a2e474e71c297e452279d4bcdea --yes """ client: ApiClient = ctx.obj["client"] config: CliConfig = ctx.obj["config"] @@ -1434,10 +1451,10 @@ def run_logs( \b Examples: - avr run logs run-abc123 - avr run logs run-abc123 --failed - avr run logs run-abc123 --job test - avr run logs run-abc123 --follow + avr run logs run-019e5a2e474e71c297e452279d4bcdea + avr run logs run-019e5a2e474e71c297e452279d4bcdea --failed + avr run logs run-019e5a2e474e71c297e452279d4bcdea --job test + avr run logs run-019e5a2e474e71c297e452279d4bcdea --follow """ if follow and failed_only: raise click.UsageError("--follow and --failed cannot be combined") @@ -1542,8 +1559,8 @@ def run_cancel(ctx, run: str, org_id, yes: bool): \b Examples: - avr run cancel run-abc123 - avr run cancel run-abc123 --yes + avr run cancel run-019e5a2e474e71c297e452279d4bcdea + avr run cancel run-019e5a2e474e71c297e452279d4bcdea --yes """ client: ApiClient = ctx.obj["client"] config: CliConfig = ctx.obj["config"] diff --git a/avrea_cli/main.py b/avrea_cli/main.py index 65836361..2fc6e85d 100644 --- a/avrea_cli/main.py +++ b/avrea_cli/main.py @@ -32,7 +32,7 @@ "Show recent runs, performance stats, and cache health.", "Core Commands", ), # noqa: E501 - ("run", "avrea_cli.commands.run", "run", "View and manage GitHub workflow runs.", "Core Commands"), + ("run", "avrea_cli.commands.run", "run", "View and manage CI workflow runs.", "Core Commands"), ("pr", "avrea_cli.commands.pr", "pr", "View pull requests.", "Core Commands"), ("job", "avrea_cli.commands.job", "job", "Inspect Avrea job VMs (SSH, metrics, logs).", "Core Commands"), ("vm", "avrea_cli.vm", "vm", "Manage long-running VMs (SSH/RDP/VNC).", "Core Commands"), diff --git a/avrea_cli/run_refs.py b/avrea_cli/run_refs.py index 13e20525..0965f139 100644 --- a/avrea_cli/run_refs.py +++ b/avrea_cli/run_refs.py @@ -9,7 +9,7 @@ import click import re -_AVREA_RUN_ID_RE = re.compile(r"run-[A-Za-z0-9]+") +_AVREA_RUN_ID_RE = re.compile(r"(?:run|awr)-[0-9a-f]{32}") _ASCII_DECIMAL_RE = re.compile(r"[0-9]+") _GITHUB_NAME_RE = re.compile(r"[A-Za-z0-9_.-]+") _ORG_SLUG_RE = re.compile(r"[A-Za-z0-9-]+") diff --git a/docs/REFERENCE.md b/docs/REFERENCE.md index 99cd9728..9d12a68c 100644 --- a/docs/REFERENCE.md +++ b/docs/REFERENCE.md @@ -36,7 +36,7 @@ avr [GLOBAL OPTIONS] COMMAND [ARGS]... - [`avr artifact`](#avr-artifact) — Manage workflow artifacts. - [`avr status`](#avr-status) — Show recent runs, performance stats, and cache health. -- [`avr run`](#avr-run) — View and manage GitHub workflow runs. +- [`avr run`](#avr-run) — View and manage CI workflow runs. - [`avr pr`](#avr-pr) — View pull requests. - [`avr job`](#avr-job) — Inspect Avrea job VMs (SSH, metrics, logs). - [`avr vm`](#avr-vm) — Manage long-running VMs (SSH/RDP/VNC). @@ -168,7 +168,7 @@ avr status [OPTIONS] ### `avr run` -View and manage GitHub workflow runs. +View and manage CI workflow runs. ```sh avr run [OPTIONS] COMMAND [ARGS]... @@ -187,8 +187,8 @@ RUN accepts the same Avrea IDs, GitHub run IDs, and run URLs as ```sh Examples: - avr run cancel run-abc123 - avr run cancel run-abc123 --yes + avr run cancel run-019e5a2e474e71c297e452279d4bcdea + avr run cancel run-019e5a2e474e71c297e452279d4bcdea --yes ``` **Arguments** @@ -215,7 +215,7 @@ prior-success workflow baseline. ```sh Examples: - avr run diagnose run-abc123 + avr run diagnose run-019e5a2e474e71c297e452279d4bcdea avr run diagnose 123456789 --json avr run diagnose https://github.com/acme/widgets/actions/runs/123456789 ``` @@ -251,7 +251,7 @@ Examples: ```sh JSON FIELDS - conclusion, created_at, display_title, duration_seconds, event, + ci_provider, workflow_name, workflow_path, conclusion, created_at, display_title, duration_seconds, event, head_branch, head_sha, platform_run_id, repository, run_attempt, run_id, run_number, status, triggering_actor, updated_at, workflow, workflow_id ``` @@ -290,10 +290,10 @@ specific job when a run has many. ```sh Examples: - avr run logs run-abc123 - avr run logs run-abc123 --failed - avr run logs run-abc123 --job test - avr run logs run-abc123 --follow + avr run logs run-019e5a2e474e71c297e452279d4bcdea + avr run logs run-019e5a2e474e71c297e452279d4bcdea --failed + avr run logs run-019e5a2e474e71c297e452279d4bcdea --job test + avr run logs run-019e5a2e474e71c297e452279d4bcdea --follow ``` **Arguments** @@ -322,9 +322,9 @@ RUN accepts the same Avrea IDs, GitHub run IDs, and run URLs as ```sh Examples: - avr run rerun run-abc123 - avr run rerun run-abc123 --failed - avr run rerun run-abc123 --yes + avr run rerun run-019e5a2e474e71c297e452279d4bcdea + avr run rerun run-019e5a2e474e71c297e452279d4bcdea --failed + avr run rerun run-019e5a2e474e71c297e452279d4bcdea --yes ``` **Arguments** @@ -354,19 +354,19 @@ runs. ```sh Examples: avr run view - avr run view run-abc123 + avr run view run-019e5a2e474e71c297e452279d4bcdea avr run view 123456789 avr run view https://github.com/acme/widgets/actions/runs/123456789 - avr run view run-abc123 --steps - avr run view run-abc123 --log-failed - avr run view run-abc123 --job Build - avr run view run-abc123 --json conclusion,jobs - avr run view run-abc123 --json '*' --jq '.jobs[].job_name' + avr run view run-019e5a2e474e71c297e452279d4bcdea --steps + avr run view run-019e5a2e474e71c297e452279d4bcdea --log-failed + avr run view run-019e5a2e474e71c297e452279d4bcdea --job Build + avr run view run-019e5a2e474e71c297e452279d4bcdea --json conclusion,jobs + avr run view run-019e5a2e474e71c297e452279d4bcdea --json '*' --jq '.jobs[].job_name' ``` ```sh JSON FIELDS - conclusion, created_at, display_title, duration_seconds, event, + ci_provider, workflow_name, workflow_path, conclusion, created_at, display_title, duration_seconds, event, head_branch, head_sha, jobs, platform_run_id, repository, run_attempt, run_id, run_number, status, triggering_actor, updated_at, workflow, workflow_id @@ -407,8 +407,8 @@ Examples: avr run watch avr run watch --repo acme/web avr run watch --repo a/x --repo b/y - avr run watch run-abc123 --exit-status - avr run watch run-abc123 --ndjson | jq -c . + avr run watch run-019e5a2e474e71c297e452279d4bcdea --exit-status + avr run watch run-019e5a2e474e71c297e452279d4bcdea --ndjson | jq -c . ``` **Arguments** @@ -495,7 +495,7 @@ Examples: ```sh JSON FIELDS - completed_at, conclusion, created_at, duration_seconds, platform_job_id, + ci_provider, run_id, completed_at, conclusion, created_at, duration_seconds, platform_job_id, platform_run_id, job_id, job_name, labels, repository, repository_id, running_on_avrea, started_at, state ``` @@ -608,7 +608,7 @@ Examples: ```sh JSON FIELDS - completed_at, conclusion, created_at, duration_seconds, platform_job_id, + ci_provider, run_id, completed_at, conclusion, created_at, duration_seconds, platform_job_id, platform_run_id, job_id, job_name, labels, repository, repository_id, running_on_avrea, started_at, state, steps, workflow_run ``` diff --git a/docs/reference.json b/docs/reference.json index 914982b3..482622b2 100644 --- a/docs/reference.json +++ b/docs/reference.json @@ -726,8 +726,8 @@ ], "full": "avr run", "is_group": true, - "short_help": "View and manage GitHub workflow runs.", - "help": "View and manage GitHub workflow runs.", + "short_help": "View and manage CI workflow runs.", + "help": "View and manage CI workflow runs.", "epilog": null, "deprecated": false, "hidden": false, @@ -744,7 +744,7 @@ "full": "avr run cancel", "is_group": false, "short_help": "Cancel an in-progress or queued workflow run.", - "help": "Cancel an in-progress or queued workflow run.\n\nRUN accepts the same Avrea IDs, GitHub run IDs, and run URLs as\n`avr run view`.\n\n\b\nExamples:\n avr run cancel run-abc123\n avr run cancel run-abc123 --yes\n", + "help": "Cancel an in-progress or queued workflow run.\n\nRUN accepts the same Avrea IDs, GitHub run IDs, and run URLs as\n`avr run view`.\n\n\b\nExamples:\n avr run cancel run-019e5a2e474e71c297e452279d4bcdea\n avr run cancel run-019e5a2e474e71c297e452279d4bcdea --yes\n", "epilog": null, "deprecated": false, "hidden": false, @@ -818,7 +818,7 @@ "full": "avr run diagnose", "is_group": false, "short_help": "Explain a failed or unexpectedly slow workflow run.", - "help": "Explain a failed or unexpectedly slow workflow run.\n\nRUN accepts the same Avrea IDs, GitHub run IDs, and run URLs as\n`avr run view`. The report combines jobs and failed steps, bounded\nfailed-job log tails, queue/execution timings, runner metrics, and a\nprior-success workflow baseline.\n\n\b\nExamples:\n avr run diagnose run-abc123\n avr run diagnose 123456789 --json\n avr run diagnose https://github.com/acme/widgets/actions/runs/123456789\n", + "help": "Explain a failed or unexpectedly slow workflow run.\n\nRUN accepts the same Avrea IDs, GitHub run IDs, and run URLs as\n`avr run view`. The report combines jobs and failed steps, bounded\nfailed-job log tails, queue/execution timings, runner metrics, and a\nprior-success workflow baseline.\n\n\b\nExamples:\n avr run diagnose run-019e5a2e474e71c297e452279d4bcdea\n avr run diagnose 123456789 --json\n avr run diagnose https://github.com/acme/widgets/actions/runs/123456789\n", "epilog": null, "deprecated": false, "hidden": false, @@ -891,7 +891,7 @@ "full": "avr run list", "is_group": false, "short_help": "List workflow runs for an organization.", - "help": "List workflow runs for an organization.\n\n\b\nExamples:\n avr run list\n avr run list --status failure --limit 5\n avr run list --status in_progress --json status,conclusion,head_branch\n avr run list --branch main --status completed\n avr run list --since 24h\n avr run list --json '?' # list available fields\n avr run list --json '*' # all fields\n avr run list --json status,conclusion -q '[.[] | select(.status == \"completed\")]'\n\n\b\nJSON FIELDS\n conclusion, created_at, display_title, duration_seconds, event,\n head_branch, head_sha, platform_run_id, repository, run_attempt, run_id,\n run_number, status, triggering_actor, updated_at, workflow, workflow_id\n", + "help": "List workflow runs for an organization.\n\n\b\nExamples:\n avr run list\n avr run list --status failure --limit 5\n avr run list --status in_progress --json status,conclusion,head_branch\n avr run list --branch main --status completed\n avr run list --since 24h\n avr run list --json '?' # list available fields\n avr run list --json '*' # all fields\n avr run list --json status,conclusion -q '[.[] | select(.status == \"completed\")]'\n\n\b\nJSON FIELDS\n ci_provider, workflow_name, workflow_path, conclusion, created_at, display_title, duration_seconds, event,\n head_branch, head_sha, platform_run_id, repository, run_attempt, run_id,\n run_number, status, triggering_actor, updated_at, workflow, workflow_id\n", "epilog": null, "deprecated": false, "hidden": false, @@ -1185,7 +1185,7 @@ "full": "avr run logs", "is_group": false, "short_help": "Fetch logs for a workflow run's GitHub jobs.", - "help": "Fetch logs for a workflow run's GitHub jobs.\n\nRUN accepts the same Avrea IDs, GitHub run IDs, and run URLs as\n`avr run view`.\n\nLong-form alternative to `avr run view --log[-failed]`. Use --follow to\ntail logs in real time for an in-progress job; pass --job to scope to a\nspecific job when a run has many.\n\n\b\nExamples:\n avr run logs run-abc123\n avr run logs run-abc123 --failed\n avr run logs run-abc123 --job test\n avr run logs run-abc123 --follow\n", + "help": "Fetch logs for a workflow run's GitHub jobs.\n\nRUN accepts the same Avrea IDs, GitHub run IDs, and run URLs as\n`avr run view`.\n\nLong-form alternative to `avr run view --log[-failed]`. Use --follow to\ntail logs in real time for an in-progress job; pass --job to scope to a\nspecific job when a run has many.\n\n\b\nExamples:\n avr run logs run-019e5a2e474e71c297e452279d4bcdea\n avr run logs run-019e5a2e474e71c297e452279d4bcdea --failed\n avr run logs run-019e5a2e474e71c297e452279d4bcdea --job test\n avr run logs run-019e5a2e474e71c297e452279d4bcdea --follow\n", "epilog": null, "deprecated": false, "hidden": false, @@ -1331,7 +1331,7 @@ "full": "avr run rerun", "is_group": false, "short_help": "Re-run a completed workflow run.", - "help": "Re-run a completed workflow run.\n\nRUN accepts the same Avrea IDs, GitHub run IDs, and run URLs as\n`avr run view`.\n\n\b\nExamples:\n avr run rerun run-abc123\n avr run rerun run-abc123 --failed\n avr run rerun run-abc123 --yes\n", + "help": "Re-run a completed workflow run.\n\nRUN accepts the same Avrea IDs, GitHub run IDs, and run URLs as\n`avr run view`.\n\n\b\nExamples:\n avr run rerun run-019e5a2e474e71c297e452279d4bcdea\n avr run rerun run-019e5a2e474e71c297e452279d4bcdea --failed\n avr run rerun run-019e5a2e474e71c297e452279d4bcdea --yes\n", "epilog": null, "deprecated": false, "hidden": false, @@ -1423,7 +1423,7 @@ "full": "avr run view", "is_group": false, "short_help": "View a workflow run with its jobs.", - "help": "View a workflow run with its jobs.\n\n\b\nRUN accepts an Avrea run ID, a positive GitHub run ID, a GitHub Actions\nrun URL, or an Avrea console run URL. Without RUN, shows 10 most recent\nruns.\n\n\b\nExamples:\n avr run view\n avr run view run-abc123\n avr run view 123456789\n avr run view https://github.com/acme/widgets/actions/runs/123456789\n avr run view run-abc123 --steps\n avr run view run-abc123 --log-failed\n avr run view run-abc123 --job Build\n avr run view run-abc123 --json conclusion,jobs\n avr run view run-abc123 --json '*' --jq '.jobs[].job_name'\n\n\b\nJSON FIELDS\n conclusion, created_at, display_title, duration_seconds, event,\n head_branch, head_sha, jobs, platform_run_id, repository, run_attempt,\n run_id, run_number, status, triggering_actor, updated_at, workflow,\n workflow_id\n", + "help": "View a workflow run with its jobs.\n\n\b\nRUN accepts an Avrea run ID, a positive GitHub run ID, a GitHub Actions\nrun URL, or an Avrea console run URL. Without RUN, shows 10 most recent\nruns.\n\n\b\nExamples:\n avr run view\n avr run view run-019e5a2e474e71c297e452279d4bcdea\n avr run view 123456789\n avr run view https://github.com/acme/widgets/actions/runs/123456789\n avr run view run-019e5a2e474e71c297e452279d4bcdea --steps\n avr run view run-019e5a2e474e71c297e452279d4bcdea --log-failed\n avr run view run-019e5a2e474e71c297e452279d4bcdea --job Build\n avr run view run-019e5a2e474e71c297e452279d4bcdea --json conclusion,jobs\n avr run view run-019e5a2e474e71c297e452279d4bcdea --json '*' --jq '.jobs[].job_name'\n\n\b\nJSON FIELDS\n ci_provider, workflow_name, workflow_path, conclusion, created_at, display_title, duration_seconds, event,\n head_branch, head_sha, jobs, platform_run_id, repository, run_attempt,\n run_id, run_number, status, triggering_actor, updated_at, workflow,\n workflow_id\n", "epilog": null, "deprecated": false, "hidden": false, @@ -1623,7 +1623,7 @@ "full": "avr run watch", "is_group": false, "short_help": "Watch a workflow run until it completes.", - "help": "Watch a workflow run until it completes.\n\n\b\nRUN accepts the same Avrea IDs, GitHub run IDs, and run URLs as\n`avr run view`. Without RUN, auto-selects the latest in-progress run.\nPass --repo (repeatable) to scope the auto-select to specific repositories.\n\n\b\nExamples:\n avr run watch\n avr run watch --repo acme/web\n avr run watch --repo a/x --repo b/y\n avr run watch run-abc123 --exit-status\n avr run watch run-abc123 --ndjson | jq -c .\n", + "help": "Watch a workflow run until it completes.\n\n\b\nRUN accepts the same Avrea IDs, GitHub run IDs, and run URLs as\n`avr run view`. Without RUN, auto-selects the latest in-progress run.\nPass --repo (repeatable) to scope the auto-select to specific repositories.\n\n\b\nExamples:\n avr run watch\n avr run watch --repo acme/web\n avr run watch --repo a/x --repo b/y\n avr run watch run-019e5a2e474e71c297e452279d4bcdea --exit-status\n avr run watch run-019e5a2e474e71c297e452279d4bcdea --ndjson | jq -c .\n", "epilog": null, "deprecated": false, "hidden": false, @@ -1962,7 +1962,7 @@ "full": "avr job list", "is_group": false, "short_help": "List jobs for an organization.", - "help": "List jobs for an organization.\n\n\b\nExamples:\n avr job list\n avr job list --status failure --limit 5\n avr job list --status in_progress --json job_name,state,conclusion\n avr job list --since 24h\n avr job list --json '?' # list available fields\n avr job list --json '*' # all fields\n\n\b\nJSON FIELDS\n completed_at, conclusion, created_at, duration_seconds, platform_job_id,\n platform_run_id, job_id, job_name, labels, repository, repository_id,\n running_on_avrea, started_at, state\n", + "help": "List jobs for an organization.\n\n\b\nExamples:\n avr job list\n avr job list --status failure --limit 5\n avr job list --status in_progress --json job_name,state,conclusion\n avr job list --since 24h\n avr job list --json '?' # list available fields\n avr job list --json '*' # all fields\n\n\b\nJSON FIELDS\n ci_provider, run_id, completed_at, conclusion, created_at, duration_seconds, platform_job_id,\n platform_run_id, job_id, job_name, labels, repository, repository_id,\n running_on_avrea, started_at, state\n", "epilog": null, "deprecated": false, "hidden": false, @@ -2619,7 +2619,7 @@ "full": "avr job view", "is_group": false, "short_help": "View a single job with its steps.", - "help": "View a single job with its steps.\n\n\b\nExamples:\n avr job view job-abc123\n avr job view job-abc123 --log-failed\n avr job view job-abc123 --json conclusion,steps\n avr job view job-abc123 --json '*' --jq '.steps[] | select(.conclusion==\"failure\")'\n\n\b\nJSON FIELDS\n completed_at, conclusion, created_at, duration_seconds, platform_job_id,\n platform_run_id, job_id, job_name, labels, repository, repository_id,\n running_on_avrea, started_at, state, steps, workflow_run\n", + "help": "View a single job with its steps.\n\n\b\nExamples:\n avr job view job-abc123\n avr job view job-abc123 --log-failed\n avr job view job-abc123 --json conclusion,steps\n avr job view job-abc123 --json '*' --jq '.steps[] | select(.conclusion==\"failure\")'\n\n\b\nJSON FIELDS\n ci_provider, run_id, completed_at, conclusion, created_at, duration_seconds, platform_job_id,\n platform_run_id, job_id, job_name, labels, repository, repository_id,\n running_on_avrea, started_at, state, steps, workflow_run\n", "epilog": null, "deprecated": false, "hidden": false, diff --git a/docs/starlight/reference/index.md b/docs/starlight/reference/index.md index 99a3368e..23fa6a8e 100644 --- a/docs/starlight/reference/index.md +++ b/docs/starlight/reference/index.md @@ -24,7 +24,7 @@ avr [GLOBAL OPTIONS] COMMAND [ARGS]... - [`avr artifact`](./artifact/) — Manage workflow artifacts. - [`avr status`](./status/) — Show recent runs, performance stats, and cache health. -- [`avr run`](./run/) — View and manage GitHub workflow runs. +- [`avr run`](./run/) — View and manage CI workflow runs. - [`avr pr`](./pr/) — View pull requests. - [`avr job`](./job/) — Inspect Avrea job VMs (SSH, metrics, logs). - [`avr vm`](./vm/) — Manage long-running VMs (SSH/RDP/VNC). diff --git a/docs/starlight/reference/job.md b/docs/starlight/reference/job.md index 6f83fc48..33fd8b3d 100644 --- a/docs/starlight/reference/job.md +++ b/docs/starlight/reference/job.md @@ -31,7 +31,7 @@ Examples: ```sh JSON FIELDS - completed_at, conclusion, created_at, duration_seconds, platform_job_id, + ci_provider, run_id, completed_at, conclusion, created_at, duration_seconds, platform_job_id, platform_run_id, job_id, job_name, labels, repository, repository_id, running_on_avrea, started_at, state ``` @@ -144,7 +144,7 @@ Examples: ```sh JSON FIELDS - completed_at, conclusion, created_at, duration_seconds, platform_job_id, + ci_provider, run_id, completed_at, conclusion, created_at, duration_seconds, platform_job_id, platform_run_id, job_id, job_name, labels, repository, repository_id, running_on_avrea, started_at, state, steps, workflow_run ``` diff --git a/docs/starlight/reference/run.md b/docs/starlight/reference/run.md index 026d7a56..f4c2cc20 100644 --- a/docs/starlight/reference/run.md +++ b/docs/starlight/reference/run.md @@ -1,9 +1,9 @@ --- title: avr run -description: "View and manage GitHub workflow runs." +description: "View and manage CI workflow runs." --- -View and manage GitHub workflow runs. +View and manage CI workflow runs. ```sh avr run [OPTIONS] COMMAND [ARGS]... @@ -24,8 +24,8 @@ RUN accepts the same Avrea IDs, GitHub run IDs, and run URLs as ```sh Examples: - avr run cancel run-abc123 - avr run cancel run-abc123 --yes + avr run cancel run-019e5a2e474e71c297e452279d4bcdea + avr run cancel run-019e5a2e474e71c297e452279d4bcdea --yes ``` **Arguments** @@ -52,7 +52,7 @@ prior-success workflow baseline. ```sh Examples: - avr run diagnose run-abc123 + avr run diagnose run-019e5a2e474e71c297e452279d4bcdea avr run diagnose 123456789 --json avr run diagnose https://github.com/acme/widgets/actions/runs/123456789 ``` @@ -88,7 +88,7 @@ Examples: ```sh JSON FIELDS - conclusion, created_at, display_title, duration_seconds, event, + ci_provider, workflow_name, workflow_path, conclusion, created_at, display_title, duration_seconds, event, head_branch, head_sha, platform_run_id, repository, run_attempt, run_id, run_number, status, triggering_actor, updated_at, workflow, workflow_id ``` @@ -127,10 +127,10 @@ specific job when a run has many. ```sh Examples: - avr run logs run-abc123 - avr run logs run-abc123 --failed - avr run logs run-abc123 --job test - avr run logs run-abc123 --follow + avr run logs run-019e5a2e474e71c297e452279d4bcdea + avr run logs run-019e5a2e474e71c297e452279d4bcdea --failed + avr run logs run-019e5a2e474e71c297e452279d4bcdea --job test + avr run logs run-019e5a2e474e71c297e452279d4bcdea --follow ``` **Arguments** @@ -159,9 +159,9 @@ RUN accepts the same Avrea IDs, GitHub run IDs, and run URLs as ```sh Examples: - avr run rerun run-abc123 - avr run rerun run-abc123 --failed - avr run rerun run-abc123 --yes + avr run rerun run-019e5a2e474e71c297e452279d4bcdea + avr run rerun run-019e5a2e474e71c297e452279d4bcdea --failed + avr run rerun run-019e5a2e474e71c297e452279d4bcdea --yes ``` **Arguments** @@ -191,19 +191,19 @@ runs. ```sh Examples: avr run view - avr run view run-abc123 + avr run view run-019e5a2e474e71c297e452279d4bcdea avr run view 123456789 avr run view https://github.com/acme/widgets/actions/runs/123456789 - avr run view run-abc123 --steps - avr run view run-abc123 --log-failed - avr run view run-abc123 --job Build - avr run view run-abc123 --json conclusion,jobs - avr run view run-abc123 --json '*' --jq '.jobs[].job_name' + avr run view run-019e5a2e474e71c297e452279d4bcdea --steps + avr run view run-019e5a2e474e71c297e452279d4bcdea --log-failed + avr run view run-019e5a2e474e71c297e452279d4bcdea --job Build + avr run view run-019e5a2e474e71c297e452279d4bcdea --json conclusion,jobs + avr run view run-019e5a2e474e71c297e452279d4bcdea --json '*' --jq '.jobs[].job_name' ``` ```sh JSON FIELDS - conclusion, created_at, display_title, duration_seconds, event, + ci_provider, workflow_name, workflow_path, conclusion, created_at, display_title, duration_seconds, event, head_branch, head_sha, jobs, platform_run_id, repository, run_attempt, run_id, run_number, status, triggering_actor, updated_at, workflow, workflow_id @@ -244,8 +244,8 @@ Examples: avr run watch avr run watch --repo acme/web avr run watch --repo a/x --repo b/y - avr run watch run-abc123 --exit-status - avr run watch run-abc123 --ndjson | jq -c . + avr run watch run-019e5a2e474e71c297e452279d4bcdea --exit-status + avr run watch run-019e5a2e474e71c297e452279d4bcdea --ndjson | jq -c . ``` **Arguments** diff --git a/man/man1/avr-job-list.1 b/man/man1/avr-job-list.1 index 9efb694e..4b7be64d 100644 --- a/man/man1/avr-job-list.1 +++ b/man/man1/avr-job-list.1 @@ -22,7 +22,7 @@ Examples: .RS 4 .nf JSON FIELDS - completed_at, conclusion, created_at, duration_seconds, platform_job_id, + ci_provider, run_id, completed_at, conclusion, created_at, duration_seconds, platform_job_id, platform_run_id, job_id, job_name, labels, repository, repository_id, running_on_avrea, started_at, state .fi diff --git a/man/man1/avr-job-view.1 b/man/man1/avr-job-view.1 index 1cf7fb92..cb4a5d75 100644 --- a/man/man1/avr-job-view.1 +++ b/man/man1/avr-job-view.1 @@ -20,7 +20,7 @@ Examples: .RS 4 .nf JSON FIELDS - completed_at, conclusion, created_at, duration_seconds, platform_job_id, + ci_provider, run_id, completed_at, conclusion, created_at, duration_seconds, platform_job_id, platform_run_id, job_id, job_name, labels, repository, repository_id, running_on_avrea, started_at, state, steps, workflow_run .fi diff --git a/man/man1/avr-run-cancel.1 b/man/man1/avr-run-cancel.1 index efad291a..794e5d0d 100644 --- a/man/man1/avr-run-cancel.1 +++ b/man/man1/avr-run-cancel.1 @@ -12,8 +12,8 @@ RUN accepts the same Avrea IDs, GitHub run IDs, and run URLs as `avr run view`. .RS 4 .nf Examples: - avr run cancel run-abc123 - avr run cancel run-abc123 --yes + avr run cancel run-019e5a2e474e71c297e452279d4bcdea + avr run cancel run-019e5a2e474e71c297e452279d4bcdea --yes .fi .RE .SH ARGUMENTS diff --git a/man/man1/avr-run-diagnose.1 b/man/man1/avr-run-diagnose.1 index 7d9160aa..7f051d00 100644 --- a/man/man1/avr-run-diagnose.1 +++ b/man/man1/avr-run-diagnose.1 @@ -12,7 +12,7 @@ RUN accepts the same Avrea IDs, GitHub run IDs, and run URLs as `avr run view`. .RS 4 .nf Examples: - avr run diagnose run-abc123 + avr run diagnose run-019e5a2e474e71c297e452279d4bcdea avr run diagnose 123456789 --json avr run diagnose https://github.com/acme/widgets/actions/runs/123456789 .fi diff --git a/man/man1/avr-run-list.1 b/man/man1/avr-run-list.1 index 8e3c77e3..0ac0d811 100644 --- a/man/man1/avr-run-list.1 +++ b/man/man1/avr-run-list.1 @@ -24,7 +24,7 @@ Examples: .RS 4 .nf JSON FIELDS - conclusion, created_at, display_title, duration_seconds, event, + ci_provider, workflow_name, workflow_path, conclusion, created_at, display_title, duration_seconds, event, head_branch, head_sha, platform_run_id, repository, run_attempt, run_id, run_number, status, triggering_actor, updated_at, workflow, workflow_id .fi diff --git a/man/man1/avr-run-logs.1 b/man/man1/avr-run-logs.1 index 7b1d658d..c9228792 100644 --- a/man/man1/avr-run-logs.1 +++ b/man/man1/avr-run-logs.1 @@ -14,10 +14,10 @@ Long-form alternative to `avr run view --log[-failed]`. Use --follow to tail log .RS 4 .nf Examples: - avr run logs run-abc123 - avr run logs run-abc123 --failed - avr run logs run-abc123 --job test - avr run logs run-abc123 --follow + avr run logs run-019e5a2e474e71c297e452279d4bcdea + avr run logs run-019e5a2e474e71c297e452279d4bcdea --failed + avr run logs run-019e5a2e474e71c297e452279d4bcdea --job test + avr run logs run-019e5a2e474e71c297e452279d4bcdea --follow .fi .RE .SH ARGUMENTS diff --git a/man/man1/avr-run-rerun.1 b/man/man1/avr-run-rerun.1 index 4005bf5c..d18cd50f 100644 --- a/man/man1/avr-run-rerun.1 +++ b/man/man1/avr-run-rerun.1 @@ -12,9 +12,9 @@ RUN accepts the same Avrea IDs, GitHub run IDs, and run URLs as `avr run view`. .RS 4 .nf Examples: - avr run rerun run-abc123 - avr run rerun run-abc123 --failed - avr run rerun run-abc123 --yes + avr run rerun run-019e5a2e474e71c297e452279d4bcdea + avr run rerun run-019e5a2e474e71c297e452279d4bcdea --failed + avr run rerun run-019e5a2e474e71c297e452279d4bcdea --yes .fi .RE .SH ARGUMENTS diff --git a/man/man1/avr-run-view.1 b/man/man1/avr-run-view.1 index ed60c63a..6ca69aaa 100644 --- a/man/man1/avr-run-view.1 +++ b/man/man1/avr-run-view.1 @@ -19,21 +19,21 @@ runs. .nf Examples: avr run view - avr run view run-abc123 + avr run view run-019e5a2e474e71c297e452279d4bcdea avr run view 123456789 avr run view https://github.com/acme/widgets/actions/runs/123456789 - avr run view run-abc123 --steps - avr run view run-abc123 --log-failed - avr run view run-abc123 --job Build - avr run view run-abc123 --json conclusion,jobs - avr run view run-abc123 --json '*' --jq '.jobs[].job_name' + avr run view run-019e5a2e474e71c297e452279d4bcdea --steps + avr run view run-019e5a2e474e71c297e452279d4bcdea --log-failed + avr run view run-019e5a2e474e71c297e452279d4bcdea --job Build + avr run view run-019e5a2e474e71c297e452279d4bcdea --json conclusion,jobs + avr run view run-019e5a2e474e71c297e452279d4bcdea --json '*' --jq '.jobs[].job_name' .fi .RE .PP .RS 4 .nf JSON FIELDS - conclusion, created_at, display_title, duration_seconds, event, + ci_provider, workflow_name, workflow_path, conclusion, created_at, display_title, duration_seconds, event, head_branch, head_sha, jobs, platform_run_id, repository, run_attempt, run_id, run_number, status, triggering_actor, updated_at, workflow, workflow_id diff --git a/man/man1/avr-run-watch.1 b/man/man1/avr-run-watch.1 index 10b32fa3..d36c0dcf 100644 --- a/man/man1/avr-run-watch.1 +++ b/man/man1/avr-run-watch.1 @@ -21,8 +21,8 @@ Examples: avr run watch avr run watch --repo acme/web avr run watch --repo a/x --repo b/y - avr run watch run-abc123 --exit-status - avr run watch run-abc123 --ndjson | jq -c . + avr run watch run-019e5a2e474e71c297e452279d4bcdea --exit-status + avr run watch run-019e5a2e474e71c297e452279d4bcdea --ndjson | jq -c . .fi .RE .SH ARGUMENTS diff --git a/man/man1/avr-run.1 b/man/man1/avr-run.1 index 7b0b50cd..7d8570a7 100644 --- a/man/man1/avr-run.1 +++ b/man/man1/avr-run.1 @@ -1,11 +1,11 @@ .TH AVR\-RUN 1 "" "avr 0.5.0" "Avrea CLI" .SH NAME -avr-run \- View and manage GitHub workflow runs. +avr-run \- View and manage CI workflow runs. .SH SYNOPSIS .B avr run [OPTIONS] COMMAND [ARGS]... .SH DESCRIPTION -View and manage GitHub workflow runs. +View and manage CI workflow runs. .PP .SH COMMANDS .TP diff --git a/man/man1/avr.1 b/man/man1/avr.1 index 291e2be5..128f2ef0 100644 --- a/man/man1/avr.1 +++ b/man/man1/avr.1 @@ -29,7 +29,7 @@ status Show recent runs, performance stats, and cache health. .TP run -View and manage GitHub workflow runs. +View and manage CI workflow runs. .TP pr View pull requests. diff --git a/tests/test_native_ci.py b/tests/test_native_ci.py new file mode 100644 index 00000000..fcfe3807 --- /dev/null +++ b/tests/test_native_ci.py @@ -0,0 +1,103 @@ +"""Native CI records use public reads without fabricated provider IDs.""" + +from avrea_cli.commands.job import _print_jobs_table +from avrea_cli.main import cli +from avrea_cli.run_refs import RunReferenceKind +from avrea_cli.run_refs import parse_run_reference +from click.testing import CliRunner +from pytest import MonkeyPatch +import json +import pytest + +_RUN = "awr-019e5a2e474e71c297e452279d4bcdea" +_JOB = "awj-019e5a2e474e71c297e452279d4bcdea" +_RUN_DATA = { + "run_id": _RUN, + "ci_provider": "avrea_ci", + "display_title": "Native build", + "workflow_name": "Native build", + "workflow_path": ".avrea/workflows/build.yml", + "platform_run_id": None, + "platform_workflow_id": None, + "run_number": None, + "run_attempt": None, + "status": "completed", + "conclusion": "success", + "workflow": None, + "jobs": [], + "repository": {"full_name": "acme/widgets"}, +} +_JOB_DATA = { + "job_id": _JOB, + "run_id": _RUN, + "ci_provider": "avrea_ci", + "job_name": "build", + "platform_job_id": None, + "platform_run_id": None, + "run_attempt": None, + "state": "completed", + "conclusion": "success", + "running_on_avrea": True, + "workflow_run": {"run_id": _RUN, "run_number": None, "run_attempt": None}, +} + + +@pytest.mark.parametrize("reference", [_RUN, f"https://console.avrea.com/org/acme/runs/{_RUN}"]) +def test_native_run_reference(reference: str) -> None: + parsed = parse_run_reference(reference, api_url="https://api.avrea.com") + assert parsed.run_id == _RUN + assert parsed.kind in (RunReferenceKind.AVREA_ID, RunReferenceKind.AVREA_URL) + + +@pytest.mark.parametrize("command,identity,data", [("run", _RUN, _RUN_DATA), ("job", _JOB, _JOB_DATA)]) +def test_native_public_view( + runner: CliRunner, monkeypatch: MonkeyPatch, command: str, identity: str, data: dict +) -> None: + paths: list[str] = [] + + def get(self: object, path: str, **kwargs: object) -> dict: + paths.append(path) + return {"data": data} + + monkeypatch.setattr("avrea_cli.api_client.ApiClient.public_get", get) + result = runner.invoke(cli, [command, "view", identity]) + assert result.exit_code == 0, result.output + assert "avrea_ci" in result.output + assert "#None" not in result.output + assert [path for path in paths if path != "/users/me/organizations"] == [ + f"/orgs/org-default/{'workflow-runs' if command == 'run' else 'jobs'}/{identity}" + ] + + +@pytest.mark.parametrize("command,data", [("run", _RUN_DATA), ("job", _JOB_DATA)]) +def test_native_list_json_provider(runner: CliRunner, monkeypatch: MonkeyPatch, command: str, data: dict) -> None: + monkeypatch.setattr("avrea_cli.api_client.ApiClient.public_get", lambda *args, **kwargs: {"data": [data]}) + result = runner.invoke(cli, [command, "list", "--json", "ci_provider"]) + assert result.exit_code == 0, result.output + assert json.loads(result.output) == [{"ci_provider": "avrea_ci"}] + + +@pytest.mark.parametrize("platform,provider", [("github", "github_actions"), ("gitlab", "gitlab_ci")]) +def test_job_list_identifies_provider_from_old_control( + platform: str, provider: str, capsys: pytest.CaptureFixture[str] +) -> None: + _print_jobs_table([{"job_id": "job-019e5a2e474e71c297e452279d4bcdea", "platform": platform, "state": "completed"}]) + assert provider in capsys.readouterr().out + + +@pytest.mark.parametrize("command", ["list", "view"]) +def test_native_runs_do_not_guess_github_source_links( + runner: CliRunner, monkeypatch: MonkeyPatch, command: str +) -> None: + data = {**_RUN_DATA, "head_branch": "main", "head_sha": "a" * 40} + monkeypatch.setattr("click.testing._NamedTextIOWrapper.isatty", lambda self: True) + monkeypatch.setattr("avrea_cli.commands.run.hyperlink", lambda text, url: f"{text}<{url}>") + monkeypatch.setattr("avrea_cli.commands.run.get_org_slug", lambda *args: "acme") + monkeypatch.setattr( + "avrea_cli.api_client.ApiClient.public_get", + lambda *args, **kwargs: {"data": [data] if command == "list" else data}, + ) + result = runner.invoke(cli, ["--links", "run", command] + ([_RUN] if command == "view" else [])) + assert result.exit_code == 0, result.output + assert "https://github.com/" not in result.output + assert "https://console.avrea.com/" in result.output diff --git a/tests/test_piped_output.py b/tests/test_piped_output.py index 9615c3d6..dcd83229 100644 --- a/tests/test_piped_output.py +++ b/tests/test_piped_output.py @@ -148,6 +148,7 @@ def test_emits_tab_separated_rows_with_header(self, runner, piped, monkeypatch): "run_id", "duration_seconds", "created_at", + "ci_provider", ] cols = lines[1].split("\t") assert cols[0] == "success" @@ -194,6 +195,7 @@ def test_emits_tab_separated_rows_with_header(self, runner, piped, monkeypatch): "job_id", "duration_seconds", "created_at", + "ci_provider", ] assert lines[1].split("\t") == [ "success", @@ -203,6 +205,7 @@ def test_emits_tab_separated_rows_with_header(self, runner, piped, monkeypatch): "job-019d7776b3cd789da094cef01d28f6a2", "134", "2026-04-12T12:03:14Z", + "github_actions", ] diff --git a/tests/test_run_attempts.py b/tests/test_run_attempts.py index 59488d5e..e92b14cf 100644 --- a/tests/test_run_attempts.py +++ b/tests/test_run_attempts.py @@ -45,13 +45,15 @@ def test_prints_new_run_id_when_attempt_appears(self, runner, monkeypatch): def fake_get(self, path, params=None): get_calls.append(path) - if path == "/orgs/org-default/workflow-runs/run-old": - return {"data": {"run_id": "run-old", "platform_run_id": 42, "run_attempt": 1}} + if path == "/orgs/org-default/workflow-runs/run-019e5a2e474e7cba06b5736faf67e54b": + return { + "data": {"run_id": "run-019e5a2e474e7cba06b5736faf67e54b", "platform_run_id": 42, "run_attempt": 1} + } if path == "/orgs/org-default/workflow-runs": return { "data": [ - {"run_id": "run-new", "platform_run_id": 42, "run_attempt": 2}, - {"run_id": "run-old", "platform_run_id": 42, "run_attempt": 1}, + {"run_id": "run-019e5a2e474e711507a0e2f5e69d5dfa", "platform_run_id": 42, "run_attempt": 2}, + {"run_id": "run-019e5a2e474e7cba06b5736faf67e54b", "platform_run_id": 42, "run_attempt": 1}, ] } raise AssertionError(f"unexpected GET: {path}") @@ -62,14 +64,14 @@ def fake_get(self, path, params=None): lambda self, path, **kw: {"status": "rerun_requested"}, ) - result = runner.invoke(cli, ["run", "rerun", "run-old", "--yes"]) + result = runner.invoke(cli, ["run", "rerun", "run-019e5a2e474e7cba06b5736faf67e54b", "--yes"]) assert result.exit_code == 0, result.output assert "Re-run requested" in result.output - assert "run-new" in result.output + assert "run-019e5a2e474e711507a0e2f5e69d5dfa" in result.output assert "attempt 2" in result.output # And the console URL hint should point at the *new* run, not the old one - assert "/runs/run-new" in result.output + assert "/runs/run-019e5a2e474e711507a0e2f5e69d5dfa" in result.output def test_failed_only_passes_through_to_post(self, runner, monkeypatch): post_bodies: list[dict] = [] @@ -82,13 +84,17 @@ def fake_post(self, path, **kw): monkeypatch.setattr( "avrea_cli.api_client.ApiClient.public_get", lambda self, path, params=None: ( - {"data": {"run_id": "run-old", "platform_run_id": 42, "run_attempt": 1}} - if path.endswith("/run-old") - else {"data": [{"run_id": "run-new", "platform_run_id": 42, "run_attempt": 2}]} + {"data": {"run_id": "run-019e5a2e474e7cba06b5736faf67e54b", "platform_run_id": 42, "run_attempt": 1}} + if path.endswith("/run-019e5a2e474e7cba06b5736faf67e54b") + else { + "data": [ + {"run_id": "run-019e5a2e474e711507a0e2f5e69d5dfa", "platform_run_id": 42, "run_attempt": 2} + ] + } ), ) - result = runner.invoke(cli, ["run", "rerun", "run-old", "--failed", "--yes"]) + result = runner.invoke(cli, ["run", "rerun", "run-019e5a2e474e7cba06b5736faf67e54b", "--failed", "--yes"]) assert result.exit_code == 0 assert post_bodies and post_bodies[0] == {"failed_only": True} @@ -97,10 +103,14 @@ def test_poll_timeout_falls_back_to_hint(self, runner, monkeypatch): with a hint instead of failing or hanging forever.""" def fake_get(self, path, params=None): - if path == "/orgs/org-default/workflow-runs/run-old": - return {"data": {"run_id": "run-old", "platform_run_id": 42, "run_attempt": 1}} + if path == "/orgs/org-default/workflow-runs/run-019e5a2e474e7cba06b5736faf67e54b": + return { + "data": {"run_id": "run-019e5a2e474e7cba06b5736faf67e54b", "platform_run_id": 42, "run_attempt": 1} + } # Poll always returns only the old attempt - return {"data": [{"run_id": "run-old", "platform_run_id": 42, "run_attempt": 1}]} + return { + "data": [{"run_id": "run-019e5a2e474e7cba06b5736faf67e54b", "platform_run_id": 42, "run_attempt": 1}] + } monkeypatch.setattr("avrea_cli.api_client.ApiClient.public_get", fake_get) monkeypatch.setattr( @@ -110,12 +120,12 @@ def fake_get(self, path, params=None): # Force the poll to time out immediately monkeypatch.setattr("avrea_cli.commands.run._poll_for_new_attempt", lambda *a, **kw: None) - result = runner.invoke(cli, ["run", "rerun", "run-old", "--yes"]) + result = runner.invoke(cli, ["run", "rerun", "run-019e5a2e474e7cba06b5736faf67e54b", "--yes"]) assert result.exit_code == 0 assert "not yet visible" in result.output # Console URL hint falls back to the old run_id - assert "/runs/run-old" in result.output + assert "/runs/run-019e5a2e474e7cba06b5736faf67e54b" in result.output def test_transient_network_error_retries_until_attempt_appears(self, monkeypatch): """Network blips during the rerun poll must not abort the command — @@ -130,11 +140,11 @@ def test_transient_network_error_retries_until_attempt_appears(self, monkeypatch client.public_get.side_effect = [ httpx.ConnectError("dns blip", request=request), httpx.ReadTimeout("read timeout", request=request), - {"data": [{"run_id": "run-new", "platform_run_id": 42, "run_attempt": 2}]}, + {"data": [{"run_id": "run-019e5a2e474e711507a0e2f5e69d5dfa", "platform_run_id": 42, "run_attempt": 2}]}, ] run = _poll_for_new_attempt(client, "org-1", platform_run_id=42, current_attempt=1, timeout=10.0) assert run is not None - assert run["run_id"] == "run-new" + assert run["run_id"] == "run-019e5a2e474e711507a0e2f5e69d5dfa" assert client.public_get.call_count == 3 def test_skips_poll_when_no_platform_run_id(self, runner, monkeypatch): @@ -148,7 +158,9 @@ def fake_poll(*a, **kw): monkeypatch.setattr( "avrea_cli.api_client.ApiClient.public_get", - lambda self, path, params=None: {"data": {"run_id": "run-old", "run_attempt": 1}}, + lambda self, path, params=None: { + "data": {"run_id": "run-019e5a2e474e7cba06b5736faf67e54b", "run_attempt": 1} + }, ) monkeypatch.setattr( "avrea_cli.api_client.ApiClient.public_post", @@ -156,7 +168,7 @@ def fake_poll(*a, **kw): ) monkeypatch.setattr("avrea_cli.commands.run._poll_for_new_attempt", fake_poll) - result = runner.invoke(cli, ["run", "rerun", "run-old", "--yes"]) + result = runner.invoke(cli, ["run", "rerun", "run-019e5a2e474e7cba06b5736faf67e54b", "--yes"]) assert result.exit_code == 0 assert polled is False assert "Re-run requested" in result.output @@ -170,10 +182,10 @@ def fake_poll(*a, **kw): class TestRunViewOtherAttempts: def test_lists_sibling_attempts(self, runner, monkeypatch): def fake_get(self, path, params=None): - if path == "/orgs/org-default/workflow-runs/run-2": + if path == "/orgs/org-default/workflow-runs/run-019e5a2e474e7d4735e3a265e16eee03": return { "data": { - "run_id": "run-2", + "run_id": "run-019e5a2e474e7d4735e3a265e16eee03", "platform_run_id": 42, "run_attempt": 2, "status": "completed", @@ -190,11 +202,11 @@ def fake_get(self, path, params=None): } } if path == "/orgs/org-default/workflow-runs": - # Three attempts; viewing run-2 — expect run-1 and run-3 listed + # Other attempts appear without repeating the currently viewed run. return { "data": [ { - "run_id": "run-3", + "run_id": "run-019e5a2e474e74e07408562bedb8b60c", "platform_run_id": 42, "run_attempt": 3, "status": "completed", @@ -202,7 +214,7 @@ def fake_get(self, path, params=None): "duration_seconds": 230, }, { - "run_id": "run-2", + "run_id": "run-019e5a2e474e7d4735e3a265e16eee03", "platform_run_id": 42, "run_attempt": 2, "status": "completed", @@ -210,7 +222,7 @@ def fake_get(self, path, params=None): "duration_seconds": 240, }, { - "run_id": "run-1", + "run_id": "run-019e5a2e474e76b86b273ff34fce19d6", "platform_run_id": 42, "run_attempt": 1, "status": "completed", @@ -223,13 +235,13 @@ def fake_get(self, path, params=None): monkeypatch.setattr("avrea_cli.api_client.ApiClient.public_get", fake_get) - result = runner.invoke(cli, ["run", "view", "run-2"]) + result = runner.invoke(cli, ["run", "view", "run-019e5a2e474e7d4735e3a265e16eee03"]) assert result.exit_code == 0, result.output assert "OTHER ATTEMPTS" in result.output - # run-1 and run-3 are siblings; run-2 (the one being viewed) must not appear in the section - assert "run-1" in result.output - assert "run-3" in result.output + # Other attempts appear without repeating the currently viewed run. + assert "run-019e5a2e474e76b86b273ff34fce19d6" in result.output + assert "run-019e5a2e474e74e07408562bedb8b60c" in result.output assert "attempt 1" in result.output assert "attempt 3" in result.output # The being-viewed run shouldn't appear *as a bullet* under OTHER @@ -238,14 +250,14 @@ def fake_get(self, path, params=None): # run on Avrea") and check there. after_header = result.output.split("OTHER ATTEMPTS", 1)[1] bullets = after_header.split("View this run on Avrea", 1)[0] - assert "run-2" not in bullets + assert "run-019e5a2e474e7d4735e3a265e16eee03" not in bullets def test_no_siblings_skips_section(self, runner, monkeypatch): def fake_get(self, path, params=None): - if path == "/orgs/org-default/workflow-runs/run-only": + if path == "/orgs/org-default/workflow-runs/run-019e5a2e474e7f905b19542ed08c9a9c": return { "data": { - "run_id": "run-only", + "run_id": "run-019e5a2e474e7f905b19542ed08c9a9c", "platform_run_id": 99, "run_attempt": 1, "status": "completed", @@ -259,12 +271,16 @@ def fake_get(self, path, params=None): } } if path == "/orgs/org-default/workflow-runs": - return {"data": [{"run_id": "run-only", "platform_run_id": 99, "run_attempt": 1}]} + return { + "data": [ + {"run_id": "run-019e5a2e474e7f905b19542ed08c9a9c", "platform_run_id": 99, "run_attempt": 1} + ] + } raise AssertionError(f"unexpected GET: {path}") monkeypatch.setattr("avrea_cli.api_client.ApiClient.public_get", fake_get) - result = runner.invoke(cli, ["run", "view", "run-only"]) + result = runner.invoke(cli, ["run", "view", "run-019e5a2e474e7f905b19542ed08c9a9c"]) assert result.exit_code == 0 assert "OTHER ATTEMPTS" not in result.output @@ -276,10 +292,10 @@ def test_skips_section_when_no_platform_run_id(self, runner, monkeypatch): def fake_get(self, path, params=None): nonlocal sibling_lookup_called - if path.endswith("/run-x"): + if path.endswith("/run-019e5a2e474e72d711642b726b044016"): return { "data": { - "run_id": "run-x", + "run_id": "run-019e5a2e474e72d711642b726b044016", # platform_run_id intentionally missing "run_attempt": 1, "status": "completed", @@ -297,7 +313,7 @@ def fake_get(self, path, params=None): monkeypatch.setattr("avrea_cli.api_client.ApiClient.public_get", fake_get) - result = runner.invoke(cli, ["run", "view", "run-x"]) + result = runner.invoke(cli, ["run", "view", "run-019e5a2e474e72d711642b726b044016"]) assert result.exit_code == 0 assert sibling_lookup_called is False @@ -324,10 +340,10 @@ def test_sibling_lookup_failure_renders_dim_hint(self, runner, monkeypatch, exc_ crash the whole command.""" def fake_get(self, path, params=None): - if path.endswith("/run-2"): + if path.endswith("/run-019e5a2e474e7d4735e3a265e16eee03"): return { "data": { - "run_id": "run-2", + "run_id": "run-019e5a2e474e7d4735e3a265e16eee03", "platform_run_id": 42, "run_attempt": 1, "status": "completed", @@ -345,7 +361,7 @@ def fake_get(self, path, params=None): monkeypatch.setattr("avrea_cli.api_client.ApiClient.public_get", fake_get) - result = runner.invoke(cli, ["run", "view", "run-2"]) + result = runner.invoke(cli, ["run", "view", "run-019e5a2e474e7d4735e3a265e16eee03"]) # Primary record still rendered. assert result.exit_code == 0, result.output diff --git a/tests/test_run_diagnose.py b/tests/test_run_diagnose.py index 0e16d973..c79d7b86 100644 --- a/tests/test_run_diagnose.py +++ b/tests/test_run_diagnose.py @@ -8,7 +8,7 @@ RUN: dict[str, Any] = { "data": { - "run_id": "run-abc123", + "run_id": "run-019e5a2e474e76ca13d52ca70c883e0f", "platform_run_id": 123456789, "repository_id": "rep-123", "platform_workflow_id": 456, @@ -133,11 +133,13 @@ def _install_api( def fake_get(self, path, params=None, **kwargs): get_calls.append((path, params)) - if path.endswith("/by-platform-id/123456789") or path.endswith("/workflow-runs/run-abc123"): + if path.endswith("/by-platform-id/123456789") or path.endswith( + "/workflow-runs/run-019e5a2e474e76ca13d52ca70c883e0f" + ): return RUN - if path.endswith("/workflow-runs/run-abc123/jobs"): + if path.endswith("/workflow-runs/run-019e5a2e474e76ca13d52ca70c883e0f/jobs"): return jobs_response - if path.endswith("/workflow-runs/run-abc123/metrics"): + if path.endswith("/workflow-runs/run-019e5a2e474e76ca13d52ca70c883e0f/metrics"): if metrics_error: request = httpx.Request("GET", "https://api.avrea.com/metrics") response = httpx.Response(503, request=request) @@ -161,11 +163,11 @@ def fake_post(self, path, json=None, **kwargs): def test_avrea_id_composes_existing_apis(runner, monkeypatch) -> None: get_calls, post_calls = _install_api(monkeypatch) - result = runner.invoke(cli, ["run", "diagnose", "run-abc123", "--json"]) + result = runner.invoke(cli, ["run", "diagnose", "run-019e5a2e474e76ca13d52ca70c883e0f", "--json"]) assert result.exit_code == 0, result.output report = json.loads(result.output) - assert report["run"]["run_id"] == "run-abc123" + assert report["run"]["run_id"] == "run-019e5a2e474e76ca13d52ca70c883e0f" assert report["timing"]["queue_seconds"] == 2 assert report["timing"]["execution_seconds"] == 90 assert report["jobs"][0]["timing"]["queue_seconds"] == 3 @@ -180,20 +182,22 @@ def test_avrea_id_composes_existing_apis(runner, monkeypatch) -> None: paths = {path for path, _ in get_calls} assert paths == { - "/orgs/org-default/workflow-runs/run-abc123", - "/orgs/org-default/workflow-runs/run-abc123/jobs", - "/orgs/org-default/workflow-runs/run-abc123/metrics", + "/orgs/org-default/workflow-runs/run-019e5a2e474e76ca13d52ca70c883e0f", + "/orgs/org-default/workflow-runs/run-019e5a2e474e76ca13d52ca70c883e0f/jobs", + "/orgs/org-default/workflow-runs/run-019e5a2e474e76ca13d52ca70c883e0f/metrics", "/orgs/org-default/workflow-runs/aggregate", } assert all(not path.endswith("/diagnostics") for path in paths) params_by_path = dict(get_calls) - assert params_by_path["/orgs/org-default/workflow-runs/run-abc123"] == {"include": ["workflow"]} - assert params_by_path["/orgs/org-default/workflow-runs/run-abc123/jobs"] == { + assert params_by_path["/orgs/org-default/workflow-runs/run-019e5a2e474e76ca13d52ca70c883e0f"] == { + "include": ["workflow"] + } + assert params_by_path["/orgs/org-default/workflow-runs/run-019e5a2e474e76ca13d52ca70c883e0f/jobs"] == { "limit": 100, "order": "created_at.asc", "include": ["steps"], } - assert params_by_path["/orgs/org-default/workflow-runs/run-abc123/metrics"] == { + assert params_by_path["/orgs/org-default/workflow-runs/run-019e5a2e474e76ca13d52ca70c883e0f/metrics"] == { "source": ["cpu", "memory", "disk-io", "network"] } assert params_by_path["/orgs/org-default/workflow-runs/aggregate"] == { @@ -224,7 +228,7 @@ def test_failed_non_avrea_job_skips_log_search(runner, monkeypatch) -> None: jobs_response={"data": [job], "pagination": {"next_cursor": None}}, ) - result = runner.invoke(cli, ["run", "diagnose", "run-abc123", "--json"]) + result = runner.invoke(cli, ["run", "diagnose", "run-019e5a2e474e76ca13d52ca70c883e0f", "--json"]) assert result.exit_code == 0, result.output report = json.loads(result.output) @@ -248,13 +252,13 @@ def test_github_id_resolves_directly_then_composes(runner, monkeypatch) -> None: "/orgs/org-default/workflow-runs/by-platform-id/123456789", {"include": ["workflow"]}, ) in get_calls - assert not any(path.endswith("/workflow-runs/run-abc123") for path, _ in get_calls) + assert not any(path.endswith("/workflow-runs/run-019e5a2e474e76ca13d52ca70c883e0f") for path, _ in get_calls) def test_human_report_prioritizes_failure_evidence(runner, monkeypatch) -> None: _install_api(monkeypatch) - result = runner.invoke(cli, ["run", "diagnose", "run-abc123"]) + result = runner.invoke(cli, ["run", "diagnose", "run-019e5a2e474e76ca13d52ca70c883e0f"]) assert result.exit_code == 0, result.output assert "CI #42 (attempt 1)" in result.output @@ -271,7 +275,7 @@ def test_human_report_prioritizes_failure_evidence(runner, monkeypatch) -> None: def test_optional_metrics_failure_returns_partial_report(runner, monkeypatch) -> None: _install_api(monkeypatch, metrics_error=True) - result = runner.invoke(cli, ["run", "diagnose", "run-abc123", "--json"]) + result = runner.invoke(cli, ["run", "diagnose", "run-019e5a2e474e76ca13d52ca70c883e0f", "--json"]) assert result.exit_code == 0, result.output report = json.loads(result.output) @@ -305,7 +309,7 @@ def test_invalid_metrics_discard_warnings_from_partial_parse(runner, monkeypatch metrics_response=metrics_response, ) - result = runner.invoke(cli, ["run", "diagnose", "run-abc123", "--json"]) + result = runner.invoke(cli, ["run", "diagnose", "run-019e5a2e474e76ca13d52ca70c883e0f", "--json"]) assert result.exit_code == 0, result.output report = json.loads(result.output) @@ -342,7 +346,7 @@ def test_invalid_run_response_fails_cleanly(runner, monkeypatch) -> None: lambda self, path, **kwargs: {"data": []}, ) - result = runner.invoke(cli, ["run", "diagnose", "run-abc123"]) + result = runner.invoke(cli, ["run", "diagnose", "run-019e5a2e474e76ca13d52ca70c883e0f"]) assert result.exit_code != 0 assert "invalid workflow-run response" in result.output.lower() diff --git a/tests/test_run_logs.py b/tests/test_run_logs.py index 6f850549..8d70d54a 100644 --- a/tests/test_run_logs.py +++ b/tests/test_run_logs.py @@ -25,7 +25,7 @@ def runner(monkeypatch): def _run_with_jobs(jobs: list[dict]) -> dict: - return {"data": {"run_id": "run-1", "jobs": jobs}} + return {"data": {"run_id": "run-019e5a2e474e76b86b273ff34fce19d6", "jobs": jobs}} class TestRunLogsFiltering: @@ -46,7 +46,7 @@ def test_default_prints_all_jobs(self, runner, monkeypatch): ) monkeypatch.setattr("avrea_cli.commands.run.print_logs_grouped", lambda entries, **kw: None) - result = runner.invoke(cli, ["run", "logs", "run-1"]) + result = runner.invoke(cli, ["run", "logs", "run-019e5a2e474e76b86b273ff34fce19d6"]) assert result.exit_code == 0, result.output assert called == ["job-a", "job-b"] @@ -81,7 +81,7 @@ def test_failed_filter_skips_succeeding_jobs(self, runner, monkeypatch): lambda client, jid, steps, **kw: called.append(jid), ) - result = runner.invoke(cli, ["run", "logs", "run-1", "--failed"]) + result = runner.invoke(cli, ["run", "logs", "run-019e5a2e474e76b86b273ff34fce19d6", "--failed"]) assert result.exit_code == 0, result.output assert called == ["job-b"] @@ -95,7 +95,7 @@ def test_failed_filter_with_no_failures_prints_message(self, runner, monkeypatch [{"job_id": "job-a", "job_name": "build", "repository_id": "rep-1", "conclusion": "success"}] ), ) - result = runner.invoke(cli, ["run", "logs", "run-1", "--failed"]) + result = runner.invoke(cli, ["run", "logs", "run-019e5a2e474e76b86b273ff34fce19d6", "--failed"]) assert result.exit_code == 0 assert "No failed jobs" in result.output @@ -116,13 +116,13 @@ def test_job_name_filter_narrows_results(self, runner, monkeypatch): ) monkeypatch.setattr("avrea_cli.commands.run.print_logs_grouped", lambda entries, **kw: None) - result = runner.invoke(cli, ["run", "logs", "run-1", "--job", "test"]) + result = runner.invoke(cli, ["run", "logs", "run-019e5a2e474e76b86b273ff34fce19d6", "--job", "test"]) assert result.exit_code == 0, result.output assert called == ["job-b"] def test_follow_and_failed_are_mutually_exclusive(self, runner): - result = runner.invoke(cli, ["run", "logs", "run-1", "--follow", "--failed"]) + result = runner.invoke(cli, ["run", "logs", "run-019e5a2e474e76b86b273ff34fce19d6", "--follow", "--failed"]) assert result.exit_code != 0 assert "cannot be combined" in result.output @@ -131,7 +131,7 @@ def test_no_jobs_prints_message(self, runner, monkeypatch): "avrea_cli.api_client.ApiClient.public_get", lambda self, path, **kw: _run_with_jobs([]), ) - result = runner.invoke(cli, ["run", "logs", "run-1"]) + result = runner.invoke(cli, ["run", "logs", "run-019e5a2e474e76b86b273ff34fce19d6"]) assert result.exit_code == 0 assert "No matching jobs" in result.output @@ -154,7 +154,7 @@ def test_follow_picks_running_job(self, runner, monkeypatch): "avrea_cli.commands.run.follow_logs", lambda client, org_id, job_id, **kw: followed.append(job_id), ) - result = runner.invoke(cli, ["run", "logs", "run-1", "--follow"]) + result = runner.invoke(cli, ["run", "logs", "run-019e5a2e474e76b86b273ff34fce19d6", "--follow"]) assert result.exit_code == 0, result.output assert followed == ["job-b"] @@ -170,7 +170,7 @@ def test_follow_falls_back_to_first_job_when_none_running(self, runner, monkeypa "avrea_cli.commands.run.follow_logs", lambda client, org_id, job_id, **kw: followed.append(job_id), ) - result = runner.invoke(cli, ["run", "logs", "run-1", "--follow"]) + result = runner.invoke(cli, ["run", "logs", "run-019e5a2e474e76b86b273ff34fce19d6", "--follow"]) assert result.exit_code == 0, result.output assert followed == ["job-a"] @@ -211,7 +211,7 @@ def test_each_failed_conclusion_kept(self, conclusion, runner, monkeypatch, setu ] ), ) - result = runner.invoke(cli, ["run", "logs", "run-1", "--failed"]) + result = runner.invoke(cli, ["run", "logs", "run-019e5a2e474e76b86b273ff34fce19d6", "--failed"]) assert result.exit_code == 0, result.output assert setup, f"--failed should have included a job with conclusion={conclusion!r}" @@ -231,7 +231,7 @@ def test_non_failed_conclusions_filtered(self, conclusion, runner, monkeypatch, ] ), ) - result = runner.invoke(cli, ["run", "logs", "run-1", "--failed"]) + result = runner.invoke(cli, ["run", "logs", "run-019e5a2e474e76b86b273ff34fce19d6", "--failed"]) assert result.exit_code == 0 assert not setup, f"--failed should NOT have included conclusion={conclusion!r}" assert "No failed jobs" in result.output diff --git a/tests/test_run_refs.py b/tests/test_run_refs.py index cbad9135..e5877ed5 100644 --- a/tests/test_run_refs.py +++ b/tests/test_run_refs.py @@ -11,7 +11,14 @@ @pytest.mark.parametrize( ("value", "api_url", "kind", "run_id", "platform_run_id", "attempt"), [ - ("run-abc123", "https://api.avrea.com", RunReferenceKind.AVREA_ID, "run-abc123", None, None), + ( + "run-019e5a2e474e76ca13d52ca70c883e0f", + "https://api.avrea.com", + RunReferenceKind.AVREA_ID, + "run-019e5a2e474e76ca13d52ca70c883e0f", + None, + None, + ), ("123456789", "https://api.avrea.com", RunReferenceKind.GITHUB_ID, None, 123456789, None), ( "https://github.com/acme/widgets/actions/runs/123456789", @@ -38,26 +45,26 @@ 2, ), ( - "https://console.avrea.com/org/acme/runs/run-abc123", + "https://console.avrea.com/org/acme/runs/run-019e5a2e474e76ca13d52ca70c883e0f", "https://api.avrea.com", RunReferenceKind.AVREA_URL, - "run-abc123", + "run-019e5a2e474e76ca13d52ca70c883e0f", None, None, ), ( - "https://console.avrea.com/org/acme/runs/run-abc123/?any=url&paremerts=true", + "https://console.avrea.com/org/acme/runs/run-019e5a2e474e76ca13d52ca70c883e0f/?any=url&paremerts=true", "https://api.avrea.com", RunReferenceKind.AVREA_URL, - "run-abc123", + "run-019e5a2e474e76ca13d52ca70c883e0f", None, None, ), ( - "https://console.test.example.com/org/acme/runs/run-def456", + "https://console.test.example.com/org/acme/runs/run-019e5a2e474e78f61ad5cfa0c471c8cb", "https://api.test.example.com", RunReferenceKind.AVREA_URL, - "run-def456", + "run-019e5a2e474e78f61ad5cfa0c471c8cb", None, None, ), @@ -86,8 +93,8 @@ def test_parse_supported_references(value, api_url, kind, run_id, platform_run_i "https://github.com:443/acme/widgets/actions/runs/123", "https://github.com/acme/widgets/actions/runs/123/attempts/0", "https://github.com/acme/widgets/actions/runs/123/extra", - "https://pr-12.console.test.example.com/org/acme/runs/run-abc123", - "https://console.local.test.example.com/org/acme/runs/run-abc123", + "https://pr-12.console.test.example.com/org/acme/runs/run-019e5a2e474e76ca13d52ca70c883e0f", + "https://console.local.test.example.com/org/acme/runs/run-019e5a2e474e76ca13d52ca70c883e0f", "https://console.avrea.com/org/acme/jobs/job-abc123", ], ) @@ -99,19 +106,19 @@ def test_parse_rejects_malformed_or_untrusted_references(value): def test_avrea_url_must_match_active_environment(): with pytest.raises(click.ClickException, match=r"avr auth switch https://api\.avrea\.com"): parse_run_reference( - "https://console.avrea.com/org/acme/runs/run-abc123", + "https://console.avrea.com/org/acme/runs/run-019e5a2e474e76ca13d52ca70c883e0f", api_url="https://api.test.example.com", ) def test_resolve_numeric_id_uses_direct_platform_endpoint(): client = MagicMock() - client.public_get.return_value = {"data": {"run_id": "run-new", "run_attempt": 3}} + client.public_get.return_value = {"data": {"run_id": "run-019e5a2e474e711507a0e2f5e69d5dfa", "run_attempt": 3}} reference = parse_run_reference("123456789", api_url="https://api.avrea.com") result = resolve_run_reference(client, "org-1", reference, include=["jobs", "workflow"]) - assert result["run_id"] == "run-new" + assert result["run_id"] == "run-019e5a2e474e711507a0e2f5e69d5dfa" client.public_get.assert_called_once_with( "/orgs/org-1/workflow-runs/by-platform-id/123456789", params={"include": ["jobs", "workflow"]}, @@ -123,12 +130,12 @@ def test_resolve_exact_attempt_uses_bounded_platform_filter(): client.public_get.return_value = { "data": [ { - "run_id": "run-new", + "run_id": "run-019e5a2e474e711507a0e2f5e69d5dfa", "run_attempt": 3, "repository": {"full_name": "acme/widgets"}, }, { - "run_id": "run-wanted", + "run_id": "run-019e5a2e474e7ca9066873bf335f3c1d", "run_attempt": 2, "repository": {"full_name": "acme/widgets"}, }, @@ -141,7 +148,7 @@ def test_resolve_exact_attempt_uses_bounded_platform_filter(): result = resolve_run_reference(client, "org-1", reference, include=["jobs"]) - assert result["run_id"] == "run-wanted" + assert result["run_id"] == "run-019e5a2e474e7ca9066873bf335f3c1d" client.public_get.assert_called_once_with( "/orgs/org-1/workflow-runs", params={ @@ -155,7 +162,9 @@ def test_resolve_exact_attempt_uses_bounded_platform_filter(): def test_resolve_github_url_rejects_repository_mismatch(): client = MagicMock() - client.public_get.return_value = {"data": {"run_id": "run-abc123", "repository": {"full_name": "other/widgets"}}} + client.public_get.return_value = { + "data": {"run_id": "run-019e5a2e474e76ca13d52ca70c883e0f", "repository": {"full_name": "other/widgets"}} + } reference = parse_run_reference( "https://github.com/acme/widgets/actions/runs/123456789", api_url="https://api.avrea.com", @@ -167,7 +176,7 @@ def test_resolve_github_url_rejects_repository_mismatch(): def test_avrea_url_exposes_org_slug_for_command_context_validation(): parsed = parse_run_reference( - "https://console.avrea.com/org/acme/runs/run-abc123", + "https://console.avrea.com/org/acme/runs/run-019e5a2e474e76ca13d52ca70c883e0f", api_url="https://api.avrea.com", ) diff --git a/tests/test_run_view.py b/tests/test_run_view.py index 1a486f90..9fd99329 100644 --- a/tests/test_run_view.py +++ b/tests/test_run_view.py @@ -14,7 +14,7 @@ SAMPLE_RUN = { "data": { - "run_id": "run-abc123", + "run_id": "run-019e5a2e474e76ca13d52ca70c883e0f", "display_title": "Fix CI build", "status": "completed", "conclusion": "success", @@ -81,7 +81,7 @@ SAMPLE_RUNS_LIST = { "data": [ { - "run_id": "run-abc123", + "run_id": "run-019e5a2e474e76ca13d52ca70c883e0f", "display_title": "Fix CI build", "status": "completed", "conclusion": "success", @@ -160,12 +160,12 @@ def fake_get(self, path, **kwargs): result = runner.invoke( cli, - ["run", "view", f"{console_url}/org/acme/runs/run-abc123", "--json", "run_id"], + ["run", "view", f"{console_url}/org/acme/runs/run-019e5a2e474e76ca13d52ca70c883e0f", "--json", "run_id"], ) assert result.exit_code == 0, result.output assert calls[-1] == ( - "/orgs/org-acme/workflow-runs/run-abc123", + "/orgs/org-acme/workflow-runs/run-019e5a2e474e76ca13d52ca70c883e0f", {"include": ["jobs", "workflow"]}, ) @@ -185,7 +185,7 @@ def fake_get(self, path, **kwargs): [ "run", "view", - "https://console.avrea.com/org/acme/runs/run-abc123", + "https://console.avrea.com/org/acme/runs/run-019e5a2e474e76ca13d52ca70c883e0f", "--org", "org-other", ], @@ -208,7 +208,7 @@ def fake_get(self, path, **kwargs): [ "run", "view", - "https://console.avrea.com/org/acme/runs/run-abc123", + "https://console.avrea.com/org/acme/runs/run-019e5a2e474e76ca13d52ca70c883e0f", "--org", "org-other", ], @@ -232,7 +232,7 @@ def test_displays_run_metadata(self, runner, monkeypatch): "avrea_cli.api_client.ApiClient.public_get", lambda self, path, **kw: SAMPLE_RUN, ) - result = runner.invoke(cli, ["run", "view", "run-abc123"]) + result = runner.invoke(cli, ["run", "view", "run-019e5a2e474e76ca13d52ca70c883e0f"]) assert result.exit_code == 0 assert "Fix CI build" in result.output assert "push" in result.output @@ -244,7 +244,7 @@ def test_displays_jobs(self, runner, monkeypatch): "avrea_cli.api_client.ApiClient.public_get", lambda self, path, **kw: SAMPLE_RUN, ) - result = runner.invoke(cli, ["run", "view", "run-abc123"]) + result = runner.invoke(cli, ["run", "view", "run-019e5a2e474e76ca13d52ca70c883e0f"]) assert "Build" in result.output assert "Deploy" in result.output assert "JOBS" in result.output @@ -254,7 +254,7 @@ def test_steps_flag_expands_each_job(self, runner, monkeypatch): "avrea_cli.api_client.ApiClient.public_get", lambda self, path, **kw: SAMPLE_RUN, ) - result = runner.invoke(cli, ["run", "view", "run-abc123", "--steps"]) + result = runner.invoke(cli, ["run", "view", "run-019e5a2e474e76ca13d52ca70c883e0f", "--steps"]) assert result.exit_code == 0 assert "Set up job" in result.output @@ -264,31 +264,33 @@ def test_json_output_all_fields(self, runner, monkeypatch): "avrea_cli.api_client.ApiClient.public_get", lambda self, path, **kw: SAMPLE_RUN, ) - result = runner.invoke(cli, ["run", "view", "run-abc123", "--json", "*"]) + result = runner.invoke(cli, ["run", "view", "run-019e5a2e474e76ca13d52ca70c883e0f", "--json", "*"]) assert result.exit_code == 0, result.output parsed = json.loads(result.output) # Output is a JSON object, not array — view commands return one record. assert isinstance(parsed, dict) - assert parsed["run_id"] == "run-abc123" + assert parsed["run_id"] == "run-019e5a2e474e76ca13d52ca70c883e0f" def test_json_output_field_selection(self, runner, monkeypatch): monkeypatch.setattr( "avrea_cli.api_client.ApiClient.public_get", lambda self, path, **kw: SAMPLE_RUN, ) - result = runner.invoke(cli, ["run", "view", "run-abc123", "--json", "run_id,conclusion"]) + result = runner.invoke( + cli, ["run", "view", "run-019e5a2e474e76ca13d52ca70c883e0f", "--json", "run_id,conclusion"] + ) assert result.exit_code == 0, result.output parsed = json.loads(result.output) assert set(parsed.keys()) == {"run_id", "conclusion"} def test_jq_requires_json(self, runner): - result = runner.invoke(cli, ["run", "view", "run-abc123", "-q", ".run_id"]) + result = runner.invoke(cli, ["run", "view", "run-019e5a2e474e76ca13d52ca70c883e0f", "-q", ".run_id"]) assert result.exit_code != 0 assert "--jq requires --json" in result.output def test_requires_auth(self, runner, monkeypatch): monkeypatch.delenv("AVR_TOKEN", raising=False) - result = runner.invoke(cli, ["run", "view", "run-abc123"]) + result = runner.invoke(cli, ["run", "view", "run-019e5a2e474e76ca13d52ca70c883e0f"]) assert result.exit_code != 0 def test_job_filter_keeps_only_matching_jobs(self, runner, monkeypatch): @@ -297,7 +299,7 @@ def test_job_filter_keeps_only_matching_jobs(self, runner, monkeypatch): "avrea_cli.api_client.ApiClient.public_get", lambda self, path, **kw: SAMPLE_RUN, ) - result = runner.invoke(cli, ["run", "view", "run-abc123", "--job", "Build"]) + result = runner.invoke(cli, ["run", "view", "run-019e5a2e474e76ca13d52ca70c883e0f", "--job", "Build"]) assert result.exit_code == 0 assert "Build" in result.output assert "Deploy" not in result.output # filtered out @@ -307,7 +309,7 @@ def test_job_filter_no_match_warns(self, runner, monkeypatch): "avrea_cli.api_client.ApiClient.public_get", lambda self, path, **kw: SAMPLE_RUN, ) - result = runner.invoke(cli, ["run", "view", "run-abc123", "--job", "nosuch"]) + result = runner.invoke(cli, ["run", "view", "run-019e5a2e474e76ca13d52ca70c883e0f", "--job", "nosuch"]) assert result.exit_code == 0 assert "No job matching" in result.output @@ -318,7 +320,7 @@ def test_stopped_by_ctrl_c_exits_130(self, capsys): client = MagicMock() client.public_get.side_effect = KeyboardInterrupt with pytest.raises(SystemExit) as exit_info: - watch_run_loop(client, "org-default", "run-abc123", interval=1, exit_status=True) + watch_run_loop(client, "org-default", "run-019e5a2e474e76ca13d52ca70c883e0f", interval=1, exit_status=True) assert exit_info.value.code == 130 assert "Stopped watching." in capsys.readouterr().out @@ -333,7 +335,7 @@ def test_no_run_id_no_active_runs(self, runner, monkeypatch): def test_requires_auth(self, runner, monkeypatch): monkeypatch.delenv("AVR_TOKEN", raising=False) - result = runner.invoke(cli, ["run", "watch", "run-abc123"]) + result = runner.invoke(cli, ["run", "watch", "run-019e5a2e474e76ca13d52ca70c883e0f"]) assert result.exit_code != 0 def test_explicit_repo_not_found_propagates(self, runner, monkeypatch): @@ -370,7 +372,7 @@ def test_emits_events_in_order_and_exits_clean(self, runner, monkeypatch): snapshots = [ { "data": { - "run_id": "run-x", + "run_id": "run-019e5a2e474e72d711642b726b044016", "status": "in_progress", "jobs": [ {"job_id": "job-1", "platform_job_id": 100, "job_name": "Build", "state": "queued"}, @@ -379,7 +381,7 @@ def test_emits_events_in_order_and_exits_clean(self, runner, monkeypatch): }, { "data": { - "run_id": "run-x", + "run_id": "run-019e5a2e474e72d711642b726b044016", "status": "in_progress", "jobs": [ {"job_id": "job-1", "platform_job_id": 100, "job_name": "Build", "state": "in_progress"}, @@ -388,7 +390,7 @@ def test_emits_events_in_order_and_exits_clean(self, runner, monkeypatch): }, { "data": { - "run_id": "run-x", + "run_id": "run-019e5a2e474e72d711642b726b044016", "status": "completed", "conclusion": "success", "duration_seconds": 30, @@ -407,7 +409,7 @@ def test_emits_events_in_order_and_exits_clean(self, runner, monkeypatch): monkeypatch.setattr("avrea_cli.api_client.ApiClient.public_get", self._stateful_get(snapshots)) monkeypatch.setattr("time.sleep", lambda _: None) - result = runner.invoke(cli, ["run", "watch", "run-x", "--ndjson"]) + result = runner.invoke(cli, ["run", "watch", "run-019e5a2e474e72d711642b726b044016", "--ndjson"]) assert result.exit_code == 0, result.output events = [json.loads(line) for line in result.output.strip().splitlines() if line.startswith("{")] @@ -421,7 +423,7 @@ def test_emits_events_in_order_and_exits_clean(self, runner, monkeypatch): def test_exit_status_propagates_failure(self, runner, monkeypatch): snapshot = { "data": { - "run_id": "run-x", + "run_id": "run-019e5a2e474e72d711642b726b044016", "status": "completed", "conclusion": "failure", "jobs": [], @@ -429,7 +431,9 @@ def test_exit_status_propagates_failure(self, runner, monkeypatch): } monkeypatch.setattr("avrea_cli.api_client.ApiClient.public_get", lambda self, path, **kw: snapshot) monkeypatch.setattr("time.sleep", lambda _: None) - result = runner.invoke(cli, ["run", "watch", "run-x", "--ndjson", "--exit-status"]) + result = runner.invoke( + cli, ["run", "watch", "run-019e5a2e474e72d711642b726b044016", "--ndjson", "--exit-status"] + ) assert result.exit_code == 1 events = [json.loads(line) for line in result.output.strip().splitlines() if line.startswith("{")] assert events[-1]["event"] == "run_completed" @@ -446,7 +450,7 @@ def raise_404(self, path, **kw): monkeypatch.setattr("avrea_cli.api_client.ApiClient.public_get", raise_404) monkeypatch.setattr("time.sleep", lambda _: None) - result = runner.invoke(cli, ["run", "watch", "run-x", "--ndjson"]) + result = runner.invoke(cli, ["run", "watch", "run-019e5a2e474e72d711642b726b044016", "--ndjson"]) # handle_http_error exits 1 for non-401 4xx assert result.exit_code == 1 # Must not have looped indefinitely emitting "Error fetching run" lines. @@ -462,11 +466,18 @@ def maybe_500(self, path, **kw): if calls["i"] == 1: req = httpx.Request("GET", f"https://api.example{path}") raise httpx.HTTPStatusError("502", request=req, response=httpx.Response(502, request=req)) - return {"data": {"run_id": "run-x", "status": "completed", "conclusion": "success", "jobs": []}} + return { + "data": { + "run_id": "run-019e5a2e474e72d711642b726b044016", + "status": "completed", + "conclusion": "success", + "jobs": [], + } + } monkeypatch.setattr("avrea_cli.api_client.ApiClient.public_get", maybe_500) monkeypatch.setattr("time.sleep", lambda _: None) - result = runner.invoke(cli, ["run", "watch", "run-x", "--ndjson"]) + result = runner.invoke(cli, ["run", "watch", "run-019e5a2e474e72d711642b726b044016", "--ndjson"]) assert result.exit_code == 0, result.output # Saw the transient error AND eventually completed. assert "Error fetching run" in result.output @@ -481,7 +492,7 @@ def test_emit_systemexit_propagates_through_runner(self, runner, monkeypatch): which "just returns" would break the real ``| head -n1`` path.""" snapshot = { "data": { - "run_id": "run-x", + "run_id": "run-019e5a2e474e72d711642b726b044016", "status": "completed", "conclusion": "success", "jobs": [], @@ -494,7 +505,7 @@ def _exit_clean(_event): raise SystemExit(0) monkeypatch.setattr("avrea_cli.commands.run._emit_ndjson_event", _exit_clean) - result = runner.invoke(cli, ["run", "watch", "run-x", "--ndjson"]) + result = runner.invoke(cli, ["run", "watch", "run-019e5a2e474e72d711642b726b044016", "--ndjson"]) assert result.exit_code == 0, result.output diff --git a/tests/test_web_flag.py b/tests/test_web_flag.py index 1c6d0e2d..b8c5b5cc 100644 --- a/tests/test_web_flag.py +++ b/tests/test_web_flag.py @@ -18,7 +18,7 @@ def _run_view_sample() -> dict: return copy.deepcopy( { "data": { - "run_id": "run-abc123", + "run_id": "run-019e5a2e474e76ca13d52ca70c883e0f", "platform_run_id": 987654, "run_attempt": 1, "repository": {"full_name": "acme/svc"}, @@ -83,13 +83,13 @@ def test_url_uses_slug_and_workflows_view(self, runner): class TestRunViewWeb: def test_avrea_url_uses_slug(self, runner, monkeypatch): _stub_get(monkeypatch, _run_view_sample()) - result = runner.invoke(cli, ["run", "view", "run-abc123", "--web"]) + result = runner.invoke(cli, ["run", "view", "run-019e5a2e474e76ca13d52ca70c883e0f", "--web"]) assert result.exit_code == 0, result.output - assert "Avrea: https://console.avrea.com/org/acme/runs/run-abc123" in result.output + assert "Avrea: https://console.avrea.com/org/acme/runs/run-019e5a2e474e76ca13d52ca70c883e0f" in result.output def test_github_url_uses_platform_run_id_not_workflow_id(self, runner, monkeypatch): _stub_get(monkeypatch, _run_view_sample()) - result = runner.invoke(cli, ["run", "view", "run-abc123", "--web"]) + result = runner.invoke(cli, ["run", "view", "run-019e5a2e474e76ca13d52ca70c883e0f", "--web"]) assert result.exit_code == 0, result.output assert "GitHub: https://github.com/acme/svc/actions/runs/987654" in result.output # First-attempt URL has no /attempts/N suffix @@ -98,21 +98,23 @@ def test_github_url_uses_platform_run_id_not_workflow_id(self, runner, monkeypat def test_github_url_appends_attempt_suffix_for_reruns(self, runner, monkeypatch): rerun_payload = { "data": { - "run_id": "run-rerun", + "run_id": "run-019e5a2e474e71fae2d16b59d6f78051", "platform_run_id": 987654, "run_attempt": 3, "repository": {"full_name": "acme/svc"}, } } _stub_get(monkeypatch, rerun_payload) - result = runner.invoke(cli, ["run", "view", "run-rerun", "--web"]) + result = runner.invoke(cli, ["run", "view", "run-019e5a2e474e71fae2d16b59d6f78051", "--web"]) assert result.exit_code == 0, result.output assert "/runs/987654/attempts/3" in result.output def test_github_url_omitted_when_platform_run_id_missing(self, runner, monkeypatch): - no_platform = {"data": {"run_id": "run-x", "repository": {"full_name": "acme/svc"}}} + no_platform = { + "data": {"run_id": "run-019e5a2e474e72d711642b726b044016", "repository": {"full_name": "acme/svc"}} + } _stub_get(monkeypatch, no_platform) - result = runner.invoke(cli, ["run", "view", "run-x", "--web"]) + result = runner.invoke(cli, ["run", "view", "run-019e5a2e474e72d711642b726b044016", "--web"]) assert result.exit_code == 0 assert "GitHub:" not in result.output diff --git a/tests/test_workflow_actions.py b/tests/test_workflow_actions.py index e7dcfbc5..575ed468 100644 --- a/tests/test_workflow_actions.py +++ b/tests/test_workflow_actions.py @@ -32,10 +32,12 @@ class TestRunCancelResponse: def test_cancel_requested_branch(self, runner, monkeypatch): monkeypatch.setattr( "avrea_cli.api_client.ApiClient.public_post", - lambda self, path, **kw: {"data": {"run_id": "run-1", "status": "cancel_requested"}}, + lambda self, path, **kw: { + "data": {"run_id": "run-019e5a2e474e76b86b273ff34fce19d6", "status": "cancel_requested"} + }, ) # public_get is hit by get_org_slug; the override above bypasses it. - result = runner.invoke(cli, ["run", "cancel", "run-1", "--yes"]) + result = runner.invoke(cli, ["run", "cancel", "run-019e5a2e474e76b86b273ff34fce19d6", "--yes"]) assert result.exit_code == 0 assert "Cancel requested" in result.output assert "had already finished" not in result.output @@ -43,9 +45,11 @@ def test_cancel_requested_branch(self, runner, monkeypatch): def test_already_terminal_branch(self, runner, monkeypatch): monkeypatch.setattr( "avrea_cli.api_client.ApiClient.public_post", - lambda self, path, **kw: {"data": {"run_id": "run-1", "status": "already_terminal"}}, + lambda self, path, **kw: { + "data": {"run_id": "run-019e5a2e474e76b86b273ff34fce19d6", "status": "already_terminal"} + }, ) - result = runner.invoke(cli, ["run", "cancel", "run-1", "--yes"]) + result = runner.invoke(cli, ["run", "cancel", "run-019e5a2e474e76b86b273ff34fce19d6", "--yes"]) assert result.exit_code == 0 assert "had already finished" in result.output assert "Cancel requested" not in result.output @@ -57,7 +61,7 @@ def test_missing_status_defaults_to_cancel_requested(self, runner, monkeypatch): "avrea_cli.api_client.ApiClient.public_post", lambda self, path, **kw: {}, ) - result = runner.invoke(cli, ["run", "cancel", "run-1", "--yes"]) + result = runner.invoke(cli, ["run", "cancel", "run-019e5a2e474e76b86b273ff34fce19d6", "--yes"]) assert result.exit_code == 0 assert "Cancel requested" in result.output @@ -72,8 +76,16 @@ def test_finds_run_by_workflow_and_created_after(self): dispatch_time = datetime(2026, 5, 4, 10, 0, 0, tzinfo=UTC) client.public_get.return_value = { "data": [ - {"run_id": "run-x", "platform_workflow_id": 200, "created_at": "2026-05-04T10:00:03Z"}, - {"run_id": "run-old", "platform_workflow_id": 200, "created_at": "2026-05-04T09:55:00Z"}, + { + "run_id": "run-019e5a2e474e72d711642b726b044016", + "platform_workflow_id": 200, + "created_at": "2026-05-04T10:00:03Z", + }, + { + "run_id": "run-019e5a2e474e7cba06b5736faf67e54b", + "platform_workflow_id": 200, + "created_at": "2026-05-04T09:55:00Z", + }, ] } @@ -86,7 +98,11 @@ def test_finds_run_by_workflow_and_created_after(self): timeout=5.0, ) - assert run == {"run_id": "run-x", "platform_workflow_id": 200, "created_at": "2026-05-04T10:00:03Z"} + assert run == { + "run_id": "run-019e5a2e474e72d711642b726b044016", + "platform_workflow_id": 200, + "created_at": "2026-05-04T10:00:03Z", + } client.public_get.assert_called_once_with( "/orgs/org-1/workflow-runs", params={"repository_ids": ["rep-abc"], "limit": 100, "order": "created_at.desc"}, @@ -97,7 +113,11 @@ def test_skips_runs_for_other_workflows(self, monkeypatch): dispatch_time = datetime(2026, 5, 4, 10, 0, 0, tzinfo=UTC) client.public_get.return_value = { "data": [ - {"run_id": "run-other", "platform_workflow_id": 999, "created_at": "2026-05-04T10:00:05Z"}, + { + "run_id": "run-019e5a2e474e7d9298a10d1b0735837d", + "platform_workflow_id": 999, + "created_at": "2026-05-04T10:00:05Z", + }, ] } monkeypatch.setattr("time.sleep", lambda _: None) @@ -117,7 +137,11 @@ def test_skips_runs_created_before_dispatch(self, monkeypatch): dispatch_time = datetime(2026, 5, 4, 10, 0, 0, tzinfo=UTC) client.public_get.return_value = { "data": [ - {"run_id": "run-old", "platform_workflow_id": 200, "created_at": "2026-05-04T09:00:00Z"}, + { + "run_id": "run-019e5a2e474e7cba06b5736faf67e54b", + "platform_workflow_id": 200, + "created_at": "2026-05-04T09:00:00Z", + }, ] } monkeypatch.setattr("time.sleep", lambda _: None)