Skip to content

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
mainfrom
n8bot/branch-ref-disambiguation
Open

fix(github-actions-grafana-jump): resolve a file view's ref when the branch name has a slash#53
nsheaps-oura wants to merge 1 commit into
mainfrom
n8bot/branch-ref-disambiguation

Conversation

@nsheaps-oura

Copy link
Copy Markdown
Collaborator

Problem

/org/repo/blob/<ref>/<path> gives no hint about where the ref ends and the file path begins, so parseRepoFileContext() 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.md reads equally well as

ref path
nate-ai generic-grafana-jump/README.md
nate-ai/generic-grafana-jump README.md

Only 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:

<button id="ref-picker-repos-header-ref-selector"
        aria-label="nate-ai/generic-grafana-jump branch" ...>
<script type="application/json" data-target="react-app.embeddedData">
  ... "refInfo":{"name":"nate-ai/generic-grafana-jump", ...} ...

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/&lt;ref&gt;/&lt;path&gt;"] --> 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" --> F
Loading

Why not GET /repos/{org}/{repo}/branches

That was the obvious approach and it's the worse one here:

  • Private repos. api.github.com is a different host than github.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 on resolveDefaultBranch() in this file (confirmed there against a real private repo) — the fix would silently do nothing exactly where a wrong ref hurts most.
  • Requests. A ref listing needs a new @connect api.github.com grant, 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.
  • Authority. refInfo.name is 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:

  • a value left behind by a soft navigation that changed refs describes a different URL, fails the check, and is ignored;
  • an abbreviated commit SHA in the picker doesn't equal the full SHA in the URL, fails the check, and the first-segment reading stands — which for a SHA URL is already the whole ref;
  • /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 cover refNameFromRefSelectorLabel on the branch/tag label forms plus a ref literally named some branch, refNameFromEmbeddedData on the payload shape and a JSON-escaped name, and refMatchesBlobPath on 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 lint all clean (the only lint warnings are pre-existing ones in two other packages).

Live-observed (real github.com pages, logged in). The aria-label and embedded refInfo.name were both read off live pages — a slashed branch, main, and a soft navigation that switched from main to 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 named n8bot exists, 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 compiled dist/ run against them:

page naive ref corrected ref
probe config on the slashed branch n8bot404 n8bot/tmp-slash-ref-check200, parsed to the probe's link
a file on nate-ai/generic-grafana-jump nate-ai → 404 nate-ai/generic-grafana-jump → 404 (no config on that branch)
a file on main (regression) main main — unchanged
stale DOM value against a main URL main main — override correctly rejected

The 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 eval and 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

…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
@github-actions

Copy link
Copy Markdown

Userscript Version Preview

Preview only — versions and CHANGELOGs are bumped automatically on merge to main, not in this PR. Manual bumps to a higher version are preserved.

Package Base New Action
github-actions-grafana-jump 0.2.10 0.2.11 will-bump

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant