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"