feat: exit listener after successful switch for automatic restart

After a successful switch deployment, the listener now exits gracefully
so systemd can restart it with the new binary. This works together with
stopIfChanged/restartIfChanged to ensure deployments complete before
the service restarts.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-07 06:11:03 +01:00
parent ac3c9c7de6
commit efacb13b86
2 changed files with 26 additions and 8 deletions

View File

@@ -34,6 +34,10 @@ type Listener struct {
// Expanded subjects for discovery responses
expandedSubjects []string
// restartCh signals that the listener should exit for restart
// (e.g., after a successful switch deployment)
restartCh chan struct{}
}
// New creates a new listener with the given configuration.
@@ -43,10 +47,11 @@ func New(cfg Config, logger *slog.Logger) *Listener {
}
return &Listener{
cfg: cfg,
executor: deploy.NewExecutor(cfg.FlakeURL, cfg.Hostname, cfg.Timeout),
lock: deploy.NewLock(),
logger: logger,
cfg: cfg,
executor: deploy.NewExecutor(cfg.FlakeURL, cfg.Hostname, cfg.Timeout),
lock: deploy.NewLock(),
logger: logger,
restartCh: make(chan struct{}, 1),
}
}
@@ -93,9 +98,13 @@ func (l *Listener) Run(ctx context.Context) error {
l.logger.Info("listener started", "deploy_subjects", l.expandedSubjects, "discover_subject", discoverSubject)
// Wait for context cancellation
<-ctx.Done()
l.logger.Info("shutting down listener")
// Wait for context cancellation or restart signal
select {
case <-ctx.Done():
l.logger.Info("shutting down listener")
case <-l.restartCh:
l.logger.Info("exiting for restart after successful switch deployment")
}
return nil
}
@@ -185,6 +194,15 @@ func (l *Listener) handleDeployRequest(subject string, data []byte) {
messages.StatusCompleted,
"deployment completed successfully",
))
// After a successful switch, signal restart so we pick up any new version
if req.Action == messages.ActionSwitch {
select {
case l.restartCh <- struct{}{}:
default:
// Channel already has a signal pending
}
}
} else {
l.logger.Error("deployment failed",
"exit_code", result.ExitCode,