Skip to content
Closed
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
1 change: 1 addition & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ func run(cfg *Config) error {
statsInfo,
selectionInfo,
cfg.ArgocdUIURL,
cfg.SummaryThreshold,
); err != nil {
log.Error().Msg("❌ Failed to generate diff")
return err
Expand Down
9 changes: 9 additions & 0 deletions cmd/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ var (
DefaultKindInternal = false
DefaultK3dOptions = ""
DefaultMaxDiffLength = uint(65536)
DefaultSummaryThreshold = uint(20)
DefaultArgocdNamespace = "argocd"
DefaultArgocdChartVersion = "latest"
DefaultArgocdChartName = "argo"
Expand Down Expand Up @@ -103,6 +104,7 @@ type RawOptions struct {
KindInternal bool `mapstructure:"kind-internal"`
K3dOptions string `mapstructure:"k3d-options"`
MaxDiffLength uint `mapstructure:"max-diff-length"`
SummaryThreshold uint `mapstructure:"summary-threshold"`
Selector string `mapstructure:"selector"`
FilesChanged string `mapstructure:"files-changed"`
IgnoreInvalidWatchPattern bool `mapstructure:"ignore-invalid-watch-pattern"`
Expand Down Expand Up @@ -149,6 +151,7 @@ type Config struct {
KindInternal bool
K3dOptions string
MaxDiffLength uint
SummaryThreshold uint
IgnoreInvalidWatchPattern bool
WatchIfNoWatchPatternFound bool
AutoDetectFilesChanged bool
Expand Down Expand Up @@ -245,6 +248,7 @@ func Parse() *Config {
viper.SetDefault("cluster", DefaultCluster)
viper.SetDefault("cluster-name", DefaultClusterName)
viper.SetDefault("max-diff-length", DefaultMaxDiffLength)
viper.SetDefault("summary-threshold", DefaultSummaryThreshold)
viper.SetDefault("argocd-namespace", DefaultArgocdNamespace)
viper.SetDefault("argocd-chart-version", DefaultArgocdChartVersion)
viper.SetDefault("argocd-chart-name", DefaultArgocdChartName)
Expand Down Expand Up @@ -310,6 +314,7 @@ func Parse() *Config {

// Other options
rootCmd.Flags().String("max-diff-length", fmt.Sprintf("%d", DefaultMaxDiffLength), "Max diff message character count")
rootCmd.Flags().Uint("summary-threshold", DefaultSummaryThreshold, "Collapse changed files list in summary when total exceeds this value (0 = always show inline)")
rootCmd.Flags().StringP("selector", "l", "", "Label selector to filter on (e.g. key1=value1,key2=value2)")
rootCmd.Flags().String("files-changed", "", "List of files changed between branches (comma, space or newline separated)")
rootCmd.Flags().Bool("auto-detect-files-changed", DefaultAutoDetectFilesChanged, "Auto detect files changed between branches")
Expand Down Expand Up @@ -383,6 +388,7 @@ func (o *RawOptions) ToConfig() (*Config, error) {
KindInternal: o.KindInternal,
K3dOptions: o.K3dOptions,
MaxDiffLength: o.MaxDiffLength,
SummaryThreshold: o.SummaryThreshold,
IgnoreInvalidWatchPattern: o.IgnoreInvalidWatchPattern,
WatchIfNoWatchPatternFound: o.WatchIfNoWatchPatternFound,
AutoDetectFilesChanged: o.AutoDetectFilesChanged,
Expand Down Expand Up @@ -659,6 +665,9 @@ func (o *Config) LogConfig() {
if o.MaxDiffLength != DefaultMaxDiffLength {
log.Info().Msgf("✨ - max-diff-length: %d", o.MaxDiffLength)
}
if o.SummaryThreshold != DefaultSummaryThreshold {
log.Info().Msgf("✨ - summary-threshold: %d", o.SummaryThreshold)
}
if len(o.FilesChanged) > 0 {
log.Info().Msgf("✨ - files-changed: %v", o.FilesChanged)
} else if o.AutoDetectFilesChanged {
Expand Down
8 changes: 4 additions & 4 deletions pkg/diff/diff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,8 +307,8 @@ spec:
}

// Run the diff generation
summary, markdownSections, htmlSections, err := generateGitDiff(
basePath, targetPath, nil, 3, false, baseApps, targetApps, "",
summary, _, markdownSections, htmlSections, err := generateGitDiff(
basePath, targetPath, nil, 3, false, baseApps, targetApps, "", 0,
)

if err != nil {
Expand Down Expand Up @@ -430,8 +430,8 @@ spec:
}

// Run the diff generation
summary, markdownSections, htmlSections, err := generateGitDiff(
basePath, targetPath, nil, 3, false, baseApps, targetApps, "",
summary, _, markdownSections, htmlSections, err := generateGitDiff(
basePath, targetPath, nil, 3, false, baseApps, targetApps, "", 0,
)

if err != nil {
Expand Down
80 changes: 41 additions & 39 deletions pkg/diff/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func GenerateDiff(
statsInfo StatsInfo,
selectionInfo SelectionInfo,
argocdUIURL string,
summaryThreshold uint,
) error {

maxDiffMessageCharCount := maxCharCount
Expand All @@ -59,19 +60,20 @@ func GenerateDiff(
// Generate diffs using go-git by creating temporary git repos
basePath := fmt.Sprintf("%s/%s", outputFolder, baseBranch.Type())
targetPath := fmt.Sprintf("%s/%s", outputFolder, targetBranch.Type())
summary, markdownFileSections, htmlFileSections, err := generateGitDiff(basePath, targetPath, diffIgnoreRegex, lineCount, hideDeletedAppDiff, baseApps, targetApps, argocdUIURL)
inlineSummary, summaryDetails, markdownFileSections, htmlFileSections, err := generateGitDiff(basePath, targetPath, diffIgnoreRegex, lineCount, hideDeletedAppDiff, baseApps, targetApps, argocdUIURL, int(summaryThreshold))
if err != nil {
return fmt.Errorf("failed to generate diff: %w", err)
}

// Markdown
log.Debug().Msg("Creating markdown output")
MarkdownOutput := MarkdownOutput{
title: title,
summary: summary,
sections: markdownFileSections,
statsInfo: statsInfo,
selectionInfo: selectionInfo,
title: title,
summary: inlineSummary,
summaryDetails: summaryDetails,
sections: markdownFileSections,
statsInfo: statsInfo,
selectionInfo: selectionInfo,
}
markdown := MarkdownOutput.printDiff(maxDiffMessageCharCount)
markdownPath := fmt.Sprintf("%s/diff.md", outputFolder)
Expand All @@ -85,7 +87,7 @@ func GenerateDiff(
log.Debug().Msg("Creating html output")
HTMLOutput := HTMLOutput{
title: title,
summary: summary,
summary: inlineSummary,
sections: htmlFileSections,
statsInfo: statsInfo,
selectionInfo: selectionInfo,
Expand Down Expand Up @@ -123,16 +125,17 @@ func generateGitDiff(
baseApps []AppInfo,
targetApps []AppInfo,
argocdUIURL string,
) (string, []MarkdownSection, []HTMLSection, error) {
summaryThreshold int,
) (string, string, []MarkdownSection, []HTMLSection, error) {

// Write base manifests to disk
if err := writeManifestsToDisk(baseApps, basePath); err != nil {
return "", nil, nil, fmt.Errorf("failed to write base manifests: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to write base manifests: %w", err)
}

// Write target manifests to disk
if err := writeManifestsToDisk(targetApps, targetPath); err != nil {
return "", nil, nil, fmt.Errorf("failed to write target manifests: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to write target manifests: %w", err)
}

baseAppsMap := make(map[string]AppInfo)
Expand All @@ -148,7 +151,7 @@ func generateGitDiff(
// Create temporary directory for single Git repository
repoPath, err := os.MkdirTemp("", "diff-repo-*")
if err != nil {
return "", nil, nil, fmt.Errorf("failed to create temp dir for repo: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to create temp dir for repo: %w", err)
}
defer func() {
if err := os.RemoveAll(repoPath); err != nil {
Expand All @@ -159,22 +162,22 @@ func generateGitDiff(
// Initialize single Git repository
repo, err := git.PlainInit(repoPath, false)
if err != nil {
return "", nil, nil, fmt.Errorf("failed to init repo: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to init repo: %w", err)
}

// Get worktree
worktree, err := repo.Worktree()
if err != nil {
return "", nil, nil, fmt.Errorf("failed to get worktree: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to get worktree: %w", err)
}

// Copy base files to repository and commit
if err := copyFilesToRepo(basePath, repoPath); err != nil {
return "", nil, nil, fmt.Errorf("failed to copy base files: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to copy base files: %w", err)
}

if err := worktree.AddGlob("."); err != nil {
return "", nil, nil, fmt.Errorf("failed to add base files to repo: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to add base files to repo: %w", err)
}

author := &object.Signature{
Expand All @@ -188,56 +191,56 @@ func generateGitDiff(
AllowEmptyCommits: true,
})
if err != nil {
return "", nil, nil, fmt.Errorf("failed to commit base state: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to commit base state: %w", err)
}

// Clear the working directory and copy target files
if err := clearWorkingDirectory(repoPath); err != nil {
return "", nil, nil, fmt.Errorf("failed to clear working directory: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to clear working directory: %w", err)
}

if err := copyFilesToRepo(targetPath, repoPath); err != nil {
return "", nil, nil, fmt.Errorf("failed to copy target files: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to copy target files: %w", err)
}

if err := worktree.AddGlob("."); err != nil {
return "", nil, nil, fmt.Errorf("failed to add target files to repo: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to add target files to repo: %w", err)
}

targetCommitHash, err := worktree.Commit("Target state", &git.CommitOptions{
Author: author,
AllowEmptyCommits: true,
})
if err != nil {
return "", nil, nil, fmt.Errorf("failed to commit target state: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to commit target state: %w", err)
}

// Retrieve commits
baseCommit, err := repo.CommitObject(baseCommitHash)
if err != nil {
return "", nil, nil, fmt.Errorf("failed to get base commit: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to get base commit: %w", err)
}

targetCommit, err := repo.CommitObject(targetCommitHash)
if err != nil {
return "", nil, nil, fmt.Errorf("failed to get target commit: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to get target commit: %w", err)
}

// Get base and target trees
baseTree, err := baseCommit.Tree()
if err != nil {
return "", nil, nil, fmt.Errorf("failed to get base tree: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to get base tree: %w", err)
}

targetTree, err := targetCommit.Tree()
if err != nil {
return "", nil, nil, fmt.Errorf("failed to get target tree: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to get target tree: %w", err)
}

// Compute diff between trees
changes, err := baseTree.Diff(targetTree)
if err != nil {
return "", nil, nil, fmt.Errorf("failed to compute diff: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to compute diff: %w", err)
}

// Keep track of file paths by change type
Expand All @@ -246,12 +249,12 @@ func generateGitDiff(
for _, change := range changes {
action, err := change.Action()
if err != nil {
return "", nil, nil, fmt.Errorf("failed to get change action: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to get change action: %w", err)
}

from, to, err := change.Files()
if err != nil {
return "", nil, nil, fmt.Errorf("failed to get files: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to get files: %w", err)
}

changeInfo := changeInfo{}
Expand All @@ -262,12 +265,12 @@ func generateGitDiff(
if to != nil {
blob, err := repo.BlobObject(to.Hash)
if err != nil {
return "", nil, nil, fmt.Errorf("failed to get target blob: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to get target blob: %w", err)
}

content, err := getBlobContent(blob)
if err != nil {
return "", nil, nil, fmt.Errorf("failed to read target blob: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to read target blob: %w", err)
}

changeInfo = formatNewFileDiff(content, diffContextLines, diffIgnore)
Expand All @@ -282,12 +285,12 @@ func generateGitDiff(
} else if from != nil {
blob, err := repo.BlobObject(from.Hash)
if err != nil {
return "", nil, nil, fmt.Errorf("failed to get base blob: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to get base blob: %w", err)
}

content, err := getBlobContent(blob)
if err != nil {
return "", nil, nil, fmt.Errorf("failed to read base blob: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to read base blob: %w", err)
}

changeInfo = formatDeletedFileDiff(content, diffContextLines, diffIgnore)
Expand All @@ -300,24 +303,24 @@ func generateGitDiff(
if from != nil {
blob, err := repo.BlobObject(from.Hash)
if err != nil {
return "", nil, nil, fmt.Errorf("failed to get base blob: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to get base blob: %w", err)
}

oldContent, err = getBlobContent(blob)
if err != nil {
return "", nil, nil, fmt.Errorf("failed to read base blob: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to read base blob: %w", err)
}
}

if to != nil {
blob, err := repo.BlobObject(to.Hash)
if err != nil {
return "", nil, nil, fmt.Errorf("failed to get target blob: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to get target blob: %w", err)
}

newContent, err = getBlobContent(blob)
if err != nil {
return "", nil, nil, fmt.Errorf("failed to read target blob: %w", err)
return "", "", nil, nil, fmt.Errorf("failed to read target blob: %w", err)
}
}

Expand Down Expand Up @@ -360,11 +363,10 @@ func generateGitDiff(
}

if len(changedFiles) == 0 {
return "No changes found", nil, nil, nil
return "No changes found", "", nil, nil, nil
}

// Build summary
summary := buildSummary(changedFiles)
inlineSummary, summaryDetails := buildSummary(changedFiles, summaryThreshold)

// Create arrays of formatted file sections
markdownFileSections := make([]MarkdownSection, 0, len(changedFiles))
Expand All @@ -381,7 +383,7 @@ func generateGitDiff(
htmlFileSections = append(htmlFileSections, diff.buildHTMLSection(argocdUIURL))
}

return summary, markdownFileSections, htmlFileSections, nil
return inlineSummary, summaryDetails, markdownFileSections, htmlFileSections, nil
}

// getBlobContent reads the content of a Git blob
Expand Down
Loading