diff --git a/apps/dokploy/__test__/compose/build-compose-command.test.ts b/apps/dokploy/__test__/compose/build-compose-command.test.ts new file mode 100644 index 0000000000..0aa2bc2d91 --- /dev/null +++ b/apps/dokploy/__test__/compose/build-compose-command.test.ts @@ -0,0 +1,52 @@ +import { getBuildComposeCommand } from "@dokploy/server/utils/builders/compose"; +import { describe, expect, it, vi } from "vitest"; + +// Isolate the command builder from the compose-file I/O performed by +// writeDomainsToCompose; we only care about the docker invocation it emits. +vi.mock("@dokploy/server/utils/docker/domain", () => ({ + writeDomainsToCompose: vi.fn().mockResolvedValue(""), +})); + +const baseCompose = { + appName: "my-app", + sourceType: "raw", + command: "", + composePath: "docker-compose.yml", + composeType: "stack", + isolatedDeployment: false, + randomize: false, + suffix: "", + serverId: null, + env: "", + mounts: [], + domains: [], + environment: { project: { env: "" }, env: "" }, +} as unknown as Parameters[0]; + +// Regression coverage for #4401: the deploy command runs under `env -i`, which +// clears the environment except for the vars listed explicitly. HOME must be +// preserved so docker can resolve ~/.docker/config.json — otherwise +// `docker stack deploy --with-registry-auth` ships no credentials to the swarm +// and private-registry images fail to pull. +describe("getBuildComposeCommand registry auth (#4401)", () => { + it("preserves HOME for swarm stack deploys", async () => { + const command = await getBuildComposeCommand({ + ...baseCompose, + composeType: "stack", + }); + + expect(command).toContain("stack deploy"); + expect(command).toContain("--with-registry-auth"); + expect(command).toContain('env -i PATH="$PATH" HOME="$HOME"'); + }); + + it("preserves HOME for docker compose deploys", async () => { + const command = await getBuildComposeCommand({ + ...baseCompose, + composeType: "docker-compose", + }); + + expect(command).toContain("compose -p my-app"); + expect(command).toContain('env -i PATH="$PATH" HOME="$HOME"'); + }); +}); diff --git a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts index e07f34ade9..eda4dace58 100644 --- a/apps/dokploy/__test__/traefik/server/update-server-config.test.ts +++ b/apps/dokploy/__test__/traefik/server/update-server-config.test.ts @@ -65,6 +65,8 @@ const baseSettings: WebServerSettings = { cleanupCacheApplications: false, cleanupCacheOnCompose: false, cleanupCacheOnPreviews: false, + remoteServersOnly: false, + enforceSSO: false, createdAt: null, updatedAt: new Date(), }; diff --git a/apps/dokploy/components/dashboard/project/add-application.tsx b/apps/dokploy/components/dashboard/project/add-application.tsx index 16fac353d5..0c50cc41f0 100644 --- a/apps/dokploy/components/dashboard/project/add-application.tsx +++ b/apps/dokploy/components/dashboard/project/add-application.tsx @@ -71,6 +71,9 @@ interface Props { export const AddApplication = ({ environmentId, projectName }: Props) => { const utils = api.useUtils(); const { data: isCloud } = api.settings.isCloud.useQuery(); + const { data: webServerSettings } = + api.settings.getWebServerSettings.useQuery(); + const showLocalOption = !isCloud && !webServerSettings?.remoteServersOnly; const [visible, setVisible] = useState(false); const slug = slugify(projectName); const { data: servers } = api.server.withSSHKey.useQuery(); @@ -171,7 +174,8 @@ export const AddApplication = ({ environmentId, projectName }: Props) => { - Select a Server {!isCloud ? "(Optional)" : ""} + Select a Server{" "} + {showLocalOption ? "(Optional)" : ""} @@ -191,17 +195,19 @@ export const AddApplication = ({ environmentId, projectName }: Props) => { - {!isCloud && ( + {showLocalOption && ( Dokploy @@ -236,7 +242,8 @@ export const AddCompose = ({ environmentId, projectName }: Props) => { ))} - Servers ({servers?.length + (!isCloud ? 1 : 0)}) + Servers ( + {servers?.length + (showLocalOption ? 1 : 0)}) diff --git a/apps/dokploy/components/dashboard/project/add-database.tsx b/apps/dokploy/components/dashboard/project/add-database.tsx index 966fe00133..a76de27752 100644 --- a/apps/dokploy/components/dashboard/project/add-database.tsx +++ b/apps/dokploy/components/dashboard/project/add-database.tsx @@ -219,6 +219,9 @@ export const AddDatabase = ({ environmentId, projectName }: Props) => { const [visible, setVisible] = useState(false); const slug = slugify(projectName); const { data: isCloud } = api.settings.isCloud.useQuery(); + const { data: webServerSettings } = + api.settings.getWebServerSettings.useQuery(); + const showLocalOption = !isCloud && !webServerSettings?.remoteServersOnly; const { data: servers } = api.server.withSSHKey.useQuery(); const libsqlMutation = api.libsql.create.useMutation(); const mariadbMutation = api.mariadb.create.useMutation(); @@ -470,19 +473,20 @@ export const AddDatabase = ({ environmentId, projectName }: Props) => {