@gpc-cli/core 0.9.26 → 0.9.28

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/dist/index.d.ts CHANGED
@@ -145,6 +145,7 @@ declare function uploadRelease(client: PlayApiClient, packageName: string, fileP
145
145
  releaseName?: string;
146
146
  mappingFile?: string;
147
147
  dryRun?: boolean;
148
+ onProgress?: (uploaded: number, total: number) => void;
148
149
  }): Promise<UploadResult | DryRunUploadResult>;
149
150
  declare function getReleasesStatus(client: PlayApiClient, packageName: string, trackFilter?: string): Promise<ReleaseStatusResult[]>;
150
151
  declare function promoteRelease(client: PlayApiClient, packageName: string, fromTrack: string, toTrack: string, options?: {
package/dist/index.js CHANGED
@@ -494,6 +494,9 @@ async function getAppInfo(client, packageName) {
494
494
  }
495
495
  }
496
496
 
497
+ // src/commands/releases.ts
498
+ import { stat as stat2 } from "fs/promises";
499
+
497
500
  // src/utils/file-validation.ts
498
501
  import { readFile, stat } from "fs/promises";
499
502
  import { extname } from "path";
@@ -615,9 +618,17 @@ ${validation.errors.join("\n")}`,
615
618
  "Check that the file is a valid AAB or APK and is not corrupted."
616
619
  );
617
620
  }
621
+ let fileSize = 0;
622
+ try {
623
+ const { size } = await stat2(filePath);
624
+ fileSize = size;
625
+ } catch {
626
+ }
627
+ if (options.onProgress) options.onProgress(0, fileSize);
618
628
  const edit = await client.edits.insert(packageName);
619
629
  try {
620
630
  const bundle = await client.bundles.upload(packageName, edit.id, filePath);
631
+ if (options.onProgress) options.onProgress(fileSize, fileSize);
621
632
  if (options.mappingFile) {
622
633
  await client.deobfuscation.upload(
623
634
  packageName,
@@ -998,7 +1009,7 @@ function isValidBcp47(tag) {
998
1009
  }
999
1010
 
1000
1011
  // src/utils/image-validation.ts
1001
- import { stat as stat2 } from "fs/promises";
1012
+ import { stat as stat3 } from "fs/promises";
1002
1013
  import { extname as extname2 } from "path";
1003
1014
  var IMAGE_SIZE_LIMITS = {
1004
1015
  icon: { maxBytes: 1024 * 1024, label: "1 MB" },
@@ -1021,7 +1032,7 @@ async function validateImage(filePath, imageType) {
1021
1032
  }
1022
1033
  let sizeBytes;
1023
1034
  try {
1024
- const stats = await stat2(filePath);
1035
+ const stats = await stat3(filePath);
1025
1036
  sizeBytes = stats.size;
1026
1037
  if (sizeBytes === 0) {
1027
1038
  errors.push("Image file is empty (0 bytes)");
@@ -1055,7 +1066,7 @@ function formatSize2(bytes) {
1055
1066
  }
1056
1067
 
1057
1068
  // src/utils/fastlane.ts
1058
- import { readFile as readFile2, writeFile, mkdir, readdir, stat as stat3 } from "fs/promises";
1069
+ import { readFile as readFile2, writeFile, mkdir, readdir, stat as stat4 } from "fs/promises";
1059
1070
  import { join } from "path";
1060
1071
  var FILE_MAP = {
1061
1072
  "title.txt": "title",
@@ -1068,7 +1079,7 @@ var FIELD_TO_FILE = Object.fromEntries(
1068
1079
  );
1069
1080
  async function exists(path) {
1070
1081
  try {
1071
- await stat3(path);
1082
+ await stat4(path);
1072
1083
  return true;
1073
1084
  } catch {
1074
1085
  return false;
@@ -1082,7 +1093,7 @@ async function readListingsFromDir(dir) {
1082
1093
  for (const lang of entries) {
1083
1094
  if (!SAFE_LANG.test(lang)) continue;
1084
1095
  const langDir = join(dir, lang);
1085
- const langStat = await stat3(langDir);
1096
+ const langStat = await stat4(langDir);
1086
1097
  if (!langStat.isDirectory()) continue;
1087
1098
  const listing = {
1088
1099
  language: lang,
@@ -1704,7 +1715,7 @@ function validateSku(sku) {
1704
1715
  }
1705
1716
 
1706
1717
  // src/utils/release-notes.ts
1707
- import { readdir as readdir3, readFile as readFile4, stat as stat4 } from "fs/promises";
1718
+ import { readdir as readdir3, readFile as readFile4, stat as stat5 } from "fs/promises";
1708
1719
  import { extname as extname3, basename, join as join3 } from "path";
1709
1720
  var MAX_NOTES_LENGTH = 500;
1710
1721
  async function readReleaseNotesFromDir(dir) {
@@ -1724,7 +1735,7 @@ async function readReleaseNotesFromDir(dir) {
1724
1735
  if (extname3(entry) !== ".txt") continue;
1725
1736
  const language = basename(entry, ".txt");
1726
1737
  const filePath = join3(dir, entry);
1727
- const stats = await stat4(filePath);
1738
+ const stats = await stat5(filePath);
1728
1739
  if (!stats.isFile()) continue;
1729
1740
  const text = (await readFile4(filePath, "utf-8")).trim();
1730
1741
  if (text.length === 0) continue;
@@ -1742,8 +1753,8 @@ function validateReleaseNotes(notes) {
1742
1753
  }
1743
1754
  seen.add(note.language);
1744
1755
  if (note.text.length > MAX_NOTES_LENGTH) {
1745
- errors.push(
1746
- `Release notes for "${note.language}" exceed ${MAX_NOTES_LENGTH} chars (${note.text.length} chars)`
1756
+ warnings.push(
1757
+ `Release notes for "${note.language}" are ${note.text.length} chars (max ${MAX_NOTES_LENGTH}) \u2014 Google Play will reject notes exceeding this limit`
1747
1758
  );
1748
1759
  }
1749
1760
  }
@@ -1751,7 +1762,7 @@ function validateReleaseNotes(notes) {
1751
1762
  }
1752
1763
 
1753
1764
  // src/commands/validate.ts
1754
- import { stat as stat5 } from "fs/promises";
1765
+ import { stat as stat6 } from "fs/promises";
1755
1766
  var STANDARD_TRACKS = /* @__PURE__ */ new Set([
1756
1767
  "internal",
1757
1768
  "alpha",
@@ -1790,7 +1801,7 @@ async function validatePreSubmission(options) {
1790
1801
  }
1791
1802
  if (options.mappingFile) {
1792
1803
  try {
1793
- const stats = await stat5(options.mappingFile);
1804
+ const stats = await stat6(options.mappingFile);
1794
1805
  checks.push({
1795
1806
  name: "mapping",
1796
1807
  passed: stats.isFile(),
@@ -1836,6 +1847,7 @@ async function validatePreSubmission(options) {
1836
1847
  passed: notesResult.valid,
1837
1848
  message: notesResult.valid ? `Release notes valid (${resolvedNotes.length} language(s))` : notesResult.errors.join("; ")
1838
1849
  });
1850
+ for (const w of notesResult.warnings) resultWarnings.push(w);
1839
1851
  }
1840
1852
  return {
1841
1853
  valid: checks.every((c) => c.passed),
@@ -3822,7 +3834,7 @@ async function deactivatePurchaseOption(client, packageName, purchaseOptionId) {
3822
3834
  }
3823
3835
 
3824
3836
  // src/commands/bundle-analysis.ts
3825
- import { readFile as readFile9, stat as stat6 } from "fs/promises";
3837
+ import { readFile as readFile9, stat as stat7 } from "fs/promises";
3826
3838
  var EOCD_SIGNATURE = 101010256;
3827
3839
  var CD_SIGNATURE = 33639248;
3828
3840
  var MODULE_SUBDIRS = /* @__PURE__ */ new Set(["dex", "manifest", "res", "assets", "lib", "resources.pb", "root"]);
@@ -3896,7 +3908,7 @@ function detectFileType2(filePath) {
3896
3908
  return "apk";
3897
3909
  }
3898
3910
  async function analyzeBundle(filePath) {
3899
- const fileInfo = await stat6(filePath).catch(() => null);
3911
+ const fileInfo = await stat7(filePath).catch(() => null);
3900
3912
  if (!fileInfo || !fileInfo.isFile()) {
3901
3913
  throw new Error(`File not found: ${filePath}`);
3902
3914
  }
@@ -4262,20 +4274,25 @@ function formatStatusSummary(status) {
4262
4274
  parts.push(`v${latestRelease.versionCode} ${latestRelease.track}`);
4263
4275
  }
4264
4276
  const { crashes, anr } = status.vitals;
4265
- if (crashes.status !== "unknown") {
4266
- const arrow = crashes.trend === "up" ? " \u2191" : crashes.trend === "down" ? " \u2193" : "";
4267
- parts.push(`crashes ${formatVitalValue(crashes)}${arrow} ${vitalIndicator(crashes)}`);
4268
- }
4269
- if (anr.status !== "unknown") {
4270
- const arrow = anr.trend === "up" ? " \u2191" : anr.trend === "down" ? " \u2193" : "";
4271
- parts.push(`ANR ${formatVitalValue(anr)}${arrow} ${vitalIndicator(anr)}`);
4277
+ const allVitalsUnknown2 = crashes.status === "unknown" && anr.status === "unknown";
4278
+ if (allVitalsUnknown2) {
4279
+ parts.push("no vitals");
4280
+ } else {
4281
+ if (crashes.status !== "unknown") {
4282
+ const arrow = crashes.trend === "up" ? " \u2191" : crashes.trend === "down" ? " \u2193" : "";
4283
+ parts.push(`crashes ${formatVitalValue(crashes)}${arrow} ${vitalIndicator(crashes)}`);
4284
+ }
4285
+ if (anr.status !== "unknown") {
4286
+ const arrow = anr.trend === "up" ? " \u2191" : anr.trend === "down" ? " \u2193" : "";
4287
+ parts.push(`ANR ${formatVitalValue(anr)}${arrow} ${vitalIndicator(anr)}`);
4288
+ }
4272
4289
  }
4273
4290
  const { averageRating, totalNew } = status.reviews;
4274
4291
  if (averageRating !== void 0) {
4275
4292
  parts.push(`avg ${averageRating.toFixed(1)}\u2605`);
4276
- }
4277
- if (totalNew > 0) {
4278
- parts.push(`${totalNew} reviews`);
4293
+ if (totalNew > 0) parts.push(`${totalNew} reviews`);
4294
+ } else {
4295
+ parts.push("no reviews");
4279
4296
  }
4280
4297
  return parts.join(" \xB7 ") + (statusHasBreach(status) ? " [ALERT]" : "");
4281
4298
  }