iOS: release/dev/prov builds, publish.sh, dev icon variants
release = org.cdnutter.<name> (main, Gitea release + Sideload) dev = org.cdnutter.<name>-dev (dev branch, Gitea pre-release + Sideload Developer) prov = org.cdnutter.<name>-prov (personal signed, never published) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
fabca5d1aa
commit
ec1cbdfe75
9 files changed
+133
-59
No files matched your search
+36
-38
@@ -1,23 +1,37 @@
|
||||
#!/bin/bash
|
||||
# Builds build/public/stemify-<version>.ipa on a Mac: ad-hoc signed, for releases and re-signing with Sideloadly/AltStore.
|
||||
# With prov/Stemify.mobileprovision, prov/key.p12 and P12_PASS set, it also writes a signed
|
||||
# build/prov/stemify-<version>-signed.ipa (keeps CarPlay/Siri) for the owner's own device.
|
||||
# Finished IPAs are kept per version; build/work is scratch and is wiped each run.
|
||||
# "build-ipa.sh dev" builds only build/dev/stemify-<version>-dev.ipa: bundle id org.cdnutter.stemify-dev,
|
||||
# named "Stemify Dev", ad-hoc signed, so it installs next to the release app. For internal tests, never released.
|
||||
# "build-ipa.sh prov" builds only the signed prov IPA (no public one).
|
||||
# ios/scripts/build-remote.sh runs this on White-M1 and copies build/public and build/prov back to DevOps.
|
||||
# Builds one Stemify IPA on a Mac. ios/scripts/build-remote.sh runs this on WHITE-M1 from DevOps.
|
||||
# build-ipa.sh release build/release/stemify-<v>.ipa org.cdnutter.stemify, ad-hoc signed. Gitea releases (main) + Sideload.
|
||||
# build-ipa.sh dev build/dev/stemify-<v>-dev.ipa org.cdnutter.stemify-dev, "Stemify Dev", ribbon icon.
|
||||
# Gitea pre-releases (dev branch) + Sideload Developer.
|
||||
# build-ipa.sh prov build/prov/stemify-<v>-prov.ipa org.cdnutter.stemify-prov, "Stemify Prov", signed with the
|
||||
# personal profile (CarPlay, Siri). Never published anywhere.
|
||||
# prov needs prov/Stemify.mobileprovision (App ID org.cdnutter.stemify-prov), prov/key.p12 and P12_PASS.
|
||||
# prov/StemifyWidgets.mobileprovision (org.cdnutter.stemify-prov.widgets) is optional, without it the widgets are left out.
|
||||
# build/work is scratch and is wiped each run.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
export PATH=/opt/homebrew/bin:$PATH
|
||||
B=build; OUT=$B/work; APP=$OUT/Payload/Stemify.app; EXT=$APP/PlugIns/StemifyWidgets.appex
|
||||
MODE=${1:-release}
|
||||
if [ "$MODE" = dev ]; then APP_ID=org.cdnutter.stemify-dev; APP_NAME="Stemify Dev"; APP_ICON=AppIconDev; DD=$B/dd-dev
|
||||
else APP_ID=org.cdnutter.stemify; APP_NAME=Stemify; APP_ICON=AppIcon; DD=$B/dd; fi
|
||||
rm -rf $OUT && mkdir -p $OUT/Payload $B/public $B/prov $B/dev
|
||||
MODE=${1:-dev}
|
||||
case $MODE in
|
||||
release) APP_ID=org.cdnutter.stemify; APP_NAME=Stemify; APP_ICON=AppIcon; SUFFIX= ;;
|
||||
dev) APP_ID=org.cdnutter.stemify-dev; APP_NAME="Stemify Dev"; APP_ICON=AppIconDev; SUFFIX=-dev ;;
|
||||
prov) APP_ID=org.cdnutter.stemify-prov; APP_NAME="Stemify Prov"; APP_ICON=AppIcon; SUFFIX=-prov ;;
|
||||
*) echo "usage: $0 release|dev|prov" >&2; exit 1 ;;
|
||||
esac
|
||||
DD=$B/dd-$MODE
|
||||
# "1.0 RC 1" -> 1.0-rc1, used in file names and release tags.
|
||||
VERSION=$(sed -nE 's/^ *VERSION_LABEL: "(.*)"/\1/p' project.yml | tr 'A-Z' 'a-z' | sed -E 's/ rc /-rc/; s/ /-/g')
|
||||
VERSION=$(sed -nE 's/^ *VERSION_LABEL: "(.*)"/\1/p' project.yml | tr 'A-Z' 'a-z' | sed -E 's/ (rc|beta) /-\1/; s/ /-/g')
|
||||
IPA=$B/$MODE/stemify-$VERSION$SUFFIX.ipa
|
||||
|
||||
if [ "$MODE" = prov ]; then
|
||||
for f in prov/Stemify.mobileprovision prov/key.p12; do [ -f $f ] || { echo "missing $f" >&2; exit 1; }; done
|
||||
[ -n "${P12_PASS:-}" ] || { echo "P12_PASS not set" >&2; exit 1; }
|
||||
PROF_ID=$(security cms -D -i prov/Stemify.mobileprovision | plutil -extract Entitlements.application-identifier raw -)
|
||||
[ "${PROF_ID#*.}" = "$APP_ID" ] || { echo "prov/Stemify.mobileprovision is for ${PROF_ID#*.}, need a profile for $APP_ID" >&2; exit 1; }
|
||||
fi
|
||||
|
||||
rm -rf $OUT && mkdir -p $OUT/Payload $B/$MODE
|
||||
# xcodebuild compiles the app, the widget extension, the asset catalog and the App Intents metadata.
|
||||
xcodegen generate -q
|
||||
xcodebuild -project Stemify.xcodeproj -scheme Stemify -configuration Release -sdk iphoneos \
|
||||
@@ -25,30 +39,16 @@ xcodebuild -project Stemify.xcodeproj -scheme Stemify -configuration Release -sd
|
||||
cp -R $DD/Build/Products/Release-iphoneos/Stemify.app $APP
|
||||
[ -d $EXT ] || { echo "widget extension missing" >&2; exit 1; }
|
||||
|
||||
# Ad-hoc signature carrying only the App Group, so Sideloadly/AltStore keep the widgets' shared data when re-signing.
|
||||
cat > $OUT/group.plist <<PL
|
||||
if [ "$MODE" != prov ]; then
|
||||
# Ad-hoc signature carrying only the App Group, so Sideloadly/AltStore keep the widgets' shared data when re-signing.
|
||||
cat > $OUT/group.plist <<PL
|
||||
<?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>com.apple.security.application-groups</key><array><string>group.$APP_ID</string></array></dict></plist>
|
||||
PL
|
||||
codesign -f -s - --entitlements $OUT/group.plist $EXT
|
||||
codesign -f -s - --entitlements $OUT/group.plist $APP
|
||||
|
||||
if [ "$MODE" = dev ]; then
|
||||
rm -f $B/dev/stemify-$VERSION-dev.ipa
|
||||
(cd $OUT && zip -qry ../dev/stemify-$VERSION-dev.ipa Payload)
|
||||
echo "$B/dev/stemify-$VERSION-dev.ipa"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Public build, for the releases and AltStore/Sideloadly.
|
||||
if [ "$MODE" != prov ]; then
|
||||
rm -f $B/public/stemify-$VERSION.ipa
|
||||
(cd $OUT && zip -qry ../public/stemify-$VERSION.ipa Payload)
|
||||
echo "$B/public/stemify-$VERSION.ipa"
|
||||
fi
|
||||
|
||||
if [ -f prov/Stemify.mobileprovision ] && [ -f prov/key.p12 ] && [ -n "${P12_PASS:-}" ]; then
|
||||
codesign -f -s - --entitlements $OUT/group.plist $EXT
|
||||
codesign -f -s - --entitlements $OUT/group.plist $APP
|
||||
else
|
||||
KC=$PWD/$OUT/sign.keychain-db; KP=$(uuidgen)
|
||||
security create-keychain -p "$KP" $KC
|
||||
security set-keychain-settings -lut 3600 $KC
|
||||
@@ -62,7 +62,6 @@ if [ -f prov/Stemify.mobileprovision ] && [ -f prov/key.p12 ] && [ -n "${P12_PAS
|
||||
ID=$(security find-identity -v -p codesigning $KC | sed -nE 's/.*"(Apple Development:.*)"/\1/p' | head -1)
|
||||
security cms -D -i prov/Stemify.mobileprovision > $OUT/prov.plist
|
||||
/usr/libexec/PlistBuddy -x -c 'Print :Entitlements' $OUT/prov.plist > $OUT/ent.plist
|
||||
# The widgets need their own profile (org.cdnutter.stemify.widgets with the App Group). Without one they're left out.
|
||||
if [ -f prov/StemifyWidgets.mobileprovision ]; then
|
||||
security cms -D -i prov/StemifyWidgets.mobileprovision > $OUT/wprov.plist
|
||||
/usr/libexec/PlistBuddy -x -c 'Print :Entitlements' $OUT/wprov.plist > $OUT/went.plist
|
||||
@@ -75,9 +74,8 @@ if [ -f prov/Stemify.mobileprovision ] && [ -f prov/key.p12 ] && [ -n "${P12_PAS
|
||||
codesign -f -s "$ID" --entitlements $OUT/ent.plist --timestamp=none $APP
|
||||
security list-keychains -d user -s $(security list-keychains -d user | tr -d "\"" | grep -v sign.keychain)
|
||||
security delete-keychain $KC
|
||||
# Personal build for this device only: stays in build/prov (gitignored), never released.
|
||||
rm -f $B/prov/stemify-$VERSION-signed.ipa
|
||||
(cd $OUT && zip -qry ../prov/stemify-$VERSION-signed.ipa Payload)
|
||||
echo "$B/prov/stemify-$VERSION-signed.ipa (signed as $ID)"
|
||||
fi
|
||||
|
||||
rm -f $IPA
|
||||
(cd $OUT && zip -qry ../$MODE/$(basename $IPA) Payload)
|
||||
echo "$IPA"
|
||||
@@ -1,23 +1,17 @@
|
||||
#!/bin/bash
|
||||
# Run from DevOps: syncs ios/ to White-M1, builds there, and pulls the IPAs back into ios/build.
|
||||
# build-remote.sh dev + prov (the usual build): build/dev and build/prov
|
||||
# build-remote.sh public public + prov, only when a public release is asked for: build/public and build/prov
|
||||
# build-remote.sh dev dev only
|
||||
# Run from DevOps: syncs ios/ to WHITE-M1, builds there, and pulls the IPAs back into ios/build/<mode>.
|
||||
# build-remote.sh dev build (the usual one): build/dev
|
||||
# build-remote.sh release release build, only when a release is asked for: build/release
|
||||
# build-remote.sh prov personal signed build: build/prov
|
||||
# build-remote.sh dev prov ... several at once
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
H=${BUILD_HOST:-white-m1}; R=stemify-build/ios
|
||||
case "${1:-}" in
|
||||
"") MODES="dev prov" ;;
|
||||
public) MODES="release" ;;
|
||||
dev) MODES="dev" ;;
|
||||
*) echo "usage: $0 [public|dev]" >&2; exit 1 ;;
|
||||
esac
|
||||
MODES=${*:-dev}
|
||||
for m in $MODES; do case $m in release|dev|prov) ;; *) echo "usage: $0 [release|dev|prov]..." >&2; exit 1 ;; esac; done
|
||||
rsync -a --exclude build --exclude prov --exclude Stemify.xcodeproj ./ $H:$R/
|
||||
for m in $MODES; do
|
||||
ssh $H "cd $R && P12_PASS=\$(cat prov/cert_pass.txt 2>/dev/null) bash scripts/build-ipa.sh $m"
|
||||
mkdir -p build/$m && rsync -a $H:$R/build/$m/ build/$m/
|
||||
done
|
||||
for m in $MODES; do
|
||||
case $m in dev) DIRS=dev ;; prov) DIRS=prov ;; release) DIRS="public prov" ;; esac
|
||||
for d in $DIRS; do mkdir -p build/$d; rsync -a $H:$R/build/$d/ build/$d/; done
|
||||
done
|
||||
ls build/*/stemify-$(sed -nE 's/^ *VERSION_LABEL: "(.*)"/\1/p' project.yml | tr 'A-Z' 'a-z' | sed -E 's/ rc /-rc/; s/ /-/g')*.ipa 2>/dev/null
|
||||
ls build/{release,dev,prov}/stemify-$(sed -nE 's/^ *VERSION_LABEL: "(.*)"/\1/p' project.yml | tr 'A-Z' 'a-z' | sed -E 's/ (rc|beta) /-\1/; s/ /-/g')*.ipa 2>/dev/null
|
||||
@@ -36,7 +36,8 @@ versions = []
|
||||
for rel in get(API + "/releases?limit=50"):
|
||||
v = rel["tag_name"].removeprefix("ios-v")
|
||||
ipa = next((a for a in rel["assets"] if a["name"] == f"stemify-{v}.ipa"), None)
|
||||
if not rel["tag_name"].startswith("ios-v") or not ipa:
|
||||
# Pre-releases are dev builds (org.cdnutter.stemify-dev), they go to Sideload Developer instead.
|
||||
if not rel["tag_name"].startswith("ios-v") or not ipa or rel["prerelease"]:
|
||||
continue
|
||||
versions.append({
|
||||
"version": v.split("-")[0],
|
||||
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/bin/bash
|
||||
# Publishes an IPA built by build-remote.sh. Run from DevOps with everything committed.
|
||||
# publish.sh dev on the dev branch: Gitea pre-release ios-v<v>-dev.<build> + Sideload Developer (WHITE-M1 :8097)
|
||||
# publish.sh release on main, only when the owner says so: Gitea release ios-v<v> + Sideload (:8093), then source.json
|
||||
# Release notes come from $NOTES, else the last commit subject. prov builds are never published.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
MODE=${1:-}; NAME=stemify; TAGP=ios-v
|
||||
API=https://git.iamchrisama.com/api/v1/repos/cdnutter/stemify
|
||||
T=$(sed -n 's#.*://cdnutter:\([^@]*\)@.*#\1#p' ~/.config/gitea/cdnutter.credentials | head -1)
|
||||
V=$(sed -nE 's/^ *VERSION_LABEL: "(.*)"/\1/p' project.yml | tr 'A-Z' 'a-z' | sed -E 's/ (rc|beta) /-\1/; s/ /-/g')
|
||||
BUILD=$(sed -nE 's/^ *CURRENT_PROJECT_VERSION: "(.*)"/\1/p' project.yml)
|
||||
BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
die() { echo "$*" >&2; exit 1; }
|
||||
case $MODE in
|
||||
dev) WANT=dev; IPA=build/dev/$NAME-$V-dev.ipa; TAG=$TAGP$V-dev.$BUILD; PRE=true; UP=http://10.0.0.11:8097 ;;
|
||||
release) WANT=main; IPA=build/release/$NAME-$V.ipa; TAG=$TAGP$V; PRE=false; UP=http://10.0.0.11:8093 ;;
|
||||
*) die "usage: $0 dev|release" ;;
|
||||
esac
|
||||
[ "$BRANCH" = "$WANT" ] || die "$MODE builds publish from $WANT (on $BRANCH)"
|
||||
[ -f "$IPA" ] || die "missing $IPA, run scripts/build-remote.sh $MODE"
|
||||
git diff --quiet HEAD || die "uncommitted changes"
|
||||
[ "$(curl -s -o /dev/null -w '%{http_code}' -H "Authorization: token $T" $API/releases/tags/$TAG)" = 404 ] \
|
||||
|| die "$TAG already exists, bump CURRENT_PROJECT_VERSION (dev) or VERSION_LABEL (release)"
|
||||
git push -q -u origin $BRANCH
|
||||
BODY=${NOTES:-$(git log -1 --format=%s)}
|
||||
ID=$(jq -n --arg t "$TAG" --arg c "$(git rev-parse HEAD)" --arg b "$BODY" --argjson p $PRE \
|
||||
'{tag_name:$t, target_commitish:$c, name:$t, body:$b, prerelease:$p}' |
|
||||
curl -fsS -H "Authorization: token $T" -H 'Content-Type: application/json' -d @- $API/releases | jq -r .id)
|
||||
curl -fsS -o /dev/null -H "Authorization: token $T" -F "attachment=@$IPA" "$API/releases/$ID/assets?name=$(basename $IPA)"
|
||||
curl -fsS -o /dev/null -X POST --data-binary @$IPA $UP/api/upload/ipa
|
||||
echo "published $TAG ($(basename $IPA)) to Gitea and $UP"
|
||||
if [ $MODE = release ]; then
|
||||
GITEA_TOKEN=$T python3 scripts/make-source.py
|
||||
git add source.json && git commit -qm "iOS source.json for $TAG" && git push -q origin main
|
||||
fi
|
||||
Reference in new issue
Block a user