CI CD for Monorepo Setting Up GitHub Actions with Turborepo and Jest

Step-by-step guide to setting up CI/CD in a JavaScript monorepo using GitHub Actions, Turborepo, and Jest for efficient builds and automated testing.

CI/CD for Monorepo: Setting Up GitHub Actions with Turborepo and Jest

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.

What Is a Monorepo and Why Use One?

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:

  • You want to reuse code across projects
  • You like keeping everything in sync
  • You’re building a product that spans multiple apps or services

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.

Why Turborepo?

Turborepo is a tool built for monorepos. It speeds things up by:

  • Caching builds and test results, so repeated runs don’t waste time
  • Running tasks only for packages that have changed
  • Handling dependencies between packages automatically

It fits perfectly into both local dev workflows and CI pipelines.

Using GitHub Actions for CI/CD

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:

  • Trigger a workflow on pushes and PRs to the main branch
  • Install dependencies
  • Build our apps
  • Run tests using Jest

Monorepo Structure

Here’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

Setting Up the GitHub Actions Workflow

Step 1: Create the workflow file

Inside your project, go to .github/workflows/ and create a file named ci.yml.

Step 2: Add the following configuration

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.

Setting Up Jest in the Monorepo

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.

Running Tests Locally with Coverage

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.

Wrapping Up

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!

Akash Srivastava
Software Engineer @ Seawoods Ventures Inc.
© 2019-2024 dev-akash.in