@radhya/mach 1.2.0 → 1.3.1
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 +369 -166
- package/dist/chunk-S64YFTRR.js +4 -0
- package/dist/chunk-VX4DWB47.js +69 -0
- package/dist/commands/isolated-upload.js +1 -1
- package/dist/credentials-PS7URNCB.js +1 -0
- package/dist/index.js +1549 -1
- package/docs/frameworks/README.md +72 -0
- package/docs/frameworks/expo.md +148 -0
- package/docs/frameworks/flutter.md +67 -0
- package/docs/frameworks/react-native.md +159 -0
- package/package.json +4 -2
- package/dist/chunk-UILXWVHH.js +0 -1
- package/dist/chunk-YZYFEHP5.js +0 -1
- package/dist/credentials-SVT5MSVT.js +0 -1
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Mach CLI
|
|
2
2
|
|
|
3
|
-
**Cloud Build
|
|
3
|
+
**Cloud Build & Delivery for React Native, Expo, and Flutter**
|
|
4
4
|
|
|
5
|
-
Mach is a
|
|
5
|
+
Mach is a CLI that lets you build, sign, and submit React Native and Expo apps from the cloud. Flutter support is modeled in configuration and documentation so it can be added without changing the command surface.
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -16,10 +16,7 @@ npm install -g @radhya/mach
|
|
|
16
16
|
# Login to Mach Dashboard
|
|
17
17
|
mach login
|
|
18
18
|
|
|
19
|
-
#
|
|
20
|
-
mach init
|
|
21
|
-
|
|
22
|
-
# Link an existing project
|
|
19
|
+
# Link your project
|
|
23
20
|
mach link
|
|
24
21
|
|
|
25
22
|
# Run a cloud build
|
|
@@ -29,108 +26,265 @@ mach build --platform android
|
|
|
29
26
|
|
|
30
27
|
## Commands
|
|
31
28
|
|
|
32
|
-
| Command
|
|
33
|
-
|
|
34
|
-
| `mach login`
|
|
35
|
-
| `mach
|
|
36
|
-
| `mach
|
|
37
|
-
| `mach
|
|
38
|
-
| `mach
|
|
39
|
-
| `mach
|
|
40
|
-
| `mach
|
|
41
|
-
| `mach
|
|
42
|
-
| `mach
|
|
43
|
-
| `mach
|
|
44
|
-
| `mach
|
|
45
|
-
| `mach
|
|
46
|
-
| `mach
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
|
51
|
-
|
|
52
|
-
| `mach
|
|
53
|
-
|
|
29
|
+
| Command | Description |
|
|
30
|
+
| -------------------------- | ------------------------------------------------------------------ |
|
|
31
|
+
| `mach login` | Authenticate with Mach Dashboard |
|
|
32
|
+
| `mach logout` | Remove saved local Mach credentials |
|
|
33
|
+
| `mach init` | Create a new Mach project |
|
|
34
|
+
| `mach link` | Link local directory to an existing project |
|
|
35
|
+
| `mach me` | Show current logged-in user |
|
|
36
|
+
| `mach build` | Start a cloud build (iOS / Android) |
|
|
37
|
+
| `mach maestro` | Queue managed Maestro tests for a build or artifact |
|
|
38
|
+
| `mach update` | Deploy an OTA update to a channel or branch |
|
|
39
|
+
| `mach submit` | Submit to App Store or Google Play |
|
|
40
|
+
| `mach credentials` | Manage iOS certificates, provisioning profiles & Android keystores |
|
|
41
|
+
| `mach credentials:service` | Upload App Store Connect or Google Play service credentials |
|
|
42
|
+
| `mach env` | Manage, list, and download environment variables and secrets |
|
|
43
|
+
| `mach config` | Show resolved configuration for a build profile |
|
|
44
|
+
| `mach start` | Start the Metro development server |
|
|
45
|
+
| `mach device` | Register test devices for ad-hoc distribution |
|
|
46
|
+
| `mach audit` | Run automated security checks on your project |
|
|
47
|
+
| `mach agent` | Manage the local device discovery agent for Install Hub |
|
|
48
|
+
| `mach host` | Set up S3 static hosting and CloudFront |
|
|
49
|
+
| `mach sitemap` | Generate sitemap.xml from Expo Router app routes |
|
|
50
|
+
|
|
51
|
+
## Managed Maestro Testing
|
|
52
|
+
|
|
53
|
+
Mach's E2E testing layer is being implemented to run Maestro without requiring Maestro Cloud:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
mach build android --profile e2e-test --maestro
|
|
57
|
+
mach maestro --latest --platform android --profile e2e-test
|
|
58
|
+
mach maestro --build-id <buildId>
|
|
59
|
+
mach maestro --local --build-id <buildId>
|
|
60
|
+
mach maestro --build-id <buildId> --maestro-use-build-source
|
|
61
|
+
mach maestro --build-id <buildId> --wait
|
|
62
|
+
mach maestro --latest --platform ios --maestro-env MAESTRO_TEST_EMAIL --maestro-env MAESTRO_TEST_PASSWORD
|
|
63
|
+
mach maestro --platform ios --local --maestro-flows .maestro-authenticated --maestro-env MAESTRO_TEST_EMAIL --maestro-env MAESTRO_TEST_PASSWORD
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Mach manages the test execution infrastructure for you, so projects do not need a separate Maestro Cloud setup.
|
|
67
|
+
Managed Android Maestro runs need an APK artifact and a `.maestro` flow file or directory. By default, `mach maestro --maestro-flows .maestro` uploads the local flow folder for that run when the path exists in the current project. If the local path is not present, Mach falls back to the build source snapshot.
|
|
68
|
+
Managed Android runners use emulators, so the tested APK must include `x86_64`. Mach automatically includes emulator ABI support when you build with `--maestro`, when the profile name looks like `maestro`/`e2e`/`test`, or when the build profile sets `"android": { "emulator": true }`.
|
|
69
|
+
Managed Maestro is billed from the Pay as you go wallet after the runner completes. Default pricing is $0.50 for the first 15 minutes, then $0.10 per additional 10-minute block; admins can change these rates from Dashboard Admin > Pricing.
|
|
70
|
+
Use `mach maestro --local` to run the same flows on your connected local emulator/device. With `--build-id`, Mach downloads and installs the build artifact first; without a build or artifact flag, it runs against the app already installed locally.
|
|
71
|
+
Use `--maestro-env <name>` for local or managed runs that need test credentials or other dynamic values. The flag is repeatable and also accepts comma-separated names. Local runs forward only names that exist in the shell or loaded dotenv environment, and the startup log lists every forwarded name. Managed runs store requested values encrypted, pass them to the iOS or Android Maestro command, redact values from runner logs, and fall back to the Mach server environment when a value is not present in the CLI environment.
|
|
72
|
+
|
|
73
|
+
Useful options:
|
|
74
|
+
|
|
75
|
+
| Flag | Description |
|
|
76
|
+
| --------------------------- | ------------------------------------------------------------- |
|
|
77
|
+
| `--maestro` | Queue tests automatically after a successful `mach build` |
|
|
78
|
+
| `--maestro-flows <path>` | Flow file or directory, defaults to `.maestro` |
|
|
79
|
+
| `--maestro-device <name>` | Managed runner device profile, defaults to Android Pixel 6 |
|
|
80
|
+
| `--maestro-retries <n>` | Retry count for failed runs, from 0 to 10 |
|
|
81
|
+
| `--maestro-record` | Enable recording when supported by the runner |
|
|
82
|
+
| `--maestro-use-build-source` | Use flows from the build source snapshot instead of local upload |
|
|
83
|
+
| `--maestro-env <name>` | Forward one env var to Maestro; repeat or comma-separate names |
|
|
84
|
+
| `--local` | Run `maestro test` locally instead of queueing a cloud runner |
|
|
85
|
+
| `--wait` | Wait for managed tests and exit non-zero if the run fails |
|
|
86
|
+
| `--wait-timeout <minutes>` | Maximum time to wait for a managed run, defaults to 60 |
|
|
87
|
+
| `--wait-interval <seconds>` | Polling interval for `--wait`, defaults to 15 |
|
|
54
88
|
|
|
55
89
|
## Cloud Builds
|
|
56
90
|
|
|
57
|
-
|
|
91
|
+
Trigger a build in the cloud — Mach handles zipping, uploading, compiling, and returning your IPA or APK.
|
|
58
92
|
|
|
59
93
|
```bash
|
|
60
|
-
# iOS build
|
|
94
|
+
# iOS build (production)
|
|
61
95
|
mach build --platform ios --profile production
|
|
62
96
|
|
|
63
97
|
# Android build
|
|
64
98
|
mach build --platform android --profile production
|
|
65
99
|
|
|
66
|
-
#
|
|
100
|
+
# Build locally
|
|
67
101
|
mach build --platform ios --local
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Installable artifacts are downloaded through Mach public file routes, not raw S3 URLs. Public build pages and QR install links use the configured files domain (for example `https://files.getmach.dev`) so users do not see bucket names or presigned S3 URLs.
|
|
105
|
+
|
|
106
|
+
### Framework Selection
|
|
107
|
+
|
|
108
|
+
Mach does not auto-detect your app framework during builds. The build framework is declared in `mach.config.json` and is treated as the source of truth:
|
|
68
109
|
|
|
69
|
-
|
|
70
|
-
|
|
110
|
+
```json
|
|
111
|
+
{
|
|
112
|
+
"framework": "react-native"
|
|
113
|
+
}
|
|
71
114
|
```
|
|
72
115
|
|
|
116
|
+
Supported values:
|
|
117
|
+
|
|
118
|
+
| Value | Status | Build behavior |
|
|
119
|
+
| ---------------- | ------------ | ------------------------------------------------------------------------------ |
|
|
120
|
+
| `react-native` | Supported | Uses committed `ios/` and `android/` native projects. No Expo prebuild runs. |
|
|
121
|
+
| `expo` | Supported | Runs Expo prebuild before native compilation and applies Expo-specific guards. |
|
|
122
|
+
| `flutter` | Planned | Reserved in config/docs. Builds fail clearly until Flutter builders are added. |
|
|
123
|
+
|
|
124
|
+
`mach init` and `mach link` create `framework: "react-native"` by default. Change it to `"expo"` for Expo projects before running builds. This explicit config prevents a React Native change from affecting Expo projects, and avoids fragile package scanning in CI.
|
|
125
|
+
|
|
126
|
+
Use the framework guide that matches your app. The command surface stays the same; only `framework` changes the build pipeline:
|
|
127
|
+
|
|
128
|
+
- [Choose a framework](docs/frameworks/README.md)
|
|
129
|
+
- [Bare React Native](docs/frameworks/react-native.md)
|
|
130
|
+
- [Expo](docs/frameworks/expo.md)
|
|
131
|
+
- [Flutter](docs/frameworks/flutter.md)
|
|
132
|
+
|
|
133
|
+
## Local Device Agent
|
|
134
|
+
|
|
135
|
+
The Install Hub can install builds on connected local devices when the Mach agent is running on your machine.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
# Run in the foreground
|
|
139
|
+
mach agent
|
|
140
|
+
|
|
141
|
+
# Install as a background login service/task
|
|
142
|
+
mach agent install
|
|
143
|
+
|
|
144
|
+
# Inspect or control the background agent
|
|
145
|
+
mach agent status
|
|
146
|
+
mach agent restart
|
|
147
|
+
mach agent stop
|
|
148
|
+
mach agent uninstall
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
`mach agent` is local-only and does not require Dashboard authentication. It exposes device discovery and install endpoints on `http://127.0.0.1:7070`.
|
|
152
|
+
|
|
73
153
|
### Build Options
|
|
74
154
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
155
|
+
| Flag | Description |
|
|
156
|
+
| ---------------- | -------------------------------------------------------------------------------- |
|
|
157
|
+
| `--platform` | `ios` or `android` (required) |
|
|
158
|
+
| `--profile` | Build profile from `mach.config.json` (default: `production`) |
|
|
159
|
+
| `--local` | Run build on local machine |
|
|
160
|
+
| `--dry-run` | Generate build script without running it |
|
|
161
|
+
| `--simulator` | iOS Simulator build (no signing needed) |
|
|
162
|
+
| `--auto-version` | Force Android versionCode or iOS buildNumber lookup from the store |
|
|
163
|
+
| `--auto-submit` | Submit the successful build to App Store Connect or Google Play |
|
|
164
|
+
| `--verbose` | Enable detailed build logging |
|
|
165
|
+
| `--json-output` | Write build metadata (`buildId`, Dashboard URL, status) to a JSON file for CI/CD |
|
|
82
166
|
|
|
83
|
-
|
|
167
|
+
### CI Build URLs
|
|
84
168
|
|
|
85
|
-
|
|
169
|
+
Use `--json-output` when a pipeline needs the Dashboard URL for Slack, deployment notes, or downstream steps:
|
|
86
170
|
|
|
87
|
-
|
|
88
|
-
|
|
171
|
+
```bash
|
|
172
|
+
mach build --platform android --profile production --json-output mach-android-build.json
|
|
173
|
+
ANDROID_URL=$(node -pe "require('./mach-android-build.json').url")
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
The JSON file includes:
|
|
89
177
|
|
|
90
|
-
### usage
|
|
91
|
-
Set the version code to `"auto"` in your config:
|
|
92
178
|
```json
|
|
93
|
-
|
|
94
|
-
"
|
|
179
|
+
{
|
|
180
|
+
"status": "success",
|
|
181
|
+
"buildId": "00000000-0000-0000-0000-000000000000",
|
|
182
|
+
"platform": "android",
|
|
183
|
+
"profile": "production",
|
|
184
|
+
"url": "https://getmach.dev/build/00000000-0000-0000-0000-000000000000"
|
|
95
185
|
}
|
|
96
186
|
```
|
|
97
|
-
Or pass the flag via CLI:
|
|
98
|
-
```bash
|
|
99
|
-
mach build --platform android --auto-version
|
|
100
|
-
```
|
|
101
187
|
|
|
102
|
-
|
|
103
|
-
|
|
188
|
+
Set `MACH_DASHBOARD_URL` only for local or staging dashboards. Production build links default to `https://getmach.dev`.
|
|
189
|
+
|
|
190
|
+
### Bitbucket Pipelines
|
|
191
|
+
|
|
192
|
+
Mach works in Bitbucket through a Dashboard access token. Create a token in **Dashboard → Settings → Access Tokens**, save it as `MACH_TOKEN`, and use `--json-output` when a pipeline needs the Mach build URL for later steps.
|
|
193
|
+
|
|
194
|
+
```yaml
|
|
195
|
+
image: node:24.13.1
|
|
196
|
+
|
|
197
|
+
definitions:
|
|
198
|
+
steps:
|
|
199
|
+
- step: &build-android
|
|
200
|
+
name: Build Android
|
|
201
|
+
size: 2x
|
|
202
|
+
caches:
|
|
203
|
+
- node
|
|
204
|
+
script:
|
|
205
|
+
- npm ci
|
|
206
|
+
- npm install -g @radhya/mach@latest
|
|
207
|
+
- mach build --platform android --profile production --auto-version --json-output mach-android-build.json
|
|
208
|
+
|
|
209
|
+
- step: &submit-android
|
|
210
|
+
name: Submit Android
|
|
211
|
+
size: 2x
|
|
212
|
+
caches:
|
|
213
|
+
- node
|
|
214
|
+
script:
|
|
215
|
+
- npm ci
|
|
216
|
+
- npm install -g @radhya/mach@latest
|
|
217
|
+
- mach submit --latest --platform android --profile production
|
|
218
|
+
```
|
|
104
219
|
|
|
220
|
+
Android submission uses the Google Play Developer API directly, so Bitbucket/Linux runners do not need Ruby or Fastlane for `.aab` uploads. Mach retries transient Play API failures before failing and records Android submissions in the Dashboard. iOS submission is queued through Mach, so the public command is the same from macOS, Linux, Windows, or CI. Users see the Mach submission URL and status.
|
|
105
221
|
|
|
106
|
-
|
|
222
|
+
Before enabling CI, make sure `mach.config.json` is committed, `MACH_TOKEN` is configured as a secure CI variable, credentials are uploaded in the Dashboard, and Android service credentials are attached to the exact package id.
|
|
107
223
|
|
|
108
|
-
|
|
224
|
+
For iOS submit, the user-facing command is universal:
|
|
109
225
|
|
|
110
226
|
```bash
|
|
111
|
-
|
|
112
|
-
mach submit --platform ios
|
|
113
|
-
|
|
114
|
-
# Submit to Google Play (internal track)
|
|
115
|
-
mach submit --platform android --track internal
|
|
227
|
+
mach submit --latest --platform ios --profile production
|
|
116
228
|
```
|
|
117
229
|
|
|
230
|
+
Mach handles the managed macOS submission environment internally. No provider-specific macOS job needs to be added to a user's CI configuration.
|
|
231
|
+
|
|
118
232
|
## Credential Management
|
|
119
233
|
|
|
120
|
-
Manage iOS certificates, provisioning profiles, Android keystores, and
|
|
234
|
+
Manage iOS certificates, provisioning profiles, Android keystores, and App Store Connect API keys — all stored securely in the Dashboard.
|
|
121
235
|
|
|
122
236
|
```bash
|
|
123
237
|
# Interactive credential manager
|
|
124
238
|
mach credentials
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
Mach walks you through each step: choosing a bundle ID, picking a distribution type (development / ad-hoc / App Store), and syncing certificates. Everything is encrypted at rest and injected at build time.
|
|
242
|
+
|
|
243
|
+
For iOS development and ad-hoc profiles, Mach also asks which registered devices should be included. After registering the selected devices with Apple, Mach refreshes the provisioning profile so the uploaded profile includes those devices instead of reusing an older cached profile.
|
|
244
|
+
|
|
245
|
+
### Apple Developer Team Selection
|
|
125
246
|
|
|
126
|
-
|
|
127
|
-
mach credentials:setup --platform ios
|
|
128
|
-
mach credentials:setup --platform android
|
|
247
|
+
For iOS credentials, Mach asks for your Apple ID and then lists the Apple Developer teams connected to that account through `@expo/apple-utils`. If the account has multiple teams, choose the team to use for signing; if Apple cannot return the list, Mach falls back to manual Team ID entry. Fastlane is still used later for the certificate/profile sync, but it is no longer required just to display the team picker.
|
|
129
248
|
|
|
130
|
-
|
|
131
|
-
|
|
249
|
+
Mach saves the selected Team ID to the Dashboard for credential storage. It also asks whether to write the Team ID into `mach.config.json` for the selected profile. Accepting the prompt writes only `submit.<profile>.ios.appleTeamId`; production defaults to yes, while development and staging default to no so local config is not cluttered unless you want profile-specific signing.
|
|
250
|
+
|
|
251
|
+
### Multiple Bundle IDs
|
|
252
|
+
|
|
253
|
+
One project can have multiple bundle identifiers (e.g. `com.example.app`, `com.example.app.staging`, `com.example.app.dev`). Each bundle ID has its own independent set of credentials per distribution type. Select or add a bundle ID at the start of `mach credentials` — or skip the prompt with the `--bundle-id` flag for CI:
|
|
254
|
+
|
|
255
|
+
```bash
|
|
256
|
+
mach credentials --bundle-id com.example.app.staging
|
|
132
257
|
```
|
|
133
258
|
|
|
259
|
+
The Dashboard shows each bundle ID as a row. Clicking it reveals its Distribution certificate, Provisioning profile, and Service credentials — matching the same layout as Expo's credentials screen. A **Password stored** badge confirms the P12 export password is saved and will be injected at build time automatically.
|
|
260
|
+
|
|
261
|
+
### Service Credentials
|
|
262
|
+
|
|
263
|
+
Service credentials are used for store automation:
|
|
264
|
+
|
|
265
|
+
- iOS: App Store Connect API key (`.p8`)
|
|
266
|
+
- Android: Google Play service account JSON
|
|
267
|
+
|
|
268
|
+
Android service credentials are scoped to a package name. This matters when one Mach project contains multiple Android application identifiers.
|
|
269
|
+
|
|
270
|
+
```bash
|
|
271
|
+
# Android: upload a Google Play service account JSON
|
|
272
|
+
mach credentials:service --platform android --package-name com.example.app --file ~/Downloads/play-service-account.json
|
|
273
|
+
|
|
274
|
+
# If --package-name is omitted, Mach fetches saved Android package ids
|
|
275
|
+
# from the Dashboard and asks you to select one.
|
|
276
|
+
mach credentials:service --platform android --file ~/Downloads/play-service-account.json
|
|
277
|
+
|
|
278
|
+
# iOS: upload or create an App Store Connect API key
|
|
279
|
+
mach credentials:service --platform ios
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
The Dashboard also supports this under **Credentials → Android → package id → Service credentials**. Uploading a Google Play service key under `*` is legacy behavior; store submission now expects the service credential to match the Android package being submitted.
|
|
283
|
+
|
|
284
|
+
App Store Connect keys are used for iOS TestFlight submission and iOS buildNumber auto-increment. Mach stores the key id, issuer id, Apple Developer Team ID, and App Store Connect provider/team metadata so build and submit commands can run without Apple ID prompts.
|
|
285
|
+
|
|
286
|
+
Google Play service keys are only needed for store automation: Android submission and Play-backed versionCode resolution. They are not signing credentials. A dev package such as `com.example.app.dev` needs its own Google Play service key only if that exact package exists in Google Play and you explicitly ask Mach to query Play for it.
|
|
287
|
+
|
|
134
288
|
## Configuration
|
|
135
289
|
|
|
136
290
|
Create a `mach.config.json` in your project root:
|
|
@@ -138,93 +292,81 @@ Create a `mach.config.json` in your project root:
|
|
|
138
292
|
```json
|
|
139
293
|
{
|
|
140
294
|
"projectId": "your-project-id",
|
|
295
|
+
"framework": "react-native",
|
|
141
296
|
"name": "MyApp",
|
|
142
297
|
"slug": "my-app",
|
|
143
|
-
"
|
|
298
|
+
"nodeVersion": "24",
|
|
144
299
|
"ios": {
|
|
145
300
|
"bundleIdentifier": "com.example.myapp",
|
|
146
|
-
"
|
|
301
|
+
"appleTeamId": "ABCDE12345",
|
|
302
|
+
"ascAppId": "1234567890",
|
|
303
|
+
"buildNumber": "auto"
|
|
147
304
|
},
|
|
148
305
|
"android": {
|
|
149
306
|
"package": "com.example.myapp",
|
|
150
307
|
"versionCode": "auto"
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
```
|
|
154
|
-
|
|
155
|
-
### Build Profiles
|
|
156
|
-
|
|
157
|
-
Define multiple build profiles under the `build` key. Profiles support deep inheritance via `extends`.
|
|
158
|
-
|
|
159
|
-
```json
|
|
160
|
-
{
|
|
161
|
-
"projectId": "your-project-id",
|
|
162
|
-
"name": "MyApp",
|
|
163
|
-
"slug": "my-app",
|
|
164
|
-
"scheme": "myapp",
|
|
165
|
-
"android": { "package": "com.example.myapp" },
|
|
166
|
-
"ios": { "bundleIdentifier": "com.example.myapp" },
|
|
308
|
+
},
|
|
167
309
|
"build": {
|
|
168
|
-
"base": {},
|
|
169
310
|
"development": {
|
|
170
|
-
"extends": "base",
|
|
171
311
|
"developmentClient": true,
|
|
312
|
+
"distribution": "development",
|
|
172
313
|
"environment": "development"
|
|
173
314
|
},
|
|
174
315
|
"staging": {
|
|
175
|
-
"extends": "base",
|
|
176
316
|
"distribution": "internal"
|
|
177
317
|
},
|
|
178
318
|
"production": {
|
|
179
|
-
"extends": "base",
|
|
180
319
|
"distribution": "store"
|
|
181
320
|
}
|
|
182
321
|
},
|
|
183
322
|
"submit": {
|
|
184
323
|
"production": {
|
|
185
|
-
"android": { "track": "internal" },
|
|
324
|
+
"android": { "track": "internal", "releaseStatus": "completed" },
|
|
186
325
|
"ios": {
|
|
187
|
-
"ascAppId": "
|
|
188
|
-
"appleTeamId": "
|
|
326
|
+
"ascAppId": "",
|
|
327
|
+
"appleTeamId": ""
|
|
189
328
|
}
|
|
190
329
|
}
|
|
191
330
|
}
|
|
192
331
|
}
|
|
193
332
|
```
|
|
194
333
|
|
|
195
|
-
###
|
|
334
|
+
### Root Properties
|
|
196
335
|
|
|
197
|
-
| Property
|
|
198
|
-
|
|
199
|
-
| `
|
|
200
|
-
| `
|
|
201
|
-
| `
|
|
202
|
-
| `
|
|
203
|
-
| `preBuild` / `postBuild` | Reference a script or run a raw command |
|
|
204
|
-
| `envGroups` | Array of environment group names to merge into this profile |
|
|
336
|
+
| Property | Description |
|
|
337
|
+
| ------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
338
|
+
| `projectId` | Auto-generated UUID linking to your Dashboard project |
|
|
339
|
+
| `framework` | Required build framework: `react-native`, `expo`, or `flutter`. Builds never infer this from `package.json` |
|
|
340
|
+
| `name` | App display name |
|
|
341
|
+
| `nodeVersion` | Node.js version to use on the build machine (e.g. `"24"`, `"20.11.0"`). Switches via nvm before installing deps |
|
|
205
342
|
|
|
206
|
-
### Platform
|
|
343
|
+
### Platform Properties
|
|
207
344
|
|
|
208
|
-
|
|
345
|
+
| Property | Description |
|
|
346
|
+
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
|
|
347
|
+
| `ios.bundleIdentifier` | iOS bundle ID used for signing, App Store Connect lookup, and submit |
|
|
348
|
+
| `ios.appleTeamId` | Optional global Apple Developer Team ID used for iOS signing. Profile-specific `submit.<profile>.ios.appleTeamId` overrides it |
|
|
349
|
+
| `ios.ascAppId` | App Store Connect app id. Optional when Mach can find the app by bundle ID; recommended for reliable iOS buildNumber lookup |
|
|
350
|
+
| `ios.buildNumber` | Set to `"auto"` to let Mach resolve the next iOS CFBundleVersion from App Store Connect and seed the server-side build counter |
|
|
351
|
+
| `android.package` | Android package name used for signing, Google Play lookup, and submit |
|
|
352
|
+
| `android.versionCode` | Set to `"auto"` to let store builds resolve the next versionCode via Google Play |
|
|
209
353
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
```
|
|
354
|
+
### Profile Properties
|
|
355
|
+
|
|
356
|
+
| Property | Description |
|
|
357
|
+
| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
358
|
+
| `extends` | Inherit settings from another profile |
|
|
359
|
+
| `environment` | Dashboard Secret Environment to use (`development`, `staging`, `production`) |
|
|
360
|
+
| `developmentClient` | Expo only: `true` to build with `expo-dev-client` |
|
|
361
|
+
| `distribution` | `development` for iOS development signing, `internal` for ad-hoc/internal testing builds, or `store` for App Store / Play Store release builds |
|
|
362
|
+
| `android.emulator` | `true` to include Android emulator ABI support (`x86_64`) for Maestro/e2e APK builds |
|
|
363
|
+
| `android.architectures` | Override Android release ABIs, for example `armeabi-v7a,arm64-v8a,x86_64` |
|
|
364
|
+
| `preBuild` / `postBuild` | Shell command or script reference to run around the build |
|
|
365
|
+
| `envGroups` | Array of environment group names to merge into this profile |
|
|
224
366
|
|
|
225
367
|
### Environment Groups
|
|
226
368
|
|
|
227
|
-
|
|
369
|
+
Reusable sets of environment variables shared across profiles:
|
|
228
370
|
|
|
229
371
|
```json
|
|
230
372
|
{
|
|
@@ -241,111 +383,172 @@ Define reusable sets of environment variables shared across profiles:
|
|
|
241
383
|
}
|
|
242
384
|
```
|
|
243
385
|
|
|
244
|
-
|
|
386
|
+
## Secrets & Environment Variables
|
|
245
387
|
|
|
246
|
-
|
|
388
|
+
Secrets are encrypted at rest and injected at build time, scoped per environment.
|
|
247
389
|
|
|
248
|
-
```
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
390
|
+
```bash
|
|
391
|
+
# Add a secret
|
|
392
|
+
mach env set API_URL=https://api.example.com SECRET_KEY=xxx
|
|
393
|
+
|
|
394
|
+
# List all secrets
|
|
395
|
+
mach env list
|
|
396
|
+
|
|
397
|
+
# Download development secrets to .env.local
|
|
398
|
+
mach env pull
|
|
399
|
+
|
|
400
|
+
# Download a specific environment
|
|
401
|
+
mach env pull --environment staging
|
|
402
|
+
mach env pull --environment production --out .env.production.local
|
|
403
|
+
|
|
404
|
+
# Replace an existing local env file
|
|
405
|
+
mach env pull --environment development --out .env.local --overwrite
|
|
259
406
|
```
|
|
260
407
|
|
|
261
|
-
|
|
408
|
+
`mach env pull` downloads decrypted secrets for local development. It defaults to `--environment development` and `--out .env.local`. The exported file includes `global` secrets plus the selected environment's overrides. Mach refuses to overwrite an existing file unless `--overwrite` is provided. `mach env download` and `mach env export` are aliases for the same behavior.
|
|
409
|
+
|
|
410
|
+
Mach includes the resolved environment in the build fingerprint, so development, staging, and production builds do not reuse a native build cache when their injected variables differ. If a stored secret cannot be decrypted, Mach surfaces that as a configuration problem instead of silently injecting `[Decryption Failed]` into a production binary.
|
|
411
|
+
|
|
412
|
+
### Android Keystore Passwords
|
|
413
|
+
|
|
414
|
+
The keystore file and its passwords are stored encrypted in the Dashboard when managed through `mach credentials`. Legacy projects can still provide Android signing passwords as environment-scoped secrets in the Dashboard, but managed credentials are recommended so passwords stay attached to the keystore record.
|
|
415
|
+
|
|
416
|
+
## Automated Versioning
|
|
417
|
+
|
|
418
|
+
Mach can automatically determine the next Android `versionCode` and iOS `buildNumber` from the store, then increment safely to prevent duplicate upload rejections.
|
|
419
|
+
|
|
420
|
+
**Android setup:** Upload a Google Service Account JSON via `mach credentials` → Service Credentials for the exact Android package being queried.
|
|
262
421
|
|
|
263
|
-
|
|
422
|
+
**iOS setup:** Upload or create an App Store Connect API key with `mach credentials:service --platform ios`. Set `ios.ascAppId` in `mach.config.json` when possible so Mach can query the exact App Store Connect app.
|
|
423
|
+
|
|
424
|
+
**Usage:**
|
|
264
425
|
|
|
265
426
|
```bash
|
|
266
|
-
|
|
267
|
-
mach
|
|
427
|
+
mach build --platform android --auto-version
|
|
428
|
+
mach build --platform ios --profile production --auto-version
|
|
429
|
+
```
|
|
268
430
|
|
|
269
|
-
|
|
270
|
-
|
|
431
|
+
Or in config:
|
|
432
|
+
|
|
433
|
+
```json
|
|
434
|
+
"android": { "versionCode": "auto" },
|
|
435
|
+
"ios": { "buildNumber": "auto" }
|
|
271
436
|
```
|
|
272
437
|
|
|
273
|
-
|
|
274
|
-
|
|
438
|
+
When `android.versionCode` is set to `"auto"`, Mach performs the Google Play lookup automatically for Android `store` builds. For `internal` builds, Mach skips the Play lookup by default so development packages do not need Google Play service keys. Pass `--auto-version` on an internal build only when that package is registered in Google Play and should be queried.
|
|
439
|
+
|
|
440
|
+
When `ios.buildNumber` is set to `"auto"`, Mach queries App Store Connect for the latest uploaded build number and seeds Mach's server-side counter from that value. If App Store Connect has build `81` and Mach's internal counter is still `1`, the next iOS build becomes at least `82`. Concurrent builds are still safe because the Dashboard API increments the counter atomically.
|
|
275
441
|
|
|
276
|
-
|
|
442
|
+
This only changes iOS `CFBundleVersion` / build number. It does not bump the public App Store version (`CFBundleShortVersionString`, usually `expo.version` in `app.json`). After a version like `1.17.0` is approved/released, Apple closes that train for new uploads; bump the app version, for example to `1.17.1`, rebuild, then submit.
|
|
443
|
+
|
|
444
|
+
Regular iOS development builds should set `distribution: "development"` and resolve Apple Development certificates/profiles. For custom profile names, Mach follows the profile fields, not the profile name: `environment` chooses Dashboard secrets, and `distribution` / `iosExportMethod` chooses signing. Dev-client builds can keep `distribution: "internal"` and set `developmentClient: true`; Mach will still use development signing credentials while producing an internal installable artifact.
|
|
445
|
+
|
|
446
|
+
For Android dev-client builds, Mach defaults to Gradle `assembleDebug` so the output opens the Metro/debug server instead of behaving like a standalone release APK. For iOS dev-client builds, Mach uses the Debug Xcode configuration.
|
|
447
|
+
|
|
448
|
+
## Store Submission
|
|
277
449
|
|
|
278
450
|
```bash
|
|
279
|
-
#
|
|
280
|
-
mach
|
|
451
|
+
# Submit the latest successful build for a profile
|
|
452
|
+
mach submit --latest --platform android --profile production
|
|
453
|
+
mach submit --latest --platform ios --profile production
|
|
454
|
+
|
|
455
|
+
# Build and submit the exact successful build in one command
|
|
456
|
+
mach build --platform ios --profile production --auto-submit
|
|
457
|
+
mach build --platform android --profile production --auto-submit --submit-track internal
|
|
458
|
+
|
|
459
|
+
# Android track and release status can be supplied on the command or in mach.config.json
|
|
460
|
+
mach submit --latest --platform android --profile production --track internal --release-status completed
|
|
461
|
+
|
|
462
|
+
# Staged rollout example
|
|
463
|
+
mach submit --latest --platform android --profile production --track production --release-status inProgress --rollout 0.1
|
|
464
|
+
|
|
465
|
+
# If Play asks for explicit review handling
|
|
466
|
+
mach submit --latest --platform android --profile production --track internal --changes-not-sent-for-review
|
|
281
467
|
|
|
282
|
-
#
|
|
283
|
-
mach
|
|
468
|
+
# Submit a specific build id
|
|
469
|
+
mach submit --platform android --build-id <build-id>
|
|
284
470
|
```
|
|
285
471
|
|
|
286
|
-
|
|
472
|
+
For Android, Mach resolves the package from `mach.config.json` / Expo config and looks for a matching `google_service_account` credential. If none exists, upload it with:
|
|
287
473
|
|
|
474
|
+
```bash
|
|
475
|
+
mach credentials:service --platform android --package-name com.example.app --file ~/Downloads/play-service-account.json
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
Android submit reads `submit.<profile>.android.track`, `submit.<profile>.android.releaseStatus`, and optional `submit.<profile>.android.rollout`, falling back to `submit.production.android.*`, then `internal` / `completed`. Valid Google Play release status values are `completed`, `draft`, `halted`, and `inProgress`. Use `rollout` only with `inProgress` or `halted`; it must be greater than `0` and less than `1`.
|
|
479
|
+
|
|
480
|
+
Android submit does not require Fastlane. Mach uploads the `.aab`, assigns the returned `versionCode` to the requested track, commits the edit through the Google Play Developer API, and records the submission in the Dashboard. Transient Google Play failures are retried up to four times before the command fails.
|
|
481
|
+
|
|
482
|
+
iOS submit queues a managed submission through Mach. Users and CI call the normal `mach submit --platform ios ...` command, and Mach handles the required macOS upload environment. The Dashboard submission row is connected to the build, records who created it, and is marked failed if the upload fails.
|
|
483
|
+
|
|
484
|
+
If `--profile` is provided with `--latest`, Mach chooses the latest successful build from that profile only. This avoids accidentally submitting a staging or preprod build to production.
|
|
288
485
|
|
|
289
486
|
## Development Server
|
|
290
487
|
|
|
291
488
|
```bash
|
|
292
|
-
# Start Metro with profile
|
|
293
489
|
mach start --build-profile development
|
|
294
|
-
|
|
295
|
-
|
|
490
|
+
mach start --ios
|
|
491
|
+
mach start --android
|
|
492
|
+
mach start --web
|
|
296
493
|
mach start --tunnel
|
|
297
|
-
|
|
298
|
-
# Clear cache
|
|
299
494
|
mach start --clear
|
|
300
495
|
```
|
|
301
496
|
|
|
302
|
-
|
|
497
|
+
When running an Expo app, `mach start` keeps the normal Expo keyboard flow available:
|
|
498
|
+
press `i` for iOS, `a` for Android, `w` for web, and `r` to reload. Use the flags above when CI/scripts or muscle memory should launch a target immediately.
|
|
303
499
|
|
|
304
|
-
|
|
500
|
+
## Security Audit
|
|
305
501
|
|
|
306
502
|
```bash
|
|
307
503
|
mach audit
|
|
308
504
|
mach audit --severity high
|
|
309
505
|
mach audit --fix
|
|
310
|
-
mach audit --json
|
|
506
|
+
mach audit --json # CI/CD-friendly JSON output
|
|
311
507
|
```
|
|
312
508
|
|
|
313
509
|
## Authentication
|
|
314
510
|
|
|
315
|
-
### Interactive
|
|
511
|
+
### Interactive
|
|
316
512
|
|
|
317
513
|
```bash
|
|
318
514
|
mach login
|
|
515
|
+
mach logout
|
|
319
516
|
```
|
|
320
517
|
|
|
321
518
|
Credentials are stored in `~/.mach/credentials.json`.
|
|
519
|
+
`mach logout` removes this local credentials file. If `MACH_TOKEN` is set in the shell, unset it separately.
|
|
322
520
|
|
|
323
521
|
### CI/CD (Token-based)
|
|
324
522
|
|
|
325
|
-
Set the `MACH_TOKEN` environment variable for non-interactive authentication:
|
|
326
|
-
|
|
327
523
|
```bash
|
|
328
524
|
export MACH_TOKEN=your-personal-access-token
|
|
329
525
|
mach build --platform ios --profile production
|
|
330
526
|
```
|
|
331
527
|
|
|
332
|
-
Generate tokens from the Mach Dashboard under Settings
|
|
528
|
+
Generate tokens from the Mach Dashboard under **Settings → Access Tokens**.
|
|
529
|
+
Access tokens can be named like CI robots (for example `BitbucketPipeline`). Builds and submissions store the token identity so the Dashboard can show who or what created the run without storing a user's email/name on the build row.
|
|
530
|
+
|
|
531
|
+
Example CI flow:
|
|
532
|
+
|
|
533
|
+
```bash
|
|
534
|
+
export MACH_TOKEN=your-personal-access-token
|
|
535
|
+
mach build --platform android --profile production
|
|
536
|
+
mach submit --latest --platform android --profile production
|
|
537
|
+
```
|
|
333
538
|
|
|
334
539
|
## Environment Variables Reference
|
|
335
540
|
|
|
336
|
-
| Variable
|
|
337
|
-
|
|
338
|
-
| `MACH_TOKEN`
|
|
339
|
-
| `
|
|
340
|
-
| `
|
|
341
|
-
| `MACH_P12_PASSWORD` | iOS certificate password (avoids interactive prompt) |
|
|
541
|
+
| Variable | Description |
|
|
542
|
+
| ------------------- | --------------------------------------------------------------- |
|
|
543
|
+
| `MACH_TOKEN` | Personal access token for CI/CD (skips login) |
|
|
544
|
+
| `MACH_DEBUG` | Set to `1` to print resolved build profile and platform details |
|
|
545
|
+
| `MACH_P12_PASSWORD` | iOS certificate password (avoids interactive prompt) |
|
|
342
546
|
|
|
343
547
|
## Requirements
|
|
344
548
|
|
|
345
549
|
- Node.js 18+
|
|
346
|
-
-
|
|
347
|
-
- For
|
|
348
|
-
- For Android builds: Android SDK (local) or cloud instance
|
|
550
|
+
- For iOS builds: Xcode (local builds) or cloud build subscription
|
|
551
|
+
- For Android builds: Android SDK (local builds) or cloud build subscription
|
|
349
552
|
|
|
350
553
|
## License
|
|
351
554
|
|