ci(desktop): stop uploading build artifacts on every run (#149) #414
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
| #------------------------------------------------------------------------------- | |
| # Workflow configuration | |
| #------------------------------------------------------------------------------- | |
| name: "Mobile CI builds" | |
| on: | |
| push: | |
| pull_request: | |
| workflow_dispatch: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
| #------------------------------------------------------------------------------- | |
| # Define application name & version | |
| #------------------------------------------------------------------------------- | |
| env: | |
| APP_NAME: "Theengs" | |
| APP_VERSION: "1.6.0" | |
| QT_VERSION: "6.10.3" | |
| #------------------------------------------------------------------------------- | |
| # Workflow jobs | |
| #------------------------------------------------------------------------------- | |
| jobs: | |
| ## Android build ############################################################# | |
| build-android: | |
| name: "Android CI build" | |
| runs-on: ubuntu-24.04 | |
| env: | |
| DISTRIBUTE: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/development' || startsWith(github.ref, 'refs/tags/v')) && github.repository == 'theengs/app' }} | |
| REPO_NAME: ${{ github.event.repository.name }} | |
| steps: | |
| # Checkout repository (and submodules) | |
| - name: Checkout repository (and submodules) | |
| uses: actions/checkout@v5 | |
| with: | |
| submodules: recursive | |
| fetch-depth: 0 # needed so `git describe` can read the TheengsDecoder submodule tag | |
| # Java environment (already installed in 'ubuntu-24.04') | |
| #- name: Setup Java environment | |
| # uses: actions/setup-java@v4 | |
| # with: | |
| # distribution: 'temurin' | |
| # java-version: '17' | |
| # Android environment | |
| - name: Setup Android environment | |
| uses: android-actions/setup-android@v4 | |
| - name: Install Android SDK / NDK / tools | |
| run: | | |
| sdkmanager "platforms;android-36" | |
| sdkmanager "ndk;28.2.13676358" | |
| sdkmanager "build-tools;36.1.0" | |
| # Install Qt (desktop + Android arm64-v8a, armeabi-v7a, x86_64) | |
| - name: Install Qt (desktop & Android arm64-v8a) | |
| uses: jurplel/install-qt-action@v4 | |
| with: | |
| version: ${{env.QT_VERSION}} | |
| host: 'linux' | |
| target: 'android' | |
| arch: 'android_arm64_v8a' | |
| modules: qtconnectivity qtwebsockets qtcharts | |
| extra: '--autodesktop' | |
| # Add x86_64 (for Chromebooks / Play Store on x86 devices) and armv7 | |
| # (32-bit ARM, for the long tail of old phones repurposed as BLE→MQTT | |
| # gateways) via aqtinstall. | |
| - name: Install Qt (Android x86_64 & armv7) via aqtinstall | |
| run: | | |
| python3 -m pip install --upgrade aqtinstall | |
| QT_PARENT="/home/runner/work/${REPO_NAME}/Qt" | |
| for arch in android_x86_64 android_armv7; do | |
| aqt install-qt linux android "${QT_VERSION}" "${arch}" \ | |
| -m qtconnectivity qtwebsockets qtcharts \ | |
| -O "${QT_PARENT}" | |
| done | |
| # Normalise +x across every Android arch's tools (aqt usually | |
| # preserves perms, but install-qt-action's arm64 install did not). | |
| for d in "${QT_PARENT}/${QT_VERSION}/android_"*; do | |
| [ -d "$d/bin" ] && chmod -R +x "$d/bin" | |
| [ -d "$d/libexec" ] && chmod -R +x "$d/libexec" | |
| done | |
| # Qt 6.10.3's prebuilt android_armv7 package was assembled on a | |
| # Windows host (target_qt.conf records HostSpec=win32-g++ and | |
| # Prefix=C:/Qt/Qt-6.10.3). Two classes of cross-host bug leak into | |
| # downstream module configures on Linux: | |
| # 1. Unix shell scripts in bin/ and libexec/ (qt-configure-module, | |
| # qt-cmake, libexec/qt-cmake-private) have backslash path | |
| # separators baked in by file(TO_NATIVE_PATH) during the | |
| # upstream build — bash treats `\lib` as a literal, not as | |
| # `/lib`, so qt-configure-module fails with "Not a file". | |
| # 2. The Windows install Prefix `C:/Qt/Qt-6.10.3` is hardcoded in | |
| # target_qt.conf and in QtBuildInternalsExtra.cmake. On Linux | |
| # `C:/...` is not absolute, so file(RELATIVE_PATH) rejects it | |
| # when downstream modules (qtmqtt, qtconnectivity) compute | |
| # their own install destinations. | |
| # Both are fixed by in-place text rewrites; arm64-v8a and x86_64 | |
| # prebuilts don't carry either defect so they need no patching. | |
| - name: Patch Qt android_armv7 Windows-host artefacts | |
| run: | | |
| QT_ARMV7="/home/runner/work/${REPO_NAME}/Qt/${QT_VERSION}/android_armv7" | |
| # (1) Backslash path separators in Unix wrapper scripts. | |
| # PCRE pattern: literal `\` followed by a Qt path component. | |
| # Replacement targets only `\<letter>` so bash line | |
| # continuations (`\` + newline) survive — clobbering those | |
| # would break qt-configure-module's qt-cmake-private call. | |
| mapfile -t bad_files < <( | |
| grep -rlP '\\(lib|bin|libexec|include|share|mkspecs)' \ | |
| "${QT_ARMV7}/bin" "${QT_ARMV7}/libexec" "${QT_ARMV7}/lib/cmake" \ | |
| 2>/dev/null || true | |
| ) | |
| echo "Normalising backslashes in ${#bad_files[@]} file(s):" | |
| printf ' %s\n' "${bad_files[@]}" | |
| for f in "${bad_files[@]}"; do | |
| sed -i 's|\\\([a-zA-Z]\)|/\1|g' "$f" | |
| done | |
| # (2) Windows install Prefix in target_qt.conf + every other | |
| # file that recorded it. Rewrite to the actual Linux install | |
| # dir of this Qt prebuilt. | |
| mapfile -t cqt_files < <( | |
| grep -rlF "C:/Qt/Qt-${QT_VERSION}" "$QT_ARMV7" 2>/dev/null || true | |
| ) | |
| echo "Rewriting Windows install Prefix in ${#cqt_files[@]} file(s):" | |
| printf ' %s\n' "${cqt_files[@]}" | |
| for f in "${cqt_files[@]}"; do | |
| sed -i "s|C:/Qt/Qt-${QT_VERSION}|${QT_ARMV7}|g" "$f" | |
| done | |
| remaining=$(grep -rlF "C:/Qt/Qt-${QT_VERSION}" "$QT_ARMV7" 2>/dev/null | wc -l) | |
| [ "$remaining" -eq 0 ] || { echo "ERROR: ${remaining} file(s) still contain C:/Qt/Qt-${QT_VERSION}"; exit 1; } | |
| # Install dependencies (from package manager) | |
| - name: Install dependencies (from package manager) | |
| run: | | |
| sudo apt-get install cmake ninja-build -y; | |
| # Setup env | |
| - name: Setup env | |
| run: | | |
| echo "QT_HOST_PATH=/home/runner/work/${REPO_NAME}/Qt/${QT_VERSION}/gcc_64" >> "$GITHUB_ENV" | |
| echo "QT_TARGET_PATH=/home/runner/work/${REPO_NAME}/Qt/${QT_VERSION}/android_arm64_v8a" >> "$GITHUB_ENV" | |
| qmake --version | |
| cmake --version | |
| ninja --version | |
| # Derive marketing version from tag (no-op on non-tag pushes) | |
| - name: Resolve version from tag | |
| run: bash scripts/ci-resolve-version.sh android | |
| # Sanity-check each Android Qt install before contribs uses them. | |
| # Surfaces "missing files" or "binary not runnable" failures with a | |
| # clear error message in the workflow log instead of a silent exit 1. | |
| - name: Verify Android Qt installs | |
| run: | | |
| QT_PARENT="/home/runner/work/${REPO_NAME}/Qt" | |
| for arch in android_arm64_v8a android_armv7 android_x86_64; do | |
| D="${QT_PARENT}/${QT_VERSION}/${arch}" | |
| echo "===== ${arch} =====" | |
| if [ ! -d "$D" ]; then echo "MISSING: $D"; exit 1; fi | |
| echo "qt-cmake: $(test -x "$D/bin/qt-cmake" && echo OK || echo MISSING)" | |
| echo "qt-configure-module:$(test -x "$D/bin/qt-configure-module" && echo OK || echo MISSING)" | |
| echo "qmake: $(test -x "$D/bin/qmake" && echo OK || echo MISSING)" | |
| echo "Qt6Config.cmake: $(test -f "$D/lib/cmake/Qt6/Qt6Config.cmake" && echo OK || echo MISSING)" | |
| echo "qtbase headers: $(test -d "$D/include/QtCore" && echo OK || echo MISSING)" | |
| "$D/bin/qmake" --version || { echo "qmake failed for ${arch}"; exit 1; } | |
| done | |
| # Build dependencies for all three Android ABIs. | |
| # Run with -x so qt-configure-module stderr is visible in the workflow log. | |
| - name: Build dependencies (from contribs script) | |
| run: | | |
| cd contribs/ | |
| python3 contribs_builder_qt.py --targets=android_armv8,android_armv7,android_x86_64 --software qtmqtt,qtconnectivity --qt-directory "${QT_ROOT_DIR}/../.." --qt-version "${QT_VERSION}" | |
| python3 contribs_builder.py --targets=android_armv8,android_armv7,android_x86_64 --software mbedtls --qt-directory "${QT_ROOT_DIR}/../.." --qt-version "${QT_VERSION}" | |
| # Decode signing keystore (release-track runs only) | |
| - name: Decode signing keystore | |
| if: env.DISTRIBUTE == 'true' | |
| env: | |
| ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} | |
| ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} | |
| ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} | |
| ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} | |
| run: | | |
| # Re-register sensitive values for the runner's log-masking filter | |
| # so they get redacted in every subsequent step's stdout/stderr, | |
| # not just steps that consume them via ${{ secrets.* }} interpolation. | |
| echo "::add-mask::$ANDROID_KEYSTORE_PASSWORD" | |
| echo "::add-mask::$ANDROID_KEY_PASSWORD" | |
| echo "::add-mask::$ANDROID_KEY_ALIAS" | |
| echo "$ANDROID_KEYSTORE_BASE64" | base64 -d > "$RUNNER_TEMP/upload.keystore" | |
| { | |
| echo "ANDROID_KEYSTORE_PATH=$RUNNER_TEMP/upload.keystore" | |
| echo "ANDROID_KEYSTORE_PASSWORD=$ANDROID_KEYSTORE_PASSWORD" | |
| echo "ANDROID_KEY_ALIAS=$ANDROID_KEY_ALIAS" | |
| echo "ANDROID_KEY_PASSWORD=$ANDROID_KEY_PASSWORD" | |
| } >> "$GITHUB_ENV" | |
| # Build application (compile-only for PRs, AAB for release branch / tags). | |
| # On release-track runs, pass QT_ANDROID_KEYSTORE_* so Qt's androiddeployqt | |
| # injects --sign into the gradle invocation it spawns. Do NOT mirror this | |
| # with a gradle signingConfigs block — Qt expects to own the signing. | |
| - name: Build application | |
| run: | | |
| "${QT_TARGET_PATH}/bin/qt-cmake" --version | |
| SIGN_ARGS=() | |
| if [ "$DISTRIBUTE" = "true" ]; then | |
| SIGN_ARGS=( | |
| -DQT_ANDROID_KEYSTORE_PATH="${ANDROID_KEYSTORE_PATH}" | |
| -DQT_ANDROID_KEYSTORE_ALIAS="${ANDROID_KEY_ALIAS}" | |
| -DQT_ANDROID_KEYSTORE_STORE_PASS="${ANDROID_KEYSTORE_PASSWORD}" | |
| -DQT_ANDROID_KEYSTORE_KEY_PASS="${ANDROID_KEY_PASSWORD}" | |
| ) | |
| fi | |
| "${QT_TARGET_PATH}/bin/qt-cmake" -B build/ -G Ninja \ | |
| -DCMAKE_SYSTEM_NAME=Android \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DCMAKE_FIND_ROOT_PATH:PATH="${QT_TARGET_PATH}" \ | |
| -DCMAKE_PREFIX_PATH:PATH="${QT_TARGET_PATH}" \ | |
| -DANDROID_SDK_ROOT="$ANDROID_SDK_ROOT" \ | |
| -DANDROID_NDK_ROOT="$ANDROID_NDK_ROOT" \ | |
| -DANDROID_PLATFORM=android-28 \ | |
| -DQT_HOST_PATH:PATH="${QT_HOST_PATH}" \ | |
| -DQT_ANDROID_BUILD_ALL_ABIS=OFF \ | |
| -DQT_ANDROID_ABIS="arm64-v8a;armeabi-v7a;x86_64" \ | |
| "${SIGN_ARGS[@]}" | |
| if [ "$DISTRIBUTE" = "true" ]; then | |
| cmake --build build/ --target aab | |
| else | |
| cmake --build build/ --config Release | |
| fi | |
| # Post-mortem if the AAB target failed: list outputs and surface any | |
| # extra log files androiddeployqt may have produced (silent exit-20 case). | |
| - name: Diagnose Android build failure | |
| if: failure() | |
| run: | | |
| echo "===== build/android-build/build/outputs/ =====" | |
| find build/android-build/build/outputs/ -type f 2>&1 | head -50 || true | |
| echo | |
| echo "===== AAB internal structure (if any) =====" | |
| for aab in build/android-build/build/outputs/bundle/release/*.aab; do | |
| [ -f "$aab" ] || continue | |
| echo "--- $aab ---" | |
| unzip -l "$aab" | head -50 | |
| done || true | |
| echo | |
| echo "===== gradle reports (if any errors) =====" | |
| find build/android-build/build/reports/ -type f -name '*.html' 2>&1 | head -10 || true | |
| # Sign the AAB with jarsigner. Qt's QT_ANDROID_KEYSTORE_* CMake vars are | |
| # read as target properties (not variables), so the -D flags we pass to | |
| # qt-cmake don't propagate into androiddeployqt's --sign injection, and | |
| # the AAB lands at outputs/bundle/release/ unsigned. Sign it ourselves | |
| # using -storepass:env / -keypass:env so passwords never appear on argv. | |
| - name: Sign AAB with jarsigner | |
| if: env.DISTRIBUTE == 'true' | |
| run: | | |
| AAB=$(ls build/android-build/build/outputs/bundle/release/*.aab | head -n1) | |
| echo "Signing: $AAB" | |
| jarsigner -sigalg SHA256withRSA -digestalg SHA-256 \ | |
| -keystore "$ANDROID_KEYSTORE_PATH" \ | |
| -storepass:env ANDROID_KEYSTORE_PASSWORD \ | |
| -keypass:env ANDROID_KEY_PASSWORD \ | |
| "$AAB" "$ANDROID_KEY_ALIAS" | |
| # Verify the signature. Do NOT use -strict: Android upload keys | |
| # are self-signed by design, and -strict treats self-signed certs | |
| # plus the missing TSA timestamp as errors. "jar verified" without | |
| # a non-zero exit is what we need; the self-signed / no-timestamp | |
| # warnings are expected and benign for Play Console uploads. | |
| echo "Verifying signature..." | |
| jarsigner -verify "$AAB" | |
| # Upload AAB to Play Console (Internal testing track) | |
| - name: Upload AAB to Play Console | |
| if: env.DISTRIBUTE == 'true' | |
| uses: r0adkll/upload-google-play@v1 | |
| with: | |
| serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }} | |
| packageName: com.theengs.app | |
| releaseFiles: build/android-build/build/outputs/bundle/release/*.aab | |
| tracks: internal | |
| status: completed | |
| whatsNewDirectory: distribution/whatsnew | |
| ## iOS build ################################################################# | |
| build-ios: | |
| name: "iOS CI build" | |
| # macos-latest tracks GitHub's current macOS image and ships the most recent | |
| # Xcode. App Store Connect now requires builds against the iOS 26 SDK | |
| # (Xcode 26+); macos-15 with Xcode 16.x is rejected at upload validation. | |
| runs-on: macos-latest | |
| env: | |
| DISTRIBUTE: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/development' || startsWith(github.ref, 'refs/tags/v')) && github.repository == 'theengs/app' }} | |
| REPO_NAME: ${{ github.event.repository.name }} | |
| steps: | |
| # Checkout repository (and submodules) | |
| - name: Checkout repository (and submodules) | |
| uses: actions/checkout@v5 | |
| with: | |
| submodules: recursive | |
| fetch-depth: 0 # needed so `git describe` can read the TheengsDecoder submodule tag | |
| # Pin Xcode to the latest stable version available on the runner so | |
| # builds are reproducible and we get the iOS 26+ SDK that App Store | |
| # Connect now requires for uploads. | |
| - name: Select latest Xcode | |
| uses: maxim-lobanov/setup-xcode@v1 | |
| with: | |
| xcode-version: latest-stable | |
| # Install Qt (desktop & iOS) | |
| - name: Install Qt (desktop & iOS) | |
| uses: jurplel/install-qt-action@v4 | |
| with: | |
| version: ${{env.QT_VERSION}} | |
| host: 'mac' | |
| target: 'ios' | |
| extra: '--autodesktop' | |
| modules: qtconnectivity qtwebsockets qtcharts | |
| # Install dependencies (from package manager) | |
| #- name: Install dependencies (from package manager) | |
| # run: | | |
| # brew install cmake ninja | |
| # Setup env | |
| - name: Setup env | |
| run: | | |
| echo "QT_HOST_PATH=/Users/runner/work/${REPO_NAME}/Qt/${QT_VERSION}/macos" >> "$GITHUB_ENV" | |
| echo "QT_TARGET_PATH=/Users/runner/work/${REPO_NAME}/Qt/${QT_VERSION}/ios" >> "$GITHUB_ENV" | |
| qmake --version | |
| cmake --version | |
| ninja --version | |
| # Derive marketing version from tag (no-op on non-tag pushes) | |
| - name: Resolve version from tag | |
| run: bash scripts/ci-resolve-version.sh ios | |
| # Build dependencies (from contribs script) | |
| - name: Build dependencies (from contribs script) | |
| run: | | |
| cd contribs/ | |
| python3 contribs_builder_qt.py --targets=ios_armv8 --software qtmqtt --qt-directory "${QT_ROOT_DIR}/../.." --qt-version "${QT_VERSION}" | |
| # Patch CFBundleVersion to a monotonic value that strictly exceeds the | |
| # previously published build (Info.plist ships with 01060001). Using a | |
| # YYMMDDHHMM timestamp gives ~10-digit values like 2605110830 — always | |
| # higher than any prior, always increasing, always unique per run. | |
| - name: Patch iOS build number | |
| if: env.DISTRIBUTE == 'true' | |
| run: | | |
| NEW_BUILD=$(date +%y%m%d%H%M) | |
| /usr/libexec/PlistBuddy -c "Set :CFBundleVersion ${NEW_BUILD}" assets/ios/Info.plist | |
| /usr/libexec/PlistBuddy -c "Print :CFBundleVersion" assets/ios/Info.plist | |
| # Import App Store distribution signing certificate | |
| - name: Import signing certificate | |
| if: env.DISTRIBUTE == 'true' | |
| uses: apple-actions/import-codesign-certs@v7 | |
| with: | |
| p12-file-base64: ${{ secrets.APPLE_DIST_CERT_P12_BASE64 }} | |
| p12-password: ${{ secrets.APPLE_DIST_CERT_PASSWORD }} | |
| # Download App Store provisioning profile via App Store Connect API | |
| - name: Download provisioning profile | |
| if: env.DISTRIBUTE == 'true' | |
| id: provisioning | |
| uses: apple-actions/download-provisioning-profiles@v6 | |
| with: | |
| bundle-id: com.theengs.app | |
| profile-type: IOS_APP_STORE | |
| issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }} | |
| api-key-id: ${{ secrets.APPSTORE_KEY_ID }} | |
| api-private-key: ${{ secrets.APPSTORE_PRIVATE_KEY }} | |
| # Configure the Xcode project (signing only for release-track runs) | |
| - name: Configure project | |
| env: | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| run: | | |
| if [ "$DISTRIBUTE" = "true" ]; then | |
| "${QT_TARGET_PATH}/bin/qt-cmake" -B build/ -G Xcode \ | |
| -DCMAKE_SYSTEM_NAME=iOS \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DCMAKE_FIND_ROOT_PATH:PATH="${QT_TARGET_PATH}" \ | |
| -DCMAKE_PREFIX_PATH:PATH="${QT_TARGET_PATH}" \ | |
| -DQT_HOST_PATH:PATH="${QT_HOST_PATH}" \ | |
| -DCMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM="${APPLE_TEAM_ID}" \ | |
| -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_STYLE=Manual \ | |
| -DCMAKE_XCODE_ATTRIBUTE_PROVISIONING_PROFILE_SPECIFIER="Theengs App Store" \ | |
| -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY="Apple Distribution" | |
| else | |
| "${QT_TARGET_PATH}/bin/qt-cmake" -B build/ -G Xcode \ | |
| -DCMAKE_SYSTEM_NAME=iOS \ | |
| -DCMAKE_BUILD_TYPE=Release \ | |
| -DCMAKE_FIND_ROOT_PATH:PATH="${QT_TARGET_PATH}" \ | |
| -DCMAKE_PREFIX_PATH:PATH="${QT_TARGET_PATH}" \ | |
| -DQT_HOST_PATH:PATH="${QT_HOST_PATH}" \ | |
| -DCMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_REQUIRED="NO" | |
| fi | |
| # Compile-only path (PRs / forks / non-release branches) | |
| - name: Build application (compile only) | |
| if: env.DISTRIBUTE != 'true' | |
| run: cmake --build build/ --config Release -- CODE_SIGNING_ALLOWED=NO | |
| # Archive + export IPA (release-track runs only) | |
| - name: Archive and export IPA | |
| if: env.DISTRIBUTE == 'true' | |
| env: | |
| APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} | |
| APPSTORE_ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }} | |
| APPSTORE_KEY_ID: ${{ secrets.APPSTORE_KEY_ID }} | |
| APPSTORE_PRIVATE_KEY: ${{ secrets.APPSTORE_PRIVATE_KEY }} | |
| run: | | |
| # Mask sensitive App Store Connect API values across all subsequent | |
| # log output (defense in depth on top of GitHub's built-in masking). | |
| echo "::add-mask::$APPLE_TEAM_ID" | |
| echo "::add-mask::$APPSTORE_ISSUER_ID" | |
| echo "::add-mask::$APPSTORE_KEY_ID" | |
| # Resolve the profile NAME from the downloaded .mobileprovision file. | |
| PROFILE_FILE=$(ls -1 "$HOME/Library/MobileDevice/Provisioning Profiles/"*.mobileprovision | head -n1) | |
| security cms -D -i "$PROFILE_FILE" > /tmp/profile.plist | |
| PROFILE_NAME=$(/usr/libexec/PlistBuddy -c "Print :Name" /tmp/profile.plist) | |
| echo "Using provisioning profile: $PROFILE_NAME" | |
| # Materialise ExportOptions.plist with concrete values | |
| sed -e "s/__TEAM_ID__/${APPLE_TEAM_ID}/g" \ | |
| -e "s|__PROFILE_NAME__|${PROFILE_NAME}|g" \ | |
| distribution/ios/ExportOptions.plist > build/ExportOptions.plist | |
| # Materialise the App Store Connect API private key on disk so | |
| # xcodebuild can authenticate during -exportArchive. Without this, | |
| # exportArchive fails with "Failed to Use Accounts" because no | |
| # Apple ID is signed into the runner. | |
| ASC_KEY_DIR="$RUNNER_TEMP/asc-keys" | |
| mkdir -p "$ASC_KEY_DIR" | |
| ASC_KEY_PATH="$ASC_KEY_DIR/AuthKey_${APPSTORE_KEY_ID}.p8" | |
| printf '%s' "$APPSTORE_PRIVATE_KEY" > "$ASC_KEY_PATH" | |
| chmod 600 "$ASC_KEY_PATH" | |
| xcodebuild -project build/Theengs.xcodeproj \ | |
| -scheme Theengs \ | |
| -configuration Release \ | |
| -destination 'generic/platform=iOS' \ | |
| -archivePath build/Theengs.xcarchive \ | |
| -allowProvisioningUpdates \ | |
| -authenticationKeyID "$APPSTORE_KEY_ID" \ | |
| -authenticationKeyIssuerID "$APPSTORE_ISSUER_ID" \ | |
| -authenticationKeyPath "$ASC_KEY_PATH" \ | |
| archive | |
| xcodebuild -exportArchive \ | |
| -archivePath build/Theengs.xcarchive \ | |
| -exportPath build/ipa \ | |
| -exportOptionsPlist build/ExportOptions.plist \ | |
| -allowProvisioningUpdates \ | |
| -authenticationKeyID "$APPSTORE_KEY_ID" \ | |
| -authenticationKeyIssuerID "$APPSTORE_ISSUER_ID" \ | |
| -authenticationKeyPath "$ASC_KEY_PATH" |