feat: Add seasonal adjustment of alb_id for vegetated surfaces #576
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: GitHub Pages Deploy | |
| # RELIABLE GITHUB PAGES DEPLOYMENT | |
| # ================================= | |
| # Deploys static content from site/ and Sphinx docs to GitHub Pages. | |
| # - Push to master: Deploy production site | |
| # - Pull request: Add preview to /preview/pr-{NUMBER}/ (site + docs) | |
| # - Manual dispatch: Force redeploy | |
| # | |
| # Previews are ephemeral - each deployment includes full site from repo. | |
| # No wget required; repo content is the single source of truth. | |
| # | |
| # NOTE: Schema hosting removed - validation handled by Pydantic at runtime | |
| # NOTE: Fork PRs cannot deploy (no OIDC tokens) but still build docs for validation | |
| on: | |
| push: | |
| branches: [master] | |
| paths: | |
| - 'site/**' | |
| - 'docs/**' | |
| - 'src/supy/data_model/**' | |
| - '.github/workflows/pages-deploy.yml' | |
| pull_request: | |
| paths: | |
| - 'site/**' | |
| - 'docs/**' | |
| - 'src/supy/data_model/**' | |
| - '.github/workflows/pages-deploy.yml' | |
| workflow_dispatch: | |
| concurrency: | |
| group: pages-deploy | |
| cancel-in-progress: false | |
| permissions: | |
| contents: read | |
| pages: write | |
| id-token: write | |
| pull-requests: write | |
| jobs: | |
| deploy: | |
| name: Deploy to GitHub Pages | |
| runs-on: ubuntu-latest | |
| # Use 'preview' environment for PRs (no branch restrictions) | |
| environment: | |
| name: ${{ github.event_name == 'pull_request' && 'preview' || 'github-pages' }} | |
| url: ${{ steps.deployment.outputs.page_url }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 # Required for sphinx-last-updated-by-git | |
| - name: Set up Python | |
| uses: actions/setup-python@v5 | |
| with: | |
| python-version: '3.13' | |
| - name: Install uv | |
| uses: astral-sh/setup-uv@v4 | |
| - name: Install pandoc | |
| run: sudo apt-get update && sudo apt-get install -y pandoc | |
| - name: Build documentation | |
| env: | |
| CI: "true" # Enable reduced computation in sphinx-gallery examples | |
| run: | | |
| uv venv | |
| source .venv/bin/activate | |
| # Use non-editable install for CI to avoid meson-python cache path issues | |
| uv pip install ".[dev]" | |
| make docs | |
| - name: Build site | |
| run: | | |
| mkdir -p _site | |
| # Copy production content | |
| cp -r site/* _site/ | |
| # Add PR preview if applicable | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| PR_NUM="${{ github.event.pull_request.number }}" | |
| PR_DIR="preview/pr-${PR_NUM}" | |
| mkdir -p "_site/${PR_DIR}" | |
| cp -r site/* "_site/${PR_DIR}/" | |
| echo "Preview created at /${PR_DIR}/" | |
| # Copy Sphinx docs to preview | |
| mkdir -p "_site/${PR_DIR}/docs" | |
| cp -r docs/build/html/* "_site/${PR_DIR}/docs/" | |
| echo "Docs preview created at /${PR_DIR}/docs/" | |
| # Rewrite absolute paths for preview context | |
| # /brand/ → /preview/pr-N/brand/ so links work within preview | |
| # https://docs.suews.io → /preview/pr-N/docs/ so docs links use preview | |
| echo "Rewriting absolute paths for preview..." | |
| find "_site/${PR_DIR}" -name "*.html" -type f -not -path "*/${PR_DIR}/docs/*" | while read -r f; do | |
| sed -i "s|href=\"/brand/|href=\"/${PR_DIR}/brand/|g" "$f" | |
| sed -i "s|href=\"/\"|href=\"/${PR_DIR}/\"|g" "$f" | |
| sed -i "s|href=\"https://docs.suews.io[/]*\"|href=\"/${PR_DIR}/docs/\"|g" "$f" | |
| done | |
| # Inject preview banner into site pages | |
| PREVIEW_BANNER='<div id="preview-banner" style="background:linear-gradient(90deg,#0077B6,#F7B538);color:#fff;padding:10px 20px;text-align:center;font-family:system-ui,-apple-system,sans-serif;font-size:13px;position:fixed;top:0;left:0;right:0;z-index:9999;box-shadow:0 2px 8px rgba(0,0,0,0.15);"><strong>Preview - PR #'"${PR_NUM}"'</strong> \ \| <a href="https://suews.io/" style="color:#fff;text-decoration:underline;">Production</a> \ \| <a href="https://github.com/UMEP-dev/SUEWS/pull/'"${PR_NUM}"'" style="color:#fff;text-decoration:underline;">View PR</a></div><style>body{padding-top:40px !important;}</style>' | |
| for page in index.html brand/index.html brand/brand-workshop.html brand/suews-logo-source.html; do | |
| target="_site/${PR_DIR}/${page}" | |
| if [ -f "$target" ]; then | |
| sed -i "s|<body>|<body>${PREVIEW_BANNER}|g" "$target" | |
| sed -i "s|<body |<body>${PREVIEW_BANNER}<body-placeholder |g" "$target" | |
| sed -i "s|<body-placeholder |<div |g" "$target" | |
| echo "Injected preview banner into ${page}" | |
| fi | |
| done | |
| # Inject preview banner into docs pages (Sphinx theme structure) | |
| DOCS_BANNER='<div id="preview-banner" style="background:linear-gradient(90deg,#0077B6,#F7B538);color:#fff;padding:8px 16px;text-align:center;font-family:system-ui,-apple-system,sans-serif;font-size:12px;position:fixed;top:0;left:0;right:0;z-index:9999;box-shadow:0 2px 8px rgba(0,0,0,0.15);"><strong>Docs Preview - PR #'"${PR_NUM}"'</strong> \ \| <a href="https://docs.suews.io/" style="color:#fff;text-decoration:underline;">Production Docs</a> \ \| <a href="https://github.com/UMEP-dev/SUEWS/pull/'"${PR_NUM}"'" style="color:#fff;text-decoration:underline;">View PR</a></div><style>.bd-header{top:36px !important;}.bd-sidebar-primary{top:calc(var(--pst-header-height) + 36px) !important;}</style>' | |
| find "_site/${PR_DIR}/docs" -name "*.html" -type f | while read -r f; do | |
| # Inject banner after <body> tag | |
| sed -i "s|<body |<body>${DOCS_BANNER}<body-placeholder |g" "$f" | |
| sed -i "s|<body-placeholder |<div |g" "$f" | |
| done | |
| echo "Injected preview banner into docs pages" | |
| fi | |
| touch _site/.nojekyll | |
| echo "Site ready: $(find _site -type f | wc -l) files" | |
| # Fork PRs cannot deploy - they lack OIDC tokens required by deploy-pages | |
| # Detect fork: compare PR head repo with base repo | |
| - name: Check if fork PR | |
| id: fork-check | |
| run: | | |
| if [ "${{ github.event_name }}" = "pull_request" ]; then | |
| HEAD_REPO="${{ github.event.pull_request.head.repo.full_name }}" | |
| BASE_REPO="${{ github.repository }}" | |
| if [ "$HEAD_REPO" != "$BASE_REPO" ]; then | |
| echo "is_fork=true" >> $GITHUB_OUTPUT | |
| echo "::notice::Fork PR detected ($HEAD_REPO) - skipping deployment" | |
| else | |
| echo "is_fork=false" >> $GITHUB_OUTPUT | |
| fi | |
| else | |
| echo "is_fork=false" >> $GITHUB_OUTPUT | |
| fi | |
| - uses: actions/configure-pages@v5 | |
| if: steps.fork-check.outputs.is_fork != 'true' | |
| - uses: actions/upload-pages-artifact@v3 | |
| if: steps.fork-check.outputs.is_fork != 'true' | |
| with: | |
| path: _site | |
| - uses: actions/deploy-pages@v4 | |
| if: steps.fork-check.outputs.is_fork != 'true' | |
| id: deployment | |
| - name: Comment fork PR status | |
| if: github.event_name == 'pull_request' && steps.fork-check.outputs.is_fork == 'true' | |
| continue-on-error: true # Token may lack permission for fork PRs | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const prNumber = context.issue.number; | |
| const body = `## Docs Build Validated ✓ | |
| Documentation built successfully. Preview deployment is not available for fork PRs (GitHub security restriction). | |
| A maintainer can test the preview locally with \`make docs\`.`; | |
| const comments = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber | |
| }); | |
| const botComment = comments.data.find(c => | |
| c.user.type === 'Bot' && c.body.includes('Docs Build Validated') | |
| ); | |
| if (!botComment) { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| body | |
| }); | |
| } | |
| - name: Comment preview URL | |
| if: github.event_name == 'pull_request' && steps.fork-check.outputs.is_fork != 'true' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const prNumber = context.issue.number; | |
| const body = `## Preview Deployed | |
| | Content | Preview URL | | |
| |---------|-------------| | |
| | Site | https://suews.io/preview/pr-${prNumber}/ | | |
| | Docs | https://suews.io/preview/pr-${prNumber}/docs/ | | |
| > [!NOTE] | |
| > This preview is ephemeral. It will be lost when: | |
| > - Another PR with \`site/\` or \`docs/\` changes is pushed | |
| > - Changes are merged to master | |
| > - A manual workflow dispatch runs | |
| > | |
| > To restore, push any commit to this PR.`; | |
| const comments = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber | |
| }); | |
| const botComment = comments.data.find(c => | |
| c.user.type === 'Bot' && c.body.includes('Preview Deployed') | |
| ); | |
| if (botComment) { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: botComment.id, | |
| body | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: prNumber, | |
| body | |
| }); | |
| } |