Skip to content

fix(ci): drop actions/checkout from renovate workflow#5

Merged
njhensley merged 1 commit into
mainfrom
ci/renovate-drop-checkout
May 4, 2026
Merged

fix(ci): drop actions/checkout from renovate workflow#5
njhensley merged 1 commit into
mainfrom
ci/renovate-drop-checkout

Conversation

@njhensley

Copy link
Copy Markdown
Owner

Summary

Removes the actions/checkout step from .github/workflows/renovate.yaml. The renovatebot/github-action clones the repo itself inside Docker and doesn't need an outer working tree.

Motivation / Context

Diagnosed during the fork practice exercise: every Renovate run since merging PR #3 would successfully create the first eligible branch (renovate/build-tools, after the cooldown filter) and then abort 0.3s later with Repository has changed during renovation — even though main did not advance during the run window (verified via the GitHub commits API).

Pattern from logs (run 25345091057):

21:43:23.04  INFO: Branch created (branch=renovate/build-tools, commitSha=...)
21:43:23.38  INFO: Repository has changed during renovation - aborting

The actions/checkout step plants a working tree + credentials at /home/runner/work/aicr/aicr. Renovate's container-internal git operations only mount /tmp:/tmp, but the action's host-side post-step does its own git config reads/writes that interfere with Renovate's view of refs. The interaction was clean in NVIDIA/gpu-operator's workflow because that one passes configurationFile: — which we deliberately don't (it causes customManager doubling, see PR #3). Without that input, our setup uses the action's default internal clone, and the outer checkout becomes redundant + harmful.

Fixes: silent abort after first branch creation
Related: #1, #3, #4

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • Build/CI/tooling

Component(s) Affected

  • Other: CI / dependency management

Implementation Notes

  • One step removed.
  • Comment block above steps: documents why future drive-bys shouldn't put checkout back.
  • No other changes; renovate-version digest pin and all env wiring preserved.

Testing

actionlint .github/workflows/renovate.yaml   # clean

After merge:

  1. Delete the orphan renovate/build-tools branch (it was created by the aborting runs but has no PR).
  2. Dispatch Renovate. Expected: branches created AND PRs opened.

Risk Assessment

  • Low — Removes a redundant step that wasn't doing useful work. Worst case: action fails to clone, in which case we put the step back.

Checklist

  • Linter passes (actionlint)
  • I did not skip/disable tests to make CI green
  • Changes follow existing patterns in the codebase
  • Commits are cryptographically signed (git commit -S)

