diff --git a/.github/workflows/rnx-build.yml b/.github/workflows/rnx-build.yml new file mode 100644 index 000000000..5a8259624 --- /dev/null +++ b/.github/workflows/rnx-build.yml @@ -0,0 +1,237 @@ +name: rnx-build +on: + workflow_dispatch: + inputs: + architecture: + description: "Supported architectures are `arm64`, `x64`" + required: true + default: "x64" + deviceType: + description: "Supported device types are `device`, `emulator`, `simulator`" + required: true + default: "simulator" + packageManager: + description: "Binary name of the package manager used in the current repo" + required: true + default: "yarn" + platform: + description: "Supported platforms are `android`, `ios`, `macos`, `windows`" + required: true + projectRoot: + description: "Root of the project" + required: true + scheme: + description: "The workspace scheme to build (iOS and macOS only)" + default: "ReactTestApp" +jobs: + build-android: + name: "Build Android" + if: ${{ github.event.inputs.platform == 'android' }} + runs-on: ubuntu-20.04 + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set up JDK + uses: actions/setup-java@v3.4.0 + with: + distribution: temurin + java-version: 11 + - name: Set up Node 16 + uses: actions/setup-node@v3.3.0 + with: + node-version: 16 + - name: Install npm dependencies + run: ${{ github.event.inputs.packageManager }} install + - name: Build Android app + uses: gradle/gradle-build-action@v2.2.1 + with: + gradle-version: wrapper + arguments: --no-daemon clean assembleDebug + build-root-directory: ${{ github.event.inputs.projectRoot }}/android + - name: Upload build artifact + uses: actions/upload-artifact@v3 + with: + name: android-artifact + path: ${{ github.event.inputs.projectRoot }}/android/app/build/outputs/apk/debug/app-debug.apk + if-no-files-found: error + build-ios: + name: "Build iOS" + if: ${{ github.event.inputs.platform == 'ios' }} + runs-on: macos-12 + env: + CERTIFICATE_FILE: build-certificate.p12 + KEYCHAIN_FILE: app-signing.keychain-db + PROVISION_PATH: "Library/MobileDevice/Provisioning Profiles/Provisioning_Profile.mobileprovision" + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set up Node 16 + uses: actions/setup-node@v3.3.0 + with: + node-version: 16 + - name: Install npm dependencies + run: ${{ github.event.inputs.packageManager }} install + - name: Install Pods + run: pod install --project-directory=ios + working-directory: ${{ github.event.inputs.projectRoot }} + - name: Disable Clang sanitizers + run: | + # We need to disable Clang sanitizers otherwise the app will crash on + # startup trying to load Clang sanitizer libraries that would only + # exist if Xcode was attached. + xcconfig=node_modules/.generated/ios/ReactTestApp/ReactTestApp.debug.xcconfig + if [[ -f "${xcconfig}" ]]; then + sed -i '' 's/CLANG_ADDRESS_SANITIZER = YES/CLANG_ADDRESS_SANITIZER = NO/g' "${xcconfig}" + sed -i '' 's/CLANG_UNDEFINED_BEHAVIOR_SANITIZER = YES/CLANG_UNDEFINED_BEHAVIOR_SANITIZER = NO/g' "${xcconfig}" + sed -i '' 's/-fsanitize=bounds//g' "${xcconfig}" + fi + working-directory: ${{ github.event.inputs.projectRoot }} + - name: Install Apple signing certificate and provisioning profile + if: ${{ github.event.inputs.deviceType == 'device' }} + env: + BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} + BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }} + KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} + P12_PASSWORD: ${{ secrets.P12_PASSWORD }} + run: | + # https://docs.github.com/en/actions/deployment/deploying-xcode-applications/installing-an-apple-certificate-on-macos-runners-for-xcode-development + mkdir -p "$(dirname "${HOME}/${PROVISION_PATH}")" + + echo -n "${BUILD_CERTIFICATE_BASE64}" | base64 --decode --output "${RUNNER_TEMP}/${CERTIFICATE_FILE}" + echo -n "${BUILD_PROVISION_PROFILE_BASE64}" | base64 --decode --output "${HOME}/${PROVISION_PATH}" + + security create-keychain -p "${KEYCHAIN_PASSWORD}" "${RUNNER_TEMP}/${KEYCHAIN_FILE}" + security set-keychain-settings -lut 21600 "${RUNNER_TEMP}/${KEYCHAIN_FILE}" + security unlock-keychain -p "${KEYCHAIN_PASSWORD}" "${RUNNER_TEMP}/${KEYCHAIN_FILE}" + + security import "${RUNNER_TEMP}/${CERTIFICATE_FILE}" -k "${RUNNER_TEMP}/${KEYCHAIN_FILE}" -t cert -f pkcs12 -P "${P12_PASSWORD}" -A -T '/usr/bin/codesign' -T '/usr/bin/security' + security set-key-partition-list -S apple-tool:,apple: -k ${KEYCHAIN_PASSWORD} "${RUNNER_TEMP}/${KEYCHAIN_FILE}" 1> /dev/null + security list-keychain -d user -s "${RUNNER_TEMP}/${KEYCHAIN_FILE}" login.keychain + - name: Build iOS app + run: | + if [[ ${{ github.event.inputs.deviceType }} == 'device' ]]; then + destination='generic/platform=iOS' + code_signing='' + else + destination='generic/platform=iOS Simulator' + code_signing='CODE_SIGNING_ALLOWED=NO' + fi + xcworkspace=$(find . -maxdepth 1 -name '*.xcworkspace' -type d | head -1) + xcodebuild -workspace ${xcworkspace} -scheme ${{ github.event.inputs.scheme }} -destination "${destination}" -configuration Debug -derivedDataPath DerivedData ${code_signing} COMPILER_INDEX_STORE_ENABLE=NO build + working-directory: ${{ github.event.inputs.projectRoot }}/ios + - name: Remove Apple signing certificate and provisioning profile + if: ${{ always() && github.event.inputs.deviceType == 'device' }} + run: | + # Always run this job step, even if previous ones fail. See also + # https://docs.github.com/en/actions/deployment/deploying-xcode-applications/installing-an-apple-certificate-on-macos-runners-for-xcode-development#required-clean-up-on-self-hosted-runners + security delete-keychain "${RUNNER_TEMP}/${KEYCHAIN_FILE}" + rm -f "${RUNNER_TEMP}/${CERTIFICATE_FILE}" "${HOME}/${PROVISION_PATH}" + - name: Prepare build artifact + run: | + output_path=DerivedData/Build/Products + app=$(find ${output_path} -maxdepth 2 -name '*.app' -type d | head -1) + # bsdtar corrupts files when archiving due to APFS sparse files. A + # workaround is to use GNU Tar instead. See also: + # - https://github.com/actions/cache/issues/403 + # - https://github.com/actions/virtual-environments/issues/2619 + gtar -cvf ios-artifact.tar -C "$(dirname ${app})" "$(basename ${app})" + shasum --algorithm 256 ios-artifact.tar + working-directory: ${{ github.event.inputs.projectRoot }}/ios + - name: Upload build artifact + uses: actions/upload-artifact@v3 + with: + name: ios-artifact + path: ${{ github.event.inputs.projectRoot }}/ios/ios-artifact.tar + if-no-files-found: error + build-macos: + name: "Build macOS" + if: ${{ github.event.inputs.platform == 'macos' }} + runs-on: macos-12 + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set up Node 16 + uses: actions/setup-node@v3.3.0 + with: + node-version: 16 + - name: Install npm dependencies + run: ${{ github.event.inputs.packageManager }} install + - name: Install Pods + run: pod install --project-directory=macos + working-directory: ${{ github.event.inputs.projectRoot }} + - name: Disable Clang sanitizers + run: | + # We need to disable Clang sanitizers otherwise the app will crash on + # startup trying to load Clang sanitizer libraries that would only + # exist if Xcode was attached. + xcconfig=node_modules/.generated/macos/ReactTestApp/ReactTestApp.debug.xcconfig + if [[ -f "${xcconfig}" ]]; then + sed -i '' 's/CLANG_ADDRESS_SANITIZER = YES/CLANG_ADDRESS_SANITIZER = NO/g' "${xcconfig}" + sed -i '' 's/CLANG_UNDEFINED_BEHAVIOR_SANITIZER = YES/CLANG_UNDEFINED_BEHAVIOR_SANITIZER = NO/g' "${xcconfig}" + sed -i '' 's/-fsanitize=bounds//g' "${xcconfig}" + fi + working-directory: ${{ github.event.inputs.projectRoot }} + - name: Build macOS app + run: | + xcworkspace=$(find . -maxdepth 1 -name '*.xcworkspace' -type d | head -1) + xcodebuild -workspace ${xcworkspace} -scheme ${{ github.event.inputs.scheme }} -configuration Debug -derivedDataPath DerivedData CODE_SIGNING_ALLOWED=NO COMPILER_INDEX_STORE_ENABLE=NO build + working-directory: ${{ github.event.inputs.projectRoot }}/macos + - name: Prepare build artifact + run: | + output_path=DerivedData/Build/Products + app=$(find ${output_path} -maxdepth 2 -name '*.app' -type d | head -1) + # bsdtar corrupts files when archiving due to APFS sparse files. A + # workaround is to use GNU Tar instead. See also: + # - https://github.com/actions/cache/issues/403 + # - https://github.com/actions/virtual-environments/issues/2619 + gtar -cvf macos-artifact.tar -C "$(dirname ${app})" "$(basename ${app})" + shasum --algorithm 256 macos-artifact.tar + working-directory: ${{ github.event.inputs.projectRoot }}/macos + - name: Upload build artifact + uses: actions/upload-artifact@v3 + with: + name: macos-artifact + path: ${{ github.event.inputs.projectRoot }}/macos/macos-artifact.tar + if-no-files-found: error + build-windows: + name: "Build Windows" + if: ${{ github.event.inputs.platform == 'windows' }} + runs-on: windows-2022 + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set up MSBuild + uses: microsoft/setup-msbuild@v1.1 + - name: Set up Node 16 + uses: actions/setup-node@v3.3.0 + with: + node-version: 16 + - name: Install npm dependencies + run: ${{ github.event.inputs.packageManager }} install + - name: Install Windows test app + run: | + npx --package react-native-test-app -- install-windows-test-app --use-nuget + working-directory: ${{ github.event.inputs.projectRoot }} + - name: Install NuGet packages + run: | + nuget restore + working-directory: ${{ github.event.inputs.projectRoot }}/windows + - name: Build Windows app + run: | + MSBuild -maxCpuCount -property:Configuration=Debug -property:Platform=${{ github.event.inputs.architecture }} -property:AppxBundlePlatforms=${{ github.event.inputs.architecture }} -property:AppxBundle=Always -property:UapAppxPackageBuildMode=SideloadOnly -property:UseBundle=false -target:Build + working-directory: ${{ github.event.inputs.projectRoot }}/windows + - name: Prepare build artifact + id: prepare-build-artifact + run: | + appx_manifest=$(find ${{ github.event.inputs.architecture }}/Debug -name AppxManifest.xml -type f | head -1) + app_name=$(basename $(dirname ${appx_manifest})) + cp ${appx_manifest} AppPackages/${app_name}/* + echo "::set-output name=app-name::${app_name}" + shell: bash + working-directory: ${{ github.event.inputs.projectRoot }}/windows + - name: Upload build artifact + uses: actions/upload-artifact@v3 + with: + name: windows-artifact + path: ${{ github.event.inputs.projectRoot }}/windows/AppPackages/${{ steps.prepare-build-artifact.outputs.app-name }} + if-no-files-found: error