diff --git a/.github/workflows/preview.yml b/.github/workflows/preview.yml new file mode 100644 index 0000000..c243e75 --- /dev/null +++ b/.github/workflows/preview.yml @@ -0,0 +1,171 @@ +# .github/workflows/preview.yml +name: Deploy PR previews + +on: + pull_request: + types: + - opened + - reopened + - synchronize + - closed + +# `pull_request` (never `pull_request_target`) is deliberate: the build below +# executes code from the pull request, so it must not run in a context that has +# access to repository secrets or a writable token. +permissions: {} + +concurrency: + group: preview-${{ github.event.pull_request.number }} + # Deploys push to gh-pages; cancelling one midway can leave it inconsistent. + cancel-in-progress: false + +jobs: + # UNTRUSTED. Builds the pull request's own code: `hatch run docs:build` runs + # packaging/docs tooling, and mkdocs.yml enables `markdown_exec`, which + # executes Python from the docs at build time. This job therefore holds no + # permissions and no credentials -- it only produces an artifact. + build: + name: Build docs + if: github.event.action != 'closed' + runs-on: ubuntu-latest + permissions: + contents: read # checkout only; deliberately nothing else + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Install graphviz + run: sudo apt-get install -y graphviz + + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.10" + + - name: Install Hatch + run: pip install hatch + + - name: Build documentation + env: + PYTHONWARNINGS: ignore + run: hatch -v run docs:build + + - name: Upload built site + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: pr-preview-site + path: ./site/ + retention-days: 1 + + # TRUSTED. Holds the write token but never runs code from the pull request: + # it checks out the base commit and only unpacks the artifact built above. + deploy: + name: Deploy preview + needs: [build] + # Forks get a read-only token no matter what `permissions` says, so a + # deploy from one cannot succeed. Skip it rather than fail the PR; the + # build job above still validates that a fork's docs compile. + if: | + always() && + github.event.pull_request.head.repo.full_name == github.repository && + (github.event.action == 'closed' || needs.build.result == 'success') + runs-on: ubuntu-latest + permissions: + contents: write # push the preview to the gh-pages branch + pull-requests: write # leave/update the sticky preview comment + deployments: read # wait-for-pages-deployment polls the Deployments API + steps: + - name: Checkout the base commit, never the PR head + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event.pull_request.base.sha }} + + - name: Download built site + if: github.event.action != 'closed' + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: pr-preview-site + path: ./site/ + + - name: Deploy preview + uses: rossjrw/pr-preview-action@ffa7509e91a3ec8dfc2e5536c4d5c1acdf7a6de9 # v1.8.1 + with: + source-dir: ./site/ + preview-branch: gh-pages + qr-code: false + wait-for-pages-deployment: true + + portal-preview: + name: Deploy doc-portal preview + needs: [deploy] + # Same-repo only (secrets + write token). Runs after deploy so mkdocs and + # portal never push to gh-pages in parallel — including on PR close. + if: | + always() && + github.event.pull_request.head.repo.full_name == github.repository && + (github.event.action == 'closed' || needs.deploy.result == 'success') + runs-on: ubuntu-latest + permissions: + contents: write # publish under portal-preview/pr-/ on gh-pages + pull-requests: write # sticky comment + env: + PORTAL_PREVIEW_PATH: portal-preview/pr-${{ github.event.pull_request.number }} + DOC_PORTAL_IMAGE: europe-west9-docker.pkg.dev/pasqal-artifact/frontend/doc-portal:source-runtime + steps: + - name: Checkout the base commit, never the PR head + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + ref: ${{ github.event.pull_request.base.sha }} + persist-credentials: false + + - name: Login to Artifact Registry + if: github.event.action != 'closed' + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + registry: europe-west9-docker.pkg.dev + username: _json_key + password: ${{ secrets.GCP_ARTIFACT_REGISTRY_SA }} + + - name: Pull the doc portal image + if: github.event.action != 'closed' + run: docker pull "$DOC_PORTAL_IMAGE" + + - name: Build the portal preview + if: github.event.action != 'closed' + run: | + mkdir -p portal-dist + docker run --rm \ + --user 0:0 \ + --volume "$PWD/portal-dist:/out" \ + --env BUILD_CONFIG='{"qek":"https://pasqal-io.github.io/quantum-evolution-kernel/pr-preview/pr-${{ github.event.pull_request.number }}/"}' \ + --env ASTRO_BASE="/quantum-evolution-kernel/${PORTAL_PREVIEW_PATH}/" \ + --entrypoint sh \ + "$DOC_PORTAL_IMAGE" \ + -c 'cd /app && pnpm --filter @pasqal/doc-portal run build:selected && cp -r /app/packages/doc-portal/dist/. /out/' + + - name: Deploy portal preview + uses: rossjrw/pr-preview-action@ffa7509e91a3ec8dfc2e5536c4d5c1acdf7a6de9 # v1.8.1 + with: + source-dir: ./portal-dist/ + preview-branch: gh-pages + umbrella-dir: portal-preview + qr-code: false + comment: false + + - name: Comment the preview link on the PR + if: github.event.action != 'closed' + uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2.9.4 + with: + header: doc-portal-preview + message: | + Doc portal preview: https://pasqal-io.github.io/quantum-evolution-kernel/${{ env.PORTAL_PREVIEW_PATH }}/applicationsolvingtools/qek/ + + Built from ${{ github.event.pull_request.head.sha }}. + + - name: Remove the preview comment + if: github.event.action == 'closed' + uses: marocchino/sticky-pull-request-comment@773744901bac0e8cbb5a0dc842800d45e9b2b405 # v2.9.4 + with: + header: doc-portal-preview + delete: true