mirror of
https://github.com/Dokploy/dokploy.git
synced 2026-07-01 20:15:29 +02:00
Compare commits
16 Commits
feat/add-a
...
copilot/fi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c96d8559a | ||
|
|
39cf706053 | ||
|
|
e77f276785 | ||
|
|
78c9a047b0 | ||
|
|
84e0f5856b | ||
|
|
2bfa4643fc | ||
|
|
8c7bc82712 | ||
|
|
44645a6fbe | ||
|
|
771d0dd8ab | ||
|
|
67725759e6 | ||
|
|
2065372d4f | ||
|
|
69d5c6f0cb | ||
|
|
53f67c6eb2 | ||
|
|
7c53a3ef75 | ||
|
|
d465fb4da1 | ||
|
|
698104e7b7 |
@@ -206,4 +206,38 @@ describe("getRegistryTag", () => {
|
|||||||
expect(result).toBe("docker.io/myuser/repo");
|
expect(result).toBe("docker.io/myuser/repo");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("special characters in username", () => {
|
||||||
|
it("should handle Harbor robot account username with $ (e.g. robot$library+dokploy)", () => {
|
||||||
|
const registry = createMockRegistry({
|
||||||
|
username: "robot$library+dokploy",
|
||||||
|
});
|
||||||
|
const result = getRegistryTag(registry, "nginx");
|
||||||
|
expect(result).toBe("docker.io/robot$library+dokploy/nginx");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle username with $ and other special characters", () => {
|
||||||
|
const registry = createMockRegistry({
|
||||||
|
username: "robot$test+app",
|
||||||
|
});
|
||||||
|
const result = getRegistryTag(registry, "myapp:latest");
|
||||||
|
expect(result).toBe("docker.io/robot$test+app/myapp:latest");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle username with multiple $ symbols", () => {
|
||||||
|
const registry = createMockRegistry({
|
||||||
|
username: "user$name$test",
|
||||||
|
});
|
||||||
|
const result = getRegistryTag(registry, "app");
|
||||||
|
expect(result).toBe("docker.io/user$name$test/app");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle username with + and - symbols", () => {
|
||||||
|
const registry = createMockRegistry({
|
||||||
|
username: "robot+test-user",
|
||||||
|
});
|
||||||
|
const result = getRegistryTag(registry, "nginx:latest");
|
||||||
|
expect(result).toBe("docker.io/robot+test-user/nginx:latest");
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { Domain } from "@dokploy/server";
|
import type { Domain } from "@dokploy/server";
|
||||||
import { createDomainLabels } from "@dokploy/server";
|
import { createDomainLabels } from "@dokploy/server";
|
||||||
import { parse, stringify } from "yaml";
|
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { parse, stringify } from "yaml";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Regression tests for Traefik Host rule label format.
|
* Regression tests for Traefik Host rule label format.
|
||||||
|
|||||||
@@ -21,7 +21,10 @@ import {
|
|||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from "@/components/ui/form";
|
} from "@/components/ui/form";
|
||||||
import { Input } from "@/components/ui/input";
|
import {
|
||||||
|
createConverter,
|
||||||
|
NumberInputWithSteps,
|
||||||
|
} from "@/components/ui/number-input";
|
||||||
import {
|
import {
|
||||||
Tooltip,
|
Tooltip,
|
||||||
TooltipContent,
|
TooltipContent,
|
||||||
@@ -30,6 +33,23 @@ import {
|
|||||||
} from "@/components/ui/tooltip";
|
} from "@/components/ui/tooltip";
|
||||||
import { api } from "@/utils/api";
|
import { api } from "@/utils/api";
|
||||||
|
|
||||||
|
const CPU_STEP = 0.25;
|
||||||
|
const MEMORY_STEP_MB = 256;
|
||||||
|
|
||||||
|
const formatNumber = (value: number, decimals = 2): string =>
|
||||||
|
Number.isInteger(value) ? String(value) : value.toFixed(decimals);
|
||||||
|
|
||||||
|
const cpuConverter = createConverter(1_000_000_000, (cpu) =>
|
||||||
|
cpu <= 0 ? "" : `${formatNumber(cpu)} CPU`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const memoryConverter = createConverter(1024 * 1024, (mb) => {
|
||||||
|
if (mb <= 0) return "";
|
||||||
|
return mb >= 1024
|
||||||
|
? `${formatNumber(mb / 1024)} GB`
|
||||||
|
: `${formatNumber(mb)} MB`;
|
||||||
|
});
|
||||||
|
|
||||||
const addResourcesSchema = z.object({
|
const addResourcesSchema = z.object({
|
||||||
memoryReservation: z.string().optional(),
|
memoryReservation: z.string().optional(),
|
||||||
cpuLimit: z.string().optional(),
|
cpuLimit: z.string().optional(),
|
||||||
@@ -51,6 +71,7 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type AddResources = z.infer<typeof addResourcesSchema>;
|
type AddResources = z.infer<typeof addResourcesSchema>;
|
||||||
|
|
||||||
export const ShowResources = ({ id, type }: Props) => {
|
export const ShowResources = ({ id, type }: Props) => {
|
||||||
const queryMap = {
|
const queryMap = {
|
||||||
postgres: () =>
|
postgres: () =>
|
||||||
@@ -163,16 +184,20 @@ export const ShowResources = ({ id, type }: Props) => {
|
|||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
<p>
|
<p>
|
||||||
Memory hard limit in bytes. Example: 1GB =
|
Memory hard limit in bytes. Example: 1GB =
|
||||||
1073741824 bytes
|
1073741824 bytes. Use +/- buttons to adjust by
|
||||||
|
256 MB.
|
||||||
</p>
|
</p>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
</div>
|
</div>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<NumberInputWithSteps
|
||||||
|
value={field.value}
|
||||||
|
onChange={field.onChange}
|
||||||
placeholder="1073741824 (1GB in bytes)"
|
placeholder="1073741824 (1GB in bytes)"
|
||||||
{...field}
|
step={MEMORY_STEP_MB}
|
||||||
|
converter={memoryConverter}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -198,16 +223,20 @@ export const ShowResources = ({ id, type }: Props) => {
|
|||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
<p>
|
<p>
|
||||||
Memory soft limit in bytes. Example: 256MB =
|
Memory soft limit in bytes. Example: 256MB =
|
||||||
268435456 bytes
|
268435456 bytes. Use +/- buttons to adjust by 256
|
||||||
|
MB.
|
||||||
</p>
|
</p>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
</div>
|
</div>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<NumberInputWithSteps
|
||||||
|
value={field.value}
|
||||||
|
onChange={field.onChange}
|
||||||
placeholder="268435456 (256MB in bytes)"
|
placeholder="268435456 (256MB in bytes)"
|
||||||
{...field}
|
step={MEMORY_STEP_MB}
|
||||||
|
converter={memoryConverter}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -234,17 +263,20 @@ export const ShowResources = ({ id, type }: Props) => {
|
|||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
<p>
|
<p>
|
||||||
CPU quota in units of 10^-9 CPUs. Example: 2
|
CPU quota in units of 10^-9 CPUs. Example: 2
|
||||||
CPUs = 2000000000
|
CPUs = 2000000000. Use +/- buttons to adjust by
|
||||||
|
0.25 CPU.
|
||||||
</p>
|
</p>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
</div>
|
</div>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input
|
<NumberInputWithSteps
|
||||||
|
value={field.value}
|
||||||
|
onChange={field.onChange}
|
||||||
placeholder="2000000000 (2 CPUs)"
|
placeholder="2000000000 (2 CPUs)"
|
||||||
{...field}
|
step={CPU_STEP}
|
||||||
value={field.value?.toString() || ""}
|
converter={cpuConverter}
|
||||||
/>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
@@ -271,14 +303,21 @@ export const ShowResources = ({ id, type }: Props) => {
|
|||||||
<TooltipContent>
|
<TooltipContent>
|
||||||
<p>
|
<p>
|
||||||
CPU shares (relative weight). Example: 1 CPU =
|
CPU shares (relative weight). Example: 1 CPU =
|
||||||
1000000000
|
1000000000. Use +/- buttons to adjust by 0.25
|
||||||
|
CPU.
|
||||||
</p>
|
</p>
|
||||||
</TooltipContent>
|
</TooltipContent>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</TooltipProvider>
|
</TooltipProvider>
|
||||||
</div>
|
</div>
|
||||||
<FormControl>
|
<FormControl>
|
||||||
<Input placeholder="1000000000 (1 CPU)" {...field} />
|
<NumberInputWithSteps
|
||||||
|
value={field.value}
|
||||||
|
onChange={field.onChange}
|
||||||
|
placeholder="1000000000 (1 CPU)"
|
||||||
|
step={CPU_STEP}
|
||||||
|
converter={cpuConverter}
|
||||||
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
|
|||||||
@@ -108,7 +108,8 @@ export const getLogType = (message: string): LogStyle => {
|
|||||||
/(?:might|may|could)\s+(?:not|cause|lead\s+to)/i.test(lowerMessage) ||
|
/(?:might|may|could)\s+(?:not|cause|lead\s+to)/i.test(lowerMessage) ||
|
||||||
/(?:!+\s*(?:warning|caution|attention)\s*!+)/i.test(lowerMessage) ||
|
/(?:!+\s*(?:warning|caution|attention)\s*!+)/i.test(lowerMessage) ||
|
||||||
/\b(?:deprecated|obsolete)\b/i.test(lowerMessage) ||
|
/\b(?:deprecated|obsolete)\b/i.test(lowerMessage) ||
|
||||||
/\b(?:unstable|experimental)\b/i.test(lowerMessage)
|
/\b(?:unstable|experimental)\b/i.test(lowerMessage) ||
|
||||||
|
/⚠|⚠️/i.test(lowerMessage)
|
||||||
) {
|
) {
|
||||||
return LOG_STYLES.warning;
|
return LOG_STYLES.warning;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Activity } from "lucide-react";
|
import { Activity } from "lucide-react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
@@ -7,7 +8,6 @@ import {
|
|||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogTrigger,
|
DialogTrigger,
|
||||||
} from "@/components/ui/dialog";
|
} from "@/components/ui/dialog";
|
||||||
import { Button } from "@/components/ui/button";
|
|
||||||
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
|
import { DropdownMenuItem } from "@/components/ui/dropdown-menu";
|
||||||
import { ShowStorageActions } from "./show-storage-actions";
|
import { ShowStorageActions } from "./show-storage-actions";
|
||||||
import { ShowTraefikActions } from "./show-traefik-actions";
|
import { ShowTraefikActions } from "./show-traefik-actions";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { PlusIcon, Pencil } from "lucide-react";
|
import { Pencil, PlusIcon } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useTranslation } from "next-i18next";
|
import { useTranslation } from "next-i18next";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
import {
|
import {
|
||||||
|
Clock,
|
||||||
|
Key,
|
||||||
KeyIcon,
|
KeyIcon,
|
||||||
Loader2,
|
Loader2,
|
||||||
MoreHorizontal,
|
MoreHorizontal,
|
||||||
ServerIcon,
|
|
||||||
Clock,
|
|
||||||
User,
|
|
||||||
Key,
|
|
||||||
Network,
|
Network,
|
||||||
Terminal,
|
|
||||||
Settings,
|
|
||||||
Pencil,
|
Pencil,
|
||||||
|
ServerIcon,
|
||||||
|
Settings,
|
||||||
|
Terminal,
|
||||||
Trash2,
|
Trash2,
|
||||||
|
User,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
|
import { ChevronDown } from "lucide-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { Fragment } from "react";
|
import { Fragment } from "react";
|
||||||
import { ChevronDown } from "lucide-react";
|
|
||||||
import {
|
import {
|
||||||
Breadcrumb,
|
Breadcrumb,
|
||||||
BreadcrumbItem,
|
BreadcrumbItem,
|
||||||
BreadcrumbLink,
|
BreadcrumbLink,
|
||||||
BreadcrumbList,
|
BreadcrumbList,
|
||||||
BreadcrumbSeparator,
|
|
||||||
BreadcrumbPage,
|
BreadcrumbPage,
|
||||||
|
BreadcrumbSeparator,
|
||||||
} from "@/components/ui/breadcrumb";
|
} from "@/components/ui/breadcrumb";
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
|
|||||||
84
apps/dokploy/components/ui/number-input.tsx
Normal file
84
apps/dokploy/components/ui/number-input.tsx
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
import { MinusIcon, PlusIcon } from "lucide-react";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
|
||||||
|
export interface UnitConverter {
|
||||||
|
toValue: (raw: string | undefined) => number;
|
||||||
|
fromValue: (value: number) => string;
|
||||||
|
formatDisplay: (value: number) => string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const createConverter = (
|
||||||
|
multiplier: number,
|
||||||
|
formatDisplay: (value: number) => string,
|
||||||
|
): UnitConverter => ({
|
||||||
|
toValue: (raw) => {
|
||||||
|
if (!raw) return 0;
|
||||||
|
const value = Number.parseInt(raw, 10);
|
||||||
|
return Number.isNaN(value) ? 0 : value / multiplier;
|
||||||
|
},
|
||||||
|
fromValue: (value) =>
|
||||||
|
value <= 0 ? "" : String(Math.round(value * multiplier)),
|
||||||
|
formatDisplay,
|
||||||
|
});
|
||||||
|
|
||||||
|
interface NumberInputWithStepsProps {
|
||||||
|
value: string | undefined;
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
placeholder: string;
|
||||||
|
step: number;
|
||||||
|
converter: UnitConverter;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NumberInputWithSteps = ({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
placeholder,
|
||||||
|
step,
|
||||||
|
converter,
|
||||||
|
}: NumberInputWithStepsProps) => {
|
||||||
|
const numericValue = converter.toValue(value);
|
||||||
|
const displayValue = converter.formatDisplay(numericValue);
|
||||||
|
|
||||||
|
const handleIncrement = () =>
|
||||||
|
onChange(converter.fromValue(numericValue + step));
|
||||||
|
const handleDecrement = () =>
|
||||||
|
onChange(converter.fromValue(Math.max(0, numericValue - step)));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
className="h-9 w-9 shrink-0"
|
||||||
|
onClick={handleDecrement}
|
||||||
|
disabled={numericValue <= 0}
|
||||||
|
>
|
||||||
|
<MinusIcon className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
<Input
|
||||||
|
placeholder={placeholder}
|
||||||
|
value={value || ""}
|
||||||
|
onChange={(e) => onChange(e.target.value)}
|
||||||
|
className="text-center"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
className="h-9 w-9 shrink-0"
|
||||||
|
onClick={handleIncrement}
|
||||||
|
>
|
||||||
|
<PlusIcon className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{displayValue && (
|
||||||
|
<span className="text-xs text-muted-foreground text-center">
|
||||||
|
{displayValue}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
"docker:build:canary": "./docker/build.sh canary",
|
"docker:build:canary": "./docker/build.sh canary",
|
||||||
"docker:push:canary": "./docker/push.sh canary",
|
"docker:push:canary": "./docker/push.sh canary",
|
||||||
"version": "echo $(node -p \"require('./package.json').version\")",
|
"version": "echo $(node -p \"require('./package.json').version\")",
|
||||||
"test": "vitest --config __test__/vitest.config.ts volume-backups",
|
"test": "vitest --config __test__/vitest.config.ts",
|
||||||
"generate:openapi": "tsx -r dotenv/config scripts/generate-openapi.ts"
|
"generate:openapi": "tsx -r dotenv/config scripts/generate-openapi.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -68,6 +68,40 @@ export const aiRouter = createTRPCRouter({
|
|||||||
{ headers: {} },
|
{ headers: {} },
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
case "perplexity":
|
||||||
|
// Perplexity doesn't have a /models endpoint, return hardcoded list
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
id: "sonar-deep-research",
|
||||||
|
object: "model",
|
||||||
|
created: Date.now(),
|
||||||
|
owned_by: "perplexity",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "sonar-reasoning-pro",
|
||||||
|
object: "model",
|
||||||
|
created: Date.now(),
|
||||||
|
owned_by: "perplexity",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "sonar-reasoning",
|
||||||
|
object: "model",
|
||||||
|
created: Date.now(),
|
||||||
|
owned_by: "perplexity",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "sonar-pro",
|
||||||
|
object: "model",
|
||||||
|
created: Date.now(),
|
||||||
|
owned_by: "perplexity",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "sonar",
|
||||||
|
object: "model",
|
||||||
|
created: Date.now(),
|
||||||
|
owned_by: "perplexity",
|
||||||
|
},
|
||||||
|
] as Model[];
|
||||||
default:
|
default:
|
||||||
if (!input.apiKey)
|
if (!input.apiKey)
|
||||||
throw new TRPCError({
|
throw new TRPCError({
|
||||||
|
|||||||
@@ -1,21 +1,32 @@
|
|||||||
{
|
{
|
||||||
"name": "@dokploy/server",
|
"name": "@dokploy/server",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"main": "./src/index.ts",
|
"main": "./dist/index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": "./src/index.ts",
|
".": {
|
||||||
|
"import": "./dist/index.js",
|
||||||
|
"require": "./dist/index.cjs.js"
|
||||||
|
},
|
||||||
"./db": {
|
"./db": {
|
||||||
"import": "./src/db/index.ts",
|
"import": "./dist/db/index.js",
|
||||||
"require": "./dist/db/index.cjs.js"
|
"require": "./dist/db/index.cjs.js"
|
||||||
},
|
},
|
||||||
"./setup/*": {
|
"./*": {
|
||||||
"import": "./src/setup/*.ts",
|
"import": "./dist/*",
|
||||||
"require": "./dist/setup/index.cjs.js"
|
"require": "./dist/*.cjs"
|
||||||
},
|
},
|
||||||
"./constants": {
|
"./dist": {
|
||||||
"import": "./src/constants/index.ts",
|
"import": "./dist/index.js",
|
||||||
"require": "./dist/constants.cjs.js"
|
"require": "./dist/index.cjs.js"
|
||||||
|
},
|
||||||
|
"./dist/db": {
|
||||||
|
"import": "./dist/db/index.js",
|
||||||
|
"require": "./dist/db/index.cjs.js"
|
||||||
|
},
|
||||||
|
"./dist/db/schema": {
|
||||||
|
"import": "./dist/db/schema/index.js",
|
||||||
|
"require": "./dist/db/schema/index.cjs.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ const getRegistryCommands = (
|
|||||||
): string => {
|
): string => {
|
||||||
return `
|
return `
|
||||||
echo "📦 [Enabled Registry] Uploading image to '${registry.registryType}' | '${registryTag}'" ;
|
echo "📦 [Enabled Registry] Uploading image to '${registry.registryType}' | '${registryTag}'" ;
|
||||||
echo "${registry.password}" | docker login ${registry.registryUrl} -u ${registry.username} --password-stdin || {
|
echo "${registry.password}" | docker login ${registry.registryUrl} -u '${registry.username}' --password-stdin || {
|
||||||
echo "❌ DockerHub Failed" ;
|
echo "❌ DockerHub Failed" ;
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -167,9 +167,15 @@ while true; do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Execute command and capture exit code
|
||||||
${exec}
|
${exec}
|
||||||
|
EXIT_CODE=$?
|
||||||
|
|
||||||
echo "Execution completed."
|
# Wait for all background processes to complete to prevent zombie processes
|
||||||
|
wait
|
||||||
|
|
||||||
|
echo "Execution completed with exit code: $EXIT_CODE"
|
||||||
|
exit $EXIT_CODE
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const cleanupCommands = {
|
const cleanupCommands = {
|
||||||
|
|||||||
Reference in New Issue
Block a user