Update watch signing
This commit is contained in:
parent
aa6314e5c4
commit
a3131ba4d2
4 changed files with 74 additions and 25 deletions
|
|
@ -43,7 +43,7 @@ A standalone watchOS Telegram client (developed in the separate `~/build/tgwatch
|
|||
|
||||
**`Telegram/WatchApp/` is a synced snapshot — do not hand-edit it.** The source of truth and dev tooling live in the `tgwatch` repo. To change the watch app, edit it there, then re-sync with `tgwatch/tools/export-sources.sh /abs/path/to/telegram-ios/Telegram/WatchApp` and commit the result. The committed `tgwatch.xcodeproj` is generated (kept via a `!tgwatch.xcodeproj` negation in `Telegram/WatchApp/.gitignore`, since the root `.gitignore` ignores `*.xcodeproj`); `.build`/`.swiftpm`/`xcuserdata` are excluded.
|
||||
|
||||
**How it's wired:** `//Telegram:TelegramWatchApp` (rule in `Telegram/prebuilt_watchos.bzl`) runs in **two actions**: `PrebuiltWatchosCompile` (`Telegram/prebuilt_watchos_compile.sh`) runs xcodebuild on the snapshot in a writable temp copy with PLACEHOLDER version/api values, emitting an unsigned `.app`; `PrebuiltWatchosPatchSign` (`Telegram/prebuilt_watchos_patch.sh`) then rewrites the four per-build Info.plist keys (`CFBundleShortVersionString`, `CFBundleVersion`, `TG_API_ID`, `TG_API_HASH`) and codesigns the `.app` + nested `TDLibFramework.framework` (identity + `ph.telegra.Telegraph.watchkitapp` profile from `--define`s). The result feeds the `Telegram` `ios_application`'s `watch_application` slot (gated by the `//Telegram:embedWatchApp` flag). **The compile action's only inputs are the snapshot (+ its worker)** — so changing the version, build number, api id/hash or signing identity re-runs only the cheap patch+sign action, not xcodebuild; xcodebuild re-runs only when the snapshot changes. This is correct because none of those four values reach the compiled binary: each lands only in the Info.plist (via `$(...)` substitution and a runtime `Bundle.main.object(forInfoDictionaryKey:)` lookup in `Secrets.swift`).
|
||||
**How it's wired:** `//Telegram:TelegramWatchApp` (rule in `Telegram/prebuilt_watchos.bzl`) runs in **two actions**: `PrebuiltWatchosCompile` (`Telegram/prebuilt_watchos_compile.sh`) runs xcodebuild on the snapshot in a writable temp copy with PLACEHOLDER version/api values (the bundle ids are baked from the snapshot's pbxproj/Info.plist — `ph.telegra.Telegraph.watchkitapp` / `ph.telegra.Telegraph`), emitting an unsigned `.app`; `PrebuiltWatchosPatchSign` (`Telegram/prebuilt_watchos_patch.sh`) then rewrites **six** per-build Info.plist keys (`CFBundleShortVersionString`, `CFBundleVersion`, `TG_API_ID`, `TG_API_HASH`, `CFBundleIdentifier`, `WKCompanionAppBundleIdentifier`) and codesigns the `.app` + nested `TDLibFramework.framework` (identity + the watchkitapp profile from `--define`s). The result feeds the `Telegram` `ios_application`'s `watch_application` slot (gated by the `//Telegram:embedWatchApp` flag). The rule takes `bundle_id` (set to `"{telegram_bundle_id}.watchkitapp"` in `Telegram/BUILD`) and derives the host bundle id by stripping the `.watchkitapp` suffix; both are passed to the patch worker as args (not action inputs), so the patch action re-runs when the host bundle id changes but the (expensive) compile stays cached. **The compile action's only inputs are the snapshot (+ its worker)** — so changing the version, build number, api id/hash, host bundle id, or signing identity re-runs only the cheap patch+sign action, not xcodebuild; xcodebuild re-runs only when the snapshot changes. This is correct because none of those values reach the compiled binary: each lands only in the Info.plist (via `$(...)` substitution and a runtime `Bundle.main.object(forInfoDictionaryKey:)` lookup in `Secrets.swift`, except for the bundle-id keys which only Info.plist consumers read).
|
||||
|
||||
**Non-obvious invariants** (also in the `.bzl` comments): `AppleBundleInfo`'s public init is banned — use the internal `new_applebundleinfo`; `watch_application` requires BOTH `AppleBundleInfo` (with a non-None `infoplist` File) AND `WatchosApplicationBundleInfo`; the embedded watch app's `CFBundleShortVersionString`/`CFBundleVersion` must exactly equal the host's (sourced from `versions.json['app']` + `--define=buildNumber`); the host does NOT re-sign the embedded watch app, so the worker must sign it; the watch bundle id `ph.telegra.Telegraph.watchkitapp` must track the host `telegram_bundle_id`.
|
||||
|
||||
|
|
|
|||
|
|
@ -1743,6 +1743,14 @@ xcode_provisioning_profile(
|
|||
|
||||
apple_prebuilt_watchos_application(
|
||||
name = "TelegramWatchApp",
|
||||
# The watch app's bundle id must be `<host>.watchkitapp` for the host
|
||||
# ios_application's child-bundle-id prefix check; the rule derives the
|
||||
# companion (host) bundle id from this by stripping `.watchkitapp` and the
|
||||
# patch worker writes both CFBundleIdentifier and WKCompanionAppBundleIdentifier
|
||||
# into the embedded watch app's Info.plist.
|
||||
bundle_id = "{telegram_bundle_id}.watchkitapp".format(
|
||||
telegram_bundle_id = telegram_bundle_id,
|
||||
),
|
||||
tags = ["manual"],
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,13 +6,17 @@ it through the providers that `ios_application(watch_application = ...)` consume
|
|||
1. PrebuiltWatchosCompile (prebuilt_watchos_compile.sh) — runs `xcodebuild` against
|
||||
the exported tgwatch source tree with PLACEHOLDER version/api values, emitting an
|
||||
unsigned .app archive. Depends only on the source snapshot.
|
||||
2. PrebuiltWatchosPatchSign (prebuilt_watchos_patch.sh) — rewrites the four per-build
|
||||
Info.plist keys (version, build number, api id/hash) on the compiled app, then
|
||||
optionally codesigns it.
|
||||
2. PrebuiltWatchosPatchSign (prebuilt_watchos_patch.sh) — rewrites the six per-build
|
||||
Info.plist keys (version, build number, api id/hash, watch CFBundleIdentifier,
|
||||
WKCompanionAppBundleIdentifier) on the compiled app, then optionally codesigns
|
||||
it. The watch + companion bundle ids must track the host's `telegram_bundle_id`
|
||||
(rules_apple validates both via the parent ios_application's
|
||||
bundle_verification_targets), so they live in the patch action — not in the
|
||||
xcodebuild snapshot — to keep the compile cached across host-bundle-id changes.
|
||||
|
||||
Splitting them lets Bazel cache the (expensive, ~4-min) compile whenever only the
|
||||
version/build number/api/identity change — those values never reach the compiled
|
||||
binary, only the Info.plist.
|
||||
version/build number/api/identity/bundle-id change — those values never reach the
|
||||
compiled binary, only the Info.plist.
|
||||
|
||||
The providers exposed:
|
||||
|
||||
|
|
@ -44,6 +48,19 @@ def _apple_prebuilt_watchos_application_impl(ctx):
|
|||
api_hash = ctx.var.get("watchApiHash", "placeholder")
|
||||
identity = ctx.var.get("watchSigningIdentity", "")
|
||||
|
||||
# The watch bundle id is `<host>.watchkitapp`; strip the suffix to recover the
|
||||
# host bundle id, which the patch worker writes to WKCompanionAppBundleIdentifier
|
||||
# (and the watch's own CFBundleIdentifier needs to be the watch bundle id, not the
|
||||
# hardcoded one baked in by xcodebuild from the snapshot's pbxproj). The host
|
||||
# ios_application validates both: child CFBundleIdentifier must start with
|
||||
# `<host>.`, and child WKCompanionAppBundleIdentifier must equal the host's
|
||||
# bundle id (see rules_apple's bundle_verification_targets in ios_rules.bzl).
|
||||
watch_bundle_id = ctx.attr.bundle_id
|
||||
_watchkitapp_suffix = ".watchkitapp"
|
||||
if not watch_bundle_id.endswith(_watchkitapp_suffix):
|
||||
fail("apple_prebuilt_watchos_application bundle_id must end with '.watchkitapp' (got %r)" % watch_bundle_id)
|
||||
host_bundle_id = watch_bundle_id[:-len(_watchkitapp_suffix)]
|
||||
|
||||
# The provisioning profile is an external, machine-specific absolute path passed via
|
||||
# --define rather than a Bazel label, so the gitignored profile need not be exposed as
|
||||
# a target. The local action reads it directly. Empty => unsigned build; when set but
|
||||
|
|
@ -104,6 +121,8 @@ def _apple_prebuilt_watchos_application_impl(ctx):
|
|||
infoplist.path,
|
||||
ctx.file.versions_json.path,
|
||||
build_number,
|
||||
host_bundle_id,
|
||||
watch_bundle_id,
|
||||
],
|
||||
inputs = [ctx.file._patch_worker, compiled_archive, ctx.file.versions_json],
|
||||
outputs = [archive, infoplist],
|
||||
|
|
|
|||
|
|
@ -2,32 +2,49 @@
|
|||
# Patch + sign worker for the apple_prebuilt_watchos_application Bazel rule (action 2 of 2).
|
||||
#
|
||||
# Takes the unsigned, placeholder-version watch .app archive produced by
|
||||
# prebuilt_watchos_compile.sh, rewrites the four per-build Info.plist values
|
||||
# (CFBundleShortVersionString, CFBundleVersion, TG_API_ID, TG_API_HASH) — none of
|
||||
# which affect the compiled binary — then — if a provisioning profile is supplied —
|
||||
# codesigns the app and its nested frameworks with the watchkitapp provisioning
|
||||
# profile and a matching identity, and finally zips the .app into the rule's output
|
||||
# archive.
|
||||
# prebuilt_watchos_compile.sh, rewrites the six per-build Info.plist values
|
||||
# (CFBundleShortVersionString, CFBundleVersion, TG_API_ID, TG_API_HASH,
|
||||
# CFBundleIdentifier, WKCompanionAppBundleIdentifier) — none of which affect the
|
||||
# compiled binary — then — if a provisioning profile is supplied — codesigns the
|
||||
# app and its nested frameworks with the watchkitapp provisioning profile and a
|
||||
# matching identity, and finally zips the .app into the rule's output archive.
|
||||
#
|
||||
# Bundle-id rewriting is needed because xcodebuild bakes the snapshot's pbxproj
|
||||
# PRODUCT_BUNDLE_IDENTIFIER (ph.telegra.Telegraph.watchkitapp) into the compiled
|
||||
# Info.plist, and WKCompanionAppBundleIdentifier is hardcoded in the snapshot's
|
||||
# source Info.plist — but the host ios_application's bundle id varies by codesigning
|
||||
# configuration (e.g. org.telegram.TelegramInternal for development) and rules_apple
|
||||
# requires the child CFBundleIdentifier to start with the host bundle id and
|
||||
# WKCompanionAppBundleIdentifier to equal it (bundle_verification_targets in
|
||||
# ios_rules.bzl). Doing the rewrite here keeps the expensive xcodebuild action
|
||||
# cached across host-bundle-id changes.
|
||||
#
|
||||
# Splitting this from the compile step lets Bazel cache the (expensive) xcodebuild
|
||||
# whenever only the version/build number/api/identity change.
|
||||
# whenever only the version/build number/api/identity/bundle-id change.
|
||||
#
|
||||
# The host ios_application embeds this archive under Watch/ and re-seals the host;
|
||||
# it does NOT re-sign the watch app, so the watch signing must happen here.
|
||||
#
|
||||
# Args:
|
||||
# $1 input_zip Compiled (unsigned, placeholder-version) .app archive from action 1
|
||||
# $2 output_zip Path (declared by Bazel) to write the final .app archive to
|
||||
# $3 api_id TG_API_ID Info.plist value
|
||||
# $4 api_hash TG_API_HASH Info.plist value
|
||||
# $5 identity Codesigning identity (SHA1 hash); empty => derived from $6's cert
|
||||
# $6 profile Path to the watchkitapp .mobileprovision; empty => unsigned build
|
||||
# $7 infoplist_out Path (declared by Bazel) to copy the patched Info.plist to
|
||||
# $8 versions_json versions.json (key 'app' => CFBundleShortVersionString)
|
||||
# $9 build_number CFBundleVersion
|
||||
# $1 input_zip Compiled (unsigned, placeholder-version) .app archive from action 1
|
||||
# $2 output_zip Path (declared by Bazel) to write the final .app archive to
|
||||
# $3 api_id TG_API_ID Info.plist value
|
||||
# $4 api_hash TG_API_HASH Info.plist value
|
||||
# $5 identity Codesigning identity (SHA1 hash); empty => derived from $6's cert
|
||||
# $6 profile Path to the watchkitapp .mobileprovision; empty => unsigned build
|
||||
# $7 infoplist_out Path (declared by Bazel) to copy the patched Info.plist to
|
||||
# $8 versions_json versions.json (key 'app' => CFBundleShortVersionString)
|
||||
# $9 build_number CFBundleVersion
|
||||
# $10 host_bundle_id WKCompanionAppBundleIdentifier value (host app bundle id)
|
||||
# $11 watch_bundle_id CFBundleIdentifier value (must be "<host_bundle_id>.watchkitapp")
|
||||
set -euo pipefail
|
||||
|
||||
IN_ZIP="$1"; OUT_ZIP="$2"; API_ID="$3"; API_HASH="$4"; IDENTITY="${5:-}"; PROFILE="${6:-}"; INFOPLIST_OUT="${7:-}"; VERSIONS_JSON="${8:-}"; BUILD_NUMBER="${9:-1}"
|
||||
IN_ZIP="$1"; OUT_ZIP="$2"; API_ID="$3"; API_HASH="$4"; IDENTITY="${5:-}"; PROFILE="${6:-}"; INFOPLIST_OUT="${7:-}"; VERSIONS_JSON="${8:-}"; BUILD_NUMBER="${9:-1}"; HOST_BUNDLE_ID="${10:-}"; WATCH_BUNDLE_ID="${11:-}"
|
||||
|
||||
if [ -z "$HOST_BUNDLE_ID" ] || [ -z "$WATCH_BUNDLE_ID" ]; then
|
||||
echo "error: host_bundle_id and watch_bundle_id must both be supplied (got host=$HOST_BUNDLE_ID watch=$WATCH_BUNDLE_ID)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Match the host app's version (rules_apple requires the embedded watch app's
|
||||
# CFBundleShortVersionString/CFBundleVersion to equal the parent's).
|
||||
|
|
@ -46,15 +63,20 @@ if [ -z "$APP" ]; then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
# Overwrite the placeholder values baked in at compile time. All four keys already
|
||||
# Overwrite the placeholder values baked in at compile time. All six keys already
|
||||
# exist in the compiled (binary-format) Info.plist, so PlistBuddy Set preserves their
|
||||
# (string) type — matching what $(...) substitution produced and what Secrets.swift
|
||||
# expects from Bundle.main.object(forInfoDictionaryKey:).
|
||||
# expects from Bundle.main.object(forInfoDictionaryKey:). The bundle-id keys track
|
||||
# the host's `telegram_bundle_id` (see the file header for why); xcodebuild bakes
|
||||
# `ph.telegra.Telegraph.watchkitapp` / `ph.telegra.Telegraph` here from the
|
||||
# snapshot's pbxproj/Info.plist regardless of what host the rest of the build uses.
|
||||
PLIST="$APP/Info.plist"
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $MARKETING_VERSION" "$PLIST"
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" "$PLIST"
|
||||
/usr/libexec/PlistBuddy -c "Set :TG_API_ID $API_ID" "$PLIST"
|
||||
/usr/libexec/PlistBuddy -c "Set :TG_API_HASH $API_HASH" "$PLIST"
|
||||
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $WATCH_BUNDLE_ID" "$PLIST"
|
||||
/usr/libexec/PlistBuddy -c "Set :WKCompanionAppBundleIdentifier $HOST_BUNDLE_ID" "$PLIST"
|
||||
|
||||
# Expose the patched watch Info.plist (the host reads it to verify the companion
|
||||
# bundle-id linkage and the child version). Codesigning does not alter Info.plist
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue