CatStation/Tools/actions_changelog_github.py
Fildrance dad5ff6516
refactor: extract get previous publish run into separate script, run it and reuse by discord/rss/changelog steps (#45690)
* refactor: extract get previous publish run into separate script, run it and reuse by discord/rss/changelog steps

* refactor: reuse changelog step

* refactor: reuse code for getting core env variables

* refactor: remove output rewrite protection,

* refactor: dry-run mode added for publish workflow, added tag marking

* fix: restore executable bit on Tools scripts run directly by the publish workflow

* feat: announce dry run mode in the publish workflow log

* refactor: use sha for ss14-changelog update call

* refactor: now actually fix last publish sha usage for changelog update

* refactor: force PR creation run even in dry-run

* refactor: remove support email from publish

* refactor: remove duplicate message

* refactor: fix wording for skipping `Create PR from stable to master`
2026-09-21 20:20:11 +00:00

124 lines
3.6 KiB
Python

#!/usr/bin/env python3
"""
Shared GitHub Actions API helpers for getting workflow information.
"""
import os
import re
from typing import Any, Iterable
import requests
GITHUB_API_URL = os.environ.get("GITHUB_API_URL", "https://api.github.com")
def make_github_session(github_token: str) -> requests.Session:
session = requests.Session()
session.headers["Authorization"] = f"Bearer {github_token}"
session.headers["Accept"] = "application/vnd.github+json"
session.headers["X-GitHub-Api-Version"] = "2022-11-28"
return session
def get_required_github_env() -> tuple[str, str, str]:
"""Read and validate the GitHub Actions env vars needed for the API."""
github_repository = os.environ.get("GITHUB_REPOSITORY")
github_run = os.environ.get("GITHUB_RUN_ID")
github_token = os.environ.get("GITHUB_TOKEN")
if not github_repository:
raise RuntimeError("GITHUB_REPOSITORY is not set")
if not github_run:
raise RuntimeError("GITHUB_RUN_ID is not set")
if not github_token:
raise RuntimeError("GITHUB_TOKEN is not set")
return github_repository, github_run, github_token
def get_most_recent_workflow(
sess: requests.Session, github_repository: str, github_run: str
) -> Any:
workflow_run = get_current_run(sess, github_repository, github_run)
past_runs = get_past_runs(sess, workflow_run)
for run in past_runs:
return run
raise RuntimeError("Could not find a previous successful workflow run")
def get_current_run(
sess: requests.Session, github_repository: str, github_run: str
) -> Any:
resp = sess.get(
f"{GITHUB_API_URL}/repos/{github_repository}/actions/runs/{github_run}"
)
resp.raise_for_status()
run = resp.json()
for key in ("id", "created_at", "workflow_url"):
if key not in run:
raise RuntimeError(
f"GitHub API response for current run is missing '{key}'"
)
return run
def get_past_runs(sess: requests.Session, current_run: Any) -> Iterable[Any]:
"""
Get all successful workflow runs before our current one.
"""
params = {
"status": "success",
"created": f"<={current_run['created_at']}",
"per_page": 100,
}
url = f"{current_run['workflow_url']}/runs"
while url:
resp = sess.get(url, params=params)
resp.raise_for_status()
runs = resp.json()
if "workflow_runs" not in runs:
raise RuntimeError(
"GitHub API response for past runs is missing 'workflow_runs'"
)
for run in runs["workflow_runs"]:
# First past successful run that isn't our current run.
if run["id"] == current_run["id"]:
continue
yield run
next_url = resp.links.get("next", {}).get("url")
if not next_url:
break
url = next_url
params = None
def get_last_publish_sha(
sess: requests.Session, github_repository: str, github_run: str
) -> str:
"""
Get the head commit SHA of the most recent successful workflow run.
Validates that the found run actually has a head commit id and that the
value looks like a commit SHA before returning it.
"""
most_recent = get_most_recent_workflow(sess, github_repository, github_run)
head_commit = most_recent.get("head_commit")
last_sha = head_commit.get("id") if head_commit else None
if not last_sha:
raise RuntimeError(
f"Workflow run {most_recent.get('id')} has no head_commit id"
)
print(f"Last successful publish job was {most_recent.get('id')}: {last_sha}")
return last_sha