TEMP: map test branch to Production to dry-run the retain path #3
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
| # Publishes Bloom Reader to the Google Play Console (internal track). | |
| # | |
| # - Pushes to `master` -> alpha flavor -> internal track of the "BR Alpha" app | |
| # - Pushes to `release` -> production flavor -> internal track of the "Bloom Reader" app | |
| # | |
| # (The Play track is set to "internal" in app/build.gradle's `play {}` block; | |
| # promotion to other tracks is done manually in the Play Console or via the | |
| # promote*Artifact gradle tasks.) | |
| # | |
| # Required repository secrets: | |
| # KEYSTORE_BASE64 base64 of keystore_bloom_reader.keystore | |
| # (e.g. `base64 -w0 keystore_bloom_reader.keystore`) | |
| # KEYSTORE_STORE_PASSWORD storePassword from keystore_bloom_reader.properties | |
| # KEYSTORE_KEY_ALIAS keyAlias from keystore_bloom_reader.properties | |
| # KEYSTORE_KEY_PASSWORD keyPassword from keystore_bloom_reader.properties | |
| # PLAY_SERVICE_ACCOUNT_JSON contents of the Google Play service account json file | |
| # | |
| # Optional repository variable: | |
| # PLAY_DRY_RUN set to "true" to build, sign, and upload to a Play | |
| # edit WITHOUT committing it — nothing becomes | |
| # visible in the Play Console. Use while verifying | |
| # this workflow; delete the variable to go live. | |
| # | |
| # Versioning: the patch number (3.4.NNN) is the number of commits since | |
| # versionMajor/versionMinor last changed in app/build.gradle, so it resets to 0 | |
| # automatically when the version is bumped, and master (alpha) and release | |
| # (production) count independently. NOTE: build.gradle computes versionCode = | |
| # major*100000 + minor*1000 + patch, so patch must stay below 1000. | |
| name: Publish to Play Console | |
| on: | |
| push: | |
| # TEMP: play-publish-gha is included only to test this workflow before | |
| # merging; remove it (revert this commit) before merging to master. | |
| branches: [master, release, play-publish-gha] | |
| workflow_dispatch: | |
| inputs: | |
| flavor: | |
| description: "Flavor to publish (both go to the internal track)" | |
| type: choice | |
| options: [Alpha, Production] | |
| default: Alpha | |
| patch: | |
| description: "Override the patch number (default: commits since the last version bump, plus the +77 legacy alpha offset)" | |
| type: string | |
| required: false | |
| default: "" | |
| # Never run two publishes at once; Play edits would race. | |
| concurrency: | |
| group: play-publish | |
| cancel-in-progress: false | |
| # contents: write lets the workflow push the release tag. | |
| permissions: | |
| contents: write | |
| jobs: | |
| publish: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 # full history: the patch number is derived from it | |
| fetch-tags: true # the tag step checks for existing release tags | |
| - name: Determine flavor and build number | |
| id: config | |
| env: | |
| PATCH_INPUT: ${{ inputs.patch }} | |
| run: | | |
| if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then | |
| FLAVOR="${{ inputs.flavor }}" | |
| elif [ "${{ github.ref }}" = "refs/heads/release" ]; then | |
| FLAVOR="Production" | |
| # TEMP: map the test branch to Production to dry-run the retain | |
| # path before merging. Drop this commit before merging to master. | |
| elif [ "${{ github.ref }}" = "refs/heads/play-publish-gha" ]; then | |
| FLAVOR="Production" | |
| else | |
| FLAVOR="Alpha" | |
| fi | |
| # A real publish must run from the flavor's own branch: the two | |
| # branches have different commit counts, so publishing a flavor | |
| # from the wrong branch corrupts that app's version numbering. | |
| # Dry runs commit nothing to Play and may run from any branch. | |
| if [ "${{ vars.PLAY_DRY_RUN }}" != "true" ]; then | |
| if [ "$FLAVOR" = "Production" ] && [ "${{ github.ref }}" != "refs/heads/release" ]; then | |
| echo "::error::A real Production publish must run from the release branch." | |
| exit 1 | |
| fi | |
| if [ "$FLAVOR" = "Alpha" ] && [ "${{ github.ref }}" = "refs/heads/release" ]; then | |
| echo "::error::A real Alpha publish must not run from the release branch." | |
| exit 1 | |
| fi | |
| fi | |
| # The patch number counts commits since the last version bump (the | |
| # last commit that changed the versionMajor/versionMinor lines in | |
| # app/build.gradle), so bumping the version resets it to 0 on its | |
| # own, and master (alpha) and release (production) count | |
| # independently on their own branches. | |
| if [ -n "$PATCH_INPUT" ]; then | |
| if ! [[ "$PATCH_INPUT" =~ ^[0-9]+$ ]]; then | |
| echo "::error::patch must be a plain number, got: $PATCH_INPUT" | |
| exit 1 | |
| fi | |
| BUILD_NUMBER="$PATCH_INPUT" | |
| else | |
| BUMP_COMMIT=$(git log -1 --format=%H -G'^def version(Major|Minor)' -- app/build.gradle) | |
| BUILD_NUMBER=$(git rev-list --count "$BUMP_COMMIT..HEAD") | |
| # Continuity with the old TeamCity build counter (alpha was at | |
| # 3.4.103 when this workflow took over). Keyed to the 3.4 bump | |
| # commit, so it expires by itself: the next version bump becomes | |
| # the new BUMP_COMMIT and this no longer applies. | |
| if [ "$BUMP_COMMIT" = "06dc6358a9a2ea410d5ce9bf6b39474177461618" ] && [ "$FLAVOR" = "Alpha" ]; then | |
| BUILD_NUMBER=$(( BUILD_NUMBER + 77 )) | |
| fi | |
| fi | |
| if [ "$BUILD_NUMBER" -gt 999 ]; then | |
| echo "::error::patch $BUILD_NUMBER would overflow into the minor-version digits of versionCode" | |
| exit 1 | |
| fi | |
| # Gradle task lists mirror the TeamCity builds ("clean" omitted: | |
| # the build dir doesn't exist on a fresh runner), plus the publish | |
| # step. TC's production build stops at assemble (publishing was a | |
| # separate manual step); here publishProductionRelease assembles | |
| # and publishes to the internal track. | |
| if [ "$FLAVOR" = "Alpha" ]; then | |
| TASKS="build publishAlphaRelease promoteAlphaReleaseArtifact" | |
| else | |
| TASKS="lintProductionRelease testProductionReleaseUnitTest publishProductionRelease" | |
| fi | |
| MAJOR=$(sed -n 's/^def versionMajor = \([0-9]*\).*/\1/p' app/build.gradle) | |
| MINOR=$(sed -n 's/^def versionMinor = \([0-9]*\).*/\1/p' app/build.gradle) | |
| echo "flavor=$FLAVOR" >> "$GITHUB_OUTPUT" | |
| echo "flavor_lc=${FLAVOR,,}" >> "$GITHUB_OUTPUT" | |
| echo "build_number=$BUILD_NUMBER" >> "$GITHUB_OUTPUT" | |
| echo "version=$MAJOR.$MINOR.$BUILD_NUMBER" >> "$GITHUB_OUTPUT" | |
| echo "tasks=$TASKS" >> "$GITHUB_OUTPUT" | |
| echo "Publishing flavor $FLAVOR version $MAJOR.$MINOR.$BUILD_NUMBER (tasks: $TASKS)" | |
| - uses: actions/setup-java@v5 | |
| with: | |
| distribution: temurin | |
| java-version: 17 | |
| - uses: gradle/actions/setup-gradle@v6 | |
| - uses: actions/setup-node@v6 | |
| with: | |
| node-version: 20 | |
| cache: yarn | |
| cache-dependency-path: app/yarn.lock | |
| - name: Install bloom-player | |
| working-directory: app | |
| run: yarn install --frozen-lockfile | |
| # Alpha builds always ship the latest alpha of bloom-player; | |
| # production builds use the version locked in yarn.lock. | |
| - name: Upgrade to latest bloom-player alpha | |
| if: steps.config.outputs.flavor == 'Alpha' | |
| working-directory: app | |
| run: yarn upgrade bloom-player@alpha | |
| - name: Set up signing and Play credentials | |
| env: | |
| KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }} | |
| KEYSTORE_STORE_PASSWORD: ${{ secrets.KEYSTORE_STORE_PASSWORD }} | |
| KEYSTORE_KEY_ALIAS: ${{ secrets.KEYSTORE_KEY_ALIAS }} | |
| KEYSTORE_KEY_PASSWORD: ${{ secrets.KEYSTORE_KEY_PASSWORD }} | |
| PLAY_SERVICE_ACCOUNT_JSON: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }} | |
| run: | | |
| # app/build.gradle reads ~/keystore/keystore_bloom_reader.properties | |
| mkdir -p "$HOME/keystore" | |
| echo "$KEYSTORE_BASE64" | base64 -d > "$HOME/keystore/keystore_bloom_reader.keystore" | |
| printf '%s' "$PLAY_SERVICE_ACCOUNT_JSON" > "$HOME/keystore/play-service-account.json" | |
| cat > "$HOME/keystore/keystore_bloom_reader.properties" <<EOF | |
| storeFile=$HOME/keystore/keystore_bloom_reader.keystore | |
| storePassword=$KEYSTORE_STORE_PASSWORD | |
| keyAlias=$KEYSTORE_KEY_ALIAS | |
| keyPassword=$KEYSTORE_KEY_PASSWORD | |
| serviceAccountJsonFile=$HOME/keystore/play-service-account.json | |
| EOF | |
| - name: Copy bloom-player assets | |
| run: ./gradlew copyBloomPlayerAssets | |
| - name: Build and publish to internal track | |
| run: > | |
| ./gradlew ${{ steps.config.outputs.tasks }} | |
| "-Pbuild.number=${{ steps.config.outputs.build_number }}" | |
| "-PplayDryRun=${{ vars.PLAY_DRY_RUN || 'false' }}" | |
| # Only reached if the publish above succeeded. | |
| - name: Tag the released commit | |
| if: steps.config.outputs.flavor == 'Production' && vars.PLAY_DRY_RUN != 'true' | |
| run: | | |
| TAG="v${{ steps.config.outputs.version }}" | |
| if git rev-parse -q --verify "refs/tags/$TAG" >/dev/null; then | |
| if [ "$(git rev-parse "refs/tags/$TAG^{commit}")" = "$(git rev-parse HEAD)" ]; then | |
| echo "Tag $TAG already exists on this commit (re-run); nothing to do." | |
| exit 0 | |
| fi | |
| echo "::error::Tag $TAG already exists on a different commit." | |
| exit 1 | |
| fi | |
| git tag "$TAG" | |
| git push origin "$TAG" | |
| - name: Clean up credentials | |
| if: always() | |
| run: rm -rf "$HOME/keystore" | |
| - name: Upload APK artifact | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: bloomreader-${{ steps.config.outputs.flavor }}-${{ steps.config.outputs.build_number }} | |
| # The build task assembles every flavor; only keep the one published. | |
| path: app/build/outputs/apk/${{ steps.config.outputs.flavor_lc }}/release/*.apk | |
| if-no-files-found: warn |