chore: update test dependencies and normalize app version parsing #20
Workflow file for this run
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: GitHub Release (gms + foss) | |
| on: | |
| push: | |
| tags: | |
| - "v*" | |
| workflow_dispatch: | |
| inputs: | |
| tag: | |
| description: "Tag to build/release" | |
| required: true | |
| type: string | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| jobs: | |
| # ── Resolve tag and version metadata (fast, shared by all jobs) ── | |
| meta: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| tag: ${{ steps.rel.outputs.tag }} | |
| sha: ${{ steps.rel.outputs.sha }} | |
| ref: ${{ steps.ref.outputs.ref }} | |
| app_version: ${{ steps.ver.outputs.app_version }} | |
| build_number: ${{ steps.ver.outputs.build_number }} | |
| full_version: ${{ steps.ver.outputs.full_version }} | |
| prerelease: ${{ steps.pre.outputs.prerelease }} | |
| steps: | |
| - name: Resolve ref (tag) | |
| id: ref | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| echo "ref=refs/tags/${{ inputs.tag }}" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "ref=${{ github.ref }}" >> "$GITHUB_OUTPUT" | |
| fi | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ steps.ref.outputs.ref }} | |
| - name: Resolve release metadata | |
| id: rel | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| TAG="" | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| TAG="${{ inputs.tag }}" | |
| else | |
| TAG="${GITHUB_REF_NAME}" | |
| fi | |
| echo "tag=$TAG" >> "$GITHUB_OUTPUT" | |
| echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" | |
| - name: Read version from tag | |
| id: ver | |
| shell: bash | |
| run: | | |
| TAG="${{ steps.rel.outputs.tag }}" | |
| SEMVER="${TAG#v}" | |
| APP_VERSION="${SEMVER%%-*}" | |
| BUILD_NUMBER="$(git rev-list --count HEAD)" | |
| echo "app_version=$APP_VERSION" >> "$GITHUB_OUTPUT" | |
| echo "build_number=$BUILD_NUMBER" >> "$GITHUB_OUTPUT" | |
| echo "full_version=$SEMVER" >> "$GITHUB_OUTPUT" | |
| - name: Determine prerelease flag | |
| id: pre | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| TAG="${{ steps.rel.outputs.tag }}" | |
| if [[ "$TAG" == *-* ]]; then | |
| echo "prerelease=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "prerelease=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| # ── GMS APK build ── | |
| build-gms: | |
| needs: meta | |
| runs-on: ubuntu-latest | |
| env: | |
| JAVA_VERSION: "21" | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ needs.meta.outputs.ref }} | |
| - name: Setup Flutter (from .flutter-version) | |
| uses: subosito/flutter-action@v2 | |
| with: | |
| flutter-version-file: .flutter-version | |
| channel: stable | |
| cache: true | |
| - name: Setup Java | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: ${{ env.JAVA_VERSION }} | |
| - name: Setup Gradle | |
| uses: gradle/actions/setup-gradle@v4 | |
| - name: Write android/local.properties | |
| shell: bash | |
| run: | | |
| set -e | |
| SDK_DIR="${ANDROID_SDK_ROOT:-$ANDROID_HOME}" | |
| FLUTTER_SDK="${FLUTTER_HOME:-$(dirname "$(dirname "$(readlink -f "$(which flutter)")")")}" | |
| { | |
| echo "sdk.dir=${SDK_DIR}" | |
| echo "flutter.sdk=${FLUTTER_SDK}" | |
| } > android/local.properties | |
| - name: Make gradlew executable | |
| run: chmod +x android/gradlew | |
| - name: Decode keystore | |
| env: | |
| ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} | |
| run: echo "$ANDROID_KEYSTORE_BASE64" | base64 -d > android/dawarich-upload.jks | |
| - name: Create keystore.properties | |
| run: | | |
| cat > android/keystore.properties <<'EOF' | |
| storePassword=${{ secrets.ANDROID_KEYSTORE_PASSWORD }} | |
| keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }} | |
| keyAlias=${{ secrets.ANDROID_KEY_ALIAS }} | |
| storeFile=../dawarich-upload.jks | |
| EOF | |
| - name: Flutter pub get | |
| run: flutter pub get | |
| - name: Run build_runner | |
| run: dart run build_runner build --delete-conflicting-outputs | |
| - name: Build APK (GMS) | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| flutter build apk --release --flavor gms \ | |
| --build-name=${{ needs.meta.outputs.app_version }} \ | |
| --build-number=${{ needs.meta.outputs.build_number }} | |
| - name: Upload GMS artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: gms-apk | |
| path: build/app/outputs/flutter-apk/app-gms-release.apk | |
| retention-days: 5 | |
| # ── FOSS APK build (parallel to GMS) ── | |
| build-foss: | |
| needs: meta | |
| runs-on: ubuntu-latest | |
| env: | |
| JAVA_VERSION: "21" | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ needs.meta.outputs.ref }} | |
| - name: Setup Flutter (from .flutter-version) | |
| uses: subosito/flutter-action@v2 | |
| with: | |
| flutter-version-file: .flutter-version | |
| channel: stable | |
| cache: true | |
| - name: Setup Java | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: temurin | |
| java-version: ${{ env.JAVA_VERSION }} | |
| - name: Setup Gradle | |
| uses: gradle/actions/setup-gradle@v4 | |
| - name: Write android/local.properties | |
| shell: bash | |
| run: | | |
| set -e | |
| SDK_DIR="${ANDROID_SDK_ROOT:-$ANDROID_HOME}" | |
| FLUTTER_SDK="${FLUTTER_HOME:-$(dirname "$(dirname "$(readlink -f "$(which flutter)")")")}" | |
| { | |
| echo "sdk.dir=${SDK_DIR}" | |
| echo "flutter.sdk=${FLUTTER_SDK}" | |
| } > android/local.properties | |
| - name: Make gradlew executable | |
| run: chmod +x android/gradlew | |
| - name: Decode keystore | |
| env: | |
| ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} | |
| run: echo "$ANDROID_KEYSTORE_BASE64" | base64 -d > android/dawarich-upload.jks | |
| - name: Create keystore.properties | |
| run: | | |
| cat > android/keystore.properties <<'EOF' | |
| storePassword=${{ secrets.ANDROID_KEYSTORE_PASSWORD }} | |
| keyPassword=${{ secrets.ANDROID_KEY_PASSWORD }} | |
| keyAlias=${{ secrets.ANDROID_KEY_ALIAS }} | |
| storeFile=../dawarich-upload.jks | |
| EOF | |
| - name: Switch pubspec to FOSS | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| cp pubspec-foss.yaml pubspec.yaml | |
| cp pubspec-foss.lock pubspec.lock | |
| - name: Flutter pub get | |
| run: flutter pub get | |
| - name: Run build_runner | |
| run: dart run build_runner build --delete-conflicting-outputs | |
| - name: FOSS proprietary dependency check | |
| shell: bash | |
| run: | | |
| chmod +x tools/check_proprietary_deps.sh | |
| tools/check_proprietary_deps.sh fossReleaseRuntimeClasspath | |
| - name: Build APK (FOSS) | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| flutter build apk --release --flavor foss \ | |
| --build-name=${{ needs.meta.outputs.app_version }} \ | |
| --build-number=${{ needs.meta.outputs.build_number }} | |
| - name: Upload FOSS artifact | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: foss-apk | |
| path: build/app/outputs/flutter-apk/app-foss-release.apk | |
| retention-days: 5 | |
| # ── Create GitHub Release (waits for both builds) ── | |
| release: | |
| needs: [meta, build-gms, build-foss] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Download GMS artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: gms-apk | |
| path: dist | |
| - name: Download FOSS artifact | |
| uses: actions/download-artifact@v4 | |
| with: | |
| name: foss-apk | |
| path: dist | |
| - name: Rename artifacts | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| VER="${{ needs.meta.outputs.app_version }}" | |
| BN="${{ needs.meta.outputs.build_number }}" | |
| mv dist/app-gms-release.apk "dist/dawarich-${VER}+${BN}-gms.apk" | |
| mv dist/app-foss-release.apk "dist/dawarich-${VER}+${BN}-foss.apk" | |
| ls -la dist | |
| - name: Create GitHub Release and upload assets | |
| uses: softprops/action-gh-release@v2 | |
| with: | |
| tag_name: ${{ needs.meta.outputs.tag }} | |
| target_commitish: ${{ needs.meta.outputs.sha }} | |
| files: dist/* | |
| generate_release_notes: false | |
| fail_on_unmatched_files: true | |
| prerelease: ${{ needs.meta.outputs.prerelease }} | |
| # ── F-Droid changelog (non-blocking, runs after meta, independent of release) ── | |
| changelog: | |
| needs: meta | |
| runs-on: ubuntu-latest | |
| # This job must never block the release | |
| continue-on-error: true | |
| steps: | |
| - uses: actions/checkout@v4 | |
| with: | |
| fetch-depth: 0 | |
| ref: ${{ needs.meta.outputs.ref }} | |
| - name: Generate and push F-Droid changelog via PR | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| run: | | |
| set -euo pipefail | |
| BUILD_NUMBER="${{ needs.meta.outputs.build_number }}" | |
| TAG="${{ needs.meta.outputs.tag }}" | |
| CHANGELOG_DIR="fastlane/metadata/android/en-US/changelogs" | |
| CHANGELOG="${CHANGELOG_DIR}/${BUILD_NUMBER}.txt" | |
| WHATSNEW="distribution/whatsnew/whatsnew-en-US" | |
| BRANCH="chore/fdroid-changelog-${BUILD_NUMBER}" | |
| # Generate the changelog file | |
| mkdir -p "$CHANGELOG_DIR" | |
| if [ -f "$WHATSNEW" ]; then | |
| cp "$WHATSNEW" "$CHANGELOG" | |
| else | |
| echo "See https://github.com/sunstep/dawarich-android/releases/tag/${TAG}" \ | |
| > "$CHANGELOG" | |
| fi | |
| # Skip if the file already exists on main | |
| git fetch origin main | |
| if git show "origin/main:${CHANGELOG}" &>/dev/null; then | |
| echo "Changelog ${BUILD_NUMBER}.txt already exists on main, skipping." | |
| exit 0 | |
| fi | |
| # Save to temp before switching branches | |
| cp "$CHANGELOG" /tmp/changelog.txt | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| # Create a new branch from main, add the file, force-push, open PR | |
| # Force-push handles retries where the branch already exists from a previous failed run | |
| git checkout -b "$BRANCH" origin/main | |
| mkdir -p "$(dirname "$CHANGELOG")" | |
| cp /tmp/changelog.txt "$CHANGELOG" | |
| git add "$CHANGELOG" | |
| git commit -m "chore: add F-Droid changelog for build ${BUILD_NUMBER} [skip ci]" | |
| git push --force origin "$BRANCH" | |
| # Only create a PR if one doesn't already exist for this branch | |
| EXISTING_PR=$(gh pr list --head "$BRANCH" --base main --json number --jq '.[0].number // empty') | |
| if [ -n "$EXISTING_PR" ]; then | |
| echo "PR #${EXISTING_PR} already exists for branch ${BRANCH}, skipping creation." | |
| PR_NUMBER="$EXISTING_PR" | |
| else | |
| PR_URL=$(gh pr create \ | |
| --base main \ | |
| --head "$BRANCH" \ | |
| --title "chore: F-Droid changelog for ${TAG}" \ | |
| --body "Auto-generated F-Droid changelog for build ${BUILD_NUMBER} (${TAG})." \ | |
| --label "automated") | |
| PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$') | |
| echo "Created PR #${PR_NUMBER}" | |
| fi | |
| # Merge the PR: try auto-merge first, fall back to immediate merge if PR is already clean | |
| if ! gh pr merge "$PR_NUMBER" --auto --squash 2>/dev/null; then | |
| echo "Auto-merge unavailable (PR may already be clean). Merging immediately." | |
| gh pr merge "$PR_NUMBER" --squash | |
| fi | |