Skip to content

Commit ba135b2

Browse files
fix: prevent branch name duplication in URL rewriting (fixes #85) (#209)
Fixed a critical bug where branch names containing slashes would be duplicated in GitHub URLs during pull request link validation. ## The Problem When a branch name like "release-please/branches/main" contained the base branch name as a substring, the greedy regex pattern would incorrectly match and duplicate the branch path: - Bad: /blob/release-please/branches/release-please/branches/main/file.md - Good: /blob/release-please/branches/main/file.md The root cause was the regex pattern `(/.*/)(main)/(.*)` which greedily matched `/blob/release-please/branches/` as the first capture group, then when replacing with the full head ref, it duplicated the path. ## The Fix 1. **More specific regex**: Changed from `(/.*/)(${GITHUB_BASE_REF})/(.*)` to `/(blob|tree)/(${escapedBaseRef})/(.*)` to only match `/blob/` or `/tree/` specifically, preventing greedy matching of branch name parts 2. **Regex escaping**: Added proper escaping for special characters in branch names (dots, asterisks, etc.) so patterns like `v1.0.0` work 3. **Proper capture groups**: Preserves whether it's a blob or tree URL in the replacement ## Tests Added (5 new comprehensive tests) - Branch names with slashes (release-please/branches/main) - Branch names with multiple slashes (feature/deep/nested/branch) - URLs already on head branch (regression test for exact bug scenario) - Branch names with special regex characters (v1.0.0) - Tree URLs in addition to blob URLs All 20 tests passing with 98.68% coverage. Fixes #85 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude <[email protected]>
1 parent 3b63c81 commit ba135b2

File tree

6 files changed

+630
-6
lines changed

6 files changed

+630
-6
lines changed

dist/index.cjs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35907,11 +35907,13 @@ async function main() {
3590735907
if (!config.urlRewriteExpressions) {
3590835908
config.urlRewriteExpressions = [];
3590935909
}
35910+
const escapeRegex = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
35911+
const escapedBaseRef = escapeRegex(GITHUB_BASE_REF);
3591035912
config.urlRewriteExpressions.push({
3591135913
pattern: new RegExp(
35912-
`github.com/${GITHUB_REPOSITORY}(/.*/)(${GITHUB_BASE_REF})/(.*)`
35914+
`github\\.com/${GITHUB_REPOSITORY}/(blob|tree)/(${escapedBaseRef})/(.*)`
3591335915
),
35914-
replacement: `github.com/${repo}$1${GITHUB_HEAD_REF}/$3`
35916+
replacement: `github.com/${repo}/$1/${GITHUB_HEAD_REF}/$3`
3591535917
});
3591635918
}
3591735919
} catch (err) {

dist/index.cjs.map

Lines changed: 2 additions & 2 deletions
Large diffs are not rendered by default.

src/action.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,18 @@ export async function main() {
7575
if (!config.urlRewriteExpressions) {
7676
config.urlRewriteExpressions = [];
7777
}
78+
// Escape special regex characters in branch names
79+
const escapeRegex = (str) =>
80+
str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
81+
const escapedBaseRef = escapeRegex(GITHUB_BASE_REF);
82+
83+
// Match GitHub blob/tree URLs specifically to avoid greedy matching
84+
// that could include parts of the branch name in the path capture
7885
config.urlRewriteExpressions.push({
7986
pattern: new RegExp(
80-
`github.com/${GITHUB_REPOSITORY}(/.*/)(${GITHUB_BASE_REF})/(.*)`,
87+
`github\\.com/${GITHUB_REPOSITORY}/(blob|tree)/(${escapedBaseRef})/(.*)`,
8188
),
82-
replacement: `github.com/${repo}$1${GITHUB_HEAD_REF}/$3`,
89+
replacement: `github.com/${repo}/$1/${GITHUB_HEAD_REF}/$3`,
8390
});
8491
}
8592
} catch (err) {
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[Contributing Guide](https://github.com/JustinBeckwith/linkinator-action/blob/main/CONTRIBUTING.md)
2+
[Code of Conduct](https://github.com/JustinBeckwith/linkinator-action/blob/main/CODE_OF_CONDUCT.md)

0 commit comments

Comments
 (0)