fix(util): recognize *.bsstag.com hosts as BrowserStack (v8, SDK-6948) by AakashHotchandani · Pull Request #183 · browserstack/wdio-browserstack-service · GitHub
Skip to content

fix(util): recognize *.bsstag.com hosts as BrowserStack (v8, SDK-6948) - #183

Merged
AakashHotchandani merged 8 commits into
v8from
SDK-6948-v8-bsstag-host-recognition
Oct 15, 2026
Merged

AakashHotchandani merged 8 commits into
v8from
SDK-6948-v8-bsstag-host-recognition

Conversation

@AakashHotchandani

@AakashHotchandani AakashHotchandani commented Sep 7, 2026 •

Copy link
Copy Markdown
Collaborator

What is this about?

Ports the SDK-6948 staging-env fixes from main/v9 (#224 and #275) to the v8 line. Internal staging consumers such as App LCA (devapplca) run WebdriverIO v8, so the main/v9 PRs don't reach them.

1. *.bsstag.com host recognition (util.ts), the v8 port of #275.

  • getCloudProvider and isBrowserstackInfra only matched browserstack.com. A session on an internal staging hub (hub-<env>.bsstag.com) was therefore treated as non-BrowserStack, so session marking and Automate <-> TRA session linking did not work.
  • A new helper, isStagingEnvBsstagHost, treats *.bsstag.com as BrowserStack only when:
    • BROWSERSTACK_STAGING_ENV is a bare env name (trimmed; blank means unset), or
    • BROWSERSTACK_ENV is staging, stag, preprod or pre-prod.
  • The host must match *.bsstag.com exactly, ignoring case. Production behaviour is unchanged. This is the same rule node-agent, python, ruby, csharp and javaagent use.
  • Scope: session marking and linking on these envs rely on the CLI flow (the default). There the binary supplies the staging session REST host through APIUtils.updateURLSForGRR. The classic (non-CLI) flow on a named staging env is not supported, the same as main/v9 fix: recognize bsstag hosts and localize session REST host on named staging envs (SDK-6948) #275: its session REST base stays on prod, and the name/status update is logged at debug instead of failing the hook.

2. BROWSERSTACK_BINARY_URL fallback when update_cli fails (cli/cliUtils.ts), the v8 port of #224.

  • Why: update_cli runs during bootstrap, before the binary has localized the API hosts, so it always calls prod api.browserstack.com. Staging credentials get a 401 there, the binary path ends up empty, and the launcher crashes with spawn('') ... 'file' cannot be empty.
  • When update_cli fails and BROWSERSTACK_BINARY_URL is set, the binary is downloaded from that URL. The same applies when a 2xx reply has no download URL and no binary is cached.
  • If that download fails, the cached binary is used when there is one, and both errors are logged.
  • This is opt-in: with the variable unset, the behaviour is exactly as before.

3. _printSessionURL no longer fails the hook (service.ts).

  • v8 fetches the session with got, which throws on a 404 or 401. With (1), bsstag hubs now reach this lookup.
  • A failed lookup, or a reply with no automation_session, is now logged at debug instead of failing before / afterTest. The session link is informational only.
  • _update (session name/status PUT, TurboScale PATCH) likewise logs an HTTP error reply at debug instead of failing beforeSuite / beforeTest / beforeFeature or cutting after() short. Network errors still reject. This matches main/v9, whose fetch resolves on 4xx/5xx.

Related Jira task/s

Release (mandatory for every PR — required for the ready-for-review label)

Version bump: (required — tick exactly one)

  • minor (backwards-compatible feature)
  • patch (bug fix or other small change)

Release notes type: (optional)

  • New Feature
  • Bug Fix
  • Other Improvement

Release notes (customer-facing): (optional but encouraged)

Release notes (internal): (required — engineer-facing; what actually changed / why)

  • SDK-6948: *.bsstag.com hubs are now recognised as BrowserStack on named internal staging envs (BROWSERSTACK_STAGING_ENV / BROWSERSTACK_ENV), so with the CLI flow (default) session marking and TRA linking work there. The classic (non-CLI) flow on a named staging env is not supported; its session update is skipped at debug.
  • SDK-6948: update_cli failures fall back to BROWSERSTACK_BINARY_URL. A failed fallback download uses the cached binary.
  • SDK-6948: _printSessionURL and _update log a failed session lookup or update (HTTP error) at debug instead of throwing.

Checklist

  • Ready to review
  • Has it been tested locally?

Validation:

  • npm run build passes, including the type check.
  • npm test: 45/45 test files pass. The changed files have 194 util, 42 cliUtils and 104 service tests.
  • Not yet run E2E with this commit on a staging env.

PR Validations

Run Tests: Comment RUN_TESTS to trigger sanity tests.

🤖 Generated with Claude Code

getCloudProvider and isBrowserstackInfra only matched browserstack.com, so
a session against an internal staging env (*.bsstag.com) was classified as
non-BrowserStack — the service skipped its instrumentation + GRR host
rewriting and the WebDriver hub stayed on prod. Recognize bsstag/.bsstag.com
in both guards. Ports #56/#65 (main/v9) to the v8 release line.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@AakashHotchandani
AakashHotchandani requested a review from a team as a code owner September 7, 2026 16:11
@AakashHotchandani
AakashHotchandani requested review from 07souravkunda and vivianludrick and removed request for a team September 7, 2026 16:11
…(SDK-6948)

update_cli runs during bootstrap — before the binary spawns and before GRR
localizes hosts — so it always targets prod api.browserstack.com. On an internal
staging run (BROWSERSTACK_STAGING_ENV) the staging creds are rejected there with
401, leaving the CLI binary path empty and crashing the launcher with
spawn('') -> ERR_INVALID_ARG_VALUE ('file' cannot be empty).

Wrap the update_cli call: on failure, if BROWSERSTACK_BINARY_URL is set, download
the binary from that URL and use it; otherwise re-throw (behaviour unchanged when
the var is unset). Adds unit tests for both paths. Validated end-to-end against
devapplca: update_cli 401 -> fallback download from a served zip -> binary spawned
-> build-start reached.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 24, 2026 •

Copy link
Copy Markdown

@AakashHotchandani

AakashHotchandani commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator Author

⚠️ Needs human review

See the SDK PR Review Agent's report from your local run.

Process integrity

The review gate could NOT certify this run — the verdict is held at ⚠️ pending regardless of findings:

  • G3 — u002: no full read receipt for card:cards/_shared.md
  • G3 — u002: no full read receipt for card:cards/wdio-service.md
  • G3 — u002: no full read receipt for pack:default
  • G3 — u002: no full read receipt for pack:external
  • G3 — u002: no full read receipt for pack:integration
  • G3 — u002: no full read receipt for pack:node
  • G3 — u002: no full read receipt for pack:observability
  • G3 — u002: no full read receipt for pack:tests
  • G3 — u002: no full read receipt for slice
  • G3 — u004: no full read receipt for card:cards/_shared.md
  • G3 — u004: no full read receipt for card:cards/wdio-service.md
  • G3 — u004: no full read receipt for pack:default
  • G3 — u004: no full read receipt for pack:external
  • G3 — u004: no full read receipt for pack:integration
  • G3 — u004: no full read receipt for pack:node
  • G3 — u004: no full read receipt for pack:observability
  • G3 — u004: no full read receipt for pack:tests
  • G3 — u004: no full read receipt for slice
  • G6 — 2 kb proposal(s) but no proposal PR recorded (kb-pr.txt) — run pr-kb-proposal.sh

Change map (generated deterministically from the diff)

graph LR
  subgraph nwdio_service["wdio-service"]
    npackages_browserstack_service_src_cli_cliUtils_ts["cliUtils.ts<br/>~81 lines"]
    npackages_browserstack_service_tests_cli_cliUtils_test_ts["cliUtils.test.ts<br/>~81 lines"]
    npackages_browserstack_service_src_service_ts["service.ts<br/>~49 lines"]
    npackages_browserstack_service_tests_service_test_ts["service.test.ts<br/>~49 lines"]
    npackages_browserstack_service_src_util_ts["util.ts<br/>~48 lines"]
    npackages_browserstack_service_tests_util_test_ts["util.test.ts<br/>~48 lines"]
    n_changeset_pr_183_md["pr-183.md<br/>~7 lines"]
    n_changeset_sdk_6948_v8_binary_url_fallback_md["sdk-6948-v8-binary-url-fallback.md<br/>~7 lines"]
    n_changeset_sdk_6948_v8_bsstag_host_recognition_md["sdk-6948-v8-bsstag-host-recognition.md<br/>~7 lines"]
  end
Loading

↻ This verdict comment is the review anchor — it's updated in place on each run (the gate posts its status separately).

— SDK PR Review Agent

@AakashHotchandani AakashHotchandani left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated SDK PR Review — findings at 25529d20

Verdict: 🔴 Fix 2 issues (0 critical · 2 warnings · 0 suggestions). This was a first, full review: 3 of 3 units and 6 of 6 diff regions were judged, and every coverage gate passed.

Both findings are inline below. I checked each one against the code at 25529d20.

Generated by Automated SDK PR review.

if (!isNullOrEmpty(browserStackBinaryUrl)) {
const status = (err as { response?: { statusCode?: number } })?.response?.statusCode ?? (err as Error)?.message
logger.warn(`update_cli request failed (${status}); falling back to BROWSERSTACK_BINARY_URL`)
const fallbackBinaryPath = await this.downloadLatestBinary(browserStackBinaryUrl as string, cliDir)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Warning — [MAINTAINABILITY] The update_cli → BROWSERSTACK_BINARY_URL fallback has side effects that aren't documented

Problem

This PR's description covers only the bsstag host matching in util.ts. It also changes how the CLI binary is obtained here: when requestToUpdateCLI throws and BROWSERSTACK_BINARY_URL is set, the version check is skipped and the binary is downloaded from that URL. The changeset sdk-6948-v8-binary-url-fallback.md documents this, but the PR description doesn't.

The fallback is opt-in. With the env var unset, the error propagates as before, and the new test covers that. Two side effects are worth fixing or stating:

  • Re-download every time. The fallback calls downloadLatestBinary even when a usable existingCliPath is already on disk, so each run where update_cli fails downloads the binary again.
  • The original error is lost. If downloadLatestBinary also fails, its error surfaces and the original update_cli error (the 401 from the comment) is never logged. Only its status is in the warn line.

Suggested Fix

  • If existingCliPath exists, return it instead of re-downloading, or at least prefer it.
  • Wrap the fallback download in a try/catch that logs or rethrows with both errors.
  • Add the fallback to the PR description, so reviewers and release notes don't treat this as a pure host-matching port.

Verified against the diff at 25529d20: the fallback path calls downloadLatestBinary without checking existingCliPath, and err is not kept if the download throws.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Partly fixed in c4d9545.

Fixed:

  • The original error is no longer lost. The update_cli error is logged at debug. If the BROWSERSTACK_BINARY_URL download then fails with no binary on disk, both errors are logged together before the download error is rethrown.
  • A failed download now falls back to the binary on disk. cliUtils.ts fallbackToBinaryUrl returns the existing binary when there is one, and the run fails only when there isn't. This matches main/v9 fix(cli): fall back to BROWSERSTACK_BINARY_URL when update_cli fails (SDK-6948) #224.
  • The PR description and both changesets now describe the fallback.

Not changed: re-downloading instead of reusing the binary on disk. When update_cli fails, BROWSERSTACK_BINARY_URL intentionally wins over the binary on disk:

  • With update_cli failing, the SDK can't tell whether the binary on disk is current. On a staging env it is often an older build that sends session-marking calls to api-<env>, which returns 404 on non-preprod envs. Reusing it silently brings back the marking bug this work fixes (SDK-7872).
  • The download only happens when update_cli fails and the user has set BROWSERSTACK_BINARY_URL, so production runs are unaffected.
  • This is the rule all the SDK-6948 PRs follow (node-agent #2655, python #1282, csharp #1375, javaagent #2296, wdio main fix(cli): fall back to BROWSERSTACK_BINARY_URL when update_cli fails (SDK-6948) #224).

Tests in tests/cli/cliUtils.test.ts: download fails and the binary on disk is used; download fails with nothing on disk and the error is rethrown; a successful reply with no download link and nothing on disk falls back; a successful reply with no download link and a binary on disk keeps it.


const isBrowserstack = (str: string ): boolean => {
return str.includes('browserstack.com')
return str.includes('browserstack.com') || str.includes('bsstag.com')

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Warning — [TEST COVERAGE] No test for the new bsstag matching in getCloudProvider / isBrowserstackInfra

Problem

Both classifiers now treat bsstag hosts as BrowserStack: the single and multiremote branches of getCloudProvider, and isBrowserstack here. Nothing tests these branches. The only test added in this PR covers the CLI fallback. tests/util.test.ts:453-468 covers getCloudProvider only for hub.browserstack.com.

v8 uses .includes() where main/v9 uses === / endsWith, so the v9 tests don't protect this adaptation either. A later edit to either predicate could drop staging recognition without any test failing.

Suggested Fix

Add cases to tests/util.test.ts:

expect(getCloudProvider({ options: { hostname: 'hub-xyz.bsstag.com' } } as any)).toEqual('browserstack')
// multiremote: one instance with a bsstag hostname -> 'browserstack'
expect(isBrowserstackInfra({ hostname: 'hub.bsstag.com' } as any)).toBe(true)

Verified: tests/util.test.ts at 25529d20 has no bsstag case. The reviewer graded this as a Suggestion, and the engine's severity table (Test Coverage + KB-medium grounding) raised it to Warning.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c4d9545.

The bsstag matching is also now limited to staging runs, to match main/v9 #275 and the other SDKs. A *.bsstag.com host counts as BrowserStack only when BROWSERSTACK_STAGING_ENV is a bare env name (trimmed, blank means unset) or BROWSERSTACK_ENV is staging, stag, preprod or pre-prod. The host must match exactly and case doesn't matter. This replaces the earlier match on any text containing bsstag, and the logic lives in a new isStagingEnvBsstagHost helper.

New tests in tests/util.test.ts, under 'SDK-6948 BROWSERSTACK_STAGING_ENV bsstag host recognition':

  • getCloudProvider for a single instance and for multiremote, and isBrowserstackInfra with the host in the config and in capabilities
  • trimming and case-insensitive matching, and the BROWSERSTACK_ENV values
  • production behaviour unchanged when no staging env is set; hub.browserstack.com is still recognised
  • blank or dotted BROWSERSTACK_STAGING_ENV values, and look-alike hosts such as hub.notbsstag.com

The full suite passes locally: 45/45 test files (npm run build && npm test).

AakashHotchandani and others added 2 commits October 13, 2026 14:14
- util: treat *.bsstag.com as BrowserStack only on a named staging env
  (BROWSERSTACK_STAGING_ENV bare token, trimmed; or BROWSERSTACK_ENV
  staging/stag/preprod/pre-prod), exact suffix match, case-insensitive.
  Replaces the unconditional `includes('bsstag')` match.
- cli: BROWSERSTACK_BINARY_URL fallback also covers a 2xx update_cli reply
  with no url and no cached binary; a failed fallback download uses the
  cached binary when present; the update_cli error is logged; the perf span
  closes on every path.
- service: _printSessionURL logs a failed or empty session lookup at debug
  instead of throwing (v8 uses got, which throws on 404/401).
- tests: bsstag gating, fallback download failures, 2xx-without-url,
  _printSessionURL 404 / missing automation_session.

Addresses review comments 4256353854 and 4256353859.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@AakashHotchandani AakashHotchandani left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated SDK PR Review — re-review at ccb370dd

Engine verdict: 🔴 Fix 1 issue (1 critical · 0 warnings · 0 suggestions). All 9 changed files were re-reviewed (4 units, 11 of 11 regions judged).

The verdict comment above shows ⚠️ pending rather than 🔴. The engine's KB check (G6) failed on its own run: it proposed a knowledge-base update and no proposal PR was opened. That is a process gap, not a judgement on the code.

Findings from the previous review (25529d20), both fixed:

  • cliUtils.ts fallback: a failed BROWSERSTACK_BINARY_URL download now falls back to the cached binary and logs both errors, and the changesets document it. ✅
  • bsstag test coverage: tests/util.test.ts now covers it. ✅

New: 1 finding, inline below. I checked it against the code at ccb370dd. My view is that its real severity is Warning, not Critical; the reason is in the comment.

Generated by Automated SDK PR review.

browserUrl = session.browser_url
}
} catch (err) {
// SDK-6948: on a named staging env the session REST host may not serve this session

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Critical (reviewer note: should be ⚠️ Warning) — [CORRECTNESS] _update now runs for bsstag sessions with no error handling, while the session GET next to it was made non-fatal

Problem

With this PR, isStagingEnvBsstagHost makes *.bsstag.com sessions count as BrowserStack. isBrowserstackSession(this._browser) is now true there, so _update() (service.ts:791-810) starts sending got.put / got.patch to ${_sessionBaseUrl}/<id>.json. That is the same session REST host this hunk now guards.

This hunk wraps the GET in try/catch because "on a named staging env the session REST host may not serve this session". The writes to that host have no such guard:

  • _setSessionName → await this._updateJob({ name }) (:887), called from beforeSuite / beforeTest / beforeFeature;
  • after() → await this._updateJob({ status, … }) (:554), for the classic non-CLI flow;
  • the multiremote reload path await this._update(oldSessionId, …) (:729).

If the host returns 401/404 for these sessions, which is the case this hunk was written for, each of these calls rejects and surfaces as a hook error instead of a debug log.

Why Warning rather than Critical: this only triggers when BROWSERSTACK_STAGING_ENV (a bare env name) or BROWSERSTACK_ENV (staging/stag/preprod/pre-prod) is set. That means internal staging only; production hosts are unchanged. GRR also rewrites APIUtils.BROWSERSTACK_AUTOMATE_API_URL to the staging API, so the call may well succeed there. Runtime was not verified.

Suggested Fix

Make _update non-fatal the same way as _printSessionURL:

return got.put(sessionUrl, { … }).catch((err) => {
    const status = err?.response?.statusCode ?? err?.message
    BStackLogger.debug(`Could not update Browserstack session (${status}) at ${sessionUrl}`)
})

(Apply the same to the got.patch turbo-scale branch.) Add a service.test.ts case where got.put rejects with 401 for a bsstag session.

Verified against ccb370dd: _update has no catch, and its 3 call sites await it without one.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction to the after() bullet above. after() is wrapped in try/catch (service.ts:609), so a rejected _updateJob there is logged as Error in after hook and doesn't surface as a hook failure. It does skip everything after it in that try: Listener.onWorkerEnd(), Percy teardown, and the rest of the worker-end cleanup. The beforeSuite / beforeTest / beforeFeature paths through _setSessionName have no catch. The suggested fix (a non-fatal _update) covers all of these.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in d91e729. The finding is valid, and Warning is the right severity: it only triggers on a named staging env.

_update (service.ts) now attaches .catch(onHttpError) to both got.put and the TurboScale got.patch. That covers all three call sites: _setSessionName from beforeSuite / beforeTest / beforeFeature, after(), and the multiremote reload path.

  • HTTP error replies (the error has a response, such as 401 or 404) are logged at debug as Could not update Browserstack session (<status>) at <url> and resolve. The hook doesn't fail, and the rest of after() still runs.
  • Network errors (no response) still reject.

This matches main/v9 exactly. v9 sends these updates with fetch, which resolves on 4xx/5xx and rejects only on network failures. The v8 difference came only from got rejecting on non-2xx replies, so prod behaviour now lines up with v9 as well.

New tests in tests/service.test.ts under _update: a PUT returning 401 resolves and logs at debug; a TurboScale PATCH returning 404 resolves; a network error still rejects.

npm run build passes, and npm test passes 45/45 test files.

AakashHotchandani and others added 2 commits October 13, 2026 14:37
…K-6948)

With *.bsstag.com sessions now recognised on named staging envs, _update
PUTs/PATCHes the session REST host there. got rejects on a non-2xx reply, so
a 401/404 surfaced as a beforeSuite/beforeTest/beforeFeature hook error (via
_setSessionName) and skipped the rest of after(). main/v9 uses fetch, which
resolves on HTTP errors; match that: log HTTP-status errors at debug and keep
rejecting on network errors.

Tests: PUT 401 and TurboScale PATCH 404 resolve with a debug log; a network
error still rejects.

Addresses review comment 4256713959.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@AakashHotchandani AakashHotchandani left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated SDK PR Review — re-review at 61570d31

Engine verdict: 🔴 Fix 2 issues (1 critical · 1 warning · 0 suggestions). This was an incremental review: only the files changed since ccb370dd were reviewed (the changeset, service.ts, service.test.ts).

The verdict comment above shows ⚠️ pending. That comes from the engine's own process checks, not the code:

  • G3: 18 "no read receipt" violations, all for the 2 units (cliUtils.ts, util.ts) that the engine deliberately skipped as unchanged.
  • G6: a KB proposal was made but no proposal PR was opened.

Previous finding (_update with no error handling), fixed in d91e729: got.put / got.patch now .catch(onHttpError). HTTP errors log at debug and network errors still reject, with 3 tests. ✅

New: 2 findings, inline below. I checked both against the code at 61570d31.

Correction to my previous review: I said GRR rewrites APIUtils.BROWSERSTACK_AUTOMATE_API_URL to the staging API. That is only true in the CLI flow (see finding 1).

Generated by Automated SDK PR review.

// SDK-6948: an HTTP error reply (e.g. 401/404 from a named staging env's session REST host)
// must not fail the calling hook. got rejects on non-2xx; main/v9 uses fetch, which resolves,
// so only HTTP-status errors are swallowed here and network errors still propagate.
const onHttpError = (err: { response?: { statusCode?: number } }) => {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Critical (reviewer note: should be ⚠️ Warning) — [INTENT MISMATCH] In the classic (non-CLI) flow, session marking on bsstag goes to the prod API and is now swallowed, but the changeset says it works

Problem

_sessionBaseUrl is built from APIUtils.BROWSERSTACK_AUTOMATE_API_URL (service.ts:51, and :210/:214 for App Automate and TurboScale). That value is the prod default https://api.browserstack.com (cli/apiUtils.ts:3). The only code that changes it is APIUtils.updateURLSForGRR, called from cli/index.ts:131, which is the CLI flow.

So in the classic non-CLI flow on a bsstag hub, _update (session name, and after() status) now sends the staging session id and credentials to prod. The likely result is 401/404, which onHttpError swallows at debug. The run won't fail, but the session won't be marked. Meanwhile .changeset/pr-183.md says "so session marking and TRA linking work there".

Why Warning rather than Critical: this is internal-staging only (gated on BROWSERSTACK_STAGING_ENV / BROWSERSTACK_ENV), production is unchanged, and the CLI flow is unaffected. It is a claim/behaviour mismatch, not customer breakage. The prod 401 itself was not runtime-verified.

Suggested Fix

Either:

  • point the session REST base at the staging API in the non-CLI flow too (derive it from the GRR / env config in the worker), or
  • narrow the changeset: on bsstag, session marking works only with the CLI flow; in the classic flow it is skipped at debug.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid. The wording overclaimed. Fixed in d0a8fba by narrowing the changeset, which is your second option.

I confirmed the cause: APIUtils.BROWSERSTACK_AUTOMATE_API_URL is localized only by APIUtils.updateURLSForGRR, which is called only from cli/index.ts:131. In the classic (non-CLI) flow, _sessionBaseUrl stays on prod api.browserstack.com.

What changed:

  • .changeset/sdk-6948-v8-bsstag-host-recognition.md and the PR's release notes (which regenerate .changeset/pr-183.md) now say marking and linking work with the CLI flow (the default), where the binary supplies the staging session REST host. The classic non-CLI flow on a named staging env is stated as not supported, and its session update is skipped at debug.
  • The PR description has a matching Scope note.

Why I didn't add staging-host derivation to the classic flow:

  • It is the same scope decision as main/v9 fix: recognize bsstag hosts and localize session REST host on named staging envs (SDK-6948) #275, whose changeset reads: "The classic (non-CLI) flow on a named staging env is not supported". The v8 port keeps parity with that.
  • Internal staging runs go through the CLI flow, since the SDK-6948 work is built around the binary supplying the hosts.
  • Building the staging API host in the worker would duplicate the binary's per-env host rules (api<env> vs api-preprod, binary #1889) in a second place, which could drift.
  • Production is unaffected.

expect(debugSpy).toHaveBeenCalledWith(expect.stringContaining('Could not update Browserstack session (401)'))
})

it('logs at debug when a TurboScale session update gets an HTTP error (SDK-6948)', async () => {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Warning — [TEST COVERAGE] The TurboScale test says "logs at debug" but doesn't assert the debug log

Problem

it('logs at debug when a TurboScale session update gets an HTTP error (SDK-6948)') only checks that _update resolves and that got.patch was called once. There is no BStackLogger.debug spy or assertion, unlike the got.put test above it, so the patch branch of onHttpError logging is untested despite the name.

Suggested Fix

const debugSpy = vi.spyOn(bstackLogger.BStackLogger, 'debug')
...
expect(debugSpy).toHaveBeenCalledWith(expect.stringContaining('Could not update Browserstack session (404)'))

Alternatively, rename the test to match what it checks.

Verified at 61570d31.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid. Fixed in d0a8fba. The TurboScale _update test now spies on BStackLogger.debug and asserts 'Could not update Browserstack session (404)', so the patch branch's logging is covered just like the put test. npm test passes 45/45 test files.

AakashHotchandani and others added 2 commits October 13, 2026 14:53
… TurboScale debug log (SDK-6948)

- Changeset: session marking and TRA linking on named staging envs work in
  the CLI flow, where the binary supplies the staging session REST host
  (APIUtils.updateURLSForGRR). The classic non-CLI flow on a named staging
  env is documented as unsupported (same as main/v9 #275); its session
  update is skipped at debug.
- service.test.ts: the TurboScale _update test now asserts the
  'Could not update Browserstack session (404)' debug log.

Addresses review comments 4256880250 and 4256880265.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

@AakashHotchandani AakashHotchandani left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated SDK PR Review — re-review at 0f1d1732: ✅ good to go (reviewer assessment)

Reviewer verdict: ✅ Good to go. No blocking issues remain. The one open item (inline below) is a non-blocking changelog nit.

Engine output, for the record: the engine's union says 🔴 Fix 1. The verdict comment above shows ⚠️ pending because of the engine's own process checks, not the code:

  • G3: 18 receipt violations, all for the cliUtils.ts / util.ts units it deliberately skipped as unchanged.
  • G6: 2 KB proposals with no proposal PR.

Its one finding is rated Critical only because the severity table maps any objective, non-cosmetic finding to Critical. I checked it, and it is a duplicate-changelog nit (details inline). I am overriding it to Suggestion.

Previous findings, all resolved:

  • cliUtils.ts fallback: cached binary, both errors logged. ✅
  • bsstag tests in util.test.ts. ✅
  • _update HTTP errors are non-fatal (.catch(onHttpError)). ✅
  • The changeset claim is narrowed to the CLI flow; the non-CLI flow is documented as unsupported. ✅
  • The TurboScale _update test asserts the debug log. ✅

Generated by Automated SDK PR review.

Comment thread .changeset/pr-183.md
---

- SDK-6948: `*.bsstag.com` hubs are now recognised as BrowserStack on named internal staging envs (`BROWSERSTACK_STAGING_ENV` / `BROWSERSTACK_ENV`), so with the CLI flow (default) session marking and TRA linking work there. The classic (non-CLI) flow on a named staging env is not supported; its session update is skipped at debug.
- SDK-6948: `update_cli` failures fall back to `BROWSERSTACK_BINARY_URL`. A failed fallback download uses the cached binary.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Suggestion (engine: 🔴 Critical, overridden: changelog nit) — [MAINTAINABILITY] The changelog will list each SDK-6948 fix twice

Problem

pr-183.md is auto-generated by .github/workflows/changeset-from-pr.yml from the PR body's ## Release section. It restates the two hand-written changesets, sdk-6948-v8-binary-url-fallback.md and sdk-6948-v8-bsstag-host-recognition.md. All three are patch for @wdio/browserstack-service, so the "Version Packages" changelog will carry each fix twice.

Suggested Fix (non-blocking)

Pick one source:

  • Delete the two hand-written changesets and keep the bot's pr-183.md, which already covers all three points. Don't delete pr-183.md itself: the bot regenerates it on every PR body edit.
  • Or add the skip-changeset label and keep the hand-written ones.

Verified at 0f1d1732: all three files are in .changeset/, and pr-183.md's commits are by github-actions[bot] ("auto-generate from PR template").

@github-actions

Copy link
Copy Markdown
Contributor

@AakashHotchandani
AakashHotchandani merged commit 563cfec into v8 Oct 15, 2026
8 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant