Commit 1a113f7129
1a113f7129bbfd9af56a4157275d50645e29fdda
parent: bb6e57f79e
Verified · cmc
cmc <hello@cleberg.net> · 2026-08-28T20:55:09Z
release: privacy manifest, export compliance, and the edit fixture
The archive carried no PrivacyInfo.xcprivacy while SessionStore uses
UserDefaults, a required-reason API. App Store Connect refuses such
uploads (ITMS-91053). The manifest declares that one access with reason
CA92.1 and states what is true otherwise: no tracking, no collection,
no third-party SDK.
ITSAppUsesNonExemptEncryption = NO so uploads stop asking; the app uses
HTTPS and nothing else.
testBlameAndEditFlows wanted cmc/ui-smoke-edit, which did not exist —
the same missing-fixture failure as #8, in a test that was not failing
when I audited that issue. The repo is seeded with notes.txt, and setUp
skips with the reason if it goes missing, since seeding a first commit
is a push no test can make.
gitbay.xcodeproj/project.pbxproj
+2
| @@ -415,6 +415,7 @@ |
| 415 | 415 | GENERATE_INFOPLIST_FILE = YES; |
| 416 | 416 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; |
| 417 | 417 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; |
| 418 | INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; |
| 418 | 419 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; |
| 419 | 420 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; |
| 420 | 421 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; |
| @@ -447,6 +448,7 @@ |
| 447 | 448 | GENERATE_INFOPLIST_FILE = YES; |
| 448 | 449 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; |
| 449 | 450 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; |
| 451 | INFOPLIST_KEY_ITSAppUsesNonExemptEncryption = NO; |
| 450 | 452 | INFOPLIST_KEY_UILaunchScreen_Generation = YES; |
| 451 | 453 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; |
| 452 | 454 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; |
gitbay/PrivacyInfo.xcprivacy
added
+33
| @@ -0,0 +1,33 @@ |
| 1 | <?xml version="1.0" encoding="UTF-8"?> |
| 2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
| 3 | <plist version="1.0"> |
| 4 | <dict> |
| 5 | <!-- No tracking: the app talks only to the gitbay instance the user |
| 6 | names at sign-in, and to nothing else. --> |
| 7 | <key>NSPrivacyTracking</key> |
| 8 | <false/> |
| 9 | <key>NSPrivacyTrackingDomains</key> |
| 10 | <array/> |
| 11 | |
| 12 | <!-- Nothing is collected. The API token is held in the Keychain on |
| 13 | the device and sent only to that instance; no analytics, no |
| 14 | crash reporting, no third-party SDK. --> |
| 15 | <key>NSPrivacyCollectedDataTypes</key> |
| 16 | <array/> |
| 17 | |
| 18 | <key>NSPrivacyAccessedAPITypes</key> |
| 19 | <array> |
| 20 | <dict> |
| 21 | <!-- SessionStore keeps which account is active across |
| 22 | relaunches. App's own data, read and written by this |
| 23 | app only. --> |
| 24 | <key>NSPrivacyAccessedAPIType</key> |
| 25 | <string>NSPrivacyAccessedAPICategoryUserDefaults</string> |
| 26 | <key>NSPrivacyAccessedAPITypeReasons</key> |
| 27 | <array> |
| 28 | <string>CA92.1</string> |
| 29 | </array> |
| 30 | </dict> |
| 31 | </array> |
| 32 | </dict> |
| 33 | </plist> |
gitbayUITests/LiveSmokeUITests.swift
+18
| @@ -46,6 +46,9 @@ final class LiveSmokeUITests: XCTestCase { |
| 46 | 46 | ensureFixtureIssue() |
| 47 | 47 | try requireFixtureBranch() |
| 48 | 48 | } |
| 49 | if name.contains("testBlameAndEditFlows") { |
| 50 | try requireFixtureRepo() |
| 51 | } |
| 49 | 52 | } |
| 50 | 53 | |
| 51 | 54 | override func tearDownWithError() throws { |
| @@ -275,6 +278,21 @@ final class LiveSmokeUITests: XCTestCase { |
| 275 | 278 | keys.tap() |
| 276 | 279 | } |
| 277 | 280 | |
| 281 | /// The edit flow saves a change to a real file. Seeding a |
| 282 | /// repository's first commit is a push, which no test can make, so |
| 283 | /// this only checks — and says why, rather than failing later at |
| 284 | /// "cmc/ui-smoke-edit not in the repo list". |
| 285 | private func requireFixtureRepo() throws { |
| 286 | guard let data = readCommand(["repo", "show", "cmc/ui-smoke-edit"]), |
| 287 | let envelope = try? JSONSerialization.jsonObject(with: data) as? [String: Any] |
| 288 | else { return } // no token, or the instance is unreachable |
| 289 | try XCTSkipUnless(envelope["data"] != nil, """ |
| 290 | cmc/ui-smoke-edit is missing. The edit flow saves a change to \ |
| 291 | notes.txt in it; recreate the repo with that file (see its \ |
| 292 | README) and run again. |
| 293 | """) |
| 294 | } |
| 295 | |
| 278 | 296 | /// Scroll a list until an element is in the hierarchy. Offscreen |
| 279 | 297 | /// rows do not exist to XCUITest, so waitForExistence alone fails on |
| 280 | 298 | /// anything below the fold. |