-
Notifications
You must be signed in to change notification settings - Fork 0
76026 Centralized dependabot configuration proposal #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
chihaiaalex
wants to merge
3
commits into
main
Choose a base branch
from
76026-centralized-dependabot-configuration
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,226 @@ | ||
| # Proposal: Centralized Dependabot Configuration | ||
|
|
||
| ## Problem | ||
|
|
||
| Dependabot configuration (dependabot.yml) needs to be kept in sync across all microservice repositories. When the configuration changes, it should propagate to all repos without manual effort per repo. Some repos may need repo-specific customizations on top of the shared base config. | ||
|
|
||
| ## Per-repo customization: Base + local overlay | ||
|
|
||
| Dependabot only supports a single `.github/dependabot.yml` per repo — no native inheritance. To allow per-repo customization without losing central management, the sync mechanism (both approaches below) supports an optional local overlay file: | ||
|
|
||
| - `.github/dependabot.yml` — **generated, do not edit manually**. Managed by the sync mechanism. | ||
| - `.github/dependabot-local.yml` — **optional, repo-specific additions**. Manually maintained. | ||
|
|
||
| The sync process: | ||
| 1. Fetches the unified base template | ||
| 2. If `.github/dependabot-local.yml` exists, merges its `updates` entries into the base using `yq` | ||
| 3. Writes the combined result to `.github/dependabot.yml` | ||
|
|
||
| This lets a repo add extra ecosystems or repo-specific rules without losing automatic updates from the central config. Example local overlay for a repo that also has a Go module: | ||
|
|
||
| ```yaml | ||
| # .github/dependabot-local.yml | ||
| updates: | ||
| - package-ecosystem: "gomod" | ||
| directory: "/" | ||
| schedule: | ||
| interval: "monthly" | ||
| target-branch: "main" | ||
| ``` | ||
|
|
||
| The merge concatenates the `updates` lists — base entries first, then local entries. `yq` is lightweight and available on GitHub Actions runners. | ||
|
|
||
| --- | ||
|
|
||
| ## Approach A: Push-based — Central distribution script | ||
|
|
||
| A script runs from a central location (locally or in CI) and pushes the correct dependabot.yml template to each target repo. | ||
|
|
||
| ### How it works | ||
|
|
||
| 1. A single unified template (`dependabot.yml`) and a repo manifest (`repos.conf`) live in a central repo (e.g. `transitdata-shared-workflows`). Dependabot ignores ecosystems that don't apply to a given repo, so one template everywhere simplifies management. | ||
| 2. Two triggers via GitHub Actions: | ||
| - **On merge to main**: Automatically runs the distribution to all repos | ||
| - **On PR (dry-run)**: Validates the change before merging — reports for each repo whether it can be applied cleanly, whether PRs would be needed, and flags any merge conflicts | ||
| 3. For each repo in the manifest: | ||
| - Shallow clone the repo | ||
| - Check out the default branch (`main`) | ||
| - Copy the unified template to `.github/dependabot.yml` | ||
| - If `.github/dependabot-local.yml` exists, merge it into the result using `yq` | ||
| - If no diff → skip | ||
| - If diff and the file applies cleanly → commit and push directly | ||
| - If merge conflict (e.g. someone manually edited the generated file) or branch protection prevents direct push → create a PR instead | ||
| 4. Supports `--dry-run` and `--repo <name>` flags for local testing | ||
|
|
||
| ### Central repo structure | ||
|
|
||
| ``` | ||
| dependabot-config/ | ||
| ├── distribute.sh | ||
| ├── dependabot.yml # single unified template for all repos | ||
| └── repos.conf # list of target repo names | ||
| ``` | ||
|
|
||
| ### Validation on PR (dry-run) | ||
|
|
||
| When a PR modifies the template or `repos.conf`, a CI workflow runs the script in dry-run mode and posts a summary comment on the PR: | ||
| - Which repos would be updated | ||
| - Which repos are already up-to-date (no diff) | ||
| - Which repos would need a PR due to merge conflicts or branch protection | ||
| - Any errors (e.g. repo not found, permission issues) | ||
|
|
||
| This gives confidence before merging that the change will apply cleanly. | ||
|
|
||
| ### Pros | ||
|
|
||
| - Simple to understand — one script, automatic on merge | ||
| - Validation before merge via dry-run on PRs | ||
| - No per-repo setup needed beyond being listed in `repos.conf` (just a repo name, no template mapping) | ||
| - Direct push when possible (no unnecessary PRs for a config-only file) | ||
| - Falls back to PR creation on merge conflicts or branch protection | ||
| - Can also be run locally for debugging | ||
|
|
||
| ### Cons | ||
|
|
||
| - Adding a new repo requires updating `repos.conf` in the central repo | ||
| - Requires a dedicated GitHub App for cross-repo push permissions (see Authentication section below) | ||
| - Central point of execution — if the script fails mid-run, some repos may be updated and others not (can be mitigated with idempotent re-runs) | ||
|
|
||
| ### Authentication: GitHub App | ||
|
|
||
| The push model requires cross-repo write access. A **dedicated GitHub App** is the recommended mechanism — it avoids tying permissions to any individual developer account. | ||
|
|
||
| **Why a GitHub App over a PAT** | ||
|
|
||
| - Not tied to any individual — the app is an org-level resource | ||
| - Minimal, explicitly scoped permissions (least-privilege) | ||
| - Short-lived installation tokens (1 hour), automatically rotated | ||
| - Clear bot identity in commits (`<app-name>[bot]`) | ||
| - Full traceability via GitHub audit log | ||
|
|
||
| **App permissions (least-privilege)** | ||
|
|
||
| | Permission | Access | Why | | ||
| |---|---|---| | ||
| | Contents | Read & Write | Clone repos and push config commits | | ||
| | Pull requests | Read & Write | Create PRs when branch protection blocks direct push | | ||
| | Metadata | Read-only | Implicit with any app installation | | ||
|
|
||
| **Installation scope** | ||
|
|
||
| Two options: | ||
|
|
||
| - **All org repos** (simplest) — new repos are automatically covered, no maintenance needed | ||
| - **Selected repos only** (tighter control) — requires manually adding each new repo to the app's install list | ||
|
|
||
| **Secrets management** | ||
|
|
||
| Store two secrets as **org-level GitHub Actions secrets** in `transitdata-shared-workflows`: | ||
|
|
||
| - `DEPENDABOT_SYNC_APP_ID` — the numeric ID of the GitHub App | ||
| - `DEPENDABOT_SYNC_APP_PRIVATE_KEY` — the PEM private key generated during app creation | ||
|
|
||
| **Token generation in the workflow** | ||
|
|
||
| Use the [`actions/create-github-app-token`](https://github.com/actions/create-github-app-token) action to exchange the app credentials for a short-lived installation token: | ||
|
|
||
| ```yaml | ||
| - uses: actions/create-github-app-token@v1 | ||
| id: app-token | ||
| with: | ||
| app-id: ${{ secrets.DEPENDABOT_SYNC_APP_ID }} | ||
| private-key: ${{ secrets.DEPENDABOT_SYNC_APP_PRIVATE_KEY }} | ||
| owner: HSLdevcom | ||
|
|
||
| - uses: actions/checkout@v4 | ||
| with: | ||
| repository: HSLdevcom/${{ matrix.repo }} | ||
| token: ${{ steps.app-token.outputs.token }} | ||
| ``` | ||
|
|
||
| **Git identity** | ||
|
|
||
| Commits appear as `<app-name>[bot] <DEPENDABOT_SYNC_APP_ID+<app-name>[bot]@users.noreply.github.com>`, making automated changes clearly distinguishable from human commits. | ||
|
|
||
| **One-time setup steps** | ||
|
|
||
| 1. Create a new GitHub App under the HSLdevcom org (Settings → Developer settings → GitHub Apps) | ||
| 2. Set the permissions listed above (Contents R/W, Pull requests R/W) | ||
| 3. Install the app on the org (all repos or selected repos) | ||
| 4. Generate a private key and note the App ID | ||
| 5. Store `DEPENDABOT_SYNC_APP_ID` and `DEPENDABOT_SYNC_APP_PRIVATE_KEY` as org-level Actions secrets in `transitdata-shared-workflows` | ||
|
|
||
| --- | ||
|
|
||
| ## Approach B: Pull-based — Scheduled GitHub Action in each repo | ||
|
|
||
| Each repo that needs centralized configuration runs a scheduled GitHub Action (via shared workflow) that pulls the latest template from the central repo and commits it if changed. | ||
|
|
||
| ### How it works | ||
|
|
||
| 1. The single unified template lives in the central repo (same structure as Approach A) | ||
| 2. A reusable workflow is defined in `transitdata-shared-workflows`: | ||
| ```yaml | ||
| # .github/workflows/sync-dependabot-config.yml | ||
| name: Sync Dependabot config | ||
| on: | ||
| workflow_call: | ||
| ``` | ||
| 3. Each consuming repo adds a minimal workflow: | ||
| ```yaml | ||
| # .github/workflows/sync-dependabot-config.yml | ||
| name: Sync Dependabot config | ||
| on: | ||
| schedule: | ||
| - cron: '0 6 * * 1' # e.g. weekly on Monday at 06:00 | ||
| workflow_dispatch: # allow manual trigger | ||
| jobs: | ||
| sync: | ||
| uses: HSLdevcom/transitdata-shared-workflows/.github/workflows/sync-dependabot-config.yml@main | ||
| secrets: inherit | ||
| ``` | ||
| 4. The shared workflow: | ||
| - Fetches the unified template from the central repo (via `gh api` or raw URL) | ||
| - If `.github/dependabot-local.yml` exists in the repo, merges it into the template using `yq` | ||
| - Compares with current `.github/dependabot.yml` | ||
| - If changed: commits and pushes directly to the target branch | ||
| - If branch protection blocks push: creates a PR instead | ||
|
|
||
| ### Pros | ||
|
|
||
| - **Self-healing**: Repos automatically pick up changes on schedule — no one needs to remember to run anything | ||
| - **Decentralized execution**: Each repo is responsible for its own sync; failure in one repo doesn't affect others | ||
| - Adding the workflow to a new repo is a one-time, minimal setup (short workflow file, no parameters needed) | ||
| - The shared workflow itself is distributed via the existing shared-workflows mechanism the team already uses | ||
| - Each repo's sync schedule and template choice are visible in its own codebase | ||
| - Workflow dispatch allows manual trigger for immediate sync | ||
|
|
||
| ### Cons | ||
|
|
||
| - **Delay**: Changes propagate on the schedule interval (e.g. up to 1 week if weekly), not immediately | ||
| - Mitigated by manual `workflow_dispatch` trigger when urgency is needed | ||
| - Requires initial per-repo setup (adding the caller workflow file) — though this is a one-time, small change | ||
| - Each repo needs appropriate permissions for the workflow to commit/push (though the default `GITHUB_TOKEN` is sufficient — no extra secrets or app setup required) | ||
| - Slightly more moving parts than a single script | ||
|
|
||
| ### Authentication note | ||
|
|
||
| The pull model has a simpler auth story: each repo's workflow uses its own `GITHUB_TOKEN` (automatically provided by GitHub Actions) to push to itself. No dedicated GitHub App, PAT, or org-level secrets are needed. If the central template repo is public (or within the same org), fetching templates requires no extra credentials either. | ||
|
|
||
| --- | ||
|
|
||
| ## Comparison | ||
|
|
||
| | Aspect | A: Push (central script) | B: Pull (scheduled action per repo) | | ||
| |--------|--------------------------|--------------------------------------| | ||
| | **Propagation** | Automatic on merge to main | Scheduled (automatic) + manual dispatch | | ||
| | **Latency** | Immediate on merge | Up to schedule interval (e.g. 1 week) | | ||
chihaiaalex marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| | **Per-repo setup** | None (just add repo name to `repos.conf`) | One-time: add ~10-line workflow file | | ||
| | **Failure isolation** | One script, all-or-nothing per run | Each repo independent | | ||
| | **Visibility** | Central — only in the script repo | Distributed — each repo shows its sync workflow and config | | ||
chihaiaalex marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| | **Remembering to run** | Automatic on merge | Automatic on schedule | | ||
| | **New repo onboarding** | Add line to `repos.conf` | Add workflow file to repo | | ||
| | **Existing patterns** | New script | Extends existing shared-workflows pattern | | ||
chihaiaalex marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| | **Cross-repo permissions** | Dedicated GitHub App (Contents + PRs write); no personal PAT needed | Each repo's `GITHUB_TOKEN` suffices (pushing to own repo) | | ||
| | **Pre-merge validation** | Dry-run on PR with summary report | No built-in validation | | ||
chihaiaalex marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| | **Per-repo customization** | Supported via dependabot-local.yml | Supported via dependabot-local.yml | | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can probably find a tool to validate the Dependabot yaml content in the CI. What other validation can we do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I looked into this. There's no official CLI or API for validating dependabot.yml — there's an open feature request at dependabot/dependabot-core#4605 (open since 2022).
Interestingly, dependabot/dependabot-core#4605 (comment) that GitHub ran a validation check automatically on a PR that modified dependabot.yml, but
dependabot/dependabot-core#4605 (comment) — so it might be a limited rollout or only for certain plans.
What exists today:
None of these are fully comprehensive — they validate structure against the schema but can't catch all Dependabot-internal validation rules. For our dry-run, a schema check would catch the most common errors
(typos, invalid keys, wrong types). Beyond that, the best validation is just that Dependabot itself silently ignores invalid configs — it won't break anything, it just won't run.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, JSON schema validation sounds useful and secure.