Skip to content

Commit e6d05a7

Browse files
feat: add git commit info extraction and GIT_HASH injection for manual deployments - Extract commit message and hash for manual deployments from Git providers - Inject GIT_HASH environment variable into applications and compose services - Support GitHub, GitLab, Bitbucket, Gitea, and Git providers - Exclude Docker provider from git info extraction - Maintain backward compatibility with auto-deploy functionality
1 parent 24729f3 commit e6d05a7

File tree

2 files changed

+284
-13
lines changed

2 files changed

+284
-13
lines changed

packages/server/src/services/application.ts

Lines changed: 114 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { docker } from "@dokploy/server/constants";
1+
import { docker, paths } from "@dokploy/server/constants";
22
import { db } from "@dokploy/server/db";
33
import {
44
type apiCreateApplication,
@@ -13,7 +13,7 @@ import {
1313
} from "@dokploy/server/utils/builders";
1414
import { sendBuildErrorNotifications } from "@dokploy/server/utils/notifications/build-error";
1515
import { sendBuildSuccessNotifications } from "@dokploy/server/utils/notifications/build-success";
16-
import { execAsyncRemote } from "@dokploy/server/utils/process/execAsync";
16+
import { execAsync, execAsyncRemote } from "@dokploy/server/utils/process/execAsync";
1717
import {
1818
cloneBitbucketRepository,
1919
getBitbucketCloneCommand,
@@ -46,8 +46,10 @@ import { getDokployUrl } from "./admin";
4646
import {
4747
createDeployment,
4848
createDeploymentPreview,
49+
updateDeployment,
4950
updateDeploymentStatus,
5051
} from "./deployment";
52+
import { join } from "node:path";
5153
import { type Domain, getDomainHost } from "./domain";
5254
import {
5355
createPreviewDeploymentComment,
@@ -63,6 +65,80 @@ import { validUniqueServerAppName } from "./project";
6365
import { createRollback } from "./rollbacks";
6466
export type Application = typeof applications.$inferSelect;
6567

68+
const tryUpdateWithGitInfoLocal = async (
69+
application: Application & { appName: string; env: string | null },
70+
deploymentId: string,
71+
defaultTitle: string,
72+
defaultDescription: string,
73+
) => {
74+
try {
75+
const { APPLICATIONS_PATH } = paths();
76+
const codePath = join(APPLICATIONS_PATH, application.appName, "code");
77+
const { stdout: hashStdout } = await execAsync(
78+
`git -C ${codePath} rev-parse HEAD`,
79+
);
80+
const gitHash = hashStdout.trim();
81+
const { stdout: msgStdout } = await execAsync(
82+
`git -C ${codePath} log -1 --pretty=%B`,
83+
);
84+
const gitMessage = msgStdout.trim();
85+
if (gitHash) {
86+
const current = application.env || "";
87+
const withoutOld = current
88+
.split("\n")
89+
.filter((l) => l.trim() && !l.startsWith("GIT_HASH="))
90+
.join("\n");
91+
const newEnv = `${withoutOld}${withoutOld ? "\n" : ""}GIT_HASH=${gitHash}`;
92+
application.env = newEnv;
93+
await updateApplication(application.applicationId, { env: newEnv });
94+
}
95+
await updateDeployment(deploymentId, {
96+
title: gitMessage || defaultTitle,
97+
description: gitHash ? `Hash: ${gitHash}` : defaultDescription,
98+
});
99+
} catch {
100+
// ignore if git data can't be fetched
101+
}
102+
};
103+
104+
const tryUpdateWithGitInfoRemote = async (
105+
application: Application & { appName: string; env: string | null; serverId: string },
106+
deploymentId: string,
107+
defaultTitle: string,
108+
defaultDescription: string,
109+
) => {
110+
try {
111+
const { APPLICATIONS_PATH } = paths(true);
112+
const codePath = join(APPLICATIONS_PATH, application.appName, "code");
113+
const { stdout: hashStdout } = await execAsyncRemote(
114+
application.serverId,
115+
`git -C ${codePath} rev-parse HEAD | tr -d '\n'`,
116+
);
117+
const gitHash = (hashStdout || "").trim();
118+
const { stdout: msgStdout } = await execAsyncRemote(
119+
application.serverId,
120+
`git -C ${codePath} log -1 --pretty=%B`,
121+
);
122+
const gitMessage = (msgStdout || "").trim();
123+
if (gitHash) {
124+
const current = application.env || "";
125+
const withoutOld = current
126+
.split("\n")
127+
.filter((l) => l.trim() && !l.startsWith("GIT_HASH="))
128+
.join("\n");
129+
const newEnv = `${withoutOld}${withoutOld ? "\n" : ""}GIT_HASH=${gitHash}`;
130+
application.env = newEnv;
131+
await updateApplication(application.applicationId, { env: newEnv });
132+
}
133+
await updateDeployment(deploymentId, {
134+
title: gitMessage || defaultTitle,
135+
description: gitHash ? `Hash: ${gitHash}` : defaultDescription,
136+
});
137+
} catch {
138+
// ignore if git data can't be fetched
139+
}
140+
};
141+
66142
export const createApplication = async (
67143
input: typeof apiCreateApplication._type,
68144
) => {
@@ -197,20 +273,50 @@ export const deployApplication = async ({
197273
...application,
198274
logPath: deployment.logPath,
199275
});
276+
await tryUpdateWithGitInfoLocal(
277+
application as any,
278+
deployment.deploymentId,
279+
titleLog,
280+
descriptionLog,
281+
);
200282
await buildApplication(application, deployment.logPath);
201283
} else if (application.sourceType === "gitlab") {
202284
await cloneGitlabRepository(application, deployment.logPath);
285+
await tryUpdateWithGitInfoLocal(
286+
application as any,
287+
deployment.deploymentId,
288+
titleLog,
289+
descriptionLog,
290+
);
203291
await buildApplication(application, deployment.logPath);
204292
} else if (application.sourceType === "gitea") {
205293
await cloneGiteaRepository(application, deployment.logPath);
294+
await tryUpdateWithGitInfoLocal(
295+
application as any,
296+
deployment.deploymentId,
297+
titleLog,
298+
descriptionLog,
299+
);
206300
await buildApplication(application, deployment.logPath);
207301
} else if (application.sourceType === "bitbucket") {
208302
await cloneBitbucketRepository(application, deployment.logPath);
303+
await tryUpdateWithGitInfoLocal(
304+
application as any,
305+
deployment.deploymentId,
306+
titleLog,
307+
descriptionLog,
308+
);
209309
await buildApplication(application, deployment.logPath);
210310
} else if (application.sourceType === "docker") {
211311
await buildDocker(application, deployment.logPath);
212312
} else if (application.sourceType === "git") {
213313
await cloneGitRepository(application, deployment.logPath);
314+
await tryUpdateWithGitInfoLocal(
315+
application as any,
316+
deployment.deploymentId,
317+
titleLog,
318+
descriptionLog,
319+
);
214320
await buildApplication(application, deployment.logPath);
215321
} else if (application.sourceType === "drop") {
216322
await buildApplication(application, deployment.logPath);
@@ -349,6 +455,12 @@ export const deployRemoteApplication = async ({
349455
command += getBuildCommand(application, deployment.logPath);
350456
}
351457
await execAsyncRemote(application.serverId, command);
458+
await tryUpdateWithGitInfoRemote(
459+
application as any,
460+
deployment.deploymentId,
461+
titleLog,
462+
descriptionLog,
463+
);
352464
await mechanizeDockerContainer(application);
353465
}
354466

0 commit comments

Comments
 (0)