CI: macos-26 + verified Metal for Bazel IPA #15
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
| # Build unsigned Telegram-iOS IPA (Bazel), styled after decoder-dev/PrivateMusic2 CI: | |
| # fast runner, unsigned artifact, tag → GitHub Release. | |
| # | |
| # Optional secrets TELEGRAM_API_ID / TELEGRAM_API_HASH / TELEGRAM_BUNDLE_ID / TELEGRAM_TEAM_ID | |
| # override build-system/appstore-configuration.json when set. | |
| name: Build iOS | |
| on: | |
| push: | |
| branches: [master] | |
| tags: ['v*'] | |
| pull_request: | |
| workflow_dispatch: | |
| inputs: | |
| configuration: | |
| description: Bazel build configuration | |
| type: choice | |
| options: | |
| - release_arm64 | |
| - debug_arm64 | |
| default: release_arm64 | |
| create_release: | |
| description: Publish a GitHub Release with the IPA | |
| type: boolean | |
| default: false | |
| concurrency: | |
| group: ios-build-${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: write | |
| jobs: | |
| build: | |
| name: Build IPA (macos-26) | |
| runs-on: macos-26 | |
| timeout-minutes: 360 | |
| env: | |
| BUILD_CONFIGURATION: ${{ github.event.inputs.configuration || 'release_arm64' }} | |
| BAZEL_USER_ROOT: /private/var/tmp/_bazel_telegram_ios | |
| BAZEL_CACHE_DIR: ${{ github.workspace }}/.telegram-bazel-cache | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v5 | |
| with: | |
| fetch-depth: 0 | |
| submodules: false | |
| - name: Fix submodule URLs for forks | |
| run: | | |
| git config --file=.gitmodules submodule.submodules/rlottie/rlottie.url \ | |
| https://github.com/TelegramMessenger/rlottie.git | |
| git config --file=.gitmodules submodule.submodules/TgVoipWebrtc/tgcalls.url \ | |
| https://github.com/TelegramMessenger/tgcalls.git | |
| git submodule sync --recursive | |
| - name: Checkout submodules | |
| run: | | |
| git submodule update --init --recursive --depth 1 --jobs 8 | |
| - name: Select Xcode | |
| run: | | |
| set -euo pipefail | |
| # Prefer versions.json (26.2). Xcode 27 preview runners often download Metal | |
| # but xcrun still cannot find `metal` for Bazel MetalCompile. | |
| XCODE_VERSION="$(python3 -c 'import json; print(json.load(open("versions.json"))["xcode"])')" | |
| DEVELOPER_DIR="/Applications/Xcode_${XCODE_VERSION}.app/Contents/Developer" | |
| if [ ! -d "$DEVELOPER_DIR" ]; then | |
| CANDIDATE="$(ls -d /Applications/Xcode_26*.app 2>/dev/null | sort -V | tail -1 || true)" | |
| [ -n "$CANDIDATE" ] || { echo "::error::No Xcode 26.x found"; exit 1; } | |
| DEVELOPER_DIR="${CANDIDATE}/Contents/Developer" | |
| echo "::warning::using $(basename "$CANDIDATE") instead of ${XCODE_VERSION}" | |
| fi | |
| sudo xcode-select -s "$DEVELOPER_DIR" | |
| xcodebuild -version | |
| # PrivateMusic-style Metal install + xcrun cache reset (Bazel calls `xcrun metal`). | |
| - name: Prepare Metal toolchain | |
| run: | | |
| set -euo pipefail | |
| sudo xcodebuild -downloadComponent MetalToolchain | |
| rm -f "$(getconf DARWIN_USER_TEMP_DIR)/xcrun_db" || true | |
| xcrun -f metal | |
| xcrun metal -v | |
| - name: Free disk space | |
| run: | | |
| set -euo pipefail | |
| df -h / | |
| sudo rm -rf \ | |
| /Users/runner/Library/Developer/CoreSimulator/Caches \ | |
| /Library/Developer/CoreSimulator/Profiles/Runtimes/* \ | |
| /Users/runner/.cargo \ | |
| /Users/runner/.rustup \ | |
| /usr/local/lib/android \ | |
| /Users/runner/Library/Android || true | |
| df -h / | |
| - name: Cache Bazel | |
| uses: actions/cache@v4 | |
| with: | |
| path: ${{ env.BAZEL_CACHE_DIR }} | |
| key: bazel-${{ runner.os }}-${{ env.BUILD_CONFIGURATION }}-${{ hashFiles('versions.json', 'MODULE.bazel', 'MODULE.bazel.lock') }} | |
| restore-keys: | | |
| bazel-${{ runner.os }}-${{ env.BUILD_CONFIGURATION }}- | |
| bazel-${{ runner.os }}- | |
| - name: Write build configuration | |
| env: | |
| TELEGRAM_API_ID: ${{ secrets.TELEGRAM_API_ID }} | |
| TELEGRAM_API_HASH: ${{ secrets.TELEGRAM_API_HASH }} | |
| TELEGRAM_BUNDLE_ID: ${{ secrets.TELEGRAM_BUNDLE_ID }} | |
| TELEGRAM_TEAM_ID: ${{ secrets.TELEGRAM_TEAM_ID }} | |
| run: | | |
| set -euo pipefail | |
| python3 <<'PY' | |
| import json, os | |
| path = "build-system/ci-configuration.json" | |
| with open("build-system/appstore-configuration.json") as f: | |
| cfg = json.load(f) | |
| api_id = os.environ.get("TELEGRAM_API_ID") or "" | |
| api_hash = os.environ.get("TELEGRAM_API_HASH") or "" | |
| if api_id and api_hash: | |
| cfg["api_id"] = api_id | |
| cfg["api_hash"] = api_hash | |
| print("Using TELEGRAM_API_* repository secrets") | |
| else: | |
| print(f"Secrets unset — using appstore-configuration.json (api_id={cfg.get('api_id')})") | |
| if os.environ.get("TELEGRAM_BUNDLE_ID"): | |
| cfg["bundle_id"] = os.environ["TELEGRAM_BUNDLE_ID"] | |
| if os.environ.get("TELEGRAM_TEAM_ID"): | |
| cfg["team_id"] = os.environ["TELEGRAM_TEAM_ID"] | |
| with open(path, "w") as f: | |
| json.dump(cfg, f, indent="\t") | |
| f.write("\n") | |
| print(f"Wrote {path} (api_id={cfg['api_id']}, bundle_id={cfg['bundle_id']})") | |
| PY | |
| - name: Compute build number | |
| run: | | |
| set -euo pipefail | |
| BUILD_NUMBER_OFFSET="$(cat build_number_offset)" | |
| APP_VERSION="$(python3 -c 'import json; print(json.load(open("versions.json"))["app"])')" | |
| COMMIT_COUNT="$(git rev-list --count HEAD)" | |
| BUILD_NUMBER="$((COMMIT_COUNT + BUILD_NUMBER_OFFSET))" | |
| { | |
| echo "APP_VERSION=$APP_VERSION" | |
| echo "BUILD_NUMBER=$BUILD_NUMBER" | |
| } >> "$GITHUB_ENV" | |
| echo "Telegram ${APP_VERSION} (${BUILD_NUMBER})" | |
| - name: Build IPA | |
| run: | | |
| set -euo pipefail | |
| mkdir -p "$BAZEL_CACHE_DIR" | |
| python3 build-system/Make/ImportCertificates.py \ | |
| --path build-system/fake-codesigning/certs | |
| # PrivateMusic-style fast path: unsigned/fake-signed IPA, no extensions, no dSYM. | |
| python3 -u build-system/Make/Make.py \ | |
| --overrideXcodeVersion \ | |
| --bazelUserRoot="$BAZEL_USER_ROOT" \ | |
| --cacheDir="$BAZEL_CACHE_DIR" \ | |
| build \ | |
| --configurationPath=build-system/ci-configuration.json \ | |
| --codesigningInformationPath=build-system/fake-codesigning \ | |
| --configuration="$BUILD_CONFIGURATION" \ | |
| --buildNumber="$BUILD_NUMBER" \ | |
| --disableExtensions \ | |
| --skipDsym \ | |
| --enableParallelSwiftmoduleGeneration \ | |
| --outputBuildArtifactsPath=build/artifacts | |
| - name: Collect artifacts | |
| run: | | |
| set -euo pipefail | |
| OUTPUT_PATH="build/artifacts" | |
| mkdir -p "$OUTPUT_PATH" | |
| if [ ! -f "$OUTPUT_PATH/Telegram.ipa" ]; then | |
| IPA="$(find -L bazel-out -path '*/Telegram/Telegram.ipa' -type f 2>/dev/null | head -1 || true)" | |
| [ -n "$IPA" ] || { echo "::error::Telegram.ipa not found"; exit 1; } | |
| cp "$IPA" "$OUTPUT_PATH/Telegram.ipa" | |
| fi | |
| mv "$OUTPUT_PATH/Telegram.ipa" \ | |
| "$OUTPUT_PATH/Telegram-${APP_VERSION}-${BUILD_NUMBER}-unsigned.ipa" | |
| ls -lh "$OUTPUT_PATH" | |
| - name: Upload unsigned IPA | |
| if: success() | |
| uses: actions/upload-artifact@v5 | |
| with: | |
| name: Telegram-iOS-${{ env.APP_VERSION }}-${{ env.BUILD_NUMBER }}-unsigned | |
| path: build/artifacts/*.ipa | |
| if-no-files-found: error | |
| retention-days: 14 | |
| - name: Publish GitHub Release | |
| if: startsWith(github.ref, 'refs/tags/v') || (github.event_name == 'workflow_dispatch' && inputs.create_release == true) | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| run: | | |
| set -euo pipefail | |
| IPA="build/artifacts/Telegram-${APP_VERSION}-${BUILD_NUMBER}-unsigned.ipa" | |
| test -f "$IPA" | |
| if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then | |
| TAG="${GITHUB_REF_NAME}" | |
| else | |
| TAG="v${APP_VERSION}-${BUILD_NUMBER}" | |
| fi | |
| NOTES="$(mktemp)" | |
| cat > "$NOTES" <<EOF | |
| Telegram iOS ${APP_VERSION} (${BUILD_NUMBER}) | |
| Unsigned (fake-codesigned) IPA — extensions and dSYMs skipped for CI speed. | |
| Sign with your own certificate before installing on a device. | |
| Configuration: \`${BUILD_CONFIGURATION}\` | |
| Commit: \`${GITHUB_SHA}\` | |
| EOF | |
| if gh release view "$TAG" >/dev/null 2>&1; then | |
| gh release upload "$TAG" "$IPA" --clobber | |
| gh release edit "$TAG" --title "Telegram ${APP_VERSION} (${BUILD_NUMBER})" --notes-file "$NOTES" | |
| else | |
| gh release create "$TAG" "$IPA" \ | |
| --title "Telegram ${APP_VERSION} (${BUILD_NUMBER})" \ | |
| --notes-file "$NOTES" | |
| fi |