@better-update/cli 0.3.0 → 0.4.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/dist/index.mjs +5190 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +12 -9
- package/CHANGELOG.md +0 -58
- package/oxlint.config.ts +0 -6
- package/src/app-layer.ts +0 -29
- package/src/application/build-workflow.ts +0 -222
- package/src/application/command-exit.ts +0 -13
- package/src/application/login.ts +0 -87
- package/src/application/update-promote.ts +0 -88
- package/src/application/update-publish.ts +0 -402
- package/src/application/update-rollback.ts +0 -275
- package/src/commands/analytics/adoption.ts +0 -40
- package/src/commands/analytics/channels.ts +0 -35
- package/src/commands/analytics/helpers.ts +0 -3
- package/src/commands/analytics/index.ts +0 -13
- package/src/commands/analytics/platforms.ts +0 -39
- package/src/commands/analytics/updates.ts +0 -35
- package/src/commands/audit-logs/helpers.ts +0 -3
- package/src/commands/audit-logs/index.ts +0 -8
- package/src/commands/audit-logs/list.ts +0 -66
- package/src/commands/branches.ts +0 -70
- package/src/commands/build/android.ts +0 -129
- package/src/commands/build/index.ts +0 -63
- package/src/commands/build/ios.ts +0 -199
- package/src/commands/build/reserve-and-upload.test.ts +0 -263
- package/src/commands/build/reserve-and-upload.ts +0 -160
- package/src/commands/build/run-step.ts +0 -131
- package/src/commands/builds/compatibility-matrix.ts +0 -48
- package/src/commands/builds/delete.ts +0 -15
- package/src/commands/builds/get.ts +0 -34
- package/src/commands/builds/helpers.ts +0 -3
- package/src/commands/builds/index.ts +0 -20
- package/src/commands/builds/install-link.ts +0 -20
- package/src/commands/builds/list.ts +0 -38
- package/src/commands/channels/create.ts +0 -37
- package/src/commands/channels/delete.ts +0 -15
- package/src/commands/channels/helpers.ts +0 -18
- package/src/commands/channels/index.ts +0 -24
- package/src/commands/channels/list.ts +0 -38
- package/src/commands/channels/pause.ts +0 -15
- package/src/commands/channels/resume.ts +0 -15
- package/src/commands/channels/rollout/complete.ts +0 -17
- package/src/commands/channels/rollout/create.ts +0 -36
- package/src/commands/channels/rollout/index.ts +0 -11
- package/src/commands/channels/rollout/revert.ts +0 -17
- package/src/commands/channels/rollout/update.ts +0 -23
- package/src/commands/channels/update.ts +0 -32
- package/src/commands/credentials/delete.ts +0 -24
- package/src/commands/credentials/index.ts +0 -10
- package/src/commands/credentials/list.ts +0 -33
- package/src/commands/credentials/upload.ts +0 -91
- package/src/commands/env/delete.ts +0 -35
- package/src/commands/env/export.ts +0 -27
- package/src/commands/env/get.ts +0 -25
- package/src/commands/env/helpers.ts +0 -13
- package/src/commands/env/import.ts +0 -31
- package/src/commands/env/index.ts +0 -24
- package/src/commands/env/list.ts +0 -44
- package/src/commands/env/pull.ts +0 -27
- package/src/commands/env/set.ts +0 -42
- package/src/commands/fingerprint/compare.ts +0 -25
- package/src/commands/fingerprint/generate.ts +0 -18
- package/src/commands/fingerprint/index.ts +0 -9
- package/src/commands/init.ts +0 -35
- package/src/commands/login.ts +0 -13
- package/src/commands/logout.ts +0 -12
- package/src/commands/projects.ts +0 -84
- package/src/commands/status.ts +0 -48
- package/src/commands/update/delete.ts +0 -15
- package/src/commands/update/helpers.ts +0 -22
- package/src/commands/update/index.ts +0 -22
- package/src/commands/update/list.ts +0 -60
- package/src/commands/update/promote.ts +0 -30
- package/src/commands/update/publish.ts +0 -94
- package/src/commands/update/rollback.ts +0 -42
- package/src/commands/update/rollout/complete.ts +0 -17
- package/src/commands/update/rollout/index.ts +0 -10
- package/src/commands/update/rollout/revert.ts +0 -17
- package/src/commands/update/rollout/set.ts +0 -23
- package/src/index.ts +0 -53
- package/src/lib/android-keystore.test.ts +0 -114
- package/src/lib/android-keystore.ts +0 -76
- package/src/lib/android-signing-gradle.test.ts +0 -95
- package/src/lib/android-signing-gradle.ts +0 -52
- package/src/lib/app-json.ts +0 -81
- package/src/lib/apple-auth.test.ts +0 -402
- package/src/lib/apple-auth.ts +0 -132
- package/src/lib/artifact-finder.test.ts +0 -195
- package/src/lib/artifact-finder.ts +0 -122
- package/src/lib/browser-login.test.ts +0 -88
- package/src/lib/browser-login.ts +0 -193
- package/src/lib/build-profile.test.ts +0 -290
- package/src/lib/build-profile.ts +0 -234
- package/src/lib/cli-schemas.ts +0 -39
- package/src/lib/command-errors.ts +0 -60
- package/src/lib/credentials-downloader.ts +0 -181
- package/src/lib/credentials-manager.ts +0 -354
- package/src/lib/env-exporter.test.ts +0 -96
- package/src/lib/env-exporter.ts +0 -28
- package/src/lib/exit-codes.ts +0 -82
- package/src/lib/expo-config.ts +0 -130
- package/src/lib/expo-export.test.ts +0 -94
- package/src/lib/expo-export.ts +0 -281
- package/src/lib/fingerprint.ts +0 -67
- package/src/lib/format-error.ts +0 -22
- package/src/lib/git-context.ts +0 -56
- package/src/lib/gradle-config.ts +0 -126
- package/src/lib/ios-export-options.test.ts +0 -98
- package/src/lib/ios-export-options.ts +0 -62
- package/src/lib/ios-keychain.ts +0 -181
- package/src/lib/ios-provisioning.test.ts +0 -115
- package/src/lib/ios-provisioning.ts +0 -179
- package/src/lib/output.ts +0 -32
- package/src/lib/pkcs12.ts +0 -73
- package/src/lib/plist.ts +0 -39
- package/src/lib/post-build-validation.ts +0 -146
- package/src/lib/presigned-upload.test.ts +0 -140
- package/src/lib/presigned-upload.ts +0 -35
- package/src/lib/record.ts +0 -5
- package/src/lib/resolve-named-resource.ts +0 -24
- package/src/lib/runtime-version.test.ts +0 -119
- package/src/lib/runtime-version.ts +0 -62
- package/src/lib/sha256.test.ts +0 -108
- package/src/lib/sha256.ts +0 -80
- package/src/lib/signed-payloads.test.ts +0 -181
- package/src/lib/signed-payloads.ts +0 -164
- package/src/lib/string-utils.ts +0 -4
- package/src/lib/temp-dir.ts +0 -14
- package/src/lib/test-utils.ts +0 -13
- package/src/lib/update-platforms.test.ts +0 -45
- package/src/lib/update-platforms.ts +0 -19
- package/src/lib/xcpretty-formatter.ts +0 -21
- package/src/services/api-client.ts +0 -42
- package/src/services/apple-session-store.ts +0 -100
- package/src/services/auth-store.ts +0 -85
- package/src/services/cli-runtime.ts +0 -46
- package/src/services/config-store.ts +0 -108
- package/src/services/presigned-upload.ts +0 -84
- package/src/services/update-asset-uploader.ts +0 -72
- package/src/types/keychain.d.ts +0 -22
- package/tests/e2e/build.test.ts +0 -270
- package/tests/e2e/commands.test.ts +0 -694
- package/tests/e2e/ota-lifecycle.test.ts +0 -275
- package/tests/e2e/publish.test.ts +0 -150
- package/tests/helpers/cli-e2e.ts +0 -426
- package/tests/helpers/pty-driver.ts +0 -142
- package/tests/interactive/harness/provider-prompt.ts +0 -54
- package/tests/interactive/login.test.ts +0 -47
- package/tests/interactive/provider-select.test.ts +0 -59
- package/tsconfig.json +0 -7
- package/vitest.config.ts +0 -38
package/package.json
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-update/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"bin": {
|
|
5
|
-
"better-update": "./
|
|
5
|
+
"better-update": "./dist/index.js"
|
|
6
6
|
},
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
7
10
|
"type": "module",
|
|
11
|
+
"main": "./dist/index.js",
|
|
8
12
|
"exports": {
|
|
9
|
-
".": "./
|
|
13
|
+
".": "./dist/index.js"
|
|
10
14
|
},
|
|
11
15
|
"publishConfig": {
|
|
12
16
|
"access": "public"
|
|
13
17
|
},
|
|
14
18
|
"dependencies": {
|
|
15
|
-
"@better-update/api": "0.1.0",
|
|
16
|
-
"@better-update/encoding": "0.0.1",
|
|
17
|
-
"@better-update/expo-protocol": "0.0.1",
|
|
18
|
-
"@better-update/safe-json": "0.0.1",
|
|
19
19
|
"@effect/cli": "^0.75.1",
|
|
20
20
|
"@effect/platform": "^0.96.0",
|
|
21
|
-
"@effect/platform-
|
|
21
|
+
"@effect/platform-node": "^0.106.0",
|
|
22
22
|
"@expo/apple-utils": "^2.1.21",
|
|
23
23
|
"@expo/config": "^55.0.15",
|
|
24
24
|
"@expo/pkcs12": "^0.4.2",
|
|
@@ -32,5 +32,8 @@
|
|
|
32
32
|
"optionalDependencies": {
|
|
33
33
|
"keychain": "^1.5.0"
|
|
34
34
|
},
|
|
35
|
-
"
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=22"
|
|
37
|
+
},
|
|
38
|
+
"gitHead": "b0814ea80e9c4e24598e3e1b43b0c8775337cb64"
|
|
36
39
|
}
|
package/CHANGELOG.md
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
# Change Log
|
|
2
|
-
|
|
3
|
-
All notable changes to this project will be documented in this file.
|
|
4
|
-
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
|
-
|
|
6
|
-
## 0.3.0 (2026-04-21)
|
|
7
|
-
|
|
8
|
-
### Features
|
|
9
|
-
|
|
10
|
-
* add CLI build command for local native iOS/Android builds ([97a3e73](https://github.com/better-update/better-update/commit/97a3e73501f120118e44087f2359696c5a913c9d)) - by @
|
|
11
|
-
* add CLI package for project, credential, and env management ([07c0ff7](https://github.com/better-update/better-update/commit/07c0ff75d55f88bae94e4760625013b070896f36)) - by @
|
|
12
|
-
* add CLI update publish command ([5f4268a](https://github.com/better-update/better-update/commit/5f4268a90bacb8a231bbc98ca92bd70452bcf02c)) - by @
|
|
13
|
-
* **cli,server,dashboard:** close EAS Update feature gaps ([#4](https://github.com/better-update/better-update/issues/4), [#6](https://github.com/better-update/better-update/issues/6), [#7](https://github.com/better-update/better-update/issues/7), [#2](https://github.com/better-update/better-update/issues/2)) ([ecdc224](https://github.com/better-update/better-update/commit/ecdc22423f4117ab27ddac15e175329bf50c1031)) - by @
|
|
14
|
-
* **cli,server:** integrate EAS CLI libraries, Apple auto-provisioning, and content-type-namespaced asset hashing ([ff3d881](https://github.com/better-update/better-update/commit/ff3d8812a27387e2331cc9e01741f4c01195e31a)) - by @
|
|
15
|
-
* **cli:** add --rollout-percentage to update publish + schema-validated args ([e656e75](https://github.com/better-update/better-update/commit/e656e75685e4fe8f872d7495325f1bd8d6534cc8)) - by @
|
|
16
|
-
* **cli:** add Android keystore certificate expiry validation ([78f3e46](https://github.com/better-update/better-update/commit/78f3e460e394632a0a39a6e3b5d7779b2dcd174d)) - by @
|
|
17
|
-
* **cli:** add Apple App Store Connect provider selection ([0cd1894](https://github.com/better-update/better-update/commit/0cd189454eeed653826867411102b993d1fc1c9f)) - by @
|
|
18
|
-
* **cli:** add full CLI coverage for all API groups ([9e05b10](https://github.com/better-update/better-update/commit/9e05b10152d27c9617288a0c182678aaec9d4864)) - by @
|
|
19
|
-
* **repo:** add rollback-to-embedded flows ([f5fe1c4](https://github.com/better-update/better-update/commit/f5fe1c439ad7424dc671d84a7b81721e9b6976ad)) - by @
|
|
20
|
-
* **repo:** add update lifecycle and credential flows ([f55cf5d](https://github.com/better-update/better-update/commit/f55cf5dea7874540fef6c9620ab8ced7c78e872f)) - by @
|
|
21
|
-
* **repo:** expand build management coverage and dashboard details ([96d8d11](https://github.com/better-update/better-update/commit/96d8d11c41a171d67a3dad82d936c924a3e614c7)) - by @
|
|
22
|
-
* **repo:** switch uploads to direct r2 flow ([736e7f1](https://github.com/better-update/better-update/commit/736e7f17d887a773b35038119981b31195b4f510)) - by @
|
|
23
|
-
* **server,cli:** add build-credentials resolver with roster-hash gated profile regen ([e3a42f8](https://github.com/better-update/better-update/commit/e3a42f86eb7dfd6ec20edeafae6e1aae9c5f76ad)) - by @
|
|
24
|
-
|
|
25
|
-
### Bug Fixes
|
|
26
|
-
|
|
27
|
-
* **cli:** defer api client auth and repair e2e harness ([77de9db](https://github.com/better-update/better-update/commit/77de9db2a93facbfbdb859fe65b5307d1f56a29e)) - by @
|
|
28
|
-
* **dashboard:** remove build upload UI and restrict uploads to CLI only ([278b011](https://github.com/better-update/better-update/commit/278b0117945a6b766171df0a74d1443b0710ab10)) - by @
|
|
29
|
-
* **repo:** add knip dead code detection, fix timing-safe HMAC verification ([ef3e3b6](https://github.com/better-update/better-update/commit/ef3e3b6c6d936e85346ed2b0f53941e92cc8d226)) - by @
|
|
30
|
-
* **repo:** harden update publish and republish flows ([5b2a7b7](https://github.com/better-update/better-update/commit/5b2a7b7dc164baf29bbe54db83174f3a1f09b503)) - by @
|
|
31
|
-
* **repo:** stream update artifacts and enable signed promote flows ([4ed09f1](https://github.com/better-update/better-update/commit/4ed09f15909e44a68e597a28d00c7b0825448211)) - by @
|
|
32
|
-
|
|
33
|
-
## 0.2.0 (2026-04-21)
|
|
34
|
-
|
|
35
|
-
### Features
|
|
36
|
-
|
|
37
|
-
* add CLI build command for local native iOS/Android builds ([97a3e73](https://github.com/better-update/better-update/commit/97a3e73501f120118e44087f2359696c5a913c9d)) - by @trancong12102
|
|
38
|
-
* add CLI package for project, credential, and env management ([07c0ff7](https://github.com/better-update/better-update/commit/07c0ff75d55f88bae94e4760625013b070896f36)) - by @trancong12102
|
|
39
|
-
* add CLI update publish command ([5f4268a](https://github.com/better-update/better-update/commit/5f4268a90bacb8a231bbc98ca92bd70452bcf02c)) - by @trancong12102
|
|
40
|
-
* **cli,server,dashboard:** close EAS Update feature gaps ([#4](https://github.com/better-update/better-update/issues/4), [#6](https://github.com/better-update/better-update/issues/6), [#7](https://github.com/better-update/better-update/issues/7), [#2](https://github.com/better-update/better-update/issues/2)) ([ecdc224](https://github.com/better-update/better-update/commit/ecdc22423f4117ab27ddac15e175329bf50c1031)) - by @trancong12102
|
|
41
|
-
* **cli,server:** integrate EAS CLI libraries, Apple auto-provisioning, and content-type-namespaced asset hashing ([ff3d881](https://github.com/better-update/better-update/commit/ff3d8812a27387e2331cc9e01741f4c01195e31a)) - by @trancong12102
|
|
42
|
-
* **cli:** add --rollout-percentage to update publish + schema-validated args ([e656e75](https://github.com/better-update/better-update/commit/e656e75685e4fe8f872d7495325f1bd8d6534cc8)) - by @trancong12102
|
|
43
|
-
* **cli:** add Android keystore certificate expiry validation ([78f3e46](https://github.com/better-update/better-update/commit/78f3e460e394632a0a39a6e3b5d7779b2dcd174d)) - by @trancong12102
|
|
44
|
-
* **cli:** add Apple App Store Connect provider selection ([0cd1894](https://github.com/better-update/better-update/commit/0cd189454eeed653826867411102b993d1fc1c9f)) - by @trancong12102
|
|
45
|
-
* **cli:** add full CLI coverage for all API groups ([9e05b10](https://github.com/better-update/better-update/commit/9e05b10152d27c9617288a0c182678aaec9d4864)) - by @trancong12102
|
|
46
|
-
* **repo:** add rollback-to-embedded flows ([f5fe1c4](https://github.com/better-update/better-update/commit/f5fe1c439ad7424dc671d84a7b81721e9b6976ad)) - by @trancong12102
|
|
47
|
-
* **repo:** add update lifecycle and credential flows ([f55cf5d](https://github.com/better-update/better-update/commit/f55cf5dea7874540fef6c9620ab8ced7c78e872f)) - by @trancong12102
|
|
48
|
-
* **repo:** expand build management coverage and dashboard details ([96d8d11](https://github.com/better-update/better-update/commit/96d8d11c41a171d67a3dad82d936c924a3e614c7)) - by @trancong12102
|
|
49
|
-
* **repo:** switch uploads to direct r2 flow ([736e7f1](https://github.com/better-update/better-update/commit/736e7f17d887a773b35038119981b31195b4f510)) - by @trancong12102
|
|
50
|
-
* **server,cli:** add build-credentials resolver with roster-hash gated profile regen ([e3a42f8](https://github.com/better-update/better-update/commit/e3a42f86eb7dfd6ec20edeafae6e1aae9c5f76ad)) - by @trancong12102
|
|
51
|
-
|
|
52
|
-
### Bug Fixes
|
|
53
|
-
|
|
54
|
-
* **cli:** defer api client auth and repair e2e harness ([77de9db](https://github.com/better-update/better-update/commit/77de9db2a93facbfbdb859fe65b5307d1f56a29e)) - by @trancong12102
|
|
55
|
-
* **dashboard:** remove build upload UI and restrict uploads to CLI only ([278b011](https://github.com/better-update/better-update/commit/278b0117945a6b766171df0a74d1443b0710ab10)) - by @trancong12102
|
|
56
|
-
* **repo:** add knip dead code detection, fix timing-safe HMAC verification ([ef3e3b6](https://github.com/better-update/better-update/commit/ef3e3b6c6d936e85346ed2b0f53941e92cc8d226)) - by @trancong12102
|
|
57
|
-
* **repo:** harden update publish and republish flows ([5b2a7b7](https://github.com/better-update/better-update/commit/5b2a7b7dc164baf29bbe54db83174f3a1f09b503)) - by @trancong12102
|
|
58
|
-
* **repo:** stream update artifacts and enable signed promote flows ([4ed09f1](https://github.com/better-update/better-update/commit/4ed09f15909e44a68e597a28d00c7b0825448211)) - by @trancong12102
|
package/oxlint.config.ts
DELETED
package/src/app-layer.ts
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { FetchHttpClient } from "@effect/platform";
|
|
2
|
-
import { BunContext } from "@effect/platform-bun";
|
|
3
|
-
import { Layer } from "effect";
|
|
4
|
-
|
|
5
|
-
import { ApiClientLive } from "./services/api-client";
|
|
6
|
-
import { AppleSessionStoreLive } from "./services/apple-session-store";
|
|
7
|
-
import { AuthStoreLive } from "./services/auth-store";
|
|
8
|
-
import { CliRuntimeLive } from "./services/cli-runtime";
|
|
9
|
-
import { ConfigStoreLive } from "./services/config-store";
|
|
10
|
-
import { PresignedUploadClientLive } from "./services/presigned-upload";
|
|
11
|
-
import { UpdateAssetUploaderLive } from "./services/update-asset-uploader";
|
|
12
|
-
|
|
13
|
-
const CliPlatformLayer = Layer.mergeAll(CliRuntimeLive, BunContext.layer, FetchHttpClient.layer);
|
|
14
|
-
const CliStoreLayer = Layer.mergeAll(AuthStoreLive, ConfigStoreLive, AppleSessionStoreLive).pipe(
|
|
15
|
-
Layer.provide(CliPlatformLayer),
|
|
16
|
-
);
|
|
17
|
-
const CliAdapterDependencies = Layer.mergeAll(CliPlatformLayer, CliStoreLayer);
|
|
18
|
-
const ApiClientLayer = ApiClientLive.pipe(Layer.provide(CliAdapterDependencies));
|
|
19
|
-
const PresignedUploadLayer = PresignedUploadClientLive.pipe(Layer.provide(CliPlatformLayer));
|
|
20
|
-
const UpdateAssetUploaderLayer = UpdateAssetUploaderLive.pipe(
|
|
21
|
-
Layer.provide(Layer.mergeAll(ApiClientLayer, PresignedUploadLayer)),
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
export const CliLive = Layer.mergeAll(
|
|
25
|
-
CliAdapterDependencies,
|
|
26
|
-
ApiClientLayer,
|
|
27
|
-
PresignedUploadLayer,
|
|
28
|
-
UpdateAssetUploaderLayer,
|
|
29
|
-
);
|
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
import { Console, Effect } from "effect";
|
|
2
|
-
|
|
3
|
-
import { runAndroidBuild } from "../commands/build/android";
|
|
4
|
-
import { runIosBuild } from "../commands/build/ios";
|
|
5
|
-
import { reserveAndUpload } from "../commands/build/reserve-and-upload";
|
|
6
|
-
import { readAppJson, readProjectId } from "../lib/app-json";
|
|
7
|
-
import { readAppMeta, readBuildProfile } from "../lib/build-profile";
|
|
8
|
-
import { pullEnvVars } from "../lib/env-exporter";
|
|
9
|
-
import { BuildProfileError } from "../lib/exit-codes";
|
|
10
|
-
import { readAppMetaFromConfig, readExpoConfig } from "../lib/expo-config";
|
|
11
|
-
import { readGitContext } from "../lib/git-context";
|
|
12
|
-
import { readGradleConfig, warnOnGradleMismatch } from "../lib/gradle-config";
|
|
13
|
-
import { printKeyValue } from "../lib/output";
|
|
14
|
-
import { resolveRuntimeVersion } from "../lib/runtime-version";
|
|
15
|
-
import { acquireBuildTempDir } from "../lib/temp-dir";
|
|
16
|
-
import { apiClient } from "../services/api-client";
|
|
17
|
-
import { CliRuntime } from "../services/cli-runtime";
|
|
18
|
-
|
|
19
|
-
import type { BuildTarget } from "../commands/build/reserve-and-upload";
|
|
20
|
-
import type { Platform } from "../lib/build-profile";
|
|
21
|
-
|
|
22
|
-
export interface RunBuildWorkflowOptions {
|
|
23
|
-
readonly platform: Platform;
|
|
24
|
-
readonly profileName: string;
|
|
25
|
-
readonly message: string | undefined;
|
|
26
|
-
readonly noUpload: boolean;
|
|
27
|
-
readonly rawOutput?: boolean;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
type AppMeta = Effect.Effect.Success<ReturnType<typeof readAppMeta>>;
|
|
31
|
-
type BuildProfile = Effect.Effect.Success<ReturnType<typeof readBuildProfile>>;
|
|
32
|
-
type ApiClient = Effect.Effect.Success<typeof apiClient>;
|
|
33
|
-
|
|
34
|
-
interface PlatformBuildInput {
|
|
35
|
-
readonly api: ApiClient;
|
|
36
|
-
readonly options: RunBuildWorkflowOptions;
|
|
37
|
-
readonly profile: BuildProfile;
|
|
38
|
-
readonly appMeta: AppMeta;
|
|
39
|
-
readonly envVars: Record<string, string>;
|
|
40
|
-
readonly projectId: string;
|
|
41
|
-
readonly projectRoot: string;
|
|
42
|
-
readonly tempDir: string;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const runIosPlatformBuild = (input: PlatformBuildInput) =>
|
|
46
|
-
Effect.gen(function* () {
|
|
47
|
-
const { api, appMeta, envVars, options, profile, projectId, projectRoot, tempDir } = input;
|
|
48
|
-
if (!profile.ios) {
|
|
49
|
-
return yield* new BuildProfileError({
|
|
50
|
-
message: `Profile "${profile.name}" has no ios section.`,
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
const iosProfile = profile.ios;
|
|
54
|
-
const iosBundleId = appMeta.bundleId;
|
|
55
|
-
if (!iosBundleId) {
|
|
56
|
-
return yield* new BuildProfileError({
|
|
57
|
-
message: "Missing expo.ios.bundleIdentifier in app.json.",
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
const build = yield* runIosBuild({
|
|
61
|
-
api,
|
|
62
|
-
tempDir,
|
|
63
|
-
projectRoot,
|
|
64
|
-
iosProfile,
|
|
65
|
-
bundleId: iosBundleId,
|
|
66
|
-
envVars,
|
|
67
|
-
projectId,
|
|
68
|
-
rawOutput: options.rawOutput,
|
|
69
|
-
});
|
|
70
|
-
const target: BuildTarget = {
|
|
71
|
-
platform: "ios",
|
|
72
|
-
distribution: iosProfile.distribution,
|
|
73
|
-
artifactFormat: "ipa",
|
|
74
|
-
};
|
|
75
|
-
return { build, target, bundleId: iosBundleId };
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
const runAndroidPlatformBuild = (input: PlatformBuildInput) =>
|
|
79
|
-
Effect.gen(function* () {
|
|
80
|
-
const { api, appMeta, envVars, profile, projectId, projectRoot, tempDir } = input;
|
|
81
|
-
if (!profile.android) {
|
|
82
|
-
return yield* new BuildProfileError({
|
|
83
|
-
message: `Profile "${profile.name}" has no android section.`,
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
const androidProfile = profile.android;
|
|
87
|
-
const androidBundleId = appMeta.androidPackage;
|
|
88
|
-
if (!androidBundleId) {
|
|
89
|
-
return yield* new BuildProfileError({
|
|
90
|
-
message: "Missing expo.android.package in app.json.",
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
// Cross-validate Gradle config against app.json (Groovy only). When
|
|
94
|
-
// Gradle resolves a different applicationId, the built APK/AAB is signed
|
|
95
|
-
// Under that id — so the Credential resolver must key off the Gradle value.
|
|
96
|
-
const androidDir = `${projectRoot}/android`;
|
|
97
|
-
const gradleConfig = yield* readGradleConfig(androidDir);
|
|
98
|
-
yield* warnOnGradleMismatch(gradleConfig, androidBundleId);
|
|
99
|
-
const applicationIdentifier = gradleConfig?.applicationId ?? androidBundleId;
|
|
100
|
-
const build = yield* runAndroidBuild({
|
|
101
|
-
api,
|
|
102
|
-
tempDir,
|
|
103
|
-
projectRoot,
|
|
104
|
-
androidProfile,
|
|
105
|
-
applicationIdentifier,
|
|
106
|
-
envVars,
|
|
107
|
-
projectId,
|
|
108
|
-
});
|
|
109
|
-
const target: BuildTarget =
|
|
110
|
-
androidProfile.format === "aab"
|
|
111
|
-
? { platform: "android", distribution: "play-store", artifactFormat: "aab" }
|
|
112
|
-
: { platform: "android", distribution: "direct", artifactFormat: "apk" };
|
|
113
|
-
return { build, target, bundleId: applicationIdentifier };
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
const runPlatformBuild = (input: PlatformBuildInput) =>
|
|
117
|
-
input.options.platform === "ios" ? runIosPlatformBuild(input) : runAndroidPlatformBuild(input);
|
|
118
|
-
|
|
119
|
-
export const runBuildWorkflow = (options: RunBuildWorkflowOptions) =>
|
|
120
|
-
Effect.scoped(
|
|
121
|
-
Effect.gen(function* () {
|
|
122
|
-
const api = yield* apiClient;
|
|
123
|
-
const runtime = yield* CliRuntime;
|
|
124
|
-
const projectRoot = yield* runtime.cwd;
|
|
125
|
-
|
|
126
|
-
const appJson = yield* readAppJson;
|
|
127
|
-
const projectId = yield* readProjectId;
|
|
128
|
-
|
|
129
|
-
const profile = yield* readBuildProfile(appJson, options.profileName);
|
|
130
|
-
|
|
131
|
-
// Load env vars BEFORE resolving dynamic config — app.config.js may read process.env
|
|
132
|
-
const envVars = yield* pullEnvVars(api, {
|
|
133
|
-
projectId,
|
|
134
|
-
environment: profile.environment,
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
// Try @expo/config for dynamic configs (app.config.js/ts), fall back to static app.json.
|
|
138
|
-
// EnvVars are applied as a scoped process.env overlay inside readExpoConfig and restored
|
|
139
|
-
// After the call so secrets do not leak to child processes spawned later.
|
|
140
|
-
const expoConfig = yield* readExpoConfig(projectRoot, envVars);
|
|
141
|
-
const appMeta = expoConfig
|
|
142
|
-
? yield* readAppMetaFromConfig(expoConfig, options.platform).pipe(
|
|
143
|
-
Effect.tap(() => Console.log("Resolved app config via @expo/config")),
|
|
144
|
-
Effect.catchAll(() => readAppMeta(appJson, options.platform)),
|
|
145
|
-
)
|
|
146
|
-
: yield* readAppMeta(appJson, options.platform);
|
|
147
|
-
|
|
148
|
-
const runtimeVersion = yield* resolveRuntimeVersion({
|
|
149
|
-
raw: appMeta.rawRuntimeVersion,
|
|
150
|
-
appVersion: appMeta.appVersion,
|
|
151
|
-
projectRoot,
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
const tempDir = yield* acquireBuildTempDir;
|
|
155
|
-
|
|
156
|
-
yield* Console.log(
|
|
157
|
-
`Building ${options.platform} artifact for profile "${profile.name}" (runtimeVersion=${runtimeVersion})`,
|
|
158
|
-
);
|
|
159
|
-
|
|
160
|
-
const outcome = yield* runPlatformBuild({
|
|
161
|
-
api,
|
|
162
|
-
options,
|
|
163
|
-
profile,
|
|
164
|
-
appMeta,
|
|
165
|
-
envVars,
|
|
166
|
-
projectId,
|
|
167
|
-
projectRoot,
|
|
168
|
-
tempDir,
|
|
169
|
-
});
|
|
170
|
-
const { build, target, bundleId } = outcome;
|
|
171
|
-
|
|
172
|
-
yield* Console.log(`Artifact produced: ${build.artifactPath}`);
|
|
173
|
-
|
|
174
|
-
if (options.noUpload) {
|
|
175
|
-
yield* printKeyValue([
|
|
176
|
-
["Artifact", build.artifactPath],
|
|
177
|
-
["SHA-256", build.sha256],
|
|
178
|
-
["Bytes", String(build.byteSize)],
|
|
179
|
-
["Upload", "skipped (--no-upload)"],
|
|
180
|
-
]);
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const rawGitContext = yield* readGitContext(projectRoot);
|
|
185
|
-
const gitContext: {
|
|
186
|
-
readonly ref?: string;
|
|
187
|
-
readonly commit?: string;
|
|
188
|
-
readonly dirty: boolean;
|
|
189
|
-
} = {
|
|
190
|
-
...(rawGitContext.ref === undefined ? {} : { ref: rawGitContext.ref }),
|
|
191
|
-
...(rawGitContext.commit === undefined ? {} : { commit: rawGitContext.commit }),
|
|
192
|
-
dirty: rawGitContext.dirty,
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
const result = yield* reserveAndUpload(api, {
|
|
196
|
-
target,
|
|
197
|
-
projectId,
|
|
198
|
-
profileName: profile.name,
|
|
199
|
-
runtimeVersion,
|
|
200
|
-
...(appMeta.appVersion === undefined ? {} : { appVersion: appMeta.appVersion }),
|
|
201
|
-
...(appMeta.buildNumber === undefined ? {} : { buildNumber: appMeta.buildNumber }),
|
|
202
|
-
bundleId,
|
|
203
|
-
gitContext,
|
|
204
|
-
...(options.message === undefined ? {} : { message: options.message }),
|
|
205
|
-
artifactPath: build.artifactPath,
|
|
206
|
-
sha256: build.sha256,
|
|
207
|
-
byteSize: build.byteSize,
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
yield* Console.log("");
|
|
211
|
-
yield* printKeyValue([
|
|
212
|
-
["Build ID", result.id],
|
|
213
|
-
["Status", result.status],
|
|
214
|
-
["Platform", options.platform],
|
|
215
|
-
["Profile", profile.name],
|
|
216
|
-
["Runtime version", runtimeVersion],
|
|
217
|
-
["Artifact", build.artifactPath],
|
|
218
|
-
["SHA-256", build.sha256],
|
|
219
|
-
["Bytes", String(build.byteSize)],
|
|
220
|
-
]);
|
|
221
|
-
}),
|
|
222
|
-
);
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { Console, Effect } from "effect";
|
|
2
|
-
|
|
3
|
-
import { CliRuntime } from "../services/cli-runtime";
|
|
4
|
-
|
|
5
|
-
export const exitWith = (code: number, message: string): Effect.Effect<void, never, CliRuntime> =>
|
|
6
|
-
Console.error(message).pipe(
|
|
7
|
-
Effect.zipRight(
|
|
8
|
-
Effect.gen(function* () {
|
|
9
|
-
const runtime = yield* CliRuntime;
|
|
10
|
-
yield* runtime.setExitCode(code);
|
|
11
|
-
}),
|
|
12
|
-
),
|
|
13
|
-
);
|
package/src/application/login.ts
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { Prompt } from "@effect/cli";
|
|
2
|
-
import { Command } from "@effect/platform";
|
|
3
|
-
import { Console, Effect, Redacted } from "effect";
|
|
4
|
-
|
|
5
|
-
import type { CommandExecutor } from "@effect/platform";
|
|
6
|
-
|
|
7
|
-
import { createBrowserLoginServer } from "../lib/browser-login";
|
|
8
|
-
import { AuthStore } from "../services/auth-store";
|
|
9
|
-
import { CliRuntime } from "../services/cli-runtime";
|
|
10
|
-
import { ConfigStore } from "../services/config-store";
|
|
11
|
-
|
|
12
|
-
const tokenPrompt = Prompt.password({
|
|
13
|
-
message: "Paste your API key (from dashboard > API Keys):",
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
const buildOpenBrowserCommand = (platform: NodeJS.Platform, url: string) => {
|
|
17
|
-
if (platform === "darwin") {
|
|
18
|
-
return Command.make("open", url);
|
|
19
|
-
}
|
|
20
|
-
if (platform === "win32") {
|
|
21
|
-
return Command.make("cmd", "/c", "start", "", url);
|
|
22
|
-
}
|
|
23
|
-
return Command.make("xdg-open", url);
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
const openBrowser = (
|
|
27
|
-
url: string,
|
|
28
|
-
): Effect.Effect<void, never, CliRuntime | CommandExecutor.CommandExecutor> =>
|
|
29
|
-
Effect.gen(function* () {
|
|
30
|
-
const runtime = yield* CliRuntime;
|
|
31
|
-
const command = buildOpenBrowserCommand(runtime.platform, url);
|
|
32
|
-
|
|
33
|
-
const opened = yield* Command.exitCode(command).pipe(
|
|
34
|
-
Effect.map((code) => code === 0),
|
|
35
|
-
Effect.catchAll(() => Effect.succeed(false)),
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
if (!opened) {
|
|
39
|
-
yield* Console.log(`Open this URL manually:\n${url}`);
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
const browserLogin = Effect.scoped(
|
|
44
|
-
Effect.gen(function* () {
|
|
45
|
-
const configStore = yield* ConfigStore;
|
|
46
|
-
const authStore = yield* AuthStore;
|
|
47
|
-
const dashboardUrl = yield* configStore.getDashboardUrl;
|
|
48
|
-
|
|
49
|
-
const loginServer = yield* Effect.acquireRelease(
|
|
50
|
-
Effect.sync(createBrowserLoginServer),
|
|
51
|
-
(server) => Effect.sync(server.stop),
|
|
52
|
-
);
|
|
53
|
-
|
|
54
|
-
const loginUrl = `${dashboardUrl}/cli-login?callbackUrl=${encodeURIComponent(loginServer.callbackUrl)}`;
|
|
55
|
-
|
|
56
|
-
yield* Console.log("Opening browser for better-update login...");
|
|
57
|
-
yield* Console.log("");
|
|
58
|
-
yield* openBrowser(loginUrl);
|
|
59
|
-
|
|
60
|
-
const token = yield* loginServer.waitForToken;
|
|
61
|
-
yield* authStore.saveToken(token);
|
|
62
|
-
yield* Console.log("");
|
|
63
|
-
yield* Console.log("Logged in successfully. Token saved to ~/.better-update/auth.json");
|
|
64
|
-
}),
|
|
65
|
-
);
|
|
66
|
-
|
|
67
|
-
const manualLogin = Effect.gen(function* () {
|
|
68
|
-
yield* Console.log("Log in to better-update with an existing API key");
|
|
69
|
-
yield* Console.log("Get your API key from the dashboard > API Keys page");
|
|
70
|
-
yield* Console.log("");
|
|
71
|
-
|
|
72
|
-
const token = Redacted.value(yield* tokenPrompt);
|
|
73
|
-
const authStore = yield* AuthStore;
|
|
74
|
-
yield* authStore.saveToken(token);
|
|
75
|
-
yield* Console.log("");
|
|
76
|
-
yield* Console.log("Logged in successfully. Token saved to ~/.better-update/auth.json");
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
export const runLogin = (options: { readonly manualApiKey: boolean }) =>
|
|
80
|
-
Effect.gen(function* () {
|
|
81
|
-
if (options.manualApiKey) {
|
|
82
|
-
yield* manualLogin;
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
yield* browserLogin;
|
|
87
|
-
});
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { Effect } from "effect";
|
|
2
|
-
|
|
3
|
-
import type { FileSystem } from "@effect/platform";
|
|
4
|
-
|
|
5
|
-
import { UpdatePromoteError } from "../lib/exit-codes";
|
|
6
|
-
import { formatCause } from "../lib/format-error";
|
|
7
|
-
import { loadOptionalSignedPayload } from "../lib/signed-payloads";
|
|
8
|
-
import { apiClient } from "../services/api-client";
|
|
9
|
-
|
|
10
|
-
import type { AuthRequiredError } from "../lib/exit-codes";
|
|
11
|
-
import type { ApiClientService } from "../services/api-client";
|
|
12
|
-
|
|
13
|
-
export interface RunUpdatePromoteOptions {
|
|
14
|
-
readonly updateId: string;
|
|
15
|
-
readonly channel: string;
|
|
16
|
-
readonly manifestBodyFile: string | undefined;
|
|
17
|
-
readonly signatureFile: string | undefined;
|
|
18
|
-
readonly certificateChainFile: string | undefined;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface UpdatePromoteResult {
|
|
22
|
-
readonly sourceUpdateId: string;
|
|
23
|
-
readonly channel: string;
|
|
24
|
-
readonly updateId: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export const runUpdatePromote = (
|
|
28
|
-
options: RunUpdatePromoteOptions,
|
|
29
|
-
): Effect.Effect<
|
|
30
|
-
UpdatePromoteResult,
|
|
31
|
-
AuthRequiredError | UpdatePromoteError,
|
|
32
|
-
ApiClientService | FileSystem.FileSystem
|
|
33
|
-
> =>
|
|
34
|
-
Effect.gen(function* () {
|
|
35
|
-
const api = yield* apiClient;
|
|
36
|
-
const signedPayload = yield* loadOptionalSignedPayload({
|
|
37
|
-
files: {
|
|
38
|
-
manifestBodyFile: options.manifestBodyFile,
|
|
39
|
-
signatureFile: options.signatureFile,
|
|
40
|
-
certificateChainFile: options.certificateChainFile,
|
|
41
|
-
},
|
|
42
|
-
label: "Signed promote",
|
|
43
|
-
makeError: (message) => new UpdatePromoteError({ message }),
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const result = yield* api.updates
|
|
47
|
-
.republish({
|
|
48
|
-
payload: {
|
|
49
|
-
sourceUpdateId: options.updateId,
|
|
50
|
-
destinationChannel: options.channel,
|
|
51
|
-
...(signedPayload
|
|
52
|
-
? {
|
|
53
|
-
signedUpdates: [
|
|
54
|
-
{
|
|
55
|
-
sourceUpdateId: options.updateId,
|
|
56
|
-
manifestBody: signedPayload.manifestBody,
|
|
57
|
-
signature: signedPayload.signature,
|
|
58
|
-
certificateChain: signedPayload.certificateChain,
|
|
59
|
-
},
|
|
60
|
-
],
|
|
61
|
-
}
|
|
62
|
-
: {}),
|
|
63
|
-
},
|
|
64
|
-
})
|
|
65
|
-
.pipe(
|
|
66
|
-
Effect.catchIf(
|
|
67
|
-
(cause): cause is Exclude<typeof cause, AuthRequiredError> =>
|
|
68
|
-
(cause as { readonly _tag?: string })._tag !== "AuthRequiredError",
|
|
69
|
-
(cause) =>
|
|
70
|
-
new UpdatePromoteError({
|
|
71
|
-
message: `Failed to promote update: ${formatCause(cause)}`,
|
|
72
|
-
}),
|
|
73
|
-
),
|
|
74
|
-
);
|
|
75
|
-
|
|
76
|
-
const [promotedUpdate] = result.updates;
|
|
77
|
-
if (!promotedUpdate) {
|
|
78
|
-
return yield* new UpdatePromoteError({
|
|
79
|
-
message: "Promote completed without returning a promoted update.",
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
sourceUpdateId: options.updateId,
|
|
85
|
-
channel: options.channel,
|
|
86
|
-
updateId: promotedUpdate.id,
|
|
87
|
-
} as const satisfies UpdatePromoteResult;
|
|
88
|
-
});
|