Publish to PyPI with trusted publishing and check uv.lock in CI - #32
Merged
Merged
Conversation
Documents the two mirrored Python/Node packages, the test layer split and its env vars, the cross-file invariants (loaded-model API, lazy Processor format init, shared VAD inference via frame userdata, fail-open), and the logging and parity conventions. Points at DEVELOPMENT.md as the authoritative long-form reference rather than duplicating it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The publish-python job now authenticates over OIDC instead of a stored PYPI_API_TOKEN: it gains id-token: write and drops the password input, so PyPI verifies the repository, workflow file, and publish environment and no long-lived PyPI credential exists. The project is not yet on PyPI, so this is registered as a pending publisher that creates it on first publish. npm has no pending-publisher equivalent and only accepts a trusted publisher on an existing package, so the first release still uses the NPM_TOKEN bootstrap secret the workflow already expects. DEVELOPMENT.md records that sequence and that the secret is deleted afterwards. CI also gains uv lock --check. It runs before uv sync --dev, which would otherwise re-lock and mask the drift it is meant to catch. npm ci already fails on a stale package-lock.json, so this gives the Python side the same guarantee and keeps both lockfiles trustworthy as dependency-scanner input. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Contributor
Author
|
@andresovela both pending publisher and NPM token have been configured. Running the publish pipeline should work. |
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
Release credentials.
publish-pythonnow authenticates to PyPI over OIDC trusted publishing instead of a stored API token. It gainsid-token: writeand drops thepassword:input, so PyPI verifies the repository, workflow filename, and deployment environment on each publish and no long-lived PyPI credential exists anywhere.ai-coustics-livekit-pluginis not on PyPI yet, so this is registered as a pending publisher that creates the project on first publish.npm is deliberately left on the bootstrap token. npm only accepts a trusted publisher in the settings of a package that already exists and has no pending-publisher equivalent, so the first release still uses the
NPM_TOKENsecret the workflow already expects.DEVELOPMENT.mdrecords that sequence, including deleting the secret once the trusted publisher is configured.Lockfile integrity. CI gains
uv lock --check.npm cialready fails on a stalepackage-lock.json; this gives the Python side the same guarantee so both lockfiles stay trustworthy as dependency-scanner input. Aikido parsesuv.lockandpackage-lock.jsonnatively, and both are already committed, so no new lockfiles orrequirements.txtexport were needed.Docs.
DEVELOPMENT.mdgains the real registry configuration and a Dependency scanning section. AddsCLAUDE.mdcovering repo layout, commands, cross-file architecture invariants, and the logging/parity conventions.Pre-Landing Review
One real bug caught and fixed in this branch's own diff:
.github/workflows/ci.yml—uv lock --checkwas placed afteruv sync --dev.uv syncre-locks when the lockfile is stale, so the check would always pass and the guard was inert. Fixed by moving it beforeuv sync. Verified empirically: injected a dependency intopyproject.tomlonly, confirmeduv lock --checkfails on the drift, then confirmed a subsequentuv sync --devmakes it pass again, which is exactly the masking the original ordering would have produced.Informational, not changed here:
pypa/gh-action-pypi-publish@release/v1is a floating ref while the other third-party actions in the same file (astral-sh/setup-uv,softprops/action-gh-release) are SHA-pinned. Left as-is because PyPA recommends the floatingrelease/v1so attestation and OIDC changes land automatically. Worth noting that moving to OIDC reduces the blast radius here: a compromised action can no longer exfiltrate a long-lived token, only a short-lived scoped one.npm auditreports 1 low-severity advisory (esbuild dev-server arbitrary file read on Windows, transitive via tsup/vitest). Pre-existing, dev-only, not shipped in the published package. Out of scope for this branch.Adversarial Review
Reduced coverage, stated explicitly rather than silently: the Codex adversarial pass timed out after 5 minutes and produced no output, and the Claude adversarial subagent was not dispatched. Findings below are from a manual adversarial pass.
Verified by hand:
permissions:replaces the workflow-level default entirely, socontents: readis restated alongsideid-token: write; nothing is silently dropped.ai-coustics/livekit-plugins, workflow filenamerelease.yml, environmentpublish. Thepublishenvironment already exists and is gated to*.*.*tags, which matches the release trigger.uv lock --checknow runs as the firstuvcommand, before any virtualenv exists. Re-verified in a clean clone with a cold cache to confirm the reordering does not depend on a prioruv sync.uv lock --checkcatches manifest/lock drift, not dependency tampering. The docs claim only drift.Test plan
ruff check/ruff format --checkcleanmypyclean (7 source files)tsc --noEmitclean,tsupbuild succeedsuv lock --checkandnpm ciconfirm neither lockfile has driftedRelease-path publishing itself cannot be tested before merge, since it only runs on a pushed tag.
Required before the next release tag
Merging this alone is not sufficient. The release will fail at
publish-pythonunless the pending publisher is registered first.ai-coustics-livekit-plugin, ownerai-coustics, repositorylivekit-plugins, workflowrelease.yml, environmentpublish. Note it does not reserve the name, and the project is created owned by the account that registers it.NPM_TOKENgranular access token (read+write on the@ai-cousticsscope) to thepublishenvironment for the first npm publish only.NPM_TOKEN.🤖 Generated with Claude Code