When working within an organization, development teams have to make many infrastructural decisions, and those decisions can lead to different software development practices. Whether development teams are maintaining a monolith, keep several applications, or have adopted microservices, the mono-repository (Monorepo) offers many advantages.

This post will talk about the advantages and disadvantages of mono-repositories and look at an approach to selectively build parts of our mono-repository using a GitHub Action.

What Is A Monorepo?

Development teams may have several applications they develop, test, and deploy throughout an organization. The mono-repository allows teams to keep an entire source code within a single repository.

Advantages To A Monorepo

There are a few advantages to adopting a monorepo approach for our development organization.

Single source for codebase

A team having a single location to work within make it more straightforward to understand the big picture.

Code Reuse

When we place our codebase into one repository, it is easier to see patterns and reuse code across multiple projects. It also makes it easier to collaborate across teams responsible for different solutions within the repository.

Large Refactoring Opportunities

Monorepos give us a better view of the dependencies between projects. This additional view allows us to make more significant refactorings with more confidence that we aren’t introducing breaking changes.

Versioning At An Organizational Level

A monorepo allows us to version and deploy our organizational solutions through one build pipeline. All parts of our solution are versioned and deployed in sync with each other. We can reduce the chance of a mismatch in service boundary contracts.

Disadvantages To A Monorepo

Like most approaches in software development, there are always trade-offs to choosing any method.

Larger Codebases

Developers dealing with a larger codebase may have to think more about resource management: upload/download speeds, local disk space, CPU, and memory limits.

More Chances For Conflicts

The more developers are involved with a single repository, the more chances there are for conflicting changes. The management requirements may slow development velocity.

More Complex Builds

Depending on our deployment strategy, we may need to adopt a more complex build pipeline that can selectively build and different artifact elements of our organizational solution.

Monorepo GitHub Action

The following script assumes that we have multiple solutions within one repository that we can build independently. Each folder within our repo has its build script, and our action will need to determine what folders have changed and trigger those builds. The approach has several benefits, amongst which is speeding up the build time.

We’ll be using the following actions as part of our script:

These actions will allow us to check out our current repository, get the current pull request number, and then get a collection of files changed.

# This is a basic workflow to help you get started with Actions

name: CI

# Controls when the action will run. 
on:
  # Triggers the workflow on push or pull request events but only for the main branch
  pull_request:
    branches: [ main ]

  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  # This workflow contains a single job called "build"
  build:
    # The type of runner that the job will run on
    runs-on: ubuntu-latest

    # Steps represent a sequence of tasks that will be executed as part of the job
    steps:
      # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
      - uses: actions/checkout@v2

      - name: Get Current Pull Request
        uses: 8BitJonny/gh-get-current-pr@1.0.1
        id: pr
        with:
          github-token: $
          filterOutClosed: true

      - id: file_changes
        name: File Changes
        uses: trilom/file-changes-action@v1.2.3
        with:
          prNumber: $
          pushBefore: ''
          pushAfter: ''

      - name: Ouput File Changes
        run: echo '$'

      # Runs a single command using the runners shell
      - name: Run one "build"
        run: echo Hello, one!
        if: contains(steps.file_changes.outputs.files , '"one/')

      - name: Run two "build"
        if: contains(steps.file_changes.outputs.files , '"two/')
        run: echo Hello, two!

      - name: Run three "build"
        if: contains(steps.file_changes.outputs.files , '"three/')
        run: echo Hello, three!

When we submit a new pull request, we can use the results of the trilom/file-changes-action to determine which project to build using step expressions.

 - name: Run three "build"
 if: contains(steps.file_changes.outputs.files , '"three/')
 run: echo Hello, three!

Let’s see what it looks like in the GitHub actions UI.

Monorepo build in GitHub Actions UI

When we add new projects to our monorepo, we can add another step. The script shown in this post is a good starting point for folks looking to move to a monorepo on GitHub. To see this script in action, feel free to fork the example GitHub repository.

Thanks for reading, and good luck on your monorepo journey.