diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index 6c74dbc02..31dbc48fb 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -20,6 +20,32 @@ jobs: with: node-version: 20.16.0 cache: "pnpm" + + - name: Install Nixpacks + if: matrix.job == 'test' + run: | + export NIXPACKS_VERSION=1.39.0 + curl -sSL https://nixpacks.com/install.sh | bash + echo "Nixpacks installed $NIXPACKS_VERSION" + + - name: Install Railpack + if: matrix.job == 'test' + run: | + export RAILPACK_VERSION=0.15.0 + curl -sSL https://railpack.com/install.sh | bash + echo "Railpack installed $RAILPACK_VERSION" + + - name: Add build tools to PATH + if: matrix.job == 'test' + run: echo "$HOME/.local/bin" >> $GITHUB_PATH + + - name: Initialize Docker Swarm + if: matrix.job == 'test' + run: | + docker swarm init + docker network create --driver overlay dokploy-network || true + echo "โœ… Docker Swarm initialized" + - run: pnpm install --frozen-lockfile - run: pnpm server:build - run: pnpm ${{ matrix.job }} diff --git a/apps/dokploy/__test__/deploy/application.command.test.ts b/apps/dokploy/__test__/deploy/application.command.test.ts new file mode 100644 index 000000000..a0c4387c8 --- /dev/null +++ b/apps/dokploy/__test__/deploy/application.command.test.ts @@ -0,0 +1,276 @@ +import * as adminService from "@dokploy/server/services/admin"; +import * as applicationService from "@dokploy/server/services/application"; +import { deployApplication } from "@dokploy/server/services/application"; +import * as deploymentService from "@dokploy/server/services/deployment"; +import * as builders from "@dokploy/server/utils/builders"; +import * as notifications from "@dokploy/server/utils/notifications/build-success"; +import * as execProcess from "@dokploy/server/utils/process/execAsync"; +import * as gitProvider from "@dokploy/server/utils/providers/git"; +import { beforeEach, describe, expect, it, vi } from "vitest"; + +vi.mock("@dokploy/server/db", () => { + const createChainableMock = (): any => { + const chain = { + set: vi.fn(() => chain), + where: vi.fn(() => chain), + returning: vi.fn().mockResolvedValue([{}] as any), + } as any; + return chain; + }; + + return { + db: { + select: vi.fn(), + insert: vi.fn(), + update: vi.fn(() => createChainableMock()), + delete: vi.fn(), + query: { + applications: { + findFirst: vi.fn(), + }, + }, + }, + }; +}); + +vi.mock("@dokploy/server/services/application", async () => { + const actual = await vi.importActual< + typeof import("@dokploy/server/services/application") + >("@dokploy/server/services/application"); + return { + ...actual, + findApplicationById: vi.fn(), + updateApplicationStatus: vi.fn(), + }; +}); + +vi.mock("@dokploy/server/services/admin", () => ({ + getDokployUrl: vi.fn(), +})); + +vi.mock("@dokploy/server/services/deployment", () => ({ + createDeployment: vi.fn(), + updateDeploymentStatus: vi.fn(), + updateDeployment: vi.fn(), +})); + +vi.mock("@dokploy/server/utils/providers/git", async () => { + const actual = await vi.importActual< + typeof import("@dokploy/server/utils/providers/git") + >("@dokploy/server/utils/providers/git"); + return { + ...actual, + getGitCommitInfo: vi.fn(), + }; +}); + +vi.mock("@dokploy/server/utils/process/execAsync", () => ({ + execAsync: vi.fn(), + ExecError: class ExecError extends Error {}, +})); + +vi.mock("@dokploy/server/utils/builders", async () => { + const actual = await vi.importActual< + typeof import("@dokploy/server/utils/builders") + >("@dokploy/server/utils/builders"); + return { + ...actual, + mechanizeDockerContainer: vi.fn(), + getBuildCommand: vi.fn(), + }; +}); + +vi.mock("@dokploy/server/utils/notifications/build-success", () => ({ + sendBuildSuccessNotifications: vi.fn(), +})); + +vi.mock("@dokploy/server/utils/notifications/build-error", () => ({ + sendBuildErrorNotifications: vi.fn(), +})); + +vi.mock("@dokploy/server/services/rollbacks", () => ({ + createRollback: vi.fn(), +})); + +import { db } from "@dokploy/server/db"; +import { cloneGitRepository } from "@dokploy/server/utils/providers/git"; + +const createMockApplication = (overrides = {}) => ({ + applicationId: "test-app-id", + name: "Test App", + appName: "test-app", + sourceType: "git" as const, + customGitUrl: "https://github.com/Dokploy/examples.git", + customGitBranch: "main", + customGitSSHKeyId: null, + buildType: "nixpacks" as const, + buildPath: "/astro", + env: "NODE_ENV=production", + serverId: null, + rollbackActive: false, + enableSubmodules: false, + environmentId: "env-id", + environment: { + projectId: "project-id", + env: "", + name: "production", + project: { + name: "Test Project", + organizationId: "org-id", + env: "", + }, + }, + domains: [], + ...overrides, +}); + +const createMockDeployment = () => ({ + deploymentId: "deployment-id", + logPath: "/tmp/test-deployment.log", +}); + +describe("deployApplication - Command Generation Tests", () => { + beforeEach(() => { + vi.clearAllMocks(); + vi.mocked(db.query.applications.findFirst).mockResolvedValue( + createMockApplication() as any, + ); + vi.mocked(applicationService.findApplicationById).mockResolvedValue( + createMockApplication() as any, + ); + vi.mocked(adminService.getDokployUrl).mockResolvedValue( + "http://localhost:3000", + ); + vi.mocked(deploymentService.createDeployment).mockResolvedValue( + createMockDeployment() as any, + ); + vi.mocked(execProcess.execAsync).mockResolvedValue({ + stdout: "", + stderr: "", + } as any); + vi.mocked(builders.mechanizeDockerContainer).mockResolvedValue( + undefined as any, + ); + vi.mocked(deploymentService.updateDeploymentStatus).mockResolvedValue( + undefined as any, + ); + vi.mocked(applicationService.updateApplicationStatus).mockResolvedValue( + {} as any, + ); + vi.mocked(notifications.sendBuildSuccessNotifications).mockResolvedValue( + undefined as any, + ); + vi.mocked(gitProvider.getGitCommitInfo).mockResolvedValue({ + message: "test commit", + hash: "abc123", + }); + vi.mocked(deploymentService.updateDeployment).mockResolvedValue({} as any); + }); + + it("should generate correct git clone command for astro example", async () => { + const app = createMockApplication(); + const command = await cloneGitRepository(app); + console.log(command); + + expect(command).toContain("https://github.com/Dokploy/examples.git"); + expect(command).not.toContain("--recurse-submodules"); + expect(command).toContain("--branch main"); + expect(command).toContain("--depth 1"); + expect(command).toContain("git clone"); + }); + + it("should generate git clone with submodules when enabled", async () => { + const app = createMockApplication({ enableSubmodules: true }); + const command = await cloneGitRepository(app); + + expect(command).toContain("--recurse-submodules"); + expect(command).toContain("https://github.com/Dokploy/examples.git"); + }); + + it("should verify nixpacks command is called with correct app", async () => { + const mockNixpacksCommand = "nixpacks build /path/to/app --name test-app"; + vi.mocked(builders.getBuildCommand).mockReturnValue(mockNixpacksCommand); + + await deployApplication({ + applicationId: "test-app-id", + titleLog: "Test deployment", + descriptionLog: "", + }); + + expect(builders.getBuildCommand).toHaveBeenCalledWith( + expect.objectContaining({ + buildType: "nixpacks", + customGitUrl: "https://github.com/Dokploy/examples.git", + buildPath: "/astro", + }), + ); + + expect(execProcess.execAsync).toHaveBeenCalledWith( + expect.stringContaining("nixpacks build"), + ); + }); + + it("should verify railpack command includes correct parameters", async () => { + const mockApp = createMockApplication({ buildType: "railpack" }); + vi.mocked(db.query.applications.findFirst).mockResolvedValue( + mockApp as any, + ); + vi.mocked(applicationService.findApplicationById).mockResolvedValue( + mockApp as any, + ); + + const mockRailpackCommand = "railpack prepare /path/to/app"; + vi.mocked(builders.getBuildCommand).mockReturnValue(mockRailpackCommand); + + await deployApplication({ + applicationId: "test-app-id", + titleLog: "Railpack test", + descriptionLog: "", + }); + + expect(builders.getBuildCommand).toHaveBeenCalledWith( + expect.objectContaining({ + buildType: "railpack", + }), + ); + + expect(execProcess.execAsync).toHaveBeenCalledWith( + expect.stringContaining("railpack prepare"), + ); + }); + + it("should execute commands in correct order", async () => { + const mockNixpacksCommand = "nixpacks build"; + vi.mocked(builders.getBuildCommand).mockReturnValue(mockNixpacksCommand); + + await deployApplication({ + applicationId: "test-app-id", + titleLog: "Test", + descriptionLog: "", + }); + + const execCalls = vi.mocked(execProcess.execAsync).mock.calls; + expect(execCalls.length).toBeGreaterThan(0); + + const fullCommand = execCalls[0]?.[0]; + expect(fullCommand).toContain("set -e"); + expect(fullCommand).toContain("git clone"); + expect(fullCommand).toContain("nixpacks build"); + }); + + it("should include log redirection in command", async () => { + const mockCommand = "nixpacks build"; + vi.mocked(builders.getBuildCommand).mockReturnValue(mockCommand); + + await deployApplication({ + applicationId: "test-app-id", + titleLog: "Test", + descriptionLog: "", + }); + + const execCalls = vi.mocked(execProcess.execAsync).mock.calls; + const fullCommand = execCalls[0]?.[0]; + + expect(fullCommand).toContain(">> /tmp/test-deployment.log 2>&1"); + }); +}); diff --git a/apps/dokploy/__test__/deploy/application.real.test.ts b/apps/dokploy/__test__/deploy/application.real.test.ts new file mode 100644 index 000000000..43ff07836 --- /dev/null +++ b/apps/dokploy/__test__/deploy/application.real.test.ts @@ -0,0 +1,479 @@ +import { existsSync } from "node:fs"; +import path from "node:path"; +import type { ApplicationNested } from "@dokploy/server"; +import { paths } from "@dokploy/server/constants"; +import { execAsync } from "@dokploy/server/utils/process/execAsync"; +import { format } from "date-fns"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +const REAL_TEST_TIMEOUT = 180000; // 3 minutes + +// Mock ONLY database and notifications +vi.mock("@dokploy/server/db", () => { + const createChainableMock = (): any => { + const chain: any = { + set: vi.fn(() => chain), + where: vi.fn(() => chain), + returning: vi.fn().mockResolvedValue([{}]), + }; + return chain; + }; + + return { + db: { + select: vi.fn(), + insert: vi.fn(), + update: vi.fn(() => createChainableMock()), + delete: vi.fn(), + query: { + applications: { + findFirst: vi.fn(), + }, + }, + }, + }; +}); + +vi.mock("@dokploy/server/services/application", async () => { + const actual = await vi.importActual< + typeof import("@dokploy/server/services/application") + >("@dokploy/server/services/application"); + return { + ...actual, + findApplicationById: vi.fn(), + updateApplicationStatus: vi.fn(), + }; +}); + +vi.mock("@dokploy/server/services/admin", () => ({ + getDokployUrl: vi.fn().mockResolvedValue("http://localhost:3000"), +})); + +vi.mock("@dokploy/server/services/deployment", () => ({ + createDeployment: vi.fn(), + updateDeploymentStatus: vi.fn(), + updateDeployment: vi.fn(), +})); + +vi.mock("@dokploy/server/utils/notifications/build-success", () => ({ + sendBuildSuccessNotifications: vi.fn(), +})); + +vi.mock("@dokploy/server/utils/notifications/build-error", () => ({ + sendBuildErrorNotifications: vi.fn(), +})); + +vi.mock("@dokploy/server/services/rollbacks", () => ({ + createRollback: vi.fn(), +})); + +// NOT mocked (executed for real): +// - execAsync +// - cloneGitRepository +// - getBuildCommand +// - mechanizeDockerContainer (requires Docker Swarm) + +import { db } from "@dokploy/server/db"; +import * as adminService from "@dokploy/server/services/admin"; +import * as applicationService from "@dokploy/server/services/application"; +import { deployApplication } from "@dokploy/server/services/application"; +import * as deploymentService from "@dokploy/server/services/deployment"; + +const createMockApplication = ( + overrides: Partial = {}, +): ApplicationNested => + ({ + applicationId: "test-app-id", + name: "Real Test App", + appName: `real-test-${Date.now()}`, + sourceType: "git" as const, + customGitUrl: "https://github.com/Dokploy/examples.git", + customGitBranch: "main", + customGitSSHKeyId: null, + customGitBuildPath: "/astro", + buildType: "nixpacks" as const, + env: "NODE_ENV=production", + serverId: null, + rollbackActive: false, + enableSubmodules: false, + environmentId: "env-id", + environment: { + projectId: "project-id", + env: "", + name: "production", + project: { + name: "Test Project", + organizationId: "org-id", + env: "", + }, + }, + domains: [], + mounts: [], + security: [], + redirects: [], + ports: [], + registry: null, + ...overrides, + }) as ApplicationNested; + +const createMockDeployment = async (appName: string) => { + const { LOGS_PATH } = paths(false); // false = local, no remote server + const formattedDateTime = format(new Date(), "yyyy-MM-dd:HH:mm:ss"); + const fileName = `${appName}-${formattedDateTime}.log`; + const logFilePath = path.join(LOGS_PATH, appName, fileName); + + // Actually create the log directory + await execAsync(`mkdir -p ${path.dirname(logFilePath)}`); + await execAsync(`echo "Initializing deployment" > ${logFilePath}`); + + return { + deploymentId: "deployment-id", + logPath: logFilePath, + }; +}; + +async function cleanupDocker(appName: string) { + try { + await execAsync(`docker stop ${appName} 2>/dev/null || true`); + await execAsync(`docker rm ${appName} 2>/dev/null || true`); + await execAsync(`docker rmi ${appName} 2>/dev/null || true`); + } catch (error) { + console.log("Docker cleanup completed"); + } +} + +async function cleanupFiles(appName: string) { + try { + const { LOGS_PATH, APPLICATIONS_PATH } = paths(false); + + // Clean cloned code directories + const appPath = path.join(APPLICATIONS_PATH, appName); + await execAsync(`rm -rf ${appPath} 2>/dev/null || true`); + + // Clean logs for appName - removes entire folder + const logPath = path.join(LOGS_PATH, appName); + await execAsync(`rm -rf ${logPath} 2>/dev/null || true`); + + console.log(`โœ… Cleaned up files and logs for ${appName}`); + } catch (error) { + console.error(`โš ๏ธ Error during cleanup for ${appName}:`, error); + } +} + +describe( + "deployApplication - REAL Execution Tests", + () => { + let currentAppName: string; + let currentDeployment: any; + const allTestAppNames: string[] = []; + + beforeEach(async () => { + vi.clearAllMocks(); + currentAppName = `real-test-${Date.now()}`; + currentDeployment = await createMockDeployment(currentAppName); + allTestAppNames.push(currentAppName); + + const mockApp = createMockApplication({ appName: currentAppName }); + + vi.mocked(db.query.applications.findFirst).mockResolvedValue( + mockApp as any, + ); + vi.mocked(applicationService.findApplicationById).mockResolvedValue( + mockApp as any, + ); + vi.mocked(adminService.getDokployUrl).mockResolvedValue( + "http://localhost:3000", + ); + vi.mocked(deploymentService.createDeployment).mockResolvedValue( + currentDeployment as any, + ); + vi.mocked(deploymentService.updateDeploymentStatus).mockResolvedValue( + undefined as any, + ); + vi.mocked(applicationService.updateApplicationStatus).mockResolvedValue( + {} as any, + ); + vi.mocked(deploymentService.updateDeployment).mockResolvedValue( + {} as any, + ); + }); + + afterEach(async () => { + // ALWAYS cleanup, even if test failed or passed + console.log(`\n๐Ÿงน Cleaning up test: ${currentAppName}`); + + // Clean current appName + try { + await cleanupDocker(currentAppName); + await cleanupFiles(currentAppName); + } catch (error) { + console.error("โš ๏ธ Error cleaning current app:", error); + } + + // Clean ALL test folders just in case + try { + const { LOGS_PATH, APPLICATIONS_PATH } = paths(false); + await execAsync(`rm -rf ${LOGS_PATH}/real-* 2>/dev/null || true`); + await execAsync( + `rm -rf ${APPLICATIONS_PATH}/real-* 2>/dev/null || true`, + ); + console.log("โœ… Cleaned up all test artifacts"); + } catch (error) { + console.error("โš ๏ธ Error cleaning all artifacts:", error); + } + + console.log("โœ… Cleanup completed\n"); + }); + + it( + "should REALLY clone git repo and build with nixpacks", + async () => { + console.log(`\n๐Ÿš€ Testing real deployment with app: ${currentAppName}`); + + const result = await deployApplication({ + applicationId: "test-app-id", + titleLog: "Real Nixpacks Test", + descriptionLog: "Testing real execution", + }); + + expect(result).toBe(true); + + // Verify that Docker image was actually created + const { stdout: dockerImages } = await execAsync( + `docker images ${currentAppName} --format "{{.Repository}}"`, + ); + console.log("dockerImages", dockerImages); + expect(dockerImages.trim()).toBe(currentAppName); + console.log(`โœ… Docker image created: ${currentAppName}`); + + // Verify log exists and has content + expect(existsSync(currentDeployment.logPath)).toBe(true); + const { stdout: logContent } = await execAsync( + `cat ${currentDeployment.logPath}`, + ); + expect(logContent).toContain("Cloning"); + expect(logContent).toContain("nixpacks"); + console.log(`โœ… Build log created with ${logContent.length} chars`); + + // Verify update functions were called + expect(deploymentService.updateDeploymentStatus).toHaveBeenCalledWith( + "deployment-id", + "done", + ); + }, + REAL_TEST_TIMEOUT, + ); + + it.skip( + "should REALLY build with railpack (SKIPPED: requires special permissions)", + async () => { + const railpackAppName = `real-railpack-${Date.now()}`; + const railpackApp = createMockApplication({ + appName: railpackAppName, + buildType: "railpack", + railpackVersion: "3", + }); + currentAppName = railpackAppName; + allTestAppNames.push(railpackAppName); + + vi.mocked(db.query.applications.findFirst).mockResolvedValue( + railpackApp as any, + ); + vi.mocked(applicationService.findApplicationById).mockResolvedValue( + railpackApp as any, + ); + + console.log(`\n๐Ÿš€ Testing real railpack deployment: ${currentAppName}`); + + const result = await deployApplication({ + applicationId: "test-app-id", + titleLog: "Real Railpack Test", + descriptionLog: "", + }); + + expect(result).toBe(true); + + const { stdout: dockerImages } = await execAsync( + `docker images ${currentAppName} --format "{{.Repository}}"`, + ); + expect(dockerImages.trim()).toBe(currentAppName); + console.log(`โœ… Railpack image created: ${currentAppName}`); + + const { stdout: logContent } = await execAsync( + `cat ${currentDeployment.logPath}`, + ); + expect(logContent).toContain("railpack"); + console.log("โœ… Railpack build completed"); + }, + REAL_TEST_TIMEOUT, + ); + + it( + "should handle REAL git clone errors", + async () => { + const errorAppName = `real-error-${Date.now()}`; + const errorApp = createMockApplication({ + appName: errorAppName, + customGitUrl: + "https://github.com/invalid/nonexistent-repo-123456.git", + }); + currentAppName = errorAppName; + allTestAppNames.push(errorAppName); + + vi.mocked(db.query.applications.findFirst).mockResolvedValue( + errorApp as any, + ); + vi.mocked(applicationService.findApplicationById).mockResolvedValue( + errorApp as any, + ); + + console.log(`\n๐Ÿš€ Testing real error handling: ${currentAppName}`); + + await expect( + deployApplication({ + applicationId: "test-app-id", + titleLog: "Real Error Test", + descriptionLog: "", + }), + ).rejects.toThrow(); + + // Verify error status was called + expect(deploymentService.updateDeploymentStatus).toHaveBeenCalledWith( + "deployment-id", + "error", + ); + + // Verify log contains error + const { stdout: logContent } = await execAsync( + `cat ${currentDeployment.logPath}`, + ); + expect(logContent.toLowerCase()).toContain("error"); + console.log("โœ… Error handling verified"); + }, + REAL_TEST_TIMEOUT, + ); + + it( + "should REALLY clone with submodules when enabled", + async () => { + const submodulesAppName = `real-submodules-${Date.now()}`; + const submodulesApp = createMockApplication({ + appName: submodulesAppName, + enableSubmodules: true, + }); + currentAppName = submodulesAppName; + allTestAppNames.push(submodulesAppName); + + vi.mocked(db.query.applications.findFirst).mockResolvedValue( + submodulesApp as any, + ); + vi.mocked(applicationService.findApplicationById).mockResolvedValue( + submodulesApp as any, + ); + + console.log(`\n๐Ÿš€ Testing real submodules support: ${currentAppName}`); + + const result = await deployApplication({ + applicationId: "test-app-id", + titleLog: "Real Submodules Test", + descriptionLog: "", + }); + + expect(result).toBe(true); + + // Verify deployment completed successfully + const { stdout: logContent } = await execAsync( + `cat ${currentDeployment.logPath}`, + ); + expect(logContent).toContain("Cloning"); + expect(logContent.length).toBeGreaterThan(100); + console.log("โœ… Submodules deployment completed"); + + // Verify image + const { stdout: dockerImages } = await execAsync( + `docker images ${currentAppName} --format "{{.Repository}}"`, + ); + expect(dockerImages.trim()).toBe(currentAppName); + }, + REAL_TEST_TIMEOUT, + ); + + it( + "should verify REAL commit info extraction", + async () => { + console.log(`\n๐Ÿš€ Testing real commit info: ${currentAppName}`); + + await deployApplication({ + applicationId: "test-app-id", + titleLog: "Real Commit Test", + descriptionLog: "", + }); + + // Verify updateDeployment was called with commit info + expect(deploymentService.updateDeployment).toHaveBeenCalled(); + const updateCall = vi.mocked(deploymentService.updateDeployment).mock + .calls[0]; + + // Real commit info should have title and hash + expect(updateCall?.[1]).toHaveProperty("title"); + expect(updateCall?.[1]).toHaveProperty("description"); + expect(updateCall?.[1]?.description).toContain("Commit:"); + + console.log( + `โœ… Real commit extracted: ${updateCall?.[1]?.title?.substring(0, 50)}...`, + ); + }, + REAL_TEST_TIMEOUT, + ); + + it( + "should REALLY build with Dockerfile", + async () => { + const dockerfileAppName = `real-dockerfile-${Date.now()}`; + const dockerfileApp = createMockApplication({ + appName: dockerfileAppName, + buildType: "dockerfile", + customGitBuildPath: "/deno", + dockerfile: "Dockerfile", + }); + currentAppName = dockerfileAppName; + allTestAppNames.push(dockerfileAppName); + + vi.mocked(db.query.applications.findFirst).mockResolvedValue( + dockerfileApp as any, + ); + vi.mocked(applicationService.findApplicationById).mockResolvedValue( + dockerfileApp as any, + ); + + console.log(`\n๐Ÿš€ Testing real Dockerfile build: ${currentAppName}`); + + const result = await deployApplication({ + applicationId: "test-app-id", + titleLog: "Real Dockerfile Test", + descriptionLog: "", + }); + + expect(result).toBe(true); + + // Verify log + const { stdout: logContent } = await execAsync( + `cat ${currentDeployment.logPath}`, + ); + expect(logContent).toContain("Building"); + expect(logContent).toContain(dockerfileAppName); + console.log("โœ… Dockerfile build log verified"); + + // Verify image + const { stdout: dockerImages } = await execAsync( + `docker images ${currentAppName} --format "{{.Repository}}"`, + ); + console.log("dockerImages", dockerImages); + expect(dockerImages.trim()).toBe(currentAppName); + console.log(`โœ… Docker image created: ${currentAppName}`); + }, + REAL_TEST_TIMEOUT, + ); + }, + REAL_TEST_TIMEOUT, +); diff --git a/apps/dokploy/__test__/vitest.config.ts b/apps/dokploy/__test__/vitest.config.ts index ddc84d6ac..7270b828a 100644 --- a/apps/dokploy/__test__/vitest.config.ts +++ b/apps/dokploy/__test__/vitest.config.ts @@ -13,7 +13,11 @@ export default defineConfig({ NODE: "test", }, }, - plugins: [tsconfigPaths()], + plugins: [ + tsconfigPaths({ + projects: [path.resolve(__dirname, "../tsconfig.json")], + }), + ], resolve: { alias: { "@dokploy/server": path.resolve( diff --git a/packages/server/src/utils/builders/index.ts b/packages/server/src/utils/builders/index.ts index 11912f6c5..a70e874c2 100644 --- a/packages/server/src/utils/builders/index.ts +++ b/packages/server/src/utils/builders/index.ts @@ -125,7 +125,8 @@ export const mechanizeDockerContainer = async ( Image: image, Env: envVariables, Mounts: [...volumesMount, ...bindsMount, ...filesMount], - ...(StopGracePeriod && { StopGracePeriod }), + ...(StopGracePeriod !== null && + StopGracePeriod !== undefined && { StopGracePeriod }), ...(command ? { Command: ["/bin/sh"], diff --git a/packages/server/src/utils/databases/mariadb.ts b/packages/server/src/utils/databases/mariadb.ts index 1db457018..852e174ef 100644 --- a/packages/server/src/utils/databases/mariadb.ts +++ b/packages/server/src/utils/databases/mariadb.ts @@ -73,7 +73,8 @@ export const buildMariadb = async (mariadb: MariadbNested) => { Image: dockerImage, Env: envVariables, Mounts: [...volumesMount, ...bindsMount, ...filesMount], - ...(StopGracePeriod && { StopGracePeriod }), + ...(StopGracePeriod !== null && + StopGracePeriod !== undefined && { StopGracePeriod }), ...(command ? { Command: ["/bin/sh"], diff --git a/packages/server/src/utils/databases/mongo.ts b/packages/server/src/utils/databases/mongo.ts index 3b71f323b..af5dc8cbd 100644 --- a/packages/server/src/utils/databases/mongo.ts +++ b/packages/server/src/utils/databases/mongo.ts @@ -121,7 +121,8 @@ ${command ?? "wait $MONGOD_PID"}`; Image: dockerImage, Env: envVariables, Mounts: [...volumesMount, ...bindsMount, ...filesMount], - ...(StopGracePeriod && { StopGracePeriod }), + ...(StopGracePeriod !== null && + StopGracePeriod !== undefined && { StopGracePeriod }), ...(replicaSets ? { Command: ["/bin/bash"], diff --git a/packages/server/src/utils/databases/mysql.ts b/packages/server/src/utils/databases/mysql.ts index 069803a7c..a1f75dd40 100644 --- a/packages/server/src/utils/databases/mysql.ts +++ b/packages/server/src/utils/databases/mysql.ts @@ -79,7 +79,8 @@ export const buildMysql = async (mysql: MysqlNested) => { Image: dockerImage, Env: envVariables, Mounts: [...volumesMount, ...bindsMount, ...filesMount], - ...(StopGracePeriod && { StopGracePeriod }), + ...(StopGracePeriod !== null && + StopGracePeriod !== undefined && { StopGracePeriod }), ...(command ? { Command: ["/bin/sh"], diff --git a/packages/server/src/utils/databases/postgres.ts b/packages/server/src/utils/databases/postgres.ts index afcd49ca1..e15f8a641 100644 --- a/packages/server/src/utils/databases/postgres.ts +++ b/packages/server/src/utils/databases/postgres.ts @@ -72,7 +72,8 @@ export const buildPostgres = async (postgres: PostgresNested) => { Image: dockerImage, Env: envVariables, Mounts: [...volumesMount, ...bindsMount, ...filesMount], - ...(StopGracePeriod && { StopGracePeriod }), + ...(StopGracePeriod !== null && + StopGracePeriod !== undefined && { StopGracePeriod }), ...(command ? { Command: ["/bin/sh"], diff --git a/packages/server/src/utils/databases/redis.ts b/packages/server/src/utils/databases/redis.ts index 7aa684565..2d9ae273d 100644 --- a/packages/server/src/utils/databases/redis.ts +++ b/packages/server/src/utils/databases/redis.ts @@ -70,7 +70,8 @@ export const buildRedis = async (redis: RedisNested) => { Image: dockerImage, Env: envVariables, Mounts: [...volumesMount, ...bindsMount, ...filesMount], - ...(StopGracePeriod && { StopGracePeriod }), + ...(StopGracePeriod !== null && + StopGracePeriod !== undefined && { StopGracePeriod }), Command: ["/bin/sh"], Args: [ "-c",