fix: detect git branch on detached-HEAD CI checkouts (SDK-7009)#63
fix: detect git branch on detached-HEAD CI checkouts (SDK-7009)#63harshit-browserstack wants to merge 6 commits into
Conversation
CI systems check out a commit SHA directly (`git checkout <sha>`), leaving the repo in a detached-HEAD state. There, `.git/HEAD` holds a raw SHA rather than `ref: refs/heads/<branch>`, so git-repo-info returns no branch and `getGitMetaData` reported `version_control.branch = null`. Builds then dropped out of branch-based dashboards and filters even though they were on master. Add a branch fallback in `getGitMetaData`: when git-repo-info yields no branch, resolve it from the CI provider's branch env var (covering the CI systems getCiInfo already detects, plus an explicit BROWSERSTACK_GIT_BRANCH override), then from a best-effort `git for-each-ref --points-at HEAD` backstop. Values are sanitized via normalizeBranchName (strips refs/heads|origin/, rejects unsafe input). Fully graceful — never throws, degrades to the prior behaviour. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
🔴 SDK PR Review gate is red. Pending:
It turns green once the latest SDK PR Review Agent run reports GTG on the current head commit. A native reviewer approval is separately required by branch protection before merge. |
|
🔴 SDK PR Review gate is red. Pending:
It turns green once the latest SDK PR Review Agent run reports GTG on the current head commit. A native reviewer approval is separately required by branch protection before merge. |
|
✅ Good to go All prior findings are resolved. The one remaining item from an earlier round —
Update on current head ( No blocking findings remain. |
|
🔴 SDK PR Review gate is red. Pending:
It turns green once the latest SDK PR Review Agent run reports GTG on the current head commit. A native reviewer approval is separately required by branch protection before merge. |
…tests - BROWSERSTACK_GIT_BRANCH now overrides even a git-repo-info-resolved branch (moved ahead of info.branch), matching its documented "override" role. - GitLab: prefer CI_COMMIT_BRANCH over CI_COMMIT_REF_NAME (the latter is the tag name on tag pipelines). - Split CI branch env vars into provider-specific (trusted directly) vs generic (BRANCH/GIT_BRANCH/…, only trusted when getCiInfo() detects a CI) to stop a stray local env var being misreported as the branch. - Add CodeFresh CF_BRANCH (parity with getCiInfo detection). - git backstop: query refs/heads (lstrip=2) then refs/remotes (lstrip=3) as two ordered calls, yielding bare branch names for ANY remote (not just origin) and making heads-before-remotes explicit rather than relying on refspec order. - normalizeBranchName: denylist unsafe/illegal ref chars instead of an allowlist, so legal names (e.g. containing '#') are no longer dropped; kept internal (un-exported) so this stays a patch. - Tests: cover the git for-each-ref backstop (mocked spawnSync) and the null-result path; assert override precedence; all env mutations via a save/restore helper (try/finally) to prevent cross-test leakage. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
🔴 SDK PR Review gate is red. Pending:
It turns green once the latest SDK PR Review Agent run reports GTG on the current head commit. A native reviewer approval is separately required by branch protection before merge. |
|
🔴 SDK PR Review gate is red. Pending:
It turns green once the latest SDK PR Review Agent run reports GTG on the current head commit. A native reviewer approval is separately required by branch protection before merge. |
…op, tests - getBranchFromGit no longer passes git-repo-info's `.root` (which can point at the main repo dir under a git worktree); the backstop runs in process.cwd(), the actual checkout being instrumented. - Convert the git for-each-ref backstop from synchronous spawnSync to async promisified execFile, and make it lazy (query refs/heads, return early, only then refs/remotes) with a 3s timeout — no blocking subprocess on getGitMetaData's async hot path. - Tests: add coverage for the generic-var anti-collision gate (BRANCH ignored outside a recognised CI; GIT_BRANCH honoured inside Jenkins), CI-specific vs generic priority, and the remotes fall-through; env helper now also neutralises CI-detection markers so getCiInfo() is deterministic under ambient CI. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
🔴 SDK PR Review gate is red. Pending:
It turns green once the latest SDK PR Review Agent run reports GTG on the current head commit. A native reviewer approval is separately required by branch protection before merge. |
|
🔴 SDK PR Review gate is red. Pending:
It turns green once the latest SDK PR Review Agent run reports GTG on the current head commit. A native reviewer approval is separately required by branch protection before merge. |
Round-3 review: the test's CI_MARKER_KEYS list (used to force getCiInfo() null in the generic-var-gating tests) didn't cover every branch the real getCiInfo() checks (Concourse, Google Cloud, Shippable, some CodeBuild/Wercker vars), so a stray ambient CI marker could silently flip those tests to the wrong branch. - Complete CI_MARKER_KEYS against every gating var in getCiInfo(). - Add self-checking guards — assert utils.getCiInfo() is null (outside-CI test) / non-null (Jenkins test) before the branch assertion — so any future drift between the list and the real detector fails loudly instead of silently. Test-only change; production code unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
🔴 SDK PR Review gate is red. Pending:
It turns green once the latest SDK PR Review Agent run reports GTG on the current head commit. A native reviewer approval is separately required by branch protection before merge. |
|
🟢 SDK PR Review gate is green — the SDK PR Review Agent has given a GTG for this PR (the A native GitHub reviewer approval is still separately required by branch protection before this PR can merge — this check does not substitute for that. |
|
🟢 SDK PR Review gate is green — the SDK PR Review Agent has given a GTG for this PR (the A native GitHub reviewer approval is still separately required by branch protection before this PR can merge — this check does not substitute for that. |
…d&test)
The module-top `import { execFile }` + `promisify(execFile)` broke every test
that partial-mocks `node:child_process` (e.g. tests/cli/frameworks/index.test.ts
mocks it as `{ spawn }` only) — `execFile` was undefined at util.ts load, so
those suites failed to load with "No execFile export is defined on the mock".
Import child_process lazily inside gitForEachRef (within its existing try/catch)
so module load never references execFile. It's only touched when the git
backstop actually runs (covered by util.test.ts's full mock); suites that never
hit the backstop are unaffected, and a missing/partial mock degrades to [].
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
🔴 SDK PR Review gate is red. Pending:
It turns green once the latest SDK PR Review Agent run reports GTG on the current head commit. A native reviewer approval is separately required by branch protection before merge. |
|
🟢 SDK PR Review gate is green — the SDK PR Review Agent has given a GTG for this PR (the A native GitHub reviewer approval is still separately required by branch protection before this PR can merge — this check does not substitute for that. |
|
🟢 SDK PR Review gate is green — the SDK PR Review Agent has given a GTG for this PR (the A native GitHub reviewer approval is still separately required by branch protection before this PR can merge — this check does not substitute for that. |
What is this about?
Fixes new CI builds reporting no branch in their VCS info, causing them to drop out of branch-based dashboards and filters even when on
master.Root cause: CI checks out a commit SHA directly (
git checkout <sha>), leaving a detached-HEAD repo (customer build runs in a container at git root/app). There,.git/HEADholds a raw SHA instead ofref: refs/heads/<branch>, sogit-repo-inforeturnsbranch: null.getGitMetaData()copiedinfo.branchverbatim intoversion_control.branchwith no fallback → payload carriedbranch: null. Local checkouts sit on a real branch, so they were unaffected — matching the ticket's two same-SHA snippets.Fix: in
getGitMetaData()(packages/browserstack-service/src/util.ts), whengit-repo-infoyields no branch, fall back to (1) the CI provider's branch env var — covering the CI systemsgetCiInfo()already detects, plus an explicitBROWSERSTACK_GIT_BRANCHoverride — then (2) a best-effortgit for-each-ref --points-at HEADbackstop. Values are sanitized vianormalizeBranchName(). Fully graceful; never throws.Verified by running the real compiled
getGitMetaData()(realgit-repo-info@2.1.1, realfs) in a real detached-HEAD repo: before →branch: null(reproduced symptom), after →branch: "master". 3 unit tests added (getGitMetaDatasuite 5/5); lint + build (tsc) pass; no new regressions.Related Jira task/s
SDK-7009
Release (mandatory for every PR — required for the
ready-for-reviewlabel)Version bump: (required — tick exactly one)
Release notes type: (optional)
Release notes (customer-facing): (optional but encouraged)
Release notes (internal): (required — engineer-facing; what actually changed / why)
getGitMetaDatanow falls back to CI branch env vars (+BROWSERSTACK_GIT_BRANCHoverride) and agit for-each-ref --points-at HEADbackstop whengit-repo-inforeturns no branch on a detached-HEAD checkout. AddsnormalizeBranchNamesanitizer. Changeset included (patch).Checklist
PR Validations
Run Tests: Comment RUN_TESTS to trigger sanity tests.