Skip to content
Open
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
5 changes: 5 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,10 @@ inputs:
description: "project name for digger to run in case of manual mode"
required: false
default: ""
drift-detection-github-action-output:
description: "Export drift notification data as a GitHub Workflow output instead of Slack or GitHub issues."
required: false
default: "false"
drift-detection-slack-notification-url:
description: "drift-detection slack drift url"
required: false
Expand Down Expand Up @@ -575,6 +579,7 @@ runs:
INPUT_DIGGER_PROJECT: ${{ inputs.project }}
INPUT_DIGGER_MODE: ${{ inputs.mode }}
INPUT_DIGGER_COMMAND: ${{ inputs.command }}
INPUT_DRIFT_DETECTION_GITHUB_ACTION_OUTPUT: ${{ inputs.drift-detection-github-action-output }}
INPUT_DRIFT_DETECTION_SLACK_NOTIFICATION_URL: ${{ inputs.drift-detection-slack-notification-url }}
INPUT_DRIFT_DETECTION_ADVANCED_SLACK_NOTIFICATION_URL: ${{ inputs.drift-detection-advanced-slack-notification-url }}
NO_BACKEND: ${{ inputs.no-backend }}
Expand Down
54 changes: 54 additions & 0 deletions cli/pkg/drift/github_action.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package drift

import (
"crypto/rand"
"encoding/hex"
"fmt"
"log/slog"
"os"
)

type GithubActionOutputNotification struct{}

func writeToGithubOutput(key, value string) error {
outputPath := os.Getenv("GITHUB_OUTPUT")
if outputPath == "" {
slog.Info(fmt.Sprintf("GITHUB_OUTPUT is not set. Would have written: %s=%s", key, value))
return nil
}

f, err := os.OpenFile(outputPath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return fmt.Errorf("failed to open GITHUB_OUTPUT file: %w", err)
}
defer f.Close()

// Generate a random delimiter for multi-line support
b := make([]byte, 16)
_, _ = rand.Read(b)
delimiter := hex.EncodeToString(b)

content := fmt.Sprintf("%s<<%s\n%s\n%s\n", key, delimiter, value, delimiter)

if _, err := f.WriteString(content); err != nil {
return fmt.Errorf("failed to write to GITHUB_OUTPUT file: %w", err)
}
return nil
}

func (n *GithubActionOutputNotification) SendNotificationForProject(projectId string, driftMessage string) error {
if err := writeToGithubOutput(fmt.Sprintf("%s_drift_detected", projectId), "true"); err != nil {
return err
}
if err := writeToGithubOutput(fmt.Sprintf("%s_drift_message", projectId), driftMessage); err != nil {
return err
}
return nil
}

func (n *GithubActionOutputNotification) SendErrorNotificationForProject(projectId string, errorMsg string) error {
if err := writeToGithubOutput(fmt.Sprintf("%s_drift_error", projectId), errorMsg); err != nil {
return err
}
return nil
}
69 changes: 69 additions & 0 deletions cli/pkg/drift/github_action_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package drift

import (
"os"
"path/filepath"
"strings"
"testing"
)

func TestWriteToGithubOutput(t *testing.T) {
// Create a temporary file to act as our GITHUB_OUTPUT
tmpDir := t.TempDir()
outputPath := filepath.Join(tmpDir, "github_output.txt")
os.Setenv("GITHUB_OUTPUT", outputPath)
defer os.Unsetenv("GITHUB_OUTPUT")

key := "test_project_drift_message"
value := "line1\nline2\nline3"

err := writeToGithubOutput(key, value)
if err != nil {
t.Fatalf("writeToGithubOutput returned unexpected error: %v", err)
}

content, err := os.ReadFile(outputPath)
if err != nil {
t.Fatalf("failed to read output file: %v", err)
}

contentStr := string(content)

if !strings.HasPrefix(contentStr, key+"<<") {
t.Errorf("expected content to start with valid multiline key syntax")
}

if !strings.Contains(contentStr, value) {
t.Errorf("expected content to contain the value")
}
}

func TestSendNotificationForProjectOutput(t *testing.T) {
tmpDir := t.TempDir()
outputPath := filepath.Join(tmpDir, "github_output.txt")
os.Setenv("GITHUB_OUTPUT", outputPath)
defer os.Unsetenv("GITHUB_OUTPUT")

notification := &GithubActionOutputNotification{}

err := notification.SendNotificationForProject("projectA", "hello\nworld")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

content, err := os.ReadFile(outputPath)
if err != nil {
t.Fatalf("failed to read output file: %v", err)
}

contentStr := string(content)
if !strings.Contains(contentStr, "projectA_drift_detected") {
t.Errorf("missing drift detected key")
}
if !strings.Contains(contentStr, "projectA_drift_message") {
t.Errorf("missing drift message key")
}
if !strings.Contains(contentStr, "hello\nworld") {
t.Errorf("missing drift message content")
}
}
5 changes: 4 additions & 1 deletion cli/pkg/drift/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,16 @@ type DriftNotificationProviderBasic struct{}
func (d DriftNotificationProviderBasic) Get(prService ci.PullRequestService) (core_drift.Notification, error) {
slackNotificationUrl := os.Getenv("INPUT_DRIFT_DETECTION_SLACK_NOTIFICATION_URL")
githubIssues := os.Getenv("INPUT_DRIFT_GITHUB_ISSUES")
githubActionOutput := os.Getenv("INPUT_DRIFT_DETECTION_GITHUB_ACTION_OUTPUT")
var notification core_drift.Notification
if slackNotificationUrl != "" {
notification = &SlackNotification{slackNotificationUrl}
} else if githubIssues != "" {
notification = &GithubIssueNotification{GithubService: &prService}
} else if githubActionOutput == "true" {
notification = &GithubActionOutputNotification{}
} else {
return nil, fmt.Errorf("could not identify drift mode, please specify using INPUT_DRIFT_DETECTION_SLACK_NOTIFICATION_URL or INPUT_DRIFT_GITHUB_ISSUES")
return nil, fmt.Errorf("could not identify drift mode, please specify using INPUT_DRIFT_DETECTION_SLACK_NOTIFICATION_URL, INPUT_DRIFT_GITHUB_ISSUES, or INPUT_DRIFT_DETECTION_GITHUB_ACTION_OUTPUT")
}
return notification, nil
}
5 changes: 4 additions & 1 deletion ee/cli/pkg/drift/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,18 @@ func (d DriftNotificationProviderAdvanced) Get(prService ci.PullRequestService)
slackNotificationUrl := os.Getenv("INPUT_DRIFT_DETECTION_SLACK_NOTIFICATION_URL")
slackNotificationAdvancedUrl := os.Getenv("INPUT_DRIFT_DETECTION_ADVANCED_SLACK_NOTIFICATION_URL")
DriftAsGithubIssues := os.Getenv("INPUT_DRIFT_GITHUB_ISSUES")
githubActionOutput := os.Getenv("INPUT_DRIFT_DETECTION_GITHUB_ACTION_OUTPUT")
var notification core_drift.Notification
if slackNotificationUrl != "" {
notification = &ce_drift.SlackNotification{Url: slackNotificationUrl}
} else if slackNotificationAdvancedUrl != "" {
notification = NewSlackAdvancedAggregatedNotificationWithAiSummary(slackNotificationAdvancedUrl)
} else if DriftAsGithubIssues != "" {
notification = &ce_drift.GithubIssueNotification{GithubService: &prService}
} else if githubActionOutput == "true" {
notification = &ce_drift.GithubActionOutputNotification{}
} else {
return nil, fmt.Errorf("could not identify drift mode, please specify using env variable INPUT_DRIFT_DETECTION_SLACK_NOTIFICATION_URL, INPUT_DRIFT_DETECTION_ADVANCED_SLACK_NOTIFICATION_URL or INPUT_DRIFT_GITHUB_ISSUES")
return nil, fmt.Errorf("could not identify drift mode, please specify using env variable INPUT_DRIFT_DETECTION_SLACK_NOTIFICATION_URL, INPUT_DRIFT_DETECTION_ADVANCED_SLACK_NOTIFICATION_URL, INPUT_DRIFT_GITHUB_ISSUES, or INPUT_DRIFT_DETECTION_GITHUB_ACTION_OUTPUT")
}
return notification, nil
}
Binary file added issues.json
Binary file not shown.
Binary file added issues.txt
Binary file not shown.