@upfluxhq/cli 1.1.0 → 1.3.0

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,35 +10,61 @@ npm install -g @upfluxhq/cli
10
10
  pnpm add -g @upfluxhq/cli
11
11
  ```
12
12
 
13
+ ---
14
+
13
15
  ## Quick Start
14
16
 
15
17
  ```bash
16
- # 1. Authenticate with your publish keys (supports multiple!)
17
- upflux login --key <staging-publish-key>
18
- upflux login --key <production-publish-key>
18
+ # 1. Initialize project config (creates upflux.json)
19
+ upflux init
20
+
21
+ # 2. Authenticate with your publish key (from Upflux dashboard)
22
+ upflux login --key <publish-key>
19
23
 
20
- # 2. Upload a release (will prompt if multiple deployments)
21
- upflux release --label v1.0.0 --mandatory
24
+ # 3. Publish a release (auto-increments buildNumber)
25
+ upflux release
22
26
 
23
- # 3. Check release status
27
+ # 4. Check deployment status
24
28
  upflux status
25
29
  ```
26
30
 
31
+ ---
32
+
27
33
  ## Commands
28
34
 
29
- ### `login`
35
+ ### `init`
30
36
 
31
- Authenticate with the Upflux server. **Supports multiple credentials** - each login adds a new deployment.
37
+ Interactive wizard to configure `upflux.json` in your project root. Replaces manual configuration in your app code with declarative deployment profiles.
32
38
 
33
39
  ```bash
34
- upflux login --key <publishKey>
40
+ upflux init [options]
35
41
  ```
36
42
 
37
43
  | Option | Description |
38
44
  |--------|-------------|
39
- | `-k, --key <publishKey>` | Publish key for authentication |
45
+ | `--force` | Overwrite existing `upflux.json` without prompting |
46
+
47
+ **What it sets up:**
48
+ - App ID & server URL
49
+ - Initial `buildNumber` (`0`) and `runtimeVersion` (e.g. `1.0.0`)
50
+ - Deployment profiles (`development`, `production`, etc.) with client keys (`upx_cli_*`)
51
+ - Safe to commit to git — client keys are read-only
52
+
53
+ ---
54
+
55
+ ### `login`
56
+
57
+ Authenticate with the Upflux server using your publish key. **Supports multiple credentials** — each login registers another deployment target.
40
58
 
41
- **Example output with multiple logins:**
59
+ ```bash
60
+ upflux login --key <publishKey>
61
+ ```
62
+
63
+ | Option | Required | Description |
64
+ |--------|----------|-------------|
65
+ | `-k, --key <publishKey>` | ✓ | Publish key (`upx_pub_*`) from the dashboard |
66
+
67
+ **Example output with multiple credentials:**
42
68
  ```
43
69
  🔐 Verifying with server...
44
70
  ✓ Login successful!
@@ -54,7 +80,7 @@ upflux login --key <publishKey>
54
80
 
55
81
  ### `logout`
56
82
 
57
- Remove stored credentials.
83
+ Remove stored publish credentials.
58
84
 
59
85
  ```bash
60
86
  upflux logout [options]
@@ -62,14 +88,14 @@ upflux logout [options]
62
88
 
63
89
  | Option | Description |
64
90
  |--------|-------------|
65
- | `--all` | Remove all credentials |
91
+ | `--all` | Remove all stored credentials |
66
92
 
67
93
  **Example:**
68
94
  ```bash
69
- # Interactive selection to remove one
95
+ # Interactive selection:
70
96
  upflux logout
71
97
 
72
- # Remove all credentials
98
+ # Clear all credentials:
73
99
  upflux logout --all
74
100
  ```
75
101
 
@@ -77,159 +103,105 @@ upflux logout --all
77
103
 
78
104
  ### `release`
79
105
 
80
- Bundle and upload a new release. **Prompts to select deployment** if multiple are stored.
106
+ Bundle and upload a new OTA release. Prompts for target deployment if multiple are configured.
81
107
 
82
108
  Automatically detects project type:
83
- - **React Native**: Uses `react-native bundle` command
84
- - **Capacitor**: Zips the web directory (detected via `capacitor.config.*`)
109
+ - **React Native**: Runs `react-native bundle` for iOS and Android
110
+ - **Capacitor**: Archives the web assets folder (detected from `capacitor.config.*`)
85
111
 
86
112
  ```bash
87
113
  upflux release [options]
88
114
  ```
89
115
 
90
- | Option | Required | Description |
91
- |--------|----------|-------------|
92
- | `-l, --label <label>` | ✓ | Release label (e.g., v1.0.0) |
93
- | `--platform <platform>` | | Target platform: `all`, `ios`, or `android` (defaults to `all`) |
94
- | `-a, --app <appId>` | | Application ID (auto-detected from credentials) |
95
- | `-d, --deployment <deploymentId>` | | Deployment ID (prompts if multiple) |
96
- | `-e, --entry-file <path>` | | Entry file for bundler (default: `index.js`) |
97
- | `-o, --output-dir <path>` | | Output directory for bundle (default: `./dist`) |
98
- | `-b, --bundle <path>` | | Skip bundling, use existing bundle file |
99
- | `--assets <paths...>` | | Skip auto-detection, use these asset files |
100
- | `-m, --mandatory` | | Mark update as mandatory |
101
- | `-r, --rollout <number>` | | Rollout percentage (0-100, default: 100) |
102
- | `-v, --version-range <range>` | | Binary version range (e.g., >=1.0.0) |
103
- | `--min-binary-version <version>` | | Minimum native binary version required. Devices below this are directed to update from the App Store / Play Store before receiving this OTA. |
104
- | `--skip-native-check` | | Skip automatic native change detection (useful in CI/CD) |
105
- | `--dev` | | Create development bundle |
106
- | `--skip-bundle` | | Skip bundling, use existing files in output dir |
107
- | `--schedule` | | Create as draft, set schedule time in dashboard |
108
- | `--capacitor` | | Force Capacitor mode (auto-detected if capacitor.config exists) |
109
- | `--web-dir <path>` | | Web directory to bundle for Capacitor (default: from config or www) |
110
-
111
- **Examples:**
116
+ | Option | Description |
117
+ |--------|-------------|
118
+ | `-l, --label <label>` | Release label (e.g. `v1.0.0`). **Optional** — if omitted, auto-increments `buildNumber` in `upflux.json` (e.g. `1.0.0+1`) |
119
+ | `--platform <platform>` | Target platform: `all`, `ios`, or `android` (default: `all`) |
120
+ | `-a, --app <appId>` | Application ID (auto-detected from stored credentials) |
121
+ | `-d, --deployment <deploymentId>` | Target deployment ID (prompts if multiple exist) |
122
+ | `-m, --mandatory` | Mark update as mandatory |
123
+ | `-r, --rollout <number>` | Rollout percentage: `0`–`100` (default: `100`) |
124
+ | `-v, --version-range <range>` | Target binary version semver range (e.g. `>=1.0.0`) |
125
+ | `--min-binary-version <version>` | Minimum native binary version required. Devices below this receive store update prompts. |
126
+ | `--skip-native-check` | Skip automatic git native change inspection |
127
+ | `--schedule` | Create release as a draft to schedule release time via dashboard |
128
+ | `-e, --entry-file <path>` | React Native entry file (default: `index.js`) |
129
+ | `-o, --output-dir <path>` | Output directory for bundles (default: `./dist`) |
130
+ | `-b, --bundle <path>` | Skip bundler, upload prebuilt bundle file |
131
+ | `--assets <paths...>` | Explicit asset files or directories to include |
132
+ | `--dev` | Create development bundle |
133
+ | `--skip-bundle` | Skip bundling, reuse existing files in output directory |
134
+ | `--capacitor` | Force Capacitor bundling mode |
135
+ | `--web-dir <path>` | Custom web asset directory for Capacitor (default: from config or `www`) |
136
+
137
+ #### Examples
112
138
 
113
139
  ```bash
114
- # Release to selected deployment (prompts if multiple)
115
- upflux release --label v1.2.0
140
+ # Recommended: Auto-increment buildNumber from upflux.json (e.g. 1.0.0+1 -> 1.0.0+2)
141
+ upflux release
116
142
 
117
- # iOS-only hotfix
118
- upflux release --label v1.2.1-ios-fix --platform ios
119
-
120
- # Android-only release with 50% rollout
121
- upflux release --label v1.2.2 --platform android --rollout 50
143
+ # Release with explicit custom label
144
+ upflux release --label v1.2.0
122
145
 
123
- # Mandatory update for all platforms
124
- upflux release --label v1.3.0 --mandatory
146
+ # Mandatory release with gradual 25% rollout
147
+ upflux release --mandatory --rollout 25
125
148
 
126
- # Schedule release (draft mode)
127
- upflux release --label v1.4.0 --schedule
149
+ # iOS-only emergency hotfix
150
+ upflux release --platform ios
128
151
 
129
- # Capacitor project with custom web directory
130
- upflux release --label v1.0.0 --capacitor --web-dir dist
131
- ```
132
-
133
- **Example output:**
134
- ```
135
- 📦 Bundling for iOS...
136
- ✓ iOS bundle created
137
- 📦 Bundling for Android...
138
- ✓ Android bundle created
139
-
140
- ⬆ Uploading release...
141
- App: app-970384d3
142
- Deployment: 6933e683aa32fe18a1311bbb
143
- Deployment Name: production
144
- Label: v1.2.0
145
- Platform: all
146
- Bundle: ./dist/ios/index.ios.bundle
147
- Assets: 24 file(s)
148
- Rollout: 100%
149
- Mandatory: No
150
-
151
- 📋 Step 1/3: Requesting upload URLs...
152
- ✓ Got 26 presigned URLs
153
- 📤 Step 2/3: Uploading files directly to storage...
154
- [1/26] index.ios.bundle
155
- ...
156
- ✓ Uploaded 26 files
157
- ✅ Step 3/3: Confirming release...
158
-
159
- ✓ Release uploaded successfully!
160
- ──────────────────────────────────
161
- ID: 693507a19f6d6a79267633e8
162
- Label: v1.2.0
163
- iOS Bundle: uploaded
164
- Android Bundle: uploaded
165
- Rollout: 100%
166
- Mandatory: No
167
- Status: active
168
- ──────────────────────────────────
152
+ # Capacitor release with custom build folder
153
+ upflux release --capacitor --web-dir build
169
154
  ```
170
155
 
171
156
  ---
172
157
 
173
- ## Native Change Detection
174
-
175
- The CLI automatically detects native code changes since your last release and warns you when a store update may be required. This helps prevent devices from receiving OTA updates that depend on native features not yet available in their installed binary.
176
-
177
- ### How It Works
158
+ ## Auto-incrementing Build Numbers
178
159
 
179
- 1. After each successful release, the CLI saves the current **git commit hash** to `.upflux`.
180
- 2. On the next release, it runs `git diff <lastHash>..HEAD` against native files.
181
- 3. If changes are found, it warns you and offers to auto-set `--min-binary-version`.
160
+ When releasing without `--label`, the CLI reads `upflux.json` at your project root:
182
161
 
183
- > **Note:** Detection requires git. If the project is not a git repo, detection is silently skipped.
184
-
185
- ### Files Watched
186
-
187
- | File | What is detected |
188
- |---|---|
189
- | `android/app/build.gradle` | `versionName` bump, `minSdkVersion` change |
190
- | `android/app/src/main/AndroidManifest.xml` | New permissions |
191
- | `ios/Podfile` | New native pods |
192
- | `ios/Podfile.lock` | Any pod version change |
193
- | `package.json` | New native dependency packages |
194
-
195
- ### Example Interaction
162
+ 1. Reads `runtimeVersion` (e.g., `"1.0.0"`) and current `buildNumber` (e.g., `0`)
163
+ 2. Increments `buildNumber` by 1 (`1`)
164
+ 3. Generates release label: `1.0.0+1`
165
+ 4. Writes updated `buildNumber` back to `upflux.json`
166
+ 5. Embeds updated `upflux.json` into the native assets directory
196
167
 
168
+ ```json
169
+ {
170
+ "version": 1,
171
+ "app": {
172
+ "id": "app-970384d3",
173
+ "buildNumber": 1,
174
+ "runtimeVersion": "1.0.0"
175
+ },
176
+ "deployments": { ... }
177
+ }
197
178
  ```
198
- ⚠️ Native changes detected since last release:
199
179
 
200
- Android:
201
- • build.gradle: versionName → 2.0.0
202
- • AndroidManifest.xml: New permission: android.permission.CAMERA
180
+ > 💡 **Best Practice:** Commit `upflux.json` to git after running `upflux release` so the build counter is synchronized across your team and CI/CD pipelines.
203
181
 
204
- 💡 Suggested flags:
205
- --min-binary-version 2.0.0
206
- --version-range ">=2.0.0"
182
+ ---
207
183
 
208
- ? How would you like to handle these native changes?
209
- ❯ Auto-apply suggested flags (--min-binary-version 2.0.0 --version-range ">=2.0.0")
210
- Enter min-binary-version manually
211
- Continue without (not recommended for native changes)
212
- ```
184
+ ## Native Change Detection
213
185
 
214
- ### Setting `--min-binary-version` Manually
186
+ The CLI automatically inspects native changes since your last release to prevent pushing OTA updates that depend on unreleased native code.
215
187
 
216
- ```bash
217
- # Require devices to have at least v2.0.0 binary before receiving this OTA
218
- upflux release --label v2.0.0-ota --min-binary-version 2.0.0 --version-range ">=2.0.0"
219
- ```
188
+ ### Files Monitored
220
189
 
221
- ### Skipping in CI/CD
190
+ | File | Monitored Changes |
191
+ |---|---|
192
+ | `android/app/build.gradle` | `versionName` changes, `minSdkVersion` bumps |
193
+ | `android/app/src/main/AndroidManifest.xml` | Added/removed Android permissions |
194
+ | `ios/Podfile` | Added native CocoaPods |
195
+ | `ios/Podfile.lock` | Updated CocoaPod versions |
196
+ | `package.json` | Newly added native dependencies |
222
197
 
223
- ```bash
224
- # In CI, skip interactive detection and pass flag explicitly
225
- upflux release --label v2.0.0 --min-binary-version 2.0.0 --skip-native-check
226
- ```
198
+ If changes are detected, the CLI prompts you to automatically set `--min-binary-version`. In CI/CD environments, use `--skip-native-check` or specify `--min-binary-version` directly.
227
199
 
228
200
  ---
229
201
 
230
202
  ### `status`
231
203
 
232
- Check deployment status and recent releases.
204
+ View deployment status, active release, rollout percentage, and recent releases.
233
205
 
234
206
  ```bash
235
207
  upflux status [options]
@@ -237,34 +209,26 @@ upflux status [options]
237
209
 
238
210
  | Option | Description |
239
211
  |--------|-------------|
240
- | `-n, --limit <number>` | Number of releases to show (default: 5) |
212
+ | `-n, --limit <number>` | Number of recent releases to display (default: 5) |
241
213
 
242
- **Example output:**
214
+ **Example:**
243
215
  ```
244
216
  ⚡ Fetching deployment status...
245
217
 
246
218
  ╭────────────────────────────────────────────────╮
247
219
  │ 📱 app-970384d3 → production │
248
220
  │ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
249
- │ Current Release: v1.2.0 (iOS & Android) │
221
+ │ Current Release: 1.0.0+4 (iOS & Android) │
250
222
  │ Status: ● active • 100% rollout │
251
- │ Published: 2h ago │
223
+ │ Published: 15m ago │
252
224
  ╰────────────────────────────────────────────────╯
253
-
254
- 📦 Recent Releases (15 total)
255
-
256
- Label Platform Status Rollout Published
257
- ────────────────────────────────────────────────────────
258
- v1.2.0 all ● active 100% 2h ago
259
- v1.1.0 all ○ expired 100% 3d ago
260
- v1.0.0 ios ○ expired 100% 1w ago
261
225
  ```
262
226
 
263
227
  ---
264
228
 
265
229
  ### `generate-manifest`
266
230
 
267
- Fetch and display the manifest for a specific release.
231
+ Fetch and display or export the update manifest for a specific release ID.
268
232
 
269
233
  ```bash
270
234
  upflux generate-manifest [options]
@@ -273,51 +237,7 @@ upflux generate-manifest [options]
273
237
  | Option | Required | Description |
274
238
  |--------|----------|-------------|
275
239
  | `-r, --release-id <id>` | ✓ | Release ID (MongoDB ObjectId) |
276
- | `-o, --output <path>` | | Save manifest to file |
277
-
278
- **Example:**
279
- ```bash
280
- # Print manifest to stdout
281
- upflux generate-manifest --release-id 693507a19f6d6a79267633e8
282
-
283
- # Save to file
284
- upflux generate-manifest --release-id 693507a19f6d6a79267633e8 --output manifest.json
285
- ```
286
-
287
- ---
288
-
289
- ## Scheduled Releases
290
-
291
- Schedule releases to go live at a specific date and time. Perfect for coordinating updates with marketing campaigns, feature launches, or maintenance windows.
292
-
293
- ### How It Works
294
-
295
- 1. Create a release with the `--schedule` flag
296
- 2. The release is uploaded as a **Draft**
297
- 3. Set the scheduled time in the Upflux Dashboard
298
- 4. The release automatically goes live at the scheduled time
299
-
300
- ### Creating a Scheduled Release
301
-
302
- ```bash
303
- # Create a draft release for scheduling
304
- upflux release --label v2.0.0 --schedule
305
-
306
- # Output:
307
- # ✓ Release uploaded successfully!
308
- # ...
309
- # Status: DRAFT
310
- # 📅 Set schedule time in dashboard
311
- ```
312
-
313
- ### Use Cases
314
-
315
- - **Feature launches** - Coordinate with marketing announcements
316
- - **Global releases** - Release at optimal times for different regions
317
- - **Maintenance windows** - Deploy during low-traffic periods
318
- - **Campaign coordination** - Sync with promotional events
319
-
320
- > 💡 **Tip:** Combine scheduled releases with rollout percentages for safer deployments. For example, schedule a 10% rollout first, then manually increase after monitoring.
240
+ | `-o, --output <path>` | | Save manifest JSON to file |
321
241
 
322
242
  ---
323
243
 
@@ -327,67 +247,16 @@ upflux release --label v2.0.0 --schedule
327
247
 
328
248
  | Variable | Description | Default |
329
249
  |----------|-------------|---------|
330
- | `UPFLUX_API_URL` | API server URL | `https://api.upflux.in/api` |
331
-
332
- ### Auth File
333
-
334
- The CLI stores credentials in `.upflux` in the current directory:
335
-
336
- ```json
337
- {
338
- "credentials": [
339
- {
340
- "key": "upflux_pub_xxx...",
341
- "appId": "app-970384d3",
342
- "deploymentId": "6933e683aa32fe18a1311bbb",
343
- "deploymentName": "production"
344
- },
345
- {
346
- "key": "upflux_pub_yyy...",
347
- "appId": "app-970384d3",
348
- "deploymentId": "6933e683aa32fe18a1311ccc",
349
- "deploymentName": "staging"
350
- }
351
- ],
352
- "activeIndex": 0,
353
- "lastReleaseHash": "a3f8c12d...",
354
- "lastReleaseLabel": "v1.5.0"
355
- }
356
- ```
357
-
358
- > ⚠️ **Important:** Never commit the `.upflux` file to version control. It contains your publish keys. Add it to your `.gitignore`.
250
+ | `UPFLUX_API_URL` | Upflux API server URL | `https://api.upflux.in/api` |
359
251
 
360
- ---
361
-
362
- ## Publish Keys
363
-
364
- Publish keys are used to authenticate the CLI and can be found in the Upflux dashboard under **Settings → Keys**.
252
+ ### Auth Storage (`.upflux`)
365
253
 
366
- Key format: `upflux_pub_<prefix>_<secret>`
254
+ Stored credentials and release metadata are saved in `.upflux` in the current project directory.
367
255
 
368
- Example: `upflux_pub_prod_a1b2c3d4e5f6...`
256
+ > ⚠️ **Important:** Add `.upflux` to your `.gitignore`. It contains your publish keys (`upx_pub_*`).
369
257
 
370
258
  ---
371
259
 
372
- ## Development
373
-
374
- ```bash
375
- # Install dependencies
376
- pnpm install
377
-
378
- # Build
379
- pnpm run build
380
-
381
- # Run locally
382
- node dist/index.js --help
383
- ```
384
-
385
- ## Requirements
386
-
387
- - Node.js >= 18.0.0
388
- - For React Native projects: `react-native` CLI installed
389
- - For Capacitor projects: Built web assets in configured `webDir`
390
-
391
260
  ## License
392
261
 
393
262
  MIT
@@ -11,7 +11,6 @@ const configStore_1 = require("../config/configStore");
11
11
  */
12
12
  // const DEFAULT_BASE_URL = "http://localhost:3000/api";
13
13
  const DEFAULT_BASE_URL = "https://api.upflux.in/api";
14
- console.log(process.env.UPFLUX_API_URL || DEFAULT_BASE_URL);
15
14
  exports.apiClient = axios_1.default.create({
16
15
  baseURL: process.env.UPFLUX_API_URL || DEFAULT_BASE_URL,
17
16
  });
@@ -96,6 +96,16 @@ async function uploadFilesWithPresignedUrls(files, urls, options) {
96
96
  }
97
97
  }
98
98
  urlMap.set(relativePath, url);
99
+ // Also index by key after release path (releases/{appId}/{deploymentName}/{releaseLabel}/...)
100
+ const afterRelease = parts.slice(4).join('/');
101
+ if (afterRelease) {
102
+ urlMap.set(afterRelease, url);
103
+ }
104
+ // Filename fallback
105
+ const basename = path.posix.basename(url.key);
106
+ if (!urlMap.has(basename)) {
107
+ urlMap.set(basename, url);
108
+ }
99
109
  }
100
110
  // Upload files in batches
101
111
  let completed = 0;
@@ -103,7 +113,16 @@ async function uploadFilesWithPresignedUrls(files, urls, options) {
103
113
  const uploadFile = async (file) => {
104
114
  // Use relativePath for lookup if available, fallback to filename
105
115
  const lookupKey = file.relativePath || file.filename;
106
- const urlInfo = urlMap.get(lookupKey);
116
+ let urlInfo;
117
+ if (file.platform && urlMap.has(`${file.platform}/${lookupKey}`)) {
118
+ urlInfo = urlMap.get(`${file.platform}/${lookupKey}`);
119
+ }
120
+ else if (urlMap.has(lookupKey)) {
121
+ urlInfo = urlMap.get(lookupKey);
122
+ }
123
+ else if (file.filename && urlMap.has(file.filename)) {
124
+ urlInfo = urlMap.get(file.filename);
125
+ }
107
126
  if (!urlInfo) {
108
127
  throw new Error(`No presigned URL found for file: ${lookupKey}`);
109
128
  }
@@ -126,6 +145,7 @@ function guessContentType(filename) {
126
145
  var _a;
127
146
  const ext = (_a = filename.split(".").pop()) === null || _a === void 0 ? void 0 : _a.toLowerCase();
128
147
  const contentTypes = {
148
+ hbc: "application/javascript",
129
149
  bundle: "application/javascript",
130
150
  js: "application/javascript",
131
151
  json: "application/json",
@@ -159,8 +179,9 @@ assetsBaseDir, iosAssetsBaseDir, androidAssetsBaseDir) {
159
179
  type: "bundle",
160
180
  platform: "ios",
161
181
  filename: path.basename(iosBundlePath),
182
+ relativePath: "ios/" + path.basename(iosBundlePath),
162
183
  localPath: iosBundlePath,
163
- contentType: "application/javascript",
184
+ contentType: guessContentType(iosBundlePath),
164
185
  size: stats.size,
165
186
  });
166
187
  }
@@ -170,8 +191,9 @@ assetsBaseDir, iosAssetsBaseDir, androidAssetsBaseDir) {
170
191
  type: "bundle",
171
192
  platform: "android",
172
193
  filename: path.basename(androidBundlePath),
194
+ relativePath: "android/" + path.basename(androidBundlePath),
173
195
  localPath: androidBundlePath,
174
- contentType: "application/javascript",
196
+ contentType: guessContentType(androidBundlePath),
175
197
  size: stats.size,
176
198
  });
177
199
  }
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ declare const init: Command;
3
+ export default init;