diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4b7ead7..10134b1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,11 +1,23 @@ # Contributing -## Bumping the JMH fork version +## Release -The JMH fork is published under group ID `io.codspeed.jmh`. To bump the version across all modules: +To create a release, run `scripts/release.sh ` from the main +branch. This script will: -```bash -cd jmh-fork && mvn versions:set -DnewVersion=x.y.z -``` +1. Automatically update the version in all relevant files +2. Create a commit with the version changes +3. Generate the `CHANGELOG.md` (skipped for prereleases) +4. Tag, push, and create a **draft** release on GitHub -This updates all `pom.xml` files in the multi-module project. After bumping, also update the version reference in `build.gradle.kts`. +The release is created as a draft โ€” review the auto-generated notes at +[github.com/CodSpeedHQ/codspeed-jvm/releases](https://github.com/CodSpeedHQ/codspeed-jvm/releases) +and click "Publish" to ship. + +### Pre-releases + +For alpha versions (e.g. `0.2.0-alpha`), the script: + +- Allows running from a non-main branch +- Skips `CHANGELOG.md` regeneration +- Marks the GitHub Release as `--latest=false` so it doesn't appear as the latest release diff --git a/cliff.toml b/cliff.toml new file mode 100644 index 0000000..febedd9 --- /dev/null +++ b/cliff.toml @@ -0,0 +1,87 @@ +# git-cliff ~ configuration file +# https://git-cliff.org/docs/configuration + +[remote.github] +owner = "CodSpeedHQ" +repo = "codspeed-jvm" + +[changelog] +header = """ +# Changelog\n + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +\n + +""" +body = """ +{%- macro remote_url() -%} + https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} +{%- endmacro -%} + +{% if version -%} + ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} +{% else -%} + ## [Unreleased] +{% endif -%} + +{% for group, commits in commits | group_by(attribute="group") %} + ### {{ group | upper_first }} + {%- for commit in commits %} + - {{ commit.message | split(pat="\n") | first | upper_first | trim }}\ + {% if commit.remote.username %} by @{{ commit.remote.username }}{%- endif -%} + {% if commit.remote.pr_number %} in \ + [#{{ commit.remote.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.remote.pr_number }}) \ + {%- endif -%} + {% endfor %} +{% endfor %}\n\n +""" +footer = """ +{%- macro remote_url() -%} + https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} +{%- endmacro -%} + +{% for release in releases -%} + {% if release.version -%} + {% if release.previous and release.previous.version -%} + [{{ release.version | trim_start_matches(pat="v") }}]: \ + {{ self::remote_url() }}/compare/{{ release.previous.version }}..{{ release.version }} + {% endif -%} + {% else -%} + {% if release.previous and release.previous.version -%} + [unreleased]: {{ self::remote_url() }}/compare/{{ release.previous.version }}..HEAD + {% endif -%} + {% endif -%} +{% endfor %} + +""" + +trim = true +postprocessors = [] + +[git] +conventional_commits = true +filter_unconventional = true +split_commits = false +commit_preprocessors = [] +commit_parsers = [ + { message = "^feat", group = "๐Ÿš€ Features" }, + { message = "^fix", group = "๐Ÿ› Bug Fixes" }, + { message = "^doc", group = "๐Ÿ“š Documentation" }, + { message = "^perf", group = "โšก Performance" }, + { message = "^refactor", group = "๐Ÿ—๏ธ Refactor" }, + { message = "^style", group = "๐ŸŽจ Styling" }, + { message = "^test", group = "๐Ÿงช Testing" }, + { message = "^chore\\(release\\): prepare for", skip = true }, + { message = "^chore: release", skip = true }, + { message = "^chore\\(deps.*\\)", skip = true }, + { message = "^chore\\(pr\\)", skip = true }, + { message = "^chore\\(pull\\)", skip = true }, + { message = "^chore|^ci", group = "โš™๏ธ Internals" }, + { body = ".*security", group = "๐Ÿ›ก๏ธ Security" }, + { message = "^revert", group = "โ—€๏ธ Revert" }, + { message = ".*", group = "๐Ÿ’ผ Other" }, +] +filter_commits = false +topo_order = false +sort_commits = "newest" +ignore_tags = "alpha|beta|rc" diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 0000000..bc249e3 --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Usage: ./scripts/release.sh +# +# is a Maven-style version without the leading 'v', e.g. 0.2.0 +# or 0.2.0-beta.1. The git tag will be prefixed with 'v' (vX.Y.Z). +# +# This script bumps the jmh-fork version everywhere it's pinned, +# regenerates CHANGELOG.md from conventional commits (skipped for +# alpha/beta/rc), commits, tags, and prints the push command. +# It does NOT push. +# +# Based on this: https://github.com/CodSpeedHQ/codspeed-cpp/blob/main/scripts/release.sh + +# First and only argument is the version number +VERSION_NO_V=${1} # The version number without the 'v' prefix +VERSION=v$1 # The version number, prefixed with 'v' + +# Validate version format (X.Y.Z or X.Y.Z-alpha where X, Y, Z are integers) +if [[ ! "$VERSION_NO_V" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-alpha)?$ ]]; then + echo "Error: Invalid version format '$VERSION_NO_V'" + echo "Usage: $0 " + echo " Version must be in format X.Y.Z or X.Y.Z-alpha (e.g., 1.2.3 or 1.2.3-alpha)" + exit 1 +fi + +# Check is on main (unless releasing an alpha version) +if [[ ! "$VERSION_NO_V" =~ -alpha ]]; then + if [ "$(git rev-parse --abbrev-ref HEAD)" != "main" ]; then + echo "Not on main branch (only alpha releases can be made from non-main branches)" + exit 1 + fi +fi + +# Check that GITHUB_TOKEN is set +if [ -z "${GITHUB_TOKEN:-}" ]; then + echo "GITHUB_TOKEN is not set. Trying to fetch it from gh" + GITHUB_TOKEN=$(gh auth token) +fi + +# Check that the tag doesn't already exist +if git rev-parse "$VERSION" >/dev/null 2>&1; then + echo "error: tag $VERSION already exists" >&2 + exit 1 +fi + + +## Bump all the versions in the repo +## + +# List of files to update with version numbers. +VERSION_FILES=( + "jmh-fork/build.gradle.kts" + "examples/example-maven/pom.xml" + "examples/example-gradle/build.gradle.kts" +) + +# Get current version from jmh-fork/build.gradle.kts +PREVIOUS_VERSION=$(awk -F'"' '/version = / {print $2; exit}' jmh-fork/build.gradle.kts) + +# Prompt the release version +echo "Previous version: ${PREVIOUS_VERSION}" +echo "New version: ${VERSION_NO_V}" +read -p "Are you sure you want to release this version? (y/n): " confirm +if [ "$confirm" != "y" ]; then + echo "Aborting release" + exit 1 +fi + +# Update version in all relevant files +echo "Updating version numbers in source files..." + +# Use sed in a cross-platform way (macOS requires empty string after -i) +sed_inplace() { + if [[ "$OSTYPE" == "darwin"* ]]; then + sed -i '' "$@" + else + sed -i "$@" + fi +} + +for file in "${VERSION_FILES[@]}"; do + sed_inplace "s/${PREVIOUS_VERSION}/${VERSION_NO_V}/g" "$file" + echo " Updated $file" +done + +# Bump the jmh-fork poms (parent + all child modules) in one shot +(cd jmh-fork && mvn versions:set -DnewVersion="$VERSION_NO_V" -DgenerateBackupPoms=false -q) +echo " Updated jmh-fork/**/pom.xml (via mvn versions:set)" + +# Commit version changes +git add \ + jmh-fork \ + examples/example-maven/pom.xml \ + examples/example-gradle/build.gradle.kts +git cliff -o CHANGELOG.md --tag "$VERSION" --github-token "$GITHUB_TOKEN" +git add CHANGELOG.md +git commit -m "chore: Release $VERSION" +git tag -s "$VERSION" -m "Release $VERSION" +git push origin HEAD +git push origin "$VERSION" + +# Create GitHub release +if [[ "$VERSION_NO_V" =~ -alpha ]]; then + gh release create "$VERSION" -t "$VERSION" --generate-notes --latest=false --draft +else + gh release create "$VERSION" -t "$VERSION" --generate-notes --latest --draft +fi