[PR #1492] feat: Allow resource syncs to self update #28684

Open
opened 2026-07-13 14:31:18 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/moghtech/komodo/pull/1492
Author: @scolastico
Created: 6/23/2026
Status: 🔄 Open

Base: mainHead: feat/allow-self-updating-syncs


📝 Commits (1)

  • 2123fc6 feat: allow resource syncs to self update

📊 Changes

8 files changed (+615 additions, -327 deletions)

View changed files

📝 bin/core/src/api/execute/sync.rs (+427 -323)
📝 bin/core/src/resource/mod.rs (+26 -1)
📝 bin/core/src/sync/execute.rs (+109 -3)
📝 client/core/rs/src/entities/sync.rs (+11 -0)
📝 client/core/ts/src/types.ts (+9 -0)
📝 docsite/docs/automate/sync-resources.md (+19 -0)
📝 ui/public/client/types.d.ts (+9 -0)
📝 ui/src/resources/sync/config.tsx (+5 -0)

📄 Description

Let resource syncs sync themselves

Fixes #676

What this does

A resource sync can declare itself among the resources it manages. Before
this change, running such a sync - when its own config had changed - always
failed with ResourceSync busy and left the sync stuck in Pending with
no clear recovery path. The only workarounds were removing the sync's own
declaration from the TOML, or hand-editing the UI config to match git exactly.

This PR makes a sync able to apply changes to itself during its own run, adds
an opt-in to immediately re-run when a sync changes its own scope, and turns
the "a sync would delete itself" case into an explicit hard error instead of a
confusing busy failure.

Why it happened

When a sync runs, it takes its own action-state lock (syncing = true) for the
entire run. Applying the sync's own changes goes through
resource::update::<ResourceSync>, which starts with a busy check:

if T::busy(&resource.id).await? {
  return Err(anyhow!("{} busy", T::resource_type()));
}

Because the sync is the thing holding the lock, this check always trips for the
sync's own entry - so it could never converge.

How it works

1. Apply self-updates, bypassing the busy check

  • resource::update was split into update (unchanged, busy-checked) and a new
    update_ignore_busy, sharing a private update_inner(..., check_busy: bool).
    No other call sites change.
  • A new execute_self_sync_updates pulls the sync's own entry out of the
    ResourceSync deltas and applies the config update directly via
    update_ignore_busy. This is safe: it runs sequentially as part of the
    in-progress sync, not concurrently. (Tag/description/template meta updates
    already worked, since update_meta has no busy check.)

2. Optional re-run when a sync changes its own scope (rerun_on_self_change)

A run reconciles against the config that was active when the run started. If a
sync changes its own config mid-run (e.g. a new resource_path), that new scope
only takes effect on the next run - the end-of-run RefreshResourceSyncPending
correctly recomputes pending against the new config, so the sync simply shows
Pending again (it does not get falsely marked "in sync").

New opt-in config field rerun_on_self_change (default false): when the sync
successfully changed its own config during a pass, run one more pass immediately
to apply the new scope, instead of waiting for an external trigger.

To support this, RunSync::resolve was refactored so the per-pass reconciliation
lives in execute_sync_pass(...), driven by a small loop hard-capped at
MAX_SYNC_PASSES = 2. The cap guarantees termination even if a sync's TOML never
quite diff-matches itself; if it's still Pending after the second pass it stays
Pending.

3. A sync can never delete itself (hard fail)

If delete/managed mode is enabled and the running sync is not declared in
its own resource files, it would otherwise be marked for deletion. This now
hard-fails the run up front - before any resource is mutated - with a clear
message, rather than silently skipping the deletion or failing as "busy":

Sync '' is configured to delete itself: it is not declared in its own
resource files while delete/managed mode is enabled. Declare it in the
resource files, or remove it manually / disable delete mode.

Failing early (right after deltas are computed) avoids a half-applied sync.

Behavior summary

Scenario Before After
Sync changes its own config ResourceSync busy, stuck Pending Applied during the run
rerun_on_self_change off (default) n/a First run applies the self change; new scope shows as Pending
rerun_on_self_change on n/a Runs once more automatically to apply the new scope
Sync would delete itself (delete/managed, not self-declared) ResourceSync busy Hard error before any mutation

Changes

  • bin/core/src/resource/mod.rs - split update into update /
    update_ignore_busy / update_inner(check_busy).
  • bin/core/src/sync/execute.rs - execute_self_sync_updates (applies the
    sync's own update, bypassing busy; returns whether config changed).
  • bin/core/src/api/execute/sync.rs - extracted execute_sync_pass, added the
    capped re-run loop and the early self-delete hard fail.
  • client/core/rs/src/entities/sync.rs - new rerun_on_self_change config
    field (default false).
  • client/core/ts/src/types.ts, ui/public/client/types.d.ts - generated TS
    type for the new field.
  • ui/src/resources/sync/config.tsx - "Re-run On Self Change" toggle.
  • docsite/docs/automate/sync-resources.md - documents self-managing syncs, the
    new option, and the self-delete hard fail.

Testing

  • cargo check -p komodo_core and -p komodo_client pass with no warnings.
  • Tested against local dev instance, see screenshots below.

Screenshots

image image

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/moghtech/komodo/pull/1492 **Author:** [@scolastico](https://github.com/scolastico) **Created:** 6/23/2026 **Status:** 🔄 Open **Base:** `main` ← **Head:** `feat/allow-self-updating-syncs` --- ### 📝 Commits (1) - [`2123fc6`](https://github.com/moghtech/komodo/commit/2123fc6fc25412b5621d3e66956f26233f4ee8cf) feat: allow resource syncs to self update ### 📊 Changes **8 files changed** (+615 additions, -327 deletions) <details> <summary>View changed files</summary> 📝 `bin/core/src/api/execute/sync.rs` (+427 -323) 📝 `bin/core/src/resource/mod.rs` (+26 -1) 📝 `bin/core/src/sync/execute.rs` (+109 -3) 📝 `client/core/rs/src/entities/sync.rs` (+11 -0) 📝 `client/core/ts/src/types.ts` (+9 -0) 📝 `docsite/docs/automate/sync-resources.md` (+19 -0) 📝 `ui/public/client/types.d.ts` (+9 -0) 📝 `ui/src/resources/sync/config.tsx` (+5 -0) </details> ### 📄 Description # Let resource syncs sync themselves Fixes #676 ## What this does A resource sync can declare *itself* among the resources it manages. Before this change, running such a sync - when its own config had changed - always failed with **`ResourceSync busy`** and left the sync stuck in `Pending` with no clear recovery path. The only workarounds were removing the sync's own declaration from the TOML, or hand-editing the UI config to match git exactly. This PR makes a sync able to apply changes to itself during its own run, adds an opt-in to immediately re-run when a sync changes its own scope, and turns the "a sync would delete itself" case into an explicit hard error instead of a confusing busy failure. ## Why it happened When a sync runs, it takes its own action-state lock (`syncing = true`) for the entire run. Applying the sync's own changes goes through `resource::update::<ResourceSync>`, which starts with a busy check: ```rust if T::busy(&resource.id).await? { return Err(anyhow!("{} busy", T::resource_type())); } ``` Because the sync is the thing holding the lock, this check always trips for the sync's own entry - so it could never converge. ## How it works ### 1. Apply self-updates, bypassing the busy check - `resource::update` was split into `update` (unchanged, busy-checked) and a new `update_ignore_busy`, sharing a private `update_inner(..., check_busy: bool)`. No other call sites change. - A new `execute_self_sync_updates` pulls the sync's own entry out of the `ResourceSync` deltas and applies the config update directly via `update_ignore_busy`. This is safe: it runs sequentially as part of the in-progress sync, not concurrently. (Tag/description/template meta updates already worked, since `update_meta` has no busy check.) ### 2. Optional re-run when a sync changes its own scope (`rerun_on_self_change`) A run reconciles against the config that was active when the run *started*. If a sync changes its own config mid-run (e.g. a new `resource_path`), that new scope only takes effect on the **next** run - the end-of-run `RefreshResourceSyncPending` correctly recomputes pending against the new config, so the sync simply shows `Pending` again (it does **not** get falsely marked "in sync"). New opt-in config field `rerun_on_self_change` (default `false`): when the sync successfully changed its own config during a pass, run one more pass immediately to apply the new scope, instead of waiting for an external trigger. To support this, `RunSync::resolve` was refactored so the per-pass reconciliation lives in `execute_sync_pass(...)`, driven by a small loop hard-capped at `MAX_SYNC_PASSES = 2`. The cap guarantees termination even if a sync's TOML never quite diff-matches itself; if it's still `Pending` after the second pass it stays `Pending`. ### 3. A sync can never delete itself (hard fail) If `delete`/`managed` mode is enabled and the running sync is **not** declared in its own resource files, it would otherwise be marked for deletion. This now **hard-fails the run up front** - before any resource is mutated - with a clear message, rather than silently skipping the deletion or failing as "busy": > Sync '<name>' is configured to delete itself: it is not declared in its own > resource files while delete/managed mode is enabled. Declare it in the > resource files, or remove it manually / disable delete mode. Failing early (right after deltas are computed) avoids a half-applied sync. ## Behavior summary | Scenario | Before | After | | --- | --- | --- | | Sync changes its own config | `ResourceSync busy`, stuck `Pending` | Applied during the run | | `rerun_on_self_change` off (default) | n/a | First run applies the self change; new scope shows as `Pending` | | `rerun_on_self_change` on | n/a | Runs once more automatically to apply the new scope | | Sync would delete itself (delete/managed, not self-declared) | `ResourceSync busy` | Hard error before any mutation | ## Changes - `bin/core/src/resource/mod.rs` - split `update` into `update` / `update_ignore_busy` / `update_inner(check_busy)`. - `bin/core/src/sync/execute.rs` - `execute_self_sync_updates` (applies the sync's own update, bypassing busy; returns whether config changed). - `bin/core/src/api/execute/sync.rs` - extracted `execute_sync_pass`, added the capped re-run loop and the early self-delete hard fail. - `client/core/rs/src/entities/sync.rs` - new `rerun_on_self_change` config field (default `false`). - `client/core/ts/src/types.ts`, `ui/public/client/types.d.ts` - generated TS type for the new field. - `ui/src/resources/sync/config.tsx` - "Re-run On Self Change" toggle. - `docsite/docs/automate/sync-resources.md` - documents self-managing syncs, the new option, and the self-delete hard fail. ## Testing - `cargo check -p komodo_core` and `-p komodo_client` pass with no warnings. - Tested against local dev instance, see screenshots below. ## Screenshots <img width="1345" height="1442" alt="image" src="https://github.com/user-attachments/assets/bda71add-f163-41a1-bb54-06cd120930f2" /> <img width="1319" height="293" alt="image" src="https://github.com/user-attachments/assets/dd1e33b3-ea6f-4039-8246-96162f861b58" /> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-07-13 14:31:18 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/komodo#28684