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

@@ -16,7 +16,7 @@ import (
"github.com/urfave/cli/v3" "github.com/urfave/cli/v3"
) )
const version = "0.1.4" const version = "0.1.5"
func main() { func main() {
app := &cli.Command{ app := &cli.Command{

View File

@@ -34,6 +34,10 @@ type Listener struct {
// Expanded subjects for discovery responses // Expanded subjects for discovery responses
expandedSubjects []string 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. // New creates a new listener with the given configuration.
@@ -47,6 +51,7 @@ func New(cfg Config, logger *slog.Logger) *Listener {
executor: deploy.NewExecutor(cfg.FlakeURL, cfg.Hostname, cfg.Timeout), executor: deploy.NewExecutor(cfg.FlakeURL, cfg.Hostname, cfg.Timeout),
lock: deploy.NewLock(), lock: deploy.NewLock(),
logger: logger, 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) l.logger.Info("listener started", "deploy_subjects", l.expandedSubjects, "discover_subject", discoverSubject)
// Wait for context cancellation // Wait for context cancellation or restart signal
<-ctx.Done() select {
case <-ctx.Done():
l.logger.Info("shutting down listener") l.logger.Info("shutting down listener")
case <-l.restartCh:
l.logger.Info("exiting for restart after successful switch deployment")
}
return nil return nil
} }
@@ -185,6 +194,15 @@ func (l *Listener) handleDeployRequest(subject string, data []byte) {
messages.StatusCompleted, messages.StatusCompleted,
"deployment completed successfully", "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 { } else {
l.logger.Error("deployment failed", l.logger.Error("deployment failed",
"exit_code", result.ExitCode, "exit_code", result.ExitCode,