ci: create a release pipeline to release 1st gen and 2nd gen#5978
ci: create a release pipeline to release 1st gen and 2nd gen#5978blunteshwar merged 60 commits intomainfrom
Conversation
|
| Name | Type |
|---|---|
| @spectrum-web-components/tags | Patch |
| @spectrum-web-components/styles | Patch |
| @spectrum-web-components/bundle | Patch |
| @spectrum-web-components/card | Patch |
| @spectrum-web-components/illustrated-message | Patch |
| @spectrum-web-components/custom-vars-viewer | Patch |
| @spectrum-web-components/vrt-compare | Patch |
| @spectrum-web-components/theme | Patch |
| @spectrum-web-components/truncated | Patch |
| documentation | Patch |
| @spectrum-web-components/overlay | Patch |
| @spectrum-web-components/slider | Patch |
| @spectrum-web-components/story-decorator | Patch |
| @spectrum-web-components/action-menu | Patch |
| @spectrum-web-components/combobox | Patch |
| @spectrum-web-components/contextual-help | Patch |
| @spectrum-web-components/menu | Patch |
| @spectrum-web-components/picker | Patch |
| @spectrum-web-components/popover | Patch |
| @spectrum-web-components/tooltip | Patch |
| @spectrum-web-components/breadcrumbs | Patch |
| @spectrum-web-components/action-bar | Patch |
| @spectrum-web-components/coachmark | Patch |
| @spectrum-web-components/accordion | Patch |
| @spectrum-web-components/action-button | Patch |
| @spectrum-web-components/action-group | Patch |
| @spectrum-web-components/alert-banner | Patch |
| @spectrum-web-components/alert-dialog | Patch |
| @spectrum-web-components/asset | Patch |
| @spectrum-web-components/avatar | Patch |
| @spectrum-web-components/badge | Patch |
| @spectrum-web-components/button-group | Patch |
| @spectrum-web-components/button | Patch |
| @spectrum-web-components/checkbox | Patch |
| @spectrum-web-components/clear-button | Patch |
| @spectrum-web-components/close-button | Patch |
| @spectrum-web-components/color-area | Patch |
| @spectrum-web-components/color-field | Patch |
| @spectrum-web-components/color-handle | Patch |
| @spectrum-web-components/color-loupe | Patch |
| @spectrum-web-components/color-slider | Patch |
| @spectrum-web-components/color-wheel | Patch |
| @spectrum-web-components/dialog | Patch |
| @spectrum-web-components/divider | Patch |
| @spectrum-web-components/dropzone | Patch |
| @spectrum-web-components/field-group | Patch |
| @spectrum-web-components/field-label | Patch |
| @spectrum-web-components/help-text | Patch |
| @spectrum-web-components/icon | Patch |
| @spectrum-web-components/icons-ui | Patch |
| @spectrum-web-components/icons-workflow | Patch |
| @spectrum-web-components/icons | Patch |
| @spectrum-web-components/iconset | Patch |
| @spectrum-web-components/infield-button | Patch |
| @spectrum-web-components/link | Patch |
| @spectrum-web-components/meter | Patch |
| @spectrum-web-components/modal | Patch |
| @spectrum-web-components/number-field | Patch |
| @spectrum-web-components/picker-button | Patch |
| @spectrum-web-components/progress-bar | Patch |
| @spectrum-web-components/progress-circle | Patch |
| @spectrum-web-components/radio | Patch |
| @spectrum-web-components/search | Patch |
| @spectrum-web-components/sidenav | Patch |
| @spectrum-web-components/split-view | Patch |
| @spectrum-web-components/status-light | Patch |
| @spectrum-web-components/swatch | Patch |
| @spectrum-web-components/switch | Patch |
| @spectrum-web-components/table | Patch |
| @spectrum-web-components/tabs | Patch |
| @spectrum-web-components/textfield | Patch |
| @spectrum-web-components/thumbnail | Patch |
| @spectrum-web-components/toast | Patch |
| @spectrum-web-components/top-nav | Patch |
| @spectrum-web-components/tray | Patch |
| @spectrum-web-components/underlay | Patch |
| @spectrum-web-components/base | Patch |
| @spectrum-web-components/grid | Patch |
| @spectrum-web-components/opacity-checkerboard | Patch |
| @spectrum-web-components/reactive-controllers | Patch |
| @spectrum-web-components/shared | Patch |
| @spectrum-web-components/eslint-plugin | Patch |
| @spectrum-web-components/stylelint-header-plugin | Patch |
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
📚 Branch Preview Links🔍 First Generation Visual Regression Test ResultsWhen 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: If the changes are expected, update the |
…n generated files
There was a problem hiding this comment.
I think this file is a result of a bad merge?
| push: | ||
| branches: | ||
| - main | ||
|
|
There was a problem hiding this comment.
Can we be on the safer side here to prevent overlapping publish/version steps?
I think we can use the cancel-in-progress: false to avoid killing an in-flight publish halfway through.
| concurrency: | |
| group: publish-1st-gen-${{ github.ref }} | |
| cancel-in-progress: false | |
There was a problem hiding this comment.
Can you elaborate on this?
| echo "Current npm version: $(npm --version)" | ||
| npx semver -r ">=11.5.1" "$(npm --version)" || { | ||
| echo "Upgrading npm for trusted publishing support (requires 11.5.1+)" | ||
| npm install -g npm@latest |
There was a problem hiding this comment.
Can we avoid installing @latest? Can we pin to a good, known version? I'd prefer our workflow to be super deterministic.
Publishing approach for Spectrum Web Components
A journey through secure, automated npm releases
The challenge
We have a monorepo containing multiple generations of Spectrum Web Components with a shared foundation:
Package architecture
The three pillars
Core (
@spectrum-web-components/core): The foundation package containing shared utilities, base classes, and common logic. Both 1st-gen and 2nd-gen packages depend on this. It lives in2nd-gen/packages/core/but is versioned separately and released alongside 1st-gen packages.1st-gen (
@spectrum-web-components/*): The established component library with 95+ individual packages, each published under the@spectrum-web-componentsnamespace.2nd-gen (
@adobe/spectrum-wc): The next generation of components, consolidated into a single package under the Adobe namespace. It imports shared logic from@spectrum-web-components/core.React wrappers (
@swc-react/*): Auto-generated React/Next.js wrapper packages for 1st-gen components.The complexity
Each package group has different requirements:
@spectrum-web-components@spectrum-web-components@adobeADOBE_BOT_NPM_TOKEN)@swc-reactThe
linkedversioning between Core and 2nd-gen means when core changes, 2nd-gen gets the same version bump—ensuring compatibility since 2nd-gen depends on Core.The solution: unified publishing workflow
Our publishing workflow (
/.github/workflows/publish-1st-gen.yml) orchestrates the entire release process through a two-job pipeline that handles all package types with their distinct authentication requirements.Triggers: when does publishing happen?
The workflow supports three trigger modes:
1. Manual dispatch (
workflow_dispatch): Team members can trigger releases directly from the GitHub Actions UI with an optional custom npm dist-tag. The default tag isnext.2. Pull request (
pull_request): The workflow runs automatically when a PR has thesnapshot-releaselabel. It triggers onlabeled(when the label is added) andsynchronize(when new commits are pushed to the PR). A job-level condition ensures only PRs with thesnapshot-releaselabel proceed:3. Push to main (
push): Automatically triggers on pushes tomainfor continuous delivery after PRs are merged.Two-job architecture
The workflow is split into two jobs to avoid unnecessary work when there are no changesets:
The
check-changesetsjob only checks out the repository and inspects.changeset/*.mdfiles. If no changesets are found, thepublishjob is skipped entirely—saving time on setup, build, and authentication.The environment protection layer
The
publishjob uses a GitHub Environment callednpm-publishfor a critical reason: security.Why use a protected environment?
Required reviewers: Publishing to npm is a sensitive operation. The environment can be configured to require approval from designated team members before any workflow run proceeds. This prevents accidental or malicious releases.
Environment-specific secrets: Tokens required for publishing (such as
ADOBE_BOT_NPM_TOKENfor the Adobe namespace) can be scoped to this environment, making them accessible only when the workflow runs within thenpm-publishcontext.Deployment protection rules: GitHub allows configuring rules like:
Audit trail: All deployments to protected environments are logged, providing accountability for releases.
Trusted publishing prerequisite: On npmjs.com, when configuring trusted publishing for a package, you can specify an environment name. If configured, npm will only accept OIDC tokens from workflow runs that execute within that specific environment—adding another layer of security.
Trusted publishing: the tokenless future
What is trusted publishing?
Traditional npm publishing requires storing a long-lived npm token as a repository secret. This approach has risks:
Trusted publishing (also called OIDC publishing) eliminates these risks by using OpenID Connect (OIDC) tokens. Here's how it works:
Which packages use trusted publishing?
@spectrum-web-components/core): ✅ OIDC trusted publishing@spectrum-web-components/*): ✅ OIDC trusted publishing@swc-react/*): ✅ OIDC trusted publishing@adobe/spectrum-wc): ❌ Currently usesADOBE_BOT_NPM_TOKEN(general org token for Adobe namespace)Note: The team is working with the Adobe npm namespace administrators to either configure
ADOBE_BOT_NPM_TOKENas a proper environment token or enable trusted publishing for the@adobenamespace, which would allow 2nd-gen packages to use OIDC authentication as well.Configuring trusted publishing
The
id-token: writepermission is essential—it allows the workflow to request OIDC tokens from GitHub.OIDC verification and NPM token configuration are combined into a single step. If OIDC is unavailable, the step fails and publishing is blocked:
The publish step only runs if this combined authentication step succeeds:
npm CLI version requirement
Trusted publishing requires npm 11.5.1 or later. The workflow uses
npx semverfor precise version comparison and automatically upgrades npm if needed.How the release tag is determined
The workflow determines the npm dist-tag through a simple priority system:
Tag resolution:
snapshot-releaselabelsnapshot-testlatest,beta)next(default)nextRelease tag validation
An additional safety check prevents publishing
latestfrom non-main branches:This ensures production releases (
latest) can only originate frommain, preventing accidental stable releases from feature branches.Changeset configuration: how versioning works
The
.changeset/config.jsondefines how packages are versioned together:{ "fixed": [ [ "@spectrum-web-components/*", "!@spectrum-web-components/core", "!@spectrum-web-components/1st-gen", "!@spectrum-web-components/2nd-gen" ] ], "linked": [["@adobe/spectrum-wc", "@spectrum-web-components/core"]], "ignore": [ "@spectrum-web-components/1st-gen", "@spectrum-web-components/2nd-gen" ] }Understanding the versioning strategy
Fixed group (1st-gen packages):
All
@spectrum-web-components/*packages (except Core and workspace roots) version together at the same version number. When any package in this group changes, all packages bump to the same version.Linked group (Core + 2nd-gen):
@adobe/spectrum-wcand@spectrum-web-components/coreare linked—they can have different absolute version numbers, but when either changes, both receive the same type of bump. This ensures 2nd-gen always has a compatible Core version.Ignored packages:
Workspace root packages (
@spectrum-web-components/1st-gen,@spectrum-web-components/2nd-gen) are never published.Why Core isn't linked with 1st-gen packages
You might notice that Core is linked with 2nd-gen (
@adobe/spectrum-wc) but not with 1st-gen packages, even though both generations depend on Core. This is due to a changesets limitation: a package cannot be both "fixed" and "linked" simultaneously.Since 1st-gen packages must remain in a fixed versioning group (all components version together), Core cannot be linked with them. This creates an important workflow requirement:
When modifying Core, you must manually add changesets for affected 1st-gen components.
Example workflow
If you modify
buttonbase(a Core utility used by button components):@spectrum-web-components/core@spectrum-web-components/buttonWhy this matters
Always review which 1st-gen components depend on the Core changes you're making and add corresponding changesets.
Publish summary
Every workflow run writes a summary to the GitHub Actions job summary page. This makes it easy to identify what triggered a release, which PR it came from, and which npm tag was used—especially useful for PR-triggered snapshot releases.
For pull request triggers, the summary includes:
feat/new-button→mainsnapshot-testThe summary is written using
$GITHUB_STEP_SUMMARYand runs withif: always()so it appears even when earlier steps fail, showing whether NPM authentication succeeded or not.Challenges we faced
Challenge 1: missing TypeScript declarations
The problem: The build failed with:
The
@adobe/postcss-tokenpackage was a JavaScript-only local workspace package.The solution: Created a TypeScript declaration file (
index.d.ts) for the package:Challenge 2: npm token expiration
The problem: Initial publishing attempts failed with:
The solution: Transitioned from classic npm tokens to OIDC trusted publishing for packages under
@spectrum-web-componentsand@swc-reactnamespaces. The@adobenamespace currently usesADOBE_BOT_NPM_TOKEN(a general org token). Work is ongoing with Adobe's npm namespace administrators to migrate this to either environment-scoped tokens or trusted publishing.Challenge 3: React wrapper provenance validation
The problem: React wrapper publishing failed with:
Trusted publishing validates that the
repository.urlinpackage.jsonmatches the provenance claims.The solution: Updated the React wrapper generator (
cem-plugin-react-wrapper.js) to include therepositoryfield in all generatedpackage.jsonfiles:Challenge 4: dual authentication strategy
The problem:
@spectrum-web-components@adobenamespace require a traditional npm tokenThe solution: Both authentication methods are configured in a single step. The npm CLI is smart enough to:
.npmrc) for packages without trusted publishingChallenge 5: Node.js version consistency
The problem: npm 11 required for trusted publishing support. Hardcoding the version in the workflow meant maintaining it in two places (
.nvmrcfor local development and the workflow for CI).The solution: Updated
.github/actions/setup-job/action.ymlto read from.nvmrcinstead of hardcoding:This approach maintains a single source of truth—the
.nvmrcfile—for both local development and CI environments. Developers usingnvm uselocally will automatically use the same version as CI.Challenge 6: unnecessary React wrapper builds
The problem: React wrappers were being built and published on every release, even when only 2nd-gen packages had changes. Since React wrappers are generated from 1st-gen components, publishing them without 1st-gen changes is wasteful and could cause confusion.
The solution: The
check-changesetsjob detects whether any 1st-gen packages are being released:The
publishjob reads this output from the upstream job:Behavior:
@adobe/spectrum-wc)@spectrum-web-components/button, etc.)Challenge 7: flaky React wrapper publishes
The problem: Publishing 95+ React wrapper packages sequentially could fail midway due to transient npm registry errors, leaving a partially published release.
The solution: Added retry logic with exponential backoff for each React wrapper package:
Each package gets up to 3 attempts with 2s, 4s backoff between retries before the workflow fails.
The complete flow
Security summary
latestblocked from non-main branchesConclusion
Our publishing workflow balances security with flexibility across a complex package architecture:
@adobe/spectrum-wc) versions with Core as a linked groupThe two-job architecture ensures fast feedback when there are no changesets to publish, while the full pipeline handles build, version, and publish for all package types.
Security is layered through:
@spectrum-web-componentsand@swc-react, token for@adobe)The journey to this solution involved debugging bash quirks, TypeScript configuration, npm authentication, and provenance validation—but the result is a robust, secure publishing pipeline that serves all generations of Spectrum Web Components.
Author's checklist
Reviewer's checklist
patch,minor, ormajorfeaturesManual review test cases
Verify that the GitHub Actions workflow for publishing is working correctly:
snapshot-releaselabel to a test PR and confirm the workflow triggers, runs both jobs, and publishes with thesnapshot-testtagbeta) and verify the correct tag is usedmainand verify the workflow runs with thenexttagpublishjob is skipped when no changesets exist (onlycheck-changesetsruns)latestfrom a non-main branch and confirm it failsDevice review