@solana-mobile/dapp-store-cli 0.8.0 → 0.8.2
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 +50 -44
- package/lib/CliUtils.js +2 -2
- package/lib/commands/create/CreateCliApp.js +1 -3
- package/lib/commands/create/CreateCliPublisher.js +6 -4
- package/lib/commands/create/CreateCliRelease.js +37 -16
- package/lib/commands/publish/PublishCliSubmit.js +16 -3
- package/lib/commands/publish/PublishCliUpdate.js +16 -3
- package/lib/config/PublishDetails.js +23 -12
- package/lib/generated/config_obj.json +1 -1
- package/lib/package.json +2 -2
- package/lib/prebuild_schema/publishing_source.yaml +9 -1
- package/package.json +2 -2
- package/src/CliSetup.ts +26 -13
- package/src/CliUtils.ts +2 -2
- package/src/commands/ValidateCommand.ts +0 -1
- package/src/commands/create/CreateCliApp.ts +1 -0
- package/src/commands/create/CreateCliPublisher.ts +3 -0
- package/src/commands/create/CreateCliRelease.ts +33 -9
- package/src/commands/publish/PublishCliSubmit.ts +11 -1
- package/src/commands/publish/PublishCliUpdate.ts +10 -1
- package/src/config/PublishDetails.ts +41 -9
- package/src/prebuild_schema/publishing_source.yaml +9 -1
package/lib/CliSetup.js
CHANGED
|
@@ -536,17 +536,12 @@ publishCommand.command("submit").description("Submit a new app to the Solana Mob
|
|
|
536
536
|
];
|
|
537
537
|
case 1:
|
|
538
538
|
_state.sent();
|
|
539
|
-
|
|
540
|
-
4,
|
|
541
|
-
checkSubmissionNetwork(url)
|
|
542
|
-
];
|
|
543
|
-
case 2:
|
|
544
|
-
_state.sent();
|
|
539
|
+
checkSubmissionNetwork(url);
|
|
545
540
|
return [
|
|
546
541
|
4,
|
|
547
542
|
loadPublishDetails(Constants.getConfigFilePath())
|
|
548
543
|
];
|
|
549
|
-
case
|
|
544
|
+
case 2:
|
|
550
545
|
config = _state.sent();
|
|
551
546
|
if (!hasAddressInConfig(config.release) && !releaseMintAddress) {
|
|
552
547
|
throw new Error("Either specify a release mint address in the config file or specify as a CLI argument to this command.");
|
|
@@ -554,8 +549,32 @@ publishCommand.command("submit").description("Submit a new app to the Solana Mob
|
|
|
554
549
|
signer = parseKeypair(keypair);
|
|
555
550
|
if (!signer) return [
|
|
556
551
|
3,
|
|
557
|
-
|
|
552
|
+
7
|
|
553
|
+
];
|
|
554
|
+
if (!(config.lastUpdatedVersionOnStore != null && config.lastSubmittedVersionOnChain.address != null)) return [
|
|
555
|
+
3,
|
|
556
|
+
4
|
|
558
557
|
];
|
|
558
|
+
return [
|
|
559
|
+
4,
|
|
560
|
+
publishUpdateCommand({
|
|
561
|
+
appMintAddress: appMintAddress,
|
|
562
|
+
releaseMintAddress: releaseMintAddress,
|
|
563
|
+
signer: signer,
|
|
564
|
+
url: url,
|
|
565
|
+
dryRun: dryRun,
|
|
566
|
+
compliesWithSolanaDappStorePolicies: compliesWithSolanaDappStorePolicies,
|
|
567
|
+
requestorIsAuthorized: requestorIsAuthorized,
|
|
568
|
+
critical: false
|
|
569
|
+
})
|
|
570
|
+
];
|
|
571
|
+
case 3:
|
|
572
|
+
_state.sent();
|
|
573
|
+
return [
|
|
574
|
+
3,
|
|
575
|
+
6
|
|
576
|
+
];
|
|
577
|
+
case 4:
|
|
559
578
|
return [
|
|
560
579
|
4,
|
|
561
580
|
publishSubmitCommand({
|
|
@@ -568,15 +587,17 @@ publishCommand.command("submit").description("Submit a new app to the Solana Mob
|
|
|
568
587
|
requestorIsAuthorized: requestorIsAuthorized
|
|
569
588
|
})
|
|
570
589
|
];
|
|
571
|
-
case
|
|
590
|
+
case 5:
|
|
572
591
|
_state.sent();
|
|
592
|
+
_state.label = 6;
|
|
593
|
+
case 6:
|
|
573
594
|
if (dryRun) {
|
|
574
595
|
dryRunSuccessMessage();
|
|
575
596
|
} else {
|
|
576
597
|
showMessage("Success", "Successfully submitted to the Solana Mobile dApp publisher portal");
|
|
577
598
|
}
|
|
578
|
-
_state.label =
|
|
579
|
-
case
|
|
599
|
+
_state.label = 7;
|
|
600
|
+
case 7:
|
|
580
601
|
return [
|
|
581
602
|
2
|
|
582
603
|
];
|
|
@@ -616,17 +637,12 @@ publishCommand.command("update").description("Update an existing app on the Sola
|
|
|
616
637
|
];
|
|
617
638
|
case 1:
|
|
618
639
|
_state.sent();
|
|
619
|
-
|
|
620
|
-
4,
|
|
621
|
-
checkSubmissionNetwork(url)
|
|
622
|
-
];
|
|
623
|
-
case 2:
|
|
624
|
-
_state.sent();
|
|
640
|
+
checkSubmissionNetwork(url);
|
|
625
641
|
return [
|
|
626
642
|
4,
|
|
627
643
|
loadPublishDetails(Constants.getConfigFilePath())
|
|
628
644
|
];
|
|
629
|
-
case
|
|
645
|
+
case 2:
|
|
630
646
|
config = _state.sent();
|
|
631
647
|
if (!hasAddressInConfig(config.release) && !releaseMintAddress) {
|
|
632
648
|
throw new Error("Either specify a release mint address in the config file or specify as a CLI argument to this command.");
|
|
@@ -634,7 +650,7 @@ publishCommand.command("update").description("Update an existing app on the Sola
|
|
|
634
650
|
signer = parseKeypair(keypair);
|
|
635
651
|
if (!signer) return [
|
|
636
652
|
3,
|
|
637
|
-
|
|
653
|
+
4
|
|
638
654
|
];
|
|
639
655
|
return [
|
|
640
656
|
4,
|
|
@@ -649,15 +665,15 @@ publishCommand.command("update").description("Update an existing app on the Sola
|
|
|
649
665
|
critical: critical
|
|
650
666
|
})
|
|
651
667
|
];
|
|
652
|
-
case
|
|
668
|
+
case 3:
|
|
653
669
|
_state.sent();
|
|
654
670
|
if (dryRun) {
|
|
655
671
|
dryRunSuccessMessage();
|
|
656
672
|
} else {
|
|
657
673
|
showMessage("Success", "dApp successfully updated on the publisher portal");
|
|
658
674
|
}
|
|
659
|
-
_state.label =
|
|
660
|
-
case
|
|
675
|
+
_state.label = 4;
|
|
676
|
+
case 4:
|
|
661
677
|
return [
|
|
662
678
|
2
|
|
663
679
|
];
|
|
@@ -697,17 +713,12 @@ publishCommand.command("remove").description("Remove an existing app from the So
|
|
|
697
713
|
];
|
|
698
714
|
case 1:
|
|
699
715
|
_state.sent();
|
|
700
|
-
|
|
701
|
-
4,
|
|
702
|
-
checkSubmissionNetwork(url)
|
|
703
|
-
];
|
|
704
|
-
case 2:
|
|
705
|
-
_state.sent();
|
|
716
|
+
checkSubmissionNetwork(url);
|
|
706
717
|
return [
|
|
707
718
|
4,
|
|
708
719
|
loadPublishDetails(Constants.getConfigFilePath())
|
|
709
720
|
];
|
|
710
|
-
case
|
|
721
|
+
case 2:
|
|
711
722
|
config = _state.sent();
|
|
712
723
|
if (!hasAddressInConfig(config.release) && !releaseMintAddress) {
|
|
713
724
|
throw new Error("Either specify a release mint address in the config file or specify as a CLI argument to this command.");
|
|
@@ -715,7 +726,7 @@ publishCommand.command("remove").description("Remove an existing app from the So
|
|
|
715
726
|
signer = parseKeypair(keypair);
|
|
716
727
|
if (!signer) return [
|
|
717
728
|
3,
|
|
718
|
-
|
|
729
|
+
4
|
|
719
730
|
];
|
|
720
731
|
return [
|
|
721
732
|
4,
|
|
@@ -729,15 +740,15 @@ publishCommand.command("remove").description("Remove an existing app from the So
|
|
|
729
740
|
critical: critical
|
|
730
741
|
})
|
|
731
742
|
];
|
|
732
|
-
case
|
|
743
|
+
case 3:
|
|
733
744
|
_state.sent();
|
|
734
745
|
if (dryRun) {
|
|
735
746
|
dryRunSuccessMessage();
|
|
736
747
|
} else {
|
|
737
748
|
showMessage("Success", "dApp successfully removed from the publisher portal");
|
|
738
749
|
}
|
|
739
|
-
_state.label =
|
|
740
|
-
case
|
|
750
|
+
_state.label = 4;
|
|
751
|
+
case 4:
|
|
741
752
|
return [
|
|
742
753
|
2
|
|
743
754
|
];
|
|
@@ -777,17 +788,12 @@ publishCommand.command("support <request_details>").description("Submit a suppor
|
|
|
777
788
|
];
|
|
778
789
|
case 1:
|
|
779
790
|
_state.sent();
|
|
780
|
-
|
|
781
|
-
4,
|
|
782
|
-
checkSubmissionNetwork(url)
|
|
783
|
-
];
|
|
784
|
-
case 2:
|
|
785
|
-
_state.sent();
|
|
791
|
+
checkSubmissionNetwork(url);
|
|
786
792
|
return [
|
|
787
793
|
4,
|
|
788
794
|
loadPublishDetails(Constants.getConfigFilePath())
|
|
789
795
|
];
|
|
790
|
-
case
|
|
796
|
+
case 2:
|
|
791
797
|
config = _state.sent();
|
|
792
798
|
if (!hasAddressInConfig(config.release) && !releaseMintAddress) {
|
|
793
799
|
throw new Error("Either specify a release mint address in the config file or specify as a CLI argument to this command.");
|
|
@@ -795,7 +801,7 @@ publishCommand.command("support <request_details>").description("Submit a suppor
|
|
|
795
801
|
signer = parseKeypair(keypair);
|
|
796
802
|
if (!signer) return [
|
|
797
803
|
3,
|
|
798
|
-
|
|
804
|
+
4
|
|
799
805
|
];
|
|
800
806
|
return [
|
|
801
807
|
4,
|
|
@@ -809,15 +815,15 @@ publishCommand.command("support <request_details>").description("Submit a suppor
|
|
|
809
815
|
requestDetails: requestDetails
|
|
810
816
|
})
|
|
811
817
|
];
|
|
812
|
-
case
|
|
818
|
+
case 3:
|
|
813
819
|
_state.sent();
|
|
814
820
|
if (dryRun) {
|
|
815
821
|
dryRunSuccessMessage();
|
|
816
822
|
} else {
|
|
817
823
|
showMessage("Success", "Support request sent successfully");
|
|
818
824
|
}
|
|
819
|
-
_state.label =
|
|
820
|
-
case
|
|
825
|
+
_state.label = 4;
|
|
826
|
+
case 4:
|
|
821
827
|
return [
|
|
822
828
|
2
|
|
823
829
|
];
|
package/lib/CliUtils.js
CHANGED
|
@@ -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.8.2");
|
|
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);
|
|
@@ -185,7 +185,7 @@ export var checkForSelfUpdate = function() {
|
|
|
185
185
|
latestVer = new ver.SemVer(updateInfo.latest);
|
|
186
186
|
currentVer = new ver.SemVer(updateInfo.current);
|
|
187
187
|
if (latestVer.major > currentVer.major || latestVer.minor > currentVer.minor) {
|
|
188
|
-
throw new Error("Please update to the latest version of the dApp Store CLI before proceeding.");
|
|
188
|
+
throw new Error("Please update to the latest version of the dApp Store CLI before proceeding.\nCurrent version is ".concat(currentVer.raw, "\nLatest version is ").concat(latestVer.raw));
|
|
189
189
|
}
|
|
190
190
|
return [
|
|
191
191
|
2
|
|
@@ -213,9 +213,7 @@ var createPublisherNft = function() {
|
|
|
213
213
|
2
|
|
214
214
|
];
|
|
215
215
|
case 8:
|
|
216
|
-
|
|
217
|
-
2
|
|
218
|
-
];
|
|
216
|
+
throw new Error("Unable to mint publisher NFT");
|
|
219
217
|
}
|
|
220
218
|
});
|
|
221
219
|
});
|
|
@@ -272,7 +270,11 @@ export var createPublisherCommand = function() {
|
|
|
272
270
|
];
|
|
273
271
|
case 4:
|
|
274
272
|
return [
|
|
275
|
-
2
|
|
273
|
+
2,
|
|
274
|
+
{
|
|
275
|
+
publisherAddress: "",
|
|
276
|
+
transactionSignature: ""
|
|
277
|
+
}
|
|
276
278
|
];
|
|
277
279
|
}
|
|
278
280
|
});
|
|
@@ -152,6 +152,8 @@ function _ts_generator(thisArg, body) {
|
|
|
152
152
|
}
|
|
153
153
|
import { createRelease } from "@solana-mobile/dapp-store-publishing-tools";
|
|
154
154
|
import { Connection, Keypair, PublicKey, sendAndConfirmTransaction } from "@solana/web3.js";
|
|
155
|
+
import fs from "fs";
|
|
156
|
+
import { createHash } from "crypto";
|
|
155
157
|
import { Constants, getMetaplexInstance, showMessage } from "../../CliUtils.js";
|
|
156
158
|
import { loadPublishDetailsWithChecks, writeToPublishDetails } from "../../config/PublishDetails.js";
|
|
157
159
|
var createReleaseNft = function() {
|
|
@@ -244,9 +246,7 @@ var createReleaseNft = function() {
|
|
|
244
246
|
2
|
|
245
247
|
];
|
|
246
248
|
case 8:
|
|
247
|
-
|
|
248
|
-
2
|
|
249
|
-
];
|
|
249
|
+
throw new Error("Unable to mint release NFT");
|
|
250
250
|
}
|
|
251
251
|
});
|
|
252
252
|
});
|
|
@@ -256,7 +256,7 @@ var createReleaseNft = function() {
|
|
|
256
256
|
}();
|
|
257
257
|
export var createReleaseCommand = function() {
|
|
258
258
|
var _ref = _async_to_generator(function(param) {
|
|
259
|
-
var appMintAddress, buildToolsPath, signer, url, _param_dryRun, dryRun, storageParams, _param_priorityFeeLamports, priorityFeeLamports, connection,
|
|
259
|
+
var appMintAddress, buildToolsPath, signer, url, _param_dryRun, dryRun, storageParams, _param_priorityFeeLamports, priorityFeeLamports, connection, config, apkEntry, mediaBuffer, hash, _config_app_address, _ref, releaseAddress, transactionSignature;
|
|
260
260
|
return _ts_generator(this, function(_state) {
|
|
261
261
|
switch(_state.label){
|
|
262
262
|
case 0:
|
|
@@ -267,38 +267,59 @@ export var createReleaseCommand = function() {
|
|
|
267
267
|
loadPublishDetailsWithChecks(buildToolsPath)
|
|
268
268
|
];
|
|
269
269
|
case 1:
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
270
|
+
config = _state.sent();
|
|
271
|
+
apkEntry = config.release.files.find(function(asset) {
|
|
272
|
+
return asset.purpose === "install";
|
|
273
|
+
});
|
|
274
|
+
return [
|
|
275
|
+
4,
|
|
276
|
+
fs.promises.readFile(apkEntry.uri)
|
|
277
|
+
];
|
|
278
|
+
case 2:
|
|
279
|
+
mediaBuffer = _state.sent();
|
|
280
|
+
hash = createHash("sha256").update(mediaBuffer).digest("base64");
|
|
281
|
+
if (config.lastSubmittedVersionOnChain != null && hash === config.lastSubmittedVersionOnChain.apk_hash) {
|
|
282
|
+
throw new Error("The last created release used the same apk file.");
|
|
283
|
+
}
|
|
284
|
+
if (config.lastSubmittedVersionOnChain != null && config.release.android_details.version_code <= config.lastSubmittedVersionOnChain.version_code) {
|
|
285
|
+
throw new Error("Each release NFT should have higher version code than previous minted release NFT.\nLast released version code is ".concat(config.lastSubmittedVersionOnChain.version_code, ".\nCurrent version code from apk file is ").concat(config.release.android_details.version_code));
|
|
286
|
+
}
|
|
287
|
+
if (config.app.android_package != config.release.android_details.android_package) {
|
|
288
|
+
throw new Error("App package name and release package name do not match.\nApp release specifies " + config.app.android_package + " while release specifies " + config.release.android_details.android_package);
|
|
273
289
|
}
|
|
274
290
|
if (!!dryRun) return [
|
|
275
291
|
3,
|
|
276
|
-
|
|
292
|
+
5
|
|
277
293
|
];
|
|
278
294
|
return [
|
|
279
295
|
4,
|
|
280
296
|
createReleaseNft({
|
|
281
|
-
appMintAddress: (
|
|
297
|
+
appMintAddress: (_config_app_address = config.app.address) !== null && _config_app_address !== void 0 ? _config_app_address : appMintAddress,
|
|
282
298
|
connection: connection,
|
|
283
299
|
publisher: signer,
|
|
284
|
-
releaseDetails: _object_spread({}, release),
|
|
285
|
-
appDetails: app,
|
|
286
|
-
publisherDetails: publisher,
|
|
300
|
+
releaseDetails: _object_spread({}, config.release),
|
|
301
|
+
appDetails: config.app,
|
|
302
|
+
publisherDetails: config.publisher,
|
|
287
303
|
storageParams: storageParams,
|
|
288
304
|
priorityFeeLamports: priorityFeeLamports
|
|
289
305
|
})
|
|
290
306
|
];
|
|
291
|
-
case
|
|
292
|
-
|
|
307
|
+
case 3:
|
|
308
|
+
_ref = _state.sent(), releaseAddress = _ref.releaseAddress, transactionSignature = _ref.transactionSignature;
|
|
293
309
|
return [
|
|
294
310
|
4,
|
|
295
311
|
writeToPublishDetails({
|
|
296
312
|
release: {
|
|
297
313
|
address: releaseAddress
|
|
314
|
+
},
|
|
315
|
+
lastSubmittedVersionOnChain: {
|
|
316
|
+
address: releaseAddress,
|
|
317
|
+
version_code: config.release.android_details.version_code,
|
|
318
|
+
apk_hash: hash
|
|
298
319
|
}
|
|
299
320
|
})
|
|
300
321
|
];
|
|
301
|
-
case
|
|
322
|
+
case 4:
|
|
302
323
|
_state.sent();
|
|
303
324
|
return [
|
|
304
325
|
2,
|
|
@@ -307,7 +328,7 @@ export var createReleaseCommand = function() {
|
|
|
307
328
|
transactionSignature: transactionSignature
|
|
308
329
|
}
|
|
309
330
|
];
|
|
310
|
-
case
|
|
331
|
+
case 5:
|
|
311
332
|
return [
|
|
312
333
|
2
|
|
313
334
|
];
|
|
@@ -126,10 +126,10 @@ import { Connection } from "@solana/web3.js";
|
|
|
126
126
|
import { publishSubmit } from "@solana-mobile/dapp-store-publishing-tools";
|
|
127
127
|
import nacl from "tweetnacl";
|
|
128
128
|
import { checkMintedStatus, showMessage } from "../../CliUtils.js";
|
|
129
|
-
import { loadPublishDetailsWithChecks } from "../../config/PublishDetails.js";
|
|
129
|
+
import { loadPublishDetailsWithChecks, writeToPublishDetails } from "../../config/PublishDetails.js";
|
|
130
130
|
export var publishSubmitCommand = function() {
|
|
131
131
|
var _ref = _async_to_generator(function(param) {
|
|
132
|
-
var appMintAddress, releaseMintAddress, signer, url, _param_dryRun, dryRun, _param_compliesWithSolanaDappStorePolicies, compliesWithSolanaDappStorePolicies, _param_requestorIsAuthorized, requestorIsAuthorized, connection, _ref, publisherDetails, appDetails, releaseDetails, solanaMobileDappPublisherPortalDetails, sign, pubAddr, appAddr, releaseAddr;
|
|
132
|
+
var appMintAddress, releaseMintAddress, signer, url, _param_dryRun, dryRun, _param_compliesWithSolanaDappStorePolicies, compliesWithSolanaDappStorePolicies, _param_requestorIsAuthorized, requestorIsAuthorized, connection, _ref, publisherDetails, appDetails, releaseDetails, solanaMobileDappPublisherPortalDetails, lastUpdatedVersionOnStore, sign, pubAddr, appAddr, releaseAddr;
|
|
133
133
|
return _ts_generator(this, function(_state) {
|
|
134
134
|
switch(_state.label){
|
|
135
135
|
case 0:
|
|
@@ -152,13 +152,16 @@ export var publishSubmitCommand = function() {
|
|
|
152
152
|
loadPublishDetailsWithChecks()
|
|
153
153
|
];
|
|
154
154
|
case 1:
|
|
155
|
-
_ref = _state.sent(), publisherDetails = _ref.publisher, appDetails = _ref.app, releaseDetails = _ref.release, solanaMobileDappPublisherPortalDetails = _ref.solana_mobile_dapp_publisher_portal;
|
|
155
|
+
_ref = _state.sent(), publisherDetails = _ref.publisher, appDetails = _ref.app, releaseDetails = _ref.release, solanaMobileDappPublisherPortalDetails = _ref.solana_mobile_dapp_publisher_portal, lastUpdatedVersionOnStore = _ref.lastUpdatedVersionOnStore;
|
|
156
156
|
sign = function(buf) {
|
|
157
157
|
return nacl.sign(buf, signer.secretKey);
|
|
158
158
|
};
|
|
159
159
|
pubAddr = publisherDetails.address;
|
|
160
160
|
appAddr = appMintAddress !== null && appMintAddress !== void 0 ? appMintAddress : appDetails.address;
|
|
161
161
|
releaseAddr = releaseMintAddress !== null && releaseMintAddress !== void 0 ? releaseMintAddress : releaseDetails.address;
|
|
162
|
+
if (lastUpdatedVersionOnStore != null && releaseAddr === lastUpdatedVersionOnStore.address) {
|
|
163
|
+
throw new Error("You've already submitted this version for review.");
|
|
164
|
+
}
|
|
162
165
|
return [
|
|
163
166
|
4,
|
|
164
167
|
checkMintedStatus(connection, pubAddr, appAddr, releaseAddr)
|
|
@@ -180,6 +183,16 @@ export var publishSubmitCommand = function() {
|
|
|
180
183
|
}, dryRun)
|
|
181
184
|
];
|
|
182
185
|
case 3:
|
|
186
|
+
_state.sent();
|
|
187
|
+
return [
|
|
188
|
+
4,
|
|
189
|
+
writeToPublishDetails({
|
|
190
|
+
lastUpdatedVersionOnStore: {
|
|
191
|
+
address: releaseAddr
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
];
|
|
195
|
+
case 4:
|
|
183
196
|
_state.sent();
|
|
184
197
|
return [
|
|
185
198
|
2
|
|
@@ -126,10 +126,10 @@ import { Connection } from "@solana/web3.js";
|
|
|
126
126
|
import { publishUpdate } from "@solana-mobile/dapp-store-publishing-tools";
|
|
127
127
|
import { checkMintedStatus, showMessage } from "../../CliUtils.js";
|
|
128
128
|
import nacl from "tweetnacl";
|
|
129
|
-
import { loadPublishDetailsWithChecks } from "../../config/PublishDetails.js";
|
|
129
|
+
import { loadPublishDetailsWithChecks, writeToPublishDetails } from "../../config/PublishDetails.js";
|
|
130
130
|
export var publishUpdateCommand = function() {
|
|
131
131
|
var _ref = _async_to_generator(function(param) {
|
|
132
|
-
var appMintAddress, releaseMintAddress, signer, url, _param_dryRun, dryRun, _param_compliesWithSolanaDappStorePolicies, compliesWithSolanaDappStorePolicies, _param_requestorIsAuthorized, requestorIsAuthorized, _param_critical, critical, connection, _ref, publisherDetails, appDetails, releaseDetails, solanaMobileDappPublisherPortalDetails, sign, pubAddr, appAddr, releaseAddr;
|
|
132
|
+
var appMintAddress, releaseMintAddress, signer, url, _param_dryRun, dryRun, _param_compliesWithSolanaDappStorePolicies, compliesWithSolanaDappStorePolicies, _param_requestorIsAuthorized, requestorIsAuthorized, _param_critical, critical, connection, _ref, publisherDetails, appDetails, releaseDetails, solanaMobileDappPublisherPortalDetails, lastUpdatedVersionOnStore, sign, pubAddr, appAddr, releaseAddr;
|
|
133
133
|
return _ts_generator(this, function(_state) {
|
|
134
134
|
switch(_state.label){
|
|
135
135
|
case 0:
|
|
@@ -152,13 +152,16 @@ export var publishUpdateCommand = function() {
|
|
|
152
152
|
loadPublishDetailsWithChecks()
|
|
153
153
|
];
|
|
154
154
|
case 1:
|
|
155
|
-
_ref = _state.sent(), publisherDetails = _ref.publisher, appDetails = _ref.app, releaseDetails = _ref.release, solanaMobileDappPublisherPortalDetails = _ref.solana_mobile_dapp_publisher_portal;
|
|
155
|
+
_ref = _state.sent(), publisherDetails = _ref.publisher, appDetails = _ref.app, releaseDetails = _ref.release, solanaMobileDappPublisherPortalDetails = _ref.solana_mobile_dapp_publisher_portal, lastUpdatedVersionOnStore = _ref.lastUpdatedVersionOnStore;
|
|
156
156
|
sign = function(buf) {
|
|
157
157
|
return nacl.sign(buf, signer.secretKey);
|
|
158
158
|
};
|
|
159
159
|
pubAddr = publisherDetails.address;
|
|
160
160
|
appAddr = appMintAddress !== null && appMintAddress !== void 0 ? appMintAddress : appDetails.address;
|
|
161
161
|
releaseAddr = releaseMintAddress !== null && releaseMintAddress !== void 0 ? releaseMintAddress : releaseDetails.address;
|
|
162
|
+
if (lastUpdatedVersionOnStore != null && releaseAddr === lastUpdatedVersionOnStore.address) {
|
|
163
|
+
throw new Error("You've already submitted this version for review.");
|
|
164
|
+
}
|
|
162
165
|
return [
|
|
163
166
|
4,
|
|
164
167
|
checkMintedStatus(connection, pubAddr, appAddr, releaseAddr)
|
|
@@ -181,6 +184,16 @@ export var publishUpdateCommand = function() {
|
|
|
181
184
|
}, dryRun)
|
|
182
185
|
];
|
|
183
186
|
case 3:
|
|
187
|
+
_state.sent();
|
|
188
|
+
return [
|
|
189
|
+
4,
|
|
190
|
+
writeToPublishDetails({
|
|
191
|
+
lastUpdatedVersionOnStore: {
|
|
192
|
+
address: releaseAddr
|
|
193
|
+
}
|
|
194
|
+
})
|
|
195
|
+
];
|
|
196
|
+
case 4:
|
|
184
197
|
_state.sent();
|
|
185
198
|
return [
|
|
186
199
|
2
|
|
@@ -265,7 +265,7 @@ export var loadPublishDetails = function() {
|
|
|
265
265
|
}();
|
|
266
266
|
export var loadPublishDetailsWithChecks = function() {
|
|
267
267
|
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,
|
|
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, previewMediaFiles, googlePkg, pkgCompare;
|
|
269
269
|
var _arguments = arguments;
|
|
270
270
|
return _ts_generator(this, function(_state) {
|
|
271
271
|
switch(_state.label){
|
|
@@ -368,11 +368,11 @@ export var loadPublishDetailsWithChecks = function() {
|
|
|
368
368
|
throw new Error("Invalid media path or file type: ".concat(item.uri, ". Please ensure the file is a jpeg, png, or webp file."));
|
|
369
369
|
}
|
|
370
370
|
});
|
|
371
|
-
|
|
372
|
-
return asset.purpose === "screenshot";
|
|
371
|
+
previewMediaFiles = (_config_release_media1 = config.release.media) === null || _config_release_media1 === void 0 ? void 0 : _config_release_media1.filter(function(asset) {
|
|
372
|
+
return asset.purpose === "screenshot" || asset.purpose === "video";
|
|
373
373
|
});
|
|
374
|
-
if (
|
|
375
|
-
showMessage("
|
|
374
|
+
if (previewMediaFiles.length < 4) {
|
|
375
|
+
showMessage("Preview media requirements changing in version 0.9.0", "At least 4 screenshots or videos are required for publishing a new release. Found only ".concat(previewMediaFiles.length), "warning");
|
|
376
376
|
}
|
|
377
377
|
validateLocalizableResources(config);
|
|
378
378
|
googlePkg = config.solana_mobile_dapp_publisher_portal.google_store_package;
|
|
@@ -490,7 +490,9 @@ var getAndroidDetails = function() {
|
|
|
490
490
|
versionCode = new RegExp(AaptPrefixes.verCodePrefix + AaptPrefixes.quoteRegex).exec(stdout);
|
|
491
491
|
versionName = new RegExp(AaptPrefixes.verNamePrefix + AaptPrefixes.quoteRegex).exec(stdout);
|
|
492
492
|
minSdk = new RegExp(AaptPrefixes.sdkPrefix + AaptPrefixes.quoteRegex).exec(stdout);
|
|
493
|
-
permissions = _to_consumable_array(stdout.matchAll(/uses-permission: name='(.*)'/g))
|
|
493
|
+
permissions = _to_consumable_array(stdout.matchAll(/uses-permission: name='(.*)'/g)).flatMap(function(permission) {
|
|
494
|
+
return permission[1];
|
|
495
|
+
});
|
|
494
496
|
locales = new RegExp(AaptPrefixes.localePrefix + AaptPrefixes.quoteNonLazyRegex).exec(stdout);
|
|
495
497
|
isDebuggable = new RegExp(AaptPrefixes.debuggableApkPrefix).exec(stdout);
|
|
496
498
|
if (isDebuggable != null) {
|
|
@@ -503,6 +505,15 @@ var getAndroidDetails = function() {
|
|
|
503
505
|
"en-US"
|
|
504
506
|
].concat(localesSrc.split("' '").slice(1));
|
|
505
507
|
}
|
|
508
|
+
if (permissions.includes("android.permission.INSTALL_PACKAGES") || permissions.includes("android.permission.DELETE_PACKAGES")) {
|
|
509
|
+
showMessage("App requests system app install/delete permission", "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.", "error");
|
|
510
|
+
}
|
|
511
|
+
if (permissions.includes("android.permission.REQUEST_INSTALL_PACKAGES") || permissions.includes("android.permission.REQUEST_DELETE_PACKAGES")) {
|
|
512
|
+
showMessage("App requests install or delete permission", "App will be subject to additional security reviews for listing on Solana dApp Store and processing time may be beyond regular review time", "warning");
|
|
513
|
+
}
|
|
514
|
+
if (permissions.includes("com.solanamobile.seedvault.ACCESS_SEED_VAULT")) {
|
|
515
|
+
showMessage("App requests Seed Vault permission", "If this is not a wallet application, your app maybe rejected from listing on Solana dApp Store.", "warning");
|
|
516
|
+
}
|
|
506
517
|
if (localeArray.length >= 60) {
|
|
507
518
|
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");
|
|
508
519
|
}
|
|
@@ -519,9 +530,7 @@ var getAndroidDetails = function() {
|
|
|
519
530
|
case 2:
|
|
520
531
|
return [
|
|
521
532
|
2,
|
|
522
|
-
(_tmp.cert_fingerprint = _state.sent(), _tmp.permissions = permissions.
|
|
523
|
-
return permission[1];
|
|
524
|
-
}), _tmp.locales = localeArray, _tmp)
|
|
533
|
+
(_tmp.cert_fingerprint = _state.sent(), _tmp.permissions = permissions, _tmp.locales = localeArray, _tmp)
|
|
525
534
|
];
|
|
526
535
|
case 3:
|
|
527
536
|
e = _state.sent();
|
|
@@ -579,11 +588,11 @@ export var extractCertFingerprint = function() {
|
|
|
579
588
|
}();
|
|
580
589
|
export var writeToPublishDetails = function() {
|
|
581
590
|
var _ref = _async_to_generator(function(param) {
|
|
582
|
-
var publisher, app, release, currentConfig, _publisher_address, _app_address, _release_address, newConfig;
|
|
591
|
+
var publisher, app, release, lastSubmittedVersionOnChain, lastUpdatedVersionOnStore, currentConfig, _publisher_address, _app_address, _release_address, newConfig;
|
|
583
592
|
return _ts_generator(this, function(_state) {
|
|
584
593
|
switch(_state.label){
|
|
585
594
|
case 0:
|
|
586
|
-
publisher = param.publisher, app = param.app, release = param.release;
|
|
595
|
+
publisher = param.publisher, app = param.app, release = param.release, lastSubmittedVersionOnChain = param.lastSubmittedVersionOnChain, lastUpdatedVersionOnStore = param.lastUpdatedVersionOnStore;
|
|
587
596
|
return [
|
|
588
597
|
4,
|
|
589
598
|
loadPublishDetailsWithChecks()
|
|
@@ -602,7 +611,9 @@ export var writeToPublishDetails = function() {
|
|
|
602
611
|
release: _object_spread_props(_object_spread({}, currentConfig.release), {
|
|
603
612
|
address: (_release_address = release === null || release === void 0 ? void 0 : release.address) !== null && _release_address !== void 0 ? _release_address : currentConfig.release.address
|
|
604
613
|
}),
|
|
605
|
-
solana_mobile_dapp_publisher_portal: currentConfig.solana_mobile_dapp_publisher_portal
|
|
614
|
+
solana_mobile_dapp_publisher_portal: currentConfig.solana_mobile_dapp_publisher_portal,
|
|
615
|
+
lastSubmittedVersionOnChain: lastSubmittedVersionOnChain !== null && lastSubmittedVersionOnChain !== void 0 ? lastSubmittedVersionOnChain : currentConfig.lastSubmittedVersionOnChain,
|
|
616
|
+
lastUpdatedVersionOnStore: lastUpdatedVersionOnStore !== null && lastUpdatedVersionOnStore !== void 0 ? lastUpdatedVersionOnStore : currentConfig.lastUpdatedVersionOnStore
|
|
606
617
|
};
|
|
607
618
|
fs.writeFileSync(Constants.getConfigFilePath(), dump(newConfig, {
|
|
608
619
|
lineWidth: -1
|
|
@@ -1 +1 @@
|
|
|
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":"<<
|
|
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_SCREENSHOT1>>"},{"purpose":"screenshot","uri":"<<RELATIVE_PATH_TO_SCREENSHOT2>>"},{"purpose":"screenshot","uri":"<<RELATIVE_PATH_TO_SCREENSHOT3>>"},{"purpose":"screenshot","uri":"<<RELATIVE_PATH_TO_SCREENSHOT4>>"},{"purpose":"video","uri":"<<RELATIVE_PATH_TO_VIDEO1>>"}],"files":[{"purpose":"install","uri":"<<RELATIVE_PATH_TO_APK>>"}],"catalog":{"en-US":{"name":"<<APP_NAME>>","short_description":"<<SHORT_APP_DESCRIPTION>>","long_description":"<<LONG_APP_DESCRIPTION>>","new_in_version":"<<WHATS_NEW_IN_THIS_VERSION>>","saga_features":"<<ANY_FEATURES_ONLY_AVAILBLE_WHEN_RUNNING_ON_SAGA>>"}}},"solana_mobile_dapp_publisher_portal":{"google_store_package":"<<ANDROID_PACKAGE_NAME_OF_GOOGLE_PLAY_STORE_VERSION_IF_DIFFERENT>>","testing_instructions":"<<TESTING_INSTRUCTIONS>>"}}
|
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.8.2",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"dependencies": {
|
|
54
54
|
"@aws-sdk/client-s3": "^3.321.1",
|
|
55
55
|
"@metaplex-foundation/js-plugin-aws": "^0.18.3",
|
|
56
|
-
"@solana-mobile/dapp-store-publishing-tools": "workspace:0.
|
|
56
|
+
"@solana-mobile/dapp-store-publishing-tools": "workspace:0.8.2",
|
|
57
57
|
"@solana/web3.js": "1.68.0",
|
|
58
58
|
"@types/semver": "^7.3.13",
|
|
59
59
|
"ajv": "^8.11.0",
|
|
@@ -24,7 +24,15 @@ release:
|
|
|
24
24
|
- purpose: icon
|
|
25
25
|
uri: <<RELATIVE_PATH_TO_RELEASE_ICON>>
|
|
26
26
|
- purpose: screenshot
|
|
27
|
-
uri: <<
|
|
27
|
+
uri: <<RELATIVE_PATH_TO_SCREENSHOT1>>
|
|
28
|
+
- purpose: screenshot
|
|
29
|
+
uri: <<RELATIVE_PATH_TO_SCREENSHOT2>>
|
|
30
|
+
- purpose: screenshot
|
|
31
|
+
uri: <<RELATIVE_PATH_TO_SCREENSHOT3>>
|
|
32
|
+
- purpose: screenshot
|
|
33
|
+
uri: <<RELATIVE_PATH_TO_SCREENSHOT4>>
|
|
34
|
+
- purpose: video
|
|
35
|
+
uri: <<RELATIVE_PATH_TO_VIDEO1>>
|
|
28
36
|
files:
|
|
29
37
|
- purpose: install
|
|
30
38
|
uri: <<RELATIVE_PATH_TO_APK>>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solana-mobile/dapp-store-cli",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.2",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@aws-sdk/client-s3": "^3.321.1",
|
|
48
48
|
"@metaplex-foundation/js-plugin-aws": "^0.18.3",
|
|
49
|
-
"@solana-mobile/dapp-store-publishing-tools": "0.8.
|
|
49
|
+
"@solana-mobile/dapp-store-publishing-tools": "0.8.2",
|
|
50
50
|
"@solana/web3.js": "1.68.0",
|
|
51
51
|
"@types/semver": "^7.3.13",
|
|
52
52
|
"ajv": "^8.11.0",
|
package/src/CliSetup.ts
CHANGED
|
@@ -310,7 +310,7 @@ publishCommand
|
|
|
310
310
|
}) => {
|
|
311
311
|
await tryWithErrorMessage(async () => {
|
|
312
312
|
await checkForSelfUpdate();
|
|
313
|
-
|
|
313
|
+
checkSubmissionNetwork(url);
|
|
314
314
|
|
|
315
315
|
const config = await loadPublishDetails(Constants.getConfigFilePath());
|
|
316
316
|
|
|
@@ -320,15 +320,28 @@ publishCommand
|
|
|
320
320
|
|
|
321
321
|
const signer = parseKeypair(keypair);
|
|
322
322
|
if (signer) {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
323
|
+
if (config.lastUpdatedVersionOnStore != null && config.lastSubmittedVersionOnChain.address != null) {
|
|
324
|
+
await publishUpdateCommand({
|
|
325
|
+
appMintAddress: appMintAddress,
|
|
326
|
+
releaseMintAddress: releaseMintAddress,
|
|
327
|
+
signer: signer,
|
|
328
|
+
url: url,
|
|
329
|
+
dryRun: dryRun,
|
|
330
|
+
compliesWithSolanaDappStorePolicies: compliesWithSolanaDappStorePolicies,
|
|
331
|
+
requestorIsAuthorized: requestorIsAuthorized,
|
|
332
|
+
critical: false,
|
|
333
|
+
});
|
|
334
|
+
} else {
|
|
335
|
+
await publishSubmitCommand({
|
|
336
|
+
appMintAddress: appMintAddress,
|
|
337
|
+
releaseMintAddress: releaseMintAddress,
|
|
338
|
+
signer: signer,
|
|
339
|
+
url: url,
|
|
340
|
+
dryRun: dryRun,
|
|
341
|
+
compliesWithSolanaDappStorePolicies: compliesWithSolanaDappStorePolicies,
|
|
342
|
+
requestorIsAuthorized: requestorIsAuthorized,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
332
345
|
|
|
333
346
|
if (dryRun) {
|
|
334
347
|
dryRunSuccessMessage()
|
|
@@ -384,7 +397,7 @@ publishCommand
|
|
|
384
397
|
}) => {
|
|
385
398
|
await tryWithErrorMessage(async () => {
|
|
386
399
|
await checkForSelfUpdate();
|
|
387
|
-
|
|
400
|
+
checkSubmissionNetwork(url);
|
|
388
401
|
|
|
389
402
|
const config = await loadPublishDetails(Constants.getConfigFilePath())
|
|
390
403
|
|
|
@@ -454,7 +467,7 @@ publishCommand
|
|
|
454
467
|
}) => {
|
|
455
468
|
await tryWithErrorMessage(async () => {
|
|
456
469
|
await checkForSelfUpdate();
|
|
457
|
-
|
|
470
|
+
checkSubmissionNetwork(url);
|
|
458
471
|
|
|
459
472
|
const config = await loadPublishDetails(Constants.getConfigFilePath())
|
|
460
473
|
|
|
@@ -517,7 +530,7 @@ publishCommand
|
|
|
517
530
|
) => {
|
|
518
531
|
await tryWithErrorMessage(async () => {
|
|
519
532
|
await checkForSelfUpdate();
|
|
520
|
-
|
|
533
|
+
checkSubmissionNetwork(url);
|
|
521
534
|
|
|
522
535
|
const config = await loadPublishDetails(Constants.getConfigFilePath())
|
|
523
536
|
|
package/src/CliUtils.ts
CHANGED
|
@@ -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.8.
|
|
21
|
+
static CLI_VERSION = "0.8.2";
|
|
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;
|
|
@@ -42,7 +42,7 @@ export const checkForSelfUpdate = async () => {
|
|
|
42
42
|
latestVer.minor > currentVer.minor
|
|
43
43
|
) {
|
|
44
44
|
throw new Error(
|
|
45
|
-
|
|
45
|
+
`Please update to the latest version of the dApp Store CLI before proceeding.\nCurrent version is ${currentVer.raw}\nLatest version is ${latestVer.raw}`
|
|
46
46
|
);
|
|
47
47
|
}
|
|
48
48
|
};
|
|
@@ -11,7 +11,6 @@ import { debug, showMessage } from "../CliUtils.js";
|
|
|
11
11
|
|
|
12
12
|
import type { Keypair } from "@solana/web3.js";
|
|
13
13
|
import type { MetaplexFile } from "@metaplex-foundation/js";
|
|
14
|
-
import { isMetaplexFile } from "@metaplex-foundation/js";
|
|
15
14
|
import { loadPublishDetailsWithChecks } from "../config/PublishDetails.js";
|
|
16
15
|
|
|
17
16
|
export const validateCommand = async ({
|
|
@@ -62,6 +62,7 @@ const createPublisherNft = async (
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
|
+
throw new Error("Unable to mint publisher NFT");
|
|
65
66
|
};
|
|
66
67
|
|
|
67
68
|
export const createPublisherCommand = async ({
|
|
@@ -96,4 +97,6 @@ export const createPublisherCommand = async ({
|
|
|
96
97
|
|
|
97
98
|
return { publisherAddress, transactionSignature };
|
|
98
99
|
}
|
|
100
|
+
|
|
101
|
+
return { publisherAddress: "", transactionSignature: "" };
|
|
99
102
|
};
|
|
@@ -10,12 +10,14 @@ import {
|
|
|
10
10
|
PublicKey,
|
|
11
11
|
sendAndConfirmTransaction,
|
|
12
12
|
} from "@solana/web3.js";
|
|
13
|
+
import fs from "fs";
|
|
14
|
+
import { createHash } from "crypto";
|
|
13
15
|
import {
|
|
14
16
|
Constants,
|
|
15
17
|
getMetaplexInstance,
|
|
16
18
|
showMessage
|
|
17
19
|
} from "../../CliUtils.js";
|
|
18
|
-
import { loadPublishDetailsWithChecks, writeToPublishDetails } from "../../config/PublishDetails.js";
|
|
20
|
+
import { PublishDetails, loadPublishDetailsWithChecks, writeToPublishDetails } from "../../config/PublishDetails.js";
|
|
19
21
|
|
|
20
22
|
type CreateReleaseCommandInput = {
|
|
21
23
|
appMintAddress: string;
|
|
@@ -89,6 +91,7 @@ const createReleaseNft = async ({
|
|
|
89
91
|
}
|
|
90
92
|
}
|
|
91
93
|
}
|
|
94
|
+
throw new Error("Unable to mint release NFT");
|
|
92
95
|
};
|
|
93
96
|
|
|
94
97
|
export const createReleaseCommand = async ({
|
|
@@ -102,28 +105,49 @@ export const createReleaseCommand = async ({
|
|
|
102
105
|
}: CreateReleaseCommandInput) => {
|
|
103
106
|
const connection = new Connection(url);
|
|
104
107
|
|
|
105
|
-
const
|
|
108
|
+
const config = await loadPublishDetailsWithChecks(buildToolsPath);
|
|
106
109
|
|
|
110
|
+
const apkEntry = config.release.files.find(
|
|
111
|
+
(asset: PublishDetails["release"]["files"][0]) => asset.purpose === "install"
|
|
112
|
+
)!;
|
|
113
|
+
const mediaBuffer = await fs.promises.readFile(apkEntry.uri);
|
|
114
|
+
const hash = createHash("sha256").update(mediaBuffer).digest("base64");
|
|
107
115
|
|
|
108
|
-
if (
|
|
109
|
-
throw new Error(
|
|
116
|
+
if (config.lastSubmittedVersionOnChain != null && hash === config.lastSubmittedVersionOnChain.apk_hash) {
|
|
117
|
+
throw new Error(`The last created release used the same apk file.`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (config.lastSubmittedVersionOnChain != null && config.release.android_details.version_code <= config.lastSubmittedVersionOnChain.version_code) {
|
|
121
|
+
throw new Error(`Each release NFT should have higher version code than previous minted release NFT.\nLast released version code is ${config.lastSubmittedVersionOnChain.version_code}.\nCurrent version code from apk file is ${config.release.android_details.version_code}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (config.app.android_package != config.release.android_details.android_package) {
|
|
125
|
+
throw new Error("App package name and release package name do not match.\nApp release specifies " + config.app.android_package + " while release specifies " + config.release.android_details.android_package)
|
|
110
126
|
}
|
|
111
127
|
|
|
112
128
|
if (!dryRun) {
|
|
113
129
|
const { releaseAddress, transactionSignature } = await createReleaseNft({
|
|
114
|
-
appMintAddress: app.address ?? appMintAddress,
|
|
130
|
+
appMintAddress: config.app.address ?? appMintAddress,
|
|
115
131
|
connection,
|
|
116
132
|
publisher: signer,
|
|
117
133
|
releaseDetails: {
|
|
118
|
-
...release,
|
|
134
|
+
...config.release,
|
|
119
135
|
},
|
|
120
|
-
appDetails: app,
|
|
121
|
-
publisherDetails: publisher,
|
|
136
|
+
appDetails: config.app,
|
|
137
|
+
publisherDetails: config.publisher,
|
|
122
138
|
storageParams: storageParams,
|
|
123
139
|
priorityFeeLamports: priorityFeeLamports,
|
|
124
140
|
});
|
|
125
141
|
|
|
126
|
-
await writeToPublishDetails(
|
|
142
|
+
await writeToPublishDetails(
|
|
143
|
+
{
|
|
144
|
+
release: { address: releaseAddress },
|
|
145
|
+
lastSubmittedVersionOnChain: {
|
|
146
|
+
address: releaseAddress,
|
|
147
|
+
version_code: config.release.android_details.version_code,
|
|
148
|
+
apk_hash: hash,
|
|
149
|
+
}
|
|
150
|
+
});
|
|
127
151
|
|
|
128
152
|
return { releaseAddress, transactionSignature };
|
|
129
153
|
}
|
|
@@ -4,7 +4,7 @@ import { publishSubmit } from "@solana-mobile/dapp-store-publishing-tools";
|
|
|
4
4
|
import nacl from "tweetnacl";
|
|
5
5
|
import { checkMintedStatus, showMessage } from "../../CliUtils.js";
|
|
6
6
|
import { Buffer } from "buffer";
|
|
7
|
-
import { loadPublishDetailsWithChecks } from "../../config/PublishDetails.js";
|
|
7
|
+
import { loadPublishDetailsWithChecks, writeToPublishDetails } from "../../config/PublishDetails.js";
|
|
8
8
|
|
|
9
9
|
type PublishSubmitCommandInput = {
|
|
10
10
|
appMintAddress: string;
|
|
@@ -49,6 +49,7 @@ export const publishSubmitCommand = async ({
|
|
|
49
49
|
app: appDetails,
|
|
50
50
|
release: releaseDetails,
|
|
51
51
|
solana_mobile_dapp_publisher_portal: solanaMobileDappPublisherPortalDetails,
|
|
52
|
+
lastUpdatedVersionOnStore: lastUpdatedVersionOnStore,
|
|
52
53
|
} = await loadPublishDetailsWithChecks();
|
|
53
54
|
|
|
54
55
|
const sign = ((buf: Buffer) =>
|
|
@@ -58,6 +59,10 @@ export const publishSubmitCommand = async ({
|
|
|
58
59
|
const appAddr = appMintAddress ?? appDetails.address;
|
|
59
60
|
const releaseAddr = releaseMintAddress ?? releaseDetails.address;
|
|
60
61
|
|
|
62
|
+
if (lastUpdatedVersionOnStore != null && releaseAddr === lastUpdatedVersionOnStore.address) {
|
|
63
|
+
throw new Error(`You've already submitted this version for review.`);
|
|
64
|
+
}
|
|
65
|
+
|
|
61
66
|
await checkMintedStatus(connection, pubAddr, appAddr, releaseAddr);
|
|
62
67
|
|
|
63
68
|
await publishSubmit(
|
|
@@ -72,4 +77,9 @@ export const publishSubmitCommand = async ({
|
|
|
72
77
|
},
|
|
73
78
|
dryRun
|
|
74
79
|
);
|
|
80
|
+
|
|
81
|
+
await writeToPublishDetails(
|
|
82
|
+
{
|
|
83
|
+
lastUpdatedVersionOnStore: { address: releaseAddr }
|
|
84
|
+
});
|
|
75
85
|
};
|
|
@@ -3,7 +3,7 @@ import type { SignWithPublisherKeypair } from "@solana-mobile/dapp-store-publish
|
|
|
3
3
|
import { publishUpdate } from "@solana-mobile/dapp-store-publishing-tools";
|
|
4
4
|
import { checkMintedStatus, showMessage } from "../../CliUtils.js";
|
|
5
5
|
import nacl from "tweetnacl";
|
|
6
|
-
import { loadPublishDetailsWithChecks } from "../../config/PublishDetails.js";
|
|
6
|
+
import { loadPublishDetailsWithChecks, writeToPublishDetails } from "../../config/PublishDetails.js";
|
|
7
7
|
|
|
8
8
|
type PublishUpdateCommandInput = {
|
|
9
9
|
appMintAddress: string;
|
|
@@ -51,6 +51,7 @@ export const publishUpdateCommand = async ({
|
|
|
51
51
|
app: appDetails,
|
|
52
52
|
release: releaseDetails,
|
|
53
53
|
solana_mobile_dapp_publisher_portal: solanaMobileDappPublisherPortalDetails,
|
|
54
|
+
lastUpdatedVersionOnStore: lastUpdatedVersionOnStore
|
|
54
55
|
} = await loadPublishDetailsWithChecks();
|
|
55
56
|
|
|
56
57
|
const sign = ((buf: Buffer) =>
|
|
@@ -60,6 +61,10 @@ export const publishUpdateCommand = async ({
|
|
|
60
61
|
const appAddr = appMintAddress ?? appDetails.address;
|
|
61
62
|
const releaseAddr = releaseMintAddress ?? releaseDetails.address;
|
|
62
63
|
|
|
64
|
+
if (lastUpdatedVersionOnStore != null && releaseAddr === lastUpdatedVersionOnStore.address) {
|
|
65
|
+
throw new Error(`You've already submitted this version for review.`);
|
|
66
|
+
}
|
|
67
|
+
|
|
63
68
|
await checkMintedStatus(connection, pubAddr, appAddr, releaseAddr);
|
|
64
69
|
|
|
65
70
|
await publishUpdate(
|
|
@@ -75,4 +80,8 @@ export const publishUpdateCommand = async ({
|
|
|
75
80
|
},
|
|
76
81
|
dryRun
|
|
77
82
|
);
|
|
83
|
+
await writeToPublishDetails(
|
|
84
|
+
{
|
|
85
|
+
lastUpdatedVersionOnStore: { address: releaseAddr }
|
|
86
|
+
});
|
|
78
87
|
};
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
AndroidDetails,
|
|
3
3
|
App,
|
|
4
|
+
LastSubmittedVersionOnChain,
|
|
5
|
+
LastUpdatedVersionOnStore,
|
|
4
6
|
Publisher,
|
|
5
7
|
Release,
|
|
6
8
|
SolanaMobileDappPublisherPortal
|
|
@@ -25,6 +27,8 @@ export interface PublishDetails {
|
|
|
25
27
|
app: App;
|
|
26
28
|
release: Release;
|
|
27
29
|
solana_mobile_dapp_publisher_portal: SolanaMobileDappPublisherPortal;
|
|
30
|
+
lastSubmittedVersionOnChain: LastSubmittedVersionOnChain
|
|
31
|
+
lastUpdatedVersionOnStore: LastUpdatedVersionOnStore,
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
const AaptPrefixes = {
|
|
@@ -42,6 +46,8 @@ type SaveToConfigArgs = {
|
|
|
42
46
|
publisher?: Pick<Publisher, "address">;
|
|
43
47
|
app?: Pick<App, "address">;
|
|
44
48
|
release?: Pick<Release, "address">;
|
|
49
|
+
lastSubmittedVersionOnChain?: LastSubmittedVersionOnChain;
|
|
50
|
+
lastUpdatedVersionOnStore?: LastUpdatedVersionOnStore;
|
|
45
51
|
};
|
|
46
52
|
|
|
47
53
|
const ajv = new Ajv({ strictTuples: false });
|
|
@@ -126,14 +132,14 @@ export const loadPublishDetailsWithChecks = async (
|
|
|
126
132
|
}
|
|
127
133
|
);
|
|
128
134
|
|
|
129
|
-
const
|
|
130
|
-
(asset: any) => asset.purpose === "screenshot"
|
|
135
|
+
const previewMediaFiles = config.release.media?.filter(
|
|
136
|
+
(asset: any) => asset.purpose === "screenshot" || asset.purpose === "video"
|
|
131
137
|
)
|
|
132
138
|
|
|
133
|
-
if (
|
|
139
|
+
if (previewMediaFiles.length < 4) {
|
|
134
140
|
showMessage(
|
|
135
|
-
"
|
|
136
|
-
`At least 4 screenshots are required for publishing a new release. Found only ${
|
|
141
|
+
"Preview media requirements changing in version 0.9.0",
|
|
142
|
+
`At least 4 screenshots or videos are required for publishing a new release. Found only ${previewMediaFiles.length}`,
|
|
137
143
|
"warning"
|
|
138
144
|
)
|
|
139
145
|
}
|
|
@@ -223,7 +229,7 @@ const getAndroidDetails = async (
|
|
|
223
229
|
const minSdk = new RegExp(
|
|
224
230
|
AaptPrefixes.sdkPrefix + AaptPrefixes.quoteRegex
|
|
225
231
|
).exec(stdout);
|
|
226
|
-
const permissions = [...stdout.matchAll(/uses-permission: name='(.*)'/g)];
|
|
232
|
+
const permissions = [...stdout.matchAll(/uses-permission: name='(.*)'/g)].flatMap(permission => permission[1]);
|
|
227
233
|
const locales = new RegExp(
|
|
228
234
|
AaptPrefixes.localePrefix + AaptPrefixes.quoteNonLazyRegex
|
|
229
235
|
).exec(stdout);
|
|
@@ -241,6 +247,30 @@ const getAndroidDetails = async (
|
|
|
241
247
|
localeArray = ["en-US"].concat(localesSrc.split("' '").slice(1));
|
|
242
248
|
}
|
|
243
249
|
|
|
250
|
+
if (permissions.includes("android.permission.INSTALL_PACKAGES") || permissions.includes("android.permission.DELETE_PACKAGES")) {
|
|
251
|
+
showMessage(
|
|
252
|
+
"App requests system app install/delete permission",
|
|
253
|
+
"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.",
|
|
254
|
+
"error"
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (permissions.includes("android.permission.REQUEST_INSTALL_PACKAGES") || permissions.includes("android.permission.REQUEST_DELETE_PACKAGES")) {
|
|
259
|
+
showMessage(
|
|
260
|
+
"App requests install or delete permission",
|
|
261
|
+
"App will be subject to additional security reviews for listing on Solana dApp Store and processing time may be beyond regular review time",
|
|
262
|
+
"warning"
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (permissions.includes("com.solanamobile.seedvault.ACCESS_SEED_VAULT")) {
|
|
267
|
+
showMessage(
|
|
268
|
+
"App requests Seed Vault permission",
|
|
269
|
+
"If this is not a wallet application, your app maybe rejected from listing on Solana dApp Store.",
|
|
270
|
+
"warning"
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
244
274
|
if (localeArray.length >= 60) {
|
|
245
275
|
showMessage(
|
|
246
276
|
"The bundle apk claims supports for following locales",
|
|
@@ -258,7 +288,7 @@ const getAndroidDetails = async (
|
|
|
258
288
|
version_code: parseInt(versionCode?.[1] ?? "0", 10),
|
|
259
289
|
version: versionName?.[1] ?? "0",
|
|
260
290
|
cert_fingerprint: await extractCertFingerprint(aaptDir, apkPath),
|
|
261
|
-
permissions: permissions
|
|
291
|
+
permissions: permissions,
|
|
262
292
|
locales: localeArray
|
|
263
293
|
};
|
|
264
294
|
} catch (e) {
|
|
@@ -283,7 +313,7 @@ export const extractCertFingerprint = async (aaptDir: string, apkPath: string):
|
|
|
283
313
|
}
|
|
284
314
|
}
|
|
285
315
|
|
|
286
|
-
export const writeToPublishDetails = async ({ publisher, app, release }: SaveToConfigArgs) => {
|
|
316
|
+
export const writeToPublishDetails = async ({ publisher, app, release, lastSubmittedVersionOnChain, lastUpdatedVersionOnStore }: SaveToConfigArgs) => {
|
|
287
317
|
const currentConfig = await loadPublishDetailsWithChecks();
|
|
288
318
|
|
|
289
319
|
delete currentConfig.publisher.icon;
|
|
@@ -302,7 +332,9 @@ export const writeToPublishDetails = async ({ publisher, app, release }: SaveToC
|
|
|
302
332
|
...currentConfig.release,
|
|
303
333
|
address: release?.address ?? currentConfig.release.address
|
|
304
334
|
},
|
|
305
|
-
solana_mobile_dapp_publisher_portal: currentConfig.solana_mobile_dapp_publisher_portal
|
|
335
|
+
solana_mobile_dapp_publisher_portal: currentConfig.solana_mobile_dapp_publisher_portal,
|
|
336
|
+
lastSubmittedVersionOnChain: lastSubmittedVersionOnChain ?? currentConfig.lastSubmittedVersionOnChain,
|
|
337
|
+
lastUpdatedVersionOnStore: lastUpdatedVersionOnStore ?? currentConfig.lastUpdatedVersionOnStore
|
|
306
338
|
};
|
|
307
339
|
|
|
308
340
|
fs.writeFileSync(Constants.getConfigFilePath(), dump(newConfig, {
|
|
@@ -24,7 +24,15 @@ release:
|
|
|
24
24
|
- purpose: icon
|
|
25
25
|
uri: <<RELATIVE_PATH_TO_RELEASE_ICON>>
|
|
26
26
|
- purpose: screenshot
|
|
27
|
-
uri: <<
|
|
27
|
+
uri: <<RELATIVE_PATH_TO_SCREENSHOT1>>
|
|
28
|
+
- purpose: screenshot
|
|
29
|
+
uri: <<RELATIVE_PATH_TO_SCREENSHOT2>>
|
|
30
|
+
- purpose: screenshot
|
|
31
|
+
uri: <<RELATIVE_PATH_TO_SCREENSHOT3>>
|
|
32
|
+
- purpose: screenshot
|
|
33
|
+
uri: <<RELATIVE_PATH_TO_SCREENSHOT4>>
|
|
34
|
+
- purpose: video
|
|
35
|
+
uri: <<RELATIVE_PATH_TO_VIDEO1>>
|
|
28
36
|
files:
|
|
29
37
|
- purpose: install
|
|
30
38
|
uri: <<RELATIVE_PATH_TO_APK>>
|