Skip to content

chore: ensure fleet repos with renovate.json are in Renovate App scope#14

Merged
ANcpLua merged 2 commits into
mainfrom
ensure-renovate-scope-in-sweep
May 25, 2026
Merged

chore: ensure fleet repos with renovate.json are in Renovate App scope#14
ANcpLua merged 2 commits into
mainfrom
ensure-renovate-scope-in-sweep

Conversation

@ANcpLua
Copy link
Copy Markdown
Owner

@ANcpLua ANcpLua commented May 25, 2026

Why

Surfaced 2026-05-25 while investigating why Renovate had never opened a PR on ANcpLua.OpenTelemetry.SemanticConventions.Analyzers despite a valid renovate.json extending github>ANcpLua/renovate-config. Root cause: the repo was created with the right files but was never added to the Renovate App's installation scope. Adding it manually via the App settings UI worked immediately (Dependency Dashboard #27, PR #25 appeared). But the same silent-gap will recur every time a new repo lands in the fleet.

The hygiene sweep already file-syncs workflow templates (auto-merge.yml, coderabbit-autofix.yml, triage-bot.yml). It did not previously cover App installation grants because those aren't files — they're API state.

What changed

One new step at the end of the enforce-user job:

  • Iterates the same curated targets.txt produced by the existing target-resolution step.
  • For every repo that actually has a renovate.json (skips opt-outs).
  • Resolves the repo's numeric id, then PUT /user/installations/79212889/repositories/<id>.
  • PUT is idempotent (204 No Content whether newly added or already present), so no pre-check.
  • Failures emit ::warning:: and continue, matching the existing pattern.

Why id 79212889 is hardcoded

Listing installations programmatically requires a user-to-server App access token, which a PAT cannot mint (/user/installations returns 403 with repo-scope PATs). The installation id is stable across runs — only changes if Renovate is uninstalled and reinstalled. If that happens, update the env var.

PAT scope

REPO_SETTINGS_PAT_USER already needs repo scope for the file-sync steps. The Add-repository-to-installation endpoint accepts the same repo scope, so no new permission is required.

What's NOT covered

enforce-org (the O-ANcppLua org-side job) does not get an equivalent step. If a separate Renovate installation exists on the org, add a mirror step there with the org-side installation id.

Manual verification before merge

gh workflow run enforce-repo-settings.yml -f sweep_mode=topic
# Watch the "Ensure fleet repos with renovate.json are in Renovate App scope" step.
# Every fleet repo with renovate.json should log "Ensured Renovate scope for ANcpLua/<repo> (id <n>)".

Closes the silent-gap pattern surfaced 2026-05-25 on
ANcpLua.OpenTelemetry.SemanticConventions.Analyzers — repo carried a
valid renovate.json extending github>ANcpLua/renovate-config yet
produced zero Dependency Dashboard issues and zero PRs for weeks
because the Renovate App's installation scope did not include it.

Workflow files are file-syncable; App installation scope is not. This
adds an idempotent PUT against the user-installations endpoint for
every fleet repo that has a renovate.json, so the weekly sweep
self-heals any new repo dropped into the fleet topic.

Installation id 79212889 is hardcoded because listing installations
requires an App user-to-server token that a PAT cannot mint. Revisit
if Renovate is reinstalled.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

ghost commented May 25, 2026

@coderabbitai autofix

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 25, 2026

📝 Walkthrough

Summary by CodeRabbit

  • Chores
    • Improved automated repository settings enforcement for Renovate onboarding.
    • System now scans repositories for Renovate configuration, attempts to register eligible repos with the Renovate installation, and logs successes and warnings for unresolved or failed registrations.
    • Process is idempotent and continues across repositories even when individual steps fail.

Walkthrough

A new enforce-user workflow step iterates targets.txt, checks four locations for a Renovate config, resolves each repository’s numeric GitHub ID, and idempotently PUTs the repo into a hardcoded Renovate App installation scope; per-repo warnings are logged on unresolved IDs or failed PUTs.

Changes

Renovate App Scope Enforcement

Layer / File(s) Summary
Renovate App enrollment step
.github/workflows/enforce-repo-settings.yml
New enforce-user step iterates targets.txt, checks for Renovate config at multiple paths, retrieves the repository's numeric ID from the GitHub API, and issues an idempotent PUT to add the repository to Renovate App installation 79212889. The step logs warnings for unresolved repository IDs or failed API calls and continues processing remaining repositories.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: adding a step to ensure fleet repositories with renovate.json are included in the Renovate App installation scope.
Description check ✅ Passed The description comprehensively explains the root cause (missing App scope), the implementation (new sweep step), why hardcoding the ID, PAT scope implications, and manual verification steps.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ensure-renovate-scope-in-sweep
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch ensure-renovate-scope-in-sweep

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 25, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Autofix skipped. No unresolved CodeRabbit review comments with fix instructions found.