The `actions/checkout` step was modeled on NVIDIA/gpu-operator's
renovate workflow, but our setup deliberately does not pass
`configurationFile:` (to avoid customManager doubling — see PR #3).
Without that input, the renovatebot/github-action clones the repo
itself inside Docker and has no need for an outer working tree.

Observed failure: every Renovate run since the practice exercise
started would create the first eligible branch (e.g.
`renovate/build-tools`) and then abort 0.3s later with
"Repository has changed during renovation" — even though no commit
to main happened during the run window. The credentials and working
tree planted at /home/runner/work by `actions/checkout` are the
suspect: Renovate's container-internal git operations and the
runner-side git state cross-contaminate, and Renovate misinterprets
its own branch push as a base-branch advance.

Removing the step removes the cross-contamination surface. The action
documents that checkout is not required when using its default
internal-clone mode.
@njhensley njhensley merged commit b0bbd19 into main May 4, 2026
21 checks passed
njhensley added a commit that referenced this pull request May 4, 2026
…kflow (#6)

Reverts toward the known-working NVIDIA/gpu-operator pattern after PRs
#5 (drop checkout) and earlier failed to clear the persistent
"Repository has changed during renovation" abort. Three concrete
changes:

1. **Add back actions/checkout.** The action documents that it clones
   internally, but the host-side git state planted by checkout
   appears to keep Renovate's pre/post hooks consistent. Removing it
   in PR #5 did not fix the abort, suggesting checkout was never the
   culprit.

2. **Pass configurationFile: .github/renovate.json5.** The earlier
   concern about customManager doubling came from
   `RENOVATE_PLATFORM=local` dry-runs; in production (platform=github)
   Renovate dedupes by manager identity, so the same-file case is
   benign. Without configurationFile, the action runs Renovate without
   a global config — and that mode is associated with mid-run race
   conditions that surface as "repository-changed" aborts right after
   a successful branch push.

3. **Set RENOVATE_DRY_RUN conditionally.** Previously the env var was
   emitted on every run as `''` (empty string when dryRun=false).
   Renovate's option parser may treat the *presence* of the env var
   differently from its unset state. The new "Configure dry-run mode"
   step writes the var to GITHUB_ENV only when explicitly requested,
   keeping the env clean for the common case.

Verified end-to-end via actionlint. Soft-launch loop continues — once
this lands, dispatch Renovate manually and check whether
`renovate/build-tools` (the only bundle past the cooldown filter)
finally produces a PR alongside its branch.
njhensley added a commit that referenced this pull request May 4, 2026
#7)

Root cause of the persistent "Repository has changed during renovation"
aborts since PR #4 — Renovate was calling POST /repos/{owner}/{repo}/
statuses/{sha} after each branch creation to write a stability status
check (tied to the cooldown / merge-confidence flow). The workflow's
permissions block had contents:write, pull-requests:write, issues:write
but not statuses:write, so the call 403'd with "integration-
unauthorized". Renovate's error handler maps that internally to
"repository-changed", which is why every prior debugging attempt
(checkout, configurationFile, dry-run env, digest pin) chased the
wrong symptom.

Confirmed via debug log on run 25346172853:
  Request failed with status code 403 (Forbidden):
  POST .../statuses/cf3dc7d1...
  "x-accepted-github-permissions": "statuses=write"
  DEBUG: Caught error setting branch status - aborting
  Error: integration-unauthorized
  DEBUG: Passing repository-changed error up

The earlier hypothesis fixes in PRs #5 and #6 weren't wrong per se
(actions/checkout and configurationFile aren't the cause, and
RENOVATE_DRY_RUN='' isn't either) — they just couldn't have fixed the
underlying permission gap. With statuses:write added, Renovate's
post-branch status-check call should succeed and the run should
complete normally.
njhensley added a commit that referenced this pull request May 13, 2026
Audit findings #5 and #6: the pointer file was duplicating fingerprint,
criteriaMatch, phaseSummary, and attestedAt — nearly a copy of the
predicate, with no Go consumer reading the denormalized fields. Two
sources of truth with no good answer for which to trust on mismatch.

A pointer's job is to *locate* the signed bundle, not to summarize it.
A reviewer who wants fingerprint dimensions or per-phase pass/fail
counts fetches the bundle from PointerBundle.OCI and reads
predicate.json — that's the authoritative copy and the only one.

  Before                                 After
  ──────                                 ─────
  schemaVersion                          schemaVersion
  recipe                                 recipe
  attestations[]:                        attestations[]:
    bundle                                 bundle
    signer                                 signer       (omitempty)
    attestedAt                             attestedAt
    fingerprint        ──── DROPPED
    criteriaMatch      ──── DROPPED
    phaseSummary       ──── DROPPED  (also resolves audit #6:
                                      pointer phaseSummary block
                                      omitted .skipped despite
                                      predicate carrying it)
    logsBundle         ──── DROPPED  (feature deferred in 0277fec;
                                      will return as omitempty
                                      when log capture lands)

Wire changes:

  - PointerAttestation: 7 fields → 3.

  - PointerFingerprint, PointerCriteriaMatch, PointerPhaseStat,
    PointerLogsBundle types deleted along with their build helpers
    (pointerFingerprintFrom, pointerPhaseSummaryFrom).

  - PointerInputs.LogsBundle dropped — no caller ever populated it.

  - Schema version stays at 1.0.0; no committed pointer files exist
    yet and the design doc's '2.0.0 reserved for multi-instance'
    plan is preserved.

Test coverage:

  - TestBuildPointer_OmitsDenormalizedFields: asserts the rendered
    YAML has no fingerprint:, criteriaMatch:, phaseSummary:, or
    logsBundle: keys. Catches regressions where a future commit adds
    a field back without updating the design rationale.

  - Existing fingerprint round-trip and logsBundle round-trip tests
    deleted (they asserted denormalization that no longer happens).

Design doc (docs/design/007-recipe-evidence.md):

  - Pointer schema example shrunk to match.

  - Added 'The pointer is a locator, not a denormalized cache'
    paragraph documenting the rationale.

  - Verifier step 7 updated to read criteriaMatch from the predicate
    rather than the pointer.

  - Verifier step 11 (logs bundle verification) marked deferred
    pending log capture; spelled out that V1 pointers omit the field
    entirely so verifiers don't error on absence.
njhensley added a commit that referenced this pull request May 13, 2026
Audit findings #5 and #6: the pointer file was duplicating fingerprint,
criteriaMatch, phaseSummary, and attestedAt — nearly a copy of the
predicate, with no Go consumer reading the denormalized fields. Two
sources of truth with no good answer for which to trust on mismatch.

A pointer's job is to *locate* the signed bundle, not to summarize it.
A reviewer who wants fingerprint dimensions or per-phase pass/fail
counts fetches the bundle from PointerBundle.OCI and reads
predicate.json — that's the authoritative copy and the only one.

  Before                                 After
  ──────                                 ─────
  schemaVersion                          schemaVersion
  recipe                                 recipe
  attestations[]:                        attestations[]:
    bundle                                 bundle
    signer                                 signer       (omitempty)
    attestedAt                             attestedAt
    fingerprint        ──── DROPPED
    criteriaMatch      ──── DROPPED
    phaseSummary       ──── DROPPED  (also resolves audit #6:
                                      pointer phaseSummary block
                                      omitted .skipped despite
                                      predicate carrying it)
    logsBundle         ──── DROPPED  (feature deferred in 0277fec;
                                      will return as omitempty
                                      when log capture lands)

Wire changes:

  - PointerAttestation: 7 fields → 3.

  - PointerFingerprint, PointerCriteriaMatch, PointerPhaseStat,
    PointerLogsBundle types deleted along with their build helpers
    (pointerFingerprintFrom, pointerPhaseSummaryFrom).

  - PointerInputs.LogsBundle dropped — no caller ever populated it.

  - Schema version stays at 1.0.0; no committed pointer files exist
    yet and the design doc's '2.0.0 reserved for multi-instance'
    plan is preserved.

Test coverage:

  - TestBuildPointer_OmitsDenormalizedFields: asserts the rendered
    YAML has no fingerprint:, criteriaMatch:, phaseSummary:, or
    logsBundle: keys. Catches regressions where a future commit adds
    a field back without updating the design rationale.

  - Existing fingerprint round-trip and logsBundle round-trip tests
    deleted (they asserted denormalization that no longer happens).

Design doc (docs/design/007-recipe-evidence.md):

  - Pointer schema example shrunk to match.

  - Added 'The pointer is a locator, not a denormalized cache'
    paragraph documenting the rationale.

  - Verifier step 7 updated to read criteriaMatch from the predicate
    rather than the pointer.

  - Verifier step 11 (logs bundle verification) marked deferred
    pending log capture; spelled out that V1 pointers omit the field
    entirely so verifiers don't error on absence.
@njhensley njhensley deleted the ci/renovate-drop-checkout branch June 23, 2026 16:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant