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:
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:
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 }}
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:
.
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)
And to release main
branch to staging
domain (settings->domain):
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
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:
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
Top comments (0)