From aacf361bb8044ce481e22750b7d89df8a7b6ba11 Mon Sep 17 00:00:00 2001 From: Keith Jackson Date: Thu, 29 Oct 2026 20:58:15 -0500 Subject: [PATCH 1/2] fix(renovate): the workflow still named renovate.json, so every run since #79 died with ENOENT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #79 renamed the config to renovate.json5 and left configurationFile pointing at the old name. Runs 315-322 (2026-10-22..29) all failed in ~1s before Renovate started. Also add a ci/lint.sh assertion that every workflow configurationFile: resolves to a real path — valid YAML, so yamllint could not catch it, and the gate stayed green throughout. Co-Authored-By: Claude Opus 5.5 --- .gitea/workflows/renovate.yaml | 6 ++- ci/lint.sh | 15 ++++++ .../2026-10-29-renovate-config-filename.md | 48 +++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 decisions/2026-10-29-renovate-config-filename.md diff --git a/.gitea/workflows/renovate.yaml b/.gitea/workflows/renovate.yaml index 91798e6..3fbb161 100644 --- a/.gitea/workflows/renovate.yaml +++ b/.gitea/workflows/renovate.yaml @@ -152,7 +152,11 @@ jobs: # Pinned, and Renovate maintains this pin itself: renovate.json's enabledManagers # includes `github-actions`, and `helpers:pinGitHubActionDigests` is in its `extends`. # This is the one dependency in the repo whose updater is its own updater. - configurationFile: renovate.json + # MUST match the file's real name. #79 renamed it to renovate.json5 and left this line + # saying `renovate.json`; every scheduled run from 2026-10-18 then died in ~1s with + # ENOENT before Renovate started, and nothing in the gate noticed. ci/lint.sh now + # asserts that every `configurationFile:` here names a file that exists. + configurationFile: renovate.json5 token: ${{ secrets.RENOVATE_TOKEN }} env: # Self-hosted Gitea. `platform` + `endpoint` were the two values renovate.json's own diff --git a/ci/lint.sh b/ci/lint.sh index be703aa..e5307fe 100755 --- a/ci/lint.sh +++ b/ci/lint.sh @@ -27,3 +27,18 @@ fi yamllint -c "${CI_DIR}/yamllint.yaml" -f "${YAMLLINT_FORMAT:-standard}" "${files[@]}" ok "yaml/lint passed (${#files[@]} files)" + +# A workflow's `configurationFile:` must name a file that exists. Renovate's did not after #79 +# renamed renovate.json -> renovate.json5, and every scheduled run failed with ENOENT for 11 days +# while the gate stayed green. Valid YAML, so yamllint cannot see it. +missing=0 +while IFS=: read -r wf _ value; do + value="${value#*configurationFile:}" + value="$(echo "$value" | sed -E 's/#.*//; s/^[[:space:]"'\'']+//; s/[[:space:]"'\'']+$//')" + if [ -n "$value" ] && [ ! -e "$value" ]; then + err "${wf}: configurationFile '${value}' does not exist in the repo" + missing=1 + fi +done < <(grep -nE '^[[:space:]]*configurationFile:' .gitea/workflows/*.y*ml 2>/dev/null || true) +[ "$missing" -eq 0 ] || exit 1 +ok "workflow configurationFile references resolve" diff --git a/decisions/2026-10-29-renovate-config-filename.md b/decisions/2026-10-29-renovate-config-filename.md new file mode 100644 index 0000000..bd30d4f --- /dev/null +++ b/decisions/2026-10-29-renovate-config-filename.md @@ -0,0 +1,48 @@ +--- +date: 2026-10-29 +branch: fix/renovate-config-filename +title: Renovate has failed every run since #79 — the workflow still named renovate.json +--- + +## What changed + +**`.gitea/workflows/renovate.yaml`** — `configurationFile: renovate.json` → `renovate.json5`. + +PR #79 (`fix/renovate-invalid-config`) renamed the config to `renovate.json5` and did not update +the one line in the workflow that names it. Measured 2026-10-29 from the Gitea Actions log of run +322 (task 513, schedule, 08:15 UTC): + + Error: ENOENT: no such file or directory, stat '/workspace/Home-Network/home-network-k3s-impl/renovate.json' + +Runs 315–322 (2026-10-22 → 10-29, every one listed) are all `failure` on the `schedule` event, +each failing in about one second. Renovate never started. `RENOVATE_TOKEN` **does** exist as a +repository Actions secret (listed by name via the API; value not read), so the token preflight +passes and this filename is the only blocker observed. + +**`ci/lint.sh`** — a new assertion after yamllint: every non-comment `configurationFile:` key in +`.gitea/workflows/*.y*ml` must name a path that exists. This is valid YAML, so yamllint cannot see +it, and the gate stayed green through 11 days of failures. + +## What I verified + +- The root cause, from the run log quoted above, and that `origin/main` has `renovate.json5` and + no `renovate.json` (`git ls-tree`). +- The new guard, both ways, run standalone: on this branch it prints + `workflow configurationFile references resolve`, rc 0. With the line reverted to `renovate.json` + it prints `configurationFile 'renovate.json' does not exist in the repo`, rc 1. My first version + also matched its own explanatory comment in the workflow and failed. That is why the grep is + anchored to non-comment lines. +- Toolbox gate: result and rc are reported in the PR, not predicted here. + +## What I did NOT verify + +- **Renovate has still never completed a run.** This removes the observed blocker. Others may + follow it. The test is a `workflow_dispatch` with `dry_run: true` after merge, and its log is + the first evidence that Renovate authenticates, extracts dependencies, and honours `.json5`. +- **The `enabledManagers: ["regex"]` vs `custom.regex` question** (#73, #77) is still open. The + same dry-run log answers it: look for the regex/custom manager's dependency-extraction count. +- **No alert for "Renovate keeps failing".** The guard prevents this specific cause from recurring. + Nothing notices a scheduled workflow that is red every day. That gap stays open. +- **Sensitive path.** `.gitea/**` and `ci/**` are both in `approval-policy.json`. CI will block + on `approval-policy` until the operator posts `/approve-override` as a review. The local gate + reports that stage as N/A. From da1b36eb64857f81852e75ec5c833fe32364cc3f Mon Sep 17 00:00:00 2001 From: Keith Jackson Date: Thu, 29 Oct 2026 21:49:38 -0500 Subject: [PATCH 2/2] =?UTF-8?q?fix(renovate):=20pin=20renovate-version=20?= =?UTF-8?q?=E2=80=94=20the=20action's=20default=20(39)=20rejects=20renovat?= =?UTF-8?q?e.json5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- .gitea/workflows/renovate.yaml | 5 +++++ decisions/2026-10-29-renovate-config-filename.md | 9 +++++++++ 2 files changed, 14 insertions(+) diff --git a/.gitea/workflows/renovate.yaml b/.gitea/workflows/renovate.yaml index 3fbb161..c313aac 100644 --- a/.gitea/workflows/renovate.yaml +++ b/.gitea/workflows/renovate.yaml @@ -158,6 +158,11 @@ jobs: # asserts that every `configurationFile:` here names a file that exists. configurationFile: renovate.json5 token: ${{ secrets.RENOVATE_TOKEN }} + # PINNED 2026-10-29. Unset, this action version runs Renovate `39` (action.yml + # `renovate-version` default at v41.0.9), i.e. 39.264.0, and renovate.json5 uses + # `managerFilePatterns` (>= 40.2.0): under 39.x validation fails with five errors and + # the run stops at CONFIG_VALIDATION. #79 validated against `latest`, not this. + renovate-version: 44.167.0 env: # Self-hosted Gitea. `platform` + `endpoint` were the two values renovate.json's own # header said were "set at runtime by the operator's renovate runner" — this is that diff --git a/decisions/2026-10-29-renovate-config-filename.md b/decisions/2026-10-29-renovate-config-filename.md index bd30d4f..cf26844 100644 --- a/decisions/2026-10-29-renovate-config-filename.md +++ b/decisions/2026-10-29-renovate-config-filename.md @@ -23,6 +23,13 @@ passes and this filename is the only blocker observed. `.gitea/workflows/*.y*ml` must name a path that exists. This is valid YAML, so yamllint cannot see it, and the gate stayed green through 11 days of failures. +**Second blocker, same PR:** the workflow did not pin a Renovate version, so +`renovatebot/github-action@v41.0.9` ran its default, Renovate **39** (`action.yml`: +`renovate-version` `default: '39'`, read at that tag). `renovate.json5` uses `managerFilePatterns`, +which needs >= 40.2.0. Under 39.264.0 the validator rejects the config with five errors. This was +found and reproduced by the image-pipeline worker (PR #81). #79 had validated against `latest`. Pinned +`renovate-version: 44.167.0`, the version the config validates clean against per that worker. + ## What I verified - The root cause, from the run log quoted above, and that `origin/main` has `renovate.json5` and @@ -36,6 +43,8 @@ it, and the gate stayed green through 11 days of failures. ## What I did NOT verify +- **The 39.x rejection and the 44.167.0 pass were not reproduced by me.** I confirmed only the + action's default (`'39'`). The validation results are the PR #81 worker's. - **Renovate has still never completed a run.** This removes the observed blocker. Others may follow it. The test is a `workflow_dispatch` with `dry_run: true` after merge, and its log is the first evidence that Renovate authenticates, extracts dependencies, and honours `.json5`.