Scoped Releases: Release with precision using scoped version, pack, and publish commands
Published on

Scoped Releases: New version, pack, and publish commands

Authors

Manage releases across workspaces with --scope: version, pack, publish

Releasing changes in a growing project or monorepo shouldn't require bespoke scripts. vlt now supports graph-aware release operations with version, vlt pack, and vlt publish. Each command supports the full Dependency Selector Syntax via --scope. You can bump versions, create tarballs, and publish only the workspaces you intend. We also added --publish-directory to both pack and publish so you can bundle into a directory (e.g. dist/) before packaging or publishing.

tl;dr

  • vlt version <inc> with --scope="<selector>" bumps the version in matched packages.
  • vlt pack --scope="<selector>" creates tarballs in each matched package.
  • vlt publish --scope="<selector>" publishes matched workspaces.
  • vlt pack and vlt publish use --publish-directory=<path> to publish from a different output directory.
  • --scope accepts the full selector syntax and works great with workspaces.

Why this is useful

  • Target only what matters: release a subset of workspaces (e.g., apps, services) without touching the rest.
  • Bundle first, then ship: build to a different directory during prepack, then pack/publish only the artifacts using --publish-directory.
  • Safer, repeatable flows: version, then pack to verify artifacts, then publish. All of this is driven by selectors.

How it works

vlt evaluates the selector passed with --scope against your project's dependency graph. For version, pack, and publish, matched items are typically your configured workspaces. version applies the increment to each matched workspace. pack produces npm-compatible tarballs, and publish sends packages to your registry. Both pack and publish respect --publish-directory to run from a different working directory.

For the complete selector reference, see the docs: Dependency Selector Syntax.

Examples

Version a package by name and all its workspace dependencies

$ vlt version patch --scope="#package-a, #package-a :workspace"
package-a: v1.4.1
package-b: v1.2.1 # dependent on package-a
package-c: v3.2.1 # dependent on package-b

Version a subset of workspaces that start with app-

$ vlt version patch --scope=":workspace[name^=app-]"
app-web: v1.2.4
app-admin: v0.9.1

Version prereleases for apps found in ./apps

$ vlt version prerelease --scope=':path("apps/*")'
apps/web: v1.4.1-pre.0
apps/api: v2.8.4-pre.0
apps/admin: v0.9.1-pre.0

Version and pack workspaces by name

Use this flow to validate artifacts before publishing.

$ vlt version minor --scope=':workspace:is(#web, #api)'
web: v1.4.0
api: v2.9.0

$ vlt pack --scope=':workspace:is(#web, #api)'
๐Ÿ“ฆ Package: [email protected]
๐Ÿ“„ File: web-1.4.0.tgz
๐Ÿ“ 52 Files
... (truncated)
๐Ÿ“Š Package Size: 1.2 MB
๐Ÿ“‚ Unpacked Size: 1.1 MB
๐Ÿ”’ Shasum: ...
๐Ÿ” Integrity: ...

๐Ÿ“ฆ Package: [email protected]
๐Ÿ“„ File: api-2.9.0.tgz
๐Ÿ“ 108 Files
... (truncated)
๐Ÿ“Š Package Size: 2.9 MB
๐Ÿ“‚ Unpacked Size: 2.8 MB
๐Ÿ”’ Shasum: ...
๐Ÿ” Integrity: ...

Version and publish all public workspaces

$ vlt version patch ":workspace:not(:private)"
app-web: v1.4.1
app-server: v2.9.1

$ vlt publish ":workspace:not(:private)" --tag=latest
๐Ÿ“ฆ Package: [email protected]
๐Ÿท๏ธ Tag: latest
๐Ÿ“ก Registry: https://registry.npmjs.org/
๐Ÿ“ 52 Files
... (truncated)
๐Ÿ“Š Package Size: 1.2 MB
๐Ÿ“‚ Unpacked Size: 1.1 MB
๐Ÿ”’ Shasum: ...
๐Ÿ” Integrity: ...

๐Ÿ“ฆ Package: [email protected]
๐Ÿ“„ File: app-server-2.9.1.tgz
๐Ÿ“ 108 Files
... (truncated)
๐Ÿ“Š Package Size: 2.9 MB
๐Ÿ“‚ Unpacked Size: 2.8 MB
๐Ÿ”’ Shasum: ...
๐Ÿ” Integrity: ...

Build to --publish-directory during prepack and then pack and publish

Bundle to dist/ during prepack, then package/publish from that directory. This allows you to publish a set of files that is completely different than what is in the source directory. This is helpful when bundling or compiling your project before publishing.

package.json:

{
  "name": "app-web",
  "version": "1.4.1",
  "private": false,
  "scripts": {
    "prepack": "vite build && cp package.json dist/ && cp README.md dist/"
  }
}

Pack from dist/:

$ vlt pack --scope="#app-web" --publish-directory=dist

Publish from dist/:

$ vlt publish --scope="#app-web" --publish-directory=dist --tag=latest

Publish all packages that have a prepack script

You can combine this with selectors to publish many packages that follow the same build layout.

$ vlt publish --scope=':workspace:path("packages/*"):attr(scripts, [prepack])' --publish-directory=dist --tag=latest
๐Ÿ“ฆ Package: @org/[email protected]
๐Ÿ“„ File: @org/foo-0.7.0.tgz
๐Ÿ“ 52 Files
... (truncated)
๐Ÿ“Š Package Size: 1.2 MB
๐Ÿ“‚ Unpacked Size: 1.1 MB
๐Ÿ”’ Shasum: ...
๐Ÿ” Integrity: ...

๐Ÿ“ฆ Package: @org/[email protected]
๐Ÿ“„ File: @org/bar-3.2.1.tgz
๐Ÿ“ 108 Files
... (truncated)
๐Ÿ“Š Package Size: 2.9 MB
๐Ÿ“‚ Unpacked Size: 2.8 MB
๐Ÿ”’ Shasum: ...
๐Ÿ” Integrity: ...

Learn more

Have questions or feedback about --scope, --publish-directory, or releasing with vlt? Join us on Discord or open an issue on GitHub.

Join the waitlist

Curious to learn more about vlt? Join our waitlist and get early access.

Sign up