-
Notifications
You must be signed in to change notification settings - Fork 11
ci(example): publish example app to Play Store internal track #347
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
evan-masseau
wants to merge
8
commits into
feat/example-app
Choose a base branch
from
ecm/ci/android-example-play-publish
base: feat/example-app
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+176
−2
Open
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
158f110
ci(example): add Play Store internal track publish workflow
evan-masseau 0924305
ci(example): add branch push trigger, bump runner to ubuntu-24.04
evan-masseau 744e5b8
fix(ci): restore Slack header block so alert title renders
evan-masseau 0e46c3d
fix(example/android): produce unsigned release AAB for CI to sign once
evan-masseau 1ae6097
ci(example): sign AAB via gradle-injected properties, drop r0adkll si…
evan-masseau 1123f5f
ci(example): inject versionCode from github.run_number
evan-masseau ebe644e
fix(ci): read injected versionCode from project property in build.gradle
evan-masseau eaedcd6
fix(ci): upload as draft release while Play listing is in Draft state
evan-masseau File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,171 @@ | ||
| # Publish the React Native SDK example app to Google Play Console internal track. | ||
| # | ||
| # Triggers: | ||
| # - workflow_dispatch: Manual publish (preferred — SDK releases don't map 1:1 to example app publishes) | ||
| # - release: published: Fires on every SDK release to keep the example app in sync | ||
| # | ||
| # Required secrets (set in repo Settings → Secrets and variables → Actions): | ||
| # GOOGLE_SERVICES_JSON - Contents of google-services.json for Firebase | ||
| # SIGNING_KEY - Base64-encoded release keystore | ||
| # ALIAS - Key alias in the keystore | ||
| # KEY_STORE_PASSWORD - Keystore password | ||
| # KEY_PASSWORD - Key password | ||
| # SERVICE_ACCOUNT_JSON - Google Play service account JSON (plain text) | ||
| # KLAVIYO_EXAMPLE_API_KEY - Klaviyo public API key for the example app build. | ||
| # Required: workflow fails early if unset (guard step after checkout). | ||
|
|
||
| name: Publish Example App to Play Store Internal Track | ||
|
|
||
| on: | ||
| workflow_dispatch: | ||
| release: | ||
| types: [published] | ||
| # Temporary: run on every push to this branch so we can iterate on the workflow | ||
| # before merge. `workflow_dispatch` only works from the default branch, so this | ||
| # is the only way to exercise the pipeline end-to-end against the PR. Remove | ||
| # this `push` trigger before merging to master. | ||
| push: | ||
| branches: [ecm/ci/android-example-play-publish] | ||
|
|
||
| jobs: | ||
| deploy: | ||
| runs-on: ubuntu-24.04 | ||
|
|
||
| steps: | ||
| - uses: actions/checkout@v4 | ||
|
|
||
| - name: Verify KLAVIYO_EXAMPLE_API_KEY secret is set | ||
| run: | | ||
| if [ -z "${KLAVIYO_EXAMPLE_API_KEY}" ]; then | ||
| echo "::error::KLAVIYO_EXAMPLE_API_KEY secret is not set. The workflow cannot proceed — the build would produce an app that crashes at launch." | ||
| exit 1 | ||
| fi | ||
| shell: bash | ||
| env: | ||
| KLAVIYO_EXAMPLE_API_KEY: ${{ secrets.KLAVIYO_EXAMPLE_API_KEY }} | ||
|
|
||
| # Node + Yarn 3 + workspace deps. Required because the React Native Gradle plugin | ||
| # invokes the Metro bundler during bundleRelease to generate the JS bundle. Installs | ||
| # the example workspace too, making react-native and its CLI available to Gradle. | ||
| - name: Setup | ||
| uses: ./.github/actions/setup | ||
|
|
||
| - name: Set up JDK 17 | ||
| uses: actions/setup-java@v4 | ||
| with: | ||
| distribution: 'zulu' | ||
| java-version: '17' | ||
| java-package: jdk | ||
|
cursor[bot] marked this conversation as resolved.
|
||
|
|
||
| - name: Add Google Services from Secrets | ||
| run: 'echo "$GOOGLE_SERVICES_JSON" > ./example/android/app/google-services.json' | ||
| shell: bash | ||
| env: | ||
| GOOGLE_SERVICES_JSON: ${{ secrets.GOOGLE_SERVICES_JSON }} | ||
|
|
||
| # Write the Klaviyo public API key into example/.env so react-native-dotenv | ||
| # can expose it to the JS layer as KLAVIYO_API_KEY (imported from '@env'). | ||
| # The guard step above ensures the secret is set before we reach this point. | ||
| - name: Write .env with Klaviyo API key | ||
| run: echo "KLAVIYO_API_KEY=${KLAVIYO_EXAMPLE_API_KEY}" > example/.env | ||
| env: | ||
| KLAVIYO_EXAMPLE_API_KEY: ${{ secrets.KLAVIYO_EXAMPLE_API_KEY }} | ||
|
evan-masseau marked this conversation as resolved.
cursor[bot] marked this conversation as resolved.
|
||
|
|
||
| # Decode the base64-encoded upload keystore from secrets into a file that | ||
| # the Gradle signing config can point at. The runner is ephemeral so the | ||
| # plaintext .jks is torn down with the VM, but we also remove it at the | ||
| # end of the job as hygiene. | ||
| - name: Decode upload keystore | ||
| run: echo "$SIGNING_KEY" | base64 -d > "${RUNNER_TEMP}/upload.jks" | ||
| env: | ||
| SIGNING_KEY: ${{ secrets.SIGNING_KEY }} | ||
|
|
||
| # bundleRelease triggers react-native bundle automatically via the RN Gradle plugin. | ||
| # We cd into the android directory so that relative paths in settings.gradle resolve correctly. | ||
| # | ||
| # Signing is done inline via `-Pandroid.injected.signing.*` gradle properties so AGP | ||
| # produces a properly v2/v3-signed AAB in a single pass — no separate re-sign step, | ||
| # no risk of double-signing (Play rejects AABs with multiple signer certificates). | ||
| # versionCode is overridden per-run via github.run_number so successive | ||
| # CI uploads never collide on Play's strictly-increasing versionCode rule. | ||
| # Read in build.gradle as `project.findProperty("versionCode")` — AGP's | ||
| # `android.injected.version.code` is an IDE flag and isn't reliably | ||
| # honored by command-line Gradle builds. | ||
| # Caveat: manual Play uploads that bump versionCode outside CI can get | ||
| # ahead of run_number — if that happens, bump CI past the manual number | ||
| # (re-trigger the workflow N times, or add an offset). | ||
| - name: Assemble Release Bundle | ||
| run: | | ||
| cd example/android && ./gradlew :app:bundleRelease \ | ||
| -PreleaseVersionCode=${{ github.run_number }} \ | ||
| -Pandroid.injected.signing.store.file="${RUNNER_TEMP}/upload.jks" \ | ||
| -Pandroid.injected.signing.store.password="${KEY_STORE_PASSWORD}" \ | ||
| -Pandroid.injected.signing.key.alias="${ALIAS}" \ | ||
| -Pandroid.injected.signing.key.password="${KEY_PASSWORD}" | ||
| env: | ||
| KEY_STORE_PASSWORD: ${{ secrets.KEY_STORE_PASSWORD }} | ||
| KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} | ||
| ALIAS: ${{ secrets.ALIAS }} | ||
| # Prevent Metro from trying to start a dev server during the build | ||
| CI: true | ||
|
evan-masseau marked this conversation as resolved.
|
||
|
|
||
| # `status: draft` — the release lands on the internal track unpublished | ||
| # so you can manually promote it in Play Console. Required while the app | ||
| # listing itself is still in Draft state (no published version yet); | ||
| # flip to `completed` once the listing is fully configured for release. | ||
| - name: Deploy to Internal Track | ||
| uses: r0adkll/[email protected] | ||
| with: | ||
| serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }} | ||
| packageName: com.klaviyoreactnativesdkexample | ||
| releaseFiles: example/android/app/build/outputs/bundle/release/app-release.aab | ||
| track: internal | ||
| status: draft | ||
|
|
||
| - name: Clean up keystore | ||
| if: always() | ||
| run: rm -f "${RUNNER_TEMP}/upload.jks" | ||
|
|
||
| - name: Notify Slack on success | ||
| if: success() | ||
| uses: slackapi/[email protected] | ||
| with: | ||
| webhook: ${{ secrets.SLACK_WEBHOOK_URL }} | ||
| webhook-type: incoming-webhook | ||
| payload: | | ||
| text: "✅ RN SDK Example App published to Play Store internal track" | ||
| blocks: | ||
| - type: "header" | ||
| text: | ||
| type: "plain_text" | ||
| text: "✅ RN SDK Example App published to Play Store internal track" | ||
| - type: "section" | ||
| text: | ||
| type: "mrkdwn" | ||
| text: "*Repository:* ${{ github.repository }}\n*Workflow:* ${{ github.workflow }}\n*Release:* `${{ github.event.release.tag_name || 'manual' }}`\n*Commit:* `${{ github.sha }}`\n*Author:* ${{ github.actor }}" | ||
| - type: "section" | ||
| text: | ||
| type: "mrkdwn" | ||
| text: "*Workflow Run:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Details>" | ||
|
|
||
| - name: Notify Slack on failure | ||
| if: failure() | ||
| uses: slackapi/[email protected] | ||
| with: | ||
| webhook: ${{ secrets.SLACK_WEBHOOK_URL }} | ||
| webhook-type: incoming-webhook | ||
| payload: | | ||
| text: "🚨 RN SDK Example App Play Store publish failed" | ||
| blocks: | ||
| - type: "header" | ||
| text: | ||
| type: "plain_text" | ||
| text: "🚨 RN SDK Example App Play Store publish failed" | ||
| - type: "section" | ||
| text: | ||
| type: "mrkdwn" | ||
| text: "*Repository:* ${{ github.repository }}\n*Workflow:* ${{ github.workflow }}\n*Release:* `${{ github.event.release.tag_name || 'manual' }}`\n*Commit:* `${{ github.sha }}`\n*Author:* ${{ github.actor }}" | ||
| - type: "section" | ||
| text: | ||
| type: "mrkdwn" | ||
| text: "*Workflow Run:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View Details>" | ||
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
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Temporary push trigger left in workflow file
Medium Severity
A temporary
pushtrigger on branchecm/ci/android-example-play-publishis still present in the workflow, with an inline comment explicitly stating "Remove thispushtrigger before merging to master." If merged as-is, any push to that branch would trigger the full publish pipeline, potentially uploading unintended builds to the Play Store internal track. The PR description only mentionsworkflow_dispatchandrelease: publishedas intended triggers.Reviewed by Cursor Bugbot for commit 0924305. Configure here.