Deploy #48
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: Deploy | |
| # Unified deploy workflow for all Netlify deploys produced by either | |
| # "Build Documentation" or "Unit Testing and Deployment". Triggered via | |
| # workflow_run so fork PR CI is never blocked waiting on deployment approval: | |
| # the parent workflows always run freely, and deploys wait for approval in | |
| # their own run which can sit pending indefinitely without affecting the PR's | |
| # required status checks. | |
| # | |
| # workflow_run runs in the context of the default branch and has access to | |
| # secrets, even when the parent run was triggered by a fork PR. GitHub loads | |
| # this workflow file from main, so fork modifications to it are ignored. | |
| on: # zizmor: ignore[dangerous-triggers] | |
| # workflow_run is needed here. It runs in base-repo context so the child has | |
| # access to Netlify secrets for fork PRs. GitHub loads this file from main | |
| # regardless of fork modifications, and no fork code is ever checked out or | |
| # executed (see the checkout comments in each job). Do not add a checkout of | |
| # github.event.workflow_run.head_sha to this workflow. | |
| workflow_run: | |
| workflows: ["Build Documentation", "Unit Testing and Deployment"] | |
| types: [completed] | |
| # Deploys and commit statuses use a GitHub App installation token (see the | |
| # per-job "Get app installation token" steps), not GITHUB_TOKEN, so the | |
| # workflow-level token only needs read access (plus actions: read to | |
| # download artifacts from the triggering workflow run). | |
| permissions: | |
| actions: read | |
| contents: read | |
| jobs: | |
| setup: | |
| name: Load PR info | |
| runs-on: ubuntu-22.04 | |
| # Run on success and failure so image-report jobs (which must run even when | |
| # tests fail) can consume the PR info outputs. preview and release have | |
| # their own stricter conclusion checks. | |
| if: github.event.workflow_run.conclusion == 'success' || github.event.workflow_run.conclusion == 'failure' | |
| outputs: | |
| pr_number: ${{ steps.parse.outputs.pr_number }} | |
| head_sha: ${{ steps.parse.outputs.head_sha }} | |
| is_fork: ${{ steps.parse.outputs.is_fork }} | |
| steps: | |
| - name: Download PR info artifact | |
| if: github.event.workflow_run.event == 'pull_request' | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: pr-info | |
| path: pr-info | |
| run-id: ${{ github.event.workflow_run.id }} | |
| github-token: ${{ github.token }} | |
| # The artifact was written by the triggering run, which a fork controls, so | |
| # only the PR number is read from it, and only if it is a plain integer. The | |
| # head SHA and fork status come from the event payload, which a fork cannot | |
| # forge; is_fork picks the deployment environment, so it must never come | |
| # from the artifact. | |
| - name: Parse PR info | |
| id: parse | |
| env: | |
| HEAD_SHA: ${{ github.event.workflow_run.head_sha }} | |
| IS_FORK: ${{ github.event.workflow_run.head_repository.full_name != github.repository }} | |
| run: | | |
| pr_number="" | |
| if [ -f pr-info/pr_number.txt ]; then | |
| pr_number="$(tr -d '[:space:]' < pr-info/pr_number.txt)" | |
| if ! [[ "$pr_number" =~ ^[0-9]+$ ]]; then | |
| echo "::error::pr-info artifact holds an invalid PR number" | |
| exit 1 | |
| fi | |
| fi | |
| echo "pr_number=$pr_number" >> "$GITHUB_OUTPUT" | |
| echo "head_sha=$HEAD_SHA" >> "$GITHUB_OUTPUT" | |
| echo "is_fork=$IS_FORK" >> "$GITHUB_OUTPUT" | |
| preview: | |
| name: Preview Development Documentation | |
| runs-on: ubuntu-22.04 | |
| needs: setup | |
| # PRs: deploy whenever a docs-build artifact exists, even if Test | |
| # Documentation failed — reviewers still need a preview of the broken | |
| # build. Pushes to main: require the whole workflow to succeed so dev | |
| # docs never publish a failed build. | |
| if: >- | |
| github.event.workflow_run.name == 'Build Documentation' && | |
| ( | |
| (github.event.workflow_run.event == 'pull_request' && | |
| (github.event.workflow_run.conclusion == 'success' || github.event.workflow_run.conclusion == 'failure')) || | |
| (github.event.workflow_run.event == 'push' && | |
| github.event.workflow_run.head_branch == 'main' && | |
| github.event.workflow_run.conclusion == 'success') | |
| ) | |
| environment: | |
| name: ${{ github.event.workflow_run.event == 'push' && 'docs-main' || (needs.setup.outputs.is_fork == 'true' && 'fork-deploys' || 'internal-deploys') }} | |
| steps: | |
| - name: Get app installation token | |
| id: app-token | |
| uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3 | |
| with: | |
| app-id: ${{ vars.PYVISTA_BOT_APP_ID }} | |
| private-key: ${{ secrets.PYVISTA_BOT_APP_PRIVATE_KEY }} | |
| owner: pyvista | |
| repositories: pyvista | |
| # nwtgck/actions-netlify creates a GitHub Deployment; the subsequent | |
| # step posts a commit status. | |
| permission-deployments: write | |
| permission-statuses: write | |
| - name: Download docs-build artifact | |
| continue-on-error: true | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: docs-build | |
| path: . | |
| run-id: ${{ github.event.workflow_run.id }} | |
| github-token: ${{ github.token }} | |
| - name: Check for docs build | |
| id: check | |
| run: | | |
| if [ -f index.html ]; then | |
| echo "has_build=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "has_build=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Preview HTML documentation | |
| if: steps.check.outputs.has_build == 'true' | |
| id: netlify | |
| uses: nwtgck/actions-netlify@d22a32a27c918fe470bbc562e984f80ec48c2668 | |
| with: | |
| publish-dir: . | |
| production-deploy: ${{ github.event.workflow_run.event == 'push' && github.event.workflow_run.head_branch == 'main' }} | |
| github-token: ${{ steps.app-token.outputs.token }} | |
| deploy-message: "Deploy from GitHub Actions" | |
| enable-pull-request-comment: false | |
| enable-commit-comment: false | |
| enable-commit-status: false | |
| github-deployment-environment: docs-preview | |
| github-deployment-description: "Documentation preview" | |
| env: | |
| NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} | |
| NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DEV_SITE_ID }} # DEV site | |
| timeout-minutes: 10 | |
| - name: Create commit status with preview link | |
| if: steps.check.outputs.has_build == 'true' | |
| env: | |
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | |
| DEPLOY_URL: ${{ steps.netlify.outputs.deploy-url }} | |
| HEAD_SHA: ${{ needs.setup.outputs.head_sha }} | |
| REPO: ${{ github.repository }} | |
| run: | | |
| gh api "repos/$REPO/statuses/$HEAD_SHA" \ | |
| -f state=success \ | |
| -f context="Deploy / Docs Preview" \ | |
| -f description="Documentation preview is ready" \ | |
| -f target_url="$DEPLOY_URL" | |
| - name: Add preview link to job summary | |
| if: steps.check.outputs.has_build == 'true' | |
| env: | |
| DEPLOY_URL: ${{ steps.netlify.outputs.deploy-url }} | |
| run: | | |
| echo "### Documentation Preview" >> $GITHUB_STEP_SUMMARY | |
| echo "$DEPLOY_URL" >> $GITHUB_STEP_SUMMARY | |
| doc-image-report: | |
| name: Doc Image Report | |
| runs-on: ubuntu-22.04 | |
| needs: setup | |
| # Run on both success and failure — the whole point of the image report is | |
| # to show what failed. Build-stage failures that produced no test artifact | |
| # are handled gracefully below via continue-on-error and the has_report | |
| # check. The report site tracks `main` through the merge queue run, whose | |
| # commit is the one that lands, and which still holds the images after a | |
| # merge skips the rebuild. | |
| if: >- | |
| (github.event.workflow_run.conclusion == 'success' || github.event.workflow_run.conclusion == 'failure') && | |
| github.event.workflow_run.name == 'Build Documentation' && | |
| (github.event.workflow_run.event == 'pull_request' || | |
| (github.event.workflow_run.event == 'merge_group' && | |
| startsWith(github.event.workflow_run.head_branch, 'gh-readonly-queue/main/'))) | |
| environment: | |
| name: ${{ github.event.workflow_run.event == 'merge_group' && 'docs-main' || (needs.setup.outputs.is_fork == 'true' && 'fork-deploys' || 'internal-deploys') }} | |
| steps: | |
| - name: Get app installation token | |
| id: app-token | |
| uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3 | |
| with: | |
| app-id: ${{ vars.PYVISTA_BOT_APP_ID }} | |
| private-key: ${{ secrets.PYVISTA_BOT_APP_PRIVATE_KEY }} | |
| owner: pyvista | |
| repositories: pyvista | |
| permission-deployments: write | |
| permission-statuses: write | |
| # Check out base repo (main). Never check out the PR head — the whole | |
| # point of workflow_run is that fork code is never executed here. | |
| - uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: 3.14 | |
| - name: Install tox-uv | |
| run: pip install tox-uv | |
| - name: Download doc failed image artifacts | |
| continue-on-error: true | |
| uses: actions/download-artifact@v8 | |
| with: | |
| pattern: doc-failed-test-images | |
| path: all_doc_failed_images | |
| run-id: ${{ github.event.workflow_run.id }} | |
| github-token: ${{ github.token }} | |
| - name: Generate HTML report | |
| id: report | |
| run: | | |
| mkdir -p all_doc_failed_images | |
| tox run -e image-report -- all_doc_failed_images _doc_image_report | |
| if [ -f _doc_image_report/index.html ]; then | |
| echo "has_report=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "has_report=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Deploy to Netlify | |
| if: steps.report.outputs.has_report == 'true' | |
| id: netlify | |
| uses: nwtgck/actions-netlify@d22a32a27c918fe470bbc562e984f80ec48c2668 | |
| with: | |
| publish-dir: _doc_image_report | |
| production-deploy: ${{ github.event.workflow_run.event == 'merge_group' }} | |
| github-token: ${{ steps.app-token.outputs.token }} | |
| deploy-message: "${{ needs.setup.outputs.pr_number != '' && format('Doc image report for PR #{0}', needs.setup.outputs.pr_number) || format('Doc image report for {0}', github.event.workflow_run.head_sha) }}" | |
| enable-pull-request-comment: false | |
| enable-commit-comment: false | |
| enable-commit-status: false | |
| github-deployment-environment: doc-image-report | |
| github-deployment-description: "Doc image comparison report" | |
| env: | |
| NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} | |
| NETLIFY_SITE_ID: ${{ secrets.NETLIFY_DOC_IMAGE_REPORT_SITE_ID }} | |
| timeout-minutes: 5 | |
| - name: Create commit status with report link | |
| if: steps.report.outputs.has_report == 'true' | |
| env: | |
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | |
| DEPLOY_URL: ${{ steps.netlify.outputs.deploy-url }} | |
| HEAD_SHA: ${{ needs.setup.outputs.head_sha }} | |
| REPO: ${{ github.repository }} | |
| run: | | |
| gh api "repos/$REPO/statuses/$HEAD_SHA" \ | |
| -f state=success \ | |
| -f context="Deploy / Doc Image Report" \ | |
| -f description="Doc image comparison report is ready" \ | |
| -f target_url="$DEPLOY_URL" | |
| - name: Add report link to job summary | |
| if: steps.report.outputs.has_report == 'true' | |
| env: | |
| DEPLOY_URL: ${{ steps.netlify.outputs.deploy-url }} | |
| run: | | |
| echo "### Doc Image Report" >> $GITHUB_STEP_SUMMARY | |
| echo "$DEPLOY_URL" >> $GITHUB_STEP_SUMMARY | |
| test-image-report: | |
| name: Test Image Report | |
| runs-on: ubuntu-22.04 | |
| needs: setup | |
| # Run on both success and failure — the whole point of the image report is | |
| # to show what failed. The report site tracks `main` through the merge queue | |
| # run, whose commit is the one that lands, and which runs the full matrix a | |
| # merge no longer repeats. | |
| if: >- | |
| (github.event.workflow_run.conclusion == 'success' || github.event.workflow_run.conclusion == 'failure') && | |
| github.event.workflow_run.name == 'Unit Testing and Deployment' && | |
| (github.event.workflow_run.event == 'pull_request' || | |
| (github.event.workflow_run.event == 'merge_group' && | |
| startsWith(github.event.workflow_run.head_branch, 'gh-readonly-queue/main/'))) | |
| environment: | |
| name: ${{ github.event.workflow_run.event == 'merge_group' && 'docs-main' || (needs.setup.outputs.is_fork == 'true' && 'fork-deploys' || 'internal-deploys') }} | |
| steps: | |
| - name: Get app installation token | |
| id: app-token | |
| uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3 | |
| with: | |
| app-id: ${{ vars.PYVISTA_BOT_APP_ID }} | |
| private-key: ${{ secrets.PYVISTA_BOT_APP_PRIVATE_KEY }} | |
| owner: pyvista | |
| repositories: pyvista | |
| permission-deployments: write | |
| permission-statuses: write | |
| # Check out base repo (main). Never check out the PR head. | |
| - uses: actions/checkout@v7 | |
| with: | |
| persist-credentials: false | |
| - uses: actions/setup-python@v7 | |
| with: | |
| python-version: 3.14 | |
| - name: Install tox-uv | |
| run: pip install tox-uv | |
| - name: Download all failed image artifacts | |
| continue-on-error: true | |
| uses: actions/download-artifact@v8 | |
| with: | |
| pattern: failed_test_images-* | |
| path: all_failed_images | |
| run-id: ${{ github.event.workflow_run.id }} | |
| github-token: ${{ github.token }} | |
| - name: Generate HTML report | |
| id: report | |
| run: | | |
| mkdir -p all_failed_images | |
| tox run -e image-report -- all_failed_images _image_report | |
| if [ -f _image_report/index.html ]; then | |
| echo "has_report=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "has_report=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Deploy to Netlify | |
| if: steps.report.outputs.has_report == 'true' | |
| id: netlify | |
| uses: nwtgck/actions-netlify@d22a32a27c918fe470bbc562e984f80ec48c2668 | |
| with: | |
| publish-dir: _image_report | |
| production-deploy: ${{ github.event.workflow_run.event == 'merge_group' }} | |
| github-token: ${{ steps.app-token.outputs.token }} | |
| deploy-message: "${{ needs.setup.outputs.pr_number != '' && format('Test image report for PR #{0}', needs.setup.outputs.pr_number) || format('Test image report for {0}', github.event.workflow_run.head_sha) }}" | |
| enable-pull-request-comment: false | |
| enable-commit-comment: false | |
| enable-commit-status: false | |
| github-deployment-environment: test-image-report | |
| github-deployment-description: "Test image comparison report" | |
| env: | |
| NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} | |
| NETLIFY_SITE_ID: ${{ secrets.NETLIFY_IMAGE_REPORT_SITE_ID }} | |
| timeout-minutes: 5 | |
| - name: Create commit status with report link | |
| if: steps.report.outputs.has_report == 'true' | |
| env: | |
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | |
| DEPLOY_URL: ${{ steps.netlify.outputs.deploy-url }} | |
| HEAD_SHA: ${{ needs.setup.outputs.head_sha }} | |
| REPO: ${{ github.repository }} | |
| run: | | |
| gh api "repos/$REPO/statuses/$HEAD_SHA" \ | |
| -f state=success \ | |
| -f context="Deploy / Test Image Report" \ | |
| -f description="Image comparison report is ready" \ | |
| -f target_url="$DEPLOY_URL" | |
| - name: Add report link to job summary | |
| if: steps.report.outputs.has_report == 'true' | |
| env: | |
| DEPLOY_URL: ${{ steps.netlify.outputs.deploy-url }} | |
| run: | | |
| echo "### Test Image Report" >> $GITHUB_STEP_SUMMARY | |
| echo "$DEPLOY_URL" >> $GITHUB_STEP_SUMMARY | |
| release: | |
| name: Publish Release Documentation | |
| runs-on: ubuntu-22.04 | |
| if: >- | |
| github.event.workflow_run.conclusion == 'success' && | |
| github.event.workflow_run.name == 'Build Documentation' && | |
| github.event.workflow_run.event == 'push' && | |
| startsWith(github.event.workflow_run.head_branch, 'v') | |
| environment: docs-release | |
| steps: | |
| - name: Download docs-build artifact | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: docs-build | |
| path: . | |
| run-id: ${{ github.event.workflow_run.id }} | |
| github-token: ${{ github.token }} | |
| - name: Deploy Release Documentation | |
| uses: nwtgck/actions-netlify@d22a32a27c918fe470bbc562e984f80ec48c2668 | |
| with: | |
| publish-dir: . | |
| production-deploy: true | |
| github-token: ${{ secrets.GITHUB_TOKEN }} | |
| deploy-message: "Deploy from GitHub Actions" | |
| enable-pull-request-comment: false | |
| enable-commit-comment: false | |
| overwrites-pull-request-comment: false | |
| env: | |
| NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }} | |
| NETLIFY_SITE_ID: ${{ secrets.NETLIFY_STABLE_SITE_ID }} # STABLE site | |
| timeout-minutes: 10 |