Skip to content

Add tag-triggered publish CI (npm, anywidget, VS Code Open VSX)#357

Merged
janosh merged 9 commits into
mainfrom
add-publish-ci
Jun 7, 2026
Merged

Add tag-triggered publish CI (npm, anywidget, VS Code Open VSX)#357
janosh merged 9 commits into
mainfrom
add-publish-ci

Conversation

@janosh

@janosh janosh commented Jun 6, 2026

Copy link
Copy Markdown
Owner

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 publishing
  • VS Code extension → Open VSX (token-based), plus a matterviz-vsix artifact for manual upload to the VS Code Marketplace

The 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 vscode job builds + uploads the .vsix artifact for a manual drag-drop at https://marketplace.visualstudio.com/manage. Open VSX (separate registry, token-based) is still auto-published.

Other details:

  • workflow_dispatch with a dry_run input (default true) 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.
  • A shared setup composite action (Node 24 + pnpm install, optional extension installs) is reused by publish, test, and lint.
  • Removes publish-anywidget.yml (folded into publish.yml).

Also bundled in this branch:

  • package.json: ./plot/* wildcard subpath export (e.g. import { ScatterPlot } from 'matterviz/plot/scatter') and dompurify pinned to 3.4.7 (3.4.8's template-scrubbing change over-strips the two-pass sanitize_html).
  • sanitize.ts: memoize sanitize_html (avoids repeated double-DOMPurify passes, e.g. HeatmapTable).
  • extensions/vscode/package.json: regenerated contributes.configuration for 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 matterviz and matterviz-anywidget on npmjs.com pointing at this repo + publish.yml.

Test plan

  • Dry run: gh workflow run publish.yml -f version=0.5.0 -f dry_run=true is green
  • Next vX.Y.Z tag publishes npm + anywidget + Open VSX and uploads the matterviz-vsix artifact
  • Lint + Tests still pass via the shared setup action

…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
@coderabbitai

coderabbitai Bot commented Jun 6, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a reusable composite GitHub Action for Node 24 + pnpm, refactors lint/test workflows to use it, introduces a gated multi-target publish workflow, inserts VS Code config schemas for box/sankey/sunburst, adds ./plot/* export subpaths, and memoizes HTML sanitization.

Changes

CI/CD Infrastructure Consolidation

Layer / File(s) Summary
Composite setup action
.github/actions/setup/action.yml
New composite action configures Node 24, enables corepack/pnpm, runs root pnpm install, and optionally installs dependencies for listed extension directories.
Lint and test workflows refactored
.github/workflows/lint.yml, .github/workflows/test.yml
Replaces inline Node/corepack/pnpm setup and per-extension installs with the local ./.github/actions/setup invocation in lint (prek/knip) and test (unit/e2e) jobs.
Unified publishing workflow
.github/workflows/publish.yml
Adds publish.yml to normalize release version/ref, gate on lint/test/verify, and run npm, VS Code extension, and anywidget publishing jobs with conditional dry-run and OIDC support.

Feature and Packaging Updates

Layer / File(s) Summary
Visualization configuration schemas
extensions/vscode/package.json
Adds matterviz.box.*, matterviz.sankey.*, and matterviz.sunburst.* configuration property groups to the VS Code extension schema.
Package exports and sanitize memoization
package.json, src/lib/sanitize.ts
Adds ./plot/* export subpath mappings and implements a bounded module-level memoization cache for sanitize_html.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • janosh/matterviz#150: Related to structure-from-string changes referenced by vector search.
  • janosh/matterviz#351: Introduced the prior anywidget publish workflow removed/merged into the new unified publish.yml.

Suggested labels

ci, vscode, dx, ecosystem

Poem

🐰 A rabbit hopped and wired the flow,

One setup action helps builds to go.
Boxes, sankeys, sunbursts bloom bright,
Sanitized HTML keeps it light.
Hops, releases — into the night.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a tag-triggered publish CI workflow for npm, anywidget, and VS Code extension publishing.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-publish-ci

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
.github/actions/setup/action.yml (1)

12-15: ⚡ Quick win

Consider enabling pnpm caching for faster CI runs.

The actions/setup-node action supports built-in dependency caching via cache: '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

📥 Commits

Reviewing files that changed from the base of the PR and between dc36716 and ba5f1f6.

📒 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.yml
  • extensions/vscode/package.json
  • package.json
  • src/lib/sanitize.ts
💤 Files with no reviewable changes (1)
  • .github/workflows/publish-anywidget.yml

Comment thread .github/workflows/publish.yml
Comment thread .github/workflows/publish.yml Outdated
Comment thread .github/workflows/publish.yml Outdated
Comment thread package.json
janosh added 3 commits June 6, 2026 19:53
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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between 4bc399d and f181ce4.

📒 Files selected for processing (1)
  • .github/workflows/publish.yml

Comment thread .github/workflows/publish.yml Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
.github/workflows/publish.yml (1)

32-35: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Normalize the concurrency key to the release tag.

Line 34 still keys workflow_dispatch runs by raw input but tag pushes by the full refs/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

📥 Commits

Reviewing files that changed from the base of the PR and between f181ce4 and f4d9692.

📒 Files selected for processing (2)
  • .github/workflows/publish.yml
  • package.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.
janosh added 4 commits June 6, 2026 21:46
…* 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.
…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.
@janosh janosh changed the title Add tag-triggered publish CI (npm + VS Code Marketplace/Open VSX + anywidget) Add tag-triggered publish CI (npm, anywidget, VS Code Open VSX) Jun 7, 2026
@janosh
janosh merged commit 7621d56 into main Jun 7, 2026
12 checks passed
@janosh
janosh deleted the add-publish-ci branch June 7, 2026 04:48
@janosh janosh added ci Continuous integration dx Developer experience ecosystem MatterViz reaching/interfacing different platforms vscode VSCode extension labels Jun 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci Continuous integration dx Developer experience ecosystem MatterViz reaching/interfacing different platforms vscode VSCode extension

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant