Working in an Nx monorepo over the last year has been really great. Sharing code between projects is really convenient. The only problem is that as you add more libraries and applications and projects, the time it takes to build or test applications grows as well. The good thing is that Nx monorepos come with a variety of commands that run on only portions of the codebase. For example, if your change only affects one library in your project, you can test just that library instead of the entire project. This is especially useful on your CI/CD server when you create pull requests. Tests can be run on the pull request branch, making sure that any new code is working and that it didn’t break any previous portions of the app. In this post, we’ll go over how to create a GitHub Action workflow to run the affected tests when a pull request is created.
In my situation, I have an Nx monorepo with two applications in it. There are a lot of libraries included as well. Some of the libraries are shared, and some are specific to the application. What I needed was a solution for running the
nx affected:test command when a pull request was created. This would run the tests for just those projects affected by the code changes. If the tests pass, the PR can safely be merged.
I first started to implement this using Google Cloud, as that is the product we use to build and deploy our applications at my full time job. I was never able to get it to work, though, because for
nx to work it needs the git history for the repo. I tried many solutions, but could never get the git history into the cloud build instance. My only solution was to run all the tests on each PR. This worked for a while, but as the repository has grown more tests and libraries have been added. Last week, the tests started timing out and wouldn’t finish. It no longer became a viable solution. Because of that, I came to GitHub Actions to try and solve the issue.
If you’ve never tried GitHub Actions before, you’ll find the process straightforward and convenient. Before creating your action, switch to a new branch in your repo. Next, create a
.github folder in the root of your repository, with a nested
mkdir .github mkdir .github/workflows
yaml files that we place inside the
.github/workflows folder will be run as GitHub Actions. In this case, the next step is creating a new yaml file, which I’ll call
nx-affected.yml. This file will define the steps for the action. I won’t go into all the details of each step; that’s more suited for another article. I will provide insight on the more important steps to accomplish our goals here, though. Here’s the full contents of the yaml file, after which we’ll break it down piece by piece.
name: Nx Affected CI on: pull_request: branches: [main] jobs: build: runs-on: ubuntu-latest strategy: matrix: node-version: [12.x] steps: - uses: actions/checkout@v2 with: fetch-depth: 0 - name: Use Node.js $ uses: actions/setup-node@v1 with: node-version: $ - run: git fetch origin main - run: npm install - name: Run Affected Tests shell: bash run: npm run affected:test -- --base=remotes/origin/main
Let’s break this down piece by piece and explain what’s going on.
name: Nx Affected CI on: pull_request: branches: [main]
At the top of the workflow yaml file we give our workflow a name. After that we determine when the workflow will be run. In this case, the workflow will run when pull requests are created for merging into the main branch. Other branches can also be targeted by being added to the array.
jobs: build: runs-on: ubuntu-latest strategy: matrix: node-version: [12.x]
The next part of the yaml file is the meat of the workflow. The first part I’ll point out is that we determine what operating system to use for the action with the
runs-on attribute. In this case,
ubuntu-latest. Next, the
matrix attributes allow us to determine multiple versions of node to use for running the tests. This part is optional. You can choose just one version if you’d like, or select multiples. The action will run for each version of node that you provide in the array.
steps: - uses: actions/checkout@v2 with: fetch-depth: 0 - name: Use Node.js $ uses: actions/setup-node@v1 with: node-version: $ - run: git fetch origin main - run: npm install
steps section of our workflow file is where we will accomplish the goal of the workflow. In other words we will prepare to run the tests and run them. These first three steps checkout the git repository into the context of the workflow.
fetch-depth: 0 ensures that we get the full git history, which is necessary for running the
nx affected commands. The second step determines the node version to use (using our
matrix from the
strategy section above). The third step fetches the latest information from the main branch. We need that information because Nx runs compares the current branch to the
main branch to determine what has changed. The last step I’ve included here is to run
npm install. This ensures that all necessary packages are ready for the application to be built and tested.
- name: Run Affected Tests shell: bash run: npm run affected:test -- --base=remotes/origin/main
This last step is where we actually run the tests. We gave the step a
name, and specified the
shell to be used while running the tests. When this step runs, we run an npm command declared in the
package.json. This will run the Nx command that runs tests against projects that have changed between the branch of the pull request and the
main branch. The tests will be run, and if they are successful the workflow will end in a success status. If any tests fail, the workflow ends with a failure status. Both of these statuses will show on the pull request page in GitHub.
To test the action, push your changes to the repository and create a pull request using the branch that you did this work on, with the intent to merge it into
main. When you create the pull request, the action you just created will start running. You can see it in the repository. You can get there by clicking the “Actions” tab on the repository’s main page:
Once you’ve clicked on that tab, you’ll see a table of the actions that you’ve run in this repository. If the workflow is currently running, you’ll see a yellow circle. If it was unsuccessful, it’s marked with a red x. If it was successful, you’ll see a green check mark.
Clicking on one of the rows will show you the details of that action. It will show each step that was defined in your action and the logs that are pertinent to that step. Each time you create a pull request, or push updates to an existing PR, the tests will run.
You can use this same step, or duplicate it, to run other Nx affected commands, such as
e2e. They can all be run in a single workflow, as separate steps, or you can have one workflow for each of them. In addition, you could use an action to build your application in a Docker image and push the image to GitHub’s package registry (or another registry like Docker Hub). Here are a couple previous articles of mine that may help you out. In addition, here’s a reference to a post on Dev.to that I used to help me get started with testing affected projects in my Nx monorepo.