Add deployment improvements and UI enhancements
- Clone specific commit SHA from webhook instead of just branch HEAD - Log webhook payload in deployment logs - Add build/deploy timing to ntfy and Slack notifications - Implement container rollback on deploy failure - Remove old container only after successful deployment - Show relative times in deployment history (hover for full date) - Update port mappings UI with labeled text inputs - Add footer with version info, license, and repo link - Format deploy key comment as upaas_DATE_appname
This commit is contained in:
@@ -27,6 +27,12 @@ const (
|
||||
httpStatusClientError = 400
|
||||
)
|
||||
|
||||
// Display constants.
|
||||
const (
|
||||
shortCommitLength = 12
|
||||
secondsPerMinute = 60
|
||||
)
|
||||
|
||||
// Sentinel errors for notification failures.
|
||||
var (
|
||||
// ErrNtfyFailed indicates the ntfy notification request failed.
|
||||
@@ -64,10 +70,16 @@ func New(_ fx.Lifecycle, params ServiceParams) (*Service, error) {
|
||||
func (svc *Service) NotifyBuildStart(
|
||||
ctx context.Context,
|
||||
app *models.App,
|
||||
_ *models.Deployment,
|
||||
deployment *models.Deployment,
|
||||
) {
|
||||
title := "Build started: " + app.Name
|
||||
message := "Building from branch " + app.Branch
|
||||
|
||||
if deployment.CommitSHA.Valid {
|
||||
shortSHA := deployment.CommitSHA.String[:minInt(shortCommitLength, len(deployment.CommitSHA.String))]
|
||||
message += " at " + shortSHA
|
||||
}
|
||||
|
||||
svc.sendNotifications(ctx, app, title, message, "info")
|
||||
}
|
||||
|
||||
@@ -75,10 +87,12 @@ func (svc *Service) NotifyBuildStart(
|
||||
func (svc *Service) NotifyBuildSuccess(
|
||||
ctx context.Context,
|
||||
app *models.App,
|
||||
_ *models.Deployment,
|
||||
deployment *models.Deployment,
|
||||
) {
|
||||
duration := time.Since(deployment.StartedAt)
|
||||
title := "Build success: " + app.Name
|
||||
message := "Image built successfully from branch " + app.Branch
|
||||
message := "Image built successfully in " + formatDuration(duration)
|
||||
|
||||
svc.sendNotifications(ctx, app, title, message, "success")
|
||||
}
|
||||
|
||||
@@ -86,11 +100,13 @@ func (svc *Service) NotifyBuildSuccess(
|
||||
func (svc *Service) NotifyBuildFailed(
|
||||
ctx context.Context,
|
||||
app *models.App,
|
||||
_ *models.Deployment,
|
||||
deployment *models.Deployment,
|
||||
buildErr error,
|
||||
) {
|
||||
duration := time.Since(deployment.StartedAt)
|
||||
title := "Build failed: " + app.Name
|
||||
message := "Build failed: " + buildErr.Error()
|
||||
message := "Build failed after " + formatDuration(duration) + ": " + buildErr.Error()
|
||||
|
||||
svc.sendNotifications(ctx, app, title, message, "error")
|
||||
}
|
||||
|
||||
@@ -98,10 +114,17 @@ func (svc *Service) NotifyBuildFailed(
|
||||
func (svc *Service) NotifyDeploySuccess(
|
||||
ctx context.Context,
|
||||
app *models.App,
|
||||
_ *models.Deployment,
|
||||
deployment *models.Deployment,
|
||||
) {
|
||||
duration := time.Since(deployment.StartedAt)
|
||||
title := "Deploy success: " + app.Name
|
||||
message := "Successfully deployed from branch " + app.Branch
|
||||
message := "Successfully deployed in " + formatDuration(duration)
|
||||
|
||||
if deployment.CommitSHA.Valid {
|
||||
shortSHA := deployment.CommitSHA.String[:minInt(shortCommitLength, len(deployment.CommitSHA.String))]
|
||||
message += " (commit " + shortSHA + ")"
|
||||
}
|
||||
|
||||
svc.sendNotifications(ctx, app, title, message, "success")
|
||||
}
|
||||
|
||||
@@ -109,14 +132,37 @@ func (svc *Service) NotifyDeploySuccess(
|
||||
func (svc *Service) NotifyDeployFailed(
|
||||
ctx context.Context,
|
||||
app *models.App,
|
||||
_ *models.Deployment,
|
||||
deployment *models.Deployment,
|
||||
deployErr error,
|
||||
) {
|
||||
duration := time.Since(deployment.StartedAt)
|
||||
title := "Deploy failed: " + app.Name
|
||||
message := "Deployment failed: " + deployErr.Error()
|
||||
message := "Deployment failed after " + formatDuration(duration) + ": " + deployErr.Error()
|
||||
|
||||
svc.sendNotifications(ctx, app, title, message, "error")
|
||||
}
|
||||
|
||||
// formatDuration formats a duration for display.
|
||||
func formatDuration(d time.Duration) string {
|
||||
if d < time.Minute {
|
||||
return fmt.Sprintf("%ds", int(d.Seconds()))
|
||||
}
|
||||
|
||||
minutes := int(d.Minutes())
|
||||
seconds := int(d.Seconds()) % secondsPerMinute
|
||||
|
||||
return fmt.Sprintf("%dm %ds", minutes, seconds)
|
||||
}
|
||||
|
||||
// minInt returns the smaller of two integers.
|
||||
func minInt(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
func (svc *Service) sendNotifications(
|
||||
ctx context.Context,
|
||||
app *models.App,
|
||||
@@ -153,7 +199,7 @@ func (svc *Service) sendNotifications(
|
||||
// even if the parent context is cancelled.
|
||||
notifyCtx := context.WithoutCancel(ctx)
|
||||
|
||||
slackErr := svc.sendSlack(notifyCtx, slackWebhook, title, message)
|
||||
slackErr := svc.sendSlack(notifyCtx, slackWebhook, title, message, priority)
|
||||
if slackErr != nil {
|
||||
svc.log.Error(
|
||||
"failed to send slack notification",
|
||||
@@ -213,6 +259,19 @@ func (svc *Service) ntfyPriority(priority string) string {
|
||||
}
|
||||
}
|
||||
|
||||
func (svc *Service) slackColor(priority string) string {
|
||||
switch priority {
|
||||
case "error":
|
||||
return "#dc3545" // red
|
||||
case "success":
|
||||
return "#28a745" // green
|
||||
case "info":
|
||||
return "#17a2b8" // blue
|
||||
default:
|
||||
return "#6c757d" // gray
|
||||
}
|
||||
}
|
||||
|
||||
// SlackPayload represents a Slack webhook payload.
|
||||
type SlackPayload struct {
|
||||
Text string `json:"text"`
|
||||
@@ -228,7 +287,7 @@ type SlackAttachment struct {
|
||||
|
||||
func (svc *Service) sendSlack(
|
||||
ctx context.Context,
|
||||
webhookURL, title, message string,
|
||||
webhookURL, title, message, priority string,
|
||||
) error {
|
||||
svc.log.Debug(
|
||||
"sending slack notification",
|
||||
@@ -239,7 +298,7 @@ func (svc *Service) sendSlack(
|
||||
payload := SlackPayload{
|
||||
Attachments: []SlackAttachment{
|
||||
{
|
||||
Color: "#36a64f",
|
||||
Color: svc.slackColor(priority),
|
||||
Title: title,
|
||||
Text: message,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user