From 0891c8030c2835c6c4141da4b6d03346c90eb755 Mon Sep 17 00:00:00 2001 From: Ryo Nakao Date: Mon, 17 Aug 2020 16:52:41 +0900 Subject: [PATCH] Ensure to generate file creation URL (#635) **What this PR does / why we need it**: Each deployment config template comes with a file creation URL, which is for creating a new file under the git path, with template content as default value. Something like: https://github.com/pipe-cd/debug/new/master/kubernetes/analysis-by-metrics?filename=.pipe.yaml&value=%23+This+configures+a+non-progressive+delivery.%0AapiVersion%3A+pipecd.dev%2Fv1beta1%0Akind%3A+KubernetesApp%0Aspec%3A%0A++input%3A%0A++++manifests%3A%0A++++++-+deployment.yaml%0A++++++-+service.yaml%0A++++kubectlVersion%3A+2.1.1 Whereas, I'm getting an issue about specifying filename: https://github.com/isaacs/github/issues/1527. I'll be glad to address it later. **Which issue(s) this PR fixes**: Fixes https://github.com/pipe-cd/pipe/issues/627 **Does this PR introduce a user-facing change?**: ```release-note NONE ``` This PR was merged by Kapetanios. --- pkg/app/api/api/web_api.go | 21 +++++- pkg/app/api/service/webservice/service.proto | 5 +- pkg/git/url.go | 36 ++++++++++ pkg/git/url_test.go | 69 ++++++++++++++++++++ 4 files changed, 128 insertions(+), 3 deletions(-) diff --git a/pkg/app/api/api/web_api.go b/pkg/app/api/api/web_api.go index 8749d704ed..c3885c88f7 100644 --- a/pkg/app/api/api/web_api.go +++ b/pkg/app/api/api/web_api.go @@ -818,8 +818,13 @@ func (a *WebAPI) GetCommand(ctx context.Context, req *webservice.GetCommandReque } func (a *WebAPI) ListDeploymentConfigTemplates(ctx context.Context, req *webservice.ListDeploymentConfigTemplatesRequest) (*webservice.ListDeploymentConfigTemplatesResponse, error) { + app, err := a.getApplication(ctx, req.ApplicationId) + if err != nil { + return nil, err + } + var templates []*webservice.DeploymentConfigTemplate - switch req.ApplicationKind { + switch app.Kind { case model.ApplicationKind_KUBERNETES: templates = k8sDeploymentConfigTemplates case model.ApplicationKind_TERRAFORM: @@ -831,7 +836,19 @@ func (a *WebAPI) ListDeploymentConfigTemplates(ctx context.Context, req *webserv case model.ApplicationKind_CLOUDRUN: templates = cloudrunDeploymentConfigTemplates default: - return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("unknown application kind %v", req.ApplicationKind)) + return nil, status.Error(codes.InvalidArgument, fmt.Sprintf("unknown application kind %v", app.Kind)) + } + for _, t := range templates { + g := app.GetGitPath() + filename := g.ConfigFilename + if filename == "" { + filename = ".pipe.yaml" + } + t.FileCreationUrl, err = git.MakeFileCreationURL(g.Repo.Remote, g.Path, g.Repo.Branch, filename, t.Content) + if err != nil { + a.logger.Error("failed to make a link to creat a file", zap.Error(err)) + return nil, status.Error(codes.Internal, "failed to make a link to creat a file") + } } if len(req.Labels) == 0 { diff --git a/pkg/app/api/service/webservice/service.proto b/pkg/app/api/service/webservice/service.proto index c0b8a95e39..8bd997225a 100644 --- a/pkg/app/api/service/webservice/service.proto +++ b/pkg/app/api/service/webservice/service.proto @@ -164,6 +164,7 @@ message AddApplicationRequest { } message AddApplicationResponse { + string application_id = 1 [(validate.rules).string.min_len = 1]; } message EnableApplicationRequest { @@ -331,6 +332,8 @@ message DeploymentConfigTemplate { string name = 2 [(validate.rules).string.min_len = 1]; repeated DeploymentConfigTemplateLabel labels = 3; string content = 4 [(validate.rules).string.min_len = 1]; + // An HTML link to create a new file under the git path, with template content as default value. + string file_creation_url = 5; } enum DeploymentConfigTemplateLabel { @@ -339,7 +342,7 @@ enum DeploymentConfigTemplateLabel { } message ListDeploymentConfigTemplatesRequest { - pipe.model.ApplicationKind application_kind = 1 [(validate.rules).enum.defined_only = true]; + string application_id = 1 [(validate.rules).string.min_len = 1]; repeated DeploymentConfigTemplateLabel labels = 2; } diff --git a/pkg/git/url.go b/pkg/git/url.go index 53f6b615e3..37dbf18400 100644 --- a/pkg/git/url.go +++ b/pkg/git/url.go @@ -86,6 +86,42 @@ func MakeDirURL(repoURL, dir, branch string) (string, error) { return fmt.Sprintf("%s://%s/%s/%s/%s/%s", scheme, u.Host, repoPath, subPath, branch, dir), nil } +// MakeFileCreationURL builds a link to create a file under the given directory. +func MakeFileCreationURL(repoURL, dir, branch, filename, value string) (string, error) { + if branch == "" { + return "", fmt.Errorf("no branch given") + } + u, err := parseGitURL(repoURL) + if err != nil { + return "", err + } + + if u.Scheme == "ssh" { + u.Scheme = "https" + u.User = nil + } + repoPath := strings.TrimSuffix(strings.Trim(u.Path, "/"), ".git") + dir = strings.Trim(dir, "/") + + switch u.Host { + case "github.com": + u.Path = fmt.Sprintf("%s/%s/%s/%s", repoPath, "new", branch, dir) + params := &url.Values{} + if filename != "" { + params.Add("filename", filename) + } + if value != "" { + params.Add("value", value) + } + u.RawQuery = params.Encode() + default: + // TODO: Allow users to specify git host + u.Path = fmt.Sprintf("%s/%s/%s/%s", repoPath, "new", branch, dir) + } + + return u.String(), nil +} + var ( knownSchemes = map[string]interface{}{ "ssh": struct{}{}, diff --git a/pkg/git/url_test.go b/pkg/git/url_test.go index 071063da8f..2efdd424ce 100644 --- a/pkg/git/url_test.go +++ b/pkg/git/url_test.go @@ -163,6 +163,75 @@ func TestMakeDirURL(t *testing.T) { }) } } + +func TestMakeFileCreationURL(t *testing.T) { + tests := []struct { + name string + repoURL string + dir string + branch string + filename string + value string + want string + wantErr bool + }{ + { + name: "given filename", + repoURL: "git@github.com:org/repo.git", + dir: "path/to", + branch: "abc", + filename: "foo.txt", + want: "https://github.com/org/repo/new/abc/path/to?filename=foo.txt", + wantErr: false, + }, + { + name: "given filename and value", + repoURL: "git@github.com:org/repo.git", + dir: "path/to", + branch: "abc", + filename: "foo.txt", + value: `# Comment +foo: + bar: + baz: + - a + - b +`, + want: "https://github.com/org/repo/new/abc/path/to?filename=foo.txt&value=%23+Comment%0Afoo%3A%0A++bar%3A%0A++++baz%3A%0A++++++-+a%0A++++++-+b%0A", + wantErr: false, + }, + { + name: "ssh to unsupported git host", + repoURL: "git@foo.com:org/repo.git", + dir: "path/to", + branch: "abc", + want: "https://foo.com/org/repo/new/abc/path/to", + wantErr: false, + }, + { + name: "no branch given", + repoURL: "1234abcd", + dir: "path/to", + wantErr: true, + }, + { + name: "unparseable url", + repoURL: "1234abcd", + dir: "path/to", + branch: "abc", + want: "", + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := MakeFileCreationURL(tt.repoURL, tt.dir, tt.branch, tt.filename, tt.value) + assert.Equal(t, tt.wantErr, err != nil) + assert.Equal(t, tt.want, got) + }) + } +} + func TestParseGitURL(t *testing.T) { tests := []struct { name string