Skip to content

Commit 6c8eb3b

Browse files
authored
Merge pull request #1635 from Dokploy/fix/use-exec-file
feat(registry): refactor Docker login command execution to use execFi…
2 parents e83efa3 + cb20950 commit 6c8eb3b

File tree

2 files changed

+55
-5
lines changed

2 files changed

+55
-5
lines changed

apps/dokploy/server/api/routers/registry.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ import {
1010
import {
1111
IS_CLOUD,
1212
createRegistry,
13-
execAsync,
1413
execAsyncRemote,
14+
execFileAsync,
1515
findRegistryById,
1616
removeRegistry,
1717
updateRegistry,
@@ -83,7 +83,13 @@ export const registryRouter = createTRPCRouter({
8383
.input(apiTestRegistry)
8484
.mutation(async ({ input }) => {
8585
try {
86-
const loginCommand = `echo ${input.password} | docker login ${input.registryUrl} --username ${input.username} --password-stdin`;
86+
const args = [
87+
"login",
88+
input.registryUrl,
89+
"--username",
90+
input.username,
91+
"--password-stdin",
92+
];
8793

8894
if (IS_CLOUD && !input.serverId) {
8995
throw new TRPCError({
@@ -93,9 +99,14 @@ export const registryRouter = createTRPCRouter({
9399
}
94100

95101
if (input.serverId && input.serverId !== "none") {
96-
await execAsyncRemote(input.serverId, loginCommand);
102+
await execAsyncRemote(
103+
input.serverId,
104+
`echo ${input.password} | docker ${args.join(" ")}`,
105+
);
97106
} else {
98-
await execAsync(loginCommand);
107+
await execFileAsync("docker", args, {
108+
input: Buffer.from(input.password).toString(),
109+
});
99110
}
100111

101112
return true;

packages/server/src/utils/process/execAsync.ts

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,48 @@
1-
import { exec } from "node:child_process";
1+
import { exec, execFile } from "node:child_process";
22
import util from "node:util";
33
import { findServerById } from "@dokploy/server/services/server";
44
import { Client } from "ssh2";
5+
56
export const execAsync = util.promisify(exec);
67

8+
export const execFileAsync = async (
9+
command: string,
10+
args: string[],
11+
options: { input?: string } = {},
12+
): Promise<{ stdout: string; stderr: string }> => {
13+
const child = execFile(command, args);
14+
15+
if (options.input && child.stdin) {
16+
child.stdin.write(options.input);
17+
child.stdin.end();
18+
}
19+
20+
return new Promise((resolve, reject) => {
21+
let stdout = "";
22+
let stderr = "";
23+
24+
child.stdout?.on("data", (data) => {
25+
stdout += data.toString();
26+
});
27+
28+
child.stderr?.on("data", (data) => {
29+
stderr += data.toString();
30+
});
31+
32+
child.on("close", (code) => {
33+
if (code === 0) {
34+
resolve({ stdout, stderr });
35+
} else {
36+
reject(
37+
new Error(`Command failed with code ${code}. Stderr: ${stderr}`),
38+
);
39+
}
40+
});
41+
42+
child.on("error", reject);
43+
});
44+
};
45+
746
export const execAsyncRemote = async (
847
serverId: string | null,
948
command: string,

0 commit comments

Comments
 (0)