e63d2ffe9b
Clicking "Update" on a stack member (and the native auto-update daemon updating one) redeployed the whole compose stack instead of updating just that container. Match Watchtower behaviour: always recreate the single container with a re-pull. The recreate endpoint preserves config + compose labels, so the container stays part of its project. Collapse all update surfaces to a single-container recreate and drop the now-dead stack-aware routing: - frontend: "Update now" button, list badge and bulk "Update selected" now recreate each container individually; remove standalone/stack/external routing, the external refusal, the PortainerStackUpdate gate and the stack-update confirm dialog. - daemon: route every outdated candidate through updateStandalone; remove updateStack, the stack/external grouping and the stackDeployer dependency. - add a regression test asserting a Portainer-managed compose-stack member is recreated individually, not stack-redeployed. Behavioural notes: git/external compose containers are now auto-updated too (were detect-only), and updating a stack member no longer requires PortainerStackUpdate (same auth as the normal Recreate action). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
54 lines
1.7 KiB
TypeScript
54 lines
1.7 KiB
TypeScript
import { QueryClient, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
|
|
import { withError } from '@/react-tools/react-query';
|
|
|
|
import { queryKeys as containerQueryKeys } from '../queries/query-keys';
|
|
|
|
import { applyContainerUpdate } from './applyContainerUpdate';
|
|
import { ContainerUpdateContext } from './types';
|
|
|
|
/**
|
|
* Refresh the data affected by a single-container image update: the container
|
|
* itself and its image-status badge (so it flips away from "outdated"). A
|
|
* single-container recreate doesn't change the stacks list, so that isn't
|
|
* invalidated here.
|
|
*/
|
|
export function invalidateContainerUpdateQueries(
|
|
queryClient: QueryClient,
|
|
context: ContainerUpdateContext
|
|
) {
|
|
queryClient.invalidateQueries(
|
|
containerQueryKeys.container(context.environmentId, context.id)
|
|
);
|
|
queryClient.invalidateQueries(
|
|
containerQueryKeys.imageStatus(
|
|
context.environmentId,
|
|
context.id,
|
|
context.nodeName
|
|
)
|
|
);
|
|
}
|
|
|
|
interface UpdateContainerImageParams {
|
|
context: ContainerUpdateContext;
|
|
pullImage?: boolean;
|
|
}
|
|
|
|
/**
|
|
* Single-container image-update mutation built on the shared apply primitive.
|
|
* Used by the details-view "Update now" button; the bulk action drives the same
|
|
* primitive directly (see useBulkUpdateContainerImages).
|
|
*/
|
|
export function useUpdateContainerImage() {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: ({ context, pullImage }: UpdateContainerImageParams) =>
|
|
applyContainerUpdate(context, { pullImage }),
|
|
onSuccess: (_result, { context }) => {
|
|
invalidateContainerUpdateQueries(queryClient, context);
|
|
},
|
|
...withError('Unable to update container image'),
|
|
});
|
|
}
|