Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions internal/cmd/track_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/spf13/viper"
"github.com/versioner-io/versioner-cli/internal/api"
"github.com/versioner-io/versioner-cli/internal/cicd"
"github.com/versioner-io/versioner-cli/internal/github"
"github.com/versioner-io/versioner-cli/internal/status"
)

Expand Down Expand Up @@ -207,10 +208,12 @@ func runBuildTrack(cmd *cobra.Command, args []string) error {
if err != nil {
if apiErr, ok := err.(*api.APIError); ok {
// API error - exit code 2
github.WriteGenericErrorAnnotation("Build", "API Error", apiErr.Error())
fmt.Fprintf(os.Stderr, "API error: %s\n", apiErr.Error())
os.Exit(2)
}
// Network or other error - exit code 2
github.WriteGenericErrorAnnotation("Build", "Network Error", err.Error())
fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error())
os.Exit(2)
}
Expand All @@ -223,5 +226,8 @@ func runBuildTrack(cmd *cobra.Command, args []string) error {
fmt.Printf(" Version ID: %s\n", resp.VersionID)
}

// Write GitHub Actions job summary
github.WriteSuccessSummary("Build", product, statusValue, version, event.SCMSha, event.BuildURL)

return nil
}
5 changes: 5 additions & 0 deletions internal/cmd/track_deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,10 +241,12 @@ func runDeploymentTrack(cmd *cobra.Command, args []string) error {
os.Exit(5) // Exit code 5 for preflight failures
}
// Other API error - exit code 4
github.WriteGenericErrorAnnotation("Deployment", "API Error", apiErr.Error())
fmt.Fprintf(os.Stderr, "API error: %s\n", apiErr.Error())
os.Exit(4)
}
// Network or other error - exit code 1
github.WriteGenericErrorAnnotation("Deployment", "Network Error", err.Error())
fmt.Fprintf(os.Stderr, "Error: %s\n", err.Error())
os.Exit(1)
}
Expand All @@ -258,6 +260,9 @@ func runDeploymentTrack(cmd *cobra.Command, args []string) error {
fmt.Printf(" Environment ID: %s\n", resp.EnvironmentID)
}

// Write GitHub Actions job summary
github.WriteSuccessSummary("Deployment", environment, statusValue, version, event.SCMSha, event.DeployURL)

return nil
}

Expand Down
116 changes: 116 additions & 0 deletions internal/github/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,122 @@ func WriteErrorAnnotation(statusCode int, errorCode, message, ruleName string, r
writeJobSummary(statusCode, errorCode, message, ruleName, retryAfter, details)
}

// WriteSuccessSummary writes a GitHub Actions job summary for successful deployment tracking
func WriteSuccessSummary(action, environment, status, version, scmSha, deployURL string) {
// Only write summaries if running in GitHub Actions
if os.Getenv("GITHUB_ACTIONS") != "true" {
return
}

summaryPath := os.Getenv("GITHUB_STEP_SUMMARY")
if summaryPath == "" {
return
}

// Build the summary
var summary string
summary += "## 🚀 Versioner Summary\n\n"

// Add key information
summary += fmt.Sprintf("- **Action:** %s\n", action)
summary += fmt.Sprintf("- **Environment:** %s\n", environment)
summary += fmt.Sprintf("- **Status:** %s\n", formatStatus(status))
summary += fmt.Sprintf("- **Version:** `%s`\n", version)

if scmSha != "" {
summary += fmt.Sprintf("- **Git SHA:** `%s`\n", scmSha)
}

if deployURL != "" {
summary += fmt.Sprintf("\n[View Deployment Run →](%s)\n", deployURL)
}

// Write to file
f, err := os.OpenFile(summaryPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
// Silently fail - don't break the CLI if we can't write the summary
return
}
defer f.Close()

_, _ = f.WriteString(summary)
}

// WriteGenericErrorAnnotation writes a GitHub Actions error annotation for generic failures
// (API errors, network errors, etc.)
func WriteGenericErrorAnnotation(action, errorType, errorMessage string) {
// Only write annotations if running in GitHub Actions
if os.Getenv("GITHUB_ACTIONS") != "true" {
return
}

summaryPath := os.Getenv("GITHUB_STEP_SUMMARY")
if summaryPath == "" {
return
}

// Write workflow command annotation
title := fmt.Sprintf("Versioner %s Failed", action)
escapedMessage := escapeWorkflowCommand(errorMessage)
fmt.Fprintf(os.Stdout, "::error title=%s::%s\n", title, escapedMessage)

// Build the summary
var summary string
summary += fmt.Sprintf("## ❌ Versioner %s Failed\n\n", action)
summary += fmt.Sprintf("### %s\n\n", errorType)
summary += fmt.Sprintf("**Error:** %s\n\n", errorMessage)

summary += "**Possible Causes:**\n"
switch errorType {
case "API Error":
summary += "- Invalid API key or authentication failure\n"
summary += "- Validation error (check required fields)\n"
summary += "- API service unavailable\n"
summary += "- Rate limiting or quota exceeded\n"
case "Network Error":
summary += "- Network connectivity issues\n"
summary += "- DNS resolution failure\n"
summary += "- API endpoint unreachable\n"
summary += "- Timeout or connection refused\n"
default:
summary += "- Check error message above for details\n"
}

summary += "\n**Action Required:**\n"
summary += "- Verify your `VERSIONER_API_KEY` is set correctly\n"
summary += "- Check network connectivity to Versioner API\n"
summary += "- Review error message for specific guidance\n"
summary += "- Contact support if issue persists\n"

// Write to file
f, err := os.OpenFile(summaryPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
// Silently fail - don't break the CLI if we can't write the summary
return
}
defer f.Close()

_, _ = f.WriteString(summary)
}

// formatStatus adds an emoji to the status for visual clarity
func formatStatus(status string) string {
switch status {
case "started", "in_progress":
return "⏳ " + status
case "completed", "success":
return "✅ " + status
case "failed":
return "❌ " + status
case "aborted", "cancelled":
return "🚫 " + status
case "pending":
return "⏸️ " + status
default:
return status
}
}

// writeWorkflowCommand outputs a GitHub Actions workflow command for error annotation
func writeWorkflowCommand(statusCode int, errorCode, message, ruleName string) {
// Format: ::error title=<title>::<message>
Expand Down
Loading