b3adfb3a56
apple / swift (push) Successful in 1m23s
release / apple (push) Successful in 9m19s
ci / web (push) Successful in 52s
ci / docs-site (push) Successful in 59s
apple / screenshots (push) Successful in 6m31s
ci / rust (push) Failing after 6m18s
ci / bench (push) Successful in 5m7s
deb / build-publish (push) Successful in 8m58s
decky / build-publish (push) Successful in 30s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 21s
arch / build-publish (push) Failing after 16m42s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 11s
docker / build-push (ci, ci/rust-ci-noble.Dockerfile, punktfunk-rust-ci-noble) (push) Successful in 10s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 46s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 15s
android / android (push) Successful in 18m40s
deb / build-publish-host (push) Successful in 9m25s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 1m5s
docker / deploy-docs (push) Successful in 25s
rpm / build-publish (43, bazzite, punktfunk-fedora-rpm) (push) Successful in 19m38s
rpm / build-publish (44, fedora-44, punktfunk-fedora44-rpm) (push) Successful in 19m36s
v0.16.0's Apple leg failed at the xcframework build with "No space left on device (os error 28)": the runner's boot volume was at 100% with 152 MB free. None of the four `xcodebuild archive` calls passed -derivedDataPath, so each one used the default ~/Library/Developer/Xcode/DerivedData/<name>-<hash> — and that hash is derived from the PROJECT'S ABSOLUTE PATH. act_runner rotates its workspace (~/.cache/act/<hash>/hostexecutor), so every rotation looked like a new project to Xcode and minted a fresh ~760 MB tree. Nothing ever collects those: they live outside the workspace, so act's own cleanup never sees them. 31 had accumulated in three days (17 -> 20 July), which with the 12 GB shared ModuleCache came to 32 GB — on a 228 GB volume already 95% full. Pin all four archives to one path. The tree is now REUSED rather than multiplied, which also keeps the module cache warm instead of rebuilding it per run. The new step additionally prunes anything week-stale left in the default root, covering both the legacy per-path trees and any other job that lands there. Cleared by hand on home-mac-mini-1 to unblock the release (152 MB -> 31 GB free); this is the change that stops it coming back. Note the same class of problem bit the Windows runner the same night from the other direction — its disk-cleanup task purges C:\t and deleted files out from under ISCC mid-pack — which is worth its own look and is NOT addressed here. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
451 lines
28 KiB
YAML
451 lines
28 KiB
YAML
# Production Apple client builds — runs on the macos-arm64 runner (home-mac-mini-1).
|
|
#
|
|
# Tag v* (or workflow_dispatch):
|
|
# macOS (Developer ID) -> sandboxed, signed, notarized + stapled .dmg, attached to a
|
|
# Gitea release on tag pushes
|
|
# macOS (App Store) -> archive + upload to TestFlight (App Store Connect)
|
|
# iOS -> archive + upload straight to TestFlight (App Store Connect)
|
|
# tvOS -> archive + upload to TestFlight (Rust core built from tier-3 targets,
|
|
# nightly -Zbuild-std, in build-xcframework.sh)
|
|
#
|
|
# One App Store listing for all platforms (universal purchase): every target shares the
|
|
# bundle ID io.unom.punktfunk.
|
|
#
|
|
# The macOS app is App-SANDBOXED for both channels (Config/Punktfunk-macOS.entitlements —
|
|
# app-sandbox + network client/server + audio-input + bluetooth/usb device access; the
|
|
# shared Config/Punktfunk.entitlements stays iOS/tvOS-only, where app-sandbox is invalid).
|
|
# The Developer ID DMG is codesigned with the SAME macOS entitlements as the App Store build,
|
|
# BUT it must ALSO embed a Developer ID provisioning profile: keychain-access-groups is a
|
|
# MANAGED entitlement that AMFI only honors when an embedded profile authorizes it. A DMG
|
|
# without one is SIGKILLed at spawn ("Launchd job spawn failed", POSIX errno 163) even though
|
|
# it is validly signed AND notarized. ⌘R hides this (Xcode embeds a development profile); the
|
|
# raw Developer ID codesign path does NOT, so ⌘R is NOT equivalent to the shipped DMG here.
|
|
#
|
|
# macOS App Store prerequisites (one-time, Apple portal — NOT done by this workflow; the
|
|
# step is continue-on-error until they exist):
|
|
# * App Store Connect: add the macOS platform to the io.unom.punktfunk app record
|
|
# (universal purchase).
|
|
# * A "Punktfunk macOS App Store Distribution" provisioning profile installed on the
|
|
# runner (under ~/Library/Developer/Xcode/UserData/Provisioning Profiles/).
|
|
# * The "3rd Party Mac Developer Installer" (Mac Installer Distribution) certificate in
|
|
# the runner's login keychain, in addition to "Apple Distribution" — the App Store
|
|
# .pkg is installer-signed with it.
|
|
#
|
|
# macOS Developer ID (DMG) prerequisite (one-time, Apple portal — the DMG step embeds it):
|
|
# * A "Punktfunk macOS Developer ID" provisioning profile (Distribution -> Developer ID,
|
|
# App ID io.unom.punktfunk, with the Keychain Sharing capability) installed on the runner
|
|
# under ~/Library/Developer/Xcode/UserData/Provisioning Profiles/. It authorizes the
|
|
# managed keychain-access-groups entitlement; without it the DMG is SIGKILLed at launch
|
|
# (errno 163). If it is missing the DMG step warns and strips that entitlement (the app
|
|
# then uses ClientIdentityStore's legacy file-keychain fallback) so the build still ships
|
|
# a launchable app.
|
|
#
|
|
# Signing setup (NOT secret-based anymore): the runner is a LaunchAgent in the user's
|
|
# logged-in Aqua session, so it uses the **login keychain** directly. Install the signing
|
|
# identities there once via Xcode (Settings -> Accounts -> Manage Certificates): Developer
|
|
# ID Application + Apple Distribution, with the WWDR intermediate present (so they show as
|
|
# *valid*). xcodebuild/codesign then sign exactly like a local build — no throwaway keychain.
|
|
# One-time, to avoid headless "codesign wants to use the key" prompts, grant codesign access:
|
|
# security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k <login-pw> \
|
|
# ~/Library/Keychains/login.keychain-db
|
|
#
|
|
# Secrets: only ASC_API_KEY_P8 / ASC_API_KEY_ID / ASC_API_ISSUER_ID (App Store Connect API
|
|
# key — notarization, TestFlight upload, automatic-signing profile fetch).
|
|
#
|
|
# Needs a RELEASE Xcode on the runner (App Store rejects beta-SDK builds); the workflow
|
|
# picks the first non-beta /Applications/Xcode*.app and only falls back to a beta with a
|
|
# loud warning.
|
|
name: release
|
|
|
|
on:
|
|
push:
|
|
# Canary: a relevant main push uploads the iOS + macOS + tvOS builds to TestFlight (Apple's
|
|
# own canary channel) — no notarized DMG (that's stable-only; see the per-step gates).
|
|
# Heavy on the shared mac-mini runner, so paths-filtered; the TestFlight steps are
|
|
# continue-on-error until the App Store Connect record exists, so this no-ops until then.
|
|
branches: [main]
|
|
paths:
|
|
- 'clients/apple/**'
|
|
- 'crates/punktfunk-core/**'
|
|
- 'scripts/build-xcframework.sh'
|
|
- 'Cargo.lock'
|
|
- '.gitea/workflows/release.yml'
|
|
# Stable: a `vX.Y.Z` tag is THE release — notarized DMG attached to the unified Gitea Release
|
|
# + macOS/iOS/tvOS to TestFlight for manual promotion to the App Store.
|
|
tags: ['v*']
|
|
workflow_dispatch:
|
|
inputs:
|
|
testflight:
|
|
description: "Upload the iOS build to TestFlight (true/false)"
|
|
required: false
|
|
default: "true"
|
|
|
|
jobs:
|
|
apple:
|
|
runs-on: macos-arm64
|
|
timeout-minutes: 120
|
|
env:
|
|
TEAM_ID: F4H37KF6WC
|
|
PROJECT: clients/apple/Punktfunk.xcodeproj
|
|
steps:
|
|
- uses: actions/checkout@v4
|
|
|
|
- name: Select release Xcode
|
|
run: |
|
|
DEV_DIR=""
|
|
for app in /Applications/Xcode.app /Applications/Xcode_*.app /Applications/Xcode-*.app; do
|
|
case "$app" in *beta*|*Beta*) continue;; esac
|
|
[ -x "$app/Contents/Developer/usr/bin/xcodebuild" ] && DEV_DIR="$app/Contents/Developer" && break
|
|
done
|
|
if [ -z "$DEV_DIR" ]; then
|
|
for app in /Applications/Xcode*.app; do
|
|
[ -x "$app/Contents/Developer/usr/bin/xcodebuild" ] && DEV_DIR="$app/Contents/Developer" && break
|
|
done
|
|
echo "::warning::No release Xcode found — using $DEV_DIR. TestFlight/App Store REJECTS beta-SDK builds."
|
|
fi
|
|
[ -n "$DEV_DIR" ] || { echo "no usable Xcode found" >&2; exit 1; }
|
|
# Scoped to xcodebuild steps only (XCODE_DEV_DIR, not DEVELOPER_DIR): cargo must
|
|
# keep the system-default linker — a newer-than-OS Xcode's ld produces dylibs the
|
|
# running dyld rejects, killing proc-macro loads (see build-xcframework.sh).
|
|
echo "XCODE_DEV_DIR=$DEV_DIR" >> "$GITHUB_ENV"
|
|
DEVELOPER_DIR="$DEV_DIR" xcodebuild -version
|
|
|
|
- name: Version from tag
|
|
run: |
|
|
eval "$(bash scripts/ci/pf-version.sh)" # -> PF_BASE, PF_CHANNEL, PF_STABLE_TAG (single source of truth)
|
|
case "$GITHUB_REF" in
|
|
refs/tags/v*) V="${GITHUB_REF_NAME#v}"; V="${V%%-*}" ;; # App Store marketing version is numeric X.Y.Z (drop -rc)
|
|
*) V="$PF_BASE" ;; # canary marketing version = one minor ahead of the latest stable tag; the build number disambiguates
|
|
esac
|
|
echo "VERSION=$V" >> "$GITHUB_ENV"
|
|
echo "BUILD_NUM=$GITHUB_RUN_NUMBER" >> "$GITHUB_ENV"
|
|
echo "version $V build $GITHUB_RUN_NUMBER (channel $PF_CHANNEL, latest stable ${PF_STABLE_TAG})"
|
|
|
|
- name: Rust toolchain (mac + iOS + tvOS slices)
|
|
run: |
|
|
RUSTUP="$(command -v rustup || echo "$HOME/.cargo/bin/rustup")"
|
|
dirname "$RUSTUP" >> "$GITHUB_PATH"
|
|
"$RUSTUP" target add aarch64-apple-darwin x86_64-apple-darwin \
|
|
aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios
|
|
# tvOS targets are tier-3 (no prebuilt std) — build-xcframework.sh compiles them with
|
|
# nightly + -Zbuild-std, so ensure nightly + rust-src are present.
|
|
"$RUSTUP" toolchain install nightly --profile minimal
|
|
"$RUSTUP" component add rust-src --toolchain nightly
|
|
|
|
# The in-core Opus decode (surround) pulls audiopus_sys, which builds a vendored static libopus
|
|
# via CMake — keep the xcframework self-contained (no runtime libopus.dylib on end-user devices).
|
|
- name: CMake (for the vendored libopus audiopus_sys builds)
|
|
run: |
|
|
# Runner steps run with `bash --noprofile --norc`, so Homebrew's bin dir isn't on PATH —
|
|
# locate brew explicitly, install cmake if missing, and export its bin dir to GITHUB_PATH so
|
|
# the xcframework build step (audiopus_sys → vendored libopus) finds `cmake`.
|
|
for B in /opt/homebrew/bin/brew /usr/local/bin/brew; do [ -x "$B" ] && BREW="$B" && break; done
|
|
if [ -z "$BREW" ]; then echo "::error::Homebrew not found on the runner"; exit 1; fi
|
|
BREW_BIN="$(dirname "$BREW")"; export PATH="$BREW_BIN:$PATH"
|
|
command -v cmake >/dev/null || "$BREW" install cmake
|
|
echo "$BREW_BIN" >> "$GITHUB_PATH"
|
|
# Homebrew's CMake 4 dropped compatibility with the vendored libopus's pre-3.5
|
|
# `cmake_minimum_required`; treat 3.5 as the policy minimum (the cmake crate's child cmake
|
|
# inherits this from the env during the xcframework build).
|
|
echo "CMAKE_POLICY_VERSION_MINIMUM=3.5" >> "$GITHUB_ENV"
|
|
|
|
- name: Pin + prune Xcode DerivedData
|
|
# Without -derivedDataPath, xcodebuild derives its DerivedData directory name from the
|
|
# PROJECT'S ABSOLUTE PATH — and act_runner rotates its workspace
|
|
# (~/.cache/act/<hash>/hostexecutor), so each rotation minted a brand new ~760 MB tree
|
|
# under ~/Library that nothing ever collected. 31 of them piled up in three days
|
|
# (~32 GB with the shared ModuleCache), filled the runner's boot volume, and failed
|
|
# v0.16.0's xcframework build with "No space left on device". Pinning one path makes the
|
|
# tree REUSED instead of multiplied — it also keeps the module cache warm between runs.
|
|
run: |
|
|
DD="$HOME/ci/derived-data/release"
|
|
mkdir -p "$DD"
|
|
echo "DERIVED_DATA=$DD" >> "$GITHUB_ENV"
|
|
# Safety net for trees the pin does not own: the legacy per-path ones from before this
|
|
# change, and anything another job leaves in the default root. Untouched for a week ⇒ gone.
|
|
if [ -d "$HOME/Library/Developer/Xcode/DerivedData" ]; then
|
|
find "$HOME/Library/Developer/Xcode/DerivedData" -mindepth 1 -maxdepth 1 \
|
|
-mtime +7 -exec rm -rf {} + 2>/dev/null || true
|
|
fi
|
|
echo "disk after prune:"; df -h /System/Volumes/Data | tail -1
|
|
|
|
- name: Build PunktfunkCore.xcframework (mac + iOS + tvOS)
|
|
# tvOS is a tier-3 target (nightly -Zbuild-std): slow on the first build, then cached on
|
|
# the self-hosted runner. Built on canary too so the tvOS archive/upload below runs on the
|
|
# same track as iOS/macOS (the nightly toolchain is installed unconditionally above).
|
|
run: BUILD_IOS=1 BUILD_TVOS=1 bash scripts/build-xcframework.sh
|
|
|
|
- name: Stage App Store Connect API key
|
|
env:
|
|
ASC_P8: ${{ secrets.ASC_API_KEY_P8 }}
|
|
run: |
|
|
printf '%s' "$ASC_P8" > "$RUNNER_TEMP/asc.p8"
|
|
chmod 600 "$RUNNER_TEMP/asc.p8"
|
|
|
|
- name: macOS — archive, codesign Developer ID, notarize, DMG
|
|
# Stable releases only — the notarized DMG is a Gatekeeper/direct-download artifact, not
|
|
# relevant to TestFlight testers (the canary channel). Skipped on canary main pushes.
|
|
if: startsWith(gitea.ref, 'refs/tags/v')
|
|
run: |
|
|
# Archive UNSIGNED, then codesign with the Developer ID Application identity from the
|
|
# login keychain. Unsigned archive sidesteps Xcode's keychain-access-groups
|
|
# provisioning-profile gate at archive time; we re-assert that authorization below by
|
|
# EMBEDDING a Developer ID profile before codesign (see the keychain note further down).
|
|
# Bundle is a single static binary.
|
|
DEVELOPER_DIR="$XCODE_DEV_DIR" xcodebuild archive \
|
|
-project "$PROJECT" -scheme Punktfunk \
|
|
-destination 'generic/platform=macOS' \
|
|
-archivePath "$RUNNER_TEMP/Punktfunk-macos.xcarchive" \
|
|
-derivedDataPath "$DERIVED_DATA" \
|
|
-skipMacroValidation -skipPackagePluginValidation \
|
|
MARKETING_VERSION="$VERSION" CURRENT_PROJECT_VERSION="$BUILD_NUM" \
|
|
CODE_SIGNING_ALLOWED=NO
|
|
APP="$RUNNER_TEMP/Punktfunk-macos.xcarchive/Products/Applications/Punktfunk.app"
|
|
# Sandboxed Developer ID: sign with the SAME macOS entitlements the App Store build
|
|
# uses. codesign won't expand $(AppIdentifierPrefix) — resolve it to the team prefix.
|
|
RESOLVED="$RUNNER_TEMP/macos.entitlements"
|
|
sed "s/\$(AppIdentifierPrefix)/${TEAM_ID}./g" \
|
|
clients/apple/Config/Punktfunk-macOS.entitlements > "$RESOLVED"
|
|
|
|
# keychain-access-groups is a MANAGED (restricted) entitlement: App Sandbox and the
|
|
# network/device keys are self-asserted for Developer ID, but a keychain access group
|
|
# must be AUTHORIZED by an embedded provisioning profile. Without one, AMFI refuses to
|
|
# spawn the sandboxed process at launch — "Launchd job spawn failed" (POSIX errno 163),
|
|
# SIGKILL before main() — even though the bundle is validly signed and notarized. Embed
|
|
# a "Developer ID" distribution profile for io.unom.punktfunk (Keychain Sharing) so its
|
|
# entitlements authorize the access group, exactly like the App Store build's profile
|
|
# does. Located by profile Name among the profiles installed on the runner (see header).
|
|
DEVID_PROFILE_NAME="Punktfunk macOS Developer ID"
|
|
PROFILE_SRC=""
|
|
for p in "$HOME/Library/Developer/Xcode/UserData/Provisioning Profiles/"*.provisionprofile \
|
|
"$HOME/Library/MobileDevice/Provisioning Profiles/"*.provisionprofile; do
|
|
[ -e "$p" ] || continue
|
|
NAME=$(security cms -D -i "$p" 2>/dev/null | plutil -extract Name raw - 2>/dev/null || true)
|
|
[ "$NAME" = "$DEVID_PROFILE_NAME" ] && PROFILE_SRC="$p" && break
|
|
done
|
|
if [ -n "$PROFILE_SRC" ]; then
|
|
# Must land BEFORE codesign so it's sealed into the bundle.
|
|
cp "$PROFILE_SRC" "$APP/Contents/embedded.provisionprofile"
|
|
echo "embedded Developer ID profile: $PROFILE_SRC"
|
|
else
|
|
# Fallback so a missing/expired profile NEVER reships the errno-163 brick: drop the
|
|
# managed entitlement and let ClientIdentityStore fall back to the legacy file keychain
|
|
# (its errSecMissingEntitlement path). Degraded (one Keychain prompt) but launchable.
|
|
echo "::warning::Developer ID profile '$DEVID_PROFILE_NAME' not installed on the runner — stripping keychain-access-groups so the DMG still launches (legacy file keychain). Create it in the Apple portal + install it on the runner to restore the no-prompt data-protection keychain."
|
|
/usr/libexec/PlistBuddy -c "Delete :keychain-access-groups" "$RESOLVED" 2>/dev/null || true
|
|
fi
|
|
|
|
codesign --force --options runtime --timestamp \
|
|
--entitlements "$RESOLVED" \
|
|
--sign "Developer ID Application" "$APP"
|
|
codesign --verify --strict --verbose=2 "$APP"
|
|
# Notarized DMG.
|
|
STAGE="$RUNNER_TEMP/dmg-stage"
|
|
mkdir -p "$STAGE"
|
|
cp -R "$APP" "$STAGE/"
|
|
ln -s /Applications "$STAGE/Applications"
|
|
DMG="$RUNNER_TEMP/Punktfunk-$VERSION.dmg"
|
|
hdiutil create -volname "Punktfunk" -srcfolder "$STAGE" -ov -format UDZO "$DMG"
|
|
DEVELOPER_DIR="$XCODE_DEV_DIR" xcrun notarytool submit "$DMG" --wait \
|
|
--key "$RUNNER_TEMP/asc.p8" \
|
|
--key-id "${{ secrets.ASC_API_KEY_ID }}" \
|
|
--issuer "${{ secrets.ASC_API_ISSUER_ID }}"
|
|
DEVELOPER_DIR="$XCODE_DEV_DIR" xcrun stapler staple "$DMG"
|
|
echo "DMG=$DMG" >> "$GITHUB_ENV"
|
|
|
|
- name: Attach DMG to the Gitea release (stable tags only)
|
|
if: startsWith(gitea.ref, 'refs/tags/v')
|
|
env:
|
|
GITEA_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
|
run: |
|
|
. scripts/ci/gitea-release.sh
|
|
RID=$(ensure_release "$GITHUB_REF_NAME" "$GITHUB_REF_NAME" auto)
|
|
upsert_asset "$RID" "$DMG" "Punktfunk-$VERSION.dmg"
|
|
|
|
- name: macOS App Store — archive + upload to TestFlight
|
|
if: gitea.event_name != 'workflow_dispatch' || inputs.testflight == 'true'
|
|
# Best-effort until the App Store Connect record has the macOS platform + the
|
|
# "Punktfunk macOS App Store Distribution" profile and the "3rd Party Mac Developer
|
|
# Installer" cert are on the runner (see the header). The macOS app is sandboxed
|
|
# (Config/Punktfunk-macOS.entitlements) — mandatory for the Mac App Store.
|
|
continue-on-error: true
|
|
run: |
|
|
# Separate archive from the Developer ID one above: App Store needs a signed, entitled
|
|
# archive that -exportArchive can re-sign for distribution, not the unsigned-then-codesign
|
|
# DMG path. Archive with AUTOMATIC signing (development). Why not a manually-specified
|
|
# profile (as this step used to do): the in-app license screens added a SwiftPM resource
|
|
# bundle (PunktfunkKit_PunktfunkKit), and a resource bundle is a product type that cannot
|
|
# carry a provisioning profile — a global PROVISIONING_PROFILE_SPECIFIER (here) or an
|
|
# sdk-scoped one (iOS/tvOS) lands on it and fails the archive ("does not support
|
|
# provisioning profiles"). Automatic signing assigns a profile only to the app and leaves
|
|
# the resource bundle (and the macOS-host macro plugins) alone, and bakes the sandbox
|
|
# entitlements in. -allowProvisioningUpdates lets Xcode sync the App ID capabilities and
|
|
# regenerate the managed *development* profile — needed because the App Groups capability
|
|
# (group.io.unom.punktfunk, in Config/Punktfunk-macOS.entitlements) invalidated the cached
|
|
# one. This is DEVELOPMENT signing against the Apple Development cert already in the
|
|
# keychain, so the App-Manager ASC key suffices. DISTRIBUTION signing happens in the export
|
|
# step below
|
|
# (manual, via the plist). Quit Xcode so it can't prune the manually-installed App Store
|
|
# distribution profile that export needs.
|
|
osascript -e 'tell application "Xcode" to quit' >/dev/null 2>&1 || true
|
|
pkill -x Xcode 2>/dev/null || true
|
|
PROFILE="Punktfunk macOS App Store Distribution"
|
|
DEVELOPER_DIR="$XCODE_DEV_DIR" xcodebuild archive \
|
|
-project "$PROJECT" -scheme Punktfunk \
|
|
-destination 'generic/platform=macOS' \
|
|
-archivePath "$RUNNER_TEMP/Punktfunk-macos-appstore.xcarchive" \
|
|
-derivedDataPath "$DERIVED_DATA" \
|
|
-skipMacroValidation -skipPackagePluginValidation \
|
|
-allowProvisioningUpdates \
|
|
-authenticationKeyPath "$RUNNER_TEMP/asc.p8" \
|
|
-authenticationKeyID "${{ secrets.ASC_API_KEY_ID }}" \
|
|
-authenticationKeyIssuerID "${{ secrets.ASC_API_ISSUER_ID }}" \
|
|
MARKETING_VERSION="$VERSION" CURRENT_PROJECT_VERSION="$BUILD_NUM" \
|
|
CODE_SIGN_STYLE=Automatic \
|
|
DEVELOPMENT_TEAM="$TEAM_ID"
|
|
cat > "$RUNNER_TEMP/export-macos-appstore.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>upload</string>
|
|
<key>teamID</key><string>$TEAM_ID</string>
|
|
<key>signingStyle</key><string>manual</string>
|
|
<key>signingCertificate</key><string>Apple Distribution</string>
|
|
<key>installerSigningCertificate</key><string>3rd Party Mac Developer Installer</string>
|
|
<key>provisioningProfiles</key>
|
|
<dict><key>io.unom.punktfunk</key><string>$PROFILE</string></dict>
|
|
</dict>
|
|
</plist>
|
|
EOF
|
|
DEVELOPER_DIR="$XCODE_DEV_DIR" xcodebuild -exportArchive \
|
|
-archivePath "$RUNNER_TEMP/Punktfunk-macos-appstore.xcarchive" \
|
|
-exportOptionsPlist "$RUNNER_TEMP/export-macos-appstore.plist" \
|
|
-exportPath "$RUNNER_TEMP/export-macos-appstore" \
|
|
-authenticationKeyPath "$RUNNER_TEMP/asc.p8" \
|
|
-authenticationKeyID "${{ secrets.ASC_API_KEY_ID }}" \
|
|
-authenticationKeyIssuerID "${{ secrets.ASC_API_ISSUER_ID }}"
|
|
|
|
- name: iOS — archive + upload to TestFlight
|
|
if: gitea.event_name != 'workflow_dispatch' || inputs.testflight == 'true'
|
|
# Best-effort until the App Store Connect app record for io.unom.punktfunk exists.
|
|
continue-on-error: true
|
|
run: |
|
|
# Archive with AUTOMATIC signing (development) — see the macOS App Store step for the full
|
|
# rationale. The SwiftPM resource bundle (PunktfunkKit_PunktfunkKit, added with the in-app
|
|
# license screens) builds for iphoneos, so even the sdk-scoped PROVISIONING_PROFILE_SPECIFIER
|
|
# this step used to set matched it and failed the archive ("does not support provisioning
|
|
# profiles"). Automatic signing profiles only the app and leaves the resource bundle (and
|
|
# the macOS-host macro plugins) alone. -allowProvisioningUpdates lets Xcode sync the App ID
|
|
# capabilities and regenerate the managed *development* profiles for both io.unom.punktfunk
|
|
# AND the embedded io.unom.punktfunk.widgets — needed because adding the App Groups
|
|
# capability (group.io.unom.punktfunk, shared with the Widget/Live-Activity extension)
|
|
# invalidated the cached managed dev profile, which had no widgets profile at all. This is
|
|
# DEVELOPMENT signing against the Apple Development cert already in the keychain — no cert
|
|
# creation, so the App-Manager ASC key is sufficient (it only manages App IDs/dev profiles).
|
|
# DISTRIBUTION signing is the export step below (manual, via the plist) and is unaffected.
|
|
# A running Xcode.app prunes unrecognized profiles — quit it so the manually-installed
|
|
# App Store distribution profile survives for export.
|
|
osascript -e 'tell application "Xcode" to quit' >/dev/null 2>&1 || true
|
|
pkill -x Xcode 2>/dev/null || true
|
|
PROFILE="Punktfunk iOS App Store Distribution"
|
|
# The embedded PunktfunkWidgetsExtension (bundle io.unom.punktfunk.widgets) is a second
|
|
# distribution artifact in the .ipa, so manual signing must map its App ID to its own
|
|
# App Store profile too — else exportArchive fails ("no profile for io.unom.punktfunk.widgets").
|
|
WIDGET_PROFILE="Punktfunk iOS Widgets App Store Distribution"
|
|
DEVELOPER_DIR="$XCODE_DEV_DIR" xcodebuild archive \
|
|
-project "$PROJECT" -scheme Punktfunk-iOS \
|
|
-destination 'generic/platform=iOS' \
|
|
-archivePath "$RUNNER_TEMP/Punktfunk-ios.xcarchive" \
|
|
-derivedDataPath "$DERIVED_DATA" \
|
|
-skipMacroValidation -skipPackagePluginValidation \
|
|
-allowProvisioningUpdates \
|
|
-authenticationKeyPath "$RUNNER_TEMP/asc.p8" \
|
|
-authenticationKeyID "${{ secrets.ASC_API_KEY_ID }}" \
|
|
-authenticationKeyIssuerID "${{ secrets.ASC_API_ISSUER_ID }}" \
|
|
MARKETING_VERSION="$VERSION" CURRENT_PROJECT_VERSION="$BUILD_NUM" \
|
|
CODE_SIGN_STYLE=Automatic \
|
|
DEVELOPMENT_TEAM="$TEAM_ID"
|
|
cat > "$RUNNER_TEMP/export-appstore.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>upload</string>
|
|
<key>teamID</key><string>$TEAM_ID</string>
|
|
<key>signingStyle</key><string>manual</string>
|
|
<key>signingCertificate</key><string>Apple Distribution</string>
|
|
<key>provisioningProfiles</key>
|
|
<dict>
|
|
<key>io.unom.punktfunk</key><string>$PROFILE</string>
|
|
<key>io.unom.punktfunk.widgets</key><string>$WIDGET_PROFILE</string>
|
|
</dict>
|
|
</dict>
|
|
</plist>
|
|
EOF
|
|
DEVELOPER_DIR="$XCODE_DEV_DIR" xcodebuild -exportArchive \
|
|
-archivePath "$RUNNER_TEMP/Punktfunk-ios.xcarchive" \
|
|
-exportOptionsPlist "$RUNNER_TEMP/export-appstore.plist" \
|
|
-exportPath "$RUNNER_TEMP/export-appstore" \
|
|
-authenticationKeyPath "$RUNNER_TEMP/asc.p8" \
|
|
-authenticationKeyID "${{ secrets.ASC_API_KEY_ID }}" \
|
|
-authenticationKeyIssuerID "${{ secrets.ASC_API_ISSUER_ID }}"
|
|
|
|
- name: tvOS — archive + upload to TestFlight
|
|
# Canary + stable, the same track as iOS/macOS — the tvOS xcframework slice is now built
|
|
# on every apple push (above), so this matches the iOS step's gate exactly.
|
|
if: gitea.event_name != 'workflow_dispatch' || inputs.testflight == 'true'
|
|
# Needs tvOS added to the App Store Connect app record + the tvOS platform installed
|
|
# on the runner (xcodebuild -downloadPlatform tvOS).
|
|
continue-on-error: true
|
|
run: |
|
|
# Archive with AUTOMATIC signing (development) — see the macOS App Store step. The SwiftPM
|
|
# resource bundle (PunktfunkKit_PunktfunkKit) builds for appletvos and rejected the
|
|
# sdk-scoped profile this step used to set; Automatic signing profiles only the app and
|
|
# leaves the resource bundle + the macOS-host macro plugins (OnceMacro/SwizzlingMacro/
|
|
# AssociationMacro) alone. -allowProvisioningUpdates lets Xcode sync the App ID capabilities
|
|
# and regenerate the managed *development* profile — the tvOS app carries the App Groups key
|
|
# (group.io.unom.punktfunk) too, which invalidated the cached one. DEVELOPMENT signing against
|
|
# the Apple Development cert already in the keychain, so the App-Manager ASC key suffices.
|
|
# DISTRIBUTION signing is the export step below (manual, plist).
|
|
osascript -e 'tell application "Xcode" to quit' >/dev/null 2>&1 || true
|
|
pkill -x Xcode 2>/dev/null || true
|
|
PROFILE="Punktfunk tvOS App Store Distribution"
|
|
DEVELOPER_DIR="$XCODE_DEV_DIR" xcodebuild archive \
|
|
-project "$PROJECT" -scheme Punktfunk-tvOS \
|
|
-destination 'generic/platform=tvOS' \
|
|
-archivePath "$RUNNER_TEMP/Punktfunk-tvos.xcarchive" \
|
|
-derivedDataPath "$DERIVED_DATA" \
|
|
-skipMacroValidation -skipPackagePluginValidation \
|
|
-allowProvisioningUpdates \
|
|
-authenticationKeyPath "$RUNNER_TEMP/asc.p8" \
|
|
-authenticationKeyID "${{ secrets.ASC_API_KEY_ID }}" \
|
|
-authenticationKeyIssuerID "${{ secrets.ASC_API_ISSUER_ID }}" \
|
|
MARKETING_VERSION="$VERSION" CURRENT_PROJECT_VERSION="$BUILD_NUM" \
|
|
CODE_SIGN_STYLE=Automatic \
|
|
DEVELOPMENT_TEAM="$TEAM_ID"
|
|
cat > "$RUNNER_TEMP/export-tvos.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>upload</string>
|
|
<key>teamID</key><string>$TEAM_ID</string>
|
|
<key>signingStyle</key><string>manual</string>
|
|
<key>signingCertificate</key><string>Apple Distribution</string>
|
|
<key>provisioningProfiles</key>
|
|
<dict><key>io.unom.punktfunk</key><string>$PROFILE</string></dict>
|
|
</dict>
|
|
</plist>
|
|
EOF
|
|
DEVELOPER_DIR="$XCODE_DEV_DIR" xcodebuild -exportArchive \
|
|
-archivePath "$RUNNER_TEMP/Punktfunk-tvos.xcarchive" \
|
|
-exportOptionsPlist "$RUNNER_TEMP/export-tvos.plist" \
|
|
-exportPath "$RUNNER_TEMP/export-tvos" \
|
|
-authenticationKeyPath "$RUNNER_TEMP/asc.p8" \
|
|
-authenticationKeyID "${{ secrets.ASC_API_KEY_ID }}" \
|
|
-authenticationKeyIssuerID "${{ secrets.ASC_API_ISSUER_ID }}"
|