@solana-mobile/dapp-store-cli 0.2.0 → 0.4.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 (44) hide show
  1. package/lib/esm/commands/create/app.js +7 -5
  2. package/lib/esm/commands/create/app.js.map +1 -1
  3. package/lib/esm/commands/create/publisher.js +7 -5
  4. package/lib/esm/commands/create/publisher.js.map +1 -1
  5. package/lib/esm/commands/create/release.js +7 -5
  6. package/lib/esm/commands/create/release.js.map +1 -1
  7. package/lib/esm/commands/publish/remove.js +2 -2
  8. package/lib/esm/commands/publish/remove.js.map +1 -1
  9. package/lib/esm/commands/publish/submit.js +2 -2
  10. package/lib/esm/commands/publish/submit.js.map +1 -1
  11. package/lib/esm/commands/publish/support.js +2 -2
  12. package/lib/esm/commands/publish/support.js.map +1 -1
  13. package/lib/esm/commands/publish/update.js +2 -2
  14. package/lib/esm/commands/publish/update.js.map +1 -1
  15. package/lib/esm/commands/validate.js +2 -2
  16. package/lib/esm/commands/validate.js.map +1 -1
  17. package/lib/esm/generated/config_obj.json +1 -1
  18. package/lib/esm/generated/config_schema.json +1 -1
  19. package/lib/esm/index.js +18 -8
  20. package/lib/esm/index.js.map +1 -1
  21. package/lib/esm/package.json +2 -2
  22. package/lib/esm/utils.js +55 -16
  23. package/lib/esm/utils.js.map +1 -1
  24. package/lib/types/commands/create/app.d.ts.map +1 -1
  25. package/lib/types/commands/create/publisher.d.ts.map +1 -1
  26. package/lib/types/commands/create/release.d.ts.map +1 -1
  27. package/lib/types/utils.d.ts +2 -2
  28. package/lib/types/utils.d.ts.map +1 -1
  29. package/package.json +2 -2
  30. package/src/commands/create/app.ts +7 -5
  31. package/src/commands/create/publisher.ts +7 -5
  32. package/src/commands/create/release.ts +7 -5
  33. package/src/commands/publish/remove.ts +2 -2
  34. package/src/commands/publish/submit.ts +2 -2
  35. package/src/commands/publish/support.ts +2 -2
  36. package/src/commands/publish/update.ts +2 -2
  37. package/src/commands/validate.ts +2 -2
  38. package/src/index.ts +25 -10
  39. package/src/prebuild_schema/publishing_source.yaml +7 -5
  40. package/src/prebuild_schema/schemagen.js +7 -0
  41. package/src/utils.ts +77 -23
  42. package/src/config/schema.json +0 -195
  43. package/src/generated/config_obj.json +0 -1
  44. package/src/generated/config_schema.json +0 -1
package/src/utils.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import fs from "fs";
2
- import type { AndroidDetails, App, Publisher, Release } from "@solana-mobile/dapp-store-publishing-tools";
2
+ import type { AndroidDetails, App, Publisher, Release, ReleaseJsonMetadata } from "@solana-mobile/dapp-store-publishing-tools";
3
3
  import type { Connection } from "@solana/web3.js";
4
4
  import { Keypair, PublicKey } from "@solana/web3.js";
5
5
  import type { CLIConfig } from "./config/index.js";
@@ -22,7 +22,7 @@ const runImgSize = util.promisify(imageSize);
22
22
  const runExec = util.promisify(exec);
23
23
 
24
24
  export class Constants {
25
- static CLI_VERSION = "0.2.0";
25
+ static CLI_VERSION = "0.4.0";
26
26
  static CONFIG_FILE_NAME = "config.yaml";
27
27
  }
28
28
 
@@ -58,9 +58,12 @@ export const parseKeypair = (pathToKeypairFile: string) => {
58
58
  const keypairFile = fs.readFileSync(pathToKeypairFile, "utf-8");
59
59
  return Keypair.fromSecretKey(Buffer.from(JSON.parse(keypairFile)));
60
60
  } catch (e) {
61
- console.error(
62
- `Something went wrong when attempting to retrieve the keypair at ${pathToKeypairFile}`
63
- );
61
+ showMessage
62
+ (
63
+ "KeyPair Error",
64
+ "Something went wrong when attempting to retrieve the keypair at " + pathToKeypairFile,
65
+ "error"
66
+ )
64
67
  }
65
68
  };
66
69
 
@@ -71,11 +74,10 @@ const AaptPrefixes = {
71
74
  verCodePrefix: "versionCode=",
72
75
  verNamePrefix: "versionName=",
73
76
  sdkPrefix: "sdkVersion:",
74
- permissionPrefix: "uses-permission: name=",
75
77
  localePrefix: "locales: ",
76
78
  };
77
79
 
78
- export const getConfigFile = async (
80
+ export const getConfigWithChecks = async (
79
81
  buildToolsDir: string | null = null
80
82
  ): Promise<CLIConfig> => {
81
83
  const configFilePath = `${process.cwd()}/${Constants.CONFIG_FILE_NAME}`;
@@ -142,6 +144,17 @@ export const getConfigFile = async (
142
144
  }
143
145
  });
144
146
 
147
+ validateLocalizableResources(config);
148
+
149
+ const googlePkg = config.solana_mobile_dapp_publisher_portal.google_store_package;
150
+ if (googlePkg?.length) {
151
+ const pkgCompare = new RegExp("[a-zA-Z0-9_]+(\\.[a-zA-Z0-9_]+)+").exec(googlePkg);
152
+
153
+ if (!pkgCompare?.length) {
154
+ throw new Error("Please provide a valid Google store package name in the Publisher Portal section of your configuration file.");
155
+ }
156
+ }
157
+
145
158
  return config;
146
159
  };
147
160
 
@@ -165,6 +178,33 @@ const checkImageExtension = (uri: string): boolean => {
165
178
  );
166
179
  };
167
180
 
181
+ /**
182
+ * We need to pre-check some things in the localized resources before we move forward
183
+ */
184
+ const validateLocalizableResources = (config: CLIConfig) => {
185
+ if (!config.release.catalog["en-US"]) {
186
+ throw new Error("Please ensure you have the en-US locale strings in your configuration file.");
187
+ }
188
+
189
+ const baselineSize = Object.keys(config.release.catalog["en-US"]).length;
190
+ Object.keys(config.release.catalog).forEach((locale) => {
191
+ const size = Object.keys(config.release.catalog[locale]).length;
192
+
193
+ if (size != baselineSize) {
194
+ throw new Error("Please ensure you have included all localized strings for all locales in your configuration file.");
195
+ }
196
+ });
197
+
198
+ const descsWrongLength = Object.values(config.release.catalog)
199
+ .map((x) => x.short_description)
200
+ .filter((desc) => !desc?.length || desc.length > 50);
201
+
202
+ if (descsWrongLength.length > 0) {
203
+ throw new Error("Please ensure all translations of short_description are between 0 and 50 characters");
204
+ }
205
+ };
206
+
207
+
168
208
  export const isDevnet = (rpcUrl: string): boolean => {
169
209
  return rpcUrl.indexOf("devnet") != -1;
170
210
  };
