From a058e7b6838d41a98f3269db9a9d1e31f752121f Mon Sep 17 00:00:00 2001 From: "Max P." Date: Wed, 21 May 2025 02:57:03 +0200 Subject: [PATCH] chore: add automated release workflow and scripts for version management --- .gitea/COMMIT_GPT.md | 50 ++++++ .gitea/HOWTO_RELEASE.md | 198 ++++++++++++++++++++++++ .gitea/scripts/cleanup_versions.sh | 45 ++++++ .gitea/scripts/get-release-id.sh | 21 +++ .gitea/scripts/set_poetry_version.sh | 14 ++ .gitea/scripts/upload-asset.sh | 40 +++++ .gitea/workflows/release.yml | 222 +++++++++++++++++++++++++++ cliff.toml | 104 +++++++++++++ 8 files changed, 694 insertions(+) create mode 100644 .gitea/COMMIT_GPT.md create mode 100644 .gitea/HOWTO_RELEASE.md create mode 100755 .gitea/scripts/cleanup_versions.sh create mode 100755 .gitea/scripts/get-release-id.sh create mode 100755 .gitea/scripts/set_poetry_version.sh create mode 100755 .gitea/scripts/upload-asset.sh create mode 100644 .gitea/workflows/release.yml create mode 100644 cliff.toml diff --git a/.gitea/COMMIT_GPT.md b/.gitea/COMMIT_GPT.md new file mode 100644 index 0000000..bfc4eb7 --- /dev/null +++ b/.gitea/COMMIT_GPT.md @@ -0,0 +1,50 @@ +# GPT Prompt + +This is a prompt for a GPT model to generate a commit message based on the changes made in the code. + +```plaintext +# System +You are a highly precise Git commit message generator for changelog-driven repositories. +Your only task is to read the provided multi-file git diff and generate **one and only one** Conventional Commit message that strictly complies with the rules below. + +# Language +You MUST write the entire message in English only. + +# Commit Rules + +1. Use exactly one of the following lowercase types: + feat, fix, docs, style, refactor, perf, test, chore, revert + +2. Optionally add a `()` after the type: + • Use a short lowercase identifier (e.g., directory, module, file group) + • Omit if no dominant scope exists + +3. Follow this structure: + `type(scope): summary` + • The summary must be in imperative mood + • Max 72 characters + • No trailing period + • Must not contain emojis or casing deviations + +4. If the body is needed: + • Add exactly one blank line after the subject + • Use `- ` bullet points + • Each line must be ≤ 72 characters + • Explain what was changed and (if shown in the diff) why + +5. Do NOT include: + • `BREAKING CHANGE:` or any `!` markers + • PR numbers, issue IDs, author names, ticket references + • Markdown formatting, emojis, or code blocks + +6. The following `chore(...)` patterns MUST be ignored and skipped from changelogs: + chore(changelog), chore(version), chore(release): prepare for, chore(deps), chore(pr), chore(pull) + +7. If the commit body contains the word `security`, it will be classified as security-relevant (🛡️), regardless of type. + +8. Output MUST consist only of the final commit message, as plain text. + No extra prose, no explanation, no formatting, no examples. + +# Now, read the diff and generate the message. +Output only the valid Conventional Commit message, exactly. +``` diff --git a/.gitea/HOWTO_RELEASE.md b/.gitea/HOWTO_RELEASE.md new file mode 100644 index 0000000..a37855d --- /dev/null +++ b/.gitea/HOWTO_RELEASE.md @@ -0,0 +1,198 @@ +# 📦 HOWTO: Release erstellen mit Auto-Changelog-Workflow + +Dieses Repository nutzt einen automatisierten CI/CD-Workflow zur **Versionsverwaltung, Changelog-Generierung und Release-Erstellung**. +Der gesamte Prozess ist deklarativ und läuft automatisch – ausgelöst durch Änderungen an einer Datei: `VERSION`. + +--- + +## 🧭 Was passiert automatisch? + +Sobald Änderungen in `main` landen, prüft der Workflow: + +- 🔍 **Hat sich die Datei `VERSION` geändert?** + - ❌ **Nein** → es wird nur das `CHANGELOG.md` aktualisiert (unreleased Abschnitt) + - ✅ **Ja** → es wird: + - ein vollständiger Changelog für diese Version erzeugt + - ein Git-Tag `vX.Y.Z` erstellt + - ein Release in Gitea veröffentlicht (inkl. Beschreibung aus dem Changelog) + +--- + +## ✅ Wie erzeuge ich ein Release? + +### 1. Erhöhe die Version in der Datei `VERSION` + +Beispiel: + +```txt +1.2.3 +``` + +> Diese Datei muss **als eigene Commit-Änderung** erfolgen – idealerweise als letzter Commit in einem PR. +> Die Commit-Nachricht sollte mit `chore(version)` beginnen, damit dieser nicht im Changelog auftaucht. + +--- + +### 2. Mergen in `main` + +Sobald `main` den Commit mit neuer `VERSION` enthält, wird automatisch: + +- das `CHANGELOG.md` regeneriert und committed +- der neue Git-Tag erstellt (`v1.2.3`) +- ein Gitea Release mit genau diesem Changelog erzeugt + +--- + +## 🛡️ Hinweis zu Tokens & Webhooks + +Damit das Release auch korrekt weitere Workflows auslösen kann (z. B. über `on: release`), ist **ein Personal Access Token notwendig**. + +### 🔐 Secret: `RELEASE_PUBLISH_TOKEN` + +> Lege ein Repository-Secret mit diesem Namen an. +> Es sollte ein **Gitea Personal Access Token** mit folgenden Berechtigungen sein: + +- `write:repo` +- `write:release` +- idealerweise: keine Ablaufzeit + +Wird dieser Token **nicht** gesetzt, fällt der Workflow auf `ACTIONS_RUNTIME_TOKEN` zurück, aber: +- Release wird trotzdem erstellt +- **⚠️ andere Workflows, die auf `release.published` reagieren, könnten nicht getriggert werden** + +--- + +## 🧪 Debugging-Tipps + +- Stelle sicher, dass `VERSION` exakt **eine gültige neue semver-Version** enthält +- Achte auf den Commit-Log: Changelog-Commits sind mit `chore(changelog):` gekennzeichnet +- Verwende nur `main` als Trigger-Zweig + +--- + +## 🧩 Erweiterung + +In `upload-assets.yml` kannst du beliebige Build-Artefakte automatisch an das Release anhängen, sobald es veröffentlicht ist. + +Dafür: +- liegt das Script `.gitea/scripts/get-release-id.sh` +- sowie `.gitea/scripts/upload-asset.sh` bereit + +Mehr dazu in der Datei: `.gitea/workflows/upload-assets.yml` + +--- + +## 🧘 Best Practice + +- Changelog-Generierung nie manuell ausführen +- Nur `VERSION` ändern, um ein neues Release auszulösen +- Auf `CHANGELOG.md` nie direkt committen +- Release-Daten niemals per Hand in Gitea pflegen + +📎 Alles wird versioniert, automatisiert und reproduzierbar erzeugt. + +--- + +## 🧠 Commit-Gruppierung & Changelog-Erzeugung + +Der Changelog wird auf Basis definierter **Commit-Gruppen** erzeugt. +Diese Regeln sind in `cliff.toml` unter `commit_parsers` konfiguriert. + +| Prefix / Muster | Gruppe | Beschreibung | +|-------------------------------|---------------------------|--------------------------------------------------| +| `feat:` | 🚀 Features | Neue Funktionalität | +| `fix:` | 🐛 Bug Fixes | Fehlerbehebungen | +| `doc:` | 📚 Documentation | Änderungen an Doku, Readmes etc. | +| `perf:` | ⚡ Performance | Leistungsverbesserungen | +| `refactor:` | 🚜 Refactor | Reorganisation ohne Verhaltensänderung | +| `style:` | 🎨 Styling | Formatierung, Whitespaces, Code-Style | +| `test:` | 🧪 Testing | Neue oder angepasste Tests | +| `ci:` oder `chore:` (ohne Spezifizierung) | ⚙️ Miscellaneous Tasks | CI-Änderungen, Aufgaben, Wartung etc. | +| `chore(changelog)`, `chore(version)`, `chore(release): prepare for`, `chore(deps...)`, `chore(pr)`, `chore(pull)` | *(ignoriert)* | Diese Commits werden im Changelog **ausgelassen** | +| Commit-Body enthält `security` | 🛡️ Security | Sicherheitsrelevante Änderungen | +| `revert:` | ◀️ Revert | Rückgängig gemachte Commits | +| alles andere | 💼 Other | Fallback für nicht erkannte Formate | + +### ✍️ Beispiel: + +```bash +git commit -m "feat: add login endpoint" +git commit -m "fix: prevent crash on null input" +git commit -m "chore(version): bump to 1.2.3" +``` + +> Nur die ersten beiden erscheinen im Changelog – der dritte wird **automatisch übersprungen**. + +--- + +## 🧾 Umgang mit `CHANGELOG.md` beim Mergen und Releasen + +Wenn du automatisiert einen Changelog mit `git-cliff` erzeugst, ist `CHANGELOG.md` ein **generiertes Artefakt** – und kein handgepflegter Quelltext. + +Beim Mergen von Feature-Branches in `main` kann es deshalb zu **unnötigen Konflikten** in dieser Datei kommen, obwohl der Inhalt später sowieso neu erzeugt wird. + +--- + +## 🧼 Umgang mit `CHANGELOG.md` in Feature-Branches + +Wenn du mit **Feature-Branches** arbeitest, wird `CHANGELOG.md` dort oft automatisch erzeugt. +Das kann beim späteren Merge in `main` zu **unnötigen Merge-Konflikten** führen. + +### ✅ Empfohlene Vorgehensweise + +**Bevor du den Branch mit `main` zusammenführst** (Merge oder Cherry-Pick): + +```bash +git rm CHANGELOG.md +git commit -m "chore(changelog): remove generated CHANGELOG.md before merge" +git push +``` + +Dadurch: + +* verhinderst du Merge-Konflikte mit `CHANGELOG.md` +* wird die Datei bei Feature-Branches nicht mehr automatisch erzeugt +* bleibt deine Historie sauber und konfliktfrei + +> 💡 Der Workflow erzeugt `CHANGELOG.md` automatisch **nur**, wenn: +> +> * die Datei schon vorhanden ist **oder** +> * der Branch `main` heißt + +--- + +## 🧩 Merge-Konflikte verhindern mit `.gitattributes` + +Damit Git bei Konflikten in `CHANGELOG.md` **automatisch deine Version bevorzugt**, kannst du folgende Zeile in die Datei `.gitattributes` aufnehmen: + +```gitattributes +CHANGELOG.md merge=ours +``` + +Das bedeutet: + +* Beim Merge wird die Version aus dem aktuellen Branch (`ours`) behalten +* Änderungen aus dem Ziel-Branch (`theirs`) werden verworfen + +### ✅ So verwendest du es richtig: + +1. **Füge die Regel in `main` hinzu**: + +```bash +echo "CHANGELOG.md merge=ours" >> .gitattributes +git add .gitattributes +git commit -m "chore(git): prevent merge conflicts in CHANGELOG.md" +git push origin main +``` + +2. **Hole sie in deinen Feature-Branch**: + +```bash +git checkout feature/xyz +git rebase origin/main +``` + +3. **Ab sofort werden Konflikte in `CHANGELOG.md` automatisch aufgelöst** – lokal. + +> ⚠️ Hinweis: Plattformen wie **Gitea, GitHub oder GitLab ignorieren `.gitattributes` beim Merge über die Web-Oberfläche**. +> Führe Merges daher **lokal** durch, wenn du Konflikte verhindern willst. diff --git a/.gitea/scripts/cleanup_versions.sh b/.gitea/scripts/cleanup_versions.sh new file mode 100755 index 0000000..c8c23bc --- /dev/null +++ b/.gitea/scripts/cleanup_versions.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +set -euo pipefail + +# cleanup_dev_versions.sh - Delete old PyPI dev versions from Gitea package registry + +# Required environment variables +USERNAME="${TWINE_USERNAME}" +TOKEN="${TWINE_PASSWORD}" +REPO="${GITHUB_REPOSITORY}" # e.g., maxp/repocat +API_BASE="${GITHUB_API_URL%/}" # Strip trailing slash if present + +OWNER="${REPO%%/*}" +PACKAGE_NAME="${REPO##*/}" +API_URL="${API_BASE}/packages/${OWNER}/pypi/${PACKAGE_NAME}" + +# Fetch the list of versions +response=$(curl -s -u "$USERNAME:$TOKEN" "$API_URL") + +# Extract all .dev versions, sort by creation time +mapfile -t versions_to_delete < <(echo "$response" | jq -r ' + map(select(.version | test("\\.dev"))) | + sort_by(.created_at) | + .[0:-1][] | + .version') + +# Determine latest version to keep +latest_version=$(echo "$response" | jq -r ' + map(select(.version | test("\\.dev"))) | + sort_by(.created_at) | + last.version') + +if [[ -z "$latest_version" || ${#versions_to_delete[@]} -eq 0 ]]; then + echo "No old .dev versions to delete." + exit 0 +fi + +echo "Keeping latest .dev version: $latest_version" + +# Delete old .dev versions +for version in "${versions_to_delete[@]}"; do + echo "Deleting old .dev version: $version" + curl -s -X DELETE -u "$USERNAME:$TOKEN" "$API_URL/$version" +done + +echo "Cleanup complete." \ No newline at end of file diff --git a/.gitea/scripts/get-release-id.sh b/.gitea/scripts/get-release-id.sh new file mode 100755 index 0000000..1058ede --- /dev/null +++ b/.gitea/scripts/get-release-id.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Eingaben +TAG="$1" +TOKEN="${ACTIONS_RUNTIME_TOKEN:-}" +REPO="${GITHUB_REPOSITORY:-owner/example}" +API="${GITHUB_API_URL:-https://gitea.example.tld/api/v1}" + +OWNER=$(echo "$REPO" | cut -d/ -f1) +NAME=$(echo "$REPO" | cut -d/ -f2) + +RESPONSE=$(curl -sf \ + -H "Authorization: token $TOKEN" \ + "$API/repos/$OWNER/$NAME/releases/tags/$TAG") + +RELEASE_ID=$(echo "$RESPONSE" | jq -r '.id') +echo "Release-ID für $TAG ist: $RELEASE_ID" + +# Für GitHub Actions als Umgebungsvariable +echo "GT_RELEASE_ID=$RELEASE_ID" >> "$GITHUB_ENV" diff --git a/.gitea/scripts/set_poetry_version.sh b/.gitea/scripts/set_poetry_version.sh new file mode 100755 index 0000000..272e938 --- /dev/null +++ b/.gitea/scripts/set_poetry_version.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +BASE_VERSION=$(cat VERSION) +NIGHTLY_SUFFIX="" + +if [[ "$1" == "nightly" ]]; then + # Beispiel: 20240511.1358 → 11. Mai, 13:58 Uhr + NIGHTLY_SUFFIX=".dev$(date +%Y%m%d%H%M)" +fi + +FULL_VERSION="${BASE_VERSION}${NIGHTLY_SUFFIX}" + +echo "Using version: $FULL_VERSION" +poetry version "$FULL_VERSION" diff --git a/.gitea/scripts/upload-asset.sh b/.gitea/scripts/upload-asset.sh new file mode 100755 index 0000000..36f5e73 --- /dev/null +++ b/.gitea/scripts/upload-asset.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Eingabeparameter +FILE_PATH="$1" # z. B. ./dist/build.zip +CUSTOM_NAME="${2:-}" # optional: anderer Name unter dem das Asset gespeichert werden soll +RELEASE_ID="${GT_RELEASE_ID:-}" # aus Umgebung + +# Validierung +if [[ -z "$RELEASE_ID" ]]; then + echo "❌ RELEASE_ID ist nicht gesetzt. Abbruch." + exit 1 +fi + +if [[ ! -f "$FILE_PATH" ]]; then + echo "❌ Datei '$FILE_PATH' existiert nicht. Abbruch." + exit 1 +fi + +# Default-Konfiguration +TOKEN="${ACTIONS_RUNTIME_TOKEN:-}" +REPO="${GITHUB_REPOSITORY:-owner/example}" +API="${GITHUB_API_URL:-https://gitea.example.tld/api/v1}" + +# Owner/Repo auflösen +OWNER=$(echo "$REPO" | cut -d/ -f1) +NAME=$(echo "$REPO" | cut -d/ -f2) + +# Dateiname setzen +FILENAME="${CUSTOM_NAME:-$(basename "$FILE_PATH")}" + +echo "🔼 Uploading '$FILE_PATH' as '$FILENAME' to release ID $RELEASE_ID" + +# Upload +curl -sf -X POST \ + -H "Authorization: token $TOKEN" \ + -F "attachment=@$FILE_PATH" \ + "$API/repos/$OWNER/$NAME/releases/$RELEASE_ID/assets?name=$FILENAME" + +echo "✅ Upload abgeschlossen: $FILENAME" diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml new file mode 100644 index 0000000..8f02b21 --- /dev/null +++ b/.gitea/workflows/release.yml @@ -0,0 +1,222 @@ +name: Auto Changelog & Release + +on: + push: + branches: + - main + - '**' + +jobs: + detect-version-change: + runs-on: ubuntu-latest + outputs: + version_changed: ${{ steps.set.outputs.version_changed }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Check if VERSION file changed + if: github.ref == 'refs/heads/main' + run: | + echo "🔍 Vergleich mit github.event.before:" + echo "Before: ${{ github.event.before }}" + echo "After: ${{ github.sha }}" + + echo "📄 Changed files between before and after:" + git diff --name-only ${{ github.event.before }} ${{ github.sha }} || echo "(diff failed)" + + if git diff --name-only ${{ github.event.before }} ${{ github.sha }} | grep -q '^VERSION$'; then + echo "✅ VERSION file was changed" + echo "VERSION_CHANGED=true" >> $GITHUB_ENV + else + echo "ℹ️ VERSION file not changed" + echo "VERSION_CHANGED=false" >> $GITHUB_ENV + fi + + - name: Set output (always) + id: set + run: | + echo "version_changed=${VERSION_CHANGED:-false}" >> $GITHUB_OUTPUT + + changelog-only: + needs: detect-version-change + if: github.ref != 'refs/heads/main' || needs.detect-version-change.outputs.version_changed == 'false' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set Git Author + run: | + git config user.name "$CI_COMMIT_AUTHOR_NAME" + git config user.email "$CI_COMMIT_AUTHOR_EMAIL" + + - name: Read CLIFF_VERSION from cliff.toml + id: cliff_version + run: | + echo "version=$(awk -F '=' '/^# CLIFF_VERSION=/ { gsub(/[" ]/, "", $2); print $2 }' cliff.toml)" >> $GITHUB_OUTPUT + + - name: Restore git-cliff cache + id: restore-cliff + uses: https://git.0xmax42.io/actions/cache@v1 + with: + key: cargo-cliff-${{ steps.cliff_version.outputs.version }} + paths: | + /root/.cargo/bin + + - name: Install git-cliff + if: steps.restore-cliff.outputs.cache-hit != 'true' + run: | + cargo install git-cliff --version "${{ steps.cliff_version.outputs.version }}" --features gitea + + - name: Generate unreleased changelog (if file exists or on main) + run: | + if [[ -f CHANGELOG.md || "${GITHUB_REF##refs/heads/}" == "main" ]]; then + echo "Generating CHANGELOG.md..." + git-cliff -c cliff.toml -o CHANGELOG.md + else + echo "CHANGELOG.md does not exist and this is not 'main'. Skipping generation." + fi + + - name: Commit updated CHANGELOG + run: | + git add CHANGELOG.md + if git diff --cached --quiet; then + echo "No changes to commit" + else + git commit -m "chore(changelog): update unreleased changelog" + git push origin "${GITHUB_REF##refs/heads/}" + fi + + release: + needs: detect-version-change + if: needs.detect-version-change.outputs.version_changed == 'true' && github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set Git Author + run: | + git config user.name "$CI_COMMIT_AUTHOR_NAME" + git config user.email "$CI_COMMIT_AUTHOR_EMAIL" + + - name: Read VERSION + id: version + run: echo "value=$(cat VERSION)" >> $GITHUB_OUTPUT + + - name: Read CLIFF_VERSION from cliff.toml + id: cliff_version + run: | + echo "version=$(awk -F '=' '/^# CLIFF_VERSION=/ { gsub(/[" ]/, "", $2); print $2 }' cliff.toml)" >> $GITHUB_OUTPUT + + - name: Restore git-cliff cache + id: restore-cliff + uses: https://git.0xmax42.io/actions/cache@v1 + with: + key: cargo-cliff-${{ steps.cliff_version.outputs.version }} + paths: | + /root/.cargo/bin + + - name: Install git-cliff + if: steps.restore-cliff.outputs.cache-hit != 'true' + run: | + cargo install git-cliff --version "${{ steps.cliff_version.outputs.version }}" --features gitea + + - name: Generate changelog for release and tag + id: generate-changelog + run: | + VERSION=${{ steps.version.outputs.value }} + git-cliff -c cliff.toml -t "v$VERSION" -o CHANGELOG.md + + BODY=$(mktemp) + ESCAPED_VERSION=$(echo "$VERSION" | sed 's/\./\\./g') + + awk -v ver="$ESCAPED_VERSION" ' + $0 ~ "^## \\[" ver "\\]" { + print_flag=1 + line = $0 + sub(/^## /, "", line) + sub(/\\s*\\(.*\\)/, "", line) # entfernt z. B. "(...)" oder "(*)" + print line + next + } + $0 ~ "^## \\[" && $0 !~ "^## \\[" ver "\\]" { + print_flag=0 + } + print_flag + ' CHANGELOG.md > "$BODY" + + echo "changelog_body_path=$BODY" >> $GITHUB_OUTPUT + + + - name: Commit updated CHANGELOG + run: | + git add CHANGELOG.md + if git diff --cached --quiet; then + echo "No changes to commit" + else + git commit -m "chore(changelog): update changelog for v${{ steps.version.outputs.value }}" + git push origin main + fi + + - name: Create Git tag (if not exists) + run: | + VERSION=${{ steps.version.outputs.value }} + if git rev-parse "v$VERSION" >/dev/null 2>&1; then + echo "Tag v$VERSION already exists, skipping tag creation." + else + git tag -a "v$VERSION" -F "${{ steps.generate-changelog.outputs.changelog_body_path }}" --cleanup=verbatim + git push origin "v$VERSION" + fi + + - name: Create Gitea release + env: + RELEASE_PUBLISH_TOKEN: ${{ secrets.RELEASE_PUBLISH_TOKEN }} + run: | + VERSION=${{ steps.version.outputs.value }} + BODY_FILE="${{ steps.generate-changelog.outputs.changelog_body_path }}" + + OWNER=$(echo "$GITHUB_REPOSITORY" | cut -d/ -f1) + REPO=$(echo "$GITHUB_REPOSITORY" | cut -d/ -f2) + + # Token-Auswahl + TOKEN="${RELEASE_PUBLISH_TOKEN:-$ACTIONS_RUNTIME_TOKEN}" + + if [[ -z "${RELEASE_PUBLISH_TOKEN:-}" ]]; then + echo "::warning title=Limited Release Propagation::" + echo "RELEASE_PUBLISH_TOKEN is not set. Using ACTIONS_RUNTIME_TOKEN instead." + echo "⚠️ Release events may not trigger other workflows if created with the runtime token." + echo + fi + + # Prüfe, ob der Release schon existiert + if curl -sf "$GITHUB_API_URL/repos/$OWNER/$REPO/releases/tags/v$VERSION" \ + -H "Authorization: token $TOKEN" > /dev/null; then + echo "🔁 Release for tag v$VERSION already exists, skipping." + exit 0 + fi + + echo "🚀 Creating Gitea release for v$VERSION" + + # Release-Beschreibung vorbereiten + RELEASE_BODY=$(tail -n +2 "$BODY_FILE" | jq -Rs .) + + curl -X POST "$GITHUB_API_URL/repos/$OWNER/$REPO/releases" \ + -H "Authorization: token $TOKEN" \ + -H "Content-Type: application/json" \ + -d @- </{{ remote.gitea.owner }}/{{ remote.gitea.repo }} +{%- endmacro -%} + +{% if version %}\ + {% if previous.version %}\ + ## [{{ version | trim_start_matches(pat="v") }}]\ + ({{ self::remote_url() }}/compare/{{ previous.version }}..{{ version }}) - {{ timestamp | date(format="%Y-%m-%d") }} + {% else %}\ + ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} + {% endif %}\ +{% else %}\ + ## [unreleased] +{% endif %}\ +{% for group, commits in commits | group_by(attribute="group") %} + ### {{ group | striptags | trim | upper_first }} + {% for commit in commits %} + - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\ + {% if commit.breaking %}[**breaking**] {% endif %}\ + {{ commit.message | upper_first }} - \ + ([{{ commit.id | truncate(length=7, end="") }}]({{ self::remote_url() }}/commit/{{ commit.id }}))\ + {% endfor %} +{% endfor %}\n +""" +# template for the changelog footer +footer = """ + +""" +# remove the leading and trailing s +trim = true + +# render body even when there are no releases to process +# render_always = true +# output file path +# output = "test.md" + +[git] +# parse the commits based on https://www.conventionalcommits.org +conventional_commits = true +# filter out the commits that are not conventional +filter_unconventional = true +# process each line of a commit as an individual commit +split_commits = false +# regex for preprocessing the commit messages +commit_preprocessors = [ + # Replace issue numbers + #{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = "([#${2}](/issues/${2}))"}, + # Check spelling of the commit with https://github.com/crate-ci/typos + # If the spelling is incorrect, it will be automatically fixed. + #{ pattern = '.*', replace_command = 'typos --write-changes -' }, +] +# regex for parsing and grouping commits +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\\(changelog\\)", skip = true }, + { message = "^chore\\(version\\)", skip = true }, + { message = "^chore\\(release\\): prepare for", skip = true }, + { message = "^chore\\(deps.*\\)", skip = true }, + { message = "^chore\\(pr\\)", skip = true }, + { message = "^chore\\(pull\\)", skip = true }, + { message = "^chore|^ci", group = "⚙️ Miscellaneous Tasks" }, + { body = ".*security", group = "🛡️ Security" }, + { message = "^revert", group = "◀️ Revert" }, + { message = ".*", group = "💼 Other" }, +] +# Regex to select git tags that represent releases. +tag_pattern = "v[0-9]+\\.[0-9]+\\.[0-9]+" +# filter out the commits that are not matched by commit parsers +filter_commits = false +# sort the tags topologically +topo_order = false +# sort the commits inside sections by oldest/newest order +sort_commits = "newest"