Skip to content

fix(repo-settings): stop reporting unreadable settings as drift - #3

Merged
KaiSchwarz-cnic merged 1 commit into
mainfrom
RSRMID-2989/repo-settings-unreadable-guard
Aug 24, 2026
Merged

fix(repo-settings): stop reporting unreadable settings as drift#3
KaiSchwarz-cnic merged 1 commit into
mainfrom
RSRMID-2989/repo-settings-unreadable-guard

Conversation

@KaiSchwarz-cnic

Copy link
Copy Markdown
Contributor

Jira: RSRMID-2989

Two defects in scripts/repo-settings.sh, both found when rtldev-middleware-php-sdk adopted this tooling under RSRMID-2988.

1. Unreadable settings were reported as drift

compare() already distinguishes "cannot read with this token" from real drift — it checks for a value of exactly unknown and counts those separately, so a permissions gap doesn't masquerade as a settings problem.

Three probes defeated that guard. They were written:

x=$(gh api … 2>/dev/null || echo unknown)

On a 403, gh api exits non-zero and writes the error JSON to stdout. So the substitution captures that JSON with unknown appended — which never equals unknown, so the guard misses and the row falls through to the drift branch, reporting a JSON blob as its observed value.

The weekly job therefore failed for a reason that wasn't true: the exact inverse of the green-by-omission failure the unreadable count exists to prevent.

Proven against a live 403:

OLD idiom -> [{"message":"Resource not accessible by personal access token...]
  equals "unknown"? NO   ← falls through to DRIFT
NEW idiom -> [unknown]
  equals "unknown"? YES  ← counted as unreadable

Fixed to x=$(gh api …) || x=unknown, so the fallback replaces the captured output rather than being appended to it.

All three affected: topics (187), secrets (319), variables (320). Any repo whose token lacks administration:read hits it — which, given the drift workflow's own comment recommends running on GITHUB_TOKEN by default, is the common case rather than the edge case.

2. SC2015 in the ruleset apply path

A && B || C, so a failure of the success-message branch would also print the failure message. Rewritten as if/else.

This repo's own quality.yml runs shellcheck --severity=warning, where SC2015 is info-level and invisible. It surfaced only when a consuming repository linting at default severity adopted the file — so the template was shipping a file its own gate couldn't check.

Verification

  • shellcheck clean at both default and --severity=warning
  • bash -n clean
  • prettier --check . clean
  • The guard fix verified behaviourally against a real 403, not by reading the code

Note

The SDK's copy is being fixed in the same issue (its PR) so the two don't diverge — it had two of the three probes fixed at adoption time, and the topics one was missed.

compare() already distinguishes "cannot read with this token" from real drift —
it checks for a value of exactly `unknown` and counts those separately, so a
permissions gap does not masquerade as a settings problem. Three probes defeated
that guard.

They were written `x=$(gh api … 2>/dev/null || echo unknown)`. On a 403 `gh api`
exits non-zero *and* writes the error JSON to stdout, so the substitution
captured that JSON with `unknown` appended. The result never equals `unknown`,
the guard missed, and the row was reported as drift with a JSON blob as its
observed value.

So the weekly job failed for a reason that was not true — the inverse of the
green-by-omission failure the unreadable count exists to prevent. `topics` (187),
`secrets` (319) and `variables` (320) were all affected; anything checked with a
token lacking administration:read hit it.

Rewritten as `x=$(gh api …) || x=unknown`, so the fallback replaces the captured
output instead of being appended to it. Verified against a real 403: the old
idiom yields `{"message":"Resource not accessible…` and the new one yields
`unknown`, which compare() then counts as unreadable.

Also fixed SC2015 in the ruleset apply path: `A && B || C` meant a failure of the
success-message branch would also print the failure message. Rewritten as
if/else. This repository's own quality job runs shellcheck at
`--severity=warning`, where SC2015 is info-level and invisible, so it surfaced
only when a consuming repository linting at default severity adopted the file.

RSRMID-2989
@KaiSchwarz-cnic
KaiSchwarz-cnic requested a review from a team as a code owner August 24, 2026 14:32
@KaiSchwarz-cnic
KaiSchwarz-cnic merged commit 52e215c into main Aug 24, 2026
4 checks passed
@KaiSchwarz-cnic
KaiSchwarz-cnic deleted the RSRMID-2989/repo-settings-unreadable-guard branch August 24, 2026 14:33
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