gowalk-cicd 1.0.23 → 1.0.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/action/.daemux-version
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.
|
|
1
|
+
1.0.24
|
|
@@ -66,6 +66,28 @@ CAPABILITY_BY_ENTITLEMENT = {
|
|
|
66
66
|
# so we say what is missing and stop.
|
|
67
67
|
APP_ATTEST_ENTITLEMENT = "com.apple.developer.devicecheck.appattest-environment"
|
|
68
68
|
|
|
69
|
+
# CarPlay capabilities are Apple-GRANTED per App ID (the CarPlay entitlement
|
|
70
|
+
# request at https://developer.apple.com/contact/carplay/); the ASC API's
|
|
71
|
+
# capabilityType enum has no CarPlay member, so a POST would 409 exactly like
|
|
72
|
+
# App Attest. They live in MANUAL_ENTITLEMENTS (message, no POST) and in
|
|
73
|
+
# PROFILE_ENTITLEMENT_KEYS (cache invalidation) — see the notes on each.
|
|
74
|
+
CARPLAY_ENTITLEMENTS = {
|
|
75
|
+
"com.apple.developer.carplay-charging",
|
|
76
|
+
"com.apple.developer.carplay-fueling",
|
|
77
|
+
"com.apple.developer.carplay-parking",
|
|
78
|
+
"com.apple.developer.carplay-quick-ordering",
|
|
79
|
+
"com.apple.developer.carplay-maps",
|
|
80
|
+
"com.apple.developer.carplay-audio",
|
|
81
|
+
"com.apple.developer.carplay-communication",
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
_CARPLAY_MESSAGE = (
|
|
85
|
+
"a CarPlay entitlement, which Apple grants per App ID through the CarPlay "
|
|
86
|
+
"entitlement request form (developer.apple.com/contact/carplay/) — the "
|
|
87
|
+
"App Store Connect API cannot enable it. Request it from Apple, wait for "
|
|
88
|
+
"the grant, then re-run"
|
|
89
|
+
)
|
|
90
|
+
|
|
69
91
|
MANUAL_ENTITLEMENTS = {
|
|
70
92
|
# App Attest is a real App ID capability in the developer portal, but it is
|
|
71
93
|
# absent from the ASC API's capabilityType enum entirely — POSTing it
|
|
@@ -82,6 +104,7 @@ MANUAL_ENTITLEMENTS = {
|
|
|
82
104
|
"com.apple.developer.in-app-payments": "Apple Pay",
|
|
83
105
|
"com.apple.developer.pass-type-identifiers": "Wallet",
|
|
84
106
|
"com.apple.developer.default-data-protection": "Data Protection",
|
|
107
|
+
**{key: _CARPLAY_MESSAGE for key in CARPLAY_ENTITLEMENTS},
|
|
85
108
|
}
|
|
86
109
|
|
|
87
110
|
|
|
@@ -95,7 +118,16 @@ MANUAL_ENTITLEMENTS = {
|
|
|
95
118
|
# Apple Pay, Wallet, Data Protection): those need App ID configuration this
|
|
96
119
|
# action does not perform, so treating them as missing would regenerate the
|
|
97
120
|
# profile on every single run without ever fixing anything.
|
|
98
|
-
|
|
121
|
+
#
|
|
122
|
+
# CarPlay IS included, deliberately: a profile cached before Apple granted
|
|
123
|
+
# the capability would otherwise be reinstalled until it expires, failing
|
|
124
|
+
# every archive with no hint. Before the grant this forces a regenerate on
|
|
125
|
+
# every run — which still fails, but loudly, with Apple's own message (and
|
|
126
|
+
# the fail-fast in profile_manager); after the grant the fresh profile
|
|
127
|
+
# carries the key and the check goes quiet.
|
|
128
|
+
PROFILE_ENTITLEMENT_KEYS = (
|
|
129
|
+
set(CAPABILITY_BY_ENTITLEMENT) | {APP_ATTEST_ENTITLEMENT} | CARPLAY_ENTITLEMENTS
|
|
130
|
+
)
|
|
99
131
|
|
|
100
132
|
|
|
101
133
|
def missing_profile_entitlements(
|
|
@@ -109,6 +141,22 @@ def missing_profile_entitlements(
|
|
|
109
141
|
}
|
|
110
142
|
|
|
111
143
|
|
|
144
|
+
def missing_carplay_entitlements(
|
|
145
|
+
profile_entitlements: dict, required_keys: set[str]
|
|
146
|
+
) -> set[str]:
|
|
147
|
+
"""CarPlay keys the app needs that an issued profile does not carry.
|
|
148
|
+
|
|
149
|
+
Used as a pre-archive assertion: a freshly minted profile without the
|
|
150
|
+
granted CarPlay capability means Apple has not (yet) granted it, and the
|
|
151
|
+
archive would die later with an opaque xcodebuild error.
|
|
152
|
+
"""
|
|
153
|
+
return {
|
|
154
|
+
key
|
|
155
|
+
for key in required_keys & CARPLAY_ENTITLEMENTS
|
|
156
|
+
if key not in profile_entitlements
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
|
|
112
160
|
def read_entitlement_keys(path: Path) -> set[str]:
|
|
113
161
|
"""Top-level keys of an entitlements plist; empty when unreadable."""
|
|
114
162
|
try:
|
|
@@ -262,6 +262,8 @@ def _provision_bundle(
|
|
|
262
262
|
bundle_pk = bundle_pk or ensure_bundle_id(token, bid)
|
|
263
263
|
delete_profile_by_name(token, name)
|
|
264
264
|
profile_der = create_profile(token, name, bundle_pk, cert_id)
|
|
265
|
+
if entitlement_keys:
|
|
266
|
+
_assert_carplay_entitlements(profile_der, entitlement_keys, bid, name)
|
|
265
267
|
uuid, team_id, expiration = install_profile(profile_der)
|
|
266
268
|
creds_store.write_cached_profile(creds_dir, uuid, profile_der)
|
|
267
269
|
entry = {
|
|
@@ -275,6 +277,38 @@ def _provision_bundle(
|
|
|
275
277
|
return name, uuid, team_id, entry
|
|
276
278
|
|
|
277
279
|
|
|
280
|
+
def _assert_carplay_entitlements(
|
|
281
|
+
profile_der: bytes, entitlement_keys: set[str], bid: str, name: str
|
|
282
|
+
) -> None:
|
|
283
|
+
"""Fail fast when a fresh profile lacks a required CarPlay capability.
|
|
284
|
+
|
|
285
|
+
CarPlay capabilities are Apple-granted per App ID and cannot be enabled
|
|
286
|
+
through the ASC API. Without this check the run dies much later inside
|
|
287
|
+
``xcodebuild archive`` with an opaque "doesn't include the ... capability"
|
|
288
|
+
— here it dies immediately, saying exactly what to do.
|
|
289
|
+
"""
|
|
290
|
+
try:
|
|
291
|
+
plist = decode_profile_plist(profile_der, _PROFILE_DIRS[0])
|
|
292
|
+
except (OSError, subprocess.CalledProcessError, ValueError) as exc:
|
|
293
|
+
print(f"::warning::could not inspect fresh profile for {bid}: {exc!r}")
|
|
294
|
+
return
|
|
295
|
+
missing = capabilities.missing_carplay_entitlements(
|
|
296
|
+
plist.get("Entitlements") or {}, entitlement_keys
|
|
297
|
+
)
|
|
298
|
+
if not missing:
|
|
299
|
+
return
|
|
300
|
+
keys = ", ".join(sorted(missing))
|
|
301
|
+
raise SystemExit(
|
|
302
|
+
f"::error::Profile {name} for {bid} was issued WITHOUT {keys}. "
|
|
303
|
+
f"CarPlay capabilities are granted by Apple per App ID via the "
|
|
304
|
+
f"CarPlay entitlement request (developer.apple.com/contact/carplay/) "
|
|
305
|
+
f"and cannot be enabled through the App Store Connect API. Until the "
|
|
306
|
+
f"grant lands, keep the CarPlay key out of the entitlements file "
|
|
307
|
+
f"used by this configuration; once Apple confirms it on the App ID, "
|
|
308
|
+
f"re-run — the profile is regenerated automatically."
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
|
|
278
312
|
def _gc_orphan_profiles(
|
|
279
313
|
creds_dir: Path, old_manifest: dict, new_entries: list[dict]
|
|
280
314
|
) -> None:
|
|
@@ -199,5 +199,63 @@ class ReconcileTest(unittest.TestCase):
|
|
|
199
199
|
self.assertTrue(any("App Groups" in w for w in warnings), warnings)
|
|
200
200
|
|
|
201
201
|
|
|
202
|
+
class CarPlayEntitlementsTest(unittest.TestCase):
|
|
203
|
+
CHARGING = "com.apple.developer.carplay-charging"
|
|
204
|
+
|
|
205
|
+
def test_every_carplay_key_is_manual_with_a_request_form_message(self) -> None:
|
|
206
|
+
for key in capabilities.CARPLAY_ENTITLEMENTS:
|
|
207
|
+
self.assertIn(key, capabilities.MANUAL_ENTITLEMENTS, key)
|
|
208
|
+
self.assertIn("carplay", capabilities.MANUAL_ENTITLEMENTS[key].lower())
|
|
209
|
+
|
|
210
|
+
def test_carplay_never_maps_to_an_asc_capability_post(self) -> None:
|
|
211
|
+
# The ASC capabilityType enum has no CarPlay member; a POST would 409.
|
|
212
|
+
self.assertEqual(
|
|
213
|
+
capabilities.required_capabilities(capabilities.CARPLAY_ENTITLEMENTS),
|
|
214
|
+
set(),
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
def test_cached_profile_without_carplay_is_detected_as_stale(self) -> None:
|
|
218
|
+
# The self-correcting-cache check: a profile minted pre-grant must be
|
|
219
|
+
# regenerated once the entitlements file declares CarPlay.
|
|
220
|
+
self.assertEqual(
|
|
221
|
+
capabilities.missing_profile_entitlements(
|
|
222
|
+
{"application-identifier": "T.com.gowalk.ev"}, {self.CHARGING}
|
|
223
|
+
),
|
|
224
|
+
{self.CHARGING},
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
def test_profile_carrying_carplay_passes_both_checks(self) -> None:
|
|
228
|
+
entitlements = {self.CHARGING: True}
|
|
229
|
+
self.assertEqual(
|
|
230
|
+
capabilities.missing_profile_entitlements(entitlements, {self.CHARGING}),
|
|
231
|
+
set(),
|
|
232
|
+
)
|
|
233
|
+
self.assertEqual(
|
|
234
|
+
capabilities.missing_carplay_entitlements(entitlements, {self.CHARGING}),
|
|
235
|
+
set(),
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
def test_missing_carplay_ignores_non_carplay_keys(self) -> None:
|
|
239
|
+
self.assertEqual(
|
|
240
|
+
capabilities.missing_carplay_entitlements(
|
|
241
|
+
{}, {"aps-environment", "keychain-access-groups"}
|
|
242
|
+
),
|
|
243
|
+
set(),
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
def test_reconcile_warns_but_does_not_post_for_carplay(self) -> None:
|
|
247
|
+
with mock.patch.object(capabilities, "get_json", return_value={"data": []}), \
|
|
248
|
+
mock.patch.object(capabilities, "request") as posted, \
|
|
249
|
+
mock.patch("builtins.print") as printed:
|
|
250
|
+
regenerate = capabilities.reconcile(
|
|
251
|
+
"tok", "PK", "com.gowalk.ev", {self.CHARGING}
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
posted.assert_not_called()
|
|
255
|
+
self.assertFalse(regenerate)
|
|
256
|
+
warnings = [c.args[0] for c in printed.call_args_list if c.args]
|
|
257
|
+
self.assertTrue(any("carplay" in w.lower() for w in warnings), warnings)
|
|
258
|
+
|
|
259
|
+
|
|
202
260
|
if __name__ == "__main__":
|
|
203
261
|
unittest.main()
|
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.
|
|
1
|
+
1.0.24
|
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0.
|
|
1
|
+
1.0.24
|