When trying to combine the concepts of an Affected Nx projects and building and deploying them in Azure Pipelines, there is no plugin or anything readily available to do so. Since it wasn’t trivial to find and compose all the bits and pieces together, I decided to write this down. Maybe it’ll help you, or maybe you can help me improve it.
One Step Further
By default, many solutions use the diff between
HEAD~1 to calculate
the affected projects. Such as Nrwl’s own Example of setting up distributed
Azure build for Nx workspace.
Although this may work well, I think this isn’t always optimal. Mostly because the latest run(s) may have failed, which Nx isn’t aware of. This may require to manually re-run a pipeline, or it may take an unknown amount of time before the container will re-build.
However, Azure has an API to set and list pipeline run tags. We can use the Azure CLI to add a tag for each pipeline run having a successful Nx project build.
The main steps in this guide include:
- Find the latest successful build and the corresponding SHA-1.
- Use this SHA-1 as the
- Store the affected Nx project names in output variables.
- Use these output variables to conditionally execute the corresponding jobs or stages to build and deploy Nx projects.
- After a successful build, tag the current pipeline run (for step #1 in the next run).
Find The Latest Successful Build
When the list of pipeline runs is filtered by the Nx project’s tag and sorted by
time, we need only the latest result and we can further simplify the output by
returning only the SHA-1 (
sourceVersion) in the most concise TSV format:
This will return the SHA-1 associated to the latest pipeline run tagged with
my-app. This is the run we are looking for, as we have tagged it only after a
successful build. Now,
nx affected can determine whether this Nx project is
currently affected or not compared with the latest SHA-1 a successful build for
this Nx project was made from.
Later we will see how to set this tag for a successful build.
Write It Down For Later
To set an output variable for use in a later stage in Azure pipelines, we need
task.setvariable logging command (Azure docs: Set variables in
scripts). This writes the value
AFFECTED to the output variable
Putting It Together
With the above ingredients, we can write a script to write the output variables. Initially I wrote a Bash script is-affected.sh as that made sense at the time. Here’s the gist:
As I think Bash scripts are not very robust and not easy to maintain, I ported this to a Node.js script is-affected.js with JSDoc/TypeScript annotations. The idea stays the same, and with both scripts the output looks like this:
When system diagnostics are enabled, also the other
echo commands that
actually set the variables are printed.
To see this script in perspective, here’s an example “Prepare” stage:
condition for the job in another (build) stage is based on the variable
that was written with the Bash script in an earlier stage. The pattern to read
Also see Azure docs to Set a variable for future stages.
When this variable has a value of
TAG_NOT_FOUND the condition
will evaluate to
true and the job to build the Nx project will run. For
brevity, here is only the relevant part:
We can move the build job(s) to a template and reuse it with
a parameter. Here’s an example how to do that:
And then in the
Note that stages referred to in
stageDependencies must be part of the
dependsOn option (
Prepare in this example). Otherwise, the value will
silently resolve to
Null without warning.
Tag Successful Builds
This task should follow the build step(s) from the job above. We can tag
successful build runs with the name of the Nx project (
Now this command from the Bash script should find the latest tag for this Nx project in the next pipeline run:
Again, you may need to set the
project first. Here’s a
This writes the
DEPLOY_MY_APP variable. In a later (deployment) stage, the
same idea can be applied to read this variable and conditionally deploy the
build to any environment. An example chunk of the “production” stage:
And then in
Again, stages referred to in
stageDependencies must be part of the
Build in this case).
I’ll update this article as I find improvements. Hopefully this guide has been of some help or inspiration.