@revopush/code-push-cli 0.0.7 → 0.0.8-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/script/binary-utils.js +176 -0
- package/bin/script/command-executor.js +377 -44
- package/bin/script/command-parser.js +265 -1
- package/bin/script/expo-utils.js +13 -0
- package/bin/script/management-sdk.js +50 -3
- package/bin/script/react-native-utils.js +23 -2
- package/bin/script/types/cli.js +2 -0
- package/bin/script/utils/file-utils.js +33 -2
- package/bin/test/management-sdk.js +7 -0
- package/package.json +8 -3
- package/script/binary-utils.ts +209 -0
- package/script/command-executor.ts +458 -51
- package/script/command-parser.ts +295 -2
- package/script/expo-utils.ts +14 -0
- package/script/management-sdk.ts +65 -3
- package/script/react-native-utils.ts +34 -3
- package/script/types/cli.ts +13 -0
- package/script/types/rest-definitions.ts +12 -0
- package/script/types.ts +1 -0
- package/script/utils/file-utils.ts +36 -1
- package/test/management-sdk.ts +9 -0
package/script/command-parser.ts
CHANGED
|
@@ -247,7 +247,13 @@ function deploymentRemove(commandName: string, yargs: yargs.Argv): void {
|
|
|
247
247
|
yargs
|
|
248
248
|
.usage(USAGE_PREFIX + " deployment " + commandName + " <appName> <deploymentName>")
|
|
249
249
|
.demand(/*count*/ 2, /*max*/ 2) // Require exactly two non-option arguments
|
|
250
|
-
.example("deployment " + commandName + " MyApp MyDeployment", 'Removes deployment "MyDeployment" from app "MyApp"')
|
|
250
|
+
.example("deployment " + commandName + " MyApp MyDeployment", 'Removes deployment "MyDeployment" from app "MyApp"')
|
|
251
|
+
.option("force", {
|
|
252
|
+
default: false,
|
|
253
|
+
demand: false,
|
|
254
|
+
description: "Bypass confirmation when removing deployments",
|
|
255
|
+
type: "boolean",
|
|
256
|
+
});
|
|
251
257
|
|
|
252
258
|
addCommonConfiguration(yargs);
|
|
253
259
|
}
|
|
@@ -732,6 +738,13 @@ yargs
|
|
|
732
738
|
demand: false,
|
|
733
739
|
description: "Path to the gradle file which specifies the binary version you want to target this release at (android only).",
|
|
734
740
|
})
|
|
741
|
+
.option("initial", {
|
|
742
|
+
alias: "i",
|
|
743
|
+
default: false,
|
|
744
|
+
demand: false,
|
|
745
|
+
description: "Specifies whether release is initial (base) for given targetBinaryVersion.",
|
|
746
|
+
type: "boolean",
|
|
747
|
+
})
|
|
735
748
|
.option("mandatory", {
|
|
736
749
|
alias: "m",
|
|
737
750
|
default: false,
|
|
@@ -844,10 +857,233 @@ yargs
|
|
|
844
857
|
alias: "eo",
|
|
845
858
|
default: [],
|
|
846
859
|
demand: false,
|
|
860
|
+
description: "Option that gets passed to react-native bundler. Can be specified multiple times.",
|
|
861
|
+
type: "array",
|
|
862
|
+
})
|
|
863
|
+
.check((argv: any, aliases: { [aliases: string]: string }): any => {
|
|
864
|
+
return checkValidReleaseOptions(argv);
|
|
865
|
+
});
|
|
866
|
+
|
|
867
|
+
addCommonConfiguration(yargs);
|
|
868
|
+
})
|
|
869
|
+
.command("release-expo", "Release an Expo / React Native update to an app deployment", (yargs: yargs.Argv) => {
|
|
870
|
+
isValidCommand = true;
|
|
871
|
+
|
|
872
|
+
yargs
|
|
873
|
+
.usage(USAGE_PREFIX + " release-expo <appName> <platform> [options]")
|
|
874
|
+
.demand(/*count*/ 2, /*max*/ 2) // Require exactly two non-option arguments
|
|
875
|
+
.example(
|
|
876
|
+
"release-expo MyApp ios",
|
|
877
|
+
'Releases the Expo-managed iOS project in the current working directory to the "MyApp" app\'s "Staging" deployment'
|
|
878
|
+
)
|
|
879
|
+
.example(
|
|
880
|
+
"release-expo MyApp android -d Production",
|
|
881
|
+
'Releases the Expo-managed Android project in the current working directory to the "MyApp" app\'s "Production" deployment'
|
|
882
|
+
)
|
|
883
|
+
.option("bundleName", {
|
|
884
|
+
alias: "b",
|
|
885
|
+
default: null,
|
|
886
|
+
demand: false,
|
|
887
|
+
description:
|
|
888
|
+
'Name of the generated JS bundle file. If unspecified, the standard bundle name will be used, depending on the specified platform: "main.jsbundle" (iOS), "index.android.bundle" (Android) or "index.windows.bundle" (Windows)',
|
|
889
|
+
type: "string",
|
|
890
|
+
})
|
|
891
|
+
.option("deploymentName", {
|
|
892
|
+
alias: "d",
|
|
893
|
+
default: "Staging",
|
|
894
|
+
demand: false,
|
|
895
|
+
description: "Deployment to release the update to",
|
|
896
|
+
type: "string",
|
|
897
|
+
})
|
|
898
|
+
.option("description", {
|
|
899
|
+
alias: "des",
|
|
900
|
+
default: null,
|
|
901
|
+
demand: false,
|
|
902
|
+
description: "Description of the changes made to the app with this release",
|
|
903
|
+
type: "string",
|
|
904
|
+
})
|
|
905
|
+
.option("development", {
|
|
906
|
+
alias: "dev",
|
|
907
|
+
default: false,
|
|
908
|
+
demand: false,
|
|
909
|
+
description: "Specifies whether to generate a dev or release build",
|
|
910
|
+
type: "boolean",
|
|
911
|
+
})
|
|
912
|
+
.option("disabled", {
|
|
913
|
+
alias: "x",
|
|
914
|
+
default: false,
|
|
915
|
+
demand: false,
|
|
916
|
+
description: "Specifies whether this release should be immediately downloadable",
|
|
917
|
+
type: "boolean",
|
|
918
|
+
})
|
|
919
|
+
.option("entryFile", {
|
|
920
|
+
alias: "e",
|
|
921
|
+
default: null,
|
|
922
|
+
demand: false,
|
|
923
|
+
description:
|
|
924
|
+
'Path to the app\'s entry Javascript file. If omitted, "index.<platform>.js" and then "index.js" will be used (if they exist)',
|
|
925
|
+
type: "string",
|
|
926
|
+
})
|
|
927
|
+
.option("gradleFile", {
|
|
928
|
+
alias: "g",
|
|
929
|
+
default: null,
|
|
930
|
+
demand: false,
|
|
931
|
+
description: "Path to the gradle file which specifies the binary version you want to target this release at (android only).",
|
|
932
|
+
})
|
|
933
|
+
.option("initial", {
|
|
934
|
+
alias: "i",
|
|
935
|
+
default: false,
|
|
936
|
+
demand: false,
|
|
937
|
+
description: "Specifies whether release is initial (base) for given targetBinaryVersion.",
|
|
938
|
+
type: "boolean",
|
|
939
|
+
})
|
|
940
|
+
.option("mandatory", {
|
|
941
|
+
alias: "m",
|
|
942
|
+
default: false,
|
|
943
|
+
demand: false,
|
|
944
|
+
description: "Specifies whether this release should be considered mandatory",
|
|
945
|
+
type: "boolean",
|
|
946
|
+
})
|
|
947
|
+
.option("noDuplicateReleaseError", {
|
|
948
|
+
default: false,
|
|
949
|
+
demand: false,
|
|
950
|
+
description:
|
|
951
|
+
"When this flag is set, releasing a package that is identical to the latest release will produce a warning instead of an error",
|
|
952
|
+
type: "boolean",
|
|
953
|
+
})
|
|
954
|
+
.option("plistFile", {
|
|
955
|
+
alias: "p",
|
|
956
|
+
default: null,
|
|
957
|
+
demand: false,
|
|
958
|
+
description: "Path to the plist file which specifies the binary version you want to target this release at (iOS only).",
|
|
959
|
+
})
|
|
960
|
+
.option("plistFilePrefix", {
|
|
961
|
+
alias: "pre",
|
|
962
|
+
default: null,
|
|
963
|
+
demand: false,
|
|
964
|
+
description: "Prefix to append to the file name when attempting to find your app's Info.plist file (iOS only).",
|
|
965
|
+
})
|
|
966
|
+
.option("rollout", {
|
|
967
|
+
alias: "r",
|
|
968
|
+
default: "100%",
|
|
969
|
+
demand: false,
|
|
970
|
+
description: "Percentage of users this release should be immediately available to",
|
|
971
|
+
type: "string",
|
|
972
|
+
})
|
|
973
|
+
.option("sourcemapOutput", {
|
|
974
|
+
alias: "s",
|
|
975
|
+
default: null,
|
|
976
|
+
demand: false,
|
|
977
|
+
description:
|
|
978
|
+
"Path to where the sourcemap for the resulting bundle should be written. If omitted, a sourcemap will not be generated.",
|
|
979
|
+
type: "string",
|
|
980
|
+
})
|
|
981
|
+
.option("targetBinaryVersion", {
|
|
982
|
+
alias: "t",
|
|
983
|
+
default: null,
|
|
984
|
+
demand: false,
|
|
985
|
+
description:
|
|
986
|
+
'Semver expression that specifies the binary app version(s) this release is targeting (e.g. 1.1.0, ~1.2.3). If omitted, the release will target the exact version specified in the "Info.plist" (iOS), "build.gradle" (Android) or "Package.appxmanifest" (Windows) files.',
|
|
987
|
+
type: "string",
|
|
988
|
+
})
|
|
989
|
+
.option("outputDir", {
|
|
990
|
+
alias: "o",
|
|
991
|
+
default: null,
|
|
992
|
+
demand: false,
|
|
993
|
+
description:
|
|
994
|
+
"Path to where the bundle and sourcemap should be written. If omitted, a bundle and sourcemap will not be written.",
|
|
995
|
+
type: "string",
|
|
996
|
+
})
|
|
997
|
+
.option("useHermes", {
|
|
998
|
+
alias: "h",
|
|
999
|
+
default: false,
|
|
1000
|
+
demand: false,
|
|
1001
|
+
description: "Enable hermes and bypass automatic checks",
|
|
1002
|
+
type: "boolean",
|
|
1003
|
+
})
|
|
1004
|
+
.option("podFile", {
|
|
1005
|
+
alias: "pod",
|
|
1006
|
+
default: null,
|
|
1007
|
+
demand: false,
|
|
1008
|
+
description: "Path to the cocopods config file (iOS only).",
|
|
1009
|
+
type: "string",
|
|
1010
|
+
})
|
|
1011
|
+
.option("extraHermesFlags", {
|
|
1012
|
+
alias: "hf",
|
|
1013
|
+
default: [],
|
|
1014
|
+
demand: false,
|
|
1015
|
+
description: "Flags that get passed to Hermes, JavaScript to bytecode compiler. Can be specified multiple times.",
|
|
1016
|
+
type: "array",
|
|
1017
|
+
})
|
|
1018
|
+
.option("privateKeyPath", {
|
|
1019
|
+
alias: "k",
|
|
1020
|
+
default: null,
|
|
1021
|
+
demand: false,
|
|
1022
|
+
description: "Path to private key used for code signing.",
|
|
1023
|
+
type: "string",
|
|
1024
|
+
})
|
|
1025
|
+
.option("xcodeProjectFile", {
|
|
1026
|
+
alias: "xp",
|
|
1027
|
+
default: null,
|
|
1028
|
+
demand: false,
|
|
1029
|
+
description: "Path to the Xcode project or project.pbxproj file",
|
|
1030
|
+
type: "string",
|
|
1031
|
+
})
|
|
1032
|
+
.option("xcodeTargetName", {
|
|
1033
|
+
alias: "xt",
|
|
1034
|
+
default: undefined,
|
|
1035
|
+
demand: false,
|
|
1036
|
+
description:
|
|
1037
|
+
"Name of target (PBXNativeTarget) which specifies the binary version you want to target this release at (iOS only)",
|
|
1038
|
+
type: "string",
|
|
1039
|
+
})
|
|
1040
|
+
.option("buildConfigurationName", {
|
|
1041
|
+
alias: "c",
|
|
1042
|
+
default: undefined,
|
|
1043
|
+
demand: false,
|
|
847
1044
|
description:
|
|
848
|
-
"
|
|
1045
|
+
"Name of build configuration which specifies the binary version you want to target this release at. For example, 'Debug' or 'Release' (iOS only)",
|
|
1046
|
+
type: "string",
|
|
1047
|
+
})
|
|
1048
|
+
.option("extraBundlerOption", {
|
|
1049
|
+
alias: "eo",
|
|
1050
|
+
default: [],
|
|
1051
|
+
demand: false,
|
|
1052
|
+
description: "Option that gets passed to react-native bundler. Can be specified multiple times.",
|
|
849
1053
|
type: "array",
|
|
850
1054
|
})
|
|
1055
|
+
.check((argv: any) => {
|
|
1056
|
+
return checkValidReleaseOptions(argv);
|
|
1057
|
+
});
|
|
1058
|
+
|
|
1059
|
+
addCommonConfiguration(yargs);
|
|
1060
|
+
})
|
|
1061
|
+
.command("release-native", "Release a binary update to an app deployment", (yargs: yargs.Argv) => {
|
|
1062
|
+
yargs
|
|
1063
|
+
.usage(USAGE_PREFIX + " release-native <appName> <platform> <targetBinary> [options]")
|
|
1064
|
+
.demand(/*count*/ 3, /*max*/ 3) // Require exactly three non-option arguments
|
|
1065
|
+
.example(
|
|
1066
|
+
"release-native MyApp ios ./app.ipa",
|
|
1067
|
+
'Releases the React Native iOS project from "./app.ipa" to the "MyApp" app\'s "Staging" deployment'
|
|
1068
|
+
)
|
|
1069
|
+
.example(
|
|
1070
|
+
"release-native MyApp android ./app.apk -d Production",
|
|
1071
|
+
'Releases the React Native Android project from "./app.apk" to the "MyApp" app\'s "Production" deployment'
|
|
1072
|
+
)
|
|
1073
|
+
.option("deploymentName", {
|
|
1074
|
+
alias: "d",
|
|
1075
|
+
default: "Staging",
|
|
1076
|
+
demand: false,
|
|
1077
|
+
description: "Deployment to release the update to",
|
|
1078
|
+
type: "string",
|
|
1079
|
+
})
|
|
1080
|
+
.option("targetBinaryVersion", {
|
|
1081
|
+
alias: "t",
|
|
1082
|
+
default: null,
|
|
1083
|
+
demand: false,
|
|
1084
|
+
description: "Semver expression that specifies the binary app version(s) this release is targeting (e.g. 1.1.0, ~1.2.3).",
|
|
1085
|
+
type: "string",
|
|
1086
|
+
})
|
|
851
1087
|
.check((argv: any, aliases: { [aliases: string]: string }): any => {
|
|
852
1088
|
return checkValidReleaseOptions(argv);
|
|
853
1089
|
});
|
|
@@ -1107,6 +1343,7 @@ export function createCommand(): cli.ICommand {
|
|
|
1107
1343
|
|
|
1108
1344
|
deploymentRemoveCommand.appName = arg2;
|
|
1109
1345
|
deploymentRemoveCommand.deploymentName = arg3;
|
|
1346
|
+
deploymentRemoveCommand.isForce = argv["force"] as any;
|
|
1110
1347
|
}
|
|
1111
1348
|
break;
|
|
1112
1349
|
|
|
@@ -1240,6 +1477,7 @@ export function createCommand(): cli.ICommand {
|
|
|
1240
1477
|
releaseReactCommand.entryFile = argv["entryFile"] as any;
|
|
1241
1478
|
releaseReactCommand.gradleFile = argv["gradleFile"] as any;
|
|
1242
1479
|
releaseReactCommand.mandatory = argv["mandatory"] as any;
|
|
1480
|
+
releaseReactCommand.initial = argv["initial"] as any;
|
|
1243
1481
|
releaseReactCommand.noDuplicateReleaseError = argv["noDuplicateReleaseError"] as any;
|
|
1244
1482
|
releaseReactCommand.plistFile = argv["plistFile"] as any;
|
|
1245
1483
|
releaseReactCommand.plistFilePrefix = argv["plistFilePrefix"] as any;
|
|
@@ -1257,6 +1495,61 @@ export function createCommand(): cli.ICommand {
|
|
|
1257
1495
|
}
|
|
1258
1496
|
break;
|
|
1259
1497
|
|
|
1498
|
+
case "release-expo":
|
|
1499
|
+
if (arg1 && arg2) {
|
|
1500
|
+
cmd = { type: cli.CommandType.releaseExpo };
|
|
1501
|
+
|
|
1502
|
+
const releaseExpoCommand = <cli.IReleaseReactCommand>cmd;
|
|
1503
|
+
|
|
1504
|
+
releaseExpoCommand.appName = arg1;
|
|
1505
|
+
releaseExpoCommand.platform = arg2;
|
|
1506
|
+
|
|
1507
|
+
releaseExpoCommand.appStoreVersion = argv["targetBinaryVersion"] as any;
|
|
1508
|
+
releaseExpoCommand.bundleName = argv["bundleName"] as any;
|
|
1509
|
+
releaseExpoCommand.deploymentName = argv["deploymentName"] as any;
|
|
1510
|
+
releaseExpoCommand.disabled = argv["disabled"] as any;
|
|
1511
|
+
releaseExpoCommand.description = argv["description"] ? backslash(argv["description"]) : "";
|
|
1512
|
+
releaseExpoCommand.development = argv["development"] as any;
|
|
1513
|
+
releaseExpoCommand.entryFile = argv["entryFile"] as any;
|
|
1514
|
+
releaseExpoCommand.gradleFile = argv["gradleFile"] as any;
|
|
1515
|
+
releaseExpoCommand.mandatory = argv["mandatory"] as any;
|
|
1516
|
+
releaseExpoCommand.initial = argv["initial"] as any;
|
|
1517
|
+
releaseExpoCommand.noDuplicateReleaseError = argv["noDuplicateReleaseError"] as any;
|
|
1518
|
+
releaseExpoCommand.plistFile = argv["plistFile"] as any;
|
|
1519
|
+
releaseExpoCommand.plistFilePrefix = argv["plistFilePrefix"] as any;
|
|
1520
|
+
releaseExpoCommand.rollout = getRolloutValue(argv["rollout"] as any);
|
|
1521
|
+
releaseExpoCommand.sourcemapOutput = argv["sourcemapOutput"] as any;
|
|
1522
|
+
releaseExpoCommand.outputDir = argv["outputDir"] as any;
|
|
1523
|
+
releaseExpoCommand.useHermes = argv["useHermes"] as any;
|
|
1524
|
+
releaseExpoCommand.extraHermesFlags = argv["extraHermesFlags"] as any;
|
|
1525
|
+
releaseExpoCommand.podFile = argv["podFile"] as any;
|
|
1526
|
+
releaseExpoCommand.privateKeyPath = argv["privateKeyPath"] as any;
|
|
1527
|
+
releaseExpoCommand.xcodeProjectFile = argv["xcodeProjectFile"] as any;
|
|
1528
|
+
releaseExpoCommand.xcodeTargetName = argv["xcodeTargetName"] as any;
|
|
1529
|
+
releaseExpoCommand.buildConfigurationName = argv["buildConfigurationName"] as any;
|
|
1530
|
+
releaseExpoCommand.extraBundlerOptions = argv["extraBundlerOption"] as any;
|
|
1531
|
+
}
|
|
1532
|
+
break;
|
|
1533
|
+
|
|
1534
|
+
case "release-native":
|
|
1535
|
+
if (arg1 && arg2 && arg3) {
|
|
1536
|
+
cmd = { type: cli.CommandType.releaseNative };
|
|
1537
|
+
|
|
1538
|
+
const releaseBinaryCommand = <cli.IReleaseNativeCommand>cmd;
|
|
1539
|
+
|
|
1540
|
+
releaseBinaryCommand.appName = arg1;
|
|
1541
|
+
releaseBinaryCommand.platform = arg2;
|
|
1542
|
+
releaseBinaryCommand.targetBinary = arg3;
|
|
1543
|
+
releaseBinaryCommand.deploymentName = argv["deploymentName"] as any;
|
|
1544
|
+
releaseBinaryCommand.appStoreVersion = argv["targetBinaryVersion"] as any;
|
|
1545
|
+
releaseBinaryCommand.initial = true;
|
|
1546
|
+
releaseBinaryCommand.disabled = true;
|
|
1547
|
+
releaseBinaryCommand.mandatory = false;
|
|
1548
|
+
// TODO add support for releaseBinaryCommand.bundleName
|
|
1549
|
+
// TODO add support for releaseBinaryCommand.outputDir
|
|
1550
|
+
}
|
|
1551
|
+
break;
|
|
1552
|
+
|
|
1260
1553
|
case "rollback":
|
|
1261
1554
|
if (arg1 && arg2) {
|
|
1262
1555
|
cmd = { type: cli.CommandType.rollback };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as childProcess from "child_process";
|
|
2
|
+
|
|
3
|
+
export function getExpoCliPath(): string {
|
|
4
|
+
const result = childProcess.spawnSync("node", ["--print", "require.resolve('@expo/cli')"]);
|
|
5
|
+
const cliPath = result.stdout.toString().trim();
|
|
6
|
+
|
|
7
|
+
if (result.status === 0 && cliPath) {
|
|
8
|
+
return cliPath;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
throw new Error(
|
|
12
|
+
'Unable to resolve "@expo/cli". Please make sure it is installed in your project (e.g. "npm install --save-dev @expo/cli").'
|
|
13
|
+
);
|
|
14
|
+
}
|
package/script/management-sdk.ts
CHANGED
|
@@ -26,7 +26,9 @@ import {
|
|
|
26
26
|
PackageInfo,
|
|
27
27
|
ServerAccessKey,
|
|
28
28
|
Session,
|
|
29
|
+
BaseRelease,
|
|
29
30
|
} from "./types";
|
|
31
|
+
import { ReactNativePackageInfo } from "./types/rest-definitions";
|
|
30
32
|
|
|
31
33
|
const packageJson = require("../../package.json");
|
|
32
34
|
|
|
@@ -273,6 +275,12 @@ class AccountManager {
|
|
|
273
275
|
return this.get(urlEncode([`/apps/${appName}/deployments/${deploymentName}`])).then((res: JsonResponse) => res.body.deployment);
|
|
274
276
|
}
|
|
275
277
|
|
|
278
|
+
public getBaseRelease(appName: string, deploymentName: string, appVerison: string): Promise<BaseRelease> {
|
|
279
|
+
return this.get(urlEncode([`/apps/${appName}/deployments/${deploymentName}/basebundle?appVersion=${appVerison}`])).then(
|
|
280
|
+
(res: JsonResponse) => res.body.basebundle
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
276
284
|
public renameDeployment(appName: string, oldDeploymentName: string, newDeploymentName: string): Promise<void> {
|
|
277
285
|
return this.patch(
|
|
278
286
|
urlEncode([`/apps/${appName}/deployments/${oldDeploymentName}`]),
|
|
@@ -300,12 +308,10 @@ class AccountManager {
|
|
|
300
308
|
appName: string,
|
|
301
309
|
deploymentName: string,
|
|
302
310
|
filePath: string,
|
|
303
|
-
targetBinaryVersion: string,
|
|
304
311
|
updateMetadata: PackageInfo,
|
|
305
312
|
uploadProgressCallback?: (progress: number) => void
|
|
306
313
|
): Promise<void> {
|
|
307
314
|
return Promise<void>((resolve, reject, notify) => {
|
|
308
|
-
updateMetadata.appVersion = targetBinaryVersion;
|
|
309
315
|
const request: superagent.Request<any> = superagent.post(
|
|
310
316
|
this._serverUrl + urlEncode([`/apps/${appName}/deployments/${deploymentName}/release`])
|
|
311
317
|
);
|
|
@@ -368,6 +374,62 @@ class AccountManager {
|
|
|
368
374
|
});
|
|
369
375
|
}
|
|
370
376
|
|
|
377
|
+
public releaseNative(
|
|
378
|
+
appName: string,
|
|
379
|
+
deploymentName: string,
|
|
380
|
+
filePath: string,
|
|
381
|
+
updateMetadata: ReactNativePackageInfo,
|
|
382
|
+
uploadProgressCallback?: (progress: number) => void
|
|
383
|
+
): Promise<void> {
|
|
384
|
+
return Promise<void>((resolve, reject, notify) => {
|
|
385
|
+
const request: superagent.Request<any> = superagent.post(
|
|
386
|
+
this._serverUrl + urlEncode([`/apps/${appName}/deployments/${deploymentName}/nativerelease`])
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
this.attachCredentials(request);
|
|
390
|
+
|
|
391
|
+
const file: any = fs.createReadStream(filePath);
|
|
392
|
+
request
|
|
393
|
+
.attach("package", file)
|
|
394
|
+
.field("packageInfo", JSON.stringify(updateMetadata))
|
|
395
|
+
.on("progress", (event: any) => {
|
|
396
|
+
if (uploadProgressCallback && event && event.total > 0) {
|
|
397
|
+
const currentProgress: number = (event.loaded / event.total) * 100;
|
|
398
|
+
uploadProgressCallback(currentProgress);
|
|
399
|
+
}
|
|
400
|
+
})
|
|
401
|
+
.end((err: any, res: superagent.Response) => {
|
|
402
|
+
fs.unlinkSync(filePath);
|
|
403
|
+
|
|
404
|
+
if (err) {
|
|
405
|
+
reject(this.getCodePushError(err, res));
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (res.ok) {
|
|
410
|
+
resolve(<void>null);
|
|
411
|
+
} else {
|
|
412
|
+
let body;
|
|
413
|
+
try {
|
|
414
|
+
body = JSON.parse(res.text);
|
|
415
|
+
} catch (err) {}
|
|
416
|
+
|
|
417
|
+
if (body) {
|
|
418
|
+
reject(<CodePushError>{
|
|
419
|
+
message: body.message,
|
|
420
|
+
statusCode: res && res.status,
|
|
421
|
+
});
|
|
422
|
+
} else {
|
|
423
|
+
reject(<CodePushError>{
|
|
424
|
+
message: res.text,
|
|
425
|
+
statusCode: res && res.status,
|
|
426
|
+
});
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
|
|
371
433
|
public patchRelease(appName: string, deploymentName: string, label: string, updateMetadata: PackageInfo): Promise<void> {
|
|
372
434
|
updateMetadata.label = label;
|
|
373
435
|
const requestBody: string = JSON.stringify({ packageInfo: updateMetadata });
|
|
@@ -569,7 +631,7 @@ class AccountManager {
|
|
|
569
631
|
|
|
570
632
|
request.set("Accept", `application/vnd.code-push.v${AccountManager.API_VERSION}+json`);
|
|
571
633
|
request.set("Authorization", `Bearer ${this._accessKey}`);
|
|
572
|
-
request.set("X-CodePush-SDK-Version", packageJson.version);
|
|
634
|
+
request.set("X-CodePush-SDK-Version", packageJson.version); // TODO get version differently without require("../../package.json");
|
|
573
635
|
}
|
|
574
636
|
}
|
|
575
637
|
|
|
@@ -3,10 +3,11 @@ import * as chalk from "chalk";
|
|
|
3
3
|
import * as path from "path";
|
|
4
4
|
import * as childProcess from "child_process";
|
|
5
5
|
import { coerce, compare, valid } from "semver";
|
|
6
|
-
import { fileDoesNotExistOrIsDirectory } from "./utils/file-utils";
|
|
6
|
+
import { downloadBlob, extractIPA, fileDoesNotExistOrIsDirectory } from "./utils/file-utils";
|
|
7
7
|
import * as dotenv from "dotenv";
|
|
8
8
|
import { DotenvParseOutput } from "dotenv";
|
|
9
9
|
import * as cli from "../script/types/cli";
|
|
10
|
+
import { log, sdk } from "./command-executor";
|
|
10
11
|
|
|
11
12
|
const g2js = require("gradle-to-js/lib/parser");
|
|
12
13
|
|
|
@@ -47,13 +48,37 @@ export async function getBundleSourceMapOutput(command: cli.IReleaseReactCommand
|
|
|
47
48
|
return bundleSourceMapOutput;
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
export async function takeHermesBaseBytecode(
|
|
52
|
+
command: cli.IReleaseReactCommand,
|
|
53
|
+
baseReleaseTmpFolder: string,
|
|
54
|
+
outputFolder: string,
|
|
55
|
+
bundleName: string
|
|
56
|
+
): Promise<string | null> {
|
|
57
|
+
const { bundleBlobUrl } = await sdk.getBaseRelease(command.appName, command.deploymentName, command.appStoreVersion);
|
|
58
|
+
if (!bundleBlobUrl) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const baseReleaseArchive = await downloadBlob(bundleBlobUrl, baseReleaseTmpFolder);
|
|
63
|
+
await extractIPA(baseReleaseArchive, baseReleaseTmpFolder);
|
|
64
|
+
const baseReleaseBundle = path.join(baseReleaseTmpFolder, path.basename(outputFolder), bundleName);
|
|
65
|
+
|
|
66
|
+
if (!fs.existsSync(baseReleaseBundle)) {
|
|
67
|
+
log(chalk.cyan("\nNo base release available...\n"));
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return baseReleaseBundle;
|
|
72
|
+
}
|
|
73
|
+
|
|
50
74
|
export async function runHermesEmitBinaryCommand(
|
|
51
75
|
command: cli.IReleaseReactCommand,
|
|
52
76
|
bundleName: string,
|
|
53
77
|
outputFolder: string,
|
|
54
78
|
sourcemapOutputFolder: string,
|
|
55
79
|
extraHermesFlags: string[],
|
|
56
|
-
gradleFile: string
|
|
80
|
+
gradleFile: string,
|
|
81
|
+
baseBytecode?: string
|
|
57
82
|
): Promise<void> {
|
|
58
83
|
const hermesArgs: string[] = [];
|
|
59
84
|
const envNodeArgs: string = process.env.CODE_PUSH_NODE_ARGS;
|
|
@@ -68,6 +93,8 @@ export async function runHermesEmitBinaryCommand(
|
|
|
68
93
|
"-out",
|
|
69
94
|
path.join(outputFolder, bundleName + ".hbc"),
|
|
70
95
|
path.join(outputFolder, bundleName),
|
|
96
|
+
"-w",
|
|
97
|
+
"-max-diagnostic-width=80",
|
|
71
98
|
...extraHermesFlags,
|
|
72
99
|
]);
|
|
73
100
|
|
|
@@ -75,6 +102,10 @@ export async function runHermesEmitBinaryCommand(
|
|
|
75
102
|
hermesArgs.push("-output-source-map");
|
|
76
103
|
}
|
|
77
104
|
|
|
105
|
+
if (baseBytecode) {
|
|
106
|
+
hermesArgs.push("-base-bytecode", baseBytecode);
|
|
107
|
+
}
|
|
108
|
+
|
|
78
109
|
console.log(chalk.cyan("Converting JS bundle to byte code via Hermes, running command:\n"));
|
|
79
110
|
const hermesCommand = await getHermesCommand(gradleFile);
|
|
80
111
|
const hermesProcess = childProcess.spawn(hermesCommand, hermesArgs);
|
|
@@ -416,7 +447,7 @@ function getComposeSourceMapsPath(): string {
|
|
|
416
447
|
}
|
|
417
448
|
|
|
418
449
|
function getNodeModulesPath(reactNativePath: string): string {
|
|
419
|
-
|
|
450
|
+
const nodeModulesPath = path.dirname(reactNativePath);
|
|
420
451
|
if (directoryExistsSync(nodeModulesPath)) {
|
|
421
452
|
return nodeModulesPath;
|
|
422
453
|
}
|
package/script/types/cli.ts
CHANGED
|
@@ -36,6 +36,8 @@ export enum CommandType {
|
|
|
36
36
|
sessionList,
|
|
37
37
|
sessionRemove,
|
|
38
38
|
whoami,
|
|
39
|
+
releaseExpo,
|
|
40
|
+
releaseNative,
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
export interface ICommand {
|
|
@@ -132,6 +134,7 @@ export interface IDeploymentListCommand extends ICommand {
|
|
|
132
134
|
export interface IDeploymentRemoveCommand extends ICommand {
|
|
133
135
|
appName: string;
|
|
134
136
|
deploymentName: string;
|
|
137
|
+
isForce?: boolean;
|
|
135
138
|
}
|
|
136
139
|
|
|
137
140
|
export interface IDeploymentRenameCommand extends ICommand {
|
|
@@ -156,6 +159,7 @@ export interface IPackageInfo {
|
|
|
156
159
|
disabled?: boolean;
|
|
157
160
|
mandatory?: boolean;
|
|
158
161
|
rollout?: number;
|
|
162
|
+
initial?: boolean;
|
|
159
163
|
}
|
|
160
164
|
|
|
161
165
|
export interface IPatchCommand extends ICommand, IPackageInfo {
|
|
@@ -190,6 +194,7 @@ export interface IReleaseCommand extends IReleaseBaseCommand {
|
|
|
190
194
|
}
|
|
191
195
|
|
|
192
196
|
export interface IReleaseReactCommand extends IReleaseBaseCommand {
|
|
197
|
+
package?: string;
|
|
193
198
|
bundleName?: string;
|
|
194
199
|
development?: boolean;
|
|
195
200
|
entryFile?: string;
|
|
@@ -209,6 +214,14 @@ export interface IReleaseReactCommand extends IReleaseBaseCommand {
|
|
|
209
214
|
extraBundlerOptions?: string[];
|
|
210
215
|
}
|
|
211
216
|
|
|
217
|
+
export interface IReleaseNativeCommand extends IReleaseBaseCommand {
|
|
218
|
+
platform: string;
|
|
219
|
+
targetBinary: string;
|
|
220
|
+
targetBinaryVersion?: string;
|
|
221
|
+
outputDir?: string;
|
|
222
|
+
bundleName?: string;
|
|
223
|
+
}
|
|
224
|
+
|
|
212
225
|
export interface IRollbackCommand extends ICommand {
|
|
213
226
|
appName: string;
|
|
214
227
|
deploymentName: string;
|
|
@@ -49,11 +49,18 @@ export interface PackageInfo {
|
|
|
49
49
|
description?: string;
|
|
50
50
|
isDisabled?: boolean;
|
|
51
51
|
isMandatory?: boolean;
|
|
52
|
+
isInitial?: boolean;
|
|
52
53
|
/*generated*/ label?: string;
|
|
53
54
|
/*generated*/ packageHash?: string;
|
|
54
55
|
rollout?: number;
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
/*inout*/
|
|
59
|
+
export interface ReactNativePackageInfo extends PackageInfo {
|
|
60
|
+
bundleName?: string;
|
|
61
|
+
outputDir?: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
57
64
|
/*out*/
|
|
58
65
|
export interface UpdateCheckResponse extends PackageInfo {
|
|
59
66
|
target_binary_range?: string;
|
|
@@ -126,6 +133,11 @@ export interface Deployment {
|
|
|
126
133
|
/*generated*/ package?: Package;
|
|
127
134
|
}
|
|
128
135
|
|
|
136
|
+
/*inout*/
|
|
137
|
+
export interface BaseRelease {
|
|
138
|
+
bundleBlobUrl?: string;
|
|
139
|
+
}
|
|
140
|
+
|
|
129
141
|
/*out*/
|
|
130
142
|
export interface BlobInfo {
|
|
131
143
|
size: number;
|
package/script/types.ts
CHANGED
|
@@ -2,6 +2,10 @@ import * as fs from "fs";
|
|
|
2
2
|
import * as path from "path";
|
|
3
3
|
import * as rimraf from "rimraf";
|
|
4
4
|
import * as temp from "temp";
|
|
5
|
+
import * as unzipper from "unzipper";
|
|
6
|
+
import * as AdmZip from "adm-zip";
|
|
7
|
+
|
|
8
|
+
import superagent = require("superagent");
|
|
5
9
|
|
|
6
10
|
export function isBinaryOrZip(path: string): boolean {
|
|
7
11
|
return path.search(/\.zip$/i) !== -1 || path.search(/\.apk$/i) !== -1 || path.search(/\.ipa$/i) !== -1;
|
|
@@ -17,7 +21,7 @@ export function fileExists(file: string): boolean {
|
|
|
17
21
|
} catch (e) {
|
|
18
22
|
return false;
|
|
19
23
|
}
|
|
20
|
-
}
|
|
24
|
+
}
|
|
21
25
|
|
|
22
26
|
export function copyFileToTmpDir(filePath: string): string {
|
|
23
27
|
if (!isDirectory(filePath)) {
|
|
@@ -44,3 +48,34 @@ export function normalizePath(filePath: string): string {
|
|
|
44
48
|
//replace all backslashes coming from cli running on windows machines by slashes
|
|
45
49
|
return filePath.replace(/\\/g, "/");
|
|
46
50
|
}
|
|
51
|
+
|
|
52
|
+
export async function downloadBlob(url: string, folder: string, filename: string = "blob.zip"): Promise<string> {
|
|
53
|
+
const destination = path.join(folder, filename);
|
|
54
|
+
const writeStream = fs.createWriteStream(destination);
|
|
55
|
+
|
|
56
|
+
return new Promise((resolve, reject) => {
|
|
57
|
+
writeStream.on("finish", () => resolve(destination));
|
|
58
|
+
writeStream.on("error", reject);
|
|
59
|
+
|
|
60
|
+
superagent
|
|
61
|
+
.get(url)
|
|
62
|
+
.ok((res) => res.status < 400)
|
|
63
|
+
.on("error", (err) => {
|
|
64
|
+
writeStream.destroy();
|
|
65
|
+
reject(err);
|
|
66
|
+
})
|
|
67
|
+
.pipe(writeStream);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export async function extractIPA(zipPath: string, extractTo: string) {
|
|
72
|
+
const extractStream = unzipper.Extract({ path: extractTo });
|
|
73
|
+
await new Promise<void>((resolve, reject) => {
|
|
74
|
+
fs.createReadStream(zipPath).pipe(extractStream).on("close", resolve).on("error", reject);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export async function extractAPK(zipPath: string, extractTo: string) {
|
|
79
|
+
const zip = new AdmZip(zipPath);
|
|
80
|
+
zip.extractAllTo(extractTo, true);
|
|
81
|
+
}
|