diff --git a/cmd/repo/command.go b/cmd/repo/command.go index d390a209a..6b45e89c4 100644 --- a/cmd/repo/command.go +++ b/cmd/repo/command.go @@ -8,6 +8,7 @@ import ( "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "github.com/giantswarm/devctl/cmd/repo/find" "github.com/giantswarm/devctl/cmd/repo/setup" ) @@ -35,6 +36,20 @@ func New(config Config) (*cobra.Command, error) { var err error + var findCmd *cobra.Command + { + c := find.Config{ + Logger: config.Logger, + Stderr: config.Stderr, + Stdout: config.Stdout, + } + + findCmd, err = find.New(c) + if err != nil { + return nil, microerror.Mask(err) + } + } + var setupCmd *cobra.Command { c := setup.Config{ @@ -67,6 +82,7 @@ func New(config Config) (*cobra.Command, error) { f.Init(c) + c.AddCommand(findCmd) c.AddCommand(setupCmd) return c, nil diff --git a/cmd/repo/find/command.go b/cmd/repo/find/command.go new file mode 100644 index 000000000..6f66df88f --- /dev/null +++ b/cmd/repo/find/command.go @@ -0,0 +1,124 @@ +// Package find provides the 'repo find' command, which helps to discover +// GitHub repositories with certain features, or certain features missing. +package find + +import ( + "io" + "os" + + "github.com/giantswarm/microerror" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" +) + +const ( + name = "find" + description = `Find repositories based on very specific criteria + +The --what flag allows to specify what search criteria should be used. When combining several critaria, +a repository will be returned when it's macthing at least one criteria (boolean OR). + +Example: + + devctl repo find --what NO_DESCRIPTION --must-have-codeowners + +Note: archived repositories are always excluded. + +Criteria: + + DEFAULT_BRANCH_MASTER + + The default branch is named 'master'. We want to rename these to 'main'. + + HAS_DOCS_DIR + + The repo has a directory named 'docs' on the root level. This is the place + where we want to store technical documentation for the repo. + + HAS_PR_TEMPLATE_IN_DOCS + + Has the file docs/pull_request_template.md, which is not the desired location. + We want this to be in .github/pull_request_template.md. + + BAD_CODEOWNERS + + Finds repositories with errors in CODEOWNERS files. + + NO_CODEOWNERS + + There is no CODEOWNERS file in the root folder, which means that the repository + has no owner. We want repos to have owners, ideally. + + BAD_DEFAULT_BRANCH_PROTECTION_GO + + The Go repo has no effective protection rules for the default branch. + + NO_DEPENDABOT_ALERTS + + Dependabot security alerts are disabled. Note: this is independent from + version updates via Dependabot. + + NO_DEPENDENCY_GRAPH + + Returns repos where the dependency graph feature is inactive. We want it to be + active. + + NO_DESCRIPTION + + Repository description is missing. We want a meaningful description to be present, + to understand easily what the repository is about, even from lists. + + NO_README + + Repository has no README.md file in the root folder. We want thjis to be present, + to have some basic info and documentation available. + + README_OLD_CIRCLECI_BAGDE + + There is an outdated (broken) CircleCI badge is present in the README. This should + better get replaced by an up-to-date one, as it can otherwise not fulfil its purpose, + and broken images never make a good impression. + + README_OLD_GODOC_LINK + + The README contains an outdated godoc.org link. Should be pkg.go.dev nowadays. +` +) + +type Config struct { + Logger *logrus.Logger + Stderr io.Writer + Stdout io.Writer +} + +func New(config Config) (*cobra.Command, error) { + if config.Logger == nil { + return nil, microerror.Maskf(invalidConfigError, "%T.Logger must not be empty", config) + } + if config.Stderr == nil { + config.Stderr = os.Stderr + } + if config.Stdout == nil { + config.Stdout = os.Stdout + } + + f := &flag{} + + r := &runner{ + flag: f, + logger: config.Logger, + stderr: config.Stderr, + stdout: config.Stdout, + } + + c := &cobra.Command{ + Use: name, + Short: description, + Long: description, + RunE: r.Run, + } + + f.Init(c) + + return c, nil +} diff --git a/cmd/repo/find/error.go b/cmd/repo/find/error.go new file mode 100644 index 000000000..07a89a5e5 --- /dev/null +++ b/cmd/repo/find/error.go @@ -0,0 +1,30 @@ +package find + +import "github.com/giantswarm/microerror" + +var invalidConfigError = µerror.Error{ + Kind: "invalidConfigError", +} + +// IsInvalidConfig asserts invalidConfigError. +func IsInvalidConfig(err error) bool { + return microerror.Cause(err) == invalidConfigError +} + +var invalidFlagError = µerror.Error{ + Kind: "invalidFlagError", +} + +// IsInvalidFlag asserts invalidFlagError. +func IsInvalidFlag(err error) bool { + return microerror.Cause(err) == invalidFlagError +} + +var envVarNotFoundError = µerror.Error{ + Kind: "envVarNotFoundError", +} + +// IsEnvVarNotFound asserts envVarNotFoundError. +func IsEnvVarNotFound(err error) bool { + return microerror.Cause(err) == envVarNotFoundError +} diff --git a/cmd/repo/find/flag.go b/cmd/repo/find/flag.go new file mode 100644 index 000000000..baa79dd02 --- /dev/null +++ b/cmd/repo/find/flag.go @@ -0,0 +1,22 @@ +package find + +import "github.com/spf13/cobra" + +type flag struct { + What []string + MustHaveCodeowners bool + + IncludeArchived bool + IncludeFork bool +} + +func (f *flag) Init(cmd *cobra.Command) { + cmd.PersistentFlags().BoolVar(&f.IncludeArchived, "include-archived", false, "Also return archived repositories.") + cmd.PersistentFlags().BoolVar(&f.IncludeFork, "include-fork", false, "Also return giantswarm forks of repositories.") + cmd.PersistentFlags().StringSliceVar(&f.What, "what", []string{}, "What repos to find. See full help for all available criteria.") + cmd.PersistentFlags().BoolVar(&f.MustHaveCodeowners, "must-have-codeowners", false, "Only return repositories that have CODEOWNERS.") +} + +func (f *flag) Validate() error { + return nil +} diff --git a/cmd/repo/find/runner.go b/cmd/repo/find/runner.go new file mode 100644 index 000000000..1fdb613bf --- /dev/null +++ b/cmd/repo/find/runner.go @@ -0,0 +1,309 @@ +package find + +import ( + "context" + b64 "encoding/base64" + "fmt" + "io" + "net/http" + "os" + "strings" + + "github.com/giantswarm/microerror" + "github.com/google/go-github/v53/github" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "k8s.io/utils/strings/slices" + + "github.com/giantswarm/devctl/pkg/githubclient" +) + +const ( + //nolint:gosec + githubTokenEnvVar = "GITHUB_TOKEN" + githubOrg = "giantswarm" + + // Criteria names + critHasDocsDirectory = "HAS_DOCS_DIR" + critHasPrTemplateInDocs = "HAS_PR_TEMPLATE_IN_DOCS" + critReadmeHasOldCircleCiBadge = "README_OLD_CIRCLECI_BAGDE" + critReadmeHasOldGodocLink = "README_OLD_GODOC_LINK" + critNoCodeownersFile = "NO_CODEOWNERS" + critCodeownersErrors = "BAD_CODOWNERS" + critNoDescription = "NO_DESCRIPTION" + critNoReadme = "NO_README" + critDefaultBranchMaster = "DEFAULT_BRANCH_MASTER" + critNoDependencyGraph = "NO_DEPENDENCY_GRAPH" + critDependabotAlertsDisabled = "NO_DEPENDABOT_ALERTS" + critBadDefaultBranchProtectionGo = "BAD_DEFAULT_BRANCH_PROTECTION_GO" +) + +type runner struct { + flag *flag + logger *logrus.Logger + stdout io.Writer + stderr io.Writer +} + +func (r *runner) Run(cmd *cobra.Command, args []string) error { + ctx := context.Background() + + err := r.flag.Validate() + if err != nil { + return microerror.Mask(err) + } + + err = r.run(ctx, cmd, args) + if err != nil { + return microerror.Mask(err) + } + + return nil +} + +func (r *runner) run(ctx context.Context, cmd *cobra.Command, args []string) error { + token, found := os.LookupEnv(githubTokenEnvVar) + if !found { + return microerror.Maskf(envVarNotFoundError, "environment variable %#q was not found", githubTokenEnvVar) + } + + if len(r.flag.What) < 1 { + return microerror.Maskf(invalidConfigError, "no search criteria specified via --what flag") + } + + c := githubclient.Config{ + Logger: r.logger, + AccessToken: token, + } + + client, err := githubclient.New(c) + if err != nil { + return microerror.Mask(err) + } + realClient := client.GetUnderlyingClient(ctx) + + repos, err := client.ListRepositories(ctx, githubOrg) + if err != nil { + return microerror.Mask(err) + } + + matchingReposCount := 0 + + for _, repo := range repos { + matched := []string{} + + repoMetadata, err := client.GetRepository(ctx, githubOrg, repo.Name) + if err != nil { + return microerror.Mask(err) + } + defaultBranch := repoMetadata.GetDefaultBranch() + + if !r.flag.IncludeArchived && repoMetadata.GetArchived() { + // Skip archived repos + continue + } + + if !r.flag.IncludeFork && *repoMetadata.Fork { + // Skip fork repos + continue + } + + // Check for matching criteria + + if slices.Contains(r.flag.What, critNoCodeownersFile) || r.flag.MustHaveCodeowners { + _, _, _, err := realClient.Repositories.GetContents(ctx, githubOrg, repo.Name, "CODEOWNERS", nil) + if err != nil { + if r.flag.MustHaveCodeowners { + // Skip repo without CODEOWNERS file + continue + } + + output := fmt.Sprintf(" - /CODEOWNERS file not present (%s)\n", critNoCodeownersFile) + if repoMetadata.Fork != nil && *repoMetadata.Fork { + output += fmt.Sprintf(" - Note: this repo is a fork of %s\n", repoMetadata.GetForksURL()) + } + matched = append(matched, output) + } + } + + if slices.Contains(r.flag.What, critDependabotAlertsDisabled) { + enabled, _, err := realClient.Repositories.GetVulnerabilityAlerts(ctx, githubOrg, repo.Name) + if err != nil { + return microerror.Mask(err) + } + + if !enabled { + output := fmt.Sprintf(" - Dependabot security alerts disabled (%s)\n", critDependabotAlertsDisabled) + output += fmt.Sprintf(" - Go to https://github.com/%s/%s/security to enable", githubOrg, repo.Name) + if repoMetadata.Fork != nil && *repoMetadata.Fork { + output += fmt.Sprintf(" - Note: this repo is a fork of %s\n", repoMetadata.GetForksURL()) + } + matched = append(matched, output) + } + } + + if slices.Contains(r.flag.What, critCodeownersErrors) { + errs, resp, err := realClient.Repositories.GetCodeownersErrors(ctx, githubOrg, repo.Name) + if err != nil { + if resp != nil && resp.StatusCode == http.StatusNotFound { + // CODEOWNERS not found in this repo. Do nothing. + } else { + return microerror.Mask(err) + } + } + + if errs != nil && len(errs.Errors) > 0 { + output := fmt.Sprintf(" - Errors found in CODEOWNERS files (%s)\n", critCodeownersErrors) + for _, item := range errs.Errors { + messageFirstLine := strings.Split(item.Message, "\n")[0] + output += fmt.Sprintf(" - https://github.com/%s/%s/blob/%s/%s#L%d - %q\n", githubOrg, repo.Name, defaultBranch, item.Path, item.Line, messageFirstLine) + } + matched = append(matched, output) + } + } + + if slices.Contains(r.flag.What, critHasDocsDirectory) { + _, items, _, err := realClient.Repositories.GetContents(ctx, githubOrg, repo.Name, "docs", nil) + if err == nil { + output := fmt.Sprintf(" - /docs directory exists (%s)\n", critHasDocsDirectory) + for _, item := range items { + path := item.GetPath() + ftype := item.GetType() + output += fmt.Sprintf(" - %s %s\n", path, ftype) + } + matched = append(matched, output) + } + } + + if slices.Contains(r.flag.What, critHasPrTemplateInDocs) { + _, _, _, err := realClient.Repositories.GetContents(ctx, githubOrg, repo.Name, "docs/pull_request_template.md", nil) + if err == nil { + output := fmt.Sprintf(" - /docs/pull_request_template.md file exists (%s)\n", critHasPrTemplateInDocs) + matched = append(matched, output) + } + } + + if slices.Contains(r.flag.What, critReadmeHasOldCircleCiBadge) || slices.Contains(r.flag.What, critNoReadme) || slices.Contains(r.flag.What, critReadmeHasOldGodocLink) { + fileContent, _, resp, err := realClient.Repositories.GetContents(ctx, githubOrg, repo.Name, "README.md", nil) + + if (slices.Contains(r.flag.What, critReadmeHasOldCircleCiBadge) || slices.Contains(r.flag.What, critReadmeHasOldGodocLink)) && err == nil { + decodedContent, _ := b64.StdEncoding.DecodeString(*fileContent.Content) + + if slices.Contains(r.flag.What, critReadmeHasOldCircleCiBadge) { + if strings.Contains(string(decodedContent), fmt.Sprintf("https://circleci.com/gh/%s", githubOrg)) { + output := fmt.Sprintf(" - /README.md has old CircleCI badge (%s)\n", critReadmeHasOldCircleCiBadge) + if repoMetadata.Fork != nil && *repoMetadata.Fork { + output += fmt.Sprintf(" Note: this repo is a fork of %s\n", repoMetadata.GetForksURL()) + } + output += fmt.Sprintf(" - Edit via https://github.com/%s/%s/edit/%s/README.md\n", githubOrg, repo.Name, defaultBranch) + output += fmt.Sprintf(" - Badge code via https://app.circleci.com/settings/project/github/%s/%s/status-badges)\n", githubOrg, repo.Name) + matched = append(matched, output) + } + } + + if slices.Contains(r.flag.What, critReadmeHasOldGodocLink) { + if strings.Contains(string(decodedContent), "godoc.org") { + output := fmt.Sprintf(" - /README.md has link to godoc.org (%s)\n", critReadmeHasOldGodocLink) + output += fmt.Sprintf(" - Should be https://pkg.go.dev/github.com/%s/%s\n", githubOrg, repo.Name) + output += fmt.Sprintf(" - Edit via https://github.com/%s/%s/edit/%s/README.md\n", githubOrg, repo.Name, defaultBranch) + matched = append(matched, output) + } + } + } + + if slices.Contains(r.flag.What, critNoReadme) && err != nil && resp.StatusCode == 404 { + output := fmt.Sprintf(" - /README.md not present (%s)\n", critNoReadme) + matched = append(matched, output) + } + } + + if slices.Contains(r.flag.What, critNoDescription) { + if repoMetadata.GetDescription() == "" { + output := fmt.Sprintf(" - Repository has no description (%s)\n", critNoDescription) + matched = append(matched, output) + } + } + + if slices.Contains(r.flag.What, critDefaultBranchMaster) { + if defaultBranch == "master" { + output := fmt.Sprintf(" - Default branch is 'master' (%s)\n", critDefaultBranchMaster) + if repoMetadata.Fork != nil && *repoMetadata.Fork { + output += fmt.Sprintf(" - Note: this repo is a fork of %s\n", repoMetadata.GetForksURL()) + } + matched = append(matched, output) + } + } + + if slices.Contains(r.flag.What, critNoDependencyGraph) { + path := fmt.Sprintf("/repos/%s/%s/dependency-graph/sbom", githubOrg, repo.Name) + req, err := realClient.NewRequest("GET", path, nil, github.WithVersion("2022-11-28")) + if err != nil { + return microerror.Mask(err) + } + + resp, err := realClient.Do(context.Background(), req, nil) + if err != nil { + if resp != nil && resp.StatusCode == 404 { + output := fmt.Sprintf(" - Dependency graph not active (%s)\n", critNoDependencyGraph) + output += fmt.Sprintf(" - Enable it here: https://github.com/%s/%s/network/dependencies\n", githubOrg, repo.Name) + matched = append(matched, output) + } else { + return microerror.Mask(err) + } + } + } + + if slices.Contains(r.flag.What, critBadDefaultBranchProtectionGo) && repoMetadata.GetLanguage() == "Go" { + protection, resp, err := realClient.Repositories.GetBranchProtection(ctx, githubOrg, repo.Name, defaultBranch) + output := "" + + if err != nil { + // Branch protection not in place + if resp != nil && resp.StatusCode == http.StatusNotFound { + output += fmt.Sprintf(" - Default branch has no protection rules (%s)\n", critBadDefaultBranchProtectionGo) + output += fmt.Sprintf(" - Default branch name is %q\n", defaultBranch) + output += fmt.Sprintf(" - Configure protection here: https://github.com/%s/%s/settings/branches\n", githubOrg, repo.Name) + } else { + return microerror.Mask(err) + } + matched = append(matched, output) + } else { + // Branch protection details + if protection.RequiredStatusChecks == nil || protection.RequiredPullRequestReviews == nil { + output += fmt.Sprintf(" - Default branch protection not sufficient (%s)\n", critBadDefaultBranchProtectionGo) + if protection.RequiredStatusChecks == nil { + output += " - Status checks are not required to pass before merging\n" + } + if protection.RequiredPullRequestReviews == nil { + output += " - PR reviews are not required\n" + } + + output += fmt.Sprintf(" - Configure protection here: https://github.com/%s/%s/settings/branches\n", githubOrg, repo.Name) + + matched = append(matched, output) + } + + if protection.RequiredStatusChecks != nil && len(protection.RequiredStatusChecks.Checks) == 0 { + output += fmt.Sprintf(" - Default branch protection not sufficient (%s)\n", critBadDefaultBranchProtectionGo) + output += " - None of the checks is required\n" + output += fmt.Sprintf(" - Fix this here: https://github.com/%s/%s/settings/branches\n", githubOrg, repo.Name) + matched = append(matched, output) + } + } + } + + // Print output per repo + if len(matched) > 0 { + matchingReposCount++ + fmt.Printf("\n- [ ] https://github.com/%s/%s\n", githubOrg, repo.Name) + for _, item := range matched { + fmt.Print(item) + } + } + + } + + fmt.Printf("\nFound %d matching non-archived repositoiries\n", matchingReposCount) + + return nil +} diff --git a/cmd/repo/setup/runner.go b/cmd/repo/setup/runner.go index 0efb9f53a..3fb018c12 100644 --- a/cmd/repo/setup/runner.go +++ b/cmd/repo/setup/runner.go @@ -8,7 +8,7 @@ import ( "strings" "github.com/giantswarm/microerror" - "github.com/google/go-github/v44/github" + "github.com/google/go-github/v53/github" "github.com/sirupsen/logrus" "github.com/spf13/cobra" diff --git a/go.mod b/go.mod index 795b77173..e12c81dcc 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/giantswarm/devctl -go 1.18 +go 1.19 require ( github.com/Masterminds/semver/v3 v3.2.1 @@ -11,7 +11,7 @@ require ( github.com/giantswarm/microerror v0.4.0 github.com/giantswarm/micrologger v1.0.0 github.com/giantswarm/release-operator/v4 v4.1.0 - github.com/google/go-github/v44 v44.1.0 + github.com/google/go-github/v53 v53.2.0 github.com/pelletier/go-toml v1.9.5 github.com/rhysd/go-github-selfupdate v1.2.3 github.com/sirupsen/logrus v1.9.0 @@ -20,10 +20,13 @@ require ( golang.org/x/net v0.13.0 golang.org/x/oauth2 v0.8.0 k8s.io/apimachinery v0.24.3 + k8s.io/utils v0.0.0-20220713171938-56c0de1e6f5e sigs.k8s.io/yaml v1.3.0 ) require ( + github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect + github.com/cloudflare/circl v1.3.3 // indirect github.com/giantswarm/k8smetadata v0.19.0 // indirect github.com/go-kit/log v0.2.1 // indirect github.com/go-logfmt/logfmt v0.5.1 // indirect @@ -54,7 +57,6 @@ require ( gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect k8s.io/klog/v2 v2.70.1 // indirect - k8s.io/utils v0.0.0-20220713171938-56c0de1e6f5e // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect ) diff --git a/go.sum b/go.sum index 28c27b57d..11946c7d3 100644 --- a/go.sum +++ b/go.sum @@ -3,6 +3,8 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA= +github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= @@ -13,8 +15,12 @@ github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdn github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0= github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= +github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I= +github.com/cloudflare/circl v1.3.3 h1:fE/Qz0QdIGqeWfnwq0RE0R7MI51s0M2E4Ga9kq5AEMs= +github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= 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.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -82,8 +88,8 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-github/v30 v30.1.0 h1:VLDx+UolQICEOKu2m4uAoMti1SxuEBAl7RSEG16L+Oo= github.com/google/go-github/v30 v30.1.0/go.mod h1:n8jBpHl45a/rlBUtRJMOG4GhNADUQFEufcolZ95JfU8= -github.com/google/go-github/v44 v44.1.0 h1:shWPaufgdhr+Ad4eo/pZv9ORTxFpsxPEPEuuXAKIQGA= -github.com/google/go-github/v44 v44.1.0/go.mod h1:iWn00mWcP6PRWHhXm0zuFJ8wbEjE5AGO5D5HXYM4zgw= +github.com/google/go-github/v53 v53.2.0 h1:wvz3FyF53v4BK+AsnvCmeNhf8AkTaeh2SoYu/XUvTtI= +github.com/google/go-github/v53 v53.2.0/go.mod h1:XhFRObz+m/l+UCm9b7KSIC3lT3NWSXGt7mOsAWEloao= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= @@ -234,6 +240,7 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/pkg/githubclient/client.go b/pkg/githubclient/client.go index 3707405e9..5d75945bc 100644 --- a/pkg/githubclient/client.go +++ b/pkg/githubclient/client.go @@ -4,7 +4,7 @@ import ( "context" "github.com/giantswarm/microerror" - "github.com/google/go-github/v44/github" + "github.com/google/go-github/v53/github" "github.com/sirupsen/logrus" "golang.org/x/oauth2" ) @@ -54,3 +54,7 @@ func (c *Client) getUnderlyingClient(ctx context.Context) *github.Client { return client } + +func (c *Client) GetUnderlyingClient(ctx context.Context) *github.Client { + return c.getUnderlyingClient(ctx) +} diff --git a/pkg/githubclient/client_content.go b/pkg/githubclient/client_content.go index 961d3f0dd..47dc308e0 100644 --- a/pkg/githubclient/client_content.go +++ b/pkg/githubclient/client_content.go @@ -4,7 +4,7 @@ import ( "context" "github.com/giantswarm/microerror" - "github.com/google/go-github/v44/github" + "github.com/google/go-github/v53/github" ) func (c *Client) GetFile(ctx context.Context, owner, repo, path, ref string) (RepositoryFile, error) { diff --git a/pkg/githubclient/client_repository.go b/pkg/githubclient/client_repository.go index 630dc4390..21db822dd 100644 --- a/pkg/githubclient/client_repository.go +++ b/pkg/githubclient/client_repository.go @@ -8,26 +8,22 @@ import ( "regexp" "github.com/giantswarm/microerror" - "github.com/google/go-github/v44/github" + "github.com/google/go-github/v53/github" ) func (c *Client) ListRepositories(ctx context.Context, owner string) ([]Repository, error) { - c.logger.Infof("listing repositories for owner %#q", owner) - underlyingClient := c.getUnderlyingClient(ctx) var repos []Repository { opt := &github.RepositoryListByOrgOptions{ ListOptions: github.ListOptions{ - PerPage: 500, + PerPage: 100, }, - + Sort: "full_name", Type: "all", } for pageCnt := 0; ; pageCnt++ { - c.logger.Infof("listing page %d of repositories for owner %#q", pageCnt, owner) - pageRepos, resp, err := underlyingClient.Repositories.ListByOrg(ctx, owner, opt) if err != nil { return nil, microerror.Mask(err) @@ -46,19 +42,13 @@ func (c *Client) ListRepositories(ctx context.Context, owner string) ([]Reposito repos = append(repos, r) } - - c.logger.Infof("listed page %d of %d repositories for owner %#q", pageCnt, len(pageRepos), owner) } } - c.logger.Infof("listed %d repositories for owner %#q", len(repos), owner) - return repos, nil } func (c *Client) GetRepository(ctx context.Context, owner, repo string) (*github.Repository, error) { - c.logger.Infof("get repository details for \"%s/%s\"", owner, repo) - underlyingClient := c.getUnderlyingClient(ctx) repository, response, err := underlyingClient.Repositories.Get(ctx, owner, repo) diff --git a/pkg/githubclient/error_internal.go b/pkg/githubclient/error_internal.go index 234201ba8..d333aaf6b 100644 --- a/pkg/githubclient/error_internal.go +++ b/pkg/githubclient/error_internal.go @@ -1,6 +1,6 @@ package githubclient -import "github.com/google/go-github/v44/github" +import "github.com/google/go-github/v53/github" func isGithub404(err error) bool { if err == nil { diff --git a/pkg/githubclient/types.go b/pkg/githubclient/types.go index f63c188fc..f4e36aaaa 100644 --- a/pkg/githubclient/types.go +++ b/pkg/githubclient/types.go @@ -4,7 +4,7 @@ import ( "time" "github.com/giantswarm/microerror" - "github.com/google/go-github/v44/github" + "github.com/google/go-github/v53/github" ) type Repository struct {