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
26 changes: 15 additions & 11 deletions pkg/argoapplication/application_sets.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func ConvertAppSetsToAppsInBothBranches(
baseTempFolder := fmt.Sprintf("%s/%s", tempFolder, git.Base)
targetTempFolder := fmt.Sprintf("%s/%s", tempFolder, git.Target)

baseApps, err := processAppSets(
baseApps, _, err := processAppSets(
argocd,
baseApps,
baseBranch,
Expand All @@ -46,13 +46,12 @@ func ConvertAppSetsToAppsInBothBranches(
repo,
redirectRevisions,
)

if err != nil {
log.Error().Str("branch", baseBranch.Name).Msg("❌ Failed to generate base apps")
return nil, nil, time.Since(startTime), err
}

targetApps, err = processAppSets(
targetApps, targetIgnoredApps, err := processAppSets(
argocd,
targetApps,
targetBranch,
Expand All @@ -67,6 +66,10 @@ func ConvertAppSetsToAppsInBothBranches(
return nil, nil, time.Since(startTime), err
}

// Filter out apps from base branch that are ignored on target branch.
// This prevents apps with argocd-diff-preview/ignore annotation from showing as "deleted".
baseApps = RemoveIgnoredApps(baseApps, targetIgnoredApps, baseBranch.Name)

return baseApps, targetApps, time.Since(startTime), nil
}

Expand All @@ -79,12 +82,12 @@ func processAppSets(
filterOptions FilterOptions,
repo string,
redirectRevisions []string,
) ([]ArgoResource, error) {
) ([]ArgoResource, []IgnoredApp, error) {

appSetTempFolder := fmt.Sprintf("%s/app-sets", tempFolder)
if err := utils.CreateFolder(appSetTempFolder, true); err != nil {
log.Error().Msgf("❌ Failed to create temp folder: %s", appSetTempFolder)
return nil, err
return nil, nil, err
}

apps, err := convertAppSetsToApps(
Expand All @@ -96,19 +99,20 @@ func processAppSets(
)
if err != nil {
log.Error().Str("branch", branch.Name).Msg("❌ Failed to generate apps")
return nil, err
return nil, nil, err
}

if len(apps) == 0 {
return apps, nil
return apps, nil, nil
}

log.Info().Str("branch", branch.Name).Msgf("🤖 Filtering %d Applications", len(apps))
apps = FilterAll(apps, filterOptions)
filterResult := FilterAll(apps, filterOptions)
apps = filterResult.Apps

if len(apps) == 0 {
log.Info().Str("branch", branch.Name).Msg("🤖 No applications left after filtering")
return apps, nil
return apps, filterResult.IgnoredApps, nil
}

log.Info().Str("branch", branch.Name).Msgf("🤖 Patching %d Applications", len(apps))
Expand All @@ -121,7 +125,7 @@ func processAppSets(
)
if err != nil {
log.Error().Str("branch", branch.Name).Msgf("❌ Failed to patch Applications on branch: %s", branch.Name)
return nil, err
return nil, nil, err
}

if debug {
Expand All @@ -138,7 +142,7 @@ func processAppSets(
}
}

return apps, nil
return apps, filterResult.IgnoredApps, nil
}

func convertAppSetsToApps(
Expand Down
38 changes: 23 additions & 15 deletions pkg/argoapplication/applications.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ func (a *ArgoResource) WriteToFolder(folder string) (string, error) {
return randomFileName, nil
}

// GetApplicationsForBranches gets applications for both base and target branches
// GetApplicationsForBranches gets applications for both base and target branches.
// Apps that are ignored via the argocd-diff-preview/ignore annotation on the target branch
// will also be filtered out from the base branch to avoid showing them as "deleted".
func GetApplicationsForBranches(
argocdNamespace string,
baseBranch *git.Branch,
Expand All @@ -77,7 +79,7 @@ func GetApplicationsForBranches(
repo string,
redirectRevisions []string,
) ([]ArgoResource, []ArgoResource, error) {
baseApps, err := getApplications(
baseApps, _, err := getApplications(
argocdNamespace,
baseBranch,
filterOptions,
Expand All @@ -88,7 +90,7 @@ func GetApplicationsForBranches(
return nil, nil, err
}

targetApps, err := getApplications(
targetApps, targetIgnoredApps, err := getApplications(
argocdNamespace,
targetBranch,
filterOptions,
Expand All @@ -99,17 +101,23 @@ func GetApplicationsForBranches(
return nil, nil, err
}

// Filter out apps from base branch that are ignored on target branch.
// This prevents apps with argocd-diff-preview/ignore annotation from showing as "deleted".
baseApps = RemoveIgnoredApps(baseApps, targetIgnoredApps, baseBranch.Name)

return baseApps, targetApps, nil
}

// getApplications gets applications for a single branch
// getApplications gets applications for a single branch.
// Returns (apps, ignoredApps, error) where ignoredApps contains the apps
// that were filtered out due to the argocd-diff-preview/ignore annotation.
func getApplications(
argocdNamespace string,
branch *git.Branch,
filterOptions FilterOptions,
repo string,
redirectRevisions []string,
) ([]ArgoResource, error) {
) ([]ArgoResource, []IgnoredApp, error) {
log.Info().Str("branch", branch.Name).Msg("🤖 Fetching all files for branch")

yamlFiles := fileparsing.GetYamlFiles(branch.FolderName(), filterOptions.FileRegex)
Expand All @@ -121,33 +129,33 @@ func getApplications(
applications := FromResourceToApplication(k8sResources)

if len(applications) == 0 {
return []ArgoResource{}, nil
return []ArgoResource{}, nil, nil
}

// filter applications
log.Info().Str("branch", branch.Name).Msgf("🤖 Filtering %d Application[Sets]", len(applications))
applications = FilterAllWithLogging(applications, filterOptions, branch)
filterResult := FilterAllWithLogging(applications, filterOptions, branch)

if len(applications) == 0 {
return []ArgoResource{}, nil
if len(filterResult.Apps) == 0 {
return []ArgoResource{}, filterResult.IgnoredApps, nil
}

log.Info().Str("branch", branch.Name).Msgf("🤖 Patching %d Application[Sets]", len(applications))
log.Info().Str("branch", branch.Name).Msgf("🤖 Patching %d Application[Sets]", len(filterResult.Apps))

applications, err := patchApplications(
patchedApps, err := patchApplications(
argocdNamespace,
applications,
filterResult.Apps,
branch,
repo,
redirectRevisions,
)
if err != nil {
return nil, err
return nil, nil, err
}

log.Debug().Str("branch", branch.Name).Msgf("Patched %d Application[Sets]", len(applications))
log.Debug().Str("branch", branch.Name).Msgf("Patched %d Application[Sets]", len(patchedApps))

return applications, nil
return patchedApps, filterResult.IgnoredApps, nil
}

// PatchApplication patches a single ArgoResource
Expand Down
79 changes: 63 additions & 16 deletions pkg/argoapplication/filter.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,47 @@ type FilterOptions struct {
WatchIfNoWatchPatternFound bool
}

func FilterAllWithLogging(apps []ArgoResource, filterOptions FilterOptions, branch *git.Branch) []ArgoResource {
// IgnoredApp represents an app that was ignored via annotation, tracking both ID and source file
type IgnoredApp struct {
Id string
FileName string
}

// FilterResult contains the filtered apps and any apps that were ignored via annotation
type FilterResult struct {
Apps []ArgoResource
IgnoredApps []IgnoredApp // Apps filtered out due to argocd-diff-preview/ignore annotation
}

// RemoveIgnoredApps filters out apps from baseApps that match any app in ignoredApps.
// Matching is done by both ID and FileName to avoid filtering apps with same name from different sources.
func RemoveIgnoredApps(baseApps []ArgoResource, ignoredApps []IgnoredApp, branchName string) []ArgoResource {
if len(ignoredApps) == 0 {
return baseApps
}

ignoredSet := make(map[string]struct{}, len(ignoredApps))
for _, ignored := range ignoredApps {
key := fmt.Sprintf("%s|%s", ignored.Id, ignored.FileName)
ignoredSet[key] = struct{}{}
}

var filtered []ArgoResource
for _, app := range baseApps {
key := fmt.Sprintf("%s|%s", app.Id, app.FileName)
if _, exists := ignoredSet[key]; !exists {
filtered = append(filtered, app)
} else {
log.Debug().Str("branch", branchName).Msgf(
"Skipping %s '%s' because it is ignored on target branch",
app.Kind.ShortName(), app.GetLongName(),
)
}
}
return filtered
}

func FilterAllWithLogging(apps []ArgoResource, filterOptions FilterOptions, branch *git.Branch) FilterResult {
// Log selector and files changed info
switch {
case len(filterOptions.Selector) > 0 && len(filterOptions.FilesChanged) > 0:
Expand Down Expand Up @@ -60,17 +100,17 @@ func FilterAllWithLogging(apps []ArgoResource, filterOptions FilterOptions, bran
numberOfAppsBeforeFiltering := len(apps)

// Filter applications
filteredApps := FilterAll(apps, filterOptions)
result := FilterAll(apps, filterOptions)

// Log filtering results
if numberOfAppsBeforeFiltering != len(filteredApps) {
if numberOfAppsBeforeFiltering != len(result.Apps) {
log.Info().Str("branch", branch.Name).Msgf(
"🤖 Found %d Application[Sets] before filtering",
numberOfAppsBeforeFiltering,
)
log.Info().Str("branch", branch.Name).Msgf(
"🤖 Found %d Application[Sets] after filtering",
len(filteredApps),
len(result.Apps),
)
} else {
log.Info().Str("branch", branch.Name).Msgf(
Expand All @@ -79,40 +119,49 @@ func FilterAllWithLogging(apps []ArgoResource, filterOptions FilterOptions, bran
)
}

return filteredApps
return result
}

func FilterAll(
apps []ArgoResource,
filterOptions FilterOptions,
) []ArgoResource {
) FilterResult {
var filteredApps []ArgoResource
var ignoredApps []IgnoredApp
for _, app := range apps {
if app.Filter(filterOptions) {
selected, ignoredByAnnotation := app.Filter(filterOptions)
if selected {
filteredApps = append(filteredApps, app)
} else if ignoredByAnnotation {
ignoredApps = append(ignoredApps, IgnoredApp{Id: app.Id, FileName: app.FileName})
}
}
return filteredApps
return FilterResult{
Apps: filteredApps,
IgnoredApps: ignoredApps,
}
}

// Filter checks if the application matches the given selectors and watches the given files
// Filter checks if the application matches the given selectors and watches the given files.
// Returns (selected bool, ignoredByAnnotation bool) where ignoredByAnnotation is true
// if the app was filtered out specifically due to the argocd-diff-preview/ignore annotation.
func (a *ArgoResource) Filter(
filterOptions FilterOptions,
) bool {
) (bool, bool) {

// First check selected annotation
selected, reason := a.filterByIgnoreAnnotation()
if !selected {
log.Debug().Str(a.Kind.ShortName(), a.GetLongName()).Msgf("%s is not selected because: %s", a.Kind.ShortName(), reason)
return false
return false, true // ignoredByAnnotation = true
}

// Then check selectors
if len(filterOptions.Selector) > 0 {
selected, reason := a.filterBySelectors(filterOptions.Selector)
if !selected {
log.Debug().Str(a.Kind.ShortName(), a.GetLongName()).Msgf("%s is not selected because: %s", a.Kind.ShortName(), reason)
return false
return false, false
}
}

Expand All @@ -121,17 +170,15 @@ func (a *ArgoResource) Filter(
selected, reason := a.filterByFilesChanged(filterOptions.FilesChanged, filterOptions.IgnoreInvalidWatchPattern, filterOptions.WatchIfNoWatchPatternFound)
if !selected {
log.Debug().Str(a.Kind.ShortName(), a.GetLongName()).Msgf("%s is not selected because: %s", a.Kind.ShortName(), reason)
return false
return false, false
}
log.Debug().Str(a.Kind.ShortName(), a.GetLongName()).Msgf("%s is selected because: %s", a.Kind.ShortName(), reason)
}

return true
return true, false
}

func (a *ArgoResource) filterByIgnoreAnnotation() (bool, string) {

// get annotations
annotations, found, err := unstructured.NestedStringMap(a.Yaml.Object, "metadata", "annotations")
if err != nil || !found || len(annotations) == 0 {
return true, "no 'argocd-diff-preview/ignore' annotation found"
Expand Down
Loading