Add tag-triggered publish CI (npm, anywidget, VS Code Open VSX) - #357
Conversation
…ation Publish pipeline (.github): - New publish.yml: on a v*.*.* tag, gate on lint + unit tests, then publish in parallel the matterviz npm package, the VS Code extension (Marketplace + Open VSX), and matterviz-anywidget. workflow_dispatch supports dry_run (default true) that builds/packages/validates without publishing. - Shared `setup` composite action (Node 24 + pnpm install, optional extension installs) reused by publish, test, and lint. - Fold the former publish-anywidget.yml into publish.yml. Also bundled: - package.json: expose plot subfolders via a ./plot/* wildcard export; pin dompurify to 3.4.7 - sanitize.ts: memoize sanitize_html (skips repeated double DOMPurify passes when a component re-sanitizes many cells per render, e.g. HeatmapTable) - extensions/vscode/package.json: regenerate contributes.configuration for the new Box/Sankey/Sunburst component settings
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
.github/actions/setup/action.yml (1)
12-15: ⚡ Quick winConsider enabling pnpm caching for faster CI runs.
The
actions/setup-nodeaction supports built-in dependency caching viacache: 'pnpm', which would speed up all workflows using this composite action.⚡ Proposed enhancement to add pnpm caching
- uses: actions/setup-node@v6 with: node-version: 24 registry-url: https://registry.npmjs.org + cache: pnpm🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/actions/setup/action.yml around lines 12 - 15, Add pnpm caching to the actions/setup-node invocation by updating the "with" inputs for the existing uses: actions/setup-node@v6 step (the block that currently sets node-version: 24 and registry-url). Specifically, add cache: 'pnpm' and set cache-dependency-path to the repository lockfile (e.g., pnpm-lock.yaml) so the action will use built-in pnpm dependency caching during CI runs.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/publish.yml:
- Around line 77-92: Move the version check so it runs before any parallel
upload/publish jobs and ensure every package (root, VS Code extension, and
matterviz-anywidget) either depends on that check or performs the same
validation; reuse the existing verification snippet that reads EXPECTED from
needs.version.outputs.version and compares it to node -p
"require('./package.json').version" (the EXPECTED env and the actual=$(...)
comparison) by extracting it into a dedicated pre-publish job or adding the same
comparison step to the anywidget job so no upload runs unless the version/tag
match.
- Around line 15-24: The workflow currently allows manual publishes from any ref
via the workflow_dispatch inputs (version, dry_run); to block live publishes,
add a guard so publishing jobs only proceed when dry_run is true OR the ref is a
trusted release ref (e.g., main branch or tags). Concretely, in the publishing
jobs (e.g., the jobs that handle vscode and anywidget publishing) add an if
condition like: only run the live-publish steps when inputs.dry_run == 'false'
AND (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/')),
otherwise skip publishing; alternatively require inputs.dry_run to never be set
to false for arbitrary refs by forcing dry_run default true and preventing false
execution unless the ref matches the trusted refs above. Ensure you reference
the workflow_dispatch inputs (version, dry_run) and update the job-level if
conditions for the vscode and anywidget publish jobs accordingly.
- Line 52: The version regex in the resolver currently allows prerelease
versions (e.g., in the line "[[ \"$version\" =~
^[0-9]+\\.[0-9]+\\.[0-9]+([.-][0-9A-Za-z.-]+)?$ ]]") but the subsequent publish
steps (the `npm publish` and the `anywidget` publish commands) do not pass an
explicit tag; fix this by either updating that resolver regex to reject
prerelease identifiers (remove the "([.-][0-9A-Za-z.-]+)?" part) or, preferable,
keep prerelease support and add an explicit tag argument to the publish commands
(add `--tag` with a computed value such as "next" or the prerelease identifier)
and apply the same `--tag` usage to both the `npm publish` and the anywidget
publish invocation so prerelease publishes succeed and do not go to "latest".
In `@package.json`:
- Line 196: The package.json downgrade of dompurify from 3.4.8 to 3.4.7 is
unexplained; verify why "dompurify": "3.4.7" was pinned in package.json, check
changelog/release notes and npm/GitHub security advisories for 3.4.7 vs 3.4.8,
and either (a) revert the dependency to 3.4.8 if there is no
compatibility/security reason, updating the lockfile accordingly, or (b) add a
brief comment/commit message (and ideally a package.json rationale field or PR
description) documenting the specific compatibility or packaging issue that
required pinning to 3.4.7 so reviewers understand the intentional downgrade.
---
Nitpick comments:
In @.github/actions/setup/action.yml:
- Around line 12-15: Add pnpm caching to the actions/setup-node invocation by
updating the "with" inputs for the existing uses: actions/setup-node@v6 step
(the block that currently sets node-version: 24 and registry-url). Specifically,
add cache: 'pnpm' and set cache-dependency-path to the repository lockfile
(e.g., pnpm-lock.yaml) so the action will use built-in pnpm dependency caching
during CI runs.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 706c0921-642d-4b62-a07c-8b53c2ab02ff
📒 Files selected for processing (8)
.github/actions/setup/action.yml.github/workflows/lint.yml.github/workflows/publish-anywidget.yml.github/workflows/publish.yml.github/workflows/test.ymlextensions/vscode/package.jsonpackage.jsonsrc/lib/sanitize.ts
💤 Files with no reviewable changes (1)
- .github/workflows/publish-anywidget.yml
matterviz + matterviz-anywidget now publish via OIDC trusted publishing instead of a long-lived token: drop NODE_AUTH_TOKEN/NPM_TOKEN and the registry-url .npmrc scaffolding (an empty _authToken would block OIDC), ensure npm >= 11.5.1, and drop --provenance (emitted automatically under trusted publishing). VS Code Marketplace + Open VSX keep PATs (Open VSX has no OIDC yet; Marketplace OIDC needs Azure federated identity).
workflow_dispatch now resolves the version to its `vX.Y.Z` tag and every job checks out that exact commit, so a manual run can (re)publish an already-pushed tag that never reached a registry -- instead of building whatever ref the workflow ran from. The version job verifies the tag exists (falls back to the triggering ref for a dry run of a not-yet-tagged version, and errors on a real run when the tag is missing). The version-match guard is skipped on dry runs since a not-yet-tagged ref won't match.
- Add a `verify` job (checked out at the release tag) that all publish jobs depend on, so a version/tag mismatch blocks every upload instead of letting parallel jobs publish partially; drop the now-duplicated inline checks in the npm/vscode jobs. - Tighten the version regex to plain X.Y.Z: a prerelease would land on npm 'latest' (no --tag is passed) and the repo ships no prereleases. - Revert dompurify 3.4.7 -> 3.4.8: 3.4.8 includes 3.4.7's CVE-2026-49978 fix plus template-scrubbing / Trusted-Types fixes with no regressions, so the downgrade was unwarranted.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/publish.yml:
- Around line 32-35: The concurrency group key currently mixes manual input and
raw refs, causing different groups for the same release; update the group
expression to normalize to the tag name (prefer github.ref_name or strip
refs/tags/) so manual workflow_dispatch input and tag pushes resolve to the same
identifier. Replace the current group: publish-${{ github.event.inputs.version
|| github.ref }} with a normalized expression that uses
github.event.inputs.version || github.ref_name ||
github.ref.replace('refs/tags/','') (ensuring a fallback) so the group always
becomes publish-<tag> and prevents duplicate publishes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 02ad8014-e0bd-45b7-a2c2-ceacf87ca813
📒 Files selected for processing (1)
.github/workflows/publish.yml
There was a problem hiding this comment.
♻️ Duplicate comments (1)
.github/workflows/publish.yml (1)
32-35:⚠️ Potential issue | 🟠 Major | ⚡ Quick winNormalize the concurrency key to the release tag.
Line 34 still keys
workflow_dispatchruns by raw input but tag pushes by the fullrefs/tags/...ref. The same release can therefore run under different groups (publish-0.4.0,publish-v0.4.0,publish-refs/tags/v0.4.0) and race duplicate npm / Marketplace publishes.Suggested fix
concurrency: - group: publish-${{ github.event.inputs.version || github.ref }} + group: publish-${{ github.event.inputs.version && (startsWith(github.event.inputs.version, 'v') && github.event.inputs.version || format('v{0}', github.event.inputs.version)) || github.ref_name || github.ref }} cancel-in-progress: false🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/publish.yml around lines 32 - 35, Normalize the concurrency group to use the tag name (without refs/) by replacing the current expression with one that prefers github.ref_name (which yields a bare tag) and falls back to the workflow_dispatch input; update the group value currently using "publish-${{ github.event.inputs.version || github.ref }}" to "publish-${{ github.ref_name || github.event.inputs.version }}" so both tag pushes and manual dispatches produce the same normalized key.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Duplicate comments:
In @.github/workflows/publish.yml:
- Around line 32-35: Normalize the concurrency group to use the tag name
(without refs/) by replacing the current expression with one that prefers
github.ref_name (which yields a bare tag) and falls back to the
workflow_dispatch input; update the group value currently using "publish-${{
github.event.inputs.version || github.ref }}" to "publish-${{ github.ref_name ||
github.event.inputs.version }}" so both tag pushes and manual dispatches produce
the same normalized key.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 9dc0d0c8-1b1a-44a2-b08c-e8da047b3a0b
📒 Files selected for processing (2)
.github/workflows/publish.ymlpackage.json
The Marketplace job now uses azure/login (OIDC federated identity) + `vsce publish --azure-credential` instead of VSCE_PAT, since Azure DevOps blocks PAT creation for the publisher's org. Adds id-token: write to the vscode job and swaps the required secrets to AZURE_CLIENT_ID + AZURE_TENANT_ID. Open VSX still uses OVSX_PAT.
…d-credential subject
…* wildcard - dompurify 3.4.8's template-scrubbing change over-strips matterviz's two-pass sanitize_html (allowed tags like <strong>/<sub> were dropped), failing sanitize.test.ts. Pin back to 3.4.7 -- this reverts an earlier review change that removed the deliberate pin. - package-exports.test.ts now expands `*` subpath patterns (e.g. ./dist/plot/*/index.js) against the real subdirectories instead of checking a literal `*` path, so the new ./plot/* wildcard export validates.
…release share a group
…rtifact The publisher's Azure org can't issue a publish PAT or register an OIDC app, so drop the Marketplace publish (and its azure/login + release environment + id-token). The vscode job now uploads a `matterviz-vsix` artifact for manual Marketplace upload and keeps auto-publishing to Open VSX. Also format package-exports.test.ts so prek passes.
* Add tag-triggered publish CI; plot/* subpath exports; sanitize memoization Publish pipeline (.github): - New publish.yml: on a v*.*.* tag, gate on lint + unit tests, then publish in parallel the matterviz npm package, the VS Code extension (Marketplace + Open VSX), and matterviz-anywidget. workflow_dispatch supports dry_run (default true) that builds/packages/validates without publishing. - Shared `setup` composite action (Node 24 + pnpm install, optional extension installs) reused by publish, test, and lint. - Fold the former publish-anywidget.yml into publish.yml. Also bundled: - package.json: expose plot subfolders via a ./plot/* wildcard export; pin dompurify to 3.4.7 - sanitize.ts: memoize sanitize_html (skips repeated double DOMPurify passes when a component re-sanitizes many cells per render, e.g. HeatmapTable) - extensions/vscode/package.json: regenerate contributes.configuration for the new Box/Sankey/Sunburst component settings * Use npm OIDC trusted publishing (drop NPM_TOKEN) matterviz + matterviz-anywidget now publish via OIDC trusted publishing instead of a long-lived token: drop NODE_AUTH_TOKEN/NPM_TOKEN and the registry-url .npmrc scaffolding (an empty _authToken would block OIDC), ensure npm >= 11.5.1, and drop --provenance (emitted automatically under trusted publishing). VS Code Marketplace + Open VSX keep PATs (Open VSX has no OIDC yet; Marketplace OIDC needs Azure federated identity). * Make manual publish (re)build the target tag's commit workflow_dispatch now resolves the version to its `vX.Y.Z` tag and every job checks out that exact commit, so a manual run can (re)publish an already-pushed tag that never reached a registry -- instead of building whatever ref the workflow ran from. The version job verifies the tag exists (falls back to the triggering ref for a dry run of a not-yet-tagged version, and errors on a real run when the tag is missing). The version-match guard is skipped on dry runs since a not-yet-tagged ref won't match. * Address review: verify gate, reject prereleases, restore dompurify 3.4.8 - Add a `verify` job (checked out at the release tag) that all publish jobs depend on, so a version/tag mismatch blocks every upload instead of letting parallel jobs publish partially; drop the now-duplicated inline checks in the npm/vscode jobs. - Tighten the version regex to plain X.Y.Z: a prerelease would land on npm 'latest' (no --tag is passed) and the repo ships no prereleases. - Revert dompurify 3.4.7 -> 3.4.8: 3.4.8 includes 3.4.7's CVE-2026-49978 fix plus template-scrubbing / Trusted-Types fixes with no regressions, so the downgrade was unwarranted. * Publish VS Code extension via Azure OIDC instead of a PAT The Marketplace job now uses azure/login (OIDC federated identity) + `vsce publish --azure-credential` instead of VSCE_PAT, since Azure DevOps blocks PAT creation for the publisher's org. Adds id-token: write to the vscode job and swaps the required secrets to AZURE_CLIENT_ID + AZURE_TENANT_ID. Open VSX still uses OVSX_PAT. * Pin Marketplace OIDC to a `release` environment for a stable federated-credential subject * Fix CI: re-pin dompurify to 3.4.7 and teach the export test the plot/* wildcard - dompurify 3.4.8's template-scrubbing change over-strips matterviz's two-pass sanitize_html (allowed tags like <strong>/<sub> were dropped), failing sanitize.test.ts. Pin back to 3.4.7 -- this reverts an earlier review change that removed the deliberate pin. - package-exports.test.ts now expands `*` subpath patterns (e.g. ./dist/plot/*/index.js) against the real subdirectories instead of checking a literal `*` path, so the new ./plot/* wildcard export validates. * Normalize publish concurrency key so tag pushes and manual runs of a release share a group * Stop auto-publishing the VS Code Marketplace; upload the VSIX as an artifact The publisher's Azure org can't issue a publish PAT or register an OIDC app, so drop the Marketplace publish (and its azure/login + release environment + id-token). The vscode job now uploads a `matterviz-vsix` artifact for manual Marketplace upload and keeps auto-publishing to Open VSX. Also format package-exports.test.ts so prek passes.
Summary
On a
v*.*.*tag, gate on lint + unit tests + a version-match check, then publish in parallel:matterviz→ npm via OIDC trusted publishing (no token; provenance automatic)matterviz-anywidget→ npm via OIDC trusted publishingmatterviz-vsixartifact for manual upload to the VS Code MarketplaceThe VS Code Marketplace is not auto-published. The publisher's Azure DevOps org can't issue a publish PAT or register an Entra OIDC app, so the
vscodejob builds + uploads the.vsixartifact for a manual drag-drop at https://marketplace.visualstudio.com/manage. Open VSX (separate registry, token-based) is still auto-published.Other details:
workflow_dispatchwith adry_runinput (defaulttrue) builds/packages/validates everything but uploads nothing. A real manual run builds the requested version's tag, so you can (re)publish an already-pushed tag that never reached a registry.setupcomposite action (Node 24 + pnpm install, optional extension installs) is reused bypublish,test, andlint.publish-anywidget.yml(folded intopublish.yml).Also bundled in this branch:
package.json:./plot/*wildcard subpath export (e.g.import { ScatterPlot } from 'matterviz/plot/scatter') and dompurify pinned to3.4.7(3.4.8's template-scrubbing change over-strips the two-passsanitize_html).sanitize.ts: memoizesanitize_html(avoids repeated double-DOMPurify passes, e.g.HeatmapTable).extensions/vscode/package.json: regeneratedcontributes.configurationfor the new Box/Sankey/Sunburst component settings.Required repo secret
OVSX_PAT— Open VSX token (one-time namespace:npx ovsx create-namespace janosh -p <OVSX_PAT>).npm needs no secret: configure a Trusted Publisher for both
mattervizandmatterviz-anywidgeton npmjs.com pointing at this repo +publish.yml.