@codacy-production
Copy link
Copy Markdown

codacy-production Bot commented May 25, 2026

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

AI Reviewer: first review requested successfully. AI can make mistakes. Always validate suggestions.

Run reviewer

TIP This summary will be updated as you push new changes.

Copy link
Copy Markdown

@codacy-production codacy-production Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

The pull request successfully automates the inclusion of repositories in the Renovate GitHub App scope by scanning for configuration files and using the GitHub API. While Codacy indicates the code is up to standards, there is a logic gap regarding the detection of Renovate configuration files.

The current implementation only looks for 'renovate.json' at the root level. This contradicts the intent of preventing a 'silent gap,' as repositories using alternative valid formats (like .json5) or locations (like .github/) will be skipped. Additionally, while environment variables are defined, there is a risk of hardcoded numeric IDs within the shell logic. Addressing these points will make the automation more robust and maintainable.

About this PR

  • The shell-based logic loop for repository processing lacks automated verification or unit tests. While functional, future changes to this logic may be error-prone without a way to validate the script in isolation.
  • The installation ID '79212889' is a hardcoded dependency. Ensure that if the Renovate App is ever reinstalled, this workflow is updated immediately to prevent global automation failure.

Test suggestions

  • Repository with renovate.json is successfully added to Renovate scope.
  • Repository without renovate.json is skipped.
  • Workflow logs a warning when a repository ID cannot be resolved.
  • Workflow logs a warning when the PUT request to the installation endpoint fails.
Prompt proposal for missing tests
Consider implementing these tests if applicable:
1. Repository with renovate.json is successfully added to Renovate scope.
2. Repository without renovate.json is skipped.
3. Workflow logs a warning when a repository ID cannot be resolved.
4. Workflow logs a warning when the PUT request to the installation endpoint fails.

TIP Improve review quality by adding custom instructions
TIP How was this review? Give us feedback

set -uo pipefail
while read -r repo; do
[ -z "$repo" ] && continue
if ! gh api "repos/$repo/contents/renovate.json" --silent 2>/dev/null; then
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM RISK

Suggestion: The configuration check is currently restricted to renovate.json in the root directory. Since Renovate supports various paths and formats (like .github/renovate.json, renovate.json5, and .github/renovate.json5), consider expanding the check to include these locations. This ensures all configured repositories are correctly added to the Renovate scope.

Suggested prompt for your coding agent:

Modify the script to also check for .github/renovate.json, renovate.json5, and .github/renovate.json5 before skipping a repository.

Address CodeRabbit suggestion on PR #14. Renovate accepts its config at
four common paths; the previous check only matched root renovate.json,
so repos using renovate.json5 or .github/renovate.json{,5} were silently
skipped by the scope-ensure step. Detection now treats any of the four
locations as "this repo wants Renovate".

Deliberately out of scope: package.json#renovate key and the .renovaterc*
dotfile variants — both require content inspection rather than path
existence and are rare in this fleet. Add if needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions
Copy link
Copy Markdown

ghost commented May 25, 2026

@coderabbitai autofix

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 25, 2026

Note

Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it.

Autofix skipped. No unresolved CodeRabbit review comments with fix instructions found.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/enforce-repo-settings.yml (1)

214-239: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Fail fast if the hardcoded Renovate installation is no longer accessible (.github/workflows/enforce-repo-settings.yml:214-239)

The loop downgrades every failed PUT into a per-repo ::warning::, so the workflow can still exit green if RENOVATE_INSTALLATION_ID=79212889 (or GH_TOKEN: secrets.REPO_SETTINGS_PAT_USER) is no longer authorized/visible. Add a one-time preflight before the loop and hard-fail when the installation can’t be read.

Proposed fix
         run: |
           set -uo pipefail
