@solana-mobile/dapp-store-cli 0.16.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/bin/dapp-store.js +3 -1
  2. package/lib/CliSetup.js +304 -505
  3. package/lib/CliUtils.js +6 -376
  4. package/lib/__tests__/CliSetupTest.js +484 -74
  5. package/lib/cli/__tests__/parseErrors.test.js +25 -0
  6. package/lib/cli/__tests__/signer.test.js +436 -0
  7. package/lib/cli/constants.js +23 -0
  8. package/lib/cli/messages.js +21 -0
  9. package/lib/cli/parseErrors.js +41 -0
  10. package/lib/{commands/publish/PublishCliSupport.js → cli/selfUpdate.js} +72 -38
  11. package/lib/{commands/publish/PublishCliRemove.js → cli/signer.js} +35 -56
  12. package/lib/index.js +96 -5
  13. package/lib/package.json +5 -24
  14. package/lib/portal/__tests__/releaseMetadata.test.js +647 -0
  15. package/lib/portal/__tests__/translators.test.js +76 -0
  16. package/lib/portal/__tests__/workflowClient.test.js +457 -0
  17. package/lib/portal/attestationClient.js +143 -0
  18. package/lib/portal/files.js +64 -0
  19. package/lib/portal/http.js +364 -0
  20. package/lib/portal/records.js +64 -0
  21. package/lib/portal/releaseMetadata.js +748 -0
  22. package/lib/portal/translators.js +460 -0
  23. package/lib/portal/types.js +1 -0
  24. package/lib/portal/workflowClient.js +704 -0
  25. package/lib/publication/PublicationProgressReporter.js +1051 -0
  26. package/lib/publication/__tests__/PublicationProgressReporter.test.js +174 -0
  27. package/lib/{commands/ValidateCommand.js → publication/__tests__/fundingPreflight.test.js} +90 -66
  28. package/lib/publication/__tests__/publicationSummary.test.js +26 -0
  29. package/lib/publication/cliValidation.js +482 -0
  30. package/lib/publication/fundingPreflight.js +246 -0
  31. package/lib/publication/publicationSummary.js +99 -0
  32. package/lib/{commands/utils.js → publication/runPublicationWorkflow.js} +16 -46
  33. package/package.json +5 -24
  34. package/src/CliSetup.ts +370 -505
  35. package/src/CliUtils.ts +9 -233
  36. package/src/__tests__/CliSetupTest.ts +272 -120
  37. package/src/cli/__tests__/parseErrors.test.ts +34 -0
  38. package/src/cli/__tests__/signer.test.ts +359 -0
  39. package/src/cli/constants.ts +3 -0
  40. package/src/cli/messages.ts +27 -0
  41. package/src/cli/parseErrors.ts +62 -0
  42. package/src/cli/selfUpdate.ts +59 -0
  43. package/src/cli/signer.ts +38 -0
  44. package/src/index.ts +31 -4
  45. package/src/portal/__tests__/releaseMetadata.test.ts +508 -0
  46. package/src/portal/__tests__/translators.test.ts +82 -0
  47. package/src/portal/__tests__/workflowClient.test.ts +278 -0
  48. package/src/portal/attestationClient.ts +19 -0
  49. package/src/portal/files.ts +73 -0
  50. package/src/portal/http.ts +170 -0
  51. package/src/portal/records.ts +38 -0
  52. package/src/portal/releaseMetadata.ts +489 -0
  53. package/src/portal/translators.ts +750 -0
  54. package/src/portal/types.ts +27 -0
  55. package/src/portal/workflowClient.ts +575 -0
  56. package/src/publication/PublicationProgressReporter.ts +1026 -0
  57. package/src/publication/__tests__/PublicationProgressReporter.test.ts +210 -0
  58. package/src/publication/__tests__/fundingPreflight.test.ts +78 -0
  59. package/src/publication/__tests__/publicationSummary.test.ts +30 -0
  60. package/src/publication/cliValidation.ts +264 -0
  61. package/src/publication/fundingPreflight.ts +123 -0
  62. package/src/publication/publicationSummary.ts +26 -0
  63. package/src/publication/runPublicationWorkflow.ts +46 -0
  64. package/lib/commands/create/CreateCliApp.js +0 -223
  65. package/lib/commands/create/CreateCliRelease.js +0 -290
  66. package/lib/commands/create/index.js +0 -40
  67. package/lib/commands/index.js +0 -3
  68. package/lib/commands/publish/PublishCliSubmit.js +0 -208
  69. package/lib/commands/publish/PublishCliUpdate.js +0 -211
  70. package/lib/commands/publish/index.js +0 -22
  71. package/lib/commands/scaffolding/ScaffoldInit.js +0 -15
  72. package/lib/commands/scaffolding/index.js +0 -1
  73. package/lib/config/EnvVariables.js +0 -59
  74. package/lib/config/PublishDetails.js +0 -915
  75. package/lib/config/S3StorageManager.js +0 -93
  76. package/lib/config/index.js +0 -2
  77. package/lib/generated/config_obj.json +0 -1
  78. package/lib/generated/config_schema.json +0 -1
  79. package/lib/prebuild_schema/publishing_source.yaml +0 -64
  80. package/lib/prebuild_schema/schemagen.js +0 -25
  81. package/lib/upload/CachedStorageDriver.js +0 -458
  82. package/lib/upload/TurboStorageDriver.js +0 -718
  83. package/lib/upload/__tests__/CachedStorageDriver.test.js +0 -437
  84. package/lib/upload/__tests__/TurboStorageDriver.test.js +0 -17
  85. package/lib/upload/__tests__/contentGateway.test.js +0 -17
  86. package/lib/upload/contentGateway.js +0 -23
  87. package/lib/upload/index.js +0 -2
  88. package/src/commands/ValidateCommand.ts +0 -82
  89. package/src/commands/create/CreateCliApp.ts +0 -93
  90. package/src/commands/create/CreateCliRelease.ts +0 -149
  91. package/src/commands/create/index.ts +0 -47
  92. package/src/commands/index.ts +0 -3
  93. package/src/commands/publish/PublishCliRemove.ts +0 -66
  94. package/src/commands/publish/PublishCliSubmit.ts +0 -93
  95. package/src/commands/publish/PublishCliSupport.ts +0 -66
  96. package/src/commands/publish/PublishCliUpdate.ts +0 -101
  97. package/src/commands/publish/index.ts +0 -29
  98. package/src/commands/scaffolding/ScaffoldInit.ts +0 -20
  99. package/src/commands/scaffolding/index.ts +0 -1
  100. package/src/commands/utils.ts +0 -33
  101. package/src/config/EnvVariables.ts +0 -39
  102. package/src/config/PublishDetails.ts +0 -456
  103. package/src/config/S3StorageManager.ts +0 -47
  104. package/src/config/index.ts +0 -2
  105. package/src/prebuild_schema/publishing_source.yaml +0 -64
  106. package/src/prebuild_schema/schemagen.js +0 -31
  107. package/src/upload/CachedStorageDriver.ts +0 -179
  108. package/src/upload/TurboStorageDriver.ts +0 -283
  109. package/src/upload/__tests__/CachedStorageDriver.test.ts +0 -246
  110. package/src/upload/__tests__/TurboStorageDriver.test.ts +0 -15
  111. package/src/upload/__tests__/contentGateway.test.ts +0 -31
  112. package/src/upload/contentGateway.ts +0 -37
  113. package/src/upload/index.ts +0 -2
@@ -1,456 +0,0 @@
1
- import {
2
- AndroidDetails,
3
- App,
4
- LastSubmittedVersionOnChain,
5
- LastUpdatedVersionOnStore,
6
- Publisher,
7
- Release,
8
- SolanaMobileDappPublisherPortal
9
- } from "@solana-mobile/dapp-store-publishing-tools";
10
- import { dump, load } from "js-yaml";
11
- import Ajv from "ajv";
12
- // eslint-disable-next-line require-extensions/require-extensions
13
- import { readFile } from 'fs/promises';
14
- const schemaJson = JSON.parse((await readFile(new URL("../generated/config_schema.json", import.meta.url))).toString());
15
- import fs from "fs";
16
- import path from "path";
17
- import { toMetaplexFile } from "@metaplex-foundation/js";
18
- import { Constants, showMessage } from "../CliUtils.js";
19
- import util from "util";
20
- import { imageSize } from "image-size";
21
- import { exec } from "child_process";
22
- import getVideoDimensions from "get-video-dimensions";
23
- import { PublicKey } from "@solana/web3.js";
24
-
25
- const runImgSize = util.promisify(imageSize);
26
- const runExec = util.promisify(exec);
27
-
28
- export interface PublishDetails {
29
- publisher: Publisher;
30
- app: App;
31
- release: Release;
32
- solana_mobile_dapp_publisher_portal: SolanaMobileDappPublisherPortal;
33
- lastSubmittedVersionOnChain: LastSubmittedVersionOnChain
34
- lastUpdatedVersionOnStore: LastUpdatedVersionOnStore,
35
- }
36
-
37
- const AaptPrefixes = {
38
- quoteRegex: "'(.*?)'",
39
- quoteNonLazyRegex: "'(.*)'",
40
- packagePrefix: "package: name=",
41
- verCodePrefix: "versionCode=",
42
- verNamePrefix: "versionName=",
43
- sdkPrefix: "(?:minSdk|sdk)Version:",
44
- debuggableApkPrefix: "application-debuggable",
45
- localePrefix: "locales: ",
46
- };
47
-
48
- type SaveToConfigArgs = {
49
- publisher?: Pick<Publisher, "address">;
50
- app?: Pick<App, "address">;
51
- release?: Release;
52
- lastSubmittedVersionOnChain?: LastSubmittedVersionOnChain;
53
- lastUpdatedVersionOnStore?: LastUpdatedVersionOnStore;
54
- };
55
-
56
- const ajv = new Ajv({ strictTuples: false });
57
- const validate = ajv.compile(schemaJson);
58
-
59
- export const loadPublishDetails = async (configPath: string) => {
60
- const configFile = await fs.promises.readFile(configPath, "utf-8");
61
-
62
- const valid = validate(load(configFile) as object);
63
-
64
- if (!valid) {
65
- console.error(validate.errors);
66
- process.exit(1);
67
- }
68
-
69
- return load(configFile) as PublishDetails;
70
- };
71
-
72
- export const loadPublishDetailsWithChecks = async (
73
- buildToolsDir: string | null = null
74
- ): Promise<PublishDetails> => {
75
- const config = await loadPublishDetails(Constants.getConfigFilePath());
76
-
77
- // We validate that the config is going to have at least one installable asset
78
- const apkEntry = config.release.files.find(
79
- (asset: PublishDetails["release"]["files"][0]) => asset.purpose === "install"
80
- )!;
81
- const apkPath = path.join(process.cwd(), apkEntry?.uri);
82
- if (!fs.existsSync(apkPath)) {
83
- throw new Error("Invalid path to APK file.");
84
- }
85
-
86
- const developerOverridenLocales = config.release?.android_details?.locales
87
-
88
- if (buildToolsDir) {
89
- config.release.android_details = await getAndroidDetails(
90
- buildToolsDir,
91
- apkPath,
92
- developerOverridenLocales
93
- );
94
- }
95
-
96
- const appIcon = config.app.media?.find(
97
- (asset: any) => asset.purpose === "icon"
98
- )?.uri;
99
-
100
- if (appIcon) {
101
- const iconPath = path.join(process.cwd(), appIcon);
102
- await checkIconCompatibility(iconPath, "App");
103
-
104
- const iconBuffer = await fs.promises.readFile(iconPath);
105
- config.app.icon = toMetaplexFile(iconBuffer, appIcon);
106
- }
107
-
108
- const releaseIcon = config.release.media?.find(
109
- (asset: any) => asset.purpose === "icon"
110
- )?.uri;
111
-
112
- if (releaseIcon) {
113
- const iconPath = path.join(process.cwd(), releaseIcon);
114
- await checkIconCompatibility(iconPath, "Release");
115
- }
116
-
117
- if (!appIcon && !releaseIcon) {
118
- throw new Error("Please specify at least one media entry of type icon in your configuration file");
119
- }
120
-
121
- const banner = config.release.media?.find(
122
- (asset: any) => asset.purpose === "banner"
123
- )?.uri;
124
-
125
- if (banner) {
126
- const bannerPath = path.join(process.cwd(), banner);
127
- await checkBannerCompatibility(bannerPath);
128
- } else {
129
- throw new Error("Please specify banner image of size 1200x600 in your configuration file");
130
- }
131
-
132
- const featureGraphic = config.release.media?.find(
133
- (asset: any) => asset.purpose === "featureGraphic"
134
- )?.uri;
135
-
136
- if (featureGraphic) {
137
- const featureGraphicPath = path.join(process.cwd(), featureGraphic);
138
- await checkFeatureGraphicCompatibility(featureGraphicPath);
139
- }
140
-
141
- config.release.media.forEach((item: PublishDetails["release"]["media"][0]) => {
142
- const mediaPath = path.join(process.cwd(), item.uri);
143
- if (!fs.existsSync(mediaPath)) {
144
- throw new Error(`File doesnt exist: ${item.uri}.`)
145
- }
146
-
147
- if (item.purpose == "screenshot" && !checkImageExtension(mediaPath)) {
148
- throw new Error(`Please ensure the file ${item.uri} is a jpeg, png, or webp file.`)
149
- }
150
-
151
- if (item.purpose == "video" && !checkVideoExtension(mediaPath)) {
152
- throw new Error(`Please ensure the file ${item.uri} is a mp4.`)
153
- }
154
- }
155
- );
156
-
157
- const screenshots = config.release.media?.filter(
158
- (asset: any) => asset.purpose === "screenshot"
159
- )
160
-
161
- for (const item of screenshots) {
162
- const mediaPath = path.join(process.cwd(), item.uri);
163
- if (await checkScreenshotDimensions(mediaPath)) {
164
- throw new Error(`Screenshot ${mediaPath} must be at least 1080px in width and height.`);
165
- }
166
- }
167
-
168
- const videos = config.release.media?.filter(
169
- (asset: any) => asset.purpose === "video"
170
- )
171
-
172
- for (const video of videos) {
173
- const mediaPath = path.join(process.cwd(), video.uri);
174
- if (await checkVideoDimensions(mediaPath)) {
175
- throw new Error(`Video ${mediaPath} must be at least 720px in width and height.`);
176
- }
177
- }
178
-
179
- if (screenshots.length + videos.length < 4) {
180
- throw new Error(`At least 4 screenshots or videos are required for publishing a new release. Found only ${screenshots.length + videos.length}`)
181
- }
182
-
183
- validateLocalizableResources(config);
184
-
185
- const googlePkg = config.solana_mobile_dapp_publisher_portal.google_store_package;
186
- if (googlePkg?.length) {
187
- const pkgCompare = new RegExp("[a-zA-Z0-9_]+(\\.[a-zA-Z0-9_]+)+").exec(googlePkg);
188
-
189
- if (!pkgCompare?.length) {
190
- throw new Error("Please provide a valid Google store package name in the Publisher Portal section of your configuration file.");
191
- }
192
- }
193
-
194
- const alpha_testers = config.solana_mobile_dapp_publisher_portal.alpha_testers;
195
- if (alpha_testers !== undefined) {
196
- for (const wallet of alpha_testers) {
197
- try {
198
- void new PublicKey(wallet.address);
199
- } catch (e: unknown) {
200
- throw new Error(`invalid alpha tester wallet address <${wallet}>`);
201
- }
202
- }
203
-
204
- if (alpha_testers.size > 10) {
205
- throw new Error(`Alpha testers are limited to 10 per app submission`);
206
- }
207
- }
208
-
209
- return config;
210
- };
211
-
212
- const checkIconCompatibility = async (path: string, typeString: string) => {
213
- if (!fs.existsSync(path) || !checkImageExtension(path)) {
214
- throw new Error(`Please check the path to your ${typeString} icon and ensure the file is a jpeg, png, or webp file.`);
215
- }
216
-
217
- if (await checkIconDimensions(path)) {
218
- throw new Error("Icons must be 512px by 512px.");
219
- }
220
- };
221
-
222
- const checkBannerCompatibility = async (path: string) => {
223
- if (!fs.existsSync(path) || !checkImageExtension(path)) {
224
- throw new Error(`Please check the path to your banner image and ensure the file is a jpeg, png, or webp file.`);
225
- }
226
-
227
- if (await checkBannerDimensions(path)) {
228
- throw new Error("Banner must be 1200px by 600px.");
229
- }
230
- };
231
-
232
- const checkFeatureGraphicCompatibility = async (path: string) => {
233
- if (!fs.existsSync(path) || !checkImageExtension(path)) {
234
- throw new Error(`Please check the path to your featureGraphic image and ensure the file is a jpeg, png, or webp file.`);
235
- }
236
-
237
- if (await checkFeatureGraphicDimensions(path)) {
238
- throw new Error("Feature Graphic must be 1200px by 1200px.");
239
- }
240
- };
241
-
242
- const checkImageExtension = (uri: string): boolean => {
243
- const fileExt = path.extname(uri).toLowerCase();
244
- return (
245
- fileExt == ".png" ||
246
- fileExt == ".jpg" ||
247
- fileExt == ".jpeg" ||
248
- fileExt == ".webp"
249
- );
250
- };
251
-
252
- const checkVideoExtension = (uri: string): boolean => {
253
- const fileExt = path.extname(uri).toLowerCase();
254
- return (
255
- fileExt == ".mp4"
256
- );
257
- };
258
-
259
- /**
260
- * We need to pre-check some things in the localized resources before we move forward
261
- */
262
- const validateLocalizableResources = (config: PublishDetails) => {
263
- if (!config.release.catalog["en-US"]) {
264
- throw new Error("Please ensure you have the en-US locale strings in your configuration file.");
265
- }
266
-
267
- const baselineSize = Object.keys(config.release.catalog["en-US"]).length;
268
- Object.keys(config.release.catalog).forEach((locale) => {
269
- const size = Object.keys(config.release.catalog[locale]).length;
270
-
271
- if (size != baselineSize) {
272
- throw new Error("Please ensure you have included all localized strings for all locales in your configuration file.");
273
- }
274
- });
275
-
276
- const descsWrongLength = Object.values(config.release.catalog)
277
- .map((x) => x.short_description)
278
- .filter((desc) => !desc?.length || desc.length > 30);
279
-
280
- if (descsWrongLength.length > 0) {
281
- throw new Error("Please ensure all translations of short_description are between 0 and 30 characters");
282
- }
283
- };
284
-
285
- const checkIconDimensions = async (iconPath: string): Promise<boolean> => {
286
- const size = await runImgSize(iconPath);
287
-
288
- return size?.width != size?.height || (size?.width ?? 0) != 512;
289
- };
290
-
291
- const checkScreenshotDimensions = async (imagePath: string): Promise<boolean> => {
292
- const size = await runImgSize(imagePath);
293
-
294
- return (size?.width ?? 0) < 1080 || (size?.height ?? 0) < 1080;
295
- }
296
-
297
- const checkBannerDimensions = async (imagePath: string): Promise<boolean> => {
298
- const size = await runImgSize(imagePath);
299
-
300
- return (size?.width ?? 0) != 1200 || (size?.height ?? 0) != 600;
301
- }
302
-
303
- const checkFeatureGraphicDimensions = async (imagePath: string): Promise<boolean> => {
304
- const size = await runImgSize(imagePath);
305
-
306
- return (size?.width ?? 0) != 1200 || (size?.height ?? 0) != 1200;
307
- }
308
-
309
- const checkVideoDimensions = async (imagePath: string): Promise<boolean> => {
310
- const size = await getVideoDimensions(imagePath);
311
-
312
- return (size?.width ?? 0) < 720 || (size?.height ?? 0) < 720;
313
- }
314
-
315
-
316
- const getAndroidDetails = async (
317
- aaptDir: string,
318
- apkPath: string,
319
- developerOverridenLocales: [string]
320
- ): Promise<AndroidDetails> => {
321
- try {
322
- const { stdout } = await runExec(`${aaptDir}/aapt2 dump badging "${apkPath}"`);
323
-
324
- const appPackage = new RegExp(
325
- AaptPrefixes.packagePrefix + AaptPrefixes.quoteRegex
326
- ).exec(stdout);
327
- const versionCode = new RegExp(
328
- AaptPrefixes.verCodePrefix + AaptPrefixes.quoteRegex
329
- ).exec(stdout);
330
- const versionName = new RegExp(
331
- AaptPrefixes.verNamePrefix + AaptPrefixes.quoteRegex
332
- ).exec(stdout);
333
- const minSdk = new RegExp(
334
- AaptPrefixes.sdkPrefix + AaptPrefixes.quoteRegex
335
- ).exec(stdout);
336
- const permissions = [...stdout.matchAll(/(?:uses-permission|uses-permission-sdk-23): name='([^']*)'/g)].flatMap(permission => permission[1]);
337
- const locales = new RegExp(
338
- AaptPrefixes.localePrefix + AaptPrefixes.quoteNonLazyRegex
339
- ).exec(stdout);
340
- const isDebuggable = new RegExp(
341
- AaptPrefixes.debuggableApkPrefix
342
- ).exec(stdout);
343
-
344
- if (isDebuggable != null) {
345
- throw new TypeError("Debug apks are not supported on Solana dApp store.\nSubmit a signed release apk")
346
- }
347
-
348
- let localeArray = Array.from(locales?.values() ?? []);
349
- if (localeArray.length == 2) {
350
- const localesSrc = localeArray[1];
351
- localeArray = ["en-US"].concat(localesSrc.split("' '").slice(1));
352
- }
353
-
354
- if (permissions.includes("android.permission.INSTALL_PACKAGES") || permissions.includes("android.permission.DELETE_PACKAGES")) {
355
- showMessage(
356
- "App requests system app install/delete permission",
357
- "Your app requests system install/delete permission which is managed by Solana dApp Store.\nThis app will be not approved for listing on Solana dApp Store.",
358
- "error"
359
- );
360
- }
361
-
362
- if (permissions.includes("android.permission.REQUEST_INSTALL_PACKAGES") || permissions.includes("android.permission.REQUEST_DELETE_PACKAGES")) {
363
- showMessage(
364
- "App requests install or delete permission",
365
- "App will be subject to additional security reviews for listing on Solana dApp Store and processing time may be beyond regular review time",
366
- "warning"
367
- );
368
- }
369
-
370
- if (permissions.includes("com.solanamobile.seedvault.ACCESS_SEED_VAULT") || permissions.includes("com.solanamobile.seedvault.ACCESS_SEED_VAULT_PRIVILEGED")) {
371
- showMessage(
372
- "App requests Seed Vault permission",
373
- "If this is not a wallet application, your app maybe rejected from listing on Solana dApp Store.",
374
- "warning"
375
- );
376
- }
377
-
378
- if (developerOverridenLocales == null && localeArray.length >= 60 || developerOverridenLocales?.length >= 60) {
379
- showMessage(
380
- "Excessive language support detected",
381
- "The bundle apk claims supports for following locales \n" +
382
- localeArray +
383
- "\nYou config.yaml claims support for following locales\n" +
384
- developerOverridenLocales +
385
- "\nIf this release does not support all these locales the release may be rejected\n." +
386
- "You can override this list of supported locales in config.yaml" +
387
- "\nSee details at https://developer.android.com/guide/topics/resources/multilingual-support#design for configuring the supported locales in your apk file",
388
- "warning"
389
- );
390
- }
391
-
392
- return {
393
- android_package: appPackage?.[1] ?? "",
394
- min_sdk: parseInt(minSdk?.[1] ?? "0", 10),
395
- version_code: parseInt(versionCode?.[1] ?? "0", 10),
396
- version: versionName?.[1] ?? "0",
397
- cert_fingerprint: await extractCertFingerprint(aaptDir, apkPath),
398
- permissions: permissions,
399
- locales: developerOverridenLocales ?? localeArray
400
- };
401
- } catch (e) {
402
- if (e instanceof TypeError) {
403
- throw e
404
- } else {
405
- throw new Error(`There was an error parsing your APK. Please ensure you have installed Java and provided a valid Android tools directory containing AAPT2.\n` + e);
406
- }
407
- }
408
- };
409
-
410
- export const extractCertFingerprint = async (aaptDir: string, apkPath: string): Promise<string> => {
411
- const { stdout } = await runExec(`${aaptDir}/apksigner verify --print-certs -v "${apkPath}"`);
412
-
413
- const regex = /Signer #1 certificate SHA-256 digest:\s*([a-fA-F0-9]+)/;
414
- const match = stdout.match(regex);
415
-
416
- if (match && match[1]) {
417
- return match[1];
418
- } else {
419
- throw new Error("Could not obtain cert fingerprint")
420
- }
421
- }
422
-
423
- export const writeToPublishDetails = async ({ app, release, lastSubmittedVersionOnChain, lastUpdatedVersionOnStore }: SaveToConfigArgs) => {
424
- const currentConfig = await loadPublishDetailsWithChecks();
425
-
426
- delete currentConfig.publisher.icon;
427
- delete currentConfig.app.icon;
428
-
429
- const newConfig: PublishDetails = {
430
- publisher: {
431
- ...currentConfig.publisher,
432
- },
433
- app: {
434
- ...currentConfig.app,
435
- address: app?.address ?? currentConfig.app.address
436
- },
437
- release: {
438
- ...currentConfig.release,
439
- address: release?.address ?? currentConfig.release.address,
440
- android_details: {
441
- cert_fingerprint: release?.android_details?.cert_fingerprint ?? currentConfig.release.android_details?.cert_fingerprint,
442
- min_sdk: release?.android_details?.min_sdk ?? currentConfig.release.android_details?.min_sdk,
443
- version: release?.android_details?.version ?? currentConfig.release.android_details?.version,
444
- version_code: release?.android_details?.version_code ?? currentConfig.release.android_details?.version_code,
445
- locales: release?.android_details?.locales ?? currentConfig.release.android_details?.locales
446
- }
447
- },
448
- solana_mobile_dapp_publisher_portal: currentConfig.solana_mobile_dapp_publisher_portal,
449
- lastSubmittedVersionOnChain: lastSubmittedVersionOnChain ?? currentConfig.lastSubmittedVersionOnChain,
450
- lastUpdatedVersionOnStore: lastUpdatedVersionOnStore ?? currentConfig.lastUpdatedVersionOnStore
451
- };
452
-
453
- fs.writeFileSync(Constants.getConfigFilePath(), dump(newConfig, {
454
- lineWidth: -1
455
- }));
456
- };
@@ -1,47 +0,0 @@
1
- import { EnvVariables, S3Config } from "./EnvVariables.js";
2
-
3
- export class S3StorageManager {
4
- private _config: S3Config | undefined = undefined
5
-
6
- public get hasS3Config(): boolean {
7
- return this._config != undefined;
8
- }
9
-
10
- public get s3Config(): S3Config {
11
- return this._config as S3Config;
12
- }
13
-
14
- constructor(private envVars: EnvVariables) {
15
- if (envVars.hasS3EnvArgs) {
16
- this._config = {
17
- accessKey: this.envVars.s3Config.accessKey,
18
- secretKey: this.envVars.s3Config.secretKey,
19
- bucketName: this.envVars.s3Config.bucketName,
20
- regionName: this.envVars.s3Config.regionName
21
- };
22
- }
23
- }
24
-
25
- parseCmdArg(cmdArg: string) {
26
- if (!cmdArg || cmdArg == "") return;
27
-
28
- try {
29
- //This will overwrite any existing parameters already obtained from the .env file
30
- const parsedArray = JSON.parse(`${cmdArg}`);
31
-
32
- if (parsedArray instanceof Array && parsedArray[0] == "s3") {
33
- if (parsedArray.length != 5) throw new Error("Invalid parameters")
34
-
35
- this._config = {
36
- accessKey: parsedArray[1],
37
- secretKey: parsedArray[2],
38
- bucketName: parsedArray[3],
39
- regionName: parsedArray[4]
40
- };
41
- }
42
- } catch (e) {
43
- throw new Error("There was an error parsing your s3 parameters from the CLI. Please ensure they are formatted correctly.");
44
- }
45
- }
46
-
47
- }
@@ -1,2 +0,0 @@
1
- export * from "./EnvVariables.js";
2
- export * from "./S3StorageManager.js";
@@ -1,64 +0,0 @@
1
- publisher:
2
- name: <<[REQUIRED] YOUR_PUBLISHER_NAME>>
3
- website: <<[REQUIRED] URL_OF_PUBLISHER_WEBSITE>>
4
- email: <<[REQUIRED] EMAIL_ADDRESS_TO_CONTACT_PUBLISHER>>
5
- support_email: <<[Optional] SUPPORT_EMAIL_ADDRESS_FOR_END_USERS>>
6
- app:
7
- name: <<[REQUIRED] APP_NAME>>
8
- address: ""
9
- android_package: <<[REQUIRED] ANDROID_PACKAGE_NAME>>
10
- urls:
11
- license_url: <<[REQUIRED] URL For App's T&C. Don't put placeholder urls.>>
12
- copyright_url: <<[REQUIRED] URL For App's Copyright. Don't put placeholder urls.>>
13
- privacy_policy_url: <<[REQUIRED] URL For App's Privacy Policy. Don't put placeholder urls.>>
14
- website: <<[REQUIRED] URL_OF_APP_WEBSITE>>
15
- media:
16
- - purpose: icon
17
- uri: <<[REQUIRED] RELATIVE_PATH_TO_APP_ICON>>
18
- release:
19
- address: ""
20
- media:
21
- - purpose: icon
22
- uri: <<[REQUIRED] RELATIVE_PATH_TO_RELEASE_ICON>>
23
- - purpose: banner
24
- uri: <<[REQUIRED] RELATIVE_PATH_TO_BANNER>>
25
- - purpose: featureGraphic
26
- uri: <<[Optional] RELATIVE_PATH_TO_FEATURE_GRAPHIC>>
27
- - purpose: screenshot
28
- uri: <<[REQUIRED] RELATIVE_PATH_TO_SCREENSHOT1>>
29
- - purpose: screenshot
30
- uri: <<[REQUIRED] RELATIVE_PATH_TO_SCREENSHOT2>>
31
- - purpose: screenshot
32
- uri: <<[REQUIRED] RELATIVE_PATH_TO_SCREENSHOT3>>
33
- - purpose: screenshot
34
- uri: <<[REQUIRED] RELATIVE_PATH_TO_SCREENSHOT4>>
35
- - purpose: video
36
- uri: <<[Optional] RELATIVE_PATH_TO_VIDEO1>>
37
- files:
38
- - purpose: install
39
- uri: <<[REQUIRED] RELATIVE_PATH_TO_APK>>
40
- catalog:
41
- en-US:
42
- name: >-
43
- <<[REQUIRED] APP_NAME>>
44
- short_description: >-
45
- <<[REQUIRED] SHORT_APP_DESCRIPTION>>
46
- long_description: >-
47
- <<[REQUIRED] LONG_APP_DESCRIPTION>>
48
- new_in_version: >-
49
- <<[REQUIRED] WHATS_NEW_IN_THIS_VERSION>>
50
- saga_features: >-
51
- <<[Optional.] ANY_FEATURES_ONLY_AVAILBLE_WHEN_RUNNING_ON_SAGA>>
52
- android_details:
53
- locales:
54
- - en-US
55
- - <Add more supported locales>
56
- solana_mobile_dapp_publisher_portal:
57
- google_store_package: <<[Optional] ANDROID_PACKAGE_NAME_OF_GOOGLE_PLAY_STORE_VERSION>>
58
- testing_instructions: >-
59
- <<[REQUIRED] TESTING_INSTRUCTIONS. Please provide any test account details if applicable>>
60
- alpha_testers:
61
- - address: <<Optional. genesis token wallet address>>
62
- comment: <<Optional. For internal use only>>
63
- - address: <<Optional. genesis token wallet address>>
64
- comment: <<Optional. For internal use only>>
@@ -1,31 +0,0 @@
1
- import fs, { read } from "fs";
2
- import yaml from "js-yaml";
3
- import generateSchema from "generate-schema";
4
-
5
- try {
6
- const yamlSrc = fs.readFileSync('./src/prebuild_schema/publishing_source.yaml', 'utf8')
7
- const convertedYaml = yaml.load(yamlSrc);
8
- fs.writeFileSync('./src/generated/config_obj.json', Buffer.from(JSON.stringify(convertedYaml)), 'utf-8');
9
-
10
- const schema = generateSchema.json('result', convertedYaml);
11
- // CLI 0.3.0: Adding requirement for `short_description` so validation will catch
12
- schema["properties"]
13
- ["release"]
14
- ["properties"]
15
- ["catalog"]
16
- ["properties"]
17
- ["en-US"].required = ["short_description"];
18
-
19
- schema["properties"]
20
- ["publisher"]
21
- .required = ["name", "website", "email"];
22
-
23
- // Generator adds some keys/values we don't need & mess up validation
24
- delete schema.$schema;
25
- delete schema.title;
26
-
27
- const toWrite = Buffer.from(JSON.stringify(schema));
28
- fs.writeFileSync('./src/generated/config_schema.json', toWrite, 'utf-8');
29
- } catch (e) {
30
- console.log(":: Schema generation step failed ::");
31
- }