@daemux/store-automator 0.10.96 → 0.10.97
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/.claude-plugin/marketplace.json +2 -2
- package/package.json +1 -1
- package/plugins/store-automator/.claude-plugin/plugin.json +1 -1
- package/templates/github/IOS_NATIVE_CI_SETUP.md +32 -6
- package/templates/scripts/ci/ios-native/mmv_floor_check.py +13 -8
- package/templates/scripts/ci/ios-native/version_utils.py +42 -21
|
@@ -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.97"
|
|
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.97",
|
|
16
16
|
"keywords": [
|
|
17
17
|
"flutter",
|
|
18
18
|
"app-store",
|
package/package.json
CHANGED
|
@@ -43,7 +43,11 @@ Subsequent builds need neither — the ASC API key handles everything.
|
|
|
43
43
|
|
|
44
44
|
## Step 4 — MARKETING_VERSION
|
|
45
45
|
|
|
46
|
-
Leave your Xcode project's `MARKETING_VERSION` at `1.0` initially.
|
|
46
|
+
Leave your Xcode project's `MARKETING_VERSION` at `1.0` initially.
|
|
47
|
+
CI auto-rolls the marketing version forward (rollover bump — patch
|
|
48
|
+
with carry at .9) whenever the prior version hits `READY_FOR_SALE` in
|
|
49
|
+
App Store Connect. Until then, the pipeline keeps bumping the build
|
|
50
|
+
number against the current marketing version.
|
|
47
51
|
|
|
48
52
|
### Auto-bumping MARKETING_VERSION
|
|
49
53
|
|
|
@@ -51,7 +55,33 @@ When the ASC combined floor (max of pending review, `preReleaseVersions`,
|
|
|
51
55
|
or builds-via-`preReleaseVersion`) exceeds your project's
|
|
52
56
|
`MARKETING_VERSION`, the action auto-bumps and commits the new value as
|
|
53
57
|
part of the same bot commit that handles cert refresh / autoupdate.
|
|
54
|
-
|
|
58
|
+
|
|
59
|
+
Default policy is `rollover` — patch with carry: at `.9` it rolls into
|
|
60
|
+
the next minor (`1.0.9` → `1.1.0`), and at minor=9 it cascades into the
|
|
61
|
+
next major (`1.9.9` → `2.0.0`). Major has no upper limit (`9.9.9` →
|
|
62
|
+
`10.0.0` just keeps growing). This produces the more natural human
|
|
63
|
+
progression most projects want — patch numbers never silently grow past
|
|
64
|
+
9.
|
|
65
|
+
|
|
66
|
+
**Four policies** are supported via the action input:
|
|
67
|
+
|
|
68
|
+
| Policy | Example bump | When to use |
|
|
69
|
+
|--------|--------------|-------------|
|
|
70
|
+
| `rollover` (default) | `1.0.5` → `1.0.6`; `1.0.9` → `1.1.0` | Natural progression, carry at .9. |
|
|
71
|
+
| `patch` | `1.0.5` → `1.0.6`; `1.0.9` → `1.0.10` | Legacy unbounded patch — pinned for backward compat. |
|
|
72
|
+
| `minor` | `1.0.5` → `1.1.0`; `1.0.9` → `1.1.0` | Projects that ship every release as a minor. |
|
|
73
|
+
| `none` | (fails the build) | Explicit semver control via human bump. |
|
|
74
|
+
|
|
75
|
+
Full rollover behaviour: `1.0.9` → `1.1.0` (patch overflow), `1.9.9`
|
|
76
|
+
→ `2.0.0` (minor cascade), `9.9.9` → `10.0.0` (major has no upper
|
|
77
|
+
limit). The `patch` policy is preserved verbatim for consumers that
|
|
78
|
+
pinned it before `rollover` shipped.
|
|
79
|
+
|
|
80
|
+
**Backward compat:** existing consumers on `0.0.27` that explicitly pin
|
|
81
|
+
`marketing-version-auto-bump: 'patch'` keep their current unbounded
|
|
82
|
+
behavior — the `'patch'` policy is unchanged. The default change from
|
|
83
|
+
`'patch'` → `'rollover'` only affects new installs and consumers that
|
|
84
|
+
do not override the input.
|
|
55
85
|
|
|
56
86
|
**Opt out** via the action input:
|
|
57
87
|
|
|
@@ -64,10 +94,6 @@ Default policy is `patch` — bumps the patch component.
|
|
|
64
94
|
In `'none'` mode, the floor check fails the build and you must bump
|
|
65
95
|
`MARKETING_VERSION` manually before retrying.
|
|
66
96
|
|
|
67
|
-
**Policy `'minor'`** bumps the minor component (e.g. `1.0.5` → `1.1.0`)
|
|
68
|
-
when floor exceeds project. Useful for projects where every release
|
|
69
|
-
is a minor.
|
|
70
|
-
|
|
71
97
|
**Side effect:** the bot commit subject reflects what was changed,
|
|
72
98
|
e.g. `ci: refresh signing identity + bump MARKETING_VERSION [skip ci]`.
|
|
73
99
|
|
|
@@ -30,8 +30,11 @@ SEM_RE = re.compile(r"^(\d+)\.(\d+)(?:\.(\d+))?$")
|
|
|
30
30
|
|
|
31
31
|
# DESIGN DECISION: auto-bump policy is read from the env var so the action
|
|
32
32
|
# step can wire it from `inputs.marketing-version-auto-bump` without a
|
|
33
|
-
# CLI-flag re-plumb of every helper. Default '
|
|
34
|
-
# input default.
|
|
33
|
+
# CLI-flag re-plumb of every helper. Default 'rollover' matches the action
|
|
34
|
+
# input default -- patch with carry at .9 (1.0.9 -> 1.1.0) is the more
|
|
35
|
+
# natural semver progression for unattended CI. ``'patch'`` is preserved
|
|
36
|
+
# unchanged for consumers pinning the historical (unbounded) behavior.
|
|
37
|
+
# ``'none'`` preserves the historical fail-the-build path.
|
|
35
38
|
_AUTO_BUMP_ENV = "MARKETING_VERSION_AUTO_BUMP"
|
|
36
39
|
|
|
37
40
|
# DESIGN DECISION: auto-bump must only fire when the resulting bumped
|
|
@@ -139,7 +142,7 @@ def _floor_error(target: str, floor: str, per_source: dict, *, strict: bool) ->
|
|
|
139
142
|
raise SystemExit(2)
|
|
140
143
|
|
|
141
144
|
|
|
142
|
-
def _persist_context_ok() -> bool:
|
|
145
|
+
def _persist_context_ok(policy: str) -> bool:
|
|
143
146
|
"""True when this run can persist a project-file bump back to the
|
|
144
147
|
default branch via the action's commit-back step. The commit-back
|
|
145
148
|
step is gated on ``event=push`` AND ``ref=refs/heads/<default>``;
|
|
@@ -149,7 +152,9 @@ def _persist_context_ok() -> bool:
|
|
|
149
152
|
|
|
150
153
|
On refusal, emits the ``::warning::`` describing why this run
|
|
151
154
|
cannot persist the bump (event/ref/default_branch values from the
|
|
152
|
-
environment).
|
|
155
|
+
environment). The warning names the active policy (rollover /
|
|
156
|
+
patch / minor) so consumers reading CI logs can see which mode
|
|
157
|
+
was attempted. Side-effecting the warning here -- rather than at
|
|
153
158
|
the caller -- keeps ``maybe_auto_bump`` under the per-function LOC
|
|
154
159
|
cap without splitting the env-read + warning into two helpers
|
|
155
160
|
(which would also push the file over the per-file functions cap)."""
|
|
@@ -164,7 +169,7 @@ def _persist_context_ok() -> bool:
|
|
|
164
169
|
if ok:
|
|
165
170
|
return True
|
|
166
171
|
print(
|
|
167
|
-
f"::warning::auto-bump=
|
|
172
|
+
f"::warning::auto-bump={policy} is enabled, but this run "
|
|
168
173
|
f"cannot persist the bump (event={event or '<unset>'}, "
|
|
169
174
|
f"ref={ref or '<unset>'}, "
|
|
170
175
|
f"default_branch={default_branch or '<unset>'}). The "
|
|
@@ -198,17 +203,17 @@ def maybe_auto_bump(target: str, floor: str) -> str | None:
|
|
|
198
203
|
runs ``git add -f`` on the touched path so the auto-bump is staged
|
|
199
204
|
in the index before any later step (e.g. prepare_signing) mutates
|
|
200
205
|
the same file."""
|
|
201
|
-
policy = (os.environ.get(_AUTO_BUMP_ENV) or "
|
|
206
|
+
policy = (os.environ.get(_AUTO_BUMP_ENV) or "rollover").strip().lower()
|
|
202
207
|
if policy == "none":
|
|
203
208
|
return None
|
|
204
|
-
if policy not in ("patch", "minor"):
|
|
209
|
+
if policy not in ("rollover", "patch", "minor"):
|
|
205
210
|
print(
|
|
206
211
|
f"::warning::Unknown {_AUTO_BUMP_ENV}={policy!r}; "
|
|
207
212
|
f"falling back to fail-on-floor behavior",
|
|
208
213
|
file=sys.stderr,
|
|
209
214
|
)
|
|
210
215
|
return None
|
|
211
|
-
if not _persist_context_ok():
|
|
216
|
+
if not _persist_context_ok(policy):
|
|
212
217
|
return None
|
|
213
218
|
new_version = version_utils.compute_next_version(target, floor, policy)
|
|
214
219
|
if not version_utils.write_marketing_version(new_version):
|
|
@@ -12,10 +12,14 @@ the project's 10-functions-per-file cap. Two concerns live here:
|
|
|
12
12
|
result > floor (so ASC accepts the new row)
|
|
13
13
|
result > current (so the bump is observable)
|
|
14
14
|
|
|
15
|
-
Policy is ``'
|
|
16
|
-
patch component
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
Policy is ``'rollover'`` (default), ``'patch'``, or ``'minor'``.
|
|
16
|
+
Rollover bumps the patch component with carry: at .9 it rolls
|
|
17
|
+
into the next minor (1.0.9 -> 1.1.0), and at minor=9, patch=9 it
|
|
18
|
+
cascades into the next major (1.9.9 -> 2.0.0). Major has no upper
|
|
19
|
+
limit (9.9.9 -> 10.0.0). Patch (legacy) bumps the patch component
|
|
20
|
+
unbounded (1.0.9 -> 1.0.10) -- preserved for backward compat with
|
|
21
|
+
consumers pinning the historical default. Minor bumps the minor
|
|
22
|
+
component and resets patch to 0.
|
|
19
23
|
|
|
20
24
|
2. ``write_marketing_version(new_version)`` -- I/O. Writes
|
|
21
25
|
``new_version`` into the project's source-of-truth. Resolution order:
|
|
@@ -100,32 +104,49 @@ def compute_next_version(current: str, floor: str, policy: str) -> str:
|
|
|
100
104
|
"""Compute the next MARKETING_VERSION that satisfies both:
|
|
101
105
|
result > floor AND result > current.
|
|
102
106
|
|
|
103
|
-
``policy`` is ``'
|
|
104
|
-
handled by the caller (it preserves the
|
|
105
|
-
semantics) and never reaches this function.
|
|
106
|
-
|
|
107
|
+
``policy`` is ``'rollover'`` (default), ``'patch'``, or ``'minor'``.
|
|
108
|
+
The ``'none'`` case is handled by the caller (it preserves the
|
|
109
|
+
existing fail-the-build semantics) and never reaches this function.
|
|
110
|
+
|
|
111
|
+
Cross-train semantics (shared by 'patch' and 'rollover'): stay on
|
|
112
|
+
the higher major.minor train of (current, floor) so we never
|
|
113
|
+
silently advance an unrelated minor or major; when trains match,
|
|
114
|
+
base patch is max(current.patch, floor.patch). 'rollover' adds
|
|
115
|
+
carry: patch>9 rolls to patch=0, minor+=1; resulting minor>9
|
|
116
|
+
rolls to minor=0, major+=1; major has no upper limit (9.9.9 ->
|
|
117
|
+
10.0.0 just keeps growing). The ``>9`` (not ``==10``) check
|
|
118
|
+
handles inputs already past the rollover boundary -- e.g. a
|
|
119
|
+
project previously on the 'patch' policy that reached 1.0.10
|
|
120
|
+
before switching to 'rollover' must still cascade
|
|
121
|
+
(1.0.10 -> 1.1.0) rather than emit a malformed 1.0.11. 'patch'
|
|
122
|
+
(legacy) is unbounded (1.0.9 -> 1.0.10), preserved verbatim for
|
|
123
|
+
backward compat with consumers that pinned the historical default.
|
|
124
|
+
"""
|
|
125
|
+
if policy not in ("rollover", "patch", "minor"):
|
|
107
126
|
raise ValueError(f"unknown policy: {policy!r}")
|
|
108
|
-
cur = _parse(current)
|
|
109
|
-
fl = _parse(floor)
|
|
127
|
+
cur, fl = _parse(current), _parse(floor)
|
|
110
128
|
if policy == "minor":
|
|
111
129
|
major = max(cur[0], fl[0])
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
# patch: stay on the higher major.minor train of (current, floor) so we
|
|
115
|
-
# never silently advance an unrelated minor or major. The patch number
|
|
116
|
-
# is +1 above whichever of (current, floor) shares that train; patches
|
|
117
|
-
# from the lower train are namespace-irrelevant and ignored.
|
|
130
|
+
return f"{major}.{max(cur[1], fl[1]) + 1}.0"
|
|
131
|
+
# patch / rollover share train resolution.
|
|
118
132
|
if cur[:2] >= fl[:2]:
|
|
119
133
|
major, minor, patch_base = cur[0], cur[1], cur[2]
|
|
120
|
-
# If current and floor share a major.minor, lift the patch above
|
|
121
|
-
# whichever is higher (floor by definition, since result must be
|
|
122
|
-
# > floor; current can equal floor only when cur[:2] == fl[:2]).
|
|
123
134
|
if cur[:2] == fl[:2]:
|
|
124
135
|
patch_base = max(cur[2], fl[2])
|
|
125
136
|
else:
|
|
126
|
-
# Floor is on a higher train; jump to it and bump from its patch.
|
|
127
137
|
major, minor, patch_base = fl
|
|
128
|
-
|
|
138
|
+
if policy == "patch":
|
|
139
|
+
return f"{major}.{minor}.{patch_base + 1}"
|
|
140
|
+
# rollover: patch+1 with carry into minor, then into major.
|
|
141
|
+
# ``>9`` (not ``==10``) so inputs already over the boundary
|
|
142
|
+
# (e.g. a 1.0.10 left over from the legacy 'patch' policy)
|
|
143
|
+
# still cascade cleanly into the next minor / major train.
|
|
144
|
+
new_patch = patch_base + 1
|
|
145
|
+
if new_patch > 9:
|
|
146
|
+
new_patch, minor = 0, minor + 1
|
|
147
|
+
if minor > 9:
|
|
148
|
+
minor, major = 0, major + 1
|
|
149
|
+
return f"{major}.{minor}.{new_patch}"
|
|
129
150
|
|
|
130
151
|
|
|
131
152
|
def _xcodebuild_args() -> tuple[list[str], Path]:
|