feat(ci): add automated workflows for releases and nightly builds

- Introduce workflows for auto-releases, changelog updates, and nightly builds
- Add scripts for version management, asset uploads, and cleanup tasks
- Remove deprecated GitHub workflow and optimize `.gitignore` entries
- Configure `git-cliff` for enhanced changelog generation with custom rules
This commit is contained in:
2025-05-24 12:55:12 +02:00
parent 6c18e41b00
commit 579c70b784
12 changed files with 822 additions and 85 deletions

50
.gitea/COMMIT_GPT.md Normal file
View File

@@ -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 `(<scope>)` 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.
```

198
.gitea/HOWTO_RELEASE.md Normal file
View File

@@ -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.

View File

@@ -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."

View File

@@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -euo pipefail
# Eingaben
TAG="$1"
TOKEN="${ACTIONS_RUNTIME_TOKEN:-<fallback_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"

View File

@@ -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"

40
.gitea/scripts/upload-asset.sh Executable file
View File

@@ -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:-<fallback_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"

View File

@@ -0,0 +1,62 @@
name: Build and Publish nightly package
on:
workflow_dispatch:
push:
branches:
- main
paths-ignore:
- 'CHANGELOG.md'
jobs:
build-and-publish:
runs-on: ubuntu-22.04
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: 🐍 Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: 🔄 Restore cache
uses: https://git.0xmax42.io/actions/cache@v1
with:
key: poetry-v1-${{ runner.os }}-${{ hashFiles('poetry.lock') }}
paths: |
~/.cache/pypoetry
~/.cache/pip
- name: Install Poetry
run: |
pip install poetry
- name: Install Project Dependencies
working-directory: .
run: |
poetry install
- name: Set version from VERSION file (with nightly suffix)
run: ./.gitea/scripts/set_poetry_version.sh nightly
- name: Build Package
working-directory: .
run: |
poetry build
- name: Publish to Gitea Package Registry
working-directory: .
env:
TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }}
TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}
run: |
poetry run twine upload --repository-url ${{ secrets.TWINE_URL }} dist/*
- name: Cleanup old dev versions
run: |
.gitea/scripts/cleanup_versions.sh '\.dev'
env:
TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }}
TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}

View File

@@ -0,0 +1,64 @@
name: Build and Publish nightly package
on:
release:
types: [published]
jobs:
build-and-publish:
runs-on: ubuntu-22.04
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ github.event.release.tag_name }}
- name: 🐍 Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: 🔄 Restore cache
uses: https://git.0xmax42.io/actions/cache@v1
with:
key: poetry-v1-${{ runner.os }}-${{ hashFiles('poetry.lock') }}
paths: |
~/.cache/pypoetry
~/.cache/pip
- name: Install Poetry
run: |
pip install poetry
- name: Install Project Dependencies
working-directory: .
run: |
poetry install
- name: Build Package
working-directory: .
run: |
poetry build
- name: Get built wheel filename
id: get_whl
run: |
echo "whl_file=$(basename dist/*.whl)" >> $GITHUB_OUTPUT
echo "sdist_file=$(basename dist/*.tar.gz)" >> $GITHUB_OUTPUT
- name: Publish to Gitea Package Registry
working-directory: .
env:
TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }}
TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }}
run: |
poetry run twine upload --repository-url ${{ secrets.TWINE_URL }} dist/*
- name: Get Release ID from tag
run: .gitea/scripts/get-release-id.sh "${{ github.event.release.tag_name }}"
- name: Upload assets
run: |
.gitea/scripts/upload-asset.sh ./dist/${{ steps.get_whl.outputs.whl_file }}
.gitea/scripts/upload-asset.sh ./dist/${{ steps.get_whl.outputs.sdist_file }}

View File

@@ -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 @- <<EOF
{
"tag_name": "v$VERSION",
"target_commitish": "main",
"name": "Release v$VERSION",
"body": $RELEASE_BODY,
"draft": false,
"prerelease": false
}
EOF
echo "✅ Release for tag $VERSION created successfully."

View File

@@ -1,85 +0,0 @@
name: Create Release
on:
push:
branches:
- main
paths:
- "VERSION"
workflow_dispatch:
jobs:
build:
runs-on: windows-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up Python
uses: actions/setup-python@v3
with:
python-version: "3.x"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pyinstaller openai
- name: Get the version
id: get_version
shell: pwsh
run: |
$VERSION = (Get-Content -Path "./VERSION" -Raw).Trim()
echo "VERSION=$VERSION" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8NoBOM -Append
- name: Run auto-py-to-exe
run: |
pyinstaller --noconfirm --onefile --console --name "ait-v${{ env.VERSION }}-winx64" --clean "./ait.py"
- name: Get previous release tag
id: get_previous_release
shell: pwsh
run: |
Write-Host "Fetching previous release tag..."
$previous_tag = git describe --tags --abbrev=0 HEAD^ 2>$null
if (-not $previous_tag) {
Write-Host "No previous tag found, using initial commit."
$previous_tag = git rev-list --max-parents=0 HEAD
}
Write-Host "Previous tag: $previous_tag"
echo "PREVIOUS_TAG=$previous_tag" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
- name: Generate release notes
id: generate_notes
shell: pwsh
run: |
Write-Host "Generating release notes from $env:PREVIOUS_TAG to HEAD..."
$repo_url = git config --get remote.origin.url
$notes = git log "$env:PREVIOUS_TAG..HEAD" --pretty=format:"- [`%h`]($repo_url/commit/%H): %s"
Write-Host "Release notes:"
Write-Host "$notes"
"### Changes in this release" | Out-File -FilePath release_notes.md -Encoding utf8
$notes | Out-File -FilePath release_notes.md -Encoding utf8 -Append
- name: Create Tag
id: create_tag
shell: pwsh
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
git tag $env:VERSION
git push origin $env:VERSION
- name: Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ env.VERSION }}
name: Release ${{ env.VERSION }}
body_path: release_notes.md
files: |
./dist/ait-v${{ env.VERSION }}-winx64.exe
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

2
.gitignore vendored
View File

@@ -5,3 +5,5 @@ ait.*.config.json
dist/
*.spec
build/
**/__pycache__
.gitea/scripts/.env

104
cliff.toml Normal file
View File

@@ -0,0 +1,104 @@
# CLIFF_VERSION=2.8.0
# git-cliff ~ default configuration file
# https://git-cliff.org/docs/configuration
#
# Lines starting with "#" are comments.
# Configuration options are organized into tables and keys.
# See documentation for more information on available options.
[remote.gitea]
owner = "maxp"
repo = "ait"
[changelog]
# postprocessors
postprocessors = [
{ pattern = '<GITEA_URL>', replace = "https://git.0xmax42.io" }, # replace gitea url
]
# template for the changelog header
header = """
# Changelog\n
All notable changes to this project will be documented in this file.\n
"""
# template for the changelog body
# https://keats.github.io/tera/docs/#introduction
body = """
{%- macro remote_url() -%}
<GITEA_URL>/{{ 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}](<REPO>/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 = "<!-- 0 -->🚀 Features" },
{ message = "^fix", group = "<!-- 1 -->🐛 Bug Fixes" },
{ message = "^doc", group = "<!-- 3 -->📚 Documentation" },
{ message = "^perf", group = "<!-- 4 -->⚡ Performance" },
{ message = "^refactor", group = "<!-- 2 -->🚜 Refactor" },
{ message = "^style", group = "<!-- 5 -->🎨 Styling" },
{ message = "^test", group = "<!-- 6 -->🧪 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 = "<!-- 7 -->⚙️ Miscellaneous Tasks" },
{ body = ".*security", group = "<!-- 8 -->🛡️ Security" },
{ message = "^revert", group = "<!-- 9 -->◀️ Revert" },
{ message = ".*", group = "<!-- 10 -->💼 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"