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.
111 lines
5.4 KiB
YAML
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
|