Skip to content

chore: remove empty v1.2.1 changelog entry before re-release #5

chore: remove empty v1.2.1 changelog entry before re-release

chore: remove empty v1.2.1 changelog entry before re-release #5

Workflow file for this run

name: Release
on:
push:
tags:
- 'v[0-9]+.[0-9]+.[0-9]+' # Only match semver tags (v1.2.3), NOT v1.2.3+456
permissions:
contents: write
jobs:
build-and-release:
name: Build Android & Create Release
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
fetch-tags: true
- name: Extract version from tag
id: version
run: |
FULL_TAG=${GITHUB_REF#refs/tags/}
# Strip 'v' prefix and any build metadata (+NNN)
TAG=$(echo "${FULL_TAG#v}" | cut -d'+' -f1)
echo "tag=$TAG" >> $GITHUB_OUTPUT
echo "full_tag=$FULL_TAG" >> $GITHUB_OUTPUT
# Get the previous tag for changelog range
PREV_TAG=$(git tag --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sed -n '2p')
if [ -z "$PREV_TAG" ]; then
echo "prev_tag=" >> $GITHUB_OUTPUT
echo "is_first=true" >> $GITHUB_OUTPUT
else
echo "prev_tag=$PREV_TAG" >> $GITHUB_OUTPUT
echo "is_first=false" >> $GITHUB_OUTPUT
fi
echo "Parsed: tag=$TAG full_tag=$FULL_TAG prev_tag=$PREV_TAG"
- name: Generate changelog and release notes
id: changelog
run: |
TAG=${{ steps.version.outputs.full_tag }}
PREV_TAG=${{ steps.version.outputs.prev_tag }}
IS_FIRST=${{ steps.version.outputs.is_first }}
if [ "$IS_FIRST" = "true" ]; then
COMMITS=$(git log --pretty=format:"- %s (%h)" --no-merges)
else
COMMITS=$(git log --pretty=format:"- %s (%h)" --no-merges "${PREV_TAG}..${TAG}")
fi
# Categorize commits
FEATURES=$(echo "$COMMITS" | grep -iE "^- (feat|add|new)" || true)
FIXES=$(echo "$COMMITS" | grep -iE "^- (fix|bug|patch|hotfix)" || true)
DOCS=$(echo "$COMMITS" | grep -iE "^- (doc|docs|readme)" || true)
OTHER=$(echo "$COMMITS" | grep -ivE "^- (feat|add|new|fix|bug|patch|hotfix|doc|docs|readme|chore: update version|chore: update changelog)" || true)
# --- RELEASE_NOTES.md: categorized notes for internal use ---
{
echo "## What's New in $TAG"
echo ""
if [ -n "$FEATURES" ]; then
echo "### ✨ New Features"
echo "$FEATURES"
echo ""
fi
if [ -n "$FIXES" ]; then
echo "### πŸ› Bug Fixes"
echo "$FIXES"
echo ""
fi
if [ -n "$DOCS" ]; then
echo "### πŸ“š Documentation"
echo "$DOCS"
echo ""
fi
if [ -n "$OTHER" ]; then
echo "### πŸ”§ Other Changes"
echo "$OTHER"
echo ""
fi
if [ "$IS_FIRST" != "true" ]; then
echo "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREV_TAG}...${TAG}"
fi
} > RELEASE_NOTES.md
echo "=== RELEASE_NOTES.md ==="
cat RELEASE_NOTES.md
- name: Generate release description
run: |
TAG=${{ steps.version.outputs.full_tag }}
# Read categorized sections from RELEASE_NOTES.md
FEATURES=$(sed -n '/### ✨/,/^###\|^$/p' RELEASE_NOTES.md | grep "^-" || true)
FIXES=$(sed -n '/### πŸ›/,/^###\|^$/p' RELEASE_NOTES.md | grep "^-" || true)
{
echo "# ⚑ Mostro Mobile ${TAG}"
echo ""
echo "A new version of the Mostro P2P Bitcoin trading app is here!"
echo ""
if [ -n "$FEATURES" ]; then
FEATURE_COUNT=$(echo "$FEATURES" | wc -l)
echo "This release brings **${FEATURE_COUNT} new feature(s)**:"
echo ""
echo "$FEATURES"
echo ""
fi
if [ -n "$FIXES" ]; then
FIX_COUNT=$(echo "$FIXES" | wc -l)
echo "We also squashed **${FIX_COUNT} bug(s)** πŸ›:"
echo ""
echo "$FIXES"
echo ""
fi
echo "---"
echo ""
echo "### πŸ“₯ Install"
echo "Download the APK for your device architecture below:"
echo "- \`mostro-${TAG}-arm64-v8a.apk\` β€” most modern Android phones (64-bit)"
echo "- \`mostro-${TAG}-armeabi-v7a.apk\` β€” older Android phones (32-bit)"
echo ""
echo "> The \`.aab\` (Android App Bundle) is for Google Play Store distribution."
echo ""
echo "### πŸ–₯️ Desktop"
echo "Linux and macOS builds are triggered separately and will be attached to this release shortly."
echo ""
echo "### πŸ“‹ Full Changelog"
echo "See [CHANGELOG.md](https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md) for the complete history."
} > RELEASE_BODY.md
echo "=== RELEASE_BODY.md ==="
cat RELEASE_BODY.md
- name: Commit changelog and version bump to main
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git fetch origin main
git checkout main
git merge --ff-only ${{ steps.version.outputs.full_tag }}
# Apply version bump β€” use clean semver (no build metadata)
VERSION=${{ steps.version.outputs.tag }}
BUILD=$(git rev-list --count HEAD)
sed -i "s/^version: .*/version: ${VERSION}+${BUILD}/" pubspec.yaml
echo "Updated pubspec.yaml to version: ${VERSION}+${BUILD}"
# Generate CHANGELOG.md on main (where the existing CHANGELOG lives)
DATESTAMP=$(date -u +"%Y-%m-%d")
TAG=${{ steps.version.outputs.full_tag }}
# Read categorized commits from RELEASE_NOTES.md (generated earlier, still in working tree)
FEATURES=$(sed -n '/### ✨/,/^###\|^$/p' RELEASE_NOTES.md | grep "^-" || true)
FIXES=$(sed -n '/### πŸ›/,/^###\|^$/p' RELEASE_NOTES.md | grep "^-" || true)
DOCS=$(sed -n '/### πŸ“š/,/^###\|^$/p' RELEASE_NOTES.md | grep "^-" || true)
OTHER=$(sed -n '/### πŸ”§/,/^###\|^$/p' RELEASE_NOTES.md | grep "^-" || true)
{
echo "# Changelog"
echo ""
echo "All notable changes to this project will be documented in this file."
echo ""
echo "The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),"
echo "and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)."
echo ""
echo "## [$TAG] - $DATESTAMP"
echo ""
if [ -n "$FEATURES" ]; then
echo "### Added"
echo "$FEATURES"
echo ""
fi
if [ -n "$FIXES" ]; then
echo "### Fixed"
echo "$FIXES"
echo ""
fi
if [ -n "$DOCS" ]; then
echo "### Documentation"
echo "$DOCS"
echo ""
fi
if [ -n "$OTHER" ]; then
echo "### Changed"
echo "$OTHER"
echo ""
fi
# Append previous changelog content (skip the header block β€” first 6 lines)
if [ -f CHANGELOG.md ]; then
tail -n +7 CHANGELOG.md
fi
} > CHANGELOG_NEW.md
mv CHANGELOG_NEW.md CHANGELOG.md
git add pubspec.yaml CHANGELOG.md
git diff --cached --quiet || git commit -m "chore: update changelog and version for ${{ steps.version.outputs.full_tag }}"
git push origin main
- name: Checkout tag for build
run: git checkout ${{ steps.version.outputs.full_tag }}
- name: Verify signing secrets
run: |
missing=""
[ -z "${{ secrets.ANDROID_KEYSTORE_FILE }}" ] && missing="$missing ANDROID_KEYSTORE_FILE"
[ -z "${{ secrets.ANDROID_KEYSTORE_PASSWORD }}" ] && missing="$missing ANDROID_KEYSTORE_PASSWORD"
[ -z "${{ secrets.ANDROID_KEY_PASSWORD }}" ] && missing="$missing ANDROID_KEY_PASSWORD"
[ -z "${{ secrets.ANDROID_KEY_ALIAS }}" ] && missing="$missing ANDROID_KEY_ALIAS"
if [ -n "$missing" ]; then
echo "::error::Missing signing secrets:$missing"
exit 1
fi
- name: Setup Java
uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
- name: Setup Android keystore
run: |
echo "${{ secrets.ANDROID_KEYSTORE_FILE }}" | base64 --decode > android/app/upload-keystore.jks
echo "storePassword=${{ secrets.ANDROID_KEYSTORE_PASSWORD }}" > android/key.properties
echo "keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }}" >> android/key.properties
echo "keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}" >> android/key.properties
echo "storeFile=upload-keystore.jks" >> android/key.properties
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
flutter-version: '3.32.2'
channel: stable
cache: true
- name: Configure Flutter minSdkVersion
run: |
set -euo pipefail
mkdir -p android
touch android/local.properties
if grep -qE '^flutter\.minSdkVersion=' android/local.properties; then
sed -i 's/^flutter\.minSdkVersion=.*/flutter.minSdkVersion=23/' android/local.properties
else
echo "flutter.minSdkVersion=23" >> android/local.properties
fi
- name: Install dependencies
run: flutter pub get
- name: Generate localization and other required files
run: dart run build_runner build -d
- name: Analyze
run: flutter analyze
- name: Run tests
run: flutter test
- name: Update version in pubspec for build
run: |
# Use clean semver from tag β€” never read from pubspec (avoids double +BUILD)
VERSION=${{ steps.version.outputs.tag }}
BUILD=$(git rev-list --count HEAD)
sed -i "s/^version: .*/version: ${VERSION}+${BUILD}/" pubspec.yaml
echo "Building with version: ${VERSION}+${BUILD}"
- name: Build APK (split per ABI)
run: flutter build apk --split-per-abi --dart-define=APP_VERSION=${{ steps.version.outputs.tag }} --dart-define=GIT_COMMIT=${{ github.sha }}
env:
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEYSTORE_FILE: upload-keystore.jks
- name: Build appBundle
run: flutter build appbundle --release --dart-define=APP_VERSION=${{ steps.version.outputs.tag }} --dart-define=GIT_COMMIT=${{ github.sha }}
env:
ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
ANDROID_KEYSTORE_FILE: upload-keystore.jks
- name: Rename split APKs
run: |
set -euo pipefail
VERSION=${{ steps.version.outputs.full_tag }}
mv build/app/outputs/flutter-apk/app-armeabi-v7a-release.apk \
build/app/outputs/flutter-apk/mostro-${VERSION}-armeabi-v7a.apk
mv build/app/outputs/flutter-apk/app-arm64-v8a-release.apk \
build/app/outputs/flutter-apk/mostro-${VERSION}-arm64-v8a.apk
ls -lh build/app/outputs/flutter-apk/mostro-${VERSION}-*.apk
- name: Verify APK signing
run: |
set -euo pipefail
VERSION=${{ steps.version.outputs.full_tag }}
SDK_ROOT="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-}}"
APKSIGNER=$(find "$SDK_ROOT"/build-tools -name apksigner -type f 2>/dev/null | sort -V | tail -1 || true)
for ARCH in armeabi-v7a arm64-v8a; do
APK="build/app/outputs/flutter-apk/mostro-${VERSION}-${ARCH}.apk"
echo "Verifying ${ARCH}..."
jarsigner -verify "$APK" > /dev/null
if [ -n "$APKSIGNER" ]; then
"$APKSIGNER" verify --print-certs "$APK"
fi
echo "βœ… ${ARCH} verified"
done
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
name: Releases
path: |
build/app/outputs/flutter-apk/mostro-${{ steps.version.outputs.full_tag }}-armeabi-v7a.apk
build/app/outputs/flutter-apk/mostro-${{ steps.version.outputs.full_tag }}-arm64-v8a.apk
build/app/outputs/bundle/release/app-release.aab
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
body_path: RELEASE_BODY.md
files: |
build/app/outputs/flutter-apk/mostro-${{ steps.version.outputs.full_tag }}-armeabi-v7a.apk
build/app/outputs/flutter-apk/mostro-${{ steps.version.outputs.full_tag }}-arm64-v8a.apk
build/app/outputs/bundle/release/app-release.aab
generate_release_notes: false
draft: false
prerelease: false
- name: Trigger desktop builds
uses: peter-evans/repository-dispatch@v3
with:
event-type: build-desktop
client-payload: '{"version": "${{ steps.version.outputs.tag }}"}'
- name: Cleanup keystore
if: always()
run: rm -f android/app/upload-keystore.jks android/key.properties