feat(apple): App Store screenshot harness + CI zip artifact
apple / swift (push) Successful in 54s
release / apple (push) Successful in 8m1s
apple / screenshots (push) Failing after 6m42s
ci / rust (push) Successful in 1m25s
ci / web (push) Successful in 42s
android / android (push) Successful in 3m27s
ci / docs-site (push) Successful in 53s
ci / bench (push) Failing after 3m1s
deb / build-publish (push) Successful in 2m33s
decky / build-publish (push) Successful in 12s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 4s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 2m13s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 8m26s
docker / deploy-docs (push) Successful in 6s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 8m7s
apple / swift (push) Successful in 54s
release / apple (push) Successful in 8m1s
apple / screenshots (push) Failing after 6m42s
ci / rust (push) Successful in 1m25s
ci / web (push) Successful in 42s
android / android (push) Successful in 3m27s
ci / docs-site (push) Successful in 53s
ci / bench (push) Failing after 3m1s
deb / build-publish (push) Successful in 2m33s
decky / build-publish (push) Successful in 12s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 4s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 2m13s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 8m26s
docker / deploy-docs (push) Successful in 6s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 8m7s
A DEBUG-only "shot mode" renders one mock-populated screen full-bleed (PUNKTFUNK_SHOT_SCENE=<name> -> ScreenshotHostView instead of ContentView), so the OS can screenshot the REAL, fully-rendered UI. tools/screenshots.sh drives it: screencapture for the mac window, `simctl io booted screenshot` for the iOS/iPad/tvOS Simulators, at exactly the App Store Connect sizes. ImageRenderer was tried first and rejected: it can't rasterize this app's chrome (NavigationStack, Form/TabView, Liquid-Glass/NSVisualEffect all render black or the "can't render" placeholder). Capturing the live window/Simulator avoids that. Only the stream hero is synthetic (StreamView needs a live connection) - a synthwave frame + the real glass HUD, overridable via PUNKTFUNK_SHOT_HERO. CI: a new `screenshots` job in apple.yml builds the iOS (+ tvOS best-effort) xcframework slices, runs the harness per platform best-effort, and attaches the result as a single zip artifact (punktfunk-appstore-screenshots). It is isolated from the build/test job and skipped on PRs, so a capture gap (missing Simulator runtime, or no Screen Recording grant for the mac window capture) never reds the core signal. Generated PNGs (clients/apple/screenshots/) are gitignored. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
// App Store screenshot harness — device catalog.
|
||||
//
|
||||
// The harness captures the REAL running UI (not an offscreen ImageRenderer snapshot, which can't
|
||||
// rasterize NavigationStack / Form / Liquid-Glass — they come out black). The app is launched in
|
||||
// "shot mode" (PUNKTFUNK_SHOT_SCENE=<name>, see ScreenshotHost) showing one mock-populated scene
|
||||
// full-bleed, and the OS screenshots it: `xcrun simctl io booted screenshot` on the iOS/tvOS
|
||||
// simulators (native pixels = the exact App Store size), `screencapture` for the mac window.
|
||||
// tools/screenshots.sh drives it. DEBUG-only — none of this ships in Release.
|
||||
//
|
||||
// This catalog records the target App Store sizes; on Apple platforms only the mac size is read
|
||||
// at runtime (to size the capture window) — the simulator IS the device, so iOS/tvOS pixels are
|
||||
// whatever the booted device is.
|
||||
|
||||
#if DEBUG
|
||||
import CoreGraphics
|
||||
|
||||
enum ShotOrientation { case natural, portrait, landscape }
|
||||
|
||||
/// A target App Store canvas: a natural-orientation pixel size + backing scale.
|
||||
struct ShotDevice {
|
||||
let id: String
|
||||
let naturalWidth: Int
|
||||
let naturalHeight: Int
|
||||
let scale: CGFloat
|
||||
|
||||
func pixels(_ o: ShotOrientation) -> (w: Int, h: Int) {
|
||||
let long = max(naturalWidth, naturalHeight)
|
||||
let short = min(naturalWidth, naturalHeight)
|
||||
switch o {
|
||||
case .natural: return (naturalWidth, naturalHeight)
|
||||
case .portrait: return (short, long)
|
||||
case .landscape: return (long, short)
|
||||
}
|
||||
}
|
||||
|
||||
/// Logical point size (pixels / scale) — used to size the mac capture window so that a
|
||||
/// `screencapture` on a 2× display yields exactly `pixels(_:)`.
|
||||
func points(_ o: ShotOrientation) -> CGSize {
|
||||
let (w, h) = pixels(o)
|
||||
return CGSize(width: CGFloat(w) / scale, height: CGFloat(h) / scale)
|
||||
}
|
||||
|
||||
/// Mac: 2880×1800 (16:10 Retina) — an accepted size; on a 1× display the window capture is
|
||||
/// 1440×900, also accepted.
|
||||
static let mac = ShotDevice(id: "mac", naturalWidth: 2880, naturalHeight: 1800, scale: 2)
|
||||
|
||||
/// iPhone 6.9" (required) — for reference / the driver script's simulator choice.
|
||||
static let iphone69 = ShotDevice(id: "iphone-6.9", naturalWidth: 1320, naturalHeight: 2868,
|
||||
scale: 3)
|
||||
/// iPad 13" (required).
|
||||
static let ipad13 = ShotDevice(id: "ipad-13", naturalWidth: 2064, naturalHeight: 2752,
|
||||
scale: 2)
|
||||
/// Apple TV (always landscape).
|
||||
static let appleTV = ShotDevice(id: "appletv", naturalWidth: 1920, naturalHeight: 1080,
|
||||
scale: 1)
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user