Skip to content

feat: auto-update time window with configurable interval and UX improvements#2405

Open
GiulioSavini wants to merge 16 commits intogetarcaneapp:mainfrom
GiulioSavini:feat/auto-update-window
Open

feat: auto-update time window with configurable interval and UX improvements#2405
GiulioSavini wants to merge 16 commits intogetarcaneapp:mainfrom
GiulioSavini:feat/auto-update-window

Conversation

@GiulioSavini
Copy link
Copy Markdown
Collaborator

@GiulioSavini GiulioSavini commented Apr 18, 2026

Summary

  • Auto-update time window: new settings (autoUpdateWindowEnabled, autoUpdateWindowStart, autoUpdateWindowEnd, autoUpdateWindowDays) gate the auto-update job so it only runs within a configured time window
  • Configurable window interval: the check frequency while the window is active (autoUpdateWindowInterval) is now a user-editable cron field (was hardcoded */5 * * * * *)
  • Run Now UX: the button shows a spinner + "Running" state for at least 3 seconds after triggering, giving clear visual feedback (the backend API returns immediately since jobs run async)
  • Save excluded containers on Run Now: clicking Run Now for the auto-update job first persists any pending changes to the excluded containers list before triggering the job
  • Schedule dialog highlight: the active cron example is highlighted with the primary colour when the edit-schedule dialog opens; clicking an example visually selects it

Test plan

  • Enable auto-update window, set start/end/days — verify auto-update job skips runs outside the window
  • Change autoUpdateWindowInterval to e.g. 0 */2 * * * *, save — verify scheduler picks up new cadence
  • Click Run Now on any job — button should spin for ~3s then return to normal with a success toast
  • Change excluded containers without saving, then click Run Now on auto-update — verify the new exclusion list is used
  • Open Edit Schedule dialog — current schedule example should be highlighted; clicking another highlights it

🤖 Generated with Claude Code

Disclaimer Greptiles Reviews use AI, make sure to check over its work.

To better help train Greptile on our codebase, if the comment is useful and valid Like the comment, if its not helpful or invalid Dislike

To have Greptile Re-Review the changes, mention greptileai.

Greptile Summary

This PR gates the auto-update job to a configurable time window (start/end time, days, check interval), adds a 3-second Run Now spinner with onBeforeRun pre-save, highlights the active cron example in the schedule dialog, and blocks Arcane self-redeploy via compose.

  • autoUpdateWindowInterval missing from changedAutoUpdate in settings_service.go: changing only the window interval and saving will not reschedule the job — the old cadence persists until restart, directly breaking the PR's own test plan.
  • autoUpdateWindowInterval missing from cronSettingKeys in libarcane/settings.go: no server-side cron validation is applied, so an invalid expression can be persisted and cause the scheduler to silently drop the auto-update job.

Confidence Score: 3/5

Two P1 defects affecting the window-interval scheduling path should be fixed before merging.

Two independent P1 bugs exist: the scheduler is not rescheduled when autoUpdateWindowInterval changes, and invalid cron expressions for that setting bypass server-side validation (potentially silently disabling auto-update). Both affect the primary new feature path and contradict the stated test plan.

backend/internal/services/settings_service.go (missing case in changedAutoUpdate switch) and backend/pkg/libarcane/settings.go (missing key in cronSettingKeys)

Comments Outside Diff (1)

  1. backend/pkg/libarcane/settings.go, line 35-42 (link)

    P1 autoUpdateWindowInterval is not validated as a cron expression

    ValidateCronSetting only checks keys in cronSettingKeys. Because autoUpdateWindowInterval is missing from that list, an invalid cron string (e.g. "not a cron") can be persisted without error. Schedule() then returns it verbatim to the cron scheduler, which will silently drop the job.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: backend/pkg/libarcane/settings.go
    Line: 35-42
    
    Comment:
    **`autoUpdateWindowInterval` is not validated as a cron expression**
    
    `ValidateCronSetting` only checks keys in `cronSettingKeys`. Because `autoUpdateWindowInterval` is missing from that list, an invalid cron string (e.g. `"not a cron"`) can be persisted without error. `Schedule()` then returns it verbatim to the cron scheduler, which will silently drop the job.
    
    
    
    How can I resolve this? If you propose a fix, please make it concise.

    Fix in Codex

Fix All in Codex

Prompt To Fix All With AI
This is a comment left during a code review.
Path: backend/internal/services/settings_service.go
Line: 759-762

Comment:
**Scheduler not rescheduled when `autoUpdateWindowInterval` changes**

`autoUpdateWindowInterval` is absent from the `changedAutoUpdate` case. When the user edits and saves the window interval, `OnAutoUpdateSettingsChanged` is never called, so the scheduler keeps its old cadence until a process restart — directly contradicting the PR test plan ("Change `autoUpdateWindowInterval`… save — verify scheduler picks up new cadence").

