@gpc-cli/core 0.9.57 → 0.9.59
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 +4 -3
- package/dist/{chunk-QEM7QCBD.js → chunk-IZKB6GBS.js} +50 -29
- package/dist/chunk-IZKB6GBS.js.map +1 -0
- package/dist/index.d.ts +41 -2
- package/dist/index.js +254 -37
- package/dist/index.js.map +1 -1
- package/dist/{releases-LHYBPIUI.js → releases-VFDJ6IX2.js} +2 -2
- package/package.json +3 -3
- package/dist/chunk-QEM7QCBD.js.map +0 -1
- /package/dist/{releases-LHYBPIUI.js.map → releases-VFDJ6IX2.js.map} +0 -0
package/README.md
CHANGED
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
const result = await uploadRelease(context, {
|
|
27
27
|
file: "app.aab",
|
|
28
28
|
track: "internal",
|
|
29
|
+
validateOnly: true, // dry-run: server-side validation without committing
|
|
29
30
|
});
|
|
30
31
|
|
|
31
32
|
// Promote between tracks
|
|
@@ -37,7 +38,7 @@ await promoteRelease(context, {
|
|
|
37
38
|
|
|
38
39
|
// Check vitals
|
|
39
40
|
const vitals = await getVitalsOverview(context);
|
|
40
|
-
console.log(formatOutput(vitals, "table"));
|
|
41
|
+
console.log(formatOutput(vitals, "table")); // also: "json", "csv", "tsv"
|
|
41
42
|
|
|
42
43
|
// Analyze bundle size
|
|
43
44
|
const analysis = await analyzeBundle("./app.aab");
|
|
@@ -59,13 +60,13 @@ const analysis = await analyzeBundle("./app.aab");
|
|
|
59
60
|
| **Users** | `listUsers`, `inviteUser`, `updateUser`, `removeUser` |
|
|
60
61
|
| **Testers** | `listTesters`, `addTesters`, `removeTesters`, `importTestersFromCsv` |
|
|
61
62
|
| **Bundle** | `analyzeBundle`, `compareBundles` (zero-dependency AAB/APK size analysis) |
|
|
62
|
-
| **Publishing** | `publish` (end-to-end: upload + track + notes + commit)
|
|
63
|
+
| **Publishing** | `publish` (end-to-end: upload + track + notes + commit; supports `validateOnly` dry-run) |
|
|
63
64
|
| **Changelog** | `generateChangelog`, `fetchChangelog`, `formatChangelogEntry`, `buildLocaleBundle`, `renderPlayStore`, `renderMarkdown`, `renderJson`, `renderPrompt`, `translateBundle`, `resolveLocales` |
|
|
64
65
|
| **Validation** | `validateUploadFile`, `validateImage`, `validatePreSubmission` |
|
|
65
66
|
|
|
66
67
|
## Utilities
|
|
67
68
|
|
|
68
|
-
- **Output formatting**
|
|
69
|
+
- **Output formatting** - `formatOutput(data, format)` supports `"json"`, `"table"`, `"csv"`, `"tsv"`; plus `detectOutputFormat()`, `redactSensitive()`
|
|
69
70
|
- **Error hierarchy** — `GpcError`, `ConfigError`, `ApiError`, `NetworkError` with exit codes
|
|
70
71
|
- **Audit logging** — `initAudit()`, `writeAuditLog()` for write operation tracking
|
|
71
72
|
- **Path safety** — `safePath()`, `safePathWithin()` for path traversal prevention
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/commands/releases.ts
|
|
2
2
|
import { stat as stat2 } from "fs/promises";
|
|
3
3
|
import { extname as extname2 } from "path";
|
|
4
|
-
import { PlayApiError } from "@gpc-cli/api";
|
|
4
|
+
import { PlayApiError as PlayApiError2 } from "@gpc-cli/api";
|
|
5
5
|
|
|
6
6
|
// src/errors.ts
|
|
7
7
|
var GpcError = class extends Error {
|
|
@@ -128,6 +128,33 @@ function formatSize(bytes) {
|
|
|
128
128
|
return `${bytes} B`;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
// src/utils/edit-helpers.ts
|
|
132
|
+
import { PlayApiError } from "@gpc-cli/api";
|
|
133
|
+
async function commitWithRescue(client, packageName, editId, commitOptions) {
|
|
134
|
+
try {
|
|
135
|
+
await client.edits.commit(packageName, editId, commitOptions);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
if (error instanceof PlayApiError && error.code === "API_CHANGES_NOT_SENT_FOR_REVIEW" && !commitOptions?.changesNotSentForReview) {
|
|
138
|
+
process.emitWarning(
|
|
139
|
+
"App has a rejected update \u2014 auto-setting changesNotSentForReview=true",
|
|
140
|
+
"AutoRescueWarning"
|
|
141
|
+
);
|
|
142
|
+
await client.edits.commit(packageName, editId, {
|
|
143
|
+
...commitOptions,
|
|
144
|
+
changesNotSentForReview: true
|
|
145
|
+
});
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async function validateAndCommit(client, packageName, editId, commitOptions) {
|
|
152
|
+
if (!commitOptions?.changesNotSentForReview) {
|
|
153
|
+
await client.edits.validate(packageName, editId);
|
|
154
|
+
}
|
|
155
|
+
await commitWithRescue(client, packageName, editId, commitOptions);
|
|
156
|
+
}
|
|
157
|
+
|
|
131
158
|
// src/commands/releases.ts
|
|
132
159
|
var BUNDLE_POLL_BACKOFF = [2e3, 3e3, 5e3, 8e3, 13e3];
|
|
133
160
|
async function waitForBundleProcessing(client, packageName, editId, versionCode, backoff = BUNDLE_POLL_BACKOFF) {
|
|
@@ -148,7 +175,7 @@ async function withRetryOnConflict(client, packageName, operation) {
|
|
|
148
175
|
try {
|
|
149
176
|
return await operation(edit);
|
|
150
177
|
} catch (error) {
|
|
151
|
-
const isConflict = error instanceof
|
|
178
|
+
const isConflict = error instanceof PlayApiError2 && error.statusCode === 409;
|
|
152
179
|
if (!isConflict) {
|
|
153
180
|
await client.edits.delete(packageName, edit.id).catch(() => {
|
|
154
181
|
});
|
|
@@ -192,7 +219,7 @@ async function withFreshEdit(client, packageName, operation) {
|
|
|
192
219
|
try {
|
|
193
220
|
return await operation(edit.id);
|
|
194
221
|
} catch (error) {
|
|
195
|
-
if (error instanceof
|
|
222
|
+
if (error instanceof PlayApiError2 && error.code === "API_EDIT_EXPIRED") {
|
|
196
223
|
await client.edits.delete(packageName, edit.id).catch(() => {
|
|
197
224
|
});
|
|
198
225
|
const freshEdit = await client.edits.insert(packageName);
|
|
@@ -301,7 +328,17 @@ ${validation.errors.join("\n")}`,
|
|
|
301
328
|
if (!options.commitOptions?.changesNotSentForReview) {
|
|
302
329
|
await client.edits.validate(packageName, edit.id);
|
|
303
330
|
}
|
|
304
|
-
|
|
331
|
+
if (options.validateOnly) {
|
|
332
|
+
await client.edits.delete(packageName, edit.id).catch(() => {
|
|
333
|
+
});
|
|
334
|
+
return {
|
|
335
|
+
versionCode: bundle.versionCode,
|
|
336
|
+
track: options.track,
|
|
337
|
+
status: release.status,
|
|
338
|
+
validateOnly: true
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
await commitWithRescue(client, packageName, edit.id, options.commitOptions);
|
|
305
342
|
return {
|
|
306
343
|
versionCode: bundle.versionCode,
|
|
307
344
|
track: options.track,
|
|
@@ -366,10 +403,7 @@ async function promoteRelease(client, packageName, fromTrack, toTrack, options)
|
|
|
366
403
|
releaseNotes: options?.releaseNotes || currentRelease.releaseNotes || []
|
|
367
404
|
};
|
|
368
405
|
await client.tracks.update(packageName, edit.id, toTrack, release);
|
|
369
|
-
|
|
370
|
-
await client.edits.validate(packageName, edit.id);
|
|
371
|
-
}
|
|
372
|
-
await client.edits.commit(packageName, edit.id, options?.commitOptions);
|
|
406
|
+
await validateAndCommit(client, packageName, edit.id, options?.commitOptions);
|
|
373
407
|
return {
|
|
374
408
|
track: toTrack,
|
|
375
409
|
status: release.status,
|
|
@@ -435,10 +469,7 @@ async function updateRollout(client, packageName, track, action, userFraction, c
|
|
|
435
469
|
releaseNotes: currentRelease.releaseNotes || []
|
|
436
470
|
};
|
|
437
471
|
await client.tracks.update(packageName, edit.id, track, release);
|
|
438
|
-
|
|
439
|
-
await client.edits.validate(packageName, edit.id);
|
|
440
|
-
}
|
|
441
|
-
await client.edits.commit(packageName, edit.id, commitOptions);
|
|
472
|
+
await validateAndCommit(client, packageName, edit.id, commitOptions);
|
|
442
473
|
return {
|
|
443
474
|
track,
|
|
444
475
|
status: newStatus,
|
|
@@ -475,10 +506,7 @@ async function createTrack(client, packageName, trackName, commitOptions) {
|
|
|
475
506
|
const edit = await client.edits.insert(packageName);
|
|
476
507
|
try {
|
|
477
508
|
const track = await client.tracks.create(packageName, edit.id, trackName);
|
|
478
|
-
|
|
479
|
-
await client.edits.validate(packageName, edit.id);
|
|
480
|
-
}
|
|
481
|
-
await client.edits.commit(packageName, edit.id, commitOptions);
|
|
509
|
+
await validateAndCommit(client, packageName, edit.id, commitOptions);
|
|
482
510
|
return track;
|
|
483
511
|
} catch (error) {
|
|
484
512
|
await client.edits.delete(packageName, edit.id).catch(() => {
|
|
@@ -511,10 +539,7 @@ async function updateTrackConfig(client, packageName, trackName, config, commitO
|
|
|
511
539
|
release.name = config["name"];
|
|
512
540
|
}
|
|
513
541
|
const track = await client.tracks.update(packageName, edit.id, trackName, release);
|
|
514
|
-
|
|
515
|
-
await client.edits.validate(packageName, edit.id);
|
|
516
|
-
}
|
|
517
|
-
await client.edits.commit(packageName, edit.id, commitOptions);
|
|
542
|
+
await validateAndCommit(client, packageName, edit.id, commitOptions);
|
|
518
543
|
return track;
|
|
519
544
|
} catch (error) {
|
|
520
545
|
await client.edits.delete(packageName, edit.id).catch(() => {
|
|
@@ -558,10 +583,7 @@ async function applyReleaseNotes(client, packageName, track, releaseNotes, commi
|
|
|
558
583
|
releaseNotes
|
|
559
584
|
};
|
|
560
585
|
await client.tracks.update(packageName, edit.id, track, patched);
|
|
561
|
-
|
|
562
|
-
await client.edits.validate(packageName, edit.id);
|
|
563
|
-
}
|
|
564
|
-
await client.edits.commit(packageName, edit.id, commitOptions);
|
|
586
|
+
await validateAndCommit(client, packageName, edit.id, commitOptions);
|
|
565
587
|
return {
|
|
566
588
|
track,
|
|
567
589
|
versionCodes: draft.versionCodes || [],
|
|
@@ -616,10 +638,7 @@ async function uploadExternallyHosted(client, packageName, data, commitOptions)
|
|
|
616
638
|
const edit = await client.edits.insert(packageName);
|
|
617
639
|
try {
|
|
618
640
|
const result = await client.apks.addExternallyHosted(packageName, edit.id, data);
|
|
619
|
-
|
|
620
|
-
await client.edits.validate(packageName, edit.id);
|
|
621
|
-
}
|
|
622
|
-
await client.edits.commit(packageName, edit.id, commitOptions);
|
|
641
|
+
await validateAndCommit(client, packageName, edit.id, commitOptions);
|
|
623
642
|
return result;
|
|
624
643
|
} catch (error) {
|
|
625
644
|
await client.edits.delete(packageName, edit.id).catch(() => {
|
|
@@ -634,6 +653,8 @@ export {
|
|
|
634
653
|
ApiError,
|
|
635
654
|
NetworkError,
|
|
636
655
|
validateUploadFile,
|
|
656
|
+
commitWithRescue,
|
|
657
|
+
validateAndCommit,
|
|
637
658
|
waitForBundleProcessing,
|
|
638
659
|
withFreshEdit,
|
|
639
660
|
uploadRelease,
|
|
@@ -648,4 +669,4 @@ export {
|
|
|
648
669
|
diffReleases,
|
|
649
670
|
uploadExternallyHosted
|
|
650
671
|
};
|
|
651
|
-
//# sourceMappingURL=chunk-
|
|
672
|
+
//# sourceMappingURL=chunk-IZKB6GBS.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/releases.ts","../src/errors.ts","../src/utils/file-validation.ts","../src/utils/edit-helpers.ts"],"sourcesContent":["import { stat } from \"node:fs/promises\";\nimport { extname } from \"node:path\";\nimport type {\n PlayApiClient,\n Release,\n Track,\n ExternallyHostedApk,\n ExternallyHostedApkResponse,\n UploadProgressEvent,\n ResumableUploadOptions,\n EditCommitOptions,\n DeobfuscationFileType,\n} from \"@gpc-cli/api\";\nimport type { AppEdit } from \"@gpc-cli/api\";\nimport { PlayApiError } from \"@gpc-cli/api\";\nimport { GpcError } from \"../errors.js\";\nimport { validateUploadFile } from \"../utils/file-validation.js\";\nimport { validateAndCommit, commitWithRescue } from \"../utils/edit-helpers.js\";\n\nconst BUNDLE_POLL_BACKOFF = [2_000, 3_000, 5_000, 8_000, 13_000];\n\nexport async function waitForBundleProcessing(\n client: PlayApiClient,\n packageName: string,\n editId: string,\n versionCode: number,\n backoff: number[] = BUNDLE_POLL_BACKOFF,\n): Promise<void> {\n for (let i = 0; i < backoff.length; i++) {\n const bundles = await client.bundles.list(packageName, editId);\n if (bundles.some((b) => b.versionCode === versionCode)) return;\n await new Promise((r) => setTimeout(r, backoff[i]));\n }\n throw new GpcError(\n `Bundle versionCode ${versionCode} not ready after ${backoff.length} poll attempts (~${Math.round(backoff.reduce((a, b) => a + b, 0) / 1000)}s)`,\n \"BUNDLE_PROCESSING_TIMEOUT\",\n 4,\n \"The AAB is still being processed by Google. Retry the upload, or use --status draft and commit later.\",\n );\n}\n\n/**\n * Retry an edit-based operation once if it fails with 409 Conflict (stale edit).\n * Automatically discards the stale edit and creates a fresh one on retry.\n */\nasync function withRetryOnConflict<T>(\n client: PlayApiClient,\n packageName: string,\n operation: (edit: AppEdit) => Promise<T>,\n): Promise<T> {\n const edit = await client.edits.insert(packageName);\n try {\n return await operation(edit);\n } catch (error) {\n const isConflict = error instanceof PlayApiError && error.statusCode === 409;\n if (!isConflict) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n // Discard stale edit, retry with fresh one\n await client.edits.delete(packageName, edit.id).catch(() => {});\n const freshEdit = await client.edits.insert(packageName);\n try {\n return await operation(freshEdit);\n } catch (retryError) {\n await client.edits.delete(packageName, freshEdit.id).catch(() => {});\n throw retryError;\n }\n }\n}\n\n/** Warn if edit is within 5 minutes of expiry. */\nlet _consoleEditWarningShown = false;\nfunction warnAboutConcurrentEdits(): void {\n if (_consoleEditWarningShown) return;\n _consoleEditWarningShown = true;\n process.emitWarning?.(\n \"If the Play Console has pending changes, they may be discarded when this edit is committed. \" +\n \"Avoid making changes in the Play Console while CLI operations are in progress.\",\n \"ConcurrentEditWarning\",\n );\n}\n\nfunction warnIfEditExpiring(edit: AppEdit): void {\n if (!edit.expiryTimeSeconds) return;\n const expiryMs = Number(edit.expiryTimeSeconds) * 1000;\n const remainingMs = expiryMs - Date.now();\n if (remainingMs < 5 * 60 * 1000 && remainingMs > 0) {\n const minutes = Math.round(remainingMs / 60_000);\n process.emitWarning?.(\n `Edit session expires in ~${minutes} minute${minutes !== 1 ? \"s\" : \"\"}. Long uploads may fail. Consider starting a fresh operation.`,\n \"EditExpiryWarning\",\n );\n }\n}\n\n/**\n * Run an edit-lifecycle operation with automatic retry on expired-edit errors.\n * If the API returns API_EDIT_EXPIRED (FAILED_PRECONDITION), the helper opens a\n * fresh edit and retries the operation exactly once.\n */\nexport async function withFreshEdit<T>(\n client: PlayApiClient,\n packageName: string,\n operation: (editId: string) => Promise<T>,\n): Promise<T> {\n const edit = await client.edits.insert(packageName);\n try {\n return await operation(edit.id);\n } catch (error) {\n if (error instanceof PlayApiError && error.code === \"API_EDIT_EXPIRED\") {\n // Discard stale edit (best effort) and retry with a fresh one\n await client.edits.delete(packageName, edit.id).catch(() => {});\n const freshEdit = await client.edits.insert(packageName);\n try {\n return await operation(freshEdit.id);\n } catch (retryError) {\n await client.edits.delete(packageName, freshEdit.id).catch(() => {});\n throw retryError;\n }\n }\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport interface UploadResult {\n versionCode: number;\n track: string;\n status: string;\n validateOnly?: true;\n}\n\nexport interface ReleaseStatusResult {\n track: string;\n status: string;\n versionCodes: string[];\n userFraction?: number;\n releaseNotes?: { language: string; text: string }[];\n}\n\nexport interface DryRunUploadResult {\n dryRun: true;\n file: { path: string; valid: boolean; errors: string[]; warnings: string[] };\n track: string;\n currentReleases: { versionCodes: string[]; status: string; userFraction?: number }[];\n plannedRelease: { status: string; userFraction?: number };\n}\n\nexport async function uploadRelease(\n client: PlayApiClient,\n packageName: string,\n filePath: string,\n options: {\n track: string;\n status?: string;\n userFraction?: number;\n releaseNotes?: { language: string; text: string }[];\n releaseName?: string;\n mappingFile?: string;\n mappingFileType?: DeobfuscationFileType;\n dryRun?: boolean;\n validateOnly?: boolean;\n onProgress?: (uploaded: number, total: number) => void;\n onUploadProgress?: (event: UploadProgressEvent) => void;\n uploadOptions?: Pick<\n ResumableUploadOptions,\n \"chunkSize\" | \"resumeSessionUri\" | \"maxResumeAttempts\"\n >;\n deviceTierConfigId?: string;\n commitOptions?: EditCommitOptions;\n },\n): Promise<UploadResult | DryRunUploadResult> {\n // Validate file before upload\n const validation = await validateUploadFile(filePath);\n\n if (options.dryRun) {\n const plannedStatus = options.status || (options.userFraction ? \"inProgress\" : \"completed\");\n\n // Fetch current track state without modifying anything\n let currentReleases: DryRunUploadResult[\"currentReleases\"] = [];\n const edit = await client.edits.insert(packageName);\n try {\n const trackData = await client.tracks.get(packageName, edit.id, options.track);\n currentReleases = (trackData.releases || []).map((r) => ({\n versionCodes: r.versionCodes || [],\n status: r.status,\n ...(r.userFraction !== undefined && { userFraction: r.userFraction }),\n }));\n } catch {\n // Track may not exist yet — that's fine for dry-run\n } finally {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n }\n\n return {\n dryRun: true,\n file: {\n path: filePath,\n valid: validation.valid,\n errors: validation.errors,\n warnings: validation.warnings,\n },\n track: options.track,\n currentReleases,\n plannedRelease: {\n status: plannedStatus,\n ...(options.userFraction !== undefined && { userFraction: options.userFraction }),\n },\n };\n }\n\n if (!validation.valid) {\n throw new GpcError(\n `File validation failed:\\n${validation.errors.join(\"\\n\")}`,\n \"RELEASE_INVALID_FILE\",\n 2,\n \"Check that the file is a valid AAB or APK and is not corrupted.\",\n );\n }\n\n // Get file size for progress reporting\n let fileSize = 0;\n try {\n const { size } = await stat(filePath);\n fileSize = size;\n } catch {\n /* ignore — file was validated above */\n }\n\n if (options.onProgress) options.onProgress(0, fileSize);\n\n const edit = await client.edits.insert(packageName);\n warnIfEditExpiring(edit);\n warnAboutConcurrentEdits();\n try {\n // Upload AAB or APK via the appropriate endpoint\n const isApk = extname(filePath).toLowerCase() === \".apk\";\n const uploadOpts = {\n ...options.uploadOptions,\n onProgress: (event: UploadProgressEvent) => {\n if (options.onProgress) options.onProgress(event.bytesUploaded, event.totalBytes);\n if (options.onUploadProgress) options.onUploadProgress(event);\n },\n };\n const bundle = isApk\n ? await client.apks.upload(packageName, edit.id, filePath, uploadOpts)\n : await client.bundles.upload(\n packageName,\n edit.id,\n filePath,\n uploadOpts,\n options.deviceTierConfigId,\n );\n\n // Wait for server-side AAB processing before proceeding.\n // Google's API returns from bundles.upload before manifest extraction\n // and signature verification finish, causing edits.validate to fail\n // with \"uploads are not completed yet\" on large bundles (~65MB+).\n if (!isApk) {\n await waitForBundleProcessing(client, packageName, edit.id, bundle.versionCode);\n }\n\n // Upload mapping file if provided\n if (options.mappingFile) {\n await client.deobfuscation.upload(\n packageName,\n edit.id,\n bundle.versionCode,\n options.mappingFile,\n options.mappingFileType,\n );\n }\n\n // Create release and assign to track\n const release: Release = {\n versionCodes: [String(bundle.versionCode)],\n status: (options.status ||\n (options.userFraction ? \"inProgress\" : \"completed\")) as Release[\"status\"],\n ...(options.userFraction && { userFraction: options.userFraction }),\n ...(options.releaseNotes && { releaseNotes: options.releaseNotes }),\n ...(options.releaseName && { name: options.releaseName }),\n };\n\n await client.tracks.update(packageName, edit.id, options.track, release);\n\n if (!options.commitOptions?.changesNotSentForReview) {\n await client.edits.validate(packageName, edit.id);\n }\n\n if (options.validateOnly) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n return {\n versionCode: bundle.versionCode,\n track: options.track,\n status: release.status,\n validateOnly: true,\n };\n }\n\n await commitWithRescue(client, packageName, edit.id, options.commitOptions);\n\n return {\n versionCode: bundle.versionCode,\n track: options.track,\n status: release.status,\n };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function getReleasesStatus(\n client: PlayApiClient,\n packageName: string,\n trackFilter?: string,\n): Promise<ReleaseStatusResult[]> {\n const edit = await client.edits.insert(packageName);\n try {\n const tracks = trackFilter\n ? [await client.tracks.get(packageName, edit.id, trackFilter)]\n : await client.tracks.list(packageName, edit.id);\n\n await client.edits.delete(packageName, edit.id);\n\n const results: ReleaseStatusResult[] = [];\n for (const track of tracks) {\n for (const release of track.releases || []) {\n results.push({\n track: track.track,\n status: release.status,\n versionCodes: release.versionCodes || [],\n userFraction: release.userFraction,\n releaseNotes: release.releaseNotes,\n });\n }\n }\n return results;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function promoteRelease(\n client: PlayApiClient,\n packageName: string,\n fromTrack: string,\n toTrack: string,\n options?: {\n status?: string;\n userFraction?: number;\n releaseNotes?: { language: string; text: string }[];\n commitOptions?: EditCommitOptions;\n },\n): Promise<ReleaseStatusResult> {\n // Validate inputs before opening an edit\n if (options?.userFraction && (options.userFraction <= 0 || options.userFraction > 1)) {\n throw new GpcError(\n \"Rollout percentage must be between 0 and 1 (e.g., 0.1 for 10%)\",\n \"RELEASE_INVALID_FRACTION\",\n 2,\n \"Use a decimal value like 0.1 for 10%, 0.5 for 50%, or 1.0 for 100%.\",\n );\n }\n\n return withRetryOnConflict(client, packageName, async (edit) => {\n // Get current release from source track\n const sourceTrack = await client.tracks.get(packageName, edit.id, fromTrack);\n const currentRelease = sourceTrack.releases?.find(\n (r) => r.status === \"completed\" || r.status === \"inProgress\",\n );\n\n if (!currentRelease) {\n throw new GpcError(\n `No active release found on track \"${fromTrack}\"`,\n \"RELEASE_NOT_FOUND\",\n 1,\n `Ensure there is a completed or in-progress release on the \"${fromTrack}\" track before promoting.`,\n );\n }\n\n const release: Release = {\n versionCodes: currentRelease.versionCodes,\n status: (options?.status ||\n (options?.userFraction ? \"inProgress\" : \"completed\")) as Release[\"status\"],\n ...(options?.userFraction && { userFraction: options.userFraction }),\n releaseNotes: options?.releaseNotes || currentRelease.releaseNotes || [],\n };\n\n await client.tracks.update(packageName, edit.id, toTrack, release);\n await validateAndCommit(client, packageName, edit.id, options?.commitOptions);\n\n return {\n track: toTrack,\n status: release.status,\n versionCodes: release.versionCodes,\n userFraction: release.userFraction,\n };\n });\n}\n\nexport async function updateRollout(\n client: PlayApiClient,\n packageName: string,\n track: string,\n action: \"increase\" | \"halt\" | \"resume\" | \"complete\",\n userFraction?: number,\n commitOptions?: EditCommitOptions,\n): Promise<ReleaseStatusResult> {\n const edit = await client.edits.insert(packageName);\n try {\n const trackData = await client.tracks.get(packageName, edit.id, track);\n const currentRelease = trackData.releases?.find(\n (r) => r.status === \"inProgress\" || r.status === \"halted\",\n );\n\n if (!currentRelease) {\n throw new GpcError(\n `No active rollout found on track \"${track}\"`,\n \"ROLLOUT_NOT_FOUND\",\n 1,\n `There is no in-progress or halted rollout on the \"${track}\" track. Start a staged rollout first with: gpc releases upload --track ${track} --status inProgress --fraction 0.1`,\n );\n }\n\n let newStatus: string;\n let newFraction: number | undefined;\n\n switch (action) {\n case \"increase\":\n if (!userFraction)\n throw new GpcError(\n \"--to <percentage> is required for rollout increase\",\n \"ROLLOUT_MISSING_FRACTION\",\n 2,\n \"Specify the target rollout percentage with --to, e.g.: gpc rollout increase --to 0.5\",\n );\n if (userFraction <= 0 || userFraction > 1) {\n throw new GpcError(\n \"Rollout percentage must be between 0 and 1 (e.g., 0.1 for 10%)\",\n \"RELEASE_INVALID_FRACTION\",\n 2,\n \"Use a decimal value like 0.1 for 10%, 0.5 for 50%, or 1.0 for 100%.\",\n );\n }\n newStatus = \"inProgress\";\n newFraction = userFraction;\n break;\n case \"halt\":\n newStatus = \"halted\";\n newFraction = currentRelease.userFraction;\n break;\n case \"resume\":\n newStatus = \"inProgress\";\n newFraction = currentRelease.userFraction;\n break;\n case \"complete\":\n newStatus = \"completed\";\n newFraction = undefined;\n break;\n }\n\n const release: Release = {\n versionCodes: currentRelease.versionCodes,\n status: newStatus as Release[\"status\"],\n ...(newFraction !== undefined && { userFraction: newFraction }),\n releaseNotes: currentRelease.releaseNotes || [],\n };\n\n await client.tracks.update(packageName, edit.id, track, release);\n await validateAndCommit(client, packageName, edit.id, commitOptions);\n\n return {\n track,\n status: newStatus,\n versionCodes: release.versionCodes,\n userFraction: newFraction,\n };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function listTracks(client: PlayApiClient, packageName: string): Promise<Track[]> {\n const edit = await client.edits.insert(packageName);\n try {\n const tracks = await client.tracks.list(packageName, edit.id);\n await client.edits.delete(packageName, edit.id);\n return tracks;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function createTrack(\n client: PlayApiClient,\n packageName: string,\n trackName: string,\n commitOptions?: EditCommitOptions,\n): Promise<Track> {\n if (!trackName || trackName.trim().length === 0) {\n throw new GpcError(\n \"Track name must not be empty\",\n \"TRACK_INVALID_NAME\",\n 2,\n \"Provide a valid custom track name, e.g.: gpc tracks create my-qa-track\",\n );\n }\n\n const edit = await client.edits.insert(packageName);\n try {\n const track = await client.tracks.create(packageName, edit.id, trackName);\n await validateAndCommit(client, packageName, edit.id, commitOptions);\n return track;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function updateTrackConfig(\n client: PlayApiClient,\n packageName: string,\n trackName: string,\n config: Record<string, unknown>,\n commitOptions?: EditCommitOptions,\n): Promise<Track> {\n if (!trackName || trackName.trim().length === 0) {\n throw new GpcError(\n \"Track name must not be empty\",\n \"TRACK_INVALID_NAME\",\n 2,\n \"Provide a valid track name.\",\n );\n }\n\n const edit = await client.edits.insert(packageName);\n try {\n const release: Release = {\n versionCodes: (config[\"versionCodes\"] as string[]) || [],\n status: ((config[\"status\"] as string) || \"completed\") as Release[\"status\"],\n };\n if (config[\"userFraction\"] !== undefined) {\n release.userFraction = config[\"userFraction\"] as number;\n }\n if (config[\"releaseNotes\"]) {\n release.releaseNotes = config[\"releaseNotes\"] as { language: string; text: string }[];\n }\n if (config[\"name\"]) {\n release.name = config[\"name\"] as string;\n }\n\n const track = await client.tracks.update(packageName, edit.id, trackName, release);\n await validateAndCommit(client, packageName, edit.id, commitOptions);\n return track;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\n/**\n * Fetch release notes from the latest active release on a given track.\n * Opens and discards an edit — read-only, no mutations.\n */\nexport async function fetchReleaseNotes(\n client: PlayApiClient,\n packageName: string,\n track: string,\n): Promise<{ language: string; text: string }[]> {\n const edit = await client.edits.insert(packageName);\n try {\n const trackData = await client.tracks.get(packageName, edit.id, track);\n const release =\n trackData.releases?.find((r) => r.status === \"completed\" || r.status === \"inProgress\") ??\n trackData.releases?.[0];\n\n if (!release) {\n throw new GpcError(\n `No release found on track \"${track}\" to copy notes from`,\n \"RELEASE_NOT_FOUND\",\n 1,\n `Ensure there is a release on the \"${track}\" track.`,\n );\n }\n\n return release.releaseNotes ?? [];\n } finally {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n }\n}\n\nexport interface ApplyReleaseNotesResult {\n track: string;\n versionCodes: string[];\n localeCount: number;\n releaseNotes: { language: string; text: string }[];\n}\n\nexport async function applyReleaseNotes(\n client: PlayApiClient,\n packageName: string,\n track: string,\n releaseNotes: { language: string; text: string }[],\n commitOptions?: EditCommitOptions,\n): Promise<ApplyReleaseNotesResult> {\n return withRetryOnConflict(client, packageName, async (edit) => {\n const trackData = await client.tracks.get(packageName, edit.id, track);\n const draft = trackData.releases?.find((r) => r.status === \"draft\");\n\n if (!draft) {\n throw new GpcError(\n `No draft release found on track \"${track}\"`,\n \"RELEASE_NO_DRAFT\",\n 1,\n `Upload an AAB/APK first to create a draft, or check the --track value. Current track: \"${track}\".`,\n );\n }\n\n const patched: Release = {\n ...draft,\n releaseNotes,\n };\n\n await client.tracks.update(packageName, edit.id, track, patched);\n await validateAndCommit(client, packageName, edit.id, commitOptions);\n\n return {\n track,\n versionCodes: draft.versionCodes || [],\n localeCount: releaseNotes.length,\n releaseNotes,\n };\n });\n}\n\nexport interface ReleaseDiff {\n field: string;\n track1Value: string;\n track2Value: string;\n}\n\nexport async function diffReleases(\n client: PlayApiClient,\n packageName: string,\n fromTrack: string,\n toTrack: string,\n): Promise<{ fromTrack: string; toTrack: string; diffs: ReleaseDiff[] }> {\n const edit = await client.edits.insert(packageName);\n try {\n const [fromData, toData] = await Promise.all([\n client.tracks.get(packageName, edit.id, fromTrack),\n client.tracks.get(packageName, edit.id, toTrack),\n ]);\n await client.edits.delete(packageName, edit.id);\n\n const fromRelease = fromData.releases?.[0];\n const toRelease = toData.releases?.[0];\n const diffs: ReleaseDiff[] = [];\n\n const fields = [\"versionCodes\", \"status\", \"userFraction\", \"releaseNotes\", \"name\"] as const;\n for (const field of fields) {\n const v1 = fromRelease ? JSON.stringify(fromRelease[field] ?? null) : \"null\";\n const v2 = toRelease ? JSON.stringify(toRelease[field] ?? null) : \"null\";\n if (v1 !== v2) {\n diffs.push({ field, track1Value: v1, track2Value: v2 });\n }\n }\n\n return { fromTrack, toTrack, diffs };\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n\nexport async function uploadExternallyHosted(\n client: PlayApiClient,\n packageName: string,\n data: ExternallyHostedApk,\n commitOptions?: EditCommitOptions,\n): Promise<ExternallyHostedApkResponse> {\n if (!data.externallyHostedUrl) {\n throw new GpcError(\n \"externallyHostedUrl is required\",\n \"EXTERNAL_APK_MISSING_URL\",\n 2,\n \"Provide a valid URL for the externally hosted APK.\",\n );\n }\n\n if (!data.packageName) {\n throw new GpcError(\n \"packageName is required in externally hosted APK data\",\n \"EXTERNAL_APK_MISSING_PACKAGE\",\n 2,\n \"Include the packageName field in the APK configuration.\",\n );\n }\n\n const edit = await client.edits.insert(packageName);\n try {\n const result = await client.apks.addExternallyHosted(packageName, edit.id, data);\n await validateAndCommit(client, packageName, edit.id, commitOptions);\n return result;\n } catch (error) {\n await client.edits.delete(packageName, edit.id).catch(() => {});\n throw error;\n }\n}\n","export class GpcError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n public readonly exitCode: number,\n public readonly suggestion?: string,\n ) {\n super(message);\n this.name = \"GpcError\";\n }\n\n toJSON() {\n return {\n success: false,\n error: {\n code: this.code,\n message: this.message,\n suggestion: this.suggestion,\n },\n };\n }\n}\n\nexport class ConfigError extends GpcError {\n constructor(message: string, code: string, suggestion?: string) {\n super(message, code, 1, suggestion);\n this.name = \"ConfigError\";\n }\n}\n\nexport class ApiError extends GpcError {\n constructor(\n message: string,\n code: string,\n public readonly statusCode?: number,\n suggestion?: string,\n ) {\n super(message, code, 4, suggestion);\n this.name = \"ApiError\";\n }\n}\n\nexport class NetworkError extends GpcError {\n constructor(message: string, suggestion?: string) {\n super(message, \"NETWORK_ERROR\", 5, suggestion);\n this.name = \"NetworkError\";\n }\n}\n","import { open, stat } from \"node:fs/promises\";\nimport { extname } from \"node:path\";\n\nexport interface FileValidationResult {\n valid: boolean;\n fileType: \"aab\" | \"apk\" | \"unknown\";\n sizeBytes: number;\n errors: string[];\n warnings: string[];\n}\n\n// ZIP magic bytes: PK\\x03\\x04\nconst ZIP_MAGIC = Buffer.from([0x50, 0x4b, 0x03, 0x04]);\n\nconst MAX_APK_SIZE = 1024 * 1024 * 1024; // 1 GB (Google Play API limit)\nconst MAX_AAB_SIZE = 2 * 1024 * 1024 * 1024; // 2 GB (Google Play API limit)\nconst LARGE_FILE_THRESHOLD = 100 * 1024 * 1024; // 100 MB — warn about upload time\n\nexport async function validateUploadFile(filePath: string): Promise<FileValidationResult> {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n // Check extension\n const ext = extname(filePath).toLowerCase();\n let fileType: FileValidationResult[\"fileType\"] = \"unknown\";\n\n if (ext === \".aab\") {\n fileType = \"aab\";\n } else if (ext === \".apk\") {\n fileType = \"apk\";\n } else {\n errors.push(`Unsupported file extension \"${ext}\". Expected .aab or .apk`);\n }\n\n // Check file exists and get size\n let sizeBytes: number;\n try {\n const stats = await stat(filePath);\n sizeBytes = stats.size;\n\n if (sizeBytes === 0) {\n errors.push(\"File is empty (0 bytes)\");\n }\n } catch {\n errors.push(`File not found: ${filePath}`);\n return { valid: false, fileType, sizeBytes: 0, errors, warnings };\n }\n\n // Check size limits\n if (fileType === \"apk\" && sizeBytes > MAX_APK_SIZE) {\n errors.push(\n `APK exceeds 1 GB limit (${formatSize(sizeBytes)}). Consider using AAB format instead.`,\n );\n }\n if (fileType === \"aab\" && sizeBytes > MAX_AAB_SIZE) {\n errors.push(`AAB exceeds 2 GB limit (${formatSize(sizeBytes)}).`);\n }\n\n if (sizeBytes > LARGE_FILE_THRESHOLD && errors.length === 0) {\n warnings.push(\n `Large file (${formatSize(sizeBytes)}). Upload may take a while on slow connections.`,\n );\n }\n\n // Check magic bytes — only read first 4 bytes, not the entire file\n if (sizeBytes > 0) {\n let fh;\n try {\n fh = await open(filePath, \"r\");\n const buf = Buffer.alloc(4);\n await fh.read(buf, 0, 4, 0);\n\n if (!buf.equals(ZIP_MAGIC)) {\n errors.push(\n \"File does not have valid ZIP magic bytes (PK\\\\x03\\\\x04). \" +\n \"Both AAB and APK files must be valid ZIP archives.\",\n );\n }\n } catch {\n errors.push(\"Unable to read file header for validation\");\n } finally {\n await fh?.close();\n }\n }\n\n return {\n valid: errors.length === 0,\n fileType,\n sizeBytes,\n errors,\n warnings,\n };\n}\n\nfunction formatSize(bytes: number): string {\n if (bytes >= 1024 * 1024 * 1024) {\n return `${(bytes / (1024 * 1024 * 1024)).toFixed(1)} GB`;\n }\n if (bytes >= 1024 * 1024) {\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n }\n if (bytes >= 1024) {\n return `${(bytes / 1024).toFixed(1)} KB`;\n }\n return `${bytes} B`;\n}\n","import type { PlayApiClient, EditCommitOptions } from \"@gpc-cli/api\";\nimport { PlayApiError } from \"@gpc-cli/api\";\n\nexport async function commitWithRescue(\n client: PlayApiClient,\n packageName: string,\n editId: string,\n commitOptions?: EditCommitOptions,\n): Promise<void> {\n try {\n await client.edits.commit(packageName, editId, commitOptions);\n } catch (error) {\n if (\n error instanceof PlayApiError &&\n error.code === \"API_CHANGES_NOT_SENT_FOR_REVIEW\" &&\n !commitOptions?.changesNotSentForReview\n ) {\n process.emitWarning(\n \"App has a rejected update — auto-setting changesNotSentForReview=true\",\n \"AutoRescueWarning\",\n );\n await client.edits.commit(packageName, editId, {\n ...commitOptions,\n changesNotSentForReview: true,\n });\n return;\n }\n throw error;\n }\n}\n\nexport async function validateAndCommit(\n client: PlayApiClient,\n packageName: string,\n editId: string,\n commitOptions?: EditCommitOptions,\n): Promise<void> {\n if (!commitOptions?.changesNotSentForReview) {\n await client.edits.validate(packageName, editId);\n }\n await commitWithRescue(client, packageName, editId, commitOptions);\n}\n"],"mappings":"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","names":["stat","extname","PlayApiError","PlayApiError","edit","stat","extname"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { OutputFormat, ResolvedConfig, WebhookConfig } from '@gpc-cli/config';
|
|
2
2
|
import { AuthClient } from '@gpc-cli/auth';
|
|
3
3
|
import { GpcPlugin, PluginManifest, CommandEvent, CommandResult, PluginError, RequestEvent, ResponseEvent, PluginCommand } from '@gpc-cli/plugin-sdk';
|
|
4
|
-
import { PlayApiClient, EditCommitOptions, Track, ExternallyHostedApk, ExternallyHostedApkResponse, DeobfuscationFileType, UploadProgressEvent, ResumableUploadOptions, Listing, ImageType, CountryAvailability, Image, AppDetails, Review, ReviewReplyResponse, Subscription, SubscriptionOffer, OffersListResponse, BasePlanMigratePricesRequest, InAppProduct, Order, SubscriptionDeferResponse, SubscriptionsV2DeferResponse, ProductPurchase, ProductPurchaseV2, SubscriptionPurchaseV2, VoidedPurchase, UsersApiClient, User, DeveloperLevelPermission, Grant, MetricRow, ReportingDimension, ReportingAggregation, VitalsMetricSet, ReportingApiClient, AnomalyDetectionResponse, MetricSetResponse, ErrorIssuesResponse, ConvertRegionPricesResponse, ReportType, StatsDimension, ReportBucket, Testers, AppRecoveryTargeting, AppRecoveryAction, CreateAppRecoveryActionRequest, DataSafety, ExternalTransaction, ExternalTransactionRefund, DeviceTierConfig, OneTimeOffer, OneTimeProduct, OneTimeOffersListResponse, OneTimeProductsListResponse, GamesApiClient, Achievement, GameEvent, Leaderboard, EnterpriseApiClient, CustomApp, GeneratedApk } from '@gpc-cli/api';
|
|
4
|
+
import { PlayApiClient, EditCommitOptions, Track, ExternallyHostedApk, ExternallyHostedApkResponse, DeobfuscationFileType, UploadProgressEvent, ResumableUploadOptions, Listing, ImageType, CountryAvailability, Image, AppDetails, Review, ReviewReplyResponse, Subscription, SubscriptionOffer, OffersListResponse, BasePlanMigratePricesRequest, InAppProduct, Order, SubscriptionDeferResponse, SubscriptionsV2DeferResponse, ProductPurchase, ProductPurchaseV2, SubscriptionPurchaseV2, VoidedPurchase, UsersApiClient, User, DeveloperLevelPermission, Grant, MetricRow, ReportingDimension, ReportingAggregation, VitalsMetricSet, ReportingApiClient, AnomalyDetectionResponse, MetricSetResponse, ErrorIssuesResponse, ConvertRegionPricesResponse, ReportType, StatsDimension, ReportBucket, Testers, AppRecoveryTargeting, AppRecoveryAction, CreateAppRecoveryActionRequest, DataSafety, ExternalTransaction, ExternalTransactionRefund, DeviceTierConfig, OneTimeOffer, OneTimeProduct, OneTimeOffersListResponse, OneTimeProductsListResponse, GamesApiClient, Achievement, GameEvent, Leaderboard, EnterpriseApiClient, CustomApp, GeneratedApk, Bundle } from '@gpc-cli/api';
|
|
5
5
|
|
|
6
6
|
declare class GpcError extends Error {
|
|
7
7
|
readonly code: string;
|
|
@@ -114,6 +114,7 @@ interface UploadResult {
|
|
|
114
114
|
versionCode: number;
|
|
115
115
|
track: string;
|
|
116
116
|
status: string;
|
|
117
|
+
validateOnly?: true;
|
|
117
118
|
}
|
|
118
119
|
interface ReleaseStatusResult {
|
|
119
120
|
track: string;
|
|
@@ -156,6 +157,7 @@ declare function uploadRelease(client: PlayApiClient, packageName: string, fileP
|
|
|
156
157
|
mappingFile?: string;
|
|
157
158
|
mappingFileType?: DeobfuscationFileType;
|
|
158
159
|
dryRun?: boolean;
|
|
160
|
+
validateOnly?: boolean;
|
|
159
161
|
onProgress?: (uploaded: number, total: number) => void;
|
|
160
162
|
onUploadProgress?: (event: UploadProgressEvent) => void;
|
|
161
163
|
uploadOptions?: Pick<ResumableUploadOptions, "chunkSize" | "resumeSessionUri" | "maxResumeAttempts">;
|
|
@@ -404,6 +406,7 @@ interface PublishOptions {
|
|
|
404
406
|
mappingFileType?: DeobfuscationFileType;
|
|
405
407
|
deviceTierConfigId?: string;
|
|
406
408
|
dryRun?: boolean;
|
|
409
|
+
validateOnly?: boolean;
|
|
407
410
|
commitOptions?: EditCommitOptions;
|
|
408
411
|
}
|
|
409
412
|
interface PublishResult {
|
|
@@ -1545,6 +1548,42 @@ declare function getKeystoreFingerprint(keystorePath: string, storePassword: str
|
|
|
1545
1548
|
declare function getApiSigningFingerprint(accessToken: string, packageName: string, apiHost?: string): Promise<ApiSigningFingerprint | null>;
|
|
1546
1549
|
declare function compareFingerprints(a: string, b: string): boolean;
|
|
1547
1550
|
|
|
1551
|
+
declare function commitWithRescue(client: PlayApiClient, packageName: string, editId: string, commitOptions?: EditCommitOptions): Promise<void>;
|
|
1552
|
+
declare function validateAndCommit(client: PlayApiClient, packageName: string, editId: string, commitOptions?: EditCommitOptions): Promise<void>;
|
|
1553
|
+
|
|
1554
|
+
declare function sha256File(filePath: string): Promise<string>;
|
|
1555
|
+
|
|
1556
|
+
interface ImageSyncOptions {
|
|
1557
|
+
lang?: string;
|
|
1558
|
+
type?: ImageType;
|
|
1559
|
+
delete?: boolean;
|
|
1560
|
+
dryRun?: boolean;
|
|
1561
|
+
commitOptions?: EditCommitOptions;
|
|
1562
|
+
}
|
|
1563
|
+
interface ImageSyncDetail {
|
|
1564
|
+
language: string;
|
|
1565
|
+
imageType: ImageType;
|
|
1566
|
+
file: string;
|
|
1567
|
+
action: "upload" | "skip" | "delete";
|
|
1568
|
+
reason?: string;
|
|
1569
|
+
}
|
|
1570
|
+
interface ImageSyncResult {
|
|
1571
|
+
uploaded: number;
|
|
1572
|
+
skipped: number;
|
|
1573
|
+
deleted: number;
|
|
1574
|
+
total: number;
|
|
1575
|
+
details: ImageSyncDetail[];
|
|
1576
|
+
}
|
|
1577
|
+
declare function syncImages(client: PlayApiClient, packageName: string, dir: string, options?: ImageSyncOptions): Promise<ImageSyncResult>;
|
|
1578
|
+
|
|
1579
|
+
interface BundlesWaitOptions {
|
|
1580
|
+
timeout?: number;
|
|
1581
|
+
interval?: number;
|
|
1582
|
+
}
|
|
1583
|
+
declare function listBundles(client: PlayApiClient, packageName: string): Promise<Bundle[]>;
|
|
1584
|
+
declare function findBundle(client: PlayApiClient, packageName: string, versionCode: number): Promise<Bundle | null>;
|
|
1585
|
+
declare function waitForBundle(client: PlayApiClient, packageName: string, versionCode: number, options?: BundlesWaitOptions): Promise<Bundle>;
|
|
1586
|
+
|
|
1548
1587
|
interface SigningConsistencyResult {
|
|
1549
1588
|
currentVersionCode: number;
|
|
1550
1589
|
currentFingerprint: string;
|
|
@@ -1578,4 +1617,4 @@ interface ChecklistInput {
|
|
|
1578
1617
|
declare function buildChecklist(input: ChecklistInput): ChecklistResult;
|
|
1579
1618
|
declare function renderChecklistMarkdown(result: ChecklistResult, accountEmail: string): string;
|
|
1580
1619
|
|
|
1581
|
-
export { ApiError, type ApiSigningFingerprint, type AppInfo, type AppStatus, type ApplyReleaseNotesResult, type AuditEntry, type BatchSyncResult, type BundleAnalysis, type BundleComparison, type BundleEntry, type BundleSizeCheckResult, type BundleSizeConfig, type ChangelogEntry, type ChecklistInput, type ChecklistItem, type ChecklistResult, type CommandContext, type CommitCluster, ConfigError, type CreateEnterpriseAppParams, DEFAULT_LIMITS, DEFAULT_MODELS, DEFAULT_PREFLIGHT_CONFIG, DEFAULT_WATCH_THRESHOLDS, type DecodedNotification, type DiffToken, type DiscoverPluginsOptions, type DryRunPublishResult, type DryRunResult, type DryRunUploadResult, type ErrorReason, type ExportImagesOptions, type ExportImagesSummary, type FastlaneDetection, type FastlaneLane, type FetchChangelogOptions, type FieldLintResult, type FileValidationResult, type FindingSeverity, GOOGLE_PLAY_LANGUAGES, type GenerateOptions, type GeneratedChangelog, type GetAppStatusOptions, type GitNotesOptions, type GitReleaseNotes, type GitRunner, GpcError, type ImageValidationResult, type InitOptions, type InitResult, type InternalSharingUploadResult, type KeystoreFingerprint, type ListIapOptions, type ListSubscriptionsOptions, type ListUsersOptions, type ListVoidedOptions, type ListingDiff, type ListingFieldLimits, type ListingLintResult, type ListingsResult, type LoadedPlugin, type LocaleBundle, type LocaleEntry, type MigrationResult, NetworkError, type OneTimeProductDiff, type OutputMode, PERMISSION_PROPAGATION_WARNING, PLACEHOLDER_TEXT, PLAY_STORE_LIMIT, PROVIDER_WHITELIST, type ParsedCommit, type ParsedManifest, type ParsedMonth, type PlayStoreFormat, type PlayStoreRenderOptions, PluginManager, type PreflightConfig, type PreflightFinding, type PreflightOptions, type PreflightResult, type PreflightScanner, type Provider, type PublishOptions, type PublishResult, type PushResult, type QuotaUsage, RENDERERS, type RawCommit, type ReleaseDiff, type ReleaseNotesValidation, type ReleaseStatusResult, type Renderer, type ResolveAiConfigOptions, type ResolveLocalesOptions, type ReviewAnalysis, type ReviewExportOptions, type ReviewsFilterOptions, type RtdnStatus, SECTION_ORDER, SENSITIVE_ARG_KEYS, SENSITIVE_KEYS, SEVERITY_ORDER, type ScaffoldOptions, type ScaffoldResult, type SigningConsistencyResult, type SigningKeyComparison, type Spinner, type StatusDiff, type StatusRelease, type StatusReviews, type StatusVitalMetric, type SubscriptionAnalytics, type SubscriptionDiff, type SyncResult, type ThresholdResult, type TrainConfig, type TrainState, type TranslateBundleOptions, type TranslatedBundle, type TranslationFailure, type TranslationPath, type TranslationResult, type Translator, type TranslatorConfig, type UploadResult, VALID_WATCH_METRICS, type ValidateCheck, type ValidateOptions, type ValidateResult, type VersionVitalsComparison, type VersionVitalsRow, type VitalsOverview, type VitalsQueryOptions, type VitalsTrendComparison, type WatchAction, type WatchCallbacks, type WatchConfig, type WatchEvent, type WatchMetric, type WatchOptions, type WatchRollout, type WatchSummary, type WatchVitalReading, type WatchVitalsOptions, type WebhookPayload, abortTrain, acknowledgeProductPurchase, activateBasePlan, activateOffer, addRecoveryTargeting, addTesters, advanceTrain, analyzeBundle, analyzeRemoteListings, analyzeReviews, applyReleaseNotes, batchGetOrders, batchSyncInAppProducts, buildChecklist, buildLocaleBundle, bundleToReleaseNotes, cancelRecoveryAction, cancelSubscriptionPurchase, cancelSubscriptionV2, checkBundleSize, checkSigningConsistency, checkThreshold, classifyError, clearAuditLog, compareBundles, compareFingerprints, compareVersionVitals, compareVitalsTrend, computeStatusDiff, consumeProductPurchase, convertRegionPrices, createAuditEntry, createDeviceTier, createEnterpriseApp, createExternalTransaction, createGrant, createInAppProduct, createOffer, createOneTimeOffer, createOneTimeProduct, createRecoveryAction, createSpinner, createSubscription, createTrack, createTranslator, deactivateBasePlan, deactivateOffer, decodeNotification, defaultGitRunner, deferSubscriptionPurchase, deferSubscriptionV2, deleteBasePlan, deleteGrant, deleteImage, deleteInAppProduct, deleteListing, deleteOffer, deleteOneTimeOffer, deleteOneTimeProduct, deleteSubscription, deployRecoveryAction, detectFastlane, detectOutputFormat, diffListings, diffListingsCommand, diffListingsEnhanced, diffOneTimeProduct, diffReleases, diffSubscription, discoverPlugins, dispatchWebhook, downloadGeneratedApk, downloadReport, exportImages, exportReviews, fetchAggregateCost, fetchChangelog, fetchReleaseNotes, formatChangelogEntry, formatCustomPayload, formatDiscordPayload, formatJunit, formatNotification, formatOutput, formatPathLabel, formatSlackPayload, formatStatusDiff, formatStatusSummary, formatStatusTable, formatWordDiff, generateChangelog, generateMigrationPlan, generateNotesFromGit, getAllScannerNames, getApiSigningFingerprint, getAppInfo, getAppStatus, getCountryAvailability, getDeviceTier, getExternalTransaction, getInAppProduct, getKeystoreFingerprint, getListings, getOffer, getOneTimeOffer, getOneTimeProduct, getOrderDetails, getProductPurchase, getProductPurchaseV2, getQuotaUsage, getReleasesStatus, getReview, getRtdnStatus, getSubscription, getSubscriptionAnalytics, getSubscriptionPurchase, getTrainStatus, getUser, getVitalsAnomalies, getVitalsAnr, getVitalsBattery, getVitalsCrashes, getVitalsErrorCount, getVitalsLmk, getVitalsMemory, getVitalsOverview, getVitalsRendering, getVitalsStartup, handleBreach, importDataSafety, importTestersFromCsv, initAudit, initProject, inviteUser, isFinancialReportType, isStatsReportType, isValidBcp47, isValidReportType, isValidStatsDimension, lintListing, lintListings, lintLocalListings, listAchievements, listAuditEvents, listDeviceTiers, listEvents, listGeneratedApks, listGrants, listImages, listInAppProducts, listLeaderboards, listOffers, listOneTimeOffers, listOneTimeProducts, listRecoveryActions, listReports, listReviews, listSubscriptions, listTesters, listTracks, listUsers, listVoidedPurchases, loadPreflightConfig, loadStatusCache, maybePaginate, migratePrices, normalizeFingerprint, parseAppfile, parseCommit, parseFastfile, parseGrantArg, parseKeytoolOutput, parseMonth, parseRemoteUrl, pauseTrain, promoteRelease, publish, publishEnterpriseApp, pullListings, pushListings, readListingsFromDir, readReleaseNotesFromDir, redactAuditArgs, redactSensitive, refundExternalTransaction, refundOrder, relativeTime, removeTesters, removeUser, renderChecklistMarkdown, renderJson, renderMarkdown, renderPlayStore, renderPlayStoreJson, renderPlayStoreMd, renderPlayStorePrompt, renderPrompt, replyToReview, resolveAiConfig, resolveLocales, revokeSubscriptionPurchase, runPreflight, runWatch, runWatchLoop, safePath, safePathWithin, saveStatusCache, scaffoldPlugin, searchAuditEvents, searchVitalsErrors, sendNotification, sendWebhook, sortResults, startTrain, statusHasBreach, syncInAppProducts, topFiles, trackBreachState, translateBundle, updateAppDetails, updateDataSafety, updateGrant, updateInAppProduct, updateListing, updateOffer, updateOneTimeOffer, updateOneTimeProduct, updateRollout, updateSubscription, updateTrackConfig, updateUser, uploadExternallyHosted, uploadImage, uploadInternalSharing, uploadRelease, validateBundleForApply, validateImage, validateLanguageCode, validatePackageName, validatePreSubmission, validateReleaseNotes, validateSku, validateTrackName, validateUploadFile, validateVersionCode, waitForBundleProcessing, watchVitalsWithAutoHalt, wordDiff, writeAuditLog, writeListingsToDir, writeMigrationOutput };
|
|
1620
|
+
export { ApiError, type ApiSigningFingerprint, type AppInfo, type AppStatus, type ApplyReleaseNotesResult, type AuditEntry, type BatchSyncResult, type BundleAnalysis, type BundleComparison, type BundleEntry, type BundleSizeCheckResult, type BundleSizeConfig, type BundlesWaitOptions, type ChangelogEntry, type ChecklistInput, type ChecklistItem, type ChecklistResult, type CommandContext, type CommitCluster, ConfigError, type CreateEnterpriseAppParams, DEFAULT_LIMITS, DEFAULT_MODELS, DEFAULT_PREFLIGHT_CONFIG, DEFAULT_WATCH_THRESHOLDS, type DecodedNotification, type DiffToken, type DiscoverPluginsOptions, type DryRunPublishResult, type DryRunResult, type DryRunUploadResult, type ErrorReason, type ExportImagesOptions, type ExportImagesSummary, type FastlaneDetection, type FastlaneLane, type FetchChangelogOptions, type FieldLintResult, type FileValidationResult, type FindingSeverity, GOOGLE_PLAY_LANGUAGES, type GenerateOptions, type GeneratedChangelog, type GetAppStatusOptions, type GitNotesOptions, type GitReleaseNotes, type GitRunner, GpcError, type ImageSyncDetail, type ImageSyncOptions, type ImageSyncResult, type ImageValidationResult, type InitOptions, type InitResult, type InternalSharingUploadResult, type KeystoreFingerprint, type ListIapOptions, type ListSubscriptionsOptions, type ListUsersOptions, type ListVoidedOptions, type ListingDiff, type ListingFieldLimits, type ListingLintResult, type ListingsResult, type LoadedPlugin, type LocaleBundle, type LocaleEntry, type MigrationResult, NetworkError, type OneTimeProductDiff, type OutputMode, PERMISSION_PROPAGATION_WARNING, PLACEHOLDER_TEXT, PLAY_STORE_LIMIT, PROVIDER_WHITELIST, type ParsedCommit, type ParsedManifest, type ParsedMonth, type PlayStoreFormat, type PlayStoreRenderOptions, PluginManager, type PreflightConfig, type PreflightFinding, type PreflightOptions, type PreflightResult, type PreflightScanner, type Provider, type PublishOptions, type PublishResult, type PushResult, type QuotaUsage, RENDERERS, type RawCommit, type ReleaseDiff, type ReleaseNotesValidation, type ReleaseStatusResult, type Renderer, type ResolveAiConfigOptions, type ResolveLocalesOptions, type ReviewAnalysis, type ReviewExportOptions, type ReviewsFilterOptions, type RtdnStatus, SECTION_ORDER, SENSITIVE_ARG_KEYS, SENSITIVE_KEYS, SEVERITY_ORDER, type ScaffoldOptions, type ScaffoldResult, type SigningConsistencyResult, type SigningKeyComparison, type Spinner, type StatusDiff, type StatusRelease, type StatusReviews, type StatusVitalMetric, type SubscriptionAnalytics, type SubscriptionDiff, type SyncResult, type ThresholdResult, type TrainConfig, type TrainState, type TranslateBundleOptions, type TranslatedBundle, type TranslationFailure, type TranslationPath, type TranslationResult, type Translator, type TranslatorConfig, type UploadResult, VALID_WATCH_METRICS, type ValidateCheck, type ValidateOptions, type ValidateResult, type VersionVitalsComparison, type VersionVitalsRow, type VitalsOverview, type VitalsQueryOptions, type VitalsTrendComparison, type WatchAction, type WatchCallbacks, type WatchConfig, type WatchEvent, type WatchMetric, type WatchOptions, type WatchRollout, type WatchSummary, type WatchVitalReading, type WatchVitalsOptions, type WebhookPayload, abortTrain, acknowledgeProductPurchase, activateBasePlan, activateOffer, addRecoveryTargeting, addTesters, advanceTrain, analyzeBundle, analyzeRemoteListings, analyzeReviews, applyReleaseNotes, batchGetOrders, batchSyncInAppProducts, buildChecklist, buildLocaleBundle, bundleToReleaseNotes, cancelRecoveryAction, cancelSubscriptionPurchase, cancelSubscriptionV2, checkBundleSize, checkSigningConsistency, checkThreshold, classifyError, clearAuditLog, commitWithRescue, compareBundles, compareFingerprints, compareVersionVitals, compareVitalsTrend, computeStatusDiff, consumeProductPurchase, convertRegionPrices, createAuditEntry, createDeviceTier, createEnterpriseApp, createExternalTransaction, createGrant, createInAppProduct, createOffer, createOneTimeOffer, createOneTimeProduct, createRecoveryAction, createSpinner, createSubscription, createTrack, createTranslator, deactivateBasePlan, deactivateOffer, decodeNotification, defaultGitRunner, deferSubscriptionPurchase, deferSubscriptionV2, deleteBasePlan, deleteGrant, deleteImage, deleteInAppProduct, deleteListing, deleteOffer, deleteOneTimeOffer, deleteOneTimeProduct, deleteSubscription, deployRecoveryAction, detectFastlane, detectOutputFormat, diffListings, diffListingsCommand, diffListingsEnhanced, diffOneTimeProduct, diffReleases, diffSubscription, discoverPlugins, dispatchWebhook, downloadGeneratedApk, downloadReport, exportImages, exportReviews, fetchAggregateCost, fetchChangelog, fetchReleaseNotes, findBundle, formatChangelogEntry, formatCustomPayload, formatDiscordPayload, formatJunit, formatNotification, formatOutput, formatPathLabel, formatSlackPayload, formatStatusDiff, formatStatusSummary, formatStatusTable, formatWordDiff, generateChangelog, generateMigrationPlan, generateNotesFromGit, getAllScannerNames, getApiSigningFingerprint, getAppInfo, getAppStatus, getCountryAvailability, getDeviceTier, getExternalTransaction, getInAppProduct, getKeystoreFingerprint, getListings, getOffer, getOneTimeOffer, getOneTimeProduct, getOrderDetails, getProductPurchase, getProductPurchaseV2, getQuotaUsage, getReleasesStatus, getReview, getRtdnStatus, getSubscription, getSubscriptionAnalytics, getSubscriptionPurchase, getTrainStatus, getUser, getVitalsAnomalies, getVitalsAnr, getVitalsBattery, getVitalsCrashes, getVitalsErrorCount, getVitalsLmk, getVitalsMemory, getVitalsOverview, getVitalsRendering, getVitalsStartup, handleBreach, importDataSafety, importTestersFromCsv, initAudit, initProject, inviteUser, isFinancialReportType, isStatsReportType, isValidBcp47, isValidReportType, isValidStatsDimension, lintListing, lintListings, lintLocalListings, listAchievements, listAuditEvents, listBundles, listDeviceTiers, listEvents, listGeneratedApks, listGrants, listImages, listInAppProducts, listLeaderboards, listOffers, listOneTimeOffers, listOneTimeProducts, listRecoveryActions, listReports, listReviews, listSubscriptions, listTesters, listTracks, listUsers, listVoidedPurchases, loadPreflightConfig, loadStatusCache, maybePaginate, migratePrices, normalizeFingerprint, parseAppfile, parseCommit, parseFastfile, parseGrantArg, parseKeytoolOutput, parseMonth, parseRemoteUrl, pauseTrain, promoteRelease, publish, publishEnterpriseApp, pullListings, pushListings, readListingsFromDir, readReleaseNotesFromDir, redactAuditArgs, redactSensitive, refundExternalTransaction, refundOrder, relativeTime, removeTesters, removeUser, renderChecklistMarkdown, renderJson, renderMarkdown, renderPlayStore, renderPlayStoreJson, renderPlayStoreMd, renderPlayStorePrompt, renderPrompt, replyToReview, resolveAiConfig, resolveLocales, revokeSubscriptionPurchase, runPreflight, runWatch, runWatchLoop, safePath, safePathWithin, saveStatusCache, scaffoldPlugin, searchAuditEvents, searchVitalsErrors, sendNotification, sendWebhook, sha256File, sortResults, startTrain, statusHasBreach, syncImages, syncInAppProducts, topFiles, trackBreachState, translateBundle, updateAppDetails, updateDataSafety, updateGrant, updateInAppProduct, updateListing, updateOffer, updateOneTimeOffer, updateOneTimeProduct, updateRollout, updateSubscription, updateTrackConfig, updateUser, uploadExternallyHosted, uploadImage, uploadInternalSharing, uploadRelease, validateAndCommit, validateBundleForApply, validateImage, validateLanguageCode, validatePackageName, validatePreSubmission, validateReleaseNotes, validateSku, validateTrackName, validateUploadFile, validateVersionCode, waitForBundle, waitForBundleProcessing, watchVitalsWithAutoHalt, wordDiff, writeAuditLog, writeListingsToDir, writeMigrationOutput };
|