Combining Multiple Repositories into a Single GitHub Action: A Step-by-Step Guide


GitHub Actions is a powerful automation tool that lets you build, test, and deploy code directly from your repository. But what if your project depends on multiple repositories? For example, you might have a microservices architecture or shared libraries stored in separate repos. In this blog post, we’ll walk through how to combine workflows across different repositories into a single GitHub Action.
Why Combine Repositories in a GitHub Action?
Here are common scenarios where this approach shines:
Microservices: Trigger a unified test suite when changes occur in any linked service.
Shared Libraries: Rebuild a core library and update dependent projects automatically.
Monorepo Alternatives: Coordinate workflows across repos without consolidating codebases.
Prerequisites
Basic familiarity with GitHub Actions and YAML.
Access to the repositories you want to integrate.
A GitHub Personal Access Token (PAT) with appropriate permissions.
Step 1: Set Up the Base Workflow
Start by creating a workflow file (e.g., .github/workflows/combined.yml
) in your primary repository. This will house the logic to interact with other repos.
name: Combined Repo Workflow
on:
push:
branches: [main]
workflow_dispatch:
Step 2: Access Other Repositories
To interact with external repositories, use the actions/checkout
action with a PAT. Here’s how:
Generate a PAT
Go to GitHub Settings > Developer Settings > Personal Access Tokens.
Create a token with
repo
scope (to access private repos if needed).Add the token as a secret (e.g.,
REPO_TOKEN
) in your primary repository’s settings.
Checkout External Repos
Add steps to fetch code from other repositories:
jobs:
fetch-dependencies:
runs-on: ubuntu-latest
steps:
- name: Checkout Primary Repo
uses: actions/checkout@v4
- name: Checkout External Repo A
uses: actions/checkout@v4
with:
repository: org/repo-a
token: ${{ secrets.REPO_TOKEN }}
path: repo-a
- name: Checkout External Repo B
uses: actions/checkout@v4
with:
repository: org/repo-b
token: ${{ secrets.REPO_TOKEN }}
path: repo-b
Step 3: Combine Workflows
Now that you’ve cloned the repos, run commands or trigger their workflows. For example:
build-and-test:
runs-on: ubuntu-latest
needs: fetch-dependencies # Wait for dependencies to fetch
steps:
- name: Build Repo A
run: |
cd repo-a
npm install
npm run build
- name: Test Repo B
run: |
cd repo-b
pip install -r requirements.txt
pytest tests/
Step 4: Share Artifacts or Trigger Follow-Up Actions
To pass data between jobs or repos, use GitHub’s upload-artifact
and download-artifact
actions. For instance:
- name: Upload Build Output
uses: actions/upload-artifact@v3
with:
name: repo-a-build
path: repo-a/dist/
deploy:
needs: build-and-test
runs-on: ubuntu-latest
steps:
- name: Download Artifact
uses: actions/download-artifact@v3
with:
name: repo-a-build
1. Clone Multiple Repositories in a Single Workflow
Use the actions/checkout
action multiple times to clone other repositories into your workflow workspace.
Use Case: Aggregate code/files from multiple repositories for processing, testing, or deployment.
Example:
jobs:
combine-repos:
runs-on: ubuntu-latest
steps:
# Checkout the current repository
- name: Checkout Current Repo
uses: actions/checkout@v4
# Checkout another repository (Repo A)
- name: Checkout Repo A
uses: actions/checkout@v4
with:
repository: your-username/repo-a
token: ${{ secrets.PAT }} # GitHub Personal Access Token (PAT)
path: repo-a # Clone into a subdirectory
# Checkout another repository (Repo B)
- name: Checkout Repo B
uses: actions/checkout@v4
with:
repository: your-username/repo-b
token: ${{ secrets.PAT }}
path: repo-b
# Example: Copy files from Repo A/B to the current repo
- name: Merge Files
run: |
cp -r repo-a/src ./combined-src/
cp -r repo-b/config ./combined-config/
Requirements:
- Create a Personal Access Token (PAT) with
repo
scope and add it as a secret (e.g.,PAT
) in your repository settings.
2. Trigger Workflows Across Repositories
Use repository_dispatch
or workflow triggers to chain workflows across repositories.
Use Case: Run a workflow in Repository B when an event occurs in Repository A.
Example (Triggering Repo B from Repo A):
Repo A Workflow:
jobs:
trigger-repo-b:
runs-on: ubuntu-latest
steps:
- name: Trigger Repo B Workflow
uses: peter-evans/repository-dispatch@v2
with:
token: ${{ secrets.PAT }}
repository: your-username/repo-b
event-type: repo-a-trigger
Repo B Workflow (in .github/workflows/repo-b.yml
):
on:
repository_dispatch:
types: [repo-a-trigger]
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: echo "Triggered by Repo A!"
3. Mirror Changes Between Repositories
Push changes from one repository to another automatically.
Use Case: Sync code between a public and private repository.
jobs:
mirror-repo:
runs-on: ubuntu-latest
steps:
- name: Checkout Source Repo
uses: actions/checkout@v4
with:
fetch-depth: 0 # Fetch all history
- name: Push to Target Repo
run: |
git remote add target https://${{ secrets.PAT }}@github.com/your-username/target-repo.git
git push target HEAD:main
4. Aggregate Data/Artifacts from Multiple Repos
Download artifacts or data from other repositories into a central workflow.
Use Case: Combine test results or reports from multiple repositories.
jobs:
aggregate-reports:
runs-on: ubuntu-latest
steps:
- name: Download Artifact from Repo A
uses: actions/download-artifact@v4
with:
repository: your-username/repo-a
workflow: tests.yml
name: test-results
token: ${{ secrets.PAT }}
path: repo-a-results
- name: Process Reports
run: |
cat repo-a-results/results.txt >> combined-report.txt
Key Details
repository
: Specify the target repo inowner/repo-name
format.path
: Each repo is cloned into a subdirectory to avoid conflicts.token
: Uses the PAT stored as a secret to authenticate access.
Handling Public Repositories
If the repositories are public, you can skip the token
parameter:
- name: Clone Public Repo
uses: actions/checkout@v4
with:
repository: your-org/public-repo
path: public-repo
Best Practices
Reuse PATs Securely: Never hardcode tokens in workflows—use GitHub Secrets.
Parallelize Cloning: Use a
matrix
strategy to clone repos in parallel:strategy: matrix: repo: - your-org/repo-a - your-org/repo-b steps: - name: Clone ${{ matrix.repo }} uses: actions/checkout@v4 with: repository: ${{ matrix.repo }} path: ${{ matrix.repo }}
Cache Dependencies: Speed up workflows by caching tools (e.g.,
npm
,pip
):- name: Cache Node Modules uses: actions/cache@v3 with: path: repo-a/node_modules key: repo-a-${{ hashFiles('repo-a/package-lock.json') }}
Shallow Clone: Reduce clone time with
fetch-depth
for large repos:- uses: actions/checkout@v4 with: repository: your-org/large-repo fetch-depth: 1 # Only fetch the latest commit
Example Use Case: Multi-Repo Testing
Clone a frontend and backend repo, then run tests:
- name: Test Frontend
run: |
cd frontend-repo
npm install
npm test
- name: Test Backend
run: |
cd backend-repo
pip install -r requirements.txt
pytest tests/
Limitations
Permissions: The PAT must have access to all repositories involved.
Workflow Complexity: Cloning many repos can slow down job execution.
By following this approach, you can seamlessly integrate code from multiple repositories into a single workflow for testing, building, or deployment.
Subscribe to my newsletter
Read articles from Kolluru Pawan directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
