Skip to content

fix(upstream-docs): the watcher cannot push to a protected main - #67

Merged
HuggeK merged 2 commits into
mainfrom
fix-upstream-docs-watch
Aug 3, 2026
Merged

fix(upstream-docs): the watcher cannot push to a protected main#67
HuggeK merged 2 commits into
mainfrom
fix-upstream-docs-watch

Conversation

@HuggeK

@HuggeK HuggeK commented Aug 3, 2026

Copy link
Copy Markdown
Collaborator

The weekly watch-upstream-docs run failed on its first outing. It fetched the watched vendor documents, rewrote upstream-docs-state.json, and pushed the result straight to main:

remote: error: GH006: Protected branch update failed for refs/heads/main.
remote: - 5 of 5 required status checks are expected.
 ! [remote rejected] main -> main (protected branch hook declined)

main is protected, so that push can never land. The baseline could never advance — and it would have failed the same way on the week a watched document actually moved, which is the week the tool exists for.

The baseline now moves the way everything else here moves: as a pull request, on one branch that is force-pushed so the newest proposal replaces the previous one rather than stacking on it. The baseline is a whole snapshot, not a delta, so replacing it is the correct merge.

What this does not fix

A bot's proposal still needs a human to start its checks, and the workflow now says so rather than pretending otherwise.

What was measured, on this pull request

A pull request opened with GITHUB_TOKEN starts no workflow run. workflow_dispatch is the documented exception, so the first version of this branch dispatched validate against the branch head and claimed that satisfied the required checks.

It does not, and this branch is the measurement. Run 30804965042 put six successful check runs on this PR's head commit — drivers, packages, public-boundary, history-secret-scan, signed channel accepts this tree, and dco skipped. The REST API lists all six on the SHA and links the suite to this PR. GraphQL nonetheless reports statusCheckRollup: null, and the PR reads BLOCKED. PR #66, whose run came from a pull_request event, reports five checks and CLEAN.

So a commit whose only check suite came from a dispatch has no rollup for branch protection to read. The dispatch is kept, because without it nothing runs against a bot's proposal and a maintainer has no verdict to read before spending attention on the diff — but the comment and the generated PR body now both say it is not what unblocks the merge. Reopening the proposal is what starts the checks that count.

Making this fully automatic needs a PAT or GitHub App token in a repo secret, which is a decision for whoever owns the repo, not something this branch should quietly assume.

The run also had nothing to say

Zero alerts, and 1 file changed, 31 insertions(+), 31 deletions(-) in a 31-line file.

upstream-docs-state.json was the only file in the repository committed with CRLF — written by a Windows checkout through write_text's text mode, which translates every \n to the platform's line ending. Every Linux run rewrote all 31 lines, so the watcher would have proposed a baseline every single week whether or not a document changed.

The writer now pins LF, the file is normalized, and .gitattributes keeps the whole class shut.

Why .gitattributes covers more than this one file

The same translation would land in index.yaml, devices.yaml and support-status.json — which CI compares with git diff --exit-code after regenerating them — and in the Lua that gets hashed byte for byte into the signed artifacts. Any of those regenerated on a Windows checkout would read as a whole-file change on a tree where nothing moved.

It costs nothing to add: git ls-files --eol reports every other tracked file as already LF in the index, so nothing is renormalized by this.

Verification

  • Run 30804965042 — the full validate suite green on this branch, including the two new tests and the git diff --exit-code comparisons that .gitattributes could have disturbed.
  • python tools/check_upstream_docs.py --check — 2 watched docs, 0 problems.
  • The rewritten workflow step was extracted from the YAML and run against stubbed git/gh in all three states — baseline unchanged, changed with no open proposal, changed with one already open — confirming the heredoc renders the body flush at column 0 and each path exits 0.

Two tests hold the line: what the tool writes, and what is committed. The second is the one that catches this in CI, since on Linux the first passes even against the old code.

The weekly run ended red on its first outing. It fetches the watched
vendor documents, rewrites upstream-docs-state.json, and pushed the
result straight to main -- which is protected, so the push came back
"GH006: Protected branch update failed... 5 of 5 required status checks
are expected". The baseline could never advance, and it would have
failed the same way on the week a document actually moved, which is the
week the tool exists for.

The baseline now moves the way everything else here moves: as a pull
request on one branch, force-pushed so the newest proposal replaces the
previous one rather than stacking on it -- the baseline is a whole
snapshot, not a delta. [skip ci] is gone from the commit message,
because the required checks are now the point. A pull request opened
with GITHUB_TOKEN starts no workflow run, so the step dispatches
validate against the branch head; workflow_dispatch is the documented
exception to that rule, and validate already accepts it.

That run also had nothing to say: zero alerts, and 31 insertions and 31
deletions in a 31-line file. upstream-docs-state.json was the only file
in the repository committed with CRLF, written by a Windows checkout
through write_text's text mode. Every Linux run rewrote all 31 lines,
so the watcher would have proposed a baseline every single week whether
or not a document changed. The writer now pins LF, the file is
normalized, and .gitattributes keeps the whole class shut -- the same
translation would land in index.yaml, devices.yaml and support-status.json,
which CI compares with git diff --exit-code, and in the Lua that gets
hashed into the signed artifacts.

Two tests hold the line: what the tool writes, and what is committed.
The second is the one that catches this in CI, since on Linux the first
passes even against the old code.

Signed-off-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
@HuggeK
HuggeK marked this pull request as ready for review August 3, 2026 09:57
The step claimed the dispatched run made the required checks green. It
does not, and this branch is the measurement: run 30804965042 put six
successful check runs on the head of #67 -- drivers, packages,
public-boundary, history-secret-scan, the channel preflight, and dco
skipped -- and the pull request still reads BLOCKED with an empty
status rollup, while #66, which got its run from a pull_request event,
reads CLEAN with five. A commit whose only check suite came from a
dispatch has no rollup for branch protection to read.

The dispatch is still worth keeping: without it nothing runs against a
bot's proposal at all, and a verdict a maintainer can read before
spending attention on the diff is the whole ask. It just is not the
thing that unblocks the merge, so neither the comment nor the pull
request body says it is. Reopening the proposal is what starts the
checks that count, and the body now says so where the maintainer will
be standing when they need it.

Signed-off-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>

@miravoss26 miravoss26 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what it does: the weekly watch-upstream-docs run can't push its baseline to a protected main, so the baseline now advances as a force-pushed PR on one branch, and generated files are pinned to LF so a Windows-authored baseline stops reading as a whole-file change.

  • correctness: solid. PR-instead-of-push is the right move, and force-pushing a single upstream-docs-baseline branch is fine because the baseline is a whole snapshot, not a delta. newline="\n" in write_state, plus .gitattributes, plus the two LF tests close the CRLF churn at both ends (tool output and committed baseline).
  • security (scopes): the workflow token gains pull-requests: write and actions: write in watch-upstream-docs.yml. both are actually used (open/refresh the PR, then gh workflow run validate.yml), so it's minimal for the new flow. flagging it only because actions: write lets the token dispatch workflows, which is the one line worth a conscious human nod.
  • nit: the PR-body heredoc is de-indented to column 0 so BODY terminates correctly inside the YAML run: block. correct, and an easy one to get wrong.

safe to merge from my read. the only judgement call for a human is granting those two token scopes, and they're correctly scoped to what the step needs.

@HuggeK
HuggeK merged commit e9f2645 into main Aug 3, 2026
11 of 12 checks passed
@HuggeK
HuggeK deleted the fix-upstream-docs-watch branch August 3, 2026 10:36
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.

3 participants