diff --git a/apps/dokploy/__test__/drop/drop.test.test.ts b/apps/dokploy/__test__/drop/drop.test.test.ts index 4ca78b36..f5b68c1d 100644 --- a/apps/dokploy/__test__/drop/drop.test.test.ts +++ b/apps/dokploy/__test__/drop/drop.test.test.ts @@ -27,6 +27,7 @@ if (typeof window === "undefined") { const baseApp: ApplicationNested = { applicationId: "", herokuVersion: "", + watchPaths: [], applicationStatus: "done", appName: "", autoDeploy: true, diff --git a/apps/dokploy/__test__/traefik/traefik.test.ts b/apps/dokploy/__test__/traefik/traefik.test.ts index df20e163..a64103ff 100644 --- a/apps/dokploy/__test__/traefik/traefik.test.ts +++ b/apps/dokploy/__test__/traefik/traefik.test.ts @@ -14,6 +14,7 @@ const baseApp: ApplicationNested = { branch: null, dockerBuildStage: "", registryUrl: "", + watchPaths: [], buildArgs: null, isPreviewDeploymentsActive: false, previewBuildArgs: null, diff --git a/apps/dokploy/components/dashboard/application/general/generic/save-bitbucket-provider.tsx b/apps/dokploy/components/dashboard/application/general/generic/save-bitbucket-provider.tsx index 9af040b7..ca1bf823 100644 --- a/apps/dokploy/components/dashboard/application/general/generic/save-bitbucket-provider.tsx +++ b/apps/dokploy/components/dashboard/application/general/generic/save-bitbucket-provider.tsx @@ -29,14 +29,23 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { CheckIcon, ChevronsUpDown } from "lucide-react"; +import { CheckIcon, ChevronsUpDown, X } from "lucide-react"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; +import { Badge } from "@/components/ui/badge"; +import { BitbucketIcon } from "@/components/icons/data-tools-icons"; +import Link from "next/link"; const BitbucketProviderSchema = z.object({ buildPath: z.string().min(1, "Path is required").default("/"), @@ -48,6 +57,7 @@ const BitbucketProviderSchema = z.object({ .required(), branch: z.string().min(1, "Branch is required"), bitbucketId: z.string().min(1, "Bitbucket Provider is required"), + watchPaths: z.array(z.string()).optional(), }); type BitbucketProvider = z.infer; @@ -73,6 +83,7 @@ export const SaveBitbucketProvider = ({ applicationId }: Props) => { }, bitbucketId: "", branch: "", + watchPaths: [], }, resolver: zodResolver(BitbucketProviderSchema), }); @@ -118,6 +129,7 @@ export const SaveBitbucketProvider = ({ applicationId }: Props) => { }, buildPath: data.bitbucketBuildPath || "/", bitbucketId: data.bitbucketId || "", + watchPaths: data.watchPaths || [], }); } }, [form.reset, data, form]); @@ -130,6 +142,7 @@ export const SaveBitbucketProvider = ({ applicationId }: Props) => { bitbucketBuildPath: data.buildPath, bitbucketId: data.bitbucketId, applicationId, + watchPaths: data.watchPaths || [], }) .then(async () => { toast.success("Service Provided Saved"); @@ -195,7 +208,20 @@ export const SaveBitbucketProvider = ({ applicationId }: Props) => { name="repository" render={({ field }) => ( - Repository +
+ Repository + {field.value.owner && field.value.repo && ( + + + View Repository + + )} +
@@ -363,6 +389,84 @@ export const SaveBitbucketProvider = ({ applicationId }: Props) => {
)} /> + ( + +
+ Watch Paths + + + +
+ ? +
+
+ +

+ Add paths to watch for changes. When files in these + paths change, a new deployment will be triggered. +

+
+
+
+
+
+ {field.value?.map((path, index) => ( + + {path} + { + const newPaths = [...(field.value || [])]; + newPaths.splice(index, 1); + form.setValue("watchPaths", newPaths); + }} + /> + + ))} +
+ +
+ { + if (e.key === "Enter") { + e.preventDefault(); + const input = e.currentTarget; + const value = input.value.trim(); + if (value) { + const newPaths = [...(field.value || []), value]; + form.setValue("watchPaths", newPaths); + input.value = ""; + } + } + }} + /> + +
+
+ +
+ )} + />
)}
- ( - - Branch - - - - - - )} - /> +
+ ( + + Branch + + + + + + )} + /> +
+ { )} /> + ( + +
+ Watch Paths + + + +
+ ? +
+
+ +

+ Add paths to watch for changes. When files in these + paths change, a new deployment will be triggered. This + will work only when manual webhook is setup. +

+
+
+
+
+
+ {field.value?.map((path, index) => ( + + {path} + { + const newPaths = [...(field.value || [])]; + newPaths.splice(index, 1); + form.setValue("watchPaths", newPaths); + }} + /> + + ))} +
+ +
+ { + if (e.key === "Enter") { + e.preventDefault(); + const input = e.currentTarget; + const value = input.value.trim(); + if (value) { + const newPaths = [...(field.value || []), value]; + form.setValue("watchPaths", newPaths); + input.value = ""; + } + } + }} + /> + +
+
+ +
+ )} + />
diff --git a/apps/dokploy/components/dashboard/application/general/generic/save-github-provider.tsx b/apps/dokploy/components/dashboard/application/general/generic/save-github-provider.tsx index adb44575..257e48ea 100644 --- a/apps/dokploy/components/dashboard/application/general/generic/save-github-provider.tsx +++ b/apps/dokploy/components/dashboard/application/general/generic/save-github-provider.tsx @@ -28,14 +28,23 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { Badge } from "@/components/ui/badge"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { CheckIcon, ChevronsUpDown } from "lucide-react"; +import { CheckIcon, ChevronsUpDown, HelpCircle, Plus, X } from "lucide-react"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; +import Link from "next/link"; +import { GithubIcon } from "@/components/icons/data-tools-icons"; const GithubProviderSchema = z.object({ buildPath: z.string().min(1, "Path is required").default("/"), @@ -47,6 +56,7 @@ const GithubProviderSchema = z.object({ .required(), branch: z.string().min(1, "Branch is required"), githubId: z.string().min(1, "Github Provider is required"), + watchPaths: z.array(z.string()).optional(), }); type GithubProvider = z.infer; @@ -113,6 +123,7 @@ export const SaveGithubProvider = ({ applicationId }: Props) => { }, buildPath: data.buildPath || "/", githubId: data.githubId || "", + watchPaths: data.watchPaths || [], }); } }, [form.reset, data, form]); @@ -125,6 +136,7 @@ export const SaveGithubProvider = ({ applicationId }: Props) => { owner: data.repository.owner, buildPath: data.buildPath, githubId: data.githubId, + watchPaths: data.watchPaths || [], }) .then(async () => { toast.success("Service Provided Saved"); @@ -187,7 +199,20 @@ export const SaveGithubProvider = ({ applicationId }: Props) => { name="repository" render={({ field }) => ( - Repository +
+ Repository + {field.value.owner && field.value.repo && ( + + + View Repository + + )} +
@@ -350,7 +375,85 @@ export const SaveGithubProvider = ({ applicationId }: Props) => { - + +
+ )} + /> + ( + +
+ Watch Paths + + + + + + +

+ Add paths to watch for changes. When files in these + paths change, a new deployment will be triggered. +

+
+
+
+
+
+ {field.value?.map((path, index) => ( + + {path} + { + const newPaths = [...(field.value || [])]; + newPaths.splice(index, 1); + field.onChange(newPaths); + }} + /> + + ))} +
+
+ + { + if (e.key === "Enter") { + e.preventDefault(); + const input = e.currentTarget; + const path = input.value.trim(); + if (path) { + field.onChange([...(field.value || []), path]); + input.value = ""; + } + } + }} + /> + + +
)} @@ -365,6 +468,16 @@ export const SaveGithubProvider = ({ applicationId }: Props) => { Save
+ {/* create github link */} +
+ + Repository + +
diff --git a/apps/dokploy/components/dashboard/application/general/generic/save-gitlab-provider.tsx b/apps/dokploy/components/dashboard/application/general/generic/save-gitlab-provider.tsx index 298a9114..c0c90a01 100644 --- a/apps/dokploy/components/dashboard/application/general/generic/save-gitlab-provider.tsx +++ b/apps/dokploy/components/dashboard/application/general/generic/save-gitlab-provider.tsx @@ -29,14 +29,23 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { Badge } from "@/components/ui/badge"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { CheckIcon, ChevronsUpDown } from "lucide-react"; +import { CheckIcon, ChevronsUpDown, HelpCircle, Plus, X } from "lucide-react"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; +import Link from "next/link"; +import { GitlabIcon } from "@/components/icons/data-tools-icons"; const GitlabProviderSchema = z.object({ buildPath: z.string().min(1, "Path is required").default("/"), @@ -50,6 +59,7 @@ const GitlabProviderSchema = z.object({ .required(), branch: z.string().min(1, "Branch is required"), gitlabId: z.string().min(1, "Gitlab Provider is required"), + watchPaths: z.array(z.string()).optional(), }); type GitlabProvider = z.infer; @@ -124,6 +134,7 @@ export const SaveGitlabProvider = ({ applicationId }: Props) => { }, buildPath: data.gitlabBuildPath || "/", gitlabId: data.gitlabId || "", + watchPaths: data.watchPaths || [], }); } }, [form.reset, data, form]); @@ -138,6 +149,7 @@ export const SaveGitlabProvider = ({ applicationId }: Props) => { applicationId, gitlabProjectId: data.repository.id, gitlabPathNamespace: data.repository.gitlabPathNamespace, + watchPaths: data.watchPaths || [], }) .then(async () => { toast.success("Service Provided Saved"); @@ -203,7 +215,20 @@ export const SaveGitlabProvider = ({ applicationId }: Props) => { name="repository" render={({ field }) => ( - Repository +
+ Repository + {field.value.owner && field.value.repo && ( + + + View Repository + + )} +
@@ -375,7 +400,85 @@ export const SaveGitlabProvider = ({ applicationId }: Props) => { - + +
+ )} + /> + ( + +
+ Watch Paths + + + + + + +

+ Add paths to watch for changes. When files in these + paths change, a new deployment will be triggered. +

+
+
+
+
+
+ {field.value?.map((path, index) => ( + + {path} + { + const newPaths = [...(field.value || [])]; + newPaths.splice(index, 1); + field.onChange(newPaths); + }} + /> + + ))} +
+
+ + { + if (e.key === "Enter") { + e.preventDefault(); + const input = e.currentTarget; + const path = input.value.trim(); + if (path) { + field.onChange([...(field.value || []), path]); + input.value = ""; + } + } + }} + /> + + +
)} diff --git a/apps/dokploy/components/dashboard/compose/general/generic/save-bitbucket-provider-compose.tsx b/apps/dokploy/components/dashboard/compose/general/generic/save-bitbucket-provider-compose.tsx index 87584133..6dc99b26 100644 --- a/apps/dokploy/components/dashboard/compose/general/generic/save-bitbucket-provider-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/general/generic/save-bitbucket-provider-compose.tsx @@ -29,14 +29,23 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { CheckIcon, ChevronsUpDown } from "lucide-react"; +import { CheckIcon, ChevronsUpDown, X } from "lucide-react"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; +import { Badge } from "@/components/ui/badge"; +import { BitbucketIcon } from "@/components/icons/data-tools-icons"; +import Link from "next/link"; const BitbucketProviderSchema = z.object({ composePath: z.string().min(1), @@ -48,6 +57,7 @@ const BitbucketProviderSchema = z.object({ .required(), branch: z.string().min(1, "Branch is required"), bitbucketId: z.string().min(1, "Bitbucket Provider is required"), + watchPaths: z.array(z.string()).optional(), }); type BitbucketProvider = z.infer; @@ -73,6 +83,7 @@ export const SaveBitbucketProviderCompose = ({ composeId }: Props) => { }, bitbucketId: "", branch: "", + watchPaths: [], }, resolver: zodResolver(BitbucketProviderSchema), }); @@ -118,6 +129,7 @@ export const SaveBitbucketProviderCompose = ({ composeId }: Props) => { }, composePath: data.composePath, bitbucketId: data.bitbucketId || "", + watchPaths: data.watchPaths || [], }); } }, [form.reset, data, form]); @@ -132,6 +144,7 @@ export const SaveBitbucketProviderCompose = ({ composeId }: Props) => { composeId, sourceType: "bitbucket", composeStatus: "idle", + watchPaths: data.watchPaths, }) .then(async () => { toast.success("Service Provided Saved"); @@ -197,7 +210,20 @@ export const SaveBitbucketProviderCompose = ({ composeId }: Props) => { name="repository" render={({ field }) => ( - Repository +
+ Repository + {field.value.owner && field.value.repo && ( + + + View Repository + + )} +
@@ -365,6 +391,84 @@ export const SaveBitbucketProviderCompose = ({ composeId }: Props) => {
)} /> + ( + +
+ Watch Paths + + + +
+ ? +
+
+ +

+ Add paths to watch for changes. When files in these + paths change, a new deployment will be triggered. +

+
+
+
+
+
+ {field.value?.map((path, index) => ( + + {path} + { + const newPaths = [...(field.value || [])]; + newPaths.splice(index, 1); + form.setValue("watchPaths", newPaths); + }} + /> + + ))} +
+ +
+ { + if (e.key === "Enter") { + e.preventDefault(); + const input = e.currentTarget; + const value = input.value.trim(); + if (value) { + const newPaths = [...(field.value || []), value]; + form.setValue("watchPaths", newPaths); + input.value = ""; + } + } + }} + /> + +
+
+ +
+ )} + />
+
+ + + + )} + />
diff --git a/apps/dokploy/components/dashboard/compose/general/generic/save-github-provider-compose.tsx b/apps/dokploy/components/dashboard/compose/general/generic/save-github-provider-compose.tsx index 7787cb3c..b58347dc 100644 --- a/apps/dokploy/components/dashboard/compose/general/generic/save-github-provider-compose.tsx +++ b/apps/dokploy/components/dashboard/compose/general/generic/save-github-provider-compose.tsx @@ -1,3 +1,4 @@ +import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Command, @@ -28,14 +29,22 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; import { cn } from "@/lib/utils"; import { api } from "@/utils/api"; import { zodResolver } from "@hookform/resolvers/zod"; -import { CheckIcon, ChevronsUpDown } from "lucide-react"; +import { CheckIcon, ChevronsUpDown, X } from "lucide-react"; import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { toast } from "sonner"; import { z } from "zod"; +import { GithubIcon } from "@/components/icons/data-tools-icons"; +import Link from "next/link"; const GithubProviderSchema = z.object({ composePath: z.string().min(1), @@ -47,6 +56,7 @@ const GithubProviderSchema = z.object({ .required(), branch: z.string().min(1, "Branch is required"), githubId: z.string().min(1, "Github Provider is required"), + watchPaths: z.array(z.string()).optional(), }); type GithubProvider = z.infer; @@ -71,6 +81,7 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => { }, githubId: "", branch: "", + watchPaths: [], }, resolver: zodResolver(GithubProviderSchema), }); @@ -113,6 +124,7 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => { }, composePath: data.composePath, githubId: data.githubId || "", + watchPaths: data.watchPaths || [], }); } }, [form.reset, data, form]); @@ -127,6 +139,7 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => { githubId: data.githubId, sourceType: "github", composeStatus: "idle", + watchPaths: data.watchPaths, }) .then(async () => { toast.success("Service Provided Saved"); @@ -183,13 +196,25 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => { )} /> - ( - Repository +
+ Repository + {field.value.owner && field.value.repo && ( + + + View Repository + + )} +
@@ -357,6 +382,84 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => {
)} /> + ( + +
+ Watch Paths + + + +
+ ? +
+
+ +

+ Add paths to watch for changes. When files in these + paths change, a new deployment will be triggered. +

+
+
+
+
+
+ {field.value?.map((path, index) => ( + + {path} + { + const newPaths = [...(field.value || [])]; + newPaths.splice(index, 1); + form.setValue("watchPaths", newPaths); + }} + /> + + ))} +
+ +
+ { + if (e.key === "Enter") { + e.preventDefault(); + const input = e.currentTarget; + const value = input.value.trim(); + if (value) { + const newPaths = [...(field.value || []), value]; + form.setValue("watchPaths", newPaths); + input.value = ""; + } + } + }} + /> + +
+
+ +
+ )} + />
+
+ + + + )} + />