feat: implement semantic versioning and automated release orchestration (#709)

- Added docs/VERSIONING_POLICY.md defining SemVer and Conventional Commit rules.
- Implemented .github/workflows/changelog.yml for automated CHANGELOG.md updates.
- Implemented .github/workflows/commit-check.yml to enforce PR title standards.
- Updated .pre-commit-config.yaml with conventional-pre-commit hook.
- Refined .github/workflows/release.yml with automated version bumping, file synchronization, and Docker tagging.
- Updated CHANGELOG.md with proper template and [Unreleased] section.

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
Co-authored-by: andonly1348 <279141437+andonly1348@users.noreply.github.com>
This commit is contained in:
andonly1348
2026-05-05 23:47:09 +00:00
committed by GitHub
co-authored by google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> andonly1348
parent 9d1bf600e0
commit d7a5ed25ea
5 changed files with 90 additions and 43 deletions
+5 -6
View File
@@ -4,10 +4,11 @@ on:
push:
branches:
- main
paths-ignore:
- 'CHANGELOG.md'
- 'docs/**'
- '*.md'
paths:
- 'src/**'
- 'main.py'
- 'pyproject.toml'
- 'requirements*.txt'
permissions:
contents: write
@@ -27,8 +28,6 @@ jobs:
uses: Conventional-Changelog/github-action-generate-changelog@v1
with:
path-to-changelog: CHANGELOG.md
# This action by default updates the Unreleased section if no tag is provided
# or appends to the top.
- name: Commit and Push Changelog
run: |
+36
View File
@@ -0,0 +1,36 @@
name: Commit Message Check
on:
pull_request:
types:
- opened
- edited
- synchronize
permissions:
pull-requests: read
jobs:
main:
name: Validate PR Title
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
# Configure which types are allowed.
types: |
feat
fix
perf
docs
refactor
style
test
chore
ci
# Configure that a scope must be always lowercase.
subjectPattern: ^(?![A-Z]).+$
# If the PR title contains a breaking change (!), it must be at the end of the type.
# e.g. "feat!: breaking change"
+26 -32
View File
@@ -4,7 +4,7 @@ on:
workflow_dispatch:
inputs:
version:
description: 'Version to release (e.g. 1.0.0). If empty, will be calculated from commits.'
description: 'Version to release (e.g. 1.2.0). If empty, will be calculated from commits.'
required: false
prerelease:
description: 'Is this a pre-release?'
@@ -14,9 +14,6 @@ on:
description: 'Dry run (do not tag or publish)'
type: boolean
default: false
push:
tags:
- 'v*'
permissions:
contents: write
@@ -140,22 +137,26 @@ jobs:
needs: validate
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Calculate Version (for Docker tagging)
id: tag_calc
if: github.event.inputs.version == ''
uses: mathieudutour/github-tag-action@v6.2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
dry_run: true
- name: Determine Version
id: version
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
if [[ "${{ github.event.inputs.version }}" != "" ]]; then
echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
echo "TAG=v${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
else
echo "VERSION=0.0.0-draft" >> $GITHUB_OUTPUT
echo "TAG=v0.0.0-draft" >> $GITHUB_OUTPUT
fi
if [[ "${{ github.event.inputs.version }}" != "" ]]; then
echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
echo "TAG=v${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
else
TAG="${GITHUB_REF#refs/tags/}"
echo "TAG=$TAG" >> $GITHUB_OUTPUT
echo "VERSION=${TAG#v}" >> $GITHUB_OUTPUT
echo "VERSION=${{ steps.tag_calc.outputs.new_version }}" >> $GITHUB_OUTPUT
echo "TAG=${{ steps.tag_calc.outputs.new_tag }}" >> $GITHUB_OUTPUT
fi
- name: Set up Docker Buildx
@@ -189,44 +190,38 @@ jobs:
name: Create Release
runs-on: ubuntu-latest
needs: build
if: (github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run == 'false') || startsWith(github.ref, 'refs/tags/v')
if: github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run == 'false'
steps:
- uses: actions/checkout@v5
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}
- name: Calculate Next Version
id: tag_action
if: github.event_name == 'workflow_dispatch' && github.event.inputs.version == ''
if: github.event.inputs.version == ''
uses: mathieudutour/github-tag-action@v6.2
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
dry_run: true
create_tag: false
- name: Determine Version
id: version
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
if [[ "${{ github.event.inputs.version }}" != "" ]]; then
echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
echo "TAG=v${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
else
echo "VERSION=${{ steps.tag_action.outputs.new_version }}" >> $GITHUB_OUTPUT
echo "TAG=${{ steps.tag_action.outputs.new_tag }}" >> $GITHUB_OUTPUT
fi
if [[ "${{ github.event.inputs.version }}" != "" ]]; then
echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
echo "TAG=v${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
else
TAG="${GITHUB_REF#refs/tags/}"
echo "TAG=$TAG" >> $GITHUB_OUTPUT
echo "VERSION=${TAG#v}" >> $GITHUB_OUTPUT
echo "VERSION=${{ steps.tag_action.outputs.new_version }}" >> $GITHUB_OUTPUT
echo "TAG=${{ steps.tag_action.outputs.new_tag }}" >> $GITHUB_OUTPUT
fi
- name: Update Version and Changelog
if: github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run == 'false'
run: |
VERSION="${{ steps.version.outputs.VERSION }}"
DATE=$(date +%Y-%m-%d)
# Update version strings using a shared variable
echo "Updating version to ${VERSION} in pyproject.toml and src/__init__.py"
sed -i "s/^version = \".*\"/version = \"${VERSION}\"/" pyproject.toml
@@ -247,8 +242,7 @@ jobs:
git push origin main
fi
- name: Create Git Tag (if manual)
if: github.event_name == 'workflow_dispatch' && github.event.inputs.dry_run == 'false'
- name: Create Git Tag
run: |
TAG="${{ steps.version.outputs.TAG }}"
git config user.name "github-actions[bot]"
+10
View File
@@ -51,3 +51,13 @@ repos:
name: pip-audit (dependency safety)
args:
- --requirement=requirements-ci.txt
# -----------------------------------------------------------
# 4. Conventional Commits — enforce commit message format
# -----------------------------------------------------------
- repo: https://github.com/compilerla/conventional-pre-commit
rev: v3.2.0
hooks:
- id: conventional-pre-commit
stages: [commit-msg]
args: [feat, fix, perf, docs, refactor, style, test, chore, ci]
+13 -5
View File
@@ -45,7 +45,13 @@ To enable automated changelog generation and version bumping, all commits to the
Indicate a breaking change by adding a `!` after the type/scope or by adding `BREAKING CHANGE:` in the footer of the commit message. This triggers a **MAJOR** version bump.
*Example*: `feat!: overhaul risk management engine`
## 3. Tagging & Release Strategy
## 3. Pull Request Labels (Optional)
While Conventional Commits are the primary driver, PR labels can be used to override or supplement automation:
- `release:major`: Forces a MAJOR version bump.
- `release:minor`: Forces a MINOR version bump.
- `release:patch`: Forces a PATCH version bump.
## 4. Tagging & Release Strategy
### Stable Releases
- **Branch**: `main`
@@ -61,16 +67,18 @@ Indicate a breaking change by adding a `!` after the type/scope or by adding `BR
- **Tag Format**: `vMAJOR.MINOR.PATCH-alpha.N` or `vMAJOR.MINOR.PATCH-beta.N`
- **Use Case**: Early testing of experimental features.
## 4. Automated Workflow
## 5. Automated Workflow
1. **Automated Changelog**: Every push to `main` triggers the `changelog.yml` workflow, which appends new conventional commits to the `CHANGELOG.md` file under the `[Unreleased]` section.
2. **Version Bump Automation**: The `Release Orchestration` (`release.yml`) workflow:
- Calculates the next version based on commit history if not manually provided.
2. **Commit Validation**: Every Pull Request is checked by the `commit-check.yml` workflow to ensure it meets Conventional Commit standards.
3. **Local Enforcement**: A pre-commit hook ensures that local commits adhere to the standard before they are even pushed.
4. **Version Bump Automation**: The `Release Orchestration` (`release.yml`) workflow:
- Calculates the next version based on commit history.
- Updates `pyproject.toml` and `src/__init__.py`.
- Transitions `[Unreleased]` content in `CHANGELOG.md` to a new versioned header.
- Creates a new Git tag and GitHub Release.
## 5. Guidance for Version Bumping
## 6. Guidance for Version Bumping
| Change Type | Version Component | Example Commit |
| :--- | :--- | :--- |