From 5d54f1729d9fbacf687decb838de1f7b735636db Mon Sep 17 00:00:00 2001 From: Shadi Samadi Date: Fri, 29 Dec 2023 15:28:39 +1100 Subject: [PATCH 01/22] Create codeql.yml --- .github/workflows/codeql.yml | 40 ++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/workflows/codeql.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 0000000..b9570d8 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,40 @@ +name: "CodeQL" + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + schedule: + - cron: '35 4 * * 6' + +jobs: + analyze: + name: Analyze + runs-on: 'ubuntu-latest' + timeout-minutes: 360 + permissions: + actions: read + contents: read + security-events: write + + strategy: + fail-fast: false + matrix: + language: [ 'go' ] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{matrix.language}}" From 29a1232339f68748f7663eb44272f31747a86713 Mon Sep 17 00:00:00 2001 From: Shadi Samadi Date: Fri, 29 Dec 2023 15:44:18 +1100 Subject: [PATCH 02/22] Create LICENSE.txt --- LICENSE.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE.txt diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..f28ab09 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright GitHub + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From a07e31a93e9bc0529f155cbd09199a1af6e69dba Mon Sep 17 00:00:00 2001 From: Shadi Samadi Date: Fri, 29 Dec 2023 15:52:10 +1100 Subject: [PATCH 03/22] Create CODEOWNERS --- .github/CODEOWNERS | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..85c632a --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# This repository is maintained by: +* @therealkujo @s-samadi From 0e3613c06741a24e1b683e8984575234998b3619 Mon Sep 17 00:00:00 2001 From: Shadi Samadi Date: Fri, 29 Dec 2023 15:55:24 +1100 Subject: [PATCH 04/22] Create CONTRIBUTING.md --- CONTRIBUTING.md | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..4a1b9b4 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,43 @@ +## Contributing + +[fork]: https://github.com/github/REPO/fork +[pr]: https://github.com/github/REPO/compare +[style]: https://github.com/github/REPO/blob/main/.golangci.yaml +[code-of-conduct]: CODE_OF_CONDUCT.md + +Hi there! We're thrilled that you'd like to contribute to this project. Your help is essential for keeping it great. + +Contributions to this project are [released](https://help.github.com/articles/github-terms-of-service/#6-contributions-under-repository-license) to the public under the [project's open source license](LICENSE.md). + +Please note that this project is released with a [Contributor Code of Conduct](CODE_OF_CONDUCT.md). By participating in this project you agree to abide by its terms. + +## Prerequisites for running and testing code + +These are one time installations required to be able to test your changes locally as part of the pull request (PR) submission process. + +1. install Go [through download](https://go.dev/doc/install) | [through Homebrew](https://formulae.brew.sh/formula/go) +1. [install golangci-lint](https://golangci-lint.run/usage/install/#local-installation) + +## Submitting a pull request + +1. [Fork][fork] and clone the repository +1. Configure and install the dependencies: `script/bootstrap` +1. Make sure the tests pass on your machine: `go test -v ./...` +1. Make sure linter passes on your machine: `golangci-lint run` +1. Create a new branch: `git checkout -b my-branch-name` +1. Make your change, add tests, and make sure the tests and linter still pass +1. Push to your fork and [submit a pull request][pr] +1. Pat yourself on the back and wait for your pull request to be reviewed and merged. + +Here are a few things you can do that will increase the likelihood of your pull request being accepted: + +- Follow the [style guide][style]. +- Write tests. +- Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests. +- Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). + +## Resources + +- [How to Contribute to Open Source](https://opensource.guide/how-to-contribute/) +- [Using Pull Requests](https://help.github.com/articles/about-pull-requests/) +- [GitHub Help](https://help.github.com) From 9ab68c27115cd14575de49ac51b10b57755f41fa Mon Sep 17 00:00:00 2001 From: Shadi Samadi Date: Tue, 9 Jan 2024 11:36:21 +1100 Subject: [PATCH 05/22] Updated tests to call stubs --- .github/workflows/test.yml | 10 +- cmd/codescanning.go | 39 ++-- cmd/common.go | 95 +++++----- cmd/common_test.go | 60 +++--- cmd/deletebranch.go | 2 +- cmd/http_mock.go | 375 +++++++++++++++++++++++++++++++++++++ cmd/test_client_setup.go | 60 ++++++ go.mod | 21 ++- go.sum | 51 +++-- 9 files changed, 576 insertions(+), 137 deletions(-) create mode 100644 cmd/http_mock.go create mode 100644 cmd/test_client_setup.go diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d009ecb..86d5df4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,9 +1,9 @@ name: Go Tests on: push: - branches: [ main ] + branches: [main] pull_request: - branches: [ main ] + branches: [main] jobs: build: @@ -14,12 +14,10 @@ jobs: - name: Setup Go uses: actions/setup-go@v4 with: - go-version: '1.20.x' + go-version: "1.20.x" - name: Install dependencies run: go get . - name: Build run: go build -v ./... - name: Test with the Go CLI - run: go test -v ./... - env: - GITHUB_TOKEN: ${{ secrets.TOKEN }} + run: go test -v ./... -cover diff --git a/cmd/codescanning.go b/cmd/codescanning.go index b27622e..bb7fdb8 100644 --- a/cmd/codescanning.go +++ b/cmd/codescanning.go @@ -2,13 +2,14 @@ package cmd import ( "encoding/csv" - "fmt" "errors" + "fmt" "io" "log" "os" "strings" + "github.com/cli/go-gh/v2/pkg/api" "github.com/spf13/cobra" ) @@ -33,7 +34,7 @@ func init() { // codeScanningCmd.MarkFlagsOneRequired("csv", "organization") // codeScanningCmd.MarkFlagsOneRequired("workflow", "template") codeScanningCmd.PersistentFlags().BoolVarP(&Force, "force", "f", false, "force enable code scanning advanced setup or update the existing code scanning workflow file") - + } var codeScanningCmd = &cobra.Command{ @@ -73,6 +74,12 @@ var codeScanningCmd = &cobra.Command{ log.Fatalln("ERROR: You cannot provide both workflow flag and template flag") } + //set up github client + client, err := api.DefaultRESTClient() + if err != nil { + log.Fatalln("ERROR: Unable to create REST client: ", err) + } + var repos []Repository if len(CsvFile) > 0 { @@ -99,7 +106,7 @@ var codeScanningCmd = &cobra.Command{ for _, repository := range repositories { log.Printf("Retrieving Repository: %s \n", repository) - repo, err := getRepo(repository) + repo, err := getRepo(repository, client) if err != nil { Errors[repository] = err } @@ -108,17 +115,17 @@ var codeScanningCmd = &cobra.Command{ } else if len(args) > 0 { for _, repository := range args { log.Printf("Retrieving Repository: %s \n", repository) - repo, err := getRepo(repository) + repo, err := getRepo(repository, client) if err != nil { Errors[repository] = err } else { - repos = append(repos, repo) + repos = append(repos, repo) } } } else { log.Printf("Retrieving Repositories for the Organization: %s \n", Organization) - if repos, err = getRepos(Organization); err != nil { + if repos, err = getRepos(Organization, client); err != nil { log.Fatalln(err) } } @@ -132,7 +139,7 @@ var codeScanningCmd = &cobra.Command{ log.Printf("Details for Repository: Full Name: %s; Name: %s; Default Branch: %s\n", repo.FullName, repo.Name, repo.DefaultBranch) //check that repo has at least one codeql supported language - coverage, err := repo.GetCodeqlLanguages() + coverage, err := repo.GetCodeqlLanguages(client) if err != nil { log.Printf("ERROR: Unable to get repo languages, skipping repository \"%s\"\n Error Message: %s\n", repo.FullName, err) continue @@ -145,7 +152,7 @@ var codeScanningCmd = &cobra.Command{ } //check that default setup is not enabled - isDefaultSetupEnabled, err := repo.checkDefaultSetupEnabled() + isDefaultSetupEnabled, err := repo.checkDefaultSetupEnabled(client) if err != nil { log.Println(err) Errors[repo.FullName] = err @@ -158,7 +165,7 @@ var codeScanningCmd = &cobra.Command{ } else if isDefaultSetupEnabled == true && Force == true { log.Printf("Default setup already enabled for this repository: %s, but force flag is set, converting repo to advanced setup", repo.FullName) - result, err := repo.disableDefaultSetup() + result, err := repo.disableDefaultSetup(client) if err != nil { Errors[repo.FullName] = err continue @@ -170,7 +177,7 @@ var codeScanningCmd = &cobra.Command{ } //check that codeql workflow file doesn't already exist - isCodeQLEnabled, sha, err := repo.doesCodeqlWorkflowExist() + isCodeQLEnabled, sha, err := repo.doesCodeqlWorkflowExist(client) if err != nil { log.Println(err) Errors[repo.FullName] = err @@ -184,18 +191,18 @@ var codeScanningCmd = &cobra.Command{ log.Printf("CodeQL workflow file already exists for this repository: %s, but force flag is set, updating workflow file", repo.FullName) } - newbranchref, err := repo.createBranchForRepo() + newbranchref, err := repo.createBranchForRepo(client) if err != nil { // log.Println(err) if strings.Contains(err.Error(), "already exists") && Force == true { log.Printf("Force flag is set, removing existing branch for repository: %s\n", repo.FullName) - err := repo.deleteBranch() + err := repo.deleteBranch(client) if err != nil { Errors[repo.FullName] = err continue } log.Printf("Successfully removed branch for repository: %s\n", repo.FullName) - newbranchref, err = repo.createBranchForRepo() + newbranchref, err = repo.createBranchForRepo(client) if err != nil { Errors[repo.FullName] = err continue @@ -210,7 +217,7 @@ var codeScanningCmd = &cobra.Command{ Errors[repo.FullName] = errors.New("Something went wrong when creating new branch") } log.Printf("Ref created succesfully at : %s\n", newbranchref) - + var workflowFile []byte if len(TemplateFile) > 0 { workflowFile, err = repo.generateCodeqlWorkflowFile(TemplateFile) @@ -226,7 +233,7 @@ var codeScanningCmd = &cobra.Command{ } } - createdFile, err := repo.commitWorkflowFile(workflowFile, sha) + createdFile, err := repo.commitWorkflowFile(client, workflowFile, sha) if err != nil { log.Println(err) continue @@ -238,7 +245,7 @@ var codeScanningCmd = &cobra.Command{ } log.Printf("Successfully created file %s on branch %s in repository %s\n", createdFile, newbranchref, repo.FullName) - createdPR, err := repo.raisePullRequest() + createdPR, err := repo.raisePullRequest(client) if err != nil { log.Println(err) continue diff --git a/cmd/common.go b/cmd/common.go index a78508d..77fcf3d 100644 --- a/cmd/common.go +++ b/cmd/common.go @@ -34,18 +34,17 @@ const ( DELETE ) -func getRepos(Organization string) ([]Repository, error) { +func getRepos(Organization string, client Client) ([]Repository, error) { requestPath := fmt.Sprintf("orgs/%s/repos", Organization) page := 1 var allrepos []Repository - for { log.Printf("Getting all repositories for organization: %s\n", Organization) data := []Repository{} - statusCode, nextPage, err := callApi(requestPath, &data, GET) + statusCode, nextPage, err := callApi(client, requestPath, &data, GET) if err != nil { // check if the error is a 404 @@ -54,8 +53,8 @@ func getRepos(Organization string) ([]Repository, error) { return allrepos, err } else { - log.Printf("ERROR: Unable to get repositories for organization %s\n", Organization) - return allrepos, err + log.Printf("ERROR: Unable to get repositories for organization %s\n", Organization) + return allrepos, err } } @@ -76,13 +75,13 @@ func getRepos(Organization string) ([]Repository, error) { return allrepos, nil } -func getRepo(RepositoryName string) (Repository, error) { +func getRepo(RepositoryName string, client Client) (Repository, error) { requestPath := fmt.Sprintf("repos/%s", RepositoryName) var repo Repository log.Printf("Getting repo: %s\n", RepositoryName) - statusCode, _, err := callApi(requestPath, &repo, GET) + statusCode, _, err := callApi(client, requestPath, &repo, GET) if err != nil { // check if the error is a 404 @@ -91,20 +90,15 @@ func getRepo(RepositoryName string) (Repository, error) { return repo, err } else { - log.Printf("ERROR: Unable to get repository %s\n", RepositoryName) - return repo, err + log.Printf("ERROR: Unable to get repository %s\n", RepositoryName) + return repo, err } } return repo, nil } -func callApi(requestPath string, parseType interface{}, method HttpMethod, postBody ...[]byte) (int, string, error) { - client, err := api.DefaultRESTClient() - if err != nil { - log.Println("ERROR: Unable to create REST client") - return -1, "", err - } +func callApi(client Client, requestPath string, parseType interface{}, method HttpMethod, postBody ...[]byte) (int, string, error) { var httpMethod string switch method { @@ -127,29 +121,28 @@ func callApi(requestPath string, parseType interface{}, method HttpMethod, postB body = nil } - - response, err := client.Request(httpMethod, requestPath, body) - if err != nil { + response, err := client.Request(httpMethod, requestPath, body) + if err != nil { var httpError *api.HTTPError errors.As(err, &httpError) return httpError.StatusCode, "", err - } + } - defer response.Body.Close() - nextPage := response.Header.Get("Link") - responseBody, err := io.ReadAll(response.Body) - if err != nil { - log.Println("ERROR: Unable to read next page link") - return response.StatusCode, nextPage, err - } + defer response.Body.Close() + nextPage := response.Header.Get("Link") + responseBody, err := io.ReadAll(response.Body) + if err != nil { + log.Println("ERROR: Unable to read next page link") + return response.StatusCode, nextPage, err + } err = decodeJSONResponse(responseBody, &parseType) if err != nil { return response.StatusCode, nextPage, err } - return response.StatusCode, nextPage, nil + return response.StatusCode, nextPage, nil } func decodeJSONResponse(body []byte, parseType interface{}) error { @@ -163,12 +156,12 @@ func decodeJSONResponse(body []byte, parseType interface{}) error { return nil } -func (repo *Repository) GetCodeqlLanguages() ([]string, error) { +func (repo *Repository) GetCodeqlLanguages(client Client) ([]string, error) { var repoLanguages map[string]int requestPath := fmt.Sprintf("repos/%s/languages", repo.FullName) //get languages for repo - _, _, err := callApi(requestPath, &repoLanguages, GET) + _, _, err := callApi(client, requestPath, &repoLanguages, GET) if err != nil { log.Printf("ERROR: Unable to get languages for repository %s\n", repo.FullName) return nil, err @@ -182,6 +175,7 @@ func (repo *Repository) GetCodeqlLanguages() ([]string, error) { } } return codeqlLanguages, nil + } func findNextPage(nextPageLink string) (string, bool) { @@ -195,10 +189,10 @@ func findNextPage(nextPageLink string) (string, bool) { } -func (repo *Repository) checkDefaultSetupEnabled() (bool, error) { +func (repo *Repository) checkDefaultSetupEnabled(client Client) (bool, error) { var defaultSetupEnabledResponse interface{} requestPath := fmt.Sprintf("repos/%s/code-scanning/default-setup", repo.FullName) - statusCode, _, err := callApi(requestPath, &defaultSetupEnabledResponse, GET) + statusCode, _, err := callApi(client, requestPath, &defaultSetupEnabledResponse, GET) if statusCode == 404 { log.Printf("The repository %s does not exist\n", repo.FullName) return false, err @@ -219,9 +213,10 @@ func (repo *Repository) checkDefaultSetupEnabled() (bool, error) { log.Printf("ERROR: Unable to get default setup status for repository %s\n", repo.FullName) return false, err + } -func (repo *Repository) disableDefaultSetup() (bool, error) { +func (repo *Repository) disableDefaultSetup(client Client) (bool, error) { type requestBody struct { State string `json:"state"` } @@ -234,7 +229,7 @@ func (repo *Repository) disableDefaultSetup() (bool, error) { } requestPath := fmt.Sprintf("repos/%s/code-scanning/default-setup", repo.FullName) - statusCode, _, err := callApi(requestPath, nil, PATCH, jsonData) + statusCode, _, err := callApi(client, requestPath, nil, PATCH, jsonData) if statusCode == 404 { log.Printf("The repository %s does not exist\n", repo.FullName) return false, err @@ -247,17 +242,18 @@ func (repo *Repository) disableDefaultSetup() (bool, error) { } else if statusCode == 200 { log.Printf("Successfully disabled default setup for the repository %s\n", repo.FullName) return true, nil - } + } log.Printf("ERROR: Unable to enable default setup for repository %s\n", repo.FullName) return false, err + } -func (repo *Repository) createBranchForRepo() (string, error) { +func (repo *Repository) createBranchForRepo(client Client) (string, error) { //get sha for default repoBranches := map[string]interface{}{} requestPath := fmt.Sprintf("repos/%s/branches/%s", repo.FullName, repo.DefaultBranch) - statusCode, _, err := callApi(requestPath, &repoBranches, GET) + statusCode, _, err := callApi(client, requestPath, &repoBranches, GET) if statusCode == 404 { log.Printf("ERROR: The branch \"%s\" in %s does not exist\n", repo.DefaultBranch, repo.FullName) return "", err @@ -285,7 +281,7 @@ func (repo *Repository) createBranchForRepo() (string, error) { var postresp interface{} requestPath = fmt.Sprintf("repos/%s/git/refs", repo.FullName) - statusCode, _, err = callApi(requestPath, &postresp, POST, jsonData) + statusCode, _, err = callApi(client, requestPath, &postresp, POST, jsonData) if statusCode == 422 { log.Printf("ERROR: The branch \"%s\" already exists in repo %s\n", request.Ref, repo.FullName) return "", err @@ -300,11 +296,11 @@ func (repo *Repository) createBranchForRepo() (string, error) { } -func (repo *Repository) doesCodeqlWorkflowExist() (bool, string, error) { +func (repo *Repository) doesCodeqlWorkflowExist(client Client) (bool, string, error) { // skipped repos - continue on error and return out if there is a response because it means the file already exists var response interface{} requestPath := fmt.Sprintf("repos/%s/contents/.github/workflows/codeql.yml", repo.FullName) - statusCode, _, err := callApi(requestPath, &response, GET) + statusCode, _, err := callApi(client, requestPath, &response, GET) if statusCode == 200 { log.Printf("CodeQL workflow file already exists for repo: %s\n", repo.FullName) sha := gojsonq.New().FromInterface(response).Find("sha") @@ -316,6 +312,7 @@ func (repo *Repository) doesCodeqlWorkflowExist() (bool, string, error) { log.Printf("ERROR: Unable to check for existence of CodeQL workflow for repository: %s\n", repo.FullName) return true, "", err } + } func (repo *Repository) readCodeqlWorkflowFile(WorkflowFile string) ([]byte, error) { @@ -354,7 +351,7 @@ func (repo *Repository) generateCodeqlWorkflowFile(TemplateWorkflowFile string) return []byte(workflowFile), nil } -func (repo *Repository) commitWorkflowFile(WorkflowFile []byte, commitSha string) (string, error) { +func (repo *Repository) commitWorkflowFile(client Client, WorkflowFile []byte, commitSha string) (string, error) { encoded := base64.StdEncoding.EncodeToString((WorkflowFile)) type Commiter struct { @@ -367,7 +364,7 @@ func (repo *Repository) commitWorkflowFile(WorkflowFile []byte, commitSha string Committer Commiter `json:"commiter"` Branch string `json:"branch"` Content string `json:"content"` - Sha *string `json:"sha,omitempty"` + Sha *string `json:"sha,omitempty"` } request := RequestBody{ @@ -378,7 +375,7 @@ func (repo *Repository) commitWorkflowFile(WorkflowFile []byte, commitSha string }, Branch: "gh-cli/codescanningworkflow", Content: encoded, - Sha: &commitSha, + Sha: &commitSha, } jsonData, err := json.Marshal(request) @@ -390,7 +387,7 @@ func (repo *Repository) commitWorkflowFile(WorkflowFile []byte, commitSha string //create workflow file var createresponse interface{} requestPath := fmt.Sprintf("repos/%s/contents/.github/workflows/codeql.yml", repo.FullName) - statusCode, _, err := callApi(requestPath, &createresponse, PUT, jsonData) + statusCode, _, err := callApi(client, requestPath, &createresponse, PUT, jsonData) if statusCode == 404 { log.Printf("ERROR: The branch \"gh-cli/codescanningworkflow\" does not exist in repo %s\n", repo.FullName) return "", err @@ -400,7 +397,7 @@ func (repo *Repository) commitWorkflowFile(WorkflowFile []byte, commitSha string } else if statusCode == 201 { log.Printf("Successfully created CodeQL workflow file for repo %s\n", repo.FullName) } else if statusCode == 200 { - log.Printf("Successfully updated CodeQL workflow file for repo %s\n", repo.FullName) + log.Printf("Successfully updated CodeQL workflow file for repo %s\n", repo.FullName) } else { log.Printf("ERROR: Unable to create CodeQL workflow file for repository %s\n", repo.FullName) return "", err @@ -408,10 +405,9 @@ func (repo *Repository) commitWorkflowFile(WorkflowFile []byte, commitSha string createdFile := gojsonq.New().FromInterface(createresponse).Find("content.name") return fmt.Sprint(createdFile), nil - } -func (repo *Repository) raisePullRequest() (string, error) { +func (repo *Repository) raisePullRequest(client Client) (string, error) { type PullRequestBody struct { Title string `json:"title"` @@ -459,7 +455,7 @@ func (repo *Repository) raisePullRequest() (string, error) { //create pull request var createPullRequest interface{} requestPath := fmt.Sprintf("repos/%s/pulls", repo.FullName) - statusCode, _, err := callApi(requestPath, &createPullRequest, POST, jsonData) + statusCode, _, err := callApi(client, requestPath, &createPullRequest, POST, jsonData) if statusCode == 201 { log.Printf("Successfully created pull request for repo %s\n", repo.FullName) } else if statusCode == 422 { @@ -474,9 +470,9 @@ func (repo *Repository) raisePullRequest() (string, error) { return fmt.Sprint(createdPullRequest), nil } -func (repo *Repository) deleteBranch() error { +func (repo *Repository) deleteBranch(client Client) error { requestPath := fmt.Sprintf("repos/%s/git/refs/heads/gh-cli/codescanningworkflow", repo.FullName) - statusCode, _, err := callApi(requestPath, nil, DELETE, nil) + statusCode, _, err := callApi(client, requestPath, nil, DELETE, nil) if statusCode == 204 { log.Printf("Successfully deleted branch for repo %s\n", repo.FullName) } else if statusCode == 422 { @@ -487,4 +483,5 @@ func (repo *Repository) deleteBranch() error { return err } return nil + } diff --git a/cmd/common_test.go b/cmd/common_test.go index 9a0231b..218e748 100644 --- a/cmd/common_test.go +++ b/cmd/common_test.go @@ -4,13 +4,12 @@ import ( "reflect" "strings" "testing" - - "github.com/cli/go-gh/v2/pkg/api" ) func Test_getRepos(t *testing.T) { + type args struct { - client *api.RESTClient + client *TestClient Organization string } @@ -85,8 +84,10 @@ func Test_getRepos(t *testing.T) { }, } for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { - got, err := getRepos(tt.args.Organization) + client := &TestClient{} + got, err := getRepos(tt.args.Organization, client) if (err != nil) != tt.wantErr { t.Errorf("getRepos() error = %v, wantErr %v", err, tt.wantErr) return @@ -139,7 +140,8 @@ func Test_getRepo(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := getRepo(tt.args.RepositoryName) + client := &TestClient{} + got, err := getRepo(tt.args.RepositoryName, client) if (err != nil) != tt.wantErr { t.Errorf("getRepo() error = %v, wantErr %v", err, tt.wantErr) return @@ -239,7 +241,8 @@ func Test_callApi(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, got1, err := callApi(tt.args.requestPath, tt.args.parseType, tt.args.method, tt.args.body) + client := &TestClient{} + got, got1, err := callApi(client, tt.args.requestPath, tt.args.parseType, tt.args.method, tt.args.body) if (err != nil) != tt.wantErr { t.Errorf("callApi() error = %v, wantErr %v", err, tt.wantErr) return @@ -317,12 +320,13 @@ func TestRepository_GetCodeqlLanguages(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + client := &TestClient{} repo := &Repository{ FullName: tt.fields.FullName, Name: tt.fields.Name, DefaultBranch: tt.fields.DefaultBranch, } - got, err := repo.GetCodeqlLanguages() + got, err := repo.GetCodeqlLanguages(client) if (err != nil) != tt.wantErr { t.Errorf("Repository.GetCodeqlLanguages() error = %v, wantErr %v", err, tt.wantErr) return @@ -451,12 +455,13 @@ func TestRepository_checkDefaultSetupEnabled(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + client := &TestClient{} repo := &Repository{ FullName: tt.fields.FullName, Name: tt.fields.Name, DefaultBranch: tt.fields.DefaultBranch, } - got, err := repo.checkDefaultSetupEnabled() + got, err := repo.checkDefaultSetupEnabled(client) if (err != nil) != tt.wantErr { t.Errorf("Repository.checkDefaultSetupEnabled() error = %v, wantErr %v", err, tt.wantErr) return @@ -537,12 +542,13 @@ func TestRepository_disableDefaultSetup(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + client := &TestClient{} repo := &Repository{ FullName: tt.fields.FullName, Name: tt.fields.Name, DefaultBranch: tt.fields.DefaultBranch, } - got, err := repo.disableDefaultSetup() + got, err := repo.disableDefaultSetup(client) if (err != nil) != tt.wantErr { t.Errorf("Repository.disableDefaultSetup() error = %v, wantErr %v", err, tt.wantErr) return @@ -599,8 +605,8 @@ func TestRepository_createBranchForRepo(t *testing.T) { { name: "When the repository has the branch codescanningworkflow", fields: fields{ - FullName: "paradisisland/maria", - Name: "maria", + FullName: "paradisisland/rose", + Name: "rose", DefaultBranch: "main", }, want: "", @@ -621,12 +627,13 @@ func TestRepository_createBranchForRepo(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + client := &TestClient{} repo := &Repository{ FullName: tt.fields.FullName, Name: tt.fields.Name, DefaultBranch: tt.fields.DefaultBranch, } - got, err := repo.createBranchForRepo() + got, err := repo.createBranchForRepo(client) if (err != nil) != tt.wantErr { t.Errorf("Repository.createBranchForRepo() error = %v, wantErr %v", err, tt.wantErr) return @@ -709,12 +716,13 @@ func TestRepository_doesCodeqlWorkflowExist(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + client := &TestClient{} repo := &Repository{ FullName: tt.fields.FullName, Name: tt.fields.Name, DefaultBranch: tt.fields.DefaultBranch, } - got, got1, err := repo.doesCodeqlWorkflowExist() + got, got1, err := repo.doesCodeqlWorkflowExist(client) if (err != nil) != tt.wantErr { t.Errorf("Repository.doesCodeqlWorkflowExist() error = %v, wantErr %v", err, tt.wantErr) return @@ -799,6 +807,7 @@ func TestRepository_readCodeqlWorkflowFile(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + repo := &Repository{ FullName: tt.fields.FullName, Name: tt.fields.Name, @@ -902,8 +911,6 @@ func TestRepository_generateCodeqlWorkflowFile(t *testing.T) { } } - - func TestRepository_commitWorkflowFile(t *testing.T) { type fields struct { FullName string @@ -948,8 +955,8 @@ func TestRepository_commitWorkflowFile(t *testing.T) { { name: "When the repository has the CodeQL workflow file", fields: fields{ - FullName: "paradisisland/maria", - Name: "maria", + FullName: "paradisisland/rose", + Name: "rose", DefaultBranch: "main", }, args: args{ @@ -991,16 +998,16 @@ func TestRepository_commitWorkflowFile(t *testing.T) { want: "codeql.yml", wantErr: false, }, - } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + client := &TestClient{} repo := &Repository{ FullName: tt.fields.FullName, Name: tt.fields.Name, DefaultBranch: tt.fields.DefaultBranch, } - got, err := repo.commitWorkflowFile(tt.args.WorkflowFile, tt.args.commitSha) + got, err := repo.commitWorkflowFile(client, tt.args.WorkflowFile, tt.args.commitSha) if (err != nil) != tt.wantErr { t.Errorf("Repository.commitWorkflowFile() error = %v, wantErr %v", err, tt.wantErr) return @@ -1046,8 +1053,8 @@ func TestRepository_raisePullRequest(t *testing.T) { { name: "When the repository has a pull request", fields: fields{ - FullName: "paradisisland/maria", - Name: "maria", + FullName: "paradisisland/rose", + Name: "rose", DefaultBranch: "main", }, want: "", @@ -1068,12 +1075,13 @@ func TestRepository_raisePullRequest(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + client := &TestClient{} repo := &Repository{ FullName: tt.fields.FullName, Name: tt.fields.Name, DefaultBranch: tt.fields.DefaultBranch, } - got, err := repo.raisePullRequest() + got, err := repo.raisePullRequest(client) if (err != nil) != tt.wantErr { t.Errorf("Repository.raisePullRequest() error = %v, wantErr %v", err, tt.wantErr) return @@ -1129,8 +1137,8 @@ func TestRepository_deleteBranch(t *testing.T) { { name: "When the repository does not have the codescanningworkflow branch", fields: fields{ - FullName: "paradisisland/maria", - Name: "maria", + FullName: "paradisisland/rose", + Name: "rose", DefaultBranch: "main", }, wantErr: true, @@ -1149,15 +1157,15 @@ func TestRepository_deleteBranch(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + client := &TestClient{} repo := &Repository{ FullName: tt.fields.FullName, Name: tt.fields.Name, DefaultBranch: tt.fields.DefaultBranch, } - if err := repo.deleteBranch(); (err != nil) != tt.wantErr { + if err := repo.deleteBranch(client); (err != nil) != tt.wantErr { t.Errorf("Repository.deleteBranch() error = %v, wantErr %v", err, tt.wantErr) } }) } } - diff --git a/cmd/deletebranch.go b/cmd/deletebranch.go index c045df5..91ca29e 100644 --- a/cmd/deletebranch.go +++ b/cmd/deletebranch.go @@ -41,7 +41,7 @@ var deleteBranchCmd = &cobra.Command{ log.Printf("Retrieving Repositories for the Organization: %s .\n", Organization) var repos []Repository - if repos, err = getRepos(Organization); err != nil { + if repos, err = getRepos(Organization, client); err != nil { log.Fatalln(err) } diff --git a/cmd/http_mock.go b/cmd/http_mock.go new file mode 100644 index 0000000..284e87f --- /dev/null +++ b/cmd/http_mock.go @@ -0,0 +1,375 @@ +package cmd + +import ( + "fmt" + "net/http" + "strings" + + "github.com/cli/go-gh/v2/pkg/api" +) + +// MockDeleteResponse simulates a DELETE HTTP response for a given path. +// It returns a JSON string, a status code, and an error if the operation fails. +func MockDeleteResponse(path string) (string, int, error) { + switch path { + case "repos/paradisisland/maria/git/refs/heads/gh-cli/codescanningworkflow", + "repos/paradisisland/shiganshima/git/refs/heads/gh-cli/codescanningworkflow": + return `{}`, 204, nil + case "repos/paradisisland/rose/git/refs/heads/gh-cli/codescanningworkflow", + "repos/paradisisland/marley/git/refs/heads/gh-cli/codescanningworkflow": + return `{}`, 422, &api.HTTPError{Message: "Reference does not exist", StatusCode: 422} + default: + return "", 0, fmt.Errorf("MockDeleteResponse: Unhandled path: %s", path) + } +} + +// MockPutResponse mocks the response for a PUT request to a specific path. +// It returns the response body, status code, and an error. +// The response body is a JSON string representing the content of the file at the specified path. +// The status code indicates the success or failure of the request. +// An error is returned if the path is not handled by the mock. +func MockPutResponse(path string) (string, int, error) { + switch path { + + case "repos/paradisisland/maria/contents/.github/workflows/codeql.yml": + return `{ + "content": { + "name": "codeql.yml", + "path": ".github/workflows/codeql.yml", + "sha": "95b966ae1c166bd92f8ae7d1c313e738c731dfc3", + "size": 9, + "url": "https://api.github.com/repos/paradiisland/maria/contents/.github/workflows/codeql.yml?ref=main", + "html_url": "https://github.com/paradisisland/maria/blob/main/.github/workflows/codeql.yml", + "git_url": "https://api.github.com/repos/octocat/Hello-World/git/blobs/95b966ae1c166bd92f8ae7d1c313e738c731dfc3", + "download_url": "https://raw.githubusercontent.com/octocat/HelloWorld/master/notes/hello.txt", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/octocat/Hello-World/contents/notes/hello.txt", + "git": "https://api.github.com/repos/octocat/Hello-World/git/blobs/95b966ae1c166bd92f8ae7d1c313e738c731dfc3", + "html": "https://github.com/octocat/Hello-World/blob/master/notes/hello.txt" + } + }, + "commit": { + "sha": "7638417db6d59f3c431d3e1f261cc637155684cd", + "node_id": "MDY6Q29tbWl0NzYzODQxN2RiNmQ1OWYzYzQzMWQzZTFmMjYxY2M2MzcxNTU2ODRjZA==", + "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/7638417db6d59f3c431d3e1f261cc637155684cd", + "html_url": "https://github.com/octocat/Hello-World/git/commit/7638417db6d59f3c431d3e1f261cc637155684cd", + "author": { + "date": "2014-11-07T22:01:45Z", + "name": "Monalisa Octocat", + "email": "octocat@github.com" + }, + "committer": { + "date": "2014-11-07T22:01:45Z", + "name": "Monalisa Octocat", + "email": "octocat@github.com" + }, + "message": "my commit message", + "tree": { + "url": "https://api.github.com/repos/octocat/Hello-World/git/trees/691272480426f78a0138979dd3ce63b77f706feb", + "sha": "691272480426f78a0138979dd3ce63b77f706feb" + }, + "parents": [ + { + "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/1acc419d4d6a9ce985db7be48c6349a0475975b5", + "html_url": "https://github.com/octocat/Hello-World/git/commit/1acc419d4d6a9ce985db7be48c6349a0475975b5", + "sha": "1acc419d4d6a9ce985db7be48c6349a0475975b5" + } + ], + "verification": { + "verified": false, + "reason": "unsigned", + "signature": null, + "payload": null + } + } + }`, 200, nil + + case "repos/paradisisland/rose/contents/.github/workflows/codeql.yml": + return `{}`, 422, &api.HTTPError{Message: "Reference already exists", StatusCode: 422} + + case "repos/paradisisland/marley/contents/.github/workflows/codeql.yml": + return `{}`, 404, &api.HTTPError{Message: "Not Found", StatusCode: 404} + + case "repos/paradisisland/shiganshima/contents/.github/workflows/codeql.yml": + return `{ + "content": { + "name": "codeql.yml", + "path": ".github/workflows/codeql.yml", + "sha": "95b966ae1c166bd92f8ae7d1c313e738c731dfc3", + "size": 9, + "url": "https://api.github.com/repos/paradiisland/shiganshima/contents/.github/workflows/codeql.yml?ref=main", + "html_url": "https://github.com/paradisisland/shiganshima/blob/main/.github/workflows/codeql.yml", + "git_url": "https://api.github.com/repos/octocat/Hello-World/git/blobs/95b966ae1c166bd92f8ae7d1c313e738c731dfc3", + "download_url": "https://raw.githubusercontent.com/octocat/HelloWorld/master/notes/hello.txt", + "type": "file", + "_links": { + "self": "https://api.github.com/repos/octocat/Hello-World/contents/notes/hello.txt", + "git": "https://api.github.com/repos/octocat/Hello-World/git/blobs/95b966ae1c166bd92f8ae7d1c313e738c731dfc3", + "html": "https://github.com/octocat/Hello-World/blob/master/notes/hello.txt" + } + }, + "commit": { + "sha": "7638417db6d59f3c431d3e1f261cc637155684cd", + "node_id": "MDY6Q29tbWl0NzYzODQxN2RiNmQ1OWYzYzQzMWQzZTFmMjYxY2M2MzcxNTU2ODRjZA==", + "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/7638417db6d59f3c431d3e1f261cc637155684cd", + "html_url": "https://github.com/octocat/Hello-World/git/commit/7638417db6d59f3c431d3e1f261cc637155684cd", + "author": { + "date": "2014-11-07T22:01:45Z", + "name": "Monalisa Octocat", + "email": "octocat@github.com" + }, + "committer": { + "date": "2014-11-07T22:01:45Z", + "name": "Monalisa Octocat", + "email": "octocat@github.com" + }, + "message": "my commit message", + "tree": { + "url": "https://api.github.com/repos/octocat/Hello-World/git/trees/691272480426f78a0138979dd3ce63b77f706feb", + "sha": "691272480426f78a0138979dd3ce63b77f706feb" + }, + "parents": [ + { + "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/1acc419d4d6a9ce985db7be48c6349a0475975b5", + "html_url": "https://github.com/octocat/Hello-World/git/commit/1acc419d4d6a9ce985db7be48c6349a0475975b5", + "sha": "1acc419d4d6a9ce985db7be48c6349a0475975b5" + } + ], + "verification": { + "verified": false, + "reason": "unsigned", + "signature": null, + "payload": null + } + } + + }`, 200, nil + default: + return "", 0, fmt.Errorf("MockPutResponse: Unhandled path: %s", path) + } + +} + +// MockPostResponse simulates a POST HTTP response for a given path. +// It returns a JSON string, a status code, and an error if the operation fails. +func MockPostResponse(path string) (string, int, error) { + switch path { + case "repos/paradisisland/maria/git/refs", "repos/paradisisland/shiganshima/git/refs": + return `{ + "ref": "refs/heads/gh-cli/codescanningworkflow", + "node_id": "MDM6UmVmcmVmcy9oZWFkcy9mZWF0dXJlQQ==", + "url": "https://api.github.com/repos/paradisisland/maria/git/refs/heads/featureA", + "object": { + "type": "commit", + "sha": "aa218f56b14c9653891f9e74264a383fa43fefbd", + "url": "https://api.github.com/repos/paradisisland/maria/commits/aa218f56b14c9653891f9e74264a383fa43fefbd" + } + }`, 200, nil + case "repos/paradisisland/rose/git/refs": + return `{}`, 422, &api.HTTPError{Message: "Reference already exists", StatusCode: 422} + case "repos/paradisisland/maria/pulls": + return `{ + "url": "https://api.github.com/repos/paradisisland/maria/pulls/1347", + "id": 1, + "node_id": "MDExOlB1bGxSZXF1ZXN0MQ==", + "html_url": "https://github.com/paradisisland/maria/pull/" + }`, 201, nil + case "repos/paradisisland/rose/pulls": + return `{}`, 422, &api.HTTPError{Message: "Validation Failed", StatusCode: 422} + case "repos/paradisisland/marley/pulls": + return `{}`, 404, &api.HTTPError{Message: "Not Found", StatusCode: 404} + default: + return "", 0, fmt.Errorf("MockPostResponse: Unexpected path: %s", path) + } +} + +// MockPatchResponse simulates a PATCH HTTP response for a given path. +// It returns a JSON string, a status code, and an error if the operation fails. +func MockPatchResponse(path string) (string, int, error) { + switch path { + case "repos/paradisisland/shiganshima/code-scanning/default-setup", + "repos/paradisisland/maria/code-scanning/default-setup": + return `{}`, 200, nil + case "repos/paradisisland/rose/code-scanning/default-setup": + return `{}`, 403, &api.HTTPError{Message: "GHAS Not Enabled", StatusCode: 403} + case "repos/paradisisland/marley/code-scanning/default-setup": + return `{}`, 404, &api.HTTPError{Message: "Not Found", StatusCode: 404} + default: + return "", 0, fmt.Errorf("MockPatchResponse: Unexpected path: %s", path) + } +} + +// MockOrgGetResponses simulates a GET HTTP response for a given path. +// It returns a JSON string, a status code, and an error if the operation fails. +func MockOrgGetResponses(path string) (string, int, error) { + switch path { + case "orgs/paradisisland/repos": + return `[ + { + "full_name": "paradisisland/maria", + "name": "maria", + "default_branch": "main" + }, + { + "full_name": "paradisisland/rose", + "name": "rose", + "default_branch": "main" + }, + { + "full_name": "paradisisland/sheena", + "name": "sheena", + "default_branch": "main" + }, + { + "full_name": "paradisisland/titanforest", + "name": "titanforest", + "default_branch": "main" + }, + { + "full_name": "paradisisland/shiganshima", + "name": "shiganshima", + "default_branch": "main" + } + ]`, 200, nil + case "orgs/sandora-desert/repos", "orgs/ansible/repos": + return `[]`, 200, nil + case "orgs/atotallyrealorgname/repos": + return `[]`, 404, &api.HTTPError{Message: "Not Found", StatusCode: 404} + default: + return "", 0, fmt.Errorf("MockOrgGetResponses: Unexpected path: %s", path) + } +} + +// MockRepoGetResponses simulates a GET HTTP response for a given path. +// It returns a JSON string, a status code, and an error if the operation fails. +func MockRepoGetResponses(path string) (string, int, error) { + switch path { + case "repos/paradisisland/maria": + return `{"full_name":"paradisisland/maria","name":"maria","default_branch":"main"}`, 200, nil + case "repos/paradisisland/marley": + return `[]`, 404, &api.HTTPError{Message: "Not Found", StatusCode: 404} + case "repos/paradisisland/maria/languages": + return `{ + "Go": 100, + "Java": 200, + "JavaScript": 300, + "Python": 400 + }`, 200, nil + case "repos/paradisisland/titanforest/languages": + return `{}`, 200, nil + case "repos/paradisisland/marley/languages": + return `[]`, 404, &api.HTTPError{Message: "Not Found", StatusCode: 404} + case "repos/paradisisland/sheena/code-scanning/default-setup": + return `{ + "state": "configured", + "languages": [ + "ruby", + "python" + ], + "query_suite": "default", + "updated_at": "2023-01-19T11:21:34Z", + "schedule": "weekly" + }`, 200, nil + case "repos/paradisisland/maria/code-scanning/default-setup": + return `{"state": "not-configured"}`, 200, nil + case "repos/paradisisland/rose/code-scanning/default-setup": + return `{}`, 403, &api.HTTPError{Message: "GHAS Not Enabled", StatusCode: 403} + case "repos/paradisisland/marley/code-scanning/default-setup": + return `[]`, 404, &api.HTTPError{Message: "Not Found", StatusCode: 404} + //Removed most of the following response for brevity, but full response is here: https://docs.github.com/en/rest/branches/branches?apiVersion=2022-11-28#get-a-branch + case "repos/paradisisland/maria/branches/main", "repos/paradisisland/rose/branches/main", "repos/paradisisland/shiganshima/branches/main": + return `{ + "name": "main", + "commit": { + "sha": "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", + "node_id": "MDY6Q29tbWl0MTI5NjI2OTo3ZmQxYTYwYjAxZjkxYjMxNGY1OTk1NWE0ZTRkNGU4MGQ4ZWRmMTFk", + "commit": { + "author": { + "name": "The Octocat", + "email": "octocat@nowhere.com", + "date": "2012-03-06T23:06:50Z" + }, + "committer": { + "name": "The Octocat", + "email": "octocat@nowhere.com", + "date": "2012-03-06T23:06:50Z" + }, + "message": "Merge pull request #6 from Spaceghost/patch-1\n\nNew line at end of file.", + "tree": { + "sha": "b4eecafa9be2f2006ce1b709d6857b07069b4608", + "url": "https://api.github.com/repos/octocat/Hello-World/git/trees/b4eecafa9be2f2006ce1b709d6857b07069b4608" + } + } + } + }`, 200, nil + case "repos/paradisisland/marley/branches/main": + return `{}`, 500, &api.HTTPError{Message: "Internal Server Error", StatusCode: 500} + case "repos/paradisisland/maria/contents/.github/workflows/codeql.yml": + return `{}`, 404, &api.HTTPError{Message: "Not Found", StatusCode: 404} + case "repos/paradisisland/sheena/contents/.github/workflows/codeql.yml": + return `{ + "type": "file", + "encoding": "base64", + "size": 5362, + "name": "codeql.yml", + "path": "codeql.yml", + "content": "IyBZb2dhIEJvmsgaW4gcHJvZ3Jlc3MhIEZlZWwgdAoKOndhcm5pbmc6IFdvc\\nZnJlZSBmUgdG8gY0byBjaGVjayBvdXQgdGhlIGFwcCwgYnV0IGJlIHN1c29t\\nZSBiYWNrIG9uY2UgaXQgaXMgY29tcGxldGUuCgpBIHdlYiBhcHAgdGhhdCBs\\nZWFkcyB5b3UgdGhyb3VnaCBhIHlvZ2Egc2Vzc2lvbi4KCltXb3Jrb3V0IG5v\\ndyFdKGh0dHBzOi8vc2tlZHdhcmRzODguZ2l0aHViLmlvL3lvZ2EvKQoKPGlt\\nZyBzcmM9InNyYy9pbWFnZXMvbWFza2FibGVfaWNvbl81MTIucG5nIiBhbHQ9\\nImJvdCBsaWZ0aW5nIHdlaWdodHMiIHdpZHRoPSIxMDAiLz4KCkRvIHlvdSBo\\nYXZlIGZlZWRiYWNrIG9yIGlkZWFzIGZvciBpbXByb3ZlbWVudD8gW09wZW4g\\nYW4gaXNzdWVdKGh0dHBzOi8vZ2l0aHViLmNvbS9za2Vkd2FyZHM4OC95b2dh\\nL2lzc3Vlcy9uZXcpLgoKV2FudCBtb3JlIGdhbWVzPyBWaXNpdCBbQ25TIEdh\\nbWVzXShodHRwczovL3NrZWR3YXJkczg4LmdpdGh1Yi5pby9wb3J0Zm9saW8v\\nKS4KCiMjIERldmVsb3BtZW50CgpUbyBhZGQgYSBuZXcgcG9zZSwgYWRkIGFu\\nIGVudHJ5IHRvIHRoZSByZWxldmFudCBmaWxlIGluIGBzcmMvYXNhbmFzYC4K\\nClRvIGJ1aWxkLCBydW4gYG5wbSBydW4gYnVpbGRgLgoKVG8gcnVuIGxvY2Fs\\nbHkgd2l0aCBsaXZlIHJlbG9hZGluZyBhbmQgbm8gc2VydmljZSB3b3JrZXIs\\nIHJ1biBgbnBtIHJ1biBkZXZgLiAoSWYgYSBzZXJ2aWNlIHdvcmtlciB3YXMg\\ncHJldmlvdXNseSByZWdpc3RlcmVkLCB5b3UgY2FuIHVucmVnaXN0ZXIgaXQg\\naW4gY2hyb21lIGRldmVsb3BlciB0b29sczogYEFwcGxpY2F0aW9uYCA+IGBT\\nZXJ2aWNlIHdvcmtlcnNgID4gYFVucmVnaXN0ZXJgLikKClRvIHJ1biBsb2Nh\\nbGx5IGFuZCByZWdpc3RlciB0aGUgc2VydmljZSB3b3JrZXIsIHJ1biBgbnBt\\nIHN0YXJ0YC4KClRvIGRlcGxveSwgcHVzaCB0byBgbWFpbmAgb3IgbWFudWFs\\nbHkgdHJpZ2dlciB0aGUgYC5naXRodWIvd29ya2Zsb3dzL2RlcGxveS55bWxg\\nIHdvcmtmbG93Lgo=\\n", + "sha": "8d1c8b69c3fce7bea45c73efd06983e3c419a92f", + "url": "https://api.github.com/repos/paradisisland/sheena/contents/codeql.yml", + "git_url": "https://api.github.com/repos/paradisisland/sheena/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1", + "html_url": "https://github.com/paradisisland/sheena/blob/master/codeql.yml", + "download_url": "https://raw.githubusercontent.com/paradisisland/sheena/main/codeql.yml", + "_links": { + "git": "https://api.github.com/repos/paradisisland/sheena/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1", + "self": "https://api.github.com/repos/paradisisland/sheena/contents/codeql.yml", + "html": "https://github.com/paradisisland/sheena/blob/main/codeql.yml" + } + }`, 200, nil + case "repos/paradisisland/shiganshima/contents/.github/workflows/codeql.yml": + return `{ + "type": "file", + "encoding": "base64", + "size": 5362, + "name": "codeql.yml", + "path": "codeql.yml", + "content": "IyBZb2dhIEJvmsgaW4gcHJvZ3Jlc3MhIEZlZWwgdAoKOndhcm5pbmc6IFdvc\\nZnJlZSBmUgdG8gY0byBjaGVjayBvdXQgdGhlIGFwcCwgYnV0IGJlIHN1c29t\\nZSBiYWNrIG9uY2UgaXQgaXMgY29tcGxldGUuCgpBIHdlYiBhcHAgdGhhdCBs\\nZWFkcyB5b3UgdGhyb3VnaCBhIHlvZ2Egc2Vzc2lvbi4KCltXb3Jrb3V0IG5v\\ndyFdKGh0dHBzOi8vc2tlZHdhcmRzODguZ2l0aHViLmlvL3lvZ2EvKQoKPGlt\\nZyBzcmM9InNyYy9pbWFnZXMvbWFza2FibGVfaWNvbl81MTIucG5nIiBhbHQ9\\nImJvdCBsaWZ0aW5nIHdlaWdodHMiIHdpZHRoPSIxMDAiLz4KCkRvIHlvdSBo\\nYXZlIGZlZWRiYWNrIG9yIGlkZWFzIGZvciBpbXByb3ZlbWVudD8gW09wZW4g\\nYW4gaXNzdWVdKGh0dHBzOi8vZ2l0aHViLmNvbS9za2Vkd2FyZHM4OC95b2dh\\nL2lzc3Vlcy9uZXcpLgoKV2FudCBtb3JlIGdhbWVzPyBWaXNpdCBbQ25TIEdh\\nbWVzXShodHRwczovL3NrZWR3YXJkczg4LmdpdGh1Yi5pby9wb3J0Zm9saW8v\\nKS4KCiMjIERldmVsb3BtZW50CgpUbyBhZGQgYSBuZXcgcG9zZSwgYWRkIGFu\\nIGVudHJ5IHRvIHRoZSByZWxldmFudCBmaWxlIGluIGBzcmMvYXNhbmFzYC4K\\nClRvIGJ1aWxkLCBydW4gYG5wbSBydW4gYnVpbGRgLgoKVG8gcnVuIGxvY2Fs\\nbHkgd2l0aCBsaXZlIHJlbG9hZGluZyBhbmQgbm8gc2VydmljZSB3b3JrZXIs\\nIHJ1biBgbnBtIHJ1biBkZXZgLiAoSWYgYSBzZXJ2aWNlIHdvcmtlciB3YXMg\\ncHJldmlvdXNseSByZWdpc3RlcmVkLCB5b3UgY2FuIHVucmVnaXN0ZXIgaXQg\\naW4gY2hyb21lIGRldmVsb3BlciB0b29sczogYEFwcGxpY2F0aW9uYCA+IGBT\\nZXJ2aWNlIHdvcmtlcnNgID4gYFVucmVnaXN0ZXJgLikKClRvIHJ1biBsb2Nh\\nbGx5IGFuZCByZWdpc3RlciB0aGUgc2VydmljZSB3b3JrZXIsIHJ1biBgbnBt\\nIHN0YXJ0YC4KClRvIGRlcGxveSwgcHVzaCB0byBgbWFpbmAgb3IgbWFudWFs\\nbHkgdHJpZ2dlciB0aGUgYC5naXRodWIvd29ya2Zsb3dzL2RlcGxveS55bWxg\\nIHdvcmtmbG93Lgo=\\n", + "sha": "0ae040b692ec3e927163db2b984135aa3c088cba", + "url": "https://api.github.com/repos/paradisisland/shiganshima/contents/codeql.yml", + "git_url": "https://api.github.com/repos/paradisisland/shiganshima/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1", + "html_url": "https://github.com/paradisisland/shiganshima/blob/master/codeql.yml", + "download_url": "https://raw.githubusercontent.com/paradisisland/shiganshima/main/codeql.yml", + "_links": { + "git": "https://api.github.com/repos/paradisisland/shiganshima/git/blobs/3d21ec53a331a6f037a91c368710b99387d012c1", + "self": "https://api.github.com/repos/paradisisland/shiganshima/contents/codeql.yml", + "html": "https://github.com/paradisisland/shiganshima/blob/main/codeql.yml" + } + }`, 200, nil + case "repos/paradisisland/marley/contents/.github/workflows/codeql.yml": + return `{}`, 404, nil + default: + return "", 0, fmt.Errorf("MockRepoGetResponses: Unexpected path: %s", path) + } +} + +func MockGetResponse(path string) (string, int, error) { + + //Orgs + if strings.HasPrefix(path, "orgs/") { + return MockOrgGetResponses(path) + } + + //Get Branches + if strings.HasPrefix(path, "repos/") { + return MockRepoGetResponses(path) + } + // Default return if no condition is met + return "", 0, fmt.Errorf("Invalid path: %s", path) + +} + +func MockNextPageHeader(response *http.Response, path string) { + if path == "orgs/ansible/repos" { + response.Header.Set("Link", "; rel=\"next\", ; rel=\"last\"") + } +} diff --git a/cmd/test_client_setup.go b/cmd/test_client_setup.go new file mode 100644 index 0000000..4916ae1 --- /dev/null +++ b/cmd/test_client_setup.go @@ -0,0 +1,60 @@ +package cmd + +import ( + "io" + "io/ioutil" + "log" + "net/http" + "strings" +) + +type Client interface { + Request(method string, path string, body io.Reader) (*http.Response, error) +} + +type TestClient struct { + http *http.Client +} + +// Request implements Client. +func (*TestClient) Request(method string, path string, body io.Reader) (*http.Response, error) { + var jsonResponse string + var statusCode int + var err error + + switch { + case method == "GET": + jsonResponse, statusCode, err = MockGetResponse(path) + case method == "PATCH": + jsonResponse, statusCode, err = MockPatchResponse(path) + case method == "POST": + jsonResponse, statusCode, err = MockPostResponse(path) + case method == "PUT": + jsonResponse, statusCode, err = MockPutResponse(path) + case method == "DELETE": + jsonResponse, statusCode, err = MockDeleteResponse(path) + } + + //add error handling + if statusCode == 0 { + log.Fatalln("Something went wrong with the mock response", err) + } + + // Create a new response + response := &http.Response{ + StatusCode: statusCode, + Body: ioutil.NopCloser(strings.NewReader(jsonResponse)), + Header: make(http.Header), + } + response.Header.Set("Content-Type", "application/json") + + //Mock Next Page Header + MockNextPageHeader(response, path) + + // Return the response + return response, err + + // If the method and path don't match, return an error + //return nil, fmt.Errorf("unsupported method or path") + +} diff --git a/go.mod b/go.mod index 9bf55e8..45b4f63 100644 --- a/go.mod +++ b/go.mod @@ -8,24 +8,27 @@ require ( github.com/thedevsaddam/gojsonq/v2 v2.5.2 ) +require ( + github.com/stretchr/testify v1.8.4 // indirect + gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect +) + require ( github.com/aymanbagabas/go-osc52 v1.0.3 // indirect - github.com/cli/go-gh v1.2.1 - github.com/cli/safeexec v1.0.0 // indirect - github.com/cli/shurcooL-graphql v0.0.3 // indirect - github.com/henvic/httpretty v0.0.6 // indirect + github.com/cli/safeexec v1.0.1 // indirect + github.com/cli/shurcooL-graphql v0.0.4 // indirect + github.com/henvic/httpretty v0.1.3 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/kr/text v0.2.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect github.com/muesli/termenv v0.13.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/sys v0.8.0 // indirect - golang.org/x/term v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/sys v0.13.0 // indirect + golang.org/x/term v0.13.0 // indirect + golang.org/x/text v0.13.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 9eb9341..53abaf1 100644 --- a/go.sum +++ b/go.sum @@ -1,20 +1,17 @@ -github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/aymanbagabas/go-osc52 v1.0.3 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZspWD+Mg= github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= -github.com/cli/go-gh v1.2.1 h1:xFrjejSsgPiwXFP6VYynKWwxLQcNJy3Twbu82ZDlR/o= -github.com/cli/go-gh v1.2.1/go.mod h1:Jxk8X+TCO4Ui/GarwY9tByWm/8zp4jJktzVZNlTW5VM= -github.com/cli/go-gh/v2 v2.3.0 h1:FAQAP4PaWSAJf4VSxFEIYDQ1oBIs+bKB4GXQAiRr2sQ= -github.com/cli/go-gh/v2 v2.3.0/go.mod h1:6WBUuf7LUVAc+eXYYX/nYTYURRc6M03K9cJNwBKvwT0= -github.com/cli/safeexec v1.0.0 h1:0VngyaIyqACHdcMNWfo6+KdUYnqEr2Sg+bSP1pdF+dI= -github.com/cli/safeexec v1.0.0/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= -github.com/cli/shurcooL-graphql v0.0.3 h1:CtpPxyGDs136/+ZeyAfUKYmcQBjDlq5aqnrDCW5Ghh8= -github.com/cli/shurcooL-graphql v0.0.3/go.mod h1:tlrLmw/n5Q/+4qSvosT+9/W5zc8ZMjnJeYBxSdb4nWA= +github.com/cli/go-gh/v2 v2.4.1-0.20231120145612-d32c104a9a25 h1:m2opPgNTaKx1QydI4NfGdZqiYkA/Kl9a7tsDSjHgWWg= +github.com/cli/go-gh/v2 v2.4.1-0.20231120145612-d32c104a9a25/go.mod h1:h3salfqqooVpzKmHp6aUdeNx62UmxQRpLbagFSHTJGQ= +github.com/cli/safeexec v1.0.1 h1:e/C79PbXF4yYTN/wauC4tviMxEV13BwljGj0N9j+N00= +github.com/cli/safeexec v1.0.1/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= +github.com/cli/shurcooL-graphql v0.0.4 h1:6MogPnQJLjKkaXPyGqPRXOI2qCsQdqNfUY1QSJu2GuY= +github.com/cli/shurcooL-graphql v0.0.4/go.mod h1:3waN4u02FiZivIV+p1y4d0Jo1jc6BViMA73C+sZo2fk= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= -github.com/henvic/httpretty v0.0.6 h1:JdzGzKZBajBfnvlMALXXMVQWxWMF/ofTy8C3/OSUTxs= -github.com/henvic/httpretty v0.0.6/go.mod h1:X38wLjWXHkXT7r2+uK8LjCMne9rsuNaBLJ+5cU2/Pmo= +github.com/henvic/httpretty v0.1.3 h1:4A6vigjz6Q/+yAfTD4wqipCv+Px69C7Th/NhT0ApuU8= +github.com/henvic/httpretty v0.1.3/go.mod h1:UUEv7c2kHZ5SPQ51uS3wBpzPDibg2U3Y+IaXyHy5GBg= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= @@ -23,11 +20,10 @@ github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0= github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -39,30 +35,25 @@ github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/thedevsaddam/gojsonq/v2 v2.5.2 h1:CoMVaYyKFsVj6TjU6APqAhAvC07hTI6IQen8PHzHYY0= github.com/thedevsaddam/gojsonq/v2 v2.5.2/go.mod h1:bv6Xa7kWy82uT0LnXPE2SzGqTj33TAEeR560MdJkiXs= github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e h1:BuzhfgfWQbX0dWzYzT1zsORLnHRv3bcRcsaUk0VmXA8= github.com/thlib/go-timezone-local v0.0.0-20210907160436-ef149e42d28e/go.mod h1:/Tnicc6m/lsJE0irFMA0LfIwTBo4QP7A8IfyIv4zZKI= -golang.org/x/net v0.0.0-20220923203811-8be639271d50/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/h2non/gock.v1 v1.1.2 h1:jBbHXgGBK/AoPVfJh5x4r/WxIrElvbLel8TCZkkZJoY= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 8c8f64c87f2f76b4c522f3fe814fdf28b50c9968 Mon Sep 17 00:00:00 2001 From: Shadi Samadi Date: Tue, 9 Jan 2024 11:52:46 +1100 Subject: [PATCH 06/22] cleaned up mock and .gitignore file --- .gitignore | 3 ++- cmd/http_mock.go | 46 +++++++++++++++++++++++----------------------- go.mod | 2 +- go.sum | 3 +++ 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index f3ef4b7..aeb99de 100644 --- a/.gitignore +++ b/.gitignore @@ -28,7 +28,6 @@ go.work .vscode/* !.vscode/settings.json !.vscode/tasks.json -!.vscode/launch.json !.vscode/extensions.json !.vscode/*.code-snippets @@ -43,6 +42,8 @@ go.work .history .ionide + + # Log Files .log # End of https://www.toptal.com/developers/gitignore/api/go,visualstudiocode diff --git a/cmd/http_mock.go b/cmd/http_mock.go index 284e87f..d3b6769 100644 --- a/cmd/http_mock.go +++ b/cmd/http_mock.go @@ -38,22 +38,22 @@ func MockPutResponse(path string) (string, int, error) { "path": ".github/workflows/codeql.yml", "sha": "95b966ae1c166bd92f8ae7d1c313e738c731dfc3", "size": 9, - "url": "https://api.github.com/repos/paradiisland/maria/contents/.github/workflows/codeql.yml?ref=main", + "url": "https://api.github.com/repos/paradisisland/maria/contents/.github/workflows/codeql.yml?ref=main", "html_url": "https://github.com/paradisisland/maria/blob/main/.github/workflows/codeql.yml", - "git_url": "https://api.github.com/repos/octocat/Hello-World/git/blobs/95b966ae1c166bd92f8ae7d1c313e738c731dfc3", - "download_url": "https://raw.githubusercontent.com/octocat/HelloWorld/master/notes/hello.txt", + "git_url": "https://api.github.com/repos/paradisisland/maria/git/blobs/95b966ae1c166bd92f8ae7d1c313e738c731dfc3", + "download_url": "https://raw.githubusercontent.com/paradisisland/maria/main/.github/workflows/codeql.yml", "type": "file", "_links": { - "self": "https://api.github.com/repos/octocat/Hello-World/contents/notes/hello.txt", - "git": "https://api.github.com/repos/octocat/Hello-World/git/blobs/95b966ae1c166bd92f8ae7d1c313e738c731dfc3", - "html": "https://github.com/octocat/Hello-World/blob/master/notes/hello.txt" + "self": "https://api.github.com/repos/paradisisland/maria/main/.github/workflows/codeql.yml", + "git": "https://api.github.com/repos/paradisisland/maria/git/blobs/95b966ae1c166bd92f8ae7d1c313e738c731dfc3", + "html": "https://github.com/paradisisland/maria/blob/main/.github/workflows/codeql.yml" } }, "commit": { "sha": "7638417db6d59f3c431d3e1f261cc637155684cd", "node_id": "MDY6Q29tbWl0NzYzODQxN2RiNmQ1OWYzYzQzMWQzZTFmMjYxY2M2MzcxNTU2ODRjZA==", - "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/7638417db6d59f3c431d3e1f261cc637155684cd", - "html_url": "https://github.com/octocat/Hello-World/git/commit/7638417db6d59f3c431d3e1f261cc637155684cd", + "url": "https://api.github.com/repos/paradisisland/maria/git/commits/7638417db6d59f3c431d3e1f261cc637155684cd", + "html_url": "https://github.com/paradisisland/maria/git/commit/7638417db6d59f3c431d3e1f261cc637155684cd", "author": { "date": "2014-11-07T22:01:45Z", "name": "Monalisa Octocat", @@ -66,13 +66,13 @@ func MockPutResponse(path string) (string, int, error) { }, "message": "my commit message", "tree": { - "url": "https://api.github.com/repos/octocat/Hello-World/git/trees/691272480426f78a0138979dd3ce63b77f706feb", + "url": "https://api.github.com/repos/paradisisland/maria/git/trees/691272480426f78a0138979dd3ce63b77f706feb", "sha": "691272480426f78a0138979dd3ce63b77f706feb" }, "parents": [ { - "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/1acc419d4d6a9ce985db7be48c6349a0475975b5", - "html_url": "https://github.com/octocat/Hello-World/git/commit/1acc419d4d6a9ce985db7be48c6349a0475975b5", + "url": "https://api.github.com/repos/paradisisland/maria/git/commits/1acc419d4d6a9ce985db7be48c6349a0475975b5", + "html_url": "https://github.com/paradisisland/maria/git/commit/1acc419d4d6a9ce985db7be48c6349a0475975b5", "sha": "1acc419d4d6a9ce985db7be48c6349a0475975b5" } ], @@ -98,22 +98,22 @@ func MockPutResponse(path string) (string, int, error) { "path": ".github/workflows/codeql.yml", "sha": "95b966ae1c166bd92f8ae7d1c313e738c731dfc3", "size": 9, - "url": "https://api.github.com/repos/paradiisland/shiganshima/contents/.github/workflows/codeql.yml?ref=main", + "url": "https://api.github.com/repos/paradisisland/shiganshima/contents/.github/workflows/codeql.yml?ref=main", "html_url": "https://github.com/paradisisland/shiganshima/blob/main/.github/workflows/codeql.yml", - "git_url": "https://api.github.com/repos/octocat/Hello-World/git/blobs/95b966ae1c166bd92f8ae7d1c313e738c731dfc3", - "download_url": "https://raw.githubusercontent.com/octocat/HelloWorld/master/notes/hello.txt", + "git_url": "https://api.github.com/repos/paradisisland/shiganshima/git/blobs/95b966ae1c166bd92f8ae7d1c313e738c731dfc3", + "download_url": "https://raw.githubusercontent.com/shiganshima/contents/.github/workflows/codeql.yml", "type": "file", "_links": { - "self": "https://api.github.com/repos/octocat/Hello-World/contents/notes/hello.txt", - "git": "https://api.github.com/repos/octocat/Hello-World/git/blobs/95b966ae1c166bd92f8ae7d1c313e738c731dfc3", - "html": "https://github.com/octocat/Hello-World/blob/master/notes/hello.txt" + "self": "https://api.github.com/repos/shiganshima/contents/.github/workflows/codeql.yml", + "git": "https://api.github.com/repos/shiganshima/git/blobs/95b966ae1c166bd92f8ae7d1c313e738c731dfc3", + "html": "https://github.com/shiganshima/contents/.github/workflows/codeql.yml" } }, "commit": { "sha": "7638417db6d59f3c431d3e1f261cc637155684cd", "node_id": "MDY6Q29tbWl0NzYzODQxN2RiNmQ1OWYzYzQzMWQzZTFmMjYxY2M2MzcxNTU2ODRjZA==", - "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/7638417db6d59f3c431d3e1f261cc637155684cd", - "html_url": "https://github.com/octocat/Hello-World/git/commit/7638417db6d59f3c431d3e1f261cc637155684cd", + "url": "https://api.github.com/repos/paradisisland/shiganshima/git/commits/7638417db6d59f3c431d3e1f261cc637155684cd", + "html_url": "https://github.com/paradisisland/shiganshima/git/commit/7638417db6d59f3c431d3e1f261cc637155684cd", "author": { "date": "2014-11-07T22:01:45Z", "name": "Monalisa Octocat", @@ -126,13 +126,13 @@ func MockPutResponse(path string) (string, int, error) { }, "message": "my commit message", "tree": { - "url": "https://api.github.com/repos/octocat/Hello-World/git/trees/691272480426f78a0138979dd3ce63b77f706feb", + "url": "https://api.github.com/repos/paradisisland/shiganshima/git/trees/691272480426f78a0138979dd3ce63b77f706feb", "sha": "691272480426f78a0138979dd3ce63b77f706feb" }, "parents": [ { - "url": "https://api.github.com/repos/octocat/Hello-World/git/commits/1acc419d4d6a9ce985db7be48c6349a0475975b5", - "html_url": "https://github.com/octocat/Hello-World/git/commit/1acc419d4d6a9ce985db7be48c6349a0475975b5", + "url": "https://api.github.com/repos/paradisisland/shiganshima/git/commits/1acc419d4d6a9ce985db7be48c6349a0475975b5", + "html_url": "https://github.com/paradisisland/shiganshima/git/commit/1acc419d4d6a9ce985db7be48c6349a0475975b5", "sha": "1acc419d4d6a9ce985db7be48c6349a0475975b5" } ], @@ -298,7 +298,7 @@ func MockRepoGetResponses(path string) (string, int, error) { "message": "Merge pull request #6 from Spaceghost/patch-1\n\nNew line at end of file.", "tree": { "sha": "b4eecafa9be2f2006ce1b709d6857b07069b4608", - "url": "https://api.github.com/repos/octocat/Hello-World/git/trees/b4eecafa9be2f2006ce1b709d6857b07069b4608" + "url": "https://api.github.com/repos/paradisisland/Hello-World/git/trees/b4eecafa9be2f2006ce1b709d6857b07069b4608" } } } diff --git a/go.mod b/go.mod index 45b4f63..0a717ef 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/s-samadi/gh-add-files go 1.20 require ( - github.com/cli/go-gh/v2 v2.3.0 + github.com/cli/go-gh/v2 v2.4.0 github.com/spf13/cobra v1.7.0 github.com/thedevsaddam/gojsonq/v2 v2.5.2 ) diff --git a/go.sum b/go.sum index 53abaf1..e83aaf3 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/aymanbagabas/go-osc52 v1.0.3 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZspWD+Mg= github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= +github.com/cli/go-gh/v2 v2.4.0 h1:6j3YxA8uJVOL4lBWjqDmMiAQNnJ2fiZagCuEmQXl+pU= +github.com/cli/go-gh/v2 v2.4.0/go.mod h1:h3salfqqooVpzKmHp6aUdeNx62UmxQRpLbagFSHTJGQ= github.com/cli/go-gh/v2 v2.4.1-0.20231120145612-d32c104a9a25 h1:m2opPgNTaKx1QydI4NfGdZqiYkA/Kl9a7tsDSjHgWWg= github.com/cli/go-gh/v2 v2.4.1-0.20231120145612-d32c104a9a25/go.mod h1:h3salfqqooVpzKmHp6aUdeNx62UmxQRpLbagFSHTJGQ= github.com/cli/safeexec v1.0.1 h1:e/C79PbXF4yYTN/wauC4tviMxEV13BwljGj0N9j+N00= @@ -27,6 +29,7 @@ github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0= github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= From 03715673c63bda56e1f5ad34ad7991446c21c744 Mon Sep 17 00:00:00 2001 From: Shadi Samadi Date: Tue, 9 Jan 2024 15:43:43 +1100 Subject: [PATCH 07/22] adding linting and made some minor tweaks to the documentation --- .github/workflows/golangci-lint.yml | 56 +++++++++++++++++++++++++++++ CONTRIBUTING.md | 1 - README.md | 8 +++++ cmd/codescanning.go | 14 ++++---- cmd/deletebranch.go | 2 -- cmd/test_client_setup.go | 3 +- go.sum | 3 -- 7 files changed, 72 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/golangci-lint.yml diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml new file mode 100644 index 0000000..9652b3f --- /dev/null +++ b/.github/workflows/golangci-lint.yml @@ -0,0 +1,56 @@ +name: golangci-lint +on: + push: + branches: + - main + pull_request: + branches: + - main + +permissions: + contents: read + # Optional: allow read access to pull request. Use with `only-new-issues` option. + # pull-requests: read + +jobs: + golangci: + name: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-go@v4 + with: + go-version: "1.20.x" + cache: false + - name: golangci-lint + uses: golangci/golangci-lint-action@v3 + with: + # Require: The version of golangci-lint to use. + # When `install-mode` is `binary` (default) the value can be v1.2 or v1.2.3 or `latest` to use the latest version. + # When `install-mode` is `goinstall` the value can be v1.2.3, `latest`, or the hash of a commit. + version: latest + + # Optional: working directory, useful for monorepos + # working-directory: somedir + + # Optional: golangci-lint command line arguments. + # + # Note: By default, the `.golangci.yml` file should be at the root of the repository. + # The location of the configuration file can be changed by using `--config=` + # args: --timeout=30m --config=/my/path/.golangci.yml --issues-exit-code=0 + + # Optional: show only new issues if it's a pull request. The default value is `false`. + # only-new-issues: true + + # Optional: if set to true, then all caching functionality will be completely disabled, + # takes precedence over all other caching options. + # skip-cache: true + + # Optional: if set to true, then the action won't cache or restore ~/go/pkg. + # skip-pkg-cache: true + + # Optional: if set to true, then the action won't cache or restore ~/.cache/go-build. + # skip-build-cache: true + + # Optional: The mode to install golangci-lint. It can be 'binary' or 'goinstall'. + # install-mode: "goinstall" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4a1b9b4..4641644 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -31,7 +31,6 @@ These are one time installations required to be able to test your changes locall Here are a few things you can do that will increase the likelihood of your pull request being accepted: -- Follow the [style guide][style]. - Write tests. - Keep your change as focused as possible. If there are multiple changes you would like to make that are not dependent upon each other, consider submitting them as separate pull requests. - Write a [good commit message](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). diff --git a/README.md b/README.md index 4de6c44..86bb3a1 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,14 @@ The following flags are mandatory: - `-l` - specify the path where the log file will be saved +## License + +This project is licensed under the terms of the MIT open source license. Please refer to [MIT](./LICENSE.txt) for the full terms. + +## Maintainers + +See the [CODEOWNERS](./.github/CODEOWNERS) for the list of maintainers. + diff --git a/cmd/codescanning.go b/cmd/codescanning.go index bb7fdb8..61468bd 100644 --- a/cmd/codescanning.go +++ b/cmd/codescanning.go @@ -158,11 +158,11 @@ var codeScanningCmd = &cobra.Command{ Errors[repo.FullName] = err continue } - if isDefaultSetupEnabled == true && Force == false { + if isDefaultSetupEnabled && !Force { log.Printf("Default setup already enabled for this repository: %s, skipping enablement.", repo.FullName) defaultScan = append(defaultScan, repo.FullName) continue - } else if isDefaultSetupEnabled == true && Force == true { + } else if isDefaultSetupEnabled && Force { log.Printf("Default setup already enabled for this repository: %s, but force flag is set, converting repo to advanced setup", repo.FullName) result, err := repo.disableDefaultSetup(client) @@ -171,7 +171,7 @@ var codeScanningCmd = &cobra.Command{ continue } - if result == true { + if result { log.Printf("Default setup disabled for repository: %s", repo.FullName) } } @@ -183,18 +183,18 @@ var codeScanningCmd = &cobra.Command{ Errors[repo.FullName] = err continue } - if isCodeQLEnabled == true && Force == false { + if isCodeQLEnabled && !Force { log.Printf("CodeQL workflow file already exists for this repository: %s, skipping enablement.", repo.FullName) advancedSetup = append(advancedSetup, repo.FullName) continue - } else if isCodeQLEnabled == true && Force == true { + } else if isCodeQLEnabled && Force { log.Printf("CodeQL workflow file already exists for this repository: %s, but force flag is set, updating workflow file", repo.FullName) } newbranchref, err := repo.createBranchForRepo(client) if err != nil { // log.Println(err) - if strings.Contains(err.Error(), "already exists") && Force == true { + if strings.Contains(err.Error(), "already exists") && Force { log.Printf("Force flag is set, removing existing branch for repository: %s\n", repo.FullName) err := repo.deleteBranch(client) if err != nil { @@ -301,6 +301,6 @@ var codeScanningCmd = &cobra.Command{ } log.Printf("Finished enable code scanning! \n") - return + }, } diff --git a/cmd/deletebranch.go b/cmd/deletebranch.go index 91ca29e..ec341cd 100644 --- a/cmd/deletebranch.go +++ b/cmd/deletebranch.go @@ -59,7 +59,5 @@ var deleteBranchCmd = &cobra.Command{ log.Printf("Successfully deleted branch %s from repository %s\n", Branch, repo.FullName) } - return - }, } diff --git a/cmd/test_client_setup.go b/cmd/test_client_setup.go index 4916ae1..b32ae60 100644 --- a/cmd/test_client_setup.go +++ b/cmd/test_client_setup.go @@ -2,7 +2,6 @@ package cmd import ( "io" - "io/ioutil" "log" "net/http" "strings" @@ -43,7 +42,7 @@ func (*TestClient) Request(method string, path string, body io.Reader) (*http.Re // Create a new response response := &http.Response{ StatusCode: statusCode, - Body: ioutil.NopCloser(strings.NewReader(jsonResponse)), + Body: io.NopCloser(strings.NewReader(jsonResponse)), Header: make(http.Header), } response.Header.Set("Content-Type", "application/json") diff --git a/go.sum b/go.sum index e83aaf3..1fb7ef3 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,6 @@ github.com/aymanbagabas/go-osc52 v1.0.3 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZs github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= github.com/cli/go-gh/v2 v2.4.0 h1:6j3YxA8uJVOL4lBWjqDmMiAQNnJ2fiZagCuEmQXl+pU= github.com/cli/go-gh/v2 v2.4.0/go.mod h1:h3salfqqooVpzKmHp6aUdeNx62UmxQRpLbagFSHTJGQ= -github.com/cli/go-gh/v2 v2.4.1-0.20231120145612-d32c104a9a25 h1:m2opPgNTaKx1QydI4NfGdZqiYkA/Kl9a7tsDSjHgWWg= -github.com/cli/go-gh/v2 v2.4.1-0.20231120145612-d32c104a9a25/go.mod h1:h3salfqqooVpzKmHp6aUdeNx62UmxQRpLbagFSHTJGQ= github.com/cli/safeexec v1.0.1 h1:e/C79PbXF4yYTN/wauC4tviMxEV13BwljGj0N9j+N00= github.com/cli/safeexec v1.0.1/go.mod h1:Z/D4tTN8Vs5gXYHDCbaM1S/anmEDnJb1iW0+EJ5zx3Q= github.com/cli/shurcooL-graphql v0.0.4 h1:6MogPnQJLjKkaXPyGqPRXOI2qCsQdqNfUY1QSJu2GuY= @@ -29,7 +27,6 @@ github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0= github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= From be3b4c284636db86baca44ff44178f51eceec54d Mon Sep 17 00:00:00 2001 From: Shadi Samadi Date: Tue, 9 Jan 2024 15:49:35 +1100 Subject: [PATCH 08/22] Create SUPPORT.md --- SUPPORT.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 SUPPORT.md diff --git a/SUPPORT.md b/SUPPORT.md new file mode 100644 index 0000000..608b35f --- /dev/null +++ b/SUPPORT.md @@ -0,0 +1,13 @@ +# Support + +## How to file issues and get help + +This project uses GitHub issues to track bugs and feature requests. Please search the existing issues before filing new issues to avoid duplicates. For new issues, file your bug or feature request as a new issue. + +For help or questions about using this project, please raise an issue and add the question label. + +`gh-add-files` is under active development and maintained by GitHub Advanced Security Expert Services staff. We will do our best to respond to support, feature requests, and community questions in a timely manner. + +## GitHub Support Policy + +Support for this project is limited to the resources listed above. From 75995ceda2df87ebabfafaa57e49af9be9f306c7 Mon Sep 17 00:00:00 2001 From: Shadi Samadi Date: Tue, 9 Jan 2024 15:52:29 +1100 Subject: [PATCH 09/22] Update README.md --- README.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 86bb3a1..6fd1560 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This tool currently streamlies the process of enabling advanced setup for Code S ### Prerequisites -1. Install gh-cli. For further instructions please see [here]https://github.com/cli/cli#installation +1. Install gh-cli. For further instructions please see [here](https://github.com/cli/cli#installation ) 2. This extension modifies files in the `.github/workflows` directory. Therefore you must authenticate with the `workflow` and `project` scope. You will also need write access to all required repositories. For example run command `gh auth login -s "workflow project". Alternatively, you can authenticate with a PAT that has the required scope. @@ -104,6 +104,9 @@ This project is licensed under the terms of the MIT open source license. Please See the [CODEOWNERS](./.github/CODEOWNERS) for the list of maintainers. +## Support + +The support process is outlined in the [SUPPORT.md](./SUPPORT.md) From fb9c1d12f19036130647b3d43935345932938329 Mon Sep 17 00:00:00 2001 From: Shadi Samadi Date: Tue, 9 Jan 2024 15:55:36 +1100 Subject: [PATCH 10/22] Added code_of_conduct and security documentation --- CODE_OF_CONDUCT.md | 74 ++++++++++++++++++++++++++++++++++++++++++++++ SECURITY.md | 31 +++++++++++++++++++ 2 files changed, 105 insertions(+) create mode 100644 CODE_OF_CONDUCT.md create mode 100644 SECURITY.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..8044ebe --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at opensource@github.com. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ \ No newline at end of file diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..b7530a1 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,31 @@ +Thanks for helping make GitHub safe for everyone. + +# Security + +GitHub takes the security of our software products and services seriously, including all of the open source code repositories managed through our GitHub organizations, such as [GitHub](https://github.com/GitHub). + +Even though [open source repositories are outside of the scope of our bug bounty program](https://bounty.github.com/index.html#scope) and therefore not eligible for bounty rewards, we will ensure that your finding gets passed along to the appropriate maintainers for remediation. + +## Reporting Security Issues + +If you believe you have found a security vulnerability in any GitHub-owned repository, please report it to us through coordinated disclosure. + +**Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.** + +Instead, please send an email to opensource-security[@]github.com, s-samadi[@]github.com, therealkujo[@]github.com + +Please include as much of the information listed below as you can to help us better understand and resolve the issue: + + * The type of issue (e.g., buffer overflow, SQL injection, or cross-site scripting) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +## Policy + +See [GitHub's Safe Harbor Policy](https://docs.github.com/en/site-policy/security-policies/github-bug-bounty-program-legal-safe-harbor#1-safe-harbor-terms) \ No newline at end of file From 780c3c0c0eddb1ae77b89fa4ad3654570012f976 Mon Sep 17 00:00:00 2001 From: Shadi Samadi Date: Tue, 9 Jan 2024 19:30:18 +1100 Subject: [PATCH 11/22] Create .golangci.yml --- .github/workflows/.golangci.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/.golangci.yml diff --git a/.github/workflows/.golangci.yml b/.github/workflows/.golangci.yml new file mode 100644 index 0000000..e0b1b20 --- /dev/null +++ b/.github/workflows/.golangci.yml @@ -0,0 +1,20 @@ +# This file contains all available configuration options +# with their default values. + +# options for analysis running +run: + go: '1.20.x' + +issues: + # List of regexps of issue texts to exclude. + # + # But independently of this option we use default exclude patterns, + # it can be disabled by `exclude-use-default: false`. + # To list all excluded by default patterns execute `golangci-lint run --help` + # + # Default: https://golangci-lint.run/usage/false-positives/#default-exclusions + exclude: + - "Error return value of `deleteBranchCmd.MarkPersistentFlagRequired` is not checked" + - "field `http` is unused" + - "field `client` is unused" + - "S1039: unnecessary use of fmt.Sprintf" From c54258731e1f552be23854c0fd5fc174d3ca2a57 Mon Sep 17 00:00:00 2001 From: Shadi Samadi Date: Tue, 9 Jan 2024 19:31:10 +1100 Subject: [PATCH 12/22] Update golangci-lint.yml --- .github/workflows/golangci-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 9652b3f..7ef1fef 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -37,7 +37,7 @@ jobs: # # Note: By default, the `.golangci.yml` file should be at the root of the repository. # The location of the configuration file can be changed by using `--config=` - # args: --timeout=30m --config=/my/path/.golangci.yml --issues-exit-code=0 + args: --config= ./.github/workflows/.golangci.yml # Optional: show only new issues if it's a pull request. The default value is `false`. # only-new-issues: true From 19af233f9e01030a2db475122c57b49b58a529fd Mon Sep 17 00:00:00 2001 From: Shadi Samadi Date: Tue, 9 Jan 2024 19:35:36 +1100 Subject: [PATCH 13/22] Rename .github/workflows/.golangci.yml to .golangci.yml --- .github/workflows/.golangci.yml => .golangci.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/.golangci.yml => .golangci.yml (100%) diff --git a/.github/workflows/.golangci.yml b/.golangci.yml similarity index 100% rename from .github/workflows/.golangci.yml rename to .golangci.yml From bc395e76bb238528ff59e61c87fad2644bdffa79 Mon Sep 17 00:00:00 2001 From: Shadi Samadi Date: Tue, 9 Jan 2024 19:35:59 +1100 Subject: [PATCH 14/22] Update golangci-lint.yml --- .github/workflows/golangci-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 7ef1fef..db7d7ba 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -37,7 +37,7 @@ jobs: # # Note: By default, the `.golangci.yml` file should be at the root of the repository. # The location of the configuration file can be changed by using `--config=` - args: --config= ./.github/workflows/.golangci.yml + # args: --config= ./.github/workflows/.golangci.yml # Optional: show only new issues if it's a pull request. The default value is `false`. # only-new-issues: true From 969d8bd574aea81648c1bf69b46b29f4064b7fd0 Mon Sep 17 00:00:00 2001 From: Shadi Samadi Date: Tue, 9 Jan 2024 19:37:34 +1100 Subject: [PATCH 15/22] Rename .golangci.yml to .github/workflows/.golangci.yml --- .golangci.yml => .github/workflows/.golangci.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .golangci.yml => .github/workflows/.golangci.yml (100%) diff --git a/.golangci.yml b/.github/workflows/.golangci.yml similarity index 100% rename from .golangci.yml rename to .github/workflows/.golangci.yml From b4e4e669ccd0b8dea0a9aefbb2085d68949feea1 Mon Sep 17 00:00:00 2001 From: Shadi Samadi Date: Tue, 9 Jan 2024 19:38:12 +1100 Subject: [PATCH 16/22] Update golangci-lint.yml --- .github/workflows/golangci-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index db7d7ba..13dfb07 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -37,7 +37,7 @@ jobs: # # Note: By default, the `.golangci.yml` file should be at the root of the repository. # The location of the configuration file can be changed by using `--config=` - # args: --config= ./.github/workflows/.golangci.yml + # args: --config= .github/workflows/.golangci.yml # Optional: show only new issues if it's a pull request. The default value is `false`. # only-new-issues: true From e3f650f3aa43ff100180c088e950e1ca0eff46ce Mon Sep 17 00:00:00 2001 From: Shadi Samadi Date: Tue, 9 Jan 2024 19:40:30 +1100 Subject: [PATCH 17/22] Update golangci-lint.yml --- .github/workflows/golangci-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 13dfb07..ad49546 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -37,7 +37,7 @@ jobs: # # Note: By default, the `.golangci.yml` file should be at the root of the repository. # The location of the configuration file can be changed by using `--config=` - # args: --config= .github/workflows/.golangci.yml + args: --config= .github/workflows/.golangci.yml # Optional: show only new issues if it's a pull request. The default value is `false`. # only-new-issues: true From b009f7f2a17857ab09deb31b0ca601a8b61d3e92 Mon Sep 17 00:00:00 2001 From: Shadi Samadi Date: Tue, 9 Jan 2024 19:41:56 +1100 Subject: [PATCH 18/22] Rename .github/workflows/.golangci.yml to .golangci.yml --- .github/workflows/.golangci.yml => .golangci.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/.golangci.yml => .golangci.yml (100%) diff --git a/.github/workflows/.golangci.yml b/.golangci.yml similarity index 100% rename from .github/workflows/.golangci.yml rename to .golangci.yml From c466845e8e5ff4cc3eca6cd2a1356503935f7636 Mon Sep 17 00:00:00 2001 From: Shadi Samadi Date: Tue, 9 Jan 2024 19:42:20 +1100 Subject: [PATCH 19/22] Update golangci-lint.yml --- .github/workflows/golangci-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index ad49546..13dfb07 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -37,7 +37,7 @@ jobs: # # Note: By default, the `.golangci.yml` file should be at the root of the repository. # The location of the configuration file can be changed by using `--config=` - args: --config= .github/workflows/.golangci.yml + # args: --config= .github/workflows/.golangci.yml # Optional: show only new issues if it's a pull request. The default value is `false`. # only-new-issues: true From 4397c591ecfb960802368434de56023f6d70fcd6 Mon Sep 17 00:00:00 2001 From: Shadi Samadi Date: Wed, 10 Jan 2024 08:49:28 +1100 Subject: [PATCH 20/22] Create dependency-review.yml --- .github/dependency-review.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/dependency-review.yml diff --git a/.github/dependency-review.yml b/.github/dependency-review.yml new file mode 100644 index 0000000..ad5704c --- /dev/null +++ b/.github/dependency-review.yml @@ -0,0 +1,22 @@ +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging. +# +# Source repository: https://github.com/actions/dependency-review-action +# Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement +name: 'Dependency Review' +on: + pull_request: + branches: main + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: 'Checkout Repository' + uses: actions/checkout@v3 + - name: 'Dependency Review' + uses: actions/dependency-review-action@v3 From dbee180d46c6bbd9fc2d6c6f66920fd1d9535124 Mon Sep 17 00:00:00 2001 From: Shadi Samadi Date: Wed, 10 Jan 2024 08:50:30 +1100 Subject: [PATCH 21/22] Rename .github/dependency-review.yml to .github/workflows/dependency-review.yml --- .github/{ => workflows}/dependency-review.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/{ => workflows}/dependency-review.yml (100%) diff --git a/.github/dependency-review.yml b/.github/workflows/dependency-review.yml similarity index 100% rename from .github/dependency-review.yml rename to .github/workflows/dependency-review.yml From bf64a72f9c5222ec18993acee517d977f3dc1dde Mon Sep 17 00:00:00 2001 From: Shadi Samadi Date: Tue, 30 Jan 2024 10:10:38 +1100 Subject: [PATCH 22/22] fixed grammar typo --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4641644..2c5cc1c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,8 +15,8 @@ Please note that this project is released with a [Contributor Code of Conduct](C These are one time installations required to be able to test your changes locally as part of the pull request (PR) submission process. -1. install Go [through download](https://go.dev/doc/install) | [through Homebrew](https://formulae.brew.sh/formula/go) -1. [install golangci-lint](https://golangci-lint.run/usage/install/#local-installation) +1. Install Go [through download](https://go.dev/doc/install) | [through Homebrew](https://formulae.brew.sh/formula/go) +1. [Install golangci-lint](https://golangci-lint.run/usage/install/#local-installation) ## Submitting a pull request