From e76cfecac8b42af383370adcf5913547d7db8f2d Mon Sep 17 00:00:00 2001 From: dijunkun Date: Thu, 1 Oct 2026 23:31:51 +0800 Subject: [PATCH] [refactor] extract standalone iOS build workflow --- .github/workflows/build-ios.yml | 256 ++++++++++++++++++++++++++++++++ .github/workflows/build.yml | 225 +--------------------------- apps/ios/README.md | 24 ++- docs/BUILD.md | 2 + docs/BUILD_EN.md | 2 + 5 files changed, 290 insertions(+), 219 deletions(-) create mode 100644 .github/workflows/build-ios.yml diff --git a/.github/workflows/build-ios.yml b/.github/workflows/build-ios.yml new file mode 100644 index 00000000..9061a337 --- /dev/null +++ b/.github/workflows/build-ios.yml @@ -0,0 +1,256 @@ +name: Build iOS + +on: + workflow_dispatch: + inputs: + patch: + description: "Optional patch number, matching the main build workflow." + required: false + type: string + default: "0" + workflow_call: + inputs: + patch: + description: "Optional patch number from the main build workflow." + required: false + type: string + default: "0" + outputs: + version: + description: "Independent iOS version and build date." + value: ${{ jobs.build.outputs.version }} + pull_request: + paths: + - ".github/workflows/build-ios.yml" + - ".github/workflows/build.yml" + - ".gitmodules" + - "LICENSE" + - "apps/ios/**" + - "deps/submodules/minirtc" + - "libs/wire/**" + - "xmake.lua" + +permissions: + contents: read + +jobs: + build: + name: Build on iOS (arm64) + runs-on: macos-26 + timeout-minutes: 120 + outputs: + version: ${{ steps.ios_version.outputs.version }} + env: + GITHUB_TOKEN: ${{ github.token }} + IOS_DEPLOYMENT_TARGET: "16.0" + EXPECTED_IOS_SDK: "26.5" + XMAKE_VERSION: "3.1.1" + XMAKE_REPO_REVISION: "121109352da272cd1a3d4ed52f2dcda34f839955" + + steps: + - name: Extract version number + shell: bash + env: + PATCH_NUMBER: ${{ inputs.patch || '0' }} + run: | + VERSION_REF="${GITHUB_REF##*/}" + VERSION_BASE="${VERSION_REF#v}" + BUILD_DATE=$(TZ=Asia/Shanghai date +%Y%m%d) + + if [[ ! "${PATCH_NUMBER}" =~ ^[0-9]+$ ]]; then + PATCH_NUMBER=0 + fi + + if [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]+)-([0-9]{8})$ ]]; then + VERSION_BASE="${BASH_REMATCH[1]}" + PATCH_NUMBER="${BASH_REMATCH[3]}" + BUILD_DATE="${BASH_REMATCH[4]}" + elif [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]{8})-([0-9]+)$ ]]; then + VERSION_BASE="${BASH_REMATCH[1]}" + BUILD_DATE="${BASH_REMATCH[3]}" + PATCH_NUMBER="${BASH_REMATCH[4]}" + elif [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]{8})$ ]]; then + VERSION_BASE="${BASH_REMATCH[1]}" + BUILD_DATE="${BASH_REMATCH[3]}" + elif [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]+)$ && "${PATCH_NUMBER}" == "0" ]]; then + VERSION_BASE="${BASH_REMATCH[1]}" + PATCH_NUMBER="${BASH_REMATCH[3]}" + fi + + if [[ "${PATCH_NUMBER}" != "0" ]]; then + VERSION_NUM="v${VERSION_BASE}-${PATCH_NUMBER}-${BUILD_DATE}" + else + VERSION_NUM="v${VERSION_BASE}-${BUILD_DATE}" + fi + + echo "VERSION_NUM=${VERSION_NUM}" >> "${GITHUB_ENV}" + echo "VERSION_NUM=${VERSION_NUM}" + + - name: Select Xcode 26.6 + shell: bash + run: | + sudo xcode-select -s /Applications/Xcode_26.6.app/Contents/Developer + xcodebuild -version + SDK_VERSION="$(xcrun --sdk iphoneos --show-sdk-version)" + if [[ "${SDK_VERSION}" != "${EXPECTED_IOS_SDK}" ]]; then + echo "Unexpected iOS SDK: expected ${EXPECTED_IOS_SDK}, got ${SDK_VERSION}" >&2 + exit 1 + fi + + - name: Checkout code + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Select corresponding source tag + shell: bash + run: | + if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then + echo "CROSSDESK_SOURCE_TAG=${GITHUB_REF_NAME}" >> "${GITHUB_ENV}" + echo "CROSSDESK_RELEASE_BUILD=YES" >> "${GITHUB_ENV}" + fi + + - name: Read independent iOS version + id: ios_version + shell: bash + run: | + IOS_VERSION_BASE="$(xcodebuild \ + -project apps/ios/CrossDeskMobile.xcodeproj \ + -scheme CrossDeskMobile \ + -configuration Release \ + -destination 'generic/platform=iOS' \ + -showBuildSettings CODE_SIGNING_ALLOWED=NO | \ + awk '$1 == "MARKETING_VERSION" && $2 == "=" { print $3 }')" + if [[ ! "${IOS_VERSION_BASE}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Invalid iOS marketing version: ${IOS_VERSION_BASE}" >&2 + exit 1 + fi + VERSION_NUM="v${IOS_VERSION_BASE}-${VERSION_NUM##*-}" + echo "IOS_VERSION_BASE=${IOS_VERSION_BASE}" >> "${GITHUB_ENV}" + echo "VERSION_NUM=${VERSION_NUM}" >> "${GITHUB_ENV}" + echo "version=${VERSION_NUM}" >> "${GITHUB_OUTPUT}" + + - name: Initialize submodules + run: git submodule update --init --recursive + + - name: Cache xmake dependencies + uses: actions/cache@v5 + with: + path: apps/ios/.xmake/packages + key: "${{ runner.os }}-xmake${{ env.XMAKE_VERSION }}-deps-ios-arm64-source-v1-xcode26.6-xrepo1211093-min16-${{ github.run_id }}" + restore-keys: | + ${{ runner.os }}-xmake${{ env.XMAKE_VERSION }}-deps-ios-arm64-source-v1-xcode26.6-xrepo1211093-min16- + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.13" + + - name: Verify iOS license catalog + run: | + python3 -B -m unittest discover -s apps/ios/tests -p 'third_party_licenses_test.py' -v + python3 -B apps/ios/scripts/generate_third_party_licenses.py + + - name: Verify iOS privacy controls + run: python3 -B -m unittest discover -s apps/ios/tests -p 'privacy_controls_test.py' -v + + - name: Install xmake + uses: xmake-io/github-action-setup-xmake@v1 + with: + xmake-version: "${{ env.XMAKE_VERSION }}" + + - name: Verify xmake version + shell: bash + run: | + XMAKE_VERSION_OUTPUT="$(xmake --version)" + echo "${XMAKE_VERSION_OUTPUT}" + if [[ "${XMAKE_VERSION_OUTPUT}" != *"xmake v${XMAKE_VERSION}"* ]]; then + echo "Unexpected xmake version: expected ${XMAKE_VERSION}" >&2 + exit 1 + fi + + - name: Pin xmake package repository + shell: bash + run: | + xmake repo -u + XMAKE_REPO_DIR="${HOME}/.xmake/repositories/xmake-repo" + git -C "${XMAKE_REPO_DIR}" fetch --depth=1 origin "${XMAKE_REPO_REVISION}" + git -C "${XMAKE_REPO_DIR}" checkout --detach "${XMAKE_REPO_REVISION}" + test "$(git -C "${XMAKE_REPO_DIR}" rev-parse HEAD)" = "${XMAKE_REPO_REVISION}" + + - name: Build CrossDesk Mobile + shell: bash + run: | + DERIVED_DATA="${RUNNER_TEMP}/CrossDeskMobileDerivedData" + echo "DERIVED_DATA=${DERIVED_DATA}" >> "${GITHUB_ENV}" + xcodebuild \ + -project apps/ios/CrossDeskMobile.xcodeproj \ + -scheme CrossDeskMobile \ + -configuration Release \ + -destination 'generic/platform=iOS' \ + -derivedDataPath "${DERIVED_DATA}" \ + CODE_SIGNING_ALLOWED=NO \ + CODE_SIGNING_REQUIRED=NO \ + COMPILER_INDEX_STORE_ENABLE=NO \ + IPHONEOS_DEPLOYMENT_TARGET="${IOS_DEPLOYMENT_TARGET}" \ + build + + - name: Verify CrossDesk Mobile + shell: bash + run: | + APP_PATH="${DERIVED_DATA}/Build/Products/Release-iphoneos/CrossDeskMobile.app" + EXECUTABLE_PATH="${APP_PATH}/CrossDeskMobile" + INFO_PLIST="${APP_PATH}/Info.plist" + + if [[ ! -x "${EXECUTABLE_PATH}" ]]; then + echo "CrossDeskMobile executable is missing from ${APP_PATH}" >&2 + exit 1 + fi + + xcrun lipo "${EXECUTABLE_PATH}" -verify_arch arm64 + APP_VERSION=$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "${INFO_PLIST}") + if [[ "${APP_VERSION}" != "${IOS_VERSION_BASE}" ]]; then + echo "Unexpected iOS app version: expected ${IOS_VERSION_BASE}, got ${APP_VERSION}" >&2 + exit 1 + fi + MINIMUM_OS=$(/usr/libexec/PlistBuddy -c 'Print :MinimumOSVersion' "${INFO_PLIST}") + if [[ "${MINIMUM_OS}" != "${IOS_DEPLOYMENT_TARGET}" ]]; then + echo "Unexpected minimum iOS version: expected ${IOS_DEPLOYMENT_TARGET}, got ${MINIMUM_OS}" >&2 + exit 1 + fi + + VERIFY_FLAGS=() + if [[ "${CROSSDESK_RELEASE_BUILD:-NO}" == "YES" ]]; then + VERIFY_FLAGS+=(--require-release) + fi + python3 apps/ios/scripts/verify_app_bundle.py "${APP_PATH}" "${VERIFY_FLAGS[@]}" + xcrun lipo -info "${EXECUTABLE_PATH}" + echo "Minimum iOS version: ${MINIMUM_OS}" + + - name: Package CrossDesk Mobile + shell: bash + run: | + APP_PATH="${DERIVED_DATA}/Build/Products/Release-iphoneos/CrossDeskMobile.app" + PACKAGE_DIR="${RUNNER_TEMP}/crossdesk-ios-arm64-unsigned-${VERSION_NUM}" + PACKAGE_FILE="${GITHUB_WORKSPACE}/crossdesk-ios-arm64-unsigned-${VERSION_NUM}.zip" + + mkdir -p "${PACKAGE_DIR}" + cp -R "${APP_PATH}" "${PACKAGE_DIR}/" + cp apps/ios/licenses/sources.json "${PACKAGE_DIR}/ThirdPartySources.json" + cp apps/ios/licenses/OPEN_SOURCE_NOTICE.txt "${PACKAGE_DIR}/OPEN_SOURCE_NOTICE.txt" + cp "${APP_PATH}/SourceMetadata.json" "${PACKAGE_DIR}/SourceMetadata.json" + + DSYM_PATH="${DERIVED_DATA}/Build/Products/Release-iphoneos/CrossDeskMobile.app.dSYM" + if [[ -d "${DSYM_PATH}" ]]; then + cp -R "${DSYM_PATH}" "${PACKAGE_DIR}/" + fi + + ditto -c -k --sequesterRsrc --keepParent "${PACKAGE_DIR}" "${PACKAGE_FILE}" + echo "PACKAGE_FILE=${PACKAGE_FILE}" >> "${GITHUB_ENV}" + + - name: Upload build artifact + uses: actions/upload-artifact@v6 + with: + name: crossdesk-ios-arm64-unsigned-${{ env.VERSION_NUM }} + path: ${{ env.PACKAGE_FILE }} + if-no-files-found: error diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index b45e941d..eff22fdc 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -311,225 +311,14 @@ jobs: mkdir -p release cp crossdesk-macos-${{ matrix.arch }}-${{ env.VERSION_NUM }}.pkg release/ - # iOS + # iOS (also available as a standalone workflow) build-ios: - name: Build on iOS (arm64) - runs-on: macos-26 - timeout-minutes: 120 - outputs: - version: ${{ steps.ios_version.outputs.version }} - env: - IOS_DEPLOYMENT_TARGET: "16.0" - EXPECTED_IOS_SDK: "26.5" - XMAKE_VERSION: "3.1.1" - XMAKE_REPO_REVISION: "121109352da272cd1a3d4ed52f2dcda34f839955" - - steps: - - name: Extract version number - shell: bash - run: | - VERSION_REF="${GITHUB_REF##*/}" - VERSION_BASE="${VERSION_REF#v}" - BUILD_DATE=$(TZ=Asia/Shanghai date +%Y%m%d) - PATCH_NUMBER="${{ github.event.inputs.patch }}" - - if [[ ! "${PATCH_NUMBER}" =~ ^[0-9]+$ ]]; then - PATCH_NUMBER=0 - fi - - if [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]+)-([0-9]{8})$ ]]; then - VERSION_BASE="${BASH_REMATCH[1]}" - PATCH_NUMBER="${BASH_REMATCH[3]}" - BUILD_DATE="${BASH_REMATCH[4]}" - elif [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]{8})-([0-9]+)$ ]]; then - VERSION_BASE="${BASH_REMATCH[1]}" - BUILD_DATE="${BASH_REMATCH[3]}" - PATCH_NUMBER="${BASH_REMATCH[4]}" - elif [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]{8})$ ]]; then - VERSION_BASE="${BASH_REMATCH[1]}" - BUILD_DATE="${BASH_REMATCH[3]}" - elif [[ "${VERSION_BASE}" =~ ^([0-9]+(\.[0-9]+){1,3})-([0-9]+)$ && "${PATCH_NUMBER}" == "0" ]]; then - VERSION_BASE="${BASH_REMATCH[1]}" - PATCH_NUMBER="${BASH_REMATCH[3]}" - fi - - if [[ "${PATCH_NUMBER}" != "0" ]]; then - VERSION_NUM="v${VERSION_BASE}-${PATCH_NUMBER}-${BUILD_DATE}" - else - VERSION_NUM="v${VERSION_BASE}-${BUILD_DATE}" - fi - - echo "VERSION_NUM=${VERSION_NUM}" >> "${GITHUB_ENV}" - echo "VERSION_NUM=${VERSION_NUM}" - - - name: Select Xcode 26.6 - shell: bash - run: | - sudo xcode-select -s /Applications/Xcode_26.6.app/Contents/Developer - xcodebuild -version - SDK_VERSION="$(xcrun --sdk iphoneos --show-sdk-version)" - if [[ "${SDK_VERSION}" != "${EXPECTED_IOS_SDK}" ]]; then - echo "Unexpected iOS SDK: expected ${EXPECTED_IOS_SDK}, got ${SDK_VERSION}" >&2 - exit 1 - fi - - - name: Checkout code - uses: actions/checkout@v5 - with: - fetch-depth: 0 - - - name: Select corresponding source tag - shell: bash - run: | - if [[ "${GITHUB_REF_TYPE}" == "tag" ]]; then - echo "CROSSDESK_SOURCE_TAG=${GITHUB_REF_NAME}" >> "${GITHUB_ENV}" - echo "CROSSDESK_RELEASE_BUILD=YES" >> "${GITHUB_ENV}" - fi - - - name: Read independent iOS version - id: ios_version - shell: bash - run: | - IOS_VERSION_BASE="$(xcodebuild \ - -project apps/ios/CrossDeskMobile.xcodeproj \ - -scheme CrossDeskMobile \ - -configuration Release \ - -destination 'generic/platform=iOS' \ - -showBuildSettings CODE_SIGNING_ALLOWED=NO | \ - awk '$1 == "MARKETING_VERSION" && $2 == "=" { print $3 }')" - if [[ ! "${IOS_VERSION_BASE}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then - echo "Invalid iOS marketing version: ${IOS_VERSION_BASE}" >&2 - exit 1 - fi - VERSION_NUM="v${IOS_VERSION_BASE}-${VERSION_NUM##*-}" - echo "IOS_VERSION_BASE=${IOS_VERSION_BASE}" >> "${GITHUB_ENV}" - echo "VERSION_NUM=${VERSION_NUM}" >> "${GITHUB_ENV}" - echo "version=${VERSION_NUM}" >> "${GITHUB_OUTPUT}" - - - name: Initialize submodules - run: git submodule update --init --recursive - - - name: Cache xmake dependencies - uses: actions/cache@v5 - with: - path: apps/ios/.xmake/packages - key: "${{ runner.os }}-xmake${{ env.XMAKE_VERSION }}-deps-ios-arm64-source-v1-xcode26.6-xrepo1211093-min16-${{ github.run_id }}" - restore-keys: | - ${{ runner.os }}-xmake${{ env.XMAKE_VERSION }}-deps-ios-arm64-source-v1-xcode26.6-xrepo1211093-min16- - - - name: Set up Python - uses: actions/setup-python@v6 - with: - python-version: "3.13" - - - name: Verify iOS license catalog - run: | - python3 -B -m unittest discover -s apps/ios/tests -p 'third_party_licenses_test.py' -v - python3 -B apps/ios/scripts/generate_third_party_licenses.py - - - name: Verify iOS privacy controls - run: python3 -B -m unittest discover -s apps/ios/tests -p 'privacy_controls_test.py' -v - - - name: Install xmake - uses: xmake-io/github-action-setup-xmake@v1 - with: - xmake-version: "${{ env.XMAKE_VERSION }}" - - - name: Verify xmake version - shell: bash - run: | - XMAKE_VERSION_OUTPUT="$(xmake --version)" - echo "${XMAKE_VERSION_OUTPUT}" - if [[ "${XMAKE_VERSION_OUTPUT}" != *"xmake v${XMAKE_VERSION}"* ]]; then - echo "Unexpected xmake version: expected ${XMAKE_VERSION}" >&2 - exit 1 - fi - - - name: Pin xmake package repository - shell: bash - run: | - xmake repo -u - XMAKE_REPO_DIR="${HOME}/.xmake/repositories/xmake-repo" - git -C "${XMAKE_REPO_DIR}" fetch --depth=1 origin "${XMAKE_REPO_REVISION}" - git -C "${XMAKE_REPO_DIR}" checkout --detach "${XMAKE_REPO_REVISION}" - test "$(git -C "${XMAKE_REPO_DIR}" rev-parse HEAD)" = "${XMAKE_REPO_REVISION}" - - - name: Build CrossDesk Mobile - shell: bash - run: | - DERIVED_DATA="${RUNNER_TEMP}/CrossDeskMobileDerivedData" - echo "DERIVED_DATA=${DERIVED_DATA}" >> "${GITHUB_ENV}" - xcodebuild \ - -project apps/ios/CrossDeskMobile.xcodeproj \ - -scheme CrossDeskMobile \ - -configuration Release \ - -destination 'generic/platform=iOS' \ - -derivedDataPath "${DERIVED_DATA}" \ - CODE_SIGNING_ALLOWED=NO \ - CODE_SIGNING_REQUIRED=NO \ - COMPILER_INDEX_STORE_ENABLE=NO \ - IPHONEOS_DEPLOYMENT_TARGET="${IOS_DEPLOYMENT_TARGET}" \ - build - - - name: Verify CrossDesk Mobile - shell: bash - run: | - APP_PATH="${DERIVED_DATA}/Build/Products/Release-iphoneos/CrossDeskMobile.app" - EXECUTABLE_PATH="${APP_PATH}/CrossDeskMobile" - INFO_PLIST="${APP_PATH}/Info.plist" - - if [[ ! -x "${EXECUTABLE_PATH}" ]]; then - echo "CrossDeskMobile executable is missing from ${APP_PATH}" >&2 - exit 1 - fi - - xcrun lipo "${EXECUTABLE_PATH}" -verify_arch arm64 - APP_VERSION=$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "${INFO_PLIST}") - if [[ "${APP_VERSION}" != "${IOS_VERSION_BASE}" ]]; then - echo "Unexpected iOS app version: expected ${IOS_VERSION_BASE}, got ${APP_VERSION}" >&2 - exit 1 - fi - MINIMUM_OS=$(/usr/libexec/PlistBuddy -c 'Print :MinimumOSVersion' "${INFO_PLIST}") - if [[ "${MINIMUM_OS}" != "${IOS_DEPLOYMENT_TARGET}" ]]; then - echo "Unexpected minimum iOS version: expected ${IOS_DEPLOYMENT_TARGET}, got ${MINIMUM_OS}" >&2 - exit 1 - fi - - VERIFY_FLAGS=() - if [[ "${CROSSDESK_RELEASE_BUILD:-NO}" == "YES" ]]; then - VERIFY_FLAGS+=(--require-release) - fi - python3 apps/ios/scripts/verify_app_bundle.py "${APP_PATH}" "${VERIFY_FLAGS[@]}" - xcrun lipo -info "${EXECUTABLE_PATH}" - echo "Minimum iOS version: ${MINIMUM_OS}" - - - name: Package CrossDesk Mobile - shell: bash - run: | - APP_PATH="${DERIVED_DATA}/Build/Products/Release-iphoneos/CrossDeskMobile.app" - PACKAGE_DIR="${RUNNER_TEMP}/crossdesk-ios-arm64-unsigned-${VERSION_NUM}" - PACKAGE_FILE="${GITHUB_WORKSPACE}/crossdesk-ios-arm64-unsigned-${VERSION_NUM}.zip" - - mkdir -p "${PACKAGE_DIR}" - cp -R "${APP_PATH}" "${PACKAGE_DIR}/" - cp apps/ios/licenses/sources.json "${PACKAGE_DIR}/ThirdPartySources.json" - cp apps/ios/licenses/OPEN_SOURCE_NOTICE.txt "${PACKAGE_DIR}/OPEN_SOURCE_NOTICE.txt" - cp "${APP_PATH}/SourceMetadata.json" "${PACKAGE_DIR}/SourceMetadata.json" - - DSYM_PATH="${DERIVED_DATA}/Build/Products/Release-iphoneos/CrossDeskMobile.app.dSYM" - if [[ -d "${DSYM_PATH}" ]]; then - cp -R "${DSYM_PATH}" "${PACKAGE_DIR}/" - fi - - ditto -c -k --sequesterRsrc --keepParent "${PACKAGE_DIR}" "${PACKAGE_FILE}" - echo "PACKAGE_FILE=${PACKAGE_FILE}" >> "${GITHUB_ENV}" - - - name: Upload build artifact - uses: actions/upload-artifact@v6 - with: - name: crossdesk-ios-arm64-unsigned-${{ env.VERSION_NUM }} - path: ${{ env.PACKAGE_FILE }} - if-no-files-found: error + name: Build iOS + uses: ./.github/workflows/build-ios.yml + with: + patch: ${{ inputs.patch || '0' }} + permissions: + contents: read # Android (also available as a standalone workflow) build-android: diff --git a/apps/ios/README.md b/apps/ios/README.md index 3bdd57de..ceca374e 100644 --- a/apps/ios/README.md +++ b/apps/ios/README.md @@ -59,7 +59,7 @@ and builds their dependencies, then merges the iPhoneOS archives into a local li `Vendor/`. OpenSSL is kept separate and packaged as a static framework with SDK privacy resources in the Xcode products directory. This build path targets physical devices; simulator builds are not supported. The exact tool versions used by CI are recorded in the -[workflow](../../.github/workflows/build.yml). +[iOS workflow](../../.github/workflows/build-ios.yml). You can also build the target without code signing. The resulting app, like the unsigned CI artifact, requires signing before device installation: @@ -77,6 +77,28 @@ for that server. Remote control sessions then log in as `C-` and use the desktop-compatible `DisplayN`, `control_audio`, `mouse`, `keyboard`, `control_data`, `clipboard`, `file`, and `file_feedback` streams. +## GitHub Actions + +[`Build iOS`](../../.github/workflows/build-ios.yml) can run independently from +the Actions page. Pull requests changing the iOS app, MiniRTC, shared wire code +or relevant build configuration also trigger it. The main +[`Build and Release`](../../.github/workflows/build.yml) workflow calls the same +workflow on branch/tag pushes and manual runs, and waits for its result before +publishing a version-tag release. + +The workflow retains the pinned Xcode/SDK and Xmake versions, dependency cache, +license and privacy checks, unsigned arm64 Release build, and app-bundle +verification. It uploads `crossdesk-ios-arm64-unsigned-v-.zip`, +including the app, source metadata, third-party source catalog, rights notice +and any generated dSYM. The version comes from the iOS target; dated repository +tags provide the build date, otherwise the date uses the Asia/Shanghai timezone. +The main workflow collects this same ZIP for GitHub Releases and the download +server. Standalone runs only upload the build artifact. + +No Apple signing secrets are needed. Sign the app yourself before installing +it on a device. Push any referenced MiniRTC commit before the parent repository +so the runner can check out all corresponding sources. + ## Announcements The home toolbar's bell opens **通知公告** and shows the unread count (capped diff --git a/docs/BUILD.md b/docs/BUILD.md index a7429a37..181128a3 100644 --- a/docs/BUILD.md +++ b/docs/BUILD.md @@ -238,6 +238,8 @@ xmake r -d crossdesk 原生客户端使用独立 Xcode 工程,要求 iOS 16+ 的 arm64 真机。构建与音视频、文件传输验证步骤见 [iOS 开发说明](../apps/ios/README.md)。当前 CI 导出未签名应用,真机安装需要自行签名。 +[iOS CI](../.github/workflows/build-ios.yml) 可独立手动运行,并由主构建工作流调用;相关 Pull Request 也会触发。工具链、许可证及隐私检查、应用验证和 ZIP 打包沿用现有流程,标签发布继续收集同名的未签名应用归档。 + ## 更新界面截图 README 的桌面图片直接由当前 Slint UI 渲染,复现方式见 [截图说明](images/README.md)。 diff --git a/docs/BUILD_EN.md b/docs/BUILD_EN.md index 1d7a5c77..b5f8bc5f 100644 --- a/docs/BUILD_EN.md +++ b/docs/BUILD_EN.md @@ -250,6 +250,8 @@ For more information, please refer to the [official Xmake documentation](https:/ The native client has a separate Xcode project and requires an arm64 physical device running iOS 16+. See the [iOS development guide](../apps/ios/README.md) for build and media/file-transfer checks. CI exports an unsigned app; device installation requires your own signing. +[iOS CI](../.github/workflows/build-ios.yml) can run manually on its own, is called by the main build workflow, and runs for relevant pull requests. It retains the existing toolchain, license/privacy checks, app verification and ZIP packaging. Tag releases continue to collect the unsigned app archive under the same artifact name. + ## Refreshing screenshots Desktop README images are rendered directly from the current Slint UI. See the [screenshot notes](images/README.md) for reproduction commands.