Build and release #20
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
| # Manual-only release build (workflow_dispatch). No push/tag triggers. | |
| # | |
| # - Git tag: creates v{versionName} from app/build.gradle.kts at the workflow commit (github.sha). | |
| # - Artifacts (signed release, same versionCode): | |
| # - filepipe-v{version}-github.apk (GitHub Releases / sideload; REQUEST_INSTALL_PACKAGES) | |
| # - filepipe-v{version}-playstore.aab (Play Store upload; same playstore flavor as former APK) | |
| # - GitHub Release: always draft; publish from the Releases UI when ready. | |
| # - Run from any branch (e.g. main); the tagged commit is the commit that was built. | |
| # | |
| # Optional Play Console upload (workflow input play_track): | |
| # - Requires repository secret PLAY_SERVICE_ACCOUNT_JSON_B64 = base64-encoded Google Play service account JSON | |
| # (Google Cloud: enable Play Android Developer API, create key; Play Console: add service account and grant access to releases and store listing as needed). | |
| # - Runs Fastlane deploy_internal / deploy_beta / deploy_production: uploads playstore AAB to the chosen track and | |
| # syncs listing from fastlane/metadata/android (screenshots and image assets upload only if PNGs/JPEGs are present there). | |
| # - Tracks: internal (internal testing), beta (open/public testing), production. Choose "none" to skip upload. | |
| name: Build and release | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| play_track: | |
| description: "Upload AAB + Play listing (needs PLAY_SERVICE_ACCOUNT_JSON_B64). internal / beta (open testing) / production, or none to skip." | |
| required: true | |
| default: none | |
| type: choice | |
| options: | |
| - none | |
| - internal | |
| - beta | |
| - production | |
| permissions: | |
| contents: write | |
| jobs: | |
| build: | |
| runs-on: ubuntu-latest | |
| # Node 20 actions are deprecated; official actions below use Node 24. Third-party actions | |
| # still on node20 (e.g. softprops/action-gh-release) run on Node 24 when this is set. | |
| # https://github.blog/changelog/2025-09-19-deprecation-of-node-20-on-github-actions-runners/ | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| steps: | |
| - uses: actions/checkout@v6 | |
| - name: Grant execute permission for gradlew | |
| run: chmod +x gradlew | |
| - uses: actions/setup-java@v5 | |
| with: | |
| distribution: temurin | |
| java-version: "21" | |
| - uses: android-actions/setup-android@v4 | |
| with: | |
| packages: platforms;android-36 build-tools;36.0.0 | |
| log-accepted-android-sdk-licenses: false | |
| - name: Cache Gradle | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| ~/.gradle/caches | |
| ~/.gradle/wrapper | |
| key: gradle-${{ runner.os }}-${{ hashFiles('**/*.gradle*', '**/libs.versions.toml', '**/gradle-wrapper.properties') }} | |
| restore-keys: gradle-${{ runner.os }}- | |
| - name: Resolve version label | |
| id: version | |
| run: | | |
| VERSION=$(grep -oE 'versionName = "[^"]+"' app/build.gradle.kts | head -1 | sed -E 's/versionName = "([^"]+)"/\1/') | |
| if [ -z "$VERSION" ]; then echo "Could not parse versionName from app/build.gradle.kts"; exit 1; fi | |
| echo "version=$VERSION" >> "$GITHUB_OUTPUT" | |
| echo "Building versionName=$VERSION (tag will be v${VERSION})" | |
| - name: Configure signing | |
| run: | | |
| echo "$KEYSTORE_B64" | base64 -d > filepipe-release.keystore | |
| KEY_PASS_EFFECTIVE="${KEY_PASS:-$KS_PASS}" | |
| { | |
| echo "storeFile=filepipe-release.keystore" | |
| echo "storePassword=$KS_PASS" | |
| echo "keyPassword=$KEY_PASS_EFFECTIVE" | |
| echo "keyAlias=$K_ALIAS" | |
| } > keystore.properties | |
| env: | |
| KEYSTORE_B64: ${{ secrets.KEYSTORE_BASE64 }} | |
| KS_PASS: ${{ secrets.KEYSTORE_PASSWORD }} | |
| KEY_PASS: ${{ secrets.KEY_PASSWORD }} | |
| K_ALIAS: ${{ secrets.KEY_ALIAS }} | |
| - name: Build GitHub APK and Play bundle | |
| run: ./gradlew :app:assembleGithubRelease :app:bundlePlaystoreRelease --no-configuration-cache | |
| - name: Collect and rename release assets | |
| run: | | |
| set -euo pipefail | |
| version="${{ steps.version.outputs.version }}" | |
| out="release-assets" | |
| mkdir -p "$out" | |
| github_apk="app/build/outputs/apk/github/release/app-github-release.apk" | |
| play_aab="app/build/outputs/bundle/playstoreRelease/app-playstore-release.aab" | |
| if [ ! -f "$github_apk" ]; then | |
| echo "Expected GitHub flavor APK not found: $github_apk" | |
| find app/build/outputs/apk -name '*.apk' -print || true | |
| exit 1 | |
| fi | |
| if [ ! -f "$play_aab" ]; then | |
| echo "Expected Play Store bundle not found: $play_aab" | |
| find app/build/outputs/bundle -name '*.aab' -print || true | |
| exit 1 | |
| fi | |
| cp "$github_apk" "$out/filepipe-v${version}-github.apk" | |
| cp "$play_aab" "$out/filepipe-v${version}-playstore.aab" | |
| ls -la "$out" | |
| - name: Write Play service account JSON | |
| if: ${{ inputs.play_track != 'none' }} | |
| env: | |
| PLAY_SERVICE_ACCOUNT_JSON_B64: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON_B64 }} | |
| run: | | |
| set -euo pipefail | |
| if [ -z "${PLAY_SERVICE_ACCOUNT_JSON_B64}" ]; then | |
| echo "Missing repository secret PLAY_SERVICE_ACCOUNT_JSON_B64 (base64-encoded service account JSON)." | |
| exit 1 | |
| fi | |
| key_path="${RUNNER_TEMP}/play-service-account.json" | |
| printf '%s' "$PLAY_SERVICE_ACCOUNT_JSON_B64" | tr -d '\n\r ' | base64 -d > "$key_path" | |
| python3 -c "import json,sys; json.load(open(sys.argv[1],encoding='utf-8'))" "$key_path" | |
| - name: Set up Ruby | |
| if: ${{ inputs.play_track != 'none' }} | |
| uses: ruby/setup-ruby@v1 | |
| with: | |
| ruby-version: "3.3" | |
| bundler-cache: false | |
| - name: Install Fastlane | |
| if: ${{ inputs.play_track != 'none' }} | |
| run: bundle install --jobs 4 --retry 3 | |
| - name: Upload to Play (Fastlane) | |
| if: ${{ inputs.play_track != 'none' }} | |
| env: | |
| PLAY_SERVICE_ACCOUNT_JSON: ${{ runner.temp }}/play-service-account.json | |
| run: | | |
| set -euo pipefail | |
| case "${{ inputs.play_track }}" in | |
| internal) bundle exec fastlane deploy_internal ;; | |
| beta) bundle exec fastlane deploy_beta ;; | |
| production) bundle exec fastlane deploy_production ;; | |
| *) echo "Unexpected play_track"; exit 1 ;; | |
| esac | |
| - name: Upload release artifacts | |
| uses: actions/upload-artifact@v6 | |
| with: | |
| name: filepipe-v${{ steps.version.outputs.version }}-release | |
| path: release-assets/* | |
| # Pass token via `with.token`, not env GITHUB_TOKEN — env-only breaks | |
| # `on: release: types: [published]` for downstream workflows (softprops/action-gh-release #770). | |
| - name: Create draft GitHub Release (tag v${{ steps.version.outputs.version }}) | |
| uses: softprops/action-gh-release@v2.6.1 | |
| with: | |
| token: ${{ github.token }} | |
| draft: true | |
| tag_name: v${{ steps.version.outputs.version }} | |
| name: v${{ steps.version.outputs.version }} | |
| target_commitish: ${{ github.sha }} | |
| body: | | |
| **Release assets (same versionCode; distribution flavors)** | |
| | File | Use | | |
| |------|-----| | |
| | `filepipe-v${{ steps.version.outputs.version }}-github.apk` | Sideload / GitHub Releases (`REQUEST_INSTALL_PACKAGES` for APK updates) | | |
| | `filepipe-v${{ steps.version.outputs.version }}-playstore.aab` | Upload to Play Console (Android App Bundle; not for direct sideload) | | |
| files: | | |
| release-assets/filepipe-v${{ steps.version.outputs.version }}-github.apk | |
| release-assets/filepipe-v${{ steps.version.outputs.version }}-playstore.aab | |
| generate_release_notes: true |