From 23c5c4a8be25865b34e484d7862534fce41eaa35 Mon Sep 17 00:00:00 2001 From: Shaun Arman Date: Sun, 12 Apr 2026 21:51:56 -0500 Subject: [PATCH] feat(ci): add automated changelog generation via git-cliff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add cliff.toml with Tera template: feat/fix/perf/docs/refactor included; ci/chore/build/test/style excluded - Bootstrap CHANGELOG.md from all existing semver tags (v0.1.0–v0.2.49) - Add changelog job to auto-tag.yml: runs after autotag in parallel with build jobs; installs git-cliff v2.7.0 musl binary, generates CHANGELOG.md, PATCHes Gitea release body with per-release notes, commits CHANGELOG.md to master with [skip ci] to prevent re-trigger, uploads as release asset - Add set -eu to all changelog job steps - Null-check RELEASE_ID before API calls; create release if missing (race-condition fix: changelog finishes before build jobs create release) - Add Changelog Generation section to docs/wiki/CICD-Pipeline.md --- .gitea/workflows/auto-tag.yml | 103 +++++++++++++ CHANGELOG.md | 267 ++++++++++++++++++++++++++++++++++ cliff.toml | 41 ++++++ docs/wiki/CICD-Pipeline.md | 46 ++++++ ticket-git-cliff-changelog.md | 74 ++++++++++ 5 files changed, 531 insertions(+) create mode 100644 CHANGELOG.md create mode 100644 cliff.toml create mode 100644 ticket-git-cliff-changelog.md diff --git a/.gitea/workflows/auto-tag.yml b/.gitea/workflows/auto-tag.yml index c48edde7..9d4bb6fe 100644 --- a/.gitea/workflows/auto-tag.yml +++ b/.gitea/workflows/auto-tag.yml @@ -65,6 +65,109 @@ jobs: echo "Tag $NEXT pushed successfully" + changelog: + needs: autotag + runs-on: linux-amd64 + container: + image: alpine:latest + steps: + - name: Install dependencies + run: | + set -eu + apk add --no-cache git curl jq + + - name: Checkout (full history + all tags) + env: + RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} + run: | + set -eu + git init + git remote add origin \ + "http://oauth2:${RELEASE_TOKEN}@172.0.0.29:3000/${GITHUB_REPOSITORY}.git" + git fetch --tags --depth=2147483647 origin + git checkout FETCH_HEAD + git config user.name "gitea-actions[bot]" + git config user.email "gitea-actions@local" + + - name: Install git-cliff + run: | + set -eu + CLIFF_VER="2.7.0" + curl -fsSL \ + "https://github.com/orhun/git-cliff/releases/download/v${CLIFF_VER}/git-cliff-${CLIFF_VER}-x86_64-unknown-linux-musl.tar.gz" \ + | tar -xz --strip-components=1 -C /usr/local/bin \ + "git-cliff-${CLIFF_VER}-x86_64-unknown-linux-musl/git-cliff" + + - name: Generate changelog + run: | + set -eu + git-cliff --config cliff.toml --output CHANGELOG.md + git-cliff --config cliff.toml --latest --strip all > /tmp/release_body.md + echo "=== Release body preview ===" + cat /tmp/release_body.md + + - name: Update Gitea release body + env: + RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} + run: | + set -eu + API="http://172.0.0.29:3000/api/v1/repos/$GITHUB_REPOSITORY" + TAG=$(git describe --tags --abbrev=0) + # Create release if it doesn't exist yet (build jobs may still be running) + curl -sf -X POST "$API/releases" \ + -H "Authorization: token $RELEASE_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"tag_name\":\"$TAG\",\"name\":\"TFTSR $TAG\",\"body\":\"Release $TAG\",\"draft\":false}" || true + RELEASE_ID=$(curl -sf "$API/releases/tags/$TAG" \ + -H "Authorization: token $RELEASE_TOKEN" | jq -r '.id') + if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "null" ]; then + echo "ERROR: Failed to get release ID for $TAG" + exit 1 + fi + curl -sf -X PATCH "$API/releases/$RELEASE_ID" \ + -H "Authorization: token $RELEASE_TOKEN" \ + -H "Content-Type: application/json" \ + --data-binary "{\"body\":$(jq -Rs . < /tmp/release_body.md)}" + echo "✓ Release body updated" + + - name: Commit CHANGELOG.md to master + env: + RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} + run: | + set -eu + git add CHANGELOG.md + if git diff --staged --quiet; then + echo "No changelog changes" + else + TAG=$(git describe --tags --abbrev=0) + git commit -m "chore: update CHANGELOG.md for ${TAG} [skip ci]" + git push origin HEAD:master + fi + + - name: Upload CHANGELOG.md as release asset + env: + RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} + run: | + set -eu + API="http://172.0.0.29:3000/api/v1/repos/$GITHUB_REPOSITORY" + TAG=$(git describe --tags --abbrev=0) + RELEASE_ID=$(curl -sf "$API/releases/tags/$TAG" \ + -H "Authorization: token $RELEASE_TOKEN" | jq -r '.id') + if [ -z "$RELEASE_ID" ] || [ "$RELEASE_ID" = "null" ]; then + echo "ERROR: Failed to get release ID for $TAG" + exit 1 + fi + EXISTING=$(curl -sf "$API/releases/$RELEASE_ID" \ + -H "Authorization: token $RELEASE_TOKEN" \ + | jq -r '.assets[]? | select(.name=="CHANGELOG.md") | .id') + [ -n "$EXISTING" ] && curl -sf -X DELETE \ + "$API/releases/$RELEASE_ID/assets/$EXISTING" \ + -H "Authorization: token $RELEASE_TOKEN" + curl -sf -X POST "$API/releases/$RELEASE_ID/assets" \ + -H "Authorization: token $RELEASE_TOKEN" \ + -F "attachment=@CHANGELOG.md;filename=CHANGELOG.md" + echo "✓ CHANGELOG.md uploaded" + wiki-sync: runs-on: linux-amd64 container: diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..dca0080b --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,267 @@ +# Changelog + +All notable changes to TFTSR are documented here. +Commit types shown: feat, fix, perf, docs, refactor. +CI, chore, and build changes are excluded. + +## [Unreleased] + +### Bug Fixes +- Rename GITEA_TOKEN to TF_TOKEN to comply with naming restrictions +- Remove actions/checkout to avoid Node.js dependency +- Use ubuntu container with git installed +- Use actions/checkout with token auth and self-hosted runner +- Use IP addresses for internal services +- Simplified workflow syntax +- Add debugging output for Ollamaresponse +- Correct Ollama URL, API endpoint, and JSON construction in pr-review workflow +- Add diagnostics to identify empty Ollama response root cause +- Use bash shell and remove bash-only substring expansion in pr-review +- Restore migration 014, bump version to 0.2.50, harden pr-review workflow +- Harden pr-review workflow and sync versions to 0.2.50 +- Configure container DNS to resolve ollama-ui.tftsr.com +- Harden pr-review workflow — URLs, DNS, correctness and reliability +- Resolve AI review false positives and address high/medium issues +- Replace github.server_url with hardcoded gogs.tftsr.com for container access +- Revert to two-dot diff — three-dot requires merge base unavailable in shallow clone +- Harden pr-review workflow — secret redaction, log safety, auth header +- **ci**: Address AI review — rustup idempotency and cargo --locked +- **ci**: Replace docker:24-cli with alpine + docker-cli in build-images +- **docker**: Add ca-certificates to arm64 base image step 1 +- **ci**: Resolve test.yml failures — Cargo.lock, updated test assertions +- **ci**: Address second AI review — || true, ca-certs, cache@v4, key suffixes +- **ci**: Add APPIMAGE_EXTRACT_AND_RUN to build-linux-amd64 + +### Documentation +- **docker**: Expand rebuild trigger comments to include OpenSSL and Tauri CLI + +### Features +- Add automated PR review workflow with Ollama AI + +### Performance +- **ci**: Use pre-baked images and add cargo/npm caching + +## [0.2.49] — 2026-04-10 + +### Bug Fixes +- Add missing ai_providers migration (014) + +## [0.2.48] — 2026-04-10 + +### Bug Fixes +- Lint fixes and formatting cleanup + +### Features +- Support GenAI datastore file uploads and fix paste image upload + +## [0.2.47] — 2026-04-09 + +### Bug Fixes +- Use 'provider' argument name to match Rust command signature + +## [0.2.46] — 2026-04-09 + +### Bug Fixes +- Add @types/testing-library__react for TypeScript compilation + +### Update +- Node_modules from npm install + +## [0.2.45] — 2026-04-09 + +### Bug Fixes +- Force single test thread for Rust tests to eliminate race conditions + +## [0.2.43] — 2026-04-09 + +### Bug Fixes +- Fix encryption test race condition with parallel tests +- OpenWebUI provider connection and missing command registrations + +### Features +- Add image attachment support with PII detection + +## [0.2.42] — 2026-04-07 + +### Documentation +- Add AGENTS.md and SECURITY_AUDIT.md + +## [0.2.41] — 2026-04-07 + +### Bug Fixes +- **db,auth**: Auto-generate encryption keys for release builds +- **lint**: Use inline format args in auth.rs +- **lint**: Resolve all clippy warnings for CI compliance +- **fmt**: Apply rustfmt formatting to webview_fetch.rs +- **types**: Replace normalizeApiFormat() calls with direct value + +### Documentation +- **architecture**: Add C4 diagrams, ADRs, and architecture overview + +### Features +- **ai**: Add tool-calling and integration search as AI data source + +## [0.2.40] — 2026-04-06 + +### Bug Fixes +- Add user_id support and OAuth shell permission (v0.2.6) +- Use Wiki secret for authenticated wiki sync (v0.2.8) +- Persist integration settings and implement persistent browser windows +- ARM64 build uses native target instead of cross-compile +- Resolve clippy uninlined_format_args in integrations and related modules +- Resolve clippy format-args failures and OpenSSL vendoring issue +- Resolve macOS bundle path after app rename +- **ci**: Make release artifacts reliable across platforms +- **ci**: Harden release asset uploads for reruns +- **ci**: Trigger release workflow from auto-tag pushes +- **ci**: Guarantee release jobs run after auto-tag +- **ci**: Use stable auto-tag job outputs for release fanout +- **ci**: Run post-tag release builds without job-output gating +- **ci**: Repair auto-tag workflow yaml so jobs trigger +- **ci**: Force explicit linux arm64 target for release artifacts +- **ci**: Run linux arm release natively and enforce arm artifacts +- **ci**: Unblock release jobs and namespace linux artifacts by arch +- **security**: Harden secret handling and audit integrity +- **pii**: Remove lookahead from hostname regex, fix fmt in analysis test +- **security**: Enforce PII redaction before AI log transmission +- **ci**: Unblock release jobs and namespace linux artifacts by arch +- **ci**: Fix arm64 cross-compile, drop cargo install tauri-cli, move wiki-sync +- **ci**: Rebuild apt sources with per-arch entries before arm64 cross-compile install +- **ci**: Add workflow_dispatch and concurrency guard to auto-tag +- **ci**: Replace heredoc with printf in arm64 install step +- **ci**: Switch build-linux-arm64 to Ubuntu 22.04 with ports mirror +- **ci**: Remove GITHUB_PATH append that was breaking arm64 install step +- **ci**: Use POSIX dot instead of source in arm64 build step +- **ci**: Add make to arm64 host tools for OpenSSL vendored build +- **ci**: Set APPIMAGE_EXTRACT_AND_RUN=1 for arm64 AppImage bundling +- **ci**: Restrict arm64 bundles to deb,rpm — skip AppImage +- **security**: Add path canonicalization and actionable permission error in install_ollama_from_bundle +- **ci**: Skip Ollama download on macOS build — runner has no access to GitHub binary assets +- **ci**: Remove all Ollama bundle download steps — use UI download button instead +- **ci**: Remove explicit docker.sock mount — act_runner mounts it automatically + +### Documentation +- Add Custom REST provider documentation +- Update wiki for v0.2.6 - integrations and Custom REST provider +- Update CI pipeline wiki and add ticket summary for arm64 fix + +### Features +- Implement Confluence, ServiceNow, and Azure DevOps REST API clients +- Add Custom REST provider support +- Add automatic wiki sync to CI workflow (v0.2.7) +- Add temperature and max_tokens support for Custom REST providers (v0.2.9) +- Add multi-mode authentication for integrations (v0.2.10) +- Complete webview cookie extraction implementation +- Add custom_rest provider mode and rebrand application name +- **rebrand**: Rename binary to trcaa and auto-generate DB key +- **ui**: Fix model dropdown, auth prefill, PII persistence, theme toggle, and Ollama bundle +- **ci**: Add persistent pre-baked Docker builder images + +### Refactoring +- **ci**: Remove standalone release workflow +- **ollama**: Remove download/install buttons — show plain install instructions only + +## [0.2.4] — 2026-04-03 + +### Features +- Implement OAuth2 token exchange and AES-256-GCM encryption +- Add OAuth2 Tauri commands for integration authentication +- Implement OAuth2 callback server with automatic token exchange +- Add OAuth2 frontend UI and complete integration flow + +## [0.2.3] — 2026-04-03 + +### Bug Fixes +- Improve Cancel button contrast in AI disclaimer modal + +### Features +- Add database schema for integration credentials and config + +## [0.2.1] — 2026-04-03 + +### Bug Fixes +- Button text visibility, toggle contrast, create_issue IPC, ad-hoc codesign +- Dropdown text invisible on macOS + correct codesign order for DMG +- Add explicit text-foreground to SelectTrigger, SelectValue, and SelectItem +- Ollama detection, install guide UI, and AI Providers auto-fill +- Provider test FK error, model pull white screen, RECOMMENDED badge +- Provider routing uses provider_type, Active badge, fmt +- Navigate to /logs after issue creation, fix dashboard category display +- Dashboard shows — while loading, exposes errors, adds refresh button +- ListIssuesCmd was sending {query} but Rust expects {filter} — caused dashboard to always show 0 open issues +- Arm64 linux cross-compilation — add multiarch and pkg-config env vars +- Close from chat works before issue loads; save user reason as resolution step; dynamic version +- DomainPrompts closing brace too early; arm64 use native platform image +- UI contrast issues and ARM64 build failure +- Remove Woodpecker CI and fix Gitea Actions ARM64 build +- UI visibility issues, export errors, filtering, and audit log enhancement +- ARM64 build native compilation instead of cross-compilation +- Improve release artifact upload error handling +- Install jq in Linux/Windows build containers +- Improve download button visibility and add DOCX export +- Implement native DOCX export without pandoc dependency + +### Documentation +- Add LiteLLM + AWS Bedrock integration guide + +### Features +- Add macOS arm64 act_runner and release build job +- Auto-increment patch tag on every merge to master +- Inline file/screenshot attachment in triage chat +- Close issues, restore history, auto-save resolution steps +- Expand domains to 13 — add Telephony, Security/Vault, Public Safety, Application, Automation/CI-CD +- Add HPE, Dell, Identity domains + expand k8s/security/observability/VESTA NXT +- Add AI disclaimer modal before creating new issues + +## [0.1.1] — 2026-03-30 + +### Bug Fixes +- Remove unused tauri-plugin-updater + SQLCipher 16KB page size +- Prevent WebKit/GTK system theme from overriding input text colors on Linux +- Set SQLCipher cipher_page_size BEFORE first database access + +### Documentation +- Update README, wiki, and UI version to v0.1.1 + +## [0.1.0] — 2026-03-29 + +### Bug Fixes +- Resolve all clippy lints (uninlined format args, range::contains, push_str single chars) +- Inline format args for Rust 1.88 clippy compatibility +- Retain GPU-VRAM-eligible models in recommender even when RAM is low +- Use alpine/git with explicit checkout for tag-based release builds +- Set CI=true for cargo tauri build — Woodpecker sets CI=woodpecker which Tauri CLI rejects +- Arm64 cross-compilation — add multiarch pkg-config sysroot setup +- Remove arm64 from release pipeline — webkit2gtk multiarch conflict on x86_64 host +- Write artifacts to workspace (shared between steps), not /artifacts/ +- Upload step needs gogs_default network to reach Gogs API (host firewall blocks default bridge) +- Use bundled-sqlcipher-vendored-openssl for portable Windows cross-compilation +- Add make to windows build step (required by vendored OpenSSL) +- Replace empty icon placeholder files with real app icons +- Suppress MinGW auto-export to resolve Windows DLL ordinal overflow +- Use when: platform: for arm64 step routing (Woodpecker 0.15.4 compat) +- Remove unused tauri-plugin-cli causing startup crash +- Use $GITHUB_REF_NAME env var instead of ${{ github.ref_name }} expression + +### Documentation +- Update PLAN.md with accurate implementation status +- Add CLAUDE.md with development guidance +- Add wiki source files and CI auto-sync pipeline +- Update PLAN.md - Phase 11 complete, redact token references +- Update README and wiki for v0.1.0-alpha release +- Remove broken arm64 CI step, document Woodpecker 0.15.4 limitation +- Update README and wiki for Gitea Actions migration + +### Features +- Add Windows amd64 cross-compile to release pipeline; add arm64 QEMU agent +- Add native linux/arm64 release build step + +### Security +- Rotate exposed token, redact from PLAN.md, add secret patterns to .gitignore + +## [0.1.0-test] — 2026-03-15 + +### Features +- Initial implementation of TFTSR IT Triage & RCA application + + diff --git a/cliff.toml b/cliff.toml new file mode 100644 index 00000000..2c9f59ed --- /dev/null +++ b/cliff.toml @@ -0,0 +1,41 @@ +[changelog] +header = """ +# Changelog + +All notable changes to TFTSR are documented here. +Commit types shown: feat, fix, perf, docs, refactor. +CI, chore, and build changes are excluded. + +""" +body = """ +{% 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 -%} +- {% if commit.scope %}**{{ commit.scope }}**: {% endif %}{{ commit.message | upper_first }} +{% endfor %} +{% endfor %} +""" +footer = "" +trim = true + +[git] +conventional_commits = true +filter_unconventional = true +tag_pattern = "v[0-9].*" +ignore_tags = "rc|alpha|beta" +sort_commits = "oldest" +commit_parsers = [ + { message = "^feat", group = "Features" }, + { message = "^fix", group = "Bug Fixes" }, + { message = "^perf", group = "Performance" }, + { message = "^docs", group = "Documentation" }, + { message = "^refactor", group = "Refactoring" }, + { message = "^ci|^chore|^build|^test|^style", skip = true }, +] diff --git a/docs/wiki/CICD-Pipeline.md b/docs/wiki/CICD-Pipeline.md index 2d10f30c..bd210144 100644 --- a/docs/wiki/CICD-Pipeline.md +++ b/docs/wiki/CICD-Pipeline.md @@ -257,6 +257,52 @@ UPDATE protect_branch SET protected=true, require_pull_request=true WHERE repo_i --- +## Changelog Generation + +Changelogs are generated automatically by **git-cliff** on every release. +Configuration lives in `cliff.toml` at the repo root. + +### How it works + +A `changelog` job in `auto-tag.yml` runs in parallel with the build jobs, immediately +after `autotag` completes: + +1. Clones the full repo history with all tags (`--depth=2147483647` — git-cliff needs + every tag to compute version boundaries). +2. Downloads the git-cliff v2.7.0 static musl binary (~5 MB, no image change needed). +3. Runs `git-cliff --output CHANGELOG.md` to regenerate the full cumulative changelog. +4. Runs `git-cliff --latest --strip all` to produce release notes for the new tag only. +5. PATCHes the Gitea release body with those notes (replaces the static `"Release vX.Y.Z"`). +6. Commits `CHANGELOG.md` to master with `[skip ci]` appended to the message. + The `[skip ci]` token prevents `auto-tag.yml` from re-triggering on the CHANGELOG commit. +7. Uploads `CHANGELOG.md` as a release asset (replaces any previous version). + +### cliff.toml reference + +| Setting | Value | +|---------|-------| +| `tag_pattern` | `v[0-9].*` | +| `ignore_tags` | `rc\|alpha\|beta` | +| `filter_unconventional` | `true` — non-conventional commits are dropped | +| Included types | `feat`, `fix`, `perf`, `docs`, `refactor` | +| Excluded types | `ci`, `chore`, `build`, `test`, `style` | + +### Loop prevention + +The `[skip ci]` suffix on the CHANGELOG commit message is recognised by Gitea Actions +and causes the workflow to be skipped for that push. Without it, the CHANGELOG commit +would trigger `auto-tag.yml` again, incrementing the patch version forever. + +### Bootstrap + +The initial `CHANGELOG.md` was generated locally before the first PR: +```sh +git-cliff --config cliff.toml --output CHANGELOG.md +``` +Subsequent runs are fully automated by CI. + +--- + ## Known Issues & Fixes ### Debian Multiarch Breaks arm64 Cross-Compile (`held broken packages`) diff --git a/ticket-git-cliff-changelog.md b/ticket-git-cliff-changelog.md new file mode 100644 index 00000000..ef4f246d --- /dev/null +++ b/ticket-git-cliff-changelog.md @@ -0,0 +1,74 @@ +# feat: Automated Changelog via git-cliff + +## Description + +Introduces automated changelog generation using **git-cliff**, a tool that parses +conventional commits and produces formatted Markdown changelogs. + +Previously, every Gitea release body contained only the static text `"Release vX.Y.Z"`. +With this change, releases display a categorised, human-readable list of all commits +since the previous version. + +**Root cause / motivation:** No changelog tooling existed. The project follows +Conventional Commits throughout but the information was never surfaced to end-users. + +**Files changed:** +- `cliff.toml` (new) — git-cliff configuration; defines commit parsers, ignored tags, + output template, and which commit types appear in the changelog +- `CHANGELOG.md` (new) — bootstrapped from all existing tags; maintained by CI going forward +- `.gitea/workflows/auto-tag.yml` — new `changelog` job that runs after `autotag` +- `docs/wiki/CICD-Pipeline.md` — "Changelog Generation" section added + +## Acceptance Criteria + +- [ ] `cliff.toml` present at repo root with working Tera template +- [ ] `CHANGELOG.md` present at repo root, bootstrapped from all existing semver tags +- [ ] `changelog` job in `auto-tag.yml` runs after `autotag` (parallel with build jobs) +- [ ] Each Gitea release body shows grouped conventional-commit entries instead of + static `"Release vX.Y.Z"` +- [ ] `CHANGELOG.md` committed to master on every release with `[skip ci]` suffix + (no infinite re-trigger loop) +- [ ] `CHANGELOG.md` uploaded as a downloadable release asset +- [ ] CI/chore/build/test/style commits excluded from changelog output +- [ ] `docs/wiki/CICD-Pipeline.md` documents the changelog generation process + +## Work Implemented + +### `cliff.toml` +- Tera template with proper whitespace control (`-%}` / `{%- `) for clean output +- Included commit types: `feat`, `fix`, `perf`, `docs`, `refactor` +- Excluded commit types: `ci`, `chore`, `build`, `test`, `style` +- `ignore_tags = "rc|alpha|beta"` — pre-release tags excluded from version boundaries +- `filter_unconventional = true` — non-conventional commits dropped silently +- `sort_commits = "oldest"` — chronological order within each version + +### `CHANGELOG.md` +- Bootstrapped locally using git-cliff v2.7.0 (aarch64 musl binary) +- Covers all tagged versions from `v0.1.0` through `v0.2.49` plus `[Unreleased]` +- 267 lines covering the full project history + +### `.gitea/workflows/auto-tag.yml` — `changelog` job +- `needs: autotag` — waits for the new tag to exist before running +- Full history clone: `git fetch --tags --depth=2147483647` so git-cliff can resolve + all version boundaries +- git-cliff v2.7.0 downloaded as a static x86_64 musl binary (~5 MB); no custom + image required +- Generates full `CHANGELOG.md` and per-release notes (`--latest --strip all`) +- PATCHes the Gitea release body via API with JSON-safe escaping (`jq -Rs .`) +- Commits `CHANGELOG.md` to master with `[skip ci]` to prevent workflow re-trigger +- Deletes any existing `CHANGELOG.md` asset before re-uploading (rerun-safe) +- Runs in parallel with all build jobs — no added wall-clock latency + +### `docs/wiki/CICD-Pipeline.md` +- Added "Changelog Generation" section before "Known Issues & Fixes" +- Describes the five-step process, cliff.toml settings, and loop prevention mechanism + +## Testing Needed + +- [ ] Merge this PR to master; verify `changelog` CI job succeeds in Gitea Actions +- [ ] Check Gitea release body for the new version tag — should show grouped commit list +- [ ] Verify `CHANGELOG.md` was committed to master (check git log after CI runs) +- [ ] Verify `CHANGELOG.md` appears as a downloadable asset on the release page +- [ ] Push a subsequent commit to master; confirm the `[skip ci]` CHANGELOG commit does + NOT trigger a second run of `auto-tag.yml` +- [ ] Confirm CI/chore commits are absent from the release body