Repository: argoproj/argo-cd
Pull request #8516 (2022-02-15 22:21:47)

refactor: use argocd-git-ask-pass to pass git credentials to git/kustomize 
Signed-off-by: Alexander Matyushentsev <AMatyushentsev@gmail.com>

In order to provide git credentials to Kustomize Argo CD sets `GIT_ASKPASS=git-ask-pass.sh` env variable so that git clone call `git-ask-pass.sh`  for credentials. The `git-ask-pass.sh` needs to get credentials somewhere so we also set `GIT_USERNAME` and `GIT_PASSWORD`  env variables. The problem is that `GIT_USERNAME` and `GIT_PASSWORD` also available to kustomize and user can leverage kustomize config map generator to just print env variables.

**Fix:**

I think we agreed that it is not safe to have any secret in env variable or file, because one way or another almost all config tools provide a way to read file/env variable. But we can assume config management tool does not allow user to run any custom binary. So proposing following solution:

* introduce `argocd-git-ask-pass` binary and new, localhost-only endpoint in repo-server
* during manifests generation repo server generates uuid, associate it with git credentials and fork/exec config management tool with GIT_ASKPASS='argocd-git-ask-pass <uuid>'
* git executes argocd-git-ask-pass <uuid> , then argocd-git-ask-pass calls send uuid to new repo-server endpoint and receives associated credentials
* once manifest generation is completed repo-server forget about uuid and associated credentials

