vydanne 0.5.0 → 0.7.0
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/GETTING_STARTED.md +69 -40
- package/README.md +161 -23
- package/SKILL.md +158 -26
- package/bin/vydanne.mjs +67 -13
- package/package.json +4 -3
- package/src/client.mjs +78 -7
- package/src/commands/accessibility.mjs +13 -0
- package/src/commands/ageRating.mjs +94 -21
- package/src/commands/bridge.mjs +328 -0
- package/src/commands/compliance.mjs +78 -9
- package/src/commands/diff.mjs +63 -19
- package/src/commands/fill.mjs +85 -13
- package/src/commands/inspect.mjs +4 -2
- package/src/commands/preflight.mjs +65 -6
- package/src/commands/prepare.mjs +220 -0
- package/src/commands/prerelease.mjs +25 -5
- package/src/commands/previews.mjs +43 -5
- package/src/commands/privacy.mjs +75 -5
- package/src/commands/push.mjs +118 -0
- package/src/commands/reviewContact.mjs +65 -14
- package/src/config.mjs +55 -4
- package/src/crossStore.mjs +151 -0
- package/src/index.mjs +60 -3
- package/src/locales.mjs +27 -5
- package/src/play/aab.mjs +133 -0
- package/src/play/client.mjs +6 -3
- package/src/play/commands/diff.mjs +40 -0
- package/src/play/commands/fill.mjs +32 -24
- package/src/play/commands/preflight.mjs +5 -0
- package/src/play/commands/prerelease.mjs +96 -22
- package/src/play/images.mjs +67 -0
- package/src/registry.mjs +27 -9
- package/src/screenshots.mjs +112 -0
- package/src/upload.mjs +6 -1
- package/types/index.d.ts +240 -10
- package/vydanne.config.example.mjs +54 -1
package/GETTING_STARTED.md
CHANGED
|
@@ -20,10 +20,10 @@ submit anything for review.
|
|
|
20
20
|
| vydanne does | You (or your developer) still do |
|
|
21
21
|
|---|---|
|
|
22
22
|
| Write/push the store text in every language | **Create the app record** in App Store Connect / Play Console |
|
|
23
|
-
| Upload screenshots, app previews, Play graphics | **Build and
|
|
24
|
-
|
|
|
25
|
-
|
|
|
26
|
-
|
|
|
23
|
+
| Upload screenshots, app previews, Play graphics | **Build and sign the binary** (`.ipa` / `.aab`) |
|
|
24
|
+
| Upload that binary to testers (`prerelease`: TestFlight internal / a Play testing track) | Answer **App Privacy** in Apple's web UI (see [step 9](#9-finish-in-the-browser-the-parts-no-api-can-do)) |
|
|
25
|
+
| Set age rating, App Review contact, IAP text | Play **Data Safety** + content-rating questionnaires |
|
|
26
|
+
| Generate the US export-compliance PDF · check everything is complete (`preflight`) | **Press Submit for Review** |
|
|
27
27
|
|
|
28
28
|
> **It never submits.** That's deliberate — a human should always be the one who ships.
|
|
29
29
|
|
|
@@ -220,14 +220,20 @@ export default {
|
|
|
220
220
|
| `primaryLocale` | Your main App Store language. Any language you *don't* translate falls back to this, so it must be complete. |
|
|
221
221
|
| `platforms` | `["IOS"]`, or `["IOS", "MAC_OS"]` if you ship a Mac app too. **Mac is a separate listing** — its text is not shared with iOS. |
|
|
222
222
|
| `uiLocales` | Short language codes you publish in. vydanne converts them to Apple's codes (`de` → `de-DE`) and warns about any language the App Store doesn't offer. |
|
|
223
|
+
| `localeMap` | Optional. Your code → Apple's code, for anything the built-in table doesn't cover (`{ "nb": "no" }`). Merged over the defaults, so list only your exceptions. |
|
|
223
224
|
| `metadataDir` | Where your listing text lives. `fastlane/metadata` is the default. |
|
|
224
|
-
| `
|
|
225
|
+
| `screenshots` | Optional. Where your screenshots live, per platform: `{ IOS: "…", MAC_OS: "…" }`. Defaults to `fastlane/screenshots` and `fastlane/screenshots-macos`. |
|
|
226
|
+
| `rating` | Age rating, e.g. `"4+"`. Anything other than `"4+"` also needs `ageRating` below. |
|
|
227
|
+
| `ageRating` | The content descriptors behind a rating above 4+, e.g. `{ violenceCartoonOrFantasy: "INFREQUENT_OR_MILD" }`. Merged over an all-NONE base — Apple computes the band from what you declare. |
|
|
228
|
+
| `reviewContact` | Optional `{ demoAccountRequired }`. By default this is inferred from whether `review_information/demo_user.txt` exists. |
|
|
225
229
|
| `asc` | Optional `{ keyId, issuerId }` — only if you'd rather not use environment variables. |
|
|
226
230
|
| `privacy` | What data actually leaves the device, e.g. `{ collected: ["CRASH_DATA"], tracking: false }`. |
|
|
227
231
|
| `iaps` | Your in-app purchases (name ≤30 chars, description ≤45). |
|
|
228
232
|
| `previews` | App Preview videos — see step 7. |
|
|
229
|
-
| `export` | Export-compliance details for the PDF: `{ encryption
|
|
230
|
-
| `google` | The Play block — `{ packageName, metadataDir, defaultLocale }`. Omit it if you're iOS-only. |
|
|
233
|
+
| `export` | Export-compliance details for the PDF: `{ encryption, appName, version, teamId, algorithms, statement, filed }`. `algorithms` and `statement` are **required** when `encryption` is `"standard"` — the command will not invent your cryptography. |
|
|
234
|
+
| `google` | The Play block — `{ packageName, metadataDir, defaultLocale, track, images, imageLocales }`. Omit it if you're iOS-only. |
|
|
235
|
+
| `bridge` | Optional `{ out, apple, play }` — where zdymak wrote, and which of its output folders feed which store slot. Only needed when `dir:` in `zdymak.config.mjs` makes a folder name differ from its target name. |
|
|
236
|
+
| `push` | Optional `{ skip: [...] }` — pipeline steps this app never runs. |
|
|
231
237
|
|
|
232
238
|
Check your languages resolved correctly:
|
|
233
239
|
|
|
@@ -288,11 +294,16 @@ image is for. Anything after it is yours — but files upload in alphabetical or
|
|
|
288
294
|
| `watch_` | Apple Watch Ultra |
|
|
289
295
|
| `macos_` | Mac (in `screenshots-macos/`) |
|
|
290
296
|
|
|
291
|
-
A file whose prefix isn't in that table is **
|
|
292
|
-
the
|
|
297
|
+
A file whose prefix isn't in that table is **not uploaded**, and `fill` says so by name — it lists every
|
|
298
|
+
file it skipped and the prefixes it knows. Same for a folder that isn't an App Store locale code (`de`
|
|
299
|
+
instead of `de-DE`, the classic one): named, not dropped in silence.
|
|
293
300
|
|
|
294
|
-
|
|
295
|
-
|
|
301
|
+
PNG and JPEG are both accepted.
|
|
302
|
+
|
|
303
|
+
> **Screenshots must have no transparency.** Simulator captures often carry an alpha channel and Apple
|
|
304
|
+
> rejects those. `npx vydanne bridge` refuses before copying anything if it finds one, and tells you
|
|
305
|
+
> which file; to fix a single image in place, `VYDANNE_FLATTEN=path/to.png npx vydanne iap` converts it
|
|
306
|
+
> to RGB. Note that `fill` itself does **not** convert — flatten at the source.
|
|
296
307
|
|
|
297
308
|
**Google Play** uses its **own** language codes (`de-DE`, `zh-CN`, `iw-IL`, `ar` — *not* Apple's
|
|
298
309
|
`zh-Hans`/`he`), and only three text files:
|
|
@@ -305,8 +316,8 @@ fastlane/metadata/android/
|
|
|
305
316
|
└─ full_description.txt (≤4000)
|
|
306
317
|
```
|
|
307
318
|
|
|
308
|
-
Play images are read from **fixed paths
|
|
309
|
-
|
|
319
|
+
Play images are read from **fixed paths**. Each is uploaded only if the file exists, so a missing set
|
|
320
|
+
never wipes what's live:
|
|
310
321
|
|
|
311
322
|
| Play asset | Path vydanne reads |
|
|
312
323
|
|---|---|
|
|
@@ -318,6 +329,12 @@ writes them). Each is uploaded only if the file exists, so a missing set never w
|
|
|
318
329
|
|
|
319
330
|
*(These paths are not configurable yet — create the folders at those locations, or symlink them.)*
|
|
320
331
|
|
|
332
|
+
> **Capturing with [zdymak](https://www.npmjs.com/package/zdymak)?** Its output layout is different
|
|
333
|
+
> from all of the above (one `store-assets/` tree, short locale codes, numbered filenames). Run
|
|
334
|
+
> **`npx vydanne bridge`** after every capture — it renames and files everything into the Apple and
|
|
335
|
+
> Play layouts on this page, holds back screenshots for locales that have no listing text yet, and
|
|
336
|
+
> refuses images with an alpha channel before Apple can. `--dry-run` shows what it would do.
|
|
337
|
+
|
|
321
338
|
---
|
|
322
339
|
|
|
323
340
|
## 7. Optional extras
|
|
@@ -345,40 +362,46 @@ separate ANSSI declaration.
|
|
|
345
362
|
|
|
346
363
|
## 8. Push it to the store
|
|
347
364
|
|
|
348
|
-
|
|
365
|
+
**Every command that changes a store is a dry run until you add `--apply`** — it prints each write it
|
|
366
|
+
would make and sends nothing. So the safe rhythm is always: run it, read the plan, run it again with
|
|
367
|
+
`--apply`.
|
|
368
|
+
|
|
369
|
+
The whole Apple release is one command, which runs the seven steps in the only order that works
|
|
370
|
+
(`prepare` → `fill` → `previews` → `age-rating` → `review-contact` → `accessibility` → `preflight`) and
|
|
371
|
+
stops at the first problem:
|
|
349
372
|
|
|
350
373
|
```sh
|
|
351
|
-
npx vydanne preflight # is anything missing or over a character limit?
|
|
352
374
|
npx vydanne diff # exactly what would change vs what's live now
|
|
375
|
+
npx vydanne push # DRY RUN of the whole pipeline — read it
|
|
376
|
+
npx vydanne push --apply # do it; ends at a green preflight
|
|
377
|
+
npx vydanne privacy # prints the answers to type into Apple's web UI
|
|
353
378
|
```
|
|
354
379
|
|
|
355
|
-
`
|
|
380
|
+
Each step is also its own command (`npx vydanne fill --apply`, etc.) if you prefer to go one at a time.
|
|
381
|
+
Two things worth knowing:
|
|
356
382
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
npx vydanne accessibility # saved as a draft; publishes only once your app is live
|
|
363
|
-
npx vydanne privacy # prints the answers to type into Apple's web UI
|
|
364
|
-
```
|
|
383
|
+
- **Updating an app that's already live?** The draft version to write into doesn't exist until
|
|
384
|
+
`prepare` creates it — `push` runs it first, or run `npx vydanne prepare --apply` yourself. Without
|
|
385
|
+
it, `fill` refuses rather than touching the listing your customers are reading.
|
|
386
|
+
- **Replacing screenshots or previews that are already on the store** needs `VYDANNE_REPLACE=1` — by
|
|
387
|
+
default a populated slot is skipped (and says so), never overwritten.
|
|
365
388
|
|
|
366
|
-
**For Google Play**, `
|
|
367
|
-
half-finished local folder can't overwrite your live listing:
|
|
389
|
+
**For Google Play**, the same `--apply` rule applies — a dry run validates against Google for real,
|
|
390
|
+
then throws the change away, so a half-finished local folder can't overwrite your live listing:
|
|
368
391
|
|
|
369
392
|
```sh
|
|
370
393
|
# macOS / Linux
|
|
371
|
-
npx vydanne fill --store google
|
|
372
|
-
|
|
394
|
+
npx vydanne fill --store google # dry run: shows what would happen
|
|
395
|
+
npx vydanne fill --store google --apply # actually commit it
|
|
373
396
|
```
|
|
374
397
|
```powershell
|
|
375
|
-
# Windows PowerShell —
|
|
398
|
+
# Windows PowerShell — same flag, no environment variables needed
|
|
376
399
|
npx vydanne fill --store google
|
|
377
|
-
|
|
378
|
-
Remove-Item Env:\VYDANNE_COMMIT # clear it so later runs stay dry
|
|
400
|
+
npx vydanne fill --store google --apply
|
|
379
401
|
```
|
|
380
402
|
|
|
381
|
-
Finally, run `npx vydanne diff` once more
|
|
403
|
+
Finally, run `npx vydanne diff` once more — it compares text *and* image content against the store —
|
|
404
|
+
and eyeball one screenshot per platform in the web UI.
|
|
382
405
|
|
|
383
406
|
---
|
|
384
407
|
|
|
@@ -386,7 +409,9 @@ Finally, run `npx vydanne diff` once more and eyeball one screenshot per platfor
|
|
|
386
409
|
|
|
387
410
|
1. **App Privacy** (Apple) — Apple's privacy API isn't reachable with an API key, so `vydanne privacy`
|
|
388
411
|
prints the exact answers; you paste them into App Store Connect by hand.
|
|
389
|
-
2. **
|
|
412
|
+
2. **Build the binary** — Xcode / Gradle, as usual. `npx vydanne prerelease --apply` can then upload it
|
|
413
|
+
for you (TestFlight internal groups, or a Play testing track). Shipping it to the *public* stays in
|
|
414
|
+
the consoles, always.
|
|
390
415
|
3. **Play Data Safety + content rating** — questionnaires in the Play Console.
|
|
391
416
|
4. **Submit for Review** — yours to press.
|
|
392
417
|
|
|
@@ -400,13 +425,13 @@ Finally, run `npx vydanne diff` once more and eyeball one screenshot per platfor
|
|
|
400
425
|
| `ASC key not found at …` | The `.p8` isn't where vydanne looks. | The error prints the exact path it wants — move the file there. List it with `ls ~/.appstoreconnect/private_keys/` (macOS/Linux) or `dir "$env:USERPROFILE\.appstoreconnect\private_keys"` (Windows). |
|
|
401
426
|
| `app '…' not found for this ASC key` | The bundle ID is wrong, or the key's team doesn't own the app. | Check `bundleId` matches App Store Connect exactly. |
|
|
402
427
|
| `401` / `403` from Apple | Key lacks permission, or the IDs are swapped. | Key access must be **App Manager**+. Confirm `ASC_KEY_ID` vs `ASC_ISSUER_ID` aren't reversed. |
|
|
403
|
-
| `no editable version` |
|
|
428
|
+
| `no editable version` | The only version is live (read-only) and no draft exists yet. | `npx vydanne prepare --apply` creates the next version — that's its job. |
|
|
404
429
|
| A locale was ignored | The folder name isn't an Apple code. | Run `npx vydanne locales` and rename the folder to the code shown. |
|
|
405
|
-
| Screenshots didn't upload | Wrong filename prefix, or the slot already has images. | Use the prefix table above
|
|
406
|
-
| Apple rejects a screenshot | It has an alpha channel. | Flatten to RGB — macOS/Linux: `VYDANNE_FLATTEN=shot.png npx vydanne iap` · Windows: `$env:VYDANNE_FLATTEN="shot.png"; npx vydanne iap`. |
|
|
407
|
-
| `VYDANNE_… =1` "does nothing" on Windows | PowerShell doesn't support the Unix `VAR=1 command` form. | Set it first: `$env:
|
|
430
|
+
| Screenshots didn't upload | Wrong filename prefix, or the slot already has images. | `fill` now names both cases in its output. Use the prefix table above; to replace an already-populated slot, re-run with `VYDANNE_REPLACE=1`. |
|
|
431
|
+
| Apple rejects a screenshot | It has an alpha channel. | Flatten to RGB — macOS/Linux: `VYDANNE_FLATTEN=shot.png npx vydanne iap` · Windows: `$env:VYDANNE_FLATTEN="shot.png"; npx vydanne iap`. (`bridge` checks this for you.) |
|
|
432
|
+
| `VYDANNE_… =1` "does nothing" on Windows | PowerShell doesn't support the Unix `VAR=1 command` form. | Set it first: `$env:VYDANNE_REPLACE = "1"`, then run the command. |
|
|
408
433
|
| `no google block in config` | Play isn't configured. | Add the `google` block and set `PLAY_JSON_KEY_FILE`. |
|
|
409
|
-
| Play changes didn't stick |
|
|
434
|
+
| Play changes didn't stick | Store-changing commands are dry by default. | Re-run with `--apply`. |
|
|
410
435
|
| `accessibility` returns 409 | Labels can't publish before the app is live. | Leave it as a draft; publish after launch with `VYDANNE_A11Y_PUBLISH=1`. |
|
|
411
436
|
|
|
412
437
|
---
|
|
@@ -419,10 +444,14 @@ Finally, run `npx vydanne diff` once more and eyeball one screenshot per platfor
|
|
|
419
444
|
| `PLAY_JSON_KEY_FILE` | Path to the Play service-account JSON (**required for Play**). |
|
|
420
445
|
| `VYDANNE_CONFIG` | Use a different config file (same as `--config`). |
|
|
421
446
|
| `VYDANNE_SKIP_METADATA` / `VYDANNE_SKIP_SCREENSHOTS` | `fill`: push only one half. |
|
|
422
|
-
| `
|
|
423
|
-
| `
|
|
447
|
+
| `VYDANNE_REPLACE=1` | `fill` / `previews`: delete what's in an already-populated slot and upload yours. |
|
|
448
|
+
| `VYDANNE_VERSION=<x>` | `prepare`: name the version when it's created before its build exists. |
|
|
449
|
+
| `VYDANNE_IPA=<path>` / `VYDANNE_AAB=<path>` | `prerelease`: override where the binary is found. |
|
|
450
|
+
| `VYDANNE_TRACK` / `VYDANNE_RELEASE_NAME` | `prerelease --store google`: testing track / release name. |
|
|
424
451
|
| `VYDANNE_FLATTEN=<png>` | `iap`: convert an image to RGB (removes transparency). |
|
|
425
452
|
| `VYDANNE_A11Y_PUBLISH=1` | `accessibility`: publish the labels (only once the app is live). |
|
|
453
|
+
| `VYDANNE_ALLOW_CROSS_STORE=1` | Skip the other-store-mention check for one run. |
|
|
454
|
+
| `VYDANNE_COMMIT=1` | Legacy alias for `--apply` — prefer the flag. |
|
|
426
455
|
|
|
427
456
|
Set them with `export NAME=value` on macOS/Linux, or `$env:NAME = "value"` in Windows PowerShell.
|
|
428
457
|
|
package/README.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# vydanne
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/vydanne)
|
|
4
|
+
[](https://www.npmjs.com/package/vydanne)
|
|
5
|
+
[](https://nodejs.org)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://github.com/Lonli-Lokli/zdymak)
|
|
8
|
+
|
|
3
9
|
**Fill in your app's App Store and Google Play listing — in every language — from files on your computer.**
|
|
4
10
|
|
|
5
11
|
Publishing an app means typing the same things into a web form over and over: a name, a subtitle, a
|
|
@@ -7,7 +13,9 @@ description, keywords, screenshots… once per language, twice per store, again
|
|
|
7
13
|
box and the store quietly shows a blank page to half the world. Get a folder name wrong and the whole
|
|
8
14
|
upload fails.
|
|
9
15
|
|
|
10
|
-
vydanne does that typing for you, then **checks your work before Apple or Google does
|
|
16
|
+
vydanne does that typing for you, then **checks your work before Apple or Google does** — and puts
|
|
17
|
+
your build in front of testers on both stores. It stops at the one step that should stay a human's:
|
|
18
|
+
it never submits for review, and never ships to the public.
|
|
11
19
|
|
|
12
20
|
> *выданне* (Belarusian) — "publishing".
|
|
13
21
|
|
|
@@ -27,15 +35,19 @@ actual app. vydanne handles the *listing*, not the software.
|
|
|
27
35
|
|
|
28
36
|
## The two halves of a release
|
|
29
37
|
|
|
30
|
-
vydanne has a sibling — **[zdymak](https://
|
|
38
|
+
vydanne has a sibling — **[zdymak](https://github.com/Lonli-Lokli/zdymak)**. They split the job cleanly:
|
|
31
39
|
|
|
32
40
|
| | [zdymak](https://github.com/Lonli-Lokli/zdymak) 📸 | **vydanne** 📝 |
|
|
33
41
|
|---|---|---|
|
|
34
42
|
| Makes | The **pictures** — screenshots, App Preview videos, the Play feature graphic | The **words and the paperwork** — listing text, ratings, contacts, privacy, compliance |
|
|
35
43
|
| Ends with | Image and video files on disk | A listing filled in and verified, ready for a human to submit |
|
|
36
44
|
|
|
37
|
-
Use them together: zdymak produces the assets, vydanne
|
|
38
|
-
|
|
45
|
+
Use them together: zdymak produces the assets, **`npx vydanne bridge`** moves them to where vydanne
|
|
46
|
+
reads (zdymak writes one `store-assets/` tree with short locale codes and numbered files; vydanne reads
|
|
47
|
+
`fastlane/screenshots/<Apple-locale>/` with device-prefixed names, and fixed `marketing/out/` paths for
|
|
48
|
+
Play), and `fill` uploads them alongside your text. Run `bridge` after every capture — skipping it means
|
|
49
|
+
`fill` quietly re-uploads whatever was bridged last time. Use vydanne alone if you already have your
|
|
50
|
+
screenshots in its layout.
|
|
39
51
|
|
|
40
52
|
<br>
|
|
41
53
|
|
|
@@ -141,6 +153,19 @@ export default {
|
|
|
141
153
|
};
|
|
142
154
|
```
|
|
143
155
|
|
|
156
|
+
Every field is optional except `bundleId` and `primaryLocale`. The ones you reach for next:
|
|
157
|
+
|
|
158
|
+
| Field | What it's for |
|
|
159
|
+
|---|---|
|
|
160
|
+
| `screenshots` | Where your screenshots live, per platform. Defaults to `fastlane/screenshots` + `fastlane/screenshots-macos`. |
|
|
161
|
+
| `localeMap` | Your language codes → Apple's, for anything the built-in table misses (`{ nb: "no" }`). |
|
|
162
|
+
| `ageRating` | The content descriptors behind any rating above `4+`. Apple computes the band from them. |
|
|
163
|
+
| `reviewContact` | `{ demoAccountRequired }` — otherwise inferred from whether `review_information/demo_user.txt` exists. |
|
|
164
|
+
| `export` | Export-compliance details. `algorithms` + `statement` are required for `encryption: "standard"`. |
|
|
165
|
+
| `bridge` | `{ out, apple, play }` — which zdymak output folder feeds which store slot, when `dir:` overrides make them differ. |
|
|
166
|
+
| `push` | `{ skip: [...] }` — pipeline steps this app never runs. |
|
|
167
|
+
| `google` | The Play block, including `track`, `images` and `imageLocales`. |
|
|
168
|
+
|
|
144
169
|
**3. Write your listing** as plain text files, one folder per language:
|
|
145
170
|
|
|
146
171
|
```
|
|
@@ -155,33 +180,111 @@ fastlane/metadata/en-US/keywords.txt
|
|
|
155
180
|
```sh
|
|
156
181
|
npx vydanne preflight # anything missing or too long?
|
|
157
182
|
npx vydanne diff # what exactly would change?
|
|
158
|
-
npx vydanne fill #
|
|
183
|
+
npx vydanne fill # DRY RUN — prints every write it would make
|
|
184
|
+
npx vydanne fill --apply # do it
|
|
159
185
|
```
|
|
160
186
|
|
|
161
187
|
Run vydanne **from your project folder** — it finds everything relative to where you are.
|
|
162
188
|
|
|
163
189
|
<br>
|
|
164
190
|
|
|
191
|
+
## `--apply`, or nothing happens
|
|
192
|
+
|
|
193
|
+
**Every command that can change a store is a dry run unless you pass `--apply`.** It reads the store,
|
|
194
|
+
prints each write it would make, and sends nothing:
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
DRY RUN — 'fill' will not change App Store Connect. Add --apply to write.
|
|
198
|
+
would PATCH /v1/appStoreVersionLocalizations/ad2f… — description, keywords, whatsNew
|
|
199
|
+
…
|
|
200
|
+
DRY RUN — 40 store write(s) withheld. Re-run with --apply to perform them.
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
The commands this applies to are marked `✎` in the usage text (`vydanne` with no arguments): `prepare`, `push`, `fill`, `previews`,
|
|
204
|
+
`age-rating`, `review-contact`, `accessibility`, `prerelease`. Everything else only reads, and ignores
|
|
205
|
+
the flag.
|
|
206
|
+
|
|
207
|
+
Two details worth knowing:
|
|
208
|
+
|
|
209
|
+
- **A dry run walks the whole plan.** It does not stop at the first locale — the count at the end is the
|
|
210
|
+
number to compare against `diff`. "Nothing to write" and "nothing happened" are different sentences, and
|
|
211
|
+
only the first one means your local files already match the store.
|
|
212
|
+
- **The two stores enforce it differently, deliberately.** Play builds the Edit and *validates it against
|
|
213
|
+
Google* for real, then discards it — so a dry run catches everything a commit would have caught. Apple
|
|
214
|
+
has no transaction to roll back, so there the block is at the HTTP layer: no `POST`/`PATCH`/`PUT`/
|
|
215
|
+
`DELETE` leaves the process at all. `prerelease` also refuses the `altool` upload, after validating the
|
|
216
|
+
archive.
|
|
217
|
+
|
|
218
|
+
> Upgrading from ≤ 0.5? The Apple half used to write immediately — `vydanne fill` now needs `--apply`.
|
|
219
|
+
> `VYDANNE_COMMIT=1` still works as an alias so existing Play scripts keep running, but prefer the flag.
|
|
220
|
+
|
|
221
|
+
<br>
|
|
222
|
+
|
|
165
223
|
## What each command does
|
|
166
224
|
|
|
167
225
|
| Command | In plain English |
|
|
168
226
|
|---|---|
|
|
169
|
-
| `preflight` | **Run this first.** Checks the listing is complete
|
|
227
|
+
| `preflight` | **Run this first.** Checks the listing is complete, nothing is over a character limit, no locale mentions the other app store — and that the screenshots on the store are your *current* ones, not a stale set. Green means submittable. |
|
|
228
|
+
| `prepare` | Starts the next release: creates the App Store version you're preparing and attaches your newest build to it. **Needed before `fill` on an app that already has a version on sale** — until a draft exists there is nothing for the listing text to go into. Reuses the draft if it's already there, so it's safe to re-run. It does *not* submit. |
|
|
229
|
+
| `push` | **The whole release, one command**: runs `prepare` → `fill` → `previews` → `age-rating` → `review-contact` → `accessibility` → `preflight`, in that order, stopping at the first failure. Dry run without `--apply`, like everything else. Ends at a green preflight — it never submits. |
|
|
170
230
|
| `diff` | Shows exactly what's different between your files and what's live. Nothing is changed — a safe preview. |
|
|
171
|
-
| `fill` | Uploads your listing text and screenshots. Handles iPhone, iPad and Mac. |
|
|
231
|
+
| `fill` | Uploads your listing text and screenshots. Handles iPhone, iPad and Mac. Refuses to upload text that names the other mobile platform. |
|
|
172
232
|
| `previews` | Uploads App Preview videos. |
|
|
173
233
|
| `inspect` | Shows the app's current state in the store. Read-only. |
|
|
174
234
|
| `locales` | Lists your languages and Apple's code for each — and warns about any language the App Store doesn't offer. |
|
|
175
|
-
| `age-rating` | Sets the age rating. |
|
|
235
|
+
| `age-rating` | Sets the age rating. `rating: "4+"` needs nothing else; any higher rating is described feature-by-feature in `ageRating` and Apple computes the band from it. |
|
|
176
236
|
| `review-contact` | Fills in the App Review contact details (who Apple calls if there's a problem). |
|
|
177
237
|
| `accessibility` | Saves Accessibility Nutrition Labels from the `accessibility` block in your config. Stays a draft until your app is live. Refuses to run if you have not declared one — see below. |
|
|
178
238
|
| `privacy` | Prints the privacy answers to paste into Apple's website (Apple's privacy section has no API). |
|
|
179
239
|
| `iap` | Checks your in-app purchase text fits, and can strip transparency from an image. |
|
|
180
240
|
| `compliance` | Generates the US encryption self-classification PDF that Apple asks for. |
|
|
241
|
+
| `bridge` | Moves [zdymak](https://github.com/Lonli-Lokli/zdymak)'s captured screenshots into the folders `fill` reads — renaming locales to the stores' codes and files to the device-slot convention. Local files only; `--dry-run` previews. Run it after every capture. |
|
|
181
242
|
| `version` | Prints the version of vydanne. |
|
|
182
243
|
|
|
183
244
|
For **Google Play**, add `--store google` to `inspect`, `diff`, `preflight`, `fill`, or `prerelease`.
|
|
184
245
|
|
|
246
|
+
`prepare`, `push`, `fill`, `previews`, `age-rating`, `review-contact`, `accessibility` and `prerelease`
|
|
247
|
+
change the store, so they need [`--apply`](#--apply-or-nothing-happens); without it they report and exit.
|
|
248
|
+
|
|
249
|
+
## The release pipeline — the order matters
|
|
250
|
+
|
|
251
|
+
A release is the same seven steps in the same order, every time. `prepare` must come first (until the
|
|
252
|
+
draft version exists, nothing has anywhere to write) and `preflight` must come last (green has to be
|
|
253
|
+
measured *after* the writes it blesses, or it blesses nothing). That ordering is exactly the kind of
|
|
254
|
+
thing that lives in someone's head until the day it doesn't — so `push` runs it for you, stopping at the
|
|
255
|
+
first failure, and each step's own refusals still apply:
|
|
256
|
+
|
|
257
|
+
```sh
|
|
258
|
+
npx vydanne prerelease --apply # whenever the build is ready — before or after push is fine
|
|
259
|
+
npx vydanne push # DRY RUN of the whole pipeline: read the plan
|
|
260
|
+
npx vydanne push --apply # do it: prepare → fill → previews → age-rating
|
|
261
|
+
# → review-contact → accessibility → preflight
|
|
262
|
+
# then, in App Store Connect: Add to Review -> Submit (always you, never vydanne)
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
The two flows differ only at the first step, and `push` absorbs the difference:
|
|
266
|
+
|
|
267
|
+
- **First release.** Creating the app record in App Store Connect already gave you a version in *Prepare
|
|
268
|
+
for Submission*, so `prepare` finds it and reuses it — a no-op that simply reports what's there.
|
|
269
|
+
- **An update to a live app.** A version on sale is read-only and there is no draft until someone makes
|
|
270
|
+
one. Here `prepare` is the step that matters: it creates the next version (numbered from the newest
|
|
271
|
+
build's own `CFBundleShortVersionString`, so it can't drift from the binary) and attaches that build.
|
|
272
|
+
|
|
273
|
+
Skip `prepare` on a live app and `fill` has nowhere valid to aim — it refuses, rather than falling back
|
|
274
|
+
to the version **on sale** and rewriting the listing your customers are reading.
|
|
275
|
+
|
|
276
|
+
One wrinkle worth knowing: on a live app, a *dry run* of `push` stops at `fill`, because the draft the
|
|
277
|
+
later steps write into doesn't exist until `prepare` is applied. `push` says so up front. Creating the
|
|
278
|
+
draft is safe — it isn't a submission — so `vydanne prepare --apply` first, then a dry `push`, previews
|
|
279
|
+
the whole plan. Preparing a version before its build exists is the one case that needs telling:
|
|
280
|
+
|
|
281
|
+
```sh
|
|
282
|
+
VYDANNE_VERSION=1.2 npx vydanne prepare --apply
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Prefer doing it step by step? Every step is its own command (the table above), and `push` is nothing
|
|
286
|
+
more than those commands in the right order.
|
|
287
|
+
|
|
185
288
|
<br>
|
|
186
289
|
|
|
187
290
|
## Google Play
|
|
@@ -191,9 +294,8 @@ Add a `google` block to your config and point `PLAY_JSON_KEY_FILE` at a service-
|
|
|
191
294
|
|
|
192
295
|
```sh
|
|
193
296
|
npx vydanne preflight --store google
|
|
194
|
-
npx vydanne fill --store google
|
|
195
|
-
|
|
196
|
-
# Windows PowerShell: $env:VYDANNE_COMMIT = "1"; npx vydanne fill --store google
|
|
297
|
+
npx vydanne fill --store google # dry run — shows what would change
|
|
298
|
+
npx vydanne fill --store google --apply # actually do it
|
|
197
299
|
```
|
|
198
300
|
|
|
199
301
|
### `prerelease` — the build, to testers
|
|
@@ -244,8 +346,8 @@ a paid app they are the testers who install without buying it.
|
|
|
244
346
|
**Google Play — a closed track**
|
|
245
347
|
|
|
246
348
|
```sh
|
|
247
|
-
npx vydanne prerelease --store google
|
|
248
|
-
|
|
349
|
+
npx vydanne prerelease --store google # dry run
|
|
350
|
+
npx vydanne prerelease --store google --apply # publish to the track
|
|
249
351
|
```
|
|
250
352
|
|
|
251
353
|
```js
|
|
@@ -263,13 +365,22 @@ the same line the Apple side draws by never submitting.
|
|
|
263
365
|
**Paid app? Use `internal`.** It is the only track where testers install without buying; closed and open
|
|
264
366
|
testers pay like everyone else.
|
|
265
367
|
|
|
266
|
-
Release notes follow supply's layout, so an existing repo needs no migration —
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
368
|
+
Release notes follow supply's layout, so an existing repo needs no migration — per locale, first match
|
|
369
|
+
wins:
|
|
370
|
+
|
|
371
|
+
| File in `<metadataDir>/<play-locale>/changelogs/` | When to use it |
|
|
372
|
+
|---|---|
|
|
373
|
+
| `<versionCode>.txt` | You know the exact code (supply's own convention). |
|
|
374
|
+
| `next.txt` | **The notes for the release you're about to cut** — for when the versionCode isn't knowable in advance (e.g. it's derived from the git commit count, so every commit moves it). After a real (`--apply`) publish, vydanne renames it to `<versionCode>.txt`, so the *next* release can't inherit this one's notes by accident. |
|
|
375
|
+
| `default.txt` | Evergreen fallback ("bug fixes and improvements"). Falling back to it is **warned**, because notes written for one release quietly serving every later one is how a listing shows last release's news. |
|
|
376
|
+
|
|
377
|
+
Notes are truncated to Play's 500-char cap with a warning. The versionCode comes from the bundle's own
|
|
378
|
+
manifest — vydanne reads it out of the `.aab` locally and reports which changelog file each locale
|
|
379
|
+
resolves to *before* the upload, so a wrong file costs a re-run, not a re-release. Re-uploading a used
|
|
380
|
+
code fails loudly instead of silently replacing a binary. Overrides: `VYDANNE_AAB`, `VYDANNE_TRACK`,
|
|
381
|
+
`VYDANNE_RELEASE_NAME`.
|
|
271
382
|
|
|
272
|
-
**Play is dry by default on purpose.** Nothing goes live until you add
|
|
383
|
+
**Play is dry by default on purpose.** Nothing goes live until you add `--apply`, so a
|
|
273
384
|
half-finished folder can never overwrite a good listing. Play also uses its **own** language codes
|
|
274
385
|
(`zh-CN`, `iw-IL`) which are *not* Apple's — `vydanne locales` and the
|
|
275
386
|
[layout guide](GETTING_STARTED.md#6-put-your-text-and-images-where-vydanne-looks) keep them straight.
|
|
@@ -300,9 +411,10 @@ don't have to learn them the hard way.
|
|
|
300
411
|
| Your main language is left empty → most of the world sees a blank page | `preflight` refuses to pass |
|
|
301
412
|
| macOS is a **separate** listing; its text is not shared with iOS | Fills each platform independently |
|
|
302
413
|
| Apple's list endpoints return blank text, so tools "see" an empty listing | Reads each language individually |
|
|
303
|
-
| Screenshots with transparency get rejected |
|
|
414
|
+
| Screenshots with transparency get rejected | `bridge` refuses before copying and names the files; `VYDANNE_FLATTEN` converts one |
|
|
304
415
|
| Once a version is *Ready for Review*, most tools can no longer edit it | Uses a method that still works |
|
|
305
|
-
| Character limits (
|
|
416
|
+
| Character limits (name/subtitle 30, keywords 100, promo 170; purchases 30 / 45) | All checked by `preflight` before upload, not after rejection |
|
|
417
|
+
| One translation says "also on Google Play" → rejected under guideline 2.3.10 | Every locale is scanned before upload; `preflight` and `fill` both refuse |
|
|
306
418
|
| Apple's privacy section can't be reached by any API key | Prints the exact answers to paste in |
|
|
307
419
|
| Accessibility labels can't publish before launch | Saved as a draft automatically |
|
|
308
420
|
| In-app purchases need **two** different images, easily confused | Labels both slots |
|
|
@@ -313,8 +425,12 @@ don't have to learn them the hard way.
|
|
|
313
425
|
|
|
314
426
|
## What vydanne will never do
|
|
315
427
|
|
|
316
|
-
- **It never submits
|
|
317
|
-
|
|
428
|
+
- **It never submits for review, and never ships to the public.** It *will* put a build in front of
|
|
429
|
+
your testers — TestFlight internal, or a Play closed track — and point the version you are
|
|
430
|
+
preparing at it. Pressing Submit, promoting to Play production, and distributing to external
|
|
431
|
+
TestFlight (which needs Beta App Review) all stay yours. Those are refusals, not flags: there is
|
|
432
|
+
no argument combination that reaches the public.
|
|
433
|
+
- It doesn't build or sign your binary. It uploads the `.ipa` / `.aab` you already produced.
|
|
318
434
|
- It doesn't create the app record — make that in App Store Connect / Play Console first.
|
|
319
435
|
|
|
320
436
|
<br>
|
|
@@ -337,6 +453,28 @@ Releasing a new version: [RELEASING.md](RELEASING.md).
|
|
|
337
453
|
MIT.
|
|
338
454
|
|
|
339
455
|
|
|
456
|
+
### One store never mentions the other
|
|
457
|
+
|
|
458
|
+
Both stores reject a listing that advertises the competing platform — Apple under App Review
|
|
459
|
+
guideline **2.3.10** ("no names, icons, or imagery of other mobile platforms"), Google under its
|
|
460
|
+
Store Listing and Promotion policy. It is an easy mistake to make and an expensive one to find: the
|
|
461
|
+
two listings come from the same source copy, so a single translator writing "auch für Android"
|
|
462
|
+
costs a review cycle, in one locale out of twenty, days later.
|
|
463
|
+
|
|
464
|
+
`preflight` and `fill` both scan the local metadata before anything is uploaded, per locale, per
|
|
465
|
+
field — store names, store URLs, and the other platform's device names, in Latin script and in the
|
|
466
|
+
localized forms (安卓, Андроид, アンドロイド, …). A store's OWN platform is never flagged: "Android"
|
|
467
|
+
belongs in a Play listing. Neither does the bare word "Play", which every game listing uses.
|
|
468
|
+
|
|
469
|
+
Ambiguous words ("apple" in a game about fruit) are reported as warnings and never block. If one
|
|
470
|
+
genuinely belongs in your copy:
|
|
471
|
+
|
|
472
|
+
```js
|
|
473
|
+
allowCrossStoreTerms: ["Apple"],
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
`VYDANNE_ALLOW_CROSS_STORE=1` overrides the whole check for one run.
|
|
477
|
+
|
|
340
478
|
### Accessibility Nutrition Labels
|
|
341
479
|
|
|
342
480
|
Every other thing vydanne writes is a *fact* about your app. This one is a **claim about its
|