feat(web-sdk_angular): Web SDK + Angular reference implementation [NT-3465] #1534
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
| name: Main Pipeline | |
| permissions: | |
| contents: read | |
| on: | |
| pull_request: | |
| push: | |
| branches: | |
| - main | |
| concurrency: | |
| group: main-pipeline-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| changes: | |
| name: 🔎 Detect Changes | |
| runs-on: namespace-profile-linux-8-vcpu-16-gb-ram-optimal | |
| outputs: | |
| build: ${{ steps.filter.outputs.build }} | |
| lock: ${{ steps.filter.outputs.lock }} | |
| unit: ${{ steps.filter.outputs.unit }} | |
| e2e_node_sdk: ${{ steps.filter.outputs.e2e_node_sdk }} | |
| e2e_node_sdk_web_sdk: ${{ steps.filter.outputs.e2e_node_sdk_web_sdk }} | |
| e2e_web_sdk: ${{ steps.filter.outputs.e2e_web_sdk }} | |
| 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 }} | |
| swift_package: ${{ steps.filter.outputs.swift_package }} | |
| android_library: ${{ steps.filter.outputs.android_library }} | |
| steps: | |
| - uses: namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1 | |
| - uses: dorny/paths-filter@fbd0ab8f3e69293af611ebaee6363fc25e6d187d # v4.0.1 | |
| id: filter | |
| with: | |
| predicate-quantifier: every | |
| # Intentional CI policy: | |
| # - Markdown plus docs/documentation changes do not trigger build, unit, native, or E2E jobs. | |
| # - Implementation E2E jobs run only for implementations relevant to changed paths. | |
| # - Skipped E2E jobs due to unmatched filters are expected behavior. | |
| # - Keep this mapping aligned with CONTRIBUTING.md ("E2E Coverage and Environment"). | |
| filters: | | |
| build: | |
| - '{lib/**,packages/**,implementations/**,package.json,pnpm-lock.yaml,pnpm-workspace.yaml,tsconfig*.json,.github/workflows/main-pipeline.yaml}' | |
| - '!**/*.@(md|mdx|markdown)' | |
| - '!{docs/**,documentation/**,**/docs/**,**/documentation/**}' | |
| lock: | |
| - 'pnpm-lock.yaml' | |
| unit: | |
| - '{lib/**,packages/**,**/rstest.config.ts,package.json,pnpm-lock.yaml,pnpm-workspace.yaml,.github/workflows/main-pipeline.yaml}' | |
| - '!**/*.@(md|mdx|markdown)' | |
| - '!{docs/**,documentation/**,**/docs/**,**/documentation/**}' | |
| # Node SSR Only implementation E2E coverage scope. | |
| e2e_node_sdk: | |
| - '{implementations/node-sdk/**,lib/**,packages/node/node-sdk/**,packages/universal/core-sdk/**,packages/universal/api-client/**,packages/universal/api-schemas/**,package.json,pnpm-lock.yaml,.github/workflows/main-pipeline.yaml}' | |
| - '!**/*.@(md|mdx|markdown)' | |
| - '!{docs/**,documentation/**,**/docs/**,**/documentation/**}' | |
| # Node SSR + Web Vanilla implementation E2E coverage scope. | |
| e2e_node_sdk_web_sdk: | |
| - '{implementations/node-sdk+web-sdk/**,lib/**,packages/node/node-sdk/**,packages/web/web-sdk/**,packages/web/preview-panel/**,packages/universal/core-sdk/**,packages/universal/api-client/**,packages/universal/api-schemas/**,package.json,pnpm-lock.yaml,.github/workflows/main-pipeline.yaml}' | |
| - '!**/*.@(md|mdx|markdown)' | |
| - '!{docs/**,documentation/**,**/docs/**,**/documentation/**}' | |
| # Web Vanilla implementation E2E coverage scope. | |
| e2e_web_sdk: | |
| - '{implementations/web-sdk/**,lib/**,packages/web/web-sdk/**,packages/web/preview-panel/**,packages/universal/core-sdk/**,packages/universal/api-client/**,packages/universal/api-schemas/**,package.json,pnpm-lock.yaml,.github/workflows/main-pipeline.yaml}' | |
| - '!**/*.@(md|mdx|markdown)' | |
| - '!{docs/**,documentation/**,**/docs/**,**/documentation/**}' | |
| # React + Web SDK implementation E2E coverage scope. | |
| e2e_web_sdk_react: | |
| - '{implementations/web-sdk_react/**,lib/**,packages/web/web-sdk/**,packages/web/preview-panel/**,packages/universal/core-sdk/**,packages/universal/api-client/**,packages/universal/api-schemas/**,package.json,pnpm-lock.yaml,.github/workflows/main-pipeline.yaml}' | |
| - '!**/*.@(md|mdx|markdown)' | |
| - '!{docs/**,documentation/**,**/docs/**,**/documentation/**}' | |
| # React Web SDK (optimization-react-web) implementation E2E coverage scope. | |
| e2e_react_web_sdk: | |
| - '{implementations/react-web-sdk/**,lib/**,packages/web/frameworks/react-web-sdk/**,packages/web/web-sdk/**,packages/web/preview-panel/**,packages/universal/core-sdk/**,packages/universal/api-client/**,packages/universal/api-schemas/**,package.json,pnpm-lock.yaml,.github/workflows/main-pipeline.yaml}' | |
| - '!**/*.@(md|mdx|markdown)' | |
| - '!{docs/**,documentation/**,**/docs/**,**/documentation/**}' | |
| # React Native Android implementation E2E coverage scope. | |
| e2e_react_native_android: | |
| - '{implementations/react-native-sdk/**,lib/**,packages/react-native-sdk/**,packages/universal/core-sdk/**,packages/universal/api-client/**,packages/universal/api-schemas/**,package.json,pnpm-lock.yaml,.github/workflows/main-pipeline.yaml}' | |
| - '!**/*.@(md|mdx|markdown)' | |
| - '!{docs/**,documentation/**,**/docs/**,**/documentation/**}' | |
| # 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}' | |
| - '!**/*.@(md|mdx|markdown)' | |
| - '!{docs/**,documentation/**,**/docs/**,**/documentation/**}' | |
| # 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}' | |
| - '!**/*.@(md|mdx|markdown)' | |
| - '!{docs/**,documentation/**,**/docs/**,**/documentation/**}' | |
| # Swift Package build/test scope (the package published to optimization.swift). | |
| swift_package: | |
| - '{packages/ios/ContentfulOptimization/**,packages/universal/optimization-js-bridge/**,package.json,pnpm-lock.yaml,.github/workflows/main-pipeline.yaml}' | |
| - '!**/*.@(md|mdx|markdown)' | |
| - '!{docs/**,documentation/**,**/docs/**,**/documentation/**}' | |
| # Android library scope (the AAR published to Maven Central). | |
| android_library: | |
| - '{packages/android/ContentfulOptimization/**,packages/universal/optimization-js-bridge/**,package.json,pnpm-lock.yaml,.github/workflows/main-pipeline.yaml}' | |
| - '!**/*.@(md|mdx|markdown)' | |
| - '!{docs/**,documentation/**,**/docs/**,**/documentation/**}' | |
| setup: | |
| name: 🛠️ pnpm install | |
| runs-on: namespace-profile-linux-8-vcpu-16-gb-ram-optimal | |
| timeout-minutes: 15 | |
| 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 up pnpm cache (Namespace) | |
| uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1.4.3 | |
| with: | |
| cache: pnpm | |
| - run: pnpm install --prefer-offline --frozen-lockfile | |
| license-check: | |
| name: 📄 License Check | |
| runs-on: namespace-profile-linux-8-vcpu-16-gb-ram-optimal | |
| needs: [setup, changes] | |
| if: needs.changes.outputs.lock == 'true' | |
| timeout-minutes: 15 | |
| 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 up pnpm cache (Namespace) | |
| uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1.4.3 | |
| with: | |
| cache: pnpm | |
| - run: pnpm install --prefer-offline --frozen-lockfile | |
| - run: | | |
| echo "In case of error, please see ./CONTRIBUTING.md" | |
| pnpx license-checker \ | |
| --summary \ | |
| --production \ | |
| --relativeLicensePath \ | |
| --onlyAllow 'MIT;Apache-2.0;ISC;BSD-3-Clause;BSD-2-Clause;MIT*;Apache 2.0;Unlicense;Unlicensed;:CC0-1.0;CC-BY-4.0;WTFPL;0BSD;UNLICENSED;Python-2.0;MPL-2.0;CC-BY-3.0;CC0-1.0' | |
| format: | |
| name: 🎨 Format Check | |
| runs-on: namespace-profile-linux-8-vcpu-16-gb-ram-optimal | |
| needs: setup | |
| timeout-minutes: 15 | |
| 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 | |
| - uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1.4.3 | |
| with: | |
| cache: pnpm | |
| - run: pnpm install --prefer-offline --frozen-lockfile | |
| - run: pnpm format:check | |
| build: | |
| name: 📦 Build | |
| runs-on: namespace-profile-linux-8-vcpu-16-gb-ram-optimal | |
| timeout-minutes: 15 | |
| needs: [setup, changes] | |
| if: needs.changes.outputs.build == 'true' | |
| 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 | |
| - uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1.4.3 | |
| with: | |
| cache: pnpm | |
| - run: pnpm install --prefer-offline --frozen-lockfile | |
| - run: pnpm build:ci | |
| - run: pnpm size:check | |
| - name: Pack SDK tarballs for implementations | |
| run: pnpm run pack:pkgs | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: sdk-package-tarballs | |
| path: pkgs/*.tgz | |
| if-no-files-found: error | |
| retention-days: 1 | |
| type-check: | |
| name: 🔷 Type Check | |
| runs-on: namespace-profile-linux-8-vcpu-16-gb-ram-optimal | |
| needs: [setup, changes] | |
| if: needs.changes.outputs.build == 'true' | |
| timeout-minutes: 15 | |
| 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 | |
| - uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1.4.3 | |
| with: | |
| cache: pnpm | |
| - run: pnpm install --prefer-offline --frozen-lockfile | |
| - run: pnpm typecheck | |
| lint: | |
| name: 🎨 Lint | |
| runs-on: namespace-profile-linux-8-vcpu-16-gb-ram-optimal | |
| needs: [setup, changes, build] | |
| if: >- | |
| always() && needs.changes.outputs.build == 'true' && needs.setup.result == 'success' && | |
| (needs.build.result == 'success' || needs.build.result == 'skipped') | |
| timeout-minutes: 15 | |
| 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 | |
| - uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1.4.3 | |
| with: | |
| cache: pnpm | |
| - run: pnpm install --prefer-offline --frozen-lockfile | |
| - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| if: needs.build.result == 'success' | |
| with: | |
| name: sdk-package-tarballs | |
| path: pkgs | |
| - run: pnpm lint | |
| - if: needs.build.result == 'skipped' | |
| run: pnpm build:pkgs | |
| - run: pnpm store prune | |
| - run: pnpm run implementation:install -- --no-frozen-lockfile | |
| - run: pnpm implementation:lint | |
| test-unit: | |
| name: 🧪 Test (${{ matrix.package }}) | |
| runs-on: namespace-profile-linux-8-vcpu-16-gb-ram-optimal | |
| timeout-minutes: 15 | |
| needs: [setup, changes] | |
| if: needs.changes.outputs.unit == 'true' | |
| strategy: | |
| fail-fast: true | |
| matrix: | |
| include: | |
| - package: '@contentful/optimization-api-schemas' | |
| - package: '@contentful/optimization-api-client' | |
| - package: '@contentful/optimization-core' | |
| - package: '@contentful/optimization-node' | |
| - package: '@contentful/optimization-web' | |
| - package: '@contentful/optimization-web-preview-panel' | |
| - package: '@contentful/optimization-react-native' | |
| 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 | |
| - uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1.4.3 | |
| with: | |
| cache: pnpm | |
| - run: pnpm install --prefer-offline --frozen-lockfile | |
| - run: pnpm --filter ${{ matrix.package }} test:unit | |
| test-android-sdk-unit: | |
| name: 🤖 Test Android SDK (JVM unit) | |
| runs-on: namespace-profile-linux-8-vcpu-16-gb-ram-optimal | |
| timeout-minutes: 15 | |
| needs: [setup, changes] | |
| if: needs.changes.outputs.e2e_android == 'true' | |
| env: | |
| 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/cache" | |
| - name: Set up caches (Namespace) | |
| uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1.4.3 | |
| with: | |
| cache: | | |
| pnpm | |
| gradle | |
| path: | | |
| ~/.android/sdk | |
| ~/.android/cache | |
| - 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: Run Android SDK JVM unit tests | |
| working-directory: implementations/android-sdk | |
| run: ./gradlew :ContentfulOptimization:testDebugUnitTest | |
| - name: Upload test reports on failure | |
| if: failure() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: ci-results-android-sdk-unit | |
| path: packages/android/ContentfulOptimization/build/reports/tests/ | |
| retention-days: 7 | |
| e2e-node-sdk: | |
| name: 🖥️ E2E Node SSR Only | |
| runs-on: namespace-profile-linux-8-vcpu-16-gb-ram-optimal | |
| timeout-minutes: 15 | |
| needs: [setup, changes, build] | |
| if: needs.changes.outputs.e2e_node_sdk == 'true' | |
| steps: | |
| - uses: namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1 | |
| - name: Create .env from .env.example | |
| run: cp implementations/node-sdk/.env.example implementations/node-sdk/.env | |
| - 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 up caches (Namespace) | |
| uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1.4.3 | |
| with: | |
| cache: | | |
| pnpm | |
| playwright | |
| apt | |
| - run: pnpm install --prefer-offline --frozen-lockfile | |
| - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: sdk-package-tarballs | |
| path: pkgs | |
| - run: pnpm store prune | |
| - run: pnpm run implementation:node-sdk -- implementation:install -- --no-frozen-lockfile | |
| - run: pnpm run implementation:node-sdk -- implementation:playwright:install -- --with-deps | |
| - run: pnpm run implementation:node-sdk -- implementation:test:e2e:run | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| if: ${{ !cancelled() }} | |
| with: | |
| name: ci-results-node-sdk | |
| path: | | |
| ./implementations/node-sdk/playwright-report/ | |
| ./implementations/node-sdk/test-results/ | |
| retention-days: 1 | |
| e2e-node-sdk-web-sdk: | |
| name: 🖥️ E2E Node SSR + Web Vanilla | |
| runs-on: namespace-profile-linux-8-vcpu-16-gb-ram-optimal | |
| timeout-minutes: 15 | |
| needs: [setup, changes, build] | |
| if: needs.changes.outputs.e2e_node_sdk_web_sdk == 'true' | |
| steps: | |
| - uses: namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1 | |
| - name: Create .env from .env.example | |
| run: > | |
| cp implementations/node-sdk+web-sdk/.env.example implementations/node-sdk+web-sdk/.env | |
| - 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 up caches (Namespace) | |
| uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1.4.3 | |
| with: | |
| cache: | | |
| pnpm | |
| playwright | |
| apt | |
| - run: pnpm install --prefer-offline --frozen-lockfile | |
| - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: sdk-package-tarballs | |
| path: pkgs | |
| - run: pnpm store prune | |
| - run: | |
| pnpm run implementation:node-sdk+web-sdk -- implementation:install -- --no-frozen-lockfile | |
| - run: | |
| pnpm run implementation:node-sdk+web-sdk -- implementation:playwright:install -- | |
| --with-deps | |
| - run: pnpm run implementation:node-sdk+web-sdk -- implementation:test:e2e:run | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| if: ${{ !cancelled() }} | |
| with: | |
| name: ci-results-node-sdk+web-sdk | |
| path: | | |
| ./implementations/node-sdk+web-sdk/playwright-report/ | |
| ./implementations/node-sdk+web-sdk/test-results/ | |
| retention-days: 1 | |
| e2e-web-sdk: | |
| name: 🖥️ E2E Web Vanilla | |
| runs-on: namespace-profile-linux-8-vcpu-16-gb-ram-optimal | |
| timeout-minutes: 15 | |
| needs: [setup, changes, build] | |
| if: needs.changes.outputs.e2e_web_sdk == 'true' | |
| steps: | |
| - uses: docker/setup-compose-action@8cccb8c14b6500aaffebff1aa49c502c34d2e5e6 # v2.1.0 | |
| - uses: namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1 | |
| - name: Create .env from .env.example | |
| run: cp implementations/web-sdk/.env.example implementations/web-sdk/.env | |
| - 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 up caches (Namespace) | |
| uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1.4.3 | |
| with: | |
| cache: | | |
| pnpm | |
| playwright | |
| apt | |
| - run: pnpm install --prefer-offline --frozen-lockfile | |
| - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: sdk-package-tarballs | |
| path: pkgs | |
| - run: pnpm store prune | |
| - run: pnpm run implementation:web-sdk -- implementation:install -- --no-frozen-lockfile | |
| - run: pnpm run implementation:web-sdk -- implementation:playwright:install -- --with-deps | |
| - run: pnpm run implementation:web-sdk -- implementation:test:e2e:run | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| if: ${{ !cancelled() }} | |
| with: | |
| name: ci-results-web-sdk | |
| path: | | |
| ./implementations/web-sdk/playwright-report/ | |
| ./implementations/web-sdk/test-results/ | |
| retention-days: 1 | |
| e2e-web-sdk_react: | |
| name: 🖥️ E2E React + Web SDK | |
| runs-on: namespace-profile-linux-8-vcpu-16-gb-ram-optimal | |
| timeout-minutes: 15 | |
| needs: [setup, changes, build] | |
| if: needs.changes.outputs.e2e_web_sdk_react == 'true' | |
| steps: | |
| - uses: namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1 | |
| - name: Create .env from .env.example | |
| run: cp implementations/web-sdk_react/.env.example implementations/web-sdk_react/.env | |
| - 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 up caches (Namespace) | |
| uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1.4.3 | |
| with: | |
| cache: | | |
| pnpm | |
| playwright | |
| apt | |
| - run: pnpm install --prefer-offline --frozen-lockfile | |
| - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: sdk-package-tarballs | |
| path: pkgs | |
| - run: pnpm store prune | |
| - run: pnpm run implementation:web-sdk_react -- implementation:install -- --no-frozen-lockfile | |
| - run: | |
| pnpm run implementation:web-sdk_react -- implementation:playwright:install -- --with-deps | |
| - run: pnpm run implementation:web-sdk_react -- implementation:test:e2e:run | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| if: ${{ !cancelled() }} | |
| with: | |
| name: ci-results-web-sdk_react | |
| path: | | |
| ./implementations/web-sdk_react/playwright-report/ | |
| ./implementations/web-sdk_react/test-results/ | |
| retention-days: 1 | |
| e2e-react-web-sdk: | |
| name: ⚛️ E2E React Web SDK | |
| runs-on: namespace-profile-linux-8-vcpu-16-gb-ram-optimal | |
| timeout-minutes: 15 | |
| needs: [setup, changes, build] | |
| if: needs.changes.outputs.e2e_react_web_sdk == 'true' | |
| steps: | |
| - uses: namespacelabs/nscloud-checkout-action@938f5d2d403d6224d9a0c0dc559b1dae09c2ede4 # v8.1.1 | |
| - name: Create .env from .env.example | |
| run: cp implementations/react-web-sdk/.env.example implementations/react-web-sdk/.env | |
| - 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 up caches (Namespace) | |
| uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1.4.3 | |
| with: | |
| cache: | | |
| pnpm | |
| playwright | |
| apt | |
| - run: pnpm install --prefer-offline --frozen-lockfile | |
| - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: sdk-package-tarballs | |
| path: pkgs | |
| - run: pnpm store prune | |
| - run: pnpm run implementation:react-web-sdk -- implementation:install -- --no-frozen-lockfile | |
| - run: | |
| pnpm run implementation:react-web-sdk -- implementation:playwright:install -- --with-deps | |
| - run: pnpm run implementation:react-web-sdk -- implementation:test:e2e:run | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| if: ${{ !cancelled() }} | |
| with: | |
| name: ci-results-react-web-sdk | |
| path: | | |
| ./implementations/react-web-sdk/playwright-report/ | |
| ./implementations/react-web-sdk/test-results/ | |
| retention-days: 1 | |
| e2e-react-native-android-build: | |
| name: 📱 Build React Native Android APK | |
| runs-on: namespace-profile-linux-16-vcpu-32-gb-ram-optimal | |
| timeout-minutes: 30 | |
| needs: [setup, changes, build] | |
| if: needs.changes.outputs.e2e_react_native_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/cache | |
| - 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: Download SDK package tarballs | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: sdk-package-tarballs | |
| path: pkgs | |
| - name: Prune pnpm store metadata cache | |
| run: pnpm store prune | |
| - name: Install React Native implementation dependencies | |
| run: > | |
| pnpm run implementation:react-native-sdk -- implementation:install -- --no-frozen-lockfile | |
| - name: Create .env from .env.example | |
| run: cp implementations/react-native-sdk/.env.example implementations/react-native-sdk/.env | |
| - name: Build Android app (Detox release) | |
| run: pnpm run implementation:react-native-sdk -- test:e2e:android:build:release | |
| - name: Upload Detox APKs | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: detox-android-apks | |
| path: implementations/react-native-sdk/android/app/build/outputs/apk/ | |
| if-no-files-found: error | |
| retention-days: 1 | |
| e2e-android-sdk-build: | |
| name: 🤖 Build Android APKs | |
| runs-on: namespace-profile-linux-16-vcpu-32-gb-ram-optimal | |
| timeout-minutes: 30 | |
| 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/cache" | |
| - name: Set up caches (Namespace) | |
| uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1.4.3 | |
| with: | |
| cache: | | |
| pnpm | |
| gradle | |
| path: | | |
| ~/.android/sdk | |
| ~/.android/cache | |
| - 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 Compose and Views APKs | |
| working-directory: implementations/android-sdk | |
| run: ./gradlew :compose:assembleDebug :views:assembleDebug | |
| - name: Collect APK artifacts at stable paths | |
| run: | | |
| mkdir -p android-apks | |
| cp implementations/android-sdk/compose/build/outputs/apk/debug/compose-debug.apk android-apks/ | |
| cp implementations/android-sdk/views/build/outputs/apk/debug/views-debug.apk android-apks/ | |
| ls -la android-apks/ | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: android-apks | |
| path: android-apks/ | |
| if-no-files-found: error | |
| retention-days: 1 | |
| e2e-android-maestro: | |
| name: 🤖 E2E Android Maestro (${{ matrix.app }}) | |
| runs-on: ubuntu-latest | |
| timeout-minutes: 45 | |
| needs: [setup, changes, e2e-android-sdk-build] | |
| if: needs.changes.outputs.e2e_android == 'true' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - app: compose | |
| package: com.contentful.optimization.app | |
| apk: compose-debug.apk | |
| - app: views | |
| package: com.contentful.optimization.app.views | |
| apk: views-debug.apk | |
| env: | |
| CI: 'true' | |
| MAESTRO_VERSION: '2.6.0' | |
| MAESTRO_ITERATIONS: '1' | |
| API_LEVEL: '35' | |
| ARCH: x86_64 | |
| TARGET: aosp_atd | |
| PROFILE: pixel_7 | |
| EMULATOR_OPTIONS: | |
| -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - 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: Enable KVM | |
| run: | | |
| echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules | |
| sudo udevadm control --reload-rules | |
| sudo udevadm trigger --name-match=kvm | |
| if [ ! -e /dev/kvm ]; then | |
| echo "/dev/kvm not found; Android hardware acceleration unavailable." >&2 | |
| exit 1 | |
| fi | |
| ls -l /dev/kvm | |
| - 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 | |
| - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: android-apks | |
| path: android-apks | |
| - name: AVD cache | |
| uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 | |
| id: avd-cache | |
| with: | |
| path: | | |
| ~/.android/avd/* | |
| ~/.android/adb* | |
| key: avd-${{ env.API_LEVEL }}-${{ env.ARCH }}-${{ env.TARGET }}-${{ env.PROFILE }} | |
| - name: Create AVD and generate snapshot for caching | |
| if: steps.avd-cache.outputs.cache-hit != 'true' | |
| uses: reactivecircus/android-emulator-runner@e89f39f1abbbd05b1113a29cf4db69e7540cae5a # v2.37.0 | |
| with: | |
| api-level: ${{ env.API_LEVEL }} | |
| arch: ${{ env.ARCH }} | |
| target: ${{ env.TARGET }} | |
| profile: ${{ env.PROFILE }} | |
| avd-name: test | |
| force-avd-creation: false | |
| emulator-boot-timeout: 600 | |
| cores: 3 | |
| ram-size: 6144M | |
| disk-size: 8G | |
| disable-animations: false | |
| emulator-options: ${{ env.EMULATOR_OPTIONS }} | |
| script: echo "Generated AVD snapshot for caching." | |
| - 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 Maestro E2E (emulator, warm boot from cached snapshot) | |
| uses: reactivecircus/android-emulator-runner@e89f39f1abbbd05b1113a29cf4db69e7540cae5a # v2.37.0 | |
| with: | |
| api-level: ${{ env.API_LEVEL }} | |
| arch: ${{ env.ARCH }} | |
| target: ${{ env.TARGET }} | |
| profile: ${{ env.PROFILE }} | |
| avd-name: test | |
| force-avd-creation: false | |
| emulator-boot-timeout: 600 | |
| cores: 3 | |
| ram-size: 6144M | |
| disk-size: 8G | |
| disable-animations: true | |
| emulator-options: ${{ env.EMULATOR_OPTIONS }} -no-snapshot-save | |
| script: | | |
| echo "Installing Maestro CLI ($MAESTRO_VERSION)..." | |
| curl -Ls "https://get.maestro.mobile.dev" | bash | |
| echo "Installing ${{ matrix.app }} APK (${{ matrix.apk }})..." | |
| echo "Removing any cached install of ${{ matrix.package }} to avoid signature mismatches from the cached AVD..." | |
| adb uninstall ${{ matrix.package }} >/dev/null 2>&1 || true | |
| adb install -r android-apks/${{ matrix.apk }} | |
| echo "Setting up adb reverse (harmless localhost fallback; the apps use 10.0.2.2)..." | |
| adb reverse tcp:8000 tcp:8000 || echo "::warning::adb reverse failed; apps use 10.0.2.2 so continuing" | |
| sleep 3 | |
| echo "Verifying the host mock is reachable from the emulator via 10.0.2.2..." | |
| adb shell "for i in 1 2 3 4 5 6 7 8 9 10; do toybox nc -z 10.0.2.2 8000 2>/dev/null && echo 'Mock server reachable via 10.0.2.2' && exit 0; sleep 1; done; echo '::warning::mock 10.0.2.2 verification timed out'" | |
| echo "Starting background logcat capture (diagnostics on failure)..." | |
| adb logcat -c || true | |
| nohup adb logcat -v threadtime > /tmp/android-logcat.txt 2>&1 & | |
| echo "Running Maestro flows for ${{ matrix.package }} ($MAESTRO_ITERATIONS iteration(s); only failed flows are retried)..." | |
| bash implementations/android-sdk/scripts/ci-maestro-run.sh ${{ matrix.package }} | |
| - name: Print debug context on failure | |
| if: failure() | |
| run: | | |
| echo "=== Mock Server Logs ===" | |
| cat /tmp/mock-server.log || echo "No mock server logs found" | |
| echo "=== Maestro test output tree (~/.maestro/tests) ===" | |
| ls -la "$HOME/.maestro/tests" 2>/dev/null || echo "No Maestro test output found" | |
| echo "=== logcat ANR / crash lines ===" | |
| grep -nE "ANR in|FATAL EXCEPTION|not responding|lowmemorykiller|Process .* died|Low on memory" /tmp/android-logcat.txt 2>/dev/null | head -60 || echo "No logcat captured" | |
| - 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-maestro-${{ matrix.app }} | |
| path: | | |
| ~/.maestro/tests/ | |
| /tmp/mock-server.log | |
| /tmp/android-logcat.txt | |
| 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 | |
| timeout-minutes: 30 | |
| needs: [setup, changes] | |
| if: needs.changes.outputs.e2e_ios == 'true' | |
| env: | |
| DERIVED_DATA: /tmp/optimization-ios-derived-data | |
| 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: Install XcodeGen and xcbeautify | |
| run: brew install xcodegen xcbeautify | |
| - name: Set up caches (Namespace) | |
| uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1.4.3 | |
| with: | |
| cache: pnpm | |
| path: | | |
| ~/Library/Caches/org.swift.swiftpm | |
| - name: Show toolchain | |
| run: | | |
| xcodebuild -version | |
| xcrun simctl list runtimes | head | |
| - run: pnpm install --prefer-offline --frozen-lockfile | |
| - name: Build the JS bridge | |
| run: pnpm run ios:bridge | |
| - name: Build iOS UI test bundles (SwiftUI + UIKit) | |
| run: pnpm run implementation:ios-sdk -- test:e2e:ios:build:release | |
| - name: Stage Build/Products for artifact | |
| run: | | |
| mkdir -p /tmp/ios-artifact | |
| cp -R "$DERIVED_DATA/Build/Products" /tmp/ios-artifact/Products | |
| ls /tmp/ios-artifact/Products/*.xctestrun | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: ios-uitest-bundles | |
| path: /tmp/ios-artifact/ | |
| if-no-files-found: error | |
| retention-days: 1 | |
| swift-package-build: | |
| name: 🍎 Swift Package Build & Test | |
| runs-on: namespace-profile-macos-apple-silicon-arm64-6-cpu-14-gb | |
| timeout-minutes: 30 | |
| needs: [setup, changes] | |
| if: needs.changes.outputs.swift_package == 'true' | |
| 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 up caches (Namespace) | |
| uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1.4.3 | |
| with: | |
| cache: pnpm | |
| path: | | |
| ~/Library/Caches/org.swift.swiftpm | |
| - name: Show toolchain | |
| run: | | |
| swift --version | |
| xcodebuild -version | |
| - run: pnpm install --prefer-offline --frozen-lockfile | |
| - name: Build the JS bridge | |
| run: pnpm run ios:bridge | |
| - name: Swift build | |
| run: swift build --package-path packages/ios/ContentfulOptimization | |
| - name: Swift test | |
| run: swift test --package-path packages/ios/ContentfulOptimization | |
| android-library-build: | |
| name: 🤖 Android Library Build & Publish-Local Smoke | |
| runs-on: namespace-profile-linux-8-vcpu-16-gb-ram-optimal | |
| timeout-minutes: 30 | |
| needs: [setup, changes] | |
| if: needs.changes.outputs.android_library == 'true' | |
| 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" | |
| - name: Set up caches (Namespace) | |
| uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1.4.3 | |
| with: | |
| cache: pnpm | |
| path: | | |
| ~/.android/sdk | |
| ~/.gradle/caches | |
| ~/.gradle/wrapper | |
| - run: pnpm install --prefer-offline --frozen-lockfile | |
| - name: Build the JS bridge | |
| run: pnpm --filter @contentful/optimization-js-bridge build | |
| - name: Set up JDK 17 | |
| uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5.2.0 | |
| with: | |
| distribution: temurin | |
| java-version: '17' | |
| - name: Set up Android SDK | |
| uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699 # v4.0.1 | |
| # Smoke-test packaging: assemble the AAR + sources/javadoc/POM the release would publish | |
| # (no Central access, no signing) so packaging breaks are caught on PRs. | |
| - name: Verify Maven publishing assembles | |
| working-directory: packages/android/ContentfulOptimization | |
| run: | | |
| ./gradlew publishToMavenLocal -Pcontentful.optimization.version=0.0.0-ci \ | |
| -Pcontentful.optimization.thirdPartyNoticesFile=THIRD_PARTY_NOTICES.txt \ | |
| --no-configuration-cache --no-daemon --console=plain | |
| e2e-react-native-android: | |
| name: 📱 E2E React Native Android (shard ${{ matrix.shard }}/2) | |
| runs-on: namespace-profile-linux-16-vcpu-32-gb-ram-optimal | |
| timeout-minutes: 45 | |
| needs: [setup, changes, e2e-react-native-android-build] | |
| if: needs.changes.outputs.e2e_react_native_android == 'true' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| shard: [1, 2] | |
| env: | |
| DETOX_AVD_NAME: test | |
| CI: 'true' | |
| 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: | | |
| apt | |
| pnpm | |
| path: | | |
| ~/.android/sdk | |
| ~/.android/avd | |
| ~/.android/cache | |
| - name: Install system dependencies (Android emulator + React Native) | |
| run: | | |
| sudo apt-get update | |
| sudo apt-get install -y --no-install-recommends \ | |
| ca-certificates curl unzip zip git \ | |
| python3 python3-pip build-essential \ | |
| netcat-openbsd watchman \ | |
| 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: Download SDK package tarballs | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: sdk-package-tarballs | |
| path: pkgs | |
| - name: Prune pnpm store metadata cache | |
| run: pnpm store prune | |
| - name: Install React Native implementation dependencies | |
| run: > | |
| pnpm run implementation:react-native-sdk -- implementation:install -- --no-frozen-lockfile | |
| - name: Create .env from .env.example | |
| run: cp implementations/react-native-sdk/.env.example implementations/react-native-sdk/.env | |
| - name: Download pre-built Detox APKs | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: detox-android-apks | |
| path: implementations/react-native-sdk/android/app/build/outputs/apk/ | |
| - 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: 36 | |
| arch: x86_64 | |
| target: google_apis | |
| profile: pixel_tablet | |
| 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: | | |
| echo "Verifying JAVA_HOME: $JAVA_HOME" | |
| java -version | |
| echo "Setting up adb reverse port forwarding..." | |
| adb reverse tcp:8000 tcp:8000 | |
| echo "Running E2E tests..." | |
| pnpm run implementation:react-native-sdk -- test:e2e:android:run:release -- --retries 1 --shard=${{ matrix.shard }}/2 | |
| - 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-react-native-android-shard-${{ matrix.shard }} | |
| path: | | |
| implementations/react-native-sdk/.detox/ | |
| /tmp/mock-server.log | |
| retention-days: 7 | |
| e2e-ios-sdk: | |
| name: 🍎 E2E iOS UI (${{ matrix.scheme }}) | |
| runs-on: namespace-profile-macos-apple-silicon-arm64-6-cpu-14-gb | |
| timeout-minutes: 45 | |
| needs: [setup, changes, e2e-ios-sdk-build] | |
| if: needs.changes.outputs.e2e_ios == 'true' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| include: | |
| - scheme: SwiftUI | |
| - scheme: UIKit | |
| env: | |
| DERIVED_DATA: /tmp/optimization-ios-derived-data | |
| IOS_SCHEME: ${{ matrix.scheme }} | |
| IOS_SIM_NAME: 'iPhone 16' | |
| IOS_SIM_OS: 'latest' | |
| IOS_ONLY_TESTING: OptimizationAppUITests${{ matrix.scheme }}/PreviewPanelTests | |
| 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: Install xcbeautify | |
| run: brew install xcbeautify | |
| - uses: namespacelabs/nscloud-cache-action@15799a6b54e5765f85b2aac25b3f0df43ed571c0 # v1.4.3 | |
| with: | |
| cache: pnpm | |
| - run: pnpm install --prefer-offline --frozen-lockfile | |
| - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: ios-uitest-bundles | |
| path: /tmp/ios-artifact/ | |
| - name: Reconstruct DerivedData layout at stable path | |
| run: | | |
| mkdir -p "$DERIVED_DATA/Build" | |
| mv /tmp/ios-artifact/Products "$DERIVED_DATA/Build/Products" | |
| ls "$DERIVED_DATA/Build/Products" | |
| - name: Boot iOS Simulator | |
| run: | | |
| DEVICE_UDID=$(xcrun simctl create "ci-${{ matrix.scheme }}" "$IOS_SIM_NAME") | |
| echo "DEVICE_UDID=$DEVICE_UDID" >> "$GITHUB_ENV" | |
| xcrun simctl boot "$DEVICE_UDID" | |
| xcrun simctl bootstatus "$DEVICE_UDID" -b | |
| - 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: Verify built xctestrun exists for ${{ matrix.scheme }} | |
| shell: 'bash -eo pipefail {0}' | |
| run: | | |
| shopt -s nullglob | |
| matches=("$DERIVED_DATA"/Build/Products/OptimizationApp"$IOS_SCHEME"_*.xctestrun) | |
| if [ ${#matches[@]} -eq 0 ]; then | |
| echo "No xctestrun found for scheme $IOS_SCHEME under $DERIVED_DATA/Build/Products/" >&2 | |
| ls -la "$DERIVED_DATA/Build/Products/" || true | |
| exit 1 | |
| fi | |
| printf 'Found xctestrun: %s\n' "${matches[@]}" | |
| - name: Run iOS UI tests (${{ matrix.scheme }}) | |
| shell: 'bash -eo pipefail {0}' | |
| run: pnpm run implementation:ios-sdk -- test:e2e:ios:run:release | |
| - 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: ${{ !cancelled() }} | |
| with: | |
| name: ci-results-ios-${{ matrix.scheme }} | |
| path: | | |
| /tmp/optimization-ios-derived-data/Test-*.xcresult | |
| /tmp/mock-server.log | |
| retention-days: 7 |