Skip to content

fix(ci): make Scorecard skip cleanly on private forks - #69

Merged
JartanFTW merged 2 commits into
mainfrom
feature/scorecard-private-safe
Jul 16, 2026
Merged

fix(ci): make Scorecard skip cleanly on private forks#69
JartanFTW merged 2 commits into
mainfrom
feature/scorecard-private-safe

Conversation

@JartanFTW

Copy link
Copy Markdown
Contributor

Summary

Makes scorecard.yml skip cleanly on a private fork instead of failing its publish/SARIF upload (both require a public repo). Extracted from the closed CodeQL PR #68 — this is the one piece that stands on its own, independent of any CodeQL decision.

Change

A small visibility guard job resolves repo visibility via gh api repos/$GITHUB_REPOSITORY --jq .visibility (the API works on every trigger; github.event.repository is absent on schedule runs) and exposes it as an output. The analysis job gates on needs.visibility.outputs.public == 'true':

  • public repo → runs normally.
  • private forkanalysis is skipped (green) — no failed upload, no manual deletion needed.

Also drops the now-stale "delete the workflow if the repo is private" guidance (workflow header + README checklist) since deletion is no longer required.

Verification

  • actionlint (incl. shellcheck on the guard run:), zizmor, check-yaml, and markdownlint all pass.
  • Fail-safe: a failing gh api degrades to public=false under the if-test idiom, so visibility stays green and analysis skips (confirmed in adversarial review).
  • No conflict with GitHub's default CodeQL setup — Scorecard's SARIF is a separate analysis category.

🤖 Generated with Claude Code

Scorecard's publish + SARIF upload require a public repo; on a private fork
they fail. Add a `visibility` guard job (resolves repo visibility via the API,
which works on every trigger unlike the schedule event payload) and gate the
analysis job on it, so a private fork skips cleanly instead of going red.
Drops the stale "set false / delete on a private fork" guidance and updates
the README checklist to match.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@JartanFTW
JartanFTW force-pushed the feature/scorecard-private-safe branch from 318d4f1 to 2f1ad1b Compare July 16, 2026 04:21
@JartanFTW

Copy link
Copy Markdown
Contributor Author

Code Review

Files reviewed: 2 files across CI (.github/workflows/scorecard.yml) and docs (README.md)

Overall a clean, correct fix. The visibility gate resolves repo visibility via the API — the only approach that works across all three triggers, since schedule runs carry no github.event.repository payload — the wiring $GITHUB_OUTPUToutputs.publicneeds.visibility.outputs.public == 'true' is right, and the fail-safe was confirmed empirically: a failing gh api inside the if [ "$(...)" = "public" ] condition suppresses errexit, yields empty output, and degrades to public=false (green, analysis skipped). The README and comment cleanup is accurate. All findings below are Minor.

Critical Issues

None found.

Important Issues

None found.

Minor Issues

  1. Redundant inline comment restates the gate.github/workflows/scorecard.yml:58. # Publishes results + badge to OpenSSF (public only; the visibility gate skips private forks). repeats what the job-level comment at lines 17-19 and the if: gate already establish. Trim to # Publishes results + badge to OpenSSF. — the load-bearing "why" lives on the visibility job.

  2. contents: read on the visibility job is superfluous.github/workflows/scorecard.yml:24-25. gh api repos/$GITHUB_REPOSITORY --jq .visibility only reads repo metadata, and the metadata: read scope is always granted to GITHUB_TOKEN even under a restrictive permissions: block (per GitHub docs, metadata is the one scope always kept at read). So contents: read grants more than the call needs, and the comment # gh reads repo metadata actually describes the metadata scope, not contents. Harmless, but for a template that models least privilege you can tighten to permissions: {}.

  3. Transient gh api failure silently skips Scorecard on a public repo.github/workflows/scorecard.yml:33. The fail-safe correctly keeps the job green, but on a genuinely public repo a transient API blip produces the same public=false as a real private fork, so Scorecard silently doesn't run that invocation with nothing in the log to distinguish the two cases. Capture the value and emit a ::warning:: on the non-public branch:

    visibility="$(gh api "repos/$GITHUB_REPOSITORY" --jq '.visibility' 2>&1)" || true
    if [ "$visibility" = "public" ]; then
      echo "public=true" >> "$GITHUB_OUTPUT"
    else
      echo "::warning::visibility='$visibility' (non-public or API failure) — Scorecard analysis skipped"
      echo "public=false" >> "$GITHUB_OUTPUT"
    fi

    Same green/skip behavior, but a public-repo API blip is now visible in the log.

Suspected

  • Is "Private repo with GitHub Advanced Security? Drop the gate." (line 19) complete guidance? GHAS lets Scorecard run on a private repo (code scanning / SARIF upload), but it does not make the repo public to OpenSSF — and publishing results + badge is a public-only path (the very premise this PR relies on to skip private forks). So dropping the gate while leaving publish_results: true sends the publish step at a public-only endpoint on a private repo; whether the action errors there or skips silently is unconfirmed. Worth verifying against a live private-GHAS run; if it errors, the note should read "Drop the gate and set publish_results: false." Edge case on the deliberate opt-out path — does not block.

Summary

Approve. The core fix is correct and well-commented, and the fail-safe genuinely fails green across all triggers. Every graded finding is Minor: Finding 1 and Finding 2 are comment/permission nits, and Finding 3 is an observability nicety. The Suspected item touches only the deliberate private-GHAS opt-out path and deserves a one-line verification before that guidance is relied on. Nice work.


Generated by Claude

— reply with /praxis:address-review 69 to work through findings

- trim redundant inline comment on publish_results
- tighten visibility job to permissions: {} (metadata:read is always granted)
- case-split visibility check: silent skip on private/internal, warn only on
  unresolved/API-failure so a transient public-repo blip is visible in the log
- note publish_results: false alongside "drop the gate" GHAS guidance

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@JartanFTW
JartanFTW merged commit 0466758 into main Jul 16, 2026
12 checks passed
@JartanFTW
JartanFTW deleted the feature/scorecard-private-safe branch July 16, 2026 18:34
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