vydanne 0.9.0 → 0.10.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/README.md +14 -1
- package/SKILL.md +1 -0
- package/package.json +1 -1
- package/src/config.mjs +6 -0
- package/src/play/commands/prerelease.mjs +62 -4
- package/types/index.d.ts +9 -0
- package/vydanne.config.example.mjs +5 -0
package/README.md
CHANGED
|
@@ -218,6 +218,11 @@ Two details worth knowing:
|
|
|
218
218
|
> Upgrading from ≤ 0.5? The Apple half used to write immediately — `vydanne fill` now needs `--apply`.
|
|
219
219
|
> `VYDANNE_COMMIT=1` still works as an alias so existing Play scripts keep running, but prefer the flag.
|
|
220
220
|
|
|
221
|
+
`fill` takes four more overrides, all off by default: `VYDANNE_REPLACE=1` replaces a store screenshot
|
|
222
|
+
set instead of leaving the live one alone (what you want after re-rendering them), and
|
|
223
|
+
`VYDANNE_SKIP_METADATA=1` / `VYDANNE_SKIP_SCREENSHOTS=1` do one half of the job when the other is
|
|
224
|
+
already right. `VYDANNE_FLATTEN=1` reads a flat screenshot folder rather than per-locale ones.
|
|
225
|
+
|
|
221
226
|
<br>
|
|
222
227
|
|
|
223
228
|
## What each command does
|
|
@@ -379,7 +384,15 @@ Notes are truncated to Play's 500-char cap with a warning. The versionCode comes
|
|
|
379
384
|
manifest — vydanne reads it out of the `.aab` locally and reports which changelog file each locale
|
|
380
385
|
resolves to *before* the upload, so a wrong file costs a re-run, not a re-release. Re-uploading a used
|
|
381
386
|
code fails loudly instead of silently replacing a binary. Overrides: `VYDANNE_AAB`, `VYDANNE_TRACK`,
|
|
382
|
-
`VYDANNE_RELEASE_NAME
|
|
387
|
+
`VYDANNE_RELEASE_NAME`, `VYDANNE_STATUS`; and `VYDANNE_IPA` for the App Store half.
|
|
388
|
+
|
|
389
|
+
**An app that has never been published needs `releaseStatus: "draft"`.** Play calls it a *draft app*
|
|
390
|
+
and refuses a `completed` release on every track except `internal`, answering *"Only releases with
|
|
391
|
+
status draft may be created on draft app."* — which names neither the track nor the fix, and is why
|
|
392
|
+
the same bundle uploads to `internal` and fails on `alpha`. Set it in `google.releaseStatus` (or
|
|
393
|
+
`VYDANNE_STATUS=draft` for one run); the build then waits in Play Console for a person to start the
|
|
394
|
+
rollout, which is where an unpublished app's first one belongs. Remove it once the app is live.
|
|
395
|
+
vydanne explains this rejection rather than printing Play's version of it.
|
|
383
396
|
|
|
384
397
|
**Play is dry by default on purpose.** Nothing goes live until you add `--apply`, so a
|
|
385
398
|
half-finished folder can never overwrite a good listing. Play also uses its **own** language codes
|
package/SKILL.md
CHANGED
|
@@ -286,6 +286,7 @@ With `--store google` it uploads an `.aab` to a **closed testing track** with re
|
|
|
286
286
|
edit transaction. `production` is REFUSED — not flag-gated — so no argument combination ships to the
|
|
287
287
|
public; promoting the tested build stays a human's job, mirroring the Apple side never submitting. Track
|
|
288
288
|
comes from `google.track` / `VYDANNE_TRACK`, default `internal`; the bundle from `google.aab` /
|
|
289
|
+
- `VYDANNE_STATUS` — release status for `prerelease --store google`: `draft`, `inProgress`, `halted`, `completed` (default). Use `draft` for an app that has never been published: Play refuses a completed release on any track but `internal` until it is live, and says so in a message that names neither the track nor the fix. Also settable as `google.releaseStatus`.
|
|
289
290
|
`VYDANNE_AAB` (a directory takes its newest `.aab`). **For a PAID app use `internal`** — it's the only
|
|
290
291
|
track where testers install without buying. Notes follow supply's layout, per locale, first match wins:
|
|
291
292
|
`<google.metadataDir>/<play-locale>/changelogs/<versionCode>.txt` → `next.txt` → `default.txt`, capped
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vydanne",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "App Store Connect + Google Play submission prep — the companion to zdymak (media). Native Node (no fastlane/Ruby/Python): localized listings, screenshot/preview/icon upload, ratings, review contact, accessibility & privacy labels, IAP, export docs, build upload to TestFlight / a Play closed track, a diff of local-vs-store, and a preflight verifier that encodes the store gotchas. Never submits for review. iOS/macOS via the ASC REST API; Android via the Play Developer Edits API (--store google).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"app-store-connect",
|
package/src/config.mjs
CHANGED
|
@@ -98,6 +98,12 @@ export async function loadConfig(p) {
|
|
|
98
98
|
defaultLocale: raw.google.defaultLocale || raw.primaryLocale,
|
|
99
99
|
aab: raw.google.aab || null,
|
|
100
100
|
track: raw.google.track || "internal", // testing only — `prerelease` refuses production
|
|
101
|
+
// What state a new release is created in: draft | inProgress | halted | completed.
|
|
102
|
+
// Left null so `prerelease` keeps its own default ("completed"). The one case that needs
|
|
103
|
+
// it is an app that has never been published — Play calls that a "draft app" and refuses
|
|
104
|
+
// a completed release on any track but internal, so the first closed or open rollout has
|
|
105
|
+
// to be created as "draft" and started by a person in Console.
|
|
106
|
+
releaseStatus: raw.google.releaseStatus || null,
|
|
101
107
|
// Play image type -> local source path. Merged over the defaults so an app overrides only the
|
|
102
108
|
// slots whose layout differs; the default for `icon` points at znachok's output, which is a
|
|
103
109
|
// sensible default for this portfolio and a mystery to anyone else, so it is overridable.
|
|
@@ -92,13 +92,26 @@ export async function run(config, client) {
|
|
|
92
92
|
if (!notes) notes = readNotes(g.metadataDir, versionCode, g.defaultLocale);
|
|
93
93
|
|
|
94
94
|
// One complete release object: Play replaces the track's releases wholesale.
|
|
95
|
-
|
|
95
|
+
//
|
|
96
|
+
// The status is settable because a DRAFT APP — one that has never been published — refuses a
|
|
97
|
+
// "completed" release on any track but internal, with
|
|
98
|
+
// "Only releases with status draft may be created on draft app."
|
|
99
|
+
// which names neither the track nor the fix. Set VYDANNE_STATUS=draft for the first closed
|
|
100
|
+
// or open rollout of an app that is not live yet; the release then waits in Play Console for
|
|
101
|
+
// a human to start it, which is where an unpublished app's first rollout belongs anyway.
|
|
102
|
+
const status = process.env.VYDANNE_STATUS || g.releaseStatus || "completed";
|
|
103
|
+
if (!RELEASE_STATUSES.includes(status)) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
`release status "${status}" is not one Play accepts — use one of ${RELEASE_STATUSES.join(", ")}`,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
const release = { status, versionCodes: [String(versionCode)] };
|
|
96
109
|
if (notes.entries.length) release.releaseNotes = notes.entries;
|
|
97
110
|
const name = process.env.VYDANNE_RELEASE_NAME;
|
|
98
111
|
if (name) release.name = name;
|
|
99
112
|
|
|
100
113
|
const put = await client.putTrack(editId, track, [release]);
|
|
101
|
-
if (put.status >= 300) throw new Error(
|
|
114
|
+
if (put.status >= 300) throw new Error(explainPlayError("tracks.update", put, track, status));
|
|
102
115
|
|
|
103
116
|
if (client.dryRun) {
|
|
104
117
|
await client.deleteEdit(editId);
|
|
@@ -106,8 +119,17 @@ export async function run(config, client) {
|
|
|
106
119
|
return true;
|
|
107
120
|
}
|
|
108
121
|
const res = await client.commit(editId);
|
|
109
|
-
if (res.status >= 300) throw new Error(
|
|
110
|
-
|
|
122
|
+
if (res.status >= 300) throw new Error(explainPlayError("edits.commit", res, track, status));
|
|
123
|
+
// "live" is only true of a release that has actually started. A draft one is uploaded and
|
|
124
|
+
// waiting, and telling somebody it is live is how a build sits unnoticed for a week.
|
|
125
|
+
console.log(
|
|
126
|
+
status === "draft"
|
|
127
|
+
? green(`\n committed — versionCode ${versionCode} is on "${track}" as a DRAFT release.`)
|
|
128
|
+
: green(`\n committed — versionCode ${versionCode} is live on "${track}".`),
|
|
129
|
+
);
|
|
130
|
+
if (status === "draft") {
|
|
131
|
+
console.log(` Nobody has it yet: open Play Console and start the rollout when you are ready.`);
|
|
132
|
+
}
|
|
111
133
|
archiveNextNotes(notes, versionCode);
|
|
112
134
|
console.log(" Production stays manual: promote it in Play Console when you're ready.");
|
|
113
135
|
return true;
|
|
@@ -209,3 +231,39 @@ function archiveNextNotes(notes, versionCode) {
|
|
|
209
231
|
}
|
|
210
232
|
}
|
|
211
233
|
}
|
|
234
|
+
|
|
235
|
+
/** The release statuses Play's Publishing API accepts. A typo here costs a round trip otherwise. */
|
|
236
|
+
const RELEASE_STATUSES = ["draft", "inProgress", "halted", "completed"];
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Play's rejection, plus what to do about it.
|
|
240
|
+
*
|
|
241
|
+
* One rejection is worth translating rather than printing. An app that has never been published is
|
|
242
|
+
* a "draft app", and Play will not accept a `completed` release on any track except internal:
|
|
243
|
+
*
|
|
244
|
+
* Only releases with status draft may be created on draft app.
|
|
245
|
+
*
|
|
246
|
+
* That sentence names neither the track it is talking about nor the setting that fixes it, and it
|
|
247
|
+
* arrives identically from the track update and from the commit — so the same upload succeeds on
|
|
248
|
+
* `internal` and fails on `alpha` with a message that suggests nothing about tracks at all. The
|
|
249
|
+
* fix is one setting, and it belongs in the error rather than in somebody's memory.
|
|
250
|
+
*/
|
|
251
|
+
function explainPlayError(where, res, track, status) {
|
|
252
|
+
const body = JSON.stringify(res.json).slice(0, 300);
|
|
253
|
+
const message = res.json?.error?.message || "";
|
|
254
|
+
if (/draft app/i.test(message)) {
|
|
255
|
+
return [
|
|
256
|
+
`${where} ${res.status}: ${message}`,
|
|
257
|
+
"",
|
|
258
|
+
` This app has never been published, so Play calls it a draft app — and a draft app only`,
|
|
259
|
+
` accepts releases whose status is "draft". You asked for "${status}" on track "${track}".`,
|
|
260
|
+
"",
|
|
261
|
+
` Set it once in your config: google: { releaseStatus: "draft" }`,
|
|
262
|
+
` Or for this run only: VYDANNE_STATUS=draft`,
|
|
263
|
+
"",
|
|
264
|
+
` The build then waits in Play Console for a person to start the rollout, which is where an`,
|
|
265
|
+
` unpublished app's first one belongs. Remove the setting once the app is live.`,
|
|
266
|
+
].join("\n");
|
|
267
|
+
}
|
|
268
|
+
return `${where} ${res.status}: ${body}`;
|
|
269
|
+
}
|
package/types/index.d.ts
CHANGED
|
@@ -139,6 +139,15 @@ export interface GoogleConfig {
|
|
|
139
139
|
* track you created in Play Console. Only 'production' is refused — that release is a human's.
|
|
140
140
|
*/
|
|
141
141
|
track?: string;
|
|
142
|
+
/**
|
|
143
|
+
* What state a new release is created in. Defaults to 'completed' — the rollout starts on upload.
|
|
144
|
+
*
|
|
145
|
+
* Set 'draft' for an app that has NEVER been published: Play calls that a "draft app" and refuses
|
|
146
|
+
* a completed release on any track but internal, with a message that names neither the track nor
|
|
147
|
+
* the fix. A draft release waits in Play Console for a person to start it. Remove once live.
|
|
148
|
+
* Override: VYDANNE_STATUS.
|
|
149
|
+
*/
|
|
150
|
+
releaseStatus?: "draft" | "inProgress" | "halted" | "completed";
|
|
142
151
|
/**
|
|
143
152
|
* Play image type -> local source path. Merged over the defaults, so declare only what differs.
|
|
144
153
|
* A type whose source does not exist is skipped; a missing local set never deletes the live one.
|
|
@@ -100,6 +100,11 @@ export default {
|
|
|
100
100
|
// 'internal' (default), 'alpha', 'beta', or the name of any closed track you made in Play Console.
|
|
101
101
|
// Only 'production' is refused.
|
|
102
102
|
// track: "internal",
|
|
103
|
+
// What state a new release is created in: "draft" | "inProgress" | "halted" | "completed".
|
|
104
|
+
// Defaults to "completed" — the rollout starts on upload. An app that has NEVER been published
|
|
105
|
+
// is a "draft app" to Play, which refuses a completed release on any track but internal; set
|
|
106
|
+
// "draft" until it is live and start the rollout yourself in Console. Override: VYDANNE_STATUS.
|
|
107
|
+
// releaseStatus: "draft",
|
|
103
108
|
// Play image type -> local source. Merged over the defaults, so override only what differs.
|
|
104
109
|
// images: { icon: "brand/icons/play/icon-512.png", phoneScreenshots: "marketing/out/play-phone-plain" },
|
|
105
110
|
// Play holds graphics PER LANGUAGE. Default is one set at `defaultLocale`; list locales (or "*")
|