How can I ignore a build step based on git information (e.g. branch name) without Vercel cloning the repo?

Summary

Hi :slight_smile:

To reduce the waiting time for the builds in our monorepo, we’re using an ignore build step script to not build a project if it’s not needed. However, Vercel still spends about 15 seconds for each project in the monorepo cloning and running the ignore build step script, because the codebase grew in size.

We would like to reduce those wait times by not cloning the repo on every check. Because 95% of the time, the cloning would not be necessary since we can check the commit metadata (e.g. branch name) to a environment variable to see if we actually need to build a project. So no code is required (besides the script atm).

Note
We want to reduce the time spent cloning, since that takes ~10-15s. So, depending on which project comes first, we have to wait numberOfProjectsBeforeTheCorrectOne * 15s for the intended build to start. Because every project will clone the repo first, and only afterwards execute the script.

We are using Turborepo for our monorepo atm.

Steps to Reproduce

Our current script:

/* eslint-disable no-console */
const { VERCEL_GIT_COMMIT_REF, BRANCH_PREFIX } = process.env;

console.log(`BRANCH_PREFIX: ${BRANCH_PREFIX}`);
console.log(`VERCEL_GIT_COMMIT_REF: ${VERCEL_GIT_COMMIT_REF}`);

if (
  VERCEL_GIT_COMMIT_REF == 'dev' ||
  VERCEL_GIT_COMMIT_REF == 'main' ||
  VERCEL_GIT_COMMIT_REF.startsWith(`${BRANCH_PREFIX}/`)
) {
  // Proceed with the build
  console.log('✅ - Build can proceed');
  process.exit(1);
}

// Don't build
console.log('🛑 - Build cancelled');
process.exit(0);

Hey @michaelschufi! Funny you should bring this up. We just released an update to make it easier to skip unchanged projects from monorepos. :smile:

Otherwise, Ignored Build Step or a custom workflow are your best options if more customization is needed.

3 Likes

This sounds like an interesting feature. :smiley: Thanks!

However, this will still build feature branches that update a specific internal dependency (e.g. adding a utility to the shared internal utility package), correct?
There is no way to exclude such builds until there’s a PR or until a branch is merged, correct?
What happens if we skip such a build after a feature branch has updated a dependency? Will the next commit of the same feature branch (that doesn’t alter internal packages compared to the first commit) try to build again?

However, this will still build feature branches that update a specific internal dependency (e.g. adding a utility to the shared internal utility package), correct?

If an internal dependency is used by your Vercel project, it will create a deployment, yes. If the internal dependency does not appear in the root package.json or your project’s package.json, it would not create a deployment.

Will the next commit of the same feature branch (that doesn’t alter internal packages compared to the first commit) try to build again?

The feature is based on the push event from Github. So if the push event contains 5 commits, we’ll use the contents of those 5 commits to determine whether your project changed. If those 5 commits include an internal dependency that affects your project, it will build.

4 Likes

Thank you, that sounds great. Sorry, but I still have a few more things I’d like to clarify. This feels like magic :sweat_smile:

Subsequent skips

The feature is based on the push event from Github. So if the push event contains 5 commits, we’ll use the contents of those 5 commits to determine whether your project changed. If those 5 commits include an internal dependency that affects your project, it will build.

What I don’t quite understand. What is taken into account when you determine whether your project changed. Will you compare it to the last successful build, or any build that has been run?

Say we have a monorepo with an internal package packages/utils and app packages apps/web and apps/docs that have utils in the dependencies inside package.json 3 commits:

  1. initial (sets up all packages)
  2. branching for a docs feature branch
    a. utils update
    b. code update in the docs package
  3. Feature branch gets merged

Assuming the initial commit builds the project successfully, the build trigger behaviour of the web app is the following (when pushing after each commit)

  • Commit 2a will trigger a build. The utils package is a dependency and its source code has changed.
  • Commit 2b will not trigger a build, the web app is unaffected
  • Commit 3 will trigger a build, since the merge commit includes the updates to utils and

Do I understand this correctly?

Now, what if we were to ignore commit 2a in an ignore build step (since it’s a feature branch, we only want to build it later when a PR is open). Will commit 2b trigger an update or not? Compared to the last successful build (the initial commit) the utils package is different.

Triggering a build on PR
A question that came to mind writing this.
If all the commits in the feature branch are ignored after the commit 2a - what’s the best way to trigger a build when a PR is opened so it doesn’t get skipped?

In order to check if the changes inside utils are affecting web in an unintended way. Previously, we could potentially check this inside the ignore build step script. But now, if the script is not even triggered, that’s not an option, correct?
I’m not up-to-date with how GitHub’s webhooks handle commits that are in an open PR - maybe they are handled differently?

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.

We look at files changed in the commit, and determine what packages in your monorepo are affected by those files changed. Then we match the paths of those packages to the rootDirectory of associated Vercel projects to determine which projects need to be redeployed.

Assuming you did a git push for 2a, 2b would probably not deploy web, becuase 2b is compared to 2a (the last previous deploy). This is the behavior today, but we are open to feedback on how it should work.

Curious, what’s your logic for skipping the deployment in the Ignored Build Step? I ask because I’m not sure how you can customize behavior on push, and then have something else happen when the PR is opened.

To answer your question more directly, some ways to do that right now (assuming it’s possible to end up in the situation you describe), is to

  • push up another commit (not great, but then you have some changes the can trigger a deploy)
  • on the canceled build in the vercel.com UI, use the Redeploy button to manually trigger a deployment.

You can opt out of the feature and continue using the Ignored Build Step if you want to know more granularly whether utils/ affects web. We did consider implementing file tracing to do this, but decided not to in the first iteration. Using the package dependency graph naively covers a bulk of the use cases. If your utils pacakge is fairly large and not all changes affect the upstream apps that use it, it may also be a good signal that utils should get split up!

1 Like

Thank you for all the answers! :pray:

Okay, so as long as there’s a git push of 2a, 2b will probably be skipped - regardless of the build outcome of 2a (canceled by user/build step ignore/error)?
I would also expect this behaviour (always comparing to the latest build that’s in the Deployments list, including the cancelled ones).

The idea would be to build projects not involved in the feature branch as late as possible. If, for example, 2a is reverted before opening the PR, web will never have deployed.
But if the PR opens and 2a isn’t reverted, web should be built before the merge happens.

The logic itself is so far:
Given a branch in the format projectBranchName/feature-name if the word before the / does not match an env variable, we skip the build. (see also my OP)

If there’s always a cancelled build when the build is skipped by this feature, I think that would be sufficient. Even if it requires a manual interaction.
Although, we would have to press the button for each affected project… :thinking: (That’s why we probably won’t use the previously mentioned ignore build step strategy anymore.)

Meaning, the whole utils package should get split up and not just its exported modules inside it?
I assume this would be possible only with file tracing, correct?

Looks nice!
I think we will just have to try it out. I’ll gladly give more feedback later!

yes exactly. In theory at least. I know it’s very common for large util/ packages in practice though.

:+1:

yeah, depending on the number of projects you have this isn’t great.

2 Likes