feat(workout): surface store submission blockers

Record Google Play and App Store Connect submission targets as external mobile blockers, and make verification require those blockers to be acknowledged in the release kit.

req: examples/006
This commit is contained in:
slhx agent
2026-06-12 12:57:13 +02:00
parent 815392b232
commit f13e21868c
4 changed files with 22 additions and 8 deletions
+1 -1
View File
@@ -810,7 +810,7 @@ what a valid business email is.
005 Canonical examples must not contain user-authored browser JavaScript. They may load the shared hemx runtime (`/hemx.js`) and may use declarative `data-hemx-*` attributes; inline `<script>`, `on*=` event handlers, and `javascript:` URLs are forbidden outside opaque leaf-widget examples. 005 Canonical examples must not contain user-authored browser JavaScript. They may load the shared hemx runtime (`/hemx.js`) and may use declarative `data-hemx-*` attributes; inline `<script>`, `on*=` event handlers, and `javascript:` URLs are forbidden outside opaque leaf-widget examples.
### req: examples/006 ### req: examples/006
006 The Workout exemplar must have one boring command surface for local development, product tests, production server build, Android/iOS mobile release metadata, and mobile verification. The mobile commands must make app identity, version, production origin, runtime asset policy, cache/offline state policy, environment/secrets boundary, rollback expectation, and external store-signing blockers explicit without adding a broad `hemx-mobile` framework. 006 The Workout exemplar must have one boring command surface for local development, product tests, production server build, Android/iOS mobile release metadata, and mobile verification. The mobile commands must make app identity, version, production origin, runtime asset policy, cache/offline state policy, environment/secrets boundary, rollback expectation, and external store-signing/submission blockers explicit without adding a broad `hemx-mobile` framework.
--- ---
+5 -4
View File
@@ -75,10 +75,11 @@ The command writes release-ready metadata, not store-signed binaries. That is th
honest boundary: producing `.aab`/`.apk` and `.ipa` files requires vendor SDKs, honest boundary: producing `.aab`/`.apk` and `.ipa` files requires vendor SDKs,
signing credentials, and store accounts on the release machine. signing credentials, and store accounts on the release machine.
Android blockers are reported when the Android SDK/JDK/signing key are not Android blockers are reported when the Android SDK/JDK/signing key or Play
visible. iOS blockers are reported when Xcode or the Apple signing team is not Console submission target are not visible. iOS blockers are reported when Xcode,
visible. These blockers are copied into `BLOCKERS.md` so the release kit can be the Apple signing team, or the App Store Connect submission team are not visible.
reviewed without guessing what is still external. req: examples/006 These blockers are copied into `BLOCKERS.md` so the release kit can be reviewed
without guessing what is still external. req: examples/006
## What this does not add ## What this does not add
+3 -2
View File
@@ -87,8 +87,9 @@ state, and host capability boundaries fail closed with the mobile artifacts. The
kit records app identity, version, production origin, runtime asset policy, kit records app identity, version, production origin, runtime asset policy,
cache/offline state policy, secrets/signing boundaries, rollback expectations, cache/offline state policy, secrets/signing boundaries, rollback expectations,
Android TWA metadata, iOS WebView metadata, and any external blocker such as Android TWA metadata, iOS WebView metadata, and any external blocker such as
missing Android SDK, Xcode, or store signing credentials. See missing Android SDK, Xcode, store signing credentials, or Play/App Store
`docs/recipes/mobile-release.md`. req: examples/006 submission account targets. See `docs/recipes/mobile-release.md`. req:
examples/006
## Boundaries proven ## Boundaries proven
+13 -1
View File
@@ -333,6 +333,9 @@ fn mobile_external_blockers(config: &WorkoutMobileConfig) -> Vec<String> {
if env::var_os("HEMX_WORKOUT_ANDROID_KEYSTORE").is_none() { if env::var_os("HEMX_WORKOUT_ANDROID_KEYSTORE").is_none() {
blockers.push("Android signing key not configured: set HEMX_WORKOUT_ANDROID_KEYSTORE for store-ready signing".into()); blockers.push("Android signing key not configured: set HEMX_WORKOUT_ANDROID_KEYSTORE for store-ready signing".into());
} }
if env::var_os("HEMX_WORKOUT_GOOGLE_PLAY_TRACK").is_none() {
blockers.push("Google Play submission target not configured: set HEMX_WORKOUT_GOOGLE_PLAY_TRACK after choosing the Play Console track outside this repo".into());
}
if !has_command("xcodebuild") { if !has_command("xcodebuild") {
blockers.push( blockers.push(
"Xcode command line tools not found: iOS archive/export requires xcodebuild on macOS" "Xcode command line tools not found: iOS archive/export requires xcodebuild on macOS"
@@ -342,6 +345,9 @@ fn mobile_external_blockers(config: &WorkoutMobileConfig) -> Vec<String> {
if env::var_os("HEMX_WORKOUT_IOS_TEAM_ID").is_none() { if env::var_os("HEMX_WORKOUT_IOS_TEAM_ID").is_none() {
blockers.push("iOS signing team not configured: set HEMX_WORKOUT_IOS_TEAM_ID for App Store/TestFlight export".into()); blockers.push("iOS signing team not configured: set HEMX_WORKOUT_IOS_TEAM_ID for App Store/TestFlight export".into());
} }
if env::var_os("HEMX_WORKOUT_APP_STORE_CONNECT_TEAM").is_none() {
blockers.push("App Store Connect submission team not configured: set HEMX_WORKOUT_APP_STORE_CONNECT_TEAM after choosing the Apple account outside this repo".into());
}
blockers blockers
} }
@@ -573,7 +579,7 @@ fn workout_mobile_blockers_md(blockers: &[String]) -> String {
fn android_release_readme(config: &WorkoutMobileConfig) -> String { fn android_release_readme(config: &WorkoutMobileConfig) -> String {
format!( format!(
"# Workout Android release\n\nUse `twa-release.json` as the Android shell authority for `{}`. Build the hemx server with the same release and serve `{}/` over HTTPS. Android SDK, Java, and signing credentials are external inputs; this repository does not own them.\n", "# Workout Android release\n\nUse `twa-release.json` as the Android shell authority for `{}`. Build the hemx server with the same release and serve `{}/` over HTTPS. Android SDK, Java, signing credentials, Play Console account state, and submission track are external inputs; this repository does not own them.\n",
config.android_package, config.origin config.android_package, config.origin
) )
} }
@@ -1055,6 +1061,12 @@ mod tests {
assert!(failures assert!(failures
.iter() .iter()
.any(|failure| failure.contains("BLOCKERS.md") && failure.contains("Android"))); .any(|failure| failure.contains("BLOCKERS.md") && failure.contains("Android")));
assert!(failures
.iter()
.any(|failure| failure.contains("Google Play")));
assert!(failures
.iter()
.any(|failure| failure.contains("App Store Connect")));
let _ = fs::remove_dir_all(&config.out_dir); let _ = fs::remove_dir_all(&config.out_dir);
} }