rnbuilder 0.6.68 → 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 +14 -13
- package/lib/commands/build.d.ts +1 -0
- package/lib/commands/build.js +16 -8
- 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
|
|
@@ -64,6 +64,7 @@ OPTIONS
|
|
|
64
64
|
-g, --gitReset Do git reset when building
|
|
65
65
|
-h, --help show CLI help
|
|
66
66
|
-i, --installr=installr If enabled, the build will be uploaded to installR
|
|
67
|
+
-n, --install Install dependencies before building
|
|
67
68
|
-s, --cleanup Do cleanup when building
|
|
68
69
|
-t, --target=android|ios|android,ios Set the build target: android, ios or both
|
|
69
70
|
-t, --testersIos=testersIos Tester ids for iOS distribution
|
|
@@ -78,7 +79,7 @@ EXAMPLE
|
|
|
78
79
|
hello world from ./src/build.ts!
|
|
79
80
|
```
|
|
80
81
|
|
|
81
|
-
_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)_
|
|
82
83
|
|
|
83
84
|
## `rnbuilder check-version [MATCH-CHECK]`
|
|
84
85
|
|
|
@@ -97,7 +98,7 @@ EXAMPLE
|
|
|
97
98
|
Nuke certificates ./src/match-check.ts!
|
|
98
99
|
```
|
|
99
100
|
|
|
100
|
-
_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)_
|
|
101
102
|
|
|
102
103
|
## `rnbuilder codepush [BUILD]`
|
|
103
104
|
|
|
@@ -116,7 +117,7 @@ EXAMPLE
|
|
|
116
117
|
Push changes to existing apps ./src/firebase-sync-udids.ts!
|
|
117
118
|
```
|
|
118
119
|
|
|
119
|
-
_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)_
|
|
120
121
|
|
|
121
122
|
## `rnbuilder google-key [MATCH-CHECK]`
|
|
122
123
|
|
|
@@ -135,7 +136,7 @@ EXAMPLE
|
|
|
135
136
|
Nuke certificates ./src/match-check.ts!
|
|
136
137
|
```
|
|
137
138
|
|
|
138
|
-
_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)_
|
|
139
140
|
|
|
140
141
|
## `rnbuilder help [COMMAND]`
|
|
141
142
|
|
|
@@ -167,7 +168,7 @@ EXAMPLE
|
|
|
167
168
|
initialize environment for first run from ./src/initialize.ts!
|
|
168
169
|
```
|
|
169
170
|
|
|
170
|
-
_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)_
|
|
171
172
|
|
|
172
173
|
## `rnbuilder match-check [MATCH-CHECK]`
|
|
173
174
|
|
|
@@ -186,7 +187,7 @@ EXAMPLE
|
|
|
186
187
|
Check match certificates ./src/match-check.ts!
|
|
187
188
|
```
|
|
188
189
|
|
|
189
|
-
_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)_
|
|
190
191
|
|
|
191
192
|
## `rnbuilder match-nuke [BUILD]`
|
|
192
193
|
|
|
@@ -205,7 +206,7 @@ EXAMPLE
|
|
|
205
206
|
Nuke certificates ./src/match-nuke.ts!
|
|
206
207
|
```
|
|
207
208
|
|
|
208
|
-
_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)_
|
|
209
210
|
|
|
210
211
|
## `rnbuilder onesignal [BUILD]`
|
|
211
212
|
|
|
@@ -227,7 +228,7 @@ EXAMPLE
|
|
|
227
228
|
Create and upload push certificate to One Signal from ./src/commands/onesignal.ts!
|
|
228
229
|
```
|
|
229
230
|
|
|
230
|
-
_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)_
|
|
231
232
|
|
|
232
233
|
## `rnbuilder reject-review [REJECT-REVIEW]`
|
|
233
234
|
|
|
@@ -245,7 +246,7 @@ EXAMPLE
|
|
|
245
246
|
$ rnbuilder reject-review
|
|
246
247
|
```
|
|
247
248
|
|
|
248
|
-
_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)_
|
|
249
250
|
|
|
250
251
|
## `rnbuilder setup [SETUP]`
|
|
251
252
|
|
|
@@ -263,7 +264,7 @@ EXAMPLE
|
|
|
263
264
|
hello world from ./src/setup.ts!
|
|
264
265
|
```
|
|
265
266
|
|
|
266
|
-
_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)_
|
|
267
268
|
|
|
268
269
|
## `rnbuilder submit-review [SUBMIT-REVIEW]`
|
|
269
270
|
|
|
@@ -282,7 +283,7 @@ EXAMPLE
|
|
|
282
283
|
Push changes to existing apps ./src/firebase-sync-udids.ts!
|
|
283
284
|
```
|
|
284
285
|
|
|
285
|
-
_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)_
|
|
286
287
|
|
|
287
288
|
## `rnbuilder sync-udids [BUILD]`
|
|
288
289
|
|
|
@@ -301,5 +302,5 @@ EXAMPLE
|
|
|
301
302
|
Sync device UDIDs from firebase from ./src/firebase-sync-udids.ts!
|
|
302
303
|
```
|
|
303
304
|
|
|
304
|
-
_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)_
|
|
305
306
|
<!-- commandsstop -->
|
package/lib/commands/build.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export default class Build extends Command {
|
|
|
19
19
|
groupsIos: flags.IOptionFlag<string | undefined>;
|
|
20
20
|
groupsAndroid: flags.IOptionFlag<string | undefined>;
|
|
21
21
|
skipCheckVersion: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
|
|
22
|
+
install: import("@oclif/parser/lib/flags").IBooleanFlag<boolean>;
|
|
22
23
|
};
|
|
23
24
|
static args: {
|
|
24
25
|
name: string;
|
package/lib/commands/build.js
CHANGED
|
@@ -205,7 +205,8 @@ class Build extends command_1.Command {
|
|
|
205
205
|
: 'email(s)'),
|
|
206
206
|
suffix: ' for iOS (separated by commas)',
|
|
207
207
|
default: testersAnswer.testers === 'groups'
|
|
208
|
-
? (_c = answers.groupsAndroid) !== null && _c !== void 0 ? _c : common_1.getTesterGroups(this.fastlaneConfigs, result.env)
|
|
208
|
+
? ((_c = answers.groupsAndroid) !== null && _c !== void 0 ? _c : common_1.getTesterGroups(this.fastlaneConfigs, result.env))
|
|
209
|
+
: answers.testersAndroid,
|
|
209
210
|
},
|
|
210
211
|
]);
|
|
211
212
|
result = Object.assign(Object.assign({}, result), anotherAnswer);
|
|
@@ -253,13 +254,15 @@ class Build extends command_1.Command {
|
|
|
253
254
|
if (flags.cleanup) {
|
|
254
255
|
shell.exec('rm -rf android/.gradle android/build android/app/build android/app/.cxx node_modules');
|
|
255
256
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
257
|
+
if (flags.install) {
|
|
258
|
+
const installCmd = shell.test('-f', 'bun.lockb') || shell.test('-f', 'bun.lock')
|
|
259
|
+
? 'bun install'
|
|
260
|
+
: shell.test('-f', 'pnpm-lock.yaml')
|
|
261
|
+
? 'pnpm install'
|
|
262
|
+
: 'yarn install';
|
|
263
|
+
shell.exec(installCmd);
|
|
264
|
+
shell.exec('cd android && ./gradlew generateCodegenArtifactsFromSchema');
|
|
265
|
+
}
|
|
263
266
|
if (client) {
|
|
264
267
|
client.forEach((cl) => {
|
|
265
268
|
const c = cl.split(' ')[0];
|
|
@@ -345,5 +348,10 @@ Build.flags = {
|
|
|
345
348
|
skipCheckVersion: command_1.flags.boolean({
|
|
346
349
|
description: 'Skip checking current version on stores',
|
|
347
350
|
}),
|
|
351
|
+
install: command_1.flags.boolean({
|
|
352
|
+
char: 'n',
|
|
353
|
+
description: 'Install dependencies before building',
|
|
354
|
+
default: false,
|
|
355
|
+
}),
|
|
348
356
|
};
|
|
349
357
|
Build.args = [{ name: 'build' }];
|
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"}]}}}
|