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.
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.
Thank you, that sounds great. Sorry, but I still have a few more things I’d like to clarify. This feels like magic
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:
initial (sets up all packages)
branching for a docs feature branch
a. utils update
b. code update in the docs package
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?
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!
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… (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!