+          if ! gh api "/user/installations/$RENOVATE_INSTALLATION_ID" --jq '.id' >/dev/null 2>&1; then
+            echo "::error::Renovate installation $RENOVATE_INSTALLATION_ID is not accessible to the current GH_TOKEN"
+            exit 1
+          fi
           while read -r repo; do
             [ -z "$repo" ] && continue
             has_renovate_config=false
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/enforce-repo-settings.yml around lines 214 - 239, Add a
one-time preflight check that validates the RENOVATE_INSTALLATION_ID is
accessible before entering the while loop: perform a gh api GET on
"/user/installations/$RENOVATE_INSTALLATION_ID" (or equivalent) and if it fails
(non-zero / no JSON id) call exit 1 and emit a clear error, so the workflow
hard-fails rather than turning every failure into a per-repo warning; place this
check immediately before the while read -r repo; do loop that uses
RENOVATE_INSTALLATION_ID.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/enforce-repo-settings.yml:
- Around line 220-227: The probe currently suppresses all gh api failures and
treats any non-OK result as “no config”; change the probe so gh api
"repos/$repo/contents/$cfg" is invoked in a way that captures the HTTP status
(e.g., include response headers or status output) and only treat 404 as “absent”
(set has_renovate_config=false); for any other non-200/404 status log a
warning/error with repository and status and stop/raise so enrollment isn’t
silently skipped. Likewise, stop swallowing the PUT enrollment call to
/user/installations/$RENOVATE_INSTALLATION_ID/repositories/$repo_id (remove
>/dev/null 2>&1 || true and --silent) and surface its exit code and response
body; if the PUT fails log the repo, installation id, status and response and
fail or return a non-zero exit to surface bad RENOVATE_INSTALLATION_ID or auth
errors. Ensure the probe also checks and logs the result of
/repos/$repo/installation rather than ignoring it.

---

Outside diff comments:
In @.github/workflows/enforce-repo-settings.yml:
- Around line 214-239: Add a one-time preflight check that validates the
RENOVATE_INSTALLATION_ID is accessible before entering the while loop: perform a
gh api GET on "/user/installations/$RENOVATE_INSTALLATION_ID" (or equivalent)
and if it fails (non-zero / no JSON id) call exit 1 and emit a clear error, so
the workflow hard-fails rather than turning every failure into a per-repo
warning; place this check immediately before the while read -r repo; do loop
that uses RENOVATE_INSTALLATION_ID.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 554f515b-9c24-46b4-af94-187203ea939e

📥 Commits

Reviewing files that changed from the base of the PR and between d5c6804 and b883387.

📒 Files selected for processing (1)
  • .github/workflows/enforce-repo-settings.yml
📜 Review details
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: claude-review

Comment on lines +220 to +227
for cfg in renovate.json renovate.json5 .github/renovate.json .github/renovate.json5; do
if gh api "repos/$repo/contents/$cfg" --silent 2>/dev/null; then
has_renovate_config=true
break
fi
done
if [ "$has_renovate_config" = false ]; then
continue
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/enforce-repo-settings.yml"
echo "== exists =="
ls -l "$FILE" || true
echo
echo "== context around lines 200-260 =="
nl -ba "$FILE" | sed -n '180,260p'
echo
echo "== search for set -u/o pipefail and has_renovate_config usage =="
rg -n "set -[^\n]*pipefail|has_renovate_config|RENOVATE_INSTALLATION_ID|PUT /user/installations" "$FILE"

Repository: ANcpLua/github-settings-automation

Length of output: 282


🌐 Web query:

gh cli gh api -i include response headers output format first line

💡 Result:

When you run gh api -i / --include, the first line of output is the HTTP status line in the form HTTP/<major>.<minor> <status-code> <reason-phrase> (the code prints resp.Proto and resp.Status as the first line, followed by headers). [1] Example shape: - First line: HTTP/1.1 200 OK - Then response headers (and a blank line after headers before the body, if any). [1] Notes: - -i/--include is documented as “Include HTTP response status line and headers in the output.” [2]

Citations:


🌐 Web query:

``gh api exit code 404 nonzero behavior

💡 Result:

