From 8981715e7307e20e84c235d8f4fff0ebc2c37c4f Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 3 Aug 2026 13:03:27 +0000 Subject: [PATCH 1/2] Add GitHub Actions iOS CI on macos-26 Replace the outdated macos-13 workflow with a macos-26 + Xcode 26.2 Bazel build that uses fake codesigning, uploads IPA/dSYM artifacts, and optionally publishes a GitHub Release. API credentials come from repository secrets. Point fork-relative rlottie/tgcalls submodule URLs at upstream TelegramMessenger so recursive checkout works on decoder-dev forks. Co-authored-by: D3C0Y --- .github/workflows/build.yml | 285 ++++++++++++++++++++++++++---------- .gitmodules | 4 +- 2 files changed, 213 insertions(+), 76 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7259ebd7e73..2d78dd6385d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,109 +1,246 @@ -name: CI +# Build Telegram-iOS (Bazel) on GitHub-hosted macOS runners. +# +# Required repository secrets (Settings → Secrets and variables → Actions): +# TELEGRAM_API_ID – app api_id from https://my.telegram.org +# TELEGRAM_API_HASH – app api_hash from https://my.telegram.org +# +# Optional secrets: +# TELEGRAM_BUNDLE_ID – defaults to ph.telegra.Telegraph +# TELEGRAM_TEAM_ID – Apple Team ID (empty is fine for unsigned/fake-signed IPA) +# +# Manual run: Actions → Build iOS → Run workflow +# Artifacts: Telegram.ipa + Telegram.DSYMs.zip (unsigned / fake-codesigned) + +name: Build iOS on: push: - branches: [ master ] - + branches: [master] + 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: - runs-on: macos-13 + name: Build (${{ github.event.inputs.configuration || 'release_arm64' }}) + runs-on: macos-26 + timeout-minutes: 480 + + env: + BUILD_CONFIGURATION: ${{ github.event.inputs.configuration || 'release_arm64' }} + SOURCE_DIR: /Users/Shared/telegram-ios + BAZEL_USER_ROOT: /private/var/tmp/_bazel_telegram_ios + BAZEL_CACHE_DIR: /Users/Shared/telegram-bazel-cache steps: - - uses: actions/checkout@v2 + - name: Check API secrets + env: + TELEGRAM_API_ID: ${{ secrets.TELEGRAM_API_ID }} + TELEGRAM_API_HASH: ${{ secrets.TELEGRAM_API_HASH }} + run: | + if [ -z "$TELEGRAM_API_ID" ] || [ -z "$TELEGRAM_API_HASH" ]; then + echo "::error::Set repository secrets TELEGRAM_API_ID and TELEGRAM_API_HASH (from https://my.telegram.org)." + exit 1 + fi + + - name: Checkout + uses: actions/checkout@v4 with: - submodules: 'recursive' - fetch-depth: '0' + fetch-depth: 0 + submodules: false - - name: Set active Xcode path + # Relative submodule URLs (../rlottie.git, ../tgcalls.git) resolve under the + # fork owner and 404. Point them at the upstream TelegramMessenger repos. + - name: Fix submodule URLs for forks run: | - XCODE_VERSION=$(cat versions.json | python3 -c 'import json,sys;obj=json.load(sys.stdin);print(obj["xcode"]);') - sudo xcode-select -s /Applications/Xcode_$XCODE_VERSION.app/Contents/Developer + 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: Create canonical source directory + - name: Checkout submodules run: | - set -x - sudo mkdir -p /Users/Shared - cp -R $GITHUB_WORKSPACE /Users/Shared/ - mv /Users/Shared/$(basename $GITHUB_WORKSPACE) /Users/Shared/telegram-ios + git submodule update --init --recursive --depth 1 --jobs 8 - - name: Build the App + - name: Select Xcode run: | - set -x - - # source code paths are included in the final binary, so we need to make them stable across builds - SOURCE_DIR=/Users/Shared/telegram-ios - - # use canonical bazel root - BAZEL_USER_ROOT="/private/var/tmp/_bazel_containerhost" + set -euo pipefail + 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 + echo "Xcode ${XCODE_VERSION} not found. Installed:" + ls /Applications | grep -i Xcode || true + # Prefer an exact match; otherwise use the newest installed 26.x. + CANDIDATE="$(ls -d /Applications/Xcode_26*.app 2>/dev/null | sort -V | tail -1 || true)" + if [ -z "$CANDIDATE" ]; then + echo "::error::No Xcode 26.x installation found on the runner." + exit 1 + fi + DEVELOPER_DIR="${CANDIDATE}/Contents/Developer" + echo "::warning::versions.json requests Xcode ${XCODE_VERSION}; using $(basename "$CANDIDATE") instead." + fi + sudo xcode-select -s "$DEVELOPER_DIR" + xcodebuild -version + echo "DEVELOPER_DIR=$DEVELOPER_DIR" >> "$GITHUB_ENV" + + - name: Free disk space + run: | + set -euo pipefail + df -h / + # Drop unused simulator runtimes / large caches that Bazel does not need. + 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 / - cd $SOURCE_DIR + - name: Create canonical source directory + run: | + set -euo pipefail + # Source paths are embedded in the binary; keep them stable across CI runs. + sudo mkdir -p /Users/Shared + sudo rm -rf "$SOURCE_DIR" + sudo cp -R "$GITHUB_WORKSPACE" "$SOURCE_DIR" + sudo chown -R "$(whoami)" "$SOURCE_DIR" + - name: Write build configuration from secrets + working-directory: ${{ env.SOURCE_DIR }} + 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 + cfg = { + "bundle_id": os.environ.get("TELEGRAM_BUNDLE_ID") or "ph.telegra.Telegraph", + "api_id": os.environ["TELEGRAM_API_ID"], + "api_hash": os.environ["TELEGRAM_API_HASH"], + "team_id": os.environ.get("TELEGRAM_TEAM_ID") or "", + "app_center_id": "0", + "is_internal_build": "false", + "is_appstore_build": "true", + "appstore_id": "686449807", + "app_specific_url_scheme": "tg", + "premium_iap_product_id": "org.telegram.telegramPremium.monthly", + "enable_siri": True, + "enable_icloud": True, + } + path = "build-system/ci-configuration.json" + 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 + working-directory: ${{ env.SOURCE_DIR }} + 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 + working-directory: ${{ env.SOURCE_DIR }} + run: | + set -euo pipefail + mkdir -p "$BAZEL_CACHE_DIR" - export APP_VERSION=$(cat versions.json | python3 -c 'import json,sys;obj=json.load(sys.stdin);print(obj["app"]);') - export COMMIT_COUNT=$(git rev-list --count HEAD) - export COMMIT_COUNT="$(($COMMIT_COUNT+$BUILD_NUMBER_OFFSET))" - export BUILD_NUMBER="$COMMIT_COUNT" - echo "BUILD_NUMBER=$(echo $BUILD_NUMBER)" >> $GITHUB_ENV - echo "APP_VERSION=$(echo $APP_VERSION)" >> $GITHUB_ENV + python3 build-system/Make/ImportCertificates.py \ + --path build-system/fake-codesigning/certs - python3 build-system/Make/ImportCertificates.py --path build-system/fake-codesigning/certs python3 -u build-system/Make/Make.py \ + --overrideXcodeVersion \ --bazelUserRoot="$BAZEL_USER_ROOT" \ + --cacheDir="$BAZEL_CACHE_DIR" \ build \ - --configurationPath="build-system/appstore-configuration.json" \ + --configurationPath=build-system/ci-configuration.json \ --codesigningInformationPath=build-system/fake-codesigning \ - --configuration=release_arm64 \ + --configuration="$BUILD_CONFIGURATION" \ --buildNumber="$BUILD_NUMBER" - # collect ipa + - name: Collect artifacts + working-directory: ${{ env.SOURCE_DIR }} + run: | + set -euo pipefail OUTPUT_PATH="build/artifacts" rm -rf "$OUTPUT_PATH" mkdir -p "$OUTPUT_PATH" - for f in bazel-out/applebin_ios-ios_arm*-opt-ST-*/bin/Telegram/Telegram.ipa; do - cp "$f" $OUTPUT_PATH/ - done - # collect dsym + IPA="$(find -L bazel-out -path '*/Telegram/Telegram.ipa' -type f 2>/dev/null | head -1 || true)" + if [ -z "$IPA" ]; then + echo "::error::Telegram.ipa not found under bazel-out" + find -L bazel-out -name '*.ipa' 2>/dev/null | head -50 || true + exit 1 + fi + cp "$IPA" "$OUTPUT_PATH/Telegram.ipa" + echo "IPA=$IPA" + mkdir -p build/DSYMs - for f in bazel-out/applebin_ios-ios_arm*-opt-ST-*/bin/Telegram/*.dSYM; do - cp -R "$f" build/DSYMs/ - done - zip -r "./$OUTPUT_PATH/Telegram.DSYMs.zip" build/DSYMs 1>/dev/null - - - name: Create Release - id: create_release - uses: actions/create-release@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + while IFS= read -r dsym; do + cp -R "$dsym" build/DSYMs/ + done < <(find -L bazel-out -path '*/Telegram/*.dSYM' -type d 2>/dev/null || true) + if [ -n "$(ls -A build/DSYMs 2>/dev/null || true)" ]; then + zip -r "$OUTPUT_PATH/Telegram.DSYMs.zip" build/DSYMs >/dev/null + else + echo "::warning::No dSYM bundles found" + fi + + ls -lh "$OUTPUT_PATH" + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: Telegram-iOS-${{ env.APP_VERSION }}-${{ env.BUILD_NUMBER }} + path: | + ${{ env.SOURCE_DIR }}/build/artifacts/Telegram.ipa + ${{ env.SOURCE_DIR }}/build/artifacts/Telegram.DSYMs.zip + if-no-files-found: error + retention-days: 14 + + - name: Create GitHub Release + if: github.event_name == 'workflow_dispatch' && inputs.create_release == true + uses: softprops/action-gh-release@v2 with: tag_name: build-${{ env.BUILD_NUMBER }} - release_name: Telegram ${{ env.APP_VERSION }} (${{ env.BUILD_NUMBER }}) + name: Telegram ${{ env.APP_VERSION }} (${{ env.BUILD_NUMBER }}) body: | - An unsigned build of Telegram for iOS ${{ env.APP_VERSION }} (${{ env.BUILD_NUMBER }}) - draft: false - prerelease: false - - - name: Upload Release IPA - id: upload-release-ipa - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: /Users/Shared/telegram-ios/build/artifacts/Telegram.ipa - asset_name: Telegram.ipa - asset_content_type: application/zip - - - name: Upload Release DSYM - id: upload-release-dsym - uses: actions/upload-release-asset@v1 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - upload_url: ${{ steps.create_release.outputs.upload_url }} - asset_path: /Users/Shared/telegram-ios/build/artifacts/Telegram.DSYMs.zip - asset_name: Telegram.DSYMs.zip - asset_content_type: application/zip + Unsigned (fake-codesigned) Telegram iOS build **${{ env.APP_VERSION }}** (${{ env.BUILD_NUMBER }}). + Configuration: `${{ env.BUILD_CONFIGURATION }}`. + Commit: `${{ github.sha }}`. + files: | + ${{ env.SOURCE_DIR }}/build/artifacts/Telegram.ipa + ${{ env.SOURCE_DIR }}/build/artifacts/Telegram.DSYMs.zip + fail_on_unmatched_files: false + generate_release_notes: true diff --git a/.gitmodules b/.gitmodules index 54ccfe35164..a8e9af28d0b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,7 @@ [submodule "submodules/rlottie/rlottie"] path = submodules/rlottie/rlottie - url=../rlottie.git + url = https://github.com/TelegramMessenger/rlottie.git [submodule "build-system/bazel-rules/rules_apple"] path = build-system/bazel-rules/rules_apple url=https://github.com/ali-fareed/rules_apple.git @@ -13,7 +13,7 @@ url=https://github.com/bazelbuild/rules_swift.git url = https://github.com/bazelbuild/apple_support.git [submodule "submodules/TgVoipWebrtc/tgcalls"] path = submodules/TgVoipWebrtc/tgcalls -url=../tgcalls.git + url = https://github.com/TelegramMessenger/tgcalls.git [submodule "third-party/libvpx/libvpx"] path = third-party/libvpx/libvpx url = https://github.com/webmproject/libvpx.git From 2c871039616bbdb5ae92bb0cef3e863cceb85744 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 3 Aug 2026 13:06:18 +0000 Subject: [PATCH 2/2] CI: build without requiring API secrets Fall back to build-system/appstore-configuration.json when TELEGRAM_API_* secrets are unset so the agent/fork CI can run without Actions secrets write access. Secrets still override when present. Co-authored-by: D3C0Y --- .github/workflows/build.yml | 59 ++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 34 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2d78dd6385d..14df0f80b36 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,15 +1,11 @@ # Build Telegram-iOS (Bazel) on GitHub-hosted macOS runners. # -# Required repository secrets (Settings → Secrets and variables → Actions): -# TELEGRAM_API_ID – app api_id from https://my.telegram.org -# TELEGRAM_API_HASH – app api_hash from https://my.telegram.org +# Optional repository secrets (override the committed appstore-configuration.json): +# TELEGRAM_API_ID / TELEGRAM_API_HASH – from https://my.telegram.org +# TELEGRAM_BUNDLE_ID / TELEGRAM_TEAM_ID # -# Optional secrets: -# TELEGRAM_BUNDLE_ID – defaults to ph.telegra.Telegraph -# TELEGRAM_TEAM_ID – Apple Team ID (empty is fine for unsigned/fake-signed IPA) -# -# Manual run: Actions → Build iOS → Run workflow -# Artifacts: Telegram.ipa + Telegram.DSYMs.zip (unsigned / fake-codesigned) +# If secrets are unset, CI uses build-system/appstore-configuration.json (same as +# the previous open-source workflow). Artifacts: Telegram.ipa + Telegram.DSYMs.zip. name: Build iOS @@ -51,16 +47,6 @@ jobs: BAZEL_CACHE_DIR: /Users/Shared/telegram-bazel-cache steps: - - name: Check API secrets - env: - TELEGRAM_API_ID: ${{ secrets.TELEGRAM_API_ID }} - TELEGRAM_API_HASH: ${{ secrets.TELEGRAM_API_HASH }} - run: | - if [ -z "$TELEGRAM_API_ID" ] || [ -z "$TELEGRAM_API_HASH" ]; then - echo "::error::Set repository secrets TELEGRAM_API_ID and TELEGRAM_API_HASH (from https://my.telegram.org)." - exit 1 - fi - - name: Checkout uses: actions/checkout@v4 with: @@ -125,7 +111,7 @@ jobs: sudo cp -R "$GITHUB_WORKSPACE" "$SOURCE_DIR" sudo chown -R "$(whoami)" "$SOURCE_DIR" - - name: Write build configuration from secrets + - name: Write build configuration working-directory: ${{ env.SOURCE_DIR }} env: TELEGRAM_API_ID: ${{ secrets.TELEGRAM_API_ID }} @@ -136,21 +122,26 @@ jobs: set -euo pipefail python3 <<'PY' import json, os - cfg = { - "bundle_id": os.environ.get("TELEGRAM_BUNDLE_ID") or "ph.telegra.Telegraph", - "api_id": os.environ["TELEGRAM_API_ID"], - "api_hash": os.environ["TELEGRAM_API_HASH"], - "team_id": os.environ.get("TELEGRAM_TEAM_ID") or "", - "app_center_id": "0", - "is_internal_build": "false", - "is_appstore_build": "true", - "appstore_id": "686449807", - "app_specific_url_scheme": "tg", - "premium_iap_product_id": "org.telegram.telegramPremium.monthly", - "enable_siri": True, - "enable_icloud": True, - } + path = "build-system/ci-configuration.json" + base_path = "build-system/appstore-configuration.json" + with open(base_path) 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 {base_path} (api_id={cfg.get('api_id')})") + + if os.environ.get("TELEGRAM_BUNDLE_ID"): + cfg["bundle_id"] = os.environ["TELEGRAM_BUNDLE_ID"] + if "TELEGRAM_TEAM_ID" in os.environ and os.environ["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")