[autofix.ci] apply automated fixes

This commit is contained in:
autofix-ci[bot]
2026-02-17 06:04:26 +00:00
committed by GitHub
parent 0fc043d0ad
commit 752f90c330
10 changed files with 21293 additions and 22887 deletions

View File

@@ -1,4 +1,3 @@
import { existsSync } from "node:fs"; import { existsSync } from "node:fs";
import path from "node:path"; import path from "node:path";
import type { ApplicationNested } from "@dokploy/server"; import type { ApplicationNested } from "@dokploy/server";
@@ -80,9 +79,8 @@ vi.mock("@dokploy/server/services/rollbacks", () => ({
})); }));
vi.mock("@dokploy/server/services/patch", async (importOriginal) => { vi.mock("@dokploy/server/services/patch", async (importOriginal) => {
const actual = await importOriginal< const actual =
typeof import("@dokploy/server/services/patch") await importOriginal<typeof import("@dokploy/server/services/patch")>();
>();
return { return {
...actual, ...actual,
findPatchesByApplicationId: vi.fn().mockResolvedValue([]), findPatchesByApplicationId: vi.fn().mockResolvedValue([]),
@@ -507,7 +505,8 @@ describe(
// 1. Setup local temporary git repo // 1. Setup local temporary git repo
const tempRepo = await mkdtemp(join(tmpdir(), "real-patch-repo-")); const tempRepo = await mkdtemp(join(tmpdir(), "real-patch-repo-"));
// Helper for local git commands // Helper for local git commands
const execLocal = async (cmd: string) => execAsync(cmd, { cwd: tempRepo }); const execLocal = async (cmd: string) =>
execAsync(cmd, { cwd: tempRepo });
await execLocal("git init"); await execLocal("git init");
await execLocal("git config user.email 'test@dokploy.com'"); await execLocal("git config user.email 'test@dokploy.com'");
@@ -518,7 +517,7 @@ describe(
await writeFile(join(tempRepo, "app.py"), "print('Original App')\n"); await writeFile(join(tempRepo, "app.py"), "print('Original App')\n");
await writeFile( await writeFile(
join(tempRepo, "Dockerfile"), join(tempRepo, "Dockerfile"),
"FROM python:3.9-slim\nCOPY app.py .\nCMD [\"python\", \"app.py\"]\n", 'FROM python:3.9-slim\nCOPY app.py .\nCMD ["python", "app.py"]\n',
); );
await execLocal("git add ."); await execLocal("git add .");

View File

@@ -1,4 +1,3 @@
import { generatePatch } from "@dokploy/server/services/patch"; import { generatePatch } from "@dokploy/server/services/patch";
import { describe, expect, it, afterEach } from "vitest"; import { describe, expect, it, afterEach } from "vitest";
import { mkdtemp, rm, writeFile, readFile } from "node:fs/promises"; import { mkdtemp, rm, writeFile, readFile } from "node:fs/promises";
@@ -23,16 +22,18 @@ describe("Patch System Integration", () => {
tempDir = await mkdtemp(join(tmpdir(), "dokploy-patch-test-")); tempDir = await mkdtemp(join(tmpdir(), "dokploy-patch-test-"));
const fileName = "test.txt"; const fileName = "test.txt";
const filePath = join(tempDir, fileName); const filePath = join(tempDir, fileName);
await execAsyncLocal("git init", { cwd: tempDir }); await execAsyncLocal("git init", { cwd: tempDir });
await execAsyncLocal("git config user.email 'test@test.com'", { cwd: tempDir }); await execAsyncLocal("git config user.email 'test@test.com'", {
cwd: tempDir,
});
await execAsyncLocal("git config user.name 'Test'", { cwd: tempDir }); await execAsyncLocal("git config user.name 'Test'", { cwd: tempDir });
// Original content // Original content
await writeFile(filePath, "line1\nline2\n"); await writeFile(filePath, "line1\nline2\n");
await execAsyncLocal(`git add ${fileName}`, { cwd: tempDir }); await execAsyncLocal(`git add ${fileName}`, { cwd: tempDir });
await execAsyncLocal("git commit -m 'init'", { cwd: tempDir }); await execAsyncLocal("git commit -m 'init'", { cwd: tempDir });
// Generate patch (modify content) // Generate patch (modify content)
const newContent = "line1\nline2\nline3\n"; const newContent = "line1\nline2\nline3\n";
const patchContent = await generatePatch({ const patchContent = await generatePatch({
@@ -41,7 +42,7 @@ describe("Patch System Integration", () => {
newContent, newContent,
serverId: null, serverId: null,
}); });
// Verify patch format // Verify patch format
expect(patchContent.endsWith("\n")).toBe(true); expect(patchContent.endsWith("\n")).toBe(true);
@@ -49,20 +50,22 @@ describe("Patch System Integration", () => {
await execAsyncLocal("git checkout .", { cwd: tempDir }); await execAsyncLocal("git checkout .", { cwd: tempDir });
const savedContent = await readFile(filePath, "utf-8"); const savedContent = await readFile(filePath, "utf-8");
expect(savedContent).toBe("line1\nline2\n"); expect(savedContent).toBe("line1\nline2\n");
// Apply patch verification // Apply patch verification
// We simulate what Deployment Service does: write patch to file and run git apply // We simulate what Deployment Service does: write patch to file and run git apply
const patchFile = join(tempDir, "changes.patch"); const patchFile = join(tempDir, "changes.patch");
await writeFile(patchFile, patchContent); await writeFile(patchFile, patchContent);
try { try {
await execAsyncLocal(`git apply --whitespace=fix ${patchFile}`, { cwd: tempDir }); await execAsyncLocal(`git apply --whitespace=fix ${patchFile}`, {
cwd: tempDir,
});
} catch (e: any) { } catch (e: any) {
console.error("Git apply failed:", e.message); console.error("Git apply failed:", e.message);
console.log("Patch content:", JSON.stringify(patchContent)); console.log("Patch content:", JSON.stringify(patchContent));
throw e; throw e;
} }
const appliedContent = await readFile(filePath, "utf-8"); const appliedContent = await readFile(filePath, "utf-8");
expect(appliedContent).toBe(newContent); expect(appliedContent).toBe(newContent);
}); });
@@ -72,17 +75,19 @@ describe("Patch System Integration", () => {
tempDir = await mkdtemp(join(tmpdir(), "dokploy-patch-test-noline-")); tempDir = await mkdtemp(join(tmpdir(), "dokploy-patch-test-noline-"));
const fileName = "noline.txt"; const fileName = "noline.txt";
const filePath = join(tempDir, fileName); const filePath = join(tempDir, fileName);
await execAsyncLocal("git init", { cwd: tempDir }); await execAsyncLocal("git init", { cwd: tempDir });
await execAsyncLocal("git config user.email 'test@test.com'", { cwd: tempDir }); await execAsyncLocal("git config user.email 'test@test.com'", {
cwd: tempDir,
});
await execAsyncLocal("git config user.name 'Test'", { cwd: tempDir }); await execAsyncLocal("git config user.name 'Test'", { cwd: tempDir });
// Original content WITHOUT newline // Original content WITHOUT newline
await writeFile(filePath, "line1"); await writeFile(filePath, "line1");
await execAsyncLocal(`git add ${fileName}`, { cwd: tempDir }); await execAsyncLocal(`git add ${fileName}`, { cwd: tempDir });
await execAsyncLocal("git commit -m 'init'", { cwd: tempDir }); await execAsyncLocal("git commit -m 'init'", { cwd: tempDir });
// Generate patch // Generate patch
const newContent = "line1\nline2"; const newContent = "line1\nline2";
const patchContent = await generatePatch({ const patchContent = await generatePatch({
codePath: tempDir, codePath: tempDir,
@@ -93,13 +98,15 @@ describe("Patch System Integration", () => {
// Verify patch format // Verify patch format
expect(patchContent.endsWith("\n")).toBe(true); expect(patchContent.endsWith("\n")).toBe(true);
// Apply patch // Apply patch
const patchFile = join(tempDir, "changes.patch"); const patchFile = join(tempDir, "changes.patch");
await writeFile(patchFile, patchContent); await writeFile(patchFile, patchContent);
await execAsyncLocal(`git apply --whitespace=fix ${patchFile}`, { cwd: tempDir }); await execAsyncLocal(`git apply --whitespace=fix ${patchFile}`, {
cwd: tempDir,
});
const appliedContent = await readFile(filePath, "utf-8"); const appliedContent = await readFile(filePath, "utf-8");
expect(appliedContent).toBe(newContent); expect(appliedContent).toBe(newContent);
}); });

View File

@@ -1,9 +1,22 @@
import { ArrowLeft, ChevronRight, File, Folder, Loader2, Save } from "lucide-react"; import {
ArrowLeft,
ChevronRight,
File,
Folder,
Loader2,
Save,
} from "lucide-react";
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { CodeEditor } from "@/components/shared/code-editor"; import { CodeEditor } from "@/components/shared/code-editor";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { ScrollArea } from "@/components/ui/scroll-area"; import { ScrollArea } from "@/components/ui/scroll-area";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import type { RouterOutputs } from "@/utils/api"; import type { RouterOutputs } from "@/utils/api";
@@ -31,7 +44,9 @@ export const PatchEditor = ({
const [selectedFile, setSelectedFile] = useState<string | null>(null); const [selectedFile, setSelectedFile] = useState<string | null>(null);
const [fileContent, setFileContent] = useState<string>(""); const [fileContent, setFileContent] = useState<string>("");
const [originalContent, setOriginalContent] = useState<string>(""); const [originalContent, setOriginalContent] = useState<string>("");
const [expandedFolders, setExpandedFolders] = useState<Set<string>>(new Set()); const [expandedFolders, setExpandedFolders] = useState<Set<string>>(
new Set(),
);
const [isSaving, setIsSaving] = useState(false); const [isSaving, setIsSaving] = useState(false);
// Fetch directory tree // Fetch directory tree

View File

@@ -1,9 +1,23 @@
import { AlertCircle, ChevronRight, File, Folder, Loader2, Power, Trash2 } from "lucide-react"; import {
AlertCircle,
ChevronRight,
File,
Folder,
Loader2,
Power,
Trash2,
} from "lucide-react";
import { useState } from "react"; import { useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import { import {
Table, Table,
@@ -136,8 +150,8 @@ export const ShowPatches = ({ applicationId, composeId }: Props) => {
<div> <div>
<CardTitle>Patches</CardTitle> <CardTitle>Patches</CardTitle>
<CardDescription> <CardDescription>
Apply code patches to your repository during build. Patches are applied after Apply code patches to your repository during build. Patches are
cloning the repository and before building. applied after cloning the repository and before building.
</CardDescription> </CardDescription>
</div> </div>
<Button onClick={handleOpenEditor} disabled={isLoadingRepo}> <Button onClick={handleOpenEditor} disabled={isLoadingRepo}>
@@ -155,8 +169,8 @@ export const ShowPatches = ({ applicationId, composeId }: Props) => {
<AlertCircle className="h-4 w-4" /> <AlertCircle className="h-4 w-4" />
<AlertTitle>No patches</AlertTitle> <AlertTitle>No patches</AlertTitle>
<AlertDescription> <AlertDescription>
No patches have been created for this application yet. Click "Create Patch" No patches have been created for this application yet. Click
to add modifications to your code during build. "Create Patch" to add modifications to your code during build.
</AlertDescription> </AlertDescription>
</Alert> </Alert>
) : ( ) : (

View File

@@ -33,7 +33,9 @@ import {
} from "@/server/db/schema"; } from "@/server/db/schema";
// Helper to get git config from application // Helper to get git config from application
const getApplicationGitConfig = (app: Awaited<ReturnType<typeof findApplicationById>>) => { const getApplicationGitConfig = (
app: Awaited<ReturnType<typeof findApplicationById>>,
) => {
switch (app.sourceType) { switch (app.sourceType) {
case "github": case "github":
return { return {
@@ -73,7 +75,9 @@ const getApplicationGitConfig = (app: Awaited<ReturnType<typeof findApplicationB
}; };
// Helper to get git config from compose // Helper to get git config from compose
const getComposeGitConfig = (compose: Awaited<ReturnType<typeof findComposeById>>) => { const getComposeGitConfig = (
compose: Awaited<ReturnType<typeof findComposeById>>,
) => {
switch (compose.sourceType) { switch (compose.sourceType) {
case "github": case "github":
return { return {
@@ -153,11 +157,9 @@ export const patchRouter = createTRPCRouter({
return await createPatch(input); return await createPatch(input);
}), }),
one: protectedProcedure one: protectedProcedure.input(apiFindPatch).query(async ({ input }) => {
.input(apiFindPatch) return await findPatchById(input.patchId);
.query(async ({ input }) => { }),
return await findPatchById(input.patchId);
}),
byApplicationId: protectedProcedure byApplicationId: protectedProcedure
.input(apiFindPatchesByApplicationId) .input(apiFindPatchesByApplicationId)

44030
openapi.json

File diff suppressed because it is too large Load Diff

View File

@@ -208,8 +208,10 @@ export const deployApplication = async ({
// Apply patches after cloning (for non-docker sources only) // Apply patches after cloning (for non-docker sources only)
if (application.sourceType !== "docker") { if (application.sourceType !== "docker") {
const patches = await findPatchesByApplicationId(application.applicationId); const patches = await findPatchesByApplicationId(
const enabledPatches = patches.filter(p => p.enabled); application.applicationId,
);
const enabledPatches = patches.filter((p) => p.enabled);
if (enabledPatches.length > 0) { if (enabledPatches.length > 0) {
command += generateApplyPatchesCommand({ command += generateApplyPatchesCommand({
appName: application.appName, appName: application.appName,

View File

@@ -40,10 +40,7 @@ import {
updateDeployment, updateDeployment,
updateDeploymentStatus, updateDeploymentStatus,
} from "./deployment"; } from "./deployment";
import { import { findPatchesByComposeId, generateApplyPatchesCommand } from "./patch";
findPatchesByComposeId,
generateApplyPatchesCommand,
} from "./patch";
import { validUniqueServerAppName } from "./project"; import { validUniqueServerAppName } from "./project";
export type Compose = typeof compose.$inferSelect; export type Compose = typeof compose.$inferSelect;
@@ -255,7 +252,7 @@ export const deployCompose = async ({
// Apply patches after cloning (for non-raw sources only) // Apply patches after cloning (for non-raw sources only)
if (compose.sourceType !== "raw") { if (compose.sourceType !== "raw") {
const patches = await findPatchesByComposeId(compose.composeId); const patches = await findPatchesByComposeId(compose.composeId);
const enabledPatches = patches.filter(p => p.enabled); const enabledPatches = patches.filter((p) => p.enabled);
if (enabledPatches.length > 0) { if (enabledPatches.length > 0) {
const patchCommand = generateApplyPatchesCommand({ const patchCommand = generateApplyPatchesCommand({
appName: compose.appName, appName: compose.appName,

View File

@@ -76,7 +76,7 @@ echo "Repository cloned successfully";
}); });
} }
} else { } else {
// Repo exists - check if on correct branch and update // Repo exists - check if on correct branch and update
const updateCommand = ` const updateCommand = `
set -e; set -e;
cd "${repoPath}"; cd "${repoPath}";
@@ -161,7 +161,7 @@ export const readPatchRepoDirectory = async (
for (let i = 0; i < parts.length; i++) { for (let i = 0; i < parts.length; i++) {
const part = parts[i]; const part = parts[i];
if (!part) continue; if (!part) continue;
const isFile = i === parts.length - 1; const isFile = i === parts.length - 1;
const parentPath = currentPath; const parentPath = currentPath;
currentPath = currentPath ? `${currentPath}/${part}` : part; currentPath = currentPath ? `${currentPath}/${part}` : part;
@@ -295,7 +295,9 @@ rm -rf "${tempDir}";
/** /**
* Clean all patch repos * Clean all patch repos
*/ */
export const cleanPatchRepos = async (serverId?: string | null): Promise<void> => { export const cleanPatchRepos = async (
serverId?: string | null,
): Promise<void> => {
const { PATCH_REPOS_PATH } = paths(!!serverId); const { PATCH_REPOS_PATH } = paths(!!serverId);
const command = `rm -rf "${PATCH_REPOS_PATH}"/* 2>/dev/null || true`; const command = `rm -rf "${PATCH_REPOS_PATH}"/* 2>/dev/null || true`;

View File

@@ -1,10 +1,7 @@
import { join } from "node:path"; import { join } from "node:path";
import { paths } from "@dokploy/server/constants"; import { paths } from "@dokploy/server/constants";
import { db } from "@dokploy/server/db"; import { db } from "@dokploy/server/db";
import { import { type apiCreatePatch, patch } from "@dokploy/server/db/schema";
type apiCreatePatch,
patch,
} from "@dokploy/server/db/schema";
import { import {
execAsync, execAsync,
execAsyncRemote, execAsyncRemote,
@@ -98,10 +95,7 @@ export const findPatchByFilePath = async (
return null; return null;
}; };
export const updatePatch = async ( export const updatePatch = async (patchId: string, data: Partial<Patch>) => {
patchId: string,
data: Partial<Patch>,
) => {
const result = await db const result = await db
.update(patch) .update(patch)
.set({ .set({