Skip to content

Commit 928341d

Browse files
authored
ci: [SDK-4687] add Unity SDK E2E workflow (#870)
1 parent db420a7 commit 928341d

3 files changed

Lines changed: 312 additions & 0 deletions

File tree

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: "Create demo .env"
2+
description: "Writes the Unity demo app's .env file used by E2E builds"
3+
inputs:
4+
onesignal-app-id:
5+
description: "OneSignal App ID for the demo app"
6+
required: true
7+
onesignal-api-key:
8+
description: "OneSignal REST API key for the demo app"
9+
required: true
10+
onesignal-android-channel-id:
11+
description: "Android Notification Channel ID used by the WITH SOUND test notification"
12+
required: false
13+
default: "7ec2ece9-c538-4656-9516-1316f48a005c"
14+
e2e-mode:
15+
description: "Whether to enable E2E_MODE in the demo app"
16+
required: false
17+
default: "true"
18+
runs:
19+
using: "composite"
20+
steps:
21+
- name: Write .env
22+
working-directory: examples/demo
23+
shell: bash
24+
env:
25+
APP_ID: ${{ inputs.onesignal-app-id }}
26+
API_KEY: ${{ inputs.onesignal-api-key }}
27+
ANDROID_CHANNEL_ID: ${{ inputs.onesignal-android-channel-id }}
28+
E2E_MODE: ${{ inputs.e2e-mode }}
29+
run: |
30+
{
31+
echo "ONESIGNAL_APP_ID=$APP_ID"
32+
echo "ONESIGNAL_API_KEY=$API_KEY"
33+
echo "ONESIGNAL_ANDROID_CHANNEL_ID=$ANDROID_CHANNEL_ID"
34+
echo "E2E_MODE=$E2E_MODE"
35+
} > .env

.github/workflows/e2e.yml

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
name: E2E Tests
2+
3+
on:
4+
push:
5+
branches:
6+
- rel/**
7+
workflow_dispatch:
8+
inputs:
9+
platform:
10+
description: "Platform to test"
11+
required: true
12+
default: "both"
13+
type: choice
14+
options:
15+
- android
16+
- ios
17+
- both
18+
19+
permissions:
20+
contents: read
21+
22+
concurrency:
23+
group: ${{ github.workflow }}-${{ github.ref }}
24+
cancel-in-progress: true
25+
26+
jobs:
27+
build-android:
28+
if: >-
29+
github.event_name == 'push' ||
30+
github.event.inputs.platform == 'android' ||
31+
github.event.inputs.platform == 'both'
32+
runs-on: ubuntu-latest
33+
steps:
34+
- name: Checkout
35+
uses: actions/checkout@v6
36+
with:
37+
lfs: true
38+
39+
- name: Create demo .env
40+
uses: ./.github/actions/create-demo-env
41+
with:
42+
onesignal-app-id: ${{ vars.APPIUM_ONESIGNAL_APP_ID }}
43+
onesignal-api-key: ${{ secrets.APPIUM_ONESIGNAL_API_KEY }}
44+
45+
- name: Resolve OneSignal Android SDK version
46+
id: android-sdk-version
47+
run: |
48+
VERSION=$(grep -oE 'com\.onesignal:OneSignal:[^"]+' com.onesignal.unity.android/Editor/OneSignalAndroidDependencies.xml | head -n1 | cut -d: -f3)
49+
if [ -z "$VERSION" ]; then
50+
echo "::error::Could not parse OneSignal Android SDK version from OneSignalAndroidDependencies.xml"
51+
exit 1
52+
fi
53+
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
54+
55+
- name: Wait for OneSignal Android SDK on Maven Central
56+
uses: OneSignal/sdk-shared/.github/actions/wait-for-maven-artifact@main
57+
with:
58+
version: ${{ steps.android-sdk-version.outputs.version }}
59+
60+
- name: Cache Unity Library
61+
uses: actions/cache@v5
62+
with:
63+
path: examples/demo/Library
64+
key: unity-library-android-${{ hashFiles('examples/demo/Packages/manifest.json', 'examples/demo/ProjectSettings/ProjectVersion.txt') }}
65+
restore-keys: unity-library-android-
66+
67+
- name: Build APK
68+
uses: game-ci/unity-builder@v4
69+
env:
70+
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
71+
UNITY_EMAIL: ${{ secrets.UNITY_USERNAME }}
72+
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
73+
with:
74+
projectPath: examples/demo
75+
unityVersion: auto
76+
targetPlatform: Android
77+
buildMethod: BuildScript.BuildAndroidEmulator
78+
allowDirtyBuild: true
79+
80+
- name: Upload APK
81+
uses: actions/upload-artifact@v7
82+
with:
83+
name: demo-apk
84+
path: examples/demo/Build/Android/onesignal-demo.apk
85+
retention-days: 1
86+
compression-level: 0
87+
88+
build-ios:
89+
if: >-
90+
github.event_name == 'push' ||
91+
github.event.inputs.platform == 'ios' ||
92+
github.event.inputs.platform == 'both'
93+
runs-on: macos-latest
94+
steps:
95+
- name: Checkout
96+
uses: actions/checkout@v6
97+
with:
98+
lfs: true
99+
100+
- name: Create demo .env
101+
uses: ./.github/actions/create-demo-env
102+
with:
103+
onesignal-app-id: ${{ vars.APPIUM_ONESIGNAL_APP_ID }}
104+
onesignal-api-key: ${{ secrets.APPIUM_ONESIGNAL_API_KEY }}
105+
106+
- name: Cache Unity Library
107+
uses: actions/cache@v5
108+
with:
109+
path: examples/demo/Library
110+
key: unity-library-ios-${{ hashFiles('examples/demo/Packages/manifest.json', 'examples/demo/ProjectSettings/ProjectVersion.txt') }}
111+
restore-keys: unity-library-ios-
112+
113+
- name: Cache Xcode DerivedData
114+
# Hash inputs to the Unity iOS export (files that exist at checkout
115+
# time) rather than the exported Xcode project / Podfile, which
116+
# don't exist until later steps run. Mirrors the Android Unity
117+
# Library cache key shape above.
118+
uses: actions/cache@v5
119+
with:
120+
path: examples/demo/Build/iOS-DerivedData
121+
key: deriveddata-${{ runner.os }}-${{ hashFiles('examples/demo/Packages/manifest.json', 'examples/demo/ProjectSettings/ProjectVersion.txt', 'examples/demo/ProjectSettings/ProjectSettings.asset', 'com.onesignal.unity.ios/Editor/**', 'com.onesignal.unity.core/Editor/**', 'examples/demo/iOS/ExportOptions.plist') }}
122+
restore-keys: deriveddata-${{ runner.os }}-
123+
124+
- name: Export Xcode project from Unity
125+
uses: game-ci/unity-builder@v4
126+
env:
127+
UNITY_SERIAL: ${{ secrets.UNITY_SERIAL }}
128+
UNITY_EMAIL: ${{ secrets.UNITY_USERNAME }}
129+
UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
130+
with:
131+
projectPath: examples/demo
132+
unityVersion: auto
133+
targetPlatform: iOS
134+
buildMethod: BuildScript.BuildiOSDevice
135+
allowDirtyBuild: true
136+
cacheUnityInstallationOnMac: true
137+
138+
- name: Pod install
139+
working-directory: examples/demo/Build/iOS
140+
run: pod install --repo-update
141+
142+
- name: Set up iOS codesigning
143+
uses: OneSignal/sdk-shared/.github/actions/setup-ios-demo-codesigning@main
144+
with:
145+
p12-base64: ${{ secrets.APPIUM_IOS_DEV_CERT_P12_BASE64 }}
146+
p12-password: ${{ secrets.APPIUM_IOS_DEV_CERT_PASSWORD }}
147+
asc-key-id: ${{ secrets.APPIUM_APP_STORE_CONNECT_KEY_ID }}
148+
asc-issuer-id: ${{ secrets.APPIUM_APP_STORE_CONNECT_ISSUER_ID }}
149+
asc-private-key: ${{ secrets.APPIUM_APP_STORE_CONNECT_PRIVATE_KEY }}
150+
151+
- name: Build signed IPA
152+
working-directory: examples/demo/Build/iOS
153+
# Unity always names the generated Xcode project Unity-iPhone, even
154+
# though the demo's product name is "OneSignal Demo". After
155+
# `pod install` an Unity-iPhone.xcworkspace exists and must be used;
156+
# without CocoaPods we fall back to the .xcodeproj. The
157+
# SigningPostProcessor already pinned DEVELOPMENT_TEAM and the NSE /
158+
# Live Activity bundle IDs to match ExportOptions.plist.
159+
run: |
160+
if [ -d Unity-iPhone.xcworkspace ]; then
161+
CONTAINER_ARGS=(-workspace Unity-iPhone.xcworkspace)
162+
else
163+
CONTAINER_ARGS=(-project Unity-iPhone.xcodeproj)
164+
fi
165+
xcodebuild archive \
166+
"${CONTAINER_ARGS[@]}" \
167+
-scheme Unity-iPhone \
168+
-configuration Release \
169+
-sdk iphoneos \
170+
-destination 'generic/platform=iOS' \
171+
-archivePath build/App.xcarchive \
172+
-derivedDataPath ../iOS-DerivedData \
173+
-quiet \
174+
-hideShellScriptEnvironment \
175+
CODE_SIGN_STYLE=Manual \
176+
COMPILER_INDEX_STORE_ENABLE=NO
177+
xcodebuild -exportArchive \
178+
-archivePath build/App.xcarchive \
179+
-exportOptionsPlist "$GITHUB_WORKSPACE/examples/demo/iOS/ExportOptions.plist" \
180+
-exportPath build/ipa \
181+
-quiet
182+
183+
- name: Locate and stage IPA
184+
id: ipa
185+
working-directory: examples/demo
186+
run: |
187+
IPA=$(ls Build/iOS/build/ipa/*.ipa | head -n1)
188+
if [ -z "$IPA" ]; then
189+
echo "::error::No IPA produced by xcodebuild -exportArchive"
190+
exit 1
191+
fi
192+
cp "$IPA" demo.ipa
193+
echo "path=examples/demo/demo.ipa" >> "$GITHUB_OUTPUT"
194+
195+
- name: Verify aps-environment in IPA
196+
working-directory: examples/demo
197+
run: |
198+
unzip -oq demo.ipa -d /tmp/ipa
199+
APP=$(ls -d /tmp/ipa/Payload/*.app | head -n1)
200+
codesign -d --entitlements - "$APP" 2>&1 | tee /tmp/entitlements.txt
201+
if ! grep -q 'aps-environment' /tmp/entitlements.txt; then
202+
echo "::error::Built IPA is missing aps-environment entitlement; push subscription will not work"
203+
exit 1
204+
fi
205+
206+
- name: Upload IPA
207+
uses: actions/upload-artifact@v7
208+
with:
209+
name: demo-ipa
210+
path: ${{ steps.ipa.outputs.path }}
211+
retention-days: 1
212+
compression-level: 0
213+
214+
e2e-android:
215+
needs: build-android
216+
uses: OneSignal/sdk-shared/.github/workflows/appium-e2e.yml@main
217+
secrets: inherit
218+
with:
219+
platform: android
220+
app-artifact: demo-apk
221+
app-filename: onesignal-demo.apk
222+
sdk-type: unity
223+
build-name: unity-android-${{ github.ref_name }}-${{ github.run_number }}
224+
225+
e2e-ios:
226+
needs: build-ios
227+
uses: OneSignal/sdk-shared/.github/workflows/appium-e2e.yml@main
228+
secrets: inherit
229+
with:
230+
platform: ios
231+
app-artifact: demo-ipa
232+
app-filename: demo.ipa
233+
sdk-type: unity
234+
build-name: unity-ios-${{ github.ref_name }}-${{ github.run_number }}

examples/demo/Assets/Scripts/Editor/BuildScript.cs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,49 @@ public static void BuildiOSSimulator()
110110
HandleReport(report, IOSOutputDir);
111111
}
112112

113+
/// <summary>
114+
/// Builds an Xcode project targeting physical iOS devices, used by CI to
115+
/// produce a signed IPA for BrowserStack. The OneSignal SDK and demo
116+
/// post-processors add push capabilities, the NSE / Live Activity widget
117+
/// targets, and pin DEVELOPMENT_TEAM + aps-environment=development so the
118+
/// archive step can sign with the Appium provisioning profiles in
119+
/// ExportOptions.plist.
120+
/// </summary>
121+
public static void BuildiOSDevice()
122+
{
123+
Directory.CreateDirectory(IOSOutputDir);
124+
125+
PlayerSettings.SetScriptingBackend(NamedBuildTarget.iOS, ScriptingImplementation.IL2CPP);
126+
PlayerSettings.iOS.sdkVersion = iOSSdkVersion.DeviceSDK;
127+
128+
Debug.Log(
129+
$"[BuildScript] iOS sdk={PlayerSettings.iOS.sdkVersion} backend={PlayerSettings.GetScriptingBackend(NamedBuildTarget.iOS)}"
130+
);
131+
132+
PlayerSettings.SetIl2CppCodeGeneration(
133+
NamedBuildTarget.iOS,
134+
Il2CppCodeGeneration.OptimizeSize
135+
);
136+
PlayerSettings.SetManagedStrippingLevel(NamedBuildTarget.iOS, ManagedStrippingLevel.High);
137+
PlayerSettings.stripEngineCode = true;
138+
139+
PlayerSettings.SetIl2CppCompilerConfiguration(
140+
NamedBuildTarget.iOS,
141+
Il2CppCompilerConfiguration.Release
142+
);
143+
144+
var options = new BuildPlayerOptions
145+
{
146+
scenes = GetScenes(),
147+
locationPathName = IOSOutputDir,
148+
target = BuildTarget.iOS,
149+
options = BuildOptions.None,
150+
};
151+
152+
var report = BuildPipeline.BuildPlayer(options);
153+
HandleReport(report, IOSOutputDir);
154+
}
155+
113156
private static string[] GetScenes()
114157
{
115158
var scenes = EditorBuildSettings.scenes;

0 commit comments

Comments
 (0)