mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-02-25 20:35:10 +00:00
[autofix.ci] apply automated fixes
This commit is contained in:
@@ -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 .");
|
||||||
|
|||||||
@@ -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);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -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
44030
openapi.json
File diff suppressed because it is too large
Load Diff
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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`;
|
||||||
|
|||||||
@@ -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({
|
||||||
|
|||||||
Reference in New Issue
Block a user