```suggestion
		case "autoUpdate", "autoUpdateInterval",
			"autoUpdateWindowEnabled", "autoUpdateWindowStart",
			"autoUpdateWindowEnd", "autoUpdateWindowDays",
			"autoUpdateWindowInterval":
			changedAutoUpdate = true
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: backend/pkg/libarcane/settings.go
Line: 35-42

Comment:
**`autoUpdateWindowInterval` is not validated as a cron expression**

`ValidateCronSetting` only checks keys in `cronSettingKeys`. Because `autoUpdateWindowInterval` is missing from that list, an invalid cron string (e.g. `"not a cron"`) can be persisted without error. `Schedule()` then returns it verbatim to the cron scheduler, which will silently drop the job.

```suggestion
var cronSettingKeys = []string{
	"scheduledPruneInterval",
	"autoUpdateInterval",
	"autoUpdateWindowInterval",
	"pollingInterval",
	"environmentHealthInterval",
	"eventCleanupInterval",
	"vulnerabilityScanInterval",
}
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: backend/pkg/scheduler/auto_update_job.go
Line: 67-68

Comment:
**Unexported functions missing `Internal` suffix**

Both `runAt` and `isWithinWindow` are unexported (lowercase) but don't carry the required `Internal` suffix per the project's naming convention. They should be renamed to `runAtInternal` and `isWithinWindowInternal`, and the test file updated accordingly.

```suggestion
func (j *AutoUpdateJob) runAtInternal(ctx context.Context, now time.Time) {
```

**Rule Used:** What: All unexported functions must have the "Inte... ([source](https://app.greptile.com/review/custom-context?memory=306fc233-4d2f-4ac4-bdf7-8059588e8a43))

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: frontend/src/lib/components/job-card/job-card.svelte
Line: 65-71

Comment:
**`onBeforeRun` failure is silently swallowed**

If saving the excluded containers list fails, the error is only printed to the console and the job is still triggered with the old exclusion list. The user receives no toast notification, so they may believe their changes took effect when they did not.

Consider surfacing the error to the user before proceeding:

```ts
if (onBeforeRun) {
    try {
        await onBeforeRun();
    } catch (err) {
        console.error('onBeforeRun failed:', err);
        toast.error(err instanceof Error ? err.message : m.jobs_run_failed());
        return;
    }
}
```

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: backend/pkg/libarcane/settings.go
Line: 44-50

Comment:
**`autoUpdateWindowSettingKeys` is declared but never used**

This slice is defined here but has no callers anywhere in the codebase. If it was intended for future use or as a helper for external consumers, it should be documented; otherwise it can be removed to avoid dead code.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "feat: configurable window interval, run-..." | Re-trigger Greptile

Greptile also left 4 inline comments on this PR.

@kmendell
Copy link
Copy Markdown
Member

kmendell commented Apr 18, 2026

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Comment on lines +759 to 762
case "autoUpdate", "autoUpdateInterval",
"autoUpdateWindowEnabled", "autoUpdateWindowStart",
"autoUpdateWindowEnd", "autoUpdateWindowDays":
changedAutoUpdate = true
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Scheduler not rescheduled when autoUpdateWindowInterval changes

autoUpdateWindowInterval is absent from the changedAutoUpdate case. When the user edits and saves the window interval, OnAutoUpdateSettingsChanged is never called, so the scheduler keeps its old cadence until a process restart — directly contradicting the PR test plan ("Change autoUpdateWindowInterval… save — verify scheduler picks up new cadence").

Suggested change
case "autoUpdate", "autoUpdateInterval",
"autoUpdateWindowEnabled", "autoUpdateWindowStart",
"autoUpdateWindowEnd", "autoUpdateWindowDays":
changedAutoUpdate = true
case "autoUpdate", "autoUpdateInterval",
"autoUpdateWindowEnabled", "autoUpdateWindowStart",
"autoUpdateWindowEnd", "autoUpdateWindowDays",
"autoUpdateWindowInterval":
changedAutoUpdate = true
Prompt To Fix With AI
This is a comment left during a code review.
Path: backend/internal/services/settings_service.go
Line: 759-762

Comment:
**Scheduler not rescheduled when `autoUpdateWindowInterval` changes**

`autoUpdateWindowInterval` is absent from the `changedAutoUpdate` case. When the user edits and saves the window interval, `OnAutoUpdateSettingsChanged` is never called, so the scheduler keeps its old cadence until a process restart — directly contradicting the PR test plan ("Change `autoUpdateWindowInterval`… save — verify scheduler picks up new cadence").

