name: Make Release on: workflow_dispatch: push: tags: - 'v*' concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true permissions: contents: write pull-requests: read id-token: write attestations: write jobs: prepare-build-info: runs-on: ubuntu-latest outputs: APP_VERSION_NAME: ${{ steps.get_version_name.outputs.APP_VERSION_NAME }} APP_VERSION_CODE: ${{ steps.calculate_version_code.outputs.versionCode }} BASE_TAG: ${{ steps.get_base_tag.outputs.BASE_TAG }} steps: - name: Checkout code uses: actions/checkout@v5 with: fetch-depth: 0 submodules: 'recursive' - name: Set up JDK 21 uses: actions/setup-java@v5 with: java-version: '21' distribution: 'jetbrains' - name: Setup Gradle uses: gradle/actions/setup-gradle@v5 with: cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} build-scan-publish: true build-scan-terms-of-use-url: 'https://gradle.com/terms-of-service' build-scan-terms-of-use-agree: 'yes' - name: Determine Version Name from Tag id: get_version_name run: echo "APP_VERSION_NAME=$(echo ${GITHUB_REF_NAME#v} | sed 's/-.*//')" >> $GITHUB_OUTPUT - name: Get Base Tag (for release/artifact naming) id: get_base_tag run: | VERSION_NAME=$(echo ${GITHUB_REF_NAME#v} | sed 's/-.*//') echo "BASE_TAG=v${VERSION_NAME}" >> $GITHUB_OUTPUT - name: Extract VERSION_CODE_OFFSET from config.properties id: get_version_code_offset run: | OFFSET=$(grep '^VERSION_CODE_OFFSET=' config.properties | cut-d'=' -f2) echo "VERSION_CODE_OFFSET=$OFFSET" >> $GITHUB_OUTPUT - name: Calculate Version Code from Git Commit Count id: calculate_version_code run: | COMMIT_COUNT=$(git rev-list --count HEAD) OFFSET=${{ steps.get_version_code_offset.outputs.VERSION_CODE_OFFSET }} VERSION_CODE=$((COMMIT_COUNT + OFFSET)) echo "versionCode=$VERSION_CODE" >> $GITHUB_OUTPUT shell: bash prepare-release-environment: runs-on: ubuntu-latest needs: prepare-build-info outputs: exists: ${{ steps.check_and_clean.outputs.exists }} steps: - name: Checkout code uses: actions/checkout@v5 - name: Check for Existing Release and Clean if Superseded id: check_and_clean env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | BASE_TAG=${{ needs.prepare-build-info.outputs.BASE_TAG }} COMMIT_SHA=$(git rev-parse HEAD) RELEASE_INFO=$(gh release view $BASE_TAG --json targetCommitish -q ".targetCommitish.oid" || echo "") if [ -z "$RELEASE_INFO" ]; then echo "No existing release for tag '${BASE_TAG}'. Starting fresh." echo "exists=false" >> $GITHUB_OUTPUT elif [ "$RELEASE_INFO" == "$COMMIT_SHA" ]; then echo "Existing release for '${BASE_TAG}' found on the current commit. This is a promotion." echo "exists=true" >> $GITHUB_OUTPUT else echo "Existing release for '${BASE_TAG}' found on a DIFFERENT commit ($RELEASE_INFO)." echo "This new tag supersedes the old one. Deleting old release and tag to restart the process." gh release delete $BASE_TAG --cleanup-tag --yes || echo "Could not delete release. It might have been deleted already." echo "Old release and tag deleted. A new build will be created." echo "exists=false" >> $GITHUB_OUTPUT fi release-google: runs-on: ubuntu-latest needs: [prepare-build-info, prepare-release-environment] steps: - name: Checkout code uses: actions/checkout@v5 with: fetch-depth: 0 submodules: 'recursive' - name: Set up JDK 21 uses: actions/setup-java@v5 with: java-version: '21' distribution: 'jetbrains' - name: Setup Gradle uses: gradle/actions/setup-gradle@v5 with: cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} build-scan-publish: true build-scan-terms-of-use-url: 'https://gradle.com/terms-of-service' build-scan-terms-of-use-agree: 'yes' - name: Load secrets env: GSERVICES: ${{ secrets.GSERVICES }} KEYSTORE: ${{ secrets.KEYSTORE }} KEYSTORE_FILENAME: ${{ secrets.KEYSTORE_FILENAME }} KEYSTORE_PROPERTIES: ${{ secrets.KEYSTORE_PROPERTIES }} DATADOG_APPLICATION_ID: ${{ secrets.DATADOG_APPLICATION_ID }} DATADOG_CLIENT_TOKEN: ${{ secrets.DATADOG_CLIENT_TOKEN }} GOOGLE_MAPS_API_KEY: ${{ secrets.GOOGLE_MAPS_API_KEY }} GOOGLE_PLAY_JSON_KEY: ${{ secrets.GOOGLE_PLAY_JSON_KEY }} run: | rm -f ./app/google-services.json # Ensure clean state echo $GSERVICES > ./app/google-services.json echo $KEYSTORE | base64 -di > ./app/$KEYSTORE_FILENAME echo "$KEYSTORE_PROPERTIES" > ./keystore.properties echo "datadogApplicationId=$DATADOG_APPLICATION_ID" >> ./secrets.properties echo "datadogClientToken=$DATADOG_CLIENT_TOKEN" >> ./secrets.properties echo "MAPS_API_KEY=$GOOGLE_MAPS_API_KEY" >> ./secrets.properties echo "$GOOGLE_PLAY_JSON_KEY" > ./fastlane/play-store-credentials.json - name: Setup Fastlane uses: ruby/setup-ruby@v1 with: ruby-version: '3.2' bundler-cache: true - name: Build and Deploy to Internal Track if: needs.prepare-release-environment.outputs.exists == 'false' env: VERSION_NAME: ${{ needs.prepare-build-info.outputs.APP_VERSION_NAME }} VERSION_CODE: ${{ needs.prepare-build-info.outputs.APP_VERSION_CODE }} run: bundle exec fastlane internal - name: Determine Fastlane Promotion Lane id: fastlane_lane if: "!contains(github.ref_name, '-internal')" run: | TAG_NAME="${{ github.ref_name }}" if [[ "$TAG_NAME" == *"-closed"* ]]; then echo "lane=closed" >> $GITHUB_OUTPUT elif [[ "$TAG_NAME" == *"-open"* ]]; then echo "lane=open" >> $GITHUB_OUTPUT else echo "lane=production" >> $GITHUB_OUTPUT fi - name: Promote on Google Play if: "steps.fastlane_lane.outputs.lane != ''" env: VERSION_NAME: ${{ needs.prepare-build-info.outputs.APP_VERSION_NAME }} VERSION_CODE: ${{ needs.prepare-build-info.outputs.APP_VERSION_CODE }} run: bundle exec fastlane ${{ steps.fastlane_lane.outputs.lane }} - name: Upload Google AAB artifact if: needs.prepare-release-environment.outputs.exists == 'false' uses: actions/upload-artifact@v4 with: name: google-aab path: app/build/outputs/bundle/googleRelease/app-google-release.aab retention-days: 1 - name: Upload Google APK artifact if: needs.prepare-release-environment.outputs.exists == 'false' uses: actions/upload-artifact@v4 with: name: google-apk path: app/build/outputs/apk/google/release/app-google-release.apk retention-days: 1 - name: Attest Google artifacts provenance if: needs.prepare-release-environment.outputs.exists == 'false' uses: actions/attest-build-provenance@v3 with: subject-path: | app/build/outputs/bundle/googleRelease/app-google-release.aab app/build/outputs/apk/google/release/app-google-release.apk release-fdroid: if: needs.prepare-release-environment.outputs.exists == 'false' runs-on: ubuntu-latest needs: [prepare-build-info, prepare-release-environment] steps: - name: Checkout code uses: actions/checkout@v5 with: fetch-depth: 0 submodules: 'recursive' - name: Set up JDK 21 uses: actions/setup-java@v5 with: java-version: '21' distribution: 'jetbrains' - name: Setup Gradle uses: gradle/actions/setup-gradle@v5 with: cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }} build-scan-publish: true build-scan-terms-of-use-url: 'https://gradle.com/terms-of-service' build-scan-terms-of-use-agree: 'yes' - name: Load secrets env: KEYSTORE: ${{ secrets.KEYSTORE }} KEYSTORE_FILENAME: ${{ secrets.KEYSTORE_FILENAME }} KEYSTORE_PROPERTIES: ${{ secrets.KEYSTORE_PROPERTIES }} run: | echo $KEYSTORE | base64 -di > ./app/$KEYSTORE_FILENAME echo "$KEYSTORE_PROPERTIES" > ./keystore.properties - name: Setup Fastlane uses: ruby/setup-ruby@v1 with: ruby-version: '3.2' bundler-cache: true - name: Build F-Droid with Fastlane env: VERSION_NAME: ${{ needs.prepare-build-info.outputs.APP_VERSION_NAME }} VERSION_CODE: ${{ needs.prepare-build-info.outputs.APP_VERSION_CODE }} run: bundle exec fastlane fdroid_build - name: Upload F-Droid APK artifact uses: actions/upload-artifact@v4 with: name: fdroid-apk path: app/build/outputs/apk/fdroid/release/app-fdroid-release.apk retention-days: 1 - name: Attest F-Droid APK provenance uses: actions/attest-build-provenance@v3 with: subject-path: app/build/outputs/apk/fdroid/release/app-fdroid-release.apk manage-github-release: runs-on: ubuntu-latest needs: [prepare-build-info, prepare-release-environment, release-google, release-fdroid] steps: - name: Download all artifacts if: needs.prepare-release-environment.outputs.exists == 'false' uses: actions/download-artifact@v5 with: path: ./artifacts - name: Create GitHub Release if: needs.prepare-release-environment.outputs.exists == 'false' uses: softprops/action-gh-release@v2 with: tag_name: ${{ needs.prepare-build-info.outputs.BASE_TAG }} name: v${{ needs.prepare-build-info.outputs.APP_VERSION_NAME }}-internal generate_release_notes: true files: ./artifacts/*/* draft: true prerelease: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Determine Release Properties for Promotion if: "!contains(github.ref_name, '-internal')" id: release_properties run: | TAG_NAME="${{ github.ref_name }}" if [[ "$TAG_NAME" == *"-closed"* ]]; then echo "draft=false" >> $GITHUB_OUTPUT echo "prerelease=true" >> $GITHUB_OUTPUT elif [[ "$TAG_NAME" == *"-open"* ]]; then echo "draft=false" >> $GITHUB_OUTPUT echo "prerelease=true" >> $GITHUB_OUTPUT else echo "draft=false" >> $GITHUB_OUTPUT echo "prerelease=false" >> $GITHUB_OUTPUT fi - name: Promote GitHub Release if: "!contains(github.ref_name, '-internal')" uses: softprops/action-gh-release@v2 with: tag_name: ${{ needs.prepare-build-info.outputs.BASE_TAG }} name: ${{ github.ref_name }} draft: ${{ steps.release_properties.outputs.draft }} prerelease: ${{ steps.release_properties.outputs.prerelease }} env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}