Files
Felitendo 8b06b305f5 feat: give the manager the island the app has
FTStore Manager puts an app on a device through the same three stages FTStore
does, and had nothing to show for any of it once the screen was off. It has a
Live Activity now: the same widget the app carries, embedded as its own
extension under its own identifier, naming the app, filling a bar while the
bytes arrive, adding the rate and the time left, then Preparing and Installing.

It is the app's island with the pause button taken out. This download manager
has no pause and no screen to offer one from, so there are no intents to compile
into the extension and no paused state in the payload. The island is read-only
on every version rather than only on the older ones.

It ends when the installer answers, which is the part the app had to learn the
hard way. Open launches what was just installed and leaves the sheet standing
behind it, so ending on dismissal left the island describing an install that had
finished, until somebody came back and closed a receipt they were done with.
Dismissal still ends it, which is what covers a flow that never gets an answer
at all.

A dropped connection is now one of those answers. It used to remove the download
and tell nobody, leaving the flow spinning with no way to find out; that was
merely invisible before and would now be an island counting bytes that stopped
arriving.

None of this is visible from a build: an IPA whose extension is missing, misnamed
or misfiled installs, runs, and simply shows no island. Both of the Manager's
release workflows therefore run the same check the app's do against the built
IPA, and the ad-hoc signing step seals the nested extension. Two products now
build an extension of the same name out of projects that share their
identifiers, so the separate derived data paths carry more weight than before,
and the notes that explain them say so.
2026-09-01 08:35:02 +02:00

111 lines
5.4 KiB
YAML

name: Manager Dev Repo
# Builds an unsigned dev IPA of FTStore Manager and publishes it together with a
# freshly-generated AltStore source (manager-devrepo.json) to a rolling
# "manager-dev" prerelease.
# https://github.com/FTStuff/FTStore/releases/download/manager-dev/manager-devrepo.json
#
# Monorepo: the Manager's Xcode project lives in manager/, so the build steps run
# there. The tag is `manager-dev`, NOT `dev` — the app's own dev prerelease
# (ios-dev-repo.yml) owns that one, and sharing it would make each build
# overwrite the other's assets.
on:
push:
branches: [main]
paths:
- "manager/**"
- ".github/workflows/manager-dev-repo.yml"
workflow_dispatch:
concurrency:
group: manager-dev-repo-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: write
jobs:
build:
# macOS label required: the bare `self-hosted` label also matches the org's
# Linux runners, which have no xcodebuild and fail the build outright.
runs-on: [self-hosted, macOS]
env:
DEVELOPER_DIR: /Applications/Xcode.app/Contents/Developer # statt sudo xcode-select
steps:
- name: Checkout
uses: actions/checkout@v5
with:
submodules: recursive
fetch-depth: 1
- name: Cache SwiftPM
uses: actions/cache@v5
with:
path: ~/Library/Caches/org.swift.swiftpm
key: spm-${{ runner.os }}-${{ hashFiles('**/Package.resolved') }}
restore-keys: spm-${{ runner.os }}-
- name: Compute dev version
run: |
BASE=$(grep '^FTSTORE_PROJECT_VERSION' manager/FTStore.xcconfig | cut -d= -f2 | tr -d '[:space:]')
echo "VERSION=${BASE}-dev.${{ github.run_number }}" >> "$GITHUB_ENV"
echo "DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$GITHUB_ENV"
- name: Stamp marketing version
# NOT agvtool: it writes CFBundleShortVersionString into Info.plist, but
# the target sets MARKETING_VERSION = $(FTSTORE_PROJECT_VERSION) and
# GENERATE_INFOPLIST_FILE = YES, so Xcode regenerates that key from the
# build setting and the edit is thrown away. The IPA then reports the
# base version while manager-devrepo.json advertises the dev one, and
# every AltStore client sees a permanent "update available". Set the
# variable the build actually reads, and fail loudly if it ever stops
# matching.
run: |
sed -i '' "s|^FTSTORE_PROJECT_VERSION=.*|FTSTORE_PROJECT_VERSION=${VERSION}|" manager/FTStore.xcconfig
grep -q "^FTSTORE_PROJECT_VERSION=${VERSION}$" manager/FTStore.xcconfig
- name: Stamp build number (best effort)
# Records the source commit in CFBundleVersion. Best effort because
# agvtool trips over GENERATE_INFOPLIST_FILE=YES ("Cannot find …/YES")
# while still doing the write.
continue-on-error: true
working-directory: manager
run: agvtool new-version -all "${{ github.sha }}"
- name: Build (iphoneos, unsigned)
working-directory: manager
run: make iphoneos
- name: Verify the Live Activity extension shipped and can load
# The Dynamic Island lives in an embedded appex, so an IPA that is wrong
# about it installs and runs perfectly — the island just never appears,
# and nothing in the build says why. The same three silent failures the
# app checks for apply here unchanged: the extension missing, its bundle
# id not sitting under the host app's, and the wrong extension point.
# See ios/scripts/verify-live-activity.sh for what each one costs.
#
# The missing case is not hypothetical between these two products: they
# share a project name, a scheme, a target name and their pbxproj GUIDs,
# and now they both have an extension to sweep.
run: ios/scripts/verify-live-activity.sh manager/packages/FTStore-Manager.ipa
- name: Generate manager-devrepo.json
run: |
IPA=manager/packages/FTStore-Manager.ipa
SIZE=$(stat -f%z "$IPA")
URL="https://github.com/${{ github.repository }}/releases/download/manager-dev/FTStore-Manager.ipa"
jq \
--arg v "$VERSION" --arg d "$DATE" --argjson s "$SIZE" --arg u "$URL" '
.name = "FTStore Manager (Dev)" |
.identifier = "app.ftstore.manager.devrepo" |
.apps[0].name = "FTStore Manager (Dev)" |
.apps[0].subtitle = "Latest commit build — for testing" |
.apps[0].version = $v |
.apps[0].versionDate = $d |
.apps[0].size = $s |
.apps[0].downloadURL = $u |
.apps[0].versions = [ { "version": $v, "date": $d, "size": $s, "downloadURL": $u } ] |
.news = []
' manager/app-repo.json > manager-devrepo.json
echo "Generated manager-devrepo.json for $VERSION (${SIZE} bytes)"
- name: Publish dev prerelease
uses: softprops/action-gh-release@v3
with:
tag_name: manager-dev
name: Manager dev build
prerelease: true
make_latest: false
body: "Rolling development build of FTStore Manager from the latest `main` commit (${{ github.sha }}). FTStore Manager installs and updates FTStore on-device. Unsigned — sign it on-device at install (e.g. with your own certificate)."
files: |
manager/packages/FTStore-Manager.ipa
manager-devrepo.json