Repository: argoproj/argo
Pull request #3278 (2020-06-23 16:55:22)

fix: Fix delete --complete
Checklist:

* [x] Either (a) I've created an [enhancement proposal](https://github.com/argoproj/argo/issues/new/choose) and discussed it with the community, (b) this is a bug fix, or (c) this is a chore.
* [x] The title of the PR is (a) [conventional](https://www.conventionalcommits.org/en/v1.0.0/), (b) states what changed, and (c) suffixes the related issues number. E.g. `"fix(controller): Updates such and such. Fixes #1234"`.  
* [x] I've signed the CLA.
* [ ] I have written unit and/or e2e tests for my change. PRs without these are unlikely to be merged.
* [ ] My builds are green. Try syncing with master if they are not. 
* [x] My organization is added to [USERS.md](https://github.com/argoproj/argo/blob/master/USERS.md).



Patch:
diff --git a/.gitignore b/.gitignore
index 1dc33ec82..fde72085d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@ hack/**/debug
 /argo
 /argoexec
 debug.test
+git-ask-pass.sh
 *.iml
 /coverage.out
 .envrc
diff --git a/Makefile b/Makefile
index ee90b12d0..4c711f5c6 100644
--- a/Makefile
+++ b/Makefile
@@ -20,6 +20,8 @@ DOCKERFILE            := Dockerfile
 
 # docker image publishing options
 IMAGE_NAMESPACE       ?= argoproj
+# The name of the namespace where Kubernetes resources/RBAC will be installed
+KUBE_NAMESPACE        ?= argo
 
 # The rules for what version are, in order of precedence
 # 1. If anything passed at the command line (e.g. make release VERSION=...)
@@ -335,8 +337,9 @@ install: dist/$(PROFILE).yaml
 ifeq ($(K3D),true)
 	k3d start
 endif
-	kubectl apply -f test/e2e/manifests/argo-ns.yaml
-	kubectl -n argo apply -l app.kubernetes.io/part-of=argo --prune --force -f dist/$(PROFILE).yaml
+	cat test/e2e/manifests/argo-ns.yaml | sed 's/argo/$(KUBE_NAMESPACE)/' > dist/argo-ns.yaml
+	kubectl apply -f dist/argo-ns.yaml
+	kubectl -n $(KUBE_NAMESPACE) apply -l app.kubernetes.io/part-of=argo --prune --force -f dist/$(PROFILE).yaml
 
 .PHONY: pull-build-images
 pull-build-images:
@@ -368,8 +371,8 @@ $(GOPATH)/bin/goreman:
 
 .PHONY: start
 start: status stop install controller cli executor-image $(GOPATH)/bin/goreman
-	kubectl config set-context --current --namespace=argo
-	kubectl -n argo wait --for=condition=Ready pod --all -l app --timeout 2m
+	kubectl config set-context --current --namespace=$(KUBE_NAMESPACE)
+	kubectl -n $(KUBE_NAMESPACE) wait --for=condition=Ready pod --all -l app --timeout 2m
 	./hack/port-forward.sh
 	# Check dex, minio, postgres and mysql are in hosts file
 ifeq ($(AUTH_MODE),sso)
@@ -405,7 +408,7 @@ env:
 .PHONY: logs
 logs:
 	# Tail logs
-	kubectl -n argo logs -f -l app --max-log-requests 10 --tail 100
+	kubectl -n $(KUBE_NAMESPACE) logs -f -l app --max-log-requests 10 --tail 100
 
 .PHONY: postgres-cli
 postgres-cli:
diff --git a/README.md b/README.md
index 1905ce675..066e35ed9 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,5 @@
 [![slack](https://img.shields.io/badge/slack-argoproj-brightgreen.svg?logo=slack)](https://argoproj.github.io/community/join-slack)
-[![CircleCI](https://circleci.com/gh/argoproj/argo.svg?style=svg)](https://circleci.com/gh/argoproj/argo)
-[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=argoproj_argo&metric=alert_status)](https://sonarcloud.io/dashboard?id=argoproj_argo)
+![CI](https://github.com/argoproj/argo/workflows/CI/badge.svg)
 
 ![Argo Image](docs/assets/argo.png)
 
diff --git a/cmd/argo/commands/delete.go b/cmd/argo/commands/delete.go
index 103b2f319..43ad7e8be 100644
--- a/cmd/argo/commands/delete.go
+++ b/cmd/argo/commands/delete.go
@@ -30,6 +30,7 @@ func NewDeleteCommand() *cobra.Command {
   argo delete my-wf
 
 # Delete the latest workflow:
+
   argo delete @latest
 `,
 		Run: func(cmd *cobra.Command, args []string) {
diff --git a/cmd/argo/commands/list.go b/cmd/argo/commands/list.go
index 2eb5ac94a..90f28dddc 100644
--- a/cmd/argo/commands/list.go
+++ b/cmd/argo/commands/list.go
@@ -79,7 +79,8 @@ func listWorkflows(ctx context.Context, serviceClient workflowpkg.WorkflowServic
 	listOpts := &metav1.ListOptions{
 		Limit: flags.chunkSize,
 	}
-	labelSelector := labels.NewSelector()
+	labelSelector, err := labels.Parse(flags.labels)
+	errors.CheckError(err)
 	if len(flags.status) != 0 {
 		req, _ := labels.NewRequirement(common.LabelKeyPhase, selection.In, flags.status)
 		if req != nil {
@@ -94,10 +95,7 @@ func listWorkflows(ctx context.Context, serviceClient workflowpkg.WorkflowServic
 		req, _ := labels.NewRequirement(common.LabelKeyCompleted, selection.NotEquals, []string{"true"})
 		labelSelector = labelSelector.Add(*req)
 	}
-	if listOpts.LabelSelector = labelSelector.String(); listOpts.LabelSelector != "" {
-		listOpts.LabelSelector = listOpts.LabelSelector + ","
-	}
-	listOpts.LabelSelector = listOpts.LabelSelector + flags.labels
+	listOpts.LabelSelector = labelSelector.String()
 	listOpts.FieldSelector = flags.fields
 	var workflows wfv1.Workflows
 	for {
diff --git a/docs/cluster-workflow-templates.md b/docs/cluster-workflow-templates.md
index fb036cc21..70e196418 100644
--- a/docs/cluster-workflow-templates.md
+++ b/docs/cluster-workflow-templates.md
@@ -55,6 +55,64 @@ spec:
             - name: message
               value: "hello world"
 ```
+> 2.9 and after
+#### Create `Workflow` from `ClusterWorkflowTemplate` Spec
+You can create `Workflow` from `ClusterWorkflowTemplate` spec using `workflowTemplateRef` with `clusterScope: true`. If you pass the arguments to created `Workflow`, it will be merged with ClusterWorkflowTemplate arguments 
+
+Here is an example for `ClusterWorkflowTemplate` with `entrypoint` and `arguments`
+```yaml
+apiVersion: argoproj.io/v1alpha1
+kind: ClusterWorkflowTemplate
+metadata:
+  name: cluster-workflow-template-submittable
+spec:
+  entryPoint: whalesay-template
+  arguments:
+    parameters:
+      - name: message
+        value: hello world
+  templates:
+    - name: whalesay-template
+      inputs:
+        parameters:
+          - name: message
+      container:
+        image: docker/whalesay
+        command: [cowsay]
+        args: ["{{inputs.parameters.message}}"]
+
+```
+Here is an example for creating `ClusterWorkflowTemplate` as Workflow with passing `entrypoint` and `arguments` to `ClusterWorkflowTemplate`
+```yaml
+apiVersion: argoproj.io/v1alpha1
+kind: Workflow
+metadata:
+  generateName: cluster-workflow-template-hello-world-
+spec:
+  entrypoint: whalesay-template
+  arguments:
+    parameters:
+      - name: message
+        value: "from workflow"
+  workflowTemplateRef:
+    name: cluster-workflow-template-submittable
+    clusterScope: true
+```  
+
+Here is an example of a creating `WorkflowTemplate` as Workflow and using `WorkflowTemplates`'s `entrypoint` and `Workflow Arguments`
+```yaml
+apiVersion: argoproj.io/v1alpha1
+kind: Workflow
+metadata:
+  generateName: cluster-workflow-template-hello-world-
+spec:
+  workflowTemplateRef:
+    name: cluster-workflow-template-submittable
+    clusterScope: true
+
+```
+
+
 
 ## Managing `ClusterWorkflowTemplates`
 
@@ -72,6 +130,13 @@ The submit a workflow using one of those templates:
 argo submit https://raw.githubusercontent.com/argoproj/argo/master/examples/cluster-workflow-template/cluster-wftmpl-dag.yaml
 ```
 
+> 2.7 and after
+>
+The submit a `ClusterWorkflowTemplate` as a `Workflow`:
+```shell script
+argo submit --from clusterworkflowtemplate/workflow-template-submittable
+```
+
 ### `kubectl`
 
 Using `kubectl apply -f` and `kubectl get cwft`
diff --git a/docs/releasing.md b/docs/releasing.md
index 70622b34a..39c56dba0 100644
--- a/docs/releasing.md
+++ b/docs/releasing.md
@@ -6,7 +6,7 @@ Allow 1h to do a release.
 
 Cherry-pick your changes from master onto the release branch.
 
-The release branch should be green [in CircleCI](https://app.circleci.com/github/argoproj/argo/pipelines) before you start.
+The release branch should be green in CI before you start.
 
 ## Release
 
@@ -17,7 +17,7 @@ To generate new manifests and perform basic checks:
 Publish the images and local Git changes (disabling K3D as this is faster and more reliable for releases):
 
     make publish-release K3D=false VERSION=v2.7.2
-
+    
 * [ ] Check the images were pushed successfully.
 
 ```
@@ -36,6 +36,8 @@ docker run argoproj/argocli:v2.7.2 version
 
 * [ ] Check the manifests contain the correct tags: https://raw.githubusercontent.com/argoproj/argo/v2.7.2/manifests/install.yaml
 
+* [ ] Check the manifests apply: `kubectl -n argo apply -f https://raw.githubusercontent.com/argoproj/argo/v2.7.2/manifests/install.yaml`
+
 ### Release Notes
 
 Create [the release](https://github.com/argoproj/argo/releases) in Github. You can get some text for this using [Github Toolkit](https://github.com/alexec/github-toolkit):
diff --git a/docs/workflow-templates.md b/docs/workflow-templates.md
index 64e1ae414..805696879 100644
--- a/docs/workflow-templates.md
+++ b/docs/workflow-templates.md
@@ -187,6 +187,37 @@ to pass in "live" arguments and reference other templates (those other templates
 
 This behavior has been problematic and dangerous. It causes confusion and has design inconsistencies.
 
+> 2.9 and after
+#### Create `Workflow` from `WorkflowTemplate` Spec
+You can create `Workflow` from `WorkflowTemplate` spec using `workflowTemplateRef`. If you pass the arguments to created `Workflow`, it will be merged with WorkflowTemplate arguments 
+Here is an example for referring `WorkflowTemplate` as Workflow with passing `entrypoint` and `Workflow Arguments` to `WorkflowTemplate`
+```yaml
+apiVersion: argoproj.io/v1alpha1
+kind: Workflow
+metadata:
+  generateName: workflow-template-hello-world-
+spec:
+  entrypoint: whalesay-template
+  arguments:
+    parameters:
+      - name: message
+        value: "from workflow"
+  workflowTemplateRef:
+    name: workflow-template-submittable
+```  
+
+Here is an example of a referring `WorkflowTemplate` as Workflow and using `WorkflowTemplates`'s `entrypoint` and `Workflow Arguments`
+```yaml
+apiVersion: argoproj.io/v1alpha1
+kind: Workflow
+metadata:
+  generateName: workflow-template-hello-world-
+spec:
+  workflowTemplateRef:
+    name: workflow-template-submittable
+
+```
+
 ## Managing `WorkflowTemplates`
 
 ### CLI
@@ -201,6 +232,12 @@ The submit a workflow using one of those templates:
 
 ```
 argo submit https://raw.githubusercontent.com/argoproj/argo/master/examples/workflow-template/hello-world.yaml
+```
+> 2.7 and after
+The submit a `WorkflowTemplate` as a `Workflow`:
+```shell script
+argo submit --from workflowtemplate/workflow-template-submittable
+
 ```
 
 ### `kubectl`
diff --git a/pkg/apis/workflow/v1alpha1/workflow_types.go b/pkg/apis/workflow/v1alpha1/workflow_types.go
index 903041373..f958ef237 100644
--- a/pkg/apis/workflow/v1alpha1/workflow_types.go
+++ b/pkg/apis/workflow/v1alpha1/workflow_types.go
@@ -1440,6 +1440,13 @@ func (g *GitArtifact) HasLocation() bool {
 	return g != nil && g.Repo != ""
 }
 
+func (g *GitArtifact) GetDepth() int {
+	if g == nil || g.Depth == nil {
+		return 0
+	}
+	return int(*g.Depth)
+}
+
 // ArtifactoryAuth describes the secret selectors required for authenticating to artifactory
 type ArtifactoryAuth struct {
 	// UsernameSecret is the secret selector to the repository username
diff --git a/server/workflow/workflow_server.go b/server/workflow/workflow_server.go
index e8121ee9c..a6b019165 100644
--- a/server/workflow/workflow_server.go
+++ b/server/workflow/workflow_server.go
@@ -391,7 +391,7 @@ func (s *workflowServer) getWorkflow(wfClient versioned.Interface, namespace str
 		if err != nil {
 			return nil, err
 		}
-		log.Infof("Resolved alias %s to workflow %s.\n", latestAlias, latest.Name)
+		log.Debugf("Resolved alias %s to workflow %s.\n", latestAlias, latest.Name)
 		return latest, nil
 	}
 	wf, err := wfClient.ArgoprojV1alpha1().Workflows(namespace).Get(name, options)
diff --git a/workflow/artifacts/git/git.go b/workflow/artifacts/git/git.go
index 98c31e43c..5c2fc7d7e 100644
--- a/workflow/artifacts/git/git.go
+++ b/workflow/artifacts/git/git.go
@@ -1,22 +1,21 @@
 package git
 
 import (
-	"fmt"
+	"errors"
 	"io/ioutil"
 	"os"
 	"os/exec"
-	"os/user"
+	"path/filepath"
 	"strings"
 
 	log "github.com/sirupsen/logrus"
 	"golang.org/x/crypto/ssh"
-	git "gopkg.in/src-d/go-git.v4"
+	"gopkg.in/src-d/go-git.v4"
 	"gopkg.in/src-d/go-git.v4/config"
 	"gopkg.in/src-d/go-git.v4/plumbing/transport"
 	"gopkg.in/src-d/go-git.v4/plumbing/transport/http"
 	ssh2 "gopkg.in/src-d/go-git.v4/plumbing/transport/ssh"
 
-	"github.com/argoproj/argo/errors"
 	wfv1 "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1"
 )
 
@@ -28,130 +27,140 @@ type GitArtifactDriver struct {
 	InsecureIgnoreHostKey bool
 }
 
-// Load download artifacts from an git URL
-func (g *GitArtifactDriver) Load(inputArtifact *wfv1.Artifact, path string) error {
+func (g *GitArtifactDriver) auth() (func(), transport.AuthMethod, []string, error) {
 	if g.SSHPrivateKey != "" {
 		signer, err := ssh.ParsePrivateKey([]byte(g.SSHPrivateKey))
 		if err != nil {
-			return errors.InternalWrapError(err)
+			return nil, nil, nil, err
+		}
+		privateKeyFile, err := ioutil.TempFile("", "id_rsa.")
+		if err != nil {
+			return nil, nil, nil, err
+		}
+		err = ioutil.WriteFile(privateKeyFile.Name(), []byte(g.SSHPrivateKey), 0600)
+		if err != nil {
+			return nil, nil, nil, err
 		}
 		auth := &ssh2.PublicKeys{User: "git", Signer: signer}
 		if g.InsecureIgnoreHostKey {
 			auth.HostKeyCallback = ssh.InsecureIgnoreHostKey()
 		}
-		return gitClone(path, inputArtifact, auth, g.SSHPrivateKey)
+		args := []string{"ssh", "-i", privateKeyFile.Name()}
+		if g.InsecureIgnoreHostKey {
+			args = append(args, "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null")
+		} else {
+			args = append(args, "-o", "StrictHostKeyChecking=yes", "-o")
+		}
+		env := []string{"GIT_SSH_COMMAND=" + strings.Join(args, " ")}
+		if g.InsecureIgnoreHostKey {
+			auth.HostKeyCallback = ssh.InsecureIgnoreHostKey()
+			env = append(env, "GIT_SSL_NO_VERIFY=true")
+		}
+		return func() { _ = os.Remove(privateKeyFile.Name()) },
+			auth,
+			env,
+			nil
 	}
 	if g.Username != "" || g.Password != "" {
-		auth := &http.BasicAuth{Username: g.Username, Password: g.Password}
-		return gitClone(path, inputArtifact, auth, "")
+		filename, err := filepath.Abs("git-ask-pass.sh")
+		if err != nil {
+			return nil, nil, nil, err
+		}
+		_, err = os.Stat(filename)
+		if os.IsNotExist(err) {
+			err := ioutil.WriteFile(filename, []byte(`#!/bin/sh
+case "$1" in
+Username*) echo "${GIT_USERNAME}" ;;
+Password*) echo "${GIT_PASSWORD}" ;;
+esac
+`), 0755)
+			if err != nil {
+				return nil, nil, nil, err
+			}
+		}
+		return func() {},
+			&http.BasicAuth{Username: g.Username, Password: g.Password},
+			[]string{
+				"GIT_ASKPASS=" + filename,
+				"GIT_USERNAME=" + g.Username,
+				"GIT_PASSWORD=" + g.Password,
+			},
+			nil
 	}
-	return gitClone(path, inputArtifact, nil, "")
+	return func() {}, nil, nil, nil
 }
 
 // Save is unsupported for git output artifacts
 func (g *GitArtifactDriver) Save(string, *wfv1.Artifact) error {
-	return errors.Errorf(errors.CodeBadRequest, "Git output artifacts unsupported")
+	return errors.New("git output artifacts unsupported")
 }
 
-func writePrivateKey(key string, insecureIgnoreHostKey bool) error {
-	usr, err := user.Current()
-	if err != nil {
-		return errors.InternalWrapError(err)
-	}
-	sshDir := fmt.Sprintf("%s/.ssh", usr.HomeDir)
-	err = os.MkdirAll(sshDir, 0700)
-	if err != nil {
-		return errors.InternalWrapError(err)
-	}
-
-	if insecureIgnoreHostKey {
-		sshConfig := `Host *
-	StrictHostKeyChecking no
-	UserKnownHostsFile /dev/null`
-		err = ioutil.WriteFile(fmt.Sprintf("%s/config", sshDir), []byte(sshConfig), 0644)
-		if err != nil {
-			return errors.InternalWrapError(err)
-		}
-	}
-	err = ioutil.WriteFile(fmt.Sprintf("%s/id_rsa", sshDir), []byte(key), 0600)
+func (g *GitArtifactDriver) Load(inputArtifact *wfv1.Artifact, path string) error {
+	closer, auth, env, err := g.auth()
 	if err != nil {
-		return errors.InternalWrapError(err)
+		return err
 	}
-
-	return nil
-}
-
-func gitClone(path string, inputArtifact *wfv1.Artifact, auth transport.AuthMethod, privateKey string) error {
-	cloneOptions := git.CloneOptions{
+	defer closer()
+	repo, err := git.PlainClone(path, false, &git.CloneOptions{
 		URL:               inputArtifact.Git.Repo,
 		RecurseSubmodules: git.DefaultSubmoduleRecursionDepth,
 		Auth:              auth,
-	}
-	if inputArtifact.Git.Depth != nil {
-		cloneOptions.Depth = int(*inputArtifact.Git.Depth)
-	}
-
-	repo, err := git.PlainClone(path, false, &cloneOptions)
+		Depth:             inputArtifact.Git.GetDepth(),
+	})
 	if err != nil {
-		return errors.InternalWrapError(err)
+		return err
 	}
-
 	if inputArtifact.Git.Fetch != nil {
 		refSpecs := make([]config.RefSpec, len(inputArtifact.Git.Fetch))
 		for i, spec := range inputArtifact.Git.Fetch {
 			refSpecs[i] = config.RefSpec(spec)
 		}
-
 		fetchOptions := git.FetchOptions{
+			Auth:     auth,
 			RefSpecs: refSpecs,
+			Depth:    inputArtifact.Git.GetDepth(),
 		}
-		if inputArtifact.Git.Depth != nil {
-			fetchOptions.Depth = int(*inputArtifact.Git.Depth)
-		}
-
 		err = fetchOptions.Validate()
 		if err != nil {
-			return errors.InternalWrapError(err)
+			return err
 		}
-
 		err = repo.Fetch(&fetchOptions)
-		if err != nil {
-			return errors.InternalWrapError(err)
+		if isAlreadyUpToDateErr(err) {
+			return err
 		}
 	}
-
 	if inputArtifact.Git.Revision != "" {
 		// We still rely on forking git for checkout, since go-git does not have a reliable
 		// way of resolving revisions (e.g. mybranch, HEAD^, v1.2.3)
 		log.Infof("Checking out revision %s", inputArtifact.Git.Revision)
 		cmd := exec.Command("git", "checkout", inputArtifact.Git.Revision)
 		cmd.Dir = path
+		cmd.Env = env
 		output, err := cmd.Output()
 		if err != nil {
-			if exErr, ok := err.(*exec.ExitError); ok {
-				log.Errorf("`%s` stderr:\n%s", cmd.Args, string(exErr.Stderr))
-				return errors.InternalError(strings.Split(string(exErr.Stderr), "\n")[0])
-			}
-			return errors.InternalWrapError(err)
-		}
-		log.Errorf("`%s` stdout:\n%s", cmd.Args, string(output))
-		if privateKey != "" {
-			err := writePrivateKey(privateKey, inputArtifact.Git.InsecureIgnoreHostKey)
-			if err != nil {
-				return errors.InternalWrapError(err)
-			}
+			return g.error(err, cmd)
 		}
+		log.Infof("`%s` stdout:\n%s", cmd.Args, string(output))
 		submodulesCmd := exec.Command("git", "submodule", "update", "--init", "--recursive", "--force")
 		submodulesCmd.Dir = path
+		submodulesCmd.Env = env
 		submoduleOutput, err := submodulesCmd.Output()
 		if err != nil {
-			if exErr, ok := err.(*exec.ExitError); ok {
-				log.Errorf("`%s` stderr:\n%s", submodulesCmd.Args, string(exErr.Stderr))
-				return errors.InternalError(strings.Split(string(exErr.Stderr), "\n")[0])
-			}
-			return errors.InternalWrapError(err)
+			return g.error(err, cmd)
 		}
-		log.Errorf("`%s` stdout:\n%s", submodulesCmd.Args, string(submoduleOutput))
+		log.Infof("`%s` stdout:\n%s", cmd.Args, string(submoduleOutput))
 	}
 	return nil
 }
+
+func isAlreadyUpToDateErr(err error) bool {
+	return err != nil && err.Error() != "already up-to-date"
+}
+
+func (g *GitArtifactDriver) error(err error, cmd *exec.Cmd) error {
+	if exErr, ok := err.(*exec.ExitError); ok {
+		log.Errorf("`%s` stderr:\n%s", cmd.Args, string(exErr.Stderr))
+		return errors.New(strings.Split(string(exErr.Stderr), "\n")[0])
+	}
+	return err
+}


Test patch:
diff --git a/cmd/argo/commands/list_test.go b/cmd/argo/commands/list_test.go
index 3ca2c1f94..dfb87bb17 100644
--- a/cmd/argo/commands/list_test.go
+++ b/cmd/argo/commands/list_test.go
@@ -15,20 +15,26 @@ import (
 )
 
 func Test_listWorkflows(t *testing.T) {
+	t.Run("Nothing", func(t *testing.T) {
+		workflows, err := list(&metav1.ListOptions{}, listFlags{})
+		if assert.NoError(t, err) {
+			assert.NotNil(t, workflows)
+		}
+	})
 	t.Run("Status", func(t *testing.T) {
-		workflows, err := list(&metav1.ListOptions{LabelSelector: "workflows.argoproj.io/phase in (Pending,Running),"}, listFlags{status: []string{"Running", "Pending"}})
+		workflows, err := list(&metav1.ListOptions{LabelSelector: "workflows.argoproj.io/phase in (Pending,Running)"}, listFlags{status: []string{"Running", "Pending"}})
 		if assert.NoError(t, err) {
 			assert.NotNil(t, workflows)
 		}
 	})
 	t.Run("Completed", func(t *testing.T) {
-		workflows, err := list(&metav1.ListOptions{LabelSelector: "workflows.argoproj.io/completed=true,"}, listFlags{completed: true})
+		workflows, err := list(&metav1.ListOptions{LabelSelector: "workflows.argoproj.io/completed=true"}, listFlags{completed: true})
 		if assert.NoError(t, err) {
 			assert.NotNil(t, workflows)
 		}
 	})
 	t.Run("Running", func(t *testing.T) {
-		workflows, err := list(&metav1.ListOptions{LabelSelector: "workflows.argoproj.io/completed!=true,"}, listFlags{running: true})
+		workflows, err := list(&metav1.ListOptions{LabelSelector: "workflows.argoproj.io/completed!=true"}, listFlags{running: true})
 		if assert.NoError(t, err) {
 			assert.NotNil(t, workflows)
 		}
diff --git a/workflow/artifacts/git/git_test.go b/workflow/artifacts/git/git_test.go
index 9b5d04e40..c2d51424a 100644
--- a/workflow/artifacts/git/git_test.go
+++ b/workflow/artifacts/git/git_test.go
@@ -1,6 +1,7 @@
 package git
 
 import (
+	"io/ioutil"
 	"os"
 	"testing"
 
@@ -9,23 +10,28 @@ import (
 	wfv1 "github.com/argoproj/argo/pkg/apis/workflow/v1alpha1"
 )
 
+var d = uint64(1)
+
 func TestGitArtifactDriver_Load(t *testing.T) {
+	_ = os.Remove("git-ask-pass.sh")
 	driver := &GitArtifactDriver{}
-	t.Run("Found", func(t *testing.T) {
-		path := "/tmp/git-found"
-		assert.NoError(t, os.RemoveAll(path))
-		assert.NoError(t, os.MkdirAll(path, 0777))
-		d := uint64(1)
-		err := driver.Load(&wfv1.Artifact{
-			ArtifactLocation: wfv1.ArtifactLocation{
-				Git: &wfv1.GitArtifact{Repo: "https://github.com/argoproj/argoproj.git", Revision: "master", Depth: &d},
+	path := "/tmp/git-found"
+	assert.NoError(t, os.RemoveAll(path))
+	assert.NoError(t, os.MkdirAll(path, 0777))
+	err := driver.Load(&wfv1.Artifact{
+		ArtifactLocation: wfv1.ArtifactLocation{
+			Git: &wfv1.GitArtifact{
+				Repo:     "https://github.com/argoproj/argoproj.git",
+				Fetch:    []string{"+refs/heads/*:refs/remotes/origin/*"},
+				Revision: "HEAD",
+				Depth:    &d,
 			},
-		}, path)
-		if assert.NoError(t, err) {
-			_, err := os.Stat(path)
-			assert.NoError(t, err)
-		}
-	})
+		},
+	}, path)
+	if assert.NoError(t, err) {
+		_, err := os.Stat(path)
+		assert.NoError(t, err)
+	}
 }
 
 func TestGitArtifactDriver_Save(t *testing.T) {
@@ -33,3 +39,62 @@ func TestGitArtifactDriver_Save(t *testing.T) {
 	err := driver.Save("", nil)
 	assert.Error(t, err)
 }
+
+func TestGitArtifactDriverLoad_HTTPS(t *testing.T) {
+	if os.Getenv("GITHUB_TOKEN") == "" {
+		t.Skip("not running an GITHUB_TOKEN not set")
+	}
+	_ = os.Remove("git-ask-pass.sh")
+	tmp, err := ioutil.TempDir("", "")
+	assert.NoError(t, err)
+	driver := &GitArtifactDriver{Username: os.Getenv("GITHUB_TOKEN")}
+	assert.NotEmpty(t, driver.Username)
+	err = driver.Load(&wfv1.Artifact{
+		ArtifactLocation: wfv1.ArtifactLocation{
+			Git: &wfv1.GitArtifact{
+				Repo:     "https://github.com/argoproj/argo.git",
+				Fetch:    []string{"+refs/heads/*:refs/remotes/origin/*"},
+				Revision: "HEAD",
+				Depth:    &d,
+			},
+		},
+	}, tmp)
+	assert.NoError(t, err)
+	println(tmp)
+}
+
+func TestGitArtifactDriverLoad_SSL(t *testing.T) {
+	for _, tt := range []struct {
+		name     string
+		insecure bool
+	}{
+		{"Insecure", true},
+		{"Secure", false},
+	} {
+		t.Run(tt.name, func(t *testing.T) {
+			_ = os.Remove("git-ask-pass.sh")
+			key := os.Getenv("HOME") + "/.ssh/id_rsa"
+			data, err := ioutil.ReadFile(key)
+			if err != nil && os.IsNotExist(err) {
+				t.Skip(key + " does not exist")
+			}
+			assert.NoError(t, err)
+			tmp, err := ioutil.TempDir("", "")
+			assert.NoError(t, err)
+			println(tmp)
+			driver := &GitArtifactDriver{SSHPrivateKey: string(data)}
+			err = driver.Load(&wfv1.Artifact{
+				ArtifactLocation: wfv1.ArtifactLocation{
+					Git: &wfv1.GitArtifact{
+						Repo:                  "git@github.com:argoproj/argo.git",
+						Fetch:                 []string{"+refs/heads/*:refs/remotes/origin/*"},
+						Revision:              "HEAD",
+						InsecureIgnoreHostKey: tt.insecure,
+						Depth:                 &d,
+					},
+				},
+			}, tmp)
+			assert.NoError(t, err)
+		})
+	}
+}