Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6647729
feat(calendar): add support for CalVer
wikando-ck Dec 15, 2025
3df9a50
feat(calendar): test edge cases
wikando-ck Dec 15, 2025
46989ec
feat(calendar): fix minor/patch bumps
wikando-ck Dec 16, 2025
dddf40a
refactor(calendar): rename to calendar / CalendarVersioningStrategy
wikando-ck Dec 16, 2025
46dc06c
refactor(calendar): restructure file contents
wikando-ck Dec 16, 2025
dc4a476
refactor(calendar): fix prettier issues
wikando-ck Dec 16, 2025
0ae1b03
docs(calendar): add documentation for CalVer
wikando-ck Dec 16, 2025
e8fd0f4
test(calendar): test more edge-cases regarding major/minor bumps with…
wikando-ck Dec 16, 2025
120612b
fix(calendar): fix four-segment support
wikando-ck Dec 16, 2025
0e311f5
feat(calendar): ensure january starts with week 1
wikando-ck Dec 16, 2025
b96a0e2
refactor(calendar): harden regexes and add missing error handling in …
wikando-ck Dec 16, 2025
8938fc5
refactor(calendar): add handling for dates older than the 2000 epoch
wikando-ck Dec 16, 2025
66d05f9
docs(calendar): document four-segment behavior
wikando-ck Dec 16, 2025
96f6806
chore(calendar): remove preMajor options
wikando-ck Dec 16, 2025
03454f6
refactor(calendar): rename date-format to calver-scheme
wikando-ck Dec 16, 2025
c522c28
test(calendar): add tests to document MAJOR bumping behaviour on date…
wikando-ck Dec 16, 2025
568358b
refactor(calendar): reduce bump method complexity
wikando-ck Dec 16, 2025
e31966b
refactor(calendar): replace regexp based createVersionFromString by b…
wikando-ck Dec 16, 2025
3bca933
refactor(calendar): simplify CalendarVersionUpdate.bump() and date ex…
wikando-ck Dec 16, 2025
4291caa
refactor(calendar): replace formatSegment switch with lookup table
wikando-ck Dec 16, 2025
aa0235f
refactor(calendar): extract determineBumpType function
wikando-ck Dec 16, 2025
87a135a
refactor(calendar): simplify formatVersion by removing redundant toke…
wikando-ck Dec 16, 2025
6fb601c
refactor(calendar): use matchAll in parseFormat
wikando-ck Dec 16, 2025
b89a1c9
refactor(calendar): consolidate date tokens into DATE_TOKENS Set
wikando-ck Dec 16, 2025
4636663
refactor(calendar): extract CalVerToken type alias
wikando-ck Dec 16, 2025
678518c
feat(calendar): add cli-args and config schema for calver-scheme
wikando-ck Dec 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions __snapshots__/cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ Options:
[string]
--date-format format in strftime format for updating dates
[string]
--calver-scheme CalVer scheme format for calendar versioning
(e.g., YYYY.0M.MICRO) [string]
--label comma-separated list of labels to add to
from release PR
[default: "autorelease: pending"]
Expand Down
145 changes: 145 additions & 0 deletions docs/calendar-versioning.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
# Calendar Versioning (CalVer)

