Monorepos & Release Workspaces
A release workspace is an independently versioned release unit inside one
repository. Each workspace gets its own versions, tags, release branches,
changelog, and targets, while sharing one .craft.yml.
Use release workspaces when one repository ships products on separate release
cycles, such as a CLI at 1.4.0 and an MCP server at 0.3.0.
Define workspaces
Section titled “Define workspaces”Add a top-level workspaces map. Each key is a directory path relative to
.craft.yml; each value is a release unit configuration. Release workspaces
require minVersion: 2.29.0 or later.
minVersion: 2.29.0github: owner: getsentry repo: toolkitchangelog: policy: autoworkspaces: packages/cli: releaseBranchPrefix: release/cli targets: - name: github tagPrefix: 'cli@' - name: npm tools/mcp: releaseBranchPrefix: release/mcp targets: - name: github tagPrefix: 'mcp@'Releasing 1.2.3 for each workspace produces the tags cli@1.2.3 and
mcp@1.2.3 on the branches release/cli/1.2.3 and release/mcp/1.2.3, so
the two products never collide.
Inheritance
Section titled “Inheritance”Top-level settings are defaults for every workspace. A workspace overrides a setting by declaring it:
- These fields can be set per workspace:
targets,changelog,versioning,releaseBranchPrefix,preReleaseCommand,postReleaseCommand,requireNames,statusProvider,artifactProvider,noMerge, andgithub. - A declared field replaces the inherited value. For example, a
workspace’s
targetslist replaces the top-level list, and a workspacechangelogobject replaces the top-level object. githubis the exception: it is merged, so a workspace can override justownerorrepo.
Run craft config --workspace <path> to print the fully resolved
configuration for one workspace.
Paths and patterns
Section titled “Paths and patterns”Keys may be literal paths or glob patterns. Craft expands patterns to the
concrete directories that exist relative to .craft.yml, and ignores files
matched by a glob.
workspaces: plugins/*: targets: - name: npmWorkspace paths use nonempty ASCII path segments containing letters, digits,
periods, underscores, and hyphens. Segments cannot be ., .., __proto__,
or start with -. Craft rejects overlapping workspace keys, so each concrete
path belongs to exactly one key.
A configuration with workspaces cannot use github.projectPath.
Placeholders
Section titled “Placeholders”String settings can use placeholders that expand for the selected workspace. They work in workspace settings and in the top-level settings a workspace inherits:
| Placeholder | Value for static/packages/scraps |
|---|---|
{{workspace}} |
static/packages/scraps |
{{workspaceName}} |
scraps |
{{packageName}} |
@sentry/scraps |
{{packageSimpleName}} |
sentry-scraps |
{{packageName}} and {{packageSimpleName}} read name from the workspace’s
package.json; Craft fails if the file or its name is missing. Other
{{...}} markers, such as the npm target’s {{name}}, {{simpleName}}, and
{{version}} artifact templates, are left alone.
One pattern can then cover independently released packages:
workspaces: static/packages/*: releaseBranchPrefix: '{{workspaceName}}/release' statusProvider: name: github config: contexts: ['{{workspaceName}}-package-pack'] artifactProvider: name: github config: artifacts: Package: ['{{workspaceName}}-tarball'] targets: - name: github tagPrefix: '{{workspaceName}}@' - name: npm access: publicReleasing static/packages/scraps at 1.2.3 uses the branch
scraps/release/1.2.3 and the tag scraps@1.2.3.
Select a workspace
Section titled “Select a workspace”Once workspaces is defined, every release command needs to know which
workspace to act on. Pass --workspace <path> or set CRAFT_WORKSPACE:
craft prepare auto --workspace packages/clicraft publish 1.2.3 --workspace packages/cli
CRAFT_WORKSPACE=tools/mcp craft changelogCraft fails with the list of available workspaces when none is selected. Use a concrete path, not a glob pattern.
craft workspace list prints every concrete workspace path as a JSON array,
which is handy for CI matrices:
$ craft workspace list[ "packages/cli", "tools/mcp"]Tags and version history
Section titled “Tags and version history”Give each workspace’s github target its own tagPrefix. Craft scopes every
tag lookup (latest tag, changelog base, CalVer scans) to that prefix, so
products never pick up each other’s version history. See
GitHub target → Tag Prefixes.
Inside a workspace, GitHub release titles also carry the tag prefix (for
example cli@0.2.0), so releases of different products stay distinguishable.
See GitHub target → Release Workspaces.
Workspace-aware changelogs
Section titled “Workspace-aware changelogs”When a workspace is selected, its changelog file defaults to
<workspace>/CHANGELOG.md (when the workspace is a directory). Set
changelog.filePath to override it.
Automatic changelogs include commits that change
files in the selected workspace and commits that change shared files outside
all workspaces. Commits confined to another workspace are excluded. Craft
detects package ownership from pnpm, npm, or Yarn workspaces as well as the
release workspaces in .craft.yml. Commits spanning workspaces appear in each
affected workspace’s changelog. This also scopes
automatic version detection.
If a release includes files from another workspace, add its directory to
includePaths on that release unit. Paths are relative to the repository root,
match whole directory names, and cannot escape the repository:
workspaces: packages/cli: changelog: policy: auto includePaths: - apps/cli-docsMost workspaces need no includePaths setting. Craft rejects includePaths and
unownedPaths in configurations without workspaces, because there is no
workspace ownership to adjust.
Files outside every workspace, such as root build files or an application that
lives at the repository root, count as shared by default. Use
changelog.unownedPaths to change that:
include(default): shared files appear in this workspace’s changelog.exclude: this workspace ignores shared files.own: this workspace owns them, and every other workspace ignores them. At most one workspace can useown.
For example, when the main application lives at the repository root next to independently released packages:
workspaces: app: changelog: policy: auto unownedPaths: own packages/ui: changelog: policy: autoapp gets commits to root files, and packages/ui gets only commits that touch
packages/ui.
A workspace’s first release has no previous tag. Craft then starts its
changelog at the commit that added the workspace to .craft.yml instead of the
start of the repository history. Push a tag with the workspace’s tag prefix to
start from a different commit.
Version bumps
Section titled “Version bumps”Without a preReleaseCommand,
automatic version bumping runs in
the selected workspace’s directory, so releasing packages/cli bumps only the
project files in packages/cli. An npm target there bumps that package and any
package workspaces it defines itself, not the rest of a repository-wide pnpm,
npm, or Yarn workspace. Workspaces that are not directories bump from the
directory that contains .craft.yml.
A custom preReleaseCommand still runs from the directory that contains
.craft.yml.
Publishing follows the workspace too: a NuGet target with workspaces: true
publishes only the projects inside the selected workspace, while solutionPath
stays relative to the repository root. See
NuGet → Release Workspaces.
GitHub Actions
Section titled “GitHub Actions”The Craft Action and reusable release workflow accept a
workspace input:
jobs: release: permissions: contents: write uses: getsentry/craft/.github/workflows/release.yml@v2 with: version: auto workspace: packages/cli secrets: inheritPublish request titles include the full workspace path, for example
publish: getsentry/toolkit/packages/cli@1.2.3. A workflow cannot set both
path and workspace.
The changelog preview workflow needs no
workspace input. It previews each pull request in every workspace whose
changelog would list it, using the same path rules as the release changelog,
and posts a single comment or status that names each of those workspaces with
its version bump. Running craft changelog --pr <number> without a workspace
does the same.
Known limitations
Section titled “Known limitations”- GitHub tracks a single “Latest” release per repository, so the badge can move between products. See the GitHub target.
Alternative: one config per product
Section titled “Alternative: one config per product”Before release workspaces, monorepos used one .craft.yml per product, each
with its own github target tagPrefix and a matching releaseBranchPrefix,
and ran Craft from that product’s directory (the path input in GitHub
Actions). This still works, but release workspaces keep everything in one file
and add workspace-aware changelogs.