@solana-mobile/dapp-store-cli 0.1.1-0 → 0.1.1-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/src/index.ts CHANGED
@@ -75,30 +75,33 @@ async function main() {
75
75
  .option("-u, --url", "RPC URL", "https://devnet.genesysgo.net")
76
76
  .option("-d, --dry-run", "Flag for dry run. Doesn't mint an NFT")
77
77
  .action(async ({ publisherMintAddress, keypair, url, dryRun }) => {
78
- const config = await getConfigFile();
79
- if (!config.isValid) return;
78
+ try {
79
+ const config = await getConfigFile();
80
80
 
81
- if (!hasAddressInConfig(config.publisher) && !publisherMintAddress) {
82
- showUserErrorMessage(
83
- "Either specify an publisher mint address in the config file, or specify as a CLI argument to this command."
84
- );
85
- createCommand.showHelpAfterError();
86
- return;
87
- }
81
+ if (!hasAddressInConfig(config.publisher) && !publisherMintAddress) {
82
+ showUserErrorMessage(
83
+ "Either specify an publisher mint address in the config file, or specify as a CLI argument to this command."
84
+ );
85
+ createCommand.showHelpAfterError();
86
+ return;
87
+ }
88
88
 
89
- const signer = parseKeypair(keypair);
90
- if (signer) {
91
- await createAppCommand({
92
- publisherMintAddress: publisherMintAddress,
93
- signer,
94
- url,
95
- dryRun,
96
- });
89
+ const signer = parseKeypair(keypair);
90
+ if (signer) {
91
+ await createAppCommand({
92
+ publisherMintAddress: publisherMintAddress,
93
+ signer,
94
+ url,
95
+ dryRun,
96
+ });
97
+ }
98
+ } catch (e) {
99
+ showUserErrorMessage((e as Error | null)?.message ?? "");
97
100
  }
98
101
  });
99
102
 
100
103
  createCommand
101
- .command("release <version>")
104
+ .command("release")
102
105
  .description("Create a release")
103
106
  .requiredOption(
104
107
  "-k, --keypair <path-to-keypair-file>",
@@ -114,48 +117,46 @@ async function main() {
114
117
  "-b, --build-tools-path <build-tools-path>",
115
118
  "Path to Android build tools which contains AAPT2"
116
119
  )
117
- .action(
118
- async (
119
- version,
120
- { appMintAddress, keypair, url, dryRun, buildToolsPath }
121
- ) => {
122
- const toolsEnvDir = process.env.ANDROID_TOOLS_DIR ?? "";
123
-
124
- let buildTools = "";
125
- if (toolsEnvDir && toolsEnvDir.length > 0) {
126
- buildTools = toolsEnvDir;
127
- } else if (buildToolsPath) {
128
- buildTools = buildToolsPath;
129
- } else {
130
- showUserErrorMessage(
131
- "\n\n::: Please specify an Android build tools directory in the .env file or via the command line argument. :::\n\n"
132
- );
133
- createCommand.showHelpAfterError();
134
- return;
135
- }
136
-
137
- const config = await getConfigFile();
138
- if (!config.isValid) return;
139
-
140
- if (!hasAddressInConfig(config.app) && !appMintAddress) {
141
- showUserErrorMessage(
142
- "\n\n::: Either specify an app mint address in the config file, or specify as a CLI argument to this command. :::\n\n"
143
- );
144
- createCommand.showHelpAfterError();
145
- return;
146
- }
147
-
148
- const signer = parseKeypair(keypair);
149
-
150
- if (signer) {
151
- const result = await createReleaseCommand({
152
- appMintAddress: appMintAddress,
153
- version,
154
- buildToolsPath: buildTools,
155
- signer,
156
- url,
157
- dryRun,
158
- });
120
+ .action(async ({ appMintAddress, keypair, url, dryRun, buildToolsPath }) => {
121
+ try {
122
+ const toolsEnvDir = process.env.ANDROID_TOOLS_DIR ?? "";
123
+
124
+ let buildTools = "";
125
+ if (toolsEnvDir && toolsEnvDir.length > 0) {
126
+ buildTools = toolsEnvDir;
127
+ } else if (buildToolsPath) {
128
+ buildTools = buildToolsPath;
129
+ } else {
130
+ showUserErrorMessage(
131
+ "\n\n::: Please specify an Android build tools directory in the .env file or via the command line argument. :::\n\n"
132
+ );
133
+ createCommand.showHelpAfterError();
134
+ return;
135
+ }
136
+
137
+ const config = await getConfigFile();
138
+
139
+ if (!hasAddressInConfig(config.app) && !appMintAddress) {
140
+ showUserErrorMessage(
141
+ "\n\n::: Either specify an app mint address in the config file, or specify as a CLI argument to this command. :::\n\n"
142
+ );
143
+ createCommand.showHelpAfterError();
144
+ return;
145
+ }
146
+
147
+ const signer = parseKeypair(keypair);
148
+
149
+ if (signer) {
150
+ const result = await createReleaseCommand({
151
+ appMintAddress: appMintAddress,
152
+ buildToolsPath: buildTools,
153
+ signer,
154
+ url,
155
+ dryRun,
156
+ });
157
+ }
158
+ } catch (e) {
159
+ showUserErrorMessage((e as Error | null)?.message ?? "");
159
160
  }
160
161
  }
161
162
  );
@@ -230,27 +231,30 @@ async function main() {
230
231
  requestorIsAuthorized,
231
232
  dryRun,
232
233
  }) => {
233
- const config = await getConfigFile();
234
- if (!config.isValid) return;
235
-
236
- if (!hasAddressInConfig(config.publisher) && !releaseMintAddress) {
237
- showUserErrorMessage(
238
- "\n\n::: Either specify an release mint address in the config file, or specify as a CLI argument to this command. :::\n\n"
239
- );
240
- publishCommand.showHelpAfterError();
241
- return;
242
- }
243
-
244
- const signer = parseKeypair(keypair);
245
- if (signer) {
246
- await publishSubmitCommand({
247
- releaseMintAddress,
248
- signer,
249
- url,
250
- dryRun,
251
- compliesWithSolanaDappStorePolicies,
252
- requestorIsAuthorized,
253
- });
234
+ try {
235
+ const config = await getConfigFile();
236
+
237
+ if (!hasAddressInConfig(config.publisher) && !releaseMintAddress) {
238
+ showUserErrorMessage(
239
+ "\n\n::: Either specify an release mint address in the config file, or specify as a CLI argument to this command. :::\n\n"
240
+ );
241
+ publishCommand.showHelpAfterError();
242
+ return;
243
+ }
244
+
245
+ const signer = parseKeypair(keypair);
246
+ if (signer) {
247
+ await publishSubmitCommand({
248
+ releaseMintAddress,
249
+ signer,
250
+ url,
251
+ dryRun,
252
+ compliesWithSolanaDappStorePolicies,
253
+ requestorIsAuthorized,
254
+ });
255
+ }
256
+ } catch (e) {
257
+ showUserErrorMessage((e as Error | null)?.message ?? "");
254
258
  }
255
259
  }
256
260
  );
@@ -292,29 +296,32 @@ async function main() {
292
296
  critical,
293
297
  dryRun,
294
298
  }) => {
295
- const config = await getConfigFile();
296
- if (!config.isValid) return;
297
-
298
- if (!hasAddressInConfig(config.publisher) && !releaseMintAddress) {
299
- showUserErrorMessage(
300
- "\n\n::: Either specify an release mint address in the config file, or specify as a CLI argument to this command. :::\n\n"
301
- );
302
- publishCommand.showHelpAfterError();
303
- return;
304
- }
305
-
306
- const signer = parseKeypair(keypair);
307
-
308
- if (signer) {
309
- await publishUpdateCommand({
310
- releaseMintAddress,
311
- signer,
312
- url,
313
- dryRun,
314
- compliesWithSolanaDappStorePolicies,
315
- requestorIsAuthorized,
316
- critical,
317
- });
299
+ try {
300
+ const config = await getConfigFile();
301
+
302
+ if (!hasAddressInConfig(config.publisher) && !releaseMintAddress) {
303
+ showUserErrorMessage(
304
+ "\n\n::: Either specify an release mint address in the config file, or specify as a CLI argument to this command. :::\n\n"
305
+ );
306
+ publishCommand.showHelpAfterError();
307
+ return;
308
+ }
309
+
310
+ const signer = parseKeypair(keypair);
311
+
312
+ if (signer) {
313
+ await publishUpdateCommand({
314
+ releaseMintAddress,
315
+ signer,
316
+ url,
317
+ dryRun,
318
+ compliesWithSolanaDappStorePolicies,
319
+ requestorIsAuthorized,
320
+ critical,
321
+ });
322
+ }
323
+ } catch (e) {
324
+ showUserErrorMessage((e as Error | null)?.message ?? "");
318
325
  }
319
326
  }
320
327
  );
@@ -351,28 +358,31 @@ async function main() {
351
358
  critical,
352
359
  dryRun,
353
360
  }) => {
354
- const config = await getConfigFile();
355
- if (!config.isValid) return;
356
-
357
- if (!hasAddressInConfig(config.publisher) && !releaseMintAddress) {
358
- showUserErrorMessage(
359
- "\n\n::: Either specify an release mint address in the config file, or specify as a CLI argument to this command. :::\n\n"
360
- );
361
- publishCommand.showHelpAfterError();
362
- return;
363
- }
364
-
365
- const signer = parseKeypair(keypair);
366
-
367
- if (signer) {
368
- await publishRemoveCommand({
369
- releaseMintAddress,
370
- signer,
371
- url,
372
- dryRun,
373
- requestorIsAuthorized,
374
- critical,
375
- });
361
+ try {
362
+ const config = await getConfigFile();
363
+
364
+ if (!hasAddressInConfig(config.publisher) && !releaseMintAddress) {
365
+ showUserErrorMessage(
366
+ "\n\n::: Either specify an release mint address in the config file, or specify as a CLI argument to this command. :::\n\n"
367
+ );
368
+ publishCommand.showHelpAfterError();
369
+ return;
370
+ }
371
+
372
+ const signer = parseKeypair(keypair);
373
+
374
+ if (signer) {
375
+ await publishRemoveCommand({
376
+ releaseMintAddress,
377
+ signer,
378
+ url,
379
+ dryRun,
380
+ requestorIsAuthorized,
381
+ critical,
382
+ });
383
+ }
384
+ } catch (e) {
385
+ showUserErrorMessage((e as Error | null)?.message ?? "");
376
386
  }
377
387
  }
378
388
  );
@@ -404,28 +414,31 @@ async function main() {
404
414
  requestDetails,
405
415
  { releaseMintAddress, keypair, url, requestorIsAuthorized, dryRun }
406
416
  ) => {
407
- const config = await getConfigFile();
408
- if (!config.isValid) return;
409
-
410
- if (!hasAddressInConfig(config.publisher) && !releaseMintAddress) {
411
- showUserErrorMessage(
412
- "\n\n::: Either specify an release mint address in the config file, or specify as a CLI argument to this command. :::\n\n"
413
- );
414
- publishCommand.showHelpAfterError();
415
- return;
416
- }
417
-
418
- const signer = parseKeypair(keypair);
419
-
420
- if (signer) {
421
- await publishSupportCommand({
422
- releaseMintAddress,
423
- signer,
424
- url,
425
- dryRun,
426
- requestorIsAuthorized,
427
- requestDetails,
428
- });
417
+ try {
418
+ const config = await getConfigFile();
419
+
420
+ if (!hasAddressInConfig(config.publisher) && !releaseMintAddress) {
421
+ showUserErrorMessage(
422
+ "\n\n::: Either specify an release mint address in the config file, or specify as a CLI argument to this command. :::\n\n"
423
+ );
424
+ publishCommand.showHelpAfterError();
425
+ return;
426
+ }
427
+
428
+ const signer = parseKeypair(keypair);
429
+
430
+ if (signer) {
431
+ await publishSupportCommand({
432
+ releaseMintAddress,
433
+ signer,
434
+ url,
435
+ dryRun,
436
+ requestorIsAuthorized,
437
+ requestDetails,
438
+ });
439
+ }
440
+ } catch (e) {
441
+ showUserErrorMessage((e as Error | null)?.message ?? "");
429
442
  }
430
443
  }
431
444
  );
@@ -19,7 +19,10 @@ export type AssetManifestSchema = {
19
19
 
20
20
  // TODO(jon): We need to manage the removal / replacement of assets in the manifest
21
21
  export class CachedStorageDriver implements StorageDriver {
22
- static readonly SCHEMA_VERSION = "0.2.2";
22
+ // NOTE: this schema version is independent of the publishing JSON schema. It should be updated
23
+ // when the AssetManifestSchema or Asset types are updated.
24
+ static readonly SCHEMA_VERSION = "0.1";
25
+
23
26
  assetManifest: AssetManifestSchema;
24
27
  assetManifestPath: string;
25
28
  storageDriver: StorageDriver;
package/src/utils.ts CHANGED
@@ -1,10 +1,5 @@
1
1
  import fs from "fs";
2
- import type {
3
- AndroidDetails,
4
- App,
5
- Publisher,
6
- Release,
7
- } from "@solana-mobile/dapp-store-publishing-tools";
2
+ import type { AndroidDetails, App, Publisher, Release } from "@solana-mobile/dapp-store-publishing-tools";
8
3
  import type { Connection } from "@solana/web3.js";
9
4
  import { Keypair } from "@solana/web3.js";
10
5
  import type { CLIConfig } from "./config/index.js";
@@ -14,12 +9,7 @@ import { dump } from "js-yaml";
14
9
  import * as util from "util";
15
10
  import { exec } from "child_process";
16
11
  import * as path from "path";
17
- import {
18
- BundlrStorageDriver,
19
- keypairIdentity,
20
- Metaplex,
21
- toMetaplexFile,
22
- } from "@metaplex-foundation/js";
12
+ import { BundlrStorageDriver, keypairIdentity, Metaplex, toMetaplexFile } from "@metaplex-foundation/js";
23
13
  import { imageSize } from "image-size";
24
14
 
25
15
  import { CachedStorageDriver } from "./upload/CachedStorageDriver.js";
@@ -53,11 +43,10 @@ const AaptPrefixes = {
53
43
 
54
44
  export const getConfigFile = async (
55
45
  buildToolsDir: string | null = null
56
- ): Promise<CLIConfig & { isValid: boolean }> => {
46
+ ): Promise<CLIConfig> => {
57
47
  const configFilePath = `${process.cwd()}/config.yaml`;
58
48
 
59
49
  const config = await getConfig(configFilePath);
60
- config.isValid = true;
61
50
 
62
51
  console.info(`Pulling details from ${configFilePath}`);
63
52
 
@@ -68,9 +57,7 @@ export const getConfigFile = async (
68
57
  )!;
69
58
  const apkPath = path.join(process.cwd(), apkEntry?.uri);
70
59
  if (!fs.existsSync(apkPath)) {
71
- showUserErrorMessage("Invalid path to APK file.");
72
- config.isValid = false;
73
- return config;
60
+ throw new Error("Invalid path to APK file.");
74
61
  }
75
62
 
76
63
  config.release.android_details = await getAndroidDetails(
@@ -85,27 +72,16 @@ export const getConfigFile = async (
85
72
  if (publisherIcon) {
86
73
  const iconPath = path.join(process.cwd(), publisherIcon);
87
74
  if (!fs.existsSync(iconPath) || !checkImageExtension(iconPath)) {
88
- showUserErrorMessage(
89
- "Please check the path to your Publisher icon ensure the file is a jpeg, png, or webp file."
90
- );
91
- config.isValid = false;
92
- return config;
75
+ throw new Error("Please check the path to your Publisher icon and ensure the file is a jpeg, png, or webp file.");
93
76
  }
94
77
 
95
78
  const iconBuffer = await fs.promises.readFile(iconPath);
96
79
 
97
80
  if (await checkIconDimensions(iconPath)) {
98
- showUserErrorMessage(
99
- "Icons must have square dimensions and be no greater than 512px by 512px."
100
- );
101
- config.isValid = false;
102
- return config;
81
+ throw new Error("Icons must have square dimensions and be no greater than 512px by 512px.")
103
82
  }
104
83
 
105
- config.publisher.icon = toMetaplexFile(
106
- iconBuffer,
107
- path.join("media", publisherIcon)
108
- );
84
+ config.publisher.icon = toMetaplexFile(iconBuffer, publisherIcon);
109
85
  }
110
86
 
111
87
  const appIcon = config.app.media?.find(
@@ -114,34 +90,22 @@ export const getConfigFile = async (
114
90
  if (appIcon) {
115
91
  const iconPath = path.join(process.cwd(), appIcon);
116
92
  if (!fs.existsSync(iconPath) || !checkImageExtension(iconPath)) {
117
- showUserErrorMessage(
118
- "Please check the path to your App icon ensure the file is a jpeg, png, or webp file."
119
- );
120
- config.isValid = false;
121
- return config;
93
+ throw new Error("Please check the path to your App icon and ensure the file is a jpeg, png, or webp file.")
122
94
  }
123
95
 
124
96
  const iconBuffer = await fs.promises.readFile(iconPath);
125
97
 
126
98
  if (await checkIconDimensions(iconPath)) {
127
- showUserErrorMessage(
128
- "Icons must have square dimensions and be no greater than 512px by 512px."
129
- );
130
- config.isValid = false;
131
- return config;
99
+ throw new Error("Icons must have square dimensions and be no greater than 512px by 512px.")
132
100
  }
133
101
 
134
- config.app.icon = toMetaplexFile(iconBuffer, path.join("media", appIcon));
102
+ config.app.icon = toMetaplexFile(iconBuffer, appIcon);
135
103
  }
136
104
 
137
105
  config.release.media.forEach((item: CLIConfig["release"]["media"][0]) => {
138
106
  const imagePath = path.join(process.cwd(), item.uri);
139
107
  if (!fs.existsSync(imagePath) || !checkImageExtension(imagePath)) {
140
- showUserErrorMessage(
141
- `Invalid media path or file type: ${item.uri}. Please ensure the file is a jpeg, png, or webp file.`
142
- );
143
- config.isValid = false;
144
- return config;
108
+ throw new Error(`Invalid media path or file type: ${item.uri}. Please ensure the file is a jpeg, png, or webp file.`)
145
109
  }
146
110
  });
147
111
 
@@ -182,8 +146,9 @@ const getAndroidDetails = async (
182
146
  const versionCode = new RegExp(
183
147
  AaptPrefixes.verCodePrefix + AaptPrefixes.quoteRegex
184
148
  ).exec(stdout);
185
- //TODO: Return this and use automatically replacing command line arg
186
- //const versionName = new RegExp(prefixes.verNamePrefix + prefixes.quoteRegex).exec(stdout);
149
+ const versionName = new RegExp(
150
+ AaptPrefixes.verNamePrefix + AaptPrefixes.quoteRegex
151
+ ).exec(stdout);
187
152
  const minSdk = new RegExp(
188
153
  AaptPrefixes.sdkPrefix + AaptPrefixes.quoteRegex
189
154
  ).exec(stdout);
@@ -209,6 +174,7 @@ const getAndroidDetails = async (
209
174
  android_package: appPackage?.[1] ?? "",
210
175
  min_sdk: parseInt(minSdk?.[1] ?? "0", 10),
211
176
  version_code: parseInt(versionCode?.[1] ?? "0", 10),
177
+ version: versionName?.[1] ?? "0",
212
178
  permissions: permissionArray,
213
179
  locales: localeArray,
214
180
  };
@@ -217,7 +183,7 @@ const getAndroidDetails = async (
217
183
  type SaveToConfigArgs = {
218
184
  publisher?: Pick<Publisher, "address">;
219
185
  app?: Pick<App, "address">;
220
- release?: Pick<Release, "address" | "version">;
186
+ release?: Pick<Release, "address">;
221
187
  };
222
188
 
223
189
  export const saveToConfig = async ({
@@ -242,11 +208,9 @@ export const saveToConfig = async ({
242
208
  release: {
243
209
  ...currentConfig.release,
244
210
  address: release?.address ?? currentConfig.release.address,
245
- version: release?.version ?? currentConfig.release.version,
246
211
  },
247
212
  solana_mobile_dapp_publisher_portal:
248
213
  currentConfig.solana_mobile_dapp_publisher_portal,
249
- isValid: currentConfig.isValid,
250
214
  };
251
215
 
252
216
  // TODO(jon): Verify the contents of the YAML file