rnbuilder 0.5.24 → 0.5.26

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
@@ -20,7 +20,7 @@ $ npm install -g rnbuilder
20
20
  $ rnbuilder COMMAND
21
21
  running command...
22
22
  $ rnbuilder (-v|--version|version)
23
- rnbuilder/0.5.24 darwin-arm64 node-v16.19.0
23
+ rnbuilder/0.5.26 darwin-arm64 node-v16.19.0
24
24
  $ rnbuilder --help [COMMAND]
25
25
  USAGE
26
26
  $ rnbuilder COMMAND
@@ -59,7 +59,7 @@ EXAMPLE
59
59
  hello world from ./src/build.ts!
60
60
  ```
61
61
 
62
- _See code: [src/commands/build.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.5.24/src/commands/build.ts)_
62
+ _See code: [src/commands/build.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.5.26/src/commands/build.ts)_
63
63
 
64
64
  ## `rnbuilder codepush [BUILD]`
65
65
 
@@ -78,7 +78,7 @@ EXAMPLE
78
78
  Push changes to existing apps ./src/firebase-sync-udids.ts!
79
79
  ```
80
80
 
81
- _See code: [src/commands/codepush.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.5.24/src/commands/codepush.ts)_
81
+ _See code: [src/commands/codepush.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.5.26/src/commands/codepush.ts)_
82
82
 
83
83
  ## `rnbuilder help [COMMAND]`
84
84
 
@@ -110,7 +110,7 @@ EXAMPLE
110
110
  initialize environment for first run from ./src/initialize.ts!
111
111
  ```
112
112
 
113
- _See code: [src/commands/initialize.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.5.24/src/commands/initialize.ts)_
113
+ _See code: [src/commands/initialize.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.5.26/src/commands/initialize.ts)_
114
114
 
115
115
  ## `rnbuilder match-nuke [BUILD]`
116
116
 
@@ -129,7 +129,7 @@ EXAMPLE
129
129
  Nuke certificates ./src/match-nuke.ts!
130
130
  ```
131
131
 
132
- _See code: [src/commands/match-nuke.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.5.24/src/commands/match-nuke.ts)_
132
+ _See code: [src/commands/match-nuke.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.5.26/src/commands/match-nuke.ts)_
133
133
 
134
134
  ## `rnbuilder setup [SETUP]`
135
135
 
@@ -147,7 +147,7 @@ EXAMPLE
147
147
  hello world from ./src/setup.ts!
148
148
  ```
149
149
 
150
- _See code: [src/commands/setup.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.5.24/src/commands/setup.ts)_
150
+ _See code: [src/commands/setup.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.5.26/src/commands/setup.ts)_
151
151
 
152
152
  ## `rnbuilder sync-udids [BUILD]`
153
153
 
@@ -166,5 +166,5 @@ EXAMPLE
166
166
  Sync device UDIDs from firebase from ./src/firebase-sync-udids.ts!
167
167
  ```
168
168
 
169
- _See code: [src/commands/sync-udids.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.5.24/src/commands/sync-udids.ts)_
169
+ _See code: [src/commands/sync-udids.ts](https://github.com/r0b0t3d/rnbuilder/blob/v0.5.26/src/commands/sync-udids.ts)_
170
170
  <!-- commandsstop -->
@@ -205,6 +205,8 @@ class Build extends command_1.Command {
205
205
  shell.exec('bundle update cocoapods');
206
206
  shell.cd('../');
207
207
  shell.exec('yarn install && npx pod-install');
208
+ // Clean fastlane builds
209
+ shell.exec('rm -rf fastlane/builds');
208
210
  // Clean android if any
209
211
  if (target.includes('android')) {
210
212
  shell.cd('./android');
@@ -11,9 +11,10 @@ const client_1 = require("../utils/setup/client");
11
11
  const env_1 = require("../utils/env");
12
12
  const apple_1 = require("../utils/setup/apple");
13
13
  const onesignal_1 = require("../utils/setup/onesignal");
14
+ const common_1 = require("../utils/common");
15
+ const fastlane_1 = require("../utils/setup/fastlane");
14
16
  class Setup extends command_1.Command {
15
17
  async run() {
16
- const envVars = env_1.readEnvVars(path.join(process.cwd(), 'template/env.template'));
17
18
  const questions = [
18
19
  {
19
20
  type: 'input',
@@ -32,10 +33,13 @@ class Setup extends command_1.Command {
32
33
  },
33
34
  ];
34
35
  const { client } = await inquirer.prompt(questions);
36
+ let envPath = path.join(process.cwd(), `configs/${client}/.env.prod`);
37
+ if (!common_1.checkFileExists(envPath)) {
38
+ envPath = path.join(process.cwd(), 'template/env.template');
39
+ }
40
+ const envVars = env_1.readEnvVars(envPath);
35
41
  const { bundleId, applicationId, appName } = await client_1.createClientConfig(client);
36
42
  env_1.setEnvValue('CLIENT', client, envVars);
37
- env_1.setEnvValue('BUNDLE_ID', bundleId, envVars);
38
- env_1.setEnvValue('APPLICATION_ID', applicationId, envVars);
39
43
  const deeplinkHost = envVars.DEEP_LINK_HOST.value.replace('[client]', client);
40
44
  env_1.setEnvValue('DEEP_LINK_HOST', deeplinkHost, envVars);
41
45
  const { appleTeamId, itcTeamId } = await apple_1.getAppleTeam();
@@ -67,18 +71,61 @@ class Setup extends command_1.Command {
67
71
  // Store dev env
68
72
  env_1.setEnvValue('ENV', 'dev', envVars);
69
73
  const devEnvVars = env_1.updateServiceDomain(envVars, 'app');
70
- env_1.setEnvValue('APP_NAME', `${appName} Dev`, devEnvVars);
74
+ if (appName) {
75
+ env_1.setEnvValue('APP_NAME', `${appName} Dev`, devEnvVars);
76
+ }
77
+ env_1.setEnvValue('BUNDLE_ID', `${bundleId}.dev`, devEnvVars);
78
+ env_1.setEnvValue('APPLICATION_ID', `${applicationId}.dev`, devEnvVars);
71
79
  env_1.saveEnvValues(devEnvVars, path.join(process.cwd(), `/configs/${client}/.env.dev`));
72
80
  // Store staging env
73
81
  env_1.setEnvValue('ENV', 'staging', envVars);
74
82
  const stagingEnvVars = env_1.updateServiceDomain(envVars, 'app');
75
- env_1.setEnvValue('APP_NAME', `${appName} Staging`, stagingEnvVars);
83
+ if (appName) {
84
+ env_1.setEnvValue('APP_NAME', `${appName} Staging`, stagingEnvVars);
85
+ }
86
+ env_1.setEnvValue('BUNDLE_ID', `${bundleId}.staging`, stagingEnvVars);
87
+ env_1.setEnvValue('APPLICATION_ID', `${applicationId}.staging`, stagingEnvVars);
76
88
  env_1.saveEnvValues(stagingEnvVars, path.join(process.cwd(), `/configs/${client}/.env.staging`));
77
89
  // Store prod env
78
90
  env_1.setEnvValue('ENV', 'prod', envVars);
79
- const prodEnvVars = env_1.updateServiceDomain(envVars, 'app');
80
- env_1.setEnvValue('APP_NAME', appName, prodEnvVars);
91
+ const prodEnvVars = env_1.updateServiceDomain(envVars, 'com');
92
+ if (appName) {
93
+ env_1.setEnvValue('APP_NAME', appName, prodEnvVars);
94
+ }
95
+ env_1.setEnvValue('BUNDLE_ID', bundleId, prodEnvVars);
96
+ env_1.setEnvValue('APPLICATION_ID', applicationId, prodEnvVars);
81
97
  env_1.saveEnvValues(prodEnvVars, path.join(process.cwd(), `/configs/${client}/.env.prod`));
98
+ // Setup fastlane
99
+ const { firebaseIosApp, firebaseAndroidApp } = await fastlane_1.setupFastlane({
100
+ client,
101
+ });
102
+ const fastlaneDir = path.join(process.cwd(), `fastlane/clients/${client}/fastlane`);
103
+ const fastlaneEnvVars = env_1.readEnvVars(path.join(fastlaneDir, '/.env'));
104
+ env_1.setEnvValue('CLIENT', client, fastlaneEnvVars);
105
+ if (bundleId) {
106
+ env_1.setEnvValue('APP_IDENTIFIER', bundleId, fastlaneEnvVars);
107
+ }
108
+ if (applicationId) {
109
+ env_1.setEnvValue('APP_IDENTIFIER_ANDROID', bundleId, fastlaneEnvVars);
110
+ }
111
+ if (appName) {
112
+ env_1.setEnvValue('APP_NAME', appName, fastlaneEnvVars);
113
+ }
114
+ env_1.setEnvValue('XCODEPROJ_SCHEME', `${client} prod`, fastlaneEnvVars);
115
+ if (appleTeamId) {
116
+ env_1.setEnvValue('APPLE_TEAM_ID', appleTeamId, fastlaneEnvVars);
117
+ }
118
+ if (itcTeamId) {
119
+ env_1.setEnvValue('ITC_TEAM_ID', itcTeamId, fastlaneEnvVars);
120
+ }
121
+ env_1.setEnvValue('FLAVOR', common_1.capitalize(client), fastlaneEnvVars);
122
+ if (firebaseIosApp) {
123
+ env_1.setEnvValue('FIREBASE_IOS_APP', firebaseIosApp, fastlaneEnvVars);
124
+ }
125
+ if (firebaseAndroidApp) {
126
+ env_1.setEnvValue('FIREBASE_ANDROID_APP', firebaseAndroidApp, fastlaneEnvVars);
127
+ }
128
+ env_1.saveEnvValues(fastlaneEnvVars, fastlaneDir + '/.env');
82
129
  }
83
130
  }
84
131
  exports.default = Setup;
@@ -5,4 +5,7 @@ export declare const getAppVersion: (env: string, client?: string | undefined, t
5
5
  build: number;
6
6
  };
7
7
  export declare const copyDir: (src: string, dest: string) => Promise<unknown>;
8
+ export declare const copyFile: (srcFile: string, destFile: string) => Promise<unknown>;
9
+ export declare const checkFileExists: (path: string) => boolean;
8
10
  export declare const normalise: (input: string) => string;
11
+ export declare const capitalize: (string: string) => string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.normalise = exports.copyDir = exports.getAppVersion = exports.getDirectories = exports.buildKeyValuePairs = void 0;
3
+ exports.capitalize = exports.normalise = exports.checkFileExists = exports.copyFile = exports.copyDir = exports.getAppVersion = exports.getDirectories = exports.buildKeyValuePairs = void 0;
4
4
  const fs = require("fs");
5
5
  exports.buildKeyValuePairs = (params) => Object.keys(params).map(key => `${key}:${params[key]}`);
6
6
  exports.getDirectories = (path) => {
@@ -45,4 +45,19 @@ exports.copyDir = (src, dest) => new Promise((resolve, reject) => {
45
45
  }
46
46
  });
47
47
  });
48
+ exports.copyFile = (srcFile, destFile) => new Promise((resolve, reject) => {
49
+ console.log('Copy file', srcFile, destFile);
50
+ fs.cp(srcFile, destFile, (err) => {
51
+ if (err) {
52
+ reject(err);
53
+ }
54
+ else {
55
+ resolve(true);
56
+ }
57
+ });
58
+ });
59
+ exports.checkFileExists = (path) => fs.existsSync(path);
48
60
  exports.normalise = (input) => input.replace(/'/g, '').trim();
61
+ exports.capitalize = (string) => {
62
+ return string.charAt(0).toUpperCase() + string.slice(1);
63
+ };
@@ -17,4 +17,4 @@ export declare const saveEnvValues: (newValues: any, destFilePath: string) => vo
17
17
  * @param {string} value Value to update/insert
18
18
  */
19
19
  export declare const setEnvValue: (key: string, value: string, envVars: any) => void;
20
- export declare const updateServiceDomain: (envVars: any, domain: string) => any[];
20
+ export declare const updateServiceDomain: (envVars: any, domain: string) => any;
package/lib/utils/env.js CHANGED
@@ -51,12 +51,14 @@ exports.setEnvValue = (key, value, envVars) => {
51
51
  envVars[key] = Object.assign(Object.assign({}, envVars[key]), { value });
52
52
  };
53
53
  exports.updateServiceDomain = (envVars, domain) => {
54
- return Object.keys(envVars).map(key => {
54
+ const result = {};
55
+ Object.keys(envVars).forEach(key => {
55
56
  var _a;
56
- const data = envVars[key];
57
+ let data = envVars[key];
57
58
  if ((_a = data.value) === null || _a === void 0 ? void 0 : _a.includes('[domain]')) {
58
- return Object.assign(Object.assign({}, data), { value: data.value.replace('[domain]', domain) });
59
+ data = Object.assign(Object.assign({}, data), { value: data.value.replace('[domain]', domain) });
59
60
  }
60
- return data;
61
+ result[key] = data;
61
62
  });
63
+ return result;
62
64
  };
@@ -18,7 +18,7 @@ exports.getAppleTeam = async () => {
18
18
  {
19
19
  type: 'input',
20
20
  name: 'team',
21
- message: 'What is Apple team name?',
21
+ message: 'What is Apple team name (using for searching)?',
22
22
  },
23
23
  ]);
24
24
  shell.exec(`fastlane get_team_names search:${team} id:${account}`);
@@ -12,7 +12,7 @@ exports.createClientConfig = (client) => {
12
12
  {
13
13
  type: 'confirm',
14
14
  name: 'ios-client',
15
- message: `Please add **${client} prod** scheme and config`,
15
+ message: `Please add **${client} prod** scheme and config Pre actions`,
16
16
  },
17
17
  {
18
18
  type: 'input',
@@ -0,0 +1,4 @@
1
+ export declare const setupFastlane: ({ client }: any) => Promise<{
2
+ firebaseIosApp: string;
3
+ firebaseAndroidApp: string;
4
+ }>;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setupFastlane = void 0;
4
+ const common_1 = require("../common");
5
+ const path = require("path");
6
+ const inquirer = require("inquirer");
7
+ exports.setupFastlane = async ({ client }) => {
8
+ const clientDir = path.join(process.cwd(), `fastlane/clients/${client}`);
9
+ const fastlaneDir = path.join(clientDir, 'fastlane');
10
+ // Copy template folders
11
+ await common_1.copyDir(path.join(process.cwd(), 'template/fastlane'), fastlaneDir);
12
+ const { firebaseServiceAccountFile, firebaseIosApp, firebaseAndroidApp, jsonKeyFile, } = await inquirer.prompt([
13
+ {
14
+ type: 'input',
15
+ name: 'firebaseServiceAccountFile',
16
+ message: 'Firebase Service Account file?',
17
+ },
18
+ {
19
+ type: 'input',
20
+ name: 'firebaseIosApp',
21
+ message: 'What is Firebase iOS App Id?',
22
+ },
23
+ {
24
+ type: 'input',
25
+ name: 'firebaseAndroidApp',
26
+ message: 'What is Firebase Android App Id?',
27
+ },
28
+ {
29
+ type: 'input',
30
+ name: 'jsonKeyFile',
31
+ message: 'Path to Google json file? See https://docs.fastlane.tools/actions/supply/#setup',
32
+ },
33
+ ]);
34
+ if (firebaseServiceAccountFile) {
35
+ await common_1.copyFile(common_1.normalise(firebaseServiceAccountFile), path.join(fastlaneDir, 'firebase.json'));
36
+ }
37
+ if (jsonKeyFile) {
38
+ await common_1.copyFile(common_1.normalise(jsonKeyFile), path.join(clientDir, 'key.json'));
39
+ }
40
+ return {
41
+ firebaseIosApp: common_1.normalise(firebaseIosApp),
42
+ firebaseAndroidApp: common_1.normalise(firebaseAndroidApp),
43
+ };
44
+ };
@@ -39,8 +39,8 @@ exports.prepareLaunchScreen = async ({ iosAssetFolder, androidAssetFolder, }) =>
39
39
  const inputFile = common_1.normalise(launchScreenFile);
40
40
  // Copy template files
41
41
  if (launchScreen === 'icon') {
42
- await common_1.copyDir('./template/BootSplashLogo.imageset', path.join(iosAssetFolder, 'BootSplashLogo.imageset'));
43
42
  if (inputFile) {
43
+ await common_1.copyDir('./template/BootSplashLogo.imageset', path.join(iosAssetFolder, 'BootSplashLogo.imageset'));
44
44
  await sharp(inputFile)
45
45
  .resize(512, 512)
46
46
  .png()
@@ -88,8 +88,8 @@ exports.prepareLaunchScreen = async ({ iosAssetFolder, androidAssetFolder, }) =>
88
88
  }
89
89
  }
90
90
  else if (launchScreen === 'fullscreen') {
91
- await common_1.copyDir('./template/BootSplashImage.imageset', path.join(iosAssetFolder, 'BootSplashImage.imageset'));
92
91
  if (inputFile) {
92
+ await common_1.copyDir('./template/BootSplashImage.imageset', path.join(iosAssetFolder, 'BootSplashImage.imageset'));
93
93
  await sharp(inputFile)
94
94
  .resize(512, 512)
95
95
  .png()
@@ -1 +1 @@
1
- {"version":"0.5.24","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"]},"env":{"name":"env","type":"option","char":"e","description":"Set the environment: prod, adhoc, staging","options":["staging","adhoc","prod"]},"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"}},"args":[{"name":"build"}]},"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"}]},"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-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"}]},"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"}]},"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.5.26","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"]},"env":{"name":"env","type":"option","char":"e","description":"Set the environment: prod, adhoc, staging","options":["staging","adhoc","prod"]},"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"}},"args":[{"name":"build"}]},"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"}]},"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-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"}]},"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"}]},"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.5.24",
4
+ "version": "0.5.26",
5
5
  "author": "Tuan Luong @r0b0t3d",
6
6
  "bin": {
7
7
  "rnbuilder": "./bin/run"