Release Please supports [calendar-based versioning](https://calver.org) through the `calendar`
versioning strategy. This allows you to create versions based on dates combined with
optional semantic version segments.

## Configuration

To use calendar versioning, set the `versioning` option to `calendar` in your
release-please configuration:

```json
{
"versioning": "calendar"
}
```

You can customize the CalVer scheme using the `calver-scheme` option:

```json
{
"versioning": "calendar",
"calver-scheme": "YYYY.0M.MICRO"
}
```

## Format Tokens

CalVer format strings are composed of tokens that represent date or semantic
segments.

### Date Segments

Date segments are automatically updated based on the current date when a release is
created.

| Token | Description | Example |
|--------|----------------------------------------------------|---------|
| `YYYY` | Full year | 2024 |
| `YY` | Short year without zero-padding (relative to 2000) | 24 |
| `0Y` | Zero-padded short year (relative to 2000) | 06, 24 |
| `MM` | Month without zero-padding | 1, 12 |
| `0M` | Zero-padded month | 01, 12 |
| `WW` | Week of year without zero-padding | 1, 52 |
| `0W` | Zero-padded week of year | 01, 52 |
| `DD` | Day without zero-padding | 1, 31 |
| `0D` | Zero-padded day | 01, 31 |

Week numbers are calculated such that January 1 is always in week 1, and weeks roll
over on Monday. This differs from ISO 8601 week numbering where January 1 may fall in
week 52/53 of the previous year.

### Semantic Segments

Semantic segments work like traditional semver and are incremented based on commit
types.

| Token | Description | Bumped By |
|---------|----------------------------|------------------|
| `MAJOR` | Major version number | Breaking changes |
| `MINOR` | Minor version number | Features |
| `MICRO` | Micro/patch version number | Bug fixes |

## Default Format

The default format is `YYYY.0M.0D`, which produces versions like `2024.01.15`.

## Example Formats

| Format | Example Version | Description |
|--------------------|-----------------|----------------------------------|
| `YYYY.0M.0D` | `2024.01.15` | Full date-based version |
| `YY.MM.MICRO` | `24.1.3` | Year, month, and patch counter |
| `YYYY.MINOR.MICRO` | `2024.2.1` | Year with semantic minor/patch |
| `YY.0M.0D` | `24.01.15` | Short year with zero-padded date |
| `YYYY.0M.MICRO` | `2024.06.0` | Year, month, and patch counter |

## Behavior

### Date Changes

When the date changes (based on the format tokens), semantic segments (`MAJOR`,
`MINOR`, `MICRO`) are reset to 0. For example:

- Current version: `2024.01.5` (format: `YYYY.0M.MICRO`)
- Date changes to February
- Next version: `2024.02.0` (MICRO resets to 0)

### Semantic Bumping

When the date has not changed, semantic segments are bumped based on conventional
commits:

- **Breaking changes** bump `MAJOR` (or `MINOR` if no `MAJOR` in format)
- **Features** bump `MINOR` (or `MICRO` if no `MINOR` in format)
- **Bug fixes** bump `MICRO`

When a higher segment is bumped, lower segments reset to 0:

- Current version: `2024.2.5` (format: `YYYY.MINOR.MICRO`)
- Feature commit added
- Next version: `2024.3.0` (MINOR bumped, MICRO reset)

### Release-As

You can force a specific version using the `Release-As` footer in commit messages:

```
feat: add new feature

Release-As: 2024.06.0
```

## Multiple Releases Per Day

If you release multiple times on the same day using a pure date format like
`YYYY.0M.0D`, each release will have the same version number (which may cause
issues). To support multiple releases per day, include a semantic segment:

```json
{
"versioning": "calendar",
"calver-scheme": "YYYY.0M.0D.MICRO"
}
```

This produces versions like `2024.01.15.0`, `2024.01.15.1`, etc.

## Four-Segment Versions

CalVer supports formats with four segments, such as `YY.MM.MINOR.MICRO` which might
produce versions like `24.6.5.123`. However, there's an important limitation to be
aware of:

**Version Object Mapping**: Internally, Release Please uses a `Version` object that
only has three semantic fields: `major`, `minor`, and `patch`. When using four-segment
formats:

- The first three segments map to `major.minor.patch`
- The fourth segment is preserved only in the string representation
- The fourth segment is not accessible via the Version object's properties

This limitation doesn't affect version bumping or comparison, but it's important to
understand if you're programmatically accessing version properties.

11 changes: 6 additions & 5 deletions docs/customizing.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,9 @@ version given a list of parsed commits.
|---------------------|-------------------------------------------------------------------------------------------------------------|
| `default` | Breaking changes bump the major version, features bump the minor version, bugfixes bump the patch version |
| `always-bump-patch` | Always bump patch version. This is useful for backporting bugfixes to previous major/minor release branches |
| `always-bump-minor` | Always bump minor version | |
| `always-bump-major` | Always bump major version |
| `always-bump-minor` | Always bump minor version |
| `always-bump-major` | Always bump major version |
| `calendar` | [Calendar-based versioning (CalVer)](calendar-versioning.md) with customizable date formats |
| `service-pack` | Designed for Java backport fixes. Uses Maven's specification for service pack versions (e.g. 1.2.3-sp.1) |
| `prerelease` | Bumping prerelease number (eg. 1.2.0-beta01 to 1.2.0-beta02) or if prerelease type is set, using that in the prerelease part (eg. 1.2.1 to 1.3.0-beta). Works together with the "prerelease" settings from [manifest-releaser](/docs/manifest-releaser.md) (see for more infos) - A prerelease version number will only be created if the prerelease setting is set to `true`. Default: `false`. For more information, see [Manifest Driven release-please](manifest-releaser.md). |

Expand Down Expand Up @@ -101,11 +102,11 @@ title or body format).

The default pull request title uses this pattern:
`chore${scope}: release${component} ${version}` so a common release pull
request title would be `chore(main): release foo-bar v1.2.3`.
Please note that by default `${component}` will be parsed to ` ${component}` (With space in front of).
request title would be `chore(main): release foo-bar v1.2.3`.
Please note that by default `${component}` will be parsed to ` ${component}` (With space in front of).
If you wish to avoid that, consider using `component-no-space: true`/`--component-no-space=true` parameter.

> [!WARNING]
> [!WARNING]
> Setting `component-no-space` option when release PR already exists might break the parsing
> resulting in another PR being opened.

Expand Down
5 changes: 5 additions & 0 deletions schemas/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,10 @@
"description": "Date format given as a strftime expression for the generic strategy.",
"type": "string"
},
"calver-scheme": {
"description": "CalVer scheme format for calendar versioning (e.g., YYYY.0M.MICRO).",
"type": "string"
},
"extra-files": {
"description": "Specify extra generic files to replace versions.",
"type": "array",
Expand Down Expand Up @@ -491,6 +495,7 @@
"always-update": true,
"tag-separator": true,
"date-format": true,
"calver-scheme": true,
"extra-files": true,
"version-file": true,
"snapshot-label": true,
Expand Down
4 changes: 4 additions & 0 deletions src/factories/versioning-strategy-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {AlwaysBumpPatch} from '../versioning-strategies/always-bump-patch';
import {AlwaysBumpMinor} from '../versioning-strategies/always-bump-minor';
import {AlwaysBumpMajor} from '../versioning-strategies/always-bump-major';
import {ServicePackVersioningStrategy} from '../versioning-strategies/service-pack';
import {CalendarVersioningStrategy} from '../versioning-strategies/calendar';
import {GitHub} from '../github';
import {ConfigurationError} from '../errors';
import {PrereleaseVersioningStrategy} from '../versioning-strategies/prerelease';
Expand All @@ -30,6 +31,8 @@ export interface VersioningStrategyFactoryOptions {
bumpPatchForMinorPreMajor?: boolean;
prereleaseType?: string;
prerelease?: boolean;
dateFormat?: string;
calverScheme?: string;
github: GitHub;
}

Expand All @@ -44,6 +47,7 @@ const versioningTypes: Record<string, VersioningStrategyBuilder> = {
'always-bump-major': options => new AlwaysBumpMajor(options),
'service-pack': options => new ServicePackVersioningStrategy(options),
prerelease: options => new PrereleaseVersioningStrategy(options),
calendar: options => new CalendarVersioningStrategy(options),
};

export function buildVersioningStrategy(
Expand Down
5 changes: 5 additions & 0 deletions src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export interface ReleaserConfig {
extraLabels?: string[];
initialVersion?: string;
dateFormat?: string;
calverScheme?: string;

// Changelog options
changelogSections?: ChangelogSection[];
Expand Down Expand Up @@ -192,6 +193,7 @@ interface ReleaserConfigJson {
'initial-version'?: string;
'exclude-paths'?: string[]; // manifest-only
'date-format'?: string;
'calver-scheme'?: string;
}

export interface ManifestOptions {
Expand All @@ -217,6 +219,7 @@ export interface ManifestOptions {
commitSearchDepth?: number;
logger?: Logger;
dateFormat?: string;
calverScheme?: string;
}

export interface ReleaserPackageConfig extends ReleaserConfigJson {
Expand Down Expand Up @@ -1416,6 +1419,7 @@ function extractReleaserConfig(
initialVersion: config['initial-version'],
excludePaths: config['exclude-paths'],
dateFormat: config['date-format'],
calverScheme: config['calver-scheme'],
};
}

Expand Down Expand Up @@ -1779,6 +1783,7 @@ function mergeReleaserConfig(
extraLabels: pathConfig.extraLabels ?? defaultConfig.extraLabels,
excludePaths: pathConfig.excludePaths ?? defaultConfig.excludePaths,
dateFormat: pathConfig.dateFormat ?? defaultConfig.dateFormat,
calverScheme: pathConfig.calverScheme ?? defaultConfig.calverScheme,
};
}

Expand Down
1 change: 1 addition & 0 deletions src/updaters/release-please-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ function releaserConfigToJsonConfig(
'version-file': config.versionFile,
'snapshot-label': config.snapshotLabels?.join(','), // Java-only
'date-format': config.dateFormat,
'calver-scheme': config.calverScheme,
};
return jsonConfig;
}
Loading
Loading