Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/actions/setup-demo/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: 'Setup Demo'
description: 'Installs the JDK and Android toolchains, caches Gradle, and writes the demo local.properties file'
inputs:
onesignal-app-id:
description: 'OneSignal App ID written into examples/demo/local.properties'
required: true
onesignal-api-key:
description: 'OneSignal REST API key written into examples/demo/local.properties'
required: true
runs:
using: 'composite'
steps:
- name: Set up Java
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'

- name: Set up Android SDK
uses: android-actions/setup-android@v3
with:
cmdline-tools-version: '10406996'
log-accepted-android-sdk-licenses: false

- name: Cache Gradle
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-demo-${{ hashFiles('examples/demo/**/*.gradle*', 'examples/demo/**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-demo-
${{ runner.os }}-gradle-

# Mirrors the Capacitor demo's `.env`: writes the override file before any
# build runs so the OneSignal App ID + channel id end up baked into
# BuildConfig. `examples/demo/app/build.gradle.kts` resolves each key in
# this order: `-PKEY=value` from the CLI > local.properties > built-in
# default, so writing to local.properties is the closest equivalent to
# the Capacitor `.env` flow.
- name: Write demo local.properties
shell: bash
working-directory: examples/demo
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
Comment on lines +45 to +53
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 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

  1. .github/workflows/e2e.yml:32 passes secrets.APPIUM_ONESIGNAL_API_KEY to the composite action's onesignal-api-key input.
  2. .github/actions/setup-demo/action.yml:51 writes ONESIGNAL_API_KEY=$ONESIGNAL_API_KEY to examples/demo/local.properties.
  3. ./gradlew :app:assembleGmsDebug runs (e2e.yml:69). Gradle parses build.gradle.kts, which only calls demoOverride("ONESIGNAL_APP_ID") and demoOverride("ONESIGNAL_ANDROID_CHANNEL_ID"). The ONESIGNAL_API_KEY line in local.properties is ignored.
  4. The resulting app-gms-debug.apk is uploaded as demo-apk and consumed by the reusable Appium workflow. The APK contains no ONESIGNAL_API_KEY value, and local.properties is not shipped with it.
  5. 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 without APPIUM_ONESIGNAL_API_KEY set will fail the workflow at the with: 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-key input (action.yml:7-9), the ONESIGNAL_API_KEY env+echo lines (action.yml:48, 51), and the onesignal-api-key: line in e2e.yml:32; or
  • Wire it through examples/demo/app/build.gradle.kts by adding demoOverride("ONESIGNAL_API_KEY") and exposing it via buildConfigField/resValue so the demo app (or a runtime test hook) can actually consume it.

82 changes: 82 additions & 0 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: E2E Tests

on:
push:
branches:
- rel/**
workflow_dispatch:
inputs:
sdk-version:
description: 'OneSignal Android SDK version to wait for and build against (defaults to OneSignalSDK/gradle.properties).'
required: false
type: string

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build-android:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up demo
uses: ./.github/actions/setup-demo
with:
onesignal-app-id: ${{ vars.APPIUM_ONESIGNAL_APP_ID }}
onesignal-api-key: ${{ secrets.APPIUM_ONESIGNAL_API_KEY }}

- name: Resolve OneSignal Android SDK version
id: android-sdk-version
env:
INPUT_VERSION: ${{ github.event.inputs.sdk-version }}
run: |
if [ -n "$INPUT_VERSION" ]; then
VERSION="$INPUT_VERSION"
else
VERSION=$(grep -E '^SDK_VERSION=' OneSignalSDK/gradle.properties | cut -d '=' -f2)
fi
if [ -z "$VERSION" ]; then
echo "::error::Could not resolve OneSignal Android SDK version"
exit 1
fi
echo "Resolved OneSignal Android SDK version: $VERSION"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"

- name: Wait for OneSignal Android SDK on Maven Central
uses: OneSignal/sdk-shared/.github/actions/wait-for-maven-artifact@main
with:
version: ${{ steps.android-sdk-version.outputs.version }}

- name: Build debug APK
working-directory: examples/demo
# Build the gms-debug variant: BrowserStack devices all run Google
# Play Services, and debug ensures isDebuggable=true so Appium's
# UiAutomator2 driver can attach. The Maven-resolved OneSignal
# dependency is pinned to the version we just waited for, so the
# APK exercises the actual published artifact.
run: ./gradlew :app:assembleGmsDebug -PSDK_VERSION=${{ steps.android-sdk-version.outputs.version }} --console=plain --warning-mode=summary

- name: Upload APK
uses: actions/upload-artifact@v4
with:
name: demo-apk
path: examples/demo/app/build/outputs/apk/gms/debug/app-gms-debug.apk
retention-days: 1
compression-level: 0

e2e-android:
needs: build-android
uses: OneSignal/sdk-shared/.github/workflows/appium-e2e.yml@main
secrets: inherit
with:
platform: android
app-artifact: demo-apk
app-filename: app-gms-debug.apk
sdk-type: android
build-name: android-${{ github.ref_name }}-${{ github.run_number }}
Loading
Loading