From fa3be2af338c2749d622067160e9f57035beb36f Mon Sep 17 00:00:00 2001 From: NullVoxPopuli-ai-agent <268630448+NullVoxPopuli-ai-agent@users.noreply.github.com> Date: Mon, 20 Jul 2026 10:09:45 -0400 Subject: [PATCH] Deploy docs the way ember-primitives does MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ci.yml: the old publish job (cloudflare/pages-action@v1, secret-gated, crowdstrike-era UX_OSS_* secret names) becomes two jobs: 'Build: Docs' runs everywhere and uploads the dist artifact, 'Deploy: Production' (main only) downloads it and publishes via cloudflare/wrangler-action `pages deploy --project-name=ue-form` (matching table's ue- naming) - deploy-preview.yml: trusted workflow_run-triggered preview deploys for PRs, ported from ember-primitives — only the Build job touches PR code (no secrets), the deploy job only handles the built artifact, and a sticky comment posts the preview URL; also manually dispatchable by PR number Requires the ue-form Cloudflare Pages project plus CLOUDFLARE_API_TOKEN / CLOUDFLARE_ACCOUNT_ID repo secrets. Co-Authored-By: Claude Fable 5 --- .github/workflows/ci.yml | 60 +++++----- .github/workflows/deploy-preview.yml | 161 +++++++++++++++++++++++++++ 2 files changed, 195 insertions(+), 26 deletions(-) create mode 100644 .github/workflows/deploy-preview.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 02294ad..f26e706 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -144,40 +144,48 @@ jobs: run: pnpm turbo test --force env: ${{ matrix.env }} - publishDocs: - name: Publish Docs to Cloudflare Pages + build_docs: + name: 'Build: Docs' runs-on: ubuntu-latest + timeout-minutes: 15 needs: - install_dependencies - permissions: - contents: read - deployments: write - pull-requests: write - env: - CLOUDFLARE_API_TOKEN: ${{ secrets.UX_OSS_CLOUDFLARE_API_TOKEN }} steps: - uses: actions/checkout@v4 with: persist-credentials: false + - name: TurboRepo local server + uses: felixmosh/turborepo-gh-artifacts@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} - uses: ./.github/actions/pnpm - run: pnpm build:docs - - name: Publish to Cloudflare Pages - id: publishStep - # skip (rather than fail) when the Cloudflare secrets are not - # configured, e.g. on forks or PRs from forks - if: env.CLOUDFLARE_API_TOKEN != '' - uses: cloudflare/pages-action@v1 + - uses: actions/upload-artifact@v4 + with: + name: docs-app-dist + if-no-files-found: error + path: | + ./docs-app/dist/**/* + !node_modules/ + !./**/node_modules/ + + deploy_docs: + name: 'Deploy: Production' + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + timeout-minutes: 15 + needs: [build_docs] + permissions: + contents: read + deployments: write + steps: + - uses: actions/download-artifact@v4 with: - apiToken: ${{ secrets.UX_OSS_CLOUDFLARE_API_TOKEN }} - accountId: ${{ secrets.UX_OSS_CLOUDFLARE_ACCOUNT_ID }} - projectName: ember-headless-form - directory: ./docs-app/dist - gitHubToken: ${{ secrets.GITHUB_TOKEN }} - - uses: marocchino/sticky-pull-request-comment@v2 - if: env.CLOUDFLARE_API_TOKEN != '' && github.event_name == 'pull_request' + name: docs-app-dist + path: docs-app-dist + - name: Publish + uses: cloudflare/wrangler-action@v4 with: - message: |+ - ## Preview URLs - Env: ${{ steps.publishStep.outputs.environment }} - Docs: ${{ steps.publishStep.outputs.url }} -# # api docs: ${{ steps.publishStep.outputs.url }}/api/modules.html \ No newline at end of file + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + command: pages deploy ./docs-app-dist/ --project-name=ue-form diff --git a/.github/workflows/deploy-preview.yml b/.github/workflows/deploy-preview.yml new file mode 100644 index 0000000..518ead3 --- /dev/null +++ b/.github/workflows/deploy-preview.yml @@ -0,0 +1,161 @@ +# Because C.I. jobs could expose secrets to malicious pull requests, +# GitHub prevents (by default) exposing action secrets to pull requests +# from forks. +# +# This is great, however, the jobs that use the secrets are still useful on +# pull requests. +# +# To run a _trusted_ workflow, we can trigger it from an event from an _untrusted_ +# workflow. This keeps the secrets out of reach from the fork, but still allows +# us to keep the utility of pull request preview deploys. +# Normally, this _trusted_ behavior is offloaded to Cloudflare, Netlify, Vercel, etc +# -- their own workers are trusted and can push comments / updates to pull requests. +# +# To be *most* secure, you'd need to build all the artifacts in the PR, +# then upload them to then be downloaded in the trusted workflows. +# Trusted workflows should not run any scripts from a PR, as malicious +# submitters may tweak the build scripts. +# Since all build artifacts are for the web browser, and not executed in +# node-space, we can be reasonably confident that downloading and deploying +# those artifacts does not compromise our secrets. +# +# More information here: +# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ +name: Deploy Preview + +# read-write repo token +# access to secrets +on: + workflow_dispatch: + inputs: + prNum: + description: 'PR #' + required: true + type: string + + workflow_run: + workflows: ['CI'] + types: + # as early as possible + - requested + +concurrency: + group: deploy-preview-${{ github.event.workflow_run.pull_requests[0].number || github.event.inputs.prNum || github.ref }} + cancel-in-progress: true + +env: + TURBO_API: http://127.0.0.1:9080 + TURBO_TOKEN: this-is-not-a-secret + TURBO_TEAM: myself + +jobs: + determinePR: + # this job gates the others -- if the workflow_run request did not come from a PR, + # exit as early as possible + runs-on: ubuntu-latest + if: github.event.workflow_run.event == 'pull_request' || github.event.inputs.prNum + outputs: + number: ${{ steps.pr-info.outputs.number }} + branch: ${{ steps.pr-info.outputs.branch }} + repo: ${{ steps.pr-info.outputs.repo }} + steps: + - id: pr-info + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + if [ -n "${{ github.event.inputs.prNum }}" ]; then + PR_NUM="${{ github.event.inputs.prNum }}" + PR_JSON=$(gh pr view "$PR_NUM" --repo "${{ github.repository }}" --json headRefName,headRepository,headRepositoryOwner) + BRANCH=$(echo "$PR_JSON" | jq -r '.headRefName') + REPO=$(echo "$PR_JSON" | jq -r '.headRepositoryOwner.login + "/" + .headRepository.name') + else + # For fork PRs, pull_requests[] is empty, so look up by SHA + PR_NUM="${{ github.event.workflow_run.pull_requests[0].number }}" + if [ -z "$PR_NUM" ]; then + HEAD_SHA="${{ github.event.workflow_run.head_sha }}" + PR_NUM=$(gh pr list --repo "${{ github.repository }}" --json number,headRefOid \ + --jq ".[] | select(.headRefOid == \"$HEAD_SHA\") | .number") + fi + BRANCH="${{ github.event.workflow_run.head_branch }}" + REPO="${{ github.event.workflow_run.head_repository.full_name }}" + fi + + echo "number=$PR_NUM" >> "$GITHUB_OUTPUT" + echo "branch=$BRANCH" >> "$GITHUB_OUTPUT" + echo "repo=${REPO:-${{ github.repository }}}" >> "$GITHUB_OUTPUT" + + # This is the only job that needs access to the source code + Build: + runs-on: ubuntu-latest + timeout-minutes: 15 + needs: [determinePR] + steps: + - uses: actions/checkout@v4 + with: + repository: ${{ needs.determinePR.outputs.repo }} + ref: ${{ needs.determinePR.outputs.branch }} + persist-credentials: false + - name: TurboRepo local server + uses: felixmosh/turborepo-gh-artifacts@v3 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + - uses: wyvox/action-setup-pnpm@v4 + with: + node-version: 24 + # a real (pnpm-run) build so pnpm re-syncs the injected + # workspace-package copies created by the install above + - run: pnpm build:packages --force + - run: pnpm build:docs + - uses: actions/upload-artifact@v4 + with: + name: deploy-prep-dist + if-no-files-found: error + path: | + ./docs-app/dist/**/* + !node_modules/ + !./**/node_modules/ + + ################################################################# + # For the rest: + # Does not checkout code, has access to secrets + ################################################################# + + DeployPreview_Docs: + name: 'Deploy: Preview' + runs-on: ubuntu-latest + timeout-minutes: 15 + needs: [Build, determinePR] + permissions: + contents: read + deployments: write + outputs: + docsUrl: ${{ steps.deploy.outputs.deployment-url }} + steps: + - uses: actions/download-artifact@v4 + with: + name: deploy-prep-dist + path: deploy-prep-dist + - id: deploy + uses: cloudflare/wrangler-action@v4 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + command: pages deploy ./deploy-prep-dist/ --project-name=ue-form --branch=${{ needs.determinePR.outputs.branch }} + + PostComment: + name: Post Preview URL as comment to PR + runs-on: ubuntu-latest + needs: [DeployPreview_Docs, determinePR] + permissions: + pull-requests: write + steps: + - uses: marocchino/sticky-pull-request-comment@v3 + with: + header: preview-urls + number: ${{ needs.determinePR.outputs.number }} + message: |+ + | Project | Preview URL | + | ------- | ----------- | + | Docs | ${{ needs.DeployPreview_Docs.outputs.docsUrl }} | + + [Logs](https://github.com/universal-ember/form/actions/runs/${{ github.run_id }})