Skip to content

feat(release): add npm publishing workflow for connector-protocol and collector-runtime - #32

Merged
tnunamak merged 1 commit into
mainfrom
npm-release-workflow
Aug 21, 2026
Merged

feat(release): add npm publishing workflow for connector-protocol and collector-runtime#32
tnunamak merged 1 commit into
mainfrom
npm-release-workflow

Conversation

@tnunamak

Copy link
Copy Markdown
Contributor

Summary

@pdpp/connector-protocol and @pdpp/collector-runtime are both marked "private": true with no CI path to npm, which is why the two downstream repos that need them (pdpp, data-connectors) currently vendor a committed .tgz tarball instead of a real npm dependency. This PR adds an npm-release workflow for just these two packages, modeled on pdpp's existing semantic-release.yml (OIDC trusted publishing, no stored NPM_TOKEN) but with commit-scope gating this repo needs and pdpp doesn't, since this repo also ships an unrelated Tauri desktop app with its own commit history that must never trigger an npm release.

Full reasoning — including two non-obvious @semantic-release/commit-analyzer behaviors (glob, not regex, scope matching; a naive catch-all rule silently cancelling real releases in a mixed commit batch) confirmed by testing against the real installed plugin rather than its docs — is in the commit message.

Test plan

  • Root + per-package typecheck clean
  • scripts/npm-release-commit-analyzer.test.ts — 18/18 pass, including the two bug-reproduction cases
  • npm run release:npm:dry-run loads all plugins and correctly refuses to run off main
  • npm pack both packages + install in an isolated (non-workspace) directory + Node ESM resolution smoke test — clean, no @pdpp/reference-contract reference anywhere (re-verifies fix(collector-runtime): remove unpublishable @pdpp/reference-contract dependency #31's fix survives packaging)
  • Known pre-existing gap, confirmed via git stash against unmodified main: both packages' verify script fails at the lint step (a Biome formatting mismatch in connector-protocol, a self-expiring lint-suppression check in collector-runtime) for reasons unrelated to this PR. This workflow's quality job runs verify, so the first real release run will fail there until someone fixes those two checks in a separate PR — flagging explicitly rather than silently shipping a workflow that can't complete a release yet.

Assisted-by: AI

… collector-runtime

@pdpp/connector-protocol and @pdpp/collector-runtime (two packages in this
repo, PDP-Connect/data-connect, under packages/connector-protocol and
packages/collector-runtime) currently have no way to publish to npm: both
are marked "private": true, and no CI workflow here calls `npm publish` or
`semantic-release` for them. This blocks the org's plan to stop vendoring
prebuilt tarballs of these two packages into two downstream repos (pdpp and
data-connectors), which today copy in a committed .tgz file instead of
depending on a real npm version.

This change adds that publishing path, modeled on the workflow
PDP-Connect/pdpp already runs in production for three of its own packages
(@pdpp/cli, @pdpp/mcp-server, @pdpp/read-core):

- .releaserc.yaml configures semantic-release (a tool that reads Conventional
  Commit messages like "fix(scope): ..." or "feat(scope): ..." to decide the
  next version number and changelog automatically, instead of a human picking
  a version by hand) to publish both packages together, from one shared
  version number, whenever a commit lands on `main`.
- .github/workflows/npm-release.yml is a new workflow for GitHub Actions
  (GitHub's built-in CI system, which runs shell/tool steps in response to
  repo events like a push) that runs semantic-release on every push to
  `main`. It publishes using OIDC trusted publishing — npm's mechanism for
  letting one specific GitHub Actions workflow publish on an account's
  behalf without a long-lived password-like token. The workflow grants
  itself `id-token: write`, a GitHub Actions permission that lets the job
  request a short-lived identity token proving which exact workflow and repo
  it is; npm's registry checks that token against the trusted-publisher
  config on the package instead of requiring an NPM_TOKEN secret. This is
  the same mechanism pdpp's own release workflow already uses.
- Unlike pdpp, this repository also ships an unrelated Tauri desktop app
  (Tauri is the framework that builds this repo's installable desktop
  builds) with its own commit history — commits like "feat(playwright-runner):
  ..." that have nothing to do with these two npm packages. If every fix/feat
  commit triggered a release the way pdpp's config does, an unrelated desktop
  commit would cut a pointless npm release. .releaserc.yaml's releaseRules
  section restricts releases to commits scoped to `connector-protocol` or
  `collector-runtime` (including this repo's existing convention of listing
  several affected workspaces in one scope, e.g. "fix(collector-runtime,
  connector-protocol): ...").

That scope restriction needed two non-obvious fixes, both confirmed by
running the actual installed semantic-release plugin
(@semantic-release/commit-analyzer, the plugin that reads commit messages
and decides release type) against real commit messages, not by reading its
documentation:

1. Its `scope` matching uses shell-glob syntax (the same wildcard syntax as
   a .gitignore pattern), not JavaScript regular expressions, even though a
   glob and a regex can look similar for simple patterns.
2. A single catch-all rule of the form "anything not already matched
   releases nothing" does not behave as "ignore extra commits" the way it
   sounds — it behaves as "if this batch contains ANY commit that isn't
   scoped to connector-protocol/collector-runtime, cancel the whole
   release," because the plugin ranks "release nothing" as higher priority
   than any real version bump when multiple rules match different commits
   in the same batch. Verified by constructing a batch with one
   collector-runtime fix commit and one unrelated fix commit together: the
   naive rule produced zero release for either. The two catch-all rules in
   this file are written so neither one can ever match a commit that a real
   release rule already matched, which avoids that cancellation.

scripts/npm-release-commit-analyzer.test.ts exercises the real
analyzeCommits() call from this exact .releaserc.yaml against 18 commit-message
cases (14 single commits, 4 multi-commit batches), including the two bug
scenarios above, so a future edit to the scope rules that reintroduces either
bug fails a test here instead of silently breaking releases.

scripts/semantic-release-github-output.ts is a small, generic helper
(ported from pdpp's own copy, which is likewise untested there) that turns
semantic-release's dry-run log or publish result into GitHub Actions step
outputs — GitHub Actions' mechanism for one job to pass a value (here,
whether a release actually happened, and its version) to a later job in the
same workflow run. npm-release.yml uses this to decide whether its two
later jobs — `quality` (running each package's existing lint/typecheck/test
`verify` script) and `release` (the actual `npm publish` step) — run at all,
so a push with no release-worthy commit skips both instead of running them
for nothing.

packages/connector-protocol/package.json and
packages/collector-runtime/package.json both: drop "private": true (the
field that currently makes `npm publish` refuse to run for these two
packages at all); add a `publishConfig` block setting public access and
turning on npm's provenance attestation (a signed, publicly verifiable
record of exactly which GitHub Actions run and commit produced a given
published version — on here since this repo is public, unlike pdpp's
private-repo default of leaving it off, since provenance would otherwise
expose private-repo details); add `repository.directory` so npm's registry
page and that provenance record point at the correct subdirectory of this
monorepo instead of its root; and change collector-runtime's dependency on
connector-protocol from a bare "*" (which in a real published package would
resolve against any version ever published, not the workspace copy) to an
explicit ">=0.0.1 <1.0.0" range. Both packages also gain a README.md, since
neither had one and npm's registry page for a package is built from it.

Verified: root and per-package `typecheck` are clean on this branch; the 18
new commit-analyzer tests pass; a semantic-release dry run
(`release:npm:dry-run`) loads every configured plugin successfully and
correctly refuses to run outside of `main` (this branch is not `main`, so
that refusal is the expected result — the on-`main` dry-run path itself is
still unexercised and will only run for real once this lands).

Not fixed by this change, and pre-existing on `main` before this branch
(confirmed by stashing this branch's changes and re-running the same
commands against unmodified `main`): both packages' own `verify` script,
which chains typecheck, lint, and test into one command, fails at the lint
step for two unrelated reasons. connector-protocol's `biome.jsonc` (the
config file for Biome, this repo's JS/TS formatter-and-linter) has a
formatting mismatch the tool itself flags. collector-runtime fails a
separate, self-expiring check this repo already has in place: a script that
starts failing on purpose once a previously-agreed deadline passes, as a
forced reminder to revisit a lint rule the team had temporarily disabled.
The new workflow's `quality` job runs these same `verify` scripts, so the
first real release run will hit these same two failures and the `quality`
job will fail — not a regression introduced here, but it does mean the
workflow cannot successfully publish until someone fixes those two
pre-existing check failures separately.

Assisted-by: AI
Signed-off-by: Tim Nunamaker <tnunamak@gmail.com>
@tnunamak
tnunamak merged commit dbecbd0 into main Aug 21, 2026
7 of 9 checks passed
@tnunamak
tnunamak deleted the npm-release-workflow branch August 21, 2026 18:38
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.

1 participant