Files
portainer/app/react/docker/containers/update/resolveContainerUpdatePath.ts
T
claude code agent 922f506fe5 feat(automation): guard update→rollback loop; name Settings types; tests & doc fixes (F1-F7)
F1: record rolled-back targets per service (endpointID/containerName + remote
    digest) and skip auto-update during a 24h cooldown unless the remote digest
    changes — breaks the infinite update→rollback loop on a persistently
    unhealthy image, without blocking a genuinely new image.
F2: unit-test applyContainerUpdate dispatch/payload mapping.
F3: settings_update.go comments mention auto-heal AND auto-update.
F4: drop stale '(future M4)' TS docs; primitives are frontend-only.
F5: replace the anonymous ContainerAutomation settings struct with named
    types (identical JSON tags).
F6: drop parseEnable (duplicate of boolLabel).
F7: remove the unused gitService dependency.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 14:29:57 +03:00

50 lines
1.7 KiB
TypeScript

import { COMPOSE_STACK_NAME_LABEL } from '@/react/constants';
import { Stack, StackType } from '@/react/common/stacks/types';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { ContainerUpdatePath } from './types';
/**
* Decide how a container's image update must be applied, given the list of
* Portainer-managed stacks. Pure and side-effect free so it can be unit-tested
* and reused by the details button and the bulk action. (The backend auto-update
* daemon mirrors the same routing separately in Go.)
*
* - No compose project label -> `standalone` (recreate-with-pull).
* - Compose project that matches a Portainer Docker Compose `Stack` (same name +
* endpoint + compose type) -> `stack` (redeploy-with-pull, keeps the container
* in its stack).
* - Compose project with no matching Portainer compose stack -> `external`:
* managed outside Portainer (or a same-named stack of another type), so we
* must not recreate it (would detach it / drift).
*/
export function resolveContainerUpdatePath(
context: { labels?: Record<string, string>; environmentId: EnvironmentId },
stacks: Stack[]
): ContainerUpdatePath {
const projectName = context.labels?.[COMPOSE_STACK_NAME_LABEL];
if (!projectName) {
return { kind: 'standalone' };
}
// Match by name + endpoint, but only a Docker Compose stack: a same-named
// swarm/kubernetes stack must not be redeployed via the compose update path.
const stack = stacks.find(
(s) =>
s.Name === projectName &&
s.EndpointId === context.environmentId &&
s.Type === StackType.DockerCompose
);
if (!stack) {
return { kind: 'external' };
}
return {
kind: 'stack',
stackId: stack.Id,
isGitStack: !!stack.GitConfig,
};
}