rnbuilder 0.6.70 → 0.6.72

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 CHANGED
@@ -10,10 +10,28 @@ React Native builder tool
10
10
 
11
11
  <!-- toc -->
12
12
  * [rnbuilder](#rnbuilder)
13
+ * [Setup](#setup)
13
14
  * [Usage](#usage)
14
15
  * [Commands](#commands)
15
16
  <!-- tocstop -->
16
17
 
18
+ # Setup
19
+
20
+ ## Google Play Store (Android)
21
+
22
+ Builds are uploaded to Google Play using **GCP service account impersonation** — no private key files stored on disk.
23
+
24
+ Run once per machine:
25
+
26
+ ```sh-session
27
+ $ rnbuilder setup-gcp
28
+ ```
29
+
30
+ Each client `.env` must set `GOOGLE_APPLICATION_CREDENTIALS` pointing to the SA's ADC file.
31
+ Google Play Console limits one service account to 10 developer accounts — use multiple SAs for more clients.
32
+
33
+ See [docs/gcp-service-account-impersonation.md](docs/gcp-service-account-impersonation.md) for full setup, adding new service accounts, client mapping, and troubleshooting.
34
+
17
35
  # Usage
18
36
 
19
37
  <!-- usage -->
@@ -22,7 +40,7 @@ $ npm install -g rnbuilder
22
40
  $ rnbuilder COMMAND
23
41
  running command...
24
42
  $ rnbuilder (-v|--version|version)
25
- rnbuilder/0.6.70 darwin-arm64 node-v24.13.0
43
+ rnbuilder/0.6.72 darwin-arm64 node-v24.13.0
26
44
  $ rnbuilder --help [COMMAND]
27
45
  USAGE
28
46
  $ rnbuilder COMMAND
@@ -44,6 +62,7 @@ USAGE
44
62
  * [`rnbuilder onesignal [BUILD]`](#rnbuilder-onesignal-build)
45
63
  * [`rnbuilder reject-review [REJECT-REVIEW]`](#rnbuilder-reject-review-reject-review)
46
64
  * [`rnbuilder setup [SETUP]`](#rnbuilder-setup-setup)
65
+ * [`rnbuilder setup-gcp`](#rnbuilder-setup-gcp)
47
66
  * [`rnbuilder submit-review [SUBMIT-REVIEW]`](#rnbuilder-submit-review-submit-review)
48
67
  * [`rnbuilder sync-udids [BUILD]`](#rnbuilder-sync-udids-build)
49
68
 
@@ -79,7 +98,7 @@ EXAMPLE
79
98
  hello world from ./src/build.ts!
80
99
  ```
81
100
 
82
- _See code: [src/commands/build.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/build.ts)_
101
+ _See code: [src/commands/build.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.72/src/commands/build.ts)_
83
102
 
84
103
  ## `rnbuilder check-version [MATCH-CHECK]`
85
104
 
@@ -98,7 +117,7 @@ EXAMPLE
98
117
  Nuke certificates ./src/match-check.ts!
99
118
  ```
100
119
 
101
- _See code: [src/commands/check-version.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/check-version.ts)_
120
+ _See code: [src/commands/check-version.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.72/src/commands/check-version.ts)_
102
121
 
103
122
  ## `rnbuilder codepush [BUILD]`
104
123
 
@@ -117,7 +136,7 @@ EXAMPLE
117
136
  Push changes to existing apps ./src/firebase-sync-udids.ts!
118
137
  ```
119
138
 
120
- _See code: [src/commands/codepush.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/codepush.ts)_
139
+ _See code: [src/commands/codepush.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.72/src/commands/codepush.ts)_
121
140
 
122
141
  ## `rnbuilder google-key [MATCH-CHECK]`
123
142
 
@@ -136,7 +155,7 @@ EXAMPLE
136
155
  Nuke certificates ./src/match-check.ts!
137
156
  ```
138
157
 
139
- _See code: [src/commands/google-key.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/google-key.ts)_
158
+ _See code: [src/commands/google-key.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.72/src/commands/google-key.ts)_
140
159
 
141
160
  ## `rnbuilder help [COMMAND]`
142
161
 
@@ -168,7 +187,7 @@ EXAMPLE
168
187
  initialize environment for first run from ./src/initialize.ts!
169
188
  ```
170
189
 
171
- _See code: [src/commands/initialize.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/initialize.ts)_
190
+ _See code: [src/commands/initialize.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.72/src/commands/initialize.ts)_
172
191
 
173
192
  ## `rnbuilder match-check [MATCH-CHECK]`
174
193
 
@@ -187,7 +206,7 @@ EXAMPLE
187
206
  Check match certificates ./src/match-check.ts!
188
207
  ```
189
208
 
190
- _See code: [src/commands/match-check.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/match-check.ts)_
209
+ _See code: [src/commands/match-check.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.72/src/commands/match-check.ts)_
191
210
 
192
211
  ## `rnbuilder match-nuke [BUILD]`
193
212
 
@@ -206,7 +225,7 @@ EXAMPLE
206
225
  Nuke certificates ./src/match-nuke.ts!
207
226
  ```
208
227
 
209
- _See code: [src/commands/match-nuke.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/match-nuke.ts)_
228
+ _See code: [src/commands/match-nuke.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.72/src/commands/match-nuke.ts)_
210
229
 
211
230
  ## `rnbuilder onesignal [BUILD]`
212
231
 
@@ -228,7 +247,7 @@ EXAMPLE
228
247
  Create and upload push certificate to One Signal from ./src/commands/onesignal.ts!
229
248
  ```
230
249
 
231
- _See code: [src/commands/onesignal.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/onesignal.ts)_
250
+ _See code: [src/commands/onesignal.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.72/src/commands/onesignal.ts)_
232
251
 
233
252
  ## `rnbuilder reject-review [REJECT-REVIEW]`
234
253
 
@@ -246,7 +265,7 @@ EXAMPLE
246
265
  $ rnbuilder reject-review
247
266
  ```
248
267
 
249
- _See code: [src/commands/reject-review.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/reject-review.ts)_
268
+ _See code: [src/commands/reject-review.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.72/src/commands/reject-review.ts)_
250
269
 
251
270
  ## `rnbuilder setup [SETUP]`
252
271
 
@@ -264,7 +283,21 @@ EXAMPLE
264
283
  hello world from ./src/setup.ts!
265
284
  ```
266
285
 
267
- _See code: [src/commands/setup.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/setup.ts)_
286
+ _See code: [src/commands/setup.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.72/src/commands/setup.ts)_
287
+
288
+ ## `rnbuilder setup-gcp`
289
+
290
+ Setup GCP service account impersonation
291
+
292
+ ```
293
+ USAGE
294
+ $ rnbuilder setup-gcp
295
+
296
+ OPTIONS
297
+ -h, --help show CLI help
298
+ ```
299
+
300
+ _See code: [src/commands/setup-gcp.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.72/src/commands/setup-gcp.ts)_
268
301
 
269
302
  ## `rnbuilder submit-review [SUBMIT-REVIEW]`
270
303
 
@@ -283,7 +316,7 @@ EXAMPLE
283
316
  Push changes to existing apps ./src/firebase-sync-udids.ts!
284
317
  ```
285
318
 
286
- _See code: [src/commands/submit-review.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/submit-review.ts)_
319
+ _See code: [src/commands/submit-review.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.72/src/commands/submit-review.ts)_
287
320
 
288
321
  ## `rnbuilder sync-udids [BUILD]`
289
322
 
@@ -302,5 +335,5 @@ EXAMPLE
302
335
  Sync device UDIDs from firebase from ./src/firebase-sync-udids.ts!
303
336
  ```
304
337
 
305
- _See code: [src/commands/sync-udids.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.70/src/commands/sync-udids.ts)_
338
+ _See code: [src/commands/sync-udids.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.6.72/src/commands/sync-udids.ts)_
306
339
  <!-- commandsstop -->
@@ -246,15 +246,15 @@ class Build extends command_1.Command {
246
246
  }
247
247
  }
248
248
  shell.exec('git pull');
249
- shell.exec('bundle update --bundler');
250
- shell.exec('bundle install');
251
- // Clean fastlane builds
252
- shell.exec('rm -rf fastlane/builds');
253
- shell.exec('rm ios/.xcode.env.local');
254
249
  if (flags.cleanup) {
250
+ // Clean fastlane builds
251
+ shell.exec('rm -rf fastlane/builds');
252
+ shell.exec('rm ios/.xcode.env.local');
255
253
  shell.exec('rm -rf android/.gradle android/build android/app/build android/app/.cxx node_modules');
256
254
  }
257
255
  if (flags.install) {
256
+ shell.exec('bundle update --bundler');
257
+ shell.exec('bundle install');
258
258
  const installCmd = shell.test('-f', 'bun.lockb') || shell.test('-f', 'bun.lock')
259
259
  ? 'bun install'
260
260
  : shell.test('-f', 'pnpm-lock.yaml')
@@ -0,0 +1,8 @@
1
+ import { Command } from '@oclif/command';
2
+ export default class SetupGcp extends Command {
3
+ static description: string;
4
+ static flags: {
5
+ help: import("@oclif/parser/lib/flags").IBooleanFlag<void>;
6
+ };
7
+ run(): Promise<void>;
8
+ }
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const command_1 = require("@oclif/command");
4
+ const child_process_1 = require("child_process");
5
+ const path = require("path");
6
+ const common_1 = require("../utils/common");
7
+ class SetupGcp extends command_1.Command {
8
+ async run() {
9
+ var _a, _b, _c, _d, _e;
10
+ const configs = common_1.getFastlaneConfigs();
11
+ const gcp = (_a = configs.gcp) !== null && _a !== void 0 ? _a : {};
12
+ const scriptPath = path.join(__dirname, '../../scripts/setup_gcp_impersonation.sh');
13
+ const result = child_process_1.spawnSync('bash', [scriptPath], {
14
+ stdio: 'inherit',
15
+ env: Object.assign(Object.assign({}, process.env), { GCP_PROJECT_ID: (_b = gcp.projectId) !== null && _b !== void 0 ? _b : '', GCP_USER_EMAIL: (_c = gcp.userEmail) !== null && _c !== void 0 ? _c : '', GCP_SERVICE_ACCOUNTS: ((_d = gcp.serviceAccounts) !== null && _d !== void 0 ? _d : []).join(',') }),
16
+ });
17
+ if (result.status !== 0) {
18
+ this.exit((_e = result.status) !== null && _e !== void 0 ? _e : 1);
19
+ }
20
+ }
21
+ }
22
+ exports.default = SetupGcp;
23
+ SetupGcp.description = 'Setup GCP service account impersonation';
24
+ SetupGcp.flags = {
25
+ help: command_1.flags.help({ char: 'h' }),
26
+ };
@@ -1,4 +1,3 @@
1
- export declare function addProductFlavor({ client, applicationId, }: {
1
+ export declare function addProductFlavor({ client, }: {
2
2
  client: string;
3
- applicationId: string;
4
3
  }): void;
@@ -3,49 +3,57 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.addProductFlavor = void 0;
4
4
  const fs = require("fs");
5
5
  const path = require("path");
6
- function insertIntoProductFlavors(gradle, flavor) {
7
- // Find productFlavors block and insert before its closing brace
6
+ function insertIntoProductFlavors(gradle, client) {
7
+ var _a;
8
8
  const blockStart = gradle.indexOf('productFlavors {');
9
9
  if (blockStart === -1) {
10
- // No productFlavors block — append one inside android {}
11
10
  const androidEnd = gradle.lastIndexOf('}');
12
11
  return (gradle.slice(0, androidEnd) +
13
- `\n productFlavors {\n${flavor}\n }\n` +
12
+ `\n productFlavors {\n ${client}\n }\n` +
14
13
  gradle.slice(androidEnd));
15
14
  }
16
- // Walk forward matching braces to find closing }
17
- let depth = 0;
18
- let i = blockStart;
19
- while (i < gradle.length) {
15
+ const bracePos = gradle.indexOf('{', blockStart) + 1;
16
+ // Find closing brace of productFlavors block
17
+ let depth = 1;
18
+ let i = bracePos;
19
+ while (i < gradle.length && depth > 0) {
20
20
  if (gradle[i] === '{')
21
21
  depth++;
22
- else if (gradle[i] === '}') {
22
+ else if (gradle[i] === '}')
23
23
  depth--;
24
- if (depth === 0)
25
- break;
26
- }
27
24
  i++;
28
25
  }
29
- return gradle.slice(0, i) + '\n' + flavor + '\n' + gradle.slice(i);
26
+ const blockEnd = i - 1;
27
+ const blockContent = gradle.slice(bracePos, blockEnd);
28
+ const lines = blockContent.split('\n');
29
+ // Detect indentation from existing flavor lines
30
+ const existingLine = lines.find(l => /^\s+\w+\s*$/.test(l));
31
+ const indent = existingLine ? ((_a = existingLine.match(/^(\s+)/)) !== null && _a !== void 0 ? _a : ['', ' '])[1] : ' ';
32
+ // Find insertion point to maintain alphabetical order
33
+ let insertAt = lines.length - 1; // default: before last empty/closing line
34
+ for (let li = 0; li < lines.length; li++) {
35
+ const name = lines[li].trim();
36
+ if (/^\w+$/.test(name) && name.localeCompare(client) > 0) {
37
+ insertAt = li;
38
+ break;
39
+ }
40
+ }
41
+ lines.splice(insertAt, 0, `${indent}${client}`);
42
+ return gradle.slice(0, bracePos) + lines.join('\n') + gradle.slice(blockEnd);
30
43
  }
31
- function addProductFlavor({ client, applicationId, }) {
44
+ function addProductFlavor({ client, }) {
32
45
  const gradlePath = path.join(process.cwd(), 'android/app/build.gradle');
33
46
  if (!fs.existsSync(gradlePath)) {
34
47
  console.warn(`build.gradle not found at ${gradlePath} — skipping`);
35
48
  return;
36
49
  }
37
50
  const gradle = fs.readFileSync(gradlePath, 'utf-8');
38
- // Already has this flavor
39
- const flavorRegex = new RegExp(`\\b${client}\\s*\\{`);
51
+ const flavorRegex = new RegExp(`^\\s+${client}\\s*$`, 'm');
40
52
  if (flavorRegex.test(gradle)) {
41
53
  console.log(`Flavor "${client}" already exists in build.gradle — skipping`);
42
54
  return;
43
55
  }
44
- const flavorBlock = ` ${client} {\n` +
45
- ` dimension "default"\n` +
46
- ` applicationId "${applicationId}"\n` +
47
- ` }`;
48
- const updated = insertIntoProductFlavors(gradle, flavorBlock);
56
+ const updated = insertIntoProductFlavors(gradle, client);
49
57
  fs.writeFileSync(gradlePath, updated, 'utf-8');
50
58
  console.log(`Added flavor "${client}" to android/app/build.gradle`);
51
59
  }
@@ -24,7 +24,7 @@ exports.createClientConfig = async (client) => {
24
24
  default: `com.edular.${client}`,
25
25
  },
26
26
  ]);
27
- android_1.addProductFlavor({ client, applicationId });
27
+ android_1.addProductFlavor({ client });
28
28
  ios_scheme_1.createXcodeScheme({ client, bundleId });
29
29
  return { appName, bundleId, applicationId };
30
30
  };
@@ -1 +1 @@
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"}]}}}
1
+ {"version":"0.6.72","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-gcp":{"id":"setup-gcp","description":"Setup GCP service account impersonation","pluginName":"rnbuilder","pluginType":"core","aliases":[],"flags":{"help":{"name":"help","type":"boolean","char":"h","description":"show CLI help","allowNo":false}},"args":[]},"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"}]}}}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "rnbuilder",
3
3
  "description": "React Native builder tool",
4
- "version": "0.6.70",
4
+ "version": "0.6.72",
5
5
  "author": "Tuan Luong @r0b0t3d",
6
6
  "bin": "./bin/run",
7
7
  "bugs": "https://github.com/r0b0t3d/rnbuilder/issues",
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ if ! command -v gcloud >/dev/null 2>&1; then
5
+ echo "gcloud is not installed. Install Google Cloud SDK first."
6
+ exit 1
7
+ fi
8
+
9
+ if ! command -v python3 >/dev/null 2>&1; then
10
+ echo "python3 is required but not found."
11
+ exit 1
12
+ fi
13
+
14
+ echo "Available projects:"
15
+ gcloud projects list --format="table(projectId,name)" 2>/dev/null || echo " (could not list projects)"
16
+ echo ""
17
+
18
+ DEFAULT_PROJECT_ID="${GCP_PROJECT_ID:-edular-19fe4}"
19
+ read -rp "Enter Google Cloud project ID [$DEFAULT_PROJECT_ID]: " PROJECT_ID
20
+ PROJECT_ID="${PROJECT_ID:-$DEFAULT_PROJECT_ID}"
21
+
22
+ DEFAULT_USER_EMAIL="${GCP_USER_EMAIL:-klassapp2013@gmail.com}"
23
+ read -rp "Enter your Google account email [$DEFAULT_USER_EMAIL]: " USER_EMAIL
24
+ USER_EMAIL="${USER_EMAIL:-$DEFAULT_USER_EMAIL}"
25
+
26
+ gcloud config set project "$PROJECT_ID"
27
+
28
+ echo ""
29
+ echo "Logging into gcloud as $USER_EMAIL..."
30
+ gcloud auth login "$USER_EMAIL"
31
+
32
+ echo ""
33
+ echo "Creating base ADC (user credentials, no impersonation)..."
34
+ gcloud auth application-default login
35
+
36
+ BASE_ADC="$HOME/.config/gcloud/application_default_credentials.json"
37
+ ADC_DIR="$HOME/.config/gcloud"
38
+
39
+ # Generate an impersonated ADC file for a service account.
40
+ # Reuses the base user credentials — no extra gcloud login needed.
41
+ generate_sa_adc() {
42
+ local sa_email="$1"
43
+ local sa_slug
44
+ sa_slug=$(printf '%s' "$sa_email" | cut -d'@' -f1 | tr -cs '[:alnum:]' '_' | sed 's/_*$//')
45
+ local output="$ADC_DIR/adc_${sa_slug}.json"
46
+
47
+ echo ""
48
+ echo "Granting roles/iam.serviceAccountTokenCreator to $USER_EMAIL for $sa_email..."
49
+ # Unset gcloud impersonation so the grant runs as the user, not as an SA
50
+ gcloud config unset auth/impersonate_service_account 2>/dev/null || true
51
+ gcloud iam service-accounts add-iam-policy-binding \
52
+ "$sa_email" \
53
+ --role="roles/iam.serviceAccountTokenCreator" \
54
+ --member="user:$USER_EMAIL" \
55
+ --project="$PROJECT_ID"
56
+
57
+ python3 - "$BASE_ADC" "$sa_email" "$output" <<'PYEOF'
58
+ import json, sys
59
+
60
+ base_path, sa_email, output_path = sys.argv[1], sys.argv[2], sys.argv[3]
61
+
62
+ with open(base_path) as f:
63
+ base = json.load(f)
64
+
65
+ # If base is already an impersonated ADC, extract its source_credentials.
66
+ # Otherwise use base directly as the source (authorized_user).
67
+ src = base.get("source_credentials", base)
68
+
69
+ adc = {
70
+ "delegates": [],
71
+ "service_account_impersonation_url": (
72
+ "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/"
73
+ f"{sa_email}:generateAccessToken"
74
+ ),
75
+ "source_credentials": src,
76
+ "type": "impersonated_service_account",
77
+ }
78
+
79
+ with open(output_path, "w") as f:
80
+ json.dump(adc, f, indent=2)
81
+
82
+ print(f"Created: {output_path}")
83
+ print(f"Add to client .env:")
84
+ print(f" GOOGLE_APPLICATION_CREDENTIALS={output_path}")
85
+ PYEOF
86
+ }
87
+
88
+ # Service accounts from repo's fastlane/configs.json (gcp.serviceAccounts), if provided.
89
+ if [ -n "${GCP_SERVICE_ACCOUNTS:-}" ]; then
90
+ echo ""
91
+ echo "Service accounts from fastlane/configs.json:"
92
+ IFS=',' read -ra CONFIG_SAS <<< "$GCP_SERVICE_ACCOUNTS"
93
+ for sa in "${CONFIG_SAS[@]}"; do
94
+ echo " - $sa"
95
+ done
96
+ for sa in "${CONFIG_SAS[@]}"; do
97
+ generate_sa_adc "$sa"
98
+ done
99
+ else
100
+ read -rp "Enter service account email [fastlane@edular-19fe4.iam.gserviceaccount.com]: " SERVICE_ACCOUNT_EMAIL
101
+ SERVICE_ACCOUNT_EMAIL="${SERVICE_ACCOUNT_EMAIL:-fastlane@edular-19fe4.iam.gserviceaccount.com}"
102
+ generate_sa_adc "$SERVICE_ACCOUNT_EMAIL"
103
+ fi
104
+
105
+ # Additional service accounts
106
+ while true; do
107
+ echo ""
108
+ read -rp "Add another service account? (y/N): " ADD_MORE
109
+ case "${ADD_MORE:-N}" in
110
+ [Yy]*)
111
+ read -rp "Enter service account email: " EXTRA_SA
112
+ if [ -n "${EXTRA_SA:-}" ]; then
113
+ generate_sa_adc "$EXTRA_SA"
114
+ fi
115
+ ;;
116
+ *) break ;;
117
+ esac
118
+ done
119
+
120
+ echo ""
121
+ echo "Done."
122
+ echo ""
123
+ echo "To test a service account token:"
124
+ echo " gcloud auth print-access-token --impersonate-service-account=<SA_EMAIL>"
125
+ echo ""
126
+ echo "ADC files are in: $ADC_DIR/adc_*.json"
127
+ echo "Each client .env needs:"
128
+ echo " GOOGLE_APPLICATION_CREDENTIALS=<path to their SA's adc file>"