Merge pull request #3441 from Dokploy/3260-dokploy-automatically-updates-itself-but-automated-updates-are-disabled-in-the-settings

chore(dependencies): update semver to version 7.7.3 and add @types/se…
This commit is contained in:
Mauricio Siu
2026-01-11 22:16:09 -06:00
committed by GitHub
5 changed files with 124 additions and 61 deletions

View File

@@ -153,9 +153,11 @@
"xterm-addon-fit": "^0.8.0",
"yaml": "2.8.1",
"zod": "^3.25.32",
"zod-form-data": "^2.0.7"
"zod-form-data": "^2.0.7",
"semver": "7.7.3"
},
"devDependencies": {
"@types/semver": "7.7.1",
"@types/shell-quote": "^1.7.5",
"@types/adm-zip": "^0.5.7",
"@types/bcrypt": "5.0.2",

View File

@@ -88,7 +88,7 @@ export const settingsRouter = createTRPCRouter({
if (IS_CLOUD) {
return true;
}
await reloadDockerResource("dokploy");
await reloadDockerResource("dokploy", undefined, packageInfo.version);
return true;
}),
cleanRedis: adminProcedure.mutation(async () => {
@@ -399,7 +399,7 @@ export const settingsRouter = createTRPCRouter({
return DEFAULT_UPDATE_DATA;
}
return await getUpdateData();
return await getUpdateData(packageInfo.version);
}),
updateServer: adminProcedure.mutation(async () => {
if (IS_CLOUD) {

View File

@@ -78,9 +78,11 @@
"ssh2": "1.15.0",
"toml": "3.0.0",
"ws": "8.16.0",
"zod": "^3.25.32"
"zod": "^3.25.32",
"semver": "7.7.3"
},
"devDependencies": {
"@types/semver": "7.7.1",
"@types/adm-zip": "^0.5.7",
"@types/bcrypt": "5.0.2",
"@types/dockerode": "3.3.23",

View File

@@ -5,12 +5,12 @@ import {
execAsync,
execAsyncRemote,
} from "@dokploy/server/utils/process/execAsync";
import semver from "semver";
import {
initializeStandaloneTraefik,
initializeTraefikService,
type TraefikOptions,
} from "../setup/traefik-setup";
export interface IUpdateData {
latestVersion: string | null;
updateAvailable: boolean;
@@ -55,56 +55,95 @@ export const getServiceImageDigest = async () => {
};
/** Returns latest version number and information whether server update is available by comparing current image's digest against digest for provided image tag via Docker hub API. */
export const getUpdateData = async (): Promise<IUpdateData> => {
let currentDigest: string;
export const getUpdateData = async (
currentVersion: string,
): Promise<IUpdateData> => {
try {
currentDigest = await getServiceImageDigest();
} catch (error) {
// TODO: Docker versions 29.0.0 change the way to get the service image digest, so we need to update this in the future we upgrade to that version.
return DEFAULT_UPDATE_DATA;
}
const baseUrl =
"https://hub.docker.com/v2/repositories/dokploy/dokploy/tags";
let url: string | null = `${baseUrl}?page_size=100`;
let allResults: { digest: string; name: string }[] = [];
const baseUrl = "https://hub.docker.com/v2/repositories/dokploy/dokploy/tags";
let url: string | null = `${baseUrl}?page_size=100`;
let allResults: { digest: string; name: string }[] = [];
while (url) {
const response = await fetch(url, {
method: "GET",
headers: { "Content-Type": "application/json" },
});
// Fetch all tags from Docker Hub
while (url) {
const response = await fetch(url, {
method: "GET",
headers: { "Content-Type": "application/json" },
});
const data = (await response.json()) as {
next: string | null;
results: { digest: string; name: string }[];
};
const data = (await response.json()) as {
next: string | null;
results: { digest: string; name: string }[];
};
allResults = allResults.concat(data.results);
url = data?.next;
}
allResults = allResults.concat(data.results);
url = data?.next;
}
const imageTag = getDokployImageTag();
const searchedDigest = allResults.find((t) => t.name === imageTag)?.digest;
const currentImageTag = getDokployImageTag();
if (!searchedDigest) {
return DEFAULT_UPDATE_DATA;
}
// Special handling for canary and feature branches
// For development versions (canary/feature), don't perform update checks
// These are unstable versions that change frequently, and users on these
// branches are expected to manually manage updates
if (currentImageTag === "canary" || currentImageTag === "feature") {
const currentDigest = await getServiceImageDigest();
const latestDigest = allResults.find(
(t) => t.name === currentImageTag,
)?.digest;
if (!latestDigest) {
return DEFAULT_UPDATE_DATA;
}
if (currentDigest !== latestDigest) {
return {
latestVersion: currentImageTag,
updateAvailable: true,
};
}
return {
latestVersion: currentImageTag,
updateAvailable: false,
};
}
if (imageTag === "latest") {
const versionedTag = allResults.find(
(t) => t.digest === searchedDigest && t.name.startsWith("v"),
);
// For stable versions, use semver comparison
// Find the "latest" tag and get its digest
const latestTag = allResults.find((t) => t.name === "latest");
if (!versionedTag) {
if (!latestTag) {
return DEFAULT_UPDATE_DATA;
}
const { name: latestVersion, digest } = versionedTag;
const updateAvailable = digest !== currentDigest;
// Find the versioned tag (v0.x.x) that has the same digest as "latest"
const latestVersionTag = allResults.find(
(t) => t.digest === latestTag.digest && t.name.startsWith("v"),
);
return { latestVersion, updateAvailable };
if (!latestVersionTag) {
return DEFAULT_UPDATE_DATA;
}
const latestVersion = latestVersionTag.name;
// Use semver to compare versions for stable releases
const cleanedCurrent = semver.clean(currentVersion);
const cleanedLatest = semver.clean(latestVersion);
if (!cleanedCurrent || !cleanedLatest) {
return DEFAULT_UPDATE_DATA;
}
// Check if the latest version is greater than the current version
const updateAvailable = semver.gt(cleanedLatest, cleanedCurrent);
return {
latestVersion,
updateAvailable,
};
} catch (error) {
console.error("Error fetching update data:", error);
return DEFAULT_UPDATE_DATA;
}
const updateAvailable = searchedDigest !== currentDigest;
return { latestVersion: imageTag, updateAvailable };
};
interface TreeDataItem {
@@ -254,11 +293,22 @@ fi`;
export const reloadDockerResource = async (
resourceName: string,
serverId?: string,
version?: string,
) => {
const resourceType = await getDockerResourceType(resourceName, serverId);
let command = "";
if (resourceType === "service") {
command = `docker service update --force ${resourceName}`;
if (resourceName === "dokploy") {
const currentImageTag = getDokployImageTag();
let imageTag = version;
if (currentImageTag === "canary" || currentImageTag === "feature") {
imageTag = currentImageTag;
}
command = `docker service update --force --image dokploy/dokploy:${imageTag} ${resourceName}`;
} else {
command = `docker service update --force ${resourceName}`;
}
} else if (resourceType === "standalone") {
command = `docker restart ${resourceName}`;
} else {

45
pnpm-lock.yaml generated
View File

@@ -400,6 +400,9 @@ importers:
recharts:
specifier: ^2.15.3
version: 2.15.3(react-dom@18.2.0(react@18.2.0))(react@18.2.0)
semver:
specifier: 7.7.3
version: 7.7.3
shell-quote:
specifier: ^1.8.1
version: 1.8.2
@@ -485,6 +488,9 @@ importers:
'@types/react-dom':
specifier: 18.3.0
version: 18.3.0
'@types/semver':
specifier: 7.7.1
version: 7.7.1
'@types/shell-quote':
specifier: ^1.7.5
version: 1.7.5
@@ -717,6 +723,9 @@ importers:
react-dom:
specifier: 18.2.0
version: 18.2.0(react@18.2.0)
semver:
specifier: 7.7.3
version: 7.7.3
shell-quote:
specifier: ^1.8.1
version: 1.8.2
@@ -772,6 +781,9 @@ importers:
'@types/react-dom':
specifier: 18.3.0
version: 18.3.0
'@types/semver':
specifier: 7.7.1
version: 7.7.1
'@types/shell-quote':
specifier: ^1.7.5
version: 1.7.5
@@ -4048,6 +4060,9 @@ packages:
'@types/readable-stream@4.0.20':
resolution: {integrity: sha512-eLgbR5KwUh8+6pngBDxS32MymdCsCHnGtwHTrC0GDorbc7NbcnkZAWptDLgZiRk9VRas+B6TyRgPDucq4zRs8g==}
'@types/semver@7.7.1':
resolution: {integrity: sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==}
'@types/shell-quote@1.7.5':
resolution: {integrity: sha512-+UE8GAGRPbJVQDdxi16dgadcBfQ+KG2vgZhV1+3A1XmHbmwcdwhCUwIdy+d3pAGrbvgRoVSjeI9vOWyq376Yzw==}
@@ -7069,11 +7084,6 @@ packages:
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
hasBin: true
semver@7.7.2:
resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
engines: {node: '>=10'}
hasBin: true
semver@7.7.3:
resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==}
engines: {node: '>=10'}
@@ -8101,7 +8111,7 @@ snapshots:
'@commitlint/is-ignored@19.8.1':
dependencies:
'@commitlint/types': 19.8.1
semver: 7.7.2
semver: 7.7.3
'@commitlint/lint@19.8.1':
dependencies:
@@ -8718,7 +8728,7 @@ snapshots:
nopt: 5.0.0
npmlog: 5.0.1
rimraf: 3.0.2
semver: 7.7.2
semver: 7.7.3
tar: 6.2.1
transitivePeerDependencies:
- encoding
@@ -9309,7 +9319,7 @@ snapshots:
'@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0)
'@opentelemetry/semantic-conventions': 1.28.0
forwarded-parse: 2.1.2
semver: 7.7.2
semver: 7.7.3
transitivePeerDependencies:
- supports-color
@@ -9510,7 +9520,7 @@ snapshots:
'@types/shimmer': 1.2.0
import-in-the-middle: 1.14.2
require-in-the-middle: 7.5.2
semver: 7.7.2
semver: 7.7.3
shimmer: 1.2.1
transitivePeerDependencies:
- supports-color
@@ -9655,7 +9665,7 @@ snapshots:
'@opentelemetry/propagator-b3': 1.30.1(@opentelemetry/api@1.9.0)
'@opentelemetry/propagator-jaeger': 1.30.1(@opentelemetry/api@1.9.0)
'@opentelemetry/sdk-trace-base': 1.30.1(@opentelemetry/api@1.9.0)
semver: 7.7.2
semver: 7.7.3
'@opentelemetry/semantic-conventions@1.28.0': {}
@@ -11403,6 +11413,8 @@ snapshots:
dependencies:
'@types/node': 20.17.51
'@types/semver@7.7.1': {}
'@types/shell-quote@1.7.5': {}
'@types/shimmer@1.2.0': {}
@@ -11802,7 +11814,7 @@ snapshots:
lodash: 4.17.21
msgpackr: 1.11.4
node-abort-controller: 3.1.1
semver: 7.7.2
semver: 7.7.3
tslib: 2.8.1
uuid: 9.0.1
transitivePeerDependencies:
@@ -12318,7 +12330,7 @@ snapshots:
'@one-ini/wasm': 0.1.1
commander: 10.0.1
minimatch: 9.0.1
semver: 7.7.2
semver: 7.7.3
electron-to-chromium@1.5.159: {}
@@ -12632,7 +12644,7 @@ snapshots:
'@petamoriken/float16': 3.9.2
debug: 4.4.1
env-paths: 3.0.0
semver: 7.7.2
semver: 7.7.3
shell-quote: 1.8.2
which: 4.0.0
transitivePeerDependencies:
@@ -13118,7 +13130,7 @@ snapshots:
lodash.isstring: 4.0.1
lodash.once: 4.1.1
ms: 2.1.3
semver: 7.7.2
semver: 7.7.3
jss-plugin-camel-case@10.10.0:
dependencies:
@@ -14650,10 +14662,7 @@ snapshots:
semver@6.3.1: {}
semver@7.7.2: {}
semver@7.7.3:
optional: true
semver@7.7.3: {}
serialize-error-cjs@0.1.4: {}