feat(release): add npm publishing workflow for connector-protocol and collector-runtime - #32
Merged
Merged
Conversation
… 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
@pdpp/connector-protocoland@pdpp/collector-runtimeare both marked"private": truewith no CI path to npm, which is why the two downstream repos that need them (pdpp,data-connectors) currently vendor a committed.tgztarball instead of a real npm dependency. This PR adds an npm-release workflow for just these two packages, modeled onpdpp's existingsemantic-release.yml(OIDC trusted publishing, no storedNPM_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-analyzerbehaviors (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
typecheckcleanscripts/npm-release-commit-analyzer.test.ts— 18/18 pass, including the two bug-reproduction casesnpm run release:npm:dry-runloads all plugins and correctly refuses to run offmainnpm packboth packages + install in an isolated (non-workspace) directory + Node ESM resolution smoke test — clean, no@pdpp/reference-contractreference anywhere (re-verifies fix(collector-runtime): remove unpublishable @pdpp/reference-contract dependency #31's fix survives packaging)git stashagainst unmodifiedmain: both packages'verifyscript 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'squalityjob runsverify, 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