Skip to content

Release strategy - #6638

Open
blunteshwar wants to merge 24 commits into
mainfrom
release-strategy
Open

Release strategy#6638
blunteshwar wants to merge 24 commits into
mainfrom
release-strategy

Conversation

@blunteshwar

@blunteshwar blunteshwar commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Description

Redesigns the npm publish/release automation into two fully independent pipelines, publish.yml (gen1) and publish-2nd-gen.yml (gen2), each with its own concurrency group, changeset folder, Version PR branch, git tag prefix, and npm dist-tags — a gen1 release can never race or collide with a gen2 release.

Key behavior:

  • Real release (release job): triggered on push to main, scoped with a paths filter (1st-gen/** / 2nd-gen/**) so a push touching only one generation doesn't trigger the other's workflow. Versions via changesets, opens/updates a dedicated Version PR (changeset-release/1st-gen / changeset-release/2nd-gen) targeting whichever branch triggered the push, then publishes once that PR merges — gated behind the npm-publish environment's required-reviewer approval.
  • Idempotency guards: before publishing, both generations check whether their release has already gone out, so a re-run after a completed release never republishes — gen1 checks for git tag gen1-<version>, gen2 checks for git tag gen2-<version> (both generations now tag every real release the same way).
  • Throwaway snapshot (snapshot job): triggered on push, by adding a per-generation snapshot-release-gen1/snapshot-release-gen2 label to a PR, or manually via workflow_dispatch. Publishes a disposable prerelease version under an npm dist-tag (next by default, or snapshot-test from a labeled PR) without touching git, changelog, or opening any PR.
  • Per-generation PR labels (snapshot-release-gen1 / snapshot-release-gen2) replace the old shared snapshot-release label, so a PR can opt a specific generation into a snapshot without affecting the other.
  • Generated commit messages, PR titles, and step summaries consistently say "gen1"/"gen2" instead of "1st-gen"/"2nd-gen" (directory paths, npm workspace names, and branch names are unchanged).

Temporary, to remove before merging to main (marked with TODO/testing comments in both workflow files):

  • release-strategy in the push trigger's branches list — added to validate the full pipeline on this branch before merging.
  • --snapshot test on the real release's changeset version call, and --tag test on its publish steps — lets the release job run its full real path (Version PR → merge → publish → git tag) without shipping an actual latest/beta version while testing.

Motivation and context

The previous release workflow shared a single changesets/action call and concurrency group across both generations, which meant a gen1 release and a gen2 release could overwrite each other's Version PR branch or serialize behind each other unnecessarily. This redesign gives each generation its own release lifecycle end-to-end.

Validated with multiple full end-to-end cycles directly on this branch: label-triggered snapshot publishes for both generations, real Version PR open → merge → publish cycles (including reverts when testing surfaced issues), and fixes for real problems found along the way — an expired npm token, a missing paths filter that let a gen1-only push trigger gen2's workflow too, and a git tag left behind by a revert that would have silently blocked the next release's idempotency check.

Related issue(s)

  • fixes [Issue Number]

Screenshots (if appropriate)


Author's checklist

  • I have read the CONTRIBUTING and PULL_REQUESTS documents.
  • I have reviewed at the Accessibility Practices for this feature, see: Aria Practices
  • I have added automated tests to cover my changes.
  • I have included a well-written changeset if my change needs to be published.
  • I have included updated documentation if my change required it.

Reviewer's checklist

  • Includes a Github Issue with appropriate flag or Jira ticket number without a link
  • Includes thoughtfully written changeset if changes suggested include patch, minor, or major features
  • Automated tests cover all use cases and follow best practices for writing
  • Validated on all supported browsers
  • All VRTs are approved before the author can update Golden Hash

Manual review test cases

  • Descriptive Test Statement

    1. Go here
    2. Do this action
    3. Expect this result
  • Descriptive Test Statement

    1. Go here
    2. Do this action
    3. Expect this result

Device review

  • Did it pass in Desktop?
  • Did it pass in (emulated) Mobile?
  • Did it pass in (emulated) iPad?

Accessibility testing checklist

Required: Complete each applicable item and document your testing steps (replace the placeholders with your component-specific instructions).

  • Keyboard (required — document steps below) — What to test for: Focus order is logical; Tab reaches the component and all interactive descendants; Enter/Space activate where appropriate; arrow keys work for tabs, menus, sliders, etc.; no focus traps; Escape dismisses when applicable; focus indicator is visible.

    1. Go here
    2. Do this action
    3. Expect this result
  • Screen reader (required — document steps below) — What to test for: Role and name are announced correctly; state changes (e.g. expanded, selected) are announced; labels and relationships are clear; no unnecessary or duplicate announcements.

    1. Go here
    2. Do this action
    3. Expect this result

blunteshwar and others added 2 commits August 19, 2026 10:53
…#6607)

* ci(release): split gen1/gen2 changesets and publish workflows

Splits the shared publish pipeline into independent gen1 (main -> next/latest)
and gen2 (gen2-beta -> beta, pre-release mode) release streams per SWC-2315,
each with its own changeset folder so a version bump for one generation no
longer forces a decision about the other.

Introduces a fine-grained bot PAT (RELEASE_BOT_TOKEN, to be added as a repo
secret + branch-protection bypass actor) for the version-bump push, replacing
the default GITHUB_TOKEN. Adds a release-branch-lock composite action that
temporarily blocks PR merges to the target branch for the duration of a real
release run, closing the window where a concurrent merge could land between
this job's checkout and its final push-back.

* ci(release): fully separate changesets per generation

Moves from a root-level .changeset + .changeset-2nd-gen split (which needed
a copy-into-.changeset shim at release time, since @changesets/cli only ever
reads .changeset/ relative to wherever it's invoked) to two genuinely
independent changesets instances: 1st-gen/.changeset/ and 2nd-gen/.changeset/,
each with its own config.json, README, and now its own @changesets/cli
devDependency so `yarn changeset` resolves correctly from within either
directory. Removes the runtime copy-in/copy-out step from publish-2nd-gen.yml
entirely - it's no longer needed.

Also fixes every other repo-root reference to the old single .changeset/
path that would have silently broken or degraded (lint-staged, the 1st-gen
changelog/test-changed scripts, the CircleCI docs-only-change filter, and two
doc links), since 1st-gen's own nested `workspaces` field means changesets
invoked there can no longer see out-of-scope packages by design - those
ignore/fixed entries are dropped as dead weight rather than kept as unusable
safety nets.

* ci(release): make snapshot-release path fully independent per gen

publish.yml no longer touches 2nd-gen at all, even for snapshot-release PRs -
it never reads 2nd-gen/.changeset/ and has no 2nd-gen version/publish steps.

publish-2nd-gen.yml gets its own pull_request (labeled/synchronize, gated to
snapshot-release) trigger and handles its own snapshot testing independently:
checks out the PR's own head ref, versions with --snapshot snapshot-test
(never touching the persistent pre-release state), publishes under
snapshot-test, and skips the branch lock and gen2-beta commit/push entirely
since a PR run never touches gen2-beta.

* fix(ci): use dedicated status-check endpoint for release-branch-lock

Replace the read-whole-protection-object-then-PUT-it-back approach with
GitHub's dedicated add/remove-contexts endpoints
(POST|DELETE .../protection/required_status_checks/contexts). The prior PUT
only round-tripped the fields this script knew about (required_status_checks,
enforce_admins, required_pull_request_reviews, restrictions), so any other
configured protection setting (allow_force_pushes, required_linear_history,
etc.) would silently reset to its API default on every lock and unlock.

Also renames action.yaml -> action.yml to match the repo's predominant
action/workflow file extension.

* chore: update release workflows and documentation for gen1/gen2 split

* fix(ci): pin release-branch-lock pre-checkout ref to github.sha

@main doesn't have .github/actions/release-branch-lock yet - it only exists
on this unmerged branch, so every pre-checkout lock step hard-failed with
"Can't find action.yml" (seen in run 30274425726). Pin to ${{ github.sha }}
instead, which resolves the action from whatever commit is actually running:
works now while testing on this branch, and continues to work after merge.

* fix(ci): replace composite-action self-reference with inline gh api call

uses: doesn't support any expression context, including github - so pinning
to ${{ github.sha }} was invalid syntax, not just a wrong ref (confirmed by
the workflow validator: "Unrecognized named-value: 'github'"). And a literal
@main reference 404s since this action doesn't exist on main yet.

Drop the composite-action self-reference for the pre-checkout lock step
entirely and inline the gh api call directly - a raw run: step needs no
uses: and no repo checkout, so there's no ref to get wrong. Post-checkout
steps (unlock in both workflows, both lock/unlock in release-branch-unlock.yml)
keep using the composite action via its local ./path reference, which is fine
once checkout has happened.

* fix(ci): add missing GITHUB_TOKEN to 2nd-gen version step

@changesets/changelog-github (2nd-gen's changelog generator, same as
1st-gen's) needs GITHUB_TOKEN to fetch PR/commit info for changelog entries.
publish.yml's equivalent step already sets it; publish-2nd-gen.yml's
"Version packages" step didn't, so `yarn changeset version` would fail with
"Please create a GitHub personal access token ... and add it as the
GITHUB_TOKEN environment variable" - reproduced locally before this fix,
confirmed resolved after (2.0.0-beta.2 -> 2.0.0-beta.3, CHANGELOG.md and
package.json updated correctly for both 2nd-gen packages).

* fix(ci): authenticate 2nd-gen npm verify via NODE_AUTH_TOKEN

The Verify NPM authentication step wrote ~/.npmrc, but setup-node sets
NPM_CONFIG_USERCONFIG so npm reads its own temp .npmrc instead, which
authenticates through ${NODE_AUTH_TOKEN}. That token was never set here,
so npm whoami got the placeholder and returned 401. Set NODE_AUTH_TOKEN to
ADOBE_BOT_NPM_TOKEN and drop the no-op ~/.npmrc write.

* ci(release): publish 2nd-gen from main instead of gen2-beta

2nd-gen now releases from main on its own cadence (still beta tag,
changesets pre-release mode). Retarget every gen2-beta reference to main:
push trigger, branch lock/unlock, checkout ref, commit/push target, the
changeset baseBranch, the force-unlock branch choice, and the changeset
README. No gen2-beta branch is involved anymore.

* ci(release): split gen2 into next (auto) and beta (manual) streams

2nd-gen now publishes two dist-tags with different mechanics:
- push to main -> next: continuous throwaway snapshot (changeset version
  --snapshot next, publish --tag next). No pre-release mode, no branch lock,
  nothing committed back to main - a rolling build of main for consumers
  (e.g. the Photoshop dry-run PR) to pull the latest changes.
- workflow_dispatch (main only) -> beta: planned pre-release. Enters
  changesets pre-release mode (beta.N), locks main, versions + changelog,
  commits the bump back to main.
- pull_request + snapshot-release label -> snapshot-test (unchanged).

A manual dispatch is restricted to main (github.ref guard); off-main
dispatches are skipped. latest is intentionally not supported yet.

* chore: ignore .superpowers/ SDD scratch workspace

* ci(release): replace 1st-gen direct push-to-main with a Version PR

* ci(release): restore snapshot job steps (manifests, build:confirm, React wrappers)

* ci(release): replace 2nd-gen direct push-to-main with a beta Version PR

* ci(release): add push trigger to check-changesets condition

* fix(ci): add push trigger to check-changesets; snapshot runs alongside release on main

* chore(ci): remove release-branch-lock machinery

* docs(release): describe the Version PR release flow

* docs(release): clarify PR title differences between gen1 and gen2

* ci(release): replace changesets/action PR management with peter-evans/create-pull-request

changesets/action derives its Version PR branch name solely from the base
branch (changeset-release/<baseBranch>), with no per-generation namespacing.
Since publish.yml and publish-2nd-gen.yml both target main, both workflows
computed the identical branch changeset-release/main and would silently
overwrite each other's Version PR whenever both generations had pending
changesets.

Keep yarn changeset version/publish directly (unaffected by the bug) and
manage each generation's Version PR branch explicitly via
peter-evans/create-pull-request@v6: changeset-release/1st-gen and
changeset-release/2nd-gen. The release job in each workflow now depends on
check-changesets and branches on has_changesets instead of relying on
changesets/action's own detection.

Also remove .github/scripts/toggle-release-lock.mjs, dead code left over
from the removed release-branch-lock action, and update the releasing-swc
doc to describe the new per-generation branch scheme.

* ci(release): manage Version PR branches directly instead of via changesets/action

changesets/action derives its Version PR branch name solely from the base
branch (changeset-release/<baseBranch>), with no per-generation namespacing.
Since publish.yml and publish-2nd-gen.yml both target main, both workflows
computed the identical branch changeset-release/main and would silently
overwrite each other's Version PR whenever both generations had pending
changesets.

Keep yarn changeset version/publish directly (unaffected by the bug) and
manage each generation's Version PR branch explicitly via
peter-evans/create-pull-request@v6: changeset-release/1st-gen and
changeset-release/2nd-gen. The release job in each workflow now depends on
check-changesets and branches on has_changesets instead of relying on
changesets/action's own detection.

Update the releasing-swc doc to describe the new per-generation branch
scheme.

* ci(release): skip build/publish for gen2 when version already on npm

Adds a cheap npm-registry check at the top of the publish step so an
ordinary push to main with no pending changesets short-circuits before
running install/build/changeset publish, mirroring the git-tag guard
already used on the 1st-gen side.

* ci(release): address review feedback on PR #6601

- Refresh the lockfile after `yarn changeset version`, before opening the
  Version PR, so the PR's yarn.lock diff actually reflects the bumped
  internal dependency pins instead of going stale.
- Restrict workflow_dispatch's tag input to type: choice (next,
  snapshot-test) so a manual dispatch can no longer publish under `latest`
  outside the reviewed Version PR flow.
- Give each workflow its own concurrency group (publish-1st-gen-/
  publish-2nd-gen-) instead of sharing one repo-wide key across both files
  - nothing about the new design needs gen1 and gen2 runs to block each
    other anymore.
- Drop the redundant `token: secrets.GITHUB_TOKEN` input on both
  peter-evans/create-pull-request steps; it's already the action's default.

* docs(release): correct the "continuous next" claim to match the actual gate

The snapshot job (and its doc description) claimed next is published on
every push to main. It's actually gated on has_changesets, same as the
Version PR step, so it only refreshes when a push happens to coincide
with a pending changeset. Rewords the workflow comments and the
contributor doc to describe that instead of the aspirational behavior.

* ci(release): use RELEASE_BOT_TOKEN for the Version PR, not GITHUB_TOKEN

A PR/branch created with the default GITHUB_TOKEN doesn't trigger other
workflows (GitHub's own loop-prevention), so the Version PR - which
rewrites every package's version and the lockfile - was merging without
this repo's lint/test/VRT checks ever running on it. RELEASE_BOT_TOKEN
carries a distinct identity so those pull_request-triggered workflows
fire normally. No bypass-actor grant needed this time - it only ever
pushes to the changeset-release/* branches, never to a protected branch.

* ci(release): address latest review feedback and drop stale "global" naming

- Remove RELEASE_BOT_TOKEN from both peter-evans/create-pull-request steps
  per review feedback - reverting to the default GITHUB_TOKEN.
- Remove the redundant "Build all packages" step from all four jobs
  (snapshot + release, both generations): .github/actions/setup-job
  already runs yarn build as its last step, so this was a duplicate
  build on every single run.
- Rename 1st-gen/scripts/update-global-changelog.js to update-changelog.js
  and drop its dead @spectrum-web-components/core changelog branch: that
  package was renamed to @adobe/spectrum-wc-core and its changesets live
  in 2nd-gen/.changeset/, which this script never read - the "global"
  half of the script was unreachable dead code. Renames the npm script
  changelog:global to changelog:1st-gen and updates its two callers
  (publish.yml, scripts/publish.js).

* ci(release): address latest review feedback and drop stale "global" naming

- Remove RELEASE_BOT_TOKEN from both peter-evans/create-pull-request steps
  per review feedback - reverting to the default GITHUB_TOKEN.
- Remove the redundant "Build all packages" step from all four jobs
  (snapshot + release, both generations): .github/actions/setup-job
  already runs yarn build as its last step, so this was a duplicate
  build on every single run.
- Drop update-changelog.js's dead @spectrum-web-components/core changelog
  branch: that package was renamed to @adobe/spectrum-wc-core and its
  changesets live in 2nd-gen/.changeset/, which this script never read -
  the "global" half of the script was unreachable dead code. Renames the
  npm script changelog:global to changelog:1st-gen and updates its two
  callers (publish.yml, scripts/publish.js).

The previous commit (dc140f5) only landed the bare file rename with no
content changes due to a staging mistake - this carries the actual edits.

* ci(release): fix missing git identity for tag creation, drop stale unified publish script

Set git user.name/user.email before create-git-tag.js runs in the release
job's publish step - it was dropped when the direct-push-to-main flow was
replaced with the Version PR flow, but create-git-tag.js still runs
`git tag -a`, which fails without a configured identity. Left unfixed, the
tag never lands and every subsequent push re-runs the non-idempotent React
wrapper publish loop.

Remove scripts/publish.js and its `publish`/`publish:snapshot` yarn scripts:
it still ran `yarn changeset version`/`publish` from the repo root, but the
root .changeset/config.json no longer exists now that 1st-gen and 2nd-gen
have fully independent changesets instances, and nothing in CI or docs
referenced this script.

* chore: test

* Revert "chore: test"

This reverts commit 86a939e.

---------

Co-authored-by: pvashish_adobe <pvashish@adobe.com>
@changeset-bot

changeset-bot Bot commented Aug 19, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: fe6c691

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

📚 Branch Preview Links

🔍 Gen1 Visual Regression Test Results

When a visual regression test fails (or has previously failed while working on this branch), its results can be found in the following URLs:

Deployed to Azure Blob Storage: pr-6638

If the changes are expected, update the current_golden_images_cache hash in the circleci config to accept the new images. Instructions are included in that file.
If the changes are unexpected, you can investigate the cause of the differences and update the code accordingly.

@coveralls

coveralls commented Aug 19, 2026

Copy link
Copy Markdown
Collaborator

Coverage Report for CI Build 33086904203

Warning

Build has drifted: This PR's base is out of sync with its target branch, so coverage data may include unrelated changes.
Quick fix: rebase this PR. Learn more →

Coverage remained the same at 96.257%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 39445
Covered Lines: 38170
Line Coverage: 96.77%
Relevant Branches: 6480
Covered Branches: 6036
Branch Coverage: 93.15%
Branches in Coverage %: Yes
Coverage Strength: 456.84 hits per line

💛 - Coveralls

@blunteshwar
blunteshwar marked this pull request as ready for review August 27, 2026 05:04
@blunteshwar
blunteshwar requested a review from a team as a code owner August 27, 2026 05:04
…merge validation

Adds release-strategy as a temporary push trigger, points the gen1/gen2
Version PRs at the triggering branch instead of hardcoded main, and
publishes gen1 under the test dist-tag so real consumers on latest are
unaffected while validating the release job end-to-end.
Co-authored-by: blunteshwar <32033946+blunteshwar@users.noreply.github.com>
Co-authored-by: blunteshwar <32033946+blunteshwar@users.noreply.github.com>
Co-authored-by: blunteshwar <32033946+blunteshwar@users.noreply.github.com>
@blunteshwar
blunteshwar deployed to npm-publish August 27, 2026 12:43 — with GitHub Actions Active
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.

2 participants