How to deploy and remove Vercel Preview Deployment from GitHub Actions
Tags: nextjs, vercel, preview, github
Published: 2023-09-09 10:24:28

---
title: How to deploy and remove Vercel Preview Deployment from GitHub Actions
published: true
description: How to automatically remove all preview deployments created by a pull request from Vercel.
tags: nextjs, vercel, preview, github
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lippr68p0j63zp8nfu8y.png
# Use a ratio of 100:42 for best results.
# published_at: 2023-09-09 12:25 +0000
---

Recently I have started my journey learning about NextJS and the Vercel platform. As a experienced React Developer, I've developed several frontend applications in past 6 years with this library and it's really nice to see the whole React ecosystem building/following on what a company's says and interacts with the community. The future shines for React and any similarity to PHP is not a coincidence.

So let's get started. As I am building my startup, we decided to create the frontend + backend with NextJS. It's the best thing for our product, having a monolith with the backend and frontend included... and some other benefits like SSR and caching. Since we want to have fast iteration in the product, having a service like [Vercel](https://vercel.com) is helping us to build our product without caring about infrastructure (AWS, GCP). The prices are very modest, to be honest I think it's worthy to pay $20 for a full stack infrastructure with a included Postgres instance. 

Instead of using their automatic build after pushing a commit to a branch, I've decided to deactivate this feature and trigger the builds from a GitHub Actions.

## Step 1 - Gather the necessary ids
To interact with the **Vercel API** you need the `projectId` and `teamId` values. To obtain those values:

* Go to the Vercel dashboard and select the team


![Dashboard image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ofl50td3m474o3vd9ikq.png)

* Click on settings

![Settings image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3egqxv1i9b6g9v8dkpvm.png)

* Copy your team id

![Team Id image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/alb9jm63be1mu6d1702v.png)

* Select the project that you're working on by clicking in Overview

![Overview image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z5owhuyuzo2to2naiops.png)

* Click in settings

![Settings image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pm31gwpql6ryd04djs7q.png)

* Copy your project id

![Project id image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3cix8uxpb8aeoaw1m89g.png)

* Create an admin token
Create the admin token by visiting [Tokens](https://vercel.com/account/tokens) page. Don't forget to select **Full Scope** options. This is important because this token will be responsible to delete the deployments.

![Admin token](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p066rfzzt94ne5u73qjr.png)

Summarizing, you'll need 3 tokens from vercel:

| Token | Description | Variable name |
| ----- | ----------- | ------------- |
| VERCEL_ORG_ID | It's the team id | VERCEL_ORG_ID |
| VERCEL_PROJECT_ID | It's the project id | VERCEL_PROJECT_ID |
| VERCEL_TOKEN | It's the API key token | VERCEL_TOKEN  | 


## Step 1 - Create the environment variables in GitHub Actions

Visit the repository settings and create the environment variables there.

![GitHub Actions secrets](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a5f748dsn1oxx29jh8b1.png)

## Step 2 - Setup the `deploy_preview.yml` workflow

1. Go into your project, create a new workflow named `deploy_preview.yml` in the `.github/workflows` folder.
2. Paste the following workflow content:

```yml
name: Vercel Preview Deployment

env:
  VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
  VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
  VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

permissions:
  contents: read
  statuses: write
  pull-requests: write

on:
  pull_request:
    types:
      - opened
      - synchronize

jobs:
  deploy-preview:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - name: Extract branch name
        shell: bash
        run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
        id: extract_branch

      - name: Hash branch name
        uses: pplanel/hash-calculator-action@v1.3.1
        id: hash_branch
        with:
          input: ${{ steps.extract_branch.outputs.branch }}
          method: MD5

      - name: Install Vercel CLI
        run: npm install --global vercel@latest

      - name: Cache dependencies
        uses: actions/cache@v2
        id: cache-npm
        with:
          path: ~/.npm
          key: npm-${{ hashFiles('package-lock.json') }}
          restore-keys: npm-

      - name: Install dependencies
        if: ${{ steps.cache-npm.outputs.cache-hit != 'true' }}
        run: npm ci --ignore-scripts

      - name: Pull Vercel Environment Information
        run: vercel pull --yes --environment=preview --token=$VERCEL_TOKEN

      - name: Deploy Project Artifacts to Vercel
        id: vercel
        env:
          META_TAG: ${{ steps.hash_branch.outputs.digest }}-${{ github.run_number }}-${{ github.run_attempt}}
        run: |
          vercel --version
          vercel pull --yes --environment=preview --token=$VERCEL_TOKEN
          vercel build --token=$VERCEL_TOKEN
          vercel deploy --prebuilt --archive=tgz --token=$VERCEL_TOKEN --meta base_hash=${{ env.META_TAG }}

          vercel ls --token=$VERCEL_TOKEN --meta base_hash=${{ env.META_TAG }} &> vercel-output
          url=$(cat vercel-output | grep http | awk '{print $2}')
          echo "New preview URL: $url"
          echo "META_TAG=$META_TAG"
          echo "VERCEL_URL=$url" >> "$GITHUB_OUTPUT"

      - uses: mshick/add-pr-comment@v2
        with:
          message: |
            Your build has completed!

            [Preview deployment](${{ steps.vercel.outputs.VERCEL_URL }})

```

This workflow is a basic workflow which will be executed whenever a pull request is created or synchronized (receives a new commit). The steps executed are:

1. Checkout the PR content into the working directory.
2. Get the branch name.
3. Create a unique hash for the branch name. This is important because Vercel allow meta tags into builds, this will help us to identify later when we have to remove all preview deployments from a certain branch.
4. Install Vercel CLI.
5. Check the cache for npm modules (faster builds)
6. Install dependencies if needed
7. Pull the environment variables defined from Vercel
8. Build and deploy artifacts. Pay attention to the `META_TAG` variable. This is important because it will be used a metadata to identify every preview deployment.
9. Comment the preview URL.

Within this steps, you'll create automatic preview links on every push to your branch.

## Step 3 - Setup the `remove_deploy_preview.yml` workflow and script

This workflow will be mandatory in the case you want to delete all the previews builds that the project has generated.

### Bash script to remove the builds from Vercel
 
In your project root folder, create the `scripts/delete-deployment-preview.sh` file.

```shell
#!/bin/bash
# Set the pipefail option.
set -o pipefail

# Get the Vercel API endpoints.
GET_DEPLOYMENTS_ENDPOINT="https://api.vercel.com/v6/deployments"
DELETE_DEPLOYMENTS_ENDPOINT="https://api.vercel.com/v13/deployments"

# Create a list of deployments.
deployments=$(curl -s -X GET "$GET_DEPLOYMENTS_ENDPOINT/?projectId=$VERCEL_PROJECT_ID&teamId=$VERCEL_ORG_ID" -H "Authorization: Bearer $VERCEL_TOKEN ")

# Filter the deployments list by meta.base_hash === meta tag.
filtered_deployments=$(echo $deployments | jq --arg META_TAG "$META_TAG" '[.deployments[] | select(.meta.base_hash | type == "string" and contains($META_TAG)) | .uid] | join(",")')
filtered_deployments="${filtered_deployments//\"/}" # Remove double quotes

# Clears the values from filtered_deployments
IFS=',' read -ra values <<<"$filtered_deployments"

echo "META_TAG ${META_TAG}"
echo "Filtered deployments ${filtered_deployments}"

# Iterate over the filtered deployments list.
for uid in "${values[@]}"; do
    echo "Deleting ${uid}"

    delete_url=${DELETE_DEPLOYMENTS_ENDPOINT}/${uid}?teamId=${VERCEL_ORG_ID}
    echo $delete_url

    # Make DELETE a request to the /v13/deployments/{id} endpoint.
    curl -X DELETE $delete_url -H "Authorization: Bearer $VERCEL_TOKEN"

    echo "Deleted!"
done

```

Don't forget to run `chmod`:
`chmod a+x ./scripts/delete-deployment-preview.sh`

### Deployment workflow

Create the `remove_deploy_preview.yml` into the `.github/workflows` folder and paste the following content:

```yml

name: Remove deploy preview

permissions:
  contents: read
  statuses: write
  pull-requests: write

env:
  VERCEL_TOKEN: ${{ secrets.VERCEL_TOKEN }}
  VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
  VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}

on:
  pull_request:
    types:
      - closed

jobs:
  delete-deployments:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - name: Extract branch name
        shell: bash
        run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
        id: extract_branch

      - name: Hash branch name
        uses: pplanel/hash-calculator-action@v1.3.1
        id: hash_branch
        with:
          input: ${{ steps.extract_branch.outputs.branch }}
          method: MD5

      - name: Call the delete-deployment-preview.sh script
        env:
          META_TAG: ${{ steps.hash_branch.outputs.digest }}
        run: |
          bash ./scripts/delete-deployment-preview.sh

```

This workflow will run when a existent PR is closed or merged. So be aware that this job will possibly run without deleting any previews.

The workflow steps are the following:

1. Checkout the PR contents into the working space.
2. Get pull request branch name.
3. Create a hash for it.
4. Execute the `bash` script that we've created earlier with the `META_TAG` environment variable.

The script will make a GET request to the `https://api.vercel.com/v6/deployments` endpoint and will fetch all the preview deployments that you have. Then, we use `jq` to create a list of `uid` that matches with the `meta.base_hash` that we passed earlier when we created a new preview.

If there's any available deployment, we delete it. If none, it's all good, no need to worry (unless you don't have any at all).


![Delete deployment preview](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zb5fgmr5dudotwl8q740.png)


That's all folks, I hope you can sort this out. Leave a comment if you liked and follow me (all about my complains) on Twitter.

[dev_reis on Twitter](https://twitter.com/dev_reis)
[dev_reis on Instagram](https://instagram.com/dev_reis)