When you run gh api and the GitHub API returns HTTP 404, gh treats it as an error and exits with a non-zero status code (exit code 1 in the cited behavior). This means you should not expect gh api to “succeed” on not-found responses; you should handle 404 explicitly (e.g., by checking the exit status or by using flags/logic that tolerate errors). Evidence: - The gh issue discussion notes: “using gh api to read non-existing resources will cause the command to exit with a status code of 1” and attributes this to the HTTP 404 response status returned by the API [1]. - The gh api command is documented as making an authenticated HTTP request and printing the response; when the API responds with an error like 404, the command surfaces that as an error (“gh: Not Found (HTTP 404)”) in examples [2][3]. Related notes (why you might see 404s even when something “should” exist): - Missing OAuth token scopes can sometimes produce confusing 404s (e.g., missing workflow scope when creating releases) [4]. If you tell me the exact gh api command you’re running (including flags like --silent, --method, and whether you’re using pipelines), I can suggest a reliable way to detect/ignore 404 in your script.

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE=".github/workflows/enforce-repo-settings.yml"
echo "== exists =="
ls -l "$FILE" || true
echo
echo "== context around lines 200-260 =="
nl -ba "$FILE" | sed -n '180,260p'
echo
echo "== search for set -u/o pipefail and has_renovate_config usage =="
rg -n "set -[^\n]*pipefail|has_renovate_config|RENOVATE_INSTALLATION_ID|PUT /user/installations" "$FILE"

Repository: ANcpLua/github-settings-automation

Length of output: 282


🌐 Web query:

gh cli gh api -i include response headers output format first line

💡 Result:

When you run gh api -i / gh api --include, the output starts with the HTTP status line (the “response status line”), i.e. the first line is HTTP/<version> <status code> <reason phrase> (e.g., HTTP/2 200 OK). This happens because the --include flag prints resp.Proto (protocol) and resp.Status (status line contents) as the first header line before printing the rest of the response headers. [1] After that first line, gh api -i prints the individual response headers, then a blank line (\r\n). [1]

Citations:


🌐 Web query:

``gh api exit code 404 nonzero behavior

💡 Result:

When you run gh api and the GitHub REST API returns HTTP 404 (Not Found), gh api exits non-zero (typically exit code 1). This matches GitHub’s expectation that 404 indicates an error condition for the requested resource/permission, and GitHub CLI treats non-2xx responses as failures. Evidence: - GitHub’s REST API troubleshooting doc notes that a 404 may be returned when a resource is private or authentication is missing/incorrect, and instructs you to check authentication/URL when you see 404. [1] - gh CLI issue reports show gh api surfacing a 404 as an error (example: HTTP 404: Not Found ...). [2] - A gh CLI issue clarifies that “using gh api to read non-existing resources will cause the command to exit with a status code of 1” (even with -X HEAD). [3] Practical implications for scripting: - Don’t rely on output parsing from a 404; handle the exit code. - If you need different behavior on “not found,” capture the exit status and treat it as a special case. Example: - if ! gh api repos/OWNER/REPO/...; then ... # handle 404/not-found ... fi If you want, paste the exact gh api ... command you’re running and how you’re checking the exit code/output, and I can suggest the cleanest conditional for your shell.

Citations:


Differentiate 404 from other gh api failures in the Renovate config probe, and stop swallowing enrollment errors

The config probe currently treats any non-zero gh api repos/$repo/contents/$cfg result as “no config” (stderr is suppressed), so 401/403/429/5xx can silently skip enrollment. Enrollment is also effectively unobservable: the PUT /user/installations/$RENOVATE_INSTALLATION_ID/repositories/$repo_id call is fully swallowed (--silent ... >/dev/null 2>&1 || true), and the existing /repos/$repo/installation call is ignored as well—so a stale/unauthorized RENOVATE_INSTALLATION_ID can result in zero enrollments with no signal. Handle only 404 as “absent”, warn/fail on other statuses, and log/report PUT failures.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/enforce-repo-settings.yml around lines 220 - 227, The
probe currently suppresses all gh api failures and treats any non-OK result as
“no config”; change the probe so gh api "repos/$repo/contents/$cfg" is invoked
in a way that captures the HTTP status (e.g., include response headers or status
output) and only treat 404 as “absent” (set has_renovate_config=false); for any
other non-200/404 status log a warning/error with repository and status and
stop/raise so enrollment isn’t silently skipped. Likewise, stop swallowing the
PUT enrollment call to
/user/installations/$RENOVATE_INSTALLATION_ID/repositories/$repo_id (remove
>/dev/null 2>&1 || true and --silent) and surface its exit code and response
body; if the PUT fails log the repo, installation id, status and response and
fail or return a non-zero exit to surface bad RENOVATE_INSTALLATION_ID or auth
errors. Ensure the probe also checks and logs the result of
/repos/$repo/installation rather than ignoring it.

@ANcpLua ANcpLua merged commit 4563c6d into main May 25, 2026
9 of 11 checks passed
@ANcpLua ANcpLua deleted the ensure-renovate-scope-in-sweep branch May 25, 2026 04:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant