@capuchoo/cli 0.2.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 +351 -0
- package/bin/dev.cmd +3 -0
- package/bin/dev.js +5 -0
- package/bin/run.cmd +3 -0
- package/bin/run.js +5 -0
- package/dist/base-command.d.ts +15 -0
- package/dist/base-command.d.ts.map +1 -0
- package/dist/base-command.js +61 -0
- package/dist/base-command.js.map +1 -0
- package/dist/commands/auth/login.d.ts +19 -0
- package/dist/commands/auth/login.d.ts.map +1 -0
- package/dist/commands/auth/login.js +74 -0
- package/dist/commands/auth/login.js.map +1 -0
- package/dist/commands/auth/logout.d.ts +6 -0
- package/dist/commands/auth/logout.d.ts.map +1 -0
- package/dist/commands/auth/logout.js +22 -0
- package/dist/commands/auth/logout.js.map +1 -0
- package/dist/commands/auth/whoami.d.ts +9 -0
- package/dist/commands/auth/whoami.d.ts.map +1 -0
- package/dist/commands/auth/whoami.js +77 -0
- package/dist/commands/auth/whoami.js.map +1 -0
- package/dist/commands/channel/list.d.ts +9 -0
- package/dist/commands/channel/list.d.ts.map +1 -0
- package/dist/commands/channel/list.js +47 -0
- package/dist/commands/channel/list.js.map +1 -0
- package/dist/commands/config/list.d.ts +9 -0
- package/dist/commands/config/list.d.ts.map +1 -0
- package/dist/commands/config/list.js +116 -0
- package/dist/commands/config/list.js.map +1 -0
- package/dist/commands/config/set.d.ts +11 -0
- package/dist/commands/config/set.d.ts.map +1 -0
- package/dist/commands/config/set.js +44 -0
- package/dist/commands/config/set.js.map +1 -0
- package/dist/commands/deploy/native.d.ts +23 -0
- package/dist/commands/deploy/native.d.ts.map +1 -0
- package/dist/commands/deploy/native.js +39 -0
- package/dist/commands/deploy/native.js.map +1 -0
- package/dist/commands/deploy/ota.d.ts +20 -0
- package/dist/commands/deploy/ota.d.ts.map +1 -0
- package/dist/commands/deploy/ota.js +20 -0
- package/dist/commands/deploy/ota.js.map +1 -0
- package/dist/commands/init.d.ts +17 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +232 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/version/bump.d.ts +13 -0
- package/dist/commands/version/bump.d.ts.map +1 -0
- package/dist/commands/version/bump.js +52 -0
- package/dist/commands/version/bump.js.map +1 -0
- package/dist/commands/version/sync.d.ts +12 -0
- package/dist/commands/version/sync.d.ts.map +1 -0
- package/dist/commands/version/sync.js +81 -0
- package/dist/commands/version/sync.js.map +1 -0
- package/dist/deploy/execute.d.ts +48 -0
- package/dist/deploy/execute.d.ts.map +1 -0
- package/dist/deploy/execute.js +280 -0
- package/dist/deploy/execute.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/pipeline/android.d.ts +37 -0
- package/dist/pipeline/android.d.ts.map +1 -0
- package/dist/pipeline/android.js +96 -0
- package/dist/pipeline/android.js.map +1 -0
- package/dist/pipeline/build.d.ts +69 -0
- package/dist/pipeline/build.d.ts.map +1 -0
- package/dist/pipeline/build.js +138 -0
- package/dist/pipeline/build.js.map +1 -0
- package/dist/pipeline/deploy.d.ts +74 -0
- package/dist/pipeline/deploy.d.ts.map +1 -0
- package/dist/pipeline/deploy.js +198 -0
- package/dist/pipeline/deploy.js.map +1 -0
- package/dist/pipeline/flavour.d.ts +66 -0
- package/dist/pipeline/flavour.d.ts.map +1 -0
- package/dist/pipeline/flavour.js +120 -0
- package/dist/pipeline/flavour.js.map +1 -0
- package/dist/pipeline/native-config.d.ts +43 -0
- package/dist/pipeline/native-config.d.ts.map +1 -0
- package/dist/pipeline/native-config.js +127 -0
- package/dist/pipeline/native-config.js.map +1 -0
- package/dist/pipeline/toolchain.d.ts +80 -0
- package/dist/pipeline/toolchain.d.ts.map +1 -0
- package/dist/pipeline/toolchain.js +146 -0
- package/dist/pipeline/toolchain.js.map +1 -0
- package/dist/pipeline/zip.d.ts +22 -0
- package/dist/pipeline/zip.d.ts.map +1 -0
- package/dist/pipeline/zip.js +188 -0
- package/dist/pipeline/zip.js.map +1 -0
- package/dist/services/cloud.d.ts +58 -0
- package/dist/services/cloud.d.ts.map +1 -0
- package/dist/services/cloud.js +89 -0
- package/dist/services/cloud.js.map +1 -0
- package/dist/utils/config.d.ts +52 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +144 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/exec.d.ts +44 -0
- package/dist/utils/exec.d.ts.map +1 -0
- package/dist/utils/exec.js +136 -0
- package/dist/utils/exec.js.map +1 -0
- package/dist/utils/http.d.ts +37 -0
- package/dist/utils/http.d.ts.map +1 -0
- package/dist/utils/http.js +137 -0
- package/dist/utils/http.js.map +1 -0
- package/dist/utils/reporter.d.ts +48 -0
- package/dist/utils/reporter.d.ts.map +1 -0
- package/dist/utils/reporter.js +108 -0
- package/dist/utils/reporter.js.map +1 -0
- package/oclif.manifest.json +568 -0
- package/package.json +81 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { CloudApp, CloudChannel, CloudOrganization, CloudRelease, UserProfile } from "@capuchoo/core";
|
|
2
|
+
/**
|
|
3
|
+
* The Capucho API client.
|
|
4
|
+
*
|
|
5
|
+
* Replaces the previous split between `CloudService`, `CloudConfigService`,
|
|
6
|
+
* `ChannelService` and `AuthService`. Those overlapped: two of them fetched
|
|
7
|
+
* channels, `ChannelService` called a `/api/channel` endpoint the backend does
|
|
8
|
+
* not implement and silently returned `["development", "staging", "production"]`
|
|
9
|
+
* on failure - hard-coded names that then failed validation server-side - and
|
|
10
|
+
* `CloudConfigService` cached a `/api/project/config` response nobody read.
|
|
11
|
+
*/
|
|
12
|
+
export declare class CloudClient {
|
|
13
|
+
private readonly options;
|
|
14
|
+
constructor(endpoint: string, apiKey: string);
|
|
15
|
+
get endpoint(): string;
|
|
16
|
+
/** Verifies the credentials and returns the profile behind them. */
|
|
17
|
+
whoami(): Promise<UserProfile | null>;
|
|
18
|
+
organizations(): Promise<CloudOrganization[]>;
|
|
19
|
+
apps(): Promise<CloudApp[]>;
|
|
20
|
+
createApp(input: {
|
|
21
|
+
name: string;
|
|
22
|
+
app_id: string;
|
|
23
|
+
platform: string;
|
|
24
|
+
organization_id: string;
|
|
25
|
+
}): Promise<CloudApp>;
|
|
26
|
+
channels(cloudAppId: string): Promise<CloudChannel[]>;
|
|
27
|
+
releases(cloudAppId: string, channel?: string): Promise<CloudRelease[]>;
|
|
28
|
+
/**
|
|
29
|
+
* Resolves a channel by name and rejects the ones that cannot be deployed to.
|
|
30
|
+
*
|
|
31
|
+
* A channel without an `environment` is unusable: the environment is what
|
|
32
|
+
* selects the build flavour, so there would be nothing to build. The old code
|
|
33
|
+
* checked this at two separate call sites with slightly different messages.
|
|
34
|
+
*/
|
|
35
|
+
requireChannel(cloudAppId: string, name: string): Promise<CloudChannel>;
|
|
36
|
+
uploadBundle(input: {
|
|
37
|
+
filePath: string;
|
|
38
|
+
appId: string;
|
|
39
|
+
channel: string;
|
|
40
|
+
platform: string;
|
|
41
|
+
versionName: string;
|
|
42
|
+
releaseNotes: string;
|
|
43
|
+
active: boolean;
|
|
44
|
+
required: boolean;
|
|
45
|
+
}): Promise<import("../utils/http.js").UploadResult>;
|
|
46
|
+
uploadNative(input: {
|
|
47
|
+
filePath: string;
|
|
48
|
+
appId: string;
|
|
49
|
+
channel: string;
|
|
50
|
+
platform: string;
|
|
51
|
+
versionName: string;
|
|
52
|
+
versionCode: number;
|
|
53
|
+
releaseNotes: string;
|
|
54
|
+
active: boolean;
|
|
55
|
+
required: boolean;
|
|
56
|
+
}): Promise<import("../utils/http.js").UploadResult>;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=cloud.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud.d.ts","sourceRoot":"","sources":["../../src/services/cloud.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EACZ,iBAAiB,EACjB,YAAY,EACZ,WAAW,EACZ,MAAM,gBAAgB,CAAC;AAGxB;;;;;;;;;GASG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;gBAE1B,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAI5C,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,oEAAoE;IAC9D,MAAM,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAQ3C,aAAa,IAAI,OAAO,CAAC,iBAAiB,EAAE,CAAC;IAI7C,IAAI,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;IAI3B,SAAS,CAAC,KAAK,EAAE;QACf,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,eAAe,EAAE,MAAM,CAAC;KACzB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAIrB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAIrD,QAAQ,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAKvE;;;;;;OAMG;IACG,cAAc,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAmB7E,YAAY,CAAC,KAAK,EAAE;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,MAAM,EAAE,OAAO,CAAC;QAChB,QAAQ,EAAE,OAAO,CAAC;KACnB;IAiBD,YAAY,CAAC,KAAK,EAAE;QAClB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;QACpB,YAAY,EAAE,MAAM,CAAC;QACrB,MAAM,EAAE,OAAO,CAAC;QAChB,QAAQ,EAAE,OAAO,CAAC;KACnB;CAiBF"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { get, post, uploadArtifact } from "../utils/http.js";
|
|
2
|
+
/**
|
|
3
|
+
* The Capucho API client.
|
|
4
|
+
*
|
|
5
|
+
* Replaces the previous split between `CloudService`, `CloudConfigService`,
|
|
6
|
+
* `ChannelService` and `AuthService`. Those overlapped: two of them fetched
|
|
7
|
+
* channels, `ChannelService` called a `/api/channel` endpoint the backend does
|
|
8
|
+
* not implement and silently returned `["development", "staging", "production"]`
|
|
9
|
+
* on failure - hard-coded names that then failed validation server-side - and
|
|
10
|
+
* `CloudConfigService` cached a `/api/project/config` response nobody read.
|
|
11
|
+
*/
|
|
12
|
+
export class CloudClient {
|
|
13
|
+
options;
|
|
14
|
+
constructor(endpoint, apiKey) {
|
|
15
|
+
this.options = { endpoint, apiKey };
|
|
16
|
+
}
|
|
17
|
+
get endpoint() {
|
|
18
|
+
return this.options.endpoint;
|
|
19
|
+
}
|
|
20
|
+
/** Verifies the credentials and returns the profile behind them. */
|
|
21
|
+
async whoami() {
|
|
22
|
+
try {
|
|
23
|
+
return await get("/api/auth/me", this.options);
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
organizations() {
|
|
30
|
+
return get("/api/organizations", this.options);
|
|
31
|
+
}
|
|
32
|
+
apps() {
|
|
33
|
+
return get("/api/apps", this.options);
|
|
34
|
+
}
|
|
35
|
+
createApp(input) {
|
|
36
|
+
return post("/api/apps", input, this.options);
|
|
37
|
+
}
|
|
38
|
+
channels(cloudAppId) {
|
|
39
|
+
return get(`/api/apps/${cloudAppId}/channels`, this.options);
|
|
40
|
+
}
|
|
41
|
+
releases(cloudAppId, channel) {
|
|
42
|
+
const query = channel ? `?channel=${encodeURIComponent(channel)}` : "";
|
|
43
|
+
return get(`/api/apps/${cloudAppId}/releases${query}`, this.options);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Resolves a channel by name and rejects the ones that cannot be deployed to.
|
|
47
|
+
*
|
|
48
|
+
* A channel without an `environment` is unusable: the environment is what
|
|
49
|
+
* selects the build flavour, so there would be nothing to build. The old code
|
|
50
|
+
* checked this at two separate call sites with slightly different messages.
|
|
51
|
+
*/
|
|
52
|
+
async requireChannel(cloudAppId, name) {
|
|
53
|
+
const channels = await this.channels(cloudAppId);
|
|
54
|
+
const channel = channels.find((candidate) => candidate.name === name);
|
|
55
|
+
if (!channel) {
|
|
56
|
+
const available = channels.map((c) => c.name).join(", ") || "none";
|
|
57
|
+
throw new Error(`Channel "${name}" does not exist for this app. Available: ${available}.`);
|
|
58
|
+
}
|
|
59
|
+
if (!channel.environment) {
|
|
60
|
+
throw new Error(`Channel "${name}" has no environment set, so the CLI cannot tell which ` +
|
|
61
|
+
"flavour to build. Set it in the dashboard.");
|
|
62
|
+
}
|
|
63
|
+
return channel;
|
|
64
|
+
}
|
|
65
|
+
uploadBundle(input) {
|
|
66
|
+
return uploadArtifact("/api/admin/upload", input.filePath, {
|
|
67
|
+
app_id: input.appId,
|
|
68
|
+
channel: input.channel,
|
|
69
|
+
platform: input.platform,
|
|
70
|
+
version_name: input.versionName,
|
|
71
|
+
release_notes: input.releaseNotes,
|
|
72
|
+
active: String(input.active),
|
|
73
|
+
required: String(input.required),
|
|
74
|
+
}, { ...this.options, fileField: "bundle" });
|
|
75
|
+
}
|
|
76
|
+
uploadNative(input) {
|
|
77
|
+
return uploadArtifact("/api/admin/native-upload", input.filePath, {
|
|
78
|
+
app_id: input.appId,
|
|
79
|
+
channel: input.channel,
|
|
80
|
+
platform: input.platform,
|
|
81
|
+
version_name: input.versionName,
|
|
82
|
+
version_code: String(input.versionCode),
|
|
83
|
+
release_notes: input.releaseNotes,
|
|
84
|
+
active: String(input.active),
|
|
85
|
+
required: String(input.required),
|
|
86
|
+
}, { ...this.options, fileField: "bundle" });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=cloud.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud.js","sourceRoot":"","sources":["../../src/services/cloud.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,cAAc,EAAoB,MAAM,kBAAkB,CAAC;AAE/E;;;;;;;;;GASG;AACH,MAAM,OAAO,WAAW;IACL,OAAO,CAAc;IAEtC,YAAY,QAAgB,EAAE,MAAc;QAC1C,IAAI,CAAC,OAAO,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACtC,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;IAC/B,CAAC;IAED,oEAAoE;IACpE,KAAK,CAAC,MAAM;QACV,IAAI,CAAC;YACH,OAAO,MAAM,GAAG,CAAc,cAAc,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAC9D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,aAAa;QACX,OAAO,GAAG,CAAsB,oBAAoB,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACtE,CAAC;IAED,IAAI;QACF,OAAO,GAAG,CAAa,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACpD,CAAC;IAED,SAAS,CAAC,KAKT;QACC,OAAO,IAAI,CAAW,WAAW,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1D,CAAC;IAED,QAAQ,CAAC,UAAkB;QACzB,OAAO,GAAG,CAAiB,aAAa,UAAU,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/E,CAAC;IAED,QAAQ,CAAC,UAAkB,EAAE,OAAgB;QAC3C,MAAM,KAAK,GAAG,OAAO,CAAC,CAAC,CAAC,YAAY,kBAAkB,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvE,OAAO,GAAG,CAAiB,aAAa,UAAU,YAAY,KAAK,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACvF,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,cAAc,CAAC,UAAkB,EAAE,IAAY;QACnD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;QACjD,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;QAEtE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,SAAS,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,CAAC;YACnE,MAAM,IAAI,KAAK,CAAC,YAAY,IAAI,6CAA6C,SAAS,GAAG,CAAC,CAAC;QAC7F,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YACzB,MAAM,IAAI,KAAK,CACb,YAAY,IAAI,yDAAyD;gBACvE,4CAA4C,CAC/C,CAAC;QACJ,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,YAAY,CAAC,KASZ;QACC,OAAO,cAAc,CACnB,mBAAmB,EACnB,KAAK,CAAC,QAAQ,EACd;YACE,MAAM,EAAE,KAAK,CAAC,KAAK;YACnB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,YAAY,EAAE,KAAK,CAAC,WAAW;YAC/B,aAAa,EAAE,KAAK,CAAC,YAAY;YACjC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;YAC5B,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC;SACjC,EACD,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,CACzC,CAAC;IACJ,CAAC;IAED,YAAY,CAAC,KAUZ;QACC,OAAO,cAAc,CACnB,0BAA0B,EAC1B,KAAK,CAAC,QAAQ,EACd;YACE,MAAM,EAAE,KAAK,CAAC,KAAK;YACnB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,YAAY,EAAE,KAAK,CAAC,WAAW;YAC/B,YAAY,EAAE,MAAM,CAAC,KAAK,CAAC,WAAW,CAAC;YACvC,aAAa,EAAE,KAAK,CAAC,YAAY;YACjC,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;YAC5B,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC;SACjC,EACD,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,CACzC,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type ProjectConfig, type ResolvedProjectConfig } from "@capuchoo/core";
|
|
2
|
+
export interface GlobalConfig {
|
|
3
|
+
endpoint?: string;
|
|
4
|
+
apiKey?: string;
|
|
5
|
+
user?: {
|
|
6
|
+
id: string;
|
|
7
|
+
email: string;
|
|
8
|
+
};
|
|
9
|
+
authenticatedAt?: string;
|
|
10
|
+
defaultChannel?: string;
|
|
11
|
+
}
|
|
12
|
+
/** Credentials resolved for this invocation, and where they came from. */
|
|
13
|
+
export interface Credentials {
|
|
14
|
+
endpoint: string;
|
|
15
|
+
apiKey: string;
|
|
16
|
+
source: "environment" | "config";
|
|
17
|
+
}
|
|
18
|
+
export declare function globalConfigPath(): string;
|
|
19
|
+
export declare function projectConfigPath(appDir: string): string;
|
|
20
|
+
export declare function readGlobalConfig(): GlobalConfig;
|
|
21
|
+
export declare function writeGlobalConfig(config: GlobalConfig): void;
|
|
22
|
+
export declare function updateGlobalConfig(patch: Partial<GlobalConfig>): GlobalConfig;
|
|
23
|
+
/**
|
|
24
|
+
* Resolves credentials.
|
|
25
|
+
*
|
|
26
|
+
* Environment variables win, and are never written to disk. That is what makes
|
|
27
|
+
* a CI run safe: `CAPUCHO_API_KEY` lives in the job for its lifetime and leaves
|
|
28
|
+
* nothing behind on the runner.
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveCredentials(): Credentials | null;
|
|
31
|
+
export declare function readProjectConfig(appDir: string): ProjectConfig | null;
|
|
32
|
+
export declare function writeProjectConfig(appDir: string, config: ProjectConfig): void;
|
|
33
|
+
/**
|
|
34
|
+
* Loads and validates the project config, resolving every default.
|
|
35
|
+
*
|
|
36
|
+
* Throws with the list of problems rather than returning null, so callers do
|
|
37
|
+
* not each reinvent the error message.
|
|
38
|
+
*/
|
|
39
|
+
export declare function requireProjectConfig(appDir: string): ResolvedProjectConfig;
|
|
40
|
+
/** Reads the application's semantic version from its package.json. */
|
|
41
|
+
export declare function readAppVersion(appDir: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Writes a new version to the application's package.json.
|
|
44
|
+
*
|
|
45
|
+
* This replaces `npm version <type> --no-git-tag-version`. In a workspace npm
|
|
46
|
+
* resolves the nearest package.json from the *process* working directory, so
|
|
47
|
+
* running it from a monorepo root bumped the root package rather than the app.
|
|
48
|
+
* Writing the file directly also leaves the rest of it byte-identical, instead
|
|
49
|
+
* of npm reformatting the whole file.
|
|
50
|
+
*/
|
|
51
|
+
export declare function writeAppVersion(appDir: string, version: string): void;
|
|
52
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC3B,MAAM,gBAAgB,CAAC;AAyBxB,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,0EAA0E;AAC1E,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,aAAa,GAAG,QAAQ,CAAC;CAClC;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAEzC;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAExD;AAgBD,wBAAgB,gBAAgB,IAAI,YAAY,CAE/C;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAW5D;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,CAAC,YAAY,CAAC,GAAG,YAAY,CAO7E;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,IAAI,WAAW,GAAG,IAAI,CAsBvD;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAEtE;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CAE9E;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,qBAAqB,CAa1E;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CASrD;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,CAYrE"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { normaliseProjectConfig, validateProjectConfig, } from "@capuchoo/core";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
/**
|
|
6
|
+
* Configuration and credential storage.
|
|
7
|
+
*
|
|
8
|
+
* Two files, with distinct jobs:
|
|
9
|
+
*
|
|
10
|
+
* ~/.capuchoo/config.json credentials and preferences. Never committed.
|
|
11
|
+
* <app>/.capuchoo/project.json which app this directory publishes.
|
|
12
|
+
* Committed - it is not a secret.
|
|
13
|
+
*
|
|
14
|
+
* The old `ConfigManager` merged both into one flat object and then read
|
|
15
|
+
* `apiKey` out of the result, which meant a project.json could override the
|
|
16
|
+
* credentials of whoever ran the deploy. Keeping them separate removes that,
|
|
17
|
+
* and removes the 40-field `CapuchoConfig` interface that had accumulated every
|
|
18
|
+
* key any command had ever wanted.
|
|
19
|
+
*/
|
|
20
|
+
const DIR = ".capuchoo";
|
|
21
|
+
const GLOBAL_FILE = "config.json";
|
|
22
|
+
const PROJECT_FILE = "project.json";
|
|
23
|
+
export function globalConfigPath() {
|
|
24
|
+
return path.join(os.homedir(), DIR, GLOBAL_FILE);
|
|
25
|
+
}
|
|
26
|
+
export function projectConfigPath(appDir) {
|
|
27
|
+
return path.join(appDir, DIR, PROJECT_FILE);
|
|
28
|
+
}
|
|
29
|
+
function readJson(file) {
|
|
30
|
+
if (!fs.existsSync(file))
|
|
31
|
+
return null;
|
|
32
|
+
try {
|
|
33
|
+
return JSON.parse(fs.readFileSync(file, "utf8"));
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
throw new Error(`${file} is not valid JSON. Fix or delete it.`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function writeJson(file, value) {
|
|
40
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
41
|
+
fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`);
|
|
42
|
+
}
|
|
43
|
+
export function readGlobalConfig() {
|
|
44
|
+
return readJson(globalConfigPath()) ?? {};
|
|
45
|
+
}
|
|
46
|
+
export function writeGlobalConfig(config) {
|
|
47
|
+
const file = globalConfigPath();
|
|
48
|
+
writeJson(file, config);
|
|
49
|
+
// The file holds an API key. Restrict it where the platform supports it;
|
|
50
|
+
// Windows ACLs are not modelled by chmod, so failure is not fatal.
|
|
51
|
+
try {
|
|
52
|
+
fs.chmodSync(file, 0o600);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
/* best effort */
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function updateGlobalConfig(patch) {
|
|
59
|
+
const next = { ...readGlobalConfig(), ...patch };
|
|
60
|
+
for (const key of Object.keys(next)) {
|
|
61
|
+
if (next[key] === undefined)
|
|
62
|
+
delete next[key];
|
|
63
|
+
}
|
|
64
|
+
writeGlobalConfig(next);
|
|
65
|
+
return next;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Resolves credentials.
|
|
69
|
+
*
|
|
70
|
+
* Environment variables win, and are never written to disk. That is what makes
|
|
71
|
+
* a CI run safe: `CAPUCHO_API_KEY` lives in the job for its lifetime and leaves
|
|
72
|
+
* nothing behind on the runner.
|
|
73
|
+
*/
|
|
74
|
+
export function resolveCredentials() {
|
|
75
|
+
const envEndpoint = process.env.CAPUCHO_ENDPOINT;
|
|
76
|
+
const envKey = process.env.CAPUCHO_API_KEY;
|
|
77
|
+
if (envEndpoint && envKey) {
|
|
78
|
+
return {
|
|
79
|
+
endpoint: envEndpoint.replace(/\/+$/, ""),
|
|
80
|
+
apiKey: envKey,
|
|
81
|
+
source: "environment",
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const config = readGlobalConfig();
|
|
85
|
+
if (config.endpoint && config.apiKey) {
|
|
86
|
+
return {
|
|
87
|
+
endpoint: config.endpoint.replace(/\/+$/, ""),
|
|
88
|
+
apiKey: config.apiKey,
|
|
89
|
+
source: "config",
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
export function readProjectConfig(appDir) {
|
|
95
|
+
return readJson(projectConfigPath(appDir));
|
|
96
|
+
}
|
|
97
|
+
export function writeProjectConfig(appDir, config) {
|
|
98
|
+
writeJson(projectConfigPath(appDir), config);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Loads and validates the project config, resolving every default.
|
|
102
|
+
*
|
|
103
|
+
* Throws with the list of problems rather than returning null, so callers do
|
|
104
|
+
* not each reinvent the error message.
|
|
105
|
+
*/
|
|
106
|
+
export function requireProjectConfig(appDir) {
|
|
107
|
+
const raw = readProjectConfig(appDir);
|
|
108
|
+
const problems = validateProjectConfig(raw);
|
|
109
|
+
if (problems.length > 0) {
|
|
110
|
+
const location = path.relative(process.cwd(), projectConfigPath(appDir));
|
|
111
|
+
throw new Error(`${location} is not usable:\n - ${problems.join("\n - ")}\n\n` +
|
|
112
|
+
`Run "capuchoo init" in this directory to create it.`);
|
|
113
|
+
}
|
|
114
|
+
return normaliseProjectConfig(raw);
|
|
115
|
+
}
|
|
116
|
+
/** Reads the application's semantic version from its package.json. */
|
|
117
|
+
export function readAppVersion(appDir) {
|
|
118
|
+
const file = path.join(appDir, "package.json");
|
|
119
|
+
const pkg = readJson(file);
|
|
120
|
+
if (!pkg?.version) {
|
|
121
|
+
throw new Error(`${file} has no "version" field, so there is nothing to publish.`);
|
|
122
|
+
}
|
|
123
|
+
return pkg.version;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Writes a new version to the application's package.json.
|
|
127
|
+
*
|
|
128
|
+
* This replaces `npm version <type> --no-git-tag-version`. In a workspace npm
|
|
129
|
+
* resolves the nearest package.json from the *process* working directory, so
|
|
130
|
+
* running it from a monorepo root bumped the root package rather than the app.
|
|
131
|
+
* Writing the file directly also leaves the rest of it byte-identical, instead
|
|
132
|
+
* of npm reformatting the whole file.
|
|
133
|
+
*/
|
|
134
|
+
export function writeAppVersion(appDir, version) {
|
|
135
|
+
const file = path.join(appDir, "package.json");
|
|
136
|
+
const contents = fs.readFileSync(file, "utf8");
|
|
137
|
+
// Replace only the top-level "version" value, preserving formatting.
|
|
138
|
+
const updated = contents.replace(/^(\s*"version"\s*:\s*")[^"]*(")/m, `$1${version}$2`);
|
|
139
|
+
if (updated === contents) {
|
|
140
|
+
throw new Error(`Could not update the version field in ${file}`);
|
|
141
|
+
}
|
|
142
|
+
fs.writeFileSync(file, updated);
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Command execution for the deploy pipeline.
|
|
3
|
+
*
|
|
4
|
+
* Two things drove this rewrite. The old helper used `exec` with a shell string
|
|
5
|
+
* built by concatenation, so any path containing a space silently produced the
|
|
6
|
+
* wrong command, and it buffered all output in memory - a Gradle build easily
|
|
7
|
+
* exceeds the default 1 MB `maxBuffer` and used to fail with a truncated,
|
|
8
|
+
* meaningless error. `spawn` with an argv array fixes both, and streams output
|
|
9
|
+
* to a log file as it arrives.
|
|
10
|
+
*/
|
|
11
|
+
export interface RunOptions {
|
|
12
|
+
cwd: string;
|
|
13
|
+
/** Extra environment for this command only. */
|
|
14
|
+
env?: Record<string, string>;
|
|
15
|
+
/** Append stdout/stderr here as they arrive. */
|
|
16
|
+
logFile?: string;
|
|
17
|
+
/** Mirror output to the terminal. */
|
|
18
|
+
verbose?: boolean;
|
|
19
|
+
/** Kill the command after this many milliseconds. */
|
|
20
|
+
timeoutMs?: number;
|
|
21
|
+
}
|
|
22
|
+
export interface RunResult {
|
|
23
|
+
code: number;
|
|
24
|
+
stdout: string;
|
|
25
|
+
stderr: string;
|
|
26
|
+
}
|
|
27
|
+
export declare class CommandError extends Error {
|
|
28
|
+
readonly command: string;
|
|
29
|
+
readonly code: number;
|
|
30
|
+
readonly stdout: string;
|
|
31
|
+
readonly stderr: string;
|
|
32
|
+
readonly logFile?: string;
|
|
33
|
+
constructor(command: string, result: RunResult, logFile?: string);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Runs a command, streaming its output to the log file.
|
|
37
|
+
*
|
|
38
|
+
* `file` and `args` are passed to the OS separately - there is no shell on
|
|
39
|
+
* POSIX, so nothing in `args` can be reinterpreted as shell syntax.
|
|
40
|
+
*/
|
|
41
|
+
export declare function run(file: string, args: string[], options: RunOptions): Promise<RunResult>;
|
|
42
|
+
/** Runs a command and returns false instead of throwing when it fails. */
|
|
43
|
+
export declare function tryRun(file: string, args: string[], options: RunOptions): Promise<boolean>;
|
|
44
|
+
//# sourceMappingURL=exec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AAEH,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,+CAA+C;IAC/C,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC7B,gDAAgD;IAChD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;gBAEd,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,MAAM;CAcjE;AAkDD;;;;;GAKG;AACH,wBAAsB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CA4D/F;AAED,0EAA0E;AAC1E,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAOhG"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
export class CommandError extends Error {
|
|
5
|
+
command;
|
|
6
|
+
code;
|
|
7
|
+
stdout;
|
|
8
|
+
stderr;
|
|
9
|
+
logFile;
|
|
10
|
+
constructor(command, result, logFile) {
|
|
11
|
+
// Put the last few lines of stderr in the message. The old code told the
|
|
12
|
+
// user to "see capuchoo-deploy.log" and nothing else, which meant every
|
|
13
|
+
// failure needed a second round trip to diagnose.
|
|
14
|
+
const tail = (result.stderr || result.stdout).trimEnd().split("\n").slice(-6).join("\n");
|
|
15
|
+
super(`${command} exited with ${result.code}${tail ? `\n${tail}` : ""}`);
|
|
16
|
+
this.name = "CommandError";
|
|
17
|
+
this.command = command;
|
|
18
|
+
this.code = result.code;
|
|
19
|
+
this.stdout = result.stdout;
|
|
20
|
+
this.stderr = result.stderr;
|
|
21
|
+
this.logFile = logFile;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function appendLog(logFile, chunk) {
|
|
25
|
+
if (!logFile)
|
|
26
|
+
return;
|
|
27
|
+
try {
|
|
28
|
+
fs.mkdirSync(path.dirname(logFile), { recursive: true });
|
|
29
|
+
fs.appendFileSync(logFile, chunk);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// Never fail a deploy because logging failed.
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Windows cannot execute a `.cmd` or `.bat` directly - and almost every
|
|
37
|
+
* `node_modules/.bin` entry is one - so those need `cmd.exe`.
|
|
38
|
+
*
|
|
39
|
+
* `spawn(..., { shell: true })` is the usual shortcut, but Node 26 deprecates
|
|
40
|
+
* passing an args array with it (DEP0190): the arguments are concatenated
|
|
41
|
+
* rather than escaped, so anything containing a space or a shell
|
|
42
|
+
* metacharacter changes the command. This builds the `cmd.exe` invocation
|
|
43
|
+
* explicitly and does its own quoting, which is both warning-free and
|
|
44
|
+
* predictable.
|
|
45
|
+
*
|
|
46
|
+
* Everything else - real executables, and every POSIX case - is spawned
|
|
47
|
+
* directly with no shell at all.
|
|
48
|
+
*/
|
|
49
|
+
function windowsSafeSpawn(file, args) {
|
|
50
|
+
const needsShell = process.platform === "win32" && /\.(cmd|bat)$/i.test(file);
|
|
51
|
+
if (!needsShell)
|
|
52
|
+
return { file, args, options: {} };
|
|
53
|
+
const command = [file, ...args].map(quoteForCmd).join(" ");
|
|
54
|
+
return {
|
|
55
|
+
file: process.env.ComSpec ?? "cmd.exe",
|
|
56
|
+
// /d skips AutoRun scripts, /s treats the rest as one command, /c runs it.
|
|
57
|
+
args: ["/d", "/s", "/c", `"${command}"`],
|
|
58
|
+
// We have already quoted; stop Node quoting again on top.
|
|
59
|
+
options: { windowsVerbatimArguments: true },
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function quoteForCmd(value) {
|
|
63
|
+
// Bare tokens are left alone so the command stays readable in the log.
|
|
64
|
+
if (/^[\w.:\\/=@-]+$/.test(value))
|
|
65
|
+
return value;
|
|
66
|
+
return `"${value.replaceAll('"', '""')}"`;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Runs a command, streaming its output to the log file.
|
|
70
|
+
*
|
|
71
|
+
* `file` and `args` are passed to the OS separately - there is no shell on
|
|
72
|
+
* POSIX, so nothing in `args` can be reinterpreted as shell syntax.
|
|
73
|
+
*/
|
|
74
|
+
export async function run(file, args, options) {
|
|
75
|
+
const printable = [file, ...args].join(" ");
|
|
76
|
+
appendLog(options.logFile, `\n$ ${printable}\n cwd: ${options.cwd}\n${"-".repeat(60)}\n`);
|
|
77
|
+
const spawned = windowsSafeSpawn(file, args);
|
|
78
|
+
return new Promise((resolve, reject) => {
|
|
79
|
+
const child = spawn(spawned.file, spawned.args, {
|
|
80
|
+
cwd: options.cwd,
|
|
81
|
+
env: { ...process.env, ...options.env },
|
|
82
|
+
windowsHide: true,
|
|
83
|
+
...spawned.options,
|
|
84
|
+
});
|
|
85
|
+
let stdout = "";
|
|
86
|
+
let stderr = "";
|
|
87
|
+
let timer;
|
|
88
|
+
if (options.timeoutMs) {
|
|
89
|
+
timer = setTimeout(() => {
|
|
90
|
+
child.kill();
|
|
91
|
+
reject(new Error(`${printable} did not finish within ${options.timeoutMs}ms and was stopped`));
|
|
92
|
+
}, options.timeoutMs);
|
|
93
|
+
}
|
|
94
|
+
child.stdout?.on("data", (data) => {
|
|
95
|
+
const text = data.toString();
|
|
96
|
+
stdout += text;
|
|
97
|
+
appendLog(options.logFile, text);
|
|
98
|
+
if (options.verbose)
|
|
99
|
+
process.stdout.write(text);
|
|
100
|
+
});
|
|
101
|
+
child.stderr?.on("data", (data) => {
|
|
102
|
+
const text = data.toString();
|
|
103
|
+
stderr += text;
|
|
104
|
+
appendLog(options.logFile, text);
|
|
105
|
+
if (options.verbose)
|
|
106
|
+
process.stderr.write(text);
|
|
107
|
+
});
|
|
108
|
+
child.on("error", (error) => {
|
|
109
|
+
if (timer)
|
|
110
|
+
clearTimeout(timer);
|
|
111
|
+
appendLog(options.logFile, `\nspawn failed: ${error.message}\n`);
|
|
112
|
+
reject(error);
|
|
113
|
+
});
|
|
114
|
+
child.on("close", (code) => {
|
|
115
|
+
if (timer)
|
|
116
|
+
clearTimeout(timer);
|
|
117
|
+
const result = { code: code ?? 1, stdout, stderr };
|
|
118
|
+
if (result.code !== 0) {
|
|
119
|
+
reject(new CommandError(printable, result, options.logFile));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
resolve(result);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
/** Runs a command and returns false instead of throwing when it fails. */
|
|
127
|
+
export async function tryRun(file, args, options) {
|
|
128
|
+
try {
|
|
129
|
+
await run(file, args, options);
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
catch {
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=exec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP for the CLI.
|
|
3
|
+
*
|
|
4
|
+
* Node 22 has `fetch`, `FormData` and `openAsBlob`, which between them cover
|
|
5
|
+
* everything the CLI was using `axios` and `form-data` for. `openAsBlob` in
|
|
6
|
+
* particular reads lazily, so a 60 MB APK is streamed rather than buffered -
|
|
7
|
+
* the previous `fs.createReadStream` plus `maxBodyLength: Infinity` combination
|
|
8
|
+
* worked, but only because the limit was disabled.
|
|
9
|
+
*/
|
|
10
|
+
export declare class HttpError extends Error {
|
|
11
|
+
readonly status: number;
|
|
12
|
+
readonly body: unknown;
|
|
13
|
+
constructor(message: string, status: number, body: unknown);
|
|
14
|
+
}
|
|
15
|
+
export interface HttpOptions {
|
|
16
|
+
endpoint: string;
|
|
17
|
+
apiKey: string;
|
|
18
|
+
timeoutMs?: number;
|
|
19
|
+
}
|
|
20
|
+
export declare function get<T>(pathname: string, options: HttpOptions): Promise<T>;
|
|
21
|
+
export declare function post<T>(pathname: string, body: unknown, options: HttpOptions): Promise<T>;
|
|
22
|
+
export interface UploadResult {
|
|
23
|
+
status: number;
|
|
24
|
+
body: unknown;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Uploads a build artefact as multipart form data.
|
|
28
|
+
*
|
|
29
|
+
* The upload timeout is separate and generous: a release APK on a slow
|
|
30
|
+
* connection legitimately takes minutes, and the 30 second default used for API
|
|
31
|
+
* calls would abort it.
|
|
32
|
+
*/
|
|
33
|
+
export declare function uploadArtifact(pathname: string, filePath: string, fields: Record<string, string>, options: HttpOptions & {
|
|
34
|
+
fileField?: string;
|
|
35
|
+
timeoutMs?: number;
|
|
36
|
+
}): Promise<UploadResult>;
|
|
37
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/utils/http.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AAEH,qBAAa,SAAU,SAAQ,KAAK;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;gBAEX,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO;CAM3D;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAwED,wBAAgB,GAAG,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAEzE;AAED,wBAAgB,IAAI,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAEzF;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;CACf;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC9B,OAAO,EAAE,WAAW,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAChE,OAAO,CAAC,YAAY,CAAC,CA8CvB"}
|