@mittwald/cli 1.11.0 → 1.11.2
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/lib/apiutil/api_retry.js +2 -1
- package/dist/lib/basecommands/RenderBaseCommand.js +1 -0
- package/dist/lib/resources/stack/enrich.js +9 -2
- package/dist/lib/resources/stack/enrich.test.d.ts +1 -0
- package/dist/lib/resources/stack/enrich.test.js +167 -0
- package/dist/lib/resources/stack/types.d.ts +1 -1
- package/package.json +2 -2
|
@@ -32,9 +32,10 @@ export function configureAxiosRetry(axios) {
|
|
|
32
32
|
return true;
|
|
33
33
|
}
|
|
34
34
|
const isSafeRequest = error.config?.method?.toLowerCase() === "get";
|
|
35
|
+
const isConditionalRequest = !!error.config?.headers?.["if-event-reached"];
|
|
35
36
|
const isPreconditionFailed = error.response?.status === 412;
|
|
36
37
|
const isAccessDenied = error.response?.status === 403;
|
|
37
|
-
if (isPreconditionFailed) {
|
|
38
|
+
if (isPreconditionFailed && isConditionalRequest) {
|
|
38
39
|
return true;
|
|
39
40
|
}
|
|
40
41
|
return isSafeRequest && isAccessDenied && shouldRetryAccessDenied;
|
|
@@ -52,6 +52,7 @@ export class RenderBaseCommand extends ExtendedBaseCommand {
|
|
|
52
52
|
useIncreaseInkStdoutColumns();
|
|
53
53
|
return this.render();
|
|
54
54
|
} }) }) }) }) }));
|
|
55
|
+
await handle.waitUntilExit();
|
|
55
56
|
}
|
|
56
57
|
useAppInstallationId(command) {
|
|
57
58
|
return usePromise(() => this.withAppInstallationId(command), []);
|
|
@@ -14,8 +14,15 @@ async function setEnvironmentFromEnvFile(service) {
|
|
|
14
14
|
return service;
|
|
15
15
|
}
|
|
16
16
|
const enriched = structuredClone(service);
|
|
17
|
-
const
|
|
18
|
-
|
|
17
|
+
const envFiles = Array.isArray(service.env_file)
|
|
18
|
+
? service.env_file
|
|
19
|
+
: [service.env_file];
|
|
20
|
+
let envVars = {};
|
|
21
|
+
for (const envFile of envFiles) {
|
|
22
|
+
const envFileContent = await readFile(envFile, "utf-8");
|
|
23
|
+
const fileEnvVars = parse(envFileContent);
|
|
24
|
+
envVars = { ...envVars, ...fileEnvVars };
|
|
25
|
+
}
|
|
19
26
|
delete enriched.env_file;
|
|
20
27
|
enriched.envs = {
|
|
21
28
|
...envVars,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { describe, expect, it, jest, beforeEach } from "@jest/globals";
|
|
2
|
+
const mockReadFile = jest.fn();
|
|
3
|
+
jest.unstable_mockModule("fs/promises", () => ({
|
|
4
|
+
readFile: mockReadFile,
|
|
5
|
+
}));
|
|
6
|
+
const { enrichStackDefinition } = await import("./enrich.js");
|
|
7
|
+
describe("enrichStackDefinition", () => {
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
jest.clearAllMocks();
|
|
10
|
+
});
|
|
11
|
+
it("should handle service without env_file", async () => {
|
|
12
|
+
const input = {
|
|
13
|
+
services: {
|
|
14
|
+
nginx: {
|
|
15
|
+
image: "nginx:latest",
|
|
16
|
+
ports: ["80:80"],
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
const result = await enrichStackDefinition(input);
|
|
21
|
+
expect(result).toEqual(input);
|
|
22
|
+
expect(mockReadFile).not.toHaveBeenCalled();
|
|
23
|
+
});
|
|
24
|
+
it("should handle single env_file as string", async () => {
|
|
25
|
+
mockReadFile.mockResolvedValueOnce("FOO=bar\nBAZ=qux");
|
|
26
|
+
const input = {
|
|
27
|
+
services: {
|
|
28
|
+
nginx: {
|
|
29
|
+
image: "nginx:latest",
|
|
30
|
+
env_file: ".env",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
const result = await enrichStackDefinition(input);
|
|
35
|
+
expect(mockReadFile).toHaveBeenCalledWith(".env", "utf-8");
|
|
36
|
+
expect(result).toEqual({
|
|
37
|
+
services: {
|
|
38
|
+
nginx: {
|
|
39
|
+
image: "nginx:latest",
|
|
40
|
+
envs: {
|
|
41
|
+
FOO: "bar",
|
|
42
|
+
BAZ: "qux",
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
it("should handle env_file as array", async () => {
|
|
49
|
+
mockReadFile
|
|
50
|
+
.mockResolvedValueOnce("FOO=bar\nBAZ=qux")
|
|
51
|
+
.mockResolvedValueOnce("FOO=overridden\nNEW=value");
|
|
52
|
+
const input = {
|
|
53
|
+
services: {
|
|
54
|
+
nginx: {
|
|
55
|
+
image: "nginx:latest",
|
|
56
|
+
env_file: [".env", ".env.local"],
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
const result = await enrichStackDefinition(input);
|
|
61
|
+
expect(mockReadFile).toHaveBeenCalledWith(".env", "utf-8");
|
|
62
|
+
expect(mockReadFile).toHaveBeenCalledWith(".env.local", "utf-8");
|
|
63
|
+
expect(result).toEqual({
|
|
64
|
+
services: {
|
|
65
|
+
nginx: {
|
|
66
|
+
image: "nginx:latest",
|
|
67
|
+
envs: {
|
|
68
|
+
FOO: "overridden", // Later file should override
|
|
69
|
+
BAZ: "qux",
|
|
70
|
+
NEW: "value",
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
it("should merge env_file variables with existing envs", async () => {
|
|
77
|
+
mockReadFile.mockResolvedValueOnce("FOO=from_file\nFILE_VAR=file_value");
|
|
78
|
+
const input = {
|
|
79
|
+
services: {
|
|
80
|
+
nginx: {
|
|
81
|
+
image: "nginx:latest",
|
|
82
|
+
env_file: ".env",
|
|
83
|
+
envs: {
|
|
84
|
+
FOO: "existing", // Should override env_file
|
|
85
|
+
EXISTING_VAR: "existing_value",
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
};
|
|
90
|
+
const result = await enrichStackDefinition(input);
|
|
91
|
+
expect(result).toEqual({
|
|
92
|
+
services: {
|
|
93
|
+
nginx: {
|
|
94
|
+
image: "nginx:latest",
|
|
95
|
+
envs: {
|
|
96
|
+
FOO: "existing", // Existing envs take precedence
|
|
97
|
+
FILE_VAR: "file_value",
|
|
98
|
+
EXISTING_VAR: "existing_value",
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
it("should handle multiple services with different env_file configurations", async () => {
|
|
105
|
+
mockReadFile
|
|
106
|
+
.mockResolvedValueOnce("NGINX_VAR=nginx_value")
|
|
107
|
+
.mockResolvedValueOnce("APP_VAR=app_value1")
|
|
108
|
+
.mockResolvedValueOnce("APP_VAR=app_value2\nOTHER=other");
|
|
109
|
+
const input = {
|
|
110
|
+
services: {
|
|
111
|
+
nginx: {
|
|
112
|
+
image: "nginx:latest",
|
|
113
|
+
env_file: ".env.nginx",
|
|
114
|
+
},
|
|
115
|
+
app: {
|
|
116
|
+
image: "app:latest",
|
|
117
|
+
env_file: [".env.app", ".env.app.local"],
|
|
118
|
+
},
|
|
119
|
+
db: {
|
|
120
|
+
image: "postgres:latest",
|
|
121
|
+
// No env_file
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
const result = await enrichStackDefinition(input);
|
|
126
|
+
expect(result).toEqual({
|
|
127
|
+
services: {
|
|
128
|
+
nginx: {
|
|
129
|
+
image: "nginx:latest",
|
|
130
|
+
envs: {
|
|
131
|
+
NGINX_VAR: "nginx_value",
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
app: {
|
|
135
|
+
image: "app:latest",
|
|
136
|
+
envs: {
|
|
137
|
+
APP_VAR: "app_value2", // Later file overrides
|
|
138
|
+
OTHER: "other",
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
db: {
|
|
142
|
+
image: "postgres:latest",
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
it("should handle empty env files", async () => {
|
|
148
|
+
mockReadFile.mockResolvedValueOnce("");
|
|
149
|
+
const input = {
|
|
150
|
+
services: {
|
|
151
|
+
nginx: {
|
|
152
|
+
image: "nginx:latest",
|
|
153
|
+
env_file: ".env",
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
const result = await enrichStackDefinition(input);
|
|
158
|
+
expect(result).toEqual({
|
|
159
|
+
services: {
|
|
160
|
+
nginx: {
|
|
161
|
+
image: "nginx:latest",
|
|
162
|
+
envs: {},
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
});
|
|
@@ -3,7 +3,7 @@ type ContainerServiceDeclareRequest = MittwaldAPIV2.Components.Schemas.Container
|
|
|
3
3
|
export type ContainerServiceInput = ContainerServiceDeclareRequest & {
|
|
4
4
|
command?: string[] | string;
|
|
5
5
|
entrypoint?: string[] | string;
|
|
6
|
-
env_file?: string;
|
|
6
|
+
env_file?: string | string[];
|
|
7
7
|
environment?: {
|
|
8
8
|
[k: string]: string;
|
|
9
9
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mittwald/cli",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.2",
|
|
4
4
|
"description": "Hand-crafted CLI for the mittwald API",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"envfile": "^7.1.0",
|
|
57
57
|
"fast-xml-parser": "^5.2.5",
|
|
58
58
|
"ink": "^6.2.3",
|
|
59
|
-
"ink-link": "^
|
|
59
|
+
"ink-link": "^5.0.0",
|
|
60
60
|
"ink-text-input": "^6.0.0",
|
|
61
61
|
"js-yaml": "^4.1.0",
|
|
62
62
|
"marked": "^15.0.12",
|