Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,8 @@
"access": "public",
"baseBranch": "main",
"updateInternalDependencies": "patch",
"ignore": []
"ignore": [],
"snapshot": {
"useCalculatedVersion": true
}
}
5 changes: 5 additions & 0 deletions .changeset/port-upstream-service-parity.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@wdio/browserstack-service": minor
---

Publish from the standalone `browserstack/wdio-browserstack-service` repo, at parity with the WebdriverIO monorepo. Includes Load Testing Service (LTS) support, one-to-many Test-Case-ID tagging (`setCustomTestCaseId`), correct test/hook finish on mocha timeouts, per-batch failure isolation in the request queue, accessibility Browser type augmentations, and `yauzl` upgraded to `^3.4.0`. No user-facing API change.
58 changes: 52 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,29 @@ name: Release
#
# The gRPC/protobuf client is generated inline at build time (buf generate); there is no
# separate core package for this workflow to version or publish.
#
# Release model (publishing is MANUAL):
# * push to main / v8 -> opens/updates the "Version Packages" PR only. NEVER publishes;
# merging that PR does NOT publish either.
# * Run workflow (publish) -> publishes the merged version to npm (main -> `latest`,
# v8 -> `v8`) with git tag + GitHub release + provenance.
# * Run workflow (canary) -> snapshot prerelease to the npm `canary` dist-tag.

on:
push:
branches:
- main # v9 line -> dist-tag "latest"
- v8 # v8 line -> dist-tag "v8" (from publishConfig.tag on the v8 branch)
- main # v9 line -> opens Version PR (publish target: dist-tag "latest")
- v8 # v8 line -> opens Version PR (publish target: dist-tag "v8" via publishConfig.tag)
workflow_dispatch:
inputs:
publish:
description: 'Publish the merged version to npm (main -> `latest`, v8 -> `v8`). Merge the "Version Packages" PR first so there are no pending changesets.'
type: boolean
default: false
canary:
description: 'Publish a canary prerelease to the npm `canary` dist-tag (validates OIDC + provenance end-to-end; never touches `latest`). Requires at least one pending changeset.'
type: boolean
default: false

# Never run main and v8 releases on top of each other.
concurrency: release-${{ github.ref }}
Expand All @@ -42,8 +59,6 @@ jobs:
# NODE_AUTH_TOKEN that becomes an empty token line that can shadow OIDC Trusted
# Publishing at `npm publish` time. npm already defaults to registry.npmjs.org and
# publishConfig.access=public handles the scoped publish, so the line isn't needed.
# (Follow-up to consider: split changesets/action so publish runs as its own
# top-level step — further de-risks OIDC. Tracked, not done here.)
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 22 # resolves to >= 22.14 on the runner (OIDC floor)
Expand All @@ -63,13 +78,44 @@ jobs:
- name: Test
run: npm test

- name: Create Release PR or publish to npm
# Version PR (push to main / v8): run changesets in VERSION-ONLY mode — open or update the
# "Version Packages" PR (version bump + CHANGELOG). This step NEVER publishes: with no
# `publish:` input, once the Version PR is merged (a push with no pending changesets) the
# action simply no-ops. Publishing is the separate, manual step below.
- name: Open/update the "Version Packages" PR
if: github.event_name == 'push'

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

why we added the trigger on push?

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.

Good question — it's intentional, and it's the standard Changesets release flow. The on: push: to main/v8 is what makes a release happen at all, and it drives both halves:

  1. Merge a PR that carries a pending changeset → push to mainchangesets/action sees the pending changeset and opens/updates the "Version Packages" PR (bumps version + CHANGELOG). It does not publish yet.
  2. Merge that "Version Packages" PR → this is another push to main, now with no pending changesets → the same action runs changeset publish → publishes to npm (latest).

Without the push trigger, nothing would ever version or publish. The if: github.event_name == 'push' on this step just scopes it to that normal flow so it does not fire on the manual workflow_dispatch canary run — that's the separate step below (if: github.event_name == 'workflow_dispatch' && inputs.canary). The two steps are mutually exclusive by event type.

(No NPM_TOKEN anywhere — auth is OIDC trusted publishing via the id-token: write permission.)

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.

