Skip to content
GitHub Agentic Workflows

CentralRepoOps

CentralRepoOps is a MultiRepoOps deployment variant where a single private repository acts as a control plane for large-scale operations across many repositories.

Use this pattern for organization-wide rollouts, phased adoption (pilot waves first), central governance, and security-aware prioritization across tens or hundreds of repositories. Each orchestrator run delivers consistent policy gates, controlled fan-out (max), and a complete decision trail — without pushing main changes to individual target repositories.

Example: Dependabot Rollout (Orchestrator + Worker)

Section titled “Example: Dependabot Rollout (Orchestrator + Worker)”

This pattern maps directly to your Dependabot rollout pair:

  • dependabot-rollout-orchestrator.md decides where to roll out next.
  • dependabot-rollout.md executes how to configure each target repository.

Let’s say you want to roll out a new Dependabot configuration across 100 repositories. This example shows you how to do this based on a small subset.

Navigate to your central repository and create a workflow file .github/workflows/dependabot-rollout-orchestrator.md with the following contents:

---
on:
schedule:
- cron: '0 9 * * 1'
tools:
github:
github-token: ${{ secrets.GH_AW_READ_ORG_TOKEN }}
toolsets: [repos]
safe-outputs:
dispatch-workflow:
workflows: [dependabot-rollout]
max: 5
---
# Dependabot Rollout Orchestrator
Categorize and orchestrate Dependabot rollout across repositories.
**Target repos**: All repos in the organization
## Task
1. **Filter** - Parse repos (from input or variable), check each for existing `.github/dependabot.yml`, keep only repos without it
2. **Categorize** - Read repo contents to assess complexity:
- Simple: Single package.json, <50 dependencies, standard structure
- Complex: Multiple package.json files, >100 deps, or multiple ecosystems
- Conflicting: Has Renovate config or custom update scripts
- Security: Open security alerts or public with dependencies
3. **Prioritize** - Order repos by rollout preference: simple → security → complex → conflicting
4. **Dispatch** - Dispatch `dependabot-rollout` worker for every prioritized repository
5. **Summarize** - Report total candidates, categorization breakdown, selected repos with rationale

Compile this workflow to generate the lock file: gh aw compile. Create a fine-grained PAT GH_AW_READ_ORG_TOKEN (this link pre-fills the token name, description, and Contents: Read permission) with the organization as an owner, select “All repositories” (or allowlist of specific repos), and grant Repository permission: Contents: Read-only. Add this into your Actions repository secrets. This gives the orchestrator read access to all candidate repositories.

Next, create the worker workflow .github/workflows/dependabot-rollout.md in your central repository that will operate on each target repository via checkout:

---
on:
workflow_dispatch:
inputs:
target_repo:
description: 'Target repository (owner/repo format)'
required: true
type: string
run-name: Dependabot rollout for ${{ github.event.inputs.target_repo }}
concurrency:
group: gh-aw-${{ github.workflow }}-${{ github.event.inputs.target_repo }}
engine:
id: copilot
concurrency:
group: gh-aw-copilot-${{ github.workflow }}-${{ github.event.inputs.target_repo }}
steps:
- name: Checkout target repository
uses: actions/checkout@v5
with:
token: ${{ secrets.ORG_REPO_CHECKOUT_TOKEN }}
repository: ${{ github.event.inputs.target_repo }}
persist-credentials: false
permissions:
contents: read
issues: read
pull-requests: read
tools:
github:
github-token: ${{ secrets.GH_AW_READ_ORG_TOKEN }}
toolsets: [repos]
safe-outputs:
github-token: ${{ secretsGH_AW_CROSS_REPO_PAT }}
create-pull-request:
target-repo: ${{ github.event.inputs.target_repo }}
title-prefix: '[dependabot] '
max: 1
create-issue:
target-repo: ${{ github.event.inputs.target_repo }}
title-prefix: '[dependabot-config] '
max: 1
---
# Intelligent Dependabot Configuration
You are creating a **customized** Dependabot configuration based on analyzing this specific repository.
**Target Repository**: ${{ github.event.inputs.target_repo }}
## Why AI is Required
You must analyze the repository structure and create an intelligent, customized configuration - not a generic template.
## Step 1: Analyze Repository
**Check for conflicts:**
- Does `.github/dependabot.yml` already exist? → Stop, create issue explaining it exists
- Does `.github/renovate.json` or `renovate.json` exist? → Create issue about migrating from Renovate
- Are there custom dependency update scripts? → Create issue suggesting Dependabot alternative
**Analyze package manager complexity:**
For **npm** (if package.json exists):
- Count total dependencies (dependencies + devDependencies)
- Check for monorepo: Are there multiple package.json files in subdirectories?
- Simple: <20 dependencies, single package.json
- Complex: >100 dependencies OR monorepo structure
For **Python** (requirements.txt, setup.py, pyproject.toml):
- Count dependencies
- Check for multiple requirement files
For **Go** (go.mod):
- Note if present
For **GitHub Actions** (.github/workflows/*.yml):
- Count workflow files
**Security context:**
- Use GitHub tools to check for open security alerts
- If critical alerts exist, prioritize security updates
## Step 2: Create Customized Configuration
Based on your analysis, create an appropriate config:
### Simple Repository (<20 npm deps, no monorepo)
```yaml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "daily" # Low complexity = more frequent
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
```
### Complex Repository (>100 deps OR security alerts)
```yaml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly" # High complexity = less frequent
groups:
production:
patterns: ["*"]
exclude-patterns: ["@types/*", "@jest/*"]
dev-dependencies:
patterns: ["@types/*", "@jest/*", "eslint*"]
```
### Monorepo (multiple package.json)
```yaml
version: 2
updates:
- package-ecosystem: "npm"
directory: "/packages/frontend"
schedule:
interval: "weekly"
- package-ecosystem: "npm"
directory: "/packages/backend"
schedule:
interval: "weekly"
```
## Step 3: Deliver Configuration
**If config is straightforward (no Renovate conflict):**
- Create `.github/dependabot.yml` with your customized config
- Create pull request with:
- Title: "[dependabot] Add customized Dependabot configuration"
- Body explaining: dependency count, why weekly vs daily, grouping strategy, etc.
**If Renovate detected:**
- Create issue explaining migration benefits and proposed config
- Include generated config in issue body
**If no package managers found:**
- Create issue: "No supported package managers detected"
## Key: Explain Your Reasoning
In the PR/issue body, explain **why** you chose this specific configuration (not a generic template).

Compile this workflow to generate the lock file: gh aw compile. Create a fine-grained PAT ORG_REPO_CHECKOUT_TOKEN (this link pre-fills the token name, description, and permissions) with the organization as an owner, select “All repositories” (or allowlist of specific repos), and grant Repository permission: Contents: Read & write, Actions: Read & write. This allows the worker to check out the target repository.

Also create a fine-grained PAT REPO_SAFE_OUTPUTS_TOKEN (this link pre-fills the token name, description, and permissions) with the organization as an owner, select “All repositories” (or allowlist of specific repos), and grant Repository permission: Contents: Write, Issues: Write, Pull Requests: Write. This allows the worker to create pull requests and issues in the target repository based on the orchestrator’s instructions.

After the setup is complete, you can run the orchestrator with workflow_dispatch (target_repos) or let the schedule trigger run automatically.

Embedding a schedule: trigger directly in the orchestrator is the simplest setup, but it limits the orchestrator to time-based execution only. A trigger file — a plain, hand-authored GitHub Actions workflow — solves this by separating the trigger definition from the agent logic:

  • The compiled orchestrator exposes a workflow_call trigger.
  • A stable, rarely-changing .yml file defines the actual GitHub events that kick off the run.
  • The trigger file calls the compiled orchestrator via workflow_call, forwarding any inputs and secrets.

This means you can update when and why the orchestrator runs — reacting to a label, a repository push, a security alert, or a manual dispatch — without touching or recompiling the agentic workflow.

Extend the orchestrator’s on: section with workflow_call and declare any inputs you want callers to pass:

---
on:
schedule: weekly on monday
workflow_call:
inputs:
reason:
description: "Why this run was triggered (label name, event type, etc.)"
type: string
default: "scheduled"
tools:
github:
github-token: ${{ secrets.GH_AW_READ_ORG_TOKEN }}
toolsets: [repos]
safe-outputs:
dispatch-workflow:
workflows: [dependabot-rollout]
max: 5
---
# Dependabot Rollout Orchestrator
...

Compile the workflow after adding workflow_call: gh aw compile.

Add a plain GitHub Actions workflow alongside the compiled orchestrator. This file is written by hand, committed once, and rarely needs to change:

.github/workflows/central-ops-trigger.yml
name: Central Ops Trigger
on:
# Trigger when a repository is labeled for Dependabot rollout
issues:
types: [labeled]
# Trigger on any push to main (e.g. config change)
push:
branches: [main]
# Allow manual runs with context
workflow_dispatch:
inputs:
reason:
description: "Reason for manual trigger"
required: false
default: "manual"
jobs:
trigger:
uses: ./.github/workflows/dependabot-rollout-orchestrator.lock.yml
with:
reason: ${{ github.event_name }}
secrets: inherit

Calling the Orchestrator from Another Repository

Section titled “Calling the Orchestrator from Another Repository”

The trigger file can also live in a different repository — for example, inside each target repo — and call back into the orchestrator in the central control repository. This lets individual repositories decide when to request a rollout without requiring direct access to the central repo.

.github/workflows/request-dependabot-rollout.yml
name: Request Dependabot Rollout
on:
# Trigger when a maintainer labels the repo for rollout
issues:
types: [labeled]
workflow_dispatch:
jobs:
trigger:
# Call the orchestrator workflow in the central control repo
uses: my-org/central-ops/.github/workflows/dependabot-rollout-orchestrator.lock.yml@main
with:
reason: "${{ github.event_name }} in ${{ github.repository }}"
secrets:
GH_AW_READ_ORG_TOKEN: ${{ secrets.GH_AW_READ_ORG_TOKEN }}
Schedule onlyTrigger file + workflow_call
SetupSingle file, no extra configTwo files (orchestrator + trigger file)
Trigger flexibilityCron/schedule onlyAny GitHub event (issues, push, PRs, labels…)
Change trigger without recompileNo — trigger is embedded in the agentic workflowYes — edit the plain .yml trigger file
Pass event context to agentNot possibleYes — via workflow_call inputs
StabilityTrigger changes on every recompileTrigger file stays fixed across recompiles
When to useSimple recurring jobs with no event dependencyJobs that should react to repository activity or need flexible scheduling

The schedule-only approach is perfectly fine for a standalone nightly or weekly orchestrator. Move to the trigger file pattern when you need to react to repository events, pass extra context to the agent, or decouple trigger changes from the compilation cycle.

A trigger file can live in a different repository (for example, in each application repo) and call back into the agentic workflow hosted in a central platform repo. This pattern is useful for multi-tenant platforms where each application repo triggers a shared agentic workflow.

.github/workflows/platform-relay.yml (in application repo)
name: Platform Relay
on:
issue_comment:
types: [created]
jobs:
relay:
uses: my-org/platform-repo/.github/workflows/platform-gateway.lock.yml@main
with:
issue_number: ${{ github.event.issue.number }}
source_repo: ${{ github.repository }}
secrets: inherit

The callee (platform) repository must meet one of these requirements:

  • Public — accessible to anyone
  • Internal — accessible within the organization
  • Private with Actions access — in repository Settings → Actions → General, under Access, select “Accessible from repositories in the [organization] organization”

COPILOT_GITHUB_TOKEN must be configured in the caller repository’s secrets. When using secrets: inherit, all caller secrets are forwarded to the platform workflow automatically. This means:

  • Premium Copilot requests bill to the caller’s token, not the platform’s
  • Each application team manages their own COPILOT_GITHUB_TOKEN
  • The platform repo does not need to hold tokens for all callers

When the compiler detects workflow_call in the on: section, it generates a cross-repo-aware checkout step in the activation job. Instead of always checking out github.repository (which is the caller in a workflow_call context), the step uses a conditional expression:

repository: ${{ github.event_name == 'workflow_call' && github.action_repository || github.repository }}

This ensures the activation job checks out the platform repo’s .github folder — where the workflow markdown and runtime imports live — regardless of which application repo triggered the workflow. For non-workflow_call events, the expression falls back to the current repository.

  • Keep orchestrator permissions narrow; delegate repo-specific writes to workers.
  • Use safe output limits (max) and explicit target workflow allowlists.
  • Add correlation IDs to worker dispatch inputs for tracking.