Require confirmation and block deleting library actions still referenced by node groups.

This commit is contained in:
2026-07-20 21:00:24 +02:00
parent d188ca41b8
commit be255341e5
5 changed files with 217 additions and 0 deletions
@@ -128,6 +128,23 @@ func GroupsForNode(store NodeActionGroupStore, nodeID string) []NodeActionGroup
return result
}
// LibraryActionInUse reports whether any group item still references actionID as a library source.
// Local clones (source local) do not count even if they previously came from that action.
func LibraryActionInUse(store NodeActionGroupStore, actionID string) bool {
actionID = strings.TrimSpace(actionID)
if actionID == "" {
return false
}
for _, group := range store.Groups {
for _, item := range group.Items {
if item.Source == ActionItemSourceLibrary && item.LibraryActionID == actionID {
return true
}
}
}
return false
}
// UpdateGroupLastRunAt sets LastRunAt and UpdatedAt for a group and saves.
func UpdateGroupLastRunAt(dir string, groupID string, at time.Time) error {
store, err := LoadNodeActionGroupsOrEmpty(dir)
@@ -66,6 +66,48 @@ func TestNodeActionGroupsRoundTrip(t *testing.T) {
}
}
func TestLibraryActionInUse(t *testing.T) {
store := NodeActionGroupStore{
Groups: []NodeActionGroup{
{
ID: "g1",
NodeID: "n1",
Name: "Nightly",
Items: []NodeActionItem{
{
ID: "lib-item",
Source: ActionItemSourceLibrary,
LibraryActionID: "custom-1",
},
{
ID: "local-item",
Source: ActionItemSourceLocal,
Name: "Echo",
Kind: ActionKindShell,
Body: "echo hi",
},
},
},
},
}
if !LibraryActionInUse(store, "custom-1") {
t.Fatal("expected library action custom-1 to be in use")
}
if LibraryActionInUse(store, "other") {
t.Fatal("expected unrelated action not in use")
}
store.Groups[0].Items[0] = NodeActionItem{
ID: "cloned",
Source: ActionItemSourceLocal,
Name: "Echo host",
Kind: ActionKindShell,
Body: "echo",
}
if LibraryActionInUse(store, "custom-1") {
t.Fatal("expected cloned local item not to count as in use")
}
}
func TestIsScheduleDueInterval(t *testing.T) {
now := time.Date(2026, 7, 19, 12, 0, 0, 0, time.Local)
group := NodeActionGroup{