If you’re working with a monorepo and want to automate your build and testing workflow using GitHub Actions, you're in the right place.
Setting up CI/CD doesn’t have to be complex — even in a monorepo setup. With the help of Turborepo and Jest, you can create a reliable, fast, and scalable pipeline that runs on every push or pull request.
In this post, I’ll walk you through exactly how I did it.
A monorepo is basically a single repository that holds multiple projects — for example, a frontend app, backend API, shared libraries, and utilities — all under one roof.
It’s super useful when:
But yeah, it comes with some challenges — especially when you want to automate things like building and testing. That’s where Turborepo makes life easier.
Turborepo is a tool built for monorepos. It speeds things up by:
It fits perfectly into both local dev workflows and CI pipelines.
GitHub Actions lets you automate tasks like testing, building, and deploying your code every time someone pushes changes or opens a pull request.
In this setup, we’ll:
main
branchHere’s what the basic folder layout looks like in my project:
├── apps/
│ └── web/ # The main frontend app
├── packages/
│ ├── ui/ # Shared UI components
│ └── utils/ # Shared utility/helper functions
├── .github/
│ └── workflows/
│ └── ci.yml # Our GitHub Actions workflow file
├── turbo.json # Turborepo configuration
└── package.json # Root-level config
Inside your project, go to .github/workflows/
and create a file named ci.yml
.
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: 18
- name: Install dependencies
run: npm install
- name: Run Build
run: npx turbo run build
- name: Run Tests
run: npx turbo run test
Let me break that down a bit:
checkout
pulls your repo’s code.setup-node
makes sure the runner has Node.js installed.npm install
installs all dependencies from your root package.json
.turbo run build
builds only the changed packages.turbo run test
runs tests using Jest or any other tool you've configured.In each package where I need tests, I’ve added a jest.config.js
file like this:
module.exports = { preset: 'ts-jest', testEnvironment: 'node', testMatch: ['**/__tests__/**/*.test.ts'], setupFiles: ['/jest.setup.js']};
Each package also has a test script in its package.json
:
{ "scripts": { "test": "jest" }}
Thanks to Turborepo, when I run turbo run test
, it picks up the test
scripts from each relevant package and runs them only where needed.
Before pushing any code, I usually check coverage locally using:
npx jest --coverage
This gives a nice summary of what’s covered and what isn’t — so you’re not caught off guard by a failing pipeline after pushing.
And that’s pretty much it.
By combining Turborepo, Jest, and GitHub Actions, you can build a solid, efficient CI pipeline for your monorepo. It ensures every pull request and push to main
is automatically verified — no more surprises in production, and faster feedback during development.
This setup has saved me hours of manual testing and helps me move quickly with confidence. I hope it helps you too!
Thanks for reading!