Skip to content

Deploy Preview

Deploy Preview #3407

# Because C.I. jobs could expose secrets to malicious pull requsets,
# 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, browserstack running, etc.
# Normally, this _trusted_ behavior is offloaded by Cloudflare, Netlify, Vercel, etc
# -- their own workers are trusted and can push comments / updates to pull requests.
#
# We don't want to use their slower (and sometimes paid) hardware.
# When we use our own workflows, we can re-use the cache built from the PR
# (or elsewhere).
#
# 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 testing/deploying
# those artifacts does not compromise our secrets.
#
#
# More information here:
# https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
#
# Things that would make this easier:
# Readablity: https://github.com/actions/download-artifact/issues/172
# Security:
# - if there was a way to avoid pnpm install *entirely*
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:
# debug:
# runs-on: ubuntu-latest
# steps:
# - run: echo "${{ toJson(github.event) }}"
# - run: echo "${{ toJson(github.event.workflow_run) }}"
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@v6
with:
repository: ${{ needs.determinePR.outputs.repo }}
ref: ${{ needs.determinePR.outputs.branch }}
- name: TurboRepo local server
uses: felixmosh/turborepo-gh-artifacts@v4
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- uses: wyvox/action-setup-pnpm@v4
- run: pnpm turbo build:dev
- uses: actions/upload-artifact@v7
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@v8
name: deploy-prep-dist
- id: deploy
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy ./deploy-prep-dist/ --project-name=ember-primitives --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/ember-primitives/actions/runs/${{ github.run_id }})