Update: based on this, we've made publishing manual (pushed as c9f1ed8). The push trigger now does strictly one thing — open/update the "Version Packages" PR in version-only mode. It never publishes, and merging the Version PR is now a no-op.

Publishing is a separate manual step: Actions → Release → Run workflow → publish: true (main → latest, v8 → v8), with git tag + GitHub release + provenance over the same OIDC publisher. Canary is unchanged (canary: true).

So the answer to "why on push?" is now: only to keep the version/changelog PR up to date automatically — nothing reaches npm without someone clicking Run workflow.

uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d # v1
with:
version: npm run version # changeset version — manages the Version PR only
createGithubReleases: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

# Publish (manual, workflow_dispatch with publish=true): publish the already-versioned
# package to npm — main -> `latest`, v8 -> `v8` (via publishConfig.tag) — with git tag,
# GitHub release, and provenance, over the OIDC trusted publisher (no NPM_TOKEN).
# Merge the "Version Packages" PR first: this publishes only when there are no pending
# changesets. If any remain, the action safely opens/updates the Version PR instead.
- name: Publish to npm
if: github.event_name == 'workflow_dispatch' && inputs.publish
uses: changesets/action@a45c4d594aa4e2c509dc14a9f2b3b67ba3780d0d # v1
with:
version: npm run version # changeset version (opens the "Version Packages" PR)
publish: npm run release # changeset publish (honors publishConfig.tag per branch)
createGithubReleases: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# No NPM_TOKEN: auth is OIDC via id-token: write above.
NPM_CONFIG_PROVENANCE: 'true'

# Canary (manual, workflow_dispatch with canary=true): snapshot-version the pending
# changesets and publish a prerelease to the `canary` dist-tag via the SAME OIDC trusted
# publisher. Validates OIDC + provenance end-to-end without touching `latest`.
# snapshot.useCalculatedVersion=true in .changeset/config.json makes the version
# <next>-canary-<datetime> (e.g. 9.30.0-canary-...). Requires >=1 pending changeset.
- name: Canary publish to `canary` dist-tag
if: github.event_name == 'workflow_dispatch' && inputs.canary
run: |
npx changeset version --snapshot canary
npx changeset publish --no-git-tag --tag canary
env:
NPM_CONFIG_PROVENANCE: 'true'
Comment on lines +115 to +121

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Did we test these commands?

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.

Fair question. Honest status: the flags and config are verified, but the commands have not been run end-to-end yet — that's exactly what the manual canary run is for, and it's designed to be safe.

What's verified:

  • changeset version --snapshot canary with snapshot.useCalculatedVersion: true in .changeset/config.json → produces 9.30.0-canary-<datetime> (the workspace pkg is at 9.29.1; two minor changesets → 9.30.0). Snapshot mode also auto-disables committing and does not consume the changeset, so it stays on the branch for the real release afterward.
  • changeset publish --no-git-tag --tag canary — both flags confirmed valid against @changesets/cli (its usage string is literally publish [--tag <name>] [--otp <code>] [--no-git-tag]; we resolve 2.31.x from ^2.27.9). --tag canary publishes under the canary dist-tag (never latest); --no-git-tag skips git tag creation, which is right for an ephemeral canary.

Why running it live is safe (it IS the test): it only publishes a prerelease to the canary dist-tag; latest (currently 9.29.1) is untouched; and any OIDC/provenance misconfig makes the publish step fail cleanly — no bad latest can escape.

To validate end-to-end: Actions → Release → Run workflow → canary: true, then:

npm view @wdio/browserstack-service@canary version          # -> 9.30.0-canary-...
npm view @wdio/browserstack-service@canary dist.attestations # provenance present
npm view @wdio/browserstack-service dist-tags.latest         # still 9.29.1

2 changes: 1 addition & 1 deletion packages/browserstack-service/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@wdio/browserstack-service",
"version": "9.28.0",
"version": "9.29.1",
"description": "WebdriverIO service for better Browserstack integration",
"author": "BrowserStack <support@browserstack.com>",
"homepage": "https://github.com/browserstack/wdio-browserstack-service",
Expand Down
Loading