Add commit URL to Slack notifications with link and backtick formatting
- Add commit_url column to webhook_events and deployments tables - Extract commit URL from webhook payload (from commit object or repo URL) - Format Slack messages with backticks for branch and commit SHA - Link commit SHA to the actual commit URL on the git server - Keep plain text format for ntfy notifications
This commit is contained in:
@@ -439,9 +439,15 @@ func (svc *Service) createDeploymentRecord(
|
||||
}
|
||||
}
|
||||
|
||||
// Set commit SHA from webhook event
|
||||
if webhookEvent != nil && webhookEvent.CommitSHA.Valid {
|
||||
deployment.CommitSHA = webhookEvent.CommitSHA
|
||||
// Set commit SHA and URL from webhook event
|
||||
if webhookEvent != nil {
|
||||
if webhookEvent.CommitSHA.Valid {
|
||||
deployment.CommitSHA = webhookEvent.CommitSHA
|
||||
}
|
||||
|
||||
if webhookEvent.CommitURL.Valid {
|
||||
deployment.CommitURL = webhookEvent.CommitURL
|
||||
}
|
||||
}
|
||||
|
||||
deployment.Status = models.DeploymentStatusBuilding
|
||||
|
||||
@@ -4,6 +4,7 @@ package notify
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -73,14 +74,24 @@ func (svc *Service) NotifyBuildStart(
|
||||
deployment *models.Deployment,
|
||||
) {
|
||||
title := "Build started: " + app.Name
|
||||
message := "Building from branch " + app.Branch
|
||||
|
||||
// Plain text message for ntfy
|
||||
ntfyMessage := "Building from branch " + app.Branch
|
||||
|
||||
if deployment.CommitSHA.Valid {
|
||||
shortSHA := deployment.CommitSHA.String[:minInt(shortCommitLength, len(deployment.CommitSHA.String))]
|
||||
message += " at " + shortSHA
|
||||
shortSHA := truncateSHA(deployment.CommitSHA.String)
|
||||
ntfyMessage += " at " + shortSHA
|
||||
}
|
||||
|
||||
svc.sendNotifications(ctx, app, title, message, "info")
|
||||
// Slack message with formatting
|
||||
slackMessage := "Building from branch `" + app.Branch + "`"
|
||||
|
||||
if deployment.CommitSHA.Valid {
|
||||
shortSHA := truncateSHA(deployment.CommitSHA.String)
|
||||
slackMessage += " at " + formatCommitLink(shortSHA, deployment.CommitURL)
|
||||
}
|
||||
|
||||
svc.sendNotifications(ctx, app, title, ntfyMessage, slackMessage, "info")
|
||||
}
|
||||
|
||||
// NotifyBuildSuccess sends a build success notification.
|
||||
@@ -93,7 +104,7 @@ func (svc *Service) NotifyBuildSuccess(
|
||||
title := "Build success: " + app.Name
|
||||
message := "Image built successfully in " + formatDuration(duration)
|
||||
|
||||
svc.sendNotifications(ctx, app, title, message, "success")
|
||||
svc.sendNotifications(ctx, app, title, message, message, "success")
|
||||
}
|
||||
|
||||
// NotifyBuildFailed sends a build failed notification.
|
||||
@@ -107,7 +118,7 @@ func (svc *Service) NotifyBuildFailed(
|
||||
title := "Build failed: " + app.Name
|
||||
message := "Build failed after " + formatDuration(duration) + ": " + buildErr.Error()
|
||||
|
||||
svc.sendNotifications(ctx, app, title, message, "error")
|
||||
svc.sendNotifications(ctx, app, title, message, message, "error")
|
||||
}
|
||||
|
||||
// NotifyDeploySuccess sends a deploy success notification.
|
||||
@@ -118,14 +129,24 @@ func (svc *Service) NotifyDeploySuccess(
|
||||
) {
|
||||
duration := time.Since(deployment.StartedAt)
|
||||
title := "Deploy success: " + app.Name
|
||||
message := "Successfully deployed in " + formatDuration(duration)
|
||||
|
||||
// Plain text message for ntfy
|
||||
ntfyMessage := "Successfully deployed in " + formatDuration(duration)
|
||||
|
||||
if deployment.CommitSHA.Valid {
|
||||
shortSHA := deployment.CommitSHA.String[:minInt(shortCommitLength, len(deployment.CommitSHA.String))]
|
||||
message += " (commit " + shortSHA + ")"
|
||||
shortSHA := truncateSHA(deployment.CommitSHA.String)
|
||||
ntfyMessage += " (commit " + shortSHA + ")"
|
||||
}
|
||||
|
||||
svc.sendNotifications(ctx, app, title, message, "success")
|
||||
// Slack message with formatting
|
||||
slackMessage := "Successfully deployed in " + formatDuration(duration)
|
||||
|
||||
if deployment.CommitSHA.Valid {
|
||||
shortSHA := truncateSHA(deployment.CommitSHA.String)
|
||||
slackMessage += " (commit " + formatCommitLink(shortSHA, deployment.CommitURL) + ")"
|
||||
}
|
||||
|
||||
svc.sendNotifications(ctx, app, title, ntfyMessage, slackMessage, "success")
|
||||
}
|
||||
|
||||
// NotifyDeployFailed sends a deploy failed notification.
|
||||
@@ -139,7 +160,7 @@ func (svc *Service) NotifyDeployFailed(
|
||||
title := "Deploy failed: " + app.Name
|
||||
message := "Deployment failed after " + formatDuration(duration) + ": " + deployErr.Error()
|
||||
|
||||
svc.sendNotifications(ctx, app, title, message, "error")
|
||||
svc.sendNotifications(ctx, app, title, message, message, "error")
|
||||
}
|
||||
|
||||
// formatDuration formats a duration for display.
|
||||
@@ -154,19 +175,28 @@ func formatDuration(d time.Duration) string {
|
||||
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
|
||||
// truncateSHA truncates a commit SHA to shortCommitLength characters.
|
||||
func truncateSHA(sha string) string {
|
||||
if len(sha) > shortCommitLength {
|
||||
return sha[:shortCommitLength]
|
||||
}
|
||||
|
||||
return b
|
||||
return sha
|
||||
}
|
||||
|
||||
// formatCommitLink formats a commit SHA as a Slack link if URL is available.
|
||||
func formatCommitLink(shortSHA string, commitURL sql.NullString) string {
|
||||
if commitURL.Valid && commitURL.String != "" {
|
||||
return "<" + commitURL.String + "|`" + shortSHA + "`>"
|
||||
}
|
||||
|
||||
return "`" + shortSHA + "`"
|
||||
}
|
||||
|
||||
func (svc *Service) sendNotifications(
|
||||
ctx context.Context,
|
||||
app *models.App,
|
||||
title, message, priority string,
|
||||
title, ntfyMessage, slackMessage, priority string,
|
||||
) {
|
||||
// Send to ntfy if configured
|
||||
if app.NtfyTopic.Valid && app.NtfyTopic.String != "" {
|
||||
@@ -178,7 +208,7 @@ func (svc *Service) sendNotifications(
|
||||
// even if the parent context is cancelled.
|
||||
notifyCtx := context.WithoutCancel(ctx)
|
||||
|
||||
ntfyErr := svc.sendNtfy(notifyCtx, ntfyTopic, title, message, priority)
|
||||
ntfyErr := svc.sendNtfy(notifyCtx, ntfyTopic, title, ntfyMessage, priority)
|
||||
if ntfyErr != nil {
|
||||
svc.log.Error(
|
||||
"failed to send ntfy notification",
|
||||
@@ -199,7 +229,7 @@ func (svc *Service) sendNotifications(
|
||||
// even if the parent context is cancelled.
|
||||
notifyCtx := context.WithoutCancel(ctx)
|
||||
|
||||
slackErr := svc.sendSlack(notifyCtx, slackWebhook, title, message, priority)
|
||||
slackErr := svc.sendSlack(notifyCtx, slackWebhook, title, slackMessage, priority)
|
||||
if slackErr != nil {
|
||||
svc.log.Error(
|
||||
"failed to send slack notification",
|
||||
|
||||
@@ -50,10 +50,12 @@ type GiteaPushPayload struct {
|
||||
Ref string `json:"ref"`
|
||||
Before string `json:"before"`
|
||||
After string `json:"after"`
|
||||
CompareURL string `json:"compare_url"`
|
||||
Repository struct {
|
||||
FullName string `json:"full_name"`
|
||||
CloneURL string `json:"clone_url"`
|
||||
SSHURL string `json:"ssh_url"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
} `json:"repository"`
|
||||
Pusher struct {
|
||||
Username string `json:"username"`
|
||||
@@ -61,6 +63,7 @@ type GiteaPushPayload struct {
|
||||
} `json:"pusher"`
|
||||
Commits []struct {
|
||||
ID string `json:"id"`
|
||||
URL string `json:"url"`
|
||||
Message string `json:"message"`
|
||||
Author struct {
|
||||
Name string `json:"name"`
|
||||
@@ -90,6 +93,7 @@ func (svc *Service) HandleWebhook(
|
||||
// Extract branch from ref
|
||||
branch := extractBranch(pushPayload.Ref)
|
||||
commitSHA := pushPayload.After
|
||||
commitURL := extractCommitURL(pushPayload)
|
||||
|
||||
// Check if branch matches
|
||||
matched := branch == app.Branch
|
||||
@@ -100,6 +104,7 @@ func (svc *Service) HandleWebhook(
|
||||
event.EventType = eventType
|
||||
event.Branch = branch
|
||||
event.CommitSHA = sql.NullString{String: commitSHA, Valid: commitSHA != ""}
|
||||
event.CommitURL = sql.NullString{String: commitURL, Valid: commitURL != ""}
|
||||
event.Payload = sql.NullString{String: string(payload), Valid: true}
|
||||
event.Matched = matched
|
||||
event.Processed = false
|
||||
@@ -160,3 +165,21 @@ func extractBranch(ref string) string {
|
||||
|
||||
return ref
|
||||
}
|
||||
|
||||
// extractCommitURL extracts the commit URL from the webhook payload.
|
||||
// Prefers the URL from the head commit, falls back to constructing from repo URL.
|
||||
func extractCommitURL(payload GiteaPushPayload) string {
|
||||
// Try to find the URL from the head commit (matching After SHA)
|
||||
for _, commit := range payload.Commits {
|
||||
if commit.ID == payload.After && commit.URL != "" {
|
||||
return commit.URL
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to constructing URL from repo HTML URL
|
||||
if payload.Repository.HTMLURL != "" && payload.After != "" {
|
||||
return payload.Repository.HTMLURL + "/commit/" + payload.After
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user