Confirm Identification Screen + animation json dist setup #1275
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: Mobile E2E | |
env: | |
# Build environment versions | |
JAVA_VERSION: 17 | |
ANDROID_API_LEVEL: 33 | |
ANDROID_NDK_VERSION: 27.0.12077973 | |
XCODE_VERSION: 16.4 | |
# Cache versions | |
GH_CACHE_VERSION: v1 # Global cache version | |
GH_GEMS_CACHE_VERSION: v1 # Ruby gems cache version | |
# Performance optimizations | |
GRADLE_OPTS: -Dorg.gradle.workers.max=4 -Dorg.gradle.parallel=true -Dorg.gradle.caching=true | |
CI: true | |
# Disable Maestro analytics in CI | |
MAESTRO_CLI_NO_ANALYTICS: true | |
MAESTRO_VERSION: 1.41.0 | |
on: | |
pull_request: | |
branches: | |
- dev | |
- staging | |
- main | |
paths: | |
- "app/**" | |
- "packages/mobile-sdk-alpha/**" | |
- ".github/workflows/mobile-e2e.yml" | |
jobs: | |
android-build-test: | |
# Currently build-only for Android with private repos. E2E steps are preserved but skipped (if: false). | |
# To re-enable full E2E: change `if: false` to `if: true` on Maestro and emulator steps. | |
concurrency: | |
group: ${{ github.workflow }}-android-${{ github.ref }} | |
cancel-in-progress: true | |
timeout-minutes: 120 | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Read and sanitize Node.js version | |
shell: bash | |
run: | | |
if [ ! -f .nvmrc ] || [ -z "$(cat .nvmrc)" ]; then | |
echo "β .nvmrc is missing or empty"; exit 1; | |
fi | |
VERSION="$(tr -d '\r\n' < .nvmrc)&qu 8000 ot; | |
VERSION="${VERSION#v}" | |
if ! [[ "$VERSION" =~ ^[0-9]+(\.[0-9]+){0,2}$ ]]; then | |
echo "Invalid .nvmrc content: '$VERSION'"; exit 1; | |
fi | |
echo "NODE_VERSION=$VERSION" >> "$GITHUB_ENV" | |
echo "NODE_VERSION_SANITIZED=${VERSION//\//-}" >> "$GITHUB_ENV" | |
- uses: actions/setup-node@v4 | |
with: | |
node-version: ${{ env.NODE_VERSION }} | |
- run: corepack enable | |
- run: corepack prepare yarn@4.6.0 --activate | |
- name: Cache Yarn dependencies | |
uses: ./.github/actions/cache-yarn | |
with: | |
path: | | |
.yarn/cache | |
.yarn/install-state.gz | |
.yarn/unplugged | |
cache-version: ${{ env.GH_CACHE_VERSION }}-node-${{ env.NODE_VERSION_SANITIZED }}-${{ hashFiles('.yarnrc.yml') }} | |
- name: Toggle Yarn hardened mode for trusted PRs | |
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false }} | |
run: echo "YARN_ENABLE_HARDENED_MODE=0" >> $GITHUB_ENV | |
- name: Install deps (internal PRs and protected branches) | |
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false }} | |
run: yarn install --immutable --silent | |
env: | |
SELFXYZ_INTERNAL_REPO_PAT: ${{ secrets.SELFXYZ_INTERNAL_REPO_PAT }} | |
- name: Install deps (forked PRs - no secrets) | |
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true }} | |
run: yarn install --immutable --silent | |
- name: Validate Maestro test file | |
if: false # Skip for build-only test - keep logic for future E2E | |
run: | | |
[ -f app/tests/e2e/launch.android.flow.yaml ] || { echo "β Android E2E test file missing"; exit 1; } | |
- name: Cache Maestro | |
if: false # Skip for build-only test - keep logic for future E2E | |
id: cache-maestro | |
uses: actions/cache@v4 | |
with: | |
path: ~/.maestro | |
key: ${{ runner.os }}-maestro-${{ env.MAESTRO_VERSION }} | |
- name: Install Maestro | |
if: false # Skip for build-only test - keep logic for future E2E | |
run: curl -Ls "https://get.maestro.mobile.dev" | bash | |
- name: Add Maestro to path | |
if: false # Skip for build-only test - keep logic for future E2E | |
run: echo "$HOME/.maestro/bin" >> "$GITHUB_PATH" | |
- name: Free up disk space | |
uses: ./.github/actions/free-disk-space | |
- name: Setup Java environment | |
uses: actions/setup-java@v4 | |
with: | |
distribution: "temurin" | |
java-version: ${{ env.JAVA_VERSION }} | |
- name: Cache Gradle packages | |
uses: ./.github/actions/cache-gradle | |
- name: Setup Android SDK | |
uses: android-actions/setup-android@v3 | |
with: | |
accept-android-sdk-licenses: true | |
- name: Install NDK | |
run: sdkmanager "ndk;${{ env.ANDROID_NDK_VERSION }}" | |
- name: Build dependencies (outside emulator) | |
run: | | |
echo "Building dependencies..." | |
# Ensure Yarn 4.6.0 is active | |
corepack enable | |
corepack prepare yarn@4.6.0 --activate | |
yarn workspace @selfxyz/mobile-app run build:deps || { echo "β Dependency build failed"; exit 1; } | |
echo "β Dependencies built successfully" | |
- name: Clone android-passport-nfc-reader | |
uses: ./.github/actions/clone-android-passport-nfc-reader | |
with: | |
working_directory: app | |
selfxyz_internal_pat: ${{ secrets.SELFXYZ_INTERNAL_REPO_PAT }} | |
- name: Build Android APK | |
run: | | |
echo "Building Android APK..." | |
chmod +x app/android/gradlew | |
(cd app/android && ./gradlew assembleDebug --quiet --parallel --build-cache --no-configuration-cache) || { echo "β Android build failed"; exit 1; } | |
echo "β Android build succeeded" | |
- name: Clean up Gradle build artifacts | |
uses: ./.github/actions/cleanup-gradle-artifacts | |
- name: Verify APK and android-passport-nfc-reader integration | |
run: | | |
echo "π Verifying build artifacts..." | |
APK_PATH="app/android/app/build/outputs/apk/debug/app-debug.apk" | |
[ -f "$APK_PATH" ] || { echo "β APK not found at $APK_PATH"; exit 1; } | |
echo "β APK found at $APK_PATH" | |
# Check APK size | |
APK_SIZE=$(stat -f%z "$APK_PATH" 2>/dev/null || stat -c%s "$APK_PATH" 2>/dev/null || echo "unknown") | |
echo "π± APK size: $APK_SIZE bytes" | |
# Verify android-passport-nfc-reader was properly integrated (skip for forks) | |
if [ -z "${SELFXYZ_INTERNAL_REPO_PAT:-}" ]; then | |
echo "π No PAT available β skipping private module verification" | |
elif [ -d "app/android/android-passport-nfc-reader" ]; then | |
echo "β android-passport-nfc-reader directory exists" | |
echo "π android-passport-nfc-reader contents:" | |
ls -la app/android/android-passport-nfc-reader/ | head -10 | |
else | |
echo "β android-passport-nfc-reader directory not found" | |
exit 1 | |
fi | |
echo "π Build verification completed successfully!" | |
echo "βΉοΈ Emulator testing is temporarily disabled - build testing only" | |
- name: Install and Test on Android | |
if: false # Skip emulator/E2E for build-only test - keep logic for future E2E | |
uses: reactivecircus/android-emulator-runner@v2 | |
with: | |
api-level: ${{ env.ANDROID_API_LEVEL }} | |
arch: x86_64 | |
target: google_apis | |
force-avd-creation: false | |
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none -camera-front none -memory 8192 -cores 4 -accel on | |
disable-animations: true | |
script: | | |
echo "Installing app on emulator..." | |
APK_PATH="app/android/app/build/outputs/apk/debug/app-debug.apk" | |
[ -f "$APK_PATH" ] || { echo "β APK not found at $APK_PATH"; exit 1; } | |
adb install -r "$APK_PATH" || { echo "β App installation failed"; exit 1; } | |
echo "β App installed successfully" | |
echo "β° Giving the emulator a moment to settle..." | |
sleep 5 | |
echo "π Running Maestro tests..." | |
export MAESTRO_DRIVER_STARTUP_TIMEOUT=180000 | |
maestro test app/tests/e2e/launch.android.flow.yaml --format junit --output app/maestro-results.xml | |
- name: Upload test results | |
if: false # Skip for build-only test - keep logic for future E2E | |
uses: actions/upload-artifact@v4 | |
with: | |
name: maestro-results-android | |
path: app/maestro-results.xml | |
if-no-files-found: warn | |
e2e-ios: | |
timeout-minutes: 120 | |
runs-on: macos-latest-large | |
concurrency: | |
group: ${{ github.workflow }}-ios-${{ github.ref }} | |
cancel-in-progress: true | |
env: | |
# iOS project configuration - hardcoded for E2E testing stability | |
# Note: During migration, project name is "Self" but scheme is still "OpenPassport" | |
# mobile-deploy.yml uses secrets for production deployment | |
IOS_PROJECT_NAME: "Self" | |
IOS_PROJECT_SCHEME: "OpenPassport" | |
steps: | |
- uses: actions/checkout@v4 | |
- name: Read and sanitize Node.js version | |
shell: bash | |
run: | | |
if [ ! -f .nvmrc ] || [ -z "$(cat .nvmrc)" ]; then | |
echo "β .nvmrc is missing or empty"; exit 1; | |
fi | |
VERSION="$(tr -d '\r\n' < .nvmrc)" | |
VERSION="${VERSION#v}" | |
if ! [[ "$VERSION" =~ ^[0-9]+(\.[0-9]+){0,2}$ ]]; then | |
echo "Invalid .nvmrc content: '$VERSION'"; exit 1; | |
fi | |
echo "NODE_VERSION=$VERSION" >> "$GITHUB_ENV" | |
echo "NODE_VERSION_SANITIZED=${VERSION//\//-}" >> "$GITHUB_ENV" | |
- uses: actions/setup-node@v4 | |
with: | |
node-version: ${{ env.NODE_VERSION }} | |
- run: corepack enable | |
- run: corepack prepare yarn@4.6.0 --activate | |
- name: Cache Yarn dependencies | |
uses: ./.github/actions/cache-yarn | |
with: | |
path: | | |
.yarn/cache | |
.yarn/install-state.gz | |
.yarn/unplugged | |
cache-version: ${{ env.GH_CACHE_VERSION }}-node-${{ env.NODE_VERSION_SANITIZED }}-${{ hashFiles('.yarnrc.yml') }} | |
- name: Toggle Yarn hardened mode for trusted PRs | |
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == false }} | |
run: echo "YARN_ENABLE_HARDENED_MODE=0" >> $GITHUB_ENV | |
- name: Install deps (internal PRs and protected branches) | |
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false }} | |
run: yarn install --immutable --silent | |
env: | |
SELFXYZ_INTERNAL_REPO_PAT: ${{ secrets.SELFXYZ_INTERNAL_REPO_PAT }} | |
- name: Install deps (forked PRs - no secrets) | |
if: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.fork == true }} | |
run: yarn install --immutable --silent | |
- name: Validate Maestro test file | |
run: | | |
[ -f app/tests/e2e/launch.ios.flow.yaml ] || { echo "β iOS E2E test file missing"; exit 1; } | |
- name: Cache Maestro | |
id: cache-maestro | |
uses: actions/cache@v4 | |
with: | |
path: ~/.maestro | |
key: ${{ runner.os }}-maestro-${{ env.MAESTRO_VERSION }} | |
- name: Install Maestro | |
if: steps.cache-maestro.outputs.cache-hit != 'true' | |
run: curl -Ls "https://get.maestro.mobile.dev" | bash | |
- name: Add Maestro to path | |
run: echo "$HOME/.maestro/bin" >> "$GITHUB_PATH" | |
- name: Set up Xcode | |
uses: maxim-lobanov/setup-xcode@v1 | |
with: | |
xcode-version: ${{ env.XCODE_VERSION }} | |
- name: Configure Xcode path | |
run: | | |
echo "π§ Configuring Xcode path to fix iOS SDK issues..." | |
# Fix for macOS 15 runner iOS SDK issues | |
# See: https://github.com/actions/runner-images/issues/12758 | |
sudo xcode-select --switch /Applications/Xcode_${{ env.XCODE_VERSION }}.app | |
echo "β Xcode path configured" | |
# Verify Xcode setup | |
echo "Xcode version:" | |
xcodebuild -version | |
echo "Xcode path:" | |
xcode-select -p | |
- name: Setup ccache | |
uses: hendrikmuhs/ccache-action@v1.2 | |
with: | |
key: ${{ github.job }}-${{ runner.os }} | |
- name: Add ccache to PATH | |
run: echo "/usr/local/opt/ccache/libexec" >> $GITHUB_PATH | |
- name: Set up Ruby | |
uses: ruby/setup-ruby@v1 | |
with: | |
ruby-version: "3.3" | |
bundler-cache: true | |
working-directory: app | |
- name: Cache Pods | |
uses: ./.github/actions/cache-pods | |
with: | |
path: | | |
app/ios/Pods | |
~/Library/Caches/CocoaPods | |
lock-file: app/ios/Podfile.lock | |
- name: Cache DerivedData | |
uses: actions/cache@v4 | |
with: | |
path: app/ios/build | |
key: ${{ runner.os }}-derived-data-${{ env.GH_CACHE_VERSION }}-${{ env.XCODE_VERSION }}-${{ hashFiles('app/ios/Podfile.lock', 'yarn.lock') }} | |
restore-keys: | | |
${{ runner.os }}-derived-data-${{ env.GH_CACHE_VERSION }}-${{ env.XCODE_VERSION }}- | |
${{ runner.os }}-derived-data-${{ env.GH_CACHE_VERSION }}- | |
- name: Verify iOS Runtime | |
run: | | |
echo "π± Verifying iOS Runtime availability..." | |
echo "Available iOS runtimes:" | |
DD26 xcrun simctl list runtimes | grep iOS | |
- name: Build dependencies (outside main flow) | |
run: | | |
echo "Building dependencies..." | |
yarn workspace @selfxyz/mobile-app run build:deps || { echo "β Dependency build failed"; exit 1; } | |
echo "β Dependencies built successfully" | |
- name: Install iOS dependencies | |
run: | | |
echo "Installing iOS dependencies..." | |
cd app/ios | |
echo "π¦ Installing pods via centralized scriptβ¦" | |
BUNDLE_GEMFILE=../Gemfile bundle exec bash scripts/pod-install-with-cache-fix.sh || { echo "β Pod install failed"; exit 1; } | |
env: | |
SELFXYZ_INTERNAL_REPO_PAT: ${{ secrets.SELFXYZ_INTERNAL_REPO_PAT }} | |
- name: Setup iOS Simulator | |
run: | | |
echo "Setting up iOS Simulator..." | |
# First, check what simulators are actually available | |
echo "Available simulators:" | |
xcrun simctl list devices available || { | |
echo "β Failed to list available devices" | |
echo "Trying to list all devices:" | |
xcrun simctl list devices || { | |
echo "β Failed to list any devices" | |
exit 1 | |
} | |
} | |
# Find iPhone SE (3rd generation) simulator | |
echo "Finding iPhone SE (3rd generation) simulator..." | |
AVAILABLE_SIMULATOR=$(xcrun simctl list devices available | grep "iPhone SE (3rd generation)" | head -1 | sed -E 's/.*\(([A-F0-9-]+)\).*/\1/') | |
if [ -z "$AVAILABLE_SIMULATOR" ]; then | |
echo "iPhone SE (3rd generation) not found, trying any iPhone SE..." | |
AVAILABLE_SIMULATOR=$(xcrun simctl list devices available | grep "iPhone SE" | head -1 | sed -E 's/.*\(([A-F0-9-]+)\).*/\1/') | |
fi | |
if [ -z "$AVAILABLE_SIMULATOR" ]; then | |
echo "No iPhone SE found, trying any iPhone..." | |
AVAILABLE_SIMULATOR=$(xcrun simctl list devices available | grep "iPhone" | head -1 | sed -E 's/.*\(([A-F0-9-]+)\).*/\1/') | |
fi | |
if [ -z "$AVAILABLE_SIMULATOR" ]; then | |
echo "β No available iPhone simulator found" | |
echo "Creating a new iPhone SE (3rd generation) simulator..." | |
# Create a new iPhone SE (3rd generation) simulator | |
xcrun simctl create "iPhone SE (3rd generation)" "iPhone SE (3rd generation)" || { | |
echo "β Failed to create iPhone SE (3rd generation) simulator" | |
echo "Trying to create any iPhone SE simulator..." | |
xcrun simctl create "iPhone SE" "iPhone SE" || { | |
echo "β Failed to create simulator" | |
exit 1 | |
} | |
} | |
AVAILABLE_SIMULATOR=$(xcrun simctl list devices | grep "iPhone SE" | sed -E 's/.*\(([A-F0-9-]+)\).*/\1/') | |
fi | |
echo "Using simulator: $AVAILABLE_SIMULATOR" | |
# Get simulator name for display | |
SIMULATOR_NAME=$(xcrun simctl list devices | grep "$AVAILABLE_SIMULATOR" | sed -E 's/^[[:space:]]*([^(]+).*/\1/' | xargs) | |
echo "Simulator name: $SIMULATOR_NAME" | |
# Boot simulator and wait for it to be ready | |
echo "Booting simulator..." | |
xcrun simctl boot "$AVAILABLE_SIMULATOR" || { | |
echo "β Failed to boot simulator" | |
exit 1 | |
} | |
echo "Waiting for simulator to be ready..." | |
xcrun simctl bootstatus "$AVAILABLE_SIMULATOR" -b | |
# Wait for simulator to be fully ready | |
echo "Waiting for simulator to be fully ready..." | |
sleep 15 | |
echo "Simulator status:" | |
xcrun simctl list devices | grep "$AVAILABLE_SIMULATOR" | |
# Store simulator ID for later use | |
echo "IOS_SIMULATOR_ID=$AVAILABLE_SIMULATOR" >> $GITHUB_ENV | |
echo "IOS_SIMULATOR_NAME=$SIMULATOR_NAME" >> $GITHUB_ENV | |
- name: Resolve iOS workspace | |
run: | | |
WORKSPACE_OPEN="app/ios/OpenPassport.xcworkspace" | |
WORKSPACE_SELF="app/ios/Self.xcworkspace" | |
if xcodebuild -list -workspace "$WORKSPACE_OPEN" 2>/dev/null | grep -q "OpenPassport"; then | |
WORKSPACE_PATH="$WORKSPACE_OPEN" | |
else | |
WORKSPACE_PATH="$WORKSPACE_SELF" | |
fi | |
echo "WORKSPACE_PATH=$WORKSPACE_PATH" >> "$GITHUB_ENV" | |
echo "Resolved workspace: $WORKSPACE_PATH" | |
- name: Build iOS App | |
run: | | |
echo "Building iOS app..." | |
echo "Project: ${{ env.IOS_PROJECT_NAME }}, Scheme: ${{ env.IOS_PROJECT_SCHEME }}" | |
# Verify workspace exists before building | |
if [ -z "$WORKSPACE_PATH" ]; then | |
echo "β WORKSPACE_PATH is not set" | |
exit 1 | |
fi | |
if [ ! -d "$WORKSPACE_PATH" ]; then | |
echo "β Workspace not found at: $WORKSPACE_PATH" | |
echo "Available workspaces:" | |
find app/ios -name "*.xcworkspace" -type d | |
exit 1 | |
fi | |
# Verify scheme exists by listing available schemes | |
echo "Verifying scheme availability..." | |
AVAILABLE_SCHEMES=$(xcodebuild -list -workspace "$WORKSPACE_PATH" 2>/dev/null | grep -A 200 "Schemes:" | grep -v "Schemes:" | xargs) | |
echo "Available schemes (first 20): $(echo $AVAILABLE_SCHEMES | cut -d' ' -f1-20)..." | |
if [[ ! "$AVAILABLE_SCHEMES" =~ ${{ env.IOS_PROJECT_SCHEME }} ]]; then | |
echo "β Scheme '${{ env.IOS_PROJECT_SCHEME }}' not found" | |
echo "Full scheme list:" | |
xcodebuild -list -workspace "$WORKSPACE_PATH" 2>/dev/null | grep -A 200 "Schemes:" | grep -v "Schemes:" | head -50 | |
exit 1 | |
fi | |
echo "β Using workspace: $WORKSPACE_PATH" | |
echo "β Using scheme: ${{ env.IOS_PROJECT_SCHEME }}" | |
# Use cached derived data and enable parallel builds for faster compilation | |
# Additional flags disable indexing, restrict architecture, and use whole-module Swift compilation | |
# Use the simulator that was set up earlier in the workflow | |
FORCE_BUNDLING=1 RCT_NO_LAUNCH_PACKAGER=1 \ | |
xcodebuild -workspace "$WORKSPACE_PATH" -scheme ${{ env.IOS_PROJECT_SCHEME }} -configuration Debug -destination "id=${{ env.IOS_SIMULATOR_ID }}" -derivedDataPath app/ios/build -jobs "$(sysctl -n hw.ncpu)" -parallelizeTargets -quiet COMPILER_INDEX_STORE_ENABLE=NO ONLY_ACTIVE_ARCH=YES SWIFT_COMPILATION_MODE=wholemodule || { echo "β iOS build failed"; exit 1; } | |
echo "β iOS build succeeded" | |
- name: Install and Test on iOS | |
run: | | |
echo "Installing app on simulator..." | |
APP_PATH=$(find app/ios/build/Build/Products/Debug-iphonesimulator -name "*.app" | head -1) | |
[ -z "$APP_PATH" ] && { echo "β Could not find built iOS app"; exit 1; } | |
echo "Found app at: $APP_PATH" | |
echo "π Determining app bundle ID from built app..." | |
IOS_BUNDLE_ID=$(/usr/libexec/PlistBuddy -c "Print CFBundleIdentifier" "$APP_PATH/Info.plist") | |
[ -z "$IOS_BUNDLE_ID" ] && { echo "β Could not determine bundle ID from $APP_PATH/Info.plist"; exit 1; } | |
echo "β App Bundle ID: $IOS_BUNDLE_ID" | |
# Use the dynamic simulator ID | |
SIMULATOR_ID="${IOS_SIMULATOR_ID:-iPhone SE (3rd generation)}" | |
echo "Installing on simulator: $SIMULATOR_ID" | |
echo "Removing any existing app installation..." | |
xcrun simctl uninstall "$SIMULATOR_ID" "$IOS_BUNDLE_ID" 2>/dev/null || true | |
echo "Installing app..." | |
xcrun simctl install "$SIMULATOR_ID" "$APP_PATH" | |
if [ $? -ne 0 ]; then | |
echo "β iOS app installation failed" | |
exit 1 | |
fi | |
echo "Verifying app installation..." | |
if xcrun simctl get_app_container "$SIMULATOR_ID" "$IOS_BUNDLE_ID" app >/dev/null 2>&1; then | |
echo "β App successfully installed" | |
else | |
echo "β App installation verification failed" | |
exit 1 | |
fi | |
echo "π Testing app launch capability..." | |
xcrun simctl launch "$SIMULATOR_ID" "$IOS_BUNDLE_ID" || { | |
echo "β οΈ Direct app launch test failed - this might be expected." | |
} | |
echo "β° Checking simulator readiness..." | |
sleep 10 | |
# Probe container as readiness check instead of listapps | |
xcrun simctl get_app_container "$SIMULATOR_ID" "$IOS_BUNDLE_ID" app >/dev/null 2>&1 || sleep 5 | |
echo "Running Maestro tests..." | |
echo "Starting test execution..." | |
maestro test app/tests/e2e/launch.ios.flow.yaml --format junit --output app/maestro-results.xml || { | |
echo "Maestro test failed, but continuing to upload results..." | |
exit 1 | |
} | |
- name: Upload test results | |
if: always() | |
uses: actions/upload-artifact@v4 | |
with: | |
name: maestro-results-ios | |
path: app/maestro-results.xml | |
if-no-files-found: warn |