Skip to content

feat: GitHub Search #173

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,24 @@ echo "gruntwork-io/terragrunt gruntwork-io/terratest" | git-xargs \
"$(pwd)/scripts/update-copyright-year.sh"
```

### Option #5: Select repos via the GitHub Search API

The repository scope can be narrowed down by using the GitHub Search API. This allows you to select repositories based on various criteria, such as language, topics, or other metadata or file content.

- `--github-repository-search` to select repositories based on the [GitHub Repository Search API](https://docs.github.com/en/search-github/searching-on-github/searching-for-repositories)
- `--github-code-search` to select repositories based on the [GitHub Code Search API](https://docs.github.com/en/search-github/github-code-search/understanding-github-code-search-syntax)
- `--github-org` still can be used, it will add `org:<name>` to the search query

If both, `--github-repository-search` and `--github-code-search` are provided, the repositories will be filtered by both criteria.

```
git-xargs \
--github-org <your-github-org> \
--github-repository-search "is:private language:go" \
--github-code-search "filename:Dockerfile ubuntu" \
"$(pwd)/scripts/update-copyright-year.sh"
```

## Notable flags

`git-xargs` exposes several flags that allow you to customize its behavior to better suit your needs. For the latest info on flags, you should run `git-xargs --help`. However, a couple of the flags are worth explaining more in depth here:
Expand Down
8 changes: 8 additions & 0 deletions auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ type githubRepositoriesService interface {
ListByOrg(ctx context.Context, org string, opts *github.RepositoryListByOrgOptions) ([]*github.Repository, *github.Response, error)
}

// The go-github package satisfies this Search service's interface in production
type githubSearchService interface {
Repositories(ctx context.Context, query string, opts *github.SearchOptions) (*github.RepositoriesSearchResult, *github.Response, error)
Code(ctx context.Context, query string, opts *github.SearchOptions) (*github.CodeSearchResult, *github.Response, error)
}

// GithubClient is the data structure that is common between production code and test code. In production code,
// go-github satisfies the PullRequests and Repositories service interfaces, whereas in test the concrete
// implementations for these same services are mocks that return a static slice of pointers to GitHub repositories,
Expand All @@ -32,12 +38,14 @@ type githubRepositoriesService interface {
type GithubClient struct {
PullRequests githubPullRequestService
Repositories githubRepositoriesService
Search githubSearchService
}

func NewClient(client *github.Client) GithubClient {
return GithubClient{
PullRequests: client.PullRequests,
Repositories: client.Repositories,
Search: client.Search,
}
}

Expand Down
2 changes: 2 additions & 0 deletions cmd/git-xargs.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ func parseGitXargsConfig(c *cli.Context) (*config.GitXargsConfig, error) {
config.TeamReviewers = c.StringSlice("team-reviewers")
config.ReposFile = c.String("repos")
config.GithubOrg = c.String("github-org")
config.GithubRepositorySearch = c.String("github-repository-search")
config.GithubCodeSearch = c.String("github-code-search")
config.RepoSlice = c.StringSlice("repo")
config.MaxConcurrentRepos = c.Int("max-concurrent-repos")
config.SecondsToSleepBetweenPRs = c.Int("seconds-between-prs")
Expand Down
10 changes: 10 additions & 0 deletions common/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ const (
DefaultSecondsBetweenPRs = 1
DefaultMaxPullRequestRetries = 3
DefaultSecondsToWaitWhenRateLimited = 60
GithubRepositorySearchFlagName = "github-repository-search"
GithubCodeSearchFlagName = "github-code-search"
)

var (
Expand Down Expand Up @@ -120,4 +122,12 @@ var (
Name: KeepClonedRepositoriesFlagName,
Usage: "By default, git-xargs deletes the cloned repositories from the temp directory after the command has finished running, to save space on your machine. Pass this flag to prevent git-xargs from deleting the cloned repositories.",
}
GenericGithubRepositorySearchFlag = cli.StringFlag{
Name: GithubRepositorySearchFlagName,
Usage: "GitHub repository search query to find repositories (e.g., 'language:go', 'is:private', 'topic:docker'). See GitHub repository search syntax for more options.",
}
GenericGithubCodeSearchFlag = cli.StringFlag{
Name: GithubCodeSearchFlagName,
Usage: "GitHub code search query to find repositories containing matching code (e.g., 'path:Dockerfile', 'filename:package.json', 'extension:py print'). Repositories will be extracted from code search results. See GitHub code search syntax for more options.",
}
)
4 changes: 4 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ type GitXargsConfig struct {
TeamReviewers []string
ReposFile string
GithubOrg string
GithubRepositorySearch string
GithubCodeSearch string
RepoSlice []string
RepoFromStdIn []string
Args []string
Expand Down Expand Up @@ -61,6 +63,8 @@ func NewGitXargsConfig() *GitXargsConfig {
TeamReviewers: []string{},
ReposFile: "",
GithubOrg: "",
GithubRepositorySearch: "",
GithubCodeSearch: "",
RepoSlice: []string{},
RepoFromStdIn: []string{},
Args: []string{},
Expand Down
3 changes: 2 additions & 1 deletion io/validate-input.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import (

// EnsureValidOptionsPassed checks that user has provided one valid method for selecting repos to operate on
func EnsureValidOptionsPassed(config *config.GitXargsConfig) error {
if len(config.RepoSlice) < 1 && config.ReposFile == "" && config.GithubOrg == "" && len(config.RepoFromStdIn) == 0 {
if len(config.RepoSlice) < 1 && config.ReposFile == "" && config.GithubOrg == "" &&
config.GithubRepositorySearch == "" && config.GithubCodeSearch == "" && len(config.RepoFromStdIn) == 0 {
return errors.WithStackTrace(types.NoRepoSelectionsMadeErr{})
}
if config.BranchName == "" {
Expand Down
2 changes: 2 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ func setupApp() *cli.App {
common.GenericMaxConcurrentClonesFlag,
common.GenericNoSkipCIFlag,
common.GenericKeepClonedRepositoriesFlag,
common.GenericGithubRepositorySearchFlag,
common.GenericGithubCodeSearchFlag,
}

app.Action = cmd.RunGitXargs
Expand Down
Loading