@@ -196,17 +236,27 @@ export const generateNetworkSuffix = (rpcUrl: string): string => {
196
236
  export const showMessage = (
197
237
  titleMessage = "",
198
238
  contentMessage = "",
199
- isError = false
200
- ) => {
201
- console.log(boxen(contentMessage, {
239
+ type: "standard" | "error" | "warning" = "standard",
240
+ ): string => {
241
+ let color = "cyan";
242
+ if (type == "error") {
243
+ color = "redBright";
244
+ } else if (type == "warning") {
245
+ color = "yellow";
246
+ }
247
+
248
+ const msg = boxen(contentMessage, {
202
249
  title: titleMessage,
203
250
  padding: 1,
204
251
  margin: 1,
205
252
  borderStyle: 'single',
206
- borderColor: isError ? "redBright" : "cyan",
253
+ borderColor: color,
207
254
  textAlignment: "left",
208
- titleAlignment: "center"
209
- }));
255
+ titleAlignment: "center",
256
+ });
257
+
258
+ console.log(msg);
259
+ return msg;
210
260
  };
211
261
 
212
262
  const checkIconDimensions = async (iconPath: string): Promise<boolean> => {
@@ -233,30 +283,34 @@ const getAndroidDetails = async (
233
283
  const minSdk = new RegExp(
234
284
  AaptPrefixes.sdkPrefix + AaptPrefixes.quoteRegex
235
285
  ).exec(stdout);
236
- const permissions = new RegExp(
237
- AaptPrefixes.permissionPrefix + AaptPrefixes.quoteNonLazyRegex
238
- ).exec(stdout);
286
+ const permissions = [...stdout.matchAll(/uses-permission: name='(.*)'/g)];
239
287
  const locales = new RegExp(
240
288
  AaptPrefixes.localePrefix + AaptPrefixes.quoteNonLazyRegex
241
289
  ).exec(stdout);
242
290
 
243
- let permissionArray = Array.from(permissions?.values() ?? []);
244
- if (permissionArray.length >= 2) {
245
- permissionArray = permissionArray.slice(1);
246
- }
247
-
248
291
  let localeArray = Array.from(locales?.values() ?? []);
249
292
  if (localeArray.length == 2) {
250
293
  const localesSrc = localeArray[1];
251
294
  localeArray = ["en-US"].concat(localesSrc.split("' '").slice(1));
252
295
  }
253
296
 
297
+ if (localeArray.length >= 60) {
298
+ showMessage(
299
+ "The bundle apk claims supports for following locales",
300
+ "Claim for supported locales::\n" +
301
+ localeArray +
302
+ "\nIf this release does not support all these locales the release may be rejected" +
303
+ "\nSee details at https://developer.android.com/guide/topics/resources/multilingual-support#design for configuring the supported locales",
304
+ "warning"
305
+ )
306
+ }
307
+
254
308
  return {
255
309
  android_package: appPackage?.[1] ?? "",
256
310
  min_sdk: parseInt(minSdk?.[1] ?? "0", 10),
257
311
  version_code: parseInt(versionCode?.[1] ?? "0", 10),
258
312
  version: versionName?.[1] ?? "0",
259
- permissions: permissionArray,
313
+ permissions: permissions.flatMap(permission => permission[1]),
260
314
  locales: localeArray,
261
315
  };
262
316
  };
@@ -272,7 +326,7 @@ export const saveToConfig = async ({
272
326
  app,
273
327
  release,
274
328
  }: SaveToConfigArgs) => {
275
- const currentConfig = await getConfigFile();
329
+ const currentConfig = await getConfigWithChecks();
276
330
 
277
331
  delete currentConfig.publisher.icon;
278
332
  delete currentConfig.app.icon;
@@ -1,195 +0,0 @@
1
- {
2
- "type": "object",
3
- "properties": {
4
- "publisher": {
5
- "type": "object",
6
- "properties": {
7
- "name": {
8
- "type": "string"
9
- },
10
- "address": {
11
- "type": "string"
12
- },
13
- "website": {
14
- "type": "string"
15
- },
16
- "email": {
17
- "type": "string"
18
- },
19
- "media": {
20
- "type": "array",
21
- "items": [
22
- {
23
- "type": "object",
24
- "properties": {
25
- "purpose": {
26
- "type": "string"
27
- },
28
- "uri": {
29
- "type": "string"
30
- }
31
- },
32
- "required": ["purpose", "uri"]
33
- }
34
- ]
35
- }
36
- },
37
- "required": ["name", "website", "email", "media"]
38
- },
39
- "app": {
40
- "type": "object",
41
- "properties": {
42
- "name": {
43
- "type": "string"
44
- },
45
- "address": {
46
- "type": "string"
47
- },
48
- "android_package": {
49
- "type": "string"
50
- },
51
- "urls": {
52
- "type": "object",
53
- "properties": {
54
- "license_url": {
55
- "type": "string"
56
- },
57
- "copyright_url": {
58
- "type": "string"
59
- },
60
- "privacy_policy_url": {
61
- "type": "string"
62
- },
63
- "website": {
64
- "type": "string"
65
- }
66
- },
67
- "required": [
68
- "license_url",
69
- "copyright_url",
70
- "privacy_policy_url",
71
- "website"
72
- ]
73
- },
74
- "media": {
75
- "type": "array",
76
- "items": [
77
- {
78
- "type": "object",
79
- "properties": {
80
- "purpose": {
81
- "type": "string"
82
- },
83
- "uri": {
84
- "type": "string"
85
- }
86
- },
87
- "required": ["purpose", "uri"]
88
- }
89
- ]
90
- }
91
- },
92
- "required": ["name", "android_package", "urls", "media"]
93
- },
94
- "release": {
95
- "type": "object",
96
- "properties": {
97
- "address": {
98
- "type": "string"
99
- },
100
- "media": {
101
- "type": "array",
102
- "items": [
103
- {
104
- "type": "object",
105
- "properties": {
106
- "purpose": {
107
- "type": "string"
108
- },
109
- "uri": {
110
- "type": "string"
111
- }
112
- },
113
- "required": ["purpose", "uri"]
114
- }
115
- ]
116
- },
117
- "files": {
118
- "type": "array",
119
- "minItems": 1,
120
- "contains": {
121
- "type": "object",
122
- "properties": {
123
- "purpose": {
124
- "type": "string",
125
- "const": "install"
126
- }
127
- }
128
- },
129
- "items": [
130
- {
131
- "type": "object",
132
- "properties": {
133
- "purpose": {
134
- "type": "string"
135
- },
136
- "uri": {
137
- "type": "string"
138
- }
139
- },
140
- "required": ["purpose", "uri"]
141
- }
142
- ]
143
- },
144
- "catalog": {
145
- "type": "object",
146
- "patternProperties": {
147
- "^[a-zA-Z]{2,8}(-[a-zA-Z0-9]{2,8})*$": {
148
- "type": "object",
149
- "properties": {
150
- "name": {
151
- "type": "string"
152
- },
153
- "long_description": {
154
- "type": "string"
155
- },
156
- "new_in_version": {
157
- "type": "string"
158
- },
159
- "saga_features": {
160
- "type": "string"
161
- }
162
- },
163
- "required": [
164
- "name",
165
- "long_description",
166
- "new_in_version",
167
- "saga_features"
168
- ]
169
- }
170
- },
171
- "additionalProperties": false
172
- }
173
- },
174
- "required": ["media", "files", "catalog"]
175
- },
176
- "solana_mobile_dapp_publisher_portal": {
177
- "type": "object",
178
- "properties": {
179
- "google_store_package": {
180
- "type": "string"
181
- },
182
- "testing_instructions": {
183
- "type": "string"
184
- }
185
- },
186
- "required": ["testing_instructions"]
187
- }
188
- },
189
- "required": [
190
- "publisher",
191
- "app",
192
- "release",
193
- "solana_mobile_dapp_publisher_portal"
194
- ]
195
- }
@@ -1 +0,0 @@
1
- {"publisher":{"name":"<<YOUR_PUBLISHER_NAME>>","address":"","website":"<<URL_OF_PUBLISHER_WEBSITE>>","email":"<<EMAIL_ADDRESS_TO_CONTACT_PUBLISHER>>","media":[{"purpose":"icon","uri":"<<RELATIVE_PATH_TO_PUBLISHER_ICON>>"}]},"app":{"name":"<<APP_NAME>>","address":"","android_package":"<<ANDROID_PACKAGE_NAME>>","urls":{"license_url":"<<URL_OF_APP_LICENSE_OR_TERMS_OF_SERVICE>>","copyright_url":"<<URL_OF_COPYRIGHT_DETAILS_FOR_APP>>","privacy_policy_url":"<<URL_OF_APP_PRIVACY_POLICY>>","website":"<<URL_OF_APP_WEBSITE>>"},"media":[{"purpose":"icon","uri":"<<RELATIVE_PATH_TO_APP_ICON>>"}]},"release":{"address":"","media":[{"purpose":"icon","uri":"<<RELATIVE_PATH_TO_RELEASE_ICON>>"},{"purpose":"screenshot","uri":"<<RELATIVE_PATH_TO_SCREENSHOT>>"}],"files":[{"purpose":"install","uri":"<<RELATIVE_PATH_TO_APK>>"}],"catalog":{"en-US":{"name":"<<APP_NAME>>\n","long_description":"<<LONG_APP_DESCRIPTION>>\n","new_in_version":"<<WHATS_NEW_IN_THIS_VERSION>>\n","saga_features":"<<ANY_FEATURES_ONLY_AVAILBLE_WHEN_RUNNING_ON_SAGA>>\n"}}},"solana_mobile_dapp_publisher_portal":{"google_store_package":"<<ANDROID_PACKAGE_NAME_OF_GOOGLE_PLAY_STORE_VERSION_IF_DIFFERENT>>","testing_instructions":"<<TESTING_INSTRUCTIONS>>\n"}}
@@ -1 +0,0 @@
1
- {"type":"object","properties":{"publisher":{"type":"object","properties":{"name":{"type":"string"},"address":{"type":"string"},"website":{"type":"string"},"email":{"type":"string"},"media":{"type":"array","items":{"type":"object","properties":{"purpose":{"type":"string"},"uri":{"type":"string"}}}}}},"app":{"type":"object","properties":{"name":{"type":"string"},"address":{"type":"string"},"android_package":{"type":"string"},"urls":{"type":"object","properties":{"license_url":{"type":"string"},"copyright_url":{"type":"string"},"privacy_policy_url":{"type":"string"},"website":{"type":"string"}}},"media":{"type":"array","items":{"type":"object","properties":{"purpose":{"type":"string"},"uri":{"type":"string"}}}}}},"release":{"type":"object","properties":{"address":{"type":"string"},"media":{"type":"array","items":{"type":"object","properties":{"purpose":{"type":"string"},"uri":{"type":"string"}},"required":["purpose","uri"]}},"files":{"type":"array","items":{"type":"object","properties":{"purpose":{"type":"string"},"uri":{"type":"string"}}}},"catalog":{"type":"object","properties":{"en-US":{"type":"object","properties":{"name":{"type":"string"},"long_description":{"type":"string"},"new_in_version":{"type":"string"},"saga_features":{"type":"string"}}}}}}},"solana_mobile_dapp_publisher_portal":{"type":"object","properties":{"google_store_package":{"type":"string"},"testing_instructions":{"type":"string"}}}}}