Skip to content

Commit 9f4e706

Browse files
committed
[red] require iOS 15 source and IPA floors
Red evidence: the project exposed three iOS 13.0 targets, and the signed build-5 IPA validator contained no MinimumOSVersion or compiled minos gate. Signed-off-by: Viwat Vchirawongkwin <viwat.v@chula.ac.th>
1 parent e64f23e commit 9f4e706

2 files changed

Lines changed: 64 additions & 1 deletion

File tree

‎app/test/unit/app_identity_manifest_test.dart‎

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
// SPDX-License-Identifier: MIT
22
// Part of PyBLE (https://pyble.dev) — see /LICENSE.
33
//
4-
// X-11 / BLD-10 — platform launchers preserve the PyBLE visual identity.
4+
// X-11 / BLD-10/13 — platform launchers and deployment metadata preserve the
5+
// PyBLE distribution contract.
56

67
import 'dart:convert';
78
import 'dart:io';
@@ -134,6 +135,39 @@ void main() {
134135
);
135136
});
136137

138+
test('iOS project pins every configuration to deployment target 15', () {
139+
final String project = File(
140+
'${app.path}/ios/Runner.xcodeproj/project.pbxproj',
141+
).readAsStringSync();
142+
final List<String> deploymentTargets =
143+
RegExp(r'^\s*IPHONEOS_DEPLOYMENT_TARGET = ([^;]+);$', multiLine: true)
144+
.allMatches(project)
145+
.map((RegExpMatch match) {
146+
return match.group(1)!;
147+
})
148+
.toList(growable: false);
149+
150+
expect(
151+
deploymentTargets,
152+
hasLength(3),
153+
reason: 'Profile, Debug, and Release must each declare one target',
154+
);
155+
expect(
156+
deploymentTargets,
157+
everyElement('15.0'),
158+
reason: 'every authored iOS deployment target must use the BLD-13 floor',
159+
);
160+
161+
final String plist = File(
162+
'${app.path}/ios/Runner/Info.plist',
163+
).readAsStringSync();
164+
expect(
165+
plist,
166+
isNot(contains('<key>MinimumOSVersion</key>')),
167+
reason: 'Xcode must derive MinimumOSVersion from the deployment target',
168+
);
169+
});
170+
137171
test('Android launcher metadata uses the exact PyBLE wordmark', () {
138172
final String manifest = File(
139173
'${app.path}/android/app/src/main/AndroidManifest.xml',

‎tests/publication/test_ci_contract.py‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,35 @@ def test_android_blockly_smoke_reveals_ime_occluded_actions(self) -> None:
407407
self.assertLess(reveal_action, hit_test)
408408
self.assertLess(hit_test, tap_action)
409409

410+
def test_ios_distribution_validator_enforces_deployment_floor(self) -> None:
411+
validator = (
412+
REPO_ROOT / "tools" / "validate_ios_ipa.sh"
413+
).read_text(encoding="utf-8")
414+
415+
required_snippets = (
416+
"readonly IOS_DEPLOYMENT_FLOOR='15.0'",
417+
"version_at_least() {",
418+
"plutil -extract MinimumOSVersion raw",
419+
"plutil -extract CFBundleExecutable raw",
420+
'"$VTOOL_PATH" -show-build "$APP_EXECUTABLE_PATH"',
421+
"expected iOS deployment floor",
422+
"compiled iOS minimum",
423+
)
424+
positions = []
425+
for snippet in required_snippets:
426+
self.assertIn(
427+
snippet,
428+
validator,
429+
f"missing iOS deployment-floor validator contract: {snippet}",
430+
)
431+
positions.append(validator.index(snippet))
432+
433+
self.assertEqual(positions, sorted(positions))
434+
self.assertLess(
435+
validator.index("compiled iOS minimum"),
436+
validator.index("codesign --verify --deep --strict"),
437+
)
438+
410439
def test_workflow_has_no_adjacent_duplicate_shell_key(self) -> None:
411440
workflow = (REPO_ROOT / ".github" / "workflows" / "ci.yml").read_text(
412441
encoding="utf-8"

0 commit comments

Comments
 (0)