func (s syncTasks) adjustDeps(isDep func(obj *unstructured.Unstructured) (string, bool), doesRefDep func(obj *unstructured.Unstructured) (string, bool)) {
	firstIndexByDepKey := map[string]int{}
	for i, t := range s {
		if t.targetObj == nil {
			continue
		}
		if depKey, ok := isDep(t.targetObj); ok {
			if index, ok := firstIndexByDepKey[depKey]; ok {
				wave := s[index].wave()
				t.waveOverride = &wave
				t.phase = s[index].phase
				for j := i; j > index; j-- {
					s[j] = s[j-1]
				}
				s[index] = t
				for ns, firstIndex := range firstIndexByDepKey {
					if firstIndex >= index {
						firstIndexByDepKey[ns] = firstIndex + 1
					}
				}
			}
		} else if depKey, ok := doesRefDep(t.targetObj); ok {
			if _, ok := firstIndexByDepKey[depKey]; !ok {
				firstIndexByDepKey[depKey] = i
			}
		}
	}
}
adjust order of tasks and bubble up tasks which are dependencies of other tasks
(e.g. namespace sync should happen before resources that resides in that namespace)
package sync
import (
	"fmt"
	"sort"
	"strings"
	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"github.com/argoproj/gitops-engine/pkg/sync/common"
	"github.com/argoproj/gitops-engine/pkg/utils/kube"
)
var syncPhaseOrder = map[common.SyncPhase]int{
	common.SyncPhasePreSync:  -1,
	common.SyncPhaseSync:     0,
	common.SyncPhasePostSync: 1,
	common.SyncPhaseSyncFail: 2,
}
var kindOrder = map[string]int{}
func init() {
	kinds := []string{
		"Namespace",
		"NetworkPolicy",
		"ResourceQuota",
		"LimitRange",
		"PodSecurityPolicy",
		"PodDisruptionBudget",
		"ServiceAccount",
		"Secret",
		"SecretList",
		"ConfigMap",
		"StorageClass",
		"PersistentVolume",
		"PersistentVolumeClaim",
		"CustomResourceDefinition",
		"ClusterRole",
		"ClusterRoleList",
		"ClusterRoleBinding",
		"ClusterRoleBindingList",
		"Role",
		"RoleList",
		"RoleBinding",
		"RoleBindingList",
		"Service",
		"DaemonSet",
		"Pod",
		"ReplicationController",
		"ReplicaSet",
		"Deployment",
		"HorizontalPodAutoscaler",
		"StatefulSet",
		"Job",
		"CronJob",
		"Ingress",
		"APIService",
	}
	for i, kind := range kinds {
		kindOrder[kind] = i - len(kinds)
	}
}
type syncTasks []*syncTask
func (s syncTasks) Len() int {
	return len(s)
}
func (s syncTasks) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
func (s syncTasks) Less(i, j int) bool {
	tA := s[i]
	tB := s[j]
	d := syncPhaseOrder[tA.phase] - syncPhaseOrder[tB.phase]
	if d != 0 {
		return d < 0
	}
	d = tA.wave() - tB.wave()
	if d != 0 {
		return d < 0
	}
	a := tA.obj()
	b := tB.obj()
	d = kindOrder[a.GetKind()] - kindOrder[b.GetKind()]
	if d != 0 {
		return d < 0
	}
	return a.GetName() < b.GetName()
}
func (s syncTasks) Sort() {
	sort.Sort(s)
	s.adjustDeps(func(obj *unstructured.Unstructured) (string, bool) {
		return obj.GetName(), obj.GetKind() == kube.NamespaceKind && obj.GroupVersionKind().Group == ""
	}, func(obj *unstructured.Unstructured) (string, bool) {
		return obj.GetNamespace(), obj.GetNamespace() != ""
	})
	s.adjustDeps(func(obj *unstructured.Unstructured) (string, bool) {
		if kube.IsCRD(obj) {
			crdGroup, ok, err := unstructured.NestedString(obj.Object, "spec", "group")
			if err != nil || !ok {
				return "", false
			}
			crdKind, ok, err := unstructured.NestedString(obj.Object, "spec", "names", "kind")
			if err != nil || !ok {
				return "", false
			}
			return fmt.Sprintf("%s/%s", crdGroup, crdKind), true
		}
		return "", false
	}, func(obj *unstructured.Unstructured) (string, bool) {
		gk := obj.GroupVersionKind()
		return fmt.Sprintf("%s/%s", gk.Group, gk.Kind), true
	})
}