Files
homelab-design/decisions/0015-ci-notifications-and-semantic-versioning.md

4.4 KiB

ADR-0015: CI Notifications and Semantic Versioning

Status

Accepted

Date

2026-02-02

Context

With Gitea Actions now handling CI/CD for our repositories (ADR-0013), we need:

  1. Visibility - Notifications when pipelines pass or fail
  2. Versioning - Consistent, automated semantic versioning for releases
  3. Traceability - Clear version tags linked to commits

We already have ntfy deployed for push notifications (used by alertmanager and Gatus).

Decision

1. CI Notifications via ntfy

All CI workflows send notifications to ntfy on pipeline completion:

env:
  NTFY_URL: http://ntfy.observability.svc.cluster.local:80

notify:
  name: Notify
  runs-on: ubuntu-latest
  needs: [lint, test]
  if: always()
  steps:
    - name: Notify on success
      if: needs.lint.result == 'success' && needs.test.result == 'success'
      run: |
        curl -s \
          -H "Title: ✅ CI Passed: ${{ gitea.repository }}" \
          -H "Priority: default" \
          -H "Tags: white_check_mark,github" \
          -H "Click: ${{ gitea.server_url }}/${{ gitea.repository }}/actions/runs/${{ gitea.run_id }}" \
          -d "Branch: ${{ gitea.ref_name }}" \
          ${{ env.NTFY_URL }}/gitea-ci

    - name: Notify on failure
      if: needs.lint.result == 'failure' || needs.test.result == 'failure'
      run: |
        curl -s \
          -H "Title: ❌ CI Failed: ${{ gitea.repository }}" \
          -H "Priority: high" \
          -H "Tags: x,github" \
          -d "..." \
          ${{ env.NTFY_URL }}/gitea-ci

Topic: gitea-ci (subscribe at ntfy.daviestechlabs.io/gitea-ci)

2. Semantic Versioning

Version bumps are determined by commit message keywords:

Keyword in Commit Version Bump Example
major: or BREAKING CHANGE Major (1.0.0 → 2.0.0) major: redesign API
minor: or feat: Minor (1.0.0 → 1.1.0) feat: add streaming
fix:, patch:, or other Patch (1.0.0 → 1.0.1) fix: null pointer

Implementation in workflow:

release:
  name: Release
  runs-on: ubuntu-latest
  needs: [lint, test]
  if: github.ref == 'refs/heads/main' && github.event_name == 'push'
  steps:
    - name: Checkout
      uses: actions/checkout@v4
      with:
        fetch-depth: 0

    - name: Determine version bump
      id: version
      run: |
        # Get latest tag or default to v0.0.0
        LATEST=$(git describe --tags --abbrev=0 2>/dev/null || echo "v0.0.0")
        VERSION=${LATEST#v}
        IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
        
        # Check commit message for keywords
        MSG="${{ github.event.head_commit.message }}"
        if echo "$MSG" | grep -qiE "^major:|BREAKING CHANGE"; then
          MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0
        elif echo "$MSG" | grep -qiE "^(minor:|feat:)"; then
          MINOR=$((MINOR + 1)); PATCH=0
        else
          PATCH=$((PATCH + 1))
        fi
        
        NEW_VERSION="v${MAJOR}.${MINOR}.${PATCH}"
        echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
        echo "Bumping $LATEST → $NEW_VERSION"

    - name: Create tag
      run: |
        git config user.name "gitea-actions[bot]"
        git config user.email "actions@git.daviestechlabs.io"
        git tag -a ${{ steps.version.outputs.version }} -m "Release ${{ steps.version.outputs.version }}"
        git push origin ${{ steps.version.outputs.version }}

    - name: Create release
      uses: softprops/action-gh-release@v2
      with:
        tag_name: ${{ steps.version.outputs.version }}
        generate_release_notes: true

3. Topic Structure

Topic Purpose
gitea-ci All CI pipeline notifications
gitea-releases New version releases (optional)

Consequences

Positive

  • Immediate visibility - Know instantly when builds fail
  • Consistent versioning - No manual version management
  • Audit trail - Git tags link versions to commits
  • Low friction - Developers just use conventional commit prefixes

Negative

  • Commit message discipline - Requires consistent prefixes
  • ntfy dependency - Notifications require ntfy to be running
  • Tag pollution - Every main push creates a tag (can skip with [skip ci])