@solana-mobile/dapp-store-cli 0.8.2 → 0.9.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.
- package/lib/CliSetup.js +5 -1
- package/lib/CliUtils.js +12 -5
- package/lib/commands/ValidateCommand.js +0 -3
- package/lib/config/PublishDetails.js +253 -8
- package/lib/package.json +5 -4
- package/package.json +5 -4
- package/src/CliSetup.ts +6 -1
- package/src/CliUtils.ts +14 -6
- package/src/commands/ValidateCommand.ts +0 -3
- package/src/config/PublishDetails.ts +85 -11
package/lib/CliSetup.js
CHANGED
|
@@ -126,7 +126,7 @@ import { Command } from "commander";
|
|
|
126
126
|
import { validateCommand } from "./commands/index.js";
|
|
127
127
|
import { createAppCommand, createPublisherCommand, createReleaseCommand } from "./commands/create/index.js";
|
|
128
128
|
import { publishRemoveCommand, publishSubmitCommand, publishSupportCommand, publishUpdateCommand } from "./commands/publish/index.js";
|
|
129
|
-
import { checkForSelfUpdate, checkSubmissionNetwork, Constants, dryRunSuccessMessage, generateNetworkSuffix, parseKeypair, showMessage } from "./CliUtils.js";
|
|
129
|
+
import { checkForSelfUpdate, checkSubmissionNetwork, Constants, dryRunSuccessMessage, generateNetworkSuffix, parseKeypair, showMessage, showNetworkWarningIfApplicable } from "./CliUtils.js";
|
|
130
130
|
import { LAMPORTS_PER_SOL } from "@solana/web3.js";
|
|
131
131
|
import * as dotenv from "dotenv";
|
|
132
132
|
import { initScaffold } from "./commands/scaffolding/index.js";
|
|
@@ -153,6 +153,7 @@ function resolveBuildToolsPath(buildToolsPath) {
|
|
|
153
153
|
* This method should be updated with each new release of the CLI, and just do nothing when there isn't anything to report
|
|
154
154
|
*/ function latestReleaseMessage() {
|
|
155
155
|
var messages = [
|
|
156
|
+
"- App details page now supports video files. (mp4 file format only and minimum resolution 720p)",
|
|
156
157
|
"- priority fee has been updated to ".concat(Constants.DEFAULT_PRIORITY_FEE, " lamports = ").concat(Constants.DEFAULT_PRIORITY_FEE / LAMPORTS_PER_SOL, ' SOL. To adjust this value use param "-p" or "--priority-fee-lamports"'),
|
|
157
158
|
"- At least 4 screenshots are now required to update or release a new app",
|
|
158
159
|
"- App icons should be exactly 512x512."
|
|
@@ -243,6 +244,7 @@ export var createPublisherCliCmd = createCliCmd.command("publisher").description
|
|
|
243
244
|
return _ts_generator(this, function(_state) {
|
|
244
245
|
switch(_state.label){
|
|
245
246
|
case 0:
|
|
247
|
+
showNetworkWarningIfApplicable(url);
|
|
246
248
|
latestReleaseMessage();
|
|
247
249
|
return [
|
|
248
250
|
4,
|
|
@@ -310,6 +312,7 @@ export var createAppCliCmd = createCliCmd.command("app").description("Create a a
|
|
|
310
312
|
return _ts_generator(this, function(_state) {
|
|
311
313
|
switch(_state.label){
|
|
312
314
|
case 0:
|
|
315
|
+
showNetworkWarningIfApplicable(url);
|
|
313
316
|
latestReleaseMessage();
|
|
314
317
|
return [
|
|
315
318
|
4,
|
|
@@ -387,6 +390,7 @@ export var createReleaseCliCmd = createCliCmd.command("release").description("Cr
|
|
|
387
390
|
return _ts_generator(this, function(_state) {
|
|
388
391
|
switch(_state.label){
|
|
389
392
|
case 0:
|
|
393
|
+
showNetworkWarningIfApplicable(url);
|
|
390
394
|
latestReleaseMessage();
|
|
391
395
|
return [
|
|
392
396
|
4,
|
package/lib/CliUtils.js
CHANGED
|
@@ -143,7 +143,7 @@ function _ts_generator(thisArg, body) {
|
|
|
143
143
|
import fs from "fs";
|
|
144
144
|
import { Keypair, PublicKey } from "@solana/web3.js";
|
|
145
145
|
import debugModule from "debug";
|
|
146
|
-
import {
|
|
146
|
+
import { IrysStorageDriver, keypairIdentity, Metaplex } from "@metaplex-foundation/js";
|
|
147
147
|
import updateNotifier from "update-notifier";
|
|
148
148
|
import cliPackage from "./package.json" assert {
|
|
149
149
|
type: "json"
|
|
@@ -159,7 +159,7 @@ export var Constants = function Constants() {
|
|
|
159
159
|
"use strict";
|
|
160
160
|
_class_call_check(this, Constants);
|
|
161
161
|
};
|
|
162
|
-
_define_property(Constants, "CLI_VERSION", "0.
|
|
162
|
+
_define_property(Constants, "CLI_VERSION", "0.9.0");
|
|
163
163
|
_define_property(Constants, "CONFIG_FILE_NAME", "config.yaml");
|
|
164
164
|
_define_property(Constants, "DEFAULT_RPC_DEVNET", "https://api.devnet.solana.com");
|
|
165
165
|
_define_property(Constants, "DEFAULT_PRIORITY_FEE", 500000);
|
|
@@ -262,6 +262,13 @@ export var generateNetworkSuffix = function(rpcUrl) {
|
|
|
262
262
|
export var dryRunSuccessMessage = function() {
|
|
263
263
|
showMessage("Dry run", "Dry run was successful", "standard");
|
|
264
264
|
};
|
|
265
|
+
export var showNetworkWarningIfApplicable = function(rpcUrl) {
|
|
266
|
+
if (isDevnet(rpcUrl)) {
|
|
267
|
+
showMessage("Devnet Mode", "Running on Devnet", "warning");
|
|
268
|
+
} else if (isTestnet(rpcUrl)) {
|
|
269
|
+
showMessage("Testnet Mode", "Running on Testnet", "warning");
|
|
270
|
+
}
|
|
271
|
+
};
|
|
265
272
|
export var showMessage = function() {
|
|
266
273
|
var titleMessage = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "", contentMessage = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "", type = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : "standard";
|
|
267
274
|
var color = "cyan";
|
|
@@ -300,13 +307,13 @@ export var getMetaplexInstance = function(connection, keypair) {
|
|
|
300
307
|
var bucketPlugin = awsStorage(awsClient, s3Mgr.s3Config.bucketName);
|
|
301
308
|
metaplex.use(bucketPlugin);
|
|
302
309
|
} else {
|
|
303
|
-
var
|
|
310
|
+
var irysStorageDriver = isDevnet ? new IrysStorageDriver(metaplex, {
|
|
304
311
|
address: "https://turbo.ardrive.dev",
|
|
305
312
|
providerUrl: Constants.DEFAULT_RPC_DEVNET
|
|
306
|
-
}) : new
|
|
313
|
+
}) : new IrysStorageDriver(metaplex, {
|
|
307
314
|
address: "https://turbo.ardrive.io"
|
|
308
315
|
});
|
|
309
|
-
metaplex.storage().setDriver(
|
|
316
|
+
metaplex.storage().setDriver(irysStorageDriver);
|
|
310
317
|
}
|
|
311
318
|
metaplex.storage().setDriver(new CachedStorageDriver(metaplex.storage().driver(), {
|
|
312
319
|
assetManifestPath: isDevnet ? "./.asset-manifest-devnet.json" : "./.asset-manifest.json"
|
|
@@ -153,7 +153,6 @@ export var validateCommand = function() {
|
|
|
153
153
|
}, metaplexFileReplacer, 2));
|
|
154
154
|
try {
|
|
155
155
|
validatePublisher(publisherJson);
|
|
156
|
-
console.info("Publisher JSON valid!");
|
|
157
156
|
} catch (e) {
|
|
158
157
|
;
|
|
159
158
|
errorMsg = (_e_message = e === null || e === void 0 ? void 0 : e.message) !== null && _e_message !== void 0 ? _e_message : "";
|
|
@@ -172,7 +171,6 @@ export var validateCommand = function() {
|
|
|
172
171
|
}, metaplexFileReplacer, 2));
|
|
173
172
|
try {
|
|
174
173
|
validateApp(appJson);
|
|
175
|
-
console.info("App JSON valid!");
|
|
176
174
|
} catch (e) {
|
|
177
175
|
;
|
|
178
176
|
errorMsg1 = (_e_message1 = e === null || e === void 0 ? void 0 : e.message) !== null && _e_message1 !== void 0 ? _e_message1 : "";
|
|
@@ -201,7 +199,6 @@ export var validateCommand = function() {
|
|
|
201
199
|
debug("releaseJson=", objStringified);
|
|
202
200
|
try {
|
|
203
201
|
validateRelease(JSON.parse(objStringified));
|
|
204
|
-
console.info("Release JSON valid!");
|
|
205
202
|
} catch (e) {
|
|
206
203
|
;
|
|
207
204
|
errorMsg2 = (_e_message2 = e === null || e === void 0 ? void 0 : e.message) !== null && _e_message2 !== void 0 ? _e_message2 : "";
|
|
@@ -219,6 +219,7 @@ import { Constants, showMessage } from "../CliUtils.js";
|
|
|
219
219
|
import util from "util";
|
|
220
220
|
import { imageSize } from "image-size";
|
|
221
221
|
import { exec } from "child_process";
|
|
222
|
+
import getVideoDimensions from "get-video-dimensions";
|
|
222
223
|
var runImgSize = util.promisify(imageSize);
|
|
223
224
|
var runExec = util.promisify(exec);
|
|
224
225
|
var AaptPrefixes = {
|
|
@@ -265,7 +266,7 @@ export var loadPublishDetails = function() {
|
|
|
265
266
|
}();
|
|
266
267
|
export var loadPublishDetailsWithChecks = function() {
|
|
267
268
|
var _ref = _async_to_generator(function() {
|
|
268
|
-
var buildToolsDir, _config_publisher_media_find, _config_publisher_media, _config_app_media_find, _config_app_media, _config_release_media_find, _config_release_media, _config_release_media1, config, apkEntry, apkPath, _, publisherIcon, iconPath, iconBuffer, appIcon, iconPath1, iconBuffer1, releaseIcon, iconPath2,
|
|
269
|
+
var buildToolsDir, _config_publisher_media_find, _config_publisher_media, _config_app_media_find, _config_app_media, _config_release_media_find, _config_release_media, _config_release_media1, _config_release_media2, config, apkEntry, apkPath, _, publisherIcon, iconPath, iconBuffer, appIcon, iconPath1, iconBuffer1, releaseIcon, iconPath2, screenshots, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, item, mediaPath, err, videos, _iteratorNormalCompletion1, _didIteratorError1, _iteratorError1, _iterator1, _step1, video, mediaPath1, err, googlePkg, pkgCompare;
|
|
269
270
|
var _arguments = arguments;
|
|
270
271
|
return _ts_generator(this, function(_state) {
|
|
271
272
|
switch(_state.label){
|
|
@@ -363,16 +364,145 @@ export var loadPublishDetailsWithChecks = function() {
|
|
|
363
364
|
throw new Error("Please specify at least one media entry of type icon in your configuration file");
|
|
364
365
|
}
|
|
365
366
|
config.release.media.forEach(function(item) {
|
|
366
|
-
var
|
|
367
|
-
if (!fs.existsSync(
|
|
368
|
-
throw new Error("
|
|
367
|
+
var mediaPath = path.join(process.cwd(), item.uri);
|
|
368
|
+
if (!fs.existsSync(mediaPath)) {
|
|
369
|
+
throw new Error("File doesnt exist: ".concat(item.uri, "."));
|
|
370
|
+
}
|
|
371
|
+
if (item.purpose == "screenshot" && !checkImageExtension(mediaPath)) {
|
|
372
|
+
throw new Error("Please ensure the file ".concat(item.uri, " is a jpeg, png, or webp file."));
|
|
373
|
+
}
|
|
374
|
+
if (item.purpose == "video" && !checkVideoExtension(mediaPath)) {
|
|
375
|
+
throw new Error("Please ensure the file ".concat(item.uri, " is a mp4."));
|
|
369
376
|
}
|
|
370
377
|
});
|
|
371
|
-
|
|
372
|
-
return asset.purpose === "screenshot"
|
|
378
|
+
screenshots = (_config_release_media1 = config.release.media) === null || _config_release_media1 === void 0 ? void 0 : _config_release_media1.filter(function(asset) {
|
|
379
|
+
return asset.purpose === "screenshot";
|
|
373
380
|
});
|
|
374
|
-
|
|
375
|
-
|
|
381
|
+
_iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
382
|
+
_state.label = 12;
|
|
383
|
+
case 12:
|
|
384
|
+
_state.trys.push([
|
|
385
|
+
12,
|
|
386
|
+
17,
|
|
387
|
+
18,
|
|
388
|
+
19
|
|
389
|
+
]);
|
|
390
|
+
_iterator = screenshots[Symbol.iterator]();
|
|
391
|
+
_state.label = 13;
|
|
392
|
+
case 13:
|
|
393
|
+
if (!!(_iteratorNormalCompletion = (_step = _iterator.next()).done)) return [
|
|
394
|
+
3,
|
|
395
|
+
16
|
|
396
|
+
];
|
|
397
|
+
item = _step.value;
|
|
398
|
+
mediaPath = path.join(process.cwd(), item.uri);
|
|
399
|
+
return [
|
|
400
|
+
4,
|
|
401
|
+
checkScreenshotSize(mediaPath)
|
|
402
|
+
];
|
|
403
|
+
case 14:
|
|
404
|
+
if (_state.sent()) {
|
|
405
|
+
throw new Error("Screenshot ".concat(mediaPath, " must be at least 1080px in width and height."));
|
|
406
|
+
}
|
|
407
|
+
_state.label = 15;
|
|
408
|
+
case 15:
|
|
409
|
+
_iteratorNormalCompletion = true;
|
|
410
|
+
return [
|
|
411
|
+
3,
|
|
412
|
+
13
|
|
413
|
+
];
|
|
414
|
+
case 16:
|
|
415
|
+
return [
|
|
416
|
+
3,
|
|
417
|
+
19
|
|
418
|
+
];
|
|
419
|
+
case 17:
|
|
420
|
+
err = _state.sent();
|
|
421
|
+
_didIteratorError = true;
|
|
422
|
+
_iteratorError = err;
|
|
423
|
+
return [
|
|
424
|
+
3,
|
|
425
|
+
19
|
|
426
|
+
];
|
|
427
|
+
case 18:
|
|
428
|
+
try {
|
|
429
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
430
|
+
_iterator.return();
|
|
431
|
+
}
|
|
432
|
+
} finally{
|
|
433
|
+
if (_didIteratorError) {
|
|
434
|
+
throw _iteratorError;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
return [
|
|
438
|
+
7
|
|
439
|
+
];
|
|
440
|
+
case 19:
|
|
441
|
+
videos = (_config_release_media2 = config.release.media) === null || _config_release_media2 === void 0 ? void 0 : _config_release_media2.filter(function(asset) {
|
|
442
|
+
return asset.purpose === "video";
|
|
443
|
+
});
|
|
444
|
+
_iteratorNormalCompletion1 = true, _didIteratorError1 = false, _iteratorError1 = undefined;
|
|
445
|
+
_state.label = 20;
|
|
446
|
+
case 20:
|
|
447
|
+
_state.trys.push([
|
|
448
|
+
20,
|
|
449
|
+
25,
|
|
450
|
+
26,
|
|
451
|
+
27
|
|
452
|
+
]);
|
|
453
|
+
_iterator1 = videos[Symbol.iterator]();
|
|
454
|
+
_state.label = 21;
|
|
455
|
+
case 21:
|
|
456
|
+
if (!!(_iteratorNormalCompletion1 = (_step1 = _iterator1.next()).done)) return [
|
|
457
|
+
3,
|
|
458
|
+
24
|
|
459
|
+
];
|
|
460
|
+
video = _step1.value;
|
|
461
|
+
mediaPath1 = path.join(process.cwd(), video.uri);
|
|
462
|
+
return [
|
|
463
|
+
4,
|
|
464
|
+
checkVideoSize(mediaPath1)
|
|
465
|
+
];
|
|
466
|
+
case 22:
|
|
467
|
+
if (_state.sent()) {
|
|
468
|
+
throw new Error("Video ".concat(mediaPath1, " must be at least 720px in width and height."));
|
|
469
|
+
}
|
|
470
|
+
_state.label = 23;
|
|
471
|
+
case 23:
|
|
472
|
+
_iteratorNormalCompletion1 = true;
|
|
473
|
+
return [
|
|
474
|
+
3,
|
|
475
|
+
21
|
|
476
|
+
];
|
|
477
|
+
case 24:
|
|
478
|
+
return [
|
|
479
|
+
3,
|
|
480
|
+
27
|
|
481
|
+
];
|
|
482
|
+
case 25:
|
|
483
|
+
err = _state.sent();
|
|
484
|
+
_didIteratorError1 = true;
|
|
485
|
+
_iteratorError1 = err;
|
|
486
|
+
return [
|
|
487
|
+
3,
|
|
488
|
+
27
|
|
489
|
+
];
|
|
490
|
+
case 26:
|
|
491
|
+
try {
|
|
492
|
+
if (!_iteratorNormalCompletion1 && _iterator1.return != null) {
|
|
493
|
+
_iterator1.return();
|
|
494
|
+
}
|
|
495
|
+
} finally{
|
|
496
|
+
if (_didIteratorError1) {
|
|
497
|
+
throw _iteratorError1;
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return [
|
|
501
|
+
7
|
|
502
|
+
];
|
|
503
|
+
case 27:
|
|
504
|
+
if (screenshots.length + videos.length < 4) {
|
|
505
|
+
throw new Error("At least 4 screenshots or videos are required for publishing a new release. Found only ".concat(screenshots.length + videos.length));
|
|
376
506
|
}
|
|
377
507
|
validateLocalizableResources(config);
|
|
378
508
|
googlePkg = config.solana_mobile_dapp_publisher_portal.google_store_package;
|
|
@@ -423,6 +553,10 @@ var checkImageExtension = function(uri) {
|
|
|
423
553
|
var fileExt = path.extname(uri).toLowerCase();
|
|
424
554
|
return fileExt == ".png" || fileExt == ".jpg" || fileExt == ".jpeg" || fileExt == ".webp";
|
|
425
555
|
};
|
|
556
|
+
var checkVideoExtension = function(uri) {
|
|
557
|
+
var fileExt = path.extname(uri).toLowerCase();
|
|
558
|
+
return fileExt == ".mp4";
|
|
559
|
+
};
|
|
426
560
|
/**
|
|
427
561
|
* We need to pre-check some things in the localized resources before we move forward
|
|
428
562
|
*/ var validateLocalizableResources = function(config) {
|
|
@@ -468,6 +602,52 @@ var checkIconDimensions = function() {
|
|
|
468
602
|
return _ref.apply(this, arguments);
|
|
469
603
|
};
|
|
470
604
|
}();
|
|
605
|
+
var checkScreenshotSize = function() {
|
|
606
|
+
var _ref = _async_to_generator(function(imagePath) {
|
|
607
|
+
var size, _size_width, _size_height;
|
|
608
|
+
return _ts_generator(this, function(_state) {
|
|
609
|
+
switch(_state.label){
|
|
610
|
+
case 0:
|
|
611
|
+
return [
|
|
612
|
+
4,
|
|
613
|
+
runImgSize(imagePath)
|
|
614
|
+
];
|
|
615
|
+
case 1:
|
|
616
|
+
size = _state.sent();
|
|
617
|
+
return [
|
|
618
|
+
2,
|
|
619
|
+
((_size_width = size === null || size === void 0 ? void 0 : size.width) !== null && _size_width !== void 0 ? _size_width : 0) < 1080 || ((_size_height = size === null || size === void 0 ? void 0 : size.height) !== null && _size_height !== void 0 ? _size_height : 0) < 1080
|
|
620
|
+
];
|
|
621
|
+
}
|
|
622
|
+
});
|
|
623
|
+
});
|
|
624
|
+
return function checkScreenshotSize(imagePath) {
|
|
625
|
+
return _ref.apply(this, arguments);
|
|
626
|
+
};
|
|
627
|
+
}();
|
|
628
|
+
var checkVideoSize = function() {
|
|
629
|
+
var _ref = _async_to_generator(function(imagePath) {
|
|
630
|
+
var size, _size_width, _size_height;
|
|
631
|
+
return _ts_generator(this, function(_state) {
|
|
632
|
+
switch(_state.label){
|
|
633
|
+
case 0:
|
|
634
|
+
return [
|
|
635
|
+
4,
|
|
636
|
+
getVideoDimensions(imagePath)
|
|
637
|
+
];
|
|
638
|
+
case 1:
|
|
639
|
+
size = _state.sent();
|
|
640
|
+
return [
|
|
641
|
+
2,
|
|
642
|
+
((_size_width = size === null || size === void 0 ? void 0 : size.width) !== null && _size_width !== void 0 ? _size_width : 0) < 720 || ((_size_height = size === null || size === void 0 ? void 0 : size.height) !== null && _size_height !== void 0 ? _size_height : 0) < 720
|
|
643
|
+
];
|
|
644
|
+
}
|
|
645
|
+
});
|
|
646
|
+
});
|
|
647
|
+
return function checkVideoSize(imagePath) {
|
|
648
|
+
return _ref.apply(this, arguments);
|
|
649
|
+
};
|
|
650
|
+
}();
|
|
471
651
|
var getAndroidDetails = function() {
|
|
472
652
|
var _ref = _async_to_generator(function(aaptDir, apkPath) {
|
|
473
653
|
var stdout, appPackage, versionCode, versionName, minSdk, permissions, locales, isDebuggable, _locales_values, localeArray, localesSrc, _appPackage_, _minSdk_, _versionCode_, _versionName_, _tmp, e;
|
|
@@ -517,6 +697,7 @@ var getAndroidDetails = function() {
|
|
|
517
697
|
if (localeArray.length >= 60) {
|
|
518
698
|
showMessage("The bundle apk claims supports for following locales", "Claim for supported locales::\n" + localeArray + "\nIf this release does not support all these locales the release may be rejected" + "\nSee details at https://developer.android.com/guide/topics/resources/multilingual-support#design for configuring the supported locales", "warning");
|
|
519
699
|
}
|
|
700
|
+
checkAbis(apkPath);
|
|
520
701
|
_tmp = {
|
|
521
702
|
android_package: (_appPackage_ = appPackage === null || appPackage === void 0 ? void 0 : appPackage[1]) !== null && _appPackage_ !== void 0 ? _appPackage_ : "",
|
|
522
703
|
min_sdk: parseInt((_minSdk_ = minSdk === null || minSdk === void 0 ? void 0 : minSdk[1]) !== null && _minSdk_ !== void 0 ? _minSdk_ : "0", 10),
|
|
@@ -554,6 +735,70 @@ var getAndroidDetails = function() {
|
|
|
554
735
|
return _ref.apply(this, arguments);
|
|
555
736
|
};
|
|
556
737
|
}();
|
|
738
|
+
var checkAbis = function() {
|
|
739
|
+
var _ref = _async_to_generator(function(apkPath) {
|
|
740
|
+
var stdout, amV7libs, x86libs, x8664libs, messages, e;
|
|
741
|
+
return _ts_generator(this, function(_state) {
|
|
742
|
+
switch(_state.label){
|
|
743
|
+
case 0:
|
|
744
|
+
_state.trys.push([
|
|
745
|
+
0,
|
|
746
|
+
2,
|
|
747
|
+
,
|
|
748
|
+
3
|
|
749
|
+
]);
|
|
750
|
+
return [
|
|
751
|
+
4,
|
|
752
|
+
runExec("zipinfo -s ".concat(apkPath, " | grep .so$"))
|
|
753
|
+
];
|
|
754
|
+
case 1:
|
|
755
|
+
stdout = _state.sent().stdout;
|
|
756
|
+
amV7libs = _to_consumable_array(stdout.matchAll(/lib\/armeabi-v7a\/(.*)/g)).flatMap(function(permission) {
|
|
757
|
+
return permission[1];
|
|
758
|
+
});
|
|
759
|
+
x86libs = _to_consumable_array(stdout.matchAll(/lib\/x86\/(.*)/g)).flatMap(function(permission) {
|
|
760
|
+
return permission[1];
|
|
761
|
+
});
|
|
762
|
+
x8664libs = _to_consumable_array(stdout.matchAll(/lib\/x86_64\/(.*)/g)).flatMap(function(permission) {
|
|
763
|
+
return permission[1];
|
|
764
|
+
});
|
|
765
|
+
if (amV7libs.length > 0 || x86libs.length > 0 || x8664libs.length > 0) {
|
|
766
|
+
messages = [
|
|
767
|
+
"Solana dApp Store only supports arm64-v8a abi.",
|
|
768
|
+
"Your apk file contains following unsupported abis"
|
|
769
|
+
].concat(_to_consumable_array(amV7libs.length > 0 ? [
|
|
770
|
+
"\narmeabi-v7a:\n" + amV7libs
|
|
771
|
+
] : []), _to_consumable_array(x86libs.length > 0 ? [
|
|
772
|
+
"\nx86:\n" + x86libs
|
|
773
|
+
] : []), _to_consumable_array(x8664libs.length > 0 ? [
|
|
774
|
+
"\nx86_64:\n" + x8664libs
|
|
775
|
+
] : []), [
|
|
776
|
+
"\n\nAlthough your app works fine on Saga, these library files are unused and increase the size of apk file making the download and update time longer for your app.",
|
|
777
|
+
"\n\nSee https://developer.android.com/games/optimize/64-bit#build-with-64-bit for how to optimize your app."
|
|
778
|
+
]).join("\n");
|
|
779
|
+
showMessage("Unsupported files found in apk", messages, "warning");
|
|
780
|
+
}
|
|
781
|
+
return [
|
|
782
|
+
3,
|
|
783
|
+
3
|
|
784
|
+
];
|
|
785
|
+
case 2:
|
|
786
|
+
e = _state.sent();
|
|
787
|
+
return [
|
|
788
|
+
3,
|
|
789
|
+
3
|
|
790
|
+
];
|
|
791
|
+
case 3:
|
|
792
|
+
return [
|
|
793
|
+
2
|
|
794
|
+
];
|
|
795
|
+
}
|
|
796
|
+
});
|
|
797
|
+
});
|
|
798
|
+
return function checkAbis(apkPath) {
|
|
799
|
+
return _ref.apply(this, arguments);
|
|
800
|
+
};
|
|
801
|
+
}();
|
|
557
802
|
export var extractCertFingerprint = function() {
|
|
558
803
|
var _ref = _async_to_generator(function(aaptDir, apkPath) {
|
|
559
804
|
var stdout, regex, match;
|
package/lib/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solana-mobile/dapp-store-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@metaplex-foundation/js": "0.18.3",
|
|
38
37
|
"@jest/globals": "^29.5.0",
|
|
39
38
|
"@jest/types": "^29.5.0",
|
|
39
|
+
"@metaplex-foundation/js": "0.20.0",
|
|
40
40
|
"@swc/jest": "^0.2.26",
|
|
41
41
|
"@types/commander": "^2.12.2",
|
|
42
42
|
"@types/debug": "^4.1.7",
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
},
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"@aws-sdk/client-s3": "^3.321.1",
|
|
55
|
-
"@metaplex-foundation/js-plugin-aws": "^0.
|
|
56
|
-
"@solana-mobile/dapp-store-publishing-tools": "workspace:0.
|
|
55
|
+
"@metaplex-foundation/js-plugin-aws": "^0.20.0",
|
|
56
|
+
"@solana-mobile/dapp-store-publishing-tools": "workspace:0.9.0",
|
|
57
57
|
"@solana/web3.js": "1.68.0",
|
|
58
58
|
"@types/semver": "^7.3.13",
|
|
59
59
|
"ajv": "^8.11.0",
|
|
@@ -64,6 +64,7 @@
|
|
|
64
64
|
"dotenv": "^16.0.3",
|
|
65
65
|
"esm": "^3.2.25",
|
|
66
66
|
"generate-schema": "^2.6.0",
|
|
67
|
+
"get-video-dimensions": "^1.0.0",
|
|
67
68
|
"image-size": "^1.0.2",
|
|
68
69
|
"js-yaml": "^4.1.0",
|
|
69
70
|
"semver": "^7.3.8",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solana-mobile/dapp-store-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"access": "public"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@metaplex-foundation/js": "0.18.3",
|
|
31
30
|
"@jest/globals": "^29.5.0",
|
|
32
31
|
"@jest/types": "^29.5.0",
|
|
32
|
+
"@metaplex-foundation/js": "0.20.0",
|
|
33
33
|
"@swc/jest": "^0.2.26",
|
|
34
34
|
"@types/commander": "^2.12.2",
|
|
35
35
|
"@types/debug": "^4.1.7",
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@aws-sdk/client-s3": "^3.321.1",
|
|
48
|
-
"@metaplex-foundation/js-plugin-aws": "^0.
|
|
49
|
-
"@solana-mobile/dapp-store-publishing-tools": "0.
|
|
48
|
+
"@metaplex-foundation/js-plugin-aws": "^0.20.0",
|
|
49
|
+
"@solana-mobile/dapp-store-publishing-tools": "0.9.0",
|
|
50
50
|
"@solana/web3.js": "1.68.0",
|
|
51
51
|
"@types/semver": "^7.3.13",
|
|
52
52
|
"ajv": "^8.11.0",
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"dotenv": "^16.0.3",
|
|
58
58
|
"esm": "^3.2.25",
|
|
59
59
|
"generate-schema": "^2.6.0",
|
|
60
|
+
"get-video-dimensions": "^1.0.0",
|
|
60
61
|
"image-size": "^1.0.2",
|
|
61
62
|
"js-yaml": "^4.1.0",
|
|
62
63
|
"semver": "^7.3.8",
|
package/src/CliSetup.ts
CHANGED
|
@@ -14,7 +14,8 @@ import {
|
|
|
14
14
|
dryRunSuccessMessage,
|
|
15
15
|
generateNetworkSuffix,
|
|
16
16
|
parseKeypair,
|
|
17
|
-
showMessage
|
|
17
|
+
showMessage,
|
|
18
|
+
showNetworkWarningIfApplicable
|
|
18
19
|
} from "./CliUtils.js";
|
|
19
20
|
import { LAMPORTS_PER_SOL } from "@solana/web3.js"
|
|
20
21
|
import * as dotenv from "dotenv";
|
|
@@ -49,6 +50,7 @@ function resolveBuildToolsPath(buildToolsPath: string | undefined) {
|
|
|
49
50
|
*/
|
|
50
51
|
function latestReleaseMessage() {
|
|
51
52
|
const messages = [
|
|
53
|
+
`- App details page now supports video files. (mp4 file format only and minimum resolution 720p)`,
|
|
52
54
|
`- priority fee has been updated to ${Constants.DEFAULT_PRIORITY_FEE} lamports = ${Constants.DEFAULT_PRIORITY_FEE / LAMPORTS_PER_SOL} SOL. To adjust this value use param "-p" or "--priority-fee-lamports"`,
|
|
53
55
|
`- At least 4 screenshots are now required to update or release a new app`,
|
|
54
56
|
`- App icons should be exactly 512x512.`
|
|
@@ -104,6 +106,7 @@ export const createPublisherCliCmd = createCliCmd
|
|
|
104
106
|
.option("-p, --priority-fee-lamports <priority-fee-lamports>", "Priority Fee lamports")
|
|
105
107
|
.action(async ({ keypair, url, dryRun, storageConfig, priorityFeeLamports }) => {
|
|
106
108
|
await tryWithErrorMessage(async () => {
|
|
109
|
+
showNetworkWarningIfApplicable(url)
|
|
107
110
|
latestReleaseMessage();
|
|
108
111
|
await checkForSelfUpdate();
|
|
109
112
|
|
|
@@ -141,6 +144,7 @@ export const createAppCliCmd = createCliCmd
|
|
|
141
144
|
.option("-p, --priority-fee-lamports <priority-fee-lamports>", "Priority Fee lamports")
|
|
142
145
|
.action(async ({ publisherMintAddress, keypair, url, dryRun, storageConfig, priorityFeeLamports }) => {
|
|
143
146
|
await tryWithErrorMessage(async () => {
|
|
147
|
+
showNetworkWarningIfApplicable(url)
|
|
144
148
|
latestReleaseMessage();
|
|
145
149
|
await checkForSelfUpdate();
|
|
146
150
|
|
|
@@ -194,6 +198,7 @@ export const createReleaseCliCmd = createCliCmd
|
|
|
194
198
|
.option("-p, --priority-fee-lamports <priority-fee-lamports>", "Priority Fee lamports")
|
|
195
199
|
.action(async ({ appMintAddress, keypair, url, dryRun, buildToolsPath, storageConfig, priorityFeeLamports }) => {
|
|
196
200
|
await tryWithErrorMessage(async () => {
|
|
201
|
+
showNetworkWarningIfApplicable(url)
|
|
197
202
|
latestReleaseMessage();
|
|
198
203
|
await checkForSelfUpdate();
|
|
199
204
|
|
package/src/CliUtils.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { Connection } from "@solana/web3.js";
|
|
|
3
3
|
import { Keypair, PublicKey } from "@solana/web3.js";
|
|
4
4
|
import debugModule from "debug";
|
|
5
5
|
import {
|
|
6
|
-
|
|
6
|
+
IrysStorageDriver,
|
|
7
7
|
keypairIdentity,
|
|
8
8
|
Metaplex,
|
|
9
9
|
} from "@metaplex-foundation/js";
|
|
@@ -18,7 +18,7 @@ import { awsStorage } from "@metaplex-foundation/js-plugin-aws";
|
|
|
18
18
|
import { S3StorageManager } from "./config/index.js";
|
|
19
19
|
|
|
20
20
|
export class Constants {
|
|
21
|
-
static CLI_VERSION = "0.
|
|
21
|
+
static CLI_VERSION = "0.9.0";
|
|
22
22
|
static CONFIG_FILE_NAME = "config.yaml";
|
|
23
23
|
static DEFAULT_RPC_DEVNET = "https://api.devnet.solana.com";
|
|
24
24
|
static DEFAULT_PRIORITY_FEE = 500000;
|
|
@@ -117,6 +117,14 @@ export const dryRunSuccessMessage = () => {
|
|
|
117
117
|
showMessage("Dry run", "Dry run was successful", "standard")
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
export const showNetworkWarningIfApplicable = (rpcUrl: string) => {
|
|
121
|
+
if (isDevnet(rpcUrl)) {
|
|
122
|
+
showMessage("Devnet Mode", "Running on Devnet", "warning")
|
|
123
|
+
} else if (isTestnet(rpcUrl)) {
|
|
124
|
+
showMessage("Testnet Mode", "Running on Testnet", "warning")
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
120
128
|
export const showMessage = (
|
|
121
129
|
titleMessage = "",
|
|
122
130
|
contentMessage = "",
|
|
@@ -167,16 +175,16 @@ export const getMetaplexInstance = (
|
|
|
167
175
|
const bucketPlugin = awsStorage(awsClient, s3Mgr.s3Config.bucketName);
|
|
168
176
|
metaplex.use(bucketPlugin);
|
|
169
177
|
} else {
|
|
170
|
-
const
|
|
171
|
-
? new
|
|
178
|
+
const irysStorageDriver = isDevnet
|
|
179
|
+
? new IrysStorageDriver(metaplex, {
|
|
172
180
|
address: "https://turbo.ardrive.dev",
|
|
173
181
|
providerUrl: Constants.DEFAULT_RPC_DEVNET,
|
|
174
182
|
})
|
|
175
|
-
: new
|
|
183
|
+
: new IrysStorageDriver(metaplex, {
|
|
176
184
|
address: "https://turbo.ardrive.io",
|
|
177
185
|
});
|
|
178
186
|
|
|
179
|
-
metaplex.storage().setDriver(
|
|
187
|
+
metaplex.storage().setDriver(irysStorageDriver);
|
|
180
188
|
}
|
|
181
189
|
|
|
182
190
|
metaplex.storage().setDriver(
|
|
@@ -36,7 +36,6 @@ export const validateCommand = async ({
|
|
|
36
36
|
|
|
37
37
|
try {
|
|
38
38
|
validatePublisher(publisherJson);
|
|
39
|
-
console.info(`Publisher JSON valid!`);
|
|
40
39
|
} catch (e) {
|
|
41
40
|
const errorMsg = (e as Error | null)?.message ?? "";
|
|
42
41
|
showMessage(
|
|
@@ -55,7 +54,6 @@ export const validateCommand = async ({
|
|
|
55
54
|
|
|
56
55
|
try {
|
|
57
56
|
validateApp(appJson);
|
|
58
|
-
console.info(`App JSON valid!`);
|
|
59
57
|
} catch (e) {
|
|
60
58
|
const errorMsg = (e as Error | null)?.message ?? "";
|
|
61
59
|
showMessage(
|
|
@@ -86,7 +84,6 @@ export const validateCommand = async ({
|
|
|
86
84
|
|
|
87
85
|
try {
|
|
88
86
|
validateRelease(JSON.parse(objStringified));
|
|
89
|
-
console.info(`Release JSON valid!`);
|
|
90
87
|
} catch (e) {
|
|
91
88
|
const errorMsg = (e as Error | null)?.message ?? "";
|
|
92
89
|
showMessage(
|
|
@@ -18,6 +18,7 @@ import { Constants, showMessage } from "../CliUtils.js";
|
|
|
18
18
|
import util from "util";
|
|
19
19
|
import { imageSize } from "image-size";
|
|
20
20
|
import { exec } from "child_process";
|
|
21
|
+
import getVideoDimensions from "get-video-dimensions";
|
|
21
22
|
|
|
22
23
|
const runImgSize = util.promisify(imageSize);
|
|
23
24
|
const runExec = util.promisify(exec);
|
|
@@ -125,23 +126,45 @@ export const loadPublishDetailsWithChecks = async (
|
|
|
125
126
|
}
|
|
126
127
|
|
|
127
128
|
config.release.media.forEach((item: PublishDetails["release"]["media"][0]) => {
|
|
128
|
-
const
|
|
129
|
-
if (!fs.existsSync(
|
|
130
|
-
throw new Error(`
|
|
129
|
+
const mediaPath = path.join(process.cwd(), item.uri);
|
|
130
|
+
if (!fs.existsSync(mediaPath)) {
|
|
131
|
+
throw new Error(`File doesnt exist: ${item.uri}.`)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (item.purpose == "screenshot" && !checkImageExtension(mediaPath)) {
|
|
135
|
+
throw new Error(`Please ensure the file ${item.uri} is a jpeg, png, or webp file.`)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (item.purpose == "video" && !checkVideoExtension(mediaPath)) {
|
|
139
|
+
throw new Error(`Please ensure the file ${item.uri} is a mp4.`)
|
|
131
140
|
}
|
|
132
141
|
}
|
|
133
142
|
);
|
|
134
143
|
|
|
135
|
-
const
|
|
136
|
-
(asset: any) => asset.purpose === "screenshot"
|
|
144
|
+
const screenshots = config.release.media?.filter(
|
|
145
|
+
(asset: any) => asset.purpose === "screenshot"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
for (const item of screenshots) {
|
|
149
|
+
const mediaPath = path.join(process.cwd(), item.uri);
|
|
150
|
+
if (await checkScreenshotSize(mediaPath)) {
|
|
151
|
+
throw new Error(`Screenshot ${mediaPath} must be at least 1080px in width and height.`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const videos = config.release.media?.filter(
|
|
156
|
+
(asset: any) => asset.purpose === "video"
|
|
137
157
|
)
|
|
138
158
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
159
|
+
for (const video of videos) {
|
|
160
|
+
const mediaPath = path.join(process.cwd(), video.uri);
|
|
161
|
+
if (await checkVideoSize(mediaPath)) {
|
|
162
|
+
throw new Error(`Video ${mediaPath} must be at least 720px in width and height.`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (screenshots.length + videos.length < 4) {
|
|
167
|
+
throw new Error(`At least 4 screenshots or videos are required for publishing a new release. Found only ${screenshots.length + videos.length}`)
|
|
145
168
|
}
|
|
146
169
|
|
|
147
170
|
validateLocalizableResources(config);
|
|
@@ -178,6 +201,13 @@ const checkImageExtension = (uri: string): boolean => {
|
|
|
178
201
|
);
|
|
179
202
|
};
|
|
180
203
|
|
|
204
|
+
const checkVideoExtension = (uri: string): boolean => {
|
|
205
|
+
const fileExt = path.extname(uri).toLowerCase();
|
|
206
|
+
return (
|
|
207
|
+
fileExt == ".mp4"
|
|
208
|
+
);
|
|
209
|
+
};
|
|
210
|
+
|
|
181
211
|
/**
|
|
182
212
|
* We need to pre-check some things in the localized resources before we move forward
|
|
183
213
|
*/
|
|
@@ -210,6 +240,19 @@ const checkIconDimensions = async (iconPath: string): Promise<boolean> => {
|
|
|
210
240
|
return size?.width != size?.height || (size?.width ?? 0) != 512;
|
|
211
241
|
};
|
|
212
242
|
|
|
243
|
+
const checkScreenshotSize = async (imagePath: string): Promise<boolean> => {
|
|
244
|
+
const size = await runImgSize(imagePath);
|
|
245
|
+
|
|
246
|
+
return (size?.width ?? 0) < 1080 || (size?.height ?? 0) < 1080;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const checkVideoSize = async (imagePath: string): Promise<boolean> => {
|
|
250
|
+
const size = await getVideoDimensions(imagePath);
|
|
251
|
+
|
|
252
|
+
return (size?.width ?? 0) < 720 || (size?.height ?? 0) < 720;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
|
|
213
256
|
const getAndroidDetails = async (
|
|
214
257
|
aaptDir: string,
|
|
215
258
|
apkPath: string
|
|
@@ -282,6 +325,8 @@ const getAndroidDetails = async (
|
|
|
282
325
|
);
|
|
283
326
|
}
|
|
284
327
|
|
|
328
|
+
checkAbis(apkPath);
|
|
329
|
+
|
|
285
330
|
return {
|
|
286
331
|
android_package: appPackage?.[1] ?? "",
|
|
287
332
|
min_sdk: parseInt(minSdk?.[1] ?? "0", 10),
|
|
@@ -300,6 +345,35 @@ const getAndroidDetails = async (
|
|
|
300
345
|
}
|
|
301
346
|
};
|
|
302
347
|
|
|
348
|
+
const checkAbis = async (apkPath: string) => {
|
|
349
|
+
try {
|
|
350
|
+
const { stdout } = await runExec(`zipinfo -s ${apkPath} | grep \.so$`);
|
|
351
|
+
const amV7libs = [...stdout.matchAll(/lib\/armeabi-v7a\/(.*)/g)].flatMap(permission => permission[1]);
|
|
352
|
+
const x86libs = [...stdout.matchAll(/lib\/x86\/(.*)/g)].flatMap(permission => permission[1]);
|
|
353
|
+
const x8664libs = [...stdout.matchAll(/lib\/x86_64\/(.*)/g)].flatMap(permission => permission[1]);
|
|
354
|
+
if (amV7libs.length > 0 || x86libs.length > 0 || x8664libs.length > 0) {
|
|
355
|
+
|
|
356
|
+
const messages = [
|
|
357
|
+
`Solana dApp Store only supports arm64-v8a abi.`,
|
|
358
|
+
`Your apk file contains following unsupported abis`,
|
|
359
|
+
... amV7libs.length > 0 ? [`\narmeabi-v7a:\n` + amV7libs] : [],
|
|
360
|
+
... x86libs.length > 0 ? [`\nx86:\n` + x86libs] : [],
|
|
361
|
+
... x8664libs.length > 0 ? [`\nx86_64:\n` + x8664libs] : [],
|
|
362
|
+
`\n\nAlthough your app works fine on Saga, these library files are unused and increase the size of apk file making the download and update time longer for your app.`,
|
|
363
|
+
`\n\nSee https://developer.android.com/games/optimize/64-bit#build-with-64-bit for how to optimize your app.`,
|
|
364
|
+
].join('\n')
|
|
365
|
+
|
|
366
|
+
showMessage(
|
|
367
|
+
`Unsupported files found in apk`,
|
|
368
|
+
messages,
|
|
369
|
+
`warning`
|
|
370
|
+
)
|
|
371
|
+
}
|
|
372
|
+
} catch (e) {
|
|
373
|
+
// Ignore this error.
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
303
377
|
export const extractCertFingerprint = async (aaptDir: string, apkPath: string): Promise<string> => {
|
|
304
378
|
const { stdout } = await runExec(`${aaptDir}/apksigner verify --print-certs -v "${apkPath}"`);
|
|
305
379
|
|