Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(github): add triggerType field to GitHub provider and handle tag creation events #1613

Open
wants to merge 4 commits into
base: canary
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,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"),
});

type GithubProvider = z.infer<typeof GithubProviderSchema>;
Expand Down Expand Up @@ -124,6 +125,7 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
buildPath: data.buildPath || "/",
githubId: data.githubId || "",
watchPaths: data.watchPaths || [],
triggerType: data.triggerType || "push",
});
}
}, [form.reset, data, form]);
Expand All @@ -137,6 +139,7 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
buildPath: data.buildPath,
githubId: data.githubId,
watchPaths: data.watchPaths || [],
triggerType: data.triggerType,
})
.then(async () => {
toast.success("Service Provided Saved");
Expand Down Expand Up @@ -379,6 +382,46 @@ export const SaveGithubProvider = ({ applicationId }: Props) => {
</FormItem>
)}
/>
<FormField
control={form.control}
name="triggerType"
render={({ field }) => (
<FormItem>
<div className="flex items-center gap-2">
<FormLabel>Trigger Type</FormLabel>
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<HelpCircle className="size-4 text-muted-foreground hover:text-foreground transition-colors cursor-pointer" />
</TooltipTrigger>
<TooltipContent>
<p>
Choose when to trigger deployments: on push to the
selected branch or when a new tag is created.
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
<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>
)}
/>
<FormField
control={form.control}
name="watchPaths"
Expand Down
5 changes: 5 additions & 0 deletions apps/dokploy/drizzle/0084_add_trigger_type.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-- Add triggerType column to application table
ALTER TABLE "application" ADD COLUMN IF NOT EXISTS "triggerType" text DEFAULT 'push';

-- Add triggerType column to compose table
ALTER TABLE "compose" ADD COLUMN IF NOT EXISTS "triggerType" text DEFAULT 'push';
118 changes: 116 additions & 2 deletions apps/dokploy/pages/api/deploy/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,12 @@ export default async function handler(

if (
req.headers["x-github-event"] !== "push" &&
req.headers["x-github-event"] !== "pull_request"
req.headers["x-github-event"] !== "pull_request" &&
req.headers["x-github-event"] !== "create"
) {
res
.status(400)
.json({ message: "We only accept push events or pull_request events" });
.json({ message: "We only accept push, pull_request, or create events" });
return;
}

Expand All @@ -89,6 +90,117 @@ export default async function handler(
return;
}

// Handle tag creation event
if (
req.headers["x-github-event"] === "create" &&
githubBody?.ref_type === "tag"
) {
try {
const tagName = githubBody?.ref;
const repository = githubBody?.repository?.name;
const owner =
githubBody?.repository?.owner?.name ||
githubBody?.repository?.owner?.login;
const deploymentTitle = `Tag created: ${tagName}`;
const deploymentHash = githubBody?.master_branch || "";

// 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: `Tag: ${tagName}`,
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: `Tag: ${tagName}`,
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/", "");
Expand All @@ -105,6 +217,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),
Expand Down Expand Up @@ -150,6 +263,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),
Expand Down
1 change: 1 addition & 0 deletions apps/dokploy/server/api/routers/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,7 @@ export const applicationRouter = createTRPCRouter({
applicationStatus: "idle",
githubId: input.githubId,
watchPaths: input.watchPaths,
triggerType: input.triggerType,
});

return true;
Expand Down
6 changes: 5 additions & 1 deletion packages/server/src/db/schema/application.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export const applications = pgTable("application", {
owner: text("owner"),
branch: text("branch"),
buildPath: text("buildPath").default("/"),
triggerType: text("triggerType").default("push"),
autoDeploy: boolean("autoDeploy").$defaultFn(() => true),
// Gitlab
gitlabProjectId: integer("gitlabProjectId"),
Expand Down Expand Up @@ -471,7 +472,10 @@ export const apiSaveGithubProvider = createSchema
githubId: true,
watchPaths: true,
})
.required();
.required()
.extend({
triggerType: z.enum(["push", "tag"]).default("push"),
});

export const apiSaveGitlabProvider = createSchema
.pick({
Expand Down
1 change: 1 addition & 0 deletions packages/server/src/db/schema/compose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ export const compose = pgTable("compose", {
suffix: text("suffix").notNull().default(""),
randomize: boolean("randomize").notNull().default(false),
isolatedDeployment: boolean("isolatedDeployment").notNull().default(false),
triggerType: text("triggerType").default("push"),
composeStatus: applicationStatus("composeStatus").notNull().default("idle"),
projectId: text("projectId")
.notNull()
Expand Down