Merge pull request #119 from brandonp2412/claude/github-actions-aa00ui #66
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Build and Release | |
| on: | |
| push: | |
| branches: [main] | |
| paths-ignore: | |
| - "pubspec.yaml" | |
| workflow_dispatch: | |
| env: | |
| PUB_SUMMARY_ONLY: true | |
| KOTLIN_VERSION: "2.1.0" | |
| jobs: | |
| quality-checks: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| submodules: recursive | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| fetch-depth: 0 | |
| - name: Setup Flutter from submodule | |
| run: | | |
| git submodule update --init --recursive flutter | |
| chmod +x flutter/bin/* | |
| echo "$PWD/flutter/bin" >> $GITHUB_PATH | |
| flutter/bin/flutter --version | |
| flutter/bin/flutter config --no-analytics | |
| - name: Get dependencies | |
| run: flutter pub get | |
| - name: Verify formatting | |
| run: dart format --set-exit-if-changed lib test | |
| - name: Analyze code | |
| run: dart analyze --no-fatal-warnings lib | |
| - name: Run tests | |
| run: flutter test | |
| version-bump: | |
| needs: quality-checks | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| outputs: | |
| version: ${{ steps.bump.outputs.version }} | |
| build_number: ${{ steps.bump.outputs.build_number }} | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| submodules: false | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Configure Git | |
| run: | | |
| git config --local user.email "action@github.com" | |
| git config --local user.name "GitHub Action" | |
| - name: Bump versions and generate changelog | |
| id: bump | |
| run: | | |
| CURRENT_VERSION=$(grep '^version:' pubspec.yaml | sed 's/version: //' | sed 's/+.*//') | |
| CURRENT_BUILD=$(grep '^version:' pubspec.yaml | sed 's/.*+//') | |
| CURRENT_MSIX=$(grep 'msix_version:' pubspec.yaml | sed 's/.*msix_version: *//') | |
| IFS='.' read -ra V <<< "$CURRENT_VERSION" | |
| NEW_VERSION="${V[0]}.${V[1]}.$((${V[2]} + 1))" | |
| NEW_BUILD=$((CURRENT_BUILD + 1)) | |
| CHANGELOG_NUMBER=$((NEW_BUILD * 10 + 3)) | |
| IFS='.' read -ra M <<< "$CURRENT_MSIX" | |
| NEW_MSIX="${M[0]}.${M[1]}.$((${M[2]} + 1)).${M[3]}" | |
| echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT | |
| echo "build_number=$NEW_BUILD" >> $GITHUB_OUTPUT | |
| sed -i "s/^version: .*/version: $NEW_VERSION+$NEW_BUILD/" pubspec.yaml | |
| sed -i "s/msix_version: .*/msix_version: $NEW_MSIX/" pubspec.yaml | |
| CHANGELOG_FILE="fastlane/metadata/android/en-US/changelogs/$CHANGELOG_NUMBER.txt" | |
| mkdir -p "$(dirname "$CHANGELOG_FILE")" | |
| LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") | |
| if [ -n "$LAST_TAG" ]; then | |
| git --no-pager log --pretty=format:'%s' "$LAST_TAG"..HEAD | |
| else | |
| git --no-pager log --pretty=format:'%s' | |
| fi | sort -u \ | |
| | grep -v "^Merge " \ | |
| | grep -v "^Release " \ | |
| | grep -v "^Bump " \ | |
| | grep -v "\[skip ci\]" \ | |
| | head -10 \ | |
| | sed 's/^/• /' > "$CHANGELOG_FILE" | |
| [ -s "$CHANGELOG_FILE" ] || echo "• Bug fixes and improvements" > "$CHANGELOG_FILE" | |
| mkdir -p fastlane/metadata/en-AU | |
| cp "$CHANGELOG_FILE" fastlane/metadata/en-AU/release_notes.txt | |
| mkdir -p assets/changelogs | |
| cp "$CHANGELOG_FILE" "assets/changelogs/$(date +%s).txt" | |
| - name: Commit and push version bump | |
| run: | | |
| git add pubspec.yaml fastlane/metadata assets/changelogs | |
| git commit -m "Release ${{ steps.bump.outputs.version }} [skip ci]" | |
| git push origin main | |
| - name: Create and push tag | |
| run: | | |
| git tag "${{ steps.bump.outputs.version }}" | |
| git push origin "${{ steps.bump.outputs.version }}" | |
| build-android: | |
| needs: version-bump | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Setup Java | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: "zulu" | |
| java-version: "17" | |
| - name: Create brandon user directory structure (match F-Droid) | |
| run: | | |
| sudo mkdir -p /home/brandon | |
| sudo chown $USER -R /home/brandon | |
| - name: Move project to F-Droid location | |
| run: | | |
| cd .. | |
| mv ${{ github.event.repository.name }} /home/brandon/fitbook | |
| - name: Setup Flutter from submodule and get dependencies | |
| working-directory: /home/brandon/fitbook | |
| run: | | |
| git submodule update --init --recursive flutter | |
| echo "/home/brandon/fitbook/flutter/bin" >> $GITHUB_PATH | |
| export PUB_CACHE=$(pwd)/.pub-cache | |
| echo "PUB_CACHE=$(pwd)/.pub-cache" >> $GITHUB_ENV | |
| flutter/bin/flutter config --no-analytics | |
| flutter/bin/flutter pub get | |
| - name: Strip non-deterministic build IDs from jni native library | |
| working-directory: /home/brandon/fitbook | |
| run: | | |
| sed -i -e 's/-Wl,/-Wl,--build-id=none,/' \ | |
| .pub-cache/hosted/pub.dev/jni-*/src/CMakeLists.txt | |
| - name: Decode Android keystore | |
| working-directory: /home/brandon/fitbook | |
| run: echo "${{ secrets.ANDROID_KEYSTORE_BASE64 }}" | base64 --decode > android/app/keystore.jks | |
| - name: Build android | |
| working-directory: /home/brandon/fitbook | |
| run: | | |
| echo "storePassword=${{ secrets.ANDROID_STORE_PASSWORD }}" > android/key.properties | |
| echo "keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }}" >> android/key.properties | |
| echo "keyAlias=${{ secrets.ANDROID_KEY_ALIAS }}" >> android/key.properties | |
| echo "storeFile=keystore.jks" >> android/key.properties | |
| # Use --split-per-abi WITH --target-platform to match F-Droid exactly. | |
| # Without --split-per-abi: produces a universal APK with all ABI native | |
| # libs and versionCode override = base*10+3 (wrong for x86_64 which needs +1). | |
| # Without --target-platform: NativeAssetsManifest.json lists all 3 archs. | |
| flutter build apk --release --split-per-abi --target-platform android-x64 | |
| flutter build apk --release --split-per-abi --target-platform android-arm64 | |
| flutter build apk --release --split-per-abi --target-platform android-arm | |
| flutter build apk --release | |
| mv build/app/outputs/flutter-apk/app-release.apk build/app/outputs/flutter-apk/fitbook.apk | |
| flutter build appbundle | |
| mkdir -p "${GITHUB_WORKSPACE}/build" | |
| mv build/* "${GITHUB_WORKSPACE}/build" | |
| - name: Upload Android artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: android-builds | |
| path: | | |
| build/app/outputs/flutter-apk/app-*-release.apk | |
| build/app/outputs/flutter-apk/fitbook.apk | |
| build/app/outputs/bundle/release/app-release.aab | |
| build-linux: | |
| needs: version-bump | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Setup Flutter from submodule | |
| run: | | |
| git submodule update --init --recursive flutter | |
| chmod +x flutter/bin/* | |
| echo "${{ github.workspace }}/flutter/bin" >> $GITHUB_PATH | |
| flutter/bin/flutter config --no-analytics | |
| - name: Get Flutter dependencies | |
| run: flutter pub get | |
| - name: Install Linux dependencies | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y clang cmake ninja-build pkg-config libgtk-3-dev liblzma-dev libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev | |
| - name: Build Linux | |
| run: flutter build linux | |
| - name: Create Linux zip | |
| run: | | |
| cd build/linux/x64/release/bundle | |
| zip -r fitbook-linux.zip . | |
| - name: Upload Linux artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: linux-builds | |
| path: build/linux/x64/release/bundle/fitbook-linux.zip | |
| build-windows: | |
| needs: version-bump | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Setup Flutter from submodule | |
| run: | | |
| git submodule update --init --recursive flutter | |
| chmod +x flutter/bin/* | |
| echo "${{ github.workspace }}\flutter\bin" >> $env:GITHUB_PATH | |
| flutter\bin\flutter config --no-analytics | |
| - name: Get Flutter dependencies | |
| run: flutter pub get | |
| - name: Build Windows | |
| run: flutter build windows | |
| - name: Create Windows zip | |
| run: Compress-Archive -Path ./build/windows/x64/runner/Release/* -DestinationPath ./fitbook-windows.zip | |
| - name: Upload Windows artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: windows-builds | |
| path: fitbook-windows.zip | |
| build-and-deploy-web: | |
| needs: version-bump | |
| runs-on: ubuntu-latest | |
| environment: | |
| name: github-pages | |
| url: ${{ steps.deployment.outputs.page_url }} | |
| permissions: | |
| contents: write | |
| pages: write | |
| id-token: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Setup Flutter from submodule | |
| run: | | |
| git submodule update --init --recursive flutter | |
| chmod +x flutter/bin/* | |
| echo "${{ github.workspace }}/flutter/bin" >> $GITHUB_PATH | |
| flutter/bin/flutter config --no-analytics | |
| - name: Get Flutter dependencies | |
| run: flutter pub get | |
| - name: Enable web support | |
| run: flutter config --enable-web | |
| - name: Build Flutter web | |
| run: flutter build web --release --base-href /${{ github.event.repository.name }}/ | |
| - name: Setup Pages | |
| uses: actions/configure-pages@v4 | |
| - name: Upload to GitHub Pages | |
| uses: actions/upload-pages-artifact@v3 | |
| with: | |
| path: ./build/web | |
| - name: Deploy to GitHub Pages | |
| id: deployment | |
| uses: actions/deploy-pages@v4 | |
| create-github-release: | |
| needs: [version-bump, build-android, build-linux, build-windows] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Download all artifacts | |
| uses: actions/download-artifact@v4 | |
| - name: Create GitHub Release | |
| uses: softprops/action-gh-release@v1 | |
| with: | |
| tag_name: ${{ needs.version-bump.outputs.version }} | |
| name: ${{ needs.version-bump.outputs.version }} | |
| body_path: fastlane/metadata/en-AU/release_notes.txt | |
| files: | | |
| android-builds/flutter-apk/app-*-release.apk | |
| android-builds/flutter-apk/fitbook.apk | |
| android-builds/bundle/release/app-release.aab | |
| linux-builds/fitbook-linux.zip | |
| windows-builds/fitbook-windows.zip | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| deploy-windows-store: | |
| needs: [version-bump, build-windows] | |
| runs-on: windows-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: main | |
| token: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Setup Flutter from submodule | |
| run: | | |
| git submodule update --init --recursive flutter | |
| echo "${{ github.workspace }}\flutter\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append | |
| flutter\bin\flutter config --no-analytics | |
| - name: Get Flutter dependencies | |
| run: flutter pub get | |
| - name: Build MSIX | |
| run: flutter pub run msix:create | |
| - name: Setup .NET 9.0 | |
| uses: actions/setup-dotnet@v4 | |
| with: | |
| dotnet-version: "9.0.x" | |
| - name: Setup Microsoft Store CLI | |
| uses: microsoft/setup-msstore-cli@v1 | |
| - name: Configure Microsoft Store CLI | |
| run: msstore reconfigure --tenantId ${{ secrets.AZURE_TENANT_ID }} --clientId ${{ secrets.AZURE_CLIENT_ID }} --clientSecret ${{ secrets.AZURE_CLIENT_SECRET }} --sellerId ${{ secrets.MSSTORE_SELLER_ID }} | |
| - name: Publish to Microsoft Store | |
| run: msstore publish --inputDirectory build/windows/x64/runner/Release --appId ${{ secrets.PRODUCT_ID }} --packageRolloutPercentage 100 || true | |
| virustotal: | |
| needs: [create-github-release, version-bump] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Download Android artifacts | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: android-builds | |
| path: android-builds | |
| - name: VirusTotal Scan | |
| id: vt | |
| uses: crazy-max/ghaction-virustotal@v4 | |
| with: | |
| vt_api_key: ${{ secrets.VT_API_KEY }} | |
| github_token: ${{ secrets.GITHUB_TOKEN }} | |
| files: | | |
| android-builds/flutter-apk/app-arm64-v8a-release.apk | |
| android-builds/flutter-apk/app-armeabi-v7a-release.apk | |
| android-builds/flutter-apk/app-x86_64-release.apk | |
| - name: Append VirusTotal links to release | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| VERSION: ${{ needs.version-bump.outputs.version }} | |
| ANALYSIS: ${{ steps.vt.outputs.analysis }} | |
| run: | | |
| VT_SECTION=$'\n\n<details>\n<summary>VirusTotal Analysis</summary>\n\n' | |
| IFS=',' read -ra ENTRIES <<< "$ANALYSIS" | |
| for entry in "${ENTRIES[@]}"; do | |
| filename=$(basename "${entry%%=*}") | |
| url="${entry#*=}" | |
| VT_SECTION+="- [${filename}](${url})"$'\n' | |
| done | |
| VT_SECTION+=$'\n</details>' | |
| CURRENT_BODY=$(gh release view "$VERSION" --json body -q .body) | |
| gh release edit "$VERSION" --notes "${CURRENT_BODY}${VT_SECTION}" |