@daemux/store-automator 0.10.75 → 0.10.77
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.
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
},
|
|
6
6
|
"metadata": {
|
|
7
7
|
"description": "App Store & Google Play automation for Flutter apps",
|
|
8
|
-
"version": "0.10.
|
|
8
|
+
"version": "0.10.77"
|
|
9
9
|
},
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
12
12
|
"name": "store-automator",
|
|
13
13
|
"source": "./plugins/store-automator",
|
|
14
14
|
"description": "3 agents for app store publishing: reviewer, meta-creator, media-designer",
|
|
15
|
-
"version": "0.10.
|
|
15
|
+
"version": "0.10.77",
|
|
16
16
|
"keywords": [
|
|
17
17
|
"flutter",
|
|
18
18
|
"app-store",
|
package/package.json
CHANGED
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
"""
|
|
3
|
-
Prepare iOS code signing on a fresh CI runner.
|
|
3
|
+
Prepare iOS code signing on a fresh CI runner (automatic-signing variant).
|
|
4
|
+
|
|
5
|
+
This script prepares ONLY the credentials Xcode needs to drive
|
|
6
|
+
`-allowProvisioningUpdates` against the App Store Connect API:
|
|
4
7
|
|
|
5
|
-
Uses the App Store Connect API (auth via P8 key) to:
|
|
6
8
|
1. Generate an RSA private key + CSR
|
|
7
9
|
2. Create a new Apple Distribution certificate from the CSR; if the per-team
|
|
8
10
|
cap is hit (409), revoke the newest existing DISTRIBUTION cert and retry.
|
|
9
|
-
3.
|
|
10
|
-
linked to the new cert. If it exists, delete+recreate to refresh it.
|
|
11
|
-
4. Install the profile into ~/Library/MobileDevice/Provisioning Profiles/
|
|
12
|
-
5. Import cert + private key into a dedicated temporary keychain and put
|
|
11
|
+
3. Import cert + private key into a dedicated temporary keychain and put
|
|
13
12
|
that keychain on the search list so codesign / xcodebuild can find it.
|
|
14
13
|
|
|
14
|
+
Provisioning profile creation + installation is DELEGATED to xcodebuild via
|
|
15
|
+
`-allowProvisioningUpdates` + ASC API auth. That path handles apps with any
|
|
16
|
+
number of targets (Network Extensions, Widgets, WatchKit extensions, etc)
|
|
17
|
+
without us having to know the full target graph up-front.
|
|
18
|
+
|
|
15
19
|
Environment inputs:
|
|
16
20
|
ASC_KEY_ID - App Store Connect API key ID
|
|
17
21
|
ASC_ISSUER_ID - App Store Connect API issuer ID
|
|
18
22
|
ASC_KEY_PATH - Path to the .p8 private key file
|
|
19
|
-
TEAM_ID - Apple developer team ID
|
|
20
|
-
BUNDLE_ID - App bundle identifier
|
|
21
|
-
PROFILE_NAME - Desired provisioning profile name
|
|
22
23
|
RUNNER_TEMP - GitHub Actions temp dir (for keychain + intermediates)
|
|
23
24
|
|
|
24
25
|
Writes nothing to stdout that would leak secrets.
|
|
@@ -28,7 +29,6 @@ from __future__ import annotations
|
|
|
28
29
|
|
|
29
30
|
import base64
|
|
30
31
|
import os
|
|
31
|
-
import plistlib
|
|
32
32
|
import subprocess
|
|
33
33
|
from pathlib import Path
|
|
34
34
|
|
|
@@ -136,68 +136,6 @@ def create_distribution_cert(token: str, csr_b64: str) -> tuple[str, bytes]:
|
|
|
136
136
|
return cert_id, cert_der
|
|
137
137
|
|
|
138
138
|
|
|
139
|
-
def find_bundle_id(token: str, identifier: str) -> str:
|
|
140
|
-
data = get_json(
|
|
141
|
-
"/bundleIds",
|
|
142
|
-
token,
|
|
143
|
-
params={"filter[identifier]": identifier, "limit": "5"},
|
|
144
|
-
)
|
|
145
|
-
for item in data.get("data", []):
|
|
146
|
-
if item["attributes"]["identifier"] == identifier:
|
|
147
|
-
return item["id"]
|
|
148
|
-
raise SystemExit(f"bundle id {identifier!r} not found in ASC")
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
def delete_profile_by_name(token: str, name: str) -> None:
|
|
152
|
-
data = get_json("/profiles", token, params={"limit": "200"})
|
|
153
|
-
for p in data.get("data", []):
|
|
154
|
-
if (p["attributes"].get("name") or "") == name:
|
|
155
|
-
pid = p["id"]
|
|
156
|
-
print(f"Deleting existing profile {pid} ({name})")
|
|
157
|
-
request("DELETE", f"/profiles/{pid}", token)
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
def create_profile(
|
|
161
|
-
token: str, name: str, bundle_id_pk: str, cert_id: str
|
|
162
|
-
) -> bytes:
|
|
163
|
-
body = {
|
|
164
|
-
"data": {
|
|
165
|
-
"type": "profiles",
|
|
166
|
-
"attributes": {
|
|
167
|
-
"name": name,
|
|
168
|
-
"profileType": "IOS_APP_STORE",
|
|
169
|
-
},
|
|
170
|
-
"relationships": {
|
|
171
|
-
"bundleId": {"data": {"type": "bundleIds", "id": bundle_id_pk}},
|
|
172
|
-
"certificates": {
|
|
173
|
-
"data": [{"type": "certificates", "id": cert_id}]
|
|
174
|
-
},
|
|
175
|
-
},
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
resp = request("POST", "/profiles", token, json_body=body)
|
|
179
|
-
data = resp.json()["data"]
|
|
180
|
-
profile_content_b64 = data["attributes"]["profileContent"]
|
|
181
|
-
return base64.b64decode(profile_content_b64)
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
def install_profile(profile_der: bytes) -> str:
|
|
185
|
-
"""Write the .mobileprovision file and return its uuid."""
|
|
186
|
-
# Profile is CMS-signed plist; decode with `security cms` to read UUID.
|
|
187
|
-
profiles_dir = Path.home() / "Library/MobileDevice/Provisioning Profiles"
|
|
188
|
-
profiles_dir.mkdir(parents=True, exist_ok=True)
|
|
189
|
-
tmp_path = profiles_dir / "tmp.mobileprovision"
|
|
190
|
-
tmp_path.write_bytes(profile_der)
|
|
191
|
-
decoded = subprocess.check_output(
|
|
192
|
-
["security", "cms", "-D", "-i", str(tmp_path)]
|
|
193
|
-
)
|
|
194
|
-
uuid = plistlib.loads(decoded)["UUID"]
|
|
195
|
-
final_path = profiles_dir / f"{uuid}.mobileprovision"
|
|
196
|
-
tmp_path.rename(final_path)
|
|
197
|
-
print(f"Installed provisioning profile {uuid} at {final_path}")
|
|
198
|
-
return uuid
|
|
199
|
-
|
|
200
|
-
|
|
201
139
|
def write_p12(
|
|
202
140
|
private_key: rsa.RSAPrivateKey, cert_der: bytes, out_path: Path, passwd: str
|
|
203
141
|
) -> None:
|
|
@@ -293,20 +231,16 @@ def main() -> None:
|
|
|
293
231
|
key_id = env("ASC_KEY_ID")
|
|
294
232
|
issuer_id = env("ASC_ISSUER_ID")
|
|
295
233
|
asc_key_path = env("ASC_KEY_PATH")
|
|
296
|
-
bundle_id = env("BUNDLE_ID")
|
|
297
|
-
profile_name = env("PROFILE_NAME")
|
|
298
234
|
runner_temp = env("RUNNER_TEMP")
|
|
299
235
|
|
|
300
236
|
token = make_jwt(key_id, issuer_id, asc_key_path)
|
|
301
237
|
|
|
302
238
|
private_key, csr_b64 = generate_key_and_csr()
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
bundle_pk = find_bundle_id(token, bundle_id)
|
|
306
|
-
delete_profile_by_name(token, profile_name)
|
|
307
|
-
profile_der = create_profile(token, profile_name, bundle_pk, cert_id)
|
|
308
|
-
install_profile(profile_der)
|
|
239
|
+
_cert_id, cert_der = create_distribution_cert(token, csr_b64.decode())
|
|
309
240
|
|
|
241
|
+
# Provisioning profile(s) are created on-demand by xcodebuild via
|
|
242
|
+
# `-allowProvisioningUpdates` — we only need the signing identity
|
|
243
|
+
# (cert + private key) to be present in a keychain Xcode can see.
|
|
310
244
|
p12_pass = "ci"
|
|
311
245
|
p12_path = Path(runner_temp) / "cert.p12"
|
|
312
246
|
write_p12(private_key, cert_der, p12_path, p12_pass)
|