package-versioner
Version:
A lightweight yet powerful CLI tool for automated semantic versioning based on Git history and conventional commits.
188 lines (132 loc) • 8.82 kB
Markdown
# Versioning Strategies and Concepts
`package-versioner` offers flexible ways to determine the next version for your project based on its history and your configuration.
## How the Next Version is Calculated
There are two primary methods the tool uses to decide the version bump (e.g., patch, minor, major), configured via the `versionStrategy` option in `version.config.json`:
### 1. Conventional Commits (`versionStrategy: "conventional"`)
This is the default strategy. `package-versioner` analyzes Git commit messages since the last Git tag that follows semver patterns. It uses the [conventional-commits](https://www.conventionalcommits.org/) specification to determine the bump:
- **Patch Bump (e.g., 1.2.3 -> 1.2.4):** Triggered by `fix:` commit types.
- **Minor Bump (e.g., 1.2.3 -> 1.3.0):** Triggered by `feat:` commit types.
- **Major Bump (e.g., 1.2.3 -> 2.0.0):** Triggered by commits with `BREAKING CHANGE:` in the footer or `feat!:`, `fix!:` etc. in the header.
The specific preset used for analysis (e.g., "angular", "conventional") can be set using the `preset` option in `version.config.json`.
**Format:** `<type>(<scope>): <subject>`
`<scope>` is optional.
**Example Commit Types:**
- `feat:` (new feature for the user)
- `fix:` (bug fix for the user)
- `docs:` (changes to the documentation)
- `style:` (formatting, missing semi-colons, etc; no production code change)
- `refactor:` (refactoring production code, e.g. renaming a variable)
- `test:` (adding missing tests, refactoring tests; no production code change)
- `chore:` (updating build tasks etc; no production code change)
**References:**
- [https://www.conventionalcommits.org/](https://www.conventionalcommits.org/)
- [https://github.com/conventional-changelog/conventional-changelog](https://github.com/conventional-changelog/conventional-changelog)
### 2. Branch Pattern (`versionStrategy: "branchPattern"`)
This strategy uses the name of the current Git branch (or the most recently merged branch matching a pattern, if applicable) to determine the version bump.
You define patterns in the `branchPattern` array in `version.config.json`. Each pattern is a string like `"prefix:bumptype"`.
**Example `version.config.json`:**
```json
{
"versionStrategy": "branchPattern",
"branchPattern": [
"feature:minor",
"hotfix:patch",
"fix:patch",
"release:major"
],
"baseBranch": "main"
}
```
**How it works:**
1. The tool checks the current branch name.
2. It might also look for the most recently merged branch into `baseBranch` that matches any pattern in `branchPattern`.
3. It compares the relevant branch name (current or last merged) against the prefixes in `branchPattern`.
4. If a match is found (e.g., current branch is `feature/add-login`), it applies the corresponding bump type (`minor` in this case).
This allows you to enforce version bumps based on your branching workflow (e.g., all branches starting with `feature/` result in a minor bump).
## Monorepo Versioning Modes
While primarily used for single packages now, `package-versioner` retains options for monorepo workflows, controlled mainly by the `synced` flag in `version.config.json`.
### Synced Mode (`synced: true`)
This is the default if the `synced` flag is present and true.
- **Behavior:** The tool calculates **one** version bump based on the overall history (or branch pattern). This single new version is applied to **all** packages within the repository (or just the root `package.json` if not a structured monorepo). A single Git tag is created (e.g., `v1.2.3`).
- **Use Case:** Suitable for monorepos where all packages are tightly coupled and released together with the same version number. Also the effective mode for single-package repositories.
### Async Mode (`synced: false`)
*(Note: This mode relies heavily on monorepo tooling and structure, like `pnpm workspaces` and correctly configured package dependencies.)*
- **Behavior (Default - No `-t` flag):** The tool analyzes commits to determine which specific packages within the monorepo have changed since the last relevant commit/tag.
- It calculates an appropriate version bump **independently for each changed package** based on the commits affecting that package.
- Only the `package.json` files of the changed packages are updated.
- A **single commit** is created grouping all the version bumps, using the commit message template. **No Git tags are created** in this mode.
- **Use Case:** Suitable for monorepos where packages are versioned independently, but a single commit represents the batch of updates for traceability.
- **Behavior (Targeted - With `-t` flag):** When using the `-t, --target <targets>` flag:
- Only the specified packages (respecting the `skip` list) are considered for versioning.
- It calculates an appropriate version bump **independently for each targeted package** based on its commit history.
- The `package.json` file of each successfully updated targeted package is modified.
- An **individual Git tag** (e.g., `packageName@1.2.3`) is created **for each successfully updated package** immediately after its version is bumped.
- Finally, a **single commit** is created including all the updated `package.json` files, using a summary commit message (e.g., `chore(release): pkg-a, pkg-b 1.2.3 [skip-ci]`).
- **Important:** Only package-specific tags are created. The global tag (e.g., `v1.2.3`) is **not** automatically generated in this mode. If your release process (like GitHub Releases) depends on a global tag, you'll need to create it manually in your CI/CD script *after* `package-versioner` completes.
- **Use Case:** Releasing specific packages independently while still tagging each released package individually.
## Prerelease Handling
`package-versioner` provides flexible handling for prerelease versions, allowing both creation of prereleases and promotion to stable releases.
### Creating Prereleases
Use the `--prerelease` flag with an identifier to create a prerelease version:
```bash
# Create a beta prerelease
npx package-versioner --bump minor --prerelease beta
# Result: 1.0.0 -> 1.1.0-beta.0
```
You can also set a default prerelease identifier in your `version.config.json`:
```json
{
"prereleaseIdentifier": "beta"
}
```
### Promoting Prereleases to Stable Releases
When using standard bump types (`major`, `minor`, `patch`) with the `--bump` flag on a prerelease version, `package-versioner` will automatically clean the prerelease identifier:
```bash
# Starting from version 1.0.0-beta.1
npx package-versioner --bump major
# Result: 1.0.0-beta.1 -> 2.0.0 (not 2.0.0-beta.0)
```
This intuitive behavior means you don't need to use an empty prerelease identifier (`--prerelease ""`) to promote a prerelease to a stable version. Simply specify the standard bump type and the tool will automatically produce a clean version number.
This applies to all standard bump types:
- `--bump major`: 1.0.0-beta.1 -> 2.0.0
- `--bump minor`: 1.0.0-beta.1 -> 1.1.0
- `--bump patch`: 1.0.0-beta.1 -> 1.0.1
## Tag Templates and Configuration
`package-versioner` provides flexible configuration for how Git tags are formatted, allowing you to customize the tag structure for both single package repositories and monorepos.
### Tag Template Configuration
You can customize how tags are formatted using the following configuration options in `version.config.json`:
```json
{
"versionPrefix": "v",
"tagTemplate": "${prefix}${version}",
"packageTagTemplate": "${packageName}@${prefix}${version}"
}
```
- **versionPrefix**: The prefix used for all version numbers in tags (default: `"v"`)
- **tagTemplate**: The template for the main Git tag (default: `"${prefix}${version}"`)
- **packageTagTemplate**: The template for package-specific Git tags in monorepos (default: `"${packageName}@${prefix}${version}"`)
### Available Template Variables
The tag templates support the following variables:
- `${prefix}`: Replaced with the value of `versionPrefix`
- `${version}`: Replaced with the calculated version number
- `${packageName}`: (Only in `packageTagTemplate`) Replaced with the package name
### Examples
#### Default Tag Format
With default settings, tags will look like:
- Single repository or synced monorepo: `v1.2.3`
- Package-specific tag in async monorepo: `@scope/package-name@v1.2.3`
#### Custom Tag Format Examples
```json
{
"versionPrefix": "",
"tagTemplate": "release-${version}"
}
```
This would produce tags like `release-1.2.3` instead of `v1.2.3`.
```json
{
"versionPrefix": "v",
"packageTagTemplate": "${packageName}-${prefix}${version}"
}
```
This would produce package tags like `@scope/package-name-v1.2.3` instead of `@scope/package-name@v1.2.3`.