ci: [SDK-4690] add Android E2E workflow#2652
Conversation
📊 Diff Coverage Report✓ Coverage check passed (no source files changed) |
| env: | ||
| ONESIGNAL_APP_ID: ${{ inputs.onesignal-app-id }} | ||
| ONESIGNAL_API_KEY: ${{ inputs.onesignal-api-key }} | ||
| run: | | ||
| { | ||
| echo "ONESIGNAL_APP_ID=${ONESIGNAL_APP_ID}" | ||
| echo "ONESIGNAL_API_KEY=${ONESIGNAL_API_KEY}" | ||
| echo "ONESIGNAL_ANDROID_CHANNEL_ID=7ec2ece9-c538-4656-9516-1316f48a005c" | ||
| } > local.properties |
There was a problem hiding this comment.
🔴 The composite action accepts a required onesignal-api-key input and writes ONESIGNAL_API_KEY=... to examples/demo/local.properties, but nothing ever reads it: examples/demo/app/build.gradle.kts only calls demoOverride() for ONESIGNAL_APP_ID and ONESIGNAL_ANDROID_CHANNEL_ID, and a repo-wide grep confirms ONESIGNAL_API_KEY appears only in the two new CI files. The required input therefore forces every caller to supply an unused REST API key and silently materializes it onto the runner disk for no benefit. Either drop the onesignal-api-key input and the corresponding echo line at action.yml:51 (plus the secrets.APPIUM_ONESIGNAL_API_KEY wiring at e2e.yml:32), or wire the value through build.gradle.kts to a real consumer (e.g., BuildConfig).
Extended reasoning...
What the bug is
.github/actions/setup-demo/action.yml declares onesignal-api-key as a required input (lines 7-9) and writes it into examples/demo/local.properties (line 51). The accompanying comment at lines 36-41 describes the file as containing only "the OneSignal App ID + channel id" — there is no mention of an API key, suggesting the API-key line was added by accident or as leftover boilerplate from the Capacitor template.
Why it's dead plumbing
examples/demo/app/build.gradle.kts is the only consumer of local.properties in this repo. Its demoOverride() helper is invoked exactly twice — for ONESIGNAL_APP_ID (line 57) and ONESIGNAL_ANDROID_CHANNEL_ID (line 64). There is no demoOverride("ONESIGNAL_API_KEY") call, and a repo-wide grep for ONESIGNAL_API_KEY returns only the two new CI files (.github/actions/setup-demo/action.yml and .github/workflows/e2e.yml). No Gradle script, Kotlin/Java source file, or AndroidManifest references the key.
Why the Appium suite can't pick it up either
local.properties is a Gradle-configuration-time file that lives on the CI runner's filesystem; it is not packaged into the APK. The APK uploaded to BrowserStack therefore never sees the key, and the shared OneSignal/sdk-shared/.github/workflows/appium-e2e.yml@main workflow obtains its OneSignal/BrowserStack secrets directly via secrets: inherit on e2e.yml:48 — not via local.properties. So there is no plausible runtime path by which the written value reaches any consumer.
Step-by-step proof
.github/workflows/e2e.yml:32passessecrets.APPIUM_ONESIGNAL_API_KEYto the composite action'sonesignal-api-keyinput..github/actions/setup-demo/action.yml:51writesONESIGNAL_API_KEY=$ONESIGNAL_API_KEYtoexamples/demo/local.properties../gradlew :app:assembleGmsDebugruns (e2e.yml:69). Gradle parsesbuild.gradle.kts, which only callsdemoOverride("ONESIGNAL_APP_ID")anddemoOverride("ONESIGNAL_ANDROID_CHANNEL_ID"). TheONESIGNAL_API_KEYline inlocal.propertiesis ignored.- The resulting
app-gms-debug.apkis uploaded asdemo-apkand consumed by the reusable Appium workflow. The APK contains noONESIGNAL_API_KEYvalue, andlocal.propertiesis not shipped with it. - The reusable workflow gets its own secrets via
secrets: inherit, independently of anything in this repo's build.
The key is therefore written to a file nothing reads, on a runner that is torn down at the end of the job.
Impact
Severity is normal, not a runtime crash but a real correctness/cleanliness issue:
- False signal: The workflow looks like it plumbs the REST API key into the build/test pipeline; it does not. A future contributor reading the action will reasonably believe
BuildConfig.ONESIGNAL_API_KEY(or similar) is available — it isn't. - Hard-fail risk: Because the input is
required: true, any caller withoutAPPIUM_ONESIGNAL_API_KEYset will fail the workflow at thewith:validation step for zero benefit. - Minor security smell: A REST API key is unnecessarily materialized onto the runner filesystem for a code path that never consumes it.
How to fix
Either:
- Remove the
onesignal-api-keyinput (action.yml:7-9), theONESIGNAL_API_KEYenv+echo lines (action.yml:48, 51), and theonesignal-api-key:line ine2e.yml:32; or - Wire it through
examples/demo/app/build.gradle.ktsby addingdemoOverride("ONESIGNAL_API_KEY")and exposing it viabuildConfigField/resValueso the demo app (or a runtime test hook) can actually consume it.
0755237 to
0d6ff30
Compare
Description
One Line Summary
Add an Android E2E GitHub Actions workflow that builds the demo APK and runs the shared Appium suite on BrowserStack, mirroring the Capacitor SDK setup.
Details
Motivation
The Capacitor, Flutter, and React Native wrappers already run the shared
OneSignal/sdk-sharedAppium suite on each release, but the native Android SDK had no equivalent. Without it there is no automated end-to-end signal that a freshly publishedcom.onesignal:OneSignalartifact actually boots, registers a subscription, and exercises the Red App flows on a real device. This PR fills that gap so every release branch can produce a BrowserStack run alongside the Capacitor and other wrapper runs.Scope
.github/workflows/e2e.yml, which triggers on pushes torel/**and onworkflow_dispatch(with an optionalsdk-versioninput). It resolves the SDK version fromOneSignalSDK/gradle.properties, callsOneSignal/sdk-shared/.github/actions/wait-for-maven-artifact@mainso we do not race the Maven publish, builds:app:assembleGmsDebugfromexamples/demo/with-PSDK_VERSION=..., uploadsapp-gms-debug.apkasdemo-apk, and then delegates toOneSignal/sdk-shared/.github/workflows/appium-e2e.yml@mainwithsdk-type: android..github/actions/setup-demo/action.yml, a composite action that installs Java 17 + the Android cmdline-tools, caches Gradle, and writesexamples/demo/local.propertieswith the OneSignal App ID, API key, and theONESIGNAL_ANDROID_CHANNEL_IDused by the shared Appium suite. This is the Android counterpart to the Capacitorsetup-demoaction, withlocal.propertiestaking the place of Capacitor's.env.ci.yml,publish-release.yml, andcreate-release-pr.ymlare untouched.OPTIONAL - Other
A few Android-specific deviations from the Capacitor template are worth calling out:
build-androidjob (no iOS matrix) since this repo only ships Android.gmsflavor and thedebugbuild type, because BrowserStack devices all run Google Play Services andisDebuggable=trueis what Appium's UiAutomator2 driver needs to attach. The current demobuild.gradle.ktsdoes not configure ABI splits, so the unqualifiedapp-gms-debug.apkis the correct artifact name.rel/**works the same way it does on Capacitor, but on this repo the rel branch bumps to an unpublished version, sowait-for-maven-artifactwill keep polling (up to ~50 minutes) untilpublish-release.ymlcompletes. If you would rather run the workflow strictly after a Maven publish, theworkflow_dispatchtrigger with the optionalsdk-versioninput is the manual escape hatch.Testing
Unit testing
N/A. Pure CI/workflow change.
Manual testing
.github/workflows/e2e.ymlwithactionlint 1.7.12(no findings)..github/actions/setup-demo/action.ymlagainst the GitHub composite-action schema (action.yml files are not workflow files, so actionlint's workflow-schema errors there are expected and not a real issue).e2e.ymland againstOneSignal/sdk-shared/.github/workflows/appium-e2e.yml@mainto confirm the input contract (platform,app-artifact,app-filename,sdk-type,build-name) matches and thatsecrets: inheritcovers the BrowserStack and Appium OneSignal secrets the shared workflow expects.examples/demo/app/build/outputs/apk/gms/debug/app-gms-debug.apkmatches AGP's default output for thegmsflavor +debugbuild type given the currentexamples/demo/app/build.gradle.kts(no ABI splits, no APK rename).Affected code checklist
(No production SDK surfaces are touched; this PR only adds CI plumbing.)
Checklist
Overview
Testing
Final pass
Made with Cursor