🛠️

Optimize Vercel preview builds using Ignore Build Step in a monorepo

Created
May 31, 2022 10:58 PM
Tags
monoreposci/cd

Ok! So, you’ve got a monorepo with more than one app in it, and some shared packages. You want to only trigger Vercel deployments when necessary rather than building all of your apps.

Checking the diff

The official docs give a few examples depending on your use case. Here’s a combined sample that uses their approach.

git diff HEAD^ HEAD --quiet . ../../packages/components ../../packages/hooks
This would check your app directory, and the components and hooks packages for changes

There’s a big catch to this approach!

⁉️
The suggested command of git diff HEAD^ HEAD only compares the commit being checked for build with the previous commit. If you open a PR that has multiple commits, the build will be ignored based only on the changes in the latest commit.

So how do you check for changes based on all commits in a PR? It should be easy? Not so fast! Vercel does a shallow clone using git clone --depth=10 (...) , which only fetches the 10 levels of git history on the current branch. You need more information than is available in the cloned repo to figure out whether a PR introduced changes in the folders you want to check.

The best approach that I found was from probablyup on GitHub — use the GitHub API to set a remote for the shallow clone, and then diff your current commit $VERCEL_GIT_COMMIT_REF against origin/main.

Always build production branches

Depending on your setup, you may want to always build production or staging branches. I added something to my script to account for this.

Putting it all together in a bash script

The vercel.sh script below brings everything together, and is run by putting bash vercel.sh in the ignored build step setting field in Vercel.

if [[ "$VERCEL_GIT_COMMIT_REF" == "prod-all" ]] || [[ "$VERCEL_GIT_COMMIT_REF" == "main" ]]; then
    # Always build production/staging branches
    echo "✅ - Production/staging branch - proceed with build"
    exit 1
elif
    # Vercel only performs a depth=10 shallow repository clone, which makes performing branch-level diffs against your main branch impossible without manually fetching more data from git
    #
    # The following command checks if the origin remote is already set up and fetches it if not, then it performs a diff of the current branch being built against `main`
    git config remote.origin.url >&- || git remote add -f origin https://[yourAccessToken]@github.com/[yourRepositoryURL]
    ! git diff --name-only origin/main...origin/$VERCEL_GIT_COMMIT_REF --quiet . ../../packages/components ../../packages/hooks
    # Only diff the app and dependencies, not the whole repo
then
    echo "✅ - Changes to app or dependencies - proceed with build"
    exit 1
else
    echo "🚫 - No changes to app or dependencies & not a production build - ignoring build step"
    exit 0
fi

Feel free to reach out to me on Twitter @jaronheard if you’ve got thoughts, ideas, or questions on this blog post!