One of the most common uses of GitHub Actions in Node.js projects is to automate code validation every time someone proposes a change via a pull request.
This type of validation usually involves several key steps:
- Install dependencies to make sure the project can run correctly.
- Running the linter, a tool that analyzes the style and quality of the code.
- Run the tests that automatically verify that the project's functionality continues to work.
- Build the project to check for errors before deploying or publishing.
In this article we will set up a GitHub Actions workflow that automates this whole process every time someone opens or updates a pull request. We will explain step by step what each part of the workflow does and why it is important.
π― The purpose of the workflow
We want that, every time a pull request is opened in our project, a set of tasks is executed to verify that the code complies with the project standards:
- It installs correctly
- Passes the linter tool
- Passes the tests
- Compiles without errors
𧬠General workflow structure
name: Node.js pull request verify
on:
pull_request: {}
jobs:
verify:
name: Verify
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: "22"
- name: Install dependencies
run: npm install
- name: Run lint
run: npm run lint
- name: Run tests
run: npm run test
- name: Build package
run: npm run build
βοΈ What parts compose a GitHub Actions workflow?
Before going into detail, it is useful to understand how a GitHub Actions workflow is structured. The main elements are:
Trigger (on:)
Defines when the workflow is executed. It can be when pushing, opening a pull request, publishing a release, executing manually, etc.
In our case, we use pull_request, which is triggered every time a PR is created or updated.
Job (jobs:)
Each workflow can have one or several jobs. A job is a set of tasks that are executed in the same environment.
All the steps of a job share the same filesystem and context. In our example, we have a single job called verify.
Runner (runs-on:)
It is the operating system where the job is executed. GitHub provides hosted runners, such as ubuntu-latest, windows-latest or macos-latest, but we also have the possibility to use self-hosted runners.
Steps (steps:)
These are the actions or commands that are executed within a job. They can be:
- Reusable actions such as actions/checkout or actions/setup-node.
- Specific commands defined with run, such as npm install or npm test.
πͺ Step-by-step of our workflow
Now that we understand how a workflow is structured, let's analyze each of the steps that make up our workflow. We will see what each action does, why it is necessary and what additional configurations we could apply depending on the case.
on: pull_request
This block indicates that the workflow will be triggered when a pull request is opened, updated or reopened in the repository. It is ideal for branch-based continuous integration flows.
uses: actions/checkout@v4
This step downloads the code from the repository so that it is available within the runtime environment.
Some additional options that this action allows are:
- fetch-depth: 0 clones the entire repository history (by default only the current commit).
- ref: branch-name** you can set a specific branch
- path: subdirectory clone the code in a specific path
For this case, just use it without extra configuration:
- uses: actions/checkout@v4
uses: actions/setup-node@v4
This action installs a specific version of Node.js, in our case version 22. It also allows you to configure dependency caching, authentication with registries, and more.
- uses: actions/setup-node@v4
with:
node-version: "22"
Other useful options are:
- cache: βnpmβ enables dependency caching using actions/cache underneath
- check-latest: true** forces to use the latest available version that complies with the semver.
- registry-url: to configure publishing to NPM
Working scripts
- run: npm install
- run: npm run lint
- run: npm run test
- run: npm run build
These steps execute the commands defined in the project's package.json file. Their purpose is to prepare and validate the code before it goes to production:
- npm install will install all the project's dependencies.
- npm run lint will run the linter, which helps to detect style errors or common problems in the code.
- npm run test** will run the automatic tests defined by the team.
- npm run build** will generate the final package, either an app, a library or a bundle ready to deploy.
π‘ If you are using package-lock.json, you can replace npm install with npm ci, which is faster and guarantees a more consistent and reproducible installation in CI environments.
β Final result
With this flow you guarantee that all code proposed via pull request has gone through a basic verification, without the need for manual reviews or local builds. You can integrate it with other additional steps such as:
- Static analysis (Sonar, extended ESLint...).
- Automatic comments on Pull Request
- Deploys to staging environments
- ...
π Do you want to avoid writing it by hand? There is a more convenient way
Understanding how a workflow works in Github Actions is indispensable. It gives you control and helps you customize everything to your needs.
Now... if you don't feel like struggling with the syntax, that's fine too.
At OctoLab we offer you the ability to set up this same flow visually: you choose the actions, fill in the fields and it generates the YAML for you on the fly.
β
Without indentation errors
β
With built-in validations
β
Copy, paste and go.
π§ Conclusion
This workflow is an excellent basis for quality assurance in Node.js projects. It can be easily adapted to other needs: monorepos, automatic deployments, publishing to NPM or running on multiple versions of Node.
If you prefer to use OctoLab we will be very grateful for your feedback! π€
Top comments (0)