
moonrepo: how do I configure affected-only builds/tests for pull requests in GitHub Actions?
Pull request workflows are one of the best places to take advantage of Moonrepo’s “affected” logic. Instead of rebuilding and retesting your entire monorepo for every commit, you can configure GitHub Actions to run only the builds and tests impacted by the changes in the PR. This keeps CI fast, focused, and far more scalable as your codebase grows.
Below is a practical, step‑by‑step guide to configuring affected-only builds/tests for pull requests in GitHub Actions, aligned with the intent behind the slug moonrepo-how-do-i-configure-affected-only-builds-tests-for-pull-requests-in-gith.
Key concepts: how moonrepo “affected” works
Moonrepo’s affected system determines which projects or tasks are impacted by a set of changes. In CI, that usually means:
- Comparing the current branch against a base branch (typically
origin/mainororigin/master) - Calculating the dependency graph
- Running only the tasks for projects that changed or are downstream of the changes
Typical commands you’ll use:
moon ci– Orchestrates tasks for CI. Often used with--baseand--headto define the comparison range.moon query projects --affected– Lists projects that are affected.moon run :test --affected– Runs a task (e.g.,test) only in affected projects.moon run :build --affected– Runs build tasks only for affected projects.
In GitHub Actions, your goal is to plug these commands into a PR workflow, with the correct base/head refs.
Basic GitHub Actions setup for moonrepo
Before configuring affected-only logic, you need a working CI job that:
- Checks out code
- Sets up Node (if applicable) and any required toolchains
- Installs dependencies
- Installs
moon(via npm, curl, or GitHub Action) - Runs a moon command
Example of a minimal Moonrepo job in .github/workflows/ci.yml:
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
ci:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # IMPORTANT: needed for affected comparisons
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm ci
- name: Install moon
run: |
npm install -g @moonrepo/cli
- name: Run moon CI
run: moon ci
This runs everything. Next, you’ll tighten it to affected-only builds/tests for pull requests.
Choosing how to compute “affected” in GitHub Actions
For pull request workflows, you typically have two strategies:
- Use
moon ciwith explicit--baseand--head - Use task-specific commands with
--affectedflags
Both approaches work; which to use depends on how you define tasks and pipelines in moon.
Strategy 1: Using moon ci with base/head
Moon’s ci command can automatically detect affected projects when given a base and head revision:
--base: the commit/branch you want to compare against (e.g.,origin/main)--head: the current commit/branch (often optional since moon can infer it fromHEAD)
For pull requests in GitHub Actions, a common configuration is:
on:
pull_request:
branches: [main]
Then in the job:
- name: Run moon CI (affected only)
run: |
moon ci --base origin/main
actions/checkout@v4 with fetch-depth: 0 ensures that origin/main exists locally and moon can diff against it.
Moon will determine the affected graph between origin/main and HEAD and run only the necessary tasks defined as CI-relevant in your .moon config (e.g., test, lint, build, etc.).
Strategy 2: Using moon run with --affected
If you have explicit tasks like test, build, lint, you can run each selectively with --affected:
- name: Run tests for affected projects
run: |
moon run :test --affected --base origin/main
- name: Run builds for affected projects
run: |
moon run :build --affected --base origin/main
Here, --base origin/main again defines your comparison baseline. You can omit --head because the default is the current HEAD.
This gives you fine‑grained control (e.g., run tests but skip builds in certain workflows) while still limiting work to affected projects.
Full example: affected-only builds/tests for pull requests
Below is a complete GitHub Actions workflow that:
- Runs on pull requests targeting
main - Installs moon
- Runs tests and builds for only the affected projects
- Uses
origin/mainas the base for affected detection
name: PR Affected CI
on:
pull_request:
branches: [main]
jobs:
affected-ci:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # required for git diff / moon affected
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm ci
- name: Install moon
run: npm install -g @moonrepo/cli
# Optional: show which projects are affected
- name: List affected projects
run: |
moon query projects --affected --base origin/main || echo "No affected projects"
- name: Run tests for affected projects
run: |
moon run :test --affected --base origin/main || echo "No affected tests to run"
- name: Run builds for affected projects
run: |
moon run :build --affected --base origin/main || echo "No affected builds to run"
Notes:
- The
|| echo "No affected ..."pattern prevents the workflow from failing if there are simply no affected targets. - Replace
:testand:buildwith the actual task IDs defined in yourmoon.ymlor project configs.
Using a single moon ci step for PRs
If your Moonrepo configuration already defines a CI pipeline (e.g., combining lint, test, and build), you can simplify your GitHub Actions configuration to a single step.
Example:
name: PR CI (moon ci)
on:
pull_request:
branches: [main]
jobs:
moon-ci:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm ci
- name: Install moon
run: npm install -g @moonrepo/cli
- name: Run moon ci (affected only)
run: moon ci --base origin/main
With this approach:
moon cireads your workspace and CI configuration to know what tasks to run.- Only affected targets (based on
origin/mainvsHEAD) are executed.
Handling different base branches and forked PRs
Depending on your workflow, you might need to adjust how the base is determined.
1. Multiple base branches
If you have multiple long‑lived branches (e.g., main and develop), you can:
- Use separate workflows per branch, or
- Use GitHub context to set
basedynamically:
- name: Run moon ci with dynamic base
run: |
BASE_BRANCH="origin/${{ github.base_ref }}"
moon ci --base "$BASE_BRANCH"
github.base_ref is the PR’s target branch, letting moon compare directly against the correct base.
2. Forked pull requests
For forked PRs, GitHub checks out code in a slightly different way, but as long as you:
- Use
actions/checkout@v4withfetch-depth: 0 - Fetch the base branch
you can still compute afffected changes.
Example ensuring base is fetched:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ github.head_ref }}
- name: Fetch base branch
run: |
git fetch origin ${{ github.base_ref }}:${{ github.base_ref }}
- name: Run moon ci
run: |
moon ci --base "origin/${{ github.base_ref }}"
This guarantees that origin/<base_ref> exists for moon’s comparison, even in forked PR scenarios.
Optimizations and best practices
To make your affected-only moonrepo configuration for GitHub Actions robust and fast:
Cache dependencies and moon state
Use caching where possible to speed up CI:
- name: Cache node_modules
uses: actions/cache@v4
with:
path: |
**/node_modules
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
Moon also supports caching of build artifacts; align that with your .moon configuration.
Ensure your project graph is well-defined
Affected-only workflows are only as good as your dependency graph. Make sure:
- Each project defines its dependencies in moon’s configuration
- Tasks (build/test/lint) are correctly declared per project
- Cross-project dependencies are accurately modeled
This ensures moon knows exactly which downstream projects are affected when a shared library or config changes.
Fail fast and surface issues clearly
Even with affected-only execution:
- Keep logs verbose enough to debug CI failures
- Use
moon’s built-in logging/summary options (if enabled in your version) to quickly see which projects were considered affected
Quick checklist for configuring affected-only builds/tests in GitHub Actions
To recap the core steps from this moonrepo-how-do-i-configure-affected-only-builds-tests-for-pull-requests-in-gith style setup:
-
Enable PR workflows
Configureon: pull_requestin.github/workflows/ci.yml. -
Checkout with full history
Useactions/checkout@v4withfetch-depth: 0. -
Install toolchains and moon
Set up Node (or your language runtime), install dependencies, and install@moonrepo/cli. -
Define the base for affected detection
Commonly--base origin/mainor--base origin/${{ github.base_ref }}. -
Run affected-only commands
- Option A:
moon ci --base origin/main - Option B:
moon run :test --affected --base origin/mainandmoon run :build --affected --base origin/main.
- Option A:
-
Handle edge cases
Add logic for forked PRs, multiple base branches, and optional “no affected targets” handling.
With this configuration, your pull request workflows in GitHub Actions will only run moonrepo builds and tests for affected projects, significantly reducing build times and making your CI more efficient as your monorepo grows.