vydanne 0.2.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 +434 -0
- package/LICENSE +21 -0
- package/README.md +212 -0
- package/SKILL.md +174 -0
- package/bin/vydanne.mjs +66 -0
- package/package.json +66 -0
- package/src/client.mjs +65 -0
- package/src/commands/accessibility.mjs +36 -0
- package/src/commands/ageRating.mjs +33 -0
- package/src/commands/compliance.mjs +45 -0
- package/src/commands/diff.mjs +107 -0
- package/src/commands/fill.mjs +84 -0
- package/src/commands/iap.mjs +31 -0
- package/src/commands/inspect.mjs +20 -0
- package/src/commands/preflight.mjs +42 -0
- package/src/commands/previews.mjs +51 -0
- package/src/commands/privacy.mjs +29 -0
- package/src/commands/reviewContact.mjs +27 -0
- package/src/config.mjs +48 -0
- package/src/index.mjs +6 -0
- package/src/jwt.mjs +17 -0
- package/src/locales.mjs +26 -0
- package/src/play/auth.mjs +29 -0
- package/src/play/client.mjs +68 -0
- package/src/play/commands/diff.mjs +47 -0
- package/src/play/commands/fill.mjs +69 -0
- package/src/play/commands/inspect.mjs +24 -0
- package/src/play/commands/preflight.mjs +37 -0
- package/src/registry.mjs +29 -0
- package/src/upload.mjs +47 -0
- package/src/util.mjs +9 -0
- package/types/index.d.ts +119 -0
- package/vydanne.config.example.mjs +56 -0
|
@@ -0,0 +1,434 @@
|
|
|
1
|
+
# Getting started with vydanne
|
|
2
|
+
|
|
3
|
+
A step-by-step setup for **your** app — written so you can follow it without being a developer. Every
|
|
4
|
+
step is a command to paste or a button to click. Budget ~30 minutes the first time; after that a release
|
|
5
|
+
is three commands.
|
|
6
|
+
|
|
7
|
+
If you'd rather skim: [What vydanne does](#0-what-vydanne-does-and-what-stays-yours) →
|
|
8
|
+
[Accounts](#1-what-you-need-before-you-start) → [Install](#2-install-vydanne) →
|
|
9
|
+
[Apple key](#3-get-your-apple-credentials-once) → [Play key](#4-get-your-google-play-credentials-once-android-only) →
|
|
10
|
+
[Config](#5-write-the-config-file) → [Folders](#6-put-your-text-and-images-where-vydanne-looks) →
|
|
11
|
+
[Push it](#8-push-it-to-the-store).
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 0. What vydanne does, and what stays yours
|
|
16
|
+
|
|
17
|
+
vydanne fills in **an app record that already exists**. It does not create apps, build software, or
|
|
18
|
+
submit anything for review.
|
|
19
|
+
|
|
20
|
+
| vydanne does | You (or your developer) still do |
|
|
21
|
+
|---|---|
|
|
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 upload the binary** (`.ipa` / `.aab`) |
|
|
24
|
+
| Set age rating, App Review contact, IAP text | Answer **App Privacy** in Apple's web UI (see [step 9](#9-finish-in-the-browser-the-parts-no-api-can-do)) |
|
|
25
|
+
| Generate the US export-compliance PDF | Play **Data Safety** + content-rating questionnaires |
|
|
26
|
+
| Check everything is complete (`preflight`) | **Press Submit for Review** |
|
|
27
|
+
|
|
28
|
+
> **It never submits.** That's deliberate — a human should always be the one who ships.
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 1. What you need before you start
|
|
33
|
+
|
|
34
|
+
**For Apple (iPhone / iPad / Mac):**
|
|
35
|
+
1. An **Apple Developer Program** membership (~$99/year) — [developer.apple.com/programs](https://developer.apple.com/programs/).
|
|
36
|
+
2. An **app already created** in [App Store Connect](https://appstoreconnect.apple.com) → **My Apps → +**.
|
|
37
|
+
You need its **Bundle ID** (e.g. `com.example.myapp`).
|
|
38
|
+
3. Permission to create API keys: your account must be **Account Holder** or **Admin**.
|
|
39
|
+
|
|
40
|
+
**For Google Play (Android)** — skip if you're iOS-only:
|
|
41
|
+
1. A **Play Console** developer account (one-time $25) — [play.google.com/console](https://play.google.com/console).
|
|
42
|
+
2. An **app already created** in Play Console, with its **package name** (e.g. `com.example.myapp`).
|
|
43
|
+
3. For a brand-new app, upload your first build through the Play Console web UI once. The API can edit a
|
|
44
|
+
listing, but a never-released app has nothing to attach a listing to.
|
|
45
|
+
|
|
46
|
+
**On your computer:**
|
|
47
|
+
- **Node.js 20.9 or newer.** Check with `node -v`. If it's missing or older, install the LTS build from
|
|
48
|
+
[nodejs.org](https://nodejs.org).
|
|
49
|
+
- **macOS, Linux, or Windows** — vydanne runs on all three.
|
|
50
|
+
|
|
51
|
+
> **Windows users:** every step below shows both versions. Use the **PowerShell** one (open *Terminal* or
|
|
52
|
+
> *Windows PowerShell* from the Start menu). The commands are not interchangeable — `export` and
|
|
53
|
+
> `VAR=1 command` are Unix-only syntax and will fail in PowerShell.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## 2. Install vydanne
|
|
58
|
+
|
|
59
|
+
Open your terminal and go to your app's project folder (the top level — the folder you'd open in an editor):
|
|
60
|
+
|
|
61
|
+
```sh
|
|
62
|
+
# macOS / Linux
|
|
63
|
+
cd /path/to/your-app
|
|
64
|
+
```
|
|
65
|
+
```powershell
|
|
66
|
+
# Windows PowerShell
|
|
67
|
+
cd C:\path\to\your-app
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
If there's no `package.json` there yet, create one (harmless — it just records your tools):
|
|
71
|
+
|
|
72
|
+
```sh
|
|
73
|
+
npm init -y
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Install vydanne and confirm it runs:
|
|
77
|
+
|
|
78
|
+
```sh
|
|
79
|
+
npm i -D vydanne
|
|
80
|
+
npx vydanne version
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
You should see something like `vydanne 0.1.0`.
|
|
84
|
+
|
|
85
|
+
> **Always run vydanne from this folder.** It reads `vydanne.config.mjs` and every file path relative to
|
|
86
|
+
> wherever you are. Running it from a subfolder will make it look in the wrong place.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 3. Get your Apple credentials (once)
|
|
91
|
+
|
|
92
|
+
vydanne talks to Apple with an **API key** — three pieces: a `.p8` file, a **Key ID**, and an **Issuer ID**.
|
|
93
|
+
|
|
94
|
+
1. Go to [App Store Connect](https://appstoreconnect.apple.com) → **Users and Access**.
|
|
95
|
+
2. Open the **Integrations** tab → **App Store Connect API** → **Team Keys**.
|
|
96
|
+
3. Click **+**, name it `vydanne`, and set **Access** to **App Manager**. *(Admin also works. Anything
|
|
97
|
+
lower can't edit a listing.)*
|
|
98
|
+
4. Click **Generate**, then **Download the key**. ⚠️ **Apple lets you download it exactly once.** If you
|
|
99
|
+
lose it, revoke the key and make a new one.
|
|
100
|
+
5. On that same page, copy the **Key ID** (next to your new key) and the **Issuer ID** (at the top of the
|
|
101
|
+
list — one per team, looks like `57246542-96fe-1a63-e053-0824d011072a`).
|
|
102
|
+
|
|
103
|
+
Now put the file where vydanne looks for it — inside a `.appstoreconnect\private_keys` folder in your
|
|
104
|
+
home directory. vydanne finds your home folder automatically on every operating system.
|
|
105
|
+
|
|
106
|
+
```sh
|
|
107
|
+
# macOS / Linux
|
|
108
|
+
mkdir -p ~/.appstoreconnect/private_keys
|
|
109
|
+
mv ~/Downloads/AuthKey_*.p8 ~/.appstoreconnect/private_keys/
|
|
110
|
+
```
|
|
111
|
+
```powershell
|
|
112
|
+
# Windows PowerShell
|
|
113
|
+
New-Item -ItemType Directory -Force "$env:USERPROFILE\.appstoreconnect\private_keys"
|
|
114
|
+
Move-Item "$env:USERPROFILE\Downloads\AuthKey_*.p8" "$env:USERPROFILE\.appstoreconnect\private_keys\"
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Tell your terminal the two IDs (substitute your own values):
|
|
118
|
+
|
|
119
|
+
```sh
|
|
120
|
+
# macOS / Linux
|
|
121
|
+
export ASC_KEY_ID=ABCD123456
|
|
122
|
+
export ASC_ISSUER_ID=57246542-96fe-1a63-e053-0824d011072a
|
|
123
|
+
```
|
|
124
|
+
```powershell
|
|
125
|
+
# Windows PowerShell
|
|
126
|
+
$env:ASC_KEY_ID = "ABCD123456"
|
|
127
|
+
$env:ASC_ISSUER_ID = "57246542-96fe-1a63-e053-0824d011072a"
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**To avoid retyping these every time you open a terminal:**
|
|
131
|
+
|
|
132
|
+
- **macOS / Linux** — append the same two `export` lines to `~/.zshrc` (or `~/.bashrc`), then run
|
|
133
|
+
`source ~/.zshrc`.
|
|
134
|
+
- **Windows** — save them permanently for your user account (run once; then reopen the terminal):
|
|
135
|
+
```powershell
|
|
136
|
+
[Environment]::SetEnvironmentVariable("ASC_KEY_ID", "ABCD123456", "User")
|
|
137
|
+
[Environment]::SetEnvironmentVariable("ASC_ISSUER_ID", "57246542-96fe-1a63-e053-0824d011072a", "User")
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Test it:**
|
|
141
|
+
|
|
142
|
+
```sh
|
|
143
|
+
npx vydanne inspect
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
If you see your app's current state, Apple is wired up. (Errors? See
|
|
147
|
+
[Troubleshooting](#troubleshooting).)
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## 4. Get your Google Play credentials (once, Android only)
|
|
152
|
+
|
|
153
|
+
Play uses a **service account** — a robot Google account with a JSON key file.
|
|
154
|
+
|
|
155
|
+
1. In [Play Console](https://play.google.com/console): **Setup → API access**. Link or create a Google
|
|
156
|
+
Cloud project if prompted.
|
|
157
|
+
2. Click through to **Google Cloud → Service Accounts**, then **Create service account**. Name it
|
|
158
|
+
`vydanne`. You can skip the optional role/user steps.
|
|
159
|
+
3. Open the new service account → **Keys → Add key → Create new key → JSON**. It downloads immediately —
|
|
160
|
+
this file is a password, keep it out of your project folder and out of git.
|
|
161
|
+
4. Back in **Play Console → Users and permissions → Invite new user**. Paste the service account's email
|
|
162
|
+
(it looks like `vydanne@your-project.iam.gserviceaccount.com`).
|
|
163
|
+
5. Give it access to **your app only**, with the **Edit store listing, pricing & distribution**
|
|
164
|
+
permission (and **Release** if you'll also push builds later). Send the invite — it's auto-accepted.
|
|
165
|
+
|
|
166
|
+
Point vydanne at the key — use the **full path**, not a shortcut like `~` (vydanne does not expand it):
|
|
167
|
+
|
|
168
|
+
```sh
|
|
169
|
+
# macOS / Linux
|
|
170
|
+
export PLAY_JSON_KEY_FILE="$HOME/keys/play-service-account.json"
|
|
171
|
+
```
|
|
172
|
+
```powershell
|
|
173
|
+
# Windows PowerShell
|
|
174
|
+
$env:PLAY_JSON_KEY_FILE = "$env:USERPROFILE\keys\play-service-account.json"
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
**Test it:**
|
|
178
|
+
|
|
179
|
+
```sh
|
|
180
|
+
npx vydanne inspect --store google
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
> vydanne is **scoped to the one `packageName`** in your config. Even a service-account key with wider
|
|
184
|
+
> access can't touch another app through vydanne.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## 5. Write the config file
|
|
189
|
+
|
|
190
|
+
Create a file named **`vydanne.config.mjs`** in your project folder. Start from the copy that ships
|
|
191
|
+
inside the package:
|
|
192
|
+
|
|
193
|
+
```sh
|
|
194
|
+
# macOS / Linux
|
|
195
|
+
cp node_modules/vydanne/vydanne.config.example.mjs vydanne.config.mjs
|
|
196
|
+
```
|
|
197
|
+
```powershell
|
|
198
|
+
# Windows PowerShell
|
|
199
|
+
Copy-Item node_modules\vydanne\vydanne.config.example.mjs vydanne.config.mjs
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
A minimal iPhone-only config looks like this — edit the values to match your app:
|
|
203
|
+
|
|
204
|
+
```js
|
|
205
|
+
export default {
|
|
206
|
+
bundleId: "com.example.myapp", // exactly as registered with Apple
|
|
207
|
+
primaryLocale: "en-US", // your main language — MUST be filled in
|
|
208
|
+
platforms: ["IOS"], // add "MAC_OS" only if you also ship a Mac app
|
|
209
|
+
uiLocales: ["en", "de", "fr"], // the languages you'll publish
|
|
210
|
+
metadataDir: "fastlane/metadata",
|
|
211
|
+
rating: "4+",
|
|
212
|
+
};
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**The fields, in plain terms:**
|
|
216
|
+
|
|
217
|
+
| Field | What to put |
|
|
218
|
+
|---|---|
|
|
219
|
+
| `bundleId` | Your app's bundle identifier, exactly as Apple has it. |
|
|
220
|
+
| `primaryLocale` | Your main App Store language. Any language you *don't* translate falls back to this, so it must be complete. |
|
|
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
|
+
| `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
|
+
| `metadataDir` | Where your listing text lives. `fastlane/metadata` is the default. |
|
|
224
|
+
| `rating` | Age rating, e.g. `"4+"`. |
|
|
225
|
+
| `asc` | Optional `{ keyId, issuerId }` — only if you'd rather not use environment variables. |
|
|
226
|
+
| `privacy` | What data actually leaves the device, e.g. `{ collected: ["CRASH_DATA"], tracking: false }`. |
|
|
227
|
+
| `iaps` | Your in-app purchases (name ≤30 chars, description ≤45). |
|
|
228
|
+
| `previews` | App Preview videos — see step 7. |
|
|
229
|
+
| `export` | Export-compliance details for the PDF: `{ encryption: "standard", appName, version, teamId }`. |
|
|
230
|
+
| `google` | The Play block — `{ packageName, metadataDir, defaultLocale }`. Omit it if you're iOS-only. |
|
|
231
|
+
|
|
232
|
+
Check your languages resolved correctly:
|
|
233
|
+
|
|
234
|
+
```sh
|
|
235
|
+
npx vydanne locales
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Anything listed as *unsupported* has no App Store language and will fall back to your primary locale —
|
|
239
|
+
**don't** create a folder for it.
|
|
240
|
+
|
|
241
|
+
---
|
|
242
|
+
|
|
243
|
+
## 6. Put your text and images where vydanne looks
|
|
244
|
+
|
|
245
|
+
This is the part that trips people up. vydanne reads **plain `.txt` files in named folders**. One folder
|
|
246
|
+
per language, named with **Apple's** code (from `vydanne locales`).
|
|
247
|
+
|
|
248
|
+
```
|
|
249
|
+
your-app/
|
|
250
|
+
├─ vydanne.config.mjs
|
|
251
|
+
├─ fastlane/
|
|
252
|
+
│ ├─ metadata/
|
|
253
|
+
│ │ ├─ en-US/ ← Apple's locale code, not "en"
|
|
254
|
+
│ │ │ ├─ name.txt (≤30) your app's store name
|
|
255
|
+
│ │ │ ├─ subtitle.txt (≤30) one benefit line
|
|
256
|
+
│ │ │ ├─ description.txt (≤4000)
|
|
257
|
+
│ │ │ ├─ keywords.txt (≤100) comma-separated, NO spaces
|
|
258
|
+
│ │ │ ├─ promotional_text.txt (≤170) editable anytime, no review
|
|
259
|
+
│ │ │ ├─ release_notes.txt what's new in this version
|
|
260
|
+
│ │ │ ├─ marketing_url.txt (optional)
|
|
261
|
+
│ │ │ └─ support_url.txt (optional)
|
|
262
|
+
│ │ ├─ de-DE/ …same files…
|
|
263
|
+
│ │ └─ review_information/ ← App Review contact. KEEP OUT OF GIT.
|
|
264
|
+
│ │ ├─ first_name.txt
|
|
265
|
+
│ │ ├─ last_name.txt
|
|
266
|
+
│ │ ├─ phone_number.txt
|
|
267
|
+
│ │ ├─ email_address.txt
|
|
268
|
+
│ │ └─ notes.txt how a reviewer tests your app
|
|
269
|
+
│ ├─ screenshots/ ← iPhone / iPad / Watch
|
|
270
|
+
│ │ └─ en-US/
|
|
271
|
+
│ │ ├─ iphone69_01.png
|
|
272
|
+
│ │ ├─ iphone69_02.png
|
|
273
|
+
│ │ └─ ipad13_01.png
|
|
274
|
+
│ └─ screenshots-macos/ ← Mac only
|
|
275
|
+
│ └─ en-US/
|
|
276
|
+
│ └─ macos_01.png
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**Screenshot file names matter.** The part **before the first underscore** tells vydanne which device the
|
|
280
|
+
image is for. Anything after it is yours — but files upload in alphabetical order, so number them
|
|
281
|
+
`_01`, `_02`, …
|
|
282
|
+
|
|
283
|
+
| Prefix | Device slot |
|
|
284
|
+
|---|---|
|
|
285
|
+
| `iphone69_` | iPhone 6.9″ |
|
|
286
|
+
| `iphone65_` | iPhone 6.5″ |
|
|
287
|
+
| `ipad13_` | iPad Pro 12.9″ |
|
|
288
|
+
| `watch_` | Apple Watch Ultra |
|
|
289
|
+
| `macos_` | Mac (in `screenshots-macos/`) |
|
|
290
|
+
|
|
291
|
+
A file whose prefix isn't in that table is **silently skipped** — if a screenshot doesn't appear, check
|
|
292
|
+
the name first.
|
|
293
|
+
|
|
294
|
+
> **Screenshots must be RGB PNGs with no transparency.** Simulator captures often have an alpha channel
|
|
295
|
+
> and Apple rejects those. `npx vydanne iap` with `VYDANNE_FLATTEN=path/to.png` converts one for you.
|
|
296
|
+
|
|
297
|
+
**Google Play** uses its **own** language codes (`de-DE`, `zh-CN`, `iw-IL`, `ar` — *not* Apple's
|
|
298
|
+
`zh-Hans`/`he`), and only three text files:
|
|
299
|
+
|
|
300
|
+
```
|
|
301
|
+
fastlane/metadata/android/
|
|
302
|
+
└─ en-US/
|
|
303
|
+
├─ title.txt (≤30)
|
|
304
|
+
├─ short_description.txt (≤80)
|
|
305
|
+
└─ full_description.txt (≤4000)
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Play images are read from **fixed paths** (they're where [zdymak](https://www.npmjs.com/package/zdymak)
|
|
309
|
+
writes them). Each is uploaded only if the file exists, so a missing set never wipes what's live:
|
|
310
|
+
|
|
311
|
+
| Play asset | Path vydanne reads |
|
|
312
|
+
|---|---|
|
|
313
|
+
| Store icon (512×512) | `brand/icons/play/icon-512.png` |
|
|
314
|
+
| Feature graphic (1024×500) | `marketing/out/play-feature-graphic.png` |
|
|
315
|
+
| Phone screenshots | `marketing/out/play-phone-plain/` |
|
|
316
|
+
| 7″ tablet screenshots | `marketing/out/play-tablet7-plain/` |
|
|
317
|
+
| 10″ tablet screenshots | `marketing/out/play-tablet-plain/` |
|
|
318
|
+
|
|
319
|
+
*(These paths are not configurable yet — create the folders at those locations, or symlink them.)*
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## 7. Optional extras
|
|
324
|
+
|
|
325
|
+
**App Preview videos** — add them to your config, then `npx vydanne previews`:
|
|
326
|
+
|
|
327
|
+
```js
|
|
328
|
+
previews: [
|
|
329
|
+
{ platform: "IOS", type: "IPHONE_67", file: "marketing/out/preview.mp4",
|
|
330
|
+
poster: "00:00:05:00", locales: ["en-US"] },
|
|
331
|
+
],
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
`poster` is the still frame shown before playback, as `HH:MM:SS:FF`. A locale that already has a preview
|
|
335
|
+
is skipped; set `VYDANNE_REPLACE=1` to swap in a new one.
|
|
336
|
+
|
|
337
|
+
**In-app purchases** — list them under `iaps` and run `npx vydanne iap` to validate the character limits
|
|
338
|
+
before you paste them into App Store Connect.
|
|
339
|
+
|
|
340
|
+
**Export compliance** — `npx vydanne compliance` writes the US self-classification PDF to
|
|
341
|
+
`export-compliance/`. Attach it in App Store Connect. If you set `france: true`, remember France needs a
|
|
342
|
+
separate ANSSI declaration.
|
|
343
|
+
|
|
344
|
+
---
|
|
345
|
+
|
|
346
|
+
## 8. Push it to the store
|
|
347
|
+
|
|
348
|
+
Always look before you leap:
|
|
349
|
+
|
|
350
|
+
```sh
|
|
351
|
+
npx vydanne preflight # is anything missing or over a character limit?
|
|
352
|
+
npx vydanne diff # exactly what would change vs what's live now
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
`preflight` must be **green**. Then:
|
|
356
|
+
|
|
357
|
+
```sh
|
|
358
|
+
npx vydanne fill # text + screenshots (iOS and Mac both)
|
|
359
|
+
npx vydanne previews # App Preview videos
|
|
360
|
+
npx vydanne age-rating
|
|
361
|
+
npx vydanne review-contact
|
|
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
|
+
```
|
|
365
|
+
|
|
366
|
+
**For Google Play**, `fill` is a **dry run by default** — it validates and throws the change away so a
|
|
367
|
+
half-finished local folder can't overwrite your live listing:
|
|
368
|
+
|
|
369
|
+
```sh
|
|
370
|
+
# macOS / Linux
|
|
371
|
+
npx vydanne fill --store google # dry run: shows what would happen
|
|
372
|
+
VYDANNE_COMMIT=1 npx vydanne fill --store google # actually commit it
|
|
373
|
+
```
|
|
374
|
+
```powershell
|
|
375
|
+
# Windows PowerShell — set the variable first; the `VAR=1 command` form does NOT work here
|
|
376
|
+
npx vydanne fill --store google
|
|
377
|
+
$env:VYDANNE_COMMIT = "1"; npx vydanne fill --store google
|
|
378
|
+
Remove-Item Env:\VYDANNE_COMMIT # clear it so later runs stay dry
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
Finally, run `npx vydanne diff` once more and eyeball one screenshot per platform in the web UI.
|
|
382
|
+
|
|
383
|
+
---
|
|
384
|
+
|
|
385
|
+
## 9. Finish in the browser (the parts no API can do)
|
|
386
|
+
|
|
387
|
+
1. **App Privacy** (Apple) — Apple's privacy API isn't reachable with an API key, so `vydanne privacy`
|
|
388
|
+
prints the exact answers; you paste them into App Store Connect by hand.
|
|
389
|
+
2. **Upload the binary** — Xcode, Transporter, or your CI. vydanne never touches your build.
|
|
390
|
+
3. **Play Data Safety + content rating** — questionnaires in the Play Console.
|
|
391
|
+
4. **Submit for Review** — yours to press.
|
|
392
|
+
|
|
393
|
+
---
|
|
394
|
+
|
|
395
|
+
## Troubleshooting
|
|
396
|
+
|
|
397
|
+
| What you see | What it means | Fix |
|
|
398
|
+
|---|---|---|
|
|
399
|
+
| `config not found at …` | You're in the wrong folder, or the file is misnamed. | `cd` to your project root; the file must be `vydanne.config.mjs`. |
|
|
400
|
+
| `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
|
+
| `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
|
+
| `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` | There's no version in an editable state. | In App Store Connect, create the next version (e.g. "1.0 Prepare for Submission"). |
|
|
404
|
+
| 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. vydanne never overwrites a set that already has screenshots. |
|
|
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:VYDANNE_COMMIT = "1"`, then run the command. |
|
|
408
|
+
| `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 | `fill --store google` is dry by default. | Re-run with `VYDANNE_COMMIT=1`. |
|
|
410
|
+
| `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
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
## Reference: environment variables
|
|
415
|
+
|
|
416
|
+
| Variable | Purpose |
|
|
417
|
+
|---|---|
|
|
418
|
+
| `ASC_KEY_ID`, `ASC_ISSUER_ID` | Apple API key identifiers (**required**). |
|
|
419
|
+
| `PLAY_JSON_KEY_FILE` | Path to the Play service-account JSON (**required for Play**). |
|
|
420
|
+
| `VYDANNE_CONFIG` | Use a different config file (same as `--config`). |
|
|
421
|
+
| `VYDANNE_SKIP_METADATA` / `VYDANNE_SKIP_SCREENSHOTS` | `fill`: push only one half. |
|
|
422
|
+
| `VYDANNE_COMMIT=1` | `fill --store google`: actually commit (otherwise dry). |
|
|
423
|
+
| `VYDANNE_REPLACE=1` | `previews`: delete the existing preview and upload a new one. |
|
|
424
|
+
| `VYDANNE_FLATTEN=<png>` | `iap`: convert an image to RGB (removes transparency). |
|
|
425
|
+
| `VYDANNE_A11Y_PUBLISH=1` | `accessibility`: publish the labels (only once the app is live). |
|
|
426
|
+
|
|
427
|
+
Set them with `export NAME=value` on macOS/Linux, or `$env:NAME = "value"` in Windows PowerShell.
|
|
428
|
+
|
|
429
|
+
Secrets — the `.p8`, the Play JSON, and `fastlane/metadata/review_information/` — should **never** be
|
|
430
|
+
committed to git. Add them to your `.gitignore`.
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
Next: the [README](README.md) for the full command list and the store gotchas vydanne encodes.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 vydanne contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
# vydanne
|
|
2
|
+
|
|
3
|
+
**Fill in your app's App Store and Google Play listing — in every language — from files on your computer.**
|
|
4
|
+
|
|
5
|
+
Publishing an app means typing the same things into a web form over and over: a name, a subtitle, a
|
|
6
|
+
description, keywords, screenshots… once per language, twice per store, again for the Mac version. Miss a
|
|
7
|
+
box and the store quietly shows a blank page to half the world. Get a folder name wrong and the whole
|
|
8
|
+
upload fails.
|
|
9
|
+
|
|
10
|
+
vydanne does that typing for you, then **checks your work before Apple or Google does**.
|
|
11
|
+
|
|
12
|
+
> *выданне* (Belarusian) — "publishing".
|
|
13
|
+
|
|
14
|
+
<br>
|
|
15
|
+
|
|
16
|
+
## Is this for me?
|
|
17
|
+
|
|
18
|
+
**Yes, if** you have an app and you're tired of the store's web forms — especially in more than one
|
|
19
|
+
language. You do not need to be a programmer: you write text files, and run three commands.
|
|
20
|
+
|
|
21
|
+
**Probably not, if** you publish one app in one language and never change it. The web form is fine.
|
|
22
|
+
|
|
23
|
+
**You'll still need** an Apple Developer account (and/or a Play Console account) and someone to build the
|
|
24
|
+
actual app. vydanne handles the *listing*, not the software.
|
|
25
|
+
|
|
26
|
+
<br>
|
|
27
|
+
|
|
28
|
+
## The two halves of a release
|
|
29
|
+
|
|
30
|
+
vydanne has a sibling — **[zdymak](https://www.npmjs.com/package/zdymak)**. They split the job cleanly:
|
|
31
|
+
|
|
32
|
+
| | [zdymak](https://github.com/Lonli-Lokli/zdymak) 📸 | **vydanne** 📝 |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| Makes | The **pictures** — screenshots, App Preview videos, the Play feature graphic | The **words and the paperwork** — listing text, ratings, contacts, privacy, compliance |
|
|
35
|
+
| Ends with | Image and video files on disk | A listing filled in and verified, ready for a human to submit |
|
|
36
|
+
|
|
37
|
+
Use them together: zdymak produces the assets, vydanne uploads them alongside your text. Use vydanne
|
|
38
|
+
alone if you already have your screenshots.
|
|
39
|
+
|
|
40
|
+
<br>
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
You need **Node.js 20.9 or newer** ([nodejs.org](https://nodejs.org) — the LTS build). Then, in your app's
|
|
45
|
+
folder:
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
npm i -D vydanne
|
|
49
|
+
npx vydanne version
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Works on **macOS, Linux and Windows**. The examples below use macOS/Linux shell syntax; Windows
|
|
53
|
+
PowerShell equivalents are in [GETTING_STARTED.md](GETTING_STARTED.md) (`$env:NAME = "value"` instead of
|
|
54
|
+
`export`, and set variables on their own line — PowerShell has no `VAR=1 command` form).
|
|
55
|
+
|
|
56
|
+
<br>
|
|
57
|
+
|
|
58
|
+
## Quick start
|
|
59
|
+
|
|
60
|
+
**→ First time? Follow [GETTING_STARTED.md](GETTING_STARTED.md).** It walks through every click:
|
|
61
|
+
getting your Apple API key, setting up a Play service account, where each file goes, and what to do when
|
|
62
|
+
something errors. This section is the short version for people who've done it before.
|
|
63
|
+
|
|
64
|
+
**1. Get a key from Apple.** App Store Connect → Users and Access → Integrations → App Store Connect API.
|
|
65
|
+
Download the `.p8` (once only!), then:
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
mkdir -p ~/.appstoreconnect/private_keys
|
|
69
|
+
mv ~/Downloads/AuthKey_*.p8 ~/.appstoreconnect/private_keys/
|
|
70
|
+
export ASC_KEY_ID=… ASC_ISSUER_ID=…
|
|
71
|
+
```
|
|
72
|
+
```powershell
|
|
73
|
+
# Windows PowerShell
|
|
74
|
+
New-Item -ItemType Directory -Force "$env:USERPROFILE\.appstoreconnect\private_keys"
|
|
75
|
+
Move-Item "$env:USERPROFILE\Downloads\AuthKey_*.p8" "$env:USERPROFILE\.appstoreconnect\private_keys\"
|
|
76
|
+
$env:ASC_KEY_ID = "…"; $env:ASC_ISSUER_ID = "…"
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**2. Describe your app** in a `vydanne.config.mjs` file next to your project:
|
|
80
|
+
|
|
81
|
+
```js
|
|
82
|
+
export default {
|
|
83
|
+
bundleId: "com.example.myapp",
|
|
84
|
+
primaryLocale: "en-US",
|
|
85
|
+
platforms: ["IOS"],
|
|
86
|
+
uiLocales: ["en", "de", "fr"],
|
|
87
|
+
metadataDir: "fastlane/metadata",
|
|
88
|
+
rating: "4+",
|
|
89
|
+
};
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
**3. Write your listing** as plain text files, one folder per language:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
fastlane/metadata/en-US/name.txt ← "My App"
|
|
96
|
+
fastlane/metadata/en-US/subtitle.txt ← "The one-line pitch"
|
|
97
|
+
fastlane/metadata/en-US/description.txt
|
|
98
|
+
fastlane/metadata/en-US/keywords.txt
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**4. Check, then push:**
|
|
102
|
+
|
|
103
|
+
```sh
|
|
104
|
+
npx vydanne preflight # anything missing or too long?
|
|
105
|
+
npx vydanne diff # what exactly would change?
|
|
106
|
+
npx vydanne fill # do it
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Run vydanne **from your project folder** — it finds everything relative to where you are.
|
|
110
|
+
|
|
111
|
+
<br>
|
|
112
|
+
|
|
113
|
+
## What each command does
|
|
114
|
+
|
|
115
|
+
| Command | In plain English |
|
|
116
|
+
|---|---|
|
|
117
|
+
| `preflight` | **Run this first.** Checks the listing is complete and nothing is over a character limit. Green means submittable. |
|
|
118
|
+
| `diff` | Shows exactly what's different between your files and what's live. Nothing is changed — a safe preview. |
|
|
119
|
+
| `fill` | Uploads your listing text and screenshots. Handles iPhone, iPad and Mac. |
|
|
120
|
+
| `previews` | Uploads App Preview videos. |
|
|
121
|
+
| `inspect` | Shows the app's current state in the store. Read-only. |
|
|
122
|
+
| `locales` | Lists your languages and Apple's code for each — and warns about any language the App Store doesn't offer. |
|
|
123
|
+
| `age-rating` | Sets the age rating. |
|
|
124
|
+
| `review-contact` | Fills in the App Review contact details (who Apple calls if there's a problem). |
|
|
125
|
+
| `accessibility` | Saves Accessibility Nutrition Labels. Stays a draft until your app is live. |
|
|
126
|
+
| `privacy` | Prints the privacy answers to paste into Apple's website (Apple's privacy section has no API). |
|
|
127
|
+
| `iap` | Checks your in-app purchase text fits, and can strip transparency from an image. |
|
|
128
|
+
| `compliance` | Generates the US encryption self-classification PDF that Apple asks for. |
|
|
129
|
+
| `version` | Prints the version of vydanne. |
|
|
130
|
+
|
|
131
|
+
For **Google Play**, add `--store google` to `inspect`, `diff`, `preflight`, or `fill`.
|
|
132
|
+
|
|
133
|
+
<br>
|
|
134
|
+
|
|
135
|
+
## Google Play
|
|
136
|
+
|
|
137
|
+
Add a `google` block to your config and point `PLAY_JSON_KEY_FILE` at a service-account key
|
|
138
|
+
([how to get one](GETTING_STARTED.md#4-get-your-google-play-credentials-once-android-only)):
|
|
139
|
+
|
|
140
|
+
```sh
|
|
141
|
+
npx vydanne preflight --store google
|
|
142
|
+
npx vydanne fill --store google # dry run — shows what would change
|
|
143
|
+
VYDANNE_COMMIT=1 npx vydanne fill --store google # actually do it
|
|
144
|
+
# Windows PowerShell: $env:VYDANNE_COMMIT = "1"; npx vydanne fill --store google
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Play is dry by default on purpose.** Nothing goes live until you add `VYDANNE_COMMIT=1`, so a
|
|
148
|
+
half-finished folder can never overwrite a good listing. Play also uses its **own** language codes
|
|
149
|
+
(`zh-CN`, `iw-IL`) which are *not* Apple's — `vydanne locales` and the
|
|
150
|
+
[layout guide](GETTING_STARTED.md#6-put-your-text-and-images-where-vydanne-looks) keep them straight.
|
|
151
|
+
|
|
152
|
+
<br>
|
|
153
|
+
|
|
154
|
+
## Your config, field by field
|
|
155
|
+
|
|
156
|
+
`bundleId` · `primaryLocale` (your main language — everything else falls back to it, so it must be
|
|
157
|
+
complete) · `asc` (Apple key IDs, if you'd rather not use environment variables) · `platforms` (iOS and
|
|
158
|
+
macOS are **separate listings**) · `uiLocales` (your languages, auto-translated to Apple's codes) ·
|
|
159
|
+
`metadataDir` (where your text lives) · `rating` · `privacy` (what data actually leaves the device) ·
|
|
160
|
+
`iaps` (in-app purchases) · `previews` (App Preview videos) · `export` (encryption compliance details) ·
|
|
161
|
+
`google` (the Play block).
|
|
162
|
+
|
|
163
|
+
A commented example ships with the package: **[`vydanne.config.example.mjs`](vydanne.config.example.mjs)**.
|
|
164
|
+
|
|
165
|
+
<br>
|
|
166
|
+
|
|
167
|
+
## The mistakes it saves you from
|
|
168
|
+
|
|
169
|
+
Every one of these has cost somebody a rejected build or a blank store page. vydanne handles them so you
|
|
170
|
+
don't have to learn them the hard way.
|
|
171
|
+
|
|
172
|
+
| The trap | What vydanne does |
|
|
173
|
+
|---|---|
|
|
174
|
+
| A wrongly-named language folder aborts the **entire** upload | Maps your codes to Apple's and flags any language the store doesn't support |
|
|
175
|
+
| Your main language is left empty → most of the world sees a blank page | `preflight` refuses to pass |
|
|
176
|
+
| macOS is a **separate** listing; its text is not shared with iOS | Fills each platform independently |
|
|
177
|
+
| Apple's list endpoints return blank text, so tools "see" an empty listing | Reads each language individually |
|
|
178
|
+
| Screenshots with transparency get rejected | Converts them to RGB |
|
|
179
|
+
| Once a version is *Ready for Review*, most tools can no longer edit it | Uses a method that still works |
|
|
180
|
+
| Character limits (30 / 30 / 100 / 170; purchases 30 / 45) | Checked before upload, not after rejection |
|
|
181
|
+
| Apple's privacy section can't be reached by any API key | Prints the exact answers to paste in |
|
|
182
|
+
| Accessibility labels can't publish before launch | Saved as a draft automatically |
|
|
183
|
+
| In-app purchases need **two** different images, easily confused | Labels both slots |
|
|
184
|
+
| Play's language codes differ from Apple's | Documented and validated separately |
|
|
185
|
+
| A missing local screenshot folder deleting your live ones | Only uploads what exists — never deletes by omission |
|
|
186
|
+
|
|
187
|
+
<br>
|
|
188
|
+
|
|
189
|
+
## What vydanne will never do
|
|
190
|
+
|
|
191
|
+
- **It never submits your app.** A human attaches the build and presses Submit. That's on purpose.
|
|
192
|
+
- It doesn't build, sign, or upload your app binary.
|
|
193
|
+
- It doesn't create the app record — make that in App Store Connect / Play Console first.
|
|
194
|
+
|
|
195
|
+
<br>
|
|
196
|
+
|
|
197
|
+
## For developers & AI agents
|
|
198
|
+
|
|
199
|
+
The package ships **[SKILL.md](SKILL.md)** — a precise, agent-facing operating guide (exact file layout,
|
|
200
|
+
the store gotchas, the ASO rules) — plus TypeScript definitions
|
|
201
|
+
([`types/index.d.ts`](types/index.d.ts), type `import('vydanne').VydanneConfig`).
|
|
202
|
+
|
|
203
|
+
Two guards keep the docs honest: `npm run check:docs` (every config field and command is documented) and
|
|
204
|
+
`npm run check:types` (every one is typed). Both run before publish, so the docs can't drift from the code.
|
|
205
|
+
|
|
206
|
+
Technically: native Node, no fastlane, Ruby, or Python. ES256 JWT via `node:crypto`, the App Store Connect
|
|
207
|
+
REST API over `fetch`, chunked uploads by hand, PDFs via `pdfkit`, image flattening via `sharp`. Android
|
|
208
|
+
goes through the Google Play Developer **Edits** API.
|
|
209
|
+
|
|
210
|
+
Releasing a new version: [RELEASING.md](RELEASING.md).
|
|
211
|
+
|
|
212
|
+
MIT.
|