From b807345d6f04340a5f05d9a28814b71c0e943898 Mon Sep 17 00:00:00 2001 From: Florian Date: Fri, 6 Nov 2026 16:14:01 +0100 Subject: [PATCH] Added workflow to generate openapi documentation --- .gitea/workflows/openapi-gateway-docs.yaml | 57 ++++++++++++++++++++++ scripts/dump_openapi.py | 55 +++++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 .gitea/workflows/openapi-gateway-docs.yaml create mode 100644 scripts/dump_openapi.py diff --git a/.gitea/workflows/openapi-gateway-docs.yaml b/.gitea/workflows/openapi-gateway-docs.yaml new file mode 100644 index 0000000..d0e5b4e --- /dev/null +++ b/.gitea/workflows/openapi-gateway-docs.yaml @@ -0,0 +1,57 @@ +name: Generate openapi.json +on: + push: + branches: [main] + workflow_dispatch: + +env: + TITLE: seadex + OUTPUT: service-seadex-openapi.json + DOCS_BRANCH_BASE: main + +jobs: + generate-docs: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + + - name: Generate openapi.json + shell: bash + run: | + pip install -r requirements.txt + python scripts/dump_openapi.py --out "${{ env.OUTPUT }}" --title "${{ env.TITLE }}" + python gen_docs.py + + - name: Clone openapi-gateway-docs repo + uses: actions/checkout@v4 + with: + repository: ${{ github.repository_owner }}/openapi-gateway-docs + path: ./openapi-gateway-docs + ref: ${{ env.DOCS_BRANCH_BASE }} + token: ${{ secrets.BOT_TOKEN }} + + - name: Move generated openapi.json + run: | + mkdir -p openapi-gateway-docs/sources + mv "${{ env.OUTPUT }}" "openapi-gateway-docs/sources/${{ env.OUTPUT }}" + + - name: Update openapi-gateway-docs + working-directory: ./openapi-gateway-docs + run: | + git config user.name "automation-bot" + git config user.email "dev@gansejunge.com" + + git add sources API-*.md + if git diff --cached --quiet; then + echo "No documentation changes to commit" + exit 0 + fi + + COMMIT_URL="https://git.gansejunge.com/${GITHUB_REPOSITORY}/commit/${GITHUB_SHA}" + git commit -m "Updated docs for ${GITHUB_REPOSITORY}" -m "Linked build commit: $COMMIT_URL" + git pull --rebase origin "$DOCS_BRANCH_BASE" + git push origin "HEAD:$DOCS_BRANCH_BASE" \ No newline at end of file diff --git a/scripts/dump_openapi.py b/scripts/dump_openapi.py new file mode 100644 index 0000000..16cd84a --- /dev/null +++ b/scripts/dump_openapi.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +"""Generate openapi.json from the routers in {root}/src/routers/*. + +Usage (from anywhere, with the build's venv): + python scripts/dump_openapi.py [--src SRC] [--out OUT] [--title TITLE] +""" +import argparse +import importlib +import json +import pkgutil +import sys +from pathlib import Path + +from fastapi import APIRouter, FastAPI + +ROOT = Path(__file__).resolve().parent.parent + + +def main() -> int: + parser = argparse.ArgumentParser() + parser.add_argument("--src", type=Path, default=ROOT / "src") + parser.add_argument("--out", type=Path, default=ROOT / "openapi.json") + parser.add_argument("--title", default=ROOT.name) + args = parser.parse_args() + + src = args.src.resolve() + routers_dir = src / "routers" + if not routers_dir.is_dir(): + print(f"No routers directory found at {routers_dir}", file=sys.stderr) + return 1 + + # Makes `import models`, `import routers.x` and similar work + sys.path.insert(0, str(src)) + + app = FastAPI(title=args.title) + seen = set() + + for mod_info in pkgutil.walk_packages([str(routers_dir)], prefix="routers."): + if mod_info.ispkg: + continue + module = importlib.import_module(mod_info.name) + for obj in vars(module).values(): + # Only include routers defined in this module, not ones it merely imports + if isinstance(obj, APIRouter) and id(obj) not in seen: + seen.add(id(obj)) + app.include_router(obj) + + # Write straight to the file so stray prints during import can't corrupt the JSON + args.out.write_text(json.dumps(app.openapi(), indent=2), encoding="utf-8") + print(f"Wrote {args.out} ({len(seen)} routers)") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file