codecov[bot]: # [Codecov](https://codecov.io/gh/argoproj/argo-cd/pull/8516?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj) Report
> Merging [#8516](https://codecov.io/gh/argoproj/argo-cd/pull/8516?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj) (4b4adcb) into [master](https://codecov.io/gh/argoproj/argo-cd/commit/12590cddb83ca712b55013eb7bb9e2dc69c28b6c?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj) (12590cd) will **increase** coverage by `0.18%`.
> The diff coverage is `34.24%`.

[![Impacted file tree graph](https://codecov.io/gh/argoproj/argo-cd/pull/8516/graphs/tree.svg?width=650&height=150&src=pr&token=kf5kwuegwE&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj)](https://codecov.io/gh/argoproj/argo-cd/pull/8516?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj)

```diff
@@            Coverage Diff             @@
##           master    #8516      +/-   ##
==========================================
+ Coverage   42.10%   42.28%   +0.18%     
==========================================
  Files         177      177              
  Lines       22911    22849      -62     
==========================================
+ Hits         9646     9662      +16     
+ Misses      11889    11811      -78     
  Partials     1376     1376              
```


| [Impacted Files](https://codecov.io/gh/argoproj/argo-cd/pull/8516?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj) | Coverage Δ | |
|---|---|---|
| [cmd/argocd/commands/app.go](https://codecov.io/gh/argoproj/argo-cd/pull/8516/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj#diff-Y21kL2FyZ29jZC9jb21tYW5kcy9hcHAuZ28=) | `0.53% <0.00%> (-0.01%)` | :arrow_down: |
| [util/git/creds.go](https://codecov.io/gh/argoproj/argo-cd/pull/8516/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj#diff-dXRpbC9naXQvY3JlZHMuZ28=) | `9.27% <0.00%> (-0.73%)` | :arrow_down: |
| [reposerver/askpass/server.go](https://codecov.io/gh/argoproj/argo-cd/pull/8516/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj#diff-cmVwb3NlcnZlci9hc2twYXNzL3NlcnZlci5nbw==) | `35.29% <35.29%> (ø)` | |
| [util/git/client.go](https://codecov.io/gh/argoproj/argo-cd/pull/8516/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj#diff-dXRpbC9naXQvY2xpZW50Lmdv) | `44.44% <50.00%> (-0.31%)` | :arrow_down: |
| [reposerver/repository/repository.go](https://codecov.io/gh/argoproj/argo-cd/pull/8516/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj#diff-cmVwb3NlcnZlci9yZXBvc2l0b3J5L3JlcG9zaXRvcnkuZ28=) | `58.80% <75.00%> (+0.08%)` | :arrow_up: |
| [util/kustomize/kustomize.go](https://codecov.io/gh/argoproj/argo-cd/pull/8516/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj#diff-dXRpbC9rdXN0b21pemUva3VzdG9taXplLmdv) | `70.00% <100.00%> (+0.20%)` | :arrow_up: |
| [cmd/argocd/commands/gpg.go](https://codecov.io/gh/argoproj/argo-cd/pull/8516/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj#diff-Y21kL2FyZ29jZC9jb21tYW5kcy9ncGcuZ28=) | `0.00% <0.00%> (ø)` | |
| [cmd/argocd/commands/cert.go](https://codecov.io/gh/argoproj/argo-cd/pull/8516/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj#diff-Y21kL2FyZ29jZC9jb21tYW5kcy9jZXJ0Lmdv) | `0.00% <0.00%> (ø)` | |
| [cmd/argocd/commands/repo.go](https://codecov.io/gh/argoproj/argo-cd/pull/8516/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj#diff-Y21kL2FyZ29jZC9jb21tYW5kcy9yZXBvLmdv) | `0.00% <0.00%> (ø)` | |
| ... and [16 more](https://codecov.io/gh/argoproj/argo-cd/pull/8516/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/argoproj/argo-cd/pull/8516?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/argoproj/argo-cd/pull/8516?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj). Last update [12590cd...4b4adcb](https://codecov.io/gh/argoproj/argo-cd/pull/8516?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=argoproj).


Patch:
diff --git a/.github/workflows/ci-build.yaml b/.github/workflows/ci-build.yaml
index 3646323dc..e957031dc 100644
--- a/.github/workflows/ci-build.yaml
+++ b/.github/workflows/ci-build.yaml
@@ -376,6 +376,9 @@ jobs:
       - name: Add /usr/local/bin to PATH
         run: |
           echo "/usr/local/bin" >> $GITHUB_PATH
+      - name: Add ./dist to PATH
+        run: |
+          echo "$(pwd)/dist" >> $GITHUB_PATH
       - name: Download Go dependencies
         run: |
           go mod download
diff --git a/Dockerfile b/Dockerfile
index 4216e5901..0e9398ff3 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -54,7 +54,6 @@ RUN groupadd -g 999 argocd && \
     apt-get clean && \
     rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
 
-COPY hack/git-ask-pass.sh /usr/local/bin/git-ask-pass.sh
 COPY hack/gpg-wrapper.sh /usr/local/bin/gpg-wrapper.sh
 COPY hack/git-verify-wrapper.sh /usr/local/bin/git-verify-wrapper.sh
 COPY --from=builder /usr/local/bin/ks /usr/local/bin/ks
diff --git a/assets/swagger.json b/assets/swagger.json
index 9dfe2b138..13f68e58b 100644
--- a/assets/swagger.json
+++ b/assets/swagger.json
@@ -2744,6 +2744,16 @@
             "type": "string",
             "name": "revision",
             "in": "query"
+          },
+          {
+            "type": "string",
+            "name": "appName",
+            "in": "query"
+          },
+          {
+            "type": "string",
+            "name": "appProject",
+            "in": "query"
           }
         ],
         "responses": {
@@ -4108,6 +4118,9 @@
         "appName": {
           "type": "string"
         },
+        "appProject": {
+          "type": "string"
+        },
         "source": {
           "$ref": "#/definitions/v1alpha1ApplicationSource"
         }
diff --git a/cmd/argocd-git-ask-pass/commands/argocd_git_ask_pass.go b/cmd/argocd-git-ask-pass/commands/argocd_git_ask_pass.go
new file mode 100644
index 000000000..8c3596562
--- /dev/null
+++ b/cmd/argocd-git-ask-pass/commands/argocd_git_ask_pass.go
@@ -0,0 +1,57 @@
+package commands
+
+import (
+	"context"
+	"fmt"
+	"os"
+	"strings"
+
+	"github.com/argoproj/argo-cd/v2/util/git"
+
+	"github.com/spf13/cobra"
+	"google.golang.org/grpc"
+
+	"github.com/argoproj/argo-cd/v2/reposerver/askpass"
+	"github.com/argoproj/argo-cd/v2/util/errors"
+	grpc_util "github.com/argoproj/argo-cd/v2/util/grpc"
+	"github.com/argoproj/argo-cd/v2/util/io"
+)
+
+const (
+	// cliName is the name of the CLI
+	cliName = "argocd-git-ask-pass"
+)
+
+func NewCommand() *cobra.Command {
+	var command = cobra.Command{
+		Use:               cliName,
+		Short:             "Argo CD git credential helper",
+		DisableAutoGenTag: true,
+		Run: func(c *cobra.Command, args []string) {
+			if len(os.Args) != 2 {
+				errors.CheckError(fmt.Errorf("expected 1 argument, got %d", len(os.Args)-1))
+			}
+			nonce := os.Getenv(git.ASKPASS_NONCE_ENV)
+			if nonce == "" {
+				errors.CheckError(fmt.Errorf("%s is not set", git.ASKPASS_NONCE_ENV))
+			}
+			conn, err := grpc_util.BlockingDial(context.Background(), "unix", askpass.SocketPath, nil, grpc.WithInsecure())
+			errors.CheckError(err)
+			defer io.Close(conn)
+			client := askpass.NewAskPassServiceClient(conn)
+
+			creds, err := client.GetCredentials(context.Background(), &askpass.CredentialsRequest{Nonce: nonce})
+			errors.CheckError(err)
+			switch {
+			case strings.HasPrefix(os.Args[1], "Username"):
+				fmt.Println(creds.Username)
+			case strings.HasPrefix(os.Args[1], "Password"):
+				fmt.Println(creds.Password)
+			default:
+				errors.CheckError(fmt.Errorf("unknown credential type '%s'", os.Args[1]))
+			}
+		},
+	}
+
+	return &command
+}
diff --git a/cmd/argocd-repo-server/commands/argocd_repo_server.go b/cmd/argocd-repo-server/commands/argocd_repo_server.go
index ef0a18952..1d3c2eaf1 100644
--- a/cmd/argocd-repo-server/commands/argocd_repo_server.go
+++ b/cmd/argocd-repo-server/commands/argocd_repo_server.go
@@ -18,6 +18,7 @@ import (
 	"github.com/argoproj/argo-cd/v2/common"
 	"github.com/argoproj/argo-cd/v2/reposerver"
 	"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
+	"github.com/argoproj/argo-cd/v2/reposerver/askpass"
 	reposervercache "github.com/argoproj/argo-cd/v2/reposerver/cache"
 	"github.com/argoproj/argo-cd/v2/reposerver/metrics"
 	"github.com/argoproj/argo-cd/v2/reposerver/repository"
@@ -94,6 +95,7 @@ func NewCommand() *cobra.Command {
 			cache, err := cacheSrc()
 			errors.CheckError(err)
 
+			askPassServer := askpass.NewServer()
 			metricsServer := metrics.NewMetricsServer()
 			cacheutil.CollectMetrics(redisClient, metricsServer)
 			server, err := reposerver.NewServer(metricsServer, cache, tlsConfigCustomizer, repository.RepoServerInitConstants{
@@ -102,7 +104,7 @@ func NewCommand() *cobra.Command {
 				PauseGenerationOnFailureForMinutes:           getPauseGenerationOnFailureForMinutes(),
 				PauseGenerationOnFailureForRequests:          getPauseGenerationOnFailureForRequests(),
 				SubmoduleEnabled:                             getSubmoduleEnabled(),
-			})
+			}, askPassServer)
 			errors.CheckError(err)
 
 			grpc := server.CreateGRPC()
@@ -133,6 +135,7 @@ func NewCommand() *cobra.Command {
 			})
 			http.Handle("/metrics", metricsServer.GetHandler())
 			go func() { errors.CheckError(http.ListenAndServe(fmt.Sprintf(":%d", metricsPort), nil)) }()
+			go func() { errors.CheckError(askPassServer.Run(askpass.SocketPath)) }()
 
 			if gpg.IsGPGEnabled() {
 				log.Infof("Initializing GnuPG keyring at %s", common.GetGnuPGHomePath())
diff --git a/cmd/argocd/commands/app.go b/cmd/argocd/commands/app.go
index c1d4c98bd..8ba572964 100644
--- a/cmd/argocd/commands/app.go
+++ b/cmd/argocd/commands/app.go
@@ -282,6 +282,7 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
 		untilTime    string
 		filter       string
 		container    string
+		previous     bool
 	)
 	var command = &cobra.Command{
 		Use:   "logs APPNAME",
@@ -311,6 +312,7 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
 					UntilTime:    &untilTime,
 					Filter:       &filter,
 					Container:    container,
+					Previous:     previous,
 				})
 				if err != nil {
 					log.Fatalf("failed to get pod logs: %v", err)
@@ -352,6 +354,7 @@ func NewApplicationLogsCommand(clientOpts *argocdclient.ClientOptions) *cobra.Co
 	command.Flags().StringVar(&untilTime, "until-time", "", "Show logs until this time")
 	command.Flags().StringVar(&filter, "filter", "", "Show logs contain this string")
 	command.Flags().StringVar(&container, "container", "", "Optional container name")
+	command.Flags().BoolVarP(&previous, "previous", "p", false, "Specify if the previously terminated container logs should be returned")
 
 	return command
 }
@@ -772,7 +775,7 @@ func getLocalObjectsString(app *argoappv1.Application, local, localRepoRoot, app
 		ApiVersions:       apiVersions,
 		Plugins:           configManagementPlugins,
 		TrackingMethod:    trackingMethod,
-	}, true)
+	}, true, &git.NoopCredsStore{})
 	errors.CheckError(err)
 
 	return res.Manifests
diff --git a/cmd/argocd/commands/project.go b/cmd/argocd/commands/project.go
index 3a9470abd..cb8f7a073 100644
--- a/cmd/argocd/commands/project.go
+++ b/cmd/argocd/commands/project.go
@@ -697,7 +697,7 @@ func printProjectLine(w io.Writer, p *v1alpha1.AppProject) {
 	fmt.Fprintf(w, "%s\t%s\t%v\t%v\t%v\t%v\t%v\t%v\n", p.Name, p.Spec.Description, destinations, sourceRepos, clusterWhitelist, namespaceBlacklist, signatureKeys, formatOrphanedResources(p))
 }
 
-func printProject(p *v1alpha1.AppProject) {
+func printProject(p *v1alpha1.AppProject, scopedRepositories []*v1alpha1.Repository, scopedClusters []*v1alpha1.Cluster) {
 	const printProjFmtStr = "%-29s%s\n"
 
 	fmt.Printf(printProjFmtStr, "Name:", p.Name)
@@ -723,6 +723,16 @@ func printProject(p *v1alpha1.AppProject) {
 		fmt.Printf(printProjFmtStr, "", p.Spec.SourceRepos[i])
 	}
 
+	//Print scoped repositories
+	scr0 := "<none>"
+	if len(scopedRepositories) > 0 {
+		scr0 = scopedRepositories[0].Repo
+	}
+	fmt.Printf(printProjFmtStr, "Scoped Repositories:", scr0)
+	for i := 1; i < len(scopedRepositories); i++ {
+		fmt.Printf(printProjFmtStr, "", scopedRepositories[i].Repo)
+	}
+
 	// Print allowed cluster resources
 	cwl0 := "<none>"
 	if len(p.Spec.ClusterResourceWhitelist) > 0 {
@@ -733,6 +743,16 @@ func printProject(p *v1alpha1.AppProject) {
 		fmt.Printf(printProjFmtStr, "", fmt.Sprintf("%s/%s", p.Spec.ClusterResourceWhitelist[i].Group, p.Spec.ClusterResourceWhitelist[i].Kind))
 	}
 
+	//Print scoped clusters
+	scc0 := "<none>"
+	if len(scopedClusters) > 0 {
+		scc0 = scopedClusters[0].Server
+	}
+	fmt.Printf(printProjFmtStr, "Scoped Clusters:", scc0)
+	for i := 1; i < len(scopedClusters); i++ {
+		fmt.Printf(printProjFmtStr, "", scopedClusters[i].Server)
+	}
+
 	// Print denied namespaced resources
 	rbl0 := "<none>"
 	if len(p.Spec.NamespaceResourceBlacklist) > 0 {
@@ -774,15 +794,15 @@ func NewProjectGetCommand(clientOpts *argocdclient.ClientOptions) *cobra.Command
 			projName := args[0]
 			conn, projIf := headless.NewClientOrDie(clientOpts, c).NewProjectClientOrDie()
 			defer argoio.Close(conn)
-			p, err := projIf.Get(context.Background(), &projectpkg.ProjectQuery{Name: projName})
+			detailedProject, err := projIf.GetDetailedProject(context.Background(), &projectpkg.ProjectQuery{Name: projName})
 			errors.CheckError(err)
 
 			switch output {
 			case "yaml", "json":
-				err := PrintResource(p, output)
+				err := PrintResource(detailedProject.Project, output)
 				errors.CheckError(err)
 			case "wide", "":
-				printProject(p)
+				printProject(detailedProject.Project, detailedProject.Repositories, detailedProject.Clusters)
 			default:
 				errors.CheckError(fmt.Errorf("unknown output format: %s", output))
 			}
diff --git a/cmd/main.go b/cmd/main.go
index 8db5f903a..1756d2ddf 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -10,6 +10,7 @@ import (
 	appcontroller "github.com/argoproj/argo-cd/v2/cmd/argocd-application-controller/commands"
 	cmpserver "github.com/argoproj/argo-cd/v2/cmd/argocd-cmp-server/commands"
 	dex "github.com/argoproj/argo-cd/v2/cmd/argocd-dex/commands"
+	gitaskpass "github.com/argoproj/argo-cd/v2/cmd/argocd-git-ask-pass/commands"
 	notification "github.com/argoproj/argo-cd/v2/cmd/argocd-notification/commands"
 	reposerver "github.com/argoproj/argo-cd/v2/cmd/argocd-repo-server/commands"
 	apiserver "github.com/argoproj/argo-cd/v2/cmd/argocd-server/commands"
@@ -42,6 +43,8 @@ func main() {
 		command = dex.NewCommand()
 	case "argocd-notifications":
 		command = notification.NewCommand()
+	case "argocd-git-ask-pass":
+		command = gitaskpass.NewCommand()
 	default:
 		command = cli.NewCommand()
 	}
diff --git a/controller/cache/cache.go b/controller/cache/cache.go
index f22160bf7..47fcbc075 100644
--- a/controller/cache/cache.go
+++ b/controller/cache/cache.go
@@ -40,6 +40,9 @@ const (
 	// EnvClusterCacheWatchResyncDuration is the env variable that holds cluster cache watch re-sync duration
 	EnvClusterCacheWatchResyncDuration = "ARGOCD_CLUSTER_CACHE_WATCH_RESYNC_DURATION"
 
+	// EnvClusterRetryTimeoutDuration is the env variable that holds cluster retry duration when sync error happens
+	EnvClusterSyncRetryTimeoutDuration = "ARGOCD_CLUSTER_SYNC_RETRY_TIMEOUT_DURATION"
+
 	// EnvClusterCacheListPageSize is the env variable to control size of the list page size when making K8s queries
 	EnvClusterCacheListPageSize = "ARGOCD_CLUSTER_CACHE_LIST_PAGE_SIZE"
 
@@ -65,6 +68,9 @@ var (
 	// for before relisting & restarting the watch
 	clusterCacheWatchResyncDuration = 10 * time.Minute
 
+	// clusterSyncRetryTimeoutDuration controls the sync retry duration when cluster sync error happens
+	clusterSyncRetryTimeoutDuration = 10 * time.Second
+
 	// The default limit of 50 is chosen based on experiments.
 	clusterCacheListSemaphoreSize int64 = 50
 
@@ -83,6 +89,7 @@ var (
 func init() {
 	clusterCacheResyncDuration = env.ParseDurationFromEnv(EnvClusterCacheResyncDuration, clusterCacheResyncDuration, 0, math.MaxInt64)
 	clusterCacheWatchResyncDuration = env.ParseDurationFromEnv(EnvClusterCacheWatchResyncDuration, clusterCacheWatchResyncDuration, 0, math.MaxInt64)
+	clusterSyncRetryTimeoutDuration = env.ParseDurationFromEnv(EnvClusterSyncRetryTimeoutDuration, clusterSyncRetryTimeoutDuration, 0, math.MaxInt64)
 	clusterCacheListPageSize = env.ParseInt64FromEnv(EnvClusterCacheListPageSize, clusterCacheListPageSize, 0, math.MaxInt64)
 	clusterCacheListSemaphoreSize = env.ParseInt64FromEnv(EnvClusterCacheListSemaphore, clusterCacheListSemaphoreSize, 0, math.MaxInt64)
 	clusterCacheAttemptLimit = int32(env.ParseInt64FromEnv(EnvClusterCacheAttemptLimit, 1, 1, math.MaxInt32))
@@ -341,6 +348,7 @@ func (c *liveStateCache) getCluster(server string) (clustercache.ClusterCache, e
 		clustercache.SetListSemaphore(semaphore.NewWeighted(clusterCacheListSemaphoreSize)),
 		clustercache.SetListPageSize(clusterCacheListPageSize),
 		clustercache.SetWatchResyncTimeout(clusterCacheWatchResyncDuration),
+		clustercache.SetClusterSyncRetryTimeout(clusterSyncRetryTimeoutDuration),
 		clustercache.SetResyncTimeout(clusterCacheResyncDuration),
 		clustercache.SetSettings(cacheSettings.clusterSettings),
 		clustercache.SetNamespaces(cluster.Namespaces),
diff --git a/docs/developer-guide/running-locally.md b/docs/developer-guide/running-locally.md
index 92a85dcb6..ac8b07acf 100644
--- a/docs/developer-guide/running-locally.md
+++ b/docs/developer-guide/running-locally.md
@@ -31,7 +31,13 @@ kubectl -n argocd scale deployment/argocd-redis --replicas 0
 
 ### Start local services
 
-Before starting local services, make sure you are present in `argocd` namespace. When you use the virtualized toolchain, starting local services is as simple as running
+The started services assume you are running in the namespace where Argo CD is installed. You can set the current context default namespace as follows:
+
+```bash
+kubectl config set-context --current --namespace=argocd
+```
+
+When you use the virtualized toolchain, starting local services is as simple as running
 
 ```bash
 make start
diff --git a/docs/getting_started.md b/docs/getting_started.md
index 09bb5c287..25afa7119 100644
--- a/docs/getting_started.md
+++ b/docs/getting_started.md
@@ -62,7 +62,7 @@ Kubectl port-forwarding can also be used to connect to the API server without ex
 kubectl port-forward svc/argocd-server -n argocd 8080:443
 ```
 
-The API server can then be accessed using the localhost:8080
+The API server can then be accessed using https://localhost:8080
 
 
 ## 4. Login Using The CLI
diff --git a/docs/requirements.txt b/docs/requirements.txt
index f3fda1b01..faf48017e 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,4 +1,4 @@
-mkdocs==1.1.2
+mkdocs==1.2.3
 mkdocs-material==7.1.7
 markdown_include==0.6.0
 pygments==2.7.4
\ No newline at end of file
diff --git a/docs/user-guide/commands/argocd_app_logs.md b/docs/user-guide/commands/argocd_app_logs.md
index 15f5e1c22..8081c9df2 100644
--- a/docs/user-guide/commands/argocd_app_logs.md
+++ b/docs/user-guide/commands/argocd_app_logs.md
@@ -17,6 +17,7 @@ argocd app logs APPNAME [flags]
       --kind string         Resource kind
       --name string         Resource name
       --namespace string    Resource namespace
+  -p, --previous            Specify if the previously terminated container logs should be returned
       --since-seconds int   A relative time in seconds before the current time from which to show logs
       --tail int            The number of lines from the end of the logs to show
       --until-time string   Show logs until this time
diff --git a/hack/gen-resources/examples/values.yaml b/hack/gen-resources/examples/values.yaml
index ceea30864..ff8f17d9d 100644
--- a/hack/gen-resources/examples/values.yaml
+++ b/hack/gen-resources/examples/values.yaml
@@ -1,5 +1,5 @@
 vcluster:
-  image: rancher/k3s:v1.18.6-k3s1 # be sure that use same cluster version either change an image tag
+  image: rancher/k3s:v1.23.3-k3s1 # be sure that use same cluster version either change an image tag
   extraArgs:
     - --service-cidr=10.100.0.0/16 # THE CLUSTER SERVICE CIDR HERE ( you can find here https://github.com/loft-sh/virtual-cluster#install how to get it)
 storage:
diff --git a/pkg/apiclient/repository/repository.pb.go b/pkg/apiclient/repository/repository.pb.go
index 6ce4bf9f1..dfd466ecd 100644
--- a/pkg/apiclient/repository/repository.pb.go
+++ b/pkg/apiclient/repository/repository.pb.go
@@ -39,6 +39,8 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
 type RepoAppsQuery struct {
 	Repo                 string   `protobuf:"bytes,1,opt,name=repo,proto3" json:"repo,omitempty"`
 	Revision             string   `protobuf:"bytes,2,opt,name=revision,proto3" json:"revision,omitempty"`
+	AppName              string   `protobuf:"bytes,3,opt,name=appName,proto3" json:"appName,omitempty"`
+	AppProject           string   `protobuf:"bytes,4,opt,name=appProject,proto3" json:"appProject,omitempty"`
 	XXX_NoUnkeyedLiteral struct{} `json:"-"`
 	XXX_unrecognized     []byte   `json:"-"`
 	XXX_sizecache        int32    `json:"-"`
@@ -91,6 +93,20 @@ func (m *RepoAppsQuery) GetRevision() string {
 	return ""
 }
 
+func (m *RepoAppsQuery) GetAppName() string {
+	if m != nil {
+		return m.AppName
+	}
+	return ""
+}
+
+func (m *RepoAppsQuery) GetAppProject() string {
+	if m != nil {
+		return m.AppProject
+	}
+	return ""
+}
+
 // AppInfo contains application type and app file path
 type AppInfo struct {
 	Type                 string   `protobuf:"bytes,1,opt,name=type,proto3" json:"type,omitempty"`
@@ -151,6 +167,7 @@ func (m *AppInfo) GetPath() string {
 type RepoAppDetailsQuery struct {
 	Source               *v1alpha1.ApplicationSource `protobuf:"bytes,1,opt,name=source,proto3" json:"source,omitempty"`
 	AppName              string                      `protobuf:"bytes,2,opt,name=appName,proto3" json:"appName,omitempty"`
+	AppProject           string                      `protobuf:"bytes,3,opt,name=appProject,proto3" json:"appProject,omitempty"`
 	XXX_NoUnkeyedLiteral struct{}                    `json:"-"`
 	XXX_unrecognized     []byte                      `json:"-"`
 	XXX_sizecache        int32                       `json:"-"`
@@ -203,6 +220,13 @@ func (m *RepoAppDetailsQuery) GetAppName() string {
 	return ""
 }
 
+func (m *RepoAppDetailsQuery) GetAppProject() string {
+	if m != nil {
+		return m.AppProject
+	}
+	return ""
+}
+
 // RepoAppsResponse contains applications of specified repository
 type RepoAppsResponse struct {
 	Items                []*AppInfo `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"`
@@ -663,77 +687,78 @@ func init() {
 }
 
 var fileDescriptor_8d38260443475705 = []byte{
-	// 1105 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0x5f, 0x6f, 0x1b, 0x45,
-	0x10, 0xd7, 0xe5, 0x8f, 0x93, 0x6c, 0xfe, 0xd4, 0xd9, 0x84, 0x72, 0xb8, 0x69, 0x1a, 0x6d, 0x4b,
-	0x15, 0xa2, 0x72, 0xd7, 0x18, 0x21, 0xaa, 0x22, 0x40, 0x69, 0x12, 0xb5, 0x11, 0x11, 0x81, 0xab,
-	0xc2, 0x03, 0x02, 0xa1, 0xcd, 0x79, 0x62, 0x1f, 0x39, 0xdf, 0x6e, 0x77, 0xd7, 0x06, 0xab, 0xea,
-	0x0b, 0x4f, 0x48, 0xf0, 0x82, 0x10, 0x52, 0xdf, 0x78, 0x41, 0xe2, 0x81, 0xcf, 0xc0, 0x3b, 0x8f,
-	0x48, 0x7c, 0x01, 0x14, 0xf1, 0x39, 0x10, 0xda, 0xdd, 0xf3, 0xdd, 0x39, 0xb1, 0x9d, 0x54, 0x84,
-	0xbc, 0xed, 0xfc, 0x66, 0x76, 0xe6, 0x37, 0xe3, 0x99, 0x59, 0x1f, 0x22, 0x12, 0x44, 0x1b, 0x84,
-	0x2f, 0x80, 0x33, 0x19, 0x29, 0x26, 0x3a, 0x85, 0xa3, 0xc7, 0x05, 0x53, 0x0c, 0xa3, 0x1c, 0xa9,
-	0x2c, 0xd6, 0x59, 0x9d, 0x19, 0xd8, 0xd7, 0x27, 0x6b, 0x51, 0x59, 0xaa, 0x33, 0x56, 0x8f, 0xc1,
-	0xa7, 0x3c, 0xf2, 0x69, 0x92, 0x30, 0x45, 0x55, 0xc4, 0x12, 0x99, 0x6a, 0xc9, 0xd1, 0x3d, 0xe9,
-	0x45, 0xcc, 0x68, 0x43, 0x26, 0xc0, 0x6f, 0xaf, 0xfb, 0x75, 0x48, 0x40, 0x50, 0x05, 0xb5, 0xd4,
-	0x66, 0xb7, 0x1e, 0xa9, 0x46, 0xeb, 0xc0, 0x0b, 0x59, 0xd3, 0xa7, 0xc2, 0x84, 0xf8, 0xc2, 0x1c,
-	0x5e, 0x0f, 0x6b, 0x7e, 0xbb, 0xea, 0xf3, 0xa3, 0xba, 0xbe, 0x2f, 0x7d, 0xca, 0x79, 0x1c, 0x85,
-	0xc6, 0xbf, 0xdf, 0x5e, 0xa7, 0x31, 0x6f, 0xd0, 0xd3, 0xde, 0xb6, 0xcf, 0xf0, 0x66, 0x12, 0x3a,
-	0x33, 0x71, 0xf2, 0x1e, 0x9a, 0x0d, 0x80, 0xb3, 0x0d, 0xce, 0xe5, 0x47, 0x2d, 0x10, 0x1d, 0x8c,
-	0xd1, 0x98, 0x36, 0x72, 0x9d, 0x15, 0x67, 0x75, 0x2a, 0x30, 0x67, 0x5c, 0x41, 0x93, 0x02, 0xda,
-	0x91, 0x8c, 0x58, 0xe2, 0x8e, 0x18, 0x3c, 0x93, 0xc9, 0x3a, 0x9a, 0xd8, 0xe0, 0x7c, 0x27, 0x39,
-	0x64, 0xfa, 0xaa, 0xea, 0x70, 0xe8, 0x5e, 0xd5, 0x67, 0x8d, 0x71, 0xaa, 0x1a, 0xe9, 0x35, 0x73,
-	0x26, 0xcf, 0x1d, 0xb4, 0x90, 0x06, 0xdd, 0x02, 0x45, 0xa3, 0x38, 0x0d, 0x5d, 0x47, 0x25, 0xc9,
-	0x5a, 0x22, 0xb4, 0x1e, 0xa6, 0xab, 0x7b, 0x5e, 0x9e, 0xa3, 0xd7, 0xcd, 0xd1, 0x1c, 0x3e, 0x0f,
-	0x6b, 0x5e, 0xbb, 0xea, 0xf1, 0xa3, 0xba, 0xa7, 0x2b, 0xe6, 0x15, 0x2a, 0xe6, 0x75, 0x2b, 0xe6,
-	0x6d, 0xe4, 0xe0, 0x63, 0xe3, 0x36, 0x48, 0xdd, 0x63, 0x17, 0x4d, 0x50, 0xce, 0x3f, 0xa0, 0x4d,
-	0x48, 0x79, 0x75, 0x45, 0xf2, 0x0e, 0x2a, 0x77, 0xcb, 0x11, 0x80, 0xe4, 0x2c, 0x91, 0x80, 0x5f,
-	0x43, 0xe3, 0x91, 0x82, 0xa6, 0x74, 0x9d, 0x95, 0xd1, 0xd5, 0xe9, 0xea, 0x82, 0x57, 0x28, 0x62,
-	0x9a, 0x7a, 0x60, 0x2d, 0xc8, 0x26, 0x9a, 0xd2, 0xd7, 0x07, 0x57, 0x92, 0xa0, 0x99, 0x43, 0xa6,
-	0xa9, 0xc0, 0xa1, 0x00, 0x69, 0xcb, 0x32, 0x19, 0xf4, 0x60, 0xe4, 0xb7, 0x31, 0x74, 0xc5, 0x90,
-	0x08, 0x43, 0x90, 0xc3, 0x7f, 0x95, 0x96, 0x04, 0x91, 0xe4, 0x69, 0x64, 0xb2, 0xd6, 0x71, 0x2a,
-	0xe5, 0x97, 0x4c, 0xd4, 0xdc, 0x51, 0xab, 0xeb, 0xca, 0xf8, 0x16, 0x9a, 0x95, 0xb2, 0xf1, 0xa1,
-	0x88, 0xda, 0x54, 0xc1, 0xfb, 0xd0, 0x71, 0xc7, 0x8c, 0x41, 0x2f, 0xa8, 0x3d, 0x44, 0x89, 0x84,
-	0xb0, 0x25, 0xc0, 0x1d, 0x37, 0x2c, 0x33, 0x19, 0xdf, 0x41, 0xf3, 0x2a, 0x96, 0x9b, 0x71, 0x04,
-	0x89, 0xda, 0x04, 0xa1, 0xb6, 0xa8, 0xa2, 0x6e, 0xc9, 0x78, 0x39, 0xad, 0xc0, 0x6b, 0xa8, 0xdc,
-	0x03, 0xea, 0x90, 0x13, 0xc6, 0xf8, 0x14, 0x9e, 0xb5, 0xd0, 0x54, 0x6f, 0x0b, 0x99, 0x1c, 0x91,
-	0xc5, 0x4c, 0x7e, 0x4b, 0x68, 0x0a, 0x12, 0x7a, 0x10, 0xc3, 0x5e, 0x18, 0xb9, 0xd3, 0x86, 0x5e,
-	0x0e, 0xe0, 0xbb, 0x68, 0xc1, 0x76, 0xce, 0x06, 0xe7, 0x85, 0x3c, 0x67, 0x8c, 0x83, 0x7e, 0x2a,
-	0xbc, 0x82, 0xa6, 0x33, 0x78, 0x67, 0xcb, 0x9d, 0x5d, 0x71, 0x56, 0x47, 0x83, 0x22, 0x84, 0xef,
-	0xa1, 0x97, 0x73, 0x31, 0x91, 0x8a, 0xc6, 0xb1, 0x69, 0xad, 0x9d, 0x2d, 0x77, 0xce, 0x58, 0x0f,
-	0x52, 0xe3, 0x77, 0x51, 0x25, 0x53, 0x6d, 0x27, 0x0a, 0x04, 0x17, 0x91, 0x84, 0x07, 0x54, 0xc2,
-	0xbe, 0x88, 0xdd, 0x2b, 0x86, 0xd4, 0x10, 0x0b, 0xbc, 0x88, 0xc6, 0xb9, 0x60, 0x5f, 0x75, 0xdc,
-	0xb2, 0x31, 0xb5, 0x82, 0xee, 0x61, 0x3d, 0x0e, 0x10, 0x2a, 0x77, 0xde, 0xf6, 0x70, 0x2a, 0x92,
-	0x39, 0x34, 0xa3, 0xdb, 0xa7, 0xdb, 0xbf, 0xe4, 0x17, 0x07, 0xcd, 0x6b, 0x60, 0x53, 0x00, 0x55,
-	0x10, 0xc0, 0x93, 0x16, 0x48, 0x85, 0x3f, 0x2d, 0x74, 0xd4, 0x74, 0xf5, 0xd1, 0x7f, 0x1b, 0xb5,
-	0x20, 0x9b, 0x88, 0xb4, 0x37, 0xaf, 0xa2, 0x52, 0x8b, 0x4b, 0x10, 0x2a, 0xed, 0xf0, 0x54, 0xd2,
-	0xbf, 0x5b, 0x28, 0xa0, 0x26, 0xf7, 0x92, 0xb8, 0x63, 0x1a, 0x73, 0x32, 0xc8, 0x01, 0xf2, 0xc4,
-	0x12, 0xdd, 0xe7, 0xb5, 0xcb, 0x22, 0x5a, 0xfd, 0x67, 0xce, 0xc6, 0xb4, 0xe0, 0x63, 0x10, 0xed,
-	0x28, 0x04, 0xfc, 0x9d, 0x83, 0xc6, 0x76, 0x23, 0xa9, 0xf0, 0x4b, 0xc5, 0x61, 0xcf, 0x46, 0xbb,
-	0xb2, 0x7b, 0x51, 0x2c, 0x74, 0x10, 0x72, 0xe3, 0xeb, 0x3f, 0xff, 0xfe, 0x61, 0xe4, 0x2a, 0x5e,
-	0x34, 0xcf, 0x47, 0x7b, 0x3d, 0xdf, 0xd2, 0x11, 0xc8, 0x6f, 0x46, 0x1c, 0xfc, 0xad, 0x83, 0x46,
-	0x1f, 0xc2, 0x40, 0x36, 0x17, 0x56, 0x13, 0x72, 0xd3, 0x30, 0xb9, 0x8e, 0xaf, 0xf5, 0x63, 0xe2,
-	0x3f, 0xd5, 0xd2, 0x33, 0xfc, 0xa3, 0x83, 0xca, 0x9a, 0x77, 0x50, 0xd0, 0x5d, 0x4e, 0xa1, 0x96,
-	0x86, 0x15, 0x0a, 0x7f, 0x86, 0x26, 0x2d, 0xad, 0xc3, 0x81, 0x74, 0xca, 0xbd, 0xf0, 0xa1, 0x24,
-	0xab, 0xc6, 0x25, 0xc1, 0x2b, 0x43, 0x32, 0xf6, 0x85, 0x76, 0xd9, 0xb4, 0xee, 0xf5, 0xd3, 0x80,
-	0x5f, 0x39, 0xe9, 0x3e, 0x7b, 0x3f, 0x2b, 0x4b, 0xfd, 0x54, 0xd9, 0x2c, 0x9e, 0x2b, 0x1c, 0xd5,
-	0x21, 0xbe, 0x77, 0xd0, 0xec, 0x43, 0x50, 0xf9, 0x1b, 0x89, 0x6f, 0xf4, 0xf1, 0x5c, 0x7c, 0x3f,
-	0x2b, 0x64, 0xb0, 0x41, 0x46, 0xe0, 0x6d, 0x43, 0xe0, 0x4d, 0x72, 0xb7, 0x3f, 0x01, 0xfb, 0x40,
-	0x1a, 0x3f, 0xfb, 0xc1, 0xae, 0xa1, 0x52, 0xb3, 0x1e, 0xee, 0x3b, 0x6b, 0xb8, 0x6d, 0x28, 0x3d,
-	0x82, 0xb8, 0xb9, 0xd9, 0xa0, 0x42, 0x0d, 0x2c, 0xf3, 0x72, 0x11, 0xce, 0xcd, 0x33, 0x12, 0x9e,
-	0x21, 0xb1, 0x8a, 0x6f, 0x0f, 0xab, 0x42, 0x03, 0xe2, 0x66, 0x68, 0xc3, 0x3c, 0x77, 0x50, 0xc9,
-	0x6e, 0x2f, 0x7c, 0xfd, 0x64, 0xc4, 0x9e, 0xad, 0x76, 0x81, 0xa3, 0xf0, 0xaa, 0xe1, 0xb8, 0x44,
-	0xfa, 0xf6, 0xda, 0x7d, 0xb3, 0x3c, 0xf4, 0x68, 0xfe, 0xe4, 0xa0, 0x72, 0x97, 0x42, 0xf7, 0xee,
-	0xe5, 0x91, 0x24, 0x67, 0x93, 0xc4, 0x3f, 0x3b, 0xa8, 0x64, 0x37, 0xea, 0x69, 0x5e, 0x3d, 0x9b,
-	0xf6, 0x02, 0x79, 0xad, 0xdb, 0x1f, 0xb8, 0x32, 0xa4, 0xcd, 0x0d, 0x95, 0x67, 0x79, 0x21, 0x7f,
-	0x75, 0x50, 0xb9, 0x4b, 0x67, 0x70, 0x21, 0xff, 0x2f, 0xc2, 0xde, 0x8b, 0x11, 0xc6, 0x14, 0x95,
-	0xb6, 0x20, 0x06, 0x05, 0x83, 0x46, 0xc0, 0x3d, 0x09, 0x67, 0xcd, 0x7f, 0xdb, 0xee, 0xd8, 0xb5,
-	0x61, 0x3b, 0x56, 0x17, 0xa4, 0x81, 0xca, 0x36, 0x44, 0xa1, 0x1e, 0x2f, 0x1c, 0xec, 0xe6, 0x39,
-	0x82, 0xe1, 0xa7, 0x68, 0xee, 0x63, 0x1a, 0x47, 0xba, 0xb2, 0xf6, 0x3f, 0x27, 0xbe, 0x76, 0x6a,
-	0x93, 0xe4, 0xff, 0x45, 0x87, 0x44, 0xab, 0x9a, 0x68, 0x77, 0xc8, 0xad, 0x61, 0x73, 0xdd, 0x4e,
-	0x43, 0xd9, 0x4a, 0x3e, 0xd8, 0xfe, 0xfd, 0x78, 0xd9, 0xf9, 0xe3, 0x78, 0xd9, 0xf9, 0xeb, 0x78,
-	0xd9, 0xf9, 0xe4, 0xad, 0xf3, 0x7d, 0x23, 0x85, 0xe6, 0x4f, 0x63, 0xe1, 0x6b, 0xe6, 0xa0, 0x64,
-	0x3e, 0x67, 0xde, 0xf8, 0x37, 0x00, 0x00, 0xff, 0xff, 0x38, 0x35, 0xe9, 0x0a, 0xed, 0x0d, 0x00,
-	0x00,
+	// 1129 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x57, 0x4d, 0x6f, 0x1c, 0x45,
+	0x13, 0xd6, 0xf8, 0x63, 0x6d, 0xb7, 0x3f, 0xb2, 0x6e, 0xfb, 0xcd, 0x3b, 0x6c, 0x1c, 0xc7, 0x9a,
+	0x84, 0xc8, 0x58, 0x61, 0x26, 0x5e, 0x84, 0x88, 0x82, 0x40, 0x72, 0x6c, 0x2b, 0xb1, 0xb0, 0x70,
+	0x98, 0xc8, 0x1c, 0x10, 0x08, 0xb5, 0x67, 0x6b, 0x77, 0x27, 0x9e, 0x9d, 0xee, 0x74, 0xf7, 0x0e,
+	0xac, 0xa2, 0x5c, 0x38, 0x21, 0xc1, 0x05, 0x21, 0x24, 0x6e, 0x5c, 0x90, 0x38, 0xf0, 0x07, 0xb8,
+	0x70, 0xe7, 0x88, 0xc4, 0x1f, 0x40, 0x16, 0xbf, 0x03, 0xa1, 0xee, 0x9e, 0x9d, 0x99, 0xf5, 0x7e,
+	0xd8, 0x11, 0xc6, 0xb7, 0xae, 0xa7, 0x6a, 0xab, 0x9e, 0x7a, 0xba, 0xba, 0x7b, 0x07, 0x39, 0x02,
+	0x78, 0x02, 0xdc, 0xe3, 0xc0, 0xa8, 0x08, 0x25, 0xe5, 0x9d, 0xc2, 0xd2, 0x65, 0x9c, 0x4a, 0x8a,
+	0x51, 0x8e, 0x54, 0x96, 0x1b, 0xb4, 0x41, 0x35, 0xec, 0xa9, 0x95, 0x89, 0xa8, 0xac, 0x34, 0x28,
+	0x6d, 0x44, 0xe0, 0x11, 0x16, 0x7a, 0x24, 0x8e, 0xa9, 0x24, 0x32, 0xa4, 0xb1, 0x48, 0xbd, 0xce,
+	0xf1, 0x3d, 0xe1, 0x86, 0x54, 0x7b, 0x03, 0xca, 0xc1, 0x4b, 0x36, 0xbd, 0x06, 0xc4, 0xc0, 0x89,
+	0x84, 0x5a, 0x1a, 0xb3, 0xdf, 0x08, 0x65, 0xb3, 0x7d, 0xe4, 0x06, 0xb4, 0xe5, 0x11, 0xae, 0x4b,
+	0x3c, 0xd5, 0x8b, 0xd7, 0x83, 0x9a, 0x97, 0x54, 0x3d, 0x76, 0xdc, 0x50, 0xbf, 0x17, 0x1e, 0x61,
+	0x2c, 0x0a, 0x03, 0x9d, 0xdf, 0x4b, 0x36, 0x49, 0xc4, 0x9a, 0xa4, 0x3f, 0xdb, 0xee, 0x19, 0xd9,
+	0x74, 0x43, 0x67, 0x36, 0xee, 0x74, 0xd0, 0xbc, 0x0f, 0x8c, 0x6e, 0x31, 0x26, 0x3e, 0x68, 0x03,
+	0xef, 0x60, 0x8c, 0x26, 0x54, 0x90, 0x6d, 0xad, 0x59, 0xeb, 0x33, 0xbe, 0x5e, 0xe3, 0x0a, 0x9a,
+	0xe6, 0x90, 0x84, 0x22, 0xa4, 0xb1, 0x3d, 0xa6, 0xf1, 0xcc, 0xc6, 0x36, 0x9a, 0x22, 0x8c, 0xbd,
+	0x4f, 0x5a, 0x60, 0x8f, 0x6b, 0x57, 0xd7, 0xc4, 0xab, 0x08, 0x11, 0xc6, 0x1e, 0x73, 0xfa, 0x14,
+	0x02, 0x69, 0x4f, 0x68, 0x67, 0x01, 0x71, 0x36, 0xd1, 0xd4, 0x16, 0x63, 0x7b, 0x71, 0x9d, 0xaa,
+	0xa2, 0xb2, 0xc3, 0xa0, 0x5b, 0x54, 0xad, 0x15, 0xc6, 0x88, 0x6c, 0xa6, 0x05, 0xf5, 0xda, 0xf9,
+	0xc5, 0x42, 0x4b, 0x29, 0xdd, 0x1d, 0x90, 0x24, 0x8c, 0x52, 0xd2, 0x0d, 0x54, 0x12, 0xb4, 0xcd,
+	0x03, 0x93, 0x61, 0xb6, 0x7a, 0xe0, 0xe6, 0xea, 0xb8, 0x5d, 0x75, 0xf4, 0xe2, 0xd3, 0xa0, 0xe6,
+	0x26, 0x55, 0x97, 0x1d, 0x37, 0x5c, 0xa5, 0xb5, 0x5b, 0xd0, 0xda, 0xed, 0x6a, 0xed, 0x6e, 0xe5,
+	0xe0, 0x13, 0x9d, 0xd6, 0x4f, 0xd3, 0x17, 0xbb, 0x1d, 0x1b, 0xd5, 0xed, 0x78, 0x5f, 0xb7, 0xef,
+	0xa0, 0x72, 0x57, 0x68, 0x1f, 0x04, 0xa3, 0xb1, 0x00, 0xfc, 0x1a, 0x9a, 0x0c, 0x25, 0xb4, 0x84,
+	0x6d, 0xad, 0x8d, 0xaf, 0xcf, 0x56, 0x97, 0xdc, 0xc2, 0xf6, 0xa4, 0xd2, 0xf8, 0x26, 0xc2, 0xd9,
+	0x46, 0x33, 0xea, 0xe7, 0xc3, 0xf7, 0xc8, 0x41, 0x73, 0x75, 0xaa, 0xa8, 0x42, 0x9d, 0x83, 0x30,
+	0xb2, 0x4d, 0xfb, 0x3d, 0x98, 0xf3, 0xeb, 0x04, 0xba, 0xa2, 0x49, 0x04, 0x01, 0x88, 0xd1, 0xfb,
+	0xdd, 0x16, 0xc0, 0xe3, 0xbc, 0xcd, 0xcc, 0x56, 0x3e, 0x46, 0x84, 0xf8, 0x8c, 0xf2, 0x5a, 0xda,
+	0x65, 0x66, 0xe3, 0x5b, 0x68, 0x5e, 0x88, 0xe6, 0x63, 0x1e, 0x26, 0x44, 0xc2, 0x7b, 0xd0, 0x49,
+	0x37, 0xbd, 0x17, 0x54, 0x19, 0xc2, 0x58, 0x40, 0xd0, 0xe6, 0x60, 0x4f, 0x6a, 0x96, 0x99, 0x8d,
+	0xef, 0xa0, 0x45, 0x19, 0x89, 0xed, 0x28, 0x84, 0x58, 0x6e, 0x03, 0x97, 0x3b, 0x44, 0x12, 0xbb,
+	0xa4, 0xb3, 0xf4, 0x3b, 0xf0, 0x06, 0x2a, 0xf7, 0x80, 0xaa, 0xe4, 0x94, 0x0e, 0xee, 0xc3, 0xb3,
+	0x11, 0x9b, 0xe9, 0x1d, 0x31, 0xdd, 0x23, 0x32, 0x98, 0xee, 0x6f, 0x05, 0xcd, 0x40, 0x4c, 0x8e,
+	0x22, 0x38, 0x08, 0x42, 0x7b, 0x56, 0xd3, 0xcb, 0x01, 0x7c, 0x17, 0x2d, 0x99, 0xc9, 0xda, 0x52,
+	0x3b, 0x9b, 0xf5, 0x39, 0xa7, 0x13, 0x0c, 0x72, 0xe1, 0x35, 0x34, 0x9b, 0xc1, 0x7b, 0x3b, 0xf6,
+	0xfc, 0x9a, 0xb5, 0x3e, 0xee, 0x17, 0x21, 0x7c, 0x0f, 0xfd, 0x3f, 0x37, 0x63, 0x21, 0x49, 0x14,
+	0xe9, 0xd1, 0xdb, 0xdb, 0xb1, 0x17, 0x74, 0xf4, 0x30, 0x37, 0x7e, 0x17, 0x55, 0x32, 0xd7, 0x6e,
+	0x2c, 0x81, 0x33, 0x1e, 0x0a, 0x78, 0x40, 0x04, 0x1c, 0xf2, 0xc8, 0xbe, 0xa2, 0x49, 0x8d, 0x88,
+	0xc0, 0xcb, 0x68, 0x92, 0x71, 0xfa, 0x79, 0xc7, 0x2e, 0xeb, 0x50, 0x63, 0xa8, 0x19, 0x67, 0xe9,
+	0x18, 0x2f, 0x9a, 0x19, 0x4f, 0x4d, 0x67, 0x01, 0xcd, 0xa9, 0xf1, 0xe9, 0xce, 0xaf, 0xf3, 0x93,
+	0x85, 0x16, 0x15, 0xb0, 0xcd, 0x81, 0x48, 0xf0, 0xe1, 0x59, 0x1b, 0x84, 0xc4, 0x1f, 0x17, 0x26,
+	0x6a, 0xb6, 0xfa, 0xe8, 0xdf, 0x1d, 0x45, 0x3f, 0x3b, 0x11, 0xe9, 0x6c, 0x5e, 0x45, 0xa5, 0x36,
+	0x13, 0xc0, 0x65, 0x3a, 0xe1, 0xa9, 0xa5, 0xf6, 0x2d, 0xe0, 0x50, 0x13, 0x07, 0x71, 0xd4, 0xd1,
+	0x83, 0x39, 0xed, 0xe7, 0x80, 0xf3, 0xcc, 0x10, 0x3d, 0x64, 0xb5, 0xcb, 0x22, 0x5a, 0xfd, 0x7b,
+	0xc1, 0xd4, 0x34, 0xe0, 0x13, 0xe0, 0x49, 0x18, 0x00, 0xfe, 0xda, 0x42, 0x13, 0xfb, 0xa1, 0x90,
+	0xf8, 0x7f, 0xc5, 0xc3, 0x9e, 0x1d, 0xed, 0xca, 0xfe, 0x45, 0xb1, 0x50, 0x45, 0x9c, 0x1b, 0x5f,
+	0xfc, 0xf1, 0xd7, 0xb7, 0x63, 0x57, 0xf1, 0xb2, 0x7e, 0x98, 0x92, 0xcd, 0xfc, 0xfe, 0x0f, 0x41,
+	0x7c, 0x39, 0x66, 0xe1, 0xaf, 0x2c, 0x34, 0xfe, 0x10, 0x86, 0xb2, 0xb9, 0x30, 0x4d, 0x9c, 0x9b,
+	0x9a, 0xc9, 0x75, 0x7c, 0x6d, 0x10, 0x13, 0xef, 0xb9, 0xb2, 0x5e, 0xe0, 0xef, 0x2c, 0x54, 0x56,
+	0xbc, 0xfd, 0x82, 0xef, 0x72, 0x84, 0x5a, 0x19, 0x25, 0x14, 0xfe, 0x04, 0x4d, 0x1b, 0x5a, 0xf5,
+	0xa1, 0x74, 0xca, 0xbd, 0x70, 0x5d, 0x38, 0xeb, 0x3a, 0xa5, 0x83, 0xd7, 0x46, 0x74, 0xec, 0x71,
+	0x95, 0xb2, 0x65, 0xd2, 0xab, 0xa7, 0x01, 0xbf, 0x72, 0x3a, 0x7d, 0xf6, 0x32, 0x57, 0x56, 0x06,
+	0xb9, 0xb2, 0xb3, 0x78, 0xae, 0x72, 0x44, 0x95, 0xf8, 0xc6, 0x42, 0xf3, 0x0f, 0x41, 0xe6, 0x6f,
+	0x28, 0xbe, 0x31, 0x20, 0x73, 0xf1, 0x7d, 0xad, 0x38, 0xc3, 0x03, 0x32, 0x02, 0x6f, 0x6b, 0x02,
+	0x6f, 0x3a, 0x77, 0x07, 0x13, 0x30, 0x0f, 0xa8, 0xce, 0x73, 0xe8, 0xef, 0x6b, 0x2a, 0x35, 0x93,
+	0xe1, 0xbe, 0xb5, 0x81, 0x13, 0x4d, 0xe9, 0x11, 0x44, 0xad, 0xed, 0x26, 0xe1, 0x72, 0xa8, 0xcc,
+	0xab, 0x45, 0x38, 0x0f, 0xcf, 0x48, 0xb8, 0x9a, 0xc4, 0x3a, 0xbe, 0x3d, 0x4a, 0x85, 0x26, 0x44,
+	0xad, 0xc0, 0x94, 0xf9, 0xde, 0x42, 0x25, 0x73, 0x7b, 0xe1, 0xeb, 0xa7, 0x2b, 0xf6, 0xdc, 0x6a,
+	0x17, 0x78, 0x14, 0x5e, 0xd5, 0x1c, 0x57, 0x9c, 0x81, 0xb3, 0x76, 0x5f, 0x5f, 0x1e, 0xea, 0x68,
+	0xfe, 0x60, 0xa1, 0x72, 0x97, 0x42, 0xf7, 0xb7, 0x97, 0x47, 0xd2, 0x39, 0x9b, 0x24, 0xfe, 0xd1,
+	0x42, 0x25, 0x73, 0xa3, 0xf6, 0xf3, 0xea, 0xb9, 0x69, 0x2f, 0x90, 0xd7, 0xa6, 0xd9, 0xe0, 0xca,
+	0x88, 0x31, 0xd7, 0x54, 0x5e, 0xe4, 0x42, 0xfe, 0x6c, 0xa1, 0x72, 0x97, 0xce, 0x70, 0x21, 0xff,
+	0x2b, 0xc2, 0xee, 0xcb, 0x11, 0xc6, 0x04, 0x95, 0x76, 0x20, 0x02, 0x09, 0xc3, 0x8e, 0x80, 0x7d,
+	0x1a, 0xce, 0x86, 0xff, 0xb6, 0xb9, 0x63, 0x37, 0x46, 0xdd, 0xb1, 0x4a, 0x90, 0x26, 0x2a, 0x9b,
+	0x12, 0x05, 0x3d, 0x5e, 0xba, 0xd8, 0xcd, 0x73, 0x14, 0xc3, 0xcf, 0xd1, 0xc2, 0x87, 0x24, 0x0a,
+	0x95, 0xb2, 0xe6, 0x3f, 0x27, 0xbe, 0xd6, 0x77, 0x93, 0xe4, 0xff, 0x45, 0x47, 0x54, 0xab, 0xea,
+	0x6a, 0x77, 0x9c, 0x5b, 0xa3, 0xce, 0x75, 0x92, 0x96, 0x32, 0x4a, 0x3e, 0xd8, 0xfd, 0xed, 0x64,
+	0xd5, 0xfa, 0xfd, 0x64, 0xd5, 0xfa, 0xf3, 0x64, 0xd5, 0xfa, 0xe8, 0xad, 0xf3, 0x7d, 0x7d, 0x05,
+	0xfa, 0x4f, 0x63, 0xe1, 0x3b, 0xe9, 0xa8, 0xa4, 0x3f, 0x94, 0xde, 0xf8, 0x27, 0x00, 0x00, 0xff,
+	0xff, 0x52, 0x69, 0xb3, 0xbe, 0x47, 0x0e, 0x00, 0x00,
 }
 
 // Reference imports to suppress errors if they are not otherwise used.
@@ -1338,6 +1363,20 @@ func (m *RepoAppsQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i -= len(m.XXX_unrecognized)
 		copy(dAtA[i:], m.XXX_unrecognized)
 	}
+	if len(m.AppProject) > 0 {
+		i -= len(m.AppProject)
+		copy(dAtA[i:], m.AppProject)
+		i = encodeVarintRepository(dAtA, i, uint64(len(m.AppProject)))
+		i--
+		dAtA[i] = 0x22
+	}
+	if len(m.AppName) > 0 {
+		i -= len(m.AppName)
+		copy(dAtA[i:], m.AppName)
+		i = encodeVarintRepository(dAtA, i, uint64(len(m.AppName)))
+		i--
+		dAtA[i] = 0x1a
+	}
 	if len(m.Revision) > 0 {
 		i -= len(m.Revision)
 		copy(dAtA[i:], m.Revision)
@@ -1420,6 +1459,13 @@ func (m *RepoAppDetailsQuery) MarshalToSizedBuffer(dAtA []byte) (int, error) {
 		i -= len(m.XXX_unrecognized)
 		copy(dAtA[i:], m.XXX_unrecognized)
 	}
+	if len(m.AppProject) > 0 {
+		i -= len(m.AppProject)
+		copy(dAtA[i:], m.AppProject)
+		i = encodeVarintRepository(dAtA, i, uint64(len(m.AppProject)))
+		i--
+		dAtA[i] = 0x1a
+	}
 	if len(m.AppName) > 0 {
 		i -= len(m.AppName)
 		copy(dAtA[i:], m.AppName)
@@ -1822,6 +1868,14 @@ func (m *RepoAppsQuery) Size() (n int) {
 	if l > 0 {
 		n += 1 + l + sovRepository(uint64(l))
 	}
+	l = len(m.AppName)
+	if l > 0 {
+		n += 1 + l + sovRepository(uint64(l))
+	}
+	l = len(m.AppProject)
+	if l > 0 {
+		n += 1 + l + sovRepository(uint64(l))
+	}
 	if m.XXX_unrecognized != nil {
 		n += len(m.XXX_unrecognized)
 	}
@@ -1862,6 +1916,10 @@ func (m *RepoAppDetailsQuery) Size() (n int) {
 	if l > 0 {
 		n += 1 + l + sovRepository(uint64(l))
 	}
+	l = len(m.AppProject)
+	if l > 0 {
+		n += 1 + l + sovRepository(uint64(l))
+	}
 	if m.XXX_unrecognized != nil {
 		n += len(m.XXX_unrecognized)
 	}
@@ -2126,6 +2184,70 @@ func (m *RepoAppsQuery) Unmarshal(dAtA []byte) error {
 			}
 			m.Revision = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field AppName", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowRepository
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthRepository
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthRepository
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.AppName = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 4:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field AppProject", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowRepository
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthRepository
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthRepository
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.AppProject = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipRepository(dAtA[iNdEx:])
@@ -2360,6 +2482,38 @@ func (m *RepoAppDetailsQuery) Unmarshal(dAtA []byte) error {
 			}
 			m.AppName = string(dAtA[iNdEx:postIndex])
 			iNdEx = postIndex
+		case 3:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field AppProject", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowRepository
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthRepository
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthRepository
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.AppProject = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
 		default:
 			iNdEx = preIndex
 			skippy, err := skipRepository(dAtA[iNdEx:])
diff --git a/pkg/apis/application/v1alpha1/repository_types.go b/pkg/apis/application/v1alpha1/repository_types.go
index dc54c604b..862627f21 100644
--- a/pkg/apis/application/v1alpha1/repository_types.go
+++ b/pkg/apis/application/v1alpha1/repository_types.go
@@ -166,18 +166,18 @@ func (repo *Repository) CopyCredentialsFrom(source *RepoCreds) {
 }
 
 // GetGitCreds returns the credentials from a repository configuration used to authenticate at a Git repository
-func (repo *Repository) GetGitCreds() git.Creds {
+func (repo *Repository) GetGitCreds(store git.CredsStore) git.Creds {
 	if repo == nil {
 		return git.NopCreds{}
 	}
 	if repo.Password != "" {
-		return git.NewHTTPSCreds(repo.Username, repo.Password, repo.TLSClientCertData, repo.TLSClientCertKey, repo.IsInsecure(), repo.Proxy)
+		return git.NewHTTPSCreds(repo.Username, repo.Password, repo.TLSClientCertData, repo.TLSClientCertKey, repo.IsInsecure(), repo.Proxy, store)
 	}
 	if repo.SSHPrivateKey != "" {
-		return git.NewSSHCreds(repo.SSHPrivateKey, getCAPath(repo.Repo), repo.IsInsecure())
+		return git.NewSSHCreds(repo.SSHPrivateKey, getCAPath(repo.Repo), repo.IsInsecure(), store)
 	}
 	if repo.GithubAppPrivateKey != "" && repo.GithubAppId != 0 && repo.GithubAppInstallationId != 0 {
-		return git.NewGitHubAppCreds(repo.GithubAppId, repo.GithubAppInstallationId, repo.GithubAppPrivateKey, repo.GitHubAppEnterpriseBaseURL, repo.Repo, repo.TLSClientCertData, repo.TLSClientCertKey, repo.IsInsecure())
+		return git.NewGitHubAppCreds(repo.GithubAppId, repo.GithubAppInstallationId, repo.GithubAppPrivateKey, repo.GitHubAppEnterpriseBaseURL, repo.Repo, repo.TLSClientCertData, repo.TLSClientCertKey, repo.IsInsecure(), store)
 	}
 	return git.NopCreds{}
 }
diff --git a/reposerver/askpass/askpass.pb.go b/reposerver/askpass/askpass.pb.go
new file mode 100644
index 000000000..d1d2a4612
--- /dev/null
+++ b/reposerver/askpass/askpass.pb.go
@@ -0,0 +1,646 @@
+// Code generated by protoc-gen-gogo. DO NOT EDIT.
+// source: reposerver/askpass/askpass.proto
+
+package askpass
+
+import (
+	context "context"
+	fmt "fmt"
+	proto "github.com/gogo/protobuf/proto"
+	grpc "google.golang.org/grpc"
+	codes "google.golang.org/grpc/codes"
+	status "google.golang.org/grpc/status"
+	io "io"
+	math "math"
+	math_bits "math/bits"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package
+
+type CredentialsRequest struct {
+	Nonce                string   `protobuf:"bytes,1,opt,name=nonce,proto3" json:"nonce,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *CredentialsRequest) Reset()         { *m = CredentialsRequest{} }
+func (m *CredentialsRequest) String() string { return proto.CompactTextString(m) }
+func (*CredentialsRequest) ProtoMessage()    {}
+func (*CredentialsRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_099f282cab154dba, []int{0}
+}
+func (m *CredentialsRequest) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *CredentialsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_CredentialsRequest.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *CredentialsRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_CredentialsRequest.Merge(m, src)
+}
+func (m *CredentialsRequest) XXX_Size() int {
+	return m.Size()
+}
+func (m *CredentialsRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_CredentialsRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_CredentialsRequest proto.InternalMessageInfo
+
+func (m *CredentialsRequest) GetNonce() string {
+	if m != nil {
+		return m.Nonce
+	}
+	return ""
+}
+
+type CredentialsResponse struct {
+	Username             string   `protobuf:"bytes,1,opt,name=username,proto3" json:"username,omitempty"`
+	Password             string   `protobuf:"bytes,2,opt,name=password,proto3" json:"password,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *CredentialsResponse) Reset()         { *m = CredentialsResponse{} }
+func (m *CredentialsResponse) String() string { return proto.CompactTextString(m) }
+func (*CredentialsResponse) ProtoMessage()    {}
+func (*CredentialsResponse) Descriptor() ([]byte, []int) {
+	return fileDescriptor_099f282cab154dba, []int{1}
+}
+func (m *CredentialsResponse) XXX_Unmarshal(b []byte) error {
+	return m.Unmarshal(b)
+}
+func (m *CredentialsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	if deterministic {
+		return xxx_messageInfo_CredentialsResponse.Marshal(b, m, deterministic)
+	} else {
+		b = b[:cap(b)]
+		n, err := m.MarshalToSizedBuffer(b)
+		if err != nil {
+			return nil, err
+		}
+		return b[:n], nil
+	}
+}
+func (m *CredentialsResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_CredentialsResponse.Merge(m, src)
+}
+func (m *CredentialsResponse) XXX_Size() int {
+	return m.Size()
+}
+func (m *CredentialsResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_CredentialsResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_CredentialsResponse proto.InternalMessageInfo
+
+func (m *CredentialsResponse) GetUsername() string {
+	if m != nil {
+		return m.Username
+	}
+	return ""
+}
+
+func (m *CredentialsResponse) GetPassword() string {
+	if m != nil {
+		return m.Password
+	}
+	return ""
+}
+
+func init() {
+	proto.RegisterType((*CredentialsRequest)(nil), "askpass.CredentialsRequest")
+	proto.RegisterType((*CredentialsResponse)(nil), "askpass.CredentialsResponse")
+}
+
+func init() { proto.RegisterFile("reposerver/askpass/askpass.proto", fileDescriptor_099f282cab154dba) }
+
+var fileDescriptor_099f282cab154dba = []byte{
+	// 231 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x28, 0x4a, 0x2d, 0xc8,
+	0x2f, 0x4e, 0x2d, 0x2a, 0x4b, 0x2d, 0xd2, 0x4f, 0x2c, 0xce, 0x2e, 0x48, 0x2c, 0x2e, 0x86, 0xd1,
+	0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0xec, 0x50, 0xae, 0x92, 0x16, 0x97, 0x90, 0x73, 0x51,
+	0x6a, 0x4a, 0x6a, 0x5e, 0x49, 0x66, 0x62, 0x4e, 0x71, 0x50, 0x6a, 0x61, 0x69, 0x6a, 0x71, 0x89,
+	0x90, 0x08, 0x17, 0x6b, 0x5e, 0x7e, 0x5e, 0x72, 0xaa, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x67, 0x10,
+	0x84, 0xa3, 0xe4, 0xcb, 0x25, 0x8c, 0xa2, 0xb6, 0xb8, 0x20, 0x3f, 0xaf, 0x38, 0x55, 0x48, 0x8a,
+	0x8b, 0xa3, 0xb4, 0x38, 0xb5, 0x28, 0x2f, 0x31, 0x17, 0xa6, 0x1e, 0xce, 0x07, 0xc9, 0x81, 0xac,
+	0x29, 0xcf, 0x2f, 0x4a, 0x91, 0x60, 0x82, 0xc8, 0xc1, 0xf8, 0x46, 0xf1, 0x5c, 0x7c, 0x8e, 0xc5,
+	0xd9, 0x01, 0x89, 0xc5, 0xc5, 0xc1, 0xa9, 0x45, 0x65, 0x99, 0xc9, 0xa9, 0x42, 0xbe, 0x5c, 0x7c,
+	0xee, 0xa9, 0x25, 0x48, 0x76, 0x08, 0x49, 0xeb, 0xc1, 0xdc, 0x8d, 0xe9, 0x4a, 0x29, 0x19, 0xec,
+	0x92, 0x10, 0x67, 0x29, 0x31, 0x38, 0xd9, 0x9f, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3,
+	0x83, 0x47, 0x72, 0x8c, 0x51, 0x86, 0xe9, 0x99, 0x25, 0x19, 0xa5, 0x49, 0x7a, 0xc9, 0xf9, 0xb9,
+	0xfa, 0x89, 0x45, 0xe9, 0xf9, 0x05, 0x45, 0xf9, 0x59, 0x60, 0x86, 0x6e, 0x72, 0x8a, 0x7e, 0x99,
+	0x91, 0x3e, 0x66, 0x98, 0x25, 0xb1, 0x81, 0x03, 0xcb, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x5a,
+	0x1e, 0xa9, 0xaf, 0x50, 0x01, 0x00, 0x00,
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion4
+
+// AskPassServiceClient is the client API for AskPassService service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type AskPassServiceClient interface {
+	GetCredentials(ctx context.Context, in *CredentialsRequest, opts ...grpc.CallOption) (*CredentialsResponse, error)
+}
+
+type askPassServiceClient struct {
+	cc *grpc.ClientConn
+}
+
+func NewAskPassServiceClient(cc *grpc.ClientConn) AskPassServiceClient {
+	return &askPassServiceClient{cc}
+}
+
+func (c *askPassServiceClient) GetCredentials(ctx context.Context, in *CredentialsRequest, opts ...grpc.CallOption) (*CredentialsResponse, error) {
+	out := new(CredentialsResponse)
+	err := c.cc.Invoke(ctx, "/askpass.AskPassService/GetCredentials", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+// AskPassServiceServer is the server API for AskPassService service.
+type AskPassServiceServer interface {
+	GetCredentials(context.Context, *CredentialsRequest) (*CredentialsResponse, error)
+}
+
+// UnimplementedAskPassServiceServer can be embedded to have forward compatible implementations.
+type UnimplementedAskPassServiceServer struct {
+}
+
+func (*UnimplementedAskPassServiceServer) GetCredentials(ctx context.Context, req *CredentialsRequest) (*CredentialsResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method GetCredentials not implemented")
+}
+
+func RegisterAskPassServiceServer(s *grpc.Server, srv AskPassServiceServer) {
+	s.RegisterService(&_AskPassService_serviceDesc, srv)
+}
+
+func _AskPassService_GetCredentials_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(CredentialsRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(AskPassServiceServer).GetCredentials(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/askpass.AskPassService/GetCredentials",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(AskPassServiceServer).GetCredentials(ctx, req.(*CredentialsRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+var _AskPassService_serviceDesc = grpc.ServiceDesc{
+	ServiceName: "askpass.AskPassService",
+	HandlerType: (*AskPassServiceServer)(nil),
+	Methods: []grpc.MethodDesc{
+		{
+			MethodName: "GetCredentials",
+			Handler:    _AskPassService_GetCredentials_Handler,
+		},
+	},
+	Streams:  []grpc.StreamDesc{},
+	Metadata: "reposerver/askpass/askpass.proto",
+}
+
+func (m *CredentialsRequest) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *CredentialsRequest) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *CredentialsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.XXX_unrecognized != nil {
+		i -= len(m.XXX_unrecognized)
+		copy(dAtA[i:], m.XXX_unrecognized)
+	}
+	if len(m.Nonce) > 0 {
+		i -= len(m.Nonce)
+		copy(dAtA[i:], m.Nonce)
+		i = encodeVarintAskpass(dAtA, i, uint64(len(m.Nonce)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func (m *CredentialsResponse) Marshal() (dAtA []byte, err error) {
+	size := m.Size()
+	dAtA = make([]byte, size)
+	n, err := m.MarshalToSizedBuffer(dAtA[:size])
+	if err != nil {
+		return nil, err
+	}
+	return dAtA[:n], nil
+}
+
+func (m *CredentialsResponse) MarshalTo(dAtA []byte) (int, error) {
+	size := m.Size()
+	return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *CredentialsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+	i := len(dAtA)
+	_ = i
+	var l int
+	_ = l
+	if m.XXX_unrecognized != nil {
+		i -= len(m.XXX_unrecognized)
+		copy(dAtA[i:], m.XXX_unrecognized)
+	}
+	if len(m.Password) > 0 {
+		i -= len(m.Password)
+		copy(dAtA[i:], m.Password)
+		i = encodeVarintAskpass(dAtA, i, uint64(len(m.Password)))
+		i--
+		dAtA[i] = 0x12
+	}
+	if len(m.Username) > 0 {
+		i -= len(m.Username)
+		copy(dAtA[i:], m.Username)
+		i = encodeVarintAskpass(dAtA, i, uint64(len(m.Username)))
+		i--
+		dAtA[i] = 0xa
+	}
+	return len(dAtA) - i, nil
+}
+
+func encodeVarintAskpass(dAtA []byte, offset int, v uint64) int {
+	offset -= sovAskpass(v)
+	base := offset
+	for v >= 1<<7 {
+		dAtA[offset] = uint8(v&0x7f | 0x80)
+		v >>= 7
+		offset++
+	}
+	dAtA[offset] = uint8(v)
+	return base
+}
+func (m *CredentialsRequest) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Nonce)
+	if l > 0 {
+		n += 1 + l + sovAskpass(uint64(l))
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
+}
+
+func (m *CredentialsResponse) Size() (n int) {
+	if m == nil {
+		return 0
+	}
+	var l int
+	_ = l
+	l = len(m.Username)
+	if l > 0 {
+		n += 1 + l + sovAskpass(uint64(l))
+	}
+	l = len(m.Password)
+	if l > 0 {
+		n += 1 + l + sovAskpass(uint64(l))
+	}
+	if m.XXX_unrecognized != nil {
+		n += len(m.XXX_unrecognized)
+	}
+	return n
+}
+
+func sovAskpass(x uint64) (n int) {
+	return (math_bits.Len64(x|1) + 6) / 7
+}
+func sozAskpass(x uint64) (n int) {
+	return sovAskpass(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *CredentialsRequest) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowAskpass
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: CredentialsRequest: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: CredentialsRequest: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Nonce", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowAskpass
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthAskpass
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthAskpass
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Nonce = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipAskpass(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthAskpass
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func (m *CredentialsResponse) Unmarshal(dAtA []byte) error {
+	l := len(dAtA)
+	iNdEx := 0
+	for iNdEx < l {
+		preIndex := iNdEx
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return ErrIntOverflowAskpass
+			}
+			if iNdEx >= l {
+				return io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= uint64(b&0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		fieldNum := int32(wire >> 3)
+		wireType := int(wire & 0x7)
+		if wireType == 4 {
+			return fmt.Errorf("proto: CredentialsResponse: wiretype end group for non-group")
+		}
+		if fieldNum <= 0 {
+			return fmt.Errorf("proto: CredentialsResponse: illegal tag %d (wire type %d)", fieldNum, wire)
+		}
+		switch fieldNum {
+		case 1:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Username", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowAskpass
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthAskpass
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthAskpass
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Username = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		case 2:
+			if wireType != 2 {
+				return fmt.Errorf("proto: wrong wireType = %d for field Password", wireType)
+			}
+			var stringLen uint64
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return ErrIntOverflowAskpass
+				}
+				if iNdEx >= l {
+					return io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				stringLen |= uint64(b&0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			intStringLen := int(stringLen)
+			if intStringLen < 0 {
+				return ErrInvalidLengthAskpass
+			}
+			postIndex := iNdEx + intStringLen
+			if postIndex < 0 {
+				return ErrInvalidLengthAskpass
+			}
+			if postIndex > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.Password = string(dAtA[iNdEx:postIndex])
+			iNdEx = postIndex
+		default:
+			iNdEx = preIndex
+			skippy, err := skipAskpass(dAtA[iNdEx:])
+			if err != nil {
+				return err
+			}
+			if (skippy < 0) || (iNdEx+skippy) < 0 {
+				return ErrInvalidLengthAskpass
+			}
+			if (iNdEx + skippy) > l {
+				return io.ErrUnexpectedEOF
+			}
+			m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
+			iNdEx += skippy
+		}
+	}
+
+	if iNdEx > l {
+		return io.ErrUnexpectedEOF
+	}
+	return nil
+}
+func skipAskpass(dAtA []byte) (n int, err error) {
+	l := len(dAtA)
+	iNdEx := 0
+	depth := 0
+	for iNdEx < l {
+		var wire uint64
+		for shift := uint(0); ; shift += 7 {
+			if shift >= 64 {
+				return 0, ErrIntOverflowAskpass
+			}
+			if iNdEx >= l {
+				return 0, io.ErrUnexpectedEOF
+			}
+			b := dAtA[iNdEx]
+			iNdEx++
+			wire |= (uint64(b) & 0x7F) << shift
+			if b < 0x80 {
+				break
+			}
+		}
+		wireType := int(wire & 0x7)
+		switch wireType {
+		case 0:
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowAskpass
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				iNdEx++
+				if dAtA[iNdEx-1] < 0x80 {
+					break
+				}
+			}
+		case 1:
+			iNdEx += 8
+		case 2:
+			var length int
+			for shift := uint(0); ; shift += 7 {
+				if shift >= 64 {
+					return 0, ErrIntOverflowAskpass
+				}
+				if iNdEx >= l {
+					return 0, io.ErrUnexpectedEOF
+				}
+				b := dAtA[iNdEx]
+				iNdEx++
+				length |= (int(b) & 0x7F) << shift
+				if b < 0x80 {
+					break
+				}
+			}
+			if length < 0 {
+				return 0, ErrInvalidLengthAskpass
+			}
+			iNdEx += length
+		case 3:
+			depth++
+		case 4:
+			if depth == 0 {
+				return 0, ErrUnexpectedEndOfGroupAskpass
+			}
+			depth--
+		case 5:
+			iNdEx += 4
+		default:
+			return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+		}
+		if iNdEx < 0 {
+			return 0, ErrInvalidLengthAskpass
+		}
+		if depth == 0 {
+			return iNdEx, nil
+		}
+	}
+	return 0, io.ErrUnexpectedEOF
+}
+
+var (
+	ErrInvalidLengthAskpass        = fmt.Errorf("proto: negative length found during unmarshaling")
+	ErrIntOverflowAskpass          = fmt.Errorf("proto: integer overflow")
+	ErrUnexpectedEndOfGroupAskpass = fmt.Errorf("proto: unexpected end of group")
+)
diff --git a/reposerver/askpass/askpass.proto b/reposerver/askpass/askpass.proto
new file mode 100644
index 000000000..4547edc3a
--- /dev/null
+++ b/reposerver/askpass/askpass.proto
@@ -0,0 +1,20 @@
+syntax = "proto3";
+option go_package = "github.com/argoproj/argo-cd/v2/reposerver/askpass";
+
+package askpass;
+
+message CredentialsRequest {
+  string nonce = 1;
+}
+
+message CredentialsResponse {
+  string username = 1;
+  string password = 2;
+}
+
+
+// AskPassService
+service AskPassService {
+  rpc GetCredentials(CredentialsRequest) returns (CredentialsResponse) {
+  }
+}
diff --git a/reposerver/askpass/common.go b/reposerver/askpass/common.go
new file mode 100644
index 000000000..3f30cc0f2
--- /dev/null
+++ b/reposerver/askpass/common.go
@@ -0,0 +1,10 @@
+package askpass
+
+var (
+	SocketPath = "/tmp/reposerver-ask-pass.sock"
+)
+
+type Creds struct {
+	Username string
+	Password string
+}
diff --git a/reposerver/askpass/server.go b/reposerver/askpass/server.go
new file mode 100644
index 000000000..c34e3c332
--- /dev/null
+++ b/reposerver/askpass/server.go
@@ -0,0 +1,90 @@
+package askpass
+
+import (
+	"context"
+	"net"
+	"os"
+	"sync"
+
+	"github.com/google/uuid"
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+
+	"github.com/argoproj/argo-cd/v2/util/git"
+	"github.com/argoproj/argo-cd/v2/util/io"
+)
+
+type Server interface {
+	git.CredsStore
+	AskPassServiceServer
+	Run(path string) error
+}
+
+type server struct {
+	lock  sync.Mutex
+	creds map[string]Creds
+}
+
+// NewServer returns a new server
+func NewServer() *server {
+	return &server{
+		creds: make(map[string]Creds),
+	}
+}
+
+func (s *server) GetCredentials(_ context.Context, q *CredentialsRequest) (*CredentialsResponse, error) {
+	if q.Nonce == "" {
+		return nil, status.Errorf(codes.InvalidArgument, "missing nonce")
+	}
+	creds, ok := s.getCreds(q.Nonce)
+	if !ok {
+		return nil, status.Errorf(codes.NotFound, "unknown nonce")
+	}
+	return &CredentialsResponse{Username: creds.Username, Password: creds.Password}, nil
+}
+
+func (s *server) Start(path string) (io.Closer, error) {
+	_ = os.Remove(path)
+	listener, err := net.Listen("unix", path)
+	if err != nil {
+		return nil, err
+	}
+	server := grpc.NewServer()
+	RegisterAskPassServiceServer(server, s)
+	go func() {
+		_ = server.Serve(listener)
+	}()
+	return io.NewCloser(listener.Close), nil
+}
+
+func (s *server) Run(path string) error {
+	_, err := s.Start(path)
+	return err
+}
+
+// Add adds a new credential to the server and returns associated id
+func (s *server) Add(username string, password string) string {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+	id := uuid.New().String()
+	s.creds[id] = Creds{
+		Username: username,
+		Password: password,
+	}
+	return id
+}
+
+// Remove removes the credential with the given id
+func (s *server) Remove(id string) {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+	delete(s.creds, id)
+}
+
+func (s *server) getCreds(id string) (*Creds, bool) {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+	creds, ok := s.creds[id]
+	return &creds, ok
+}
diff --git a/reposerver/repository/repository.go b/reposerver/repository/repository.go
index aca744dda..82cb9fa18 100644
--- a/reposerver/repository/repository.go
+++ b/reposerver/repository/repository.go
@@ -67,6 +67,7 @@ const (
 
 // Service implements ManifestService interface
 type Service struct {
+	gitCredsStore             git.CredsStore
 	repoLock                  *repositoryLock
 	cache                     *reposervercache.Cache
 	parallelismLimitSemaphore *semaphore.Weighted
@@ -88,7 +89,7 @@ type RepoServerInitConstants struct {
 }
 
 // NewService returns a new instance of the Manifest service
-func NewService(metricsServer *metrics.MetricsServer, cache *reposervercache.Cache, initConstants RepoServerInitConstants, resourceTracking argo.ResourceTracking) *Service {
+func NewService(metricsServer *metrics.MetricsServer, cache *reposervercache.Cache, initConstants RepoServerInitConstants, resourceTracking argo.ResourceTracking, gitCredsStore git.CredsStore) *Service {
 	var parallelismLimitSemaphore *semaphore.Weighted
 	if initConstants.ParallelismLimit > 0 {
 		parallelismLimitSemaphore = semaphore.NewWeighted(initConstants.ParallelismLimit)
@@ -106,6 +107,7 @@ func NewService(metricsServer *metrics.MetricsServer, cache *reposervercache.Cac
 		},
 		initConstants: initConstants,
 		now:           time.Now,
+		gitCredsStore: gitCredsStore,
 	}
 }
 
@@ -329,7 +331,7 @@ func (s *Service) runManifestGen(ctx context.Context, repoRoot, commitSHA, cache
 	var manifestGenResult *apiclient.ManifestResponse
 	opContext, err := opContextSrc()
 	if err == nil {
-		manifestGenResult, err = GenerateManifests(ctx, opContext.appPath, repoRoot, commitSHA, q, false)
+		manifestGenResult, err = GenerateManifests(ctx, opContext.appPath, repoRoot, commitSHA, q, false, s.gitCredsStore)
 	}
 	if err != nil {
 
@@ -866,7 +868,7 @@ func getRepoCredential(repoCredentials []*v1alpha1.RepoCreds, repoURL string) *v
 }
 
 // GenerateManifests generates manifests from a path
-func GenerateManifests(ctx context.Context, appPath, repoRoot, revision string, q *apiclient.ManifestRequest, isLocal bool) (*apiclient.ManifestResponse, error) {
+func GenerateManifests(ctx context.Context, appPath, repoRoot, revision string, q *apiclient.ManifestRequest, isLocal bool, gitCredsStore git.CredsStore) (*apiclient.ManifestResponse, error) {
 	var targetObjs []*unstructured.Unstructured
 	var dest *v1alpha1.ApplicationDestination
 
@@ -891,13 +893,13 @@ func GenerateManifests(ctx context.Context, appPath, repoRoot, revision string,
 		if q.KustomizeOptions != nil {
 			kustomizeBinary = q.KustomizeOptions.BinaryPath
 		}
-		k := kustomize.NewKustomizeApp(appPath, q.Repo.GetGitCreds(), repoURL, kustomizeBinary)
+		k := kustomize.NewKustomizeApp(appPath, q.Repo.GetGitCreds(gitCredsStore), repoURL, kustomizeBinary)
 		targetObjs, _, err = k.Build(q.ApplicationSource.Kustomize, q.KustomizeOptions, env)
 	case v1alpha1.ApplicationSourceTypePlugin:
 		if q.ApplicationSource.Plugin != nil && q.ApplicationSource.Plugin.Name != "" {
-			targetObjs, err = runConfigManagementPlugin(appPath, repoRoot, env, q, q.Repo.GetGitCreds())
+			targetObjs, err = runConfigManagementPlugin(appPath, repoRoot, env, q, q.Repo.GetGitCreds(gitCredsStore))
 		} else {
-			targetObjs, err = runConfigManagementPluginSidecars(ctx, appPath, repoRoot, env, q, q.Repo.GetGitCreds())
+			targetObjs, err = runConfigManagementPluginSidecars(ctx, appPath, repoRoot, env, q, q.Repo.GetGitCreds(gitCredsStore))
 			if err != nil {
 				err = fmt.Errorf("plugin sidecar failed. %s", err.Error())
 			}
@@ -1419,7 +1421,7 @@ func (s *Service) GetAppDetails(ctx context.Context, q *apiclient.RepoServerAppD
 				return err
 			}
 		case v1alpha1.ApplicationSourceTypeKustomize:
-			if err := populateKustomizeAppDetails(res, q, opContext.appPath, commitSHA); err != nil {
+			if err := populateKustomizeAppDetails(res, q, opContext.appPath, commitSHA, s.gitCredsStore); err != nil {
 				return err
 			}
 		}
@@ -1566,13 +1568,13 @@ func findHelmValueFilesInPath(path string) ([]string, error) {
 	return result, nil
 }
 
-func populateKustomizeAppDetails(res *apiclient.RepoAppDetailsResponse, q *apiclient.RepoServerAppDetailsQuery, appPath string, reversion string) error {
+func populateKustomizeAppDetails(res *apiclient.RepoAppDetailsResponse, q *apiclient.RepoServerAppDetailsQuery, appPath string, reversion string, credsStore git.CredsStore) error {
 	res.Kustomize = &apiclient.KustomizeAppSpec{}
 	kustomizeBinary := ""
 	if q.KustomizeOptions != nil {
 		kustomizeBinary = q.KustomizeOptions.BinaryPath
 	}
-	k := kustomize.NewKustomizeApp(appPath, q.Repo.GetGitCreds(), q.Repo.Repo, kustomizeBinary)
+	k := kustomize.NewKustomizeApp(appPath, q.Repo.GetGitCreds(credsStore), q.Repo.Repo, kustomizeBinary)
 	fakeManifestRequest := apiclient.ManifestRequest{
 		AppName:           q.AppName,
 		Namespace:         "", // FIXME: omit it for now
@@ -1674,7 +1676,7 @@ func fileParameters(q *apiclient.RepoServerAppDetailsQuery) []v1alpha1.HelmFileP
 
 func (s *Service) newClient(repo *v1alpha1.Repository, opts ...git.ClientOpts) (git.Client, error) {
 	opts = append(opts, git.WithEventHandlers(metrics.NewGitClientEventHandlers(s.metricsServer)))
-	return s.newGitClient(repo.Repo, repo.GetGitCreds(), repo.IsInsecure(), repo.EnableLFS, repo.Proxy, opts...)
+	return s.newGitClient(repo.Repo, repo.GetGitCreds(s.gitCredsStore), repo.IsInsecure(), repo.EnableLFS, repo.Proxy, opts...)
 }
 
 // newClientResolveRevision is a helper to perform the common task of instantiating a git client
@@ -1776,7 +1778,7 @@ func (s *Service) TestRepository(ctx context.Context, q *apiclient.TestRepositor
 	}
 	checks := map[string]func() error{
 		"git": func() error {
-			return git.TestRepo(repo.Repo, repo.GetGitCreds(), repo.IsInsecure(), repo.IsLFSEnabled(), repo.Proxy)
+			return git.TestRepo(repo.Repo, repo.GetGitCreds(s.gitCredsStore), repo.IsInsecure(), repo.IsLFSEnabled(), repo.Proxy)
 		},
 		"helm": func() error {
 			if repo.EnableOCI {
@@ -1834,7 +1836,7 @@ func (s *Service) ResolveRevision(ctx context.Context, q *apiclient.ResolveRevis
 			AmbiguousRevision: fmt.Sprintf("%v (%v)", ambiguousRevision, version.String()),
 		}, nil
 	} else {
-		gitClient, err := git.NewClient(repo.Repo, repo.GetGitCreds(), repo.IsInsecure(), repo.IsLFSEnabled(), repo.Proxy)
+		gitClient, err := git.NewClient(repo.Repo, repo.GetGitCreds(s.gitCredsStore), repo.IsInsecure(), repo.IsLFSEnabled(), repo.Proxy)
 		if err != nil {
 			return &apiclient.ResolveRevisionResponse{Revision: "", AmbiguousRevision: ""}, err
 		}
diff --git a/reposerver/server.go b/reposerver/server.go
index ecc86ca95..fb2e861c3 100644
--- a/reposerver/server.go
+++ b/reposerver/server.go
@@ -5,8 +5,6 @@ import (
 	"fmt"
 	"os"
 
-	"github.com/argoproj/argo-cd/v2/util/argo"
-
 	grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
 	grpc_logrus "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"
 	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
@@ -24,7 +22,9 @@ import (
 	"github.com/argoproj/argo-cd/v2/reposerver/metrics"
 	"github.com/argoproj/argo-cd/v2/reposerver/repository"
 	"github.com/argoproj/argo-cd/v2/server/version"
+	"github.com/argoproj/argo-cd/v2/util/argo"
 	"github.com/argoproj/argo-cd/v2/util/env"
+	"github.com/argoproj/argo-cd/v2/util/git"
 	grpc_util "github.com/argoproj/argo-cd/v2/util/grpc"
 	tlsutil "github.com/argoproj/argo-cd/v2/util/tls"
 )
@@ -33,6 +33,7 @@ import (
 type ArgoCDRepoServer struct {
 	log           *log.Entry
 	metricsServer *metrics.MetricsServer
+	gitCredsStore git.CredsStore
 	cache         *reposervercache.Cache
 	opts          []grpc.ServerOption
 	initConstants repository.RepoServerInitConstants
@@ -42,7 +43,7 @@ type ArgoCDRepoServer struct {
 var tlsHostList []string = []string{"localhost", "reposerver"}
 
 // NewServer returns a new instance of the Argo CD Repo server
-func NewServer(metricsServer *metrics.MetricsServer, cache *reposervercache.Cache, tlsConfCustomizer tlsutil.ConfigCustomizer, initConstants repository.RepoServerInitConstants) (*ArgoCDRepoServer, error) {
+func NewServer(metricsServer *metrics.MetricsServer, cache *reposervercache.Cache, tlsConfCustomizer tlsutil.ConfigCustomizer, initConstants repository.RepoServerInitConstants, gitCredsStore git.CredsStore) (*ArgoCDRepoServer, error) {
 	var tlsConfig *tls.Config
 
 	// Generate or load TLS server certificates to use with this instance of
@@ -85,6 +86,7 @@ func NewServer(metricsServer *metrics.MetricsServer, cache *reposervercache.Cach
 		cache:         cache,
 		initConstants: initConstants,
 		opts:          serverOpts,
+		gitCredsStore: gitCredsStore,
 	}, nil
 }
 
@@ -94,7 +96,7 @@ func (a *ArgoCDRepoServer) CreateGRPC() *grpc.Server {
 	versionpkg.RegisterVersionServiceServer(server, version.NewServer(nil, func() (bool, error) {
 		return true, nil
 	}))
-	manifestService := repository.NewService(a.metricsServer, a.cache, a.initConstants, argo.NewResourceTracking())
+	manifestService := repository.NewService(a.metricsServer, a.cache, a.initConstants, argo.NewResourceTracking(), a.gitCredsStore)
 	apiclient.RegisterRepoServerServiceServer(server, manifestService)
 
 	healthService := health.NewServer()
diff --git a/resource_customizations/extensions/DaemonSet/actions/discovery.lua b/resource_customizations/extensions/DaemonSet/actions/discovery.lua
deleted file mode 100644
index 49557377a..000000000
--- a/resource_customizations/extensions/DaemonSet/actions/discovery.lua
+++ /dev/null
@@ -1,3 +0,0 @@
-actions = {}
-actions["restart"] = {}
-return actions
diff --git a/resource_customizations/extensions/DaemonSet/actions/restart/action.lua b/resource_customizations/extensions/DaemonSet/actions/restart/action.lua
deleted file mode 100644
index 457c7670c..000000000
--- a/resource_customizations/extensions/DaemonSet/actions/restart/action.lua
+++ /dev/null
@@ -1,9 +0,0 @@
-local os = require("os")
-if obj.spec.template.metadata == nil then
-    obj.spec.template.metadata = {}
-end
-if obj.spec.template.metadata.annotations == nil then
-    obj.spec.template.metadata.annotations = {}
-end
-obj.spec.template.metadata.annotations["kubectl.kubernetes.io/restartedAt"] = os.date("!%Y-%m-%dT%XZ")
-return obj
diff --git a/resource_customizations/extensions/Deployment/actions/discovery.lua b/resource_customizations/extensions/Deployment/actions/discovery.lua
deleted file mode 100644
index 49557377a..000000000
--- a/resource_customizations/extensions/Deployment/actions/discovery.lua
+++ /dev/null
@@ -1,3 +0,0 @@
-actions = {}
-actions["restart"] = {}
-return actions
diff --git a/resource_customizations/extensions/Deployment/actions/restart/action.lua b/resource_customizations/extensions/Deployment/actions/restart/action.lua
deleted file mode 100644
index 457c7670c..000000000
--- a/resource_customizations/extensions/Deployment/actions/restart/action.lua
+++ /dev/null
@@ -1,9 +0,0 @@
-local os = require("os")
-if obj.spec.template.metadata == nil then
-    obj.spec.template.metadata = {}
-end
-if obj.spec.template.metadata.annotations == nil then
-    obj.spec.template.metadata.annotations = {}
-end
-obj.spec.template.metadata.annotations["kubectl.kubernetes.io/restartedAt"] = os.date("!%Y-%m-%dT%XZ")
-return obj
diff --git a/server/repository/repository.go b/server/repository/repository.go
index 6bbcfe752..8340f81c3 100644
--- a/server/repository/repository.go
+++ b/server/repository/repository.go
@@ -4,22 +4,24 @@ import (
 	"fmt"
 	"reflect"
 
-	"github.com/argoproj/argo-cd/v2/util/argo"
-
 	"github.com/argoproj/gitops-engine/pkg/utils/kube"
 	"github.com/argoproj/gitops-engine/pkg/utils/text"
 	log "github.com/sirupsen/logrus"
 	"golang.org/x/net/context"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
+	apierr "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
 	"github.com/argoproj/argo-cd/v2/common"
 	repositorypkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/repository"
+	"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
 	appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
+	applisters "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1"
 	"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
 	servercache "github.com/argoproj/argo-cd/v2/server/cache"
 	"github.com/argoproj/argo-cd/v2/server/rbacpolicy"
+	"github.com/argoproj/argo-cd/v2/util/argo"
 	"github.com/argoproj/argo-cd/v2/util/db"
 	"github.com/argoproj/argo-cd/v2/util/errors"
 	"github.com/argoproj/argo-cd/v2/util/io"
@@ -33,6 +35,8 @@ type Server struct {
 	repoClientset apiclient.Clientset
 	enf           *rbac.Enforcer
 	cache         *servercache.Cache
+	appLister     applisters.ApplicationNamespaceLister
+	projLister    applisters.AppProjectNamespaceLister
 	settings      *settings.SettingsManager
 }
 
@@ -42,6 +46,8 @@ func NewServer(
 	db db.ArgoDB,
 	enf *rbac.Enforcer,
 	cache *servercache.Cache,
+	appLister applisters.ApplicationNamespaceLister,
+	projLister applisters.AppProjectNamespaceLister,
 	settings *settings.SettingsManager,
 ) *Server {
 	return &Server{
@@ -49,14 +55,20 @@ func NewServer(
 		repoClientset: repoClientset,
 		enf:           enf,
 		cache:         cache,
+		appLister:     appLister,
+		projLister:    projLister,
 		settings:      settings,
 	}
 }
 
+var (
+	errPermissionDenied = status.Error(codes.PermissionDenied, "permission denied")
+)
+
 func (s *Server) getRepo(ctx context.Context, url string) (*appsv1.Repository, error) {
 	repo, err := s.db.GetRepository(ctx, url)
 	if err != nil {
-		return nil, status.Error(codes.PermissionDenied, "permission denied")
+		return nil, errPermissionDenied
 	}
 	return repo, nil
 }
@@ -213,14 +225,29 @@ func (s *Server) ListRefs(ctx context.Context, q *repositorypkg.RepoQuery) (*api
 	})
 }
 
-// ListApps returns list of apps in the repo
+// ListApps performs discovery of a git repository for potential sources of applications. Used
+// as a convenience to the UI for auto-complete.
 func (s *Server) ListApps(ctx context.Context, q *repositorypkg.RepoAppsQuery) (*repositorypkg.RepoAppsResponse, error) {
 	repo, err := s.getRepo(ctx, q.Repo)
 	if err != nil {
 		return nil, err
 	}
 
-	if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, createRBACObject(repo.Project, repo.Repo)); err != nil {
+	claims := ctx.Value("claims")
+	if err := s.enf.EnforceErr(claims, rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, createRBACObject(repo.Project, repo.Repo)); err != nil {
+		return nil, err
+	}
+
+	// This endpoint causes us to clone git repos & invoke config management tooling for the purposes
+	// of app discovery. Only allow this to happen if user has privileges to create or update the
+	// application which it wants to retrieve these details for.
+	appRBACresource := fmt.Sprintf("%s/%s", q.AppProject, q.AppName)
+	if !s.enf.Enforce(claims, rbacpolicy.ResourceApplications, rbacpolicy.ActionCreate, appRBACresource) &&
+		!s.enf.Enforce(claims, rbacpolicy.ResourceApplications, rbacpolicy.ActionUpdate, appRBACresource) {
+		return nil, errPermissionDenied
+	}
+	// Also ensure the repo is actually allowed in the project in question
+	if err := s.isRepoPermittedInProject(q.Repo, q.AppProject); err != nil {
 		return nil, err
 	}
 
@@ -245,6 +272,9 @@ func (s *Server) ListApps(ctx context.Context, q *repositorypkg.RepoAppsQuery) (
 	return &repositorypkg.RepoAppsResponse{Items: items}, nil
 }
 
+// GetAppDetails shows parameter values to various config tools (e.g. helm/kustomize values)
+// This is used by UI for parameter form fields during app create & edit pages.
+// It is also used when showing history of parameters used in previous syncs in the app history.
 func (s *Server) GetAppDetails(ctx context.Context, q *repositorypkg.RepoAppDetailsQuery) (*apiclient.RepoAppDetailsResponse, error) {
 	if q.Source == nil {
 		return nil, status.Errorf(codes.InvalidArgument, "missing payload in request")
@@ -253,9 +283,38 @@ func (s *Server) GetAppDetails(ctx context.Context, q *repositorypkg.RepoAppDeta
 	if err != nil {
 		return nil, err
 	}
-	if err := s.enf.EnforceErr(ctx.Value("claims"), rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, createRBACObject(repo.Project, repo.Repo)); err != nil {
+	claims := ctx.Value("claims")
+	if err := s.enf.EnforceErr(claims, rbacpolicy.ResourceRepositories, rbacpolicy.ActionGet, createRBACObject(repo.Project, repo.Repo)); err != nil {
+		return nil, err
+	}
+
+	app, err := s.appLister.Get(q.AppName)
+	appRBACObj := createRBACObject(q.AppProject, q.AppName)
+	// ensure caller has read privileges to app
+	if err := s.enf.EnforceErr(claims, rbacpolicy.ResourceApplications, rbacpolicy.ActionGet, appRBACObj); err != nil {
+		return nil, err
+	}
+	if apierr.IsNotFound(err) {
+		// app doesn't exist since it still is being formulated. verify they can create the app
+		// before we reveal repo details
+		if err := s.enf.EnforceErr(claims, rbacpolicy.ResourceApplications, rbacpolicy.ActionCreate, appRBACObj); err != nil {
+			return nil, err
+		}
+	} else {
+		// if we get here we are returning repo details of an existing app
+		if q.AppProject != app.Spec.Project {
+			return nil, errPermissionDenied
+		}
+		// verify caller is not making a request with arbitrary source values which were not in our history
+		if !isSourceInHistory(app, *q.Source) {
+			return nil, errPermissionDenied
+		}
+	}
+	// Ensure the repo is actually allowed in the project in question
+	if err := s.isRepoPermittedInProject(q.Source.RepoURL, q.AppProject); err != nil {
 		return nil, err
 	}
+
 	conn, repoClient, err := s.repoClientset.NewRepoServerClient()
 	if err != nil {
 		return nil, err
@@ -473,3 +532,33 @@ func (s *Server) testRepo(ctx context.Context, repo *appsv1.Repository) error {
 	})
 	return err
 }
+
+func (s *Server) isRepoPermittedInProject(repo string, projName string) error {
+	proj, err := s.projLister.Get(projName)
+	if err != nil {
+		return err
+	}
+	if !proj.IsSourcePermitted(appsv1.ApplicationSource{RepoURL: repo}) {
+		return status.Errorf(codes.PermissionDenied, "repository '%s' not permitted in project '%s'", repo, projName)
+	}
+	return nil
+}
+
+// isSourceInHistory checks if the supplied application source is either our current application
+// source, or was something which we synced to previously.
+func isSourceInHistory(app *v1alpha1.Application, source v1alpha1.ApplicationSource) bool {
+	if source.Equals(app.Spec.Source) {
+		return true
+	}
+	// Iterate history. When comparing items in our history, use the actual synced revision to
+	// compare with the supplied source.targetRevision in the request. This is because
+	// history[].source.targetRevision is ambiguous (e.g. HEAD), whereas
+	// history[].revision will contain the explicit SHA
+	for _, h := range app.Status.History {
+		h.Source.TargetRevision = h.Revision
+		if source.Equals(h.Source) {
+			return true
+		}
+	}
+	return false
+}
diff --git a/server/repository/repository.proto b/server/repository/repository.proto
index 87688d0b7..2635119d6 100644
--- a/server/repository/repository.proto
+++ b/server/repository/repository.proto
@@ -16,6 +16,8 @@ import "github.com/argoproj/argo-cd/v2/reposerver/repository/repository.proto";
 message RepoAppsQuery {
 	string repo = 1;
 	string revision = 2;
+	string appName = 3;
+	string appProject = 4;
 }
 
 
@@ -29,6 +31,7 @@ message AppInfo {
 message RepoAppDetailsQuery {
 	github.com.argoproj.argo_cd.v2.pkg.apis.application.v1alpha1.ApplicationSource source = 1;
 	string appName = 2;
+	string appProject = 3;
 }
 
 // RepoAppsResponse contains applications of specified repository
diff --git a/server/server.go b/server/server.go
index 0f570842e..921f47ce0 100644
--- a/server/server.go
+++ b/server/server.go
@@ -156,6 +156,7 @@ type ArgoCDServer struct {
 	settingsMgr    *settings_util.SettingsManager
 	enf            *rbac.Enforcer
 	projInformer   cache.SharedIndexInformer
+	projLister     applisters.AppProjectNamespaceLister
 	policyEnforcer *rbacpolicy.RBACPolicyEnforcer
 	appInformer    cache.SharedIndexInformer
 	appLister      applisters.ApplicationNamespaceLister
@@ -250,6 +251,7 @@ func NewServer(ctx context.Context, opts ArgoCDServerOpts) *ArgoCDServer {
 		settingsMgr:      settingsMgr,
 		enf:              enf,
 		projInformer:     projInformer,
+		projLister:       projLister,
 		appInformer:      appInformer,
 		appLister:        appLister,
 		policyEnforcer:   policyEnf,
@@ -580,7 +582,7 @@ func (a *ArgoCDServer) newGRPCServer() *grpc.Server {
 	db := db.NewDB(a.Namespace, a.settingsMgr, a.KubeClientset)
 	kubectl := kubeutil.NewKubectl()
 	clusterService := cluster.NewServer(db, a.enf, a.Cache, kubectl)
-	repoService := repository.NewServer(a.RepoClientset, db, a.enf, a.Cache, a.settingsMgr)
+	repoService := repository.NewServer(a.RepoClientset, db, a.enf, a.Cache, a.appLister, a.projLister, a.settingsMgr)
 	repoCredsService := repocreds.NewServer(a.RepoClientset, db, a.enf, a.settingsMgr)
 	var loginRateLimiter func() (io.Closer, error)
 	if maxConcurrentLoginRequestsCount > 0 {
diff --git a/ui/package.json b/ui/package.json
index a13a19a59..121f7c813 100644
--- a/ui/package.json
+++ b/ui/package.json
@@ -10,45 +10,22 @@
     "test": "jest"
   },
   "dependencies": {
-    "@types/classnames": "^2.2.3",
-    "@types/cookie": "^0.3.1",
-    "@types/dagre": "^0.7.40",
-    "@types/deepmerge": "^2.2.0",
-    "@types/git-url-parse": "^9.0.0",
-    "@types/js-yaml": "^3.11.2",
-    "@types/minimatch": "^3.0.3",
-    "@types/prop-types": "^15.5.2",
-    "@types/react": "^16.8.5",
-    "@types/react-autocomplete": "^1.8.4",
-    "@types/react-dom": "^16.9.14",
-    "@types/react-form": "^2.16.0",
-    "@types/react-helmet": "^5.0.17",
-    "@types/react-paginate": "^6.2.0",
-    "@types/react-router": "^4.0.27",
-    "@types/react-router-dom": "^4.2.3",
-    "@types/superagent": "^3.5.7",
     "ansi-to-react": "^6.1.6",
     "argo-ui": "git+https://github.com/argoproj/argo-ui.git",
     "classnames": "^2.2.5",
     "color": "^3.1.0",
     "cookie": "^0.3.1",
-    "copy-webpack-plugin": "^6.1.1",
     "dagre": "^0.8.2",
     "deepmerge": "^3.2.0",
     "foundation-sites": "^6.4.3",
     "git-url-parse": "^11.1.2",
-    "html-webpack-plugin": "^3.2.0",
-    "jest-junit": "^6.4.0",
     "js-yaml": "^3.13.1",
     "json-merge-patch": "^0.2.3",
     "lodash-es": "^4.17.21",
     "minimatch": "^3.0.4",
     "moment": "^2.24.0",
-    "monaco-editor": "^0.15.6",
-    "monaco-editor-webpack-plugin": "^1.7.0",
-    "node-sass": "^6.0.1",
+    "monaco-editor": "^0.27.0",
     "prop-types": "^15.6.0",
-    "raw-loader": "^0.5.1",
     "react": "^16.9.3",
     "react-autocomplete": "^1.8.1",
     "react-diff-view": "^2.4.7",
@@ -62,23 +39,12 @@
     "react-router": "^4.3.1",
     "react-router-dom": "^4.2.2",
     "react-svg-piechart": "^2.1.1",
-    "redoc": "^2.0.0-rc.40",
+    "redoc": "^2.0.0-rc.63",
     "rxjs": "^6.6.6",
-    "sass-loader": "^6.0.6",
-    "source-map-loader": "^0.2.3",
-    "style-loader": "^0.20.1",
     "superagent": "^3.8.2",
     "superagent-promise": "^1.1.0",
     "timezones-list": "3.0.1",
-    "ts-loader": "^6.0.4",
-    "ts-node": "^4.1.0",
-    "tslint": "^6.1.3",
-    "tslint-react": "^3.4.0",
-    "typescript": "^4.0.3",
-    "unidiff": "^1.0.2",
-    "webpack": "^4.44.2",
-    "webpack-cli": "^3.3.12",
-    "webpack-dev-server": "^3.11.0"
+    "unidiff": "^1.0.2"
   },
   "resolutions": {
     "@types/react": "^16.9.3",
@@ -90,23 +56,57 @@
     "@babel/preset-env": "^7.7.1",
     "@babel/preset-react": "^7.7.0",
     "@babel/preset-typescript": "^7.7.2",
+    "@types/classnames": "^2.2.3",
+    "@types/cookie": "^0.3.1",
+    "@types/dagre": "^0.7.40",
+    "@types/deepmerge": "^2.2.0",
+    "@types/git-url-parse": "^9.0.0",
     "@types/jest": "^24.0.13",
+    "@types/js-yaml": "^3.11.2",
     "@types/lodash-es": "^4.17.5",
+    "@types/minimatch": "^3.0.3",
+    "@types/prop-types": "^15.5.2",
+    "@types/react": "^16.8.5",
+    "@types/react-autocomplete": "^1.8.4",
+    "@types/react-dom": "^16.9.14",
+    "@types/react-form": "^2.16.0",
+    "@types/react-helmet": "^5.0.17",
+    "@types/react-paginate": "^6.2.0",
+    "@types/react-router": "^4.0.27",
+    "@types/react-router-dom": "^4.2.3",
     "@types/react-test-renderer": "^16.8.3",
+    "@types/superagent": "^3.5.7",
     "add": "^2.0.6",
     "babel-jest": "^24.9.0",
     "babel-loader": "^8.0.6",
     "codecov": "^3.7.2",
+    "copy-webpack-plugin": "^6.1.1",
     "esbuild-loader": "^2.15.1",
+    "html-webpack-plugin": "^3.2.0",
     "identity-obj-proxy": "^3.0.0",
     "jest": "^24.9.0",
+    "jest-junit": "^6.4.0",
     "jest-transform-css": "^2.0.0",
+    "monaco-editor-webpack-plugin": "^6.0.0",
+    "node-sass": "^6.0.1",
     "postcss": "^8.2.13",
     "prettier": "1.19",
+    "raw-loader": "^0.5.1",
     "react-test-renderer": "16.8.3",
+    "sass-loader": "^6.0.6",
+    "source-map-loader": "^0.2.3",
+    "style-loader": "^0.20.1",
     "ts-jest": "^24.1.0",
+    "ts-loader": "^6.0.4",
+    "ts-node": "^4.1.0",
+    "tslint": "^6.1.3",
     "tslint-config-prettier": "^1.18.0",
     "tslint-plugin-prettier": "^2.0.1",
-    "yarn": "^1.22.10"
+    "tslint-react": "^3.4.0",
+    "typescript": "^4.0.3",
+    "webpack": "^4.44.2",
+    "yarn": "^1.22.10",
+    "webpack-cli": "^3.3.12",
+    "webpack-dev-server": "^3.11.0"
   }
 }
diff --git a/ui/src/app/applications/components/application-create-panel/application-create-panel.tsx b/ui/src/app/applications/components/application-create-panel/application-create-panel.tsx
index 95af691ed..cd60efbe3 100644
--- a/ui/src/app/applications/components/application-create-panel/application-create-panel.tsx
+++ b/ui/src/app/applications/components/application-create-panel/application-create-panel.tsx
@@ -281,7 +281,7 @@ export const ApplicationCreatePanel = (props: {
                                                                 load={async src =>
                                                                     (src.repoURL &&
                                                                         services.repos
-                                                                            .apps(src.repoURL, src.revision)
+                                                                            .apps(src.repoURL, src.revision, app.metadata.name, app.spec.project)
                                                                             .then(apps => Array.from(new Set(apps.map(item => item.path))).sort())
                                                                             .catch(() => new Array<string>())) ||
                                                                     new Array<string>()
@@ -421,7 +421,7 @@ export const ApplicationCreatePanel = (props: {
                                                 }}
                                                 load={async src => {
                                                     if (src.repoURL && src.targetRevision && (src.path || src.chart)) {
-                                                        return services.repos.appDetails(src, src.appName).catch(() => ({
+                                                        return services.repos.appDetails(src, src.appName, app.spec.project).catch(() => ({
                                                             type: 'Directory',
                                                             details: {}
                                                         }));
diff --git a/ui/src/app/applications/components/application-deployment-history/application-deployment-history.tsx b/ui/src/app/applications/components/application-deployment-history/application-deployment-history.tsx
index ad02b9423..c78c631ad 100644
--- a/ui/src/app/applications/components/application-deployment-history/application-deployment-history.tsx
+++ b/ui/src/app/applications/components/application-deployment-history/application-deployment-history.tsx
@@ -80,7 +80,7 @@ export const ApplicationDeploymentHistory = ({
                                 />
                                 <DataLoader
                                     input={{...recentDeployments[index].source, targetRevision: recentDeployments[index].revision, appName: app.metadata.name}}
-                                    load={src => services.repos.appDetails(src, src.appName)}>
+                                    load={src => services.repos.appDetails(src, src.appName, app.spec.project)}>
                                     {(details: models.RepoAppDetails) => (
                                         <div>
                                             <ApplicationParameters
diff --git a/ui/src/app/applications/components/applications-list/applications-list.scss b/ui/src/app/applications/components/applications-list/applications-list.scss
index e731619d5..52d178dec 100644
--- a/ui/src/app/applications/components/applications-list/applications-list.scss
+++ b/ui/src/app/applications/components/applications-list/applications-list.scss
@@ -167,6 +167,10 @@
         position: absolute;
         top: 1em;
         right: 1em;
+
+        .large-text-height {
+            line-height: 1.5;
+        }
     }
 
     &__external-links-icon-container {
@@ -195,6 +199,6 @@
         justify-content: space-evenly;
     }
 }
-i.menu_icon{
+i.menu_icon {
     vertical-align: middle;
 }
diff --git a/ui/src/app/applications/components/applications-list/applications-table.tsx b/ui/src/app/applications/components/applications-list/applications-table.tsx
index 1f3c14bbe..2e9c78781 100644
--- a/ui/src/app/applications/components/applications-list/applications-table.tsx
+++ b/ui/src/app/applications/components/applications-list/applications-table.tsx
@@ -2,7 +2,7 @@ import {DataLoader, DropDownMenu, Tooltip} from 'argo-ui';
 import * as React from 'react';
 import {Key, KeybindingContext, useNav} from 'argo-ui/v2';
 import {Cluster} from '../../../shared/components';
-import {Consumer} from '../../../shared/context';
+import {Consumer, Context} from '../../../shared/context';
 import * as models from '../../../shared/models';
 import {ApplicationURLs} from '../application-urls';
 import * as AppUtils from '../utils';
@@ -19,6 +19,7 @@ export const ApplicationsTable = (props: {
     deleteApplication: (appName: string) => any;
 }) => {
     const [selectedApp, navApp, reset] = useNav(props.applications.length);
+    const ctxh = React.useContext(Context);
 
     const {useKeybinding} = React.useContext(KeybindingContext);
 
@@ -31,6 +32,16 @@ export const ApplicationsTable = (props: {
             return selectedApp > -1 ? true : false;
         }
     });
+    useKeybinding({
+        keys: Key.ENTER,
+        action: () => {
+            if (selectedApp > -1) {
+                ctxh.navigation.goto(`/applications/${props.applications[selectedApp].metadata.name}`);
+                return true;
+            }
+            return false;
+        }
+    });
 
     return (
         <Consumer>
diff --git a/ui/src/app/applications/components/applications-list/applications-tiles.tsx b/ui/src/app/applications/components/applications-list/applications-tiles.tsx
index 6f7509f9e..528a15df2 100644
--- a/ui/src/app/applications/components/applications-list/applications-tiles.tsx
+++ b/ui/src/app/applications/components/applications-list/applications-tiles.tsx
@@ -121,6 +121,7 @@ export const ApplicationTiles = ({applications, syncApplication, refreshApplicat
                                                         <ApplicationURLs urls={AppUtils.getExternalUrls(app.metadata.annotations, app.status.summary.externalURLs)} />
                                                         <Tooltip content={favList?.includes(app.metadata.name) ? 'Remove Favorite' : 'Add Favorite'}>
                                                             <button
+                                                                className='large-text-height'
                                                                 onClick={e => {
                                                                     e.stopPropagation();
                                                                     favList?.includes(app.metadata.name)
diff --git a/ui/src/app/applications/components/pod-logs-viewer/pod-logs-viewer.scss b/ui/src/app/applications/components/pod-logs-viewer/pod-logs-viewer.scss
index 09845335b..8e48879d1 100644
--- a/ui/src/app/applications/components/pod-logs-viewer/pod-logs-viewer.scss
+++ b/ui/src/app/applications/components/pod-logs-viewer/pod-logs-viewer.scss
@@ -23,7 +23,7 @@
     &__filter {
         display: flex;
         margin-left: auto;
-        width: 350px;
+        width: 320px;
     }
 
     &__menu {
diff --git a/ui/src/app/applications/components/pod-logs-viewer/pod-logs-viewer.tsx b/ui/src/app/applications/components/pod-logs-viewer/pod-logs-viewer.tsx
index 69fe0a6b1..7895451da 100644
--- a/ui/src/app/applications/components/pod-logs-viewer/pod-logs-viewer.tsx
+++ b/ui/src/app/applications/components/pod-logs-viewer/pod-logs-viewer.tsx
@@ -168,6 +168,16 @@ export const PodsLogsViewer = (props: PodLogsProps & {fullscreen?: boolean}) =>
                                 {prefs.appDetails.followLogs && <i className='fa fa-check' />}
                             </button>
                         </Tooltip>
+                        <Tooltip content='Wrap Lines'>
+                            <button
+                                className={`argo-button argo-button--base${prefs.appDetails.wrapLines ? '' : '-o'}`}
+                                onClick={() => {
+                                    const wrap = prefs.appDetails.wrapLines;
+                                    services.viewPreferences.updatePreferences({...prefs, appDetails: {...prefs.appDetails, wrapLines: !wrap}});
+                                }}>
+                                <i className='fa fa-paragraph' />
+                            </button>
+                        </Tooltip>
                         <Tooltip content='Show previous logs'>
                             <button
                                 className={`argo-button argo-button--base${showPreviousLogs ? '' : '-o'}`}
@@ -343,7 +353,7 @@ export const PodsLogsViewer = (props: PodLogsProps & {fullscreen?: boolean}) =>
                                             />
                                         </Tooltip>
                                     )}
-                                    <pre style={{height: '95%'}}>
+                                    <pre style={{height: '95%', whiteSpace: prefs.appDetails.wrapLines ? 'normal' : 'pre'}}>
                                         <div ref={top} style={{height: '1px'}} />
                                         {lines.map((l, i) => {
                                             const lineNum = lastLine - i;
diff --git a/ui/src/app/applications/components/resource-details/resource-details.tsx b/ui/src/app/applications/components/resource-details/resource-details.tsx
index 3680faa25..839151e01 100644
--- a/ui/src/app/applications/components/resource-details/resource-details.tsx
+++ b/ui/src/app/applications/components/resource-details/resource-details.tsx
@@ -145,7 +145,7 @@ export const ResourceDetails = (props: ResourceDetailsProps) => {
                         key='appDetails'
                         input={application}
                         load={app =>
-                            services.repos.appDetails(app.spec.source, app.metadata.name).catch(() => ({
+                            services.repos.appDetails(app.spec.source, app.metadata.name, app.spec.project).catch(() => ({
                                 type: 'Directory' as AppSourceType,
                                 path: application.spec.source.path
                             }))
diff --git a/ui/src/app/help/components/help.tsx b/ui/src/app/help/components/help.tsx
index 3b18bf65d..bf9b49a21 100644
--- a/ui/src/app/help/components/help.tsx
+++ b/ui/src/app/help/components/help.tsx
@@ -4,6 +4,7 @@ import {Consumer} from '../../shared/context';
 import {combineLatest} from 'rxjs';
 import {services} from '../../shared/services';
 import {map} from 'rxjs/operators';
+import classNames from 'classnames';
 
 require('./help.scss');
 
@@ -40,29 +41,26 @@ export const Help = () => {
                                                 <i className='fab fa-linux' /> Linux (amd64)
                                             </a>
                                             &nbsp;
-                                            {binaryUrls.hasOwnProperty('linux-arm64') && (
-                                                <a href={`${binaryUrls['linux-arm64']}`} className='user-info-panel-buttons argo-button argo-button--base'>
-                                                    <i className='fab fa-linux' /> Linux (arm64)
-                                                </a>
-                                            )}
-                                            &nbsp;
-                                            {binaryUrls.hasOwnProperty('darwin-amd64') && (
-                                                <a href={`${binaryUrls['darwin-amd64']}`} className='user-info-panel-buttons argo-button argo-button--base'>
-                                                    <i className='fab fa-apple' /> MacOS (amd64)
-                                                </a>
-                                            )}
-                                            &nbsp;
-                                            {binaryUrls.hasOwnProperty('darwin-arm64') && (
-                                                <a href={`${binaryUrls['darwin-arm64']}`} className='user-info-panel-buttons argo-button argo-button--base'>
-                                                    <i className='fab fa-apple' /> MacOS (arm64)
-                                                </a>
-                                            )}
-                                            &nbsp;
-                                            {binaryUrls.hasOwnProperty('windows-amd64') && (
-                                                <a href={`${binaryUrls['windows-amd64']}`} className='user-info-panel-buttons argo-button argo-button--base'>
-                                                    <i className='fab fa-windows' /> Windows
-                                                </a>
-                                            )}
+                                            {Object.keys(binaryUrls || {}).map(binaryName => {
+                                                const url = binaryUrls[binaryName];
+                                                const match = binaryName.match(/.*(darwin|windows|linux)-(amd64|arm64)/);
+                                                const [platform, arch] = match ? match.slice(1) : ['', ''];
+                                                return (
+                                                    <>
+                                                        &nbsp;
+                                                        <a key={binaryName} href={url} className='user-info-panel-buttons argo-button argo-button--base'>
+                                                            <i
+                                                                className={classNames('fab', {
+                                                                    'fa-windows': platform === 'windows',
+                                                                    'fa-apple': platform === 'darwin',
+                                                                    'fa-linux': platform === 'linux'
+                                                                })}
+                                                            />
+                                                            {` ${platform}`} {arch && `(${arch})`}
+                                                        </a>
+                                                    </>
+                                                );
+                                            })}
                                         </div>
                                     </div>
                                     <div className='columns large-4 small-6'>
diff --git a/ui/src/app/shared/services/repo-service.ts b/ui/src/app/shared/services/repo-service.ts
index 3b37b8f9a..c7f91ff55 100644
--- a/ui/src/app/shared/services/repo-service.ts
+++ b/ui/src/app/shared/services/repo-service.ts
@@ -132,10 +132,12 @@ export class RepositoriesService {
         return requests.get(`/repositories/${encodeURIComponent(repo)}/refs`).then(res => res.body as models.RefsInfo);
     }
 
-    public apps(repo: string, revision: string): Promise<models.AppInfo[]> {
+    public apps(repo: string, revision: string, appName: string, appProject: string): Promise<models.AppInfo[]> {
         return requests
             .get(`/repositories/${encodeURIComponent(repo)}/apps`)
             .query({revision})
+            .query({appName})
+            .query({appProject})
             .then(res => (res.body.items as models.AppInfo[]) || []);
     }
 
@@ -143,10 +145,10 @@ export class RepositoriesService {
         return requests.get(`/repositories/${encodeURIComponent(repo)}/helmcharts`).then(res => (res.body.items as models.HelmChart[]) || []);
     }
 
-    public appDetails(source: models.ApplicationSource, appName: string): Promise<models.RepoAppDetails> {
+    public appDetails(source: models.ApplicationSource, appName: string, appProject: string): Promise<models.RepoAppDetails> {
         return requests
             .post(`/repositories/${encodeURIComponent(source.repoURL)}/appdetails`)
-            .send({source, appName})
+            .send({source, appName, appProject})
             .then(res => res.body as models.RepoAppDetails);
     }
 }
diff --git a/ui/src/app/shared/services/view-preferences-service.ts b/ui/src/app/shared/services/view-preferences-service.ts
index 6a8f2dfcf..b38e73821 100644
--- a/ui/src/app/shared/services/view-preferences-service.ts
+++ b/ui/src/app/shared/services/view-preferences-service.ts
@@ -22,6 +22,7 @@ export interface AppDetailsPreferences {
     darkMode: boolean;
     followLogs: boolean;
     hideFilters: boolean;
+    wrapLines: boolean;
     groupNodes?: boolean;
 }
 
@@ -108,7 +109,8 @@ const DEFAULT_PREFERENCES: ViewPreferences = {
             hideUnschedulable: true
         },
         darkMode: false,
-        followLogs: false
+        followLogs: false,
+        wrapLines: false
     },
     appList: {
         view: 'tiles' as AppsListViewType,
diff --git a/ui/yarn.lock b/ui/yarn.lock
index 21b3b977b..8f1f34738 100644
--- a/ui/yarn.lock
+++ b/ui/yarn.lock
@@ -748,7 +748,7 @@
     "@babel/helper-plugin-utils" "^7.0.0"
     "@babel/plugin-transform-typescript" "^7.7.2"
 
-"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.4.2", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
+"@babel/runtime@^7.1.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.4.2", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
   version "7.14.6"
   resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d"
   integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==
@@ -762,6 +762,13 @@
   dependencies:
     regenerator-runtime "^0.13.4"
 
+"@babel/runtime@^7.16.7":
+  version "7.17.2"
+  resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.2.tgz#66f68591605e59da47523c631416b18508779941"
+  integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==
+  dependencies:
+    regenerator-runtime "^0.13.4"
+
 "@babel/template@^7.14.5", "@babel/template@^7.4.0", "@babel/template@^7.7.0":
   version "7.14.5"
   resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.14.5.tgz#a9bc9d8b33354ff6e55a9c60d1109200a68974f4"
@@ -1005,6 +1012,32 @@
     mkdirp "^1.0.4"
     rimraf "^3.0.2"
 
+"@redocly/ajv@^8.6.4":
+  version "8.6.4"
+  resolved "https://registry.yarnpkg.com/@redocly/ajv/-/ajv-8.6.4.tgz#94053e7a9d4146d1a4feacd3813892873f229a85"
+  integrity sha512-y9qNj0//tZtWB2jfXNK3BX18BSBp9zNR7KE7lMysVHwbZtY392OJCjm6Rb/h4UHH2r1AqjNEHFD6bRn+DqU9Mw==
+  dependencies:
+    fast-deep-equal "^3.1.1"
+    json-schema-traverse "^1.0.0"
+    require-from-string "^2.0.2"
+    uri-js "^4.2.2"
+
+"@redocly/openapi-core@^1.0.0-beta.54":
+  version "1.0.0-beta.82"
+  resolved "https://registry.yarnpkg.com/@redocly/openapi-core/-/openapi-core-1.0.0-beta.82.tgz#5f232e9c8f82499c2d96d9268b22423c8f859fe2"
+  integrity sha512-bJ0WclpgkFv4aa5QWU83ARCv3VQJy9U94reb1chOTg9s2bsTHbYuUwRv6G370a7lFXX86AyB3dT5IzS6GytLlA==
+  dependencies:
+    "@redocly/ajv" "^8.6.4"
+    "@types/node" "^14.11.8"
+    colorette "^1.2.0"
+    js-levenshtein "^1.1.6"
+    js-yaml "^4.1.0"
+    lodash.isequal "^4.5.0"
+    minimatch "^3.0.4"
+    node-fetch "^2.6.1"
+    pluralize "^8.0.0"
+    yaml-ast-parser "0.0.43"
+
 "@redocly/react-dropdown-aria@^2.0.11":
   version "2.0.12"
   resolved "https://registry.yarnpkg.com/@redocly/react-dropdown-aria/-/react-dropdown-aria-2.0.12.tgz#2e3af2b1b8e9123487109400d6117f0d4a8445a6"
@@ -1179,10 +1212,10 @@
   resolved "https://registry.yarnpkg.com/@types/node/-/node-16.3.1.tgz#24691fa2b0c3ec8c0d34bfcfd495edac5593ebb4"
   integrity sha512-N87VuQi7HEeRJkhzovao/JviiqKjDKMVKxKMfUvSKw+MbkbW8R0nA3fi/MQhhlxV2fQ+2ReM+/Nt4efdrJx3zA==
 
-"@types/node@^13.11.1":
-  version "13.13.52"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.52.tgz#03c13be70b9031baaed79481c0c0cfb0045e53f7"
-  integrity sha512-s3nugnZumCC//n4moGGe6tkNMyYEdaDBitVjwPxXmR5lnMG5dHePinH2EdxkG3Rh1ghFHHixAG4NJhpJW1rthQ==
+"@types/node@^14.11.8":
+  version "14.18.12"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.12.tgz#0d4557fd3b94497d793efd4e7d92df2f83b4ef24"
+  integrity sha512-q4jlIR71hUpWTnGhXWcakgkZeHa3CCjcQcnuzU8M891BAWA2jHiziiWEPEkdS5pFsz7H9HJiy8BrK7tBRNrY7A==
 
 "@types/normalize-package-data@^2.4.0":
   version "2.4.1"
@@ -1274,11 +1307,6 @@
   resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39"
   integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==
 
-"@types/source-list-map@*":
-  version "0.1.2"
-  resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9"
-  integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==
-
 "@types/stack-utils@^1.0.1":
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
@@ -1302,39 +1330,6 @@
     "@types/cookiejar" "*"
     "@types/node" "*"
 
-"@types/tapable@^1":
-  version "1.0.8"
-  resolved "https://registry.yarnpkg.com/@types/tapable/-/tapable-1.0.8.tgz#b94a4391c85666c7b73299fd3ad79d4faa435310"
-  integrity sha512-ipixuVrh2OdNmauvtT51o3d8z12p6LtFW9in7U79der/kwejjdNchQC5UMn5u/KxNoM7VHHOs/l8KS8uHxhODQ==
-
-"@types/uglify-js@*":
-  version "3.13.1"
-  resolved "https://registry.yarnpkg.com/@types/uglify-js/-/uglify-js-3.13.1.tgz#5e889e9e81e94245c75b6450600e1c5ea2878aea"
-  integrity sha512-O3MmRAk6ZuAKa9CHgg0Pr0+lUOqoMLpc9AS4R8ano2auvsg7IE8syF3Xh/NPr26TWklxYcqoEEFdzLLs1fV9PQ==
-  dependencies:
-    source-map "^0.6.1"
-
-"@types/webpack-sources@*":
-  version "3.2.0"
-  resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-3.2.0.tgz#16d759ba096c289034b26553d2df1bf45248d38b"
-  integrity sha512-Ft7YH3lEVRQ6ls8k4Ff1oB4jN6oy/XmU6tQISKdhfh+1mR+viZFphS6WL0IrtDOzvefmJg5a0s7ZQoRXwqTEFg==
-  dependencies:
-    "@types/node" "*"
-    "@types/source-list-map" "*"
-    source-map "^0.7.3"
-
-"@types/webpack@^4.4.19":
-  version "4.41.31"
-  resolved "https://registry.yarnpkg.com/@types/webpack/-/webpack-4.41.31.tgz#c35f252a3559ddf9c85c0d8b0b42019025e581aa"
-  integrity sha512-/i0J7sepXFIp1ZT7FjUGi1eXMCg8HCCzLJEQkKsOtbJFontsJLolBcDC+3qxn5pPwiCt1G0ZdRmYRzNBtvpuGQ==
-  dependencies:
-    "@types/node" "*"
-    "@types/tapable" "^1"
-    "@types/uglify-js" "*"
-    "@types/webpack-sources" "*"
-    anymatch "^3.0.0"
-    source-map "^0.6.0"
-
 "@types/yargs-parser@*":
   version "20.2.1"
   resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.1.tgz#3b9ce2489919d9e4fea439b76916abc34b2df129"
@@ -1585,16 +1580,6 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2:
   resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d"
   integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==
 
-ajv@^5.5.2:
-  version "5.5.2"
-  resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965"
-  integrity sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=
-  dependencies:
-    co "^4.6.0"
-    fast-deep-equal "^1.0.0"
-    fast-json-stable-stringify "^2.0.0"
-    json-schema-traverse "^0.3.0"
-
 ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4:
   version "6.12.6"
   resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
@@ -1739,7 +1724,7 @@ anymatch@^2.0.0:
     micromatch "^3.1.4"
     normalize-path "^2.1.1"
 
-anymatch@^3.0.0, anymatch@~3.1.2:
+anymatch@~3.1.2:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
   integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
@@ -1791,6 +1776,11 @@ argparse@^1.0.7:
   dependencies:
     sprintf-js "~1.0.2"
 
+argparse@^2.0.1:
+  version "2.0.1"
+  resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+  integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
 argv@0.0.2:
   version "0.0.2"
   resolved "https://registry.yarnpkg.com/argv/-/argv-0.0.2.tgz#ecbd16f8949b157183711b1bda334f37840185ab"
@@ -2043,19 +2033,6 @@ bcrypt-pbkdf@^1.0.0:
   dependencies:
     tweetnacl "^0.14.3"
 
-better-ajv-errors@^0.6.1, better-ajv-errors@^0.6.7:
-  version "0.6.7"
-  resolved "https://registry.yarnpkg.com/better-ajv-errors/-/better-ajv-errors-0.6.7.tgz#b5344af1ce10f434fe02fc4390a5a9c811e470d1"
-  integrity sha512-PYgt/sCzR4aGpyNy5+ViSQ77ognMnWq7745zM+/flYO4/Yisdtp9wDQW2IKCyVYPUxQt3E/b5GBSwfhd1LPdlg==
-  dependencies:
-    "@babel/code-frame" "^7.0.0"
-    "@babel/runtime" "^7.0.0"
-    chalk "^2.4.1"
-    core-js "^3.2.1"
-    json-to-ast "^2.0.3"
-    jsonpointer "^4.0.1"
-    leven "^3.1.0"
-
 big.js@^3.1.3:
   version "3.2.0"
   resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e"
@@ -2566,15 +2543,6 @@ cliui@^5.0.0:
     strip-ansi "^5.2.0"
     wrap-ansi "^5.1.0"
 
-cliui@^6.0.0:
-  version "6.0.0"
-  resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1"
-  integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==
-  dependencies:
-    string-width "^4.2.0"
-    strip-ansi "^6.0.0"
-    wrap-ansi "^6.2.0"
-
 cliui@^7.0.2:
   version "7.0.4"
   resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
@@ -2604,11 +2572,6 @@ co@^4.6.0:
   resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
   integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=
 
-code-error-fragment@0.0.230:
-  version "0.0.230"
-  resolved "https://registry.yarnpkg.com/code-error-fragment/-/code-error-fragment-0.0.230.tgz#d736d75c832445342eca1d1fedbf17d9618b14d7"
-  integrity sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw==
-
 code-point-at@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
@@ -2673,6 +2636,11 @@ color@^3.1.0:
     color-convert "^1.9.1"
     color-string "^1.5.2"
 
+colorette@^1.2.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40"
+  integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==
+
 colorette@^1.2.2:
   version "1.2.2"
   resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
@@ -2873,11 +2841,6 @@ core-js@^3.18.0:
   resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.18.1.tgz#289d4be2ce0085d40fc1244c0b1a54c00454622f"
   integrity sha512-vJlUi/7YdlCZeL6fXvWNaLUPh/id12WXj3MbkMw5uOyF0PfWPBNOCNbs53YqgrvtujLNlt9JQpruyIKkUZ+PKA==
 
-core-js@^3.2.1:
-  version "3.15.2"
-  resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.15.2.tgz#740660d2ff55ef34ce664d7e2455119c5bdd3d61"
-  integrity sha512-tKs41J7NJVuaya8DxIOCnl8QuPHx5/ZVbFo1oKgVl1qHFBBrDctzQGtuLjPpRdNTWmKPH6oEvgN/MUID+l485Q==
-
 core-util-is@1.0.2, core-util-is@~1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@@ -3355,10 +3318,10 @@ domhandler@^4.0.0, domhandler@^4.2.0:
   dependencies:
     domelementtype "^2.2.0"
 
-dompurify@^2.0.12:
-  version "2.3.0"
-  resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.0.tgz#07bb39515e491588e5756b1d3e8375b5964814e2"
-  integrity sha512-VV5C6Kr53YVHGOBKO/F86OYX6/iLTw2yVSI721gKetxpHCK/V5TaLEf9ODjRgl1KLSWRMY6cUhAbv/c+IUnwQw==
+dompurify@^2.2.8:
+  version "2.3.6"
+  resolved "https://registry.yarnpkg.com/dompurify/-/dompurify-2.3.6.tgz#2e019d7d7617aacac07cbbe3d88ae3ad354cf875"
+  integrity sha512-OFP2u/3T1R5CEgWCEONuJ1a5+MFKnOYpkywpUSxv/dj1LeBT1erK+JwM7zK0ROy2BRhqVCf0LRw/kHqKuMkVGg==
 
 domutils@^2.5.2, domutils@^2.6.0:
   version "2.7.0"
@@ -3628,7 +3591,7 @@ etag@~1.8.1:
   resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
   integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
 
-eventemitter3@^4.0.0, eventemitter3@^4.0.4:
+eventemitter3@^4.0.0, eventemitter3@^4.0.7:
   version "4.0.7"
   resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
   integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
@@ -3783,11 +3746,6 @@ extsprintf@1.3.0, extsprintf@^1.2.0:
   resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
   integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=
 
-fast-deep-equal@^1.0.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614"
-  integrity sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=
-
 fast-deep-equal@^3.1.1:
   version "3.1.3"
   resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
@@ -3994,11 +3952,6 @@ form-data@^2.3.1, form-data@~2.3.2:
     combined-stream "^1.0.6"
     mime-types "^2.1.12"
 
-format-util@^1.0.3:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271"
-  integrity sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg==
-
 formidable@^1.2.0:
   version "1.2.2"
   resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.2.tgz#bf69aea2972982675f00865342b982986f6b8dd9"
@@ -4273,11 +4226,6 @@ graceful-fs@^4.2.3:
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
   integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
 
-grapheme-splitter@^1.0.4:
-  version "1.0.4"
-  resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e"
-  integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==
-
 graphlib@^2.1.7:
   version "2.1.8"
   resolved "https://registry.yarnpkg.com/graphlib/-/graphlib-2.1.8.tgz#5761d414737870084c92ec7b5dbcb0592c9d35da"
@@ -5523,7 +5471,7 @@ js-base64@^2.1.8:
   resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4"
   integrity sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==
 
-js-levenshtein@^1.1.3:
+js-levenshtein@^1.1.3, js-levenshtein@^1.1.6:
   version "1.1.6"
   resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
   integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==
@@ -5533,7 +5481,7 @@ js-levenshtein@^1.1.3:
   resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
   integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
 
-js-yaml@3.13.1, js-yaml@^3.12.1, js-yaml@^3.13.1, js-yaml@^3.9.0:
+js-yaml@3.13.1, js-yaml@^3.13.1, js-yaml@^3.9.0:
   version "3.13.1"
   resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
   integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
@@ -5541,6 +5489,13 @@ js-yaml@3.13.1, js-yaml@^3.12.1, js-yaml@^3.13.1, js-yaml@^3.9.0:
     argparse "^1.0.7"
     esprima "^4.0.0"
 
+js-yaml@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
+  integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+  dependencies:
+    argparse "^2.0.1"
+
 jsbn@~0.1.0:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
@@ -5605,32 +5560,23 @@ json-parse-even-better-errors@^2.3.0:
   resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
   integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
 
-json-pointer@^0.6.0, json-pointer@^0.6.1:
+json-pointer@^0.6.1:
   version "0.6.1"
   resolved "https://registry.yarnpkg.com/json-pointer/-/json-pointer-0.6.1.tgz#3c6caa6ac139e2599f5a1659d39852154015054d"
   integrity sha512-3OvjqKdCBvH41DLpV4iSt6v2XhZXV1bPB4OROuknvUXI7ZQNofieCPkmE26stEJ9zdQuvIxDHCuYhfgxFAAs+Q==
   dependencies:
     foreach "^2.0.4"
 
-json-schema-ref-parser@^6.1.0:
-  version "6.1.0"
-  resolved "https://registry.yarnpkg.com/json-schema-ref-parser/-/json-schema-ref-parser-6.1.0.tgz#30af34aeab5bee0431da805dac0eb21b574bf63d"
-  integrity sha512-pXe9H1m6IgIpXmE5JSb8epilNTGsmTb2iPohAXpOdhqGFbQjNeHHsZxU+C8w6T81GZxSPFLeUoqDJmzxx5IGuw==
-  dependencies:
-    call-me-maybe "^1.0.1"
-    js-yaml "^3.12.1"
-    ono "^4.0.11"
-
-json-schema-traverse@^0.3.0:
-  version "0.3.1"
-  resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340"
-  integrity sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=
-
 json-schema-traverse@^0.4.1:
   version "0.4.1"
   resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
   integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
 
+json-schema-traverse@^1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
+  integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
+
 json-schema@0.2.3:
   version "0.2.3"
   resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13"
@@ -5641,14 +5587,6 @@ json-stringify-safe@~5.0.1:
   resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
   integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
 
-json-to-ast@^2.0.3:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/json-to-ast/-/json-to-ast-2.1.0.tgz#041a9fcd03c0845036acb670d29f425cea4faaf9"
-  integrity sha512-W9Lq347r8tA1DfMvAGn9QNcgYm4Wm7Yc+k8e6vezpMnRT+NHbtlxgNBXRVjXe9YM6eTn6+p/MKOlV/aABJcSnQ==
-  dependencies:
-    code-error-fragment "0.0.230"
-    grapheme-splitter "^1.0.4"
-
 json2mq@^0.2.0:
   version "0.2.0"
   resolved "https://registry.yarnpkg.com/json2mq/-/json2mq-0.2.0.tgz#b637bd3ba9eabe122c83e9720483aeb10d2c904a"
@@ -5680,11 +5618,6 @@ json5@^1.0.1:
   dependencies:
     minimist "^1.2.0"
 
-jsonpointer@^4.0.1:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.1.0.tgz#501fb89986a2389765ba09e6053299ceb4f2c2cc"
-  integrity sha512-CXcRvMyTlnR53xMcKnuMzfCA5i/nfblTnnr74CZb6C4vG39eu6w51t7nKmU5MfLfbTgGItliNyjO/ciNPDqClg==
-
 jsprim@^1.2.2:
   version "1.4.1"
   resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"
@@ -5829,6 +5762,11 @@ lodash.camelcase@^4.3.0:
   resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
   integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
 
+lodash.isequal@^4.5.0:
+  version "4.5.0"
+  resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
+  integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
+
 lodash.memoize@4.x:
   version "4.1.2"
   resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
@@ -5880,10 +5818,10 @@ lru-cache@^6.0.0:
   dependencies:
     yallist "^4.0.0"
 
-lunr@2.3.8:
-  version "2.3.8"
-  resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.8.tgz#a8b89c31f30b5a044b97d2d28e2da191b6ba2072"
-  integrity sha512-oxMeX/Y35PNFuZoHp+jUj5OSEmLCaIH4KTFJh7a93cHBoFmpw2IoPs22VIz7vyO2YUnx2Tn9dzIwO2P/4quIRg==
+lunr@^2.3.9:
+  version "2.3.9"
+  resolved "https://registry.yarnpkg.com/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1"
+  integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==
 
 make-dir@^2.0.0, make-dir@^2.1.0:
   version "2.1.0"
@@ -5939,10 +5877,10 @@ mark.js@^8.11.1:
   resolved "https://registry.yarnpkg.com/mark.js/-/mark.js-8.11.1.tgz#180f1f9ebef8b0e638e4166ad52db879beb2ffc5"
   integrity sha1-GA8fnr74sOY45BZq1S24eb6y/8U=
 
-marked@^0.7.0:
-  version "0.7.0"
-  resolved "https://registry.yarnpkg.com/marked/-/marked-0.7.0.tgz#b64201f051d271b1edc10a04d1ae9b74bb8e5c0e"
-  integrity sha512-c+yYdCZJQrsRjTPhUx7VKkApw9bwDkNbHUKo1ovgcfDjb2kc8rLuRbIFyXL5WOEUwzSSKo3IXpph2K6DqB/KZg==
+marked@^4.0.10:
+  version "4.0.12"
+  resolved "https://registry.yarnpkg.com/marked/-/marked-4.0.12.tgz#2262a4e6fd1afd2f13557726238b69a48b982f7d"
+  integrity sha512-hgibXWrEDNBWgGiK18j/4lkS6ihTe9sxtV4Q1OQppb/0zzyPSzoFANBa5MfsG/zgsWklmNnhm0XACZOH/0HBiQ==
 
 md5.js@^1.3.4:
   version "1.3.5"
@@ -5963,11 +5901,6 @@ memoize-one@^6.0.0:
   resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-6.0.0.tgz#b2591b871ed82948aee4727dc6abceeeac8c1045"
   integrity sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==
 
-memoize-one@~5.1.1:
-  version "5.1.1"
-  resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0"
-  integrity sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==
-
 memory-fs@^0.4.1:
   version "0.4.1"
   resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
@@ -6202,17 +6135,17 @@ mkdirp@^1.0.3, mkdirp@^1.0.4:
   resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
   integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
 
-mobx-react-lite@^2.2.0:
-  version "2.2.2"
-  resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-2.2.2.tgz#87c217dc72b4e47b22493daf155daf3759f868a6"
-  integrity sha512-2SlXALHIkyUPDsV4VTKVR9DW7K3Ksh1aaIv3NrNJygTbhXe2A9GrcKHZ2ovIiOp/BXilOcTYemfHHZubP431dg==
+mobx-react-lite@^3.3.0:
+  version "3.3.0"
+  resolved "https://registry.yarnpkg.com/mobx-react-lite/-/mobx-react-lite-3.3.0.tgz#7174e807201943beff6f9d3701492314c9fc0db3"
+  integrity sha512-U/kMSFtV/bNVgY01FuiGWpRkaQVHozBq5CEBZltFvPt4FcV111hEWkgwqVg9GPPZSEuEdV438PEz8mk8mKpYlA==
 
-mobx-react@^6.2.2:
-  version "6.3.1"
-  resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-6.3.1.tgz#204f9756e42e19d91cb6598837063b7e7de87c52"
-  integrity sha512-IOxdJGnRSNSJrL2uGpWO5w9JH5q5HoxEqwOF4gye1gmZYdjoYkkMzSGMDnRCUpN/BNzZcFoMdHXrjvkwO7KgaQ==
+mobx-react@^7.2.0:
+  version "7.3.0"
+  resolved "https://registry.yarnpkg.com/mobx-react/-/mobx-react-7.3.0.tgz#a17dedb71b75dad2337e3f95eb753179f6cfe732"
+  integrity sha512-RGEcwZokopqyJE5JPwXKB9FWMSqFM9NJVO2QPI+z6laJTJeBHqvPicjnKgY5mvihxTeXB1+72TnooqUePeGV1g==
   dependencies:
-    mobx-react-lite "^2.2.0"
+    mobx-react-lite "^3.3.0"
 
 moment-timezone@^0.5.33:
   version "0.5.33"
@@ -6226,17 +6159,17 @@ moment-timezone@^0.5.33:
   resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3"
   integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==
 
-monaco-editor-webpack-plugin@^1.7.0:
-  version "1.7.0"
-  resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-1.7.0.tgz#920cbeecca25f15d70d568a7e11b0ba4daf1ae83"
-  integrity sha512-oItymcnlL14Sjd7EF7q+CMhucfwR/2BxsqrXIBrWL6LQplFfAfV+grLEQRmVHeGSBZ/Gk9ptzfueXnWcoEcFuA==
+monaco-editor-webpack-plugin@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/monaco-editor-webpack-plugin/-/monaco-editor-webpack-plugin-6.0.0.tgz#628956ce1851afa2a5f6c88d0ecbb24e9a444898"
+  integrity sha512-vC886Mzpd2AkSM35XLkfQMjH+Ohz6RISVwhAejDUzZDheJAiz6G34lky1vyO8fZ702v7IrcKmsGwL1rRFnwvUA==
   dependencies:
-    "@types/webpack" "^4.4.19"
+    loader-utils "^2.0.0"
 
-monaco-editor@^0.15.6:
-  version "0.15.6"
-  resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.15.6.tgz#d63b3b06f86f803464f003b252627c3eb4a09483"
-  integrity sha512-JoU9V9k6KqT9R9Tiw1RTU8ohZ+Xnf9DMg6Ktqqw5hILumwmq7xqa/KLXw513uTUsWbhtnHoSJYYR++u3pkyxJg==
+monaco-editor@^0.27.0:
+  version "0.27.0"
+  resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.27.0.tgz#4b69108bb1dc1f60174c5dcdf51bc5306ab5ba26"
+  integrity sha512-UhwP78Wb8w0ZSYoKXQNTV/0CHObp6NS3nCt51QfKE6sKyBo5PBsvuDOHoI2ooBakc6uIwByRLHVeT7+yXQe2fQ==
 
 move-concurrently@^1.0.1:
   version "1.0.1"
@@ -6352,6 +6285,13 @@ node-fetch@^2.2.0:
   resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
   integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
 
+node-fetch@^2.6.1:
+  version "2.6.7"
+  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
+  integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
+  dependencies:
+    whatwg-url "^5.0.0"
+
 node-forge@^0.10.0:
   version "0.10.0"
   resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
@@ -6541,7 +6481,7 @@ oas-kit-common@^1.0.8:
   dependencies:
     fast-safe-stringify "^2.0.7"
 
-oas-linter@^3.1.3:
+oas-linter@^3.2.2:
   version "3.2.2"
   resolved "https://registry.yarnpkg.com/oas-linter/-/oas-linter-3.2.2.tgz#ab6a33736313490659035ca6802dc4b35d48aa1e"
   integrity sha512-KEGjPDVoU5K6swgo9hJVA/qYGlwfbFx+Kg2QB/kd7rzV5N8N5Mg6PlsoCMohVnQmo+pzJap/F610qTodKzecGQ==
@@ -6550,7 +6490,7 @@ oas-linter@^3.1.3:
     should "^13.2.1"
     yaml "^1.10.0"
 
-oas-resolver@^2.4.3:
+oas-resolver@^2.5.6:
   version "2.5.6"
   resolved "https://registry.yarnpkg.com/oas-resolver/-/oas-resolver-2.5.6.tgz#10430569cb7daca56115c915e611ebc5515c561b"
   integrity sha512-Yx5PWQNZomfEhPPOphFbZKi9W93CocQj18NlD2Pa4GWZzdZpSJvYwoiuurRI7m3SpcChrnO08hkuQDL3FGsVFQ==
@@ -6566,21 +6506,19 @@ oas-schema-walker@^1.1.5:
   resolved "https://registry.yarnpkg.com/oas-schema-walker/-/oas-schema-walker-1.1.5.tgz#74c3cd47b70ff8e0b19adada14455b5d3ac38a22"
   integrity sha512-2yucenq1a9YPmeNExoUa9Qwrt9RFkjqaMAA1X+U7sbb0AqBeTIdMHky9SQQ6iN94bO5NW0W4TRYXerG+BdAvAQ==
 
-oas-validator@^4.0.8:
-  version "4.0.8"
-  resolved "https://registry.yarnpkg.com/oas-validator/-/oas-validator-4.0.8.tgz#4f1a4d6bd9e030ad07db03fd7a7bc3a91aabcc7d"
-  integrity sha512-bIt8erTyclF7bkaySTtQ9sppqyVc+mAlPi7vPzCLVHJsL9nrivQjc/jHLX/o+eGbxHd6a6YBwuY/Vxa6wGsiuw==
+oas-validator@^5.0.8:
+  version "5.0.8"
+  resolved "https://registry.yarnpkg.com/oas-validator/-/oas-validator-5.0.8.tgz#387e90df7cafa2d3ffc83b5fb976052b87e73c28"
+  integrity sha512-cu20/HE5N5HKqVygs3dt94eYJfBi0TsZvPVXDhbXQHiEityDN+RROTleefoKRKKJ9dFAF2JBkDHgvWj0sjKGmw==
   dependencies:
-    ajv "^5.5.2"
-    better-ajv-errors "^0.6.7"
     call-me-maybe "^1.0.1"
     oas-kit-common "^1.0.8"
-    oas-linter "^3.1.3"
-    oas-resolver "^2.4.3"
+    oas-linter "^3.2.2"
+    oas-resolver "^2.5.6"
     oas-schema-walker "^1.1.5"
-    reftools "^1.1.5"
+    reftools "^1.1.9"
     should "^13.2.1"
-    yaml "^1.8.3"
+    yaml "^1.10.0"
 
 oauth-sign@~0.9.0:
   version "0.9.0"
@@ -6676,17 +6614,10 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
   dependencies:
     wrappy "1"
 
-ono@^4.0.11:
-  version "4.0.11"
-  resolved "https://registry.yarnpkg.com/ono/-/ono-4.0.11.tgz#c7f4209b3e396e8a44ef43b9cedc7f5d791d221d"
-  integrity sha512-jQ31cORBFE6td25deYeD80wxKBMj+zBmHTrVxnc6CKhx8gho6ipmWM5zj/oeoqioZ99yqBls9Z/9Nss7J26G2g==
-  dependencies:
-    format-util "^1.0.3"
-
-openapi-sampler@^1.0.0-beta.16:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/openapi-sampler/-/openapi-sampler-1.1.0.tgz#73e45a599e8f7892be5a728ff21e784b8d2284bd"
-  integrity sha512-/LhZYKNBWphLEpbAG5BdpBZbIbmLgC4vTiTj8N/MV0LF9ptmKOiJ2nETVlacNjXHt7iqDgZDELJCIoZ3q5ZG6A==
+openapi-sampler@^1.1.1:
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/openapi-sampler/-/openapi-sampler-1.2.0.tgz#2e82e4e32c1506a42f370be726d759d5275a9d4d"
+  integrity sha512-Y0tFg2iH7NWnNHYnesxhMfkXc7wWXyJXYMUTTxkGkfpl0U9u/ZOf6BxrdEXBD4sgs9uMlVWsbWLDLesVmSUU7Q==
   dependencies:
     "@types/json-schema" "^7.0.7"
     json-pointer "^0.6.1"
@@ -6886,6 +6817,11 @@ path-browserify@0.0.1:
   resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a"
   integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==
 
+path-browserify@^1.0.1:
+  version "1.0.1"
+  resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
+  integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==
+
 path-dirname@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0"
@@ -6961,10 +6897,10 @@ pbkdf2@^3.0.3:
     safe-buffer "^5.0.1"
     sha.js "^2.4.8"
 
-perfect-scrollbar@^1.4.0:
-  version "1.5.1"
-  resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.5.1.tgz#8ee5b3ca06ce9c3f7338fd4ab67a55248a6cf3be"
-  integrity sha512-MrSImINnIh3Tm1hdPT6bji6fmIeRorVEegQvyUnhqko2hDGTHhmjPefHXfxG/Jb8xVbfCwgmUIlIajERGXjVXQ==
+perfect-scrollbar@^1.5.1:
+  version "1.5.5"
+  resolved "https://registry.yarnpkg.com/perfect-scrollbar/-/perfect-scrollbar-1.5.5.tgz#41a211a2fb52a7191eff301432134ea47052b27f"
+  integrity sha512-dzalfutyP3e/FOpdlhVryN4AJ5XDVauVWxybSkLZmakFE2sS3y3pc4JnSprw8tGmHvkaG5Edr5T7LBTZ+WWU2g==
 
 performance-now@^2.1.0:
   version "2.1.0"
@@ -7024,17 +6960,22 @@ pkg-dir@^4.1.0:
   dependencies:
     find-up "^4.0.0"
 
+pluralize@^8.0.0:
+  version "8.0.0"
+  resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1"
+  integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==
+
 pn@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb"
   integrity sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==
 
-polished@^3.6.5:
-  version "3.7.2"
-  resolved "https://registry.yarnpkg.com/polished/-/polished-3.7.2.tgz#ec5ddc17a7d322a574d5e10ddd2a6f01d3e767d1"
-  integrity sha512-pQKtpZGmsZrW8UUpQMAnR7s3ppHeMQVNyMDKtUyKwuvDmklzcEyM5Kllb3JyE/sE/x7arDmyd35i+4vp99H6sQ==
+polished@^4.1.3:
+  version "4.1.4"
+  resolved "https://registry.yarnpkg.com/polished/-/polished-4.1.4.tgz#640293ba834109614961a700fdacbb6599fb12d0"
+  integrity sha512-Nq5Mbza+Auo7N3sQb1QMFaQiDO+4UexWuSGR7Cjb4Sw11SZIJcrrFtiZ+L0jT9MBsUsxDboHVASbCLbE1rnECg==
   dependencies:
-    "@babel/runtime" "^7.12.5"
+    "@babel/runtime" "^7.16.7"
 
 popper.js@^1.14.7:
   version "1.16.1"
@@ -7187,10 +7128,10 @@ pretty-format@^27.0.6:
     ansi-styles "^5.0.0"
     react-is "^17.0.1"
 
-prismjs@^1.20.0:
-  version "1.25.0"
-  resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.25.0.tgz#6f822df1bdad965734b310b315a23315cf999756"
-  integrity sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg==
+prismjs@^1.24.1:
+  version "1.27.0"
+  resolved "https://registry.yarnpkg.com/prismjs/-/prismjs-1.27.0.tgz#bb6ee3138a0b438a3653dd4d6ce0cc6510a45057"
+  integrity sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==
 
 process-nextick-args@~2.0.0:
   version "2.0.1"
@@ -7915,10 +7856,10 @@ react-svg-piechart@^2.1.1:
   resolved "https://registry.yarnpkg.com/react-svg-piechart/-/react-svg-piechart-2.4.0.tgz#93e1e2196fee6b664d7435ed9411a7f196334c2c"
   integrity sha512-IXqvZCxTuiipwPgROyHi6TihR9PVaGDSnqd14Q2NUXx2PzbNnNcW2nVwLOPLUZm3xEVPzaQhBwxrwrsGLh1uDQ==
 
-react-tabs@^3.1.1:
-  version "3.2.2"
-  resolved "https://registry.yarnpkg.com/react-tabs/-/react-tabs-3.2.2.tgz#07bdc3cdb17bdffedd02627f32a93cd4b3d6e4d0"
-  integrity sha512-/o52eGKxFHRa+ssuTEgSM8qORnV4+k7ibW+aNQzKe+5gifeVz8nLxCrsI9xdRhfb0wCLdgIambIpb1qCxaMN+A==
+react-tabs@^3.2.2:
+  version "3.2.3"
+  resolved "https://registry.yarnpkg.com/react-tabs/-/react-tabs-3.2.3.tgz#ccbb3e1241ad3f601047305c75db661239977f2f"
+  integrity sha512-jx325RhRVnS9DdFbeF511z0T0WEqEoMl1uCE3LoZ6VaZZm7ytatxbum0B8bCTmaiV0KsU+4TtLGTGevCic7SWg==
   dependencies:
     clsx "^1.1.0"
     prop-types "^15.5.0"
@@ -8061,34 +8002,33 @@ redent@^3.0.0:
     indent-string "^4.0.0"
     strip-indent "^3.0.0"
 
-redoc@^2.0.0-rc.40:
-  version "2.0.0-rc.40"
-  resolved "https://registry.yarnpkg.com/redoc/-/redoc-2.0.0-rc.40.tgz#4dd7d095265bd2af464069e684863a5b17f7a507"
-  integrity sha512-f1na5vWCr37R5+G4xhbD1TjH6j6b/he8nEMGGJOwDcIMMcDK88S0YEWuhplhdVuZdc4c61CoxZqGXqcDRp5m0w==
+redoc@^2.0.0-rc.63:
+  version "2.0.0-rc.63"
+  resolved "https://registry.yarnpkg.com/redoc/-/redoc-2.0.0-rc.63.tgz#d1bea51d3fdf2a6f5371a9bf7f06253ac5c1a487"
+  integrity sha512-PsoPqRyNqHi7+jKUyFBwJhHrzjMl4N5vieTeBloRGbhWuY3PPH2DJ3ihgrLfdEV0glzq/LMTaqfarm8WLqCc4Q==
   dependencies:
+    "@redocly/openapi-core" "^1.0.0-beta.54"
     "@redocly/react-dropdown-aria" "^2.0.11"
-    "@types/node" "^13.11.1"
-    classnames "^2.2.6"
+    classnames "^2.3.1"
     decko "^1.2.0"
-    dompurify "^2.0.12"
-    eventemitter3 "^4.0.4"
-    json-pointer "^0.6.0"
-    json-schema-ref-parser "^6.1.0"
-    lunr "2.3.8"
+    dompurify "^2.2.8"
+    eventemitter3 "^4.0.7"
+    json-pointer "^0.6.1"
+    lunr "^2.3.9"
     mark.js "^8.11.1"
-    marked "^0.7.0"
-    memoize-one "~5.1.1"
-    mobx-react "^6.2.2"
-    openapi-sampler "^1.0.0-beta.16"
-    perfect-scrollbar "^1.4.0"
-    polished "^3.6.5"
-    prismjs "^1.20.0"
+    marked "^4.0.10"
+    mobx-react "^7.2.0"
+    openapi-sampler "^1.1.1"
+    path-browserify "^1.0.1"
+    perfect-scrollbar "^1.5.1"
+    polished "^4.1.3"
+    prismjs "^1.24.1"
     prop-types "^15.7.2"
-    react-tabs "^3.1.1"
-    slugify "^1.4.4"
+    react-tabs "^3.2.2"
+    slugify "~1.4.7"
     stickyfill "^1.1.1"
-    swagger2openapi "^6.2.1"
-    tslib "^2.0.0"
+    style-loader "^3.3.1"
+    swagger2openapi "^7.0.6"
     url-template "^2.0.8"
 
 redux-logger@^3.0.6:
@@ -8113,7 +8053,7 @@ redux@^3.7.2:
     loose-envify "^1.1.0"
     symbol-observable "^1.0.3"
 
-reftools@^1.1.5, reftools@^1.1.9:
+reftools@^1.1.9:
   version "1.1.9"
   resolved "https://registry.yarnpkg.com/reftools/-/reftools-1.1.9.tgz#e16e19f662ccd4648605312c06d34e5da3a2b77e"
   integrity sha512-OVede/NQE13xBQ+ob5CKd5KyeJYU2YInb1bmV4nRoOfquZPkAkxuOXicSe1PvqIuZZ4kD13sPKBbR7UFDmli6w==
@@ -8291,7 +8231,7 @@ require-directory@^2.1.1:
   resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
   integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
 
-require-from-string@^2.0.1:
+require-from-string@^2.0.1, require-from-string@^2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
   integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
@@ -8795,10 +8735,10 @@ slash@^3.0.0:
   resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
   integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
 
-slugify@^1.4.4:
-  version "1.5.3"
-  resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.5.3.tgz#36e009864f5476bfd5db681222643d92339c890d"
-  integrity sha512-/HkjRdwPY3yHJReXu38NiusZw2+LLE2SrhkWJtmlPDB1fqFSvioYj62NkPcrKiNCgRLeGcGK7QBvr1iQwybeXw==
+slugify@~1.4.7:
+  version "1.4.7"
+  resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.4.7.tgz#e42359d505afd84a44513280868e31202a79a628"
+  integrity sha512-tf+h5W1IrjNm/9rKKj0JU2MDMruiopx0jjVA5zCdBtcGjfp0+c5rHw/zADLC3IeKlGHtVbHtpfzvYA0OYT+HKg==
 
 snapdragon-node@^2.0.1:
   version "2.1.1"
@@ -8910,11 +8850,6 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
   integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
 
-source-map@^0.7.3:
-  version "0.7.3"
-  resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
-  integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
-
 sourcemapped-stacktrace@^1.1.6:
   version "1.1.11"
   resolved "https://registry.yarnpkg.com/sourcemapped-stacktrace/-/sourcemapped-stacktrace-1.1.11.tgz#e2dede7fc148599c52a4f883276e527f8452657d"
@@ -9247,6 +9182,11 @@ style-loader@^0.20.1:
     loader-utils "^1.1.0"
     schema-utils "^0.4.5"
 
+style-loader@^3.3.1:
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-3.3.1.tgz#057dfa6b3d4d7c7064462830f9113ed417d38575"
+  integrity sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==
+
 superagent-promise@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/superagent-promise/-/superagent-promise-1.1.0.tgz#baf22d8bbdd439a9b07dd10f8c08f54fe2503533"
@@ -9301,22 +9241,22 @@ supports-color@^7.1.0:
   dependencies:
     has-flag "^4.0.0"
 
-swagger2openapi@^6.2.1:
-  version "6.2.3"
-  resolved "https://registry.yarnpkg.com/swagger2openapi/-/swagger2openapi-6.2.3.tgz#4a8059f89d851aee4c9ab178f9b7190debd904e2"
-  integrity sha512-cUUktzLpK69UwpMbcTzjMw2ns9RZChfxh56AHv6+hTx3StPOX2foZjPgds3HlJcINbxosYYBn/D3cG8nwcCWwQ==
+swagger2openapi@^7.0.6:
+  version "7.0.8"
+  resolved "https://registry.yarnpkg.com/swagger2openapi/-/swagger2openapi-7.0.8.tgz#12c88d5de776cb1cbba758994930f40ad0afac59"
+  integrity sha512-upi/0ZGkYgEcLeGieoz8gT74oWHA0E7JivX7aN9mAf+Tc7BQoRBvnIGHoPDw+f9TXTW4s6kGYCZJtauP6OYp7g==
   dependencies:
-    better-ajv-errors "^0.6.1"
     call-me-maybe "^1.0.1"
+    node-fetch "^2.6.1"
     node-fetch-h2 "^2.3.0"
     node-readfiles "^0.2.0"
     oas-kit-common "^1.0.8"
-    oas-resolver "^2.4.3"
+    oas-resolver "^2.5.6"
     oas-schema-walker "^1.1.5"
-    oas-validator "^4.0.8"
-    reftools "^1.1.5"
-    yaml "^1.8.3"
-    yargs "^15.3.1"
+    oas-validator "^5.0.8"
+    reftools "^1.1.9"
+    yaml "^1.10.0"
+    yargs "^17.0.1"
 
 symbol-observable@^1.0.3:
   version "1.2.0"
@@ -9527,6 +9467,11 @@ tr46@^1.0.1:
   dependencies:
     punycode "^2.1.0"
 
+tr46@~0.0.3:
+  version "0.0.3"
+  resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
+  integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=
+
 trim-newlines@^3.0.0:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144"
@@ -9602,11 +9547,6 @@ tslib@^1.7.1, tslib@^1.8.1, tslib@^1.9.0:
   resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
   integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
 
-tslib@^2.0.0:
-  version "2.3.0"
-  resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e"
-  integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==
-
 tslint-config-prettier@^1.18.0:
   version "1.18.0"
   resolved "https://registry.yarnpkg.com/tslint-config-prettier/-/tslint-config-prettier-1.18.0.tgz#75f140bde947d35d8f0d238e0ebf809d64592c37"
@@ -9991,6 +9931,11 @@ wbuf@^1.1.0, wbuf@^1.7.3:
   dependencies:
     minimalistic-assert "^1.0.0"
 
+webidl-conversions@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
+  integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
+
 webidl-conversions@^4.0.2:
   version "4.0.2"
   resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
@@ -10145,6 +10090,14 @@ whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0:
   resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
   integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
 
+whatwg-url@^5.0.0:
+  version "5.0.0"
+  resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
+  integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0=
+  dependencies:
+    tr46 "~0.0.3"
+    webidl-conversions "^3.0.0"
+
 whatwg-url@^6.4.1:
   version "6.5.0"
   resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8"
@@ -10221,15 +10174,6 @@ wrap-ansi@^5.1.0:
     string-width "^3.0.0"
     strip-ansi "^5.0.0"
 
-wrap-ansi@^6.2.0:
-  version "6.2.0"
-  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53"
-  integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==
-  dependencies:
-    ansi-styles "^4.0.0"
-    string-width "^4.1.0"
-    strip-ansi "^6.0.0"
-
 wrap-ansi@^7.0.0:
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
@@ -10307,7 +10251,12 @@ yallist@^4.0.0:
   resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
   integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
 
-yaml@^1.10.0, yaml@^1.8.3:
+yaml-ast-parser@0.0.43:
+  version "0.0.43"
+  resolved "https://registry.yarnpkg.com/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz#e8a23e6fb4c38076ab92995c5dca33f3d3d7c9bb"
+  integrity sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==
+
+yaml@^1.10.0:
   version "1.10.2"
   resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
   integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
@@ -10327,14 +10276,6 @@ yargs-parser@^13.1.2:
     camelcase "^5.0.0"
     decamelize "^1.2.0"
 
-yargs-parser@^18.1.2:
-  version "18.1.3"
-  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
-  integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
-  dependencies:
-    camelcase "^5.0.0"
-    decamelize "^1.2.0"
-
 yargs-parser@^20.2.2, yargs-parser@^20.2.3:
   version "20.2.9"
   resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
@@ -10356,23 +10297,6 @@ yargs@^13.3.0, yargs@^13.3.2:
     y18n "^4.0.0"
     yargs-parser "^13.1.2"
 
-yargs@^15.3.1:
-  version "15.4.1"
-  resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8"
-  integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==
-  dependencies:
-    cliui "^6.0.0"
-    decamelize "^1.2.0"
-    find-up "^4.1.0"
-    get-caller-file "^2.0.1"
-    require-directory "^2.1.1"
-    require-main-filename "^2.0.0"
-    set-blocking "^2.0.0"
-    string-width "^4.2.0"
-    which-module "^2.0.0"
-    y18n "^4.0.0"
-    yargs-parser "^18.1.2"
-
 yargs@^17.0.1:
   version "17.0.1"
   resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.0.1.tgz#6a1ced4ed5ee0b388010ba9fd67af83b9362e0bb"
diff --git a/util/git/client.go b/util/git/client.go
index 17b5ada88..cde2ad3cd 100644
--- a/util/git/client.go
+++ b/util/git/client.go
@@ -623,7 +623,7 @@ func (m *nativeGitClient) runCredentialedCmd(command string, args ...string) err
 
 func (m *nativeGitClient) runCmdOutput(cmd *exec.Cmd) (string, error) {
 	cmd.Dir = m.root
-	cmd.Env = append(cmd.Env, os.Environ()...)
+	cmd.Env = append(os.Environ(), cmd.Env...)
 	// Set $HOME to nowhere, so we can be execute Git regardless of any external
 	// authentication keys (e.g. in ~/.ssh) -- this is especially important for
 	// running tests on local machines and/or CircleCI.
diff --git a/util/git/creds.go b/util/git/creds.go
index 8e44209ed..1d94325c7 100644
--- a/util/git/creds.go
+++ b/util/git/creds.go
@@ -11,22 +11,29 @@ import (
 	"strings"
 	"time"
 
-	gocache "github.com/patrickmn/go-cache"
-
 	argoio "github.com/argoproj/gitops-engine/pkg/utils/io"
+	"github.com/argoproj/gitops-engine/pkg/utils/text"
 	"github.com/bradleyfalzon/ghinstallation/v2"
+	gocache "github.com/patrickmn/go-cache"
 	log "github.com/sirupsen/logrus"
 
 	"github.com/argoproj/argo-cd/v2/common"
-
 	certutil "github.com/argoproj/argo-cd/v2/util/cert"
+	argoioutils "github.com/argoproj/argo-cd/v2/util/io"
 )
 
-// In memory cache for storing github APP api token credentials
 var (
+	// In memory cache for storing github APP api token credentials
 	githubAppTokenCache *gocache.Cache
 )
 
+const (
+	// ASKPASS_NONCE_ENV is the environment variable that is used to pass the nonce to the askpass script
+	ASKPASS_NONCE_ENV = "ARGOCD_GIT_ASKPASS_NONCE"
+	// githubAccessTokenUsername is a username that is used to with the github access token
+	githubAccessTokenUsername = "x-access-token"
+)
+
 func init() {
 	githubAppCredsExp := common.GithubAppCredsExpirationDuration
 	if exp := os.Getenv(common.EnvGithubAppCredsExpirationDuration); exp != "" {
@@ -38,10 +45,34 @@ func init() {
 	githubAppTokenCache = gocache.New(githubAppCredsExp, 1*time.Minute)
 }
 
+type NoopCredsStore struct {
+}
+
+func (d NoopCredsStore) Add(username string, password string) string {
+	return ""
+}
+
+func (d NoopCredsStore) Remove(id string) {
+}
+
+type CredsStore interface {
+	Add(username string, password string) string
+	Remove(id string)
+}
+
 type Creds interface {
 	Environ() (io.Closer, []string, error)
 }
 
+func getGitAskPassEnv(id string) []string {
+	return []string{
+		fmt.Sprintf("GIT_ASKPASS=%s", "argocd"),
+		fmt.Sprintf("%s=%s", ASKPASS_NONCE_ENV, id),
+		"GIT_TERMINAL_PROMPT=0",
+		"ARGOCD_BINARY_NAME=argocd-git-ask-pass",
+	}
+}
+
 // nop implementation
 type NopCloser struct {
 }
@@ -84,9 +115,11 @@ type HTTPSCreds struct {
 	clientCertKey string
 	// HTTP/HTTPS proxy used to access repository
 	proxy string
+	// temporal credentials store
+	store CredsStore
 }
 
-func NewHTTPSCreds(username string, password string, clientCertData string, clientCertKey string, insecure bool, proxy string) GenericHTTPSCreds {
+func NewHTTPSCreds(username string, password string, clientCertData string, clientCertKey string, insecure bool, proxy string, store CredsStore) GenericHTTPSCreds {
 	return HTTPSCreds{
 		username,
 		password,
@@ -94,18 +127,15 @@ func NewHTTPSCreds(username string, password string, clientCertData string, clie
 		clientCertData,
 		clientCertKey,
 		proxy,
+		store,
 	}
 }
 
 // Get additional required environment variables for executing git client to
 // access specific repository via HTTPS.
 func (c HTTPSCreds) Environ() (io.Closer, []string, error) {
-	env := []string{fmt.Sprintf("GIT_ASKPASS=%s", "git-ask-pass.sh"), fmt.Sprintf("GIT_USERNAME=%s", c.username), fmt.Sprintf("GIT_PASSWORD=%s", c.password)}
-	if c.username != "" {
-		env = append(env, fmt.Sprintf("GIT_USERNAME=%s", c.username))
-	} else {
-		env = append(env, fmt.Sprintf("GIT_USERNAME=%s", "x-access-token"))
-	}
+	var env []string
+
 	httpCloser := authFilePaths(make([]string, 0))
 
 	// GIT_SSL_NO_VERIFY is used to tell git not to validate the server's cert at
@@ -157,9 +187,13 @@ func (c HTTPSCreds) Environ() (io.Closer, []string, error) {
 		}
 		// GIT_SSL_KEY is the full path to a client certificate's key to be used
 		env = append(env, fmt.Sprintf("GIT_SSL_KEY=%s", keyFile.Name()))
-
 	}
-	return httpCloser, env, nil
+	nonce := c.store.Add(text.FirstNonEmpty(c.username, githubAccessTokenUsername), c.password)
+	env = append(env, getGitAskPassEnv(nonce)...)
+	return argoioutils.NewCloser(func() error {
+		c.store.Remove(nonce)
+		return httpCloser.Close()
+	}), env, nil
 }
 
 func (g HTTPSCreds) HasClientCert() bool {
@@ -179,10 +213,11 @@ type SSHCreds struct {
 	sshPrivateKey string
 	caPath        string
 	insecure      bool
+	store         CredsStore
 }
 
-func NewSSHCreds(sshPrivateKey string, caPath string, insecureIgnoreHostKey bool) SSHCreds {
-	return SSHCreds{sshPrivateKey, caPath, insecureIgnoreHostKey}
+func NewSSHCreds(sshPrivateKey string, caPath string, insecureIgnoreHostKey bool, store CredsStore) SSHCreds {
+	return SSHCreds{sshPrivateKey, caPath, insecureIgnoreHostKey, store}
 }
 
 type sshPrivateKeyFile string
@@ -249,11 +284,12 @@ type GitHubAppCreds struct {
 	clientCertKey  string
 	insecure       bool
 	proxy          string
+	store          CredsStore
 }
 
 // NewGitHubAppCreds provide github app credentials
-func NewGitHubAppCreds(appID int64, appInstallId int64, privateKey string, baseURL string, repoURL string, clientCertData string, clientCertKey string, insecure bool) GenericHTTPSCreds {
-	return GitHubAppCreds{appID: appID, appInstallId: appInstallId, privateKey: privateKey, baseURL: baseURL, repoURL: repoURL, clientCertData: clientCertData, clientCertKey: clientCertKey, insecure: insecure}
+func NewGitHubAppCreds(appID int64, appInstallId int64, privateKey string, baseURL string, repoURL string, clientCertData string, clientCertKey string, insecure bool, store CredsStore) GenericHTTPSCreds {
+	return GitHubAppCreds{appID: appID, appInstallId: appInstallId, privateKey: privateKey, baseURL: baseURL, repoURL: repoURL, clientCertData: clientCertData, clientCertKey: clientCertKey, insecure: insecure, store: store}
 }
 
 func (g GitHubAppCreds) Environ() (io.Closer, []string, error) {
@@ -261,8 +297,7 @@ func (g GitHubAppCreds) Environ() (io.Closer, []string, error) {
 	if err != nil {
 		return NopCloser{}, nil, err
 	}
-
-	env := []string{fmt.Sprintf("GIT_ASKPASS=%s", "git-ask-pass.sh"), "GIT_USERNAME=x-access-token", fmt.Sprintf("GIT_PASSWORD=%s", token)}
+	var env []string
 	httpCloser := authFilePaths(make([]string, 0))
 
 	// GIT_SSL_NO_VERIFY is used to tell git not to validate the server's cert at
@@ -316,7 +351,12 @@ func (g GitHubAppCreds) Environ() (io.Closer, []string, error) {
 		env = append(env, fmt.Sprintf("GIT_SSL_KEY=%s", keyFile.Name()))
 
 	}
-	return httpCloser, env, nil
+	nonce := g.store.Add(githubAccessTokenUsername, token)
+	env = append(env, getGitAskPassEnv(nonce)...)
+	return argoioutils.NewCloser(func() error {
+		g.store.Remove(nonce)
+		return httpCloser.Close()
+	}), env, nil
 }
 
 // getAccessToken fetches GitHub token using the app id, install id, and private key.


Test patch:
diff --git a/reposerver/askpass/server_test.go b/reposerver/askpass/server_test.go
new file mode 100644
index 000000000..311592d7f
--- /dev/null
+++ b/reposerver/askpass/server_test.go
@@ -0,0 +1,25 @@
+package askpass
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestAdd(t *testing.T) {
+	s := NewServer()
+	nonce := s.Add("foo", "bar")
+
+	assert.Equal(t, "foo", s.creds[nonce].Username)
+	assert.Equal(t, "bar", s.creds[nonce].Password)
+}
+
+func TestRemove(t *testing.T) {
+	s := NewServer()
+	s.creds["some-id"] = Creds{Username: "foo"}
+
+	s.Remove("some-id")
+
+	_, ok := s.creds["some-id"]
+	assert.False(t, ok)
+}
diff --git a/reposerver/repository/repository_test.go b/reposerver/repository/repository_test.go
index f214733e7..6c8162f4c 100644
--- a/reposerver/repository/repository_test.go
+++ b/reposerver/repository/repository_test.go
@@ -72,7 +72,7 @@ func newServiceWithOpt(cf clientFunc) (*Service, *gitmocks.Client) {
 		cacheutil.NewCache(cacheutil.NewInMemoryCache(1*time.Minute)),
 		1*time.Minute,
 		1*time.Minute,
-	), RepoServerInitConstants{ParallelismLimit: 1}, argo.NewResourceTracking())
+	), RepoServerInitConstants{ParallelismLimit: 1}, argo.NewResourceTracking(), &git.NoopCredsStore{})
 
 	chart := "my-chart"
 	version := "1.1.0"
@@ -140,7 +140,7 @@ func TestGenerateYamlManifestInDir(t *testing.T) {
 	assert.Equal(t, countOfManifests, len(res1.Manifests))
 
 	// this will test concatenated manifests to verify we split YAMLs correctly
-	res2, err := GenerateManifests(context.Background(), "./testdata/concatenated", "/", "", &q, false)
+	res2, err := GenerateManifests(context.Background(), "./testdata/concatenated", "/", "", &q, false, &git.NoopCredsStore{})
 	assert.NoError(t, err)
 	assert.Equal(t, 3, len(res2.Manifests))
 }
@@ -1050,9 +1050,8 @@ func TestRunCustomTool(t *testing.T) {
 
 	assert.Equal(t, obj.GetName(), "test-app")
 	assert.Equal(t, obj.GetNamespace(), "test-namespace")
-	assert.Equal(t, "git-ask-pass.sh", obj.GetAnnotations()["GIT_ASKPASS"])
-	assert.Equal(t, "foo", obj.GetAnnotations()["GIT_USERNAME"])
-	assert.Equal(t, "bar", obj.GetAnnotations()["GIT_PASSWORD"])
+	assert.Empty(t, obj.GetAnnotations()["GIT_USERNAME"])
+	assert.Empty(t, obj.GetAnnotations()["GIT_PASSWORD"])
 	// Git client is mocked, so the revision is always mock.Anything
 	assert.Equal(t, map[string]string{"revision": "prefix-mock.Anything"}, obj.GetLabels())
 }
@@ -1062,7 +1061,7 @@ func TestGenerateFromUTF16(t *testing.T) {
 		Repo:              &argoappv1.Repository{},
 		ApplicationSource: &argoappv1.ApplicationSource{},
 	}
-	res1, err := GenerateManifests(context.Background(), "./testdata/utf-16", "/", "", &q, false)
+	res1, err := GenerateManifests(context.Background(), "./testdata/utf-16", "/", "", &q, false, &git.NoopCredsStore{})
 	assert.Nil(t, err)
 	assert.Equal(t, 2, len(res1.Manifests))
 }
diff --git a/resource_customizations/extensions/DaemonSet/actions/action_test.yaml b/resource_customizations/extensions/DaemonSet/actions/action_test.yaml
deleted file mode 100644
index 4298ac4ac..000000000
--- a/resource_customizations/extensions/DaemonSet/actions/action_test.yaml
+++ /dev/null
@@ -1,4 +0,0 @@
-actionTests:
-- action: restart
-  inputPath: testdata/daemonset.yaml
-  expectedOutputPath: testdata/daemonset-restarted.yaml
diff --git a/resource_customizations/extensions/DaemonSet/actions/testdata/daemonset-restarted.yaml b/resource_customizations/extensions/DaemonSet/actions/testdata/daemonset-restarted.yaml
deleted file mode 100644
index 7c43fefb6..000000000
--- a/resource_customizations/extensions/DaemonSet/actions/testdata/daemonset-restarted.yaml
+++ /dev/null
@@ -1,47 +0,0 @@
-apiVersion: extensions/v1beta1
-kind: DaemonSet
-metadata:
-  annotations:
-  creationTimestamp: "2019-09-13T08:52:50Z"
-  generation: 2
-  labels:
-    app.kubernetes.io/instance: extensions
-  name: extensions-daemonset
-  namespace: statefulset
-  resourceVersion: "7471358"
-  selfLink: /apis/extensions/v1beta1/namespaces/statefulset/daemonsets/extensions-daemonset
-  uid: de09964a-d603-11e9-9e69-42010aa8005f
-spec:
-  revisionHistoryLimit: 10
-  selector:
-    matchLabels:
-      name: extensions-daemonset
-  template:
-    metadata:
-      annotations:
-        kubectl.kubernetes.io/restartedAt: "0001-01-01T00:00:00Z"
-      labels:
-        name: extensions-daemonset
-    spec:
-      containers:
-      - image: k8s.gcr.io/nginx-slim:0.8
-        imagePullPolicy: IfNotPresent
-        name: nginx
-        resources: {}
-        terminationMessagePath: /dev/termination-log
-        terminationMessagePolicy: File
-      dnsPolicy: ClusterFirst
-      restartPolicy: Always
-      schedulerName: default-scheduler
-      securityContext: {}
-      terminationGracePeriodSeconds: 30
-  templateGeneration: 2
-  updateStrategy:
-    type: OnDelete
-status:
-  currentNumberScheduled: 4
-  desiredNumberScheduled: 4
-  numberAvailable: 4
-  numberMisscheduled: 0
-  numberReady: 4
-  observedGeneration: 2
diff --git a/resource_customizations/extensions/DaemonSet/actions/testdata/daemonset.yaml b/resource_customizations/extensions/DaemonSet/actions/testdata/daemonset.yaml
deleted file mode 100644
index 791a63a4c..000000000
--- a/resource_customizations/extensions/DaemonSet/actions/testdata/daemonset.yaml
+++ /dev/null
@@ -1,45 +0,0 @@
-apiVersion: extensions/v1beta1
-kind: DaemonSet
-metadata:
-  annotations:
-  creationTimestamp: "2019-09-13T08:52:50Z"
-  generation: 2
-  labels:
-    app.kubernetes.io/instance: extensions
-  name: extensions-daemonset
-  namespace: statefulset
-  resourceVersion: "7471358"
-  selfLink: /apis/extensions/v1beta1/namespaces/statefulset/daemonsets/extensions-daemonset
-  uid: de09964a-d603-11e9-9e69-42010aa8005f
-spec:
-  revisionHistoryLimit: 10
-  selector:
-    matchLabels:
-      name: extensions-daemonset
-  template:
-    metadata:
-      labels:
-        name: extensions-daemonset
-    spec:
-      containers:
-      - image: k8s.gcr.io/nginx-slim:0.8
-        imagePullPolicy: IfNotPresent
-        name: nginx
-        resources: {}
-        terminationMessagePath: /dev/termination-log
-        terminationMessagePolicy: File
-      dnsPolicy: ClusterFirst
-      restartPolicy: Always
-      schedulerName: default-scheduler
-      securityContext: {}
-      terminationGracePeriodSeconds: 30
-  templateGeneration: 2
-  updateStrategy:
-    type: OnDelete
-status:
-  currentNumberScheduled: 4
-  desiredNumberScheduled: 4
-  numberAvailable: 4
-  numberMisscheduled: 0
-  numberReady: 4
-  observedGeneration: 2
diff --git a/resource_customizations/extensions/Deployment/actions/action_test.yaml b/resource_customizations/extensions/Deployment/actions/action_test.yaml
deleted file mode 100644
index 65b58b66a..000000000
--- a/resource_customizations/extensions/Deployment/actions/action_test.yaml
+++ /dev/null
@@ -1,4 +0,0 @@
-actionTests:
-- action: restart
-  inputPath: testdata/deployment.yaml
-  expectedOutputPath: testdata/deployment-restarted.yaml
diff --git a/resource_customizations/extensions/Deployment/actions/testdata/deployment-restarted.yaml b/resource_customizations/extensions/Deployment/actions/testdata/deployment-restarted.yaml
deleted file mode 100644
index 373bd060b..000000000
--- a/resource_customizations/extensions/Deployment/actions/testdata/deployment-restarted.yaml
+++ /dev/null
@@ -1,63 +0,0 @@
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  annotations:
-    deployment.kubernetes.io/revision: "1"
-  creationTimestamp: "2019-09-12T01:33:53Z"
-  generation: 1
-  name: nginx-deploy
-  namespace: default
-  resourceVersion: "6897444"
-  selfLink: /apis/apps/v1/namespaces/default/deployments/nginx-deploy
-  uid: 61689d6d-d4fd-11e9-9e69-42010aa8005f
-spec:
-  progressDeadlineSeconds: 600
-  replicas: 3
-  revisionHistoryLimit: 10
-  selector:
-    matchLabels:
-      app: nginx
-  strategy:
-    rollingUpdate:
-      maxSurge: 25%
-      maxUnavailable: 25%
-    type: RollingUpdate
-  template:
-    metadata:
-      labels:
-        app: nginx
-      annotations:
-        kubectl.kubernetes.io/restartedAt: "0001-01-01T00:00:00Z"
-    spec:
-      containers:
-      - image: nginx:latest
-        imagePullPolicy: Always
-        name: nginx
-        resources: {}
-        terminationMessagePath: /dev/termination-log
-        terminationMessagePolicy: File
-      dnsPolicy: ClusterFirst
-      restartPolicy: Always
-      schedulerName: default-scheduler
-      securityContext: {}
-      terminationGracePeriodSeconds: 30
-status:
-  availableReplicas: 2
-  conditions:
-  - lastTransitionTime: "2019-09-12T01:33:53Z"
-    lastUpdateTime: "2019-09-12T01:33:53Z"
-    message: Deployment does not have minimum availability.
-    reason: MinimumReplicasUnavailable
-    status: "False"
-    type: Available
-  - lastTransitionTime: "2019-09-12T01:33:53Z"
-    lastUpdateTime: "2019-09-12T01:34:05Z"
-    message: ReplicaSet "nginx-deploy-9cb4784bd" is progressing.
-    reason: ReplicaSetUpdated
-    status: "True"
-    type: Progressing
-  observedGeneration: 1
-  readyReplicas: 2
-  replicas: 3
-  unavailableReplicas: 1
-  updatedReplicas: 3
diff --git a/resource_customizations/extensions/Deployment/actions/testdata/deployment.yaml b/resource_customizations/extensions/Deployment/actions/testdata/deployment.yaml
deleted file mode 100644
index 6d41db1de..000000000
--- a/resource_customizations/extensions/Deployment/actions/testdata/deployment.yaml
+++ /dev/null
@@ -1,61 +0,0 @@
-apiVersion: apps/v1
-kind: Deployment
-metadata:
-  annotations:
-    deployment.kubernetes.io/revision: "1"
-  creationTimestamp: "2019-09-12T01:33:53Z"
-  generation: 1
-  name: nginx-deploy
-  namespace: default
-  resourceVersion: "6897444"
-  selfLink: /apis/apps/v1/namespaces/default/deployments/nginx-deploy
-  uid: 61689d6d-d4fd-11e9-9e69-42010aa8005f
-spec:
-  progressDeadlineSeconds: 600
-  replicas: 3
-  revisionHistoryLimit: 10
-  selector:
-    matchLabels:
-      app: nginx
-  strategy:
-    rollingUpdate:
-      maxSurge: 25%
-      maxUnavailable: 25%
-    type: RollingUpdate
-  template:
-    metadata:
-      labels:
-        app: nginx
-    spec:
-      containers:
-      - image: nginx:latest
-        imagePullPolicy: Always
-        name: nginx
-        resources: {}
-        terminationMessagePath: /dev/termination-log
-        terminationMessagePolicy: File
-      dnsPolicy: ClusterFirst
-      restartPolicy: Always
-      schedulerName: default-scheduler
-      securityContext: {}
-      terminationGracePeriodSeconds: 30
-status:
-  availableReplicas: 2
-  conditions:
-  - lastTransitionTime: "2019-09-12T01:33:53Z"
-    lastUpdateTime: "2019-09-12T01:33:53Z"
-    message: Deployment does not have minimum availability.
-    reason: MinimumReplicasUnavailable
-    status: "False"
-    type: Available
-  - lastTransitionTime: "2019-09-12T01:33:53Z"
-    lastUpdateTime: "2019-09-12T01:34:05Z"
-    message: ReplicaSet "nginx-deploy-9cb4784bd" is progressing.
-    reason: ReplicaSetUpdated
-    status: "True"
-    type: Progressing
-  observedGeneration: 1
-  readyReplicas: 2
-  replicas: 3
-  unavailableReplicas: 1
-  updatedReplicas: 3
diff --git a/server/repository/repository_test.go b/server/repository/repository_test.go
index a8e35aa87..2b2770c6c 100644
--- a/server/repository/repository_test.go
+++ b/server/repository/repository_test.go
@@ -6,52 +6,39 @@ import (
 	"testing"
 	"time"
 
+	"github.com/golang-jwt/jwt/v4"
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/status"
-
-	"github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
-
-	"github.com/argoproj/argo-cd/v2/server/cache"
-
-	"github.com/stretchr/testify/mock"
-
-	"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
-
-	"github.com/argoproj/argo-cd/v2/pkg/apiclient/repository"
-
-	"github.com/argoproj/argo-cd/v2/util/db"
-
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/client-go/kubernetes/fake"
 
 	"github.com/argoproj/argo-cd/v2/common"
-	"github.com/argoproj/argo-cd/v2/util/assets"
-	"github.com/argoproj/argo-cd/v2/util/rbac"
-	"github.com/argoproj/argo-cd/v2/util/settings"
-
+	"github.com/argoproj/argo-cd/v2/pkg/apiclient/repository"
+	appsv1 "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
+	fakeapps "github.com/argoproj/argo-cd/v2/pkg/client/clientset/versioned/fake"
+	appinformer "github.com/argoproj/argo-cd/v2/pkg/client/informers/externalversions"
+	applisters "github.com/argoproj/argo-cd/v2/pkg/client/listers/application/v1alpha1"
+	"github.com/argoproj/argo-cd/v2/reposerver/apiclient"
 	"github.com/argoproj/argo-cd/v2/reposerver/apiclient/mocks"
-
-	"github.com/stretchr/testify/assert"
-
-	"github.com/golang-jwt/jwt/v4"
-	corev1 "k8s.io/api/core/v1"
-	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-
+	"github.com/argoproj/argo-cd/v2/server/cache"
+	"github.com/argoproj/argo-cd/v2/util/assets"
 	cacheutil "github.com/argoproj/argo-cd/v2/util/cache"
 	appstatecache "github.com/argoproj/argo-cd/v2/util/cache/appstate"
+	"github.com/argoproj/argo-cd/v2/util/db"
 	dbmocks "github.com/argoproj/argo-cd/v2/util/db/mocks"
+	"github.com/argoproj/argo-cd/v2/util/rbac"
+	"github.com/argoproj/argo-cd/v2/util/settings"
 )
 
 const testNamespace = "default"
 
-func Test_createRBACObject(t *testing.T) {
-	object := createRBACObject("test-prj", "test-repo")
-	assert.Equal(t, "test-prj/test-repo", object)
-	objectWithoutPrj := createRBACObject("", "test-repo")
-	assert.Equal(t, "test-repo", objectWithoutPrj)
-}
-
-func TestRepositoryServer(t *testing.T) {
-	kubeclientset := fake.NewSimpleClientset(&corev1.ConfigMap{
+var (
+	argocdCM = corev1.ConfigMap{
 		ObjectMeta: v1.ObjectMeta{
 			Namespace: testNamespace,
 			Name:      "argocd-cm",
@@ -59,7 +46,8 @@ func TestRepositoryServer(t *testing.T) {
 				"app.kubernetes.io/part-of": "argocd",
 			},
 		},
-	}, &corev1.Secret{
+	}
+	argocdSecret = corev1.Secret{
 		ObjectMeta: v1.ObjectMeta{
 			Name:      "argocd-secret",
 			Namespace: testNamespace,
@@ -68,17 +56,110 @@ func TestRepositoryServer(t *testing.T) {
 			"admin.password":   []byte("test"),
 			"server.secretkey": []byte("test"),
 		},
-	})
+	}
+	defaultProj = &appsv1.AppProject{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "AppProject",
+			APIVersion: "argoproj.io/v1alpha1",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "default",
+			Namespace: testNamespace,
+		},
+		Spec: appsv1.AppProjectSpec{
+			SourceRepos:  []string{"*"},
+			Destinations: []appsv1.ApplicationDestination{{Server: "*", Namespace: "*"}},
+		},
+	}
+
+	defaultProjNoSources = &appsv1.AppProject{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "AppProject",
+			APIVersion: "argoproj.io/v1alpha1",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "default",
+			Namespace: testNamespace,
+		},
+		Spec: appsv1.AppProjectSpec{
+			SourceRepos:  []string{},
+			Destinations: []appsv1.ApplicationDestination{{Server: "*", Namespace: "*"}},
+		},
+	}
+
+	guestbookApp = &appsv1.Application{
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "Application",
+			APIVersion: "argoproj.io/v1alpha1",
+		},
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "guestbook",
+			Namespace: testNamespace,
+		},
+		Spec: appsv1.ApplicationSpec{
+			Project: "default",
+			Source: appsv1.ApplicationSource{
+				RepoURL:        "https://test",
+				TargetRevision: "HEAD",
+				Helm: &appsv1.ApplicationSourceHelm{
+					ValueFiles: []string{"values.yaml"},
+				},
+			},
+		},
+		Status: appsv1.ApplicationStatus{
+			History: appsv1.RevisionHistories{
+				{
+					Revision: "abcdef123567",
+					Source: appsv1.ApplicationSource{
+						RepoURL:        "https://test",
+						TargetRevision: "HEAD",
+						Helm: &appsv1.ApplicationSourceHelm{
+							ValueFiles: []string{"values-old.yaml"},
+						},
+					},
+				},
+			},
+		},
+	}
+)
+
+func newAppAndProjLister(objects ...runtime.Object) (applisters.ApplicationNamespaceLister, applisters.AppProjectNamespaceLister) {
+	fakeAppsClientset := fakeapps.NewSimpleClientset(objects...)
+	factory := appinformer.NewSharedInformerFactoryWithOptions(fakeAppsClientset, 0, appinformer.WithNamespace(""), appinformer.WithTweakListOptions(func(options *metav1.ListOptions) {}))
+	projInformer := factory.Argoproj().V1alpha1().AppProjects()
+	appsInformer := factory.Argoproj().V1alpha1().Applications()
+	for _, obj := range objects {
+		switch obj.(type) {
+		case *appsv1.AppProject:
+			_ = projInformer.Informer().GetStore().Add(obj)
+		case *appsv1.Application:
+			_ = appsInformer.Informer().GetStore().Add(obj)
+		}
+	}
+	appLister := appsInformer.Lister().Applications(testNamespace)
+	projLister := projInformer.Lister().AppProjects(testNamespace)
+	return appLister, projLister
+}
+
+func Test_createRBACObject(t *testing.T) {
+	object := createRBACObject("test-prj", "test-repo")
+	assert.Equal(t, "test-prj/test-repo", object)
+	objectWithoutPrj := createRBACObject("", "test-repo")
+	assert.Equal(t, "test-repo", objectWithoutPrj)
+}
+
+func TestRepositoryServer(t *testing.T) {
+	kubeclientset := fake.NewSimpleClientset(&argocdCM, &argocdSecret)
 	settingsMgr := settings.NewSettingsManager(context.Background(), kubeclientset, testNamespace)
 	enforcer := newEnforcer(kubeclientset)
-
+	appLister, projLister := newAppAndProjLister(defaultProj)
 	argoDB := db.NewDB("default", settingsMgr, kubeclientset)
 
 	t.Run("Test_getRepo", func(t *testing.T) {
 		repoServerClient := mocks.RepoServerServiceClient{}
 		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
 
-		s := NewServer(&repoServerClientset, argoDB, enforcer, nil, settingsMgr)
+		s := NewServer(&repoServerClientset, argoDB, enforcer, nil, appLister, projLister, settingsMgr)
 		url := "https://test"
 		repo, _ := s.getRepo(context.TODO(), url)
 		assert.Equal(t, repo.Repo, url)
@@ -89,7 +170,7 @@ func TestRepositoryServer(t *testing.T) {
 		repoServerClient.On("TestRepository", mock.Anything, mock.Anything).Return(&apiclient.TestRepositoryResponse{}, nil)
 		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
 
-		s := NewServer(&repoServerClientset, argoDB, enforcer, nil, settingsMgr)
+		s := NewServer(&repoServerClientset, argoDB, enforcer, nil, appLister, projLister, settingsMgr)
 		url := "https://test"
 		_, err := s.ValidateAccess(context.TODO(), &repository.RepoAccessQuery{
 			Repo: url,
@@ -104,10 +185,10 @@ func TestRepositoryServer(t *testing.T) {
 
 		url := "https://test"
 		db := &dbmocks.ArgoDB{}
-		db.On("GetRepository", context.TODO(), url).Return(&v1alpha1.Repository{Repo: url}, nil)
+		db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
 		db.On("RepositoryExists", context.TODO(), url).Return(true, nil)
 
-		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, settingsMgr)
+		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
 		repo, err := s.Get(context.TODO(), &repository.RepoQuery{
 			Repo: url,
 		})
@@ -124,12 +205,12 @@ func TestRepositoryServer(t *testing.T) {
 		db.On("GetRepository", context.TODO(), url).Return(nil, errors.New("some error"))
 		db.On("RepositoryExists", context.TODO(), url).Return(true, nil)
 
-		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, settingsMgr)
+		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
 		repo, err := s.Get(context.TODO(), &repository.RepoQuery{
 			Repo: url,
 		})
 		assert.Nil(t, repo)
-		assert.Equal(t, err.Error(), "rpc error: code = PermissionDenied desc = permission denied")
+		assert.Equal(t, err, errPermissionDenied)
 	})
 
 	t.Run("Test_GetWithNotExistRepoShouldReturn404", func(t *testing.T) {
@@ -138,15 +219,15 @@ func TestRepositoryServer(t *testing.T) {
 
 		url := "https://test"
 		db := &dbmocks.ArgoDB{}
-		db.On("GetRepository", context.TODO(), url).Return(&v1alpha1.Repository{Repo: url}, nil)
+		db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
 		db.On("RepositoryExists", context.TODO(), url).Return(false, nil)
 
-		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, settingsMgr)
+		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
 		repo, err := s.Get(context.TODO(), &repository.RepoQuery{
 			Repo: url,
 		})
 		assert.Nil(t, repo)
-		assert.Equal(t, err.Error(), "rpc error: code = NotFound desc = repo 'https://test' not found")
+		assert.Equal(t, "rpc error: code = NotFound desc = repo 'https://test' not found", err.Error())
 	})
 
 	t.Run("Test_CreateRepositoryWithoutUpsert", func(t *testing.T) {
@@ -156,14 +237,14 @@ func TestRepositoryServer(t *testing.T) {
 
 		db := &dbmocks.ArgoDB{}
 		db.On("GetRepository", context.TODO(), "test").Return(nil, errors.New("not found"))
-		db.On("CreateRepository", context.TODO(), mock.Anything).Return(&apiclient.TestRepositoryResponse{}).Return(&v1alpha1.Repository{
+		db.On("CreateRepository", context.TODO(), mock.Anything).Return(&apiclient.TestRepositoryResponse{}).Return(&appsv1.Repository{
 			Repo:    "repo",
 			Project: "proj",
 		}, nil)
 
-		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, settingsMgr)
+		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
 		repo, err := s.CreateRepository(context.TODO(), &repository.RepoCreateRequest{
-			Repo: &v1alpha1.Repository{
+			Repo: &appsv1.Repository{
 				Repo:     "test",
 				Username: "test",
 			},
@@ -178,16 +259,16 @@ func TestRepositoryServer(t *testing.T) {
 		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
 
 		db := &dbmocks.ArgoDB{}
-		db.On("GetRepository", context.TODO(), "test").Return(&v1alpha1.Repository{
+		db.On("GetRepository", context.TODO(), "test").Return(&appsv1.Repository{
 			Repo:     "test",
 			Username: "test",
 		}, nil)
 		db.On("CreateRepository", context.TODO(), mock.Anything).Return(nil, status.Errorf(codes.AlreadyExists, "repository already exists"))
 		db.On("UpdateRepository", context.TODO(), mock.Anything).Return(nil, nil)
 
-		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, settingsMgr)
+		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
 		repo, err := s.CreateRepository(context.TODO(), &repository.RepoCreateRequest{
-			Repo: &v1alpha1.Repository{
+			Repo: &appsv1.Repository{
 				Repo:     "test",
 				Username: "test",
 			},
@@ -200,6 +281,295 @@ func TestRepositoryServer(t *testing.T) {
 
 }
 
+func TestRepositoryServerListApps(t *testing.T) {
+	kubeclientset := fake.NewSimpleClientset(&argocdCM, &argocdSecret)
+	settingsMgr := settings.NewSettingsManager(context.Background(), kubeclientset, testNamespace)
+
+	t.Run("Test_WithoutAppCreateUpdatePrivileges", func(t *testing.T) {
+		repoServerClient := mocks.RepoServerServiceClient{}
+		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
+		enforcer := newEnforcer(kubeclientset)
+		enforcer.SetDefaultRole("role:readonly")
+
+		url := "https://test"
+		db := &dbmocks.ArgoDB{}
+		db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
+		appLister, projLister := newAppAndProjLister(defaultProj)
+
+		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
+		resp, err := s.ListApps(context.TODO(), &repository.RepoAppsQuery{
+			Repo:       "https://test",
+			Revision:   "HEAD",
+			AppName:    "foo",
+			AppProject: "default",
+		})
+		assert.Nil(t, resp)
+		assert.Equal(t, err, errPermissionDenied)
+	})
+
+	t.Run("Test_WithAppCreateUpdatePrivileges", func(t *testing.T) {
+		repoServerClient := mocks.RepoServerServiceClient{}
+		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
+		enforcer := newEnforcer(kubeclientset)
+		enforcer.SetDefaultRole("role:admin")
+		appLister, projLister := newAppAndProjLister(defaultProj)
+
+		url := "https://test"
+		db := &dbmocks.ArgoDB{}
+		db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
+		repoServerClient.On("ListApps", context.TODO(), mock.Anything).Return(&apiclient.AppList{
+			Apps: map[string]string{
+				"path/to/dir": "Kustomize",
+			},
+		}, nil)
+
+		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
+		resp, err := s.ListApps(context.TODO(), &repository.RepoAppsQuery{
+			Repo:       "https://test",
+			Revision:   "HEAD",
+			AppName:    "foo",
+			AppProject: "default",
+		})
+		assert.NoError(t, err)
+		assert.Len(t, resp.Items, 1)
+		assert.Equal(t, "path/to/dir", resp.Items[0].Path)
+		assert.Equal(t, "Kustomize", resp.Items[0].Type)
+	})
+
+	t.Run("Test_WithAppCreateUpdatePrivilegesRepoNotAllowed", func(t *testing.T) {
+		repoServerClient := mocks.RepoServerServiceClient{}
+		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
+		enforcer := newEnforcer(kubeclientset)
+		enforcer.SetDefaultRole("role:admin")
+		appLister, projLister := newAppAndProjLister(defaultProjNoSources)
+
+		url := "https://test"
+		db := &dbmocks.ArgoDB{}
+		db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
+		repoServerClient.On("ListApps", context.TODO(), mock.Anything).Return(&apiclient.AppList{
+			Apps: map[string]string{
+				"path/to/dir": "Kustomize",
+			},
+		}, nil)
+
+		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
+		resp, err := s.ListApps(context.TODO(), &repository.RepoAppsQuery{
+			Repo:       "https://test",
+			Revision:   "HEAD",
+			AppName:    "foo",
+			AppProject: "default",
+		})
+		assert.Nil(t, resp)
+		assert.Error(t, err, "repository 'https://test' not permitted in project 'default'")
+	})
+}
+
+func TestRepositoryServerGetAppDetails(t *testing.T) {
+	kubeclientset := fake.NewSimpleClientset(&argocdCM, &argocdSecret)
+	settingsMgr := settings.NewSettingsManager(context.Background(), kubeclientset, testNamespace)
+
+	t.Run("Test_WithoutRepoReadPrivileges", func(t *testing.T) {
+		repoServerClient := mocks.RepoServerServiceClient{}
+		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
+		enforcer := newEnforcer(kubeclientset)
+		enforcer.SetDefaultRole("")
+
+		url := "https://test"
+		db := &dbmocks.ArgoDB{}
+		db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
+		appLister, projLister := newAppAndProjLister(defaultProj)
+
+		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
+		resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{
+			Source: &appsv1.ApplicationSource{
+				RepoURL: url,
+			},
+			AppName:    "newapp",
+			AppProject: "default",
+		})
+		assert.Nil(t, resp)
+		assert.Error(t, err, "rpc error: code = PermissionDenied desc = permission denied: repositories, get, https://test")
+	})
+	t.Run("Test_WithoutAppReadPrivileges", func(t *testing.T) {
+		repoServerClient := mocks.RepoServerServiceClient{}
+		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
+		enforcer := newEnforcer(kubeclientset)
+		_ = enforcer.SetUserPolicy("p, role:readrepos, repositories, get, *, allow")
+		enforcer.SetDefaultRole("role:readrepos")
+
+		url := "https://test"
+		db := &dbmocks.ArgoDB{}
+		db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
+		appLister, projLister := newAppAndProjLister(defaultProj)
+
+		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
+		resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{
+			Source: &appsv1.ApplicationSource{
+				RepoURL: url,
+			},
+			AppName:    "newapp",
+			AppProject: "default",
+		})
+		assert.Nil(t, resp)
+		assert.Error(t, err, "rpc error: code = PermissionDenied desc = permission denied: applications, get, default/newapp")
+	})
+	t.Run("Test_WithoutCreatePrivileges", func(t *testing.T) {
+		repoServerClient := mocks.RepoServerServiceClient{}
+		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
+		enforcer := newEnforcer(kubeclientset)
+		enforcer.SetDefaultRole("role:readonly")
+
+		url := "https://test"
+		db := &dbmocks.ArgoDB{}
+		db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
+		appLister, projLister := newAppAndProjLister(defaultProj)
+
+		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
+		resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{
+			Source: &appsv1.ApplicationSource{
+				RepoURL: url,
+			},
+			AppName:    "newapp",
+			AppProject: "default",
+		})
+		assert.Nil(t, resp)
+		assert.Error(t, err, "rpc error: code = PermissionDenied desc = permission denied: applications, create, default/newapp")
+	})
+	t.Run("Test_WithCreatePrivileges", func(t *testing.T) {
+		repoServerClient := mocks.RepoServerServiceClient{}
+		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
+		enforcer := newEnforcer(kubeclientset)
+
+		url := "https://test"
+		db := &dbmocks.ArgoDB{}
+		db.On("ListHelmRepositories", context.TODO(), mock.Anything).Return(nil, nil)
+		db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
+		expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"}
+		repoServerClient.On("GetAppDetails", context.TODO(), mock.Anything).Return(&expectedResp, nil)
+		appLister, projLister := newAppAndProjLister(defaultProj)
+
+		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
+		resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{
+			Source: &appsv1.ApplicationSource{
+				RepoURL: url,
+			},
+			AppName:    "newapp",
+			AppProject: "default",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, expectedResp, *resp)
+	})
+	t.Run("Test_RepoNotPermitted", func(t *testing.T) {
+		repoServerClient := mocks.RepoServerServiceClient{}
+		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
+		enforcer := newEnforcer(kubeclientset)
+
+		url := "https://test"
+		db := &dbmocks.ArgoDB{}
+		db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
+		expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"}
+		repoServerClient.On("GetAppDetails", context.TODO(), mock.Anything).Return(&expectedResp, nil)
+		appLister, projLister := newAppAndProjLister(defaultProjNoSources)
+
+		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
+		resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{
+			Source: &appsv1.ApplicationSource{
+				RepoURL: url,
+			},
+			AppName:    "newapp",
+			AppProject: "default",
+		})
+		assert.Error(t, err, "repository 'https://test' not permitted in project 'default'")
+		assert.Nil(t, resp)
+	})
+	t.Run("Test_ExistingApp", func(t *testing.T) {
+		repoServerClient := mocks.RepoServerServiceClient{}
+		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
+		enforcer := newEnforcer(kubeclientset)
+
+		url := "https://test"
+		db := &dbmocks.ArgoDB{}
+		db.On("ListHelmRepositories", context.TODO(), mock.Anything).Return(nil, nil)
+		db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
+		expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"}
+		repoServerClient.On("GetAppDetails", context.TODO(), mock.Anything).Return(&expectedResp, nil)
+		appLister, projLister := newAppAndProjLister(defaultProj, guestbookApp)
+
+		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
+		resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{
+			Source:     &guestbookApp.Spec.Source,
+			AppName:    "guestbook",
+			AppProject: "default",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, expectedResp, *resp)
+	})
+	t.Run("Test_ExistingAppMismatchedProjectName", func(t *testing.T) {
+		repoServerClient := mocks.RepoServerServiceClient{}
+		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
+		enforcer := newEnforcer(kubeclientset)
+
+		url := "https://test"
+		db := &dbmocks.ArgoDB{}
+		db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
+		appLister, projLister := newAppAndProjLister(defaultProj, guestbookApp)
+
+		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
+		resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{
+			Source:     &guestbookApp.Spec.Source,
+			AppName:    "guestbook",
+			AppProject: "mismatch",
+		})
+		assert.Equal(t, errPermissionDenied, err)
+		assert.Nil(t, resp)
+	})
+	t.Run("Test_ExistingAppSourceNotInHistory", func(t *testing.T) {
+		repoServerClient := mocks.RepoServerServiceClient{}
+		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
+		enforcer := newEnforcer(kubeclientset)
+
+		url := "https://test"
+		db := &dbmocks.ArgoDB{}
+		db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
+		appLister, projLister := newAppAndProjLister(defaultProj, guestbookApp)
+		differentSource := guestbookApp.Spec.Source.DeepCopy()
+		differentSource.Helm.ValueFiles = []string{"/etc/passwd"}
+
+		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
+		resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{
+			Source:     differentSource,
+			AppName:    "guestbook",
+			AppProject: "default",
+		})
+		assert.Equal(t, errPermissionDenied, err)
+		assert.Nil(t, resp)
+	})
+	t.Run("Test_ExistingAppSourceInHistory", func(t *testing.T) {
+		repoServerClient := mocks.RepoServerServiceClient{}
+		repoServerClientset := mocks.Clientset{RepoServerServiceClient: &repoServerClient}
+		enforcer := newEnforcer(kubeclientset)
+
+		url := "https://test"
+		db := &dbmocks.ArgoDB{}
+		db.On("GetRepository", context.TODO(), url).Return(&appsv1.Repository{Repo: url}, nil)
+		db.On("ListHelmRepositories", context.TODO(), mock.Anything).Return(nil, nil)
+		expectedResp := apiclient.RepoAppDetailsResponse{Type: "Directory"}
+		repoServerClient.On("GetAppDetails", context.TODO(), mock.Anything).Return(&expectedResp, nil)
+		appLister, projLister := newAppAndProjLister(defaultProj, guestbookApp)
+		previousSource := guestbookApp.Status.History[0].Source.DeepCopy()
+		previousSource.TargetRevision = guestbookApp.Status.History[0].Revision
+
+		s := NewServer(&repoServerClientset, db, enforcer, newFixtures().Cache, appLister, projLister, settingsMgr)
+		resp, err := s.GetAppDetails(context.TODO(), &repository.RepoAppDetailsQuery{
+			Source:     previousSource,
+			AppName:    "guestbook",
+			AppProject: "default",
+		})
+		assert.NoError(t, err)
+		assert.Equal(t, expectedResp, *resp)
+	})
+}
+
 type fixtures struct {
 	*cache.Cache
 }
diff --git a/test/e2e/app_management_test.go b/test/e2e/app_management_test.go
index 64fc5e1ae..d26777962 100644
--- a/test/e2e/app_management_test.go
+++ b/test/e2e/app_management_test.go
@@ -729,7 +729,9 @@ func TestKsonnetApp(t *testing.T) {
 			defer io.Close(closer)
 
 			details, err := client.GetAppDetails(context.Background(), &repositorypkg.RepoAppDetailsQuery{
-				Source: &app.Spec.Source,
+				AppName:    app.Name,
+				AppProject: app.Spec.Project,
+				Source:     &app.Spec.Source,
 			})
 			assert.NoError(t, err)
 
diff --git a/test/e2e/custom_tool_test.go b/test/e2e/custom_tool_test.go
index dfeaec573..26adcbc4e 100644
--- a/test/e2e/custom_tool_test.go
+++ b/test/e2e/custom_tool_test.go
@@ -26,7 +26,7 @@ func TestCustomToolWithGitCreds(t *testing.T) {
 				Name: Name(),
 				Generate: Command{
 					Command: []string{"sh", "-c"},
-					Args:    []string{`echo "{\"kind\": \"ConfigMap\", \"apiVersion\": \"v1\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"GitAskpass\": \"$GIT_ASKPASS\", \"GitUsername\": \"$GIT_USERNAME\", \"GitPassword\": \"$GIT_PASSWORD\"}}}"`},
+					Args:    []string{`echo "{\"kind\": \"ConfigMap\", \"apiVersion\": \"v1\", \"metadata\": { \"name\": \"$ARGOCD_APP_NAME\", \"namespace\": \"$ARGOCD_APP_NAMESPACE\", \"annotations\": {\"GitAskpass\": \"$GIT_ASKPASS\"}}}"`},
 				},
 			},
 		).
@@ -45,17 +45,7 @@ func TestCustomToolWithGitCreds(t *testing.T) {
 		And(func(app *Application) {
 			output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", Name(), "-o", "jsonpath={.metadata.annotations.GitAskpass}")
 			assert.NoError(t, err)
-			assert.Equal(t, "git-ask-pass.sh", output)
-		}).
-		And(func(app *Application) {
-			output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", Name(), "-o", "jsonpath={.metadata.annotations.GitUsername}")
-			assert.NoError(t, err)
-			assert.Equal(t, GitUsername, output)
-		}).
-		And(func(app *Application) {
-			output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", Name(), "-o", "jsonpath={.metadata.annotations.GitPassword}")
-			assert.NoError(t, err)
-			assert.Equal(t, GitPassword, output)
+			assert.Equal(t, "argocd", output)
 		})
 }
 
@@ -89,17 +79,17 @@ func TestCustomToolWithGitCredsTemplate(t *testing.T) {
 		And(func(app *Application) {
 			output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", Name(), "-o", "jsonpath={.metadata.annotations.GitAskpass}")
 			assert.NoError(t, err)
-			assert.Equal(t, "git-ask-pass.sh", output)
+			assert.Equal(t, "argocd", output)
 		}).
 		And(func(app *Application) {
 			output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", Name(), "-o", "jsonpath={.metadata.annotations.GitUsername}")
 			assert.NoError(t, err)
-			assert.Equal(t, GitUsername, output)
+			assert.Empty(t, output)
 		}).
 		And(func(app *Application) {
 			output, err := Run("", "kubectl", "-n", DeploymentNamespace(), "get", "cm", Name(), "-o", "jsonpath={.metadata.annotations.GitPassword}")
 			assert.NoError(t, err)
-			assert.Equal(t, GitPassword, output)
+			assert.Empty(t, output)
 		})
 }
 
diff --git a/test/fixture/path/hack_path.go b/test/fixture/path/hack_path.go
index e15102224..41bf98961 100644
--- a/test/fixture/path/hack_path.go
+++ b/test/fixture/path/hack_path.go
@@ -16,10 +16,10 @@ func (h AddBinDirToPath) Close() {
 	_ = os.Setenv("PATH", h.originalPath)
 }
 
-// add the hack path which has the git-ask-pass.sh shell script
+// add the hack path which has the argocd binary
 func NewBinDirToPath() AddBinDirToPath {
 	originalPath := os.Getenv("PATH")
-	binDir, err := filepath.Abs("../../hack")
+	binDir, err := filepath.Abs("../../dist")
 	errors.CheckError(err)
 	err = os.Setenv("PATH", fmt.Sprintf("%s:%s", originalPath, binDir))
 	errors.CheckError(err)
diff --git a/util/git/git_test.go b/util/git/git_test.go
index 068b83395..1d36370ac 100644
--- a/util/git/git_test.go
+++ b/util/git/git_test.go
@@ -146,7 +146,7 @@ func TestCustomHTTPClient(t *testing.T) {
 	assert.NotEqual(t, "", string(keyData))
 
 	// Get HTTPSCreds with client cert creds specified, and insecure connection
-	creds := NewHTTPSCreds("test", "test", string(certData), string(keyData), false, "http://proxy:5000")
+	creds := NewHTTPSCreds("test", "test", string(certData), string(keyData), false, "http://proxy:5000", &NoopCredsStore{})
 	client := GetRepoHTTPClient("https://localhost:9443/foo/bar", false, creds, "http://proxy:5000")
 	assert.NotNil(t, client)
 	assert.NotNil(t, client.Transport)
@@ -177,7 +177,7 @@ func TestCustomHTTPClient(t *testing.T) {
 	}()
 
 	// Get HTTPSCreds without client cert creds, but insecure connection
-	creds = NewHTTPSCreds("test", "test", "", "", true, "")
+	creds = NewHTTPSCreds("test", "test", "", "", true, "", &NoopCredsStore{})
 	client = GetRepoHTTPClient("https://localhost:9443/foo/bar", true, creds, "")
 	assert.NotNil(t, client)
 	assert.NotNil(t, client.Transport)