gowalk-cicd 1.0.7 → 1.0.8

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.
@@ -1 +1 @@
1
- 1.0.7
1
+ 1.0.8
@@ -118,6 +118,7 @@ def enabled_capabilities(token: str, bundle_pk: str) -> set[str]:
118
118
  capability = (item.get("attributes") or {}).get("capabilityType")
119
119
  if capability:
120
120
  found.add(capability)
121
+ print(f"App ID {bundle_pk} capabilities: {', '.join(sorted(found)) or '(none)'}")
121
122
  return found
122
123
 
123
124
 
@@ -132,7 +133,6 @@ def enable_capability(token: str, bundle_pk: str, capability: str) -> bool:
132
133
  }
133
134
  }
134
135
  try:
135
- # 409 means another writer got there first — the desired end state.
136
136
  response = request(
137
137
  "POST", "/bundleIdCapabilities", token,
138
138
  json_body=body, allow_status={200, 201, 409},
@@ -141,6 +141,14 @@ def enable_capability(token: str, bundle_pk: str, capability: str) -> bool:
141
141
  print(f"::warning::could not enable {capability}: {exc!r}")
142
142
  return False
143
143
  if response.status_code == 409:
144
+ # Could be "already on" — or Apple refusing outright, which reads
145
+ # identically from the status code alone. Surface the body: without it
146
+ # the run just reports a profile missing a capability we claimed to
147
+ # have enabled, and there is nothing in the log to explain why.
148
+ print(
149
+ f"::warning::App Store Connect returned 409 for {capability}: "
150
+ f"{response.text[:600]}"
151
+ )
144
152
  return False
145
153
  print(f"Enabled {capability} on the App ID")
146
154
  return True
@@ -149,9 +157,13 @@ def enable_capability(token: str, bundle_pk: str, capability: str) -> bool:
149
157
  def reconcile(token: str, bundle_pk: str, bundle_id: str, entitlement_keys: set[str]) -> bool:
150
158
  """Enable every simple capability the entitlements imply.
151
159
 
152
- Returns True when something was actually turned onthe caller must then
153
- regenerate the profile, because a cached one predates the change and still
154
- lacks the capability.
160
+ Returns True when the caller must regenerate the profile which is
161
+ whenever the App ID was missing something, NOT merely when a POST
162
+ succeeded. A cached profile was issued while the App ID lacked these, so
163
+ reusing it reproduces the same archive failure either way; regenerating at
164
+ least picks up the App ID's current state, and if the capability is still
165
+ absent the error comes from Apple describing the real problem rather than
166
+ from a stale cache.
155
167
  """
156
168
  warn_about_manual_entitlements(entitlement_keys, bundle_id)
157
169
  wanted = required_capabilities(entitlement_keys)
@@ -164,9 +176,6 @@ def reconcile(token: str, bundle_pk: str, bundle_id: str, entitlement_keys: set[
164
176
  f"{bundle_id}: entitlements require {', '.join(sorted(missing))} "
165
177
  f"but the App ID does not have them; enabling"
166
178
  )
167
- # List, not a generator: `any` short-circuits, and every missing capability
168
- # must be attempted, not just up to the first success.
169
- return any([
179
+ for capability in sorted(missing):
170
180
  enable_capability(token, bundle_pk, capability)
171
- for capability in sorted(missing)
172
- ])
181
+ return True
@@ -109,15 +109,36 @@ class ReconcileTest(unittest.TestCase):
109
109
 
110
110
  self.assertEqual(sorted(posted), ["APP_ATTEST", "PUSH_NOTIFICATIONS"])
111
111
 
112
- def test_existing_capability_race_is_not_a_change(self) -> None:
112
+ def test_regenerates_even_when_the_capability_could_not_be_enabled(self) -> None:
113
+ # A 409 (or any refusal) still means the cached profile was issued
114
+ # while the App ID lacked the capability. Reusing it reproduces the
115
+ # archive failure and hides Apple's real error behind a stale cache.
116
+ response = self._response(409)
117
+ response.text = '{"errors":[{"detail":"nope"}]}'
113
118
  with mock.patch.object(capabilities, "get_json", return_value={"data": []}), \
114
- mock.patch.object(capabilities, "request", return_value=self._response(409)):
119
+ mock.patch.object(capabilities, "request", return_value=response):
115
120
  changed = capabilities.reconcile(
116
121
  "tok", "PK", "com.gowalk.form",
117
122
  {"com.apple.developer.devicecheck.appattest-environment"},
118
123
  )
119
124
 
120
- self.assertFalse(changed)
125
+ self.assertTrue(changed)
126
+
127
+ def test_surfaces_the_409_body(self) -> None:
128
+ response = self._response(409)
129
+ response.text = '{"errors":[{"detail":"capability is not modifiable"}]}'
130
+ with mock.patch.object(capabilities, "get_json", return_value={"data": []}), \
131
+ mock.patch.object(capabilities, "request", return_value=response), \
132
+ mock.patch("builtins.print") as printed:
133
+ capabilities.reconcile(
134
+ "tok", "PK", "com.gowalk.form",
135
+ {"com.apple.developer.devicecheck.appattest-environment"},
136
+ )
137
+
138
+ messages = [c.args[0] for c in printed.call_args_list if c.args]
139
+ self.assertTrue(
140
+ any("capability is not modifiable" in m for m in messages), messages
141
+ )
121
142
 
122
143
  def test_api_failure_does_not_raise(self) -> None:
123
144
  with mock.patch.object(capabilities, "get_json", return_value={"data": []}), \
@@ -127,11 +148,13 @@ class ReconcileTest(unittest.TestCase):
127
148
  {"com.apple.developer.devicecheck.appattest-environment"},
128
149
  )
129
150
 
130
- self.assertFalse(changed)
151
+ self.assertTrue(changed)
131
152
 
132
153
  def test_systemexit_from_asc_does_not_kill_signing(self) -> None:
133
154
  # asc_common.request raises SystemExit, not Exception, so a bare
134
155
  # `except Exception` would let an Apple 4xx abort the whole run.
156
+ # An unreadable App ID is also unknown state, so the profile is
157
+ # regenerated rather than trusted from cache.
135
158
  with mock.patch.object(capabilities, "get_json", side_effect=SystemExit("400")), \
136
159
  mock.patch.object(capabilities, "request", side_effect=SystemExit("400")):
137
160
  changed = capabilities.reconcile(
@@ -139,7 +162,7 @@ class ReconcileTest(unittest.TestCase):
139
162
  {"com.apple.developer.devicecheck.appattest-environment"},
140
163
  )
141
164
 
142
- self.assertFalse(changed)
165
+ self.assertTrue(changed)
143
166
 
144
167
  def test_capability_listing_sends_no_limit_parameter(self) -> None:
145
168
  # /bundleIds/{id}/bundleIdCapabilities rejects `limit` with
@@ -1 +1 @@
1
- 1.0.7
1
+ 1.0.8
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gowalk-cicd",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "Zero-config GitHub Actions delivery for iOS TestFlight and Android Google Play.",
5
5
  "type": "module",
6
6
  "bin": {