| title | Configuration |
|---|---|
| description | Complete reference for .craft.yml configuration |
Project configuration for Craft is stored in .craft.yml in the project root.
Craft tries to determine GitHub repo information from the local git repo. You can also hard-code it:
github:
owner: getsentry
repo: sentry-javascriptThis command runs on your release branch as part of craft prepare. Default: bash scripts/bump-version.sh.
preReleaseCommand: bash scripts/bump-version.shThe command is executed with the following environment variables:
CRAFT_OLD_VERSION: The previous version (or0.0.0if no previous version exists)CRAFT_NEW_VERSION: The new version being released
The script should:
- Use these environment variables to perform version replacement
- Replace version occurrences
- Not commit changes
- Not change git state
Note: For backward compatibility, the old and new versions are also passed as the last two command-line arguments to the script, but using environment variables is safer and recommended.
Example script:
#!/bin/bash
set -eux
# Use CRAFT_NEW_VERSION provided by craft
export npm_config_git_tag_version=false
npm version "${CRAFT_NEW_VERSION}"When minVersion: "2.21.0" or higher is set and no custom preReleaseCommand is defined, Craft automatically bumps version numbers based on your configured publish targets. This eliminates the need for a scripts/bump-version.sh script in most cases.
- Craft examines your configured
targetsin.craft.yml - For each target that supports version bumping, Craft updates the appropriate project files
- Targets are processed in the order they appear in your configuration
- Each target type is only processed once (e.g., multiple npm targets won't bump
package.jsontwice)
| Target | Detection | Version Bump Method |
|---|---|---|
npm |
package.json exists |
npm version --no-git-tag-version (with workspace support) |
pypi |
pyproject.toml exists |
hatch, poetry, setuptools-scm, or direct edit |
crates |
Cargo.toml exists |
cargo set-version (requires cargo-edit) |
gem |
*.gemspec exists |
Direct edit of gemspec and lib/**/version.rb |
pub-dev |
pubspec.yaml exists |
Direct edit of pubspec.yaml |
hex |
mix.exs exists |
Direct edit of mix.exs |
nuget |
*.csproj exists |
dotnet-setversion or direct XML edit |
For npm/yarn/pnpm monorepos, Craft automatically detects and bumps versions in all workspace packages:
- npm 7+: Uses
npm version --workspacesto bump all packages at once - yarn/pnpm or npm < 7: Falls back to bumping each non-private package individually
Workspace detection checks for:
workspacesfield in rootpackage.json(npm/yarn)pnpm-workspace.yaml(pnpm)
Private packages ("private": true) are skipped during workspace version bumping.
For Python projects, Craft detects the build tool and uses the appropriate method:
- Hatch - If
[tool.hatch]section exists βhatch version <version> - Poetry - If
[tool.poetry]section exists βpoetry version <version> - setuptools-scm - If
[tool.setuptools_scm]section exists β No-op (version derived from git tags) - Direct edit - If
[project]section withversionfield exists β Editpyproject.tomldirectly
To enable automatic version bumping, ensure your .craft.yml has:
minVersion: '2.21.0'
targets:
- name: npm # or pypi, crates, etc.
# ... other targetsAnd either:
- Remove any custom
preReleaseCommand, or - Don't define
preReleaseCommandat all
To disable automatic version bumping while still using minVersion 2.21.0+:
minVersion: '2.21.0'
preReleaseCommand: '' # Explicitly set to empty stringOr define a custom script:
minVersion: '2.21.0'
preReleaseCommand: bash scripts/my-custom-bump.shIf automatic version bumping fails:
- Missing tool: Craft reports which tool is missing (e.g., "Cannot find 'npm' for version bumping")
- Command failure: Craft shows the error from the failed command
- No supported targets: Craft warns that no targets support automatic bumping
In all error cases, Craft suggests defining a custom preReleaseCommand as a fallback.
If version bumping succeeds but craft prepare fails mid-way (e.g., during changelog generation or git operations), you may need to clean up manually:
-
Check the release branch: If a release branch was created, you can delete it:
git branch -D release/<version>
-
Revert version changes: If files were modified but not committed, reset them:
git checkout -- package.json pyproject.toml Cargo.toml # or whichever files were changed -
Re-run prepare: Once the issue is fixed, run
craft prepareagain. Version bumping is idempotentβrunning it multiple times with the same version is safe.
:::tip
Use craft prepare --dry-run first to preview what changes will be made without modifying any files.
:::
This command runs after a successful craft publish. Default: bash scripts/post-release.sh.
postReleaseCommand: bash scripts/post-release.shOverride the release branch prefix. Default: release.
releaseBranchPrefix: publishFull branch name: {releaseBranchPrefix}/{version}
Craft supports simple and auto changelog management modes.
Reminds you to add a changelog entry:
changelog: CHANGESOr with options:
changelog:
filePath: CHANGES.md
policy: simpleAutomatically generates changelog from commits:
changelog:
policy: autoAuto mode uses .github/release.yml to categorize PRs. This file follows GitHub's release.yml format with Craft-specific extensions.
Craft extends GitHub's format with two additional fields:
| Field | Description |
|---|---|
commit_patterns |
Array of regex patterns to match commit/PR titles (in addition to labels) |
semver |
Version bump type for auto-versioning: major, minor, or patch |
:::caution[Required for Version Detection]
The semver field is required for Craft's automatic version detection to work.
If you define a custom .github/release.yml without semver fields, PRs will
still appear in the changelog but won't contribute to suggested version bumps.
The changelog preview will show "None" for semver impact.
:::
If .github/release.yml doesn't exist, Craft uses these defaults based on Conventional Commits:
changelog:
exclude:
labels:
- skip-changelog
categories:
- title: Breaking Changes π
commit_patterns:
- "^(?<type>\\w+(?:\\((?<scope>[^)]+)\\))?!:\\s*)"
semver: major
- title: New Features β¨
commit_patterns:
- "^(?<type>feat(?:\\((?<scope>[^)]+)\\))?!?:\\s*)"
semver: minor
- title: Bug Fixes π
commit_patterns:
- "^(?<type>fix(?:\\((?<scope>[^)]+)\\))?!?:\\s*)"
- '^Revert "'
semver: patch
- title: Documentation π
commit_patterns:
- "^(?<type>docs?(?:\\((?<scope>[^)]+)\\))?!?:\\s*)"
semver: patch
- title: Internal Changes π§
commit_patterns:
- "^(?<type>(?:build|refactor|meta|chore|ci|ref|perf)(?:\\((?<scope>[^)]+)\\))?!?:\\s*)"
semver: patchchangelog:
categories:
- title: Features
labels:
- enhancement
commit_patterns:
- "^(?<type>feat(?:\\((?<scope>[^)]+)\\))?!?:\\s*)"
semver: minor
- title: Bug Fixes
labels:
- bug
commit_patterns:
- "^(?<type>fix(?:\\((?<scope>[^)]+)\\))?!?:\\s*)"
semver: patchBy default, the changelog entry for a PR is generated from its title. However, PR authors can override this by adding a "Changelog Entry" section to the PR description. This allows for more detailed, user-facing changelog entries without cluttering the PR title.
To use this feature, add a markdown heading (level 2 or 3) titled "Changelog Entry" to your PR description, followed by the desired changelog text:
### Description
Add `foo` function, and add unit tests to thoroughly check all edge cases.
### Changelog Entry
Add a new function called `foo` which prints "Hello, world!"
### Issues
Closes #123The text under "Changelog Entry" will be used verbatim in the changelog instead of the PR title. If no such section is present, the PR title is used as usual.
-
Multiple Entries: If you use multiple top-level bullet points in the "Changelog Entry" section, each bullet will become a separate changelog entry:
### Changelog Entry - Add OAuth2 authentication - Add two-factor authentication - Add session management
-
Nested Content: Indented bullets (4+ spaces or tabs) are preserved as nested content under their parent entry:
### Changelog Entry - Add authentication system - OAuth2 support - Two-factor authentication - Session management
This will generate:
- Add authentication system by @user in [#123](url) - OAuth2 support - Two-factor authentication - Session management
Note: Nested items do NOT get author/PR attribution - only the top-level entry does.
-
Plain Text: If no bullets are used, the entire content is treated as a single changelog entry. Multi-line text is automatically joined with spaces to ensure valid markdown output.
-
Content Isolation: Only content within the "Changelog Entry" section is included in the changelog. Other sections (Description, Issues, etc.) are ignored.
Changes are automatically grouped by scope (e.g., feat(api): groups under "Api"):
changelog:
policy: auto
scopeGrouping: true # defaultScope headers are only shown for scopes with more than one entry. Entries without a scope are listed at the bottom of each category section without a sub-header.
Example output with scope grouping:
### New Features
#### Api
- Add user endpoint by @alice in [#1](https://github.com/...)
- Add auth endpoint by @bob in [#2](https://github.com/...)
#### Ui
- Add dashboard by @charlie in [#3](https://github.com/...)
- General improvement by @dave in [#4](https://github.com/...)
By default, conventional commit prefixes are stripped from changelog entries.
The type (e.g., feat:) is removed, and the scope is preserved when entries
aren't grouped under a scope header.
This behavior is controlled by named capture groups in commit_patterns:
(?<type>...)- The type prefix to strip (includes type, scope, and colon)(?<scope>...)- Scope to preserve when not under a scope header
| Original Title | Scope Header | Displayed Title |
|---|---|---|
feat(api): add endpoint |
Yes (Api) | Add endpoint |
feat(api): add endpoint |
No | (api) Add endpoint |
feat: add endpoint |
N/A | Add endpoint |
To disable stripping, provide custom patterns using non-capturing groups:
commit_patterns:
- "^feat(?:\\([^)]+\\))?!?:" # No named groups = no strippingYou can exclude PRs or commits from the changelog in several ways:
Add #skip-changelog anywhere in your commit message or PR body:
chore: Update dependencies
#skip-changelog
PRs with the skip-changelog label are automatically excluded.
Configure exclusions in .github/release.yml:
changelog:
exclude:
labels:
- skip-changelog
- dependencies
authors:
- dependabot[bot]
- renovate[bot]| Option | Description |
|---|---|
changelog |
Path to changelog file (string) OR configuration object |
changelog.filePath |
Path to changelog file. Default: CHANGELOG.md |
changelog.policy |
Mode: none, simple, or auto. Default: none |
changelog.scopeGrouping |
Enable scope-based grouping. Default: true |
Configure default versioning behavior:
versioning:
policy: auto # auto, manual, or calver| Policy | Description |
|---|---|
auto |
Analyze commits to determine version bump (default when using craft prepare auto) |
manual |
Require explicit version argument |
calver |
Use calendar-based versioning |
For projects using calendar-based versions:
versioning:
policy: calver
calver:
format: '%y.%-m' # e.g., 24.12 for December 2024
offset: 14 # Days to look back for date calculationFormat supports:
%y- 2-digit year%m- Zero-padded month%-m- Month without padding
Require a minimum Craft version:
minVersion: '0.5.0'Setting minVersion to 2.21.0 or higher enables smart defaults that simplify configuration:
minVersion: '2.21.0'| Feature | Default with minVersion >= 2.21.0 |
Default without |
|---|---|---|
changelog.policy |
auto |
none |
versioning.policy |
auto (with >= 2.14.0) |
manual |
This means a minimal configuration like this:
minVersion: '2.21.0'
targets:
- name: npm
- name: githubWill automatically:
- Generate changelogs from conventional commits
- Determine version bumps from commit analysis
- Create
CHANGELOG.mdif it doesn't exist
:::tip[Recommended for New Projects]
Use minVersion: '2.21.0' for new projects to take advantage of smart defaults.
Run craft init to automatically generate this configuration.
:::
Ensure specific artifacts exist before publishing:
requireNames:
- /^sentry-craft.*\.tgz$/
- /^gh-pages.zip$/Configure build status checks:
statusProvider:
name: github
config:
contexts:
- Travis CI - BranchConfigure where to fetch artifacts from:
artifactProvider:
name: github # or 'gcs' or 'none'By default, the GitHub artifact provider looks for artifacts named exactly as the commit SHA. You can customize this with the artifacts configuration option.
- Regex patterns: Wrapped in
/(e.g.,/^build-.*$/) - Exact strings: Plain text (e.g.,
build,release-artifacts)
1. Single artifact pattern - searches all workflows:
artifactProvider:
name: github
config:
artifacts: /^sentry-.*\.tgz$/2. Multiple artifact patterns - searches all workflows:
artifactProvider:
name: github
config:
artifacts:
- /^sentry-.*\.tgz$/
- release-bundle3. Workflow-scoped patterns - filter by workflow name:
artifactProvider:
name: github
config:
artifacts:
build: release-artifacts # exact workflow β exact artifact
/^build-.*$/: artifacts # workflow pattern β exact artifact
ci: # exact workflow β multiple artifacts
- /^output-.*$/
- bundle
/^release-.*$/: # workflow pattern β multiple artifacts
- /^dist-.*$/
- checksumsFetch artifacts named craft-binary and craft-docs from the "Build & Test" workflow:
artifactProvider:
name: github
config:
artifacts:
Build & Test:
- craft-binary
- craft-docsFetch all .tgz files from any workflow:
artifactProvider:
name: github
config:
artifacts: /\.tgz$/When artifacts is not configured, the provider uses the legacy behavior where it searches for an artifact with a name matching the commit SHA exactly. This ensures existing configurations continue to work without changes.
List release targets in your configuration:
targets:
- name: npm
- name: github
- name: registry
id: browser
type: sdk
onlyIfPresent: /^sentry-browser-.*\.tgz$/See Target Configurations for details on each target.
These options apply to all targets:
| Option | Description |
|---|---|
includeNames |
Regex: only matched files are processed |
excludeNames |
Regex: matched files are skipped |
id |
Unique ID for the target (use with -t target[id]) |
Example:
targets:
- name: github
includeNames: /^.*\.exe$/
excludeNames: /^test.exe$/