From 3507843dab2671c8246795a338134b9f7d82dd6e Mon Sep 17 00:00:00 2001 From: Jianyu Feng Date: Thu, 28 Jul 2022 14:35:42 -0400 Subject: [PATCH 1/5] add tag protection api --- example/tagprotection/main.go | 77 ++++++++++++++++++++ github/repos_tags.go | 76 ++++++++++++++++++++ github/repos_tags_test.go | 132 ++++++++++++++++++++++++++++++++++ 3 files changed, 285 insertions(+) create mode 100644 example/tagprotection/main.go create mode 100644 github/repos_tags.go create mode 100644 github/repos_tags_test.go diff --git a/example/tagprotection/main.go b/example/tagprotection/main.go new file mode 100644 index 00000000000..c94ff1b415d --- /dev/null +++ b/example/tagprotection/main.go @@ -0,0 +1,77 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// The tagprotection command demonstrates the functionality that +// prompts the user for GitHub owner, repo, tag protection pattern and token +// it will create new tag protection if the user enter pattern in prompt +// otherwise it will just list all existing tag protection +package main + +import ( + "bufio" + "context" + "encoding/json" + "fmt" + "os" + "strings" + "syscall" + + "github.com/google/go-github/v45/github" + "golang.org/x/crypto/ssh/terminal" + "golang.org/x/oauth2" +) + +func main() { + // read github owner, repo, token from standard input + r := bufio.NewReader(os.Stdin) + fmt.Print("GitHub Org/User name: ") + owner, _ := r.ReadString('\n') + owner = strings.TrimSpace(owner) + + fmt.Print("GitHub repo name: ") + repo, _ := r.ReadString('\n') + repo = strings.TrimSpace(repo) + + fmt.Print("Tag pattern(leave blank to not create new tag protection): ") + pattern, _ := r.ReadString('\n') + pattern = strings.TrimSpace(pattern) + + fmt.Print("GitHub Token: ") + byteToken, _ := terminal.ReadPassword(int(syscall.Stdin)) + println() + token := string(byteToken) + + ctx := context.Background() + ts := oauth2.StaticTokenSource( + &oauth2.Token{AccessToken: token}, + ) + tc := oauth2.NewClient(ctx, ts) + + client := github.NewClient(tc) + + // create new tag protection + if pattern != "" { + tagProtection, _, err := client.Repositories.CreateTagProtection(context.Background(), owner, repo, pattern) + if err != nil { + fmt.Printf("Error: %v\n", err) + return + } + println() + fmt.Printf("New tag protection created in github.com/%v/%v\n", owner, repo) + tp, _ := json.Marshal(tagProtection) + fmt.Println(string(tp)) + } + + // list all tag protection + println() + fmt.Printf("List all tag protection in github.com/%v/%v\n", owner, repo) + tagProtections, _, err := client.Repositories.ListTagProtection(context.Background(), owner, repo) + if err != nil { + fmt.Printf("Error: %v\n", err) + return + } + results, _ := json.Marshal(tagProtections) + fmt.Println(string(results)) +} diff --git a/github/repos_tags.go b/github/repos_tags.go new file mode 100644 index 00000000000..23bfcff6b71 --- /dev/null +++ b/github/repos_tags.go @@ -0,0 +1,76 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// TagProtection represents a repository tag protection. +type TagProtection struct { + ID *int64 `json:"id"` + Pattern *string `json:"pattern"` +} + +// TagProtectionRequest represents a request to create tag protection +type TagProtectionRequest struct { + // An optional glob pattern to match against when enforcing tag protection. + Pattern string `json:"pattern"` +} + +// ListTagProtection lists tag protection of the specified repository. +// +// GitHub API docs: https://docs.github.com/en/rest/repos/tags#list-tag-protection-states-for-a-repository +func (s *RepositoriesService) ListTagProtection(ctx context.Context, owner, repo string) ([]*TagProtection, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/tags/protection", owner, repo) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var tagProtections []*TagProtection + resp, err := s.client.Do(ctx, req, &tagProtections) + if err != nil { + return nil, resp, err + } + + return tagProtections, resp, nil +} + +// CreateTagProtection creates the tag protection of the specified repository. +// +// GitHub API docs: https://docs.github.com/en/rest/repos/tags#create-a-tag-protection-state-for-a-repository +func (s *RepositoriesService) CreateTagProtection(ctx context.Context, owner, repo, pattern string) (*TagProtection, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/tags/protection", owner, repo) + r := &TagProtectionRequest{Pattern: pattern} + req, err := s.client.NewRequest("POST", u, r) + if err != nil { + return nil, nil, err + } + + tagProtection := new(TagProtection) + resp, err := s.client.Do(ctx, req, tagProtection) + if err != nil { + return nil, resp, err + } + + return tagProtection, resp, nil +} + +// DeleteTagProtection deletes a tag protection from the specified repository. +// +// GitHub API docs: https://docs.github.com/en/rest/repos/tags#delete-a-tag-protection-state-for-a-repository +func (s *RepositoriesService) DeleteTagProtection(ctx context.Context, owner, repo string, tag_protection_id int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/tags/protection/%v", owner, repo, tag_protection_id) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/github/repos_tags_test.go b/github/repos_tags_test.go new file mode 100644 index 00000000000..21d5e5d6db8 --- /dev/null +++ b/github/repos_tags_test.go @@ -0,0 +1,132 @@ +// Copyright 2022 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestRepositoriesService_ListTagProtection(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/tags/protection", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + + fmt.Fprint(w, `[{"id":1, "pattern":"tag1"},{"id":2, "pattern":"tag2"}]`) + }) + + ctx := context.Background() + tagProtections, _, err := client.Repositories.ListTagProtection(ctx, "o", "r") + if err != nil { + t.Errorf("Repositories.ListTagProtection returned error: %v", err) + } + + want := []*TagProtection{{ID: Int64(1), Pattern: String("tag1")}, {ID: Int64(2), Pattern: String("tag2")}} + if !cmp.Equal(tagProtections, want) { + t.Errorf("Repositories.ListTagProtection returned %+v, want %+v", tagProtections, want) + } + + const methodName = "ListTagProtection" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.ListTagProtection(ctx, "\n", "\n") + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.ListTagProtection(ctx, "o", "r") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_ListTagProtection_invalidOwner(t *testing.T) { + client, _, _, teardown := setup() + defer teardown() + + ctx := context.Background() + _, _, err := client.Repositories.ListTagProtection(ctx, "%", "r") + testURLParseError(t, err) +} + +func TestRepositoriesService_CreateTagProtection(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + pattern := "tag*" + + mux.HandleFunc("/repos/o/r/tags/protection", func(w http.ResponseWriter, r *http.Request) { + v := new(TagProtectionRequest) + json.NewDecoder(r.Body).Decode(v) + + testMethod(t, r, "POST") + want := &TagProtectionRequest{Pattern: "tag*"} + if !cmp.Equal(v, want) { + t.Errorf("Request body = %+v, want %+v", v, want) + } + + fmt.Fprint(w, `{"id":1,"pattern":"tag*"}`) + }) + + ctx := context.Background() + got, _, err := client.Repositories.CreateTagProtection(ctx, "o", "r", pattern) + if err != nil { + t.Errorf("Repositories.CreateTagProtection returned error: %v", err) + } + + want := &TagProtection{ID: Int64(1), Pattern: String("tag*")} + if !cmp.Equal(got, want) { + t.Errorf("Repositories.CreateTagProtection returned %+v, want %+v", got, want) + } + + const methodName = "CreateTagProtection" + testBadOptions(t, methodName, func() (err error) { + _, _, err = client.Repositories.CreateTagProtection(ctx, "\n", "\n", pattern) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.CreateTagProtection(ctx, "o", "r", pattern) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_DeleteTagProtection(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/r/tags/protection/1", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + w.WriteHeader(http.StatusNoContent) + }) + + ctx := context.Background() + _, err := client.Repositories.DeleteTagProtection(ctx, "o", "r", 1) + if err != nil { + t.Errorf("Repositories.DeleteTagProtection returned error: %v", err) + } + + const methodName = "DeleteTagProtection" + testBadOptions(t, methodName, func() (err error) { + _, err = client.Repositories.DeleteTagProtection(ctx, "\n", "\n", 1) + return err + }) + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.DeleteTagProtection(ctx, "o", "r", 1) + }) +} From d66cfb437118129820d205190c88d9b56eb8cc46 Mon Sep 17 00:00:00 2001 From: Jianyu Feng Date: Thu, 28 Jul 2022 16:04:36 -0400 Subject: [PATCH 2/5] run go generate --- github/github-accessors.go | 16 ++++++++++++++++ github/github-accessors_test.go | 20 ++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/github/github-accessors.go b/github/github-accessors.go index 334ff395100..af6928ec231 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -17302,6 +17302,22 @@ func (t *Tag) GetVerification() *SignatureVerification { return t.Verification } +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (t *TagProtection) GetID() int64 { + if t == nil || t.ID == nil { + return 0 + } + return *t.ID +} + +// GetPattern returns the Pattern field if it's non-nil, zero value otherwise. +func (t *TagProtection) GetPattern() string { + if t == nil || t.Pattern == nil { + return "" + } + return *t.Pattern +} + // GetCompletedAt returns the CompletedAt field if it's non-nil, zero value otherwise. func (t *TaskStep) GetCompletedAt() Timestamp { if t == nil || t.CompletedAt == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index 4339a879f65..907892a9e59 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -20182,6 +20182,26 @@ func TestTag_GetVerification(tt *testing.T) { t.GetVerification() } +func TestTagProtection_GetID(tt *testing.T) { + var zeroValue int64 + t := &TagProtection{ID: &zeroValue} + t.GetID() + t = &TagProtection{} + t.GetID() + t = nil + t.GetID() +} + +func TestTagProtection_GetPattern(tt *testing.T) { + var zeroValue string + t := &TagProtection{Pattern: &zeroValue} + t.GetPattern() + t = &TagProtection{} + t.GetPattern() + t = nil + t.GetPattern() +} + func TestTaskStep_GetCompletedAt(tt *testing.T) { var zeroValue Timestamp t := &TaskStep{CompletedAt: &zeroValue} From 522a3b0cf84b76b1e176aa68a650a9a0ae2a1128 Mon Sep 17 00:00:00 2001 From: Jianyu Feng Date: Thu, 28 Jul 2022 16:20:17 -0400 Subject: [PATCH 3/5] update name --- github/repos_tags.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/github/repos_tags.go b/github/repos_tags.go index 23bfcff6b71..0e0cf233997 100644 --- a/github/repos_tags.go +++ b/github/repos_tags.go @@ -65,8 +65,8 @@ func (s *RepositoriesService) CreateTagProtection(ctx context.Context, owner, re // DeleteTagProtection deletes a tag protection from the specified repository. // // GitHub API docs: https://docs.github.com/en/rest/repos/tags#delete-a-tag-protection-state-for-a-repository -func (s *RepositoriesService) DeleteTagProtection(ctx context.Context, owner, repo string, tag_protection_id int64) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/tags/protection/%v", owner, repo, tag_protection_id) +func (s *RepositoriesService) DeleteTagProtection(ctx context.Context, owner, repo string, tagProtectionID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/tags/protection/%v", owner, repo, tagProtectionID) req, err := s.client.NewRequest("DELETE", u, nil) if err != nil { return nil, err From 978aceccd2e6419a5b7f2d0cb96e8d1641d7b059 Mon Sep 17 00:00:00 2001 From: Jianyu Feng Date: Thu, 28 Jul 2022 18:41:11 -0400 Subject: [PATCH 4/5] update tagprotection example --- example/tagprotection/main.go | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/example/tagprotection/main.go b/example/tagprotection/main.go index c94ff1b415d..6f347b97ed6 100644 --- a/example/tagprotection/main.go +++ b/example/tagprotection/main.go @@ -4,9 +4,9 @@ // license that can be found in the LICENSE file. // The tagprotection command demonstrates the functionality that -// prompts the user for GitHub owner, repo, tag protection pattern and token -// it will create new tag protection if the user enter pattern in prompt -// otherwise it will just list all existing tag protection +// prompts the user for GitHub owner, repo, tag protection pattern and token, +// then creates a new tag protection if the user entered a pattern at the prompt. +// Otherwise, it will just list all existing tag protections. package main import ( @@ -14,6 +14,7 @@ import ( "context" "encoding/json" "fmt" + "log" "os" "strings" "syscall" @@ -53,10 +54,9 @@ func main() { // create new tag protection if pattern != "" { - tagProtection, _, err := client.Repositories.CreateTagProtection(context.Background(), owner, repo, pattern) + tagProtection, _, err := client.Repositories.CreateTagProtection(ctx, owner, repo, pattern) if err != nil { - fmt.Printf("Error: %v\n", err) - return + log.Fatalf("Error: %v\n", err) } println() fmt.Printf("New tag protection created in github.com/%v/%v\n", owner, repo) @@ -67,10 +67,9 @@ func main() { // list all tag protection println() fmt.Printf("List all tag protection in github.com/%v/%v\n", owner, repo) - tagProtections, _, err := client.Repositories.ListTagProtection(context.Background(), owner, repo) + tagProtections, _, err := client.Repositories.ListTagProtection(ctx, owner, repo) if err != nil { - fmt.Printf("Error: %v\n", err) - return + log.Fatalf("Error: %v\n", err) } results, _ := json.Marshal(tagProtections) fmt.Println(string(results)) From d7f3036bbe6cb3cec4f8c63007302e71063517f8 Mon Sep 17 00:00:00 2001 From: Jianyu Feng Date: Thu, 28 Jul 2022 18:48:14 -0400 Subject: [PATCH 5/5] update tagProtectionRequest struct --- github/repos_tags.go | 6 +++--- github/repos_tags_test.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/github/repos_tags.go b/github/repos_tags.go index 0e0cf233997..ff46d90c731 100644 --- a/github/repos_tags.go +++ b/github/repos_tags.go @@ -16,8 +16,8 @@ type TagProtection struct { Pattern *string `json:"pattern"` } -// TagProtectionRequest represents a request to create tag protection -type TagProtectionRequest struct { +// tagProtectionRequest represents a request to create tag protection. +type tagProtectionRequest struct { // An optional glob pattern to match against when enforcing tag protection. Pattern string `json:"pattern"` } @@ -47,7 +47,7 @@ func (s *RepositoriesService) ListTagProtection(ctx context.Context, owner, repo // GitHub API docs: https://docs.github.com/en/rest/repos/tags#create-a-tag-protection-state-for-a-repository func (s *RepositoriesService) CreateTagProtection(ctx context.Context, owner, repo, pattern string) (*TagProtection, *Response, error) { u := fmt.Sprintf("repos/%v/%v/tags/protection", owner, repo) - r := &TagProtectionRequest{Pattern: pattern} + r := &tagProtectionRequest{Pattern: pattern} req, err := s.client.NewRequest("POST", u, r) if err != nil { return nil, nil, err diff --git a/github/repos_tags_test.go b/github/repos_tags_test.go index 21d5e5d6db8..e5e15a693c0 100644 --- a/github/repos_tags_test.go +++ b/github/repos_tags_test.go @@ -67,11 +67,11 @@ func TestRepositoriesService_CreateTagProtection(t *testing.T) { pattern := "tag*" mux.HandleFunc("/repos/o/r/tags/protection", func(w http.ResponseWriter, r *http.Request) { - v := new(TagProtectionRequest) + v := new(tagProtectionRequest) json.NewDecoder(r.Body).Decode(v) testMethod(t, r, "POST") - want := &TagProtectionRequest{Pattern: "tag*"} + want := &tagProtectionRequest{Pattern: "tag*"} if !cmp.Equal(v, want) { t.Errorf("Request body = %+v, want %+v", v, want) }