How Dewy Handles Deployment Status Notifications: A Deep Dive into the Notifier Subsystem

Dewy routes deployment status notifications through a pluggable notifier interface that supports Slack, email, and null implementations, with built-in error throttling via ErrorLimitingSender to prevent notification spam.

The linyows/dewy deployment tool provides real-time feedback throughout the deployment lifecycle using a dedicated notifier subsystem. When Dewy performs a deployment, it reports every stage—from initialization through hook execution to final success or failure—via configurable channels. Understanding how Dewy handles deployment status notifications helps operators monitor infrastructure changes and debug failures without noise.

The Notification Lifecycle in Dewy

Dewy’s notification flow follows the deployment lifecycle sequentially, with specific methods called at each stage.

Notifier Initialization

Before any deployment begins, Dewy constructs the notifier in dewy.go:

// dewy.go – New() (excerpt)
d.notifier, err = notifier.New(ctx, d.config.Notifier, d.logger.Logger)

The notifier.New factory function parses the configuration URL (e.g., slack://my-channel?quiet=true or mail://...) and returns a concrete implementation wrapped by ErrorLimitingSender. This wrapper adds error-count limiting and quiet mode handling regardless of the underlying transport.

Source: [notifier/notifier.go](https://github.com/linyows/dewy/blob/main/notifier/notifier.go)

Deployment Start Notification

When Dewy begins execution, it immediately notifies the configured channel:

msg := fmt.Sprintf(
    "Automatic shipping started by *Dewy* (v%s: %s)",
    d.config.Version, d.config.Command.String(),
)
d.logger.Info("Dewy start notification", slog.String("message", msg))
d.notifier.Send(ctx, msg)

Source: [dewy.go line 52‑55](https://github.com/linyows/dewy/blob/main/dewy.go#L52-L55)

Pre-Deploy Hook Results

Before extracting the archive, Dewy executes the configured BeforeDeployHook and reports the result:

beforeResult, beforeErr := d.execHook(d.config.BeforeDeployHook)
if beforeResult != nil {
    d.notifier.SendHookResult(ctx, "Before Deploy", beforeResult)
}

Source: [dewy.go line 10‑13](https://github.com/linyows/dewy/blob/main/dewy.go#L10-L13)

The SendHookResult method creates a detailed attachment containing stdout, stderr, exit code, and duration. In the Slack implementation, this renders as a rich message block via BuildHookAttachment.

Source: [slack.go → BuildHookAttachment](https://github.com/linyows/dewy/blob/main/notifier/slack.go#L44-L98)

Deployment Execution and Error Handling

If the deployment fails during archive extraction or symlink switching, Dewy captures the error:

if err != nil {
    d.logger.Error("Preserve failure", slog.String("error", err.Error()))
    d.notifier.SendError(ctx, err)
    return err
}

The ErrorLimitingSender.SendError implementation tracks the error count, limiting noisy notifications to a maximum of three messages while still logging all errors internally.

Source: [notifier/notifier.go line 79‑90](https://github.com/linyows/dewy/blob/main/notifier/notifier.go#L79-L90)

Post-Deploy Hook Results

After successful deployment, a deferred function executes the AfterDeployHook and reports its result:

defer func() {
    if err != nil { return }
    afterResult, afterErr := d.execHook(d.config.AfterDeployHook)
    if afterResult != nil {
        d.notifier.SendHookResult(ctx, "After Deploy", afterResult)
    }
    // …
}()

Source: [dewy.go line 23‑27](https://github.com/linyows/dewy/blob/main/dewy.go#L23-L27)

Error Throttling and Quiet Mode

Dewy prevents notification spam through the ErrorLimitingSender wrapper. This component maintains an errorCount and a quiet flag parsed from the notifier URL (e.g., ?quiet=true).

  • Send respects both quiet mode and the error count limit.
  • SendImportant bypasses quiet mode but still respects the error count.
  • maxNotifyErrors is set to 3; after three error notifications, a final "quiet" warning is sent and subsequent errors are silenced until ResetErrorCount is called (typically after a successful deployment).

Source: [notifier/notifier.go line 59‑104](https://github.com/linyows/dewy/blob/main/notifier/notifier.go#L59-L104)

Supported Notification Channels

Dewy’s notifier interface allows swapping delivery mechanisms without changing the core deployment logic.

Slack Integration

The Slack implementation formats messages with rich attachments for hook results, including color-coded status (green for success, red for failure), command output, and execution duration. Configure via slack://channel-name?token=xoxb-...&quiet=true.

Source: [notifier/slack.go](https://github.com/linyows/dewy/blob/main/notifier/slack.go)

Email Notifications

The mail sender delivers plain-text notifications suitable for traditional monitoring setups. Configure via mail://smtp.example.com:587?from=...&to=....

Source: [notifier/mail.go](https://github.com/linyows/dewy/blob/main/notifier/mail.go)

Null Notifier (Disabled Mode)

When no notifier URL is provided or explicitly set to null://, Dewy uses the null implementation which silently discards all notifications. This is useful for local testing or when logging alone is sufficient.

Source: [notifier/null.go](https://github.com/linyows/dewy/blob/main/notifier/null.go)

Implementation Example

Here is a complete example demonstrating how to instantiate and use the notifier outside of Dewy’s main loop:

ctx   := context.Background()
logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))

// Create a Slack notifier (quiet mode enabled)
notif, err := notifier.New(ctx, "slack://my-channel?quiet=true", logger)
if err != nil { log.Fatal(err) }

// Deploy start
notif.Send(ctx, "🚀 Starting deployment of v1.2.3")

// Simulate a hook result
hook := &notifier.HookResult{
    Command:  "make build",
    Stdout:   "build succeeded",
    Stderr:   "",
    ExitCode: 0,
    Duration: 2 * time.Second,
    Success:  true,
}
notif.SendHookResult(ctx, "Before Deploy", hook)

// Simulate an error
notif.SendError(ctx, fmt.Errorf("network timeout"))
// Subsequent errors will be throttled after 3 occurrences.

Summary

  • Pluggable Architecture: Dewy uses a Notifier interface with concrete implementations for Slack, email, and null (disabled) channels, configured via URL schemes like slack:// or mail://.
  • Lifecycle Coverage: Notifications are sent at every stage: deployment start, pre-deploy hook results, deployment errors, and post-deploy hook results via methods like Send, SendHookResult, and SendError.
  • Spam Prevention: The ErrorLimitingSender wrapper throttles error notifications to a maximum of three messages and supports a quiet mode to suppress non-critical updates.
  • Rich Context: Hook results include detailed attachments showing stdout, stderr, exit codes, and duration, formatted appropriately for each channel (e.g., Slack rich attachments).

Frequently Asked Questions

How do I configure Slack notifications in Dewy?

Configure Slack by setting the notifier URL to slack://channel-name?token=xoxb-your-token. You can append &quiet=true to suppress non-error messages. The Slack implementation automatically formats hook results as rich attachments with color-coded status indicators.

What happens when deployment errors occur repeatedly?

Dewy’s ErrorLimitingSender tracks error counts and limits notifications to the first three errors. After the third error, a final "quiet mode activated" message is sent, and subsequent errors are logged but not sent to the notification channel until a successful deployment resets the counter.

Can I disable notifications entirely in Dewy?

Yes. Set the notifier URL to null:// or omit the notifier configuration entirely. This activates the Null implementation, which implements the Notifier interface but silently discards all messages, effectively disabling notifications while maintaining code compatibility.

How does Dewy format hook execution results for Slack?

The SendHookResult method creates a HookResult struct containing the command, stdout, stderr, exit code, duration, and success status. The Slack implementation in slack.go converts this into a rich attachment with a color bar (green for success, red for failure), formatted fields for each output stream, and a timestamp, providing immediate visual context for hook failures.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →