Skip to content

fix(hack): make lint-docs-links actually run on macOS - #6728

Merged
rh-hemartin merged 2 commits into
fullsend-ai:mainfrom
guyoron1:fix/lint-docs-links-macos
Sep 1, 2026
Merged

fix(hack): make lint-docs-links actually run on macOS#6728
rh-hemartin merged 2 commits into
fullsend-ai:mainfrom
guyoron1:fix/lint-docs-links-macos

Conversation

@guyoron1

@guyoron1 guyoron1 commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Heyaaaa : )

Summary

Spun out of review on #6676 (@waynesun09's catch): hack/lint-docs-links passed vacuously on every macOS commit — BSD grep has no -P, the || true swallowed it, so the loop saw zero links. Measured here: 0 links extracted by the old pipeline, 2798 by the fixed one, across 211 docs files.

It turned out to be two bugs: once links actually flow, realpath -m is next in the loop, and BSD realpath has no -m either — a grep-only fix would flip the hook from silently passing to hard-failing on every macOS docs commit. Both replaced together.

Related Issue

None — raised in review on #6676.

Changes

  • Extraction: grep -oE '\]\([^)]+' piped through sed to strip the ]( — same match as the PCRE original, all occurrences per line.
  • Resolution: python3 os.path.realpath instead of realpath -m — same resolve-what-exists semantics, and python3 is guaranteed here since pre-commit itself runs on it.

Testing

  • make lint passes (stage changes first, then run)
  • Tests added/updated for new or modified logic

Extraction parity across all 211 docs files (2798 links, zero mismatches vs the original pattern); full corpus passes identically to Linux CI; a synthetic file with two escaping links is rejected with exactly those two. shellcheck clean, darwin/arm64.

Checklist

  • PR title follows Conventional Commits (correct type, ! for breaking changes)
  • Commits are signed off (DCO) — human and human-directed agent sessions only
  • I wrote this contribution myself and can explain all changes in it

The lint-docs-links pre-commit hook passed vacuously on every macOS
commit: its link extraction used grep -oP, BSD grep has no -P, and the
trailing `|| true` swallowed the "invalid option" failure, so the loop
saw zero links and the scope check succeeded over nothing. Measured on
this checkout: 0 links extracted by the old pipeline, 2798 by the fixed
one, across the 211 markdown files under docs/.

Two bugs, not one, and the second only detonates once the first is
fixed: with links actually flowing, `realpath -m` is next in the loop,
and BSD realpath has no -m either — under set -e the hook would go from
silently passing to hard-failing on every macOS commit that touches
docs/. So both are replaced together:

- Extraction is grep -oE '\]\([^)]+' piped through sed to strip the
  literal "](". Same match as the PCRE lookbehind-\K original: all
  occurrences per line, up to the next ")" or end of line, closing paren
  not required. Verified equivalent across the whole docs corpus — 2798
  links, zero mismatches against the original pattern's semantics.
- Resolution is python3 os.path.realpath, which resolves what exists
  and normalizes the rest — GNU `realpath -m` semantics for targets
  that may not exist. python3 is guaranteed wherever this hook runs,
  because pre-commit itself runs on it.

Behavior on Linux CI is unchanged: same links, same resolution, and the
full docs/ corpus passes identically. On macOS the hook now does its
job — a synthetic file with two escaping links (../../../../etc/passwd
and ../..) is rejected with exactly those two reported, while anchors,
mailto, absolute URLs, and missing-but-in-repo targets pass through as
before.

Found while sweeping grep -P portability for the prior-SHA extractor
fix; this was the one site that is not Linux-only automation — the
pre-commit config wires it over ^docs/.*\.md$ on developer machines.

Verification: shellcheck clean; make lint exit 0; extraction parity,
full-corpus run, and the synthetic escape test all on darwin/arm64
(BSD grep 2.6.0-FreeBSD, /bin/realpath without -m).

Signed-off-by: guy oron <goron@redhat.com>
@guyoron1
guyoron1 requested a review from a team as a code owner August 28, 2026 09:34
@github-actions

Copy link
Copy Markdown

E2E tests did not run

E2E tests run automatically for org/repo members and collaborators on pull requests.

For other contributors, a maintainer must add the ok-to-test label after the latest push.

See E2E testing guide for details.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Make docs link linting portable on macOS

🐞 Bug fix 🕐 Less than 10 minutes

Grey Divider

AI Description

• Replaces GNU-only link extraction so macOS commits validate documentation links.
• Resolves missing link targets portably while preserving repository-boundary enforcement.
• Keeps Linux behavior unchanged without requiring additional developer tooling.
Diagram

graph TD
    A["Markdown files"] --> B["Portable extraction"] --> C{"Relative link?"} -->|Yes| D["Python resolution"] --> E{"Inside repo?"} -->|No| F["Report violation"]
    C -->|No| G["Ignore link"]
    E -->|Yes| G
Loading
High-Level Assessment

The targeted substitutions are the best fit: BSD-compatible grep and sed retain the existing extraction pipeline, while Python supplies GNU realpath-like behavior through an already guaranteed runtime. A full Python rewrite or requiring GNU utilities would add migration scope or developer dependencies without improving this focused fix.

Files changed (1) +12 / -2

Bug fix (1) +12 / -2
lint-docs-linksMake documentation link validation work on macOS +12/-2

Make documentation link validation work on macOS

• Replaces GNU-only PCRE extraction with BSD-compatible extended grep and sed. Uses Python path resolution instead of unsupported 'realpath -m', preserving checks for missing targets and links escaping the repository.

hack/lint-docs-links

@codecov

codecov Bot commented Aug 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@qodo-code-review

qodo-code-review Bot commented Aug 28, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Interpreter launched per link ✓ Resolved 🐞 Bug ➹ Performance
Description
Every relative Markdown link now starts a separate python3 interpreter, so an all-files run over
the docs corpus performs thousands of Python startups and can substantially slow linting. The prior
resolver also used a subprocess, but replacing the small realpath utility with interpreter startup
magnifies the per-link cost.
Code

hack/lint-docs-links[R32-33]

+        resolved="$(cd "$file_dir" && python3 -c \
+            'import os,sys; print(os.path.realpath(sys.argv[1]))' "$path")"
Relevance

●● Moderate

Performance concern is plausible, but redesigning per-link resolution is semantic and lacks a close
accepted precedent; existing history is portability-focused.

PR-#2181
PR-#791
PR-#6167

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The shell loop handles each extracted target individually and invokes Python inside that loop; the
pre-commit hook passes every matching docs filename, so all-files linting multiplies interpreter
startup by the number of relative links.

hack/lint-docs-links[19-42]
.pre-commit-config.yaml[156-161]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The hook launches `python3` once for every relative Markdown link, causing substantial startup overhead during full-corpus lint runs.

## Issue Context
Keep the macOS-compatible `os.path.realpath` behavior, but batch link resolution through one Python process rather than invoking the interpreter inside the per-link loop. Use an unambiguous transport such as NUL-delimited records so paths containing whitespace remain safe.

## Fix Focus Areas
- hack/lint-docs-links[19-42]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 62 rules
Review mode: ⚖️ Balanced

Grey Divider

Tip of the day
💡 Did you know, you can describe a rule in plain language on the Rules page and Qodo drafts it for you

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread hack/lint-docs-links Outdated
Review on the previous commit flagged the cost correctly: spawning an
interpreter per relative link put a full-corpus run at ~70s on this
checkout (2798 links). Link candidates are now streamed to a single
python3 process as NUL-terminated records — file_dir, rel_file, target,
tab-separated, target last so a tab inside a link target cannot shift
the fields, NUL-terminated so targets with spaces survive — and
offenders come back newline-delimited, which is safe because targets
are read line-by-line and cannot contain one. Same corpus now lints in
under 3s.

Resolution semantics are unchanged: os.path.realpath per target against
the same root-prefix check, scheme/anchor filtering still in the shell
before Python ever sees a link. Re-verified: full corpus passes
identically, a synthetic file with three escaping links (one with a
space in the target) reports exactly those three, a link-free file
exits 0, shellcheck clean.

Signed-off-by: guy oron <goron@redhat.com>
@guyoron1

Copy link
Copy Markdown
Contributor Author

Fixed in 5f31fcf — good catch on the cost. Candidates now stream to a single python3 process (NUL-delimited records, target last so embedded tabs can't shift fields), offenders come back newline-delimited. Full corpus: ~70s → under 3s on this checkout, same verdicts — corpus passes, a synthetic file with three escaping links (one with a space in the target) reports exactly those three.

@rh-hemartin

Copy link
Copy Markdown
Member

/fs-review

@fullsend-ai-review

fullsend-ai-review Bot commented Aug 31, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 7:08 AM UTC · Completed 7:23 AM UTC

Commit: 5f31fcf · View workflow run →

Runtime: claude · Model: opus → claude-opus-4-6 · Effort: high · Cost: $3.65

@fullsend-ai-review fullsend-ai-review Bot added the risk/low PR risk: low label Aug 31, 2026
@fullsend-ai-review

Copy link
Copy Markdown

Risk Assessment: low (1/5)

Details

Single-file 63-line change to a hack/ linting script with no protected paths, no security sensitivity, no CI or dependency changes, and a non-bot returning contributor.

@fullsend-ai-review

Copy link
Copy Markdown

Review

Findings

Low

  • [architectural-coherence] hack/lint-docs-links:36 — The fix introduces an inline Python 3 dependency (via python3 -c) into a script that was previously pure bash. While Python 3 is a reasonable assumption for developer machines running pre-commit, this creates a mixed-language pattern. If more scripts need the same treatment, the team should converge on one approach.

@fullsend-ai-review fullsend-ai-review Bot added the ready-for-merge All reviewers approved — ready to merge label Aug 31, 2026
@rh-hemartin
rh-hemartin added this pull request to the merge queue Sep 1, 2026
Merged via the queue into fullsend-ai:main with commit a27ea46 Sep 1, 2026
65 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-merge All reviewers approved — ready to merge risk/low PR risk: low

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants