Cookies
This website uses cookies to improve the user experience, more information on the legal information path.
Notes
0
years
0
mons
0
days
0
hour
0
mins
0
secs
00
This is the background image
3

Continuous Integration with Github Actions workflow

Every project is different and so are its priorities, but they all have one thing in common: they need to be tested and deployed. You can do that by hand, but it gets repetitive and tedious fast — which is exactly what continuous integration and continuous deployment exist to remove.

Here I use GitHub Actions to automate deploying this project to Vercel, plus the checks that run before it: testing, linting, type-checking and documentation generation.

Design

The part that takes the most thought is the pipeline itself — which tasks run, and in what order. I sketched the flow in Excalidraw and iterated until one shape stuck. Then it is mechanical: create a .github/workflows/ folder and drop your .yml files in it. I keep two, pre-deploy.yml and post-deploy.yml, and instead of explaining YAML syntax I will explain what each job does.

pre-deploy runs four jobs in parallel — cancel-redundant-runs, linting, testing and docs — and only if they all pass does it version and deploy to pre-production. I then test the feature or bugfix manually against that preview. If it holds up, deploying to production is just a pull request from dev to master. But the pipeline does not end at the merge: once it is live, post-deploy runs a Lighthouse pass and publishes a page-by-page performance report. Splitting it this way keeps the fast gates blocking (nothing ships broken) and the slow, live-site checks out of the critical path.

Here it is graphically:

Coincidencia

This is a snippet of what the pre-deploy workflow looks like (Node 22, npm — update the versions to match your project, this is the kind of thing that goes stale):

pre-deploy.yml

name: Workflow Pre-Deploy
on:
push:
branches: [dev]
jobs:
cancel_redundancy:
name: Cancel Redundant Workflow Runs
runs-on: ubuntu-latest
steps:
- name: Cancel previous redundant workflow runs
uses: styfle/cancel-workflow-action@0.12.1
with:
access_token: ${{ secrets.TOKEN_GITHUB }}
code_checking:
name: Linting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- name: Install dependencies
env:
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
echo "//npm.pkg.github.com/:_authToken=${NPM_TOKEN}" >> ~/.npmrc
echo "@xabierlameiro:registry=https://npm.pkg.github.com/" >> ~/.npmrc
npm install --legacy-peer-deps
- name: Linter
run: npm run lint

Full workflow in my github

Three things worth doing from day one

  • Cache the install. actions/setup-node with cache: npm restores node_modules-worth of downloads between runs and cuts minutes off every job. Point it at your real lockfile.
  • Cancel redundant runs. Pushing three times in a row should not run three full pipelines. The cancel step above (or the built-in concurrency: key) kills superseded runs.
  • Keep secrets in secrets. The registry token comes from ${{ secrets.NPM_TOKEN }}, never from the file. If a token ever lands in a committed .npmrc, rotate it — the runner reads it from the environment, not the repo.

Results

Each report has its own repository on GitHub, and that is where the output of every run is published:

Conclusions

Every time I add a component in React or a page in Next.js, a battery of tests checks that I have not broken the work already there — which only works because you are the one who has to write the tests for the delicate, critical parts. The pipeline does not invent coverage; it enforces the coverage you decided to have.

You can find all this code in my github repository, and if you liked it you can help me with a ⭐️. Thanks!