fix(github-actions-grafana-jump): resolve a file view's ref when the branch name has a slash - #53
Open
nsheaps-oura wants to merge 1 commit into
Open
fix(github-actions-grafana-jump): resolve a file view's ref when the branch name has a slash#53nsheaps-oura wants to merge 1 commit into
nsheaps-oura wants to merge 1 commit into
Conversation
…branch name has a slash `/org/repo/blob/<ref>/<path>` doesn't mark where the ref ends and the path begins, so `parseRepoFileContext()` took the first segment after `/blob/` as the whole ref. On a branch whose name contains a slash that's a truncated, usually-nonexistent ref, so the branch-aware config fetch added in #51 either 404'd and silently dropped the repo's links, or matched an unrelated real branch sharing the truncated prefix. GitHub has already resolved the ref to render the page and states the answer twice in the file view's DOM: the ref-picker button's `aria-label` ("<ref> branch"/"<ref> tag") and the `refInfo.name` in its embedded React payload. Both are now read and layered on top of the pure URL parse, the same way a run page's DOM-only ref already is. Reading the page rather than reconstructing the ref from a `GET /repos/{org}/{repo}/branches` listing keeps it working on private repos - api.github.com isn't sent the browser's github.com session cookie, the trap already documented on `resolveDefaultBranch()` - and costs no requests, so there's no rate limit and nothing to cache or invalidate. Whichever source answers, the value is only used if it's a valid reading of the URL on screen (`refMatchesBlobPath()`), which covers both a value going stale mid soft-navigation and the abbreviated-commit-SHA case. When neither source answers, the truncated first-segment reading stands - today's behavior. Nothing guesses a ref. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LeMSMf28QJFLTiQ25sVM3S
Userscript Version PreviewPreview only — versions and CHANGELOGs are bumped automatically on merge to
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
/org/repo/blob/<ref>/<path>gives no hint about where the ref ends and the file path begins, soparseRepoFileContext()took the first/-separated segment after/blob/as the whole ref. On a branch whose name contains a slash that's wrong:/blob/nate-ai/generic-grafana-jump/README.mdreads equally well asnate-aigeneric-grafana-jump/README.mdnate-ai/generic-grafana-jumpREADME.mdOnly the repo's real ref list settles it, and GitHub's own UI settles it that way. Taking the first segment means the branch-aware config fetch added in #51 went looking at a truncated, usually-nonexistent ref — so it 404'd and silently dropped the repo's links, or worse matched an unrelated real branch that happened to share the truncated prefix.
What changed
GitHub has already done that resolution to render the page, and the file view's own DOM states the answer twice:
So the ref is read from the page and layered on top of the pure URL parse, exactly the way #51 already layers a run page's DOM-only ref via
repoConfigTarget().parseRepoFileContext()itself is unchanged: still pure, synchronous, and still the reading that stands when the DOM has no answer.flowchart LR A["/blob/<ref>/<path>"] --> B["parseRepoFileContext():<br/>first segment"] B --> C{"ref picker's<br/>aria-label?"} C -- "no" --> D{"embedded<br/>refInfo.name?"} C -- "yes" --> E{"is it a valid reading<br/>of this URL?"} D -- "yes" --> E D -- "no" --> F["no override:<br/>first segment stands"] E -- "yes" --> G["use the full ref"] E -- "no: stale, or short SHA" --> FWhy not
GET /repos/{org}/{repo}/branchesThat was the obvious approach and it's the worse one here:
api.github.comis a different host thangithub.com, so the browser's github.com session cookie isn't sent with it, and an anonymous branch listing for a private repo 404s. That's the same trap already documented onresolveDefaultBranch()in this file (confirmed there against a real private repo) — the fix would silently do nothing exactly where a wrong ref hurts most.@connect api.github.comgrant, is paginated for repos over 100 branches, and shares a 60/hr unauthenticated per-IP limit. Reading the page costs zero requests, so there's no rate limit, no pagination, and no cache to keep from going stale.refInfo.nameis GitHub's resolved answer for this exact URL, rather than a longest-prefix match reconstructed from a list.Why a scraped value can't go wrong
refMatchesBlobPath()only accepts a ref that is a valid reading of the URL on screen — some leading run of the segments after/blob/, decoded and rejoined, with at least one segment left over to be the file path. So:/blob/my%2Fbranch/..., where the slash arrives percent-encoded inside one segment, still matches, since every split is tried rather than assuming one URL segment per part of the name.When nothing matches there is no override and today's behavior stands. Nothing guesses a ref.
Both sources are tried in turn rather than only the first one that has a value, so a source answering with something that doesn't fit the URL can't shadow one that answers correctly.
Verification
Code-correct —
yarn test→ 78/78 pass (67 before). New cases coverrefNameFromRefSelectorLabelon the branch/tag label forms plus a ref literally namedsome branch,refNameFromEmbeddedDataon the payload shape and a JSON-escaped name, andrefMatchesBlobPathon multi-segment refs, percent-encoded refs, the stale-value and short-SHA rejections, a ref that would leave no file path, and non-file-view pathnames.yarn install --immutable,yarn build,yarn lintall clean (the only lint warnings are pre-existing ones in two other packages).Live-observed (real github.com pages, logged in). The
aria-labeland embeddedrefInfo.namewere both read off live pages — a slashed branch,main, and a soft navigation that switched frommainto the slashed branch through the ref picker, which re-rendered both sources correctly (so neither goes stale on GitHub's client-side router).End-to-end at the config-fetch layer. A disposable branch
n8bot/tmp-slash-ref-check(no branch namedn8botexists, so the naive parse truncates to a dead ref) was pushed carrying a real.github/jump-links.config.yaml. Its file view was loaded in the browser, the two DOM values captured verbatim, and the compileddist/run against them:n8bot→ 404n8bot/tmp-slash-ref-check→ 200, parsed to the probe's linknate-ai/generic-grafana-jumpnate-ai→ 404nate-ai/generic-grafana-jump→ 404 (no config on that branch)main(regression)mainmain— unchangedmainURLmainmain— override correctly rejectedThe temp branch and its remote ref have been deleted.
Not verified live: the assembled userscript still hasn't been run in a browser session with this build — github.com's CSP forbids
evaland injecting a script from anywhere but its own asset host, so the only route left is installing it over the userscript currently installed in the browser, which I didn't do unprompted. Say the word and I'll install this build and confirm a slashed branch's links actually render on its file view.Co-Authored-By: Claude noreply@anthropic.com
https://claude.ai/code/session_01LeMSMf28QJFLTiQ25sVM3S