rustodo 2.26.0

A modern, powerful task manager built with Rust
Documentation
name: Release

on:
  push:
    branches:
      - main
    paths-ignore:
      - "CHANGELOG.md"

permissions: {}

concurrency:
  group: release-${{ github.ref }}
  cancel-in-progress: false

jobs:
  check:
    name: Check if release needed
    if: |
      github.actor != 'github-actions[bot]' &&
      !contains(github.event.head_commit.message, 'chore(release)')
    runs-on: ubuntu-latest
    outputs:
      should_release: ${{ steps.check.outputs.should_release }}
      bump: ${{ steps.check.outputs.bump }}

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Check commits for release triggers
        id: check
        run: |
          git fetch --tags --force

          LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "")

          if [ -z "$LAST_TAG" ]; then
            COMMITS=$(git log --oneline --pretty=format:"%s")
          else
            COMMITS=$(git log ${LAST_TAG}..HEAD --oneline --pretty=format:"%s")
          fi

          echo "Commits since last tag:"
          echo "$COMMITS"

          COMMITS=$(echo "$COMMITS" | grep -v "\[skip ci\]" | grep -v "^chore(release)")

          if [ -z "$COMMITS" ]; then
            echo "should_release=false" >> $GITHUB_OUTPUT
            echo "bump=none" >> $GITHUB_OUTPUT
            exit 0
          fi

          if echo "$COMMITS" | grep -qE "BREAKING CHANGE|!:"; then
            echo "should_release=true" >> $GITHUB_OUTPUT
            echo "bump=major" >> $GITHUB_OUTPUT
          elif echo "$COMMITS" | grep -qE "^feat(\(.+\))?:"; then
            echo "should_release=true" >> $GITHUB_OUTPUT
            echo "bump=minor" >> $GITHUB_OUTPUT
          elif echo "$COMMITS" | grep -qE "^(fix|perf|refactor)(\(.+\))?:"; then
            echo "should_release=true" >> $GITHUB_OUTPUT
            echo "bump=patch" >> $GITHUB_OUTPUT
          else
            echo "should_release=false" >> $GITHUB_OUTPUT
            echo "bump=none" >> $GITHUB_OUTPUT
          fi

  build:
    name: Build (${{ matrix.suffix }})
    needs: check
    if: needs.check.outputs.should_release == 'true'
    permissions:
      contents: read

    strategy:
      matrix:
        include:
          - os: ubuntu-latest
            target: x86_64-unknown-linux-gnu
            suffix: linux-amd64
            binary: todo
          - os: macos-latest
            target: aarch64-apple-darwin
            suffix: darwin-arm64
            binary: todo
          - os: macos-latest
            target: x86_64-apple-darwin
            suffix: darwin-amd64
            binary: todo
          - os: windows-latest
            target: x86_64-pc-windows-msvc
            suffix: windows-amd64
            binary: todo.exe

    runs-on: ${{ matrix.os }}

    steps:
      - uses: actions/checkout@v4

      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}

      - uses: Swatinem/rust-cache@v2

      - name: Build release binary
        run: cargo build --release --target ${{ matrix.target }}

      - name: Prepare artifact
        shell: bash
        run: |
          mkdir -p dist
          if [ "${{ matrix.os }}" = "windows-latest" ]; then
            cp target/${{ matrix.target }}/release/${{ matrix.binary }} dist/todo-${{ matrix.suffix }}.exe
          else
            cp target/${{ matrix.target }}/release/${{ matrix.binary }} dist/todo-${{ matrix.suffix }}
          fi

      - uses: actions/upload-artifact@v4
        with:
          name: binary-${{ matrix.suffix }}
          path: dist/*
          retention-days: 1

  release:
    name: Release
    needs: [check, build]
    if: needs.check.outputs.should_release == 'true'
    runs-on: ubuntu-latest
    permissions:
      contents: write

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - uses: dtolnay/rust-toolchain@stable

      - uses: Swatinem/rust-cache@v2

      - uses: taiki-e/install-action@v2
        with:
          tool: git-cliff

      - uses: taiki-e/install-action@v2
        with:
          tool: cargo-edit

      - name: Configure git
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"

      - name: Fetch tags
        run: git fetch --tags --force

      - name: Bump version from last tag
        id: bump
        run: |
          BUMP=${{ needs.check.outputs.bump }}
          echo "Bumping: $BUMP"

          cargo set-version --bump $BUMP

          NEW_VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/version = "\(.*\)"/\1/')
          echo "new_version=$NEW_VERSION" >> $GITHUB_OUTPUT

      - uses: actions/download-artifact@v4
        with:
          path: dist
          merge-multiple: true

      - name: Commit and tag
        run: |
          VERSION=v${{ steps.bump.outputs.new_version }}
          git add Cargo.toml Cargo.lock
          git commit -m "chore(release): $VERSION [skip ci]"
          git tag -a "$VERSION" -m "Release $VERSION"
          git push origin main --follow-tags

      - name: Generate CHANGELOG
        run: git-cliff --config cliff.toml --output CHANGELOG.md

      - name: Generate release notes
        run: git-cliff --config cliff.toml --latest --strip header --output RELEASE_NOTES.md

      - name: Commit CHANGELOG
        run: |
          git add CHANGELOG.md
          git commit -m "chore(release): update changelog for v${{ steps.bump.outputs.new_version }} [skip ci]" || echo "No changes"
          git push

      - name: Create GitHub Release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: v${{ steps.bump.outputs.new_version }}
          name: v${{ steps.bump.outputs.new_version }}
          body_file: RELEASE_NOTES.md
          files: |
            dist/todo-linux-amd64
            dist/todo-darwin-arm64
            dist/todo-darwin-amd64
            dist/todo-windows-amd64.exe