scripts/ensure_pages_source.py
Rama main · réplica de lectura del repositorio original.
#!/usr/bin/env python3
"""Put the repository's Pages source back on *this workflow* — idempotently.
GitHub Pages publishes through exactly one of two builders:
* ``workflow`` — the ``Pages`` workflow uploads the built static site and
deploys it with ``actions/deploy-pages``. This is what every path in
docs/PAGES.md assumes.
* ``legacy`` — GitHub's built-in builder runs by itself on every push and
publishes one branch folder through Jekyll. With ``main`` / ``docs`` that is
five rendered markdown files and **no index.html**, so ``/Archiver/`` answers
GitHub's "The site configured at this address does not contain the requested
file … you must provide an index.html" 404 — the page the app's users saw on
2026-10-02, while this workflow's own deploy job was green.
``actions/configure-pages`` with ``enablement: true`` looks like the standard
way to assert this and is not: it calls ``getPagesSite()`` first and returns the
existing site when there is one, so ``build_type: workflow`` is only ever sent
when Pages is off entirely. Against a branch-sourced site it succeeds in about
a second having changed nothing. The API call therefore happens here.
The call is safe to run on every push: with the source already on the workflow
it issues a single GET and stops.
Stack: stdlib only, `gh api` for transport (``GH_TOKEN``/``GITHUB_TOKEN`` from
the environment, as the workflow provides them). The first word on stdout is
the machine-readable result — ``already`` | ``switched`` | ``created`` — which
``.github/workflows/pages.yml`` uses to decide whether a built-in build queued
for the same commit needs to settle before the deploy.
"""
from __future__ import annotations
import argparse
import json
import os
import subprocess
import sys
SETTINGS = "Settings → Pages → Build and deployment → Source → GitHub Actions"
class GhError(RuntimeError):
"""`gh api` could not read or change the Pages site."""
def plan(build_type: "str | None") -> str:
"""What to do for the build type the API reported (``None``: no Pages site)."""
if build_type == "workflow":
return "noop"
if build_type is None:
return "create"
return "switch"
def _gh_api(args: "list[str]") -> "subprocess.CompletedProcess[str]":
"""Run ``gh api ARGS`` and hand back the completed process."""
try:
return subprocess.run(["gh", "api", *args], capture_output=True, text=True)
except FileNotFoundError as exc:
raise GhError("the gh CLI is not installed") from exc
def read_build_type(repo: str) -> "str | None":
"""The live ``build_type``, or ``None`` when Pages is not enabled at all."""
proc = _gh_api([f"repos/{repo}/pages"])
if proc.returncode == 0:
try:
return json.loads(proc.stdout or "{}").get("build_type")
except json.JSONDecodeError as exc:
raise GhError(f"unreadable Pages response: {proc.stdout.strip()[:200]}") from exc
if "404" in proc.stderr:
return None
raise GhError(proc.stderr.strip() or proc.stdout.strip() or f"gh exited {proc.returncode}")
def set_workflow_source(repo: str, action: str) -> None:
"""PUT the build type (or POST the site, when Pages has to be created)."""
verb = "PUT" if action == "switch" else "POST"
proc = _gh_api(["-X", verb, f"repos/{repo}/pages", "-f", "build_type=workflow"])
if proc.returncode != 0:
raise GhError(proc.stderr.strip() or proc.stdout.strip() or f"gh exited {proc.returncode}")
def main(argv: "list[str] | None" = None) -> int:
ap = argparse.ArgumentParser(description=__doc__.splitlines()[0])
ap.add_argument("--repo", default=os.environ.get("GITHUB_REPOSITORY", ""),
help="owner/name (default: $GITHUB_REPOSITORY)")
args = ap.parse_args(argv)
if not args.repo:
print("ensure_pages_source: no repository — pass --repo owner/name or set GITHUB_REPOSITORY",
file=sys.stderr)
return 2
try:
build_type = read_build_type(args.repo)
action = plan(build_type)
if action == "noop":
print(f"already: the Pages source is this workflow ({args.repo})")
return 0
set_workflow_source(args.repo, action)
confirmed = read_build_type(args.repo)
if confirmed != "workflow":
raise GhError(f"the API still reports build_type={confirmed!r} after the change")
except GhError as exc:
print(f"ensure_pages_source: {exc}", file=sys.stderr)
print("ensure_pages_source: the Pages source must be GitHub Actions — "
f"{SETTINGS} — or GitHub's built-in branch builder publishes its own folder "
"on every push, replacing the workflow artifact on the live site "
"(with main /docs that is five markdown files and no index.html, "
"so the project page 404s). See docs/PAGES.md.", file=sys.stderr)
return 1
if action == "switch":
print(f"switched: the Pages source is now this workflow ({args.repo}, was {build_type!r})")
else:
print(f"created: the Pages site now publishes from this workflow ({args.repo}, Pages was not enabled)")
return 0
if __name__ == "__main__":
sys.exit(main())