diff --git a/.changeset/README.md b/.changeset/README.md new file mode 100644 index 0000000..7d979a8 --- /dev/null +++ b/.changeset/README.md @@ -0,0 +1,12 @@ +# Changesets + +This repo uses [Changesets](https://github.com/changesets/changesets) to version and publish +**`@wdio/browserstack-service`** on BrowserStack's own cadence (independent of WebdriverIO's +release schedule). + +- Add a changeset for any user-facing change: `npm run changeset` (pick patch/minor/major). +- On merge to `main` (the v9 line) or `v8` (the v8 line), the Release workflow opens a + "Version Packages" PR; merging that PR publishes to npm via OIDC trusted publishing. +- The gRPC/protobuf client is generated inline from the bundled `.proto` files at build time + (`buf generate`) — it is no longer a separate workspace package, so `config.json` `ignore` is + empty and there is nothing extra for this pipeline to skip. diff --git a/.changeset/config.json b/.changeset/config.json new file mode 100644 index 0000000..f25872c --- /dev/null +++ b/.changeset/config.json @@ -0,0 +1,11 @@ +{ + "$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json", + "changelog": "@changesets/cli/changelog", + "commit": false, + "fixed": [], + "linked": [], + "access": "public", + "baseBranch": "v8", + "updateInternalDependencies": "patch", + "ignore": [] +} diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..4a2540d --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,33 @@ +## What is this about? + + +## 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)* + +- + +## Checklist +- [ ] Ready to review +- [ ] Has it been tested locally? + +## PR Validations +Run Tests: Comment RUN_TESTS to trigger sanity tests. diff --git a/.github/workflows/changeset-from-pr.yml b/.github/workflows/changeset-from-pr.yml new file mode 100644 index 0000000..e5971dc --- /dev/null +++ b/.github/workflows/changeset-from-pr.yml @@ -0,0 +1,102 @@ +name: Changeset from PR template + +# Auto-generates the changeset for a PR from its "## Release" section (the same +# section the ready-for-review gate enforces), so engineers never hand-run +# `npx changeset`. Reads the ticked version bump (minor/patch) + the release notes +# and commits `.changeset/pr-.md` to the PR branch. Idempotent: re-runs on +# every body edit and only commits when the derived changeset actually changes, so +# the bot's own push (which re-fires `synchronize`) settles in one no-op pass. +# +# Scope: skips forks (no write token — they add a changeset manually), the +# changesets "Version Packages" PR (head `changeset-release/*`), and PRs labelled +# `skip-changeset` (CI/docs/chore). The ready-for-review gate still independently +# REQUIRES the bump + internal notes; this workflow only GENERATES when they're valid. + +on: + pull_request: + types: [opened, edited, synchronize, reopened, labeled, unlabeled] + +concurrency: + group: changeset-from-pr-${{ github.event.pull_request.number }} + cancel-in-progress: true + +permissions: + contents: write + +jobs: + generate: + if: >- + github.event.pull_request.state == 'open' && + github.event.pull_request.head.repo.full_name == github.repository && + !startsWith(github.event.pull_request.head.ref, 'changeset-release/') && + !contains(github.event.pull_request.labels.*.name, 'skip-changeset') + runs-on: ubuntu-latest + steps: + - name: Derive + commit the changeset from the Release section + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + const PKG = '@wdio/browserstack-service'; + const pr = context.payload.pull_request; + const body = pr.body || ''; + const head = pr.head.ref; + + // --- isolate the "## Release" block (mirrors ready-for-review-label.yml) --- + const releaseBlock = body.split(/(?=^## )/m) + .find(s => /^##[ \t]*Release[ \t]*(\(|$)/m.test(s)) || ''; + if (!releaseBlock) { + core.info('No "## Release" section in the PR body — the ready-for-review gate flags this. Skipping changeset generation.'); + return; + } + + // Count bump ticks only BEFORE the first "Release notes (" header, so a + // checkbox-looking bullet inside the notes can never be mistaken for a bump. + const bumpRegion = releaseBlock.split(/^\*\*Release notes \(/m)[0]; + const bumps = [...bumpRegion.matchAll(/- \[[xX]\]\s*(minor|patch)(?=\s|\(|$)/g)].map(m => m[1].toLowerCase()); + if (bumps.length !== 1) { + core.info(`Expected exactly one version-bump tick (minor|patch), found ${bumps.length}. Skipping (ready-for-review gate flags this).`); + return; + } + const bump = bumps[0]; + + // Extract non-empty bullets under a "**Release notes (