Files
labyricorn-site/scripts/trigger_project_refresh.py
T
Labyricorn ad2fa3729d
Deploy production / deploy (push) Successful in 19s
Limit homepage devlogs and add refresh trigger
2026-08-20 16:03:22 -07:00

101 lines
3.0 KiB
Python

#!/usr/bin/env python3
"""Request an authenticated production refresh after a remote project update."""
from __future__ import annotations
import argparse
import json
import os
import sys
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
DISPATCH_URL = (
"https://git.labyricorn.com/api/v1/repos/Labyricorn/labyricorn-site/"
"actions/workflows/deploy.yml/dispatches"
)
TOKEN_ENVIRONMENT_VARIABLE = "LABYRICORN_REFRESH_TOKEN"
class RefreshError(RuntimeError):
"""Raised when Gitea does not accept a refresh request."""
def dispatch_refresh(token: str, *, timeout: float = 15.0) -> None:
token = token.strip()
if not token:
raise RefreshError("the refresh token is empty")
if "\r" in token or "\n" in token:
raise RefreshError("the refresh token contains an invalid newline")
request = Request(
DISPATCH_URL,
data=json.dumps({"ref": "main"}).encode("utf-8"),
headers={
"Accept": "application/json",
"Authorization": f"token {token}",
"Content-Type": "application/json",
"User-Agent": "labyricorn-project-refresh/1",
},
method="POST",
)
try:
with urlopen(request, timeout=timeout) as response:
status = response.getcode()
except HTTPError as exc:
raise RefreshError(f"Gitea rejected the refresh request (HTTP {exc.code})") from exc
except URLError as exc:
reason = getattr(exc, "reason", "connection failed")
raise RefreshError(f"could not contact Gitea: {reason}") from exc
except OSError as exc:
raise RefreshError(f"could not send the refresh request: {exc}") from exc
if not 200 <= status < 300:
raise RefreshError(f"Gitea returned unexpected HTTP status {status}")
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--dry-run",
action="store_true",
help="show the target workflow without sending a request or reading a token",
)
parser.add_argument(
"--timeout",
type=float,
default=15.0,
help="request timeout in seconds (default: 15)",
)
args = parser.parse_args(argv)
if args.timeout <= 0:
parser.error("--timeout must be greater than zero")
if args.dry_run:
print(f"Would dispatch deploy.yml for main via {DISPATCH_URL}")
return 0
token = os.environ.get(TOKEN_ENVIRONMENT_VARIABLE)
if token is None:
print(
f"project-refresh: ERROR: {TOKEN_ENVIRONMENT_VARIABLE} is not set",
file=sys.stderr,
)
return 2
try:
dispatch_refresh(token, timeout=args.timeout)
except RefreshError as exc:
print(f"project-refresh: ERROR: {exc}", file=sys.stderr)
return 1
print("Production refresh requested for site main.")
print("Verify the Deploy production Action and the published project revision.")
return 0
if __name__ == "__main__":
raise SystemExit(main())