feat: add release changelog generation and package distribution integration
feat: add custom release changelog generator based on release tag ranges and prefixed commit lines build: include generated CHANGELOG.md in the distribution ZIP when present ci: generate release changelog during release workflow and use it as the GitHub release body ci: split release packaging so distZip is rebuilt after changelog generation chore: keep changelog generation out of quality-gate and report publishing workflows
This commit is contained in:
26
.github/workflows/build.yml
vendored
26
.github/workflows/build.yml
vendored
@@ -156,11 +156,31 @@ jobs:
|
|||||||
test -f gradle.properties
|
test -f gradle.properties
|
||||||
test -f gradle/verification-metadata.xml
|
test -f gradle/verification-metadata.xml
|
||||||
|
|
||||||
- name: Build release distribution, signed Maven bundle, and SBOM
|
- name: Generate release changelog for tagged builds
|
||||||
|
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/release@')
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
chmod +x ./tools/generate-release-notes.sh
|
||||||
|
mkdir -p build/generated/release-notes
|
||||||
|
./tools/generate-release-notes.sh "${GITHUB_REF_NAME}" > build/generated/release-notes/CHANGELOG.md
|
||||||
|
|
||||||
|
- name: Build release inputs, signed Maven bundle, and SBOM
|
||||||
env:
|
env:
|
||||||
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
|
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
|
||||||
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
|
SIGNING_PASSWORD: ${{ secrets.SIGNING_PASSWORD }}
|
||||||
run: ./gradlew --no-daemon clean build pmdMain javadoc jacocoTestReport distZip cyclonedxBom centralBundle
|
run: ./gradlew --no-daemon clean build pmdMain javadoc jacocoTestReport cyclonedxBom centralBundle
|
||||||
|
|
||||||
|
- name: Generate release changelog
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
chmod +x ./tools/generate-release-notes.sh
|
||||||
|
mkdir -p build/generated/release-notes
|
||||||
|
./tools/generate-release-notes.sh "${GITHUB_REF_NAME}" > build/generated/release-notes/CHANGELOG.md
|
||||||
|
|
||||||
|
- name: Package release distribution
|
||||||
|
run: ./gradlew --no-daemon distZip
|
||||||
|
|
||||||
- name: Publish bundle to Maven Central
|
- name: Publish bundle to Maven Central
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -188,7 +208,7 @@ jobs:
|
|||||||
- name: Publish GitHub release assets
|
- name: Publish GitHub release assets
|
||||||
uses: softprops/action-gh-release@v2
|
uses: softprops/action-gh-release@v2
|
||||||
with:
|
with:
|
||||||
generate_release_notes: true
|
body_path: build/generated/release-notes/CHANGELOG.md
|
||||||
files: |
|
files: |
|
||||||
build/distributions/*.zip
|
build/distributions/*.zip
|
||||||
build/reports/sbom/radixor-sbom.json
|
build/reports/sbom/radixor-sbom.json
|
||||||
|
|||||||
48
build.gradle
48
build.gradle
@@ -187,6 +187,54 @@ pitest {
|
|||||||
|
|
||||||
application {
|
application {
|
||||||
mainClass = 'org.egothor.stemmer.Compile'
|
mainClass = 'org.egothor.stemmer.Compile'
|
||||||
|
applicationName = 'radixor'
|
||||||
|
executableDir = 'bin'
|
||||||
|
}
|
||||||
|
|
||||||
|
distributions {
|
||||||
|
main {
|
||||||
|
distributionBaseName = 'radixor'
|
||||||
|
|
||||||
|
contents {
|
||||||
|
from('README.md') {
|
||||||
|
into ''
|
||||||
|
}
|
||||||
|
|
||||||
|
from('LICENSE') {
|
||||||
|
into ''
|
||||||
|
}
|
||||||
|
|
||||||
|
from('docs') {
|
||||||
|
into 'docs'
|
||||||
|
include 'quick-start.md'
|
||||||
|
include 'cli-compilation.md'
|
||||||
|
include 'dictionary-format.md'
|
||||||
|
include 'built-in-languages.md'
|
||||||
|
include 'programmatic-usage.md'
|
||||||
|
include 'architecture-and-reduction.md'
|
||||||
|
include 'quality-and-operations.md'
|
||||||
|
include 'benchmarking.md'
|
||||||
|
}
|
||||||
|
|
||||||
|
from(layout.buildDirectory.dir('generated/release-notes')) {
|
||||||
|
into ''
|
||||||
|
include 'CHANGELOG.md'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named('startScripts') {
|
||||||
|
applicationName = 'radixor'
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named('distZip', Zip) {
|
||||||
|
archiveBaseName = 'radixor'
|
||||||
|
archiveClassifier = 'bin'
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.named('distTar') {
|
||||||
|
enabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
jmh {
|
jmh {
|
||||||
|
|||||||
114
tools/generate-release-notes.sh
Executable file
114
tools/generate-release-notes.sh
Executable file
@@ -0,0 +1,114 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
current_tag="${GITHUB_REF_NAME:-${1:-}}"
|
||||||
|
if [[ -z "${current_tag}" ]]; then
|
||||||
|
echo "Current tag is not set. Provide it as GITHUB_REF_NAME or as the first argument." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
release_prefix="release@"
|
||||||
|
|
||||||
|
if [[ "${current_tag}" != "${release_prefix}"* ]]; then
|
||||||
|
echo "Current tag '${current_tag}' does not start with expected prefix '${release_prefix}'." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
git fetch --tags --force >/dev/null 2>&1 || true
|
||||||
|
|
||||||
|
all_versions="$(git tag --list "${release_prefix}*" | sed "s/^${release_prefix}//" | sort -V)"
|
||||||
|
|
||||||
|
previous_tag=""
|
||||||
|
for version in ${all_versions}; do
|
||||||
|
if [[ "${release_prefix}${version}" == "${current_tag}" ]]; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
previous_tag="${release_prefix}${version}"
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ -n "${previous_tag}" ]]; then
|
||||||
|
range="${previous_tag}..${current_tag}"
|
||||||
|
else
|
||||||
|
range="${current_tag}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Generating release notes for range: ${range}" >&2
|
||||||
|
|
||||||
|
declare -a CATEGORY_ORDER=(
|
||||||
|
"feat|Features"
|
||||||
|
"fix|Bug Fixes"
|
||||||
|
"perf|Performance"
|
||||||
|
"refactor|Refactoring"
|
||||||
|
"docs|Documentation"
|
||||||
|
"test|Tests"
|
||||||
|
"build|Build System"
|
||||||
|
"ci|CI/CD"
|
||||||
|
"style|Style"
|
||||||
|
"chore|Maintenance"
|
||||||
|
"revert|Reverts"
|
||||||
|
)
|
||||||
|
|
||||||
|
declare -A CATEGORY_TITLES
|
||||||
|
declare -A CATEGORY_ITEMS
|
||||||
|
|
||||||
|
for entry in "${CATEGORY_ORDER[@]}"; do
|
||||||
|
key="${entry%%|*}"
|
||||||
|
title="${entry##*|}"
|
||||||
|
CATEGORY_TITLES["${key}"]="${title}"
|
||||||
|
CATEGORY_ITEMS["${key}"]=""
|
||||||
|
done
|
||||||
|
|
||||||
|
supported_prefix_pattern='^(feat|fix|perf|refactor|docs|test|build|ci|style|chore|revert)(\([^)]+\))?!?:[[:space:]]*(.+)$'
|
||||||
|
separator=$'\x1f'
|
||||||
|
|
||||||
|
append_line() {
|
||||||
|
local line="$1"
|
||||||
|
local normalized_line
|
||||||
|
local category
|
||||||
|
local message
|
||||||
|
|
||||||
|
normalized_line="$(printf '%s' "${line}" | tr -d '\r' | sed 's/^[[:space:]]*//; s/[[:space:]]*$//')"
|
||||||
|
[[ -z "${normalized_line}" ]] && return 0
|
||||||
|
|
||||||
|
if [[ "${normalized_line}" =~ ${supported_prefix_pattern} ]]; then
|
||||||
|
category="${BASH_REMATCH[1]}"
|
||||||
|
message="${BASH_REMATCH[3]}"
|
||||||
|
|
||||||
|
[[ -z "${message}" ]] && return 0
|
||||||
|
|
||||||
|
CATEGORY_ITEMS["${category}"]+="- ${message}"$'\n'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
while IFS="${separator}" read -r commit_hash subject body; do
|
||||||
|
[[ -z "${commit_hash}" ]] && continue
|
||||||
|
|
||||||
|
if [[ "${subject}" =~ ^Merge[[:space:]] ]] || [[ "${subject}" == "Initial commit" ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
append_line "${subject}"
|
||||||
|
|
||||||
|
while IFS= read -r body_line; do
|
||||||
|
append_line "${body_line}"
|
||||||
|
done <<< "${body}"
|
||||||
|
done < <(git log "${range}" --no-merges --pretty=format:"%H${separator}%s${separator}%b")
|
||||||
|
|
||||||
|
body_text="## What's New"
|
||||||
|
|
||||||
|
for entry in "${CATEGORY_ORDER[@]}"; do
|
||||||
|
key="${entry%%|*}"
|
||||||
|
title="${CATEGORY_TITLES[${key}]}"
|
||||||
|
items="${CATEGORY_ITEMS[${key}]}"
|
||||||
|
|
||||||
|
if [[ -n "${items}" ]]; then
|
||||||
|
body_text+=$'\n\n'"### ${title}"$'\n'
|
||||||
|
body_text+="$(printf '%s' "${items}" | sed '/^[[:space:]]*$/d')"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [[ "${body_text}" == "## What's New" ]]; then
|
||||||
|
body_text+=$'\n\n'"No categorized changes were found in commit subjects or bodies for this release range."
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s\n' "${body_text}"
|
||||||
Reference in New Issue
Block a user