@daemux/store-automator 0.10.80 → 0.10.81
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.81"
|
|
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.81",
|
|
16
16
|
"keywords": [
|
|
17
17
|
"flutter",
|
|
18
18
|
"app-store",
|
package/package.json
CHANGED
|
@@ -29,6 +29,7 @@ from __future__ import annotations
|
|
|
29
29
|
import base64
|
|
30
30
|
import json
|
|
31
31
|
import plistlib
|
|
32
|
+
import re
|
|
32
33
|
import subprocess
|
|
33
34
|
from pathlib import Path
|
|
34
35
|
|
|
@@ -125,55 +126,135 @@ def patch_project_signing(
|
|
|
125
126
|
) -> None:
|
|
126
127
|
"""Set manual signing + per-target profile name for every signable target.
|
|
127
128
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
129
|
+
The pbxproj stays in its original OpenStep format — Xcode is very
|
|
130
|
+
opinionated about that file and any conversion (to XML/JSON) risks
|
|
131
|
+
re-ordering keys or dropping comments. Instead, we edit the specific
|
|
132
|
+
``XCBuildConfiguration`` blocks by their UUID and inject/replace the
|
|
133
|
+
handful of keys we care about. Targets outside ``targets`` (most
|
|
134
|
+
importantly SwiftPM / resource-bundle configs) are untouched.
|
|
131
135
|
"""
|
|
132
|
-
|
|
136
|
+
pbx_path = Path(project_path) / "project.pbxproj"
|
|
137
|
+
text = pbx_path.read_text(encoding="utf-8")
|
|
138
|
+
|
|
133
139
|
for target in targets:
|
|
134
140
|
profile_name = f"{PROFILE_PREFIX}{target['bundle_id']}"
|
|
135
141
|
for cid in target["config_ids"]:
|
|
136
|
-
|
|
137
|
-
_set_build_setting(pbx, cid, "CODE_SIGN_IDENTITY", "Apple Distribution")
|
|
138
|
-
_set_build_setting(pbx, cid, "DEVELOPMENT_TEAM", team_id)
|
|
139
|
-
_set_build_setting(
|
|
140
|
-
pbx, cid, "PROVISIONING_PROFILE_SPECIFIER", profile_name
|
|
141
|
-
)
|
|
142
|
-
# Clear any legacy autogenerated PROVISIONING_PROFILE uuid that
|
|
143
|
-
# would otherwise override the specifier we just set.
|
|
144
|
-
_clear_build_setting(pbx, cid, "PROVISIONING_PROFILE")
|
|
142
|
+
text = _apply_signing_to_config(text, cid, team_id, profile_name)
|
|
145
143
|
print(
|
|
146
144
|
f"Patched {target['name']!r} -> {profile_name} "
|
|
147
145
|
f"(configs={len(target['config_ids'])})"
|
|
148
146
|
)
|
|
149
147
|
|
|
148
|
+
pbx_path.write_text(text, encoding="utf-8")
|
|
149
|
+
# Sanity check — plutil -lint rejects malformed output so CI fails
|
|
150
|
+
# fast before ``xcodebuild`` tries (and mangles) the file.
|
|
151
|
+
subprocess.check_call(["plutil", "-lint", str(pbx_path)])
|
|
150
152
|
|
|
151
|
-
def _keypath(config_id: str, key: str) -> str:
|
|
152
|
-
return f"objects.{config_id}.buildSettings.{key}"
|
|
153
153
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
154
|
+
def _apply_signing_to_config(
|
|
155
|
+
text: str, config_id: str, team_id: str, profile_name: str
|
|
156
|
+
) -> str:
|
|
157
|
+
"""Return ``text`` with the given XCBuildConfiguration's buildSettings
|
|
158
|
+
patched for manual signing. Raises ``SystemExit`` if the config block
|
|
159
|
+
can't be located (indicates a pbxproj format we don't understand).
|
|
160
|
+
"""
|
|
161
|
+
start = text.find(f"\t\t{config_id} ")
|
|
162
|
+
if start < 0:
|
|
163
|
+
start = text.find(f"\t\t{config_id}\t")
|
|
164
|
+
if start < 0:
|
|
165
|
+
start = text.find(f"{config_id} = {{")
|
|
166
|
+
if start < 0:
|
|
167
|
+
raise SystemExit(
|
|
168
|
+
f"patch_project_signing: config {config_id} not found in pbxproj"
|
|
169
|
+
)
|
|
170
|
+
settings_key = "buildSettings = {"
|
|
171
|
+
s_idx = text.find(settings_key, start)
|
|
172
|
+
if s_idx < 0:
|
|
173
|
+
raise SystemExit(
|
|
174
|
+
f"patch_project_signing: buildSettings not found for {config_id}"
|
|
168
175
|
)
|
|
176
|
+
# Find matching closing '};' for buildSettings — brace-balance forward.
|
|
177
|
+
depth = 0
|
|
178
|
+
i = s_idx + len(settings_key) - 1 # position at the opening '{'
|
|
179
|
+
end = -1
|
|
180
|
+
while i < len(text):
|
|
181
|
+
ch = text[i]
|
|
182
|
+
if ch == "{":
|
|
183
|
+
depth += 1
|
|
184
|
+
elif ch == "}":
|
|
185
|
+
depth -= 1
|
|
186
|
+
if depth == 0:
|
|
187
|
+
end = i
|
|
188
|
+
break
|
|
189
|
+
i += 1
|
|
190
|
+
if end < 0:
|
|
191
|
+
raise SystemExit(
|
|
192
|
+
f"patch_project_signing: unbalanced buildSettings for {config_id}"
|
|
193
|
+
)
|
|
194
|
+
inner = text[s_idx + len(settings_key): end]
|
|
195
|
+
patched = _patch_inner_settings(inner, team_id, profile_name)
|
|
196
|
+
return text[: s_idx + len(settings_key)] + patched + text[end:]
|
|
169
197
|
|
|
170
198
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
199
|
+
_SIGNING_KEYS = {
|
|
200
|
+
"CODE_SIGN_STYLE": "Manual",
|
|
201
|
+
"CODE_SIGN_IDENTITY": '"Apple Distribution"',
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
_STRIP_KEYS = ("PROVISIONING_PROFILE",)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _patch_inner_settings(
|
|
208
|
+
inner: str, team_id: str, profile_name: str
|
|
209
|
+
) -> str:
|
|
210
|
+
"""Rewrite build setting key/value lines inside one buildSettings block."""
|
|
211
|
+
# Always-set values
|
|
212
|
+
values = dict(_SIGNING_KEYS)
|
|
213
|
+
values["DEVELOPMENT_TEAM"] = team_id
|
|
214
|
+
values["PROVISIONING_PROFILE_SPECIFIER"] = f'"{profile_name}"'
|
|
215
|
+
|
|
216
|
+
lines = inner.splitlines(keepends=True)
|
|
217
|
+
emitted_keys: set[str] = set()
|
|
218
|
+
out: list[str] = []
|
|
219
|
+
# Each setting line looks like (whitespace-prefixed):
|
|
220
|
+
# KEY = VALUE;
|
|
221
|
+
# We match leading indent, the key, `= `, the rest.
|
|
222
|
+
pattern = re.compile(r"^(\s*)([A-Z_][A-Z0-9_]*)\s*=\s*(.+);\s*$")
|
|
223
|
+
|
|
224
|
+
for line in lines:
|
|
225
|
+
m = pattern.match(line)
|
|
226
|
+
if not m:
|
|
227
|
+
out.append(line)
|
|
228
|
+
continue
|
|
229
|
+
indent, key, _old_value = m.group(1), m.group(2), m.group(3)
|
|
230
|
+
if key in _STRIP_KEYS:
|
|
231
|
+
# Drop these keys entirely.
|
|
232
|
+
continue
|
|
233
|
+
if key in values:
|
|
234
|
+
out.append(f"{indent}{key} = {values[key]};\n")
|
|
235
|
+
emitted_keys.add(key)
|
|
236
|
+
continue
|
|
237
|
+
out.append(line)
|
|
238
|
+
|
|
239
|
+
# Any key we wanted to set but didn't find — append just before close.
|
|
240
|
+
missing = [k for k in values if k not in emitted_keys]
|
|
241
|
+
if missing:
|
|
242
|
+
# Determine indent from the first KEY= line we can find, else use
|
|
243
|
+
# two tabs (the pbxproj default).
|
|
244
|
+
indent = "\t\t\t\t"
|
|
245
|
+
for line in lines:
|
|
246
|
+
m = pattern.match(line)
|
|
247
|
+
if m:
|
|
248
|
+
indent = m.group(1)
|
|
249
|
+
break
|
|
250
|
+
tail = out[-1] if out else ""
|
|
251
|
+
# Ensure we inject before the trailing whitespace on the last line.
|
|
252
|
+
new_lines = [f"{indent}{k} = {values[k]};\n" for k in missing]
|
|
253
|
+
if tail.strip() == "":
|
|
254
|
+
out = out[:-1] + new_lines + [tail]
|
|
255
|
+
else:
|
|
256
|
+
out = out + new_lines
|
|
257
|
+
return "".join(out)
|
|
177
258
|
|
|
178
259
|
|
|
179
260
|
# --------------------------------------------------------------------------- #
|