Stack update (latest RN Fabric support, TypeScript migration, example rebooted, CI tested) #52
Workflow file for this run
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: CI | |
| on: | |
| push: | |
| branches: [main, master] | |
| pull_request: | |
| branches: [main, master] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| lint-and-type-check: | |
| name: Code Quality (Strict) | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm install --legacy-peer-deps # Need --legacy-peer-deps for react-native-windows@0.74.47 | |
| - name: Check formatting (no prettier diff allowed) | |
| run: npm run format:check | |
| - name: Run TypeScript type check (strict) | |
| run: npm run type-check | |
| - name: Run linter (strict - no warnings allowed) | |
| run: npm run lint:ci | |
| - name: Build project | |
| run: npm run build | |
| - name: Run unit tests | |
| run: npm test | |
| build-android: | |
| name: Build Android APK | |
| runs-on: ubuntu-latest | |
| needs: lint-and-type-check | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Setup Java JDK | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: 'temurin' | |
| java-version: '17' | |
| - name: Cache Gradle | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.gradle/caches | |
| ~/.gradle/wrapper | |
| key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} | |
| restore-keys: | | |
| ${{ runner.os }}-gradle- | |
| - name: Install dependencies | |
| run: npm install --legacy-peer-deps # Need --legacy-peer-deps for react-native-windows@0.74.47 | |
| - name: Build library | |
| run: npm run build | |
| - name: Install example dependencies | |
| working-directory: example | |
| run: npm ci --legacy-peer-deps | |
| - name: Pre-build Skia Prefab packages | |
| working-directory: example/android | |
| run: | | |
| echo "🔨 Pre-building Skia Prefab packages to avoid CMake errors..." | |
| ./gradlew :shopify_react-native-skia:prefabDebugConfigurePackage --no-daemon | |
| - name: Build Android APK | |
| working-directory: example/android | |
| run: ./gradlew assembleDebug --no-daemon | |
| - name: Upload Android APK | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: android-apk | |
| path: example/android/app/build/outputs/apk/debug/app-debug.apk | |
| build-ios: | |
| name: Build iOS App | |
| runs-on: macos-15 # Use latest macOS with Xcode 16.1+ | |
| needs: lint-and-type-check | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Cache CocoaPods | |
| uses: actions/cache@v4 | |
| with: | |
| path: example/ios/Pods | |
| key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pods- | |
| - name: Install dependencies | |
| run: npm install --legacy-peer-deps # Need --legacy-peer-deps for react-native-windows@0.74.47 | |
| - name: Build library | |
| run: npm run build | |
| - name: Install example dependencies | |
| working-directory: example | |
| run: npm ci --legacy-peer-deps | |
| - name: Setup CocoaPods | |
| uses: maxim-lobanov/setup-cocoapods@v1 | |
| with: | |
| version: latest | |
| - name: Pod install | |
| working-directory: example/ios | |
| run: pod install --verbose | |
| - name: Build iOS | |
| working-directory: example | |
| run: | | |
| xcodebuild -workspace ios/ViewShotExample.xcworkspace \ | |
| -scheme ViewShotExample \ | |
| -configuration Debug \ | |
| -destination 'platform=iOS Simulator,name=iPhone 16,OS=18.4' \ | |
| build CODE_SIGNING_ALLOWED=NO | |
| - name: Upload iOS Build Logs | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: ios-build-logs | |
| path: ~/Library/Logs/DiagnosticReports/ | |
| build-windows: | |
| name: Build Windows | |
| runs-on: windows-latest | |
| needs: lint-and-type-check | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Install dependencies | |
| run: npm install --legacy-peer-deps # Need --legacy-peer-deps for react-native-windows@0.74.47 vs @types/react@19 | |
| - name: Build library | |
| run: npm run build | |
| - name: Setup MSBuild with UWP support | |
| uses: microsoft/setup-msbuild@v2 | |
| with: | |
| msbuild-architecture: x64 | |
| - name: Setup NuGet | |
| uses: nuget/setup-nuget@v2 | |
| with: | |
| nuget-version: 'latest' | |
| - name: Verify Windows 10 SDK | |
| run: | | |
| Write-Host "🔧 Checking Windows 10 SDK and UWP tools..." | |
| # Check Windows 10 SDK | |
| $sdkPath = "C:\Program Files (x86)\Windows Kits\10" | |
| if (Test-Path $sdkPath) { | |
| Write-Host "✅ Windows 10 SDK found" | |
| $versions = dir "$sdkPath\bin" -Directory -Name | Sort-Object -Descending | |
| Write-Host "Available SDK versions: $($versions -join ', ')" | |
| } | |
| # Check MSBuild XAML targets | |
| $xamlTargets = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild\Microsoft\WindowsXaml" | |
| if (Test-Path $xamlTargets) { | |
| Write-Host "✅ XAML targets found at $xamlTargets" | |
| dir $xamlTargets -Recurse -Name "*.targets" | Select-Object -First 5 | |
| } else { | |
| Write-Host "❌ XAML targets not found, checking alternative locations..." | |
| # Check other common locations | |
| $altPath = "C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools\MSBuild\Microsoft\WindowsXaml" | |
| if (Test-Path $altPath) { | |
| Write-Host "✅ Found XAML targets at $altPath" | |
| } | |
| } | |
| - name: Verify react-native-windows installation | |
| run: | | |
| Write-Host "🔍 Checking react-native-windows structure..." | |
| if (Test-Path "node_modules\react-native-windows") { | |
| Write-Host "✅ react-native-windows found" | |
| dir "node_modules\react-native-windows\" -Name | Select-Object -First 20 | |
| if (Test-Path "node_modules\react-native-windows\PropertySheets") { | |
| Write-Host "✅ PropertySheets found" | |
| dir "node_modules\react-native-windows\PropertySheets\" -Name | |
| if (Test-Path "node_modules\react-native-windows\PropertySheets\External") { | |
| Write-Host "✅ PropertySheets\External found" | |
| dir "node_modules\react-native-windows\PropertySheets\External\" -Name | |
| } else { | |
| Write-Host "❌ PropertySheets\External missing - checking alternative locations..." | |
| # Check if PropertySheets structure changed in newer versions | |
| if (Test-Path "node_modules\react-native-windows\Microsoft.ReactNative.Uwp.CSharpLib.props") { | |
| Write-Host "✅ Found props file in root directory" | |
| } else { | |
| Write-Host "❌ Props file not found in expected locations" | |
| Write-Host "Available files in react-native-windows:" | |
| dir "node_modules\react-native-windows\" -Recurse -Name "*.props" | Select-Object -First 10 | |
| } | |
| } | |
| } else { | |
| Write-Host "❌ PropertySheets missing - checking for alternative structure..." | |
| Write-Host "Available directories in react-native-windows:" | |
| dir "node_modules\react-native-windows\" -Directory -Name | Select-Object -First 10 | |
| } | |
| } else { | |
| Write-Host "❌ react-native-windows not found" | |
| exit 1 | |
| } | |
| - name: Verify resolved paths for build | |
| working-directory: windows/RNViewShot | |
| run: | | |
| Write-Host "🔍 Verifying resolved paths from .csproj location..." | |
| $rnwPath = "..\..\node_modules\react-native-windows" | |
| $resolvedPath = Resolve-Path $rnwPath -ErrorAction SilentlyContinue | |
| if ($resolvedPath) { | |
| Write-Host "✅ react-native-windows resolved to: $resolvedPath" | |
| # Check for props files | |
| $propsFiles = @( | |
| "\PropertySheets\External\Microsoft.ReactNative.Uwp.CSharpLib.props", | |
| "\Microsoft.ReactNative.Uwp.CSharpLib.props" | |
| ) | |
| foreach ($propsFile in $propsFiles) { | |
| $fullPath = "$resolvedPath$propsFile" | |
| if (Test-Path $fullPath) { | |
| Write-Host "✅ Found props file: $fullPath" | |
| } else { | |
| Write-Host "❌ Props file not found: $fullPath" | |
| } | |
| } | |
| # Check for targets files | |
| $targetsFiles = @( | |
| "\PropertySheets\External\Microsoft.ReactNative.Uwp.CSharpLib.targets", | |
| "\Microsoft.ReactNative.Uwp.CSharpLib.targets" | |
| ) | |
| foreach ($targetsFile in $targetsFiles) { | |
| $fullPath = "$resolvedPath$targetsFile" | |
| if (Test-Path $fullPath) { | |
| Write-Host "✅ Found targets file: $fullPath" | |
| } else { | |
| Write-Host "❌ Targets file not found: $fullPath" | |
| } | |
| } | |
| } else { | |
| Write-Host "❌ Could not resolve path: $rnwPath" | |
| Write-Host "Current directory: $(Get-Location)" | |
| Write-Host "Parent directory contents:" | |
| dir ..\.. -Name | Select-Object -First 10 | |
| } | |
| - name: Build Windows project (C# project only) | |
| working-directory: windows/RNViewShot | |
| run: | | |
| Write-Host "🔨 Building Windows C# project only..." | |
| Write-Host "Using MSBuild for UWP project..." | |
| # Set explicit paths for MSBuild extensions | |
| $env:MSBuildExtensionsPath = "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild" | |
| $env:VisualStudioVersion = "17.0" | |
| # Try to restore packages first | |
| nuget restore RNViewShot.csproj | |
| # Build with explicit parameters | |
| msbuild RNViewShot.csproj ` | |
| /p:Configuration=Debug ` | |
| /p:Platform="AnyCPU" ` | |
| /p:ReactNativeWindowsDir="../../node_modules/react-native-windows/" ` | |
| /p:MSBuildExtensionsPath="C:\Program Files\Microsoft Visual Studio\2022\Enterprise\MSBuild" ` | |
| /p:VisualStudioVersion="17.0" ` | |
| /verbosity:normal | |
| test-e2e-android: | |
| name: E2E Tests (Android) | |
| runs-on: ubuntu-latest | |
| needs: build-android | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Setup Java JDK | |
| uses: actions/setup-java@v4 | |
| with: | |
| distribution: 'temurin' | |
| java-version: '17' | |
| - name: Enable KVM group perms | |
| 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 | |
| - name: Cache Gradle | |
| uses: actions/cache@v4 | |
| with: | |
| path: | | |
| ~/.gradle/caches | |
| ~/.gradle/wrapper | |
| key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }} | |
| restore-keys: | | |
| ${{ runner.os }}-gradle- | |
| - name: Cache AVD | |
| uses: actions/cache@v4 | |
| id: avd-cache | |
| with: | |
| path: | | |
| ~/.android/avd/* | |
| ~/.android/adb* | |
| key: avd-34 | |
| - name: Create AVD and generate snapshot for caching | |
| if: steps.avd-cache.outputs.cache-hit != 'true' | |
| uses: reactivecircus/android-emulator-runner@v2 | |
| with: | |
| api-level: 34 | |
| target: google_apis | |
| arch: x86_64 | |
| profile: Nexus 6 | |
| force-avd-creation: false | |
| emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none | |
| disable-animations: false | |
| script: echo "Generated AVD snapshot for caching." | |
| - name: Install dependencies | |
| run: npm install --legacy-peer-deps | |
| - name: Build library | |
| run: npm run build | |
| - name: Install example dependencies | |
| working-directory: example | |
| run: npm ci --legacy-peer-deps | |
| - name: Build Android APK for E2E | |
| working-directory: example/android | |
| run: ./gradlew assembleDebug | |
| - name: Run Functional E2E Tests (Android) | |
| uses: reactivecircus/android-emulator-runner@v2 | |
| with: | |
| api-level: 34 | |
| target: google_apis | |
| arch: x86_64 | |
| profile: Nexus 6 | |
| force-avd-creation: false | |
| emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none | |
| disable-animations: true | |
| script: | | |
| echo "🚀 Starting Functional E2E Tests for Android" | |
| cd example | |
| # Start Metro bundler in background | |
| npm start & | |
| METRO_PID=$! | |
| # Wait for Metro to start | |
| sleep 30 | |
| # Run our comprehensive feature tests | |
| echo "📱 Running comprehensive ViewShot feature tests..." | |
| node test-all-features.js | |
| # Kill Metro | |
| kill $METRO_PID || true | |
| - name: Upload E2E Test Results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: e2e-test-results-android | |
| path: | | |
| example/test-*.png | |
| example/test-report.json | |
| test-e2e-ios: | |
| name: E2E Tests (iOS) | |
| runs-on: macos-15 | |
| needs: build-ios | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: '20' | |
| cache: 'npm' | |
| - name: Cache CocoaPods | |
| uses: actions/cache@v4 | |
| with: | |
| path: example/ios/Pods | |
| key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pods- | |
| - name: Install dependencies | |
| run: npm install --legacy-peer-deps | |
| - name: Build library | |
| run: npm run build | |
| - name: Install example dependencies | |
| working-directory: example | |
| run: npm ci --legacy-peer-deps | |
| - name: Setup CocoaPods | |
| uses: maxim-lobanov/setup-cocoapods@v1 | |
| with: | |
| version: latest | |
| - name: Pod install | |
| working-directory: example/ios | |
| run: pod install --verbose | |
| - name: List available iOS simulators | |
| run: | | |
| echo "📱 Available iOS simulators:" | |
| xcrun simctl list devices available | |
| - name: Build iOS for E2E | |
| working-directory: example | |
| run: | | |
| echo "🍎 Building iOS app for E2E testing..." | |
| xcodebuild -workspace ios/ViewShotExample.xcworkspace \ | |
| -scheme ViewShotExample \ | |
| -configuration Debug \ | |
| -sdk iphonesimulator \ | |
| -derivedDataPath ios/build \ | |
| -destination 'platform=iOS Simulator,name=iPhone 16 Pro,OS=latest' \ | |
| build | |
| - name: Run Functional E2E Tests (iOS) | |
| working-directory: example | |
| run: | | |
| echo "🚀 Starting Functional E2E Tests for iOS" | |
| # Start Metro bundler in background | |
| npm start & | |
| METRO_PID=$! | |
| # Wait for Metro to start | |
| sleep 30 | |
| # Start iOS Simulator | |
| echo "📱 Starting iOS Simulator..." | |
| xcrun simctl boot "iPhone 16 Pro" || true | |
| sleep 10 | |
| # Install the app | |
| echo "📦 Installing iOS app..." | |
| xcrun simctl install "iPhone 16 Pro" ios/build/Build/Products/Debug-iphonesimulator/ViewShotExample.app | |
| # Launch the app | |
| echo "🚀 Launching iOS app..." | |
| xcrun simctl launch "iPhone 16 Pro" com.viewshotexample | |
| # Wait for app to start | |
| sleep 10 | |
| # Create iOS-compatible test script | |
| echo "📱 Running iOS-compatible feature tests..." | |
| cat > test-ios-features.js << 'EOF' | |
| #!/usr/bin/env node | |
| const { execSync } = require('child_process'); | |
| console.log('🍎 iOS ViewShot Feature Test'); | |
| console.log('============================'); | |
| try { | |
| // Take screenshots using iOS simulator | |
| console.log('📸 Taking iOS simulator screenshots...'); | |
| // Screenshot 1: Home screen | |
| execSync('xcrun simctl io "iPhone 16 Pro" screenshot test-ios-home.png', { stdio: 'pipe' }); | |
| console.log('✅ Home screen captured'); | |
| // Simulate tap on Basic ViewShot (approximate coordinates) | |
| execSync('xcrun simctl io "iPhone 16 Pro" tap 200 400', { stdio: 'pipe' }); | |
| await new Promise(resolve => setTimeout(resolve, 2000)); | |
| // Screenshot 2: Basic test screen | |
| execSync('xcrun simctl io "iPhone 16 Pro" screenshot test-ios-basic.png', { stdio: 'pipe' }); | |
| console.log('✅ Basic test screen captured'); | |
| // Simulate tap on capture button | |
| execSync('xcrun simctl io "iPhone 16 Pro" tap 200 500', { stdio: 'pipe' }); | |
| await new Promise(resolve => setTimeout(resolve, 3000)); | |
| // Screenshot 3: After capture | |
| execSync('xcrun simctl io "iPhone 16 Pro" screenshot test-ios-after-capture.png', { stdio: 'pipe' }); | |
| console.log('✅ After capture screenshot taken'); | |
| console.log('🎉 iOS E2E test completed successfully!'); | |
| } catch (error) { | |
| console.error('❌ iOS E2E test failed:', error.message); | |
| process.exit(1); | |
| } | |
| function sleep(ms) { | |
| return new Promise(resolve => setTimeout(resolve, ms)); | |
| } | |
| EOF | |
| node test-ios-features.js | |
| # Kill Metro | |
| kill $METRO_PID || true | |
| - name: Upload E2E Test Results | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: e2e-test-results-ios | |
| path: | | |
| example/test-ios-*.png | |