pabal-store-api-mcp 1.2.2 → 1.3.1
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/src/core/helpers/screenshot-helpers.d.ts +69 -0
- package/dist/src/core/helpers/screenshot-helpers.js +122 -0
- package/dist/src/core/services/app-store-service.d.ts +3 -1
- package/dist/src/core/services/app-store-service.js +75 -1
- package/dist/src/core/services/google-play-service.d.ts +3 -1
- package/dist/src/core/services/google-play-service.js +94 -1
- package/dist/src/packages/configs/aso-config/types.d.ts +9 -0
- package/dist/src/packages/configs/aso-config/utils.js +11 -0
- package/dist/src/packages/stores/app-store/client.d.ts +29 -0
- package/dist/src/packages/stores/app-store/client.js +189 -1
- package/dist/src/packages/stores/play-store/api-converters.d.ts +1 -0
- package/dist/src/packages/stores/play-store/api-converters.js +2 -0
- package/dist/src/packages/stores/play-store/client.js +10 -2
- package/dist/src/packages/stores/play-store/types.d.ts +1 -0
- package/dist/src/tools/aso/push.js +4 -0
- package/package.json +1 -1
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Screenshot upload helper utilities
|
|
3
|
+
* Shared logic for Google Play and App Store screenshot operations
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Device type mappings for App Store screenshots
|
|
7
|
+
*/
|
|
8
|
+
export declare const APP_STORE_DEVICE_TYPES: {
|
|
9
|
+
readonly iphone67: "APP_IPHONE_67";
|
|
10
|
+
readonly iphone65: "APP_IPHONE_65";
|
|
11
|
+
readonly iphone61: "APP_IPHONE_61";
|
|
12
|
+
readonly iphone58: "APP_IPHONE_58";
|
|
13
|
+
readonly iphone55: "APP_IPHONE_55";
|
|
14
|
+
readonly iphone47: "APP_IPHONE_47";
|
|
15
|
+
readonly iphone40: "APP_IPHONE_40";
|
|
16
|
+
readonly ipadPro129: "APP_IPAD_PRO_3GEN_129";
|
|
17
|
+
readonly ipadPro11: "APP_IPAD_PRO_11";
|
|
18
|
+
readonly ipad105: "APP_IPAD_105";
|
|
19
|
+
readonly ipad97: "APP_IPAD_97";
|
|
20
|
+
};
|
|
21
|
+
export type AppStoreDeviceType = keyof typeof APP_STORE_DEVICE_TYPES;
|
|
22
|
+
export type AppStoreScreenshotDisplayType = (typeof APP_STORE_DEVICE_TYPES)[AppStoreDeviceType];
|
|
23
|
+
/**
|
|
24
|
+
* Screenshot file info parsed from filename
|
|
25
|
+
*/
|
|
26
|
+
export interface ParsedScreenshotFile {
|
|
27
|
+
filename: string;
|
|
28
|
+
path: string;
|
|
29
|
+
deviceType: string;
|
|
30
|
+
order: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Parse App Store screenshot filename
|
|
34
|
+
* @param filename e.g., "iphone65-1.png"
|
|
35
|
+
* @returns Parsed info or null if invalid
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseAppStoreScreenshotFilename(filename: string, basePath: string): ParsedScreenshotFile | null;
|
|
38
|
+
/**
|
|
39
|
+
* Group screenshots by device type with ordering
|
|
40
|
+
*/
|
|
41
|
+
export declare function groupScreenshotsByDeviceType(screenshots: ParsedScreenshotFile[]): Record<string, ParsedScreenshotFile[]>;
|
|
42
|
+
/**
|
|
43
|
+
* Get screenshot files for a locale
|
|
44
|
+
* @returns Array of PNG files in the directory, or empty array if directory doesn't exist
|
|
45
|
+
*/
|
|
46
|
+
export declare function getLocaleScreenshotFiles(screenshotsBaseDir: string, locale: string): string[];
|
|
47
|
+
/**
|
|
48
|
+
* Check if locale has any screenshot files
|
|
49
|
+
*/
|
|
50
|
+
export declare function hasScreenshots(screenshotsBaseDir: string, locale: string): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Parse and group App Store screenshots for a locale
|
|
53
|
+
*/
|
|
54
|
+
export declare function parseAppStoreScreenshots(screenshotsBaseDir: string, locale: string): {
|
|
55
|
+
valid: Record<string, ParsedScreenshotFile[]>;
|
|
56
|
+
invalid: string[];
|
|
57
|
+
unknown: string[];
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* Parse Google Play screenshot filenames
|
|
61
|
+
* Supports: phone-*.png, tablet7-*.png, tablet10-*.png, feature-graphic.png
|
|
62
|
+
*/
|
|
63
|
+
export interface GooglePlayScreenshotFiles {
|
|
64
|
+
phone: string[];
|
|
65
|
+
tablet7: string[];
|
|
66
|
+
tablet10: string[];
|
|
67
|
+
featureGraphic: string | null;
|
|
68
|
+
}
|
|
69
|
+
export declare function parseGooglePlayScreenshots(screenshotsBaseDir: string, locale: string): GooglePlayScreenshotFiles;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Screenshot upload helper utilities
|
|
3
|
+
* Shared logic for Google Play and App Store screenshot operations
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, readdirSync } from "node:fs";
|
|
6
|
+
/**
|
|
7
|
+
* Device type mappings for App Store screenshots
|
|
8
|
+
*/
|
|
9
|
+
export const APP_STORE_DEVICE_TYPES = {
|
|
10
|
+
iphone67: "APP_IPHONE_67",
|
|
11
|
+
iphone65: "APP_IPHONE_65",
|
|
12
|
+
iphone61: "APP_IPHONE_61",
|
|
13
|
+
iphone58: "APP_IPHONE_58",
|
|
14
|
+
iphone55: "APP_IPHONE_55",
|
|
15
|
+
iphone47: "APP_IPHONE_47",
|
|
16
|
+
iphone40: "APP_IPHONE_40",
|
|
17
|
+
ipadPro129: "APP_IPAD_PRO_3GEN_129",
|
|
18
|
+
ipadPro11: "APP_IPAD_PRO_11",
|
|
19
|
+
ipad105: "APP_IPAD_105",
|
|
20
|
+
ipad97: "APP_IPAD_97",
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Parse App Store screenshot filename
|
|
24
|
+
* @param filename e.g., "iphone65-1.png"
|
|
25
|
+
* @returns Parsed info or null if invalid
|
|
26
|
+
*/
|
|
27
|
+
export function parseAppStoreScreenshotFilename(filename, basePath) {
|
|
28
|
+
const match = filename.match(/^([a-zA-Z0-9]+)-(\d+)\.png$/);
|
|
29
|
+
if (!match)
|
|
30
|
+
return null;
|
|
31
|
+
const [, deviceType, orderStr] = match;
|
|
32
|
+
const order = parseInt(orderStr, 10);
|
|
33
|
+
if (!APP_STORE_DEVICE_TYPES[deviceType]) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
filename,
|
|
38
|
+
path: `${basePath}/${filename}`,
|
|
39
|
+
deviceType: APP_STORE_DEVICE_TYPES[deviceType],
|
|
40
|
+
order,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Group screenshots by device type with ordering
|
|
45
|
+
*/
|
|
46
|
+
export function groupScreenshotsByDeviceType(screenshots) {
|
|
47
|
+
const grouped = {};
|
|
48
|
+
for (const screenshot of screenshots) {
|
|
49
|
+
if (!grouped[screenshot.deviceType]) {
|
|
50
|
+
grouped[screenshot.deviceType] = [];
|
|
51
|
+
}
|
|
52
|
+
grouped[screenshot.deviceType].push(screenshot);
|
|
53
|
+
}
|
|
54
|
+
// Sort each group by order
|
|
55
|
+
for (const deviceType in grouped) {
|
|
56
|
+
grouped[deviceType].sort((a, b) => a.order - b.order);
|
|
57
|
+
}
|
|
58
|
+
return grouped;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Get screenshot files for a locale
|
|
62
|
+
* @returns Array of PNG files in the directory, or empty array if directory doesn't exist
|
|
63
|
+
*/
|
|
64
|
+
export function getLocaleScreenshotFiles(screenshotsBaseDir, locale) {
|
|
65
|
+
const localeDir = `${screenshotsBaseDir}/${locale}`;
|
|
66
|
+
if (!existsSync(localeDir)) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
return readdirSync(localeDir).filter((file) => file.endsWith(".png"));
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Check if locale has any screenshot files
|
|
73
|
+
*/
|
|
74
|
+
export function hasScreenshots(screenshotsBaseDir, locale) {
|
|
75
|
+
const files = getLocaleScreenshotFiles(screenshotsBaseDir, locale);
|
|
76
|
+
return files.length > 0;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Parse and group App Store screenshots for a locale
|
|
80
|
+
*/
|
|
81
|
+
export function parseAppStoreScreenshots(screenshotsBaseDir, locale) {
|
|
82
|
+
const localeDir = `${screenshotsBaseDir}/${locale}`;
|
|
83
|
+
const files = getLocaleScreenshotFiles(screenshotsBaseDir, locale);
|
|
84
|
+
const validScreenshots = [];
|
|
85
|
+
const invalidFilenames = [];
|
|
86
|
+
const unknownDeviceTypes = [];
|
|
87
|
+
for (const file of files) {
|
|
88
|
+
const parsed = parseAppStoreScreenshotFilename(file, localeDir);
|
|
89
|
+
if (!parsed) {
|
|
90
|
+
invalidFilenames.push(file);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (!parsed.deviceType) {
|
|
94
|
+
unknownDeviceTypes.push(file);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
validScreenshots.push(parsed);
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
valid: groupScreenshotsByDeviceType(validScreenshots),
|
|
101
|
+
invalid: invalidFilenames,
|
|
102
|
+
unknown: unknownDeviceTypes,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
export function parseGooglePlayScreenshots(screenshotsBaseDir, locale) {
|
|
106
|
+
const localeDir = `${screenshotsBaseDir}/${locale}`;
|
|
107
|
+
const files = getLocaleScreenshotFiles(screenshotsBaseDir, locale);
|
|
108
|
+
return {
|
|
109
|
+
phone: files
|
|
110
|
+
.filter((f) => f.startsWith("phone-") && f.endsWith(".png"))
|
|
111
|
+
.map((f) => `${localeDir}/${f}`),
|
|
112
|
+
tablet7: files
|
|
113
|
+
.filter((f) => f.startsWith("tablet7-") && f.endsWith(".png"))
|
|
114
|
+
.map((f) => `${localeDir}/${f}`),
|
|
115
|
+
tablet10: files
|
|
116
|
+
.filter((f) => f.startsWith("tablet10-") && f.endsWith(".png"))
|
|
117
|
+
.map((f) => `${localeDir}/${f}`),
|
|
118
|
+
featureGraphic: files.includes("feature-graphic.png")
|
|
119
|
+
? `${localeDir}/feature-graphic.png`
|
|
120
|
+
: null,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
@@ -27,11 +27,13 @@ export declare class AppStoreService {
|
|
|
27
27
|
updateReleaseNotes(bundleId: string, releaseNotes: Record<string, string>, versionId?: string, supportedLocales?: string[]): Promise<ServiceResult<UpdatedReleaseNotesResult>>;
|
|
28
28
|
pullReleaseNotes(bundleId: string): Promise<ServiceResult<AppStoreReleaseNote[]>>;
|
|
29
29
|
createVersion(bundleId: string, versionString: string, autoIncrement?: boolean): Promise<ServiceResult<CreatedAppStoreVersion>>;
|
|
30
|
-
pushAsoData({ config, bundleId, localAsoData, appStoreDataPath, }: {
|
|
30
|
+
pushAsoData({ config, bundleId, localAsoData, appStoreDataPath, uploadImages, slug, }: {
|
|
31
31
|
config: EnvConfig;
|
|
32
32
|
bundleId?: string;
|
|
33
33
|
localAsoData: PreparedAsoData;
|
|
34
34
|
appStoreDataPath: string;
|
|
35
|
+
uploadImages?: boolean;
|
|
36
|
+
slug?: string;
|
|
35
37
|
}): Promise<PushAsoResult>;
|
|
36
38
|
verifyAuth(expirationSeconds?: number): Promise<VerifyAuthResult<{
|
|
37
39
|
header: Record<string, unknown>;
|
|
@@ -161,7 +161,7 @@ export class AppStoreService {
|
|
|
161
161
|
return serviceFailure(AppError.wrap(error, HTTP_STATUS.INTERNAL_SERVER_ERROR, ERROR_CODES.APP_STORE_CREATE_VERSION_FAILED, "Failed to create App Store version"));
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
|
-
async pushAsoData({ config, bundleId, localAsoData, appStoreDataPath, }) {
|
|
164
|
+
async pushAsoData({ config, bundleId, localAsoData, appStoreDataPath, uploadImages = false, slug, }) {
|
|
165
165
|
const skip = checkPushPrerequisites({
|
|
166
166
|
storeLabel: "App Store",
|
|
167
167
|
configured: Boolean(config.appStore),
|
|
@@ -194,6 +194,80 @@ export class AppStoreService {
|
|
|
194
194
|
console.error(`[AppStore] ✅ ${locale} uploaded successfully`);
|
|
195
195
|
}
|
|
196
196
|
}
|
|
197
|
+
// Upload screenshots if enabled
|
|
198
|
+
if (uploadImages && slug) {
|
|
199
|
+
console.error(`[AppStore] 📤 Uploading screenshots...`);
|
|
200
|
+
const { getAsoPushDir } = await import("../../packages/configs/aso-config/utils.js");
|
|
201
|
+
const { parseAppStoreScreenshots, hasScreenshots } = await import("../../core/helpers/screenshot-helpers.js");
|
|
202
|
+
const pushDataDir = getAsoPushDir();
|
|
203
|
+
const screenshotsBaseDir = `${pushDataDir}/products/${slug}/store/app-store/screenshots`;
|
|
204
|
+
const uploadedLocales = [];
|
|
205
|
+
const skippedLocales = [];
|
|
206
|
+
const failedLocales = [];
|
|
207
|
+
for (const locale of localesToPush) {
|
|
208
|
+
try {
|
|
209
|
+
if (!hasScreenshots(screenshotsBaseDir, locale)) {
|
|
210
|
+
console.error(`[AppStore] ⏭️ Skipping ${locale} - no screenshots directory`);
|
|
211
|
+
skippedLocales.push(locale);
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const result = parseAppStoreScreenshots(screenshotsBaseDir, locale);
|
|
215
|
+
// Check if there are any valid screenshots
|
|
216
|
+
const totalScreenshots = Object.values(result.valid).reduce((sum, screenshots) => sum + screenshots.length, 0);
|
|
217
|
+
if (totalScreenshots === 0) {
|
|
218
|
+
console.error(`[AppStore] ⚠️ Skipping ${locale} - no valid screenshots found`);
|
|
219
|
+
skippedLocales.push(locale);
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
console.error(`[AppStore] 📤 Uploading screenshots for ${locale}...`);
|
|
223
|
+
// Report parsing issues
|
|
224
|
+
if (result.invalid.length > 0) {
|
|
225
|
+
console.error(`[AppStore] ⚠️ Invalid filenames: ${result.invalid.join(", ")}`);
|
|
226
|
+
}
|
|
227
|
+
if (result.unknown.length > 0) {
|
|
228
|
+
console.error(`[AppStore] ⚠️ Unknown device types: ${result.unknown.join(", ")}`);
|
|
229
|
+
}
|
|
230
|
+
// Upload screenshots for each device type
|
|
231
|
+
for (const [displayType, screenshots] of Object.entries(result.valid)) {
|
|
232
|
+
if (screenshots.length === 0)
|
|
233
|
+
continue;
|
|
234
|
+
console.error(`[AppStore] 📱 Uploading ${screenshots.length} screenshots for ${displayType}...`);
|
|
235
|
+
for (const screenshot of screenshots) {
|
|
236
|
+
try {
|
|
237
|
+
await client.uploadScreenshot({
|
|
238
|
+
imagePath: screenshot.path,
|
|
239
|
+
screenshotDisplayType: displayType,
|
|
240
|
+
locale,
|
|
241
|
+
});
|
|
242
|
+
console.error(`[AppStore] ✅ ${screenshot.filename}`);
|
|
243
|
+
}
|
|
244
|
+
catch (uploadError) {
|
|
245
|
+
const msg = uploadError instanceof Error
|
|
246
|
+
? uploadError.message
|
|
247
|
+
: String(uploadError);
|
|
248
|
+
console.error(`[AppStore] ❌ ${screenshot.filename}: ${msg}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
uploadedLocales.push(locale);
|
|
253
|
+
console.error(`[AppStore] ✅ Screenshots uploaded for ${locale}`);
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
console.error(`[AppStore] ❌ Failed to upload screenshots for ${locale}: ${error instanceof Error ? error.message : String(error)}`);
|
|
257
|
+
failedLocales.push(locale);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
console.error(`[AppStore] 📊 Screenshot upload summary: ${uploadedLocales.length} succeeded, ${skippedLocales.length} skipped, ${failedLocales.length} failed`);
|
|
261
|
+
if (uploadedLocales.length > 0) {
|
|
262
|
+
console.error(`[AppStore] ✅ Uploaded: ${uploadedLocales.join(", ")}`);
|
|
263
|
+
}
|
|
264
|
+
if (skippedLocales.length > 0) {
|
|
265
|
+
console.error(`[AppStore] ⏭️ Skipped: ${skippedLocales.join(", ")}`);
|
|
266
|
+
}
|
|
267
|
+
if (failedLocales.length > 0) {
|
|
268
|
+
console.error(`[AppStore] ❌ Failed: ${failedLocales.join(", ")}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
197
271
|
try {
|
|
198
272
|
const updated = updateRegisteredLocales(ensuredBundleId, "appStore", localesToPush);
|
|
199
273
|
if (updated) {
|
|
@@ -22,11 +22,13 @@ export declare class GooglePlayService {
|
|
|
22
22
|
updateReleaseNotes(packageName: string, releaseNotes: Record<string, string>, track?: string, supportedLocales?: string[]): Promise<ServiceResult<UpdatedReleaseNotesResult>>;
|
|
23
23
|
pullReleaseNotes(packageName: string): Promise<ServiceResult<GooglePlayReleaseNote[]>>;
|
|
24
24
|
createVersion(packageName: string, versionString: string, versionCodes: number[]): Promise<ServiceResult<CreatedGooglePlayVersion>>;
|
|
25
|
-
pushAsoData({ config, packageName, localAsoData, googlePlayDataPath, }: {
|
|
25
|
+
pushAsoData({ config, packageName, localAsoData, googlePlayDataPath, uploadImages, slug, }: {
|
|
26
26
|
config: EnvConfig;
|
|
27
27
|
packageName?: string;
|
|
28
28
|
localAsoData: PreparedAsoData;
|
|
29
29
|
googlePlayDataPath: string;
|
|
30
|
+
uploadImages?: boolean;
|
|
31
|
+
slug?: string;
|
|
30
32
|
}): Promise<PushAsoResult>;
|
|
31
33
|
verifyAuth(): Promise<VerifyAuthResult<{
|
|
32
34
|
client_email: string;
|
|
@@ -146,7 +146,7 @@ export class GooglePlayService {
|
|
|
146
146
|
return serviceFailure(AppError.wrap(error, HTTP_STATUS.INTERNAL_SERVER_ERROR, ERROR_CODES.GOOGLE_PLAY_CREATE_VERSION_FAILED, "Failed to create Google Play version"));
|
|
147
147
|
}
|
|
148
148
|
}
|
|
149
|
-
async pushAsoData({ config, packageName, localAsoData, googlePlayDataPath, }) {
|
|
149
|
+
async pushAsoData({ config, packageName, localAsoData, googlePlayDataPath, uploadImages = false, slug, }) {
|
|
150
150
|
const skip = checkPushPrerequisites({
|
|
151
151
|
storeLabel: "Google Play",
|
|
152
152
|
configured: Boolean(config.playStore),
|
|
@@ -168,6 +168,99 @@ export class GooglePlayService {
|
|
|
168
168
|
console.error(`[GooglePlay] 📤 Preparing locale: ${locale}`);
|
|
169
169
|
}
|
|
170
170
|
await client.pushMultilingualAsoData(googlePlayData);
|
|
171
|
+
// Push app-level contact information
|
|
172
|
+
if (googlePlayData.contactEmail || googlePlayData.contactWebsite) {
|
|
173
|
+
console.error(`[GooglePlay] 📤 Pushing app details...`);
|
|
174
|
+
await client.pushAppDetails({
|
|
175
|
+
contactEmail: googlePlayData.contactEmail,
|
|
176
|
+
contactWebsite: googlePlayData.contactWebsite,
|
|
177
|
+
});
|
|
178
|
+
console.error(`[GooglePlay] ✅ App details uploaded successfully`);
|
|
179
|
+
}
|
|
180
|
+
// Note: YouTube URL is pushed as part of listing data for each locale
|
|
181
|
+
// Upload screenshots if enabled
|
|
182
|
+
if (uploadImages && slug) {
|
|
183
|
+
console.error(`[GooglePlay] 📤 Uploading screenshots...`);
|
|
184
|
+
const { getAsoPushDir } = await import("../../packages/configs/aso-config/utils.js");
|
|
185
|
+
const { parseGooglePlayScreenshots, hasScreenshots } = await import("../../core/helpers/screenshot-helpers.js");
|
|
186
|
+
const pushDataDir = getAsoPushDir();
|
|
187
|
+
const screenshotsBaseDir = `${pushDataDir}/products/${slug}/store/google-play/screenshots`;
|
|
188
|
+
const uploadedLocales = [];
|
|
189
|
+
const skippedLocales = [];
|
|
190
|
+
const failedLocales = [];
|
|
191
|
+
for (const locale of localesToPush) {
|
|
192
|
+
try {
|
|
193
|
+
if (!hasScreenshots(screenshotsBaseDir, locale)) {
|
|
194
|
+
console.error(`[GooglePlay] ⏭️ Skipping ${locale} - no screenshots directory`);
|
|
195
|
+
skippedLocales.push(locale);
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
198
|
+
const screenshots = parseGooglePlayScreenshots(screenshotsBaseDir, locale);
|
|
199
|
+
// Google Play requires minimum 2 phone screenshots
|
|
200
|
+
const phoneCount = screenshots.phone.length + screenshots.tablet7.length;
|
|
201
|
+
if (phoneCount < 2) {
|
|
202
|
+
console.error(`[GooglePlay] ⚠️ Skipping ${locale} - needs at least 2 phone/tablet7 screenshots (found ${phoneCount})`);
|
|
203
|
+
skippedLocales.push(locale);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
console.error(`[GooglePlay] 📤 Uploading screenshots for ${locale}...`);
|
|
207
|
+
// Upload phone screenshots (phone-*.png)
|
|
208
|
+
for (const imagePath of screenshots.phone) {
|
|
209
|
+
await client.uploadScreenshot({
|
|
210
|
+
imagePath,
|
|
211
|
+
imageType: "phoneScreenshots",
|
|
212
|
+
language: locale,
|
|
213
|
+
});
|
|
214
|
+
console.error(`[GooglePlay] ✅ ${imagePath.split("/").pop()}`);
|
|
215
|
+
}
|
|
216
|
+
// Upload 7-inch tablet screenshots as phone
|
|
217
|
+
for (const imagePath of screenshots.tablet7) {
|
|
218
|
+
await client.uploadScreenshot({
|
|
219
|
+
imagePath,
|
|
220
|
+
imageType: "phoneScreenshots",
|
|
221
|
+
language: locale,
|
|
222
|
+
});
|
|
223
|
+
console.error(`[GooglePlay] ✅ ${imagePath.split("/").pop()} (as phone)`);
|
|
224
|
+
}
|
|
225
|
+
// Upload 10-inch tablet screenshots as tablet (optional)
|
|
226
|
+
if (screenshots.tablet10.length > 0) {
|
|
227
|
+
for (const imagePath of screenshots.tablet10) {
|
|
228
|
+
await client.uploadScreenshot({
|
|
229
|
+
imagePath,
|
|
230
|
+
imageType: "tenInchScreenshots",
|
|
231
|
+
language: locale,
|
|
232
|
+
});
|
|
233
|
+
console.error(`[GooglePlay] ✅ ${imagePath.split("/").pop()} (as tablet)`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// Upload feature graphic (optional)
|
|
237
|
+
if (screenshots.featureGraphic) {
|
|
238
|
+
await client.uploadScreenshot({
|
|
239
|
+
imagePath: screenshots.featureGraphic,
|
|
240
|
+
imageType: "featureGraphic",
|
|
241
|
+
language: locale,
|
|
242
|
+
});
|
|
243
|
+
console.error(`[GooglePlay] ✅ feature-graphic.png`);
|
|
244
|
+
}
|
|
245
|
+
uploadedLocales.push(locale);
|
|
246
|
+
console.error(`[GooglePlay] ✅ Screenshots uploaded for ${locale}`);
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
console.error(`[GooglePlay] ❌ Failed to upload screenshots for ${locale}: ${error instanceof Error ? error.message : String(error)}`);
|
|
250
|
+
failedLocales.push(locale);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
console.error(`[GooglePlay] 📊 Screenshot upload summary: ${uploadedLocales.length} succeeded, ${skippedLocales.length} skipped, ${failedLocales.length} failed`);
|
|
254
|
+
if (uploadedLocales.length > 0) {
|
|
255
|
+
console.error(`[GooglePlay] ✅ Uploaded: ${uploadedLocales.join(", ")}`);
|
|
256
|
+
}
|
|
257
|
+
if (skippedLocales.length > 0) {
|
|
258
|
+
console.error(`[GooglePlay] ⏭️ Skipped: ${skippedLocales.join(", ")}`);
|
|
259
|
+
}
|
|
260
|
+
if (failedLocales.length > 0) {
|
|
261
|
+
console.error(`[GooglePlay] ❌ Failed: ${failedLocales.join(", ")}`);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
171
264
|
try {
|
|
172
265
|
const updated = updateRegisteredLocales(ensuredPackage, "googlePlay", localesToPush);
|
|
173
266
|
if (updated) {
|
|
@@ -17,6 +17,7 @@ export interface GooglePlayAsoData {
|
|
|
17
17
|
screenshots: GooglePlayScreenshots;
|
|
18
18
|
featureGraphic?: string;
|
|
19
19
|
promoGraphic?: string;
|
|
20
|
+
video?: string;
|
|
20
21
|
category?: string;
|
|
21
22
|
contentRating?: string;
|
|
22
23
|
keywords?: string[];
|
|
@@ -29,6 +30,9 @@ export interface GooglePlayAsoData {
|
|
|
29
30
|
export interface GooglePlayMultilingualAsoData {
|
|
30
31
|
locales: Record<string, GooglePlayAsoData>;
|
|
31
32
|
defaultLocale?: string;
|
|
33
|
+
contactEmail?: string;
|
|
34
|
+
contactWebsite?: string;
|
|
35
|
+
youtubeUrl?: string;
|
|
32
36
|
}
|
|
33
37
|
export interface GooglePlayReleaseNote {
|
|
34
38
|
versionCode: number;
|
|
@@ -72,6 +76,11 @@ export interface AppStoreAsoData {
|
|
|
72
76
|
export interface AppStoreMultilingualAsoData {
|
|
73
77
|
locales: Record<string, AppStoreAsoData>;
|
|
74
78
|
defaultLocale?: string;
|
|
79
|
+
contactEmail?: string;
|
|
80
|
+
supportUrl?: string;
|
|
81
|
+
marketingUrl?: string;
|
|
82
|
+
privacyPolicyUrl?: string;
|
|
83
|
+
termsUrl?: string;
|
|
75
84
|
}
|
|
76
85
|
export interface AppStoreReleaseNote {
|
|
77
86
|
versionString: string;
|
|
@@ -72,12 +72,18 @@ export function prepareAsoDataForPush(slug, configData, options) {
|
|
|
72
72
|
const locales = isGooglePlayMultilingual(googlePlayData)
|
|
73
73
|
? googlePlayData.locales
|
|
74
74
|
: { [googlePlayData.defaultLanguage || DEFAULT_LOCALE]: googlePlayData };
|
|
75
|
+
// Get app-level youtubeUrl if available
|
|
76
|
+
const appLevelYoutubeUrl = isGooglePlayMultilingual(googlePlayData)
|
|
77
|
+
? googlePlayData.youtubeUrl
|
|
78
|
+
: undefined;
|
|
75
79
|
const cleanedLocales = {};
|
|
76
80
|
for (const [locale, localeData] of Object.entries(locales)) {
|
|
77
81
|
const { screenshots, featureGraphic, ...rest } = localeData;
|
|
78
82
|
cleanedLocales[locale] = {
|
|
79
83
|
...rest,
|
|
80
84
|
contactWebsite: detailPageUrl,
|
|
85
|
+
// Apply app-level youtubeUrl to all locales if not already set
|
|
86
|
+
video: rest.video || appLevelYoutubeUrl,
|
|
81
87
|
};
|
|
82
88
|
}
|
|
83
89
|
const gpDefaultLocale = isGooglePlayMultilingual(googlePlayData)
|
|
@@ -86,6 +92,11 @@ export function prepareAsoDataForPush(slug, configData, options) {
|
|
|
86
92
|
storeData.googlePlay = {
|
|
87
93
|
locales: cleanedLocales,
|
|
88
94
|
defaultLocale: gpDefaultLocale || DEFAULT_LOCALE,
|
|
95
|
+
contactEmail: isGooglePlayMultilingual(googlePlayData)
|
|
96
|
+
? googlePlayData.contactEmail
|
|
97
|
+
: undefined,
|
|
98
|
+
contactWebsite: detailPageUrl,
|
|
99
|
+
youtubeUrl: appLevelYoutubeUrl,
|
|
89
100
|
};
|
|
90
101
|
}
|
|
91
102
|
if (configData.appStore) {
|
|
@@ -70,6 +70,35 @@ export declare class AppStoreClient {
|
|
|
70
70
|
private createAppStoreVersionLocalization;
|
|
71
71
|
private listScreenshotSets;
|
|
72
72
|
private listScreenshots;
|
|
73
|
+
/**
|
|
74
|
+
* Upload a screenshot to App Store Connect
|
|
75
|
+
* Full implementation with 4-step process:
|
|
76
|
+
* 1. Find or create Screenshot Set for display type and locale
|
|
77
|
+
* 2. Create AppScreenshot with upload operation
|
|
78
|
+
* 3. Upload file to reserved URL
|
|
79
|
+
* 4. Commit upload operation
|
|
80
|
+
*/
|
|
81
|
+
uploadScreenshot(options: {
|
|
82
|
+
imagePath: string;
|
|
83
|
+
screenshotDisplayType: string;
|
|
84
|
+
locale: string;
|
|
85
|
+
}): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Find or create Screenshot Set for a specific display type
|
|
88
|
+
*/
|
|
89
|
+
private findOrCreateScreenshotSet;
|
|
90
|
+
/**
|
|
91
|
+
* Create AppScreenshot with upload operation
|
|
92
|
+
*/
|
|
93
|
+
private createAppScreenshot;
|
|
94
|
+
/**
|
|
95
|
+
* Upload file to reserved URL
|
|
96
|
+
*/
|
|
97
|
+
private uploadFileToUrl;
|
|
98
|
+
/**
|
|
99
|
+
* Commit AppScreenshot after upload
|
|
100
|
+
*/
|
|
101
|
+
private commitAppScreenshot;
|
|
73
102
|
private getApi;
|
|
74
103
|
private normalizeEndpoint;
|
|
75
104
|
private requestCollection;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* API Documentation: https://developer.apple.com/documentation/appstoreconnectapi
|
|
6
6
|
*/
|
|
7
7
|
import { AppStoreConnectAPI } from "appstore-connect-sdk";
|
|
8
|
-
import { AppsApi, AppInfosApi, AppInfoLocalizationsApi, AppScreenshotSetsApi, AppStoreVersionLocalizationsApi, AppStoreVersionsApi, ResponseError, } from "appstore-connect-sdk/openapi";
|
|
8
|
+
import { AppsApi, AppInfosApi, AppInfoLocalizationsApi, AppScreenshotSetsApi, AppScreenshotsApi, AppStoreVersionLocalizationsApi, AppStoreVersionsApi, ResponseError, } from "appstore-connect-sdk/openapi";
|
|
9
9
|
import { DEFAULT_LOCALE } from "../../../packages/configs/aso-config/constants.js";
|
|
10
10
|
import { convertToAsoData, convertToMultilingualAsoData, convertToReleaseNote, fetchScreenshotsForLocalization, mapLocalizationsByLocale, selectEnglishAppName, sortReleaseNotes, sortVersions, } from "./api-converters.js";
|
|
11
11
|
import { APP_STORE_API_BASE_URL, APP_STORE_PLATFORM, DEFAULT_APP_LIST_LIMIT, DEFAULT_VERSIONS_FETCH_LIMIT, } from "./constants.js";
|
|
@@ -565,6 +565,194 @@ export class AppStoreClient {
|
|
|
565
565
|
return await this.handleSdkError(error);
|
|
566
566
|
}
|
|
567
567
|
}
|
|
568
|
+
/**
|
|
569
|
+
* Upload a screenshot to App Store Connect
|
|
570
|
+
* Full implementation with 4-step process:
|
|
571
|
+
* 1. Find or create Screenshot Set for display type and locale
|
|
572
|
+
* 2. Create AppScreenshot with upload operation
|
|
573
|
+
* 3. Upload file to reserved URL
|
|
574
|
+
* 4. Commit upload operation
|
|
575
|
+
*/
|
|
576
|
+
async uploadScreenshot(options) {
|
|
577
|
+
const { imagePath, screenshotDisplayType, locale } = options;
|
|
578
|
+
const { readFileSync, statSync } = await import("node:fs");
|
|
579
|
+
const { basename } = await import("node:path");
|
|
580
|
+
try {
|
|
581
|
+
// Get app and version info
|
|
582
|
+
const appId = await this.findAppId();
|
|
583
|
+
const versionsResponse = await this.listAppStoreVersions(appId, {
|
|
584
|
+
platform: APP_STORE_PLATFORM,
|
|
585
|
+
limit: DEFAULT_VERSIONS_FETCH_LIMIT,
|
|
586
|
+
});
|
|
587
|
+
const version = sortVersions(versionsResponse.data || [])[0];
|
|
588
|
+
if (!version)
|
|
589
|
+
throw new Error("App Store version not found.");
|
|
590
|
+
// Find localization for this locale
|
|
591
|
+
const localizationsResponse = await this.listAppStoreVersionLocalizations(version.id, locale);
|
|
592
|
+
let localizationId;
|
|
593
|
+
if (localizationsResponse.data?.[0]) {
|
|
594
|
+
localizationId = localizationsResponse.data[0].id;
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
// Create localization if it doesn't exist
|
|
598
|
+
const createResponse = await this.createAppStoreVersionLocalization(version.id, locale, {});
|
|
599
|
+
localizationId = createResponse.data.id;
|
|
600
|
+
}
|
|
601
|
+
// Step 1: Find or create Screenshot Set
|
|
602
|
+
const screenshotSetId = await this.findOrCreateScreenshotSet(localizationId, screenshotDisplayType);
|
|
603
|
+
// Get file info
|
|
604
|
+
const fileBuffer = readFileSync(imagePath);
|
|
605
|
+
const fileSize = statSync(imagePath).size;
|
|
606
|
+
const fileName = basename(imagePath);
|
|
607
|
+
// Step 2: Create AppScreenshot with upload operation
|
|
608
|
+
const screenshot = await this.createAppScreenshot(screenshotSetId, fileName, fileSize);
|
|
609
|
+
// Step 3: Upload file to reserved URL
|
|
610
|
+
if (screenshot.uploadOperations &&
|
|
611
|
+
screenshot.uploadOperations.length > 0) {
|
|
612
|
+
const uploadOp = screenshot.uploadOperations[0];
|
|
613
|
+
await this.uploadFileToUrl(uploadOp.url, fileBuffer, uploadOp.method);
|
|
614
|
+
}
|
|
615
|
+
// Step 4: Commit screenshot
|
|
616
|
+
await this.commitAppScreenshot(screenshot.id);
|
|
617
|
+
}
|
|
618
|
+
catch (error) {
|
|
619
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
620
|
+
console.error(`[AppStore] ❌ Screenshot upload failed for ${basename(imagePath)}: ${msg}`);
|
|
621
|
+
throw error;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Find or create Screenshot Set for a specific display type
|
|
626
|
+
*/
|
|
627
|
+
async findOrCreateScreenshotSet(localizationId, screenshotDisplayType) {
|
|
628
|
+
// List existing screenshot sets
|
|
629
|
+
const setsResponse = await this.listScreenshotSets(localizationId);
|
|
630
|
+
const existingSet = (setsResponse.data || []).find((set) => set.attributes?.screenshotDisplayType === screenshotDisplayType);
|
|
631
|
+
if (existingSet) {
|
|
632
|
+
return existingSet.id;
|
|
633
|
+
}
|
|
634
|
+
// Create new screenshot set
|
|
635
|
+
const appScreenshotSetsApi = await this.getApi(AppScreenshotSetsApi);
|
|
636
|
+
try {
|
|
637
|
+
const response = await appScreenshotSetsApi.appScreenshotSetsCreateInstance({
|
|
638
|
+
appScreenshotSetCreateRequest: {
|
|
639
|
+
data: {
|
|
640
|
+
type: "appScreenshotSets",
|
|
641
|
+
attributes: {
|
|
642
|
+
screenshotDisplayType: screenshotDisplayType,
|
|
643
|
+
},
|
|
644
|
+
relationships: {
|
|
645
|
+
appStoreVersionLocalization: {
|
|
646
|
+
data: {
|
|
647
|
+
type: "appStoreVersionLocalizations",
|
|
648
|
+
id: localizationId,
|
|
649
|
+
},
|
|
650
|
+
},
|
|
651
|
+
},
|
|
652
|
+
},
|
|
653
|
+
},
|
|
654
|
+
});
|
|
655
|
+
return response.data.id;
|
|
656
|
+
}
|
|
657
|
+
catch (error) {
|
|
658
|
+
return await this.handleSdkError(error);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Create AppScreenshot with upload operation
|
|
663
|
+
*/
|
|
664
|
+
async createAppScreenshot(screenshotSetId, fileName, fileSize) {
|
|
665
|
+
const appScreenshotsApi = await this.getApi(AppScreenshotsApi);
|
|
666
|
+
try {
|
|
667
|
+
const response = await appScreenshotsApi.appScreenshotsCreateInstance({
|
|
668
|
+
appScreenshotCreateRequest: {
|
|
669
|
+
data: {
|
|
670
|
+
type: "appScreenshots",
|
|
671
|
+
attributes: {
|
|
672
|
+
fileName,
|
|
673
|
+
fileSize,
|
|
674
|
+
},
|
|
675
|
+
relationships: {
|
|
676
|
+
appScreenshotSet: {
|
|
677
|
+
data: {
|
|
678
|
+
type: "appScreenshotSets",
|
|
679
|
+
id: screenshotSetId,
|
|
680
|
+
},
|
|
681
|
+
},
|
|
682
|
+
},
|
|
683
|
+
},
|
|
684
|
+
},
|
|
685
|
+
});
|
|
686
|
+
const screenshot = response.data;
|
|
687
|
+
const uploadOps = screenshot.attributes?.uploadOperations || [];
|
|
688
|
+
return {
|
|
689
|
+
id: screenshot.id,
|
|
690
|
+
uploadOperations: uploadOps.map((op) => ({
|
|
691
|
+
url: op.url,
|
|
692
|
+
method: op.method || "PUT",
|
|
693
|
+
length: op.length,
|
|
694
|
+
offset: op.offset,
|
|
695
|
+
})),
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
catch (error) {
|
|
699
|
+
return await this.handleSdkError(error);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Upload file to reserved URL
|
|
704
|
+
*/
|
|
705
|
+
async uploadFileToUrl(url, fileBuffer, method = "PUT") {
|
|
706
|
+
const https = await import("node:https");
|
|
707
|
+
const { URL } = await import("node:url");
|
|
708
|
+
return new Promise((resolve, reject) => {
|
|
709
|
+
const parsedUrl = new URL(url);
|
|
710
|
+
const options = {
|
|
711
|
+
hostname: parsedUrl.hostname,
|
|
712
|
+
port: parsedUrl.port,
|
|
713
|
+
path: parsedUrl.pathname + parsedUrl.search,
|
|
714
|
+
method,
|
|
715
|
+
headers: {
|
|
716
|
+
"Content-Type": "image/png",
|
|
717
|
+
"Content-Length": fileBuffer.length,
|
|
718
|
+
},
|
|
719
|
+
};
|
|
720
|
+
const req = https.request(options, (res) => {
|
|
721
|
+
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
|
|
722
|
+
resolve();
|
|
723
|
+
}
|
|
724
|
+
else {
|
|
725
|
+
reject(new Error(`Upload failed with status ${res.statusCode}`));
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
req.on("error", reject);
|
|
729
|
+
req.write(fileBuffer);
|
|
730
|
+
req.end();
|
|
731
|
+
});
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* Commit AppScreenshot after upload
|
|
735
|
+
*/
|
|
736
|
+
async commitAppScreenshot(screenshotId) {
|
|
737
|
+
const appScreenshotsApi = await this.getApi(AppScreenshotsApi);
|
|
738
|
+
try {
|
|
739
|
+
await appScreenshotsApi.appScreenshotsUpdateInstance({
|
|
740
|
+
id: screenshotId,
|
|
741
|
+
appScreenshotUpdateRequest: {
|
|
742
|
+
data: {
|
|
743
|
+
type: "appScreenshots",
|
|
744
|
+
id: screenshotId,
|
|
745
|
+
attributes: {
|
|
746
|
+
uploaded: true,
|
|
747
|
+
},
|
|
748
|
+
},
|
|
749
|
+
},
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
catch (error) {
|
|
753
|
+
return await this.handleSdkError(error);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
568
756
|
async getApi(apiClass) {
|
|
569
757
|
if (!this.apiCache.has(apiClass)) {
|
|
570
758
|
this.apiCache.set(apiClass, this.sdk.create(apiClass));
|
|
@@ -121,6 +121,8 @@ export function buildListingRequestBody(data) {
|
|
|
121
121
|
body.shortDescription = data.shortDescription;
|
|
122
122
|
if (data.fullDescription)
|
|
123
123
|
body.fullDescription = data.fullDescription;
|
|
124
|
+
if (data.video)
|
|
125
|
+
body.video = data.video;
|
|
124
126
|
return body;
|
|
125
127
|
}
|
|
126
128
|
/**
|
|
@@ -138,11 +138,15 @@ export class GooglePlayClient {
|
|
|
138
138
|
};
|
|
139
139
|
try {
|
|
140
140
|
const language = data.defaultLanguage || DEFAULT_LANGUAGE;
|
|
141
|
-
if (data.title ||
|
|
141
|
+
if (data.title ||
|
|
142
|
+
data.shortDescription ||
|
|
143
|
+
data.fullDescription ||
|
|
144
|
+
data.video) {
|
|
142
145
|
const listingBody = buildListingRequestBody({
|
|
143
146
|
title: data.title,
|
|
144
147
|
shortDescription: data.shortDescription,
|
|
145
148
|
fullDescription: data.fullDescription,
|
|
149
|
+
video: data.video,
|
|
146
150
|
});
|
|
147
151
|
console.error(`[GooglePlayClient] Updating listing for ${language}:`, JSON.stringify(listingBody, null, 2));
|
|
148
152
|
try {
|
|
@@ -216,11 +220,13 @@ export class GooglePlayClient {
|
|
|
216
220
|
for (const [language, localeData] of Object.entries(data.locales)) {
|
|
217
221
|
if (localeData.title ||
|
|
218
222
|
localeData.shortDescription ||
|
|
219
|
-
localeData.fullDescription
|
|
223
|
+
localeData.fullDescription ||
|
|
224
|
+
localeData.video) {
|
|
220
225
|
const listingBody = buildListingRequestBody({
|
|
221
226
|
title: localeData.title,
|
|
222
227
|
shortDescription: localeData.shortDescription,
|
|
223
228
|
fullDescription: localeData.fullDescription,
|
|
229
|
+
video: localeData.video,
|
|
224
230
|
});
|
|
225
231
|
console.error(`[GooglePlayClient] Updating listing for ${language}...`);
|
|
226
232
|
try {
|
|
@@ -295,6 +301,8 @@ export class GooglePlayClient {
|
|
|
295
301
|
console.error(`[GooglePlayClient] No app details to update, skipping`);
|
|
296
302
|
return;
|
|
297
303
|
}
|
|
304
|
+
// Note: youtubeUrl is not part of AppDetails API
|
|
305
|
+
// YouTube URLs are managed at listing level (see pushMultilingualAsoData)
|
|
298
306
|
const authClient = await this.auth.getClient();
|
|
299
307
|
const editResponse = await this.createEdit(authClient, this.packageName);
|
|
300
308
|
const editId = editResponse.data.id;
|
|
@@ -120,6 +120,7 @@ export interface UpdateReleaseNotesOptions {
|
|
|
120
120
|
* App Details Data
|
|
121
121
|
* Internal type for app details update operations
|
|
122
122
|
* Based on AppDetails but with optional fields for partial updates
|
|
123
|
+
* Note: YouTube URLs are managed at listing level, not app details level
|
|
123
124
|
*/
|
|
124
125
|
export interface AppDetailsData {
|
|
125
126
|
contactEmail?: NonNullable<AppDetails["contactEmail"]>;
|
|
@@ -159,6 +159,8 @@ export async function handleAsoPush(options) {
|
|
|
159
159
|
packageName,
|
|
160
160
|
localAsoData,
|
|
161
161
|
googlePlayDataPath,
|
|
162
|
+
uploadImages,
|
|
163
|
+
slug,
|
|
162
164
|
});
|
|
163
165
|
results.push(formatPushResult("Google Play", result));
|
|
164
166
|
}
|
|
@@ -174,6 +176,8 @@ export async function handleAsoPush(options) {
|
|
|
174
176
|
bundleId,
|
|
175
177
|
localAsoData,
|
|
176
178
|
appStoreDataPath,
|
|
179
|
+
uploadImages,
|
|
180
|
+
slug,
|
|
177
181
|
});
|
|
178
182
|
results.push(formatPushResult("App Store", appStoreResult));
|
|
179
183
|
}
|