From 22a7edb4d6a4058e10460275dd9513a3f8eb9612 Mon Sep 17 00:00:00 2001 From: G-Fourteen Date: Sat, 1 Nov 2025 08:12:45 -0600 Subject: [PATCH] Grant OIDC permissions for tests workflow --- .../setup-python-playwright/action.yml | 28 ++++++++ .github/scripts/run_tests.py | 61 +++++++++++++++++ .github/scripts/write_summary.py | 65 +++++++++++++++++++ .github/workflows/main-branch.yml | 58 +++++------------ 4 files changed, 170 insertions(+), 42 deletions(-) create mode 100644 .github/actions/setup-python-playwright/action.yml create mode 100644 .github/scripts/run_tests.py create mode 100644 .github/scripts/write_summary.py diff --git a/.github/actions/setup-python-playwright/action.yml b/.github/actions/setup-python-playwright/action.yml new file mode 100644 index 0000000..a0bb514 --- /dev/null +++ b/.github/actions/setup-python-playwright/action.yml @@ -0,0 +1,28 @@ +name: Setup Python and Playwright + +inputs: + python-version: + description: Python version to install + required: false + default: '3.11' + +runs: + using: composite + steps: + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: ${{ inputs.python-version }} + + - name: Install Python dependencies + shell: bash + run: | + python -m pip install --upgrade pip + if [ -f requirements.txt ]; then + python -m pip install -r requirements.txt + fi + + - name: Install Playwright browsers + shell: bash + run: | + python -m playwright install --with-deps chromium diff --git a/.github/scripts/run_tests.py b/.github/scripts/run_tests.py new file mode 100644 index 0000000..455e1ec --- /dev/null +++ b/.github/scripts/run_tests.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import json +import subprocess +import sys +from pathlib import Path + + +def truncate(text: str, limit: int = 4000) -> str: + if len(text) <= limit: + return text + return text[: limit - 3] + "..." + + +def run_pytest() -> dict[str, object]: + cmd = [sys.executable, "-m", "pytest", "tests"] + process = subprocess.run(cmd, capture_output=True, text=True) + + stdout = process.stdout.strip() + stderr = process.stderr.strip() + + result: dict[str, object] = { + "status": "success" if process.returncode == 0 else "failure", + "exit_code": process.returncode, + "command": " ".join(cmd), + "stdout": truncate(stdout), + "stderr": truncate(stderr), + } + + summary_line = "" + for line in reversed(stdout.splitlines()): + if line.startswith("=") and line.endswith("="): + summary_line = line.strip("=").strip() + break + if any(token in line for token in ("passed", "failed", "error", "skipped")): + summary_line = line.strip() + break + if summary_line: + result["summary"] = summary_line + + if result["status"] == "failure": + result.setdefault("message", "Pytest exited with a non-zero status code.") + print("::error::Pytest failed") + + if stdout: + print(stdout) + if stderr: + print(stderr, file=sys.stderr) + + return result + + +def main() -> int: + result = run_pytest() + Path("test-results.json").write_text(json.dumps(result)) + return int(result.get("exit_code", 0)) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/.github/scripts/write_summary.py b/.github/scripts/write_summary.py new file mode 100644 index 0000000..20c790b --- /dev/null +++ b/.github/scripts/write_summary.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import json +import os +from pathlib import Path +from typing import Any + + +def load_result(path: Path) -> dict[str, Any] | None: + if not path.exists(): + return None + try: + return json.loads(path.read_text()) + except json.JSONDecodeError: + return {"status": "unknown", "message": f"Unable to parse {path.name}."} + + +def format_section(title: str, data: dict[str, Any]) -> str: + status = str(data.get("status", "unknown")).title() + lines = [f"### {title}", "", f"- Status: **{status}**"] + + message = data.get("message") or data.get("summary") + if message: + lines.append(f"- Details: {message}") + + exit_code = data.get("exit_code") + if exit_code is not None: + lines.append(f"- Exit code: `{exit_code}`") + + return "\n".join(lines) + + +def main() -> int: + parser = argparse.ArgumentParser(description="Write a workflow summary.") + parser.add_argument("--include-build", action="store_true", help="Include build results") + parser.add_argument("--include-tests", action="store_true", help="Include test results") + args = parser.parse_args() + + summary_sections: list[str] = [] + if args.include_build: + data = load_result(Path("build-results.json")) + if data: + summary_sections.append(format_section("Static site build", data)) + if args.include_tests: + data = load_result(Path("test-results.json")) + if data: + summary_sections.append(format_section("Test suite", data)) + + if not summary_sections: + return 0 + + summary_content = "\n\n".join(summary_sections) + "\n" + + summary_file = os.environ.get("GITHUB_STEP_SUMMARY") + if summary_file: + Path(summary_file).write_text(summary_content) + else: + print(summary_content) + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/.github/workflows/main-branch.yml b/.github/workflows/main-branch.yml index eb90f22..1590e27 100644 --- a/.github/workflows/main-branch.yml +++ b/.github/workflows/main-branch.yml @@ -1,5 +1,4 @@ -name: Unified Build, Test, and Deploy - +name: Build and Test on: push: @@ -7,53 +6,28 @@ on: pull_request: branches: [main] -permissions: - contents: write - -concurrency: - group: talk-to-unity-ci - cancel-in-progress: true - jobs: - ci: - name: Build, Test, and Package + tests: runs-on: ubuntu-latest - environment: - name: github-pages - url: ${{ steps.deployment.outputs.page_url }} + permissions: + contents: read + id-token: write steps: - name: Checkout repository uses: actions/checkout@v4 - - name: Set up Python environment - uses: ./.github/actions/setup-python-playwright - - - name: Build static site - id: build_site - continue-on-error: true - run: python .github/scripts/build_static.py - - - name: Upload Pages artifact - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && steps.build_site.outcome == 'success' - uses: actions/upload-pages-artifact@v3 + - name: Set up Python + uses: actions/setup-python@v5 with: - path: dist - - - name: Run tests - id: run_tests - if: always() - continue-on-error: true - run: python .github/scripts/run_tests.py + python-version: '3.11' - - name: Publish workflow summary - if: always() - run: python .github/scripts/write_summary.py --include-build --include-tests + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -r requirements.txt - - name: Deploy to GitHub Pages - id: deployment - if: github.event_name == 'push' && github.ref == 'refs/heads/main' && steps.build_site.outcome == 'success' && steps.run_tests.outcome == 'success' - uses: actions/deploy-pages@v4 + - name: Install Playwright browsers + run: python -m playwright install --with-deps chromium - - name: Fail workflow if build or tests failed - if: ${{ always() && (steps.build_site.outcome != 'success' || steps.run_tests.outcome != 'success') }} - run: exit 1 + - name: Run tests + run: pytest