```suggestion
		case "autoUpdate", "autoUpdateInterval",
			"autoUpdateWindowEnabled", "autoUpdateWindowStart",
			"autoUpdateWindowEnd", "autoUpdateWindowDays",
			"autoUpdateWindowInterval":
			changedAutoUpdate = true
```

How can I resolve this? If you propose a fix, please make it concise.

Fix in Codex

Comment on lines 67 to 68
func (j *AutoUpdateJob) runAt(ctx context.Context, now time.Time) {
enabled := j.settingsService.GetBoolSetting(ctx, "autoUpdate", false)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Unexported functions missing Internal suffix

Both runAt and isWithinWindow are unexported (lowercase) but don't carry the required Internal suffix per the project's naming convention. They should be renamed to runAtInternal and isWithinWindowInternal, and the test file updated accordingly.

Suggested change
func (j *AutoUpdateJob) runAt(ctx context.Context, now time.Time) {
enabled := j.settingsService.GetBoolSetting(ctx, "autoUpdate", false)
func (j *AutoUpdateJob) runAtInternal(ctx context.Context, now time.Time) {

Rule Used: What: All unexported functions must have the "Inte... (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: backend/pkg/scheduler/auto_update_job.go
Line: 67-68

Comment:
**Unexported functions missing `Internal` suffix**

Both `runAt` and `isWithinWindow` are unexported (lowercase) but don't carry the required `Internal` suffix per the project's naming convention. They should be renamed to `runAtInternal` and `isWithinWindowInternal`, and the test file updated accordingly.

```suggestion
func (j *AutoUpdateJob) runAtInternal(ctx context.Context, now time.Time) {
```

**Rule Used:** What: All unexported functions must have the "Inte... ([source](https://app.greptile.com/review/custom-context?memory=306fc233-4d2f-4ac4-bdf7-8059588e8a43))

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Fix in Codex

Comment on lines +65 to +71
if (onBeforeRun) {
try {
await onBeforeRun();
} catch (err) {
console.error('onBeforeRun failed:', err);
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 onBeforeRun failure is silently swallowed

If saving the excluded containers list fails, the error is only printed to the console and the job is still triggered with the old exclusion list. The user receives no toast notification, so they may believe their changes took effect when they did not.

Consider surfacing the error to the user before proceeding:

if (onBeforeRun) {
    try {
        await onBeforeRun();
    } catch (err) {
        console.error('onBeforeRun failed:', err);
        toast.error(err instanceof Error ? err.message : m.jobs_run_failed());
        return;
    }
}
Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/src/lib/components/job-card/job-card.svelte
Line: 65-71

Comment:
**`onBeforeRun` failure is silently swallowed**

If saving the excluded containers list fails, the error is only printed to the console and the job is still triggered with the old exclusion list. The user receives no toast notification, so they may believe their changes took effect when they did not.

Consider surfacing the error to the user before proceeding:

```ts
if (onBeforeRun) {
    try {
        await onBeforeRun();
    } catch (err) {
        console.error('onBeforeRun failed:', err);
        toast.error(err instanceof Error ? err.message : m.jobs_run_failed());
        return;
    }
}
```

How can I resolve this? If you propose a fix, please make it concise.

Fix in Codex

Comment thread backend/pkg/libarcane/settings.go Outdated
Comment on lines +44 to +50
var autoUpdateWindowSettingKeys = []string{
"autoUpdateWindowEnabled",
"autoUpdateWindowStart",
"autoUpdateWindowEnd",
"autoUpdateWindowDays",
"autoUpdateWindowInterval",
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 autoUpdateWindowSettingKeys is declared but never used

This slice is defined here but has no callers anywhere in the codebase. If it was intended for future use or as a helper for external consumers, it should be documented; otherwise it can be removed to avoid dead code.

Prompt To Fix With AI
This is a comment left during a code review.
Path: backend/pkg/libarcane/settings.go
Line: 44-50

Comment:
**`autoUpdateWindowSettingKeys` is declared but never used**

This slice is defined here but has no callers anywhere in the codebase. If it was intended for future use or as a helper for external consumers, it should be documented; otherwise it can be removed to avoid dead code.

How can I resolve this? If you propose a fix, please make it concise.

Fix in Codex

Giulio Savini added 2 commits April 18, 2026 14:00
- add autoUpdateWindowInterval to changedAutoUpdate trigger so scheduler
  reschedules immediately when the window interval is saved
- rename runAt/isWithinWindow to runAtInternal/isWithinWindowInternal per
  project naming convention; update test file accordingly
- surface onBeforeRun failure as toast and abort the run instead of
  silently proceeding with stale data
- remove unused autoUpdateWindowSettingKeys dead-code slice
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants