rnbuilder 0.6.69 → 0.6.70
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/README.md +13 -13
- package/lib/commands/setup.js +3 -0
- package/lib/utils/setup/android.d.ts +4 -0
- package/lib/utils/setup/android.js +52 -0
- package/lib/utils/setup/client.d.ts +5 -3
- package/lib/utils/setup/client.js +8 -13
- package/lib/utils/setup/fastlane.d.ts +6 -1
- package/lib/utils/setup/fastlane.js +78 -15
- package/lib/utils/setup/firebase.d.ts +10 -0
- package/lib/utils/setup/firebase.js +110 -0
- package/lib/utils/setup/ios-scheme.d.ts +4 -0
- package/lib/utils/setup/ios-scheme.js +52 -0
- package/oclif.manifest.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ $ npm install -g rnbuilder
|
|
|
22
22
|
$ rnbuilder COMMAND
|
|
23
23
|
running command...
|
|
24
24
|
$ rnbuilder (-v|--version|version)
|
|
25
|
-
rnbuilder/0.6.
|
|
25
|
+
rnbuilder/0.6.70 darwin-arm64 node-v24.13.0
|
|
26
26
|
$ rnbuilder --help [COMMAND]
|
|
27
27
|
USAGE
|
|
28
28
|
$ rnbuilder COMMAND
|
|
@@ -79,7 +79,7 @@ EXAMPLE
|
|
|
79
79
|
hello world from ./src/build.ts!
|
|
80
80
|
```
|
|
81
81
|
|
|
82
|
-
_See code: [src/commands/build.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.
|
|
82
|
+
_See code: [src/commands/build.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/build.ts)_
|
|
83
83
|
|
|
84
84
|
## `rnbuilder check-version [MATCH-CHECK]`
|
|
85
85
|
|
|
@@ -98,7 +98,7 @@ EXAMPLE
|
|
|
98
98
|
Nuke certificates ./src/match-check.ts!
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
-
_See code: [src/commands/check-version.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.
|
|
101
|
+
_See code: [src/commands/check-version.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/check-version.ts)_
|
|
102
102
|
|
|
103
103
|
## `rnbuilder codepush [BUILD]`
|
|
104
104
|
|
|
@@ -117,7 +117,7 @@ EXAMPLE
|
|
|
117
117
|
Push changes to existing apps ./src/firebase-sync-udids.ts!
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
-
_See code: [src/commands/codepush.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.
|
|
120
|
+
_See code: [src/commands/codepush.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/codepush.ts)_
|
|
121
121
|
|
|
122
122
|
## `rnbuilder google-key [MATCH-CHECK]`
|
|
123
123
|
|
|
@@ -136,7 +136,7 @@ EXAMPLE
|
|
|
136
136
|
Nuke certificates ./src/match-check.ts!
|
|
137
137
|
```
|
|
138
138
|
|
|
139
|
-
_See code: [src/commands/google-key.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.
|
|
139
|
+
_See code: [src/commands/google-key.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/google-key.ts)_
|
|
140
140
|
|
|
141
141
|
## `rnbuilder help [COMMAND]`
|
|
142
142
|
|
|
@@ -168,7 +168,7 @@ EXAMPLE
|
|
|
168
168
|
initialize environment for first run from ./src/initialize.ts!
|
|
169
169
|
```
|
|
170
170
|
|
|
171
|
-
_See code: [src/commands/initialize.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.
|
|
171
|
+
_See code: [src/commands/initialize.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/initialize.ts)_
|
|
172
172
|
|
|
173
173
|
## `rnbuilder match-check [MATCH-CHECK]`
|
|
174
174
|
|
|
@@ -187,7 +187,7 @@ EXAMPLE
|
|
|
187
187
|
Check match certificates ./src/match-check.ts!
|
|
188
188
|
```
|
|
189
189
|
|
|
190
|
-
_See code: [src/commands/match-check.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.
|
|
190
|
+
_See code: [src/commands/match-check.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/match-check.ts)_
|
|
191
191
|
|
|
192
192
|
## `rnbuilder match-nuke [BUILD]`
|
|
193
193
|
|
|
@@ -206,7 +206,7 @@ EXAMPLE
|
|
|
206
206
|
Nuke certificates ./src/match-nuke.ts!
|
|
207
207
|
```
|
|
208
208
|
|
|
209
|
-
_See code: [src/commands/match-nuke.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.
|
|
209
|
+
_See code: [src/commands/match-nuke.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/match-nuke.ts)_
|
|
210
210
|
|
|
211
211
|
## `rnbuilder onesignal [BUILD]`
|
|
212
212
|
|
|
@@ -228,7 +228,7 @@ EXAMPLE
|
|
|
228
228
|
Create and upload push certificate to One Signal from ./src/commands/onesignal.ts!
|
|
229
229
|
```
|
|
230
230
|
|
|
231
|
-
_See code: [src/commands/onesignal.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.
|
|
231
|
+
_See code: [src/commands/onesignal.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/onesignal.ts)_
|
|
232
232
|
|
|
233
233
|
## `rnbuilder reject-review [REJECT-REVIEW]`
|
|
234
234
|
|
|
@@ -246,7 +246,7 @@ EXAMPLE
|
|
|
246
246
|
$ rnbuilder reject-review
|
|
247
247
|
```
|
|
248
248
|
|
|
249
|
-
_See code: [src/commands/reject-review.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.
|
|
249
|
+
_See code: [src/commands/reject-review.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/reject-review.ts)_
|
|
250
250
|
|
|
251
251
|
## `rnbuilder setup [SETUP]`
|
|
252
252
|
|
|
@@ -264,7 +264,7 @@ EXAMPLE
|
|
|
264
264
|
hello world from ./src/setup.ts!
|
|
265
265
|
```
|
|
266
266
|
|
|
267
|
-
_See code: [src/commands/setup.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.
|
|
267
|
+
_See code: [src/commands/setup.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/setup.ts)_
|
|
268
268
|
|
|
269
269
|
## `rnbuilder submit-review [SUBMIT-REVIEW]`
|
|
270
270
|
|
|
@@ -283,7 +283,7 @@ EXAMPLE
|
|
|
283
283
|
Push changes to existing apps ./src/firebase-sync-udids.ts!
|
|
284
284
|
```
|
|
285
285
|
|
|
286
|
-
_See code: [src/commands/submit-review.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.
|
|
286
|
+
_See code: [src/commands/submit-review.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/submit-review.ts)_
|
|
287
287
|
|
|
288
288
|
## `rnbuilder sync-udids [BUILD]`
|
|
289
289
|
|
|
@@ -302,5 +302,5 @@ EXAMPLE
|
|
|
302
302
|
Sync device UDIDs from firebase from ./src/firebase-sync-udids.ts!
|
|
303
303
|
```
|
|
304
304
|
|
|
305
|
-
_See code: [src/commands/sync-udids.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.
|
|
305
|
+
_See code: [src/commands/sync-udids.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/sync-udids.ts)_
|
|
306
306
|
<!-- commandsstop -->
|
package/lib/commands/setup.js
CHANGED
|
@@ -99,6 +99,9 @@ class Setup extends command_1.Command {
|
|
|
99
99
|
// Setup fastlane
|
|
100
100
|
const { firebaseIosApp, firebaseAndroidApp } = await fastlane_1.setupFastlane({
|
|
101
101
|
client,
|
|
102
|
+
bundleId,
|
|
103
|
+
applicationId,
|
|
104
|
+
appName,
|
|
102
105
|
});
|
|
103
106
|
const fastlaneDir = path.join(process.cwd(), `fastlane/clients/${client}/fastlane`);
|
|
104
107
|
const fastlaneEnvVars = env_1.readEnvVars(path.join(fastlaneDir, '/.env'));
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.addProductFlavor = void 0;
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
function insertIntoProductFlavors(gradle, flavor) {
|
|
7
|
+
// Find productFlavors block and insert before its closing brace
|
|
8
|
+
const blockStart = gradle.indexOf('productFlavors {');
|
|
9
|
+
if (blockStart === -1) {
|
|
10
|
+
// No productFlavors block — append one inside android {}
|
|
11
|
+
const androidEnd = gradle.lastIndexOf('}');
|
|
12
|
+
return (gradle.slice(0, androidEnd) +
|
|
13
|
+
`\n productFlavors {\n${flavor}\n }\n` +
|
|
14
|
+
gradle.slice(androidEnd));
|
|
15
|
+
}
|
|
16
|
+
// Walk forward matching braces to find closing }
|
|
17
|
+
let depth = 0;
|
|
18
|
+
let i = blockStart;
|
|
19
|
+
while (i < gradle.length) {
|
|
20
|
+
if (gradle[i] === '{')
|
|
21
|
+
depth++;
|
|
22
|
+
else if (gradle[i] === '}') {
|
|
23
|
+
depth--;
|
|
24
|
+
if (depth === 0)
|
|
25
|
+
break;
|
|
26
|
+
}
|
|
27
|
+
i++;
|
|
28
|
+
}
|
|
29
|
+
return gradle.slice(0, i) + '\n' + flavor + '\n' + gradle.slice(i);
|
|
30
|
+
}
|
|
31
|
+
function addProductFlavor({ client, applicationId, }) {
|
|
32
|
+
const gradlePath = path.join(process.cwd(), 'android/app/build.gradle');
|
|
33
|
+
if (!fs.existsSync(gradlePath)) {
|
|
34
|
+
console.warn(`build.gradle not found at ${gradlePath} — skipping`);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const gradle = fs.readFileSync(gradlePath, 'utf-8');
|
|
38
|
+
// Already has this flavor
|
|
39
|
+
const flavorRegex = new RegExp(`\\b${client}\\s*\\{`);
|
|
40
|
+
if (flavorRegex.test(gradle)) {
|
|
41
|
+
console.log(`Flavor "${client}" already exists in build.gradle — skipping`);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const flavorBlock = ` ${client} {\n` +
|
|
45
|
+
` dimension "default"\n` +
|
|
46
|
+
` applicationId "${applicationId}"\n` +
|
|
47
|
+
` }`;
|
|
48
|
+
const updated = insertIntoProductFlavors(gradle, flavorBlock);
|
|
49
|
+
fs.writeFileSync(gradlePath, updated, 'utf-8');
|
|
50
|
+
console.log(`Added flavor "${client}" to android/app/build.gradle`);
|
|
51
|
+
}
|
|
52
|
+
exports.addProductFlavor = addProductFlavor;
|
|
@@ -2,18 +2,10 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createClientConfig = void 0;
|
|
4
4
|
const inquirer = require("inquirer");
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
name: 'android-client',
|
|
10
|
-
message: `Please add **${client}** to **productFlavors** in android>app>build.gradle`,
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
type: 'confirm',
|
|
14
|
-
name: 'ios-client',
|
|
15
|
-
message: `Please add **${client} prod** scheme and config Pre actions`,
|
|
16
|
-
},
|
|
5
|
+
const android_1 = require("./android");
|
|
6
|
+
const ios_scheme_1 = require("./ios-scheme");
|
|
7
|
+
exports.createClientConfig = async (client) => {
|
|
8
|
+
const { appName, bundleId, applicationId } = await inquirer.prompt([
|
|
17
9
|
{
|
|
18
10
|
type: 'input',
|
|
19
11
|
name: 'appName',
|
|
@@ -28,8 +20,11 @@ exports.createClientConfig = (client) => {
|
|
|
28
20
|
{
|
|
29
21
|
type: 'input',
|
|
30
22
|
name: 'applicationId',
|
|
31
|
-
message: 'What is application id (
|
|
23
|
+
message: 'What is application id (android, if different from bundle id)?',
|
|
32
24
|
default: `com.edular.${client}`,
|
|
33
25
|
},
|
|
34
26
|
]);
|
|
27
|
+
android_1.addProductFlavor({ client, applicationId });
|
|
28
|
+
ios_scheme_1.createXcodeScheme({ client, bundleId });
|
|
29
|
+
return { appName, bundleId, applicationId };
|
|
35
30
|
};
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
export declare const setupFastlane: ({ client }:
|
|
1
|
+
export declare const setupFastlane: ({ client, bundleId, applicationId, appName, }: {
|
|
2
|
+
client: string;
|
|
3
|
+
bundleId?: string | undefined;
|
|
4
|
+
applicationId?: string | undefined;
|
|
5
|
+
appName?: string | undefined;
|
|
6
|
+
}) => Promise<{
|
|
2
7
|
firebaseIosApp: string;
|
|
3
8
|
firebaseAndroidApp: string;
|
|
4
9
|
}>;
|
|
@@ -4,28 +4,19 @@ exports.setupFastlane = void 0;
|
|
|
4
4
|
const common_1 = require("../common");
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const inquirer = require("inquirer");
|
|
7
|
-
|
|
7
|
+
const firebase_1 = require("./firebase");
|
|
8
|
+
exports.setupFastlane = async ({ client, bundleId, applicationId, appName, }) => {
|
|
8
9
|
const clientDir = path.join(process.cwd(), `fastlane/clients/${client}`);
|
|
9
10
|
const fastlaneDir = path.join(clientDir, 'fastlane');
|
|
10
11
|
if (!common_1.exists(fastlaneDir)) {
|
|
11
|
-
// Copy template folders
|
|
12
12
|
await common_1.copyDir(path.join(process.cwd(), 'template/fastlane'), fastlaneDir);
|
|
13
13
|
}
|
|
14
|
-
const { firebaseServiceAccountFile,
|
|
14
|
+
const { firebaseServiceAccountFile, jsonKeyFile } = await inquirer.prompt([
|
|
15
15
|
{
|
|
16
16
|
type: 'input',
|
|
17
17
|
name: 'firebaseServiceAccountFile',
|
|
18
18
|
message: 'Firebase Service Account file?',
|
|
19
|
-
|
|
20
|
-
{
|
|
21
|
-
type: 'input',
|
|
22
|
-
name: 'firebaseIosApp',
|
|
23
|
-
message: 'What is Firebase iOS App Id?',
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
type: 'input',
|
|
27
|
-
name: 'firebaseAndroidApp',
|
|
28
|
-
message: 'What is Firebase Android App Id?',
|
|
19
|
+
default: process.env.FIREBASE_SERVICE_ACCOUNT_FILE,
|
|
29
20
|
},
|
|
30
21
|
{
|
|
31
22
|
type: 'input',
|
|
@@ -33,6 +24,78 @@ exports.setupFastlane = async ({ client }) => {
|
|
|
33
24
|
message: 'Path to Google json file? See https://docs.fastlane.tools/actions/supply/#setup',
|
|
34
25
|
},
|
|
35
26
|
]);
|
|
27
|
+
let firebaseIosApp = '';
|
|
28
|
+
let firebaseAndroidApp = '';
|
|
29
|
+
const resolvedServiceAccount = common_1.normalise(firebaseServiceAccountFile) ||
|
|
30
|
+
process.env.FIREBASE_SERVICE_ACCOUNT_FILE;
|
|
31
|
+
if (resolvedServiceAccount) {
|
|
32
|
+
const { autoCreate } = await inquirer.prompt([
|
|
33
|
+
{
|
|
34
|
+
type: 'confirm',
|
|
35
|
+
name: 'autoCreate',
|
|
36
|
+
message: 'Auto-create Firebase apps?',
|
|
37
|
+
default: true,
|
|
38
|
+
},
|
|
39
|
+
]);
|
|
40
|
+
if (autoCreate) {
|
|
41
|
+
const accessToken = await firebase_1.getAccessToken(resolvedServiceAccount);
|
|
42
|
+
const projectId = firebase_1.getProjectId(resolvedServiceAccount);
|
|
43
|
+
const displayName = appName || client;
|
|
44
|
+
console.log(`Creating Firebase apps in project ${projectId}...`);
|
|
45
|
+
const [iosAppId, androidAppId] = await Promise.all([
|
|
46
|
+
firebase_1.createFirebaseApp({
|
|
47
|
+
accessToken,
|
|
48
|
+
projectId,
|
|
49
|
+
platform: 'ios',
|
|
50
|
+
bundleId,
|
|
51
|
+
displayName,
|
|
52
|
+
}),
|
|
53
|
+
firebase_1.createFirebaseApp({
|
|
54
|
+
accessToken,
|
|
55
|
+
projectId,
|
|
56
|
+
platform: 'android',
|
|
57
|
+
packageName: applicationId,
|
|
58
|
+
displayName,
|
|
59
|
+
}),
|
|
60
|
+
]);
|
|
61
|
+
firebaseIosApp = iosAppId;
|
|
62
|
+
firebaseAndroidApp = androidAppId;
|
|
63
|
+
console.log(`Firebase iOS App ID: ${iosAppId}`);
|
|
64
|
+
console.log(`Firebase Android App ID: ${androidAppId}`);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
const result = await inquirer.prompt([
|
|
68
|
+
{
|
|
69
|
+
type: 'input',
|
|
70
|
+
name: 'firebaseIosApp',
|
|
71
|
+
message: 'What is Firebase iOS App Id?',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
type: 'input',
|
|
75
|
+
name: 'firebaseAndroidApp',
|
|
76
|
+
message: 'What is Firebase Android App Id?',
|
|
77
|
+
},
|
|
78
|
+
]);
|
|
79
|
+
firebaseIosApp = result.firebaseIosApp;
|
|
80
|
+
firebaseAndroidApp = result.firebaseAndroidApp;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
const result = await inquirer.prompt([
|
|
85
|
+
{
|
|
86
|
+
type: 'input',
|
|
87
|
+
name: 'firebaseIosApp',
|
|
88
|
+
message: 'What is Firebase iOS App Id?',
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
type: 'input',
|
|
92
|
+
name: 'firebaseAndroidApp',
|
|
93
|
+
message: 'What is Firebase Android App Id?',
|
|
94
|
+
},
|
|
95
|
+
]);
|
|
96
|
+
firebaseIosApp = result.firebaseIosApp;
|
|
97
|
+
firebaseAndroidApp = result.firebaseAndroidApp;
|
|
98
|
+
}
|
|
36
99
|
if (firebaseServiceAccountFile) {
|
|
37
100
|
await common_1.copyFile(common_1.normalise(firebaseServiceAccountFile), path.join(fastlaneDir, 'firebase.json'));
|
|
38
101
|
}
|
|
@@ -40,7 +103,7 @@ exports.setupFastlane = async ({ client }) => {
|
|
|
40
103
|
await common_1.copyFile(common_1.normalise(jsonKeyFile), path.join(clientDir, 'key.json'));
|
|
41
104
|
}
|
|
42
105
|
return {
|
|
43
|
-
firebaseIosApp
|
|
44
|
-
firebaseAndroidApp
|
|
106
|
+
firebaseIosApp,
|
|
107
|
+
firebaseAndroidApp,
|
|
45
108
|
};
|
|
46
109
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare function getProjectId(serviceAccountPath: string): string;
|
|
2
|
+
export declare function getAccessToken(serviceAccountPath: string): Promise<string>;
|
|
3
|
+
export declare function createFirebaseApp({ accessToken, projectId, platform, bundleId, packageName, displayName, }: {
|
|
4
|
+
accessToken: string;
|
|
5
|
+
projectId: string;
|
|
6
|
+
platform: 'ios' | 'android';
|
|
7
|
+
bundleId?: string;
|
|
8
|
+
packageName?: string;
|
|
9
|
+
displayName: string;
|
|
10
|
+
}): Promise<string>;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createFirebaseApp = exports.getAccessToken = exports.getProjectId = void 0;
|
|
4
|
+
const crypto = require("crypto");
|
|
5
|
+
const fs = require("fs");
|
|
6
|
+
const https = require("https");
|
|
7
|
+
function base64url(input) {
|
|
8
|
+
const buf = typeof input === 'string' ? Buffer.from(input) : input;
|
|
9
|
+
return buf
|
|
10
|
+
.toString('base64')
|
|
11
|
+
.replace(/=/g, '')
|
|
12
|
+
.replace(/\+/g, '-')
|
|
13
|
+
.replace(/\//g, '_');
|
|
14
|
+
}
|
|
15
|
+
function createJWT(serviceAccount) {
|
|
16
|
+
const now = Math.floor(Date.now() / 1000);
|
|
17
|
+
const header = base64url(JSON.stringify({ alg: 'RS256', typ: 'JWT' }));
|
|
18
|
+
const payload = base64url(JSON.stringify({
|
|
19
|
+
iss: serviceAccount.client_email,
|
|
20
|
+
scope: 'https://www.googleapis.com/auth/cloud-platform',
|
|
21
|
+
aud: 'https://oauth2.googleapis.com/token',
|
|
22
|
+
iat: now,
|
|
23
|
+
exp: now + 3600,
|
|
24
|
+
}));
|
|
25
|
+
const data = `${header}.${payload}`;
|
|
26
|
+
const sign = crypto.createSign('RSA-SHA256');
|
|
27
|
+
sign.update(data);
|
|
28
|
+
return `${data}.${base64url(sign.sign(serviceAccount.private_key))}`;
|
|
29
|
+
}
|
|
30
|
+
function httpsRequest(url, method, headers, body) {
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
const { hostname, pathname, search } = new URL(url);
|
|
33
|
+
const req = https.request({
|
|
34
|
+
hostname,
|
|
35
|
+
path: pathname + search,
|
|
36
|
+
method,
|
|
37
|
+
headers: body
|
|
38
|
+
? Object.assign(Object.assign({}, headers), { 'Content-Length': Buffer.byteLength(body) }) : headers,
|
|
39
|
+
}, res => {
|
|
40
|
+
let raw = '';
|
|
41
|
+
res.on('data', chunk => (raw += chunk));
|
|
42
|
+
res.on('end', () => {
|
|
43
|
+
try {
|
|
44
|
+
resolve(JSON.parse(raw));
|
|
45
|
+
}
|
|
46
|
+
catch (_a) {
|
|
47
|
+
resolve(raw);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
req.on('error', reject);
|
|
52
|
+
if (body)
|
|
53
|
+
req.write(body);
|
|
54
|
+
req.end();
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function sleep(ms) {
|
|
58
|
+
return new Promise(r => setTimeout(r, ms));
|
|
59
|
+
}
|
|
60
|
+
async function pollOperation(operationName, accessToken) {
|
|
61
|
+
const url = `https://firebase.googleapis.com/v1beta1/${operationName}`;
|
|
62
|
+
for (let i = 0; i < 20; i++) {
|
|
63
|
+
await sleep(3000);
|
|
64
|
+
const op = await httpsRequest(url, 'GET', {
|
|
65
|
+
Authorization: `Bearer ${accessToken}`,
|
|
66
|
+
});
|
|
67
|
+
if (op.done) {
|
|
68
|
+
if (op.error)
|
|
69
|
+
throw new Error(`Firebase app creation failed: ${JSON.stringify(op.error)}`);
|
|
70
|
+
return op.response.appId;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
throw new Error('Firebase app creation timed out after 60s');
|
|
74
|
+
}
|
|
75
|
+
function getProjectId(serviceAccountPath) {
|
|
76
|
+
const sa = JSON.parse(fs.readFileSync(serviceAccountPath, 'utf-8'));
|
|
77
|
+
return sa.project_id;
|
|
78
|
+
}
|
|
79
|
+
exports.getProjectId = getProjectId;
|
|
80
|
+
async function getAccessToken(serviceAccountPath) {
|
|
81
|
+
const sa = JSON.parse(fs.readFileSync(serviceAccountPath, 'utf-8'));
|
|
82
|
+
const jwt = createJWT(sa);
|
|
83
|
+
const body = new URLSearchParams({
|
|
84
|
+
grant_type: 'urn:ietf:params:oauth:grant-type:jwt-bearer',
|
|
85
|
+
assertion: jwt,
|
|
86
|
+
}).toString();
|
|
87
|
+
const result = await httpsRequest('https://oauth2.googleapis.com/token', 'POST', { 'Content-Type': 'application/x-www-form-urlencoded' }, body);
|
|
88
|
+
if (!result.access_token)
|
|
89
|
+
throw new Error(`Auth failed: ${JSON.stringify(result)}`);
|
|
90
|
+
return result.access_token;
|
|
91
|
+
}
|
|
92
|
+
exports.getAccessToken = getAccessToken;
|
|
93
|
+
async function createFirebaseApp({ accessToken, projectId, platform, bundleId, packageName, displayName, }) {
|
|
94
|
+
const endpoint = platform === 'ios' ? 'iosApps' : 'androidApps';
|
|
95
|
+
const payload = platform === 'ios'
|
|
96
|
+
? { bundleId, displayName }
|
|
97
|
+
: { packageName, displayName };
|
|
98
|
+
const result = await httpsRequest(`https://firebase.googleapis.com/v1beta1/projects/${projectId}/${endpoint}`, 'POST', {
|
|
99
|
+
Authorization: `Bearer ${accessToken}`,
|
|
100
|
+
'Content-Type': 'application/json',
|
|
101
|
+
}, JSON.stringify(payload));
|
|
102
|
+
if (result.error)
|
|
103
|
+
throw new Error(`Firebase API error: ${JSON.stringify(result.error)}`);
|
|
104
|
+
if (result.done)
|
|
105
|
+
return result.response.appId;
|
|
106
|
+
if (result.name)
|
|
107
|
+
return pollOperation(result.name, accessToken);
|
|
108
|
+
throw new Error(`Unexpected response: ${JSON.stringify(result)}`);
|
|
109
|
+
}
|
|
110
|
+
exports.createFirebaseApp = createFirebaseApp;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createXcodeScheme = void 0;
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const path = require("path");
|
|
6
|
+
function findSchemeDir() {
|
|
7
|
+
const iosDir = path.join(process.cwd(), 'ios');
|
|
8
|
+
if (!fs.existsSync(iosDir))
|
|
9
|
+
return null;
|
|
10
|
+
for (const entry of fs.readdirSync(iosDir)) {
|
|
11
|
+
if (!entry.endsWith('.xcodeproj'))
|
|
12
|
+
continue;
|
|
13
|
+
const candidate = path.join(iosDir, entry, 'xcshareddata', 'xcschemes');
|
|
14
|
+
if (fs.existsSync(candidate))
|
|
15
|
+
return candidate;
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
function findExistingProdScheme(schemeDir) {
|
|
20
|
+
const files = fs
|
|
21
|
+
.readdirSync(schemeDir)
|
|
22
|
+
.filter(f => f.endsWith(' prod.xcscheme'));
|
|
23
|
+
return files.length > 0 ? path.join(schemeDir, files[0]) : null;
|
|
24
|
+
}
|
|
25
|
+
function extractClientFromFilename(filename) {
|
|
26
|
+
return path.basename(filename, '.xcscheme').replace(/ prod$/, '');
|
|
27
|
+
}
|
|
28
|
+
function createXcodeScheme({ client, }) {
|
|
29
|
+
const schemeDir = findSchemeDir();
|
|
30
|
+
if (!schemeDir) {
|
|
31
|
+
console.warn('Could not find xcshareddata/xcschemes — add iOS scheme manually');
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const targetScheme = path.join(schemeDir, `${client} prod.xcscheme`);
|
|
35
|
+
if (fs.existsSync(targetScheme)) {
|
|
36
|
+
console.log(`Scheme "${client} prod" already exists — skipping`);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const sourceScheme = findExistingProdScheme(schemeDir);
|
|
40
|
+
if (!sourceScheme) {
|
|
41
|
+
console.warn('No existing "*prod.xcscheme" found to copy from — add iOS scheme manually');
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const oldClient = extractClientFromFilename(sourceScheme);
|
|
45
|
+
let xml = fs.readFileSync(sourceScheme, 'utf-8');
|
|
46
|
+
// Replace all occurrences of old client name — covers BuildableName,
|
|
47
|
+
// BlueprintName, and pre-action script paths like configs/oldclient/.env.*
|
|
48
|
+
xml = xml.split(oldClient).join(client);
|
|
49
|
+
fs.writeFileSync(targetScheme, xml, 'utf-8');
|
|
50
|
+
console.log(`Created iOS scheme "${client} prod" from "${path.basename(sourceScheme)}"`);
|
|
51
|
+
}
|
|
52
|
+
exports.createXcodeScheme = createXcodeScheme;
|
package/oclif.manifest.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"0.6.
|
|
1
|
+
{"version":"0.6.70","commands":{"build":{"id":"build","description":"Build react native apps","pluginName":"rnbuilder","pluginType":"core","aliases":[],"examples":["$ rnbuilder build\nhello world from ./src/build.ts!\n"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"client":{"name":"client","type":"option","char":"c","description":"Set client that you want to build app"},"target":{"name":"target","type":"option","char":"t","description":"Set the build target: android, ios or both","options":["android","ios","android,ios"]},"env":{"name":"env","type":"option","char":"e","description":"Set the environment: prod, staging, dev"},"branch":{"name":"branch","type":"option","char":"b","description":"Set the source branch used to build the app"},"version":{"name":"version","type":"option","char":"v","description":"Set the version number for this build"},"installr":{"name":"installr","type":"option","char":"i","description":"If enabled, the build will be uploaded to installR"},"distribute":{"name":"distribute","type":"option","char":"d","description":"Specify where to distribute app"},"firebase":{"name":"firebase","type":"boolean","char":"f","description":"If enabled, the build will be uploaded to firebase","allowNo":false},"testersIos":{"name":"testersIos","type":"option","char":"t","description":"Tester ids for iOS distribution"},"testersAndroid":{"name":"testersAndroid","type":"option","char":"u","description":"Tester ids for Android distribution"},"gitReset":{"name":"gitReset","type":"boolean","char":"g","description":"Do git reset when building","allowNo":false},"cleanup":{"name":"cleanup","type":"boolean","char":"s","description":"Do cleanup when building","allowNo":false},"groupsIos":{"name":"groupsIos","type":"option","description":"Groups ids for iOS distribution"},"groupsAndroid":{"name":"groupsAndroid","type":"option","description":"Groups ids for Android distribution"},"skipCheckVersion":{"name":"skipCheckVersion","type":"boolean","description":"Skip checking current version on stores","allowNo":false},"install":{"name":"install","type":"boolean","char":"n","description":"Install dependencies before building","allowNo":false}},"args":[{"name":"build"}]},"check-version":{"id":"check-version","description":"Check live versions","pluginName":"rnbuilder","pluginType":"core","aliases":[],"examples":["$ rnbuilder check-version\n Nuke certificates ./src/match-check.ts!\n"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"client":{"name":"client","type":"option","char":"c","description":"Select client that you want to sync udids"}},"args":[{"name":"match-check"}]},"codepush":{"id":"codepush","description":"Push changes to existing apps","pluginName":"rnbuilder","pluginType":"core","aliases":[],"examples":["$ rnbuilder codepush\n Push changes to existing apps ./src/firebase-sync-udids.ts!\n"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"client":{"name":"client","type":"option","char":"c","description":"Select client that you want to push update"}},"args":[{"name":"build"}]},"google-key":{"id":"google-key","description":"Nuke certificates","pluginName":"rnbuilder","pluginType":"core","aliases":[],"examples":["$ rnbuilder match-check\n Nuke certificates ./src/match-check.ts!\n"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"client":{"name":"client","type":"option","char":"c","description":"Select client that you want to sync udids"}},"args":[{"name":"match-check"}]},"initialize":{"id":"initialize","description":"Initialize the environment for first run","pluginName":"rnbuilder","pluginType":"core","aliases":[],"examples":["$ rnbuilder initialize\ninitialize environment for first run from ./src/initialize.ts!\n"],"flags":{},"args":[]},"match-check":{"id":"match-check","description":"Check match certificates","pluginName":"rnbuilder","pluginType":"core","aliases":[],"examples":["$ rnbuilder match-check\n Check match certificates ./src/match-check.ts!\n"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"client":{"name":"client","type":"option","char":"c","description":"Select client that you want to check match certificates"}},"args":[{"name":"match-check"}]},"match-nuke":{"id":"match-nuke","description":"Nuke certificates","pluginName":"rnbuilder","pluginType":"core","aliases":[],"examples":["$ rnbuilder match-nuke\n Nuke certificates ./src/match-nuke.ts!\n"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"client":{"name":"client","type":"option","char":"c","description":"Select client that you want to sync udids"}},"args":[{"name":"build"}]},"onesignal":{"id":"onesignal","description":"Create and upload push cerfiticate to One Signal","pluginName":"rnbuilder","pluginType":"core","aliases":[],"examples":["$ rnbuilder onesignal\n Create and upload push certificate to One Signal from ./src/commands/onesignal.ts!\n"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"client":{"name":"client","type":"option","char":"c","description":"Select client that you want to sync udids"},"auth_token":{"name":"auth_token","type":"option","description":"Auth token for OneSignal"},"android_token":{"name":"android_token","type":"option"},"android_gcm_sender_id":{"name":"android_gcm_sender_id","type":"option"}},"args":[{"name":"build"}]},"reject-review":{"id":"reject-review","description":"Reject current App Store review version (In Review or Pending Developer Release)","pluginName":"rnbuilder","pluginType":"core","aliases":[],"examples":["$ rnbuilder reject-review\n"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"client":{"name":"client","type":"option","char":"c","description":"Select client to reject App Store review version"}},"args":[{"name":"reject-review"}]},"setup":{"id":"setup","description":"Setup new client","pluginName":"rnbuilder","pluginType":"core","aliases":[],"examples":["$ rnbuilder build\nhello world from ./src/setup.ts!\n"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[{"name":"setup"}]},"submit-review":{"id":"submit-review","description":"Submit ios for review","pluginName":"rnbuilder","pluginType":"core","aliases":[],"examples":["$ rnbuilder submit-review\n Push changes to existing apps ./src/firebase-sync-udids.ts!\n"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"client":{"name":"client","type":"option","char":"c","description":"Select client that you want to push update"}},"args":[{"name":"submit-review"}]},"sync-udids":{"id":"sync-udids","description":"Sync device UDIDs from firebase","pluginName":"rnbuilder","pluginType":"core","aliases":[],"examples":["$ rnbuilder sync-udids\n Sync device UDIDs from firebase from ./src/firebase-sync-udids.ts!\n"],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false},"client":{"name":"client","type":"option","char":"c","description":"Select client that you want to sync udids"}},"args":[{"name":"build"}]}}}
|