rossjrw / pr-preview-action

GitHub Action that deploys a pull request preview to GitHub Pages, similar to Vercel and Netlify, and cleans up after itself.
https://github.com/marketplace/actions/deploy-pr-preview
MIT License
255 stars 39 forks source link
action actions deployment gh-pages github-actions github-pages pr-preview preview pull-requests workflow

Deploy PR Preview action

GitHub Action that deploys previews of pull requests to GitHub Pages. Works on any repository with a GitHub Pages site.

Features:

Preview URLs look like this: https://[owner].github.io/[repo]/pr-preview/pr-[number]/

Sample comment left by the action

Pictured: https://github.com/rossjrw/pr-preview-action/pull/1

This Action does not currently support deploying previews for PRs from forks, but will do so in the upcoming v2.

Usage

A GitHub Actions workflow is required to use this Action.

All the workflow needs to do first is checkout the repository and build the Pages site.

First, ensure that your repository is configured to have its GitHub Pages site deployed from a branch, by setting the source for the deployment under Settings > Pages of your repository to Deploy from branch:

GitHub Pages settings

Pictured: Repository Pages settings at /settings/page

The gh-pages branch is used for GitHub Pages deployments by convention, and will be used in examples here as well.

If your GitHub pages site is deployed from the gh-pages branch, built with e.g. an npm script to the ./build/ dir, and you're happy with the default settings, usage is very simple:

# .github/workflows/preview.yml
name: Deploy PR previews

on:
  pull_request:
    types:
      - opened
      - reopened
      - synchronize
      - closed

concurrency: preview-${{ github.ref }}

jobs:
  deploy-preview:
    runs-on: ubuntu-20.04
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Install and Build
        if: github.event.action != 'closed' # You might want to skip the build if the PR has been closed
        run: |
          npm install
          npm run build

      - name: Deploy preview
        uses: rossjrw/pr-preview-action@v1
        with:
          source-dir: ./build/

Important things to be aware of

Run only when files are changed

Consider limiting this workflow to run only when relevant files are edited to avoid deploying previews unnecessarily.

Run on all appropriate pull request events

Be sure to pick the right event types for the pull_request event. It only comes with opened, reopened, and synchronize by default — but this Action assumes by default that the preview should be removed during the closed event, which it only sees if you explicitly add it to the workflow.

Grant Actions permission to read and write to the repository

This must be changed in the repository settings by selecting "Read and write permissions" at Settings > Actions > General > Workflow permissions. Otherwise, the Action won't be able to make any changes to your deployment branch.

Set a concurrency group

I highly recommend setting a concurrency group scoped to each PR using github.ref as above, which should prevent the preview and comment from desynchronising if you are e.g. committing very frequently.

Ensure your main deployment is compatible

If you are using GitHub Actions to deploy your GitHub Pages sites (typically on push to the main branch), there are some actions you should take to avoid the PR preview overwriting the main deployment, or vice-versa.

  1. Prevent your main deployment from deleting previews

    If your root directory on the GitHub Pages deployment branch (or docs/ on the main branch) is generated automatically (e.g. on pushes to the main branch, with a tool such as Webpack), you will need to configure it not to remove the umbrella directory (pr-preview/ by default, see configuration below).

    For example, if you are using JamesIves/github-pages-deploy-action to deploy your build, you can implement this using its clean-exclude parameter:

    # .github/workflows/build-deploy-pages-site.yml
    steps:
     ...
     - uses: JamesIves/github-pages-deploy-action@v4
       ...
       with:
         clean-exclude: pr-preview/
         ...

    If you don't do this, your main deployment may delete all of your currently-existing PR previews.

  2. Don't force-push your main deployment

    Force-pushing your main deployment will cause it to overwrite any and all files in the deployment location. This will destroy any ongoing preview deployments. Instead, consider adjusting your deployment workflow to rebase or merge your main deployment onto the deployment branch such that it respects other ongoing deployments.

    For example, if you are using JamesIves/github-pages-deploy-action to deploy your build, be aware that at the time of writing (v4.3.0) it force-pushes new deployments by default. You can disable this by setting its force parameter to false, which will prompt it to rebase new deployments instead of force-pushing them:

    # .github/workflows/build-deploy-pages-site.yml
    steps:
     ...
     - uses: JamesIves/github-pages-deploy-action@v4
       ...
       with:
         force: false
         ...

    This feature was introduced in v4.3.0 of the above Action.

Configuration

The following configuration settings are provided, which can be passed to the with parameter.

Outputs

Examples

Full example

Full example with all default values added:

# .github/workflows/preview.yml
name: Deploy PR previews
concurrency: preview-${{ github.ref }}
on:
  pull_request:
    types:
      - opened
      - reopened
      - synchronize
      - closed
jobs:
  deploy-preview:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: npm i && npm run build
        if: github.event.action != 'closed'
      - uses: rossjrw/pr-preview-action@v1
        with:
          source-dir: .
          preview-branch: gh-pages
          umbrella-dir: pr-preview
          action: auto

...and an accompanying main deployment workflow:

# .github/workflows/deploy.yml
name: Deploy PR previews
on:
  push:
    branches:
      - main
jobs:
  deploy-preview:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: npm i && npm run build
      - uses: JamesIves/github-pages-deploy-action@v4
        with:
          folder: .
          branch: gh-pages
          clean-exclude: pr-preview
          force: false

Deployment from docs/

If your Pages site is built to build/ and deployed from the docs/ directory on the main branch:

# .github/workflows/preview.yml
steps:
  ...
  - uses: rossjrw/pr-preview-action@v1
    with:
      source-dir: build
      preview-branch: main
      umbrella-dir: docs/pr-preview

You should definitely limit this workflow to run only on changes to directories other than docs/, otherwise this workflow will call itself recursively.

Only remove previews for unmerged PRs

Information from the context and conditionals can be used to make more complex decisions about what to do with previews; for example, removing only those associated with unmerged PRs when they are closed:

# .github/workflows/preview.yml
steps:
  ...
  - uses: rossjrw/pr-preview-action@v1
    if: contains(['opened', 'reopened', 'synchronize'], github.event.action)
    with:
      source-dir: ./build/
      action: deploy
  - uses: rossjrw/pr-preview-action@v1
    if: github.event.action == "closed" && !github.event.pull_request.merged
    with:
      source-dir: ./build/
      action: remove

Permanent previews

If you want to keep PR previews around forever, even after the associated PR has been closed, you don't want the cleanup behaviour of auto — call deploy and never call remove:

# .github/workflows/everlasting-preview.yml
name: Deploy everlasting PR preview
concurrency: preview-${{ github.ref }}
on:
  pull_request:
    types:
      - opened
      - synchronize
jobs:
  deploy-preview:
    runs-on: ubuntu-20.04
    steps:
      - uses: actions/checkout@v3
      - run: npm i && npm run build
      - uses: rossjrw/pr-preview-action@v1
        with:
          source-dir: ./build/
          action: deploy

Acknowledgements

Big thanks to the following: