mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
Merge pull request #1613 from TheoD02/feat/github-triggerType
Some checks failed
Auto PR to main when version changes / create-pr (push) Has been cancelled
Build Docker images / build-and-push-cloud-image (push) Has been cancelled
Build Docker images / build-and-push-schedule-image (push) Has been cancelled
Build Docker images / build-and-push-server-image (push) Has been cancelled
Dokploy Docker Build / docker-amd (push) Has been cancelled
Dokploy Docker Build / docker-arm (push) Has been cancelled
autofix.ci / format (push) Has been cancelled
Dokploy Monitoring Build / docker-amd (push) Has been cancelled
Dokploy Monitoring Build / docker-arm (push) Has been cancelled
Dokploy Docker Build / combine-manifests (push) Has been cancelled
Dokploy Docker Build / generate-release (push) Has been cancelled
Dokploy Monitoring Build / combine-manifests (push) Has been cancelled
Some checks failed
Auto PR to main when version changes / create-pr (push) Has been cancelled
Build Docker images / build-and-push-cloud-image (push) Has been cancelled
Build Docker images / build-and-push-schedule-image (push) Has been cancelled
Build Docker images / build-and-push-server-image (push) Has been cancelled
Dokploy Docker Build / docker-amd (push) Has been cancelled
Dokploy Docker Build / docker-arm (push) Has been cancelled
autofix.ci / format (push) Has been cancelled
Dokploy Monitoring Build / docker-amd (push) Has been cancelled
Dokploy Monitoring Build / docker-arm (push) Has been cancelled
Dokploy Docker Build / combine-manifests (push) Has been cancelled
Dokploy Docker Build / generate-release (push) Has been cancelled
Dokploy Monitoring Build / combine-manifests (push) Has been cancelled
feat(github): add triggerType field to GitHub provider and handle tag creation events
This commit is contained in:
@@ -36,6 +36,7 @@ const baseApp: ApplicationNested = {
|
||||
watchPaths: [],
|
||||
enableSubmodules: false,
|
||||
applicationStatus: "done",
|
||||
triggerType: "push",
|
||||
appName: "",
|
||||
autoDeploy: true,
|
||||
serverId: "",
|
||||
|
||||
@@ -25,6 +25,7 @@ const baseApp: ApplicationNested = {
|
||||
buildArgs: null,
|
||||
isPreviewDeploymentsActive: false,
|
||||
previewBuildArgs: null,
|
||||
triggerType: "push",
|
||||
previewCertificateType: "none",
|
||||
previewEnv: null,
|
||||
previewHttps: false,
|
||||
|
||||
@@ -58,6 +58,7 @@ const GithubProviderSchema = z.object({
|
||||
branch: z.string().min(1, "Branch is required"),
|
||||
githubId: z.string().min(1, "Github Provider is required"),
|
||||
watchPaths: z.array(z.string()).optional(),
|
||||
triggerType: z.enum(["push", "tag"]).default("push"),
|
||||
enableSubmodules: z.boolean().default(false),
|
||||
});
|
||||
|
||||
@@ -83,6 +84,7 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
|
||||
},
|
||||
githubId: "",
|
||||
branch: "",
|
||||
triggerType: "push",
|
||||
enableSubmodules: false,
|
||||
},
|
||||
resolver: zodResolver(GithubProviderSchema),
|
||||
@@ -90,6 +92,7 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
|
||||
|
||||
const repository = form.watch("repository");
|
||||
const githubId = form.watch("githubId");
|
||||
const triggerType = form.watch("triggerType");
|
||||
|
||||
const { data: repositories, isLoading: isLoadingRepositories } =
|
||||
api.github.getGithubRepositories.useQuery(
|
||||
@@ -127,6 +130,7 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
|
||||
buildPath: data.buildPath || "/",
|
||||
githubId: data.githubId || "",
|
||||
watchPaths: data.watchPaths || [],
|
||||
triggerType: data.triggerType || "push",
|
||||
enableSubmodules: data.enableSubmodules ?? false,
|
||||
});
|
||||
}
|
||||
@@ -141,6 +145,7 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
|
||||
buildPath: data.buildPath,
|
||||
githubId: data.githubId,
|
||||
watchPaths: data.watchPaths || [],
|
||||
triggerType: data.triggerType,
|
||||
enableSubmodules: data.enableSubmodules,
|
||||
})
|
||||
.then(async () => {
|
||||
@@ -386,11 +391,11 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="watchPaths"
|
||||
name="triggerType"
|
||||
render={({ field }) => (
|
||||
<FormItem className="md:col-span-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<FormLabel>Watch Paths</FormLabel>
|
||||
<div className="flex items-center gap-2 ">
|
||||
<FormLabel>Trigger Type</FormLabel>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
@@ -398,71 +403,114 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>
|
||||
Add paths to watch for changes. When files in these
|
||||
paths change, a new deployment will be triggered.
|
||||
Choose when to trigger deployments: on push to the
|
||||
selected branch or when a new tag is created.
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2 mb-2">
|
||||
{field.value?.map((path, index) => (
|
||||
<Badge
|
||||
key={index}
|
||||
variant="secondary"
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
{path}
|
||||
<X
|
||||
className="size-3 cursor-pointer hover:text-destructive"
|
||||
onClick={() => {
|
||||
const newPaths = [...(field.value || [])];
|
||||
newPaths.splice(index, 1);
|
||||
field.onChange(newPaths);
|
||||
}}
|
||||
/>
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Select
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value}
|
||||
value={field.value}
|
||||
>
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder="Enter a path to watch (e.g., src/*, dist/*)"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
const input = e.currentTarget;
|
||||
const path = input.value.trim();
|
||||
if (path) {
|
||||
field.onChange([...(field.value || []), path]);
|
||||
input.value = "";
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select a trigger type" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={() => {
|
||||
const input = document.querySelector(
|
||||
'input[placeholder*="Enter a path"]',
|
||||
) as HTMLInputElement;
|
||||
const path = input.value.trim();
|
||||
if (path) {
|
||||
field.onChange([...(field.value || []), path]);
|
||||
input.value = "";
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Plus className="size-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<SelectContent>
|
||||
<SelectItem value="push">On Push</SelectItem>
|
||||
<SelectItem value="tag">On Tag</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{triggerType === "push" && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="watchPaths"
|
||||
render={({ field }) => (
|
||||
<FormItem className="md:col-span-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<FormLabel>Watch Paths</FormLabel>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>
|
||||
Add paths to watch for changes. When files in
|
||||
these paths change, a new deployment will be
|
||||
triggered.
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2 mb-2">
|
||||
{field.value?.map((path, index) => (
|
||||
<Badge
|
||||
key={index}
|
||||
variant="secondary"
|
||||
className="flex items-center gap-1"
|
||||
>
|
||||
{path}
|
||||
<X
|
||||
className="size-3 cursor-pointer hover:text-destructive"
|
||||
onClick={() => {
|
||||
const newPaths = [...(field.value || [])];
|
||||
newPaths.splice(index, 1);
|
||||
field.onChange(newPaths);
|
||||
}}
|
||||
/>
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<FormControl>
|
||||
<Input
|
||||
placeholder="Enter a path to watch (e.g., src/*, dist/*)"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
const input = e.currentTarget;
|
||||
const path = input.value.trim();
|
||||
if (path) {
|
||||
field.onChange([...(field.value || []), path]);
|
||||
input.value = "";
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</FormControl>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="icon"
|
||||
onClick={() => {
|
||||
const input = document.querySelector(
|
||||
'input[placeholder*="Enter a path"]',
|
||||
) as HTMLInputElement;
|
||||
const path = input.value.trim();
|
||||
if (path) {
|
||||
field.onChange([...(field.value || []), path]);
|
||||
input.value = "";
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Plus className="size-4" />
|
||||
</Button>
|
||||
</div>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<FormField
|
||||
control={form.control}
|
||||
|
||||
@@ -40,7 +40,7 @@ import {
|
||||
import { cn } from "@/lib/utils";
|
||||
import { api } from "@/utils/api";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { CheckIcon, ChevronsUpDown, X } from "lucide-react";
|
||||
import { CheckIcon, ChevronsUpDown, HelpCircle, X } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { useEffect } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
@@ -58,6 +58,7 @@ const GithubProviderSchema = z.object({
|
||||
branch: z.string().min(1, "Branch is required"),
|
||||
githubId: z.string().min(1, "Github Provider is required"),
|
||||
watchPaths: z.array(z.string()).optional(),
|
||||
triggerType: z.enum(["push", "tag"]).default("push"),
|
||||
enableSubmodules: z.boolean().default(false),
|
||||
});
|
||||
|
||||
@@ -84,6 +85,7 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => {
|
||||
githubId: "",
|
||||
branch: "",
|
||||
watchPaths: [],
|
||||
triggerType: "push",
|
||||
enableSubmodules: false,
|
||||
},
|
||||
resolver: zodResolver(GithubProviderSchema),
|
||||
@@ -91,7 +93,7 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => {
|
||||
|
||||
const repository = form.watch("repository");
|
||||
const githubId = form.watch("githubId");
|
||||
|
||||
const triggerType = form.watch("triggerType");
|
||||
const { data: repositories, isLoading: isLoadingRepositories } =
|
||||
api.github.getGithubRepositories.useQuery(
|
||||
{
|
||||
@@ -128,6 +130,7 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => {
|
||||
composePath: data.composePath,
|
||||
githubId: data.githubId || "",
|
||||
watchPaths: data.watchPaths || [],
|
||||
triggerType: data.triggerType || "push",
|
||||
enableSubmodules: data.enableSubmodules ?? false,
|
||||
});
|
||||
}
|
||||
@@ -145,6 +148,7 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => {
|
||||
composeStatus: "idle",
|
||||
watchPaths: data.watchPaths,
|
||||
enableSubmodules: data.enableSubmodules,
|
||||
triggerType: data.triggerType,
|
||||
})
|
||||
.then(async () => {
|
||||
toast.success("Service Provided Saved");
|
||||
@@ -389,82 +393,128 @@ export const SaveGithubProviderCompose = ({ composeId }: Props) => {
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="watchPaths"
|
||||
name="triggerType"
|
||||
render={({ field }) => (
|
||||
<FormItem className="md:col-span-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<FormLabel>Watch Paths</FormLabel>
|
||||
<FormLabel>Trigger Type</FormLabel>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<div className="size-4 rounded-full bg-muted flex items-center justify-center text-[10px] font-bold">
|
||||
?
|
||||
</div>
|
||||
<TooltipTrigger asChild>
|
||||
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>
|
||||
Add paths to watch for changes. When files in these
|
||||
paths change, a new deployment will be triggered.
|
||||
Choose when to trigger deployments: on push to the
|
||||
selected branch or when a new tag is created.
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2 mb-2">
|
||||
{field.value?.map((path, index) => (
|
||||
<Badge key={index} variant="secondary">
|
||||
{path}
|
||||
<X
|
||||
className="ml-1 size-3 cursor-pointer"
|
||||
onClick={() => {
|
||||
const newPaths = [...(field.value || [])];
|
||||
newPaths.splice(index, 1);
|
||||
form.setValue("watchPaths", newPaths);
|
||||
<Select
|
||||
onValueChange={field.onChange}
|
||||
defaultValue={field.value}
|
||||
value={field.value}
|
||||
>
|
||||
<FormControl>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select a trigger type" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
<SelectItem value="push">On Push</SelectItem>
|
||||
<SelectItem value="tag">On Tag</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{triggerType === "push" && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="watchPaths"
|
||||
render={({ field }) => (
|
||||
<FormItem className="md:col-span-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<FormLabel>Watch Paths</FormLabel>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<div className="size-4 rounded-full bg-muted flex items-center justify-center text-[10px] font-bold">
|
||||
?
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>
|
||||
Add paths to watch for changes. When files in
|
||||
these paths change, a new deployment will be
|
||||
triggered.
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-2 mb-2">
|
||||
{field.value?.map((path, index) => (
|
||||
<Badge key={index} variant="secondary">
|
||||
{path}
|
||||
<X
|
||||
className="ml-1 size-3 cursor-pointer"
|
||||
onClick={() => {
|
||||
const newPaths = [...(field.value || [])];
|
||||
newPaths.splice(index, 1);
|
||||
form.setValue("watchPaths", newPaths);
|
||||
}}
|
||||
/>
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
<FormControl>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
placeholder="Enter a path to watch (e.g., src/*, dist/*)"
|
||||
onKeyDown={(e) => {
|
||||
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 = "";
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Badge>
|
||||
))}
|
||||
</div>
|
||||
<FormControl>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
placeholder="Enter a path to watch (e.g., src/*, dist/*)"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
const input = e.currentTarget;
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
const input = document.querySelector(
|
||||
'input[placeholder="Enter a path to watch (e.g., src/*, dist/*)"]',
|
||||
) as HTMLInputElement;
|
||||
const value = input.value.trim();
|
||||
if (value) {
|
||||
const newPaths = [...(field.value || []), value];
|
||||
form.setValue("watchPaths", newPaths);
|
||||
input.value = "";
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
const input = document.querySelector(
|
||||
'input[placeholder="Enter a path to watch (e.g., src/*, dist/*)"]',
|
||||
) as HTMLInputElement;
|
||||
const value = input.value.trim();
|
||||
if (value) {
|
||||
const newPaths = [...(field.value || []), value];
|
||||
form.setValue("watchPaths", newPaths);
|
||||
input.value = "";
|
||||
}
|
||||
}}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
}}
|
||||
>
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="enableSubmodules"
|
||||
|
||||
3
apps/dokploy/drizzle/0087_lively_risque.sql
Normal file
3
apps/dokploy/drizzle/0087_lively_risque.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
CREATE TYPE "public"."triggerType" AS ENUM('push', 'tag');--> statement-breakpoint
|
||||
ALTER TABLE "application" ADD COLUMN "triggerType" "triggerType" DEFAULT 'push';--> statement-breakpoint
|
||||
ALTER TABLE "compose" ADD COLUMN "triggerType" "triggerType" DEFAULT 'push';
|
||||
5407
apps/dokploy/drizzle/meta/0087_snapshot.json
Normal file
5407
apps/dokploy/drizzle/meta/0087_snapshot.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -610,6 +610,13 @@
|
||||
"when": 1745706676004,
|
||||
"tag": "0086_rainy_gertrude_yorkes",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 87,
|
||||
"version": "7",
|
||||
"when": 1745723563822,
|
||||
"tag": "0087_lively_risque",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -89,6 +89,115 @@ export default async function handler(
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle tag creation event
|
||||
if (
|
||||
req.headers["x-github-event"] === "push" &&
|
||||
githubBody?.ref?.startsWith("refs/tags/")
|
||||
) {
|
||||
try {
|
||||
const tagName = githubBody?.ref.replace("refs/tags/", "");
|
||||
const repository = githubBody?.repository?.name;
|
||||
const owner = githubBody?.repository?.owner?.name;
|
||||
const deploymentTitle = `Tag created: ${tagName}`;
|
||||
const deploymentHash = extractHash(req.headers, githubBody);
|
||||
|
||||
// Find applications configured to deploy on tag
|
||||
const apps = await db.query.applications.findMany({
|
||||
where: and(
|
||||
eq(applications.sourceType, "github"),
|
||||
eq(applications.autoDeploy, true),
|
||||
eq(applications.triggerType, "tag"),
|
||||
eq(applications.repository, repository),
|
||||
eq(applications.owner, owner),
|
||||
eq(applications.githubId, githubResult.githubId),
|
||||
),
|
||||
});
|
||||
|
||||
for (const app of apps) {
|
||||
const jobData: DeploymentJob = {
|
||||
applicationId: app.applicationId as string,
|
||||
titleLog: deploymentTitle,
|
||||
descriptionLog: `Hash: ${deploymentHash}`,
|
||||
type: "deploy",
|
||||
applicationType: "application",
|
||||
server: !!app.serverId,
|
||||
};
|
||||
|
||||
if (IS_CLOUD && app.serverId) {
|
||||
jobData.serverId = app.serverId;
|
||||
await deploy(jobData);
|
||||
continue;
|
||||
}
|
||||
await myQueue.add(
|
||||
"deployments",
|
||||
{ ...jobData },
|
||||
{
|
||||
removeOnComplete: true,
|
||||
removeOnFail: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// Find compose apps configured to deploy on tag
|
||||
const composeApps = await db.query.compose.findMany({
|
||||
where: and(
|
||||
eq(compose.sourceType, "github"),
|
||||
eq(compose.autoDeploy, true),
|
||||
eq(compose.triggerType, "tag"),
|
||||
eq(compose.repository, repository),
|
||||
eq(compose.owner, owner),
|
||||
eq(compose.githubId, githubResult.githubId),
|
||||
),
|
||||
});
|
||||
|
||||
for (const composeApp of composeApps) {
|
||||
const jobData: DeploymentJob = {
|
||||
composeId: composeApp.composeId as string,
|
||||
titleLog: deploymentTitle,
|
||||
type: "deploy",
|
||||
applicationType: "compose",
|
||||
descriptionLog: `Hash: ${deploymentHash}`,
|
||||
server: !!composeApp.serverId,
|
||||
};
|
||||
|
||||
if (IS_CLOUD && composeApp.serverId) {
|
||||
jobData.serverId = composeApp.serverId;
|
||||
await deploy(jobData);
|
||||
continue;
|
||||
}
|
||||
|
||||
await myQueue.add(
|
||||
"deployments",
|
||||
{ ...jobData },
|
||||
{
|
||||
removeOnComplete: true,
|
||||
removeOnFail: true,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const totalApps = apps.length + composeApps.length;
|
||||
|
||||
if (totalApps === 0) {
|
||||
res
|
||||
.status(200)
|
||||
.json({ message: "No apps configured to deploy on tag" });
|
||||
return;
|
||||
}
|
||||
|
||||
res.status(200).json({
|
||||
message: `Deployed ${totalApps} apps based on tag ${tagName}`,
|
||||
});
|
||||
return;
|
||||
} catch (error) {
|
||||
console.error("Error deploying applications on tag:", error);
|
||||
res
|
||||
.status(400)
|
||||
.json({ message: "Error deploying applications on tag", error });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (req.headers["x-github-event"] === "push") {
|
||||
try {
|
||||
const branchName = githubBody?.ref?.replace("refs/heads/", "");
|
||||
@@ -105,6 +214,7 @@ export default async function handler(
|
||||
where: and(
|
||||
eq(applications.sourceType, "github"),
|
||||
eq(applications.autoDeploy, true),
|
||||
eq(applications.triggerType, "push"),
|
||||
eq(applications.branch, branchName),
|
||||
eq(applications.repository, repository),
|
||||
eq(applications.owner, owner),
|
||||
@@ -150,6 +260,7 @@ export default async function handler(
|
||||
where: and(
|
||||
eq(compose.sourceType, "github"),
|
||||
eq(compose.autoDeploy, true),
|
||||
eq(compose.triggerType, "push"),
|
||||
eq(compose.branch, branchName),
|
||||
eq(compose.repository, repository),
|
||||
eq(compose.owner, owner),
|
||||
|
||||
@@ -355,6 +355,7 @@ export const applicationRouter = createTRPCRouter({
|
||||
applicationStatus: "idle",
|
||||
githubId: input.githubId,
|
||||
watchPaths: input.watchPaths,
|
||||
triggerType: input.triggerType,
|
||||
enableSubmodules: input.enableSubmodules,
|
||||
});
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ import { redirects } from "./redirects";
|
||||
import { registry } from "./registry";
|
||||
import { security } from "./security";
|
||||
import { server } from "./server";
|
||||
import { applicationStatus, certificateType } from "./shared";
|
||||
import { applicationStatus, certificateType, triggerType } from "./shared";
|
||||
import { sshKeys } from "./ssh-key";
|
||||
import { generateAppName } from "./utils";
|
||||
|
||||
@@ -149,6 +149,7 @@ export const applications = pgTable("application", {
|
||||
owner: text("owner"),
|
||||
branch: text("branch"),
|
||||
buildPath: text("buildPath").default("/"),
|
||||
triggerType: triggerType("triggerType").default("push"),
|
||||
autoDeploy: boolean("autoDeploy").$defaultFn(() => true),
|
||||
// Gitlab
|
||||
gitlabProjectId: integer("gitlabProjectId"),
|
||||
@@ -473,7 +474,10 @@ export const apiSaveGithubProvider = createSchema
|
||||
watchPaths: true,
|
||||
enableSubmodules: true,
|
||||
})
|
||||
.required();
|
||||
.required()
|
||||
.extend({
|
||||
triggerType: z.enum(["push", "tag"]).default("push"),
|
||||
});
|
||||
|
||||
export const apiSaveGitlabProvider = createSchema
|
||||
.pick({
|
||||
|
||||
@@ -12,7 +12,7 @@ import { gitlab } from "./gitlab";
|
||||
import { mounts } from "./mount";
|
||||
import { projects } from "./project";
|
||||
import { server } from "./server";
|
||||
import { applicationStatus } from "./shared";
|
||||
import { applicationStatus, triggerType } from "./shared";
|
||||
import { sshKeys } from "./ssh-key";
|
||||
import { generateAppName } from "./utils";
|
||||
|
||||
@@ -77,6 +77,7 @@ export const compose = pgTable("compose", {
|
||||
suffix: text("suffix").notNull().default(""),
|
||||
randomize: boolean("randomize").notNull().default(false),
|
||||
isolatedDeployment: boolean("isolatedDeployment").notNull().default(false),
|
||||
triggerType: triggerType("triggerType").default("push"),
|
||||
composeStatus: applicationStatus("composeStatus").notNull().default("idle"),
|
||||
projectId: text("projectId")
|
||||
.notNull()
|
||||
|
||||
@@ -12,3 +12,5 @@ export const certificateType = pgEnum("certificateType", [
|
||||
"none",
|
||||
"custom",
|
||||
]);
|
||||
|
||||
export const triggerType = pgEnum("triggerType", ["push", "tag"]);
|
||||
|
||||
Reference in New Issue
Block a user