@mittwald/cli 1.4.4 → 1.5.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 +3 -0
- package/dist/commands/container/delete.d.ts +15 -0
- package/dist/commands/container/delete.js +39 -0
- package/dist/commands/container/list.d.ts +23 -0
- package/dist/commands/container/list.js +53 -0
- package/dist/commands/container/logs.d.ts +14 -0
- package/dist/commands/container/logs.js +47 -0
- package/dist/commands/container/recreate.d.ts +20 -0
- package/dist/commands/container/recreate.js +72 -0
- package/dist/commands/container/restart.d.ts +18 -0
- package/dist/commands/container/restart.js +40 -0
- package/dist/commands/container/run.d.ts +41 -0
- package/dist/commands/container/run.js +167 -0
- package/dist/commands/container/start.d.ts +18 -0
- package/dist/commands/container/start.js +40 -0
- package/dist/commands/container/stop.d.ts +18 -0
- package/dist/commands/container/stop.js +40 -0
- package/dist/commands/container/update.d.ts +36 -0
- package/dist/commands/container/update.js +174 -0
- package/dist/commands/context/set.d.ts +1 -0
- package/dist/commands/context/set.js +8 -0
- package/dist/commands/cronjob/execution/logs.js +6 -24
- package/dist/commands/cronjob/list.d.ts +2 -3
- package/dist/commands/domain/virtualhost/create.d.ts +1 -0
- package/dist/commands/domain/virtualhost/create.js +12 -0
- package/dist/commands/extension/install.js +4 -3
- package/dist/commands/extension/list-installed.js +4 -3
- package/dist/commands/mail/address/create.d.ts +2 -1
- package/dist/commands/mail/address/create.js +2 -1
- package/dist/commands/org/membership/list-own.d.ts +3 -0
- package/dist/commands/org/membership/list.d.ts +3 -0
- package/dist/commands/registry/create.d.ts +20 -0
- package/dist/commands/registry/create.js +77 -0
- package/dist/commands/registry/delete.d.ts +13 -0
- package/dist/commands/registry/delete.js +21 -0
- package/dist/commands/registry/list.d.ts +23 -0
- package/dist/commands/registry/list.js +33 -0
- package/dist/commands/registry/update.d.ts +20 -0
- package/dist/commands/registry/update.js +73 -0
- package/dist/commands/stack/delete.d.ts +16 -0
- package/dist/commands/stack/delete.js +54 -0
- package/dist/commands/stack/deploy.d.ts +18 -0
- package/dist/commands/stack/deploy.js +75 -0
- package/dist/commands/stack/list.d.ts +24 -0
- package/dist/commands/stack/list.js +60 -0
- package/dist/commands/stack/ps.d.ts +23 -0
- package/dist/commands/stack/ps.js +51 -0
- package/dist/lib/basecommands/DeleteBaseCommand.js +1 -1
- package/dist/lib/basecommands/ExtendedBaseCommand.d.ts +1 -0
- package/dist/lib/basecommands/ExtendedBaseCommand.js +4 -0
- package/dist/lib/context/Context.d.ts +5 -2
- package/dist/lib/context/Context.js +10 -1
- package/dist/lib/context/FlagSetBuilder.d.ts +3 -4
- package/dist/lib/context/FlagSetBuilder.js +22 -15
- package/dist/lib/resources/container/containerconfig.d.ts +43 -0
- package/dist/lib/resources/container/containerconfig.js +82 -0
- package/dist/lib/resources/container/flags.d.ts +13 -0
- package/dist/lib/resources/container/flags.js +34 -0
- package/dist/lib/resources/org/flags.js +7 -1
- package/dist/lib/resources/server/flags.js +7 -1
- package/dist/lib/resources/stack/enrich.d.ts +4 -0
- package/dist/lib/resources/stack/enrich.js +55 -0
- package/dist/lib/resources/stack/env.d.ts +1 -0
- package/dist/lib/resources/stack/env.js +11 -0
- package/dist/lib/resources/stack/flags.d.ts +5 -0
- package/dist/lib/resources/stack/flags.js +2 -0
- package/dist/lib/resources/stack/loader.d.ts +5 -0
- package/dist/lib/resources/stack/loader.js +47 -0
- package/dist/lib/resources/stack/loader.test.d.ts +1 -0
- package/dist/lib/resources/stack/loader.test.js +51 -0
- package/dist/lib/resources/stack/sanitize.d.ts +10 -0
- package/dist/lib/resources/stack/sanitize.js +43 -0
- package/dist/lib/resources/stack/types.d.ts +12 -0
- package/dist/lib/resources/stack/types.js +1 -0
- package/dist/lib/util/pager.d.ts +7 -0
- package/dist/lib/util/pager.js +21 -0
- package/dist/rendering/process/process.d.ts +1 -1
- package/dist/rendering/process/process_fancy.d.ts +1 -1
- package/dist/rendering/process/process_fancy.js +2 -1
- package/dist/rendering/process/process_flags.js +1 -1
- package/dist/rendering/process/process_quiet.d.ts +1 -1
- package/dist/rendering/process/process_quiet.js +3 -0
- package/package.json +21 -17
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { ExecRenderBaseCommand } from "../../lib/basecommands/ExecRenderBaseCommand.js";
|
|
3
|
+
type Result = {
|
|
4
|
+
serviceId: string;
|
|
5
|
+
};
|
|
6
|
+
export declare class Stop extends ExecRenderBaseCommand<typeof Stop, Result> {
|
|
7
|
+
static summary: string;
|
|
8
|
+
static flags: {
|
|
9
|
+
"project-id": import("@oclif/core/interfaces").OptionFlag<string>;
|
|
10
|
+
quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
|
+
};
|
|
12
|
+
static args: {
|
|
13
|
+
"container-id": import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
14
|
+
};
|
|
15
|
+
protected exec(): Promise<Result>;
|
|
16
|
+
protected render({ serviceId }: Result): ReactNode;
|
|
17
|
+
}
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Args } from "@oclif/core";
|
|
3
|
+
import { ExecRenderBaseCommand } from "../../lib/basecommands/ExecRenderBaseCommand.js";
|
|
4
|
+
import { makeProcessRenderer, processFlags, } from "../../rendering/process/process_flags.js";
|
|
5
|
+
import { projectFlags } from "../../lib/resources/project/flags.js";
|
|
6
|
+
import { withContainerAndStackId } from "../../lib/resources/container/flags.js";
|
|
7
|
+
import assertSuccess from "../../lib/apiutil/assert_success.js";
|
|
8
|
+
import { Success } from "../../rendering/react/components/Success.js";
|
|
9
|
+
import { Value } from "../../rendering/react/components/Value.js";
|
|
10
|
+
export class Stop extends ExecRenderBaseCommand {
|
|
11
|
+
static summary = "Stops a running container.";
|
|
12
|
+
static flags = {
|
|
13
|
+
...processFlags,
|
|
14
|
+
...projectFlags,
|
|
15
|
+
};
|
|
16
|
+
static args = {
|
|
17
|
+
"container-id": Args.string({
|
|
18
|
+
description: "ID or short ID of the container to stop",
|
|
19
|
+
required: true,
|
|
20
|
+
}),
|
|
21
|
+
};
|
|
22
|
+
async exec() {
|
|
23
|
+
const p = makeProcessRenderer(this.flags, "Stopping a container");
|
|
24
|
+
const [serviceId, stackId] = await withContainerAndStackId(this.apiClient, Stop, this.flags, this.args, this.config);
|
|
25
|
+
await p.runStep("stopping container", async () => {
|
|
26
|
+
const r = await this.apiClient.container.stopService({
|
|
27
|
+
serviceId,
|
|
28
|
+
stackId,
|
|
29
|
+
});
|
|
30
|
+
assertSuccess(r);
|
|
31
|
+
});
|
|
32
|
+
await p.complete(_jsxs(Success, { children: ["Container ", _jsx(Value, { children: serviceId }), " was successfully stopped."] }));
|
|
33
|
+
return { serviceId };
|
|
34
|
+
}
|
|
35
|
+
render({ serviceId }) {
|
|
36
|
+
if (this.flags.quiet) {
|
|
37
|
+
return serviceId;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ReactNode } from "react";
|
|
2
|
+
import { ExecRenderBaseCommand } from "../../lib/basecommands/ExecRenderBaseCommand.js";
|
|
3
|
+
type Result = {
|
|
4
|
+
serviceId: string;
|
|
5
|
+
};
|
|
6
|
+
export declare class Update extends ExecRenderBaseCommand<typeof Update, Result> {
|
|
7
|
+
static summary: string;
|
|
8
|
+
static description: string;
|
|
9
|
+
static flags: {
|
|
10
|
+
image: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
env: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
"env-file": import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
14
|
+
entrypoint: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
command: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
publish: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
"publish-all": import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
18
|
+
volume: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
|
+
recreate: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
20
|
+
"project-id": import("@oclif/core/interfaces").OptionFlag<string>;
|
|
21
|
+
quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
22
|
+
};
|
|
23
|
+
static args: {
|
|
24
|
+
"container-id": import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
25
|
+
};
|
|
26
|
+
protected exec(): Promise<Result>;
|
|
27
|
+
/**
|
|
28
|
+
* Builds a container service update request from command line flags
|
|
29
|
+
*
|
|
30
|
+
* @returns A properly formatted container service request with only the
|
|
31
|
+
* fields to update
|
|
32
|
+
*/
|
|
33
|
+
private buildUpdateRequest;
|
|
34
|
+
protected render({ serviceId }: Result): ReactNode;
|
|
35
|
+
}
|
|
36
|
+
export {};
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Args, Flags } from "@oclif/core";
|
|
3
|
+
import { ExecRenderBaseCommand } from "../../lib/basecommands/ExecRenderBaseCommand.js";
|
|
4
|
+
import { makeProcessRenderer, processFlags, } from "../../rendering/process/process_flags.js";
|
|
5
|
+
import { projectFlags } from "../../lib/resources/project/flags.js";
|
|
6
|
+
import { withContainerAndStackId } from "../../lib/resources/container/flags.js";
|
|
7
|
+
import assertSuccess from "../../lib/apiutil/assert_success.js";
|
|
8
|
+
import { Success } from "../../rendering/react/components/Success.js";
|
|
9
|
+
import { Value } from "../../rendering/react/components/Value.js";
|
|
10
|
+
import { assertStatus } from "@mittwald/api-client";
|
|
11
|
+
import { parseEnvironmentVariables, getPortMappings, getImageMeta, } from "../../lib/resources/container/containerconfig.js";
|
|
12
|
+
export class Update extends ExecRenderBaseCommand {
|
|
13
|
+
static summary = "Updates an existing container.";
|
|
14
|
+
static description = "Updates attributes of an existing container such as image, environment variables, etc.";
|
|
15
|
+
static flags = {
|
|
16
|
+
...processFlags,
|
|
17
|
+
...projectFlags,
|
|
18
|
+
image: Flags.string({
|
|
19
|
+
summary: "update the container image",
|
|
20
|
+
description: "Specify a new image to use for the container.",
|
|
21
|
+
required: false,
|
|
22
|
+
}),
|
|
23
|
+
env: Flags.string({
|
|
24
|
+
summary: "set environment variables in the container",
|
|
25
|
+
description: "Format: KEY=VALUE. Multiple environment variables can be specified with multiple --env flags.",
|
|
26
|
+
required: false,
|
|
27
|
+
multiple: true,
|
|
28
|
+
char: "e",
|
|
29
|
+
}),
|
|
30
|
+
"env-file": Flags.string({
|
|
31
|
+
summary: "read environment variables from a file",
|
|
32
|
+
description: "The file should contain lines in the format KEY=VALUE. Multiple files can be specified with multiple --env-file flags.",
|
|
33
|
+
multiple: true,
|
|
34
|
+
required: false,
|
|
35
|
+
}),
|
|
36
|
+
description: Flags.string({
|
|
37
|
+
summary: "update the descriptive label of the container",
|
|
38
|
+
description: "This helps identify the container's purpose or contents.",
|
|
39
|
+
required: false,
|
|
40
|
+
}),
|
|
41
|
+
entrypoint: Flags.string({
|
|
42
|
+
summary: "override the entrypoint of the container",
|
|
43
|
+
description: "The entrypoint is the command that will be executed when the container starts.",
|
|
44
|
+
required: false,
|
|
45
|
+
}),
|
|
46
|
+
command: Flags.string({
|
|
47
|
+
summary: "update the command to run in the container",
|
|
48
|
+
description: "This overrides the default command specified in the container image.",
|
|
49
|
+
required: false,
|
|
50
|
+
}),
|
|
51
|
+
publish: Flags.string({
|
|
52
|
+
summary: "update the container's port mappings",
|
|
53
|
+
description: "Map a container's port to a port on the host system. " +
|
|
54
|
+
"Format: <host-port>:<container-port> or just <container-port> (in which case the host port will be automatically assigned). " +
|
|
55
|
+
"Use multiple -p flags to publish multiple ports.",
|
|
56
|
+
required: false,
|
|
57
|
+
multiple: true,
|
|
58
|
+
char: "p",
|
|
59
|
+
}),
|
|
60
|
+
"publish-all": Flags.boolean({
|
|
61
|
+
summary: "publish all ports that are defined in the image",
|
|
62
|
+
description: "Automatically publish all ports that are exposed by the container image to random ports on the host.",
|
|
63
|
+
required: false,
|
|
64
|
+
char: "P",
|
|
65
|
+
}),
|
|
66
|
+
volume: Flags.string({
|
|
67
|
+
summary: "update volume mounts for the container",
|
|
68
|
+
description: "This flag can be used to add volume mounts to the container. It can be used multiple times to mount multiple volumes." +
|
|
69
|
+
"" +
|
|
70
|
+
"Needs to be in the format <host-path>:<container-path>. " +
|
|
71
|
+
"" +
|
|
72
|
+
"If you specify a file path as volume, this will mount a path from your hosting environment's file system (NOT your local file system) into the container. " +
|
|
73
|
+
"You can also specify a named volume, which needs to be created beforehand.",
|
|
74
|
+
required: false,
|
|
75
|
+
char: "v",
|
|
76
|
+
multiple: true,
|
|
77
|
+
}),
|
|
78
|
+
recreate: Flags.boolean({
|
|
79
|
+
summary: "recreate the container after updating",
|
|
80
|
+
description: "If set, the container will be automatically recreated after updating its configuration.",
|
|
81
|
+
required: false,
|
|
82
|
+
default: false,
|
|
83
|
+
}),
|
|
84
|
+
};
|
|
85
|
+
static args = {
|
|
86
|
+
"container-id": Args.string({
|
|
87
|
+
description: "ID or short ID of the container to update",
|
|
88
|
+
required: true,
|
|
89
|
+
}),
|
|
90
|
+
};
|
|
91
|
+
async exec() {
|
|
92
|
+
const p = makeProcessRenderer(this.flags, "Updating container");
|
|
93
|
+
const [serviceId, stackId] = await withContainerAndStackId(this.apiClient, Update, this.flags, this.args, this.config);
|
|
94
|
+
const service = await p.runStep("getting container configuration", async () => {
|
|
95
|
+
const r = await this.apiClient.container.getService({
|
|
96
|
+
serviceId,
|
|
97
|
+
stackId,
|
|
98
|
+
});
|
|
99
|
+
assertStatus(r, 200);
|
|
100
|
+
return r.data;
|
|
101
|
+
});
|
|
102
|
+
const updatePayload = await p.runStep("preparing update request", this.buildUpdateRequest());
|
|
103
|
+
if (Object.keys(updatePayload).length === 0) {
|
|
104
|
+
await p.complete(_jsx(Success, { children: "Nothing to change. Have a good day!" }));
|
|
105
|
+
return { serviceId };
|
|
106
|
+
}
|
|
107
|
+
await p.runStep("updating container configuration", async () => {
|
|
108
|
+
const r = await this.apiClient.container.updateStack({
|
|
109
|
+
stackId,
|
|
110
|
+
data: {
|
|
111
|
+
services: {
|
|
112
|
+
[service.serviceName]: updatePayload,
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
assertStatus(r, 200);
|
|
117
|
+
});
|
|
118
|
+
// Recreate the container if requested
|
|
119
|
+
if (this.flags.recreate) {
|
|
120
|
+
await p.runStep("recreating container", async () => {
|
|
121
|
+
const r = await this.apiClient.container.recreateService({
|
|
122
|
+
serviceId,
|
|
123
|
+
stackId,
|
|
124
|
+
});
|
|
125
|
+
assertSuccess(r);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
await p.complete(_jsxs(Success, { children: ["Container ", _jsx(Value, { children: serviceId }), " was successfully updated.", this.flags.recreate &&
|
|
129
|
+
" The container was recreated with the new configuration."] }));
|
|
130
|
+
return { serviceId };
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Builds a container service update request from command line flags
|
|
134
|
+
*
|
|
135
|
+
* @returns A properly formatted container service request with only the
|
|
136
|
+
* fields to update
|
|
137
|
+
*/
|
|
138
|
+
async buildUpdateRequest() {
|
|
139
|
+
const updateRequest = {};
|
|
140
|
+
if (this.flags.image) {
|
|
141
|
+
updateRequest.image = this.flags.image;
|
|
142
|
+
// Get image metadata for port mappings if publish-all is specified
|
|
143
|
+
if (this.flags["publish-all"]) {
|
|
144
|
+
const projectId = await this.withProjectId(Update);
|
|
145
|
+
const imageMeta = await getImageMeta(this.apiClient, this.flags.image, projectId);
|
|
146
|
+
updateRequest.ports = getPortMappings(imageMeta, true);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (this.flags.command) {
|
|
150
|
+
updateRequest.command = [this.flags.command];
|
|
151
|
+
}
|
|
152
|
+
if (this.flags.entrypoint) {
|
|
153
|
+
updateRequest.entrypoint = [this.flags.entrypoint];
|
|
154
|
+
}
|
|
155
|
+
if (this.flags.description) {
|
|
156
|
+
updateRequest.description = this.flags.description;
|
|
157
|
+
}
|
|
158
|
+
if (this.flags.env || this.flags["env-file"]) {
|
|
159
|
+
updateRequest.envs = await parseEnvironmentVariables(this.flags.env, this.flags["env-file"]);
|
|
160
|
+
}
|
|
161
|
+
if (this.flags.publish) {
|
|
162
|
+
updateRequest.ports = this.flags.publish;
|
|
163
|
+
}
|
|
164
|
+
if (this.flags.volume) {
|
|
165
|
+
updateRequest.volumes = this.flags.volume;
|
|
166
|
+
}
|
|
167
|
+
return updateRequest;
|
|
168
|
+
}
|
|
169
|
+
render({ serviceId }) {
|
|
170
|
+
if (this.flags.quiet) {
|
|
171
|
+
return serviceId;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -7,6 +7,7 @@ export declare class Set extends BaseCommand {
|
|
|
7
7
|
"server-id": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
8
|
"org-id": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
9
9
|
"installation-id": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
"stack-id": import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
11
|
};
|
|
11
12
|
run(): Promise<void>;
|
|
12
13
|
}
|
|
@@ -18,6 +18,10 @@ export class Set extends BaseCommand {
|
|
|
18
18
|
description: "ID or short ID of an app installation",
|
|
19
19
|
aliases: ["app-id", "app-installation-id"],
|
|
20
20
|
}),
|
|
21
|
+
"stack-id": Flags.string({
|
|
22
|
+
description: "ID of a container stack",
|
|
23
|
+
aliases: ["container-stack-id"],
|
|
24
|
+
}),
|
|
21
25
|
};
|
|
22
26
|
async run() {
|
|
23
27
|
const { flags } = await this.parse(Set);
|
|
@@ -40,5 +44,9 @@ export class Set extends BaseCommand {
|
|
|
40
44
|
const installationId = await ctx.setAppInstallationId(flags["installation-id"]);
|
|
41
45
|
this.log(`Set installation ID to ${installationId}`);
|
|
42
46
|
}
|
|
47
|
+
if (flags["stack-id"]) {
|
|
48
|
+
const stackId = await ctx.setStackId(flags["stack-id"]);
|
|
49
|
+
this.log(`Set stack ID to ${stackId}`);
|
|
50
|
+
}
|
|
43
51
|
}
|
|
44
52
|
}
|
|
@@ -2,9 +2,7 @@ import { GetBaseCommand } from "../../../lib/basecommands/GetBaseCommand.js";
|
|
|
2
2
|
import { Args, Flags } from "@oclif/core";
|
|
3
3
|
import { BaseCommand } from "../../../lib/basecommands/BaseCommand.js";
|
|
4
4
|
import { assertStatus } from "@mittwald/api-client-commons";
|
|
5
|
-
import
|
|
6
|
-
import * as fs from "fs";
|
|
7
|
-
import tempfile from "tempfile";
|
|
5
|
+
import { printToPager } from "../../../lib/util/pager.js";
|
|
8
6
|
export class Logs extends BaseCommand {
|
|
9
7
|
static summary = "Get the log output of a cronjob execution.";
|
|
10
8
|
static description = "This command prints the log output of a cronjob execution. " +
|
|
@@ -46,29 +44,13 @@ export class Logs extends BaseCommand {
|
|
|
46
44
|
if (!projectId) {
|
|
47
45
|
throw new Error("Cronjob has no project ID");
|
|
48
46
|
}
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
params: {
|
|
53
|
-
file: execution.data.logPath,
|
|
54
|
-
inline: "true",
|
|
55
|
-
},
|
|
47
|
+
const response = await this.apiClient.projectFileSystem.getFileContent({
|
|
48
|
+
projectId,
|
|
49
|
+
queryParameters: { file: execution.data.logPath, inline: true },
|
|
56
50
|
});
|
|
57
|
-
|
|
58
|
-
// projectId ,
|
|
59
|
-
// queryParameters: { file: execution.data.logPath, inline: true },
|
|
60
|
-
// });
|
|
51
|
+
assertStatus(response, 200);
|
|
61
52
|
if (usePager) {
|
|
62
|
-
|
|
63
|
-
try {
|
|
64
|
-
fs.writeFileSync(t, response.data);
|
|
65
|
-
cp.spawnSync(process.env.PAGER || "less", [t], {
|
|
66
|
-
stdio: "inherit",
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
finally {
|
|
70
|
-
fs.unlinkSync(t);
|
|
71
|
-
}
|
|
53
|
+
printToPager(response.data);
|
|
72
54
|
}
|
|
73
55
|
else {
|
|
74
56
|
this.log(response.data);
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import { Simplify } from "@mittwald/api-client-commons";
|
|
2
2
|
import { MittwaldAPIV2, MittwaldAPIV2Client } from "@mittwald/api-client";
|
|
3
3
|
import { ListBaseCommand } from "../../lib/basecommands/ListBaseCommand.js";
|
|
4
|
-
import { ListColumns } from "../../rendering/formatter/
|
|
4
|
+
import { ListColumns } from "../../rendering/formatter/Table.js";
|
|
5
5
|
type ResponseItem = Simplify<MittwaldAPIV2.Paths.V2ProjectsProjectIdCronjobs.Get.Responses.$200.Content.ApplicationJson[number]>;
|
|
6
|
-
|
|
7
|
-
export type Response = Awaited<ReturnType<MittwaldAPIV2Client["cronjob"]["listCronjobs"]>>;
|
|
6
|
+
type Response = Awaited<ReturnType<MittwaldAPIV2Client["cronjob"]["listCronjobs"]>>;
|
|
8
7
|
export declare class List extends ListBaseCommand<typeof List, ResponseItem, Response> {
|
|
9
8
|
static description: string;
|
|
10
9
|
static aliases: string[];
|
|
@@ -15,6 +15,7 @@ export default class Create extends ExecRenderBaseCommand<typeof Create, CreateR
|
|
|
15
15
|
hostname: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
16
|
"path-to-app": import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
17
|
"path-to-url": import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
|
+
"path-to-container": import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
19
|
"project-id": import("@oclif/core/interfaces").OptionFlag<string>;
|
|
19
20
|
quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
20
21
|
};
|
|
@@ -42,6 +42,11 @@ export default class Create extends ExecRenderBaseCommand {
|
|
|
42
42
|
multiple: true,
|
|
43
43
|
description: "This flag can be used to map a specific URL path to an external URL; the value for this flag should be the URL path and the external URL, separated by a colon, e.g. /:https://redirect.example. You can specify this flag multiple times to map multiple paths to different external URLs, and also combine it with the other --path-to-* flags.",
|
|
44
44
|
}),
|
|
45
|
+
"path-to-container": Flags.string({
|
|
46
|
+
summary: "add a path mapping to a container",
|
|
47
|
+
multiple: true,
|
|
48
|
+
description: "This flag can be used to map a specific URL path to a container; the value for this flag should be the URL path, the container ID and the target port, each separated by a colon, e.g. /:3ecaf1a9-6eb4-4869-b811-8a13c3a2e745:80/tcp. You can specify this flag multiple times to map multiple paths to different containers, and also combine it with the other --path-to-* flags.",
|
|
49
|
+
}),
|
|
45
50
|
};
|
|
46
51
|
async exec() {
|
|
47
52
|
const projectId = await this.withProjectId(Create);
|
|
@@ -56,6 +61,13 @@ export default class Create extends ExecRenderBaseCommand {
|
|
|
56
61
|
const [path, url] = pathToUrl.split(":");
|
|
57
62
|
paths.push({ path, target: { url } });
|
|
58
63
|
}
|
|
64
|
+
for (const pathToContainer of this.flags["path-to-container"] ?? []) {
|
|
65
|
+
const [path, container, portProtocol] = pathToContainer.split(":", 3);
|
|
66
|
+
paths.push({
|
|
67
|
+
path,
|
|
68
|
+
target: { container: { id: container, portProtocol } },
|
|
69
|
+
});
|
|
70
|
+
}
|
|
59
71
|
const { id: ingressId } = await process.runStep("creating ingress", async () => {
|
|
60
72
|
const response = await this.apiClient.domain.ingressCreateIngress({
|
|
61
73
|
data: {
|
|
@@ -4,7 +4,7 @@ import { makeProcessRenderer, processFlags, } from "../../rendering/process/proc
|
|
|
4
4
|
import { Args, Flags } from "@oclif/core";
|
|
5
5
|
import { assertStatus } from "@mittwald/api-client";
|
|
6
6
|
import { Text } from "ink";
|
|
7
|
-
import { contextIDNormalizers } from "../../lib/context/Context.js";
|
|
7
|
+
import Context, { contextIDNormalizers } from "../../lib/context/Context.js";
|
|
8
8
|
export default class Install extends ExecRenderBaseCommand {
|
|
9
9
|
static description = "Install an extension in a project or organization";
|
|
10
10
|
static flags = {
|
|
@@ -39,13 +39,14 @@ export default class Install extends ExecRenderBaseCommand {
|
|
|
39
39
|
assertStatus(response, 200);
|
|
40
40
|
return response.data;
|
|
41
41
|
});
|
|
42
|
+
const ctx = new Context(this.apiClient, this.config);
|
|
42
43
|
if (orgId !== undefined) {
|
|
43
44
|
const normalizer = contextIDNormalizers["org-id"];
|
|
44
|
-
orgId = await normalizer(this.apiClient, orgId);
|
|
45
|
+
orgId = await normalizer(this.apiClient, orgId, ctx);
|
|
45
46
|
}
|
|
46
47
|
if (projectId !== undefined) {
|
|
47
48
|
const normalizer = contextIDNormalizers["project-id"];
|
|
48
|
-
projectId = await normalizer(this.apiClient, projectId);
|
|
49
|
+
projectId = await normalizer(this.apiClient, projectId, ctx);
|
|
49
50
|
}
|
|
50
51
|
if (!consent) {
|
|
51
52
|
p.addInfo(_jsxs(Text, { children: ["This extension requires access to the following scopes:", " ", ext.scopes.join(", "), ". Please confirm your consent, or run the command with the --consent flag."] }));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { assertStatus } from "@mittwald/api-client-commons";
|
|
2
2
|
import { ListBaseCommand } from "../../lib/basecommands/ListBaseCommand.js";
|
|
3
3
|
import { Flags } from "@oclif/core";
|
|
4
|
-
import { contextIDNormalizers } from "../../lib/context/Context.js";
|
|
4
|
+
import Context, { contextIDNormalizers } from "../../lib/context/Context.js";
|
|
5
5
|
export class ListInstalled extends ListBaseCommand {
|
|
6
6
|
static description = "List installed extensions in an organization or project.";
|
|
7
7
|
static args = {};
|
|
@@ -18,13 +18,14 @@ export class ListInstalled extends ListBaseCommand {
|
|
|
18
18
|
};
|
|
19
19
|
async getData() {
|
|
20
20
|
let { "org-id": orgId, "project-id": projectId } = this.flags;
|
|
21
|
+
const ctx = new Context(this.apiClient, this.config);
|
|
21
22
|
if (orgId) {
|
|
22
23
|
const normalizer = contextIDNormalizers["org-id"];
|
|
23
|
-
orgId = await normalizer(this.apiClient, orgId);
|
|
24
|
+
orgId = await normalizer(this.apiClient, orgId, ctx);
|
|
24
25
|
}
|
|
25
26
|
if (projectId) {
|
|
26
27
|
const normalizer = contextIDNormalizers["project-id"];
|
|
27
|
-
projectId = await normalizer(this.apiClient, projectId);
|
|
28
|
+
projectId = await normalizer(this.apiClient, projectId, ctx);
|
|
28
29
|
}
|
|
29
30
|
return await this.apiClient.marketplace.extensionListExtensionInstances({
|
|
30
31
|
queryParameters: {
|
|
@@ -3,6 +3,7 @@ import { ReactNode } from "react";
|
|
|
3
3
|
import { ProcessRenderer } from "../../../rendering/process/process.js";
|
|
4
4
|
import { FlagInput, OutputFlags } from "@oclif/core/interfaces";
|
|
5
5
|
import ByteQuantity from "../../../lib/units/ByteQuantity.js";
|
|
6
|
+
import { CommandFlags } from "../../../lib/basecommands/CommandFlags.js";
|
|
6
7
|
type CreateResult = {
|
|
7
8
|
addressId: string;
|
|
8
9
|
generatedPassword: string | null;
|
|
@@ -27,7 +28,7 @@ export default class Create extends ExecRenderBaseCommand<typeof Create, CreateR
|
|
|
27
28
|
}[];
|
|
28
29
|
protected getPassword(process: ProcessRenderer): Promise<[string, boolean]>;
|
|
29
30
|
protected createForwardAddress(projectId: string, process: ProcessRenderer, flags: OutputFlags<FlagInput<typeof Create.flags>>): Promise<CreateResult>;
|
|
30
|
-
protected createMailAddress(projectId: string, process: ProcessRenderer, flags:
|
|
31
|
+
protected createMailAddress(projectId: string, process: ProcessRenderer, flags: CommandFlags<typeof Create>): Promise<CreateResult>;
|
|
31
32
|
protected exec(): Promise<CreateResult>;
|
|
32
33
|
protected render(executionResult: CreateResult): ReactNode;
|
|
33
34
|
}
|
|
@@ -116,6 +116,7 @@ export default class Create extends ExecRenderBaseCommand {
|
|
|
116
116
|
}
|
|
117
117
|
async createMailAddress(projectId, process, flags) {
|
|
118
118
|
const [password, passwordGenerated] = await this.getPassword(process);
|
|
119
|
+
const { quota } = flags;
|
|
119
120
|
const response = await process.runStep("creating mail address", async () => {
|
|
120
121
|
const response = await this.apiClient.mail.createMailAddress({
|
|
121
122
|
projectId,
|
|
@@ -124,7 +125,7 @@ export default class Create extends ExecRenderBaseCommand {
|
|
|
124
125
|
isCatchAll: flags["catch-all"],
|
|
125
126
|
mailbox: {
|
|
126
127
|
password,
|
|
127
|
-
quotaInBytes:
|
|
128
|
+
quotaInBytes: quota.bytes,
|
|
128
129
|
enableSpamProtection: flags["enable-spam-protection"],
|
|
129
130
|
},
|
|
130
131
|
},
|
|
@@ -45,11 +45,14 @@ export declare class ListOwn extends ListBaseCommand<typeof ListOwn, ResponseIte
|
|
|
45
45
|
vatId?: string | undefined;
|
|
46
46
|
vatIdValidationState?: "valid" | "invalid" | "pending" | "unspecified" | undefined;
|
|
47
47
|
};
|
|
48
|
+
avatarRef?: string;
|
|
48
49
|
customerId: string;
|
|
49
50
|
email: string;
|
|
50
51
|
expiresAt?: string;
|
|
52
|
+
firstName: string;
|
|
51
53
|
id: string;
|
|
52
54
|
inviteId?: string;
|
|
55
|
+
lastName: string;
|
|
53
56
|
memberSince?: string;
|
|
54
57
|
mfa: boolean;
|
|
55
58
|
role: MittwaldAPIV2.Components.Schemas.MembershipCustomerRoles;
|
|
@@ -48,11 +48,14 @@ export declare class List extends ListBaseCommand<typeof List, ResponseItem, Res
|
|
|
48
48
|
registeredAt?: string | undefined;
|
|
49
49
|
userId: string;
|
|
50
50
|
};
|
|
51
|
+
avatarRef?: string;
|
|
51
52
|
customerId: string;
|
|
52
53
|
email: string;
|
|
53
54
|
expiresAt?: string;
|
|
55
|
+
firstName: string;
|
|
54
56
|
id: string;
|
|
55
57
|
inviteId?: string;
|
|
58
|
+
lastName: string;
|
|
56
59
|
memberSince?: string;
|
|
57
60
|
mfa: boolean;
|
|
58
61
|
role: MittwaldAPIV2.Components.Schemas.MembershipCustomerRoles;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ExecRenderBaseCommand } from "../../lib/basecommands/ExecRenderBaseCommand.js";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
type Result = {
|
|
4
|
+
registryId: string;
|
|
5
|
+
};
|
|
6
|
+
export declare class Create extends ExecRenderBaseCommand<typeof Create, Result> {
|
|
7
|
+
static summary: string;
|
|
8
|
+
static flags: {
|
|
9
|
+
uri: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
10
|
+
description: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
username: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
password: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
13
|
+
quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
"project-id": import("@oclif/core/interfaces").OptionFlag<string>;
|
|
15
|
+
};
|
|
16
|
+
protected exec(): Promise<Result>;
|
|
17
|
+
protected render({ registryId }: Result): ReactNode;
|
|
18
|
+
private getPassword;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ExecRenderBaseCommand } from "../../lib/basecommands/ExecRenderBaseCommand.js";
|
|
3
|
+
import { makeProcessRenderer, processFlags, } from "../../rendering/process/process_flags.js";
|
|
4
|
+
import { assertStatus } from "@mittwald/api-client-commons";
|
|
5
|
+
import { Success } from "../../rendering/react/components/Success.js";
|
|
6
|
+
import { Value } from "../../rendering/react/components/Value.js";
|
|
7
|
+
import { projectFlags } from "../../lib/resources/project/flags.js";
|
|
8
|
+
import { Flags } from "@oclif/core";
|
|
9
|
+
import { Text } from "ink";
|
|
10
|
+
export class Create extends ExecRenderBaseCommand {
|
|
11
|
+
static summary = "Create a new container registry";
|
|
12
|
+
static flags = {
|
|
13
|
+
...projectFlags,
|
|
14
|
+
...processFlags,
|
|
15
|
+
uri: Flags.string({
|
|
16
|
+
summary: "uri of the registry",
|
|
17
|
+
required: true,
|
|
18
|
+
}),
|
|
19
|
+
description: Flags.string({
|
|
20
|
+
summary: "description of the registry",
|
|
21
|
+
required: true,
|
|
22
|
+
}),
|
|
23
|
+
username: Flags.string({
|
|
24
|
+
summary: "username for registry authentication",
|
|
25
|
+
required: false,
|
|
26
|
+
}),
|
|
27
|
+
password: Flags.string({
|
|
28
|
+
summary: "password for registry authentication",
|
|
29
|
+
required: false,
|
|
30
|
+
description: "If omitted but username is provided, the command will prompt interactively for a password.\n\nCAUTION: providing this flag may log your password in your shell history!",
|
|
31
|
+
}),
|
|
32
|
+
};
|
|
33
|
+
async exec() {
|
|
34
|
+
const process = makeProcessRenderer(this.flags, "Creating a new container registry");
|
|
35
|
+
const projectId = await this.withProjectId(Create);
|
|
36
|
+
const { uri, description, username } = this.flags;
|
|
37
|
+
const registryCreationPayload = {
|
|
38
|
+
uri,
|
|
39
|
+
description,
|
|
40
|
+
};
|
|
41
|
+
if (username) {
|
|
42
|
+
const password = await this.getPassword(process);
|
|
43
|
+
registryCreationPayload.credentials = {
|
|
44
|
+
username,
|
|
45
|
+
password,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const { id: registryId } = await process.runStep("creating container registry", async () => {
|
|
49
|
+
const r = await this.apiClient.container.createRegistry({
|
|
50
|
+
projectId,
|
|
51
|
+
data: registryCreationPayload,
|
|
52
|
+
});
|
|
53
|
+
assertStatus(r, 201);
|
|
54
|
+
return r.data;
|
|
55
|
+
});
|
|
56
|
+
const registry = await process.runStep("checking newly created container registry", async () => {
|
|
57
|
+
const r = await this.apiClient.container.getRegistry({
|
|
58
|
+
registryId,
|
|
59
|
+
});
|
|
60
|
+
assertStatus(r, 200);
|
|
61
|
+
return r.data;
|
|
62
|
+
});
|
|
63
|
+
await process.complete(_jsxs(Success, { children: ["The container registry \"", _jsx(Value, { children: registry.description }), "\" at", " ", _jsx(Value, { children: registry.uri }), " was successfully created."] }));
|
|
64
|
+
return { registryId };
|
|
65
|
+
}
|
|
66
|
+
render({ registryId }) {
|
|
67
|
+
if (this.flags.quiet) {
|
|
68
|
+
return registryId;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async getPassword(process) {
|
|
72
|
+
if (this.flags.password) {
|
|
73
|
+
return this.flags.password;
|
|
74
|
+
}
|
|
75
|
+
return await process.addInput(_jsx(Text, { children: "enter registry password" }), true);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { DeleteBaseCommand } from "../../lib/basecommands/DeleteBaseCommand.js";
|
|
2
|
+
export default class Delete extends DeleteBaseCommand<typeof Delete> {
|
|
3
|
+
static description: string;
|
|
4
|
+
static resourceName: string;
|
|
5
|
+
static flags: {
|
|
6
|
+
force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
quiet: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
};
|
|
9
|
+
static args: {
|
|
10
|
+
"registry-id": import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
11
|
+
};
|
|
12
|
+
protected deleteResource(): Promise<void>;
|
|
13
|
+
}
|