@mittwald/cli 1.5.0 → 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/dist/commands/container/run.d.ts +0 -16
- package/dist/commands/container/run.js +4 -61
- package/dist/commands/container/update.d.ts +36 -0
- package/dist/commands/container/update.js +174 -0
- 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/lib/resources/container/containerconfig.d.ts +43 -0
- package/dist/lib/resources/container/containerconfig.js +82 -0
- package/package.json +1 -1
|
@@ -34,23 +34,7 @@ export declare class Run extends ExecRenderBaseCommand<typeof Run, Result> {
|
|
|
34
34
|
* @returns A properly formatted container service request
|
|
35
35
|
*/
|
|
36
36
|
private buildServiceRequest;
|
|
37
|
-
/**
|
|
38
|
-
* Parses environment variables from command line flags and env files
|
|
39
|
-
*
|
|
40
|
-
* @returns An object containing environment variable key-value pairs
|
|
41
|
-
*/
|
|
42
|
-
private parseEnvironmentVariables;
|
|
43
|
-
private parseEnvironmentVariablesFromFile;
|
|
44
|
-
private parseEnvironmentVariablesFromEnvFlags;
|
|
45
|
-
/**
|
|
46
|
-
* Determines which ports to expose based on flags and image metadata
|
|
47
|
-
*
|
|
48
|
-
* @param imageMeta Metadata about the container image
|
|
49
|
-
* @returns An array of port mappings
|
|
50
|
-
*/
|
|
51
|
-
private getPortMappings;
|
|
52
37
|
private getImageAndMeta;
|
|
53
|
-
private getImageMeta;
|
|
54
38
|
private getServiceName;
|
|
55
39
|
protected render({ serviceId }: Result): ReactNode;
|
|
56
40
|
}
|
|
@@ -7,9 +7,7 @@ import { Success } from "../../rendering/react/components/Success.js";
|
|
|
7
7
|
import { Value } from "../../rendering/react/components/Value.js";
|
|
8
8
|
import * as dockerNames from "docker-names";
|
|
9
9
|
import { assertStatus } from "@mittwald/api-client";
|
|
10
|
-
import
|
|
11
|
-
import { parse } from "envfile";
|
|
12
|
-
import { pathExists } from "../../lib/util/fs/pathExists.js";
|
|
10
|
+
import { parseEnvironmentVariables, getPortMappings, getImageMeta, } from "../../lib/resources/container/containerconfig.js";
|
|
13
11
|
export class Run extends ExecRenderBaseCommand {
|
|
14
12
|
static summary = "Creates and starts a new container.";
|
|
15
13
|
static flags = {
|
|
@@ -133,8 +131,8 @@ export class Run extends ExecRenderBaseCommand {
|
|
|
133
131
|
? [this.flags.entrypoint]
|
|
134
132
|
: imageMeta.entrypoint;
|
|
135
133
|
const description = this.flags.description ?? serviceName;
|
|
136
|
-
const envs = await this.
|
|
137
|
-
const ports =
|
|
134
|
+
const envs = await parseEnvironmentVariables(this.flags.env, this.flags["env-file"]);
|
|
135
|
+
const ports = getPortMappings(imageMeta, this.flags["publish-all"], this.flags.publish);
|
|
138
136
|
const volumes = this.flags.volume;
|
|
139
137
|
return {
|
|
140
138
|
image,
|
|
@@ -146,66 +144,11 @@ export class Run extends ExecRenderBaseCommand {
|
|
|
146
144
|
volumes,
|
|
147
145
|
};
|
|
148
146
|
}
|
|
149
|
-
/**
|
|
150
|
-
* Parses environment variables from command line flags and env files
|
|
151
|
-
*
|
|
152
|
-
* @returns An object containing environment variable key-value pairs
|
|
153
|
-
*/
|
|
154
|
-
async parseEnvironmentVariables() {
|
|
155
|
-
return {
|
|
156
|
-
...this.parseEnvironmentVariablesFromEnvFlags(),
|
|
157
|
-
...(await this.parseEnvironmentVariablesFromFile()),
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
async parseEnvironmentVariablesFromFile() {
|
|
161
|
-
const result = {};
|
|
162
|
-
for (const envFile of this.flags["env-file"] ?? []) {
|
|
163
|
-
if (!(await pathExists(envFile))) {
|
|
164
|
-
throw new Error(`Env file not found: ${envFile}`);
|
|
165
|
-
}
|
|
166
|
-
const fileContent = await fs.readFile(envFile, { encoding: "utf-8" });
|
|
167
|
-
const parsed = parse(fileContent);
|
|
168
|
-
Object.assign(result, parsed);
|
|
169
|
-
}
|
|
170
|
-
return result;
|
|
171
|
-
}
|
|
172
|
-
parseEnvironmentVariablesFromEnvFlags() {
|
|
173
|
-
const splitIntoKeyAndValue = (e) => e.split("=", 2);
|
|
174
|
-
const envFlags = this.flags.env ?? [];
|
|
175
|
-
return Object.fromEntries(envFlags.map(splitIntoKeyAndValue));
|
|
176
|
-
}
|
|
177
|
-
/**
|
|
178
|
-
* Determines which ports to expose based on flags and image metadata
|
|
179
|
-
*
|
|
180
|
-
* @param imageMeta Metadata about the container image
|
|
181
|
-
* @returns An array of port mappings
|
|
182
|
-
*/
|
|
183
|
-
getPortMappings(imageMeta) {
|
|
184
|
-
if (this.flags["publish-all"]) {
|
|
185
|
-
const definedPorts = imageMeta.exposedPorts ?? [];
|
|
186
|
-
const concatPort = (p) => {
|
|
187
|
-
const [port, protocol = "tcp"] = p.port.split("/", 2);
|
|
188
|
-
return `${port}:${port}/${protocol}`;
|
|
189
|
-
};
|
|
190
|
-
return definedPorts.map(concatPort);
|
|
191
|
-
}
|
|
192
|
-
return this.flags.publish ?? [];
|
|
193
|
-
}
|
|
194
147
|
async getImageAndMeta(projectId) {
|
|
195
148
|
const { image } = this.args;
|
|
196
|
-
const meta = await this.
|
|
149
|
+
const meta = await getImageMeta(this.apiClient, image, projectId);
|
|
197
150
|
return { image, meta };
|
|
198
151
|
}
|
|
199
|
-
async getImageMeta(image, projectId) {
|
|
200
|
-
const resp = await this.apiClient.container.getContainerImageConfig({
|
|
201
|
-
queryParameters: {
|
|
202
|
-
imageReference: image,
|
|
203
|
-
useCredentialsForProjectId: projectId,
|
|
204
|
-
},
|
|
205
|
-
});
|
|
206
|
-
assertStatus(resp, 200);
|
|
207
|
-
return resp.data;
|
|
208
|
-
}
|
|
209
152
|
getServiceName() {
|
|
210
153
|
const { name } = this.flags;
|
|
211
154
|
if (name !== undefined) {
|
|
@@ -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
|
+
}
|
|
@@ -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,43 @@
|
|
|
1
|
+
import { MittwaldAPIV2, MittwaldAPIV2Client } from "@mittwald/api-client";
|
|
2
|
+
type ContainerContainerImageConfig = MittwaldAPIV2.Components.Schemas.ContainerContainerImageConfig;
|
|
3
|
+
/**
|
|
4
|
+
* Parses environment variables from command line flags and env files
|
|
5
|
+
*
|
|
6
|
+
* @param envFlags Array of environment variable strings in KEY=VALUE format
|
|
7
|
+
* @param envFiles Array of paths to env files
|
|
8
|
+
* @returns An object containing environment variable key-value pairs
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseEnvironmentVariables(envFlags?: string[], envFiles?: string[]): Promise<Record<string, string>>;
|
|
11
|
+
/**
|
|
12
|
+
* Parses environment variables from env files
|
|
13
|
+
*
|
|
14
|
+
* @param envFiles Array of paths to env files
|
|
15
|
+
* @returns An object containing environment variable key-value pairs
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseEnvironmentVariablesFromFile(envFiles?: string[]): Promise<Record<string, string>>;
|
|
18
|
+
/**
|
|
19
|
+
* Parses environment variables from command line flags
|
|
20
|
+
*
|
|
21
|
+
* @param envFlags Array of environment variable strings in KEY=VALUE format
|
|
22
|
+
* @returns An object containing environment variable key-value pairs
|
|
23
|
+
*/
|
|
24
|
+
export declare function parseEnvironmentVariablesFromEnvFlags(envFlags?: string[]): Record<string, string>;
|
|
25
|
+
/**
|
|
26
|
+
* Determines which ports to expose based on image metadata
|
|
27
|
+
*
|
|
28
|
+
* @param imageMeta Metadata about the container image
|
|
29
|
+
* @param publishAll Whether to publish all ports defined in the image
|
|
30
|
+
* @param publishPorts Array of port mappings specified by the user
|
|
31
|
+
* @returns An array of port mappings
|
|
32
|
+
*/
|
|
33
|
+
export declare function getPortMappings(imageMeta: ContainerContainerImageConfig, publishAll?: boolean, publishPorts?: string[]): string[];
|
|
34
|
+
/**
|
|
35
|
+
* Retrieves metadata for a container image
|
|
36
|
+
*
|
|
37
|
+
* @param apiClient The API client instance
|
|
38
|
+
* @param image The container image reference
|
|
39
|
+
* @param projectId The project ID for credentials
|
|
40
|
+
* @returns Metadata about the container image
|
|
41
|
+
*/
|
|
42
|
+
export declare function getImageMeta(apiClient: MittwaldAPIV2Client, image: string, projectId: string): Promise<ContainerContainerImageConfig>;
|
|
43
|
+
export {};
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { assertStatus, } from "@mittwald/api-client";
|
|
2
|
+
import * as fs from "fs/promises";
|
|
3
|
+
import { parse } from "envfile";
|
|
4
|
+
import { pathExists } from "../../util/fs/pathExists.js";
|
|
5
|
+
/**
|
|
6
|
+
* Parses environment variables from command line flags and env files
|
|
7
|
+
*
|
|
8
|
+
* @param envFlags Array of environment variable strings in KEY=VALUE format
|
|
9
|
+
* @param envFiles Array of paths to env files
|
|
10
|
+
* @returns An object containing environment variable key-value pairs
|
|
11
|
+
*/
|
|
12
|
+
export async function parseEnvironmentVariables(envFlags = [], envFiles = []) {
|
|
13
|
+
return {
|
|
14
|
+
...parseEnvironmentVariablesFromEnvFlags(envFlags),
|
|
15
|
+
...(await parseEnvironmentVariablesFromFile(envFiles)),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Parses environment variables from env files
|
|
20
|
+
*
|
|
21
|
+
* @param envFiles Array of paths to env files
|
|
22
|
+
* @returns An object containing environment variable key-value pairs
|
|
23
|
+
*/
|
|
24
|
+
export async function parseEnvironmentVariablesFromFile(envFiles = []) {
|
|
25
|
+
const result = {};
|
|
26
|
+
for (const envFile of envFiles) {
|
|
27
|
+
if (!(await pathExists(envFile))) {
|
|
28
|
+
throw new Error(`Env file not found: ${envFile}`);
|
|
29
|
+
}
|
|
30
|
+
const fileContent = await fs.readFile(envFile, { encoding: "utf-8" });
|
|
31
|
+
const parsed = parse(fileContent);
|
|
32
|
+
Object.assign(result, parsed);
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Parses environment variables from command line flags
|
|
38
|
+
*
|
|
39
|
+
* @param envFlags Array of environment variable strings in KEY=VALUE format
|
|
40
|
+
* @returns An object containing environment variable key-value pairs
|
|
41
|
+
*/
|
|
42
|
+
export function parseEnvironmentVariablesFromEnvFlags(envFlags = []) {
|
|
43
|
+
const splitIntoKeyAndValue = (e) => e.split("=", 2);
|
|
44
|
+
return Object.fromEntries(envFlags.map(splitIntoKeyAndValue));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Determines which ports to expose based on image metadata
|
|
48
|
+
*
|
|
49
|
+
* @param imageMeta Metadata about the container image
|
|
50
|
+
* @param publishAll Whether to publish all ports defined in the image
|
|
51
|
+
* @param publishPorts Array of port mappings specified by the user
|
|
52
|
+
* @returns An array of port mappings
|
|
53
|
+
*/
|
|
54
|
+
export function getPortMappings(imageMeta, publishAll = false, publishPorts = []) {
|
|
55
|
+
if (publishAll) {
|
|
56
|
+
const definedPorts = imageMeta.exposedPorts ?? [];
|
|
57
|
+
const concatPort = (p) => {
|
|
58
|
+
const [port, protocol = "tcp"] = p.port.split("/", 2);
|
|
59
|
+
return `${port}:${port}/${protocol}`;
|
|
60
|
+
};
|
|
61
|
+
return definedPorts.map(concatPort);
|
|
62
|
+
}
|
|
63
|
+
return publishPorts;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Retrieves metadata for a container image
|
|
67
|
+
*
|
|
68
|
+
* @param apiClient The API client instance
|
|
69
|
+
* @param image The container image reference
|
|
70
|
+
* @param projectId The project ID for credentials
|
|
71
|
+
* @returns Metadata about the container image
|
|
72
|
+
*/
|
|
73
|
+
export async function getImageMeta(apiClient, image, projectId) {
|
|
74
|
+
const resp = await apiClient.container.getContainerImageConfig({
|
|
75
|
+
queryParameters: {
|
|
76
|
+
imageReference: image,
|
|
77
|
+
useCredentialsForProjectId: projectId,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
assertStatus(resp, 200);
|
|
81
|
+
return resp.data;
|
|
82
|
+
}
|