Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
95d6afc
Test hardening, bug fixes on iOS, RN, and Android. Add CI testing for…
akfreas May 22, 2026
48f103a
Consolidate the duplicated iOS and Android JS bridge packages into a …
akfreas May 22, 2026
0144b89
Repoint the iOS build script and the iOS and Android SDK docs at the …
akfreas May 22, 2026
fd8665d
Restore the Android JS bridge bundle to its minified build output, re…
akfreas May 22, 2026
46a89c2
Port the getMergeTagValue and flag bridge methods into the merged opt…
akfreas May 22, 2026
6a59652
prevent CI from silently passing when Android instrumentation runner …
akfreas May 25, 2026
02739e8
Drop set -o pipefail from the emulator-runner script — the action inv…
akfreas May 25, 2026
8d8de1e
satisfy strict ESLint in OptimizationProvider injected-sdk RN tests: …
akfreas May 25, 2026
af1dcf9
satisfy strict ESLint in OptimizationProvider RN test: use Promise.wi…
akfreas May 25, 2026
998ec0c
satisfy strict ESLint in OptimizedEntry RN test: extract 1234 ms dwel…
akfreas May 25, 2026
25407e1
satisfy strict ESLint in OptimizationProvider onStatesReady react-web…
akfreas May 25, 2026
6afc512
satisfy strict ESLint in OptimizationProvider trackEntryInteraction r…
akfreas May 25, 2026
a41b0fd
Tighten the two isContentfulOptimization predicates in the RN provide…
akfreas May 25, 2026
c5ada6a
Add the missing testID='preview-panel-scroll' to the PreviewPanel Scr…
akfreas May 25, 2026
c502e57
Apply prettier formatting to the Reflect.get type assertion on the cl…
akfreas May 25, 2026
d119016
Replace reset-all Alert.alert with inline confirmation view in RN pre…
akfreas May 17, 2026
54e44b4
Surface a preview-refresh-button in the RN preview panel and wire it …
akfreas May 19, 2026
d77c1f3
Scroll the audience-toggle row back into view between the deactivate …
akfreas May 25, 2026
bded3b6
Make scrollPanelToElement tolerate the StaleObjectException that the …
akfreas May 25, 2026
b7938ac
Sort preview-panel audiences by name only (drop the qualified-first t…
akfreas May 26, 2026
07d191c
Remove the scenario 3 scroll-between-taps workaround in PreviewPanelO…
akfreas May 26, 2026
433b355
Harden the Android UI Automator test infra against the UiAutomator-pl…
akfreas May 26, 2026
f624d9a
Remove the stale Zipline references from the Android SDK docs and Pro…
akfreas May 26, 2026
2466eee
Rewrite PreviewPanelOverridesTests.scrollPanelToElement to use UiObje…
akfreas May 27, 2026
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
166 changes: 166 additions & 0 deletions .github/workflows/main-pipeline.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ jobs:
e2e_web_sdk_react: ${{ steps.filter.outputs.e2e_web_sdk_react }}
e2e_react_web_sdk: ${{ steps.filter.outputs.e2e_react_web_sdk }}
e2e_react_native_android: ${{ steps.filter.outputs.e2e_react_native_android }}
e2e_android: ${{ steps.filter.outputs.e2e_android }}
e2e_ios: ${{ steps.filter.outputs.e2e_ios }}
steps:
- uses: namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1
Expand Down Expand Up @@ -126,11 +127,21 @@ jobs:
- 'package.json'
- 'pnpm-lock.yaml'
- '.github/workflows/main-pipeline.yaml'
# Android native implementation E2E coverage scope.
e2e_android:
- 'implementations/android-sdk/**'
- 'lib/mocks/**'
- 'packages/android/**'
- 'packages/universal/optimization-js-bridge/**'
- 'package.json'
- 'pnpm-lock.yaml'
- '.github/workflows/main-pipeline.yaml'
# iOS native implementation E2E coverage scope.
e2e_ios:
- 'implementations/ios-sdk/**'
- 'lib/mocks/**'
- 'packages/ios/**'
- 'packages/universal/optimization-js-bridge/**'
- 'package.json'
- 'pnpm-lock.yaml'
- '.github/workflows/main-pipeline.yaml'
Expand Down Expand Up @@ -659,6 +670,161 @@ jobs:
if-no-files-found: error
retention-days: 1

e2e-android-sdk:
name: 🤖 E2E Android Native
runs-on: namespace-profile-linux-16-vcpu-32-gb-ram-optimal
timeout-minutes: 45
needs: [setup, changes]
if: needs.changes.outputs.e2e_android == 'true'
env:
CI: 'true'
GRADLE_OPTS: >-
-Dorg.gradle.daemon=false -Dorg.gradle.parallel=true -Dorg.gradle.jvmargs=-Xmx4g
-Dkotlin.daemon.jvm.options=-Xmx2g
steps:
- uses: namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1

- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
with:
node-version-file: '.nvmrc'
package-manager-cache: false

- uses: pnpm/action-setup@903f9c1a6ebcba6cf41d87230be49611ac97822e # v6.0.3

- name: Set Android SDK environment variables
run: |
echo "ANDROID_SDK_ROOT=$HOME/.android/sdk" >> "$GITHUB_ENV"
echo "ANDROID_HOME=$HOME/.android/sdk" >> "$GITHUB_ENV"

- name: Prepare cache directories
run: |
mkdir -p "$HOME/.android/sdk" "$HOME/.android/avd" "$HOME/.android/cache"

- name: Set up caches (Namespace)
uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1.4.3
with:
cache: |
pnpm
gradle
path: |
~/.android/sdk
~/.android/avd
~/.android/cache

- name: Install system dependencies (Android emulator)
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
ca-certificates curl unzip zip git \
netcat-openbsd cpu-checker \
libgl1 libnss3 libx11-6 libx11-xcb1 libxcomposite1 libxdamage1 libxrandr2 libxtst6 \
libxi6 libxrender1 libxkbcommon0 libgbm1 libdbus-1-3 libdrm2 libpulse0
sudo apt-get install -y --no-install-recommends libasound2 || sudo apt-get install -y --no-install-recommends libasound2t64

- name: Verify KVM is available
run: |
if [ ! -e /dev/kvm ]; then
echo "/dev/kvm not found; Android hardware acceleration will not work." >&2
exit 1
fi
ls -l /dev/kvm
sudo kvm-ok || true

- name: Setup Java
uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0
with:
distribution: 'temurin'
java-version: '17'

- name: Setup Android SDK
uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699 # v4.0.1

- name: Install JS dependencies
run: pnpm install --prefer-offline --frozen-lockfile

- name: Build the JS bridge bundles
run: pnpm --filter @contentful/optimization-js-bridge build

- name: Build app and test APKs
working-directory: implementations/android-sdk
run: ./gradlew :app:assembleDebug :uitests:assembleDebug

- name: Start Mock Server
run: |
pnpm --dir lib/mocks serve > /tmp/mock-server.log 2>&1 &
echo $! > /tmp/mock-server.pid
for i in {1..60}; do
if nc -z localhost 8000 2>/dev/null; then
echo "Mock server is ready"
break
fi
echo "Waiting for mock server... ($i/60)"
sleep 1
done
if ! nc -z localhost 8000 2>/dev/null; then
echo "Mock server failed to start:"
cat /tmp/mock-server.log
exit 1
fi

- name: Run Android E2E Tests (emulator)
uses: reactivecircus/android-emulator-runner@e89f39f1abbbd05b1113a29cf4db69e7540cae5a # v2.37.0
with:
api-level: 35
arch: x86_64
target: google_apis
profile: pixel_7
avd-name: test
force-avd-creation: true
emulator-boot-timeout: 600
cores: 6
ram-size: 4096M
disk-size: 8G
disable-animations: true
emulator-options: -no-window -no-audio -no-boot-anim -gpu swiftshader_indirect
script: |
# The emulator-runner action invokes the script via `/usr/bin/sh -c`, not bash, so
# `set -o pipefail` would error out with "Illegal option -o pipefail" and terminate
# the script before any test ran. The grep-on-test-output checks below already
# detect instrumentation failures regardless of pipe-status propagation.
echo "Disabling animations..."
adb shell settings put global window_animation_scale 0
adb shell settings put global transition_animation_scale 0
adb shell settings put global animator_duration_scale 0
echo "Installing APKs..."
adb install -r implementations/android-sdk/app/build/outputs/apk/debug/app-debug.apk
adb install -r implementations/android-sdk/uitests/build/outputs/apk/debug/uitests-debug.apk
echo "Setting up adb reverse port forwarding..."
adb reverse tcp:8000 tcp:8000
sleep 3
adb shell "for i in 1 2 3 4 5 6 7 8 9 10; do nc -z localhost 8000 2>/dev/null && echo 'Mock server tunnel verified' && exit 0; sleep 1; done; echo 'WARNING: tunnel verification timed out'"
echo "Running UI Automator 2 E2E tests..."
adb shell am instrument -w com.contentful.optimization.uitests/androidx.test.runner.AndroidJUnitRunner 2>&1 | tee /tmp/test-output.log
grep -q "FAILURES" /tmp/test-output.log && { echo "::error::Android UI tests failed"; exit 1; } || true
grep -q "Process crashed" /tmp/test-output.log && { echo "::error::Test process crashed"; exit 1; } || true
grep -q "OK (" /tmp/test-output.log || { echo "::error::Android UI tests did not complete successfully (missing OK status)"; exit 1; }

- name: Upload logs on failure
if: failure()
run: |
echo "=== Mock Server Logs ==="
cat /tmp/mock-server.log || echo "No mock server logs found"

- name: Stop Mock Server
if: always()
run: |
kill $(cat /tmp/mock-server.pid) 2>/dev/null || true

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: always()
with:
name: ci-results-android-sdk
path: |
implementations/android-sdk/logs/
/tmp/mock-server.log
/tmp/test-output.log
retention-days: 7

e2e-ios-sdk-build:
name: 🍎 Build iOS UI Test Bundles
runs-on: namespace-profile-macos-apple-silicon-arm64-6-cpu-14-gb
Expand Down
42 changes: 36 additions & 6 deletions .github/workflows/notify-slack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ jobs:
)"

has_docs=false
has_guides=false
has_concepts=false
has_packages=false
changed_packages=""

Expand All @@ -49,12 +51,20 @@ jobs:
case "$file" in
documentation/drafts/*)
;;
documentation/guides/*)
has_docs=true
has_guides=true
;;
documentation/concepts/*)
has_docs=true
has_concepts=true
;;
documentation/*)
has_docs=true
;;
packages/ios/ios-jsc-bridge/*)
packages/universal/optimization-js-bridge/*)
has_packages=true
add_package "@contentful/optimization-ios-bridge"
add_package "@contentful/optimization-js-bridge"
;;
packages/node/node-sdk/*)
has_packages=true
Expand Down Expand Up @@ -103,14 +113,34 @@ jobs:
)"

if [ "$has_docs" = "true" ] && [ "$has_packages" = "true" ]; then
message_title="📝📦 Documentation and packages just landed"
message_body="Documentation has been updated, and package changes were merged. @TECH_WRITER@, fresh docs goodness just landed for your reading list!"
if [ "$has_guides" = "true" ] && [ "$has_concepts" = "true" ]; then
message_title="📚📦 Docs and packages just landed"
message_body="Guide, concept, and package updates all landed together. @TECH_WRITER@, there is fresh guide goodness in the mix for your reading list!"
elif [ "$has_guides" = "true" ]; then
message_title="🧭📦 Guides and packages just landed"
message_body="Fresh guide updates and package changes are live. @TECH_WRITER@, something new and useful just landed in your favorite corner of the docs!"
elif [ "$has_concepts" = "true" ]; then
message_title="💡📦 Concepts and packages just landed"
message_body="Concept docs and package updates moved forward together. A tidy little upgrade for readers and builders!"
else
message_title="📝📦 Docs and packages just landed"
message_body="Documentation and package updates were merged together. Better docs, fresher packages!"
fi
elif [ "$has_packages" = "true" ]; then
message_title="📦 Package changes just landed"
message_body="Package updates were merged. Fresh bits are ready for the next build!"
elif [ "$has_guides" = "true" ] && [ "$has_concepts" = "true" ]; then
message_title="📚 Guides and concepts just landed"
message_body="Guide and concept documentation both got updates. @TECH_WRITER@, fresh guide goodness just landed for your reading list!"
elif [ "$has_guides" = "true" ]; then
message_title="🧭 Guide documentation just landed"
message_body="Fresh guide updates are live. @TECH_WRITER@, something new and useful just landed in your favorite corner of the docs!"
elif [ "$has_concepts" = "true" ]; then
message_title="💡 Concept documentation just landed"
message_body="Concept docs got a little clearer today. Nice boost for the next reader!"
else
message_title="📝 Documentation just landed"
message_body="Documentation has been updated. @TECH_WRITER@, fresh docs goodness just landed for your reading list!"
message_body="A documentation update was merged. Small polish, better docs!"
fi

{
Expand Down Expand Up @@ -141,7 +171,7 @@ jobs:

if [[ "$message_body" == *"@TECH_WRITER@"* ]]; then
if [ -z "$SLACK_TECH_WRITER_ID" ]; then
echo "SLACK_TECH_WRITER_ID is required for documentation notifications." >&2
echo "SLACK_TECH_WRITER_ID is required for guide notifications." >&2
exit 1
fi

Expand Down
14 changes: 6 additions & 8 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,12 @@ local.properties
*.keystore
!debug.keystore
!**/gradle/wrapper/gradle-wrapper.jar
**/android-sdk/.gradle/
**/android-sdk/app/build/
**/android-sdk/uitests/build/
**/android-sdk/local.properties
**/android-sdk/logs/

# Android Native
triage-out
.gradle/
app/build/
uitests/build/
local.properties
logs/

# node.js
#
Expand Down Expand Up @@ -122,4 +120,4 @@ yarn-error.log
# Local environment configuration - commented out since we use safe defaults
# Uncomment this line if you need to override with local secrets
# env.config.ts

triage-out
3 changes: 2 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ pnpm-lock.yaml
**/android/.gradle/**
**/.bundle/**
**/node_modules/**
packages/ios/ContentfulOptimization/Sources/ContentfulOptimization/Resources/optimization-ios-bridge.umd.js
packages/ios/ContentfulOptimization/Sources/ContentfulOptimization/Resources/optimization-ios-bridge.umd.js
packages/android/ContentfulOptimization/src/main/assets/optimization-android-bridge.umd.js
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ React Native support is available through
[`@contentful/optimization-react-native`](./packages/react-native-sdk/README.md).

Native iOS work is also present in this repository as a pre-release Swift Package under
[`packages/ios`](./packages/ios/README.md), backed by the
[`@contentful/optimization-ios-bridge`](./packages/ios/ios-jsc-bridge/README.md) JavaScriptCore
[`packages/ios`](./packages/ios/README.md), backed by the shared
[`@contentful/optimization-js-bridge`](./packages/universal/optimization-js-bridge/README.md)
adapter and the [iOS reference app](./implementations/ios-sdk/README.md). Treat this surface as
alpha implementation work rather than a stable public native SDK.

Expand Down
Loading
Loading