eliashaeussler/version-bumper

Composer plugin to bump project versions during release preparations

Installs: 24 863

Dependents: 7

Suggesters: 0

Security: 0

Stars: 2

Watchers: 1

Forks: 0

Open Issues: 2

Type:composer-plugin

1.3.0 2024-11-17 21:23 UTC

README

Version Bumper

Coverage Maintainability CGL Tests Supported PHP Versions

A Composer plugin to bump project versions during release preparations. Provides a Composer command bump-version and offers an easy-to-use PHP API for integration in other frameworks.

🔥 Installation

Packagist Packagist Downloads

composer require --dev eliashaeussler/version-bumper

⚡ Usage

Console command bump-version

Tip

The <range> command option can be omitted if version range auto-detection is properly configured.

$ composer bump-version [<range>] [-c|--config CONFIG] [-r|--release] [--dry-run] [--strict]

Pass the following options to the console command:

  • <range>: Version range to be bumped, can be one of:
    • major/maj: Bump version to next major version (1.2.3 -> 2.0.0)
    • minor/min: Bump version to next minor version (1.2.3 -> 1.3.0)
    • next/n/patch/p: Bump version to next patch version (1.2.3 -> 1.2.4)
    • Explicit version, e.g. 1.3.0
  • -c/--config: Path to config file, defaults to auto-detection in current working directory, can be configured in composer.json as well (see config section below).
  • -r/--release: Create a new Git tag after versions are bumped.
  • --dry-run: Do not perform any write operations, just calculate and display version bumps.
  • --strict: Fail if any unmatched file pattern is reported.

Version range auto-detection

Normally, an explicit version range or version is passed to the bump-version command. However, it may become handy if a version range is auto-detected, based on the Git history. This sort of auto-detection is automatically triggered if the <range> command option is omitted.

Important

Auto-detection is only possible if versionRangeIndicators are configured in the config file.

To use the auto-detection feature, make sure to add version range indicators to your config file:

versionRangeIndicators:
  # 1️⃣ Bump major version on breaking changes, determined by commit message
  - range: major
    patterns:
      - type: commitMessage
        pattern: '/^\[!!!]/'

  # 2️⃣ Bump major version if controllers are deleted and API schema changes
  - range: major
    # All configured patterns must match to use this indicator
    strategy: matchAll
    patterns:
      - type: fileDeleted
        pattern: '/^src\/Controller\/.+Controller\.php$/'
      - type: fileModified
        pattern: '/^res\/api\.schema\.json$/'

  # 3️⃣ Bump minor version when new features are added
  - range: minor
    patterns:
      - type: commitMessage
        pattern: '/^\[FEATURE]/'

  # 4️⃣ Bump patch version if maintenance or documentation tasks were performed
  - range: patch
    patterns:
      - type: commitMessage
        pattern: '/^\[TASK]/'
      - type: commitMessage
        pattern: '/^\[BUGFIX]/'
      - type: commitMessage
        pattern: '/^\[DOCS]/'

  # 5️⃣ Bump patch version if no sources have changed
  - range: patch
    # No configured patterns must match to use this indicator
    strategy: matchNone
    patterns:
      - type: fileAdded
        pattern: '/^src\//'
      - type: fileDeleted
        pattern: '/^src\//'
      - type: fileModified
        pattern: '/^src\//'

Note

The matching version range with the highest priority will be used as final version range (major receives the highest priority).

If no version range indicator matches, the bump-version command will fail.

Strategies

The strategy config option (see second indicator in the above example) defines how matching (or non-matching) patterns are treated to mark the whole indicator as "matching".

By default, an indicator matches if any of the configured patterns matches (matchAny). If all patterns must match, matchAll can be used.

In some cases, it may be useful to define a version range if no pattern matches. This can be achieved by the matchNone strategy.

Examples

Using the above example, the following version range would result if given preconditions are met:

Notes:

1) Even if both indicators 2️⃣ and 4️⃣ match, indicator 2️⃣ takes precedence because of the higher version range.

2) Indicator 2️⃣ does not match, because only one pattern matches, and the indicator's strategy is configured to match all patterns (matchAll).

3) No indicator contains patterns for either the commit message or modified file, hence no version range is detected.

PHP API

Tip

You can use the method argument $dryRun in both VersionBumper and VersionReleaser classes to skip any write operations (dry-run mode).

Bump versions

The main entrypoint of the plugin is the Version\VersionBumper class:

use EliasHaeussler\VersionBumper;

// Define files and patterns in which to bump new versions
$filesToModify = [
    new VersionBumper\Config\FileToModify(
        'package.json',
        [
            '"version": "{%version%}"',
        ],
    ),
    new VersionBumper\Config\FileToModify(
        'src/Version.php',
        [
            'public const VERSION = \'{%version%}\';',
        ],
    ),
];

// Define package root path and version range
$rootPath = dirname(__DIR__);
$versionRange = VersionBumper\Enum\VersionRange::Minor;

// Bump versions within configured files
$versionBumper = new VersionBumper\Version\VersionBumper();
$results = $versionBumper->bump(
    $filesToModify,
    $rootPath,
    $versionRange,
);

// Display results
foreach ($results as $result) {
    // File: package.json
    echo sprintf('File: %s', $result->file()->path());
    echo PHP_EOL;

    foreach ($result->groupedOperations() as $operations) {
        foreach ($operations as $operation) {
            // Modified: 1.2.3 => 1.3.0
            echo sprintf(
                '%s: %s => %s',
                $operation->state()->name,
                $operation->source(),
                $operation->target(),
            );
            echo PHP_EOL;
        }
    }
}

Create release

A release can be created by the Version\VersionReleaser class:

use EliasHaeussler\VersionBumper;

$options = new VersionBumper\Config\ReleaseOptions(
    tagName: 'v{%version%}', // Create tag with "v" prefix
    signTag: true, // Sign new tags
);

$versionReleaser = new VersionBumper\Version\VersionReleaser();
$result = $versionReleaser->release($results, $rootPath, $options);

echo sprintf(
    'Committed "%s" and tagged "%s" with %d file(s).',
    $result->commitMessage(),
    $result->tagName(),
    count($result->committedFiles()),
);
echo PHP_EOL;

Auto-detect version range

When bumping files, a respective version range or explicit version must be provided (see above). The library provides a Version\VersionRangeDetector class to automate this step and auto-detect a version range, based on a set of Config\VersionRangeIndicator objects:

use EliasHaeussler\VersionBumper;

$indicators = [
    new VersionBumper\Config\VersionRangeIndicator(
        // Bump major version if any commit contains breaking changes
        // (commit message starts with "[!!!]")
        VersionBumper\Enum\VersionRange::Major,
        [
            new VersionBumper\Config\VersionRangePattern(
                VersionBumper\Enum\VersionRangeIndicatorType::CommitMessage,
                '/^\[!!!]/',
            ),
        ],
    ),
];

$versionRangeDetector = new VersionBumper\Version\VersionRangeDetector();
$versionRange = $versionRangeDetector->detect($rootPath, $indicators);

echo sprintf('Auto-detected version range is "%s".', $versionRange->value);
echo PHP_EOL;

📝 Configuration

When using the console command, it is required to configure the write operations which are to be performed by the version bumper.

Formats

The following file formats are supported currently:

  • json
  • yaml, yml

Schema

The config file must follow a given schema:

filesToModify:
  - path: relative/or/absolute/path/to/file
    patterns:
      # Each pattern must contain a {%version%} placeholder
      - '"version": "{%version%}"'
    reportUnmatched: true

releaseOptions:
  commitMessage: '[RELEASE] Release of my-fancy-library {%version%}'
  overwriteExistingTag: true
  signTag: true
  tagName: 'v{%version%}'

# Relative (to config file) or absolute path to project root
rootPath: ../

versionRangeIndicators:
  - range: major
    strategy: matchAll
    patterns:
      - type: fileDeleted
        pattern: '/^src\/Controller\/.+Controller\.php$/'
      - type: fileModified
        pattern: '/^res\/api\.schema\.json$/'
      - type: commitMessage
        pattern: '/^\[!!!]/'

Tip

Have a look at the shipped JSON schema.

Files to modify

Release options

Root path

Version range indicators

Configuration in composer.json

The config file path can be passed as -c/--config command option or, alternatively, as configuration in composer.json:

{
    "extra": {
        "version-bumper": {
            "config-file": "path/to/version-bumper.json"
        }
    }
}

When configured as relative path, the config file path is calculated based on the location of the composer.json file.

Auto-detection

If no config file is explicitly configured, the config reader tries to auto-detect its location. The following order is taken into account during auto-detection:

  1. version-bumper.json
  2. version-bumper.yaml
  3. version-bumper.yml

🧑‍💻 Contributing

Please have a look at CONTRIBUTING.md.

⭐ License

This project is licensed under GNU General Public License 3.0 (or later).