Repository navigation
Stage npm releases in CI and tag successful uploads - #1022
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
commit: |
There was a problem hiding this comment.
All reported issues were addressed across 7 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
|
I may merge #1024 in first, to refactor into a single package.json and doing everything at the root. |
205d457 to
f07e852
Compare
800deac to
f08410b
Compare
1de5091 to
e88519f
Compare
e88519f to
ed139f4
Compare
ed139f4 to
ed391c3
Compare
There was a problem hiding this comment.
All reported issues were addressed across 11 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 9 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
Nicolapps
left a comment
There was a problem hiding this comment.
Thanks for doing this!
I wonder whether it could be helpful to use something like the Changesets release PR feature to avoid the local step, but this is a clear improvement over what we had before
| npm ci | ||
| npm run release:validate | ||
| npm run build | ||
| npm run lint |
There was a problem hiding this comment.
Can you please consider moving this step to a separate job that doesn’t have id-token: write? This job could store the result as an artifact, which itself would be uploaded by the “stage” step. This gives us more resilience if the install/build/lint steps are maliciously changed to execute bad code. With the current implementation they could get an OIDC token
| npm ci | ||
| npm run release:validate | ||
| npm run build | ||
| npm run lint |
There was a problem hiding this comment.
Should we verify the codegen too?
There was a problem hiding this comment.
Which codegen are you referring to? Unlike a component, I don't think it ships any convex _generated code?
There was a problem hiding this comment.
You're right, we don't need to worry about generated files here
|
|
||
| jobs: | ||
| plan: | ||
| if: github.repository == 'convex/convex-helpers' && github.ref == 'refs/heads/main' |
There was a problem hiding this comment.
With this, we won’t allow this release workflow to run from other branches for alphas. Is this intentional?
If it is intentional, something we could do to improve security is to make the stage step run in a separate GitHub environment that is configured to only run on the main branch, and modifying our NPM trust settings to only allow publication from this environment. This avoids attacks where someone modifies the release.yml file to remove this check
1b4ff70 to
66bdb82
Compare
…1024) Make the repository root the publishable `convex-helpers` package, with JavaScript and declaration entrypoints in `dist/`. Keep TypeScript sources in `packages/convex-helpers/`; the Convex examples resolve imports through the root package name. - Consolidate dependencies into root devDependencies, maintain exports explicitly (including `.js`-suffixed helper imports), remove export generation and arethetypeswrong, and compile directly into root `dist/`. - Move CHANGELOG to the root and retain symlinks at the old README/CHANGELOG locations. The root README and example UI layout are already on main through #1039. - Update build/watch/pack scripts, preview publishing, frontend hosting instructions, and the backend test harness for the root package. - Adapt the existing `publish.sh` to the root package and lockfile so this PR can land independently of the new CI release flow. Trusted staging and local release orchestration are introduced separately in #1022. Review/merge **#1024 (this PR) → #1022 → #1031**. This PR is based directly on current main. Preserve the current package version (0.1.129) and the separate Convex-example typecheck. Validation: clean install/build, lint/typechecks, all 1,237 package tests. At the stack tip, the example UI production build and package-content checks also pass: 154 packed files, all explicit entrypoints present, and no example UI, release scripts, test files, or nested package.json shipped. No npm release was published.
a4710b6 to
6d48818
Compare
66bdb82 to
3a06fb2
Compare
6d48818 to
69533b1
Compare
There was a problem hiding this comment.
All reported issues were addressed across 11 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic

A release should be easy to start locally while still requiring a maintainer's npm 2FA approval before the package becomes public. This PR replaces laptop publishing and tag-triggered publishing with a version-bump commit → CI staging → Git tag flow.
Release flow
npm run releasefrom a clean checkout containing current main. It runs checks, prepares the version/changelog/lockfile, commits, and pushes to main without creating a local tag. Direct pushes and release PRs use the same CI path; neither a release-PR bot nor a merge queue is required.--watchfollows that commit's Release run and opens the approval page after success when localnpm whoamisucceeds. Without local npm authentication it still watches CI and prints manual approval instructions. Local npm login only discovers the approval-page username; CI uses trusted publishing.npm stage publish --provenance. The planner uses npm'ssemvervalidator, and staging requires nonempty notes in the current version's changelog section. Stable versions uselatest; prerelease identifiers selectalpha,beta, etc., withnextfor identifiers npm treats as version ranges.release.ymlruns plan → build/pack → stage → tag, without a matrix or reusable workflow. Installation, build, tests, and packing have no OIDC permission. The staging job downloads that run’s immutable tarball artifact and uploads it with scripts disabled; it does not install dependencies or execute package code. The tag job createsnpm/<version>at the source commit after staging succeeds. This job has GitHub write permission but performs no checkout or execution of package code. An existing matching tag is accepted; a conflicting tag is never moved.The Git tag records successful staging, not approval or public availability. A rejected candidate can keep its tag. This deliberately avoids coupling Git bookkeeping to a later interactive approval. If we eventually choose direct publishing, the upload command and npm trust permissions change; the commit and tagging flow stays the same.
Why enforce staging in npm?
Changing the workflow command alone would not protect against a rogue push that changes it back. Before enabling this flow, configure the npm trusted publisher for
convex/convex-helpers, workflowrelease.yml, to allow staged publishing only, disable direct publishing, and remove any CI credentials that bypass approval. npm then enforces the human approval boundary even if workflow code changes. GitHub's tag rules must also permit Actions to createnpm/*tags.The workflow uses
actions/setup-nodewith.nvmrc(Node 24.18.0), whose bundled npm 11.16.0 supports staged publishing. No separate npm installation is needed.The approval page is
https://www.npmjs.com/settings/<npm-username>/staged-packages; the staging log also supplies the ID fornpm stage approve <stage-id>. There is no redundantnpm run approvewrapper.Retries and boundaries
gh workflow run release.yml --ref my-alpha-branchexplicitly stages a branch release; ordinary branch pushes and opening/updating release PRs do not. The repository guard skips forks to avoid missing-trust failures. No GitHub environment is required. npm errors—including duplicate uploads—remain visible.Stack and validation
#1024 and #1039 are merged. Review/merge #1022 (this PR) → #1031. This PR retains its reviewed local release command and
npm/<version>tags. #1031 will replace custom release preparation with standard Changesets commands and simplify the release helpers.Follow-up review adjustments isolate build/pack from OIDC staging, explain the disabled cache, and allow explicit branch dispatch while keeping automatic releases limited to main. Example codegen remains checked by the existing Test and lint workflow; this release change does not add a separate codegen step.
Validation: 20 isolated release tests pass, including manual branch dispatch, earlier-commit retries, rejection of commits outside the selected ref, automatic-trigger/fork guards, artifact handoff, tag retries/conflicts, and denied tag writes. Build, lint/typechecks, all 4,128 package tests, and packed tarball checks also pass. actionlint and zizmor pass. No package was staged or published during validation.