Skip to content

Lint only what a PR adds: diff the link checks from the merge base - #21765

Merged
psiddh merged 1 commit into
pytorch:mainfrom
psiddh:fix-link-lint-merge-base
Aug 12, 2026
Merged

Lint only what a PR adds: diff the link checks from the merge base#21765
psiddh merged 1 commit into
pytorch:mainfrom
psiddh:fix-link-lint-merge-base

Conversation

@psiddh

@psiddh psiddh commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Summary

The URL, xref, and file-size linters diffed base..head, where base is the tip of the base branch rather than the point the branch left it. A branch cut before recent commits still carries the lines those commits replaced, so against the newer tip its old copies read as additions, and the branch gets blamed for links someone else already repaired.

#21729 failed exactly this way. It touches 18 files and adds no URLs at all, but the two-dot diff scoped the lint to 143 files and flagged nine links that #21694 had already fixed or @lint-ignored. #21707 is starker: one Python file with no URLs in it, 199 files linted, the same nine failures.

base..head    143 files   <- what CI linted
base...head    18 files   <- what the PR actually changes

The stray jq: parse error lines in #21729's log are the same symptom from the other direction: the job runs the branch's own pre-#21694 copy of lint_urls.sh.

Fix

The workflow resolves the merge base and passes it down. That is the half that matters for branches already open: on a pull_request the reusable workflow resolves from the merge commit, so it carries this fix even though scripts/ still comes from the branch itself. The scripts also switch to three-dot ranges so ./scripts/lint_urls.sh main HEAD by hand behaves the same. lint_xrefs.sh and lint_file_size.sh had the identical bug and get the identical change.

Cost, stated plainly. A merge base needs real history, so this restores fetch-depth: 0 on the pull_request path — line for line what #17682 removed in February for speed. Measured on this branch, that is ~25s of added wall clock and ~79s of runner time across the three concurrent jobs. #17682's 6min → 10s was mostly the runner and Docker change rather than the fetch depth, though the commit changes both at once and I can't fully separate them. Pushes and the nightly whole-tree scan cannot use a merge base and stay shallow:

fetch-depth: ${{ github.event_name == 'pull_request' && '0' || '1' }}

The quotes matter — bare 0 is falsy in GitHub expressions, so && 0 || 1 always yields 1 and would silently disable the fix.

No silent fallback. When there is no merge base there is no usable range, so the range is left unset and the linters scan the whole tree. Substituting the base tip instead produces a range the scripts fail on quietly: verified against unrelated histories, lint_urls.sh and lint_xrefs.sh both exit 0 having checked nothing, while lint_file_size.sh exits 128 and the wrapper reports "some files exceed the 1 MB limit", which is not what happened. Unset args are loud instead — verified rc=1 on a tree containing a dead link.

--no-color. Both git diff calls now pass it, matching the git grep --no-color two lines below. With color.ui = always in a developer's config, added lines arrive wrapped in escape sequences, grep -E '^\+' matches nothing, and the check passes having found nothing (measured: 2 matches → 0).

Test plan

.ci/scripts/tests/test_link_check_diff_selection.py builds a diverged history where main repairs bad links and shrinks an oversized file while the feature branch simply predates all of it. Four fixture files each pin a different part, and curl is stubbed so there is no network:

Fixture Pins
both_sides.md — edited on both branches the per-file diff range
big.bin — oversized at the branch point, shrunk on main lint_file_size.sh's range
colorful.gitconfigcolor.ui = always --no-color
base_only.md / feature_only.md that main-only changes stay invisible and the branch's own additions are still checked
pytest .ci/scripts/tests/test_link_check_diff_selection.py   # 4 passed

Mutating each changed line individually:

inner per-file range -> ..     3 failed   caught
lint_file_size range -> ..     1 failed   caught
drop --no-color                1 failed   caught
file-selection range -> ..     4 passed   equivalent mutant, see below

The file-selection range is not pinned because it cannot be: with the per-file diff at three dots, the extra files it selects produce empty diffs. Replayed against #21707's real 199-file range, both variants emit byte-identical output. It is changed for consistency, not behaviour.

Narrowing the scope must not blunt the check, so each linter also has a positive control where the branch itself adds the bad thing:

lint_urls      adds a dead URL         -> rc=1, reports example.invalid/dead
lint_xrefs     adds a broken reference -> rc=1, reports sub/missing.md
lint_file_size adds a 1MB+ file        -> rc=1, reports feature_big.bin

End to end on real content: a synthetic commit on top of main that puts #21694's nine repaired links back, as if a PR had added them, gives rc=1 with 9 FAIL and 5 OK against the live network. Incidentally musl.cc answered this time where CI saw 000, and pybind/cmake_example now 404s where CI saw 301 — which is the retry-then-WARN path earning its keep.

Also replayed #21729's and #21707's exact CI refs through the fixed scripts (exit 0 each), and ran all three linters plus lintrunner against this PR's own diff.

Known limitations

  • The checkout is head.sha, so references still resolve against the branch tree rather than the merge result — the mirror image of the bug fixed here. Pre-existing and not worsened by this PR.
  • The whole-tree branch swallows a git grep failure and exits 0, so on a git built without PCRE the scan silently checks nothing. Reachable locally, not on the runners, which is why the nightly scan works. Pre-existing; worth a follow-up rather than widening this PR.

Rollout

This does not repair a currently red run. A rerun keeps the original GITHUB_SHA, and advancing the base alone does not fire synchronize, so an already-open PR picks the fix up only on its next newly triggered pull-request run — any push, or close/reopen. That is still cheaper than a content rebase: no history rewrite, no conflicts, nothing to re-review.

Supersedes #21762, which fixes the same bug but leaves the checkout shallow and misses lint_urls.sh. Its reviewer's shallow-checkout point is exactly right: a --depth=1 fetch writes a shallow graft even into an otherwise complete clone, after which git merge-base fails and A...B is fatal, so the two halves of this change are a pair. The regression test here is adapted from that PR.

Authored with Claude Code (Claude Opus 5).

Copilot AI lite review requested due to automatic review settings August 12, 2026 01:09
@pytorch-bot

pytorch-bot Bot commented Aug 12, 2026

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/21765

Note: Links to docs will display an error until the docs builds have been completed.

❌ 9 New Failures

As of commit 9d64ebc with merge base e7ca2b2 (image):

NEW FAILURES - The following jobs have failed:

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Aug 12, 2026
@psiddh
psiddh requested review from JakeStevens and shoumikhin and removed request for Copilot August 12, 2026 01:09
@psiddh
psiddh requested a review from JacobSzwejbka August 12, 2026 01:10
@github-actions

Copy link
Copy Markdown

This PR needs a release notes: label

If your change should be included in the release notes (i.e. would users of this library care about this change?), please use a label starting with release notes:. This helps us keep track and include your important work in the next release notes.

To add a label, you can comment to pytorchbot, for example
@pytorchbot label "release notes: none"

For more information, see
https://github.com/pytorch/pytorch/wiki/PyTorch-AutoLabel-Bot#why-categorize-for-release-notes-and-how-does-it-work.

@psiddh
psiddh force-pushed the fix-link-lint-merge-base branch from 5ac2e57 to 93ee0a4 Compare August 12, 2026 01:13
Copilot AI lite review requested due to automatic review settings August 12, 2026 01:13

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Updates the link/xref/file-size linting to diff against the PR’s merge base (rather than the base branch tip), preventing pre-existing base-branch content from being misattributed to long-lived branches. This aligns local script behavior with CI and ensures CI has sufficient git history to compute merge bases.

Changes:

  • Switch lint_urls.sh and lint_xrefs.sh to use git diff base...head so “diff mode” reflects changes since the merge base.
  • Switch lint_file_size.sh to select changed files via git diff base...head.
  • Update the reusable _link_check.yml workflow to fetch full history and pass merge-base(base_ref, head_ref) down to the scripts; add a regression test covering the long-lived-branch scenario.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.

Show a summary per file
File Description
scripts/lint_xrefs.sh Uses three-dot diff ranges so PR-only additions are selected relative to merge base.
scripts/lint_urls.sh Uses three-dot diff ranges so URL checks only cover lines introduced on the branch.
scripts/lint_file_size.sh Uses three-dot diff ranges so file-size checks target only branch-introduced changes.
.github/workflows/_link_check.yml Fetches full history and computes merge base for accurate diff selection in CI.
.ci/scripts/tests/test_link_check_diff_selection.py Adds regression coverage for merge-base-based diff selection behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@psiddh

psiddh commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

Both the lint checks pass now:

Lint / link-check / lint-urls (pull_request)
Lint / link-check / lint-urls (pull_request)Successful in 32s
Required
Lint / link-check / lint-xrefs (pull_request)
Lint / link-check / lint-xrefs (pull_request)Successful in 36s
Required
Lint / lintrunner (pull_request)

@shoumikhin shoumikhin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Went through this one carefully, including running the scripts and the new test locally.

The diagnosis is right and three dots is the correct fix. The new test fails against the pre-change scripts, with FAIL 404 on the stale URL and FAIL sub/missing.md on the xref, and passes against this branch, so it is a genuine regression test and it is wired into CI through the pytest.ini testpaths entry. Dropping --depth=1 was required rather than cosmetic: a depth-1 fetch writes a shallow graft even into an otherwise complete clone, after which git merge-base exits 1 and A...B is fatal 128. The two halves of this change are a pair, and neither works alone.

Nothing blocking. A few things worth doing before merge, two of them one-liners.

  1. The || echo "$BASE_REF" fallback at lines 37, 70 and 103 converts a merge-base failure into a green no-op for two of the three linters. Inline comment with the measurements.

  2. fetch-depth: 0 is unconditional, including on the callers that cannot use a merge base at all. Inline comment with the timings and a one-line gate.

  3. The description does not mention that this reinstates, line for line, what #17682 removed on purpose for cost. That is the one fact a reviewer needs to judge the trade, so it belongs in the description along with the measured price. Worth adding that #17682's "6 minutes to 10 seconds" was mostly the runner and Docker change rather than the fetch depth, so the restored full clone costs roughly 25 seconds of wall clock, not 6 minutes.

  4. lint.yml:128 sends github.event.before as base_ref on pushes to main and release/*. On a fast-forward push the merge base is before, so nothing changes. On a force push with diverged history it does, and the push then gets linted for content it did not change. Restricting the merge-base computation to github.event_name == 'pull_request' would settle this and the push half of point 2 together.

  5. Two smaller ones. _link_check.yml:8 still documents base_ref as "Commit to diff against", but each job now derives the diff base from it rather than using it directly. And because the checkout is head.sha, links still resolve against the branch tree rather than the merge result, which is the mirror image of the bug being fixed here. That one is pre-existing and not worsened by this PR, and I would not change it here, but it is worth a sentence in the description as a stated limitation.

One note on rollout: the description says the fix reaches branches that are already open. That is right, but only on their next newly triggered pull-request run. A rerun keeps the original GITHUB_SHA and GITHUB_REF, and advancing the base alone does not fire synchronize, so landing this does not repair a currently red run.

Comment thread .github/workflows/_link_check.yml Outdated
git fetch --no-tags origin "$BASE_REF"
# Diff from the merge base, not the base branch tip. Otherwise a branch cut
# before recent commits is blamed for every line it is merely missing.
args=("$(git merge-base "$BASE_REF" "$HEAD_REF" || echo "$BASE_REF")" "$HEAD_REF")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This fallback makes a merge-base failure silent. When git merge-base fails there is by definition no merge base, so substituting $BASE_REF hands the scripts a range that is fatal for a different reason, and the three scripts then disagree on what to do about it, for reasons of shell structure rather than git.

Running the real scripts under this workflow block with unrelated histories:

lint_urls.sh       fatal: ...: no merge base   rc=0     green, checked nothing
lint_xrefs.sh      fatal: ...: no merge base   rc=0     green, checked nothing
lint_file_size.sh  fatal: ...: no merge base   rc=128   red, but reported as an oversized file

lint_urls.sh:75 and lint_xrefs.sh:40 put the failing diff inside for filename in $(...) inside done < <( ... || true ), and the status is swallowed on the way out. lint_file_size.sh:44 uses a plain files=$(...) assignment under set -euo pipefail, so it exits 128 and the wrapper prints "some files exceed the 1 MB limit", which is not what happened.

Reachability, stated honestly: with fetch-depth: 0 in place there is always a merge base on the pull_request path, so this is not live today. It is reachable on a force push to main or release/* that replaces the branch with unrelated history. The bigger hazard is the latent one, that the day anyone trims the fetch depth again the two link linters go quiet instead of loud, which is exactly what #17682 did.

One line, and it matches both the existing all-zero-SHA guard here and the _get-changed-files.yml precedent of falling back to a whole-tree scan:

if MB=$(git merge-base "$BASE_REF" "$HEAD_REF"); then
  args=("$MB" "$HEAD_REF")
fi

An empty args then means the whole-tree scan, which is loud and safe. Same at lines 70 and 103.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed exactly as suggested — reproduced your rc=0 / rc=0 / rc=128 first, and unset args now fall through to the whole-tree scan, verified rc=1 on a tree containing a dead link

Comment thread .github/workflows/_link_check.yml Outdated
with:
ref: ${{ inputs.ref }}
# Enough history to find where this branch left the base branch.
fetch-depth: 0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

fetch-depth: 0 is required wherever a merge base has to be computed, but it is unconditional here and several callers cannot use one: nightly.yml:34-40 passes no base_ref, workflow_dispatch has no github.event.before, a branch-creation push sends the all-zero SHA, and on a fast-forward push the merge base is github.event.before already.

From the reported job durations on this head against the base, the cost is roughly 3x per job:

this head (fetch-depth 0)  lint-urls 32s  lint-xrefs 36s  lint-file-size 33s
base      (shallow)        lint-urls 12s  lint-xrefs 11s  lint-file-size did not run

The three jobs run concurrently, so that is about 25 seconds of added wall clock on a PR and about 79 seconds of runner time. A defensible price for correctness, just not one worth paying where it buys nothing:

fetch-depth: ${{ inputs.base_ref != '' && 0 || 1 }}

Same for the two checkouts below.

For what it is worth, the cheaper alternatives do not hold up. --filter=blob:none gives a working merge base and diff but saves only about 30%, because the checkout refetches the blobs undeltified. A --deepen loop measured worse than the full clone. Fetching refs/pull/N/merge at fetch-depth: 2 is the one genuinely cheap correct option, but it changes what gets linted. Gating the full clone is the smallest thing that works.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Gated, and pushes now keep their shallow fetch and two-dot range since the merge base there is github.event.before anyway.

Comment thread scripts/lint_file_size.sh Outdated
echo "Checking changed files between $base..$head"
files=$(git diff --name-only "$base..$head")
echo "Checking changed files between $base...$head"
files=$(git diff --name-only "$base...$head")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The other two scripts each get a regression test in this PR. This one gets the same range change with nothing pinning it, and it is the script with the worst behaviour when the range is bad: files=$(...) under set -euo pipefail propagates git's 128 straight out, and the workflow then reports a git failure as an oversized file.

A test in the same shape as the two above is about 15 lines and does not need the curl stub.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added, no stub needed: big.bin is oversized at the branch point and shrunk on main, so two dots picks it up from the working tree and three dots doesn't — plus a positive control that a 1MB+ file the branch actually adds still fails. Reverting the range fails the test

Comment thread scripts/lint_urls.sh Outdated
# Three dots, not two. Against the base branch tip, a branch cut before recent
# commits looks like it is adding back every line those commits touched.
for filename in $(git diff --name-only --unified=0 "$1...$2"); do
git diff --unified=0 "$1...$2" -- "$filename" "${excludes[@]}" \

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Two small ones on the lines this PR is already editing.

Neither git diff here passes --no-color, while the whole-tree branch at :83 uses git grep --no-color. With color.ui = always in a user's config the diff branch silently stops matching, since the + lines arrive wrapped in escape sequences.

Also, only the file selection on the line above is actually pinned by the new test. Reverting just this inner ... back to .. leaves both tests green, because the selection above has already excluded stale.md by then and feature.md reads as added under either range. If you want the test to hold both occurrences, one fixture where a file is modified on both sides would do it.

Same two points apply to lint_xrefs.sh.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Both fixed: --no-color on each diff with a color.ui = always test (2 matches → 0 without it), and a both_sides.md fixture edited on both branches — reverting just the inner range now fails 3 tests

def setUp(self):
self.repo = Path(tempfile.mkdtemp())
self.addCleanup(shutil.rmtree, self.repo, ignore_errors=True)
self.git("init", "-q")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Small robustness note, since this repo is checked out on developer machines with all sorts of config. commit.gpgsign, init.defaultBranch, core.autocrlf and a global .gitattributes are already defended here, but core.hooksPath, init.templateDir, core.excludesFile and inherited GIT_DIR / GIT_WORK_TREE still break this fixture. Clearing those in the subprocess env in git() closes the whole class in one line.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done, slightly wider: GIT_CONFIG_GLOBAL/SYSTEM=/dev/null plus stripping GIT_* from the child env closes your four in one line — and autocrlf/.gitattributes too, which weren't actually defended before.

Copilot AI review requested due to automatic review settings August 12, 2026 16:35
@psiddh
psiddh force-pushed the fix-link-lint-merge-base branch from 93ee0a4 to 3866c55 Compare August 12, 2026 16:35

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

Suppressed comments (5)

.github/workflows/_link_check.yml:90

  • Same issue as above: if git merge-base fails, args remains empty and lint_xrefs.sh will run a whole-tree scan instead of being scoped to PR changes.
              # lacks. No merge base means no usable range, so leave args empty for a
              # whole tree scan: the scripts treat a bad range as nothing to check.
              if merge_base=$(git merge-base "$BASE_REF" "$HEAD_REF"); then
                args=("$merge_base" "$HEAD_REF")
              fi

.github/workflows/_link_check.yml:134

  • Same issue as above: if git merge-base fails, args remains empty and lint_file_size.sh will run a whole-tree scan instead of being scoped to PR changes.
              # lacks. No merge base means no usable range, so leave args empty for a
              # whole tree scan: the scripts treat a bad range as nothing to check.
              if merge_base=$(git merge-base "$BASE_REF" "$HEAD_REF"); then
                args=("$merge_base" "$HEAD_REF")
              fi

.github/workflows/_link_check.yml:46

  • If git merge-base fails here, args stays empty and the scripts will fall back to a whole-tree scan (their else path), which defeats the PR-only intent. The comment also says a bad range is treated as “nothing to check”, but leaving args empty does the opposite.

This issue also appears in the following locations of the same file:

  • line 86
  • line 130
              # lacks. No merge base means no usable range, so leave args empty for a
              # whole tree scan: the scripts treat a bad range as nothing to check.
              if merge_base=$(git merge-base "$BASE_REF" "$HEAD_REF"); then
                args=("$merge_base" "$HEAD_REF")
              fi

scripts/lint_urls.sh:75

  • The --name-only diff doesn’t apply the excludes pathspecs, so excluded paths are still enumerated and looped over (even though the per-file diff later excludes them). Applying the excludes to the file list reduces unnecessary work on large diffs.
    for filename in $(git diff --no-color --name-only --unified=0 "$1...$2"); do

scripts/lint_xrefs.sh:40

  • The --name-only diff doesn’t apply the excludes pathspecs, so excluded paths are still enumerated and looped over (even though the per-file diff later excludes them). Applying the excludes to the file list reduces unnecessary work on large diffs.
    for filename in $(git diff --no-color --name-only --unified=0 "$1...$2"); do

The URL, xref, and file size linters diffed base..head, where base is the tip
of the base branch rather than the point the branch left it. A branch cut
before recent commits still carries the lines those commits replaced, so
against the newer tip its old copies read as additions and the branch is
blamed for links someone else already repaired. pytorch#21729 failed exactly that
way, on nine links pytorch#21694 had already fixed or ignored.

The workflow resolves the merge base and passes it down. That is the half that
matters for branches already open: on a pull request the reusable workflow
resolves from the merge commit, so it carries this fix even though scripts/
still comes from the branch itself. The scripts switch to three dot ranges so
running them by hand behaves the same way.

Computing a merge base needs history, so the pull request path checks out in
full, restoring what pytorch#17682 traded away for speed at a measured 25s of wall
clock. Pushes and the nightly whole tree scan cannot use a merge base and stay
shallow. A missing merge base leaves the range unset for a whole tree scan
rather than substituting the base tip, which two of the three scripts accept
and then quietly pass.

Authored with Claude Code (Claude Opus 5).
@psiddh
psiddh force-pushed the fix-link-lint-merge-base branch from 3866c55 to 9d64ebc Compare August 12, 2026 17:00
Copilot AI review requested due to automatic review settings August 12, 2026 17:00

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Suppressed comments (2)

.github/workflows/_link_check.yml:78

  • Same as lint-urls: because this workflow runs under workflow_call, github.event_name is workflow_call, so fetch-depth here will always be '1' and FROM_MERGE_BASE will always be false. That prevents merge-base resolution on PRs and can leave the scripts with insufficient history to compute A...B diffs reliably.
      - uses: actions/checkout@v4
        with:
          ref: ${{ inputs.ref }}
          # Only the merge base path needs history, and it costs ~25s. Quote the
          # branches: bare 0 is falsy, so `&& 0 || 1` would always yield 1.
          fetch-depth: ${{ github.event_name == 'pull_request' && '0' || '1' }}
      - name: Lint cross-references
        env:
          BASE_REF: ${{ inputs.base_ref }}
          HEAD_REF: ${{ inputs.ref }}
          FROM_MERGE_BASE: ${{ github.event_name == 'pull_request' }}
        run: |

.github/workflows/_link_check.yml:122

  • Same event-name issue applies here: fetch-depth/FROM_MERGE_BASE keyed off github.event_name == 'pull_request' will never be true under workflow_call. Also note the job-level if: ${{ github.event_name == 'pull_request' }} above this step will evaluate false in a called workflow, so this entire job is likely skipped in all contexts.
      - uses: actions/checkout@v4
        with:
          ref: ${{ inputs.ref }}
          # Only the merge base path needs history, and it costs ~25s. Quote the
          # branches: bare 0 is falsy, so `&& 0 || 1` would always yield 1.
          fetch-depth: ${{ github.event_name == 'pull_request' && '0' || '1' }}
      - name: Lint file sizes
        env:
          BASE_REF: ${{ inputs.base_ref }}
          HEAD_REF: ${{ inputs.ref }}
          FROM_MERGE_BASE: ${{ github.event_name == 'pull_request' }}
        run: |

Comment thread .github/workflows/_link_check.yml
@psiddh

psiddh commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

Went through this one carefully, including running the scripts and the new test locally.

The diagnosis is right and three dots is the correct fix. The new test fails against the pre-change scripts, with FAIL 404 on the stale URL and FAIL sub/missing.md on the xref, and passes against this branch, so it is a genuine regression test and it is wired into CI through the pytest.ini testpaths entry. Dropping --depth=1 was required rather than cosmetic: a depth-1 fetch writes a shallow graft even into an otherwise complete clone, after which git merge-base exits 1 and A...B is fatal 128. The two halves of this change are a pair, and neither works alone.

Nothing blocking. A few things worth doing before merge, two of them one-liners.

  1. The || echo "$BASE_REF" fallback at lines 37, 70 and 103 converts a merge-base failure into a green no-op for two of the three linters. Inline comment with the measurements.
  2. fetch-depth: 0 is unconditional, including on the callers that cannot use a merge base at all. Inline comment with the timings and a one-line gate.
  3. The description does not mention that this reinstates, line for line, what Use GH built-in runner for link check workflow #17682 removed on purpose for cost. That is the one fact a reviewer needs to judge the trade, so it belongs in the description along with the measured price. Worth adding that Use GH built-in runner for link check workflow #17682's "6 minutes to 10 seconds" was mostly the runner and Docker change rather than the fetch depth, so the restored full clone costs roughly 25 seconds of wall clock, not 6 minutes.
  4. lint.yml:128 sends github.event.before as base_ref on pushes to main and release/*. On a fast-forward push the merge base is before, so nothing changes. On a force push with diverged history it does, and the push then gets linted for content it did not change. Restricting the merge-base computation to github.event_name == 'pull_request' would settle this and the push half of point 2 together.
  5. Two smaller ones. _link_check.yml:8 still documents base_ref as "Commit to diff against", but each job now derives the diff base from it rather than using it directly. And because the checkout is head.sha, links still resolve against the branch tree rather than the merge result, which is the mirror image of the bug being fixed here. That one is pre-existing and not worsened by this PR, and I would not change it here, but it is worth a sentence in the description as a stated limitation.

One note on rollout: the description says the fix reaches branches that are already open. That is right, but only on their next newly triggered pull-request run. A rerun keeps the original GITHUB_SHA and GITHUB_REF, and advancing the base alone does not fire synchronize, so landing this does not repair a currently red run.

Thanks — all seven done, pushed.

@psiddh

psiddh commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

All the relates jobs pass
Lint / link-check / lint-file-size (pull_request)
Lint / link-check / lint-file-size (pull_request)Successful in 33s
Lint / link-check / lint-urls (pull_request)
Lint / link-check / lint-urls (pull_request)Successful in 37s
Required
Lint / link-check / lint-xrefs (pull_request)
Lint / link-check / lint-xrefs (pull_request)Successful in 36s

@psiddh

psiddh commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

Also checked with Anthony and he verbally agreed with the latest changes / fixes based off his review comments . Merging this now

@psiddh
psiddh merged commit 3b5bf0f into pytorch:main Aug 12, 2026
332 of 341 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants