name: Auto version & release

# Fires on every push to main. Parses commit messages since the
# last semver tag, decides patch/minor/major bump, creates the
# tag, pushes. The tag push then triggers android-release.yml and
# docker-publish.yml. Fully hands-off — you never pick a version
# number; your commit messages do.
#
# Commit message grammar (Conventional Commits):
#   feat:  → minor bump   (new feature, backward-compatible)
#   fix:   → patch bump   (bug fix)
#   feat!: / BREAKING CHANGE in body → major bump
#   everything else (docs, refactor, chore, style, ci, test) → no bump
#
# Skip conditions (no new release created):
#   - No commits match the above patterns
#   - The most recent commit is itself a release commit ("Release v…")
#   - [skip ci] appears in any commit message since the last tag

on:
  push:
    branches: [main]

# Opt in to Node 24 runtime early (deprecation of Node 20 begins 2026-06-02)
env:
  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: 'true'

permissions:
  contents: write

jobs:
  version:
    runs-on: ubuntu-latest
    if: "!contains(github.event.head_commit.message, 'Release v') && !contains(github.event.head_commit.message, '[skip ci]')"
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          # Use RELEASE_PAT (a Personal Access Token you add as a repo
          # secret) so the tag push this workflow performs actually
          # triggers the downstream tag-based workflows (android-release,
          # docker-publish). GITHUB_TOKEN pushes are deliberately
          # blocked from triggering other workflows by GitHub.
          # Fine-grained PAT with "Contents: Read and write" on this
          # repo is enough.
          token: ${{ secrets.RELEASE_PAT || secrets.GITHUB_TOKEN }}

      - name: Find last semver tag
        id: last
        run: |
          LAST=$(git tag --list 'v[0-9]*.[0-9]*.[0-9]*' --sort=-v:refname | head -1)
          if [[ -z "$LAST" ]]; then
            LAST="v0.0.0"
            echo "no previous tag, starting from v0.0.0"
          fi
          echo "tag=$LAST"
          echo "tag=$LAST" >> "$GITHUB_OUTPUT"
          echo "version=${LAST#v}" >> "$GITHUB_OUTPUT"

      - name: Decide bump type from commit messages
        id: decide
        env:
          LAST: ${{ steps.last.outputs.tag }}
        run: |
          # All commits from the last tag → HEAD (exclusive of tag commit)
          if [[ "$LAST" == "v0.0.0" ]]; then
            MSGS=$(git log --format='%s%n%b%n---')
          else
            MSGS=$(git log "${LAST}..HEAD" --format='%s%n%b%n---')
          fi

          BUMP=none
          if echo "$MSGS" | grep -qE '(^|\n)(BREAKING CHANGE:|[a-z]+(\([^)]+\))?!:)'; then
            BUMP=major
          elif echo "$MSGS" | grep -qE '(^|\n)feat(\([^)]+\))?: '; then
            BUMP=minor
          elif echo "$MSGS" | grep -qE '(^|\n)fix(\([^)]+\))?: '; then
            BUMP=patch
          fi

          echo "Bump type decided: $BUMP"
          echo "bump=$BUMP" >> "$GITHUB_OUTPUT"
          {
            echo "### Commits since $LAST"
            echo '```'
            if [[ "$LAST" == "v0.0.0" ]]; then
              git log --oneline | head -20
            else
              git log "${LAST}..HEAD" --oneline
            fi
            echo '```'
            echo ""
            echo "**Bump decision**: \`$BUMP\`"
          } >> "$GITHUB_STEP_SUMMARY"

      - name: Stop if no release-worthy commits
        if: steps.decide.outputs.bump == 'none'
        run: |
          echo "No feat / fix / BREAKING commits since last tag — not cutting a release."
          echo "::notice::No release cut. Commit with 'feat:', 'fix:', or BREAKING CHANGE to trigger one."

      - name: Compute next version
        id: next
        if: steps.decide.outputs.bump != 'none'
        env:
          CUR: ${{ steps.last.outputs.version }}
          BUMP: ${{ steps.decide.outputs.bump }}
        run: |
          IFS='.' read -r MAJ MIN PAT <<< "$CUR"
          case "$BUMP" in
            major) NEXT="$((MAJ+1)).0.0" ;;
            minor) NEXT="${MAJ}.$((MIN+1)).0" ;;
            patch) NEXT="${MAJ}.${MIN}.$((PAT+1))" ;;
          esac
          echo "next=$NEXT" >> "$GITHUB_OUTPUT"
          echo "### Next version: v$NEXT" >> "$GITHUB_STEP_SUMMARY"

      - name: Configure git
        if: steps.decide.outputs.bump != 'none'
        run: |
          git config user.name  "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

      - name: Bump version strings + tag + push
        if: steps.decide.outputs.bump != 'none'
        env:
          V: ${{ steps.next.outputs.next }}
        run: |
          IFS='.' read -r MAJ MIN PAT <<< "$V"
          ANDROID_CODE=$(( MAJ * 100000 + MIN * 1000 + PAT ))

          sed -i -E "0,/(\"version\"[[:space:]]*:[[:space:]]*\")[^\"]+(\")/ s//\1${V}\2/" package.json
          sed -i -E "0,/(\"version\"[[:space:]]*:[[:space:]]*\")[^\"]+(\")/ s//\1${V}\2/" mobile/package.json
          sed -i -E \
            -e "s/versionCode +[0-9]+/versionCode ${ANDROID_CODE}/" \
            -e "s/versionName +\"[^\"]+\"/versionName \"${V}\"/" \
            mobile/android/app/build.gradle

          git add package.json mobile/package.json mobile/android/app/build.gradle
          git commit -m "Release v${V}"
          git tag -a "v${V}" -m "Release v${V}"

          git push origin HEAD
          git push origin "v${V}"

          echo "### Released v$V" >> "$GITHUB_STEP_SUMMARY"
          echo "android-release + docker-publish workflows will now run." >> "$GITHUB_STEP_SUMMARY"
