DEV Community

Cover image for Release by tagging on Vercel
Damian Świstowski
Damian Świstowski

Posted on

Release by tagging on Vercel

Where is the problem?

I like to have my project configured to have a staging build on which I run all my acceptance tests, close to production configuration, just before the production release.

To achieve it, my staging build represents the current HEAD of the project's main branch. After running, the acceptance tests branch is tagged and released to production.

The default configuration for the project on vercel.com releases the main branch to production with every commit. It is easy to configure the second branch for staging build, by adding staging branch and domain configuration for it:

Image description

In this configuration, the head of the main branch will be released to https://vercel-release-by-tag.vercel.app/, and the changes of the staging branch will be released to https://staging-vercel-release-by-tag.vercel.app/

In order to release to production, one needs to merge the staging branch to main.

This is an acceptable workflow, but you are not using the GitHub release page to keep track of changes between different production releases. Additionally, developers need to manage two branches, which is not optimal.

First try

My first approach to solving problem with tagged releases was to use can you deploy based on tags releases on vercel guide. The proposed solution - is to create an unused branch and deploy hook to release this tag, which did not work for me. The deploy hooks are linked to the branch:

Image description

And because of that link, they can release only code which is on a specific branch. Configuring a webhook to release the staging branch will release that branch to the staging environment, I didn't find a way to change that behaviour.

Second try

My second try to solve it was to move release to GitHub actions with how can I use GitHub Actions with Vercel? guide.

As a result i created .github/workflows/deploy-prod.yml file with content:

name: Deploy to production
env:
  VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
  VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
on:
  push:
    tags:
      - 'v*'

jobs:
  Deploy-Production:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Install Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 16

      - name: install and release
        run: | 
          npm install --global vercel@latest
          vercel pull --yes --environment=production --token=${{ secrets.VERCEL_TOKEN }}
          vercel build --prod --token=${{ secrets.VERCEL_TOKEN }}
          vercel deploy --prebuilt --prod --token=${{ secrets.VERCEL_TOKEN }}
Enter fullscreen mode Exit fullscreen mode

This approach was not acceptable:

  • Build was done once in GitHub Action worker and build artefact need to be uploaded to Vercel, and this process was slower than building directly there.
  • Prisma client was not generated correctly without additional configuration, because the build environment was different than the runtime. The pros of this approach were it doesn't require creating an artificial branch.

Final approach

For the final solution, I decided to configure Vercel to build directly from the branch, but add workflow to make sure all the changes on the production branch are done automatically after applying the tag.

To achieve it I created production branch in my git repo and created for both main and production protection rules:
Image description.
Protection makes sure the production branch will be not deleted by accident after merging it to a different branch.

After that, I configured Vercel to release production code from the production branch (settings -> git)

Image description

And to release main branch to staging domain (settings->domain):

Image description

And the final piece is to update production branch on tag creation. The easies way is to create GitHub Action:

name: Deploy to production
on:
  push:
    tags:
      - 'v*'

jobs:
  Deploy-Production:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Release to production
        run: |
          git push origin HEAD:production -f
Enter fullscreen mode Exit fullscreen mode

git push origin HEAD:production -f command will force an update current production branch with the currently tagged commit (because the action is executed only on tags starting with v.

Created process:

workflow

Closing thoughts

This solution assumes workflow when the team works on the main branch and the production is updated only by action after tagging. It's possible to lock the ability to update the production branch by accident by adding a pull request requirement to the branch protection rule, creating a bot account with bypass branch protections right for this branch and using the token of that account in GitHub action to authorise git access.

The second way of defence against accidental release of production is to configure ignored build step.

Script similar to:

#!/bin/bash

echo "VERCEL_GIT_COMMIT_REF: $VERCEL_GIT_COMMIT_REF"

if [[ "$VERCEL_GIT_COMMIT_REF" == "main" ]]; then
  echo "✅ - Staging build can proceed"
  exit 1;
elif [[ "$VERCEL_GIT_COMMIT_REF" == "production" && $(git name-rev --name-only --tags HEAD) != "undefined"  ]]; then
  echo "✅ - Production build can proceed"
  exit 1;
else
  # Don't build
  echo "🛑 - Build cancelled"
  exit 0;
fi
Enter fullscreen mode Exit fullscreen mode

Top comments (0)