Files
tty7/mobile/scripts/testflight.sh
T
l0ng-aiandClaude 4c8c317b91 feat(mobile): files, changes, a custom key bar, and a steadier connection (#1029)
The phone can now hand a pane a photo or file, read what an agent changed,
answer its prompts with a tap, and keep its link across leaving a pane.

Gateway and protocol:
- `Open::Upload` puts a file (up to 20 MB) in the directory the desktop
  keeps pasted images in, under a name a shell takes unquoted, and answers
  with its path. Panes on this machine only for now.
- `Open::Diff` answers with the working tree's changes against HEAD and its
  untracked files, for the directory a pane is in.
- Both are one-shot streams; an older gateway drops them unanswered, which
  the client reports as "update tty7".

App:
- Attach button in the message box: the uploaded path goes into the draft.
- Changes sheet from a pane's menu: a block per file, lines coloured.
- Settings → Key bar: remove, reorder and add keys, from a catalog or
  written out (`/compact\r`, `^C`), across pages; reset to the default.
- An agent's numbered choices show as buttons while it waits.
- Pinch to zoom, tap a link to open it, find in the scrollback, a Copy
  button for text a program copies (OSC 52), and an optional Face ID lock.
- Swipe from the left edge to go back; the iOS WebView has no page stack.
- One workspace at a time on a machine, picked from a row of chips.
- Agent avatars in their own colours.
- Each screen's watch is its own: a second watch no longer ends the first,
  which showed as "Can't reach" and a reconnect after leaving a pane.
- Dialing no longer holds the session lock, so Forget works while a
  machine is still connecting; its confirmation is an in-app sheet, since
  `window.confirm` shows nothing in the iOS WebView.
- iOS 15 minimum, as App Store Connect will require from April 2027.
- `scripts/testflight.sh` archives, signs for the App Store at export (the
  team has no devices for a development profile) and uploads.

Co-authored-by: Claude <noreply@anthropic.com>
2026-09-30 12:30:04 +08:00

86 lines
3.2 KiB
Bash
Executable File

#!/bin/bash
# Builds the iOS app and uploads it to App Store Connect for TestFlight.
#
# Usage: scripts/testflight.sh [--build-number N] [--no-upload]
# --build-number N the build is <version>.N (default: the time, yyjjjHHMM)
# --no-upload export a signed .ipa into build/testflight/ instead
#
# Needs Xcode signed in (Settings → Accounts) to an account on team 78SZC3DQ7B.
#
# Why not just `tauri ios build --export-method app-store-connect`:
# - The archive is signed with a development profile first, and the team has
# no registered devices to make one from. So the archive is built unsigned
# and Xcode signs it for the App Store at export, where no device is needed.
#
# The default build number is the time — year, day of the year, hour, minute —
# so each upload is higher than the last without keeping a counter, and it
# stays under 2^31.
set -euo pipefail
cd "$(dirname "$0")/.."
UPLOAD=1
N=$(date +%y%j%H%M)
while [ $# -gt 0 ]; do
case "$1" in
--no-upload) UPLOAD=0 ;;
--build-number) N="${2:?--build-number needs a number}"; shift ;;
*) echo "usage: $0 [--build-number N] [--no-upload]" >&2; exit 2 ;;
esac
shift
done
TEAM=78SZC3DQ7B
PROJECT=src-tauri/gen/apple/tty7-mobile.xcodeproj/project.pbxproj
ARCHIVE=src-tauri/gen/apple/build/tty7-mobile_iOS.xcarchive
OUT=build/testflight
mkdir -p "$OUT"
[ -f "$PROJECT" ] || npm run tauri ios init
# Release signing off for the archive only; the project is put back however
# this exits.
cp "$PROJECT" "$OUT/project.pbxproj.orig"
trap 'cp "$OUT/project.pbxproj.orig" "$PROJECT"' EXIT
perl -0pi -e 's{(/\* release \*/ = \{\n\t+isa = XCBuildConfiguration;\n\t+buildSettings = \{\n)(?=(?:(?!\n\t+\};).)*?CODE_SIGN_ENTITLEMENTS)}{$1\t\t\t\tCODE_SIGNING_ALLOWED = NO;\n}sg' "$PROJECT"
if [ "$(grep -c 'CODE_SIGNING_ALLOWED = NO' "$PROJECT")" != 1 ]; then
echo "could not find the app target's release configuration in $PROJECT" >&2
exit 1
fi
echo "==> Archiving build number $N"
rm -rf "$ARCHIVE"
npx tauri ios build --archive-only --build-number "$N"
APP=$(ls -d "$ARCHIVE"/Products/Applications/*.app)
VERSION=$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "$APP/Info.plist")
BUILD=$(/usr/libexec/PlistBuddy -c "Print :CFBundleVersion" "$APP/Info.plist")
DESTINATION=$([ "$UPLOAD" = 1 ] && echo upload || echo export)
cat > "$OUT/ExportOptions.plist" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key><string>app-store-connect</string>
<key>destination</key><string>$DESTINATION</string>
<key>teamID</key><string>$TEAM</string>
<key>signingStyle</key><string>automatic</string>
<key>manageAppVersionAndBuildNumber</key><false/>
</dict>
</plist>
EOF
echo "==> Signing$([ "$UPLOAD" = 1 ] && echo " and uploading") $VERSION ($BUILD)"
xcodebuild -exportArchive \
-archivePath "$ARCHIVE" \
-exportOptionsPlist "$OUT/ExportOptions.plist" \
-exportPath "$OUT" \
-allowProvisioningUpdates
if [ "$UPLOAD" = 1 ]; then
echo "==> Uploaded $VERSION ($BUILD); it shows in TestFlight once App Store Connect has processed it"
else
echo "==> Signed .ipa in $OUT"
fi