chore: ensure fleet repos with renovate.json are in Renovate App scope#14
Conversation
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>
|
@coderabbitai autofix |
📝 WalkthroughSummary by CodeRabbit
WalkthroughA new ChangesRenovate App Scope Enforcement
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 2✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
✨ Simplify code
Comment |
|
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. |
Up to standards ✅🟢 Issues
|
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
🟡 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.json5before 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>
|
@coderabbitai autofix |
|
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. |
There was a problem hiding this comment.
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 winFail 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 ifRENOVATE_INSTALLATION_ID=79212889(orGH_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
📒 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
| 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 |
There was a problem hiding this comment.
🧩 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:
- 1: https://github.com/cli/cli/blob/trunk/pkg/cmd/api/api.go
- 2: https://cli.github.com/manual/gh_api
🌐 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:
- 1: unexpected end of JSON input when sending a HEAD request with gh api cli/cli#9927
- 2: https://cli.github.com/manual/gh_api
- 3:
gh apireturn 404 Not Found when adding sub_issues cli/cli#12258 - 4:
gh release createworks for some commit hashes but gets weird 404 for others cli/cli#9773
🏁 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:
- 1: https://docs.github.com/en/rest/using-the-rest-api/troubleshooting-the-rest-api?algolia-query=third%2Bparty&apiVersion=2022-11-28
- 2: gh repo sync not working cli/cli#7574
- 3: unexpected end of JSON input when sending a HEAD request with gh api cli/cli#9927
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.
Why
Surfaced 2026-05-25 while investigating why Renovate had never opened a PR on
ANcpLua.OpenTelemetry.SemanticConventions.Analyzersdespite a validrenovate.jsonextendinggithub>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-userjob:targets.txtproduced by the existing target-resolution step.renovate.json(skips opt-outs).PUT /user/installations/79212889/repositories/<id>.::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/installationsreturns 403 withrepo-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_USERalready needsreposcope for the file-sync steps. The Add-repository-to-installation endpoint accepts the samereposcope, 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