Replace indri-runner-logs with general-purpose runner-logs Typer CLI (#244)
## Summary - Replace bash `indri-runner-logs` with a Python Typer CLI `runner-logs` that supports filtering by runner host (`indri`, `ringtail`, or `all`) with rich table output - Add missing `#USAGE` declarations to `docs-review`, `docs-review-stale`, and `service-review` so flags work without the `--` separator - Update docs references in `review-documentation.md` and `review-services.md` to use the new flag syntax ## Test plan - [x] `mise run runner-logs all` lists runs from both runners - [x] `mise run runner-logs ringtail` filters to ringtail-only runs - [x] `mise run docs-review-stale --threshold 90` works without `--` - [x] `mise run docs-review --limit 5` works without `--` - [x] `mise run service-review --limit 3` works without `--` - [x] Pre-commit hooks pass 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: https://forge.ops.eblu.me/eblume/blumeops/pulls/244
This commit is contained in:
parent
c897fc8e1f
commit
e41c28ed90
8 changed files with 141 additions and 44 deletions
|
|
@ -24,7 +24,7 @@ This reads the `last-reviewed` frontmatter field from each card. Cards without t
|
||||||
To show more entries in the table:
|
To show more entries in the table:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mise run docs-review -- --limit 30
|
mise run docs-review --limit 30
|
||||||
```
|
```
|
||||||
|
|
||||||
### Marking a Card as Reviewed
|
### Marking a Card as Reviewed
|
||||||
|
|
|
||||||
|
|
@ -24,15 +24,15 @@ This reads the tracking file at `service-versions.yaml` (repo root) and sorts by
|
||||||
To show more entries in the table:
|
To show more entries in the table:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mise run service-review -- --limit 30
|
mise run service-review --limit 30
|
||||||
```
|
```
|
||||||
|
|
||||||
To filter by service type:
|
To filter by service type:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
mise run service-review -- --type argocd
|
mise run service-review --type argocd
|
||||||
mise run service-review -- --type ansible
|
mise run service-review --type ansible
|
||||||
mise run service-review -- --type hybrid
|
mise run service-review --type hybrid
|
||||||
```
|
```
|
||||||
|
|
||||||
## Review Process by Service Type
|
## Review Process by Service Type
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ BlumeOps operations are driven by mise tasks. Run `mise tasks` to list all avail
|
||||||
| `docs-review-stale` | Report docs by last-modified date, highlight stale ones |
|
| `docs-review-stale` | Report docs by last-modified date, highlight stale ones |
|
||||||
| `docs-review-tags` | Print frontmatter tag inventory across all docs |
|
| `docs-review-tags` | Print frontmatter tag inventory across all docs |
|
||||||
| `docs-review` | Review the most stale doc by last-reviewed date |
|
| `docs-review` | Review the most stale doc by last-reviewed date |
|
||||||
| `indri-runner-logs` | View Forgejo workflow logs from local runner |
|
| `runner-logs` | View Forgejo workflow logs (indri or ringtail runner) |
|
||||||
|
|
||||||
For ArgoCD operations, use the `argocd` CLI directly:
|
For ArgoCD operations, use the `argocd` CLI directly:
|
||||||
- `argocd app diff <service>` - Preview changes
|
- `argocd app diff <service>` - Preview changes
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
# dependencies = ["pyyaml>=6.0", "rich>=13.0.0", "typer>=0.9.0"]
|
# dependencies = ["pyyaml>=6.0", "rich>=13.0.0", "typer>=0.9.0"]
|
||||||
# ///
|
# ///
|
||||||
#MISE description="Review the most stale documentation card by last-reviewed date"
|
#MISE description="Review the most stale documentation card by last-reviewed date"
|
||||||
|
#USAGE flag "--limit <limit>" default="15" help="Number of docs to show in the table"
|
||||||
"""Review the most stale documentation card by last-reviewed date.
|
"""Review the most stale documentation card by last-reviewed date.
|
||||||
|
|
||||||
Scans all markdown files in docs/ (excluding changelog.d/) and sorts them
|
Scans all markdown files in docs/ (excluding changelog.d/) and sorts them
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
# dependencies = ["rich>=13.0.0", "typer>=0.9.0"]
|
# dependencies = ["rich>=13.0.0", "typer>=0.9.0"]
|
||||||
# ///
|
# ///
|
||||||
#MISE description="Report docs by git-last-modified date, highlighting stale ones"
|
#MISE description="Report docs by git-last-modified date, highlighting stale ones"
|
||||||
|
#USAGE flag "--threshold <threshold>" default="180" help="Days before a doc is considered stale"
|
||||||
"""Report documentation files sorted by git-last-modified date.
|
"""Report documentation files sorted by git-last-modified date.
|
||||||
|
|
||||||
Scans all markdown files in docs/ (excluding changelog.d/) and shows
|
Scans all markdown files in docs/ (excluding changelog.d/) and shows
|
||||||
|
|
|
||||||
|
|
@ -1,38 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
#MISE description="Get logs for a workflow run from indri (local runner only)"
|
|
||||||
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
RUN_ID="${1:-}"
|
|
||||||
|
|
||||||
if [[ -z "$RUN_ID" ]]; then
|
|
||||||
echo "Usage: mise run indri-runner-logs <run_id>"
|
|
||||||
echo ""
|
|
||||||
echo "Fetches logs for a Forgejo Actions run from indri's local storage."
|
|
||||||
echo "Only works for runs executed by the indri-host-runner."
|
|
||||||
echo ""
|
|
||||||
echo "Recent runs:"
|
|
||||||
curl -sf "https://forge.ops.eblu.me/api/v1/repos/eblume/blumeops/actions/tasks" | \
|
|
||||||
jq -r '.workflow_runs[:10] | .[] | " \(.id)\t\(.status)\t\(.workflow_id)\t\(.display_title | .[0:50])"'
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Logs are stored as: actions_log/<owner>/<repo>/<hex_subdir>/<run_id>.log.zst
|
|
||||||
# The hex subdir is the last 2 hex chars of the run_id
|
|
||||||
ACTIONS_LOG_DIR="/opt/homebrew/var/forgejo/data/actions_log/eblume/blumeops"
|
|
||||||
|
|
||||||
# Find the log file - hex subdir is computed from run_id
|
|
||||||
HEX_SUBDIR=$(printf '%02x' "$RUN_ID")
|
|
||||||
LOG_FILE="${ACTIONS_LOG_DIR}/${HEX_SUBDIR}/${RUN_ID}.log.zst"
|
|
||||||
|
|
||||||
# Check if log exists and decompress
|
|
||||||
if ssh indri "test -f '$LOG_FILE'"; then
|
|
||||||
ssh indri "zstd -d -c '$LOG_FILE'"
|
|
||||||
else
|
|
||||||
echo "Error: Log file not found for run $RUN_ID"
|
|
||||||
echo "Expected path: $LOG_FILE"
|
|
||||||
echo ""
|
|
||||||
echo "Available logs:"
|
|
||||||
ssh indri "find '$ACTIONS_LOG_DIR' -name '*.log.zst' -exec basename {} .log.zst \; | sort -n | tail -10"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
131
mise-tasks/runner-logs
Executable file
131
mise-tasks/runner-logs
Executable file
|
|
@ -0,0 +1,131 @@
|
||||||
|
#!/usr/bin/env -S uv run --script
|
||||||
|
# /// script
|
||||||
|
# requires-python = ">=3.12"
|
||||||
|
# dependencies = ["httpx>=0.28.0", "rich>=13.0.0", "typer>=0.15.0"]
|
||||||
|
# ///
|
||||||
|
#MISE description="Get logs for a Forgejo Actions workflow run (indri or ringtail runner)"
|
||||||
|
#USAGE arg "<runner>" help="Runner filter: indri, ringtail, or all"
|
||||||
|
#USAGE arg "[run_id]" help="Run ID to fetch logs for (omit to list recent runs)"
|
||||||
|
"""Fetch Forgejo Actions workflow logs from indri's log storage.
|
||||||
|
|
||||||
|
Both the indri k8s runner and ringtail nix-container-builder runner report
|
||||||
|
logs back to the Forgejo server on indri. This tool lists recent runs
|
||||||
|
(optionally filtered by runner) and fetches compressed logs by run ID.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
mise run runner-logs all # list recent runs from all runners
|
||||||
|
mise run runner-logs ringtail # list recent ringtail runs
|
||||||
|
mise run runner-logs all 337 # fetch logs for run 337
|
||||||
|
"""
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from typing import Annotated
|
||||||
|
|
||||||
|
import httpx
|
||||||
|
import typer
|
||||||
|
from rich.console import Console
|
||||||
|
from rich.table import Table
|
||||||
|
|
||||||
|
FORGE_API = "https://forge.ops.eblu.me/api/v1"
|
||||||
|
REPO = "eblume/blumeops"
|
||||||
|
ACTIONS_LOG_DIR = "/opt/homebrew/var/forgejo/data/actions_log/eblume/blumeops"
|
||||||
|
|
||||||
|
# Workflows using the ringtail nix-container-builder runner; everything else
|
||||||
|
# runs on the indri k8s runner.
|
||||||
|
RINGTAIL_WORKFLOWS = {"build-container-nix.yaml"}
|
||||||
|
|
||||||
|
app = typer.Typer(add_completion=False)
|
||||||
|
|
||||||
|
|
||||||
|
def runner_for_workflow(workflow_id: str) -> str:
|
||||||
|
return "ringtail" if workflow_id in RINGTAIL_WORKFLOWS else "indri"
|
||||||
|
|
||||||
|
|
||||||
|
def list_runs(runner: str, console: Console) -> None:
|
||||||
|
resp = httpx.get(
|
||||||
|
f"{FORGE_API}/repos/{REPO}/actions/tasks",
|
||||||
|
timeout=15,
|
||||||
|
)
|
||||||
|
resp.raise_for_status()
|
||||||
|
runs = resp.json().get("workflow_runs", [])
|
||||||
|
|
||||||
|
table = Table(title=f"Recent runs (filter: {runner})")
|
||||||
|
table.add_column("ID", style="cyan", no_wrap=True)
|
||||||
|
table.add_column("Status")
|
||||||
|
table.add_column("Runner")
|
||||||
|
table.add_column("Name")
|
||||||
|
table.add_column("Title")
|
||||||
|
|
||||||
|
for run in runs[:20]:
|
||||||
|
host = runner_for_workflow(run.get("workflow_id", ""))
|
||||||
|
if runner != "all" and host != runner:
|
||||||
|
continue
|
||||||
|
status = run.get("status", "")
|
||||||
|
style = "green" if status == "success" else "red" if status == "failure" else "yellow"
|
||||||
|
table.add_row(
|
||||||
|
str(run["id"]),
|
||||||
|
f"[{style}]{status}[/{style}]",
|
||||||
|
host,
|
||||||
|
(run.get("name") or "")[:40],
|
||||||
|
(run.get("display_title") or "")[:30],
|
||||||
|
)
|
||||||
|
|
||||||
|
console.print(table)
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_log(run_id: int) -> None:
|
||||||
|
hex_subdir = f"{run_id:02x}"
|
||||||
|
log_file = f"{ACTIONS_LOG_DIR}/{hex_subdir}/{run_id}.log.zst"
|
||||||
|
|
||||||
|
# All logs live on indri (the Forgejo server) regardless of runner
|
||||||
|
result = subprocess.run(
|
||||||
|
["ssh", "indri", f"test -f '{log_file}' && zstd -d -c '{log_file}'"],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
sys.stdout.write(result.stdout)
|
||||||
|
else:
|
||||||
|
typer.echo(f"Error: Log file not found for run {run_id}", err=True)
|
||||||
|
typer.echo(f"Expected path: {log_file}", err=True)
|
||||||
|
typer.echo("", err=True)
|
||||||
|
typer.echo("Available logs:", err=True)
|
||||||
|
avail = subprocess.run(
|
||||||
|
[
|
||||||
|
"ssh",
|
||||||
|
"indri",
|
||||||
|
f"find '{ACTIONS_LOG_DIR}' -name '*.log.zst' -exec basename {{}} .log.zst \\; | sort -n | tail -10",
|
||||||
|
],
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
typer.echo(avail.stdout, err=True)
|
||||||
|
raise typer.Exit(1)
|
||||||
|
|
||||||
|
|
||||||
|
@app.command()
|
||||||
|
def main(
|
||||||
|
runner: Annotated[
|
||||||
|
str,
|
||||||
|
typer.Argument(help="Runner filter: indri, ringtail, or all"),
|
||||||
|
],
|
||||||
|
run_id: Annotated[
|
||||||
|
int | None,
|
||||||
|
typer.Argument(help="Run ID to fetch logs for (omit to list recent runs)"),
|
||||||
|
] = None,
|
||||||
|
) -> None:
|
||||||
|
"""Get logs for a Forgejo Actions workflow run."""
|
||||||
|
if runner not in ("indri", "ringtail", "all"):
|
||||||
|
typer.echo(f"Error: runner must be 'indri', 'ringtail', or 'all', got '{runner}'")
|
||||||
|
raise typer.Exit(1)
|
||||||
|
|
||||||
|
if run_id is None:
|
||||||
|
list_runs(runner, Console())
|
||||||
|
else:
|
||||||
|
fetch_log(run_id)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app()
|
||||||
|
|
@ -4,6 +4,8 @@
|
||||||
# dependencies = ["pyyaml>=6.0", "rich>=13.0.0", "typer>=0.9.0"]
|
# dependencies = ["pyyaml>=6.0", "rich>=13.0.0", "typer>=0.9.0"]
|
||||||
# ///
|
# ///
|
||||||
#MISE description="Review the most stale service for version freshness"
|
#MISE description="Review the most stale service for version freshness"
|
||||||
|
#USAGE flag "--limit <limit>" default="15" help="Number of services to show in the table"
|
||||||
|
#USAGE flag "--type <type>" help="Filter by service type (argocd, ansible, hybrid)"
|
||||||
"""Review the most stale service for version freshness.
|
"""Review the most stale service for version freshness.
|
||||||
|
|
||||||
Reads ``docs/reference/services/service-versions.yaml`` and sorts services
|
Reads ``docs/reference/services/service-versions.yaml`` and sorts services
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue