From 7185047eb724362590407bb0fae21c0a3f6821d7 Mon Sep 17 00:00:00 2001 From: lear Date: Wed, 4 Mar 2026 11:07:42 +0300 Subject: [PATCH] fix: add docker login before rollback and fix execAsyncRemote argument order --- packages/server/src/services/rollbacks.ts | 31 ++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/packages/server/src/services/rollbacks.ts b/packages/server/src/services/rollbacks.ts index 00c60ebc8..301438fed 100644 --- a/packages/server/src/services/rollbacks.ts +++ b/packages/server/src/services/rollbacks.ts @@ -111,7 +111,7 @@ const deleteRollbackImage = async (image: string, serverId?: string | null) => { const command = `docker image rm ${image} --force`; if (serverId) { - await execAsyncRemote(command, serverId); + await execAsyncRemote(serverId, command); } else { await execAsync(command); } @@ -171,6 +171,27 @@ export const rollback = async (rollbackId: string) => { ); }; +const dockerLoginForRegistry = async ( + registry: Registry, + serverId?: string | null, +) => { + const escapedRegistry = shEscape(registry.registryUrl); + const escapedUser = shEscape(registry.username); + const escapedPassword = shEscape(registry.password); + const loginCommand = `printf %s ${escapedPassword} | docker login ${escapedRegistry} -u ${escapedUser} --password-stdin`; + + if (serverId) { + await execAsyncRemote(serverId, loginCommand); + } else { + await execAsync(loginCommand); + } +}; + +function shEscape(s: string | undefined): string { + if (!s) return "''"; + return `'${s.replace(/'/g, `'\\''`)}'`; +} + const rollbackApplication = async ( appName: string, image: string, @@ -188,6 +209,14 @@ const rollbackApplication = async ( throw new Error("Full context is required for rollback"); } + // Ensure Docker daemon is authenticated with the rollback registry + // before updating the swarm service. The authconfig in CreateServiceOptions + // alone is not sufficient — Docker Swarm also relies on the daemon's + // cached credentials (~/.docker/config.json) to distribute auth to nodes. + if (fullContext.rollbackRegistry) { + await dockerLoginForRegistry(fullContext.rollbackRegistry, serverId); + } + const docker = await getRemoteDocker(serverId); // Use the same configuration as mechanizeDockerContainer