Repository navigation
Drop main, module, browser, and types from package.json, publish on version tags, and harden workflows - #4211
Merged
Merged
Conversation
…publint - Publish runs when a `v*` tag is pushed, after admins run `npm version` and push the commit and tag together, so version commits land on `main`. It checks that the tag matches `package.json`, is on `main`, and isn't published yet. - Publish builds in a read-only job and publishes `lib/` from a separate job, so no dependency code runs with `id-token: write`. - Update Screenshots regenerates screenshots in a read-only job, and removes the label and pushes from separate jobs that run no dependencies. - `vp pack` lints the packed package with publint. - Remove `skipLibCheck` from `tsconfig.vite.json` and `tsconfig.website.json`. - Document the workflow conventions and the release steps in AGENTS.md. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`exports` covers every entry point, and publint suggested replacing `browser` with an `exports` condition. Tools that ignore `exports`, like TypeScript's `node10` module resolution, webpack 4, and Jest before 28, no longer resolve the package. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`rm -r test/**/screenshots/**` only reached screenshot directories one level under `test/`, as bash's `globstar` is off. Tracked screenshots are now deleted with `git ls-files`, and a single `SCREENSHOTS` pattern is shared by the git commands and the upload. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Check that the tag is on main and matches the package version in separate steps, and drop the npm check: tags can't be pushed twice, and `npm publish` rejects an existing version. - List `prerelease` in the release steps, as `patch`, `minor`, and `major` all turn a beta into a stable release. - Use the `github:` shorthand for `repository`, and drop `homepage` and `bugs`, which npm derives from it when publishing. - Enable provenance in `publishConfig`, so publishing outside of CI fails. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
nstepien
marked this pull request as ready for review
October 19, 2026 16:53
main, module, browser, and types from package.json, publish on version tags, and harden workflows
- Publish: create the GitHub release in its own job, so the job with `id-token: write` has no write token, and a failed release can be re-run on its own once the version is already on npm. - Publish: read the version with `npm pkg get version`. - Update Screenshots: only match PNGs, so the job that pushes can only commit images, not files the read-only job could have planted. - AGENTS.md: the write jobs never install the project's dependencies, which is what they guarantee, as the publish job still sets up Vite+. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Start from GitHub's `main` and tags, and push the version commit with the tag named explicitly. With `--follow-tags`, a tag that a fetch pruned before the push left only `main` to push, so nothing was published; now the push fails instead, and other local tags stay out of it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
With `push.followTags` set, naming the tag isn't enough to keep other local annotated tags reachable from `main` out of the release push. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
`vp run version <prerelease|patch|minor|major|x.y.z>` switches to `main`, matches it and the local tags to GitHub's, then runs `npm version` with the arguments, as `vp run` appends them to the last command. The push stays a separate command, as Vite Task doesn't run commands through a shell on Windows, so the tag name can't come from `$(...)` in a task. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Comment on lines
-24
to
33
| "main": "./lib/index.js", | ||
| "module": "./lib/index.js", | ||
| "browser": "./lib/index.js", | ||
| "types": "./lib/index.d.ts", | ||
| "exports": { | ||
| "./lib/styles.css": "./lib/styles.css", | ||
| ".": { | ||
| "types": "./lib/index.d.ts", | ||
| "default": "./lib/index.js" | ||
| } |
Collaborator
There was a problem hiding this comment.
https://tsdown.dev/options/package-exports#enabling-auto-exports
Should we consider enabling this?
Collaborator
Author
Collaborator
|
Need a simple summary to understand the change and benefit 😄 |
Collaborator
Author
Well... it's more secure now! |
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.

The Publish workflow ran
npm versionitself but could only push the tag, as the default branch ruleset blocks its push tomain, so the7.0.0-beta.61version commit never landed onmain. Nownpm versionruns locally, and pushing the version commit and its tag runs the workflow.How to release
versiontask invite.config.tsrunsgit switch main,git fetch --prune --prune-tags origin, which also deletes local tags that aren't on GitHub, andgit reset --keep origin/main, which movesmainto GitHub's and keeps uncommitted changes, whichnpm versionthen refuses to run with. It stops at the first failure.vp runappends the arguments to the task's last command,npm version, which commits the new version and creates an annotatedv<version>tag. During the beta, useprereleaseto go from7.0.0-beta.61to7.0.0-beta.62, aspatch,minor, andmajorall produce7.0.0.$(...)doesn't work in a task. Naming the tag makes the push fail if the tag is missing, and--no-follow-tagsleaves other local tags out, even withpush.followTagsset.--atomicpushesmainand the tag together, or neither. With--follow-tags, a missing tag only pushesmain, so nothing is published.fetch.pruneTagsenabled. If the push can't find the tag, recreate it, then push again:Only
nstepienandamanmahajan7can push tomainwithout a pull request and create tags, so only they can release (see Repository settings). The same steps are in the header ofpublish.yml.vpr versionis short forvp run version, and the commands work in both Bash and PowerShell.Changes
v*tags instead ofworkflow_dispatch. Before installing anything, it checks that the tagged commit is onmainand that the tag matches the version fromnpm pkg get version. Tags can't be pushed twice, andnpm publishrejects a version that already exists.id-token: write, every step can request an OIDC token, so any dev dependency thatvp installorvp run ciran could have published to npm as react-data-grid.buildjob runs the checks and CI, and uploadslib/.publishjob (id-token: write) downloadslib/and publishes it without installing the project's dependencies. It still sets up Vite+, sonpmis the version thatdevEnginesasks for, andsetup-vpdownloads the Vite+ install script when it runs.npm publish --ignore-scriptsskips thepreparescript (vp config), so no project code runs either.releasejob (contents: write) creates the GitHub release withgh release create --verify-tag, which fails instead of creating a tag if the tag is missing. As a separate job, the job that can publish to npm has no write token, and a failed release can be re-run on its own once the version is already on npm.remove-label(pull-requests: write) only runs gh, without checking out the repository.screenshots(read-only) installs the dependencies, regenerates the screenshots, and uploads them if they changed.push(contents: write) checks out the commit the screenshots were taken on, swaps in the new ones, commits, and pushes. The push fails if the branch moved since, as before. When nothing changed, this job is skipped, so no job with write access runs.test/**/screenshots/, at any depth, are handed fromscreenshotstopush, so the job that pushes can only commit images.vp packnow lints the tarball thatnpm pack --ignore-scriptsproduces, in strict mode, so warnings fail the build like errors do. It reports no issues.main,module,browser, andtypesare removed frompackage.json, asexportscovers every entry point. publint suggested replacingbrowserwith anexportscondition. Tools that ignoreexportsno longer resolve the package, like TypeScript'snode10module resolution (removed in TypeScript 7), webpack 4, and Jest before 28.package.jsonmetadata:repositoryuses thegithub:Comcast/react-data-gridshorthand, andhomepageandbugsare removed, as npm derives all three when publishing.publishConfig.provenanceis enabled, which trusted publishing already does, so publishing from outside CI fails by default (npm publish --no-provenanceoverrides it).npm publishlogs that it converted therepositorystring to an object, which is expected.skipLibCheckis removed fromtsconfig.vite.jsonandtsconfig.website.json.The Publish workflow's file name is unchanged, so npm's trusted publisher config still matches.
Repository settings
These changed in the repository settings alongside this PR, so they're not in the diff.
GITHUB_TOKENis now read-only, and Actions can no longer create or approve pull requests. Every workflow sets its ownpermissions, so this doesn't affect them, only a workflow that would omit them. "Require actions to be pinned to a full-length commit SHA" was already on.testandCodeQLchecks. Onlynstepienandamanmahajan7can bypass it, to push the version commit tomain. The Admin role's bypass for pull requests is removed, so other admins need an approval and passing checks to merge.main, force-push it, or add merge commits. These rules moved out of "Default branch", whose bypass would otherwise cover them.nstepienandamanmahajan7can create tags, so only they can start a release.Verification
vp run cipasses, andvp run typecheckpasses without the cache.npm versioncreated an annotated tag andgit push --atomic --no-follow-tags origin main "v$(npm pkg get version)"pushed both, without another local tag onmain, which--follow-tagswould have pushed too. Withpush.followTagsset, the same push without--no-follow-tagssent that other tag as well. After a fetch withfetch.pruneTagsdeleted the new tag, the push failed and pushed nothing, while--follow-tagspushedmainalone. The recreated tag matched the onenpm versioncreates. The checks passed for the new tag, and failed for a tag that doesn't matchpackage.jsonand a tag offmain. With npm 12,npm pkg get versionprints the bare version.vp run version prereleaseswitched tomain, deleted a local-only tag, resetmain, and created the7.0.0-beta.62commit and tag, which the push then sent. The task stopped beforenpm versionwhengit reset --keeprefused to overwrite a local change to a file that GitHub'smainhad changed, andnpm versionrefused to run with an uncommitted change. A pre-commit hook running a cachedvp runtask worked inside the task.@npmcli/package-json, whichnpm publishuses to readpackage.json, therepositoryshorthand is normalized togit+https://github.com/Comcast/react-data-grid.git, the URL that provenance is verified against, andhomepageandbugsare derived from it.lib/and nonode_modules,npm publish --dry-run --ignore-scriptspacked the 4lib/files plusLICENSE,README.md, andpackage.json. Without--ignore-scripts, it ranprepare.pushjob committed a modified, a deleted, and an added screenshot, including in a directory two levels belowtest/, and the push was rejected as non-fast-forward when the PR branch had moved after the screenshots were taken.gh auth setup-githandsGH_TOKENto git. With the*.pngpattern, a plantedtest/browser/screenshots/x.test.tsx/evil.test.tsxwas neither detected as a change nor staged, while a new PNG in a new directory was.gh release create --reposkips its local git lookups, and--verify-tagchecks the tag through the API, so thereleasejob doesn't need a checkout.package.jsonproblems, a missing file (an error) and an invalidrepository(a warning) each failvp pack, including in a local run outside CI.The next release will be Publish's first run. Update Screenshots runs a PR's own workflow file, and its
remove-labelandscreenshotsjobs passed on this PR.pushwas skipped, as no screenshot changed, so its first run will be the next label that changes screenshots.🤖 Generated with Claude Code