Commit 35c7297
authored
Fix link check on pushes that have no base commit (#21644)
## Problem
`.github/workflows/_link_check.yml` decides which commits to diff by
inspecting the event that triggered its *caller*. That is the wrong
place to make the decision. A reusable workflow is called from more than
one context, and the value it reaches for is not present in all of them.
For a pull request it uses the pull request base. For everything else it
uses `github.event.before`, which is only meaningful for a push to a
branch that already existed.
| trigger | `github.event.before` | before this change |
| --- | --- | --- |
| pull request | not used, PR base instead | changed lines checked |
| push to an existing branch | previous branch tip | changed lines
checked |
| push creating a tag | all zeros | job fails, exit 128 |
| push creating a branch | all zeros | job fails, exit 128 |
| scheduled run | absent | whole tree checked, by accident |
| manual dispatch | absent | whole tree checked, by accident |
The all-zero rows fail outright, which is what every `ciflow/nightly/*`
tag push hits:
```
git fetch --no-tags --depth=1 origin 0000000000000000000000000000000000000000
fatal: remote error: upload-pack: not our ref 0000000000000000000000000000000000000000
##[error]Process completed with exit code 128
```
The two "by accident" rows are worth spelling out, because the behavior
there is correct but nothing makes it so. With `before` absent, the
fetch becomes `git fetch --no-tags --depth=1 origin ""`, which quietly
succeeds by fetching the default head. Then in
```yaml
./scripts/lint_urls.sh $(
...
echo "${{ github.event.before }}" "${{ github.sha }}"
)
```
the unquoted command substitution word splits the empty first field
away, so the script gets one argument rather than two, fails its `[ $#
-eq 2 ]` test, and falls through to whole tree mode. The right thing
happens for the wrong reason, and only for as long as that substitution
stays unquoted.
## Fix
Take the base as an input instead:
```yaml
base_ref:
description: Commit to diff against. Empty, or all zeros, means check the whole tree.
type: string
required: false
default: ''
```
Empty means there is nothing to diff against. The three lint scripts
already support that: given two arguments they diff a range, given none
they scan the whole tree. So the empty case needs no fallback logic, it
just calls them with no arguments. The all-zero SHA maps to empty,
because a caller forwarding `github.event.before` has no way to avoid
producing it.
With the base known up front, the separate `Fetch base ref` step has
nothing left to decide, so it folds into the lint step. Each job loses a
step and the file gets shorter, 107 lines to 100:
```yaml
args=()
# A push creating a tag or branch reports an all zero SHA no remote can serve.
if [ -n "$BASE_REF" ] && [ "$BASE_REF" != "0000000000000000000000000000000000000000" ]; then
git fetch --no-tags --depth=1 origin "$BASE_REF"
args=("$BASE_REF" "$HEAD_REF")
fi
./scripts/lint_urls.sh "${args[@]}" || {
```
`lint.yml` passes the pull request base on pull requests and
`github.event.before` otherwise, one added line.
The job level `if:` conditions still read `github.event_name` and the
pull request labels. Those decide whether a job runs at all, which is a
different question and out of scope here. What changes is how the diff
base is computed.
One incidental correctness gain: the old steps checked out `inputs.ref`
but diffed against `github.sha` or `head.sha` read from the caller's
event. Now the checked out commit and the diff head are the same input,
so they cannot drift apart. That mismatch was not hypothetical, see
below.
## What `nightly.yml` stops running, and why that is also a fix
`nightly.yml` now runs the check only on the schedule and on a manual
dispatch. That drops two triggers: pull requests touching
`.github/workflows/nightly.yml`, and `ciflow/nightly/*` tag pushes.
**The pull request trigger was not providing coverage.** `nightly.yml`
passes `ref: ${{ github.sha }}`, which on a pull request is the merge
commit. The head commit is therefore never checked out and never
fetched, so `git diff base..head` cannot resolve:
```
Checking changed files between bcceab1..7e66c47
fatal: Invalid revision range bcceab1..7e66c47
```
`lint_urls.sh` and `lint_xrefs.sh` consume that diff through a process
substitution, so the failure is swallowed and they report success having
checked zero URLs. `lint_file_size.sh` reads it into a plain assignment,
which `set -e` turns into a hard failure, and there is no
`skip-file-size-lint` label to escape it. Run 31125514863 shows all
three: two green jobs that examined nothing, and one red one.
So this removes two no-ops and one permanently red, unskippable check.
Pull requests keep the real check from `lint.yml`, against their own
base, on the path where the head commit is actually checked out.
**The tag push trigger is a policy call, not a bug fix.** To be precise
about causation: the exit 128 there is removed by the zero-SHA guard
plus the empty default, not by the `if:`. Left alone, a
`ciflow/nightly/*` push would now run a clean whole tree scan. It is
skipped because there were 19 such tag pushes in the last day alone, a
whole tree URL scan takes about six minutes, and it would be red on the
pre-existing dead links every time.
**One narrow gap remains.** A pull request targeting the `nightly`
branch gets no link check at all, since `lint.yml` excludes that branch
through `branches-ignore`. Before this change it got the two no-ops and
the red file-size job, so nothing that worked is lost. Happy to add a
correct `pull_request` arm to `nightly.yml` if reviewers would rather
close it.
## Result
| trigger | after this change |
| --- | --- |
| pull request, via `lint.yml` | changed lines only, unchanged |
| push to `main` or an existing `release/*` | changed lines only,
unchanged |
| push creating a `release/*` branch | whole tree, instead of exit 128 |
| push of a `ciflow/nightly/*` tag | skipped, instead of exit 128 |
| pull request touching `nightly.yml` | skipped, was two no-ops and one
red job |
| scheduled nightly | whole tree, unchanged, now by design |
| manual dispatch, either workflow | whole tree, unchanged |
`lint.yml` declares no `tags:` in its push trigger, so the tag case only
ever reached `_link_check.yml` through `nightly.yml`.
## Testing
- All three workflow files parse, and every `run` block in
`_link_check.yml` is clean under `shellcheck -S style`.
- Ran the rewritten lint step as a standalone script for every shape of
input, across all three jobs: a real base SHA fetches and passes two
arguments; an all-zero SHA and an empty value skip the fetch and pass
none; an unservable base exits 128 with git's own message and never
reaches the lint script. Repeated with and without `set -u`, and with
stray positional parameters already set, so the argument list cannot
depend on how the runner invokes the step.
- Ran `scripts/lint_file_size.sh` with no arguments to confirm the whole
tree path works end to end: 9033 files checked, no failures.
- The whole tree paths of `lint_urls.sh` and `lint_xrefs.sh` use `git
grep -P`, which needs a git built with PCRE support that I did not have
locally, so I did not run those myself. They are already exercised in CI
though, by the current scheduled nightly, which reaches whole tree mode
through the accident described above. Its most recent `lint-urls` log
checks about 2148 URLs and reports 2126 OK, 15 WARN, 7 FAIL. Those 7 are
genuine dead links, unrelated to this change, and it neither fixes nor
hides them, so the scheduled job stays red until they are updated.
- On this pull request, `Lint` runs the changed workflows against
themselves and all three link check jobs pass with `BASE_REF` and
`HEAD_REF` taken from the inputs. The `nightly` run on the
`ciflow/nightly` tag, the case that used to exit 128, is skipped.
## Not addressed here
`lint_urls.sh` and `lint_xrefs.sh` end their input pipeline with `||
true`, needed because `git grep` exits 1 when it finds nothing. It also
means that if `git grep` fails outright the lint reports zero findings
and passes. That is the same swallowing described above, it is
pre-existing, and it deserves its own change.1 parent 793b4d6 commit 35c7297
3 files changed
Lines changed: 38 additions & 42 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
7 | 12 | | |
8 | 13 | | |
9 | 14 | | |
| |||
16 | 21 | | |
17 | 22 | | |
18 | 23 | | |
19 | | - | |
20 | | - | |
21 | | - | |
22 | | - | |
23 | | - | |
24 | | - | |
25 | | - | |
26 | 24 | | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
27 | 28 | | |
28 | | - | |
29 | | - | |
30 | | - | |
31 | | - | |
32 | | - | |
33 | | - | |
34 | | - | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
35 | 36 | | |
36 | 37 | | |
37 | 38 | | |
| |||
49 | 50 | | |
50 | 51 | | |
51 | 52 | | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | 53 | | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
60 | 57 | | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
68 | 65 | | |
69 | 66 | | |
70 | 67 | | |
| |||
82 | 79 | | |
83 | 80 | | |
84 | 81 | | |
85 | | - | |
86 | | - | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
92 | 82 | | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
93 | 86 | | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
94 | 93 | | |
95 | | - | |
96 | | - | |
97 | | - | |
98 | | - | |
99 | | - | |
100 | | - | |
101 | | - | |
| 94 | + | |
102 | 95 | | |
103 | 96 | | |
104 | 97 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
125 | 125 | | |
126 | 126 | | |
127 | 127 | | |
| 128 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
| 35 | + | |
| 36 | + | |
35 | 37 | | |
36 | 38 | | |
37 | 39 | | |
| |||
0 commit comments