@@ -3,13 +3,14 @@ The script serves to generate CodePushified React Native app to reproduce issues
3
3
4
4
Requirements:
5
5
1. npm i -g react-native-cli
6
- 2. npm i -g code-push-cli
7
- 3. code-push register
6
+ 2. npm i -g appcenter-cli
7
+ 3. appcenter login
8
+ (If you use this script on macOS for react-native v0.60+ then you need to have CocoaPods installed)
8
9
9
10
Usage: node create-app.js <appName> <reactNativeVersion> <reactNativeCodePushVersion>
10
11
1. node create-app.js
11
12
2. node create-app.js myapp
12
- 3. node create-app.js myapp react-native@0.47.1 react-native-code-push@5 .0.0-beta
13
+ 3. node create-app.js myapp react-native@0.61.5 react-native-code-push@6 .0.0
13
14
4. node create-app.js myapp react-native@latest Microsoft/react-native-code-push
14
15
15
16
Parameters:
@@ -18,14 +19,14 @@ Parameters:
18
19
3. <reactNativeCodePushVersion> - react-native-code-push@latest
19
20
*/
20
21
21
- let fs = require ( 'fs' ) ;
22
- let path = require ( 'path' ) ;
23
- let nexpect = require ( './nexpect' ) ;
24
- let child_proces = require ( 'child_process' ) ;
25
- let execSync = child_proces . execSync ;
22
+ const fs = require ( 'fs' ) ;
23
+ const path = require ( 'path' ) ;
24
+ const nexpect = require ( './nexpect' ) ;
25
+ const child_process = require ( 'child_process' ) ;
26
+ const execSync = child_process . execSync ;
26
27
27
- let args = process . argv . slice ( 2 ) ;
28
- let appName = args [ 0 ] || 'CodePushDemoAppTest' ;
28
+ const args = process . argv . slice ( 2 ) ;
29
+ const appName = args [ 0 ] || 'CodePushDemoAppTest' ;
29
30
30
31
if ( fs . existsSync ( appName ) ) {
31
32
console . error ( `Folder with name "${ appName } " already exists! Please delete` ) ;
@@ -34,17 +35,18 @@ if (fs.existsSync(appName)) {
34
35
35
36
// Checking if yarn is installed
36
37
try {
37
- execSync ( 'yarn bin' ) ;
38
+ execCommand ( 'yarn bin' ) ;
38
39
} catch ( err ) {
39
40
console . error ( `You must install 'yarn' to use this script!` ) ;
40
41
process . exit ( ) ;
41
42
}
42
43
43
- let appNameAndroid = `${ appName } -android` ;
44
- let appNameIOS = `${ appName } -ios` ;
45
- let reactNativeVersion = args [ 1 ] || `react-native@${ execSync ( 'npm view react-native version' ) } ` . trim ( ) ;
46
- let reactNativeVersionIsLowerThanV049 = isReactNativeVesionLowerThan ( 49 ) ;
47
- let reactNativeCodePushVersion = args [ 2 ] || `react-native-code-push@${ execSync ( 'npm view react-native-code-push version' ) } ` . trim ( ) ;
44
+ const appNameAndroid = `${ appName } -android` ;
45
+ const appNameIOS = `${ appName } -ios` ;
46
+ let owner = null ;
47
+ const reactNativeVersion = args [ 1 ] || `react-native@${ execCommand ( 'npm view react-native version' ) } ` . trim ( ) ;
48
+ const reactNativeVersionIsLowerThanV049 = isReactNativeVersionLowerThan ( 49 ) ;
49
+ const reactNativeCodePushVersion = args [ 2 ] || `react-native-code-push@${ execCommand ( 'npm view react-native-code-push version' ) } ` . trim ( ) ;
48
50
49
51
console . log ( `App name: ${ appName } ` ) ;
50
52
console . log ( `React Native version: ${ reactNativeVersion } ` ) ;
@@ -56,8 +58,8 @@ let iosStagingDeploymentKey = null;
56
58
57
59
58
60
//GENERATE START
59
- createCodePushApp ( appNameAndroid , 'android ' ) ;
60
- createCodePushApp ( appNameIOS , 'ios ' ) ;
61
+ createCodePushApp ( appNameAndroid , 'Android ' ) ;
62
+ createCodePushApp ( appNameIOS , 'iOS ' ) ;
61
63
62
64
generatePlainReactNativeApp ( appName , reactNativeVersion ) ;
63
65
process . chdir ( appName ) ;
@@ -67,57 +69,72 @@ linkCodePush(androidStagingDeploymentKey, iosStagingDeploymentKey);
67
69
68
70
69
71
70
- function createCodePushApp ( name , platform ) {
72
+ function createCodePushApp ( name , os ) {
71
73
try {
72
- console . log ( `Creating CodePush app "${ name } " to release updates for ${ platform } ...` ) ;
73
- execSync ( `code-push app add ${ name } ${ platform } react-native` ) ;
74
+ console . log ( `Creating CodePush app "${ name } " to release updates for ${ os } ...` ) ;
75
+ const appResult = execCommand ( `appcenter apps create -d ${ name } -n ${ name } -o ${ os } -p React-Native --output json` ) ;
76
+ const app = JSON . parse ( appResult ) ;
77
+ owner = app . owner . name ;
74
78
console . log ( `App "${ name } " has been created \n` ) ;
79
+ execCommand ( `appcenter codepush deployment add -a ${ owner } /${ name } Staging` ) ;
75
80
} catch ( e ) {
76
81
console . log ( `App "${ name } " already exists \n` ) ;
77
82
}
78
- let deploymentKeys = JSON . parse ( execSync ( `code-push deployment ls ${ name } -k --format json` ) ) ;
79
- let stagingDeploymentKey = deploymentKeys [ 1 ] . key ;
80
- console . log ( `Deployment key for ${ platform } : ${ stagingDeploymentKey } ` ) ;
81
- console . log ( `Use "code-push release-react ${ name } ${ platform } " command to release updates for ${ platform } \n` ) ;
82
-
83
- switch ( platform ) {
84
- case 'android' :
83
+ const deploymentKeysResult = execCommand ( `appcenter codepush deployment list -a ${ owner } /${ name } -k --output json` ) ;
84
+ const deploymentKeys = JSON . parse ( deploymentKeysResult ) ;
85
+ const stagingDeploymentKey = deploymentKeys [ 0 ] [ 1 ] ;
86
+ console . log ( `Deployment key for ${ os } : ${ stagingDeploymentKey } ` ) ;
87
+ console . log ( `Use "appcenter codepush release-react ${ owner } /${ name } " command to release updates for ${ os } \n` ) ;
88
+
89
+ switch ( os ) {
90
+ case 'Android' :
85
91
androidStagingDeploymentKey = stagingDeploymentKey ;
86
92
break ;
87
- case 'ios ' :
93
+ case 'iOS ' :
88
94
iosStagingDeploymentKey = stagingDeploymentKey ;
89
95
break ;
90
96
}
91
97
}
92
98
93
99
function generatePlainReactNativeApp ( appName , reactNativeVersion ) {
94
100
console . log ( `Installing React Native...` ) ;
95
- execSync ( `react-native init ${ appName } --version ${ reactNativeVersion } ` ) ;
101
+ execCommand ( `react-native init ${ appName } --version ${ reactNativeVersion } ` ) ;
96
102
console . log ( `React Native has been installed \n` ) ;
97
103
}
98
104
99
105
function installCodePush ( reactNativeCodePushVersion ) {
100
106
console . log ( `Installing React Native Module for CodePush...` ) ;
101
- execSync ( `yarn add ${ reactNativeCodePushVersion } ` ) ;
107
+ execCommand ( `yarn add ${ reactNativeCodePushVersion } ` ) ;
102
108
console . log ( `React Native Module for CodePush has been installed \n` ) ;
103
109
}
104
110
105
111
function linkCodePush ( androidStagingDeploymentKey , iosStagingDeploymentKey ) {
106
112
console . log ( `Linking React Native Module for CodePush...` ) ;
107
- nexpect . spawn ( `react-native link react-native-code-push` )
108
- . wait ( "What is your CodePush deployment key for Android (hit <ENTER> to ignore)" )
109
- . sendline ( androidStagingDeploymentKey )
110
- . wait ( "What is your CodePush deployment key for iOS (hit <ENTER> to ignore)" )
111
- . sendline ( iosStagingDeploymentKey )
112
- . run ( function ( err ) {
113
- if ( ! err ) {
114
- console . log ( `React Native Module for CodePush has been linked \n` ) ;
115
- setupAssets ( ) ;
116
- }
117
- else {
118
- console . log ( err ) ;
119
- }
120
- } ) ;
113
+ if ( isReactNativeVersionLowerThan ( 60 ) ) {
114
+ nexpect . spawn ( `react-native link react-native-code-push` )
115
+ . wait ( "What is your CodePush deployment key for Android (hit <ENTER> to ignore)" )
116
+ . sendline ( androidStagingDeploymentKey )
117
+ . wait ( "What is your CodePush deployment key for iOS (hit <ENTER> to ignore)" )
118
+ . sendline ( iosStagingDeploymentKey )
119
+ . run ( function ( err ) {
120
+ if ( ! err ) {
121
+ console . log ( `React Native Module for CodePush has been linked \n` ) ;
122
+ setupAssets ( ) ;
123
+ }
124
+ else {
125
+ console . log ( err ) ;
126
+ }
127
+ } ) ;
128
+ } else {
129
+ androidSetup ( ) ;
130
+ if ( process . platform === 'darwin' ) {
131
+ iosSetup ( ) ;
132
+ } else {
133
+ console . log ( 'Your OS is not "Mac OS" so the iOS application will not be configured' )
134
+ }
135
+ setupAssets ( ) ;
136
+ console . log ( `React Native Module for CodePush has been linked \n` ) ;
137
+ }
121
138
}
122
139
123
140
function setupAssets ( ) {
@@ -142,7 +159,7 @@ function setupAssets() {
142
159
if ( err ) {
143
160
return console . error ( err ) ;
144
161
}
145
- var result = data . replace ( / C o d e P u s h D e m o A p p / g, appName ) ;
162
+ const result = data . replace ( / C o d e P u s h D e m o A p p / g, appName ) ;
146
163
147
164
fs . writeFile ( fileToEdit , result , 'utf8' , function ( err ) {
148
165
if ( err ) return console . error ( err ) ;
@@ -159,31 +176,31 @@ function setupAssets() {
159
176
}
160
177
161
178
function optimizeToTestInDebugMode ( ) {
162
- let rnXcodeShLocationFolder = 'scripts' ;
179
+ const rnXcodeShLocationFolder = 'scripts' ;
163
180
try {
164
- let rnVersions = JSON . parse ( execSync ( `npm view react-native versions --json` ) ) ;
165
- let currentRNversion = JSON . parse ( fs . readFileSync ( './package.json' ) ) [ 'dependencies' ] [ 'react-native' ] ;
181
+ const rnVersions = JSON . parse ( execCommand ( `npm view react-native versions --json` ) ) ;
182
+ const currentRNversion = JSON . parse ( fs . readFileSync ( './package.json' ) ) [ 'dependencies' ] [ 'react-native' ] ;
166
183
if ( rnVersions . indexOf ( currentRNversion ) > - 1 &&
167
184
rnVersions . indexOf ( currentRNversion ) < rnVersions . indexOf ( "0.46.0-rc.0" ) ) {
168
185
rnXcodeShLocationFolder = 'packager' ;
169
186
}
170
- } catch ( e ) { }
187
+ } catch ( e ) { }
171
188
172
- let rnXcodeShPath = `node_modules/react-native/${ rnXcodeShLocationFolder } /react-native-xcode.sh` ;
189
+ const rnXcodeShPath = `node_modules/react-native/${ rnXcodeShLocationFolder } /react-native-xcode.sh` ;
173
190
// Replace "if [[ "$PLATFORM_NAME" == *simulator ]]; then" with "if false; then" to force bundling
174
- execSync ( `sed -ie 's/if \\[\\[ "\$PLATFORM_NAME" == \\*simulator \\]\\]; then/if false; then/' ${ rnXcodeShPath } ` ) ;
175
- execSync ( `perl -i -p0e 's/#ifdef DEBUG.*?#endif/jsCodeLocation = [CodePush bundleURL];/s' ios/${ appName } /AppDelegate.m` ) ;
176
- execSync ( `sed -ie 's/targetName.toLowerCase().contains("release")/true/' node_modules/react-native/react.gradle` ) ;
191
+ execCommand ( `sed -ie 's/if \\[\\[ "\$PLATFORM_NAME" == \\*simulator \\]\\]; then/if false; then/' ${ rnXcodeShPath } ` ) ;
192
+ execCommand ( `perl -i -p0e 's/#ifdef DEBUG.*?#endif/jsCodeLocation = [CodePush bundleURL];/s' ios/${ appName } /AppDelegate.m` ) ;
193
+ execCommand ( `sed -ie 's/targetName.toLowerCase().contains("release")/true/' node_modules/react-native/react.gradle` ) ;
177
194
}
178
195
179
196
function grantAccess ( folderPath ) {
180
- execSync ( 'chown -R `whoami` ' + folderPath ) ;
197
+ execCommand ( 'chown -R `whoami` ' + folderPath ) ;
181
198
}
182
199
183
200
function copyRecursiveSync ( src , dest ) {
184
- var exists = fs . existsSync ( src ) ;
185
- var stats = exists && fs . statSync ( src ) ;
186
- var isDirectory = exists && stats . isDirectory ( ) ;
201
+ const exists = fs . existsSync ( src ) ;
202
+ const stats = exists && fs . statSync ( src ) ;
203
+ const isDirectory = exists && stats . isDirectory ( ) ;
187
204
if ( exists && isDirectory ) {
188
205
fs . mkdirSync ( dest ) ;
189
206
fs . readdirSync ( src ) . forEach ( function ( childItemName ) {
@@ -195,12 +212,82 @@ function copyRecursiveSync(src, dest) {
195
212
}
196
213
}
197
214
198
- function isReactNativeVesionLowerThan ( version ) {
215
+ function isReactNativeVersionLowerThan ( version ) {
199
216
if ( ! reactNativeVersion ||
200
217
reactNativeVersion == "react-native@latest" ||
201
218
reactNativeVersion == "react-native@next" )
202
219
return false ;
203
220
204
- let reactNativeVersionNumberString = reactNativeVersion . split ( "@" ) [ 1 ] ;
221
+ const reactNativeVersionNumberString = reactNativeVersion . split ( "@" ) [ 1 ] ;
205
222
return reactNativeVersionNumberString . split ( '.' ) [ 1 ] < version ;
206
- }
223
+ }
224
+
225
+ // Configuring android applications for react-native version higher than 0.60
226
+ function androidSetup ( ) {
227
+ const buildGradlePath = path . join ( 'android' , 'app' , 'build.gradle' ) ;
228
+ const mainApplicationPath = path . join ( 'android' , 'app' , 'src' , 'main' , 'java' , 'com' , appName , 'MainApplication.java' ) ;
229
+ const stringsResourcesPath = path . join ( 'android' , 'app' , 'src' , 'main' , 'res' , 'values' , 'strings.xml' ) ;
230
+
231
+ let stringsResourcesContent = fs . readFileSync ( stringsResourcesPath , "utf8" ) ;
232
+ const insertAfterString = "<resources>" ;
233
+ const deploymentKeyString = `\t<string moduleConfig="true" name="CodePushDeploymentKey">${ androidStagingDeploymentKey || "deployment-key-here" } </string>` ;
234
+ stringsResourcesContent = stringsResourcesContent . replace ( insertAfterString , `${ insertAfterString } \n${ deploymentKeyString } ` ) ;
235
+ fs . writeFileSync ( stringsResourcesPath , stringsResourcesContent ) ;
236
+
237
+ let buildGradleContents = fs . readFileSync ( buildGradlePath , "utf8" ) ;
238
+ const reactGradleLink = buildGradleContents . match ( / \n a p p l y f r o m : [ " ' ] .* ?r e a c t \. g r a d l e [ " ' ] / ) [ 0 ] ;
239
+ const codePushGradleLink = `\napply from: "../../node_modules/react-native-code-push/android/codepush.gradle"` ;
240
+ buildGradleContents = buildGradleContents . replace ( reactGradleLink ,
241
+ `${ reactGradleLink } ${ codePushGradleLink } ` ) ;
242
+ fs . writeFileSync ( buildGradlePath , buildGradleContents ) ;
243
+
244
+ const getJSBundleFileOverride = `
245
+ @Override
246
+ protected String getJSBundleFile(){
247
+ return CodePush.getJSBundleFile();
248
+ }
249
+ ` ;
250
+ let mainApplicationContents = fs . readFileSync ( mainApplicationPath , "utf8" ) ;
251
+ const reactNativeHostInstantiation = "new ReactNativeHost(this) {" ;
252
+ mainApplicationContents = mainApplicationContents . replace ( reactNativeHostInstantiation ,
253
+ `${ reactNativeHostInstantiation } ${ getJSBundleFileOverride } ` ) ;
254
+
255
+ const importCodePush = `\nimport com.microsoft.codepush.react.CodePush;` ;
256
+ const reactNativeHostInstantiationImport = "import android.app.Application;" ;
257
+ mainApplicationContents = mainApplicationContents . replace ( reactNativeHostInstantiationImport ,
258
+ `${ reactNativeHostInstantiationImport } ${ importCodePush } ` ) ;
259
+ fs . writeFileSync ( mainApplicationPath , mainApplicationContents ) ;
260
+ }
261
+
262
+ // Configuring ios applications for react-native version higher than 0.60
263
+ function iosSetup ( ) {
264
+ const plistPath = path . join ( 'ios' , appName , 'Info.plist' ) ;
265
+ const appDelegatePath = path . join ( 'ios' , appName , 'AppDelegate.m' ) ;
266
+
267
+ let plistContents = fs . readFileSync ( plistPath , "utf8" ) ;
268
+ const falseInfoPlist = `<false/>` ;
269
+ const codePushDeploymentKey = iosStagingDeploymentKey || 'deployment-key-here' ;
270
+ plistContents = plistContents . replace ( falseInfoPlist ,
271
+ `${ falseInfoPlist } \n\t<key>CodePushDeploymentKey</key>\n\t<string>${ codePushDeploymentKey } </string>` ) ;
272
+ fs . writeFileSync ( plistPath , plistContents ) ;
273
+
274
+ let appDelegateContents = fs . readFileSync ( appDelegatePath , "utf8" ) ;
275
+ const appDelegateHeaderImportStatement = `#import "AppDelegate.h"` ;
276
+ const codePushHeaderImportStatementFormatted = `\n#import <CodePush/CodePush.h>` ;
277
+ appDelegateContents = appDelegateContents . replace ( appDelegateHeaderImportStatement ,
278
+ `${ appDelegateHeaderImportStatement } ${ codePushHeaderImportStatementFormatted } ` ) ;
279
+
280
+
281
+ const oldBundleUrl = "[[NSBundle mainBundle] URLForResource:@\"main\" withExtension:@\"jsbundle\"]" ;
282
+ const codePushBundleUrl = "[CodePush bundleURL]" ;
283
+ appDelegateContents = appDelegateContents . replace ( oldBundleUrl , codePushBundleUrl ) ;
284
+ fs . writeFileSync ( appDelegatePath , appDelegateContents ) ;
285
+
286
+ execCommand ( `cd ios && pod install && cd ..` ) ;
287
+ }
288
+
289
+ function execCommand ( command ) {
290
+ console . log ( `\n\x1b[2m${ command } \x1b[0m\n` ) ;
291
+ const result = execSync ( command ) . toString ( ) ;
292
+ return result ;
293
+ }
0 commit comments