feat(fleet): reapply Compose configuration without a version update (#1716)

* feat(fleet): reapply Compose configuration without a version update

Add a distinct Fleet Reapply configuration path so Compose-managed nodes can recreate Sencho from the current on-disk project when already up to date, without pulling or rewriting the image reference.

* fix(fleet): confirm remote reapply and close concurrent tracker race

Require confirmation for remote compose reapply, and lock dispatch before the remote POST so a second request cannot overwrite a successful in-flight tracker.

* fix(ui): icon-only Reapply control so Up to date badge can breathe

Collapse the Node updates Reapply label into a tooltip so the status pill no longer wraps in the Status column.

* feat(editor): Save & Reapply self-stack via fleet compose reapply (#1726)

* feat(editor): Save & Reapply self-stack via fleet compose reapply

Eligible admins can apply on-disk Compose edits to Sencho's own stack from the editor using the same confirm, dispatch, and reconnect path as Fleet Node Updates.

* fix(editor): gate Save & Reapply label to self-stack only

Ordinary stacks were labeled Save & Reapply whenever the node was
reapply-eligible. Require the selected file to be the self-stack for the
toolbar label and diff confirm CTA.

* fix(ui): move compose diff action label helper out of dialog module

Keep ComposeDiffPreviewDialog component-only so react-refresh Fast Refresh
lint passes after the Save and reapply stacked merge.
This commit is contained in:
Anso
2026-07-28 14:26:46 -04:00
committed by GitHub
parent 543e4ef256
commit b0b423b234
42 changed files with 1770 additions and 168 deletions
@@ -0,0 +1,48 @@
import { describe, it, expect, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
import { ComposeDiffPreviewDialog } from '../ComposeDiffPreviewDialog';
import { resolveComposeDiffActionLabel } from '../resolveComposeDiffActionLabel';
vi.mock('@/lib/monacoLoader', () => ({
DiffEditor: () => <div data-testid="diff-editor" />,
}));
describe('resolveComposeDiffActionLabel', () => {
it('returns Save for save-only mode', () => {
expect(resolveComposeDiffActionLabel('save', false)).toBe('Save');
expect(resolveComposeDiffActionLabel('save', true)).toBe('Save');
});
it('returns Save when mode is undefined', () => {
expect(resolveComposeDiffActionLabel(undefined, true)).toBe('Save');
});
it('returns Save & deploy for ordinary save-and-deploy', () => {
expect(resolveComposeDiffActionLabel('save-and-deploy', false)).toBe('Save & deploy');
});
it('returns Save & reapply when self-stack reapply is eligible', () => {
expect(resolveComposeDiffActionLabel('save-and-deploy', true)).toBe('Save & reapply');
});
});
describe('ComposeDiffPreviewDialog', () => {
it('renders the Save & reapply confirm CTA', () => {
render(
<ComposeDiffPreviewDialog
open
onOpenChange={vi.fn()}
stackName="sencho"
fileName="docker-compose.yml"
language="yaml"
original="a"
modified="b"
actionLabel="Save & reapply"
confirming={false}
isDarkMode={false}
onConfirm={vi.fn()}
/>,
);
expect(screen.getByRole('button', { name: 'Save & reapply' })).toBeInTheDocument();
});
});