mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-10-15 21:31:11 +00:00
feat: add "Deploy with Fresh Volumes" button for Docker Compose projects
Adds a one-click option to redeploy Docker Compose services with clean volumes, removing the need to manually edit the run command. When triggered, it runs `docker compose down --volumes` before the standard deploy build step. The feature is only available for docker-compose type projects (not swarm stacks).
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
||||
import { Ban, CheckCircle2, RefreshCcw, Rocket, Terminal } from "lucide-react";
|
||||
import { Ban, CheckCircle2, HardDriveDownload, RefreshCcw, Rocket, Terminal } from "lucide-react";
|
||||
import { useRouter } from "next/router";
|
||||
import { toast } from "sonner";
|
||||
import { DialogAction } from "@/components/shared/dialog-action";
|
||||
@@ -82,6 +82,55 @@ export const ComposeActions = ({ composeId }: Props) => {
|
||||
</Button>
|
||||
</DialogAction>
|
||||
)}
|
||||
{canDeploy &&
|
||||
data?.composeType === "docker-compose" && (
|
||||
<DialogAction
|
||||
title="Deploy with Fresh Volumes"
|
||||
description="This will remove all volumes and redeploy with a clean state. All persistent data will be permanently deleted."
|
||||
type="destructive"
|
||||
onClick={async () => {
|
||||
await deploy({
|
||||
composeId: composeId,
|
||||
freshVolumes: true,
|
||||
})
|
||||
.then(() => {
|
||||
toast.success(
|
||||
"Compose deployed with fresh volumes",
|
||||
);
|
||||
refetch();
|
||||
router.push(
|
||||
`/dashboard/project/${data?.environment.projectId}/environment/${data?.environmentId}/services/compose/${composeId}?tab=deployments`,
|
||||
);
|
||||
})
|
||||
.catch(() => {
|
||||
toast.error("Error deploying compose");
|
||||
});
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="outline"
|
||||
isLoading={data?.composeStatus === "running"}
|
||||
className="flex items-center gap-1.5 group focus-visible:ring-2 focus-visible:ring-offset-2"
|
||||
>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="flex items-center">
|
||||
<HardDriveDownload className="size-4 mr-1" />
|
||||
Fresh Volumes
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipPrimitive.Portal>
|
||||
<TooltipContent sideOffset={5} className="z-[60]">
|
||||
<p>
|
||||
Deploy with fresh volumes (removes all persistent
|
||||
data)
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</TooltipPrimitive.Portal>
|
||||
</Tooltip>
|
||||
</Button>
|
||||
</DialogAction>
|
||||
)}
|
||||
{canDeploy && (
|
||||
<DialogAction
|
||||
title="Reload Compose"
|
||||
|
||||
@@ -428,6 +428,7 @@ export const composeRouter = createTRPCRouter({
|
||||
applicationType: "compose",
|
||||
descriptionLog: input.description || "",
|
||||
server: !!compose.serverId,
|
||||
freshVolumes: input.freshVolumes,
|
||||
};
|
||||
|
||||
if (IS_CLOUD && compose.serverId) {
|
||||
@@ -477,6 +478,7 @@ export const composeRouter = createTRPCRouter({
|
||||
applicationType: "compose",
|
||||
descriptionLog: input.description || "",
|
||||
server: !!compose.serverId,
|
||||
freshVolumes: input.freshVolumes,
|
||||
};
|
||||
if (IS_CLOUD && compose.serverId) {
|
||||
jobData.serverId = compose.serverId;
|
||||
|
||||
@@ -44,12 +44,14 @@ const createDeploymentWorker = () =>
|
||||
composeId: job.data.composeId,
|
||||
titleLog: job.data.titleLog,
|
||||
descriptionLog: job.data.descriptionLog,
|
||||
freshVolumes: job.data.freshVolumes,
|
||||
});
|
||||
} else if (job.data.type === "redeploy") {
|
||||
await rebuildCompose({
|
||||
composeId: job.data.composeId,
|
||||
titleLog: job.data.titleLog,
|
||||
descriptionLog: job.data.descriptionLog,
|
||||
freshVolumes: job.data.freshVolumes,
|
||||
});
|
||||
}
|
||||
} else if (job.data.applicationType === "application-preview") {
|
||||
|
||||
@@ -16,6 +16,7 @@ type DeployJob =
|
||||
type: "deploy" | "redeploy";
|
||||
applicationType: "compose";
|
||||
serverId?: string;
|
||||
freshVolumes?: boolean;
|
||||
}
|
||||
| {
|
||||
applicationId: string;
|
||||
|
||||
@@ -198,12 +198,14 @@ export const apiDeployCompose = z.object({
|
||||
composeId: z.string().min(1),
|
||||
title: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
freshVolumes: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export const apiRedeployCompose = z.object({
|
||||
composeId: z.string().min(1),
|
||||
title: z.string().optional(),
|
||||
description: z.string().optional(),
|
||||
freshVolumes: z.boolean().optional(),
|
||||
});
|
||||
|
||||
export const apiDeleteCompose = z.object({
|
||||
|
||||
@@ -209,10 +209,12 @@ export const deployCompose = async ({
|
||||
composeId,
|
||||
titleLog = "Manual deployment",
|
||||
descriptionLog = "",
|
||||
freshVolumes = false,
|
||||
}: {
|
||||
composeId: string;
|
||||
titleLog: string;
|
||||
descriptionLog: string;
|
||||
freshVolumes?: boolean;
|
||||
}) => {
|
||||
const compose = await findComposeById(composeId);
|
||||
|
||||
@@ -266,6 +268,16 @@ export const deployCompose = async ({
|
||||
}
|
||||
}
|
||||
|
||||
if (freshVolumes && compose.composeType === "docker-compose") {
|
||||
const downCommand = `set -e; docker compose -p ${compose.appName} down --volumes 2>&1 || true;`;
|
||||
const downWithLog = `(${downCommand}) >> ${deployment.logPath} 2>&1`;
|
||||
if (compose.serverId) {
|
||||
await execAsyncRemote(compose.serverId, downWithLog);
|
||||
} else {
|
||||
await execAsync(downWithLog);
|
||||
}
|
||||
}
|
||||
|
||||
command = "set -e;";
|
||||
command += await getBuildComposeCommand(entity);
|
||||
commandWithLog = `(${command}) >> ${deployment.logPath} 2>&1`;
|
||||
@@ -339,10 +351,12 @@ export const rebuildCompose = async ({
|
||||
composeId,
|
||||
titleLog = "Rebuild deployment",
|
||||
descriptionLog = "",
|
||||
freshVolumes = false,
|
||||
}: {
|
||||
composeId: string;
|
||||
titleLog: string;
|
||||
descriptionLog: string;
|
||||
freshVolumes?: boolean;
|
||||
}) => {
|
||||
const compose = await findComposeById(composeId);
|
||||
|
||||
@@ -380,6 +394,16 @@ export const rebuildCompose = async ({
|
||||
}
|
||||
}
|
||||
|
||||
if (freshVolumes && compose.composeType === "docker-compose") {
|
||||
const downCommand = `set -e; docker compose -p ${compose.appName} down --volumes 2>&1 || true;`;
|
||||
const downWithLog = `(${downCommand}) >> ${deployment.logPath} 2>&1`;
|
||||
if (compose.serverId) {
|
||||
await execAsyncRemote(compose.serverId, downWithLog);
|
||||
} else {
|
||||
await execAsync(downWithLog);
|
||||
}
|
||||
}
|
||||
|
||||
command = "set -e;";
|
||||
command += await getBuildComposeCommand(compose);
|
||||
commandWithLog = `(${command}) >> ${deployment.logPath} 2>&1`;
|
||||
|
||||
Reference in New Issue
Block a user