mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-20 11:06:46 +08:00
[refactor] extract standalone iOS build workflow
This commit is contained in:
@@ -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
|
||||||
+7
-218
@@ -311,225 +311,14 @@ jobs:
|
|||||||
mkdir -p release
|
mkdir -p release
|
||||||
cp crossdesk-macos-${{ matrix.arch }}-${{ env.VERSION_NUM }}.pkg release/
|
cp crossdesk-macos-${{ matrix.arch }}-${{ env.VERSION_NUM }}.pkg release/
|
||||||
|
|
||||||
# iOS
|
# iOS (also available as a standalone workflow)
|
||||||
build-ios:
|
build-ios:
|
||||||
name: Build on iOS (arm64)
|
name: Build iOS
|
||||||
runs-on: macos-26
|
uses: ./.github/workflows/build-ios.yml
|
||||||
timeout-minutes: 120
|
with:
|
||||||
outputs:
|
patch: ${{ inputs.patch || '0' }}
|
||||||
version: ${{ steps.ios_version.outputs.version }}
|
permissions:
|
||||||
env:
|
contents: read
|
||||||
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
|
|
||||||
|
|
||||||
# Android (also available as a standalone workflow)
|
# Android (also available as a standalone workflow)
|
||||||
build-android:
|
build-android:
|
||||||
|
|||||||
+23
-1
@@ -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
|
`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
|
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
|
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
|
You can also build the target without code signing. The resulting app, like
|
||||||
the unsigned CI artifact, requires signing before device installation:
|
the unsigned CI artifact, requires signing before device installation:
|
||||||
@@ -77,6 +77,28 @@ for that server. Remote control sessions then log in as `C-<identity>` and use
|
|||||||
the desktop-compatible `DisplayN`, `control_audio`, `mouse`, `keyboard`,
|
the desktop-compatible `DisplayN`, `control_audio`, `mouse`, `keyboard`,
|
||||||
`control_data`, `clipboard`, `file`, and `file_feedback` streams.
|
`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<marketing-version>-<date>.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
|
## Announcements
|
||||||
|
|
||||||
The home toolbar's bell opens **通知公告** and shows the unread count (capped
|
The home toolbar's bell opens **通知公告** and shows the unread count (capped
|
||||||
|
|||||||
@@ -238,6 +238,8 @@ xmake r -d crossdesk
|
|||||||
|
|
||||||
原生客户端使用独立 Xcode 工程,要求 iOS 16+ 的 arm64 真机。构建与音视频、文件传输验证步骤见 [iOS 开发说明](../apps/ios/README.md)。当前 CI 导出未签名应用,真机安装需要自行签名。
|
原生客户端使用独立 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)。
|
README 的桌面图片直接由当前 Slint UI 渲染,复现方式见 [截图说明](images/README.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.
|
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
|
## Refreshing screenshots
|
||||||
|
|
||||||
Desktop README images are rendered directly from the current Slint UI. See the [screenshot notes](images/README.md) for reproduction commands.
|
Desktop README images are rendered directly from the current Slint UI. See the [screenshot notes](images/README.md) for reproduction commands.
|
||||||
|
|||||||
Reference in New Issue
Block a user