vibeview 0.19.0 → 0.20.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 +24 -0
- package/dist/{chunk-FG2J2BGS.js → chunk-MCCXPIGE.js} +20 -1
- package/dist/{chunk-T6FS4ELD.js → chunk-QZNZPKHV.js} +63 -15
- package/dist/cli.js +326 -147
- package/dist/{server-GMC6F545.js → server-UN5JRYGY.js} +3 -3
- package/dist/{upload-app-4PE5BEP7.js → upload-app-HUMMA6UY.js} +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -74,12 +74,33 @@ vibeview webhooks add --url https://ci.example.com/hooks/vibeview
|
|
|
74
74
|
Webhooks POST a signed payload to your endpoint whenever a build finishes.
|
|
75
75
|
Manage running builds with `vibeview builds list | logs | download | cancel`.
|
|
76
76
|
|
|
77
|
+
Set `"autoIncrement": true` in a platform's `build` block and every
|
|
78
|
+
production build gets a server-assigned build number, injected into the
|
|
79
|
+
build as `VIBEVIEW_BUILD_NUMBER` (inspect or seed the counters with
|
|
80
|
+
`vibeview versions list/set`).
|
|
81
|
+
|
|
77
82
|
Expo managed projects work out of the box — the default build command runs
|
|
78
83
|
`npx expo prebuild` in the cloud, so no `ios/` or `android/` directory is
|
|
79
84
|
needed.
|
|
80
85
|
|
|
86
|
+
When a production build succeeds, ship it straight to the stores — no
|
|
87
|
+
Xcode, no Play Console upload page:
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
vibeview submit --platform tvos --latest # → TestFlight
|
|
91
|
+
vibeview submit --platform android --track internal # → a Play track
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
TestFlight uploads use your stored App Store Connect key; Play uploads need
|
|
95
|
+
a Google Play service account key stored once with
|
|
96
|
+
`vibeview credentials play-key add --file service-account.json`. Choose a
|
|
97
|
+
Play track, upload as a draft, or start a staged rollout — and store your
|
|
98
|
+
defaults in `vibeview.json` so `vibeview submit` alone does the right
|
|
99
|
+
thing.
|
|
100
|
+
|
|
81
101
|
[Cloud Builds docs →](https://vibeview.io/docs/cloud-builds) ·
|
|
82
102
|
[App Signing →](https://vibeview.io/docs/app-signing) ·
|
|
103
|
+
[Store Submission →](https://vibeview.io/docs/store-submission) ·
|
|
83
104
|
[Build Webhooks →](https://vibeview.io/docs/build-webhooks)
|
|
84
105
|
|
|
85
106
|
## Let your AI agent drive the device
|
|
@@ -140,8 +161,11 @@ Output formats: `human`, `json`, and `junit` for any CI system. The exit code re
|
|
|
140
161
|
| `vibeview dev` | Live development on a cloud device |
|
|
141
162
|
| `vibeview build` | Build your app — locally, or on the build fleet with `--cloud` (add `--production` for a signed release) |
|
|
142
163
|
| `vibeview builds list/logs/download/cancel/delete-artifact` | Manage cloud builds and their artifacts |
|
|
164
|
+
| `vibeview versions list/set` | Server-side build-number counters for `autoIncrement` production builds |
|
|
143
165
|
| `vibeview credentials add/generate/list/rm/download` | App-signing credentials for production builds (`generate` covers iOS, tvOS, and Android) |
|
|
144
166
|
| `vibeview credentials asc-key add/list/rm` | Connect an Apple team via App Store Connect API key |
|
|
167
|
+
| `vibeview credentials play-key add/list/rm` | Store a Google Play service-account key for Play submissions |
|
|
168
|
+
| `vibeview submit` | Upload a production build to TestFlight or a Google Play track |
|
|
145
169
|
| `vibeview env list/set/rm/pull` | Build variables and secrets injected into cloud builds |
|
|
146
170
|
| `vibeview webhooks add/list/rm/test` | Notify your systems when builds finish (signed payloads) |
|
|
147
171
|
| `vibeview dev-status` / `dev-stop` | Check on, or stop, a backgrounded dev session |
|
|
@@ -484,7 +484,10 @@ var ApiClient = class {
|
|
|
484
484
|
platform: body.platform,
|
|
485
485
|
...body.track !== void 0 ? { track: body.track } : {},
|
|
486
486
|
...body.package !== void 0 ? { package: body.package } : {},
|
|
487
|
-
...body.ascKey !== void 0 ? { asc_key: body.ascKey } : {}
|
|
487
|
+
...body.ascKey !== void 0 ? { asc_key: body.ascKey } : {},
|
|
488
|
+
...body.releaseStatus !== void 0 ? { release_status: body.releaseStatus } : {},
|
|
489
|
+
...body.rollout !== void 0 ? { rollout: body.rollout } : {},
|
|
490
|
+
...body.changesNotSentForReview !== void 0 ? { changes_not_sent_for_review: body.changesNotSentForReview } : {}
|
|
488
491
|
});
|
|
489
492
|
}
|
|
490
493
|
/** GET /api/v1/submissions?build=<id> — that build's submissions, newest
|
|
@@ -503,6 +506,22 @@ var ApiClient = class {
|
|
|
503
506
|
const qs = offset !== void 0 ? `?offset=${offset}` : "";
|
|
504
507
|
return this.requestRaw("GET", `/submissions/${id}/log${qs}`);
|
|
505
508
|
}
|
|
509
|
+
/** GET /api/v1/apps/{appPublicId}/versions — the app's server-side
|
|
510
|
+
* build-number counters, one per platform that has ever allocated (or
|
|
511
|
+
* been `versions set`) a number. */
|
|
512
|
+
async listAppVersions(appId) {
|
|
513
|
+
return this.requestRaw("GET", `/apps/${encodeURIComponent(appId)}/versions`);
|
|
514
|
+
}
|
|
515
|
+
/** PUT /api/v1/apps/{appPublicId}/versions/{platform} {build_number} —
|
|
516
|
+
* seed or overwrite one counter (org-scoped, DEVELOPER role, audited
|
|
517
|
+
* server-side). Returns the updated counter. */
|
|
518
|
+
async setAppVersion(appId, platform, buildNumber) {
|
|
519
|
+
return this.requestRaw(
|
|
520
|
+
"PUT",
|
|
521
|
+
`/apps/${encodeURIComponent(appId)}/versions/${encodeURIComponent(platform)}`,
|
|
522
|
+
{ build_number: buildNumber }
|
|
523
|
+
);
|
|
524
|
+
}
|
|
506
525
|
/** GET /api/v1/signing-credentials/{id}/material. Requires ADMIN role;
|
|
507
526
|
* audited server-side. 404s (NotFoundError) for non-generated rows —
|
|
508
527
|
* uploaded material is never readable back. */
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
ApiClient,
|
|
4
|
+
UsageError,
|
|
4
5
|
createSpinner,
|
|
5
6
|
loadConfig,
|
|
6
7
|
resolveAuth,
|
|
7
8
|
uploadAppBuild
|
|
8
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-MCCXPIGE.js";
|
|
9
10
|
|
|
10
11
|
// src/lib/dev-state.ts
|
|
11
12
|
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
@@ -332,6 +333,7 @@ import { input, select } from "@inquirer/prompts";
|
|
|
332
333
|
// src/lib/project-detect.ts
|
|
333
334
|
import { existsSync as existsSync3, readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
|
|
334
335
|
import { join as join4 } from "path";
|
|
336
|
+
import ignore from "ignore";
|
|
335
337
|
function readJson(cwd, name) {
|
|
336
338
|
try {
|
|
337
339
|
return JSON.parse(readFileSync3(join4(cwd, name), "utf-8"));
|
|
@@ -358,8 +360,22 @@ function detectFlavor(cwd) {
|
|
|
358
360
|
if (isFile(join4(cwd, "manifest"))) return "roku";
|
|
359
361
|
return "unknown";
|
|
360
362
|
}
|
|
361
|
-
function
|
|
362
|
-
|
|
363
|
+
function nativeDirShipped(cwd, dir) {
|
|
364
|
+
if (!existsSync3(join4(cwd, dir))) return false;
|
|
365
|
+
try {
|
|
366
|
+
const ig = ignore().add(readFileSync3(join4(cwd, ".gitignore"), "utf-8"));
|
|
367
|
+
return !ig.ignores(`${dir}/`);
|
|
368
|
+
} catch {
|
|
369
|
+
return true;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
function isManagedExpo(cwd, platform) {
|
|
373
|
+
if (detectFlavor(cwd) !== "expo") return false;
|
|
374
|
+
const android = !nativeDirShipped(cwd, "android");
|
|
375
|
+
const ios = !nativeDirShipped(cwd, "ios");
|
|
376
|
+
if (platform === "android" || platform === "androidtv") return android;
|
|
377
|
+
if (platform === "ios" || platform === "tvos") return ios;
|
|
378
|
+
return android && ios;
|
|
363
379
|
}
|
|
364
380
|
function iosWorkspaceName(cwd) {
|
|
365
381
|
try {
|
|
@@ -371,6 +387,18 @@ function iosWorkspaceName(cwd) {
|
|
|
371
387
|
}
|
|
372
388
|
var IOS_ARTIFACT = "ios/build/Build/Products/Debug-iphonesimulator/*.app";
|
|
373
389
|
var ANDROID_ARTIFACT = "android/app/build/outputs/apk/debug/*.apk";
|
|
390
|
+
function installCommand(cwd) {
|
|
391
|
+
if (existsSync3(join4(cwd, "yarn.lock"))) return "yarn install --immutable";
|
|
392
|
+
if (existsSync3(join4(cwd, "pnpm-lock.yaml"))) return "pnpm install --frozen-lockfile";
|
|
393
|
+
if (existsSync3(join4(cwd, "package-lock.json"))) return "npm ci";
|
|
394
|
+
return "yarn install --immutable";
|
|
395
|
+
}
|
|
396
|
+
function managedExpoAndroidCommand(cwd) {
|
|
397
|
+
return `${installCommand(cwd)} && npx expo prebuild --platform android --no-install && cd android && ./gradlew assembleDebug`;
|
|
398
|
+
}
|
|
399
|
+
function managedExpoIosCommand(cwd, name) {
|
|
400
|
+
return `${installCommand(cwd)} && npx expo prebuild --platform ios && xcodebuild -workspace ios/${name}.xcworkspace -scheme ${name} -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build CODE_SIGNING_ALLOWED=NO`;
|
|
401
|
+
}
|
|
374
402
|
var ROKU_DEFAULT_ARTIFACT = "out/channel.zip";
|
|
375
403
|
var ROKU_ZIP_COMMAND = "zip -r out/channel.zip manifest source components images -x '*.DS_Store'";
|
|
376
404
|
function rokuDefaults(cwd) {
|
|
@@ -400,15 +428,17 @@ function defaultsFor(cwd, platform) {
|
|
|
400
428
|
const metroCommand = flavor === "expo" ? "npx expo start" : "yarn start";
|
|
401
429
|
if (platform === "tvos" || platform === "androidtv") return { metroCommand };
|
|
402
430
|
if (platform === "android") {
|
|
431
|
+
const managed = isManagedExpo(cwd, "android");
|
|
403
432
|
return {
|
|
404
|
-
buildCommand:
|
|
433
|
+
buildCommand: managed ? managedExpoAndroidCommand(cwd) : "cd android && ./gradlew assembleDebug",
|
|
405
434
|
artifact: ANDROID_ARTIFACT,
|
|
406
435
|
metroCommand
|
|
407
436
|
};
|
|
408
437
|
}
|
|
409
|
-
if (
|
|
438
|
+
if (isManagedExpo(cwd, "ios")) {
|
|
439
|
+
const name = managedExpoIosName(cwd);
|
|
410
440
|
return {
|
|
411
|
-
buildCommand:
|
|
441
|
+
buildCommand: name ? managedExpoIosCommand(cwd, name) : void 0,
|
|
412
442
|
artifact: IOS_ARTIFACT,
|
|
413
443
|
metroCommand
|
|
414
444
|
};
|
|
@@ -421,9 +451,13 @@ function defaultsFor(cwd, platform) {
|
|
|
421
451
|
};
|
|
422
452
|
}
|
|
423
453
|
var ANDROID_PRODUCTION_ARTIFACT = "android/app/build/outputs/bundle/release/*.aab";
|
|
424
|
-
|
|
454
|
+
function androidProductionCommand(cwd) {
|
|
455
|
+
return `${installCommand(cwd)} && cd android && ./gradlew bundleRelease`;
|
|
456
|
+
}
|
|
425
457
|
var IOS_PRODUCTION_ARTIFACT = "ios/build/*.ipa";
|
|
426
|
-
|
|
458
|
+
function managedExpoAndroidProductionCommand(cwd) {
|
|
459
|
+
return `${installCommand(cwd)} && npx expo prebuild --platform android --no-install && cd android && ./gradlew bundleRelease`;
|
|
460
|
+
}
|
|
427
461
|
function expoSanitizedName(name) {
|
|
428
462
|
return name.replace(/[\W_]+/g, "").normalize("NFD").replace(/[\u0300-\u036f]/g, "");
|
|
429
463
|
}
|
|
@@ -448,7 +482,7 @@ function iosArchiveExportCommand(ws) {
|
|
|
448
482
|
function productionDefaultsFor(cwd, platform) {
|
|
449
483
|
if (platform === "android" || platform === "androidtv") {
|
|
450
484
|
return {
|
|
451
|
-
buildCommand: isManagedExpo(cwd) ?
|
|
485
|
+
buildCommand: isManagedExpo(cwd, "android") ? managedExpoAndroidProductionCommand(cwd) : androidProductionCommand(cwd),
|
|
452
486
|
artifact: ANDROID_PRODUCTION_ARTIFACT
|
|
453
487
|
};
|
|
454
488
|
}
|
|
@@ -456,18 +490,18 @@ function productionDefaultsFor(cwd, platform) {
|
|
|
456
490
|
return { artifact: IOS_PRODUCTION_ARTIFACT };
|
|
457
491
|
}
|
|
458
492
|
if (platform === "ios") {
|
|
459
|
-
if (isManagedExpo(cwd)) {
|
|
493
|
+
if (isManagedExpo(cwd, "ios")) {
|
|
460
494
|
const name = managedExpoIosName(cwd);
|
|
461
495
|
if (!name) return { artifact: IOS_PRODUCTION_ARTIFACT };
|
|
462
496
|
return {
|
|
463
|
-
buildCommand:
|
|
497
|
+
buildCommand: `${installCommand(cwd)} && npx expo prebuild --platform ios && ` + iosArchiveExportCommand(name),
|
|
464
498
|
artifact: IOS_PRODUCTION_ARTIFACT
|
|
465
499
|
};
|
|
466
500
|
}
|
|
467
501
|
const ws = iosWorkspaceName(cwd);
|
|
468
502
|
if (!ws) return { artifact: IOS_PRODUCTION_ARTIFACT };
|
|
469
503
|
return {
|
|
470
|
-
buildCommand:
|
|
504
|
+
buildCommand: `${installCommand(cwd)} && ${iosArchiveExportCommand(ws)}`,
|
|
471
505
|
artifact: IOS_PRODUCTION_ARTIFACT
|
|
472
506
|
};
|
|
473
507
|
}
|
|
@@ -580,7 +614,7 @@ import { execFile, spawn } from "child_process";
|
|
|
580
614
|
import { lstatSync, mkdtempSync as mkdtempSync2, readFileSync as readFileSync4, rmSync as rmSync3, statSync as statSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
581
615
|
import { tmpdir as tmpdir2 } from "os";
|
|
582
616
|
import { join as join5 } from "path";
|
|
583
|
-
import
|
|
617
|
+
import ignore2 from "ignore";
|
|
584
618
|
var NotAGitRepoError = class extends Error {
|
|
585
619
|
constructor(message) {
|
|
586
620
|
super(message);
|
|
@@ -607,7 +641,7 @@ async function listSourceFiles(projectRoot) {
|
|
|
607
641
|
let filtered = rawPaths;
|
|
608
642
|
const vvignorePath = join5(projectRoot, ".vvignore");
|
|
609
643
|
if (statOrNull(vvignorePath)?.isFile()) {
|
|
610
|
-
const ig =
|
|
644
|
+
const ig = ignore2().add(readFileSync4(vvignorePath, "utf-8"));
|
|
611
645
|
filtered = filtered.filter((p) => !ig.ignores(p));
|
|
612
646
|
}
|
|
613
647
|
const result = [];
|
|
@@ -1188,6 +1222,11 @@ async function runCloudBuildCommand(cwd, cfgRef, opts, deps) {
|
|
|
1188
1222
|
for (const p of platforms) {
|
|
1189
1223
|
try {
|
|
1190
1224
|
validateBuildEnv(cfg?.platforms?.[p]?.build?.env, p);
|
|
1225
|
+
if (cfg?.platforms?.[p]?.build?.autoIncrement === true && opts.production && createFromArtifact.has(p)) {
|
|
1226
|
+
throw new UsageError(
|
|
1227
|
+
`platforms.${p}.build.autoIncrement needs the build linked to an app \u2014 set appId in vibeview.json (a brand-new app's first build can't auto-increment)`
|
|
1228
|
+
);
|
|
1229
|
+
}
|
|
1191
1230
|
} catch (err) {
|
|
1192
1231
|
fail(opts, err instanceof Error ? err.message : String(err));
|
|
1193
1232
|
}
|
|
@@ -1232,6 +1271,14 @@ ${offenders}`);
|
|
|
1232
1271
|
const production = productionBuilds.get(p);
|
|
1233
1272
|
const command = production ? production.command : build.command;
|
|
1234
1273
|
const artifactPath = production ? production.artifact : build.artifact;
|
|
1274
|
+
const autoIncrement = build.autoIncrement === true;
|
|
1275
|
+
if (autoIncrement && !production && !opts.json) {
|
|
1276
|
+
console.error(
|
|
1277
|
+
chalk2.yellow(
|
|
1278
|
+
`note: platforms.${p}.build.autoIncrement only applies to production builds (--production) \u2014 ignoring it for this simulator build`
|
|
1279
|
+
)
|
|
1280
|
+
);
|
|
1281
|
+
}
|
|
1235
1282
|
return {
|
|
1236
1283
|
platform: p,
|
|
1237
1284
|
// OMITTED, never invented, for a platform whose app the artifact will
|
|
@@ -1243,7 +1290,8 @@ ${offenders}`);
|
|
|
1243
1290
|
command,
|
|
1244
1291
|
artifact_path: artifactPath,
|
|
1245
1292
|
distribution: production ? "production" : "simulator",
|
|
1246
|
-
...build.env ? { env: build.env } : {}
|
|
1293
|
+
...build.env ? { env: build.env } : {},
|
|
1294
|
+
...autoIncrement && production ? { auto_increment: true } : {}
|
|
1247
1295
|
};
|
|
1248
1296
|
});
|
|
1249
1297
|
const uploadSpinner = !opts.json ? createSpinner("uploading source...") : null;
|