Fetch job logs via SSH to indri instead of Forgejo web endpoint
Forgejo's web action routes don't support API token auth for private repos (only session cookies or public access). Switch log fetching to read the zstd-compressed log files directly from indri via SSH — Forgejo stores all runner logs on disk regardless of which runner executed the job. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4f5a963ef6
commit
71c1c453d6
2 changed files with 38 additions and 9 deletions
|
|
@ -1 +1 @@
|
|||
runner-logs now authenticates with Forgejo API token (works on private repos) and auto-detects the repo from git remote.
|
||||
runner-logs now authenticates with Forgejo API token and auto-detects the repo from git remote. Job logs are fetched via SSH to indri (reading Forgejo's on-disk zstd log files) instead of the web endpoint, which doesn't support token auth for private repos.
|
||||
|
|
|
|||
|
|
@ -203,18 +203,47 @@ def show_jobs(run_number: int, repo: str, token: str, console: Console) -> None:
|
|||
|
||||
|
||||
def fetch_log(run_number: int, job_index: int, repo: str, token: str) -> None:
|
||||
"""Fetch logs for a specific job via the Forgejo web endpoint."""
|
||||
url = f"{FORGE_URL}/{repo}/actions/runs/{run_number}/jobs/{job_index}/attempt/1/logs"
|
||||
resp = httpx.get(url, headers=auth_headers(token), timeout=30, follow_redirects=True)
|
||||
if resp.status_code == 404:
|
||||
"""Fetch logs for a specific job via SSH to indri.
|
||||
|
||||
Forgejo stores action logs as zstd-compressed files on disk at
|
||||
~/forgejo/data/actions_log/{owner}/{repo}/{hex_prefix}/{task_id}.log.zst
|
||||
regardless of which runner executed the job. The web log endpoint doesn't
|
||||
support API-token auth for private repos, so we read the files directly.
|
||||
"""
|
||||
tasks = fetch_tasks(repo, token)
|
||||
jobs = sorted(
|
||||
[t for t in tasks if t["run_number"] == run_number],
|
||||
key=lambda x: x["id"],
|
||||
)
|
||||
if not jobs:
|
||||
typer.echo(f"Error: No jobs found for run #{run_number}", err=True)
|
||||
raise typer.Exit(1)
|
||||
if job_index < 0 or job_index >= len(jobs):
|
||||
typer.echo(
|
||||
f"Error: No logs found for run #{run_number} job {job_index}",
|
||||
f"Error: job index {job_index} out of range (run #{run_number} has {len(jobs)} jobs)",
|
||||
err=True,
|
||||
)
|
||||
typer.echo(f"URL: {url}", err=True)
|
||||
raise typer.Exit(1)
|
||||
resp.raise_for_status()
|
||||
sys.stdout.write(resp.text)
|
||||
|
||||
task_id = jobs[job_index]["id"]
|
||||
hex_prefix = f"{task_id & 0xff:02x}"
|
||||
log_path = f"~/forgejo/data/actions_log/{repo}/{hex_prefix}/{task_id}.log.zst"
|
||||
|
||||
result = subprocess.run(
|
||||
["ssh", "indri", f"zstdcat {log_path}"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
typer.echo(
|
||||
f"Error: could not read log for run #{run_number} job {job_index} (task {task_id})",
|
||||
err=True,
|
||||
)
|
||||
typer.echo(f"Path: indri:{log_path}", err=True)
|
||||
if result.stderr.strip():
|
||||
typer.echo(result.stderr.strip(), err=True)
|
||||
raise typer.Exit(1)
|
||||
sys.stdout.write(result.stdout)
|
||||
|
||||
|
||||
@app.command()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue