Skip to content
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

feat: Including Regular Expression filter when targeting Github Repositories #426

Merged
merged 26 commits into from
Nov 9, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
fd70c8f
Including Regular Expression checker when using Repository Search
josealdaco Oct 25, 2023
a87dec1
Removing unused comments
josealdaco Oct 25, 2023
5f08f6d
Including unit test
josealdaco Oct 25, 2023
e5b99af
organizing imports
josealdaco Oct 25, 2023
ebacb0a
Utilizing --repo-exclude to exclude certain repositories
josealdaco Oct 25, 2023
797f1ad
Removing unused comments
josealdaco Oct 25, 2023
3d3d49d
Removing extra comment
josealdaco Oct 25, 2023
72f8013
Refactor based on PR feedback
josealdaco Oct 27, 2023
970b04c
Merging current master of upstream branch
josealdaco Oct 27, 2023
b2ea5dd
Minor id change on RepoFilter test
josealdaco Oct 27, 2023
7583267
Adding nil statement in case a regularexpression was not used
josealdaco Oct 27, 2023
5909e8a
fix package import structure
josealdaco Oct 27, 2023
b5e70ed
feat: Modified repo-filter to repo-include and repo-exclude
josealdaco Oct 30, 2023
7d8d524
Using regex.Compile to raise regEx validation errors instead of panic
josealdaco Nov 6, 2023
ddf13b9
Merge branch 'master' of github.com:lindell/multi-gitter into regex_t…
josealdaco Nov 6, 2023
c2f8834
Minor syntax changes from golangci
josealdaco Nov 6, 2023
57fc008
Moving regex filtering away from github scm
josealdaco Nov 7, 2023
d1082cb
reverting sort duplication change
josealdaco Nov 7, 2023
a452b40
removing changes to .github.go file
josealdaco Nov 7, 2023
be71b2e
Including unit test for feature
josealdaco Nov 7, 2023
d368cc2
including more tests for regex & wrapping errors
josealdaco Nov 8, 2023
696d388
chore: minor style fixes
josealdaco Nov 9, 2023
b41d462
chore: remove changes to platform file
josealdaco Nov 9, 2023
06c8ad8
fix:moving regex compiling
josealdaco Nov 9, 2023
b4ff2b2
fix:undo removing unrelated linebreaks
josealdaco Nov 9, 2023
9ec4191
Added newline between sections
lindell Nov 9, 2023
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
58 changes: 38 additions & 20 deletions cmd/cmd-run.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"os"
"os/signal"
"regexp"
"strings"
"syscall"

Expand Down Expand Up @@ -175,7 +176,22 @@ func run(cmd *cobra.Command, _ []string) error {
<-c
os.Exit(1)
}()

josealdaco marked this conversation as resolved.
Show resolved Hide resolved
repoInclude, _ := flag.GetString("repo-include")
josealdaco marked this conversation as resolved.
Show resolved Hide resolved
repoExclude, _ := flag.GetString("repo-exclude")
var repoIncludeFilterCompile *regexp.Regexp
var repoExcludeFilterCompile *regexp.Regexp
if repoInclude != "" {
repoIncludeFilterCompile, err = regexp.Compile(repoInclude)
josealdaco marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return err
lindell marked this conversation as resolved.
Show resolved Hide resolved
}
}
if repoExclude != "" {
repoExcludeFilterCompile, err = regexp.Compile(repoExclude)
josealdaco marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return err
lindell marked this conversation as resolved.
Show resolved Hide resolved
}
}
runner := &multigitter.Runner{
ScriptPath: executablePath,
Arguments: arguments,
Expand All @@ -185,25 +201,27 @@ func run(cmd *cobra.Command, _ []string) error {

VersionController: vc,

CommitMessage: commitMessage,
PullRequestTitle: prTitle,
PullRequestBody: prBody,
Reviewers: reviewers,
TeamReviewers: teamReviewers,
MaxReviewers: maxReviewers,
MaxTeamReviewers: maxTeamReviewers,
Interactive: interactive,
DryRun: dryRun,
Fork: forkMode,
ForkOwner: forkOwner,
SkipPullRequest: skipPullRequest,
SkipRepository: skipRepository,
CommitAuthor: commitAuthor,
BaseBranch: baseBranchName,
Assignees: assignees,
ConflictStrategy: conflictStrategy,
Draft: draft,
Labels: labels,
CommitMessage: commitMessage,
PullRequestTitle: prTitle,
PullRequestBody: prBody,
Reviewers: reviewers,
TeamReviewers: teamReviewers,
MaxReviewers: maxReviewers,
MaxTeamReviewers: maxTeamReviewers,
Interactive: interactive,
DryRun: dryRun,
Fork: forkMode,
ForkOwner: forkOwner,
SkipPullRequest: skipPullRequest,
SkipRepository: skipRepository,
RegExExcludeRepository: repoExcludeFilterCompile,
RegExIncludeRepository: repoIncludeFilterCompile,
CommitAuthor: commitAuthor,
BaseBranch: baseBranchName,
Assignees: assignees,
ConflictStrategy: conflictStrategy,
Draft: draft,
Labels: labels,

Concurrent: concurrent,

Expand Down
4 changes: 2 additions & 2 deletions cmd/platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ func configurePlatform(cmd *cobra.Command) {
flags.StringSliceP("group", "G", nil, "The name of a GitLab organization. All repositories in that group will be used.")
flags.StringSliceP("user", "U", nil, "The name of a user. All repositories owned by that user will be used.")
flags.StringSliceP("repo", "R", nil, "The name, including owner of a GitHub repository in the format \"ownerName/repoName\".")
flags.StringP("repo-include", "", "", "Include repositories that match with a given Regular Expression")
josealdaco marked this conversation as resolved.
Show resolved Hide resolved
flags.StringP("repo-exclude", "", "", "Exclude repositories that match with a given Regular Expression")
flags.StringP("repo-search", "", "", "Use a repository search to find repositories to target (GitHub only). Forks are NOT included by default, use `fork:true` to include them. See the GitHub documentation for full syntax: https://docs.github.com/en/search-github/searching-on-github/searching-for-repositories")
flags.StringSliceP("topic", "", nil, "The topic of a GitHub/GitLab/Gitea repository. All repositories having at least one matching topic are targeted.")
flags.StringSliceP("project", "P", nil, "The name, including owner of a GitLab project in the format \"ownerName/repoName\".")
Expand Down Expand Up @@ -129,7 +131,6 @@ func createGithubClient(flag *flag.FlagSet, verifyFlags bool, readOnly bool) (mu
forkOwner, _ := flag.GetString("fork-owner")
sshAuth, _ := flag.GetBool("ssh-auth")
skipForks, _ := flag.GetBool("skip-forks")

if verifyFlags && len(orgs) == 0 && len(users) == 0 && len(repos) == 0 && repoSearch == "" {
return nil, errors.New("no organization, user, repo or repo-search set")
}
Expand Down Expand Up @@ -160,7 +161,6 @@ func createGithubClient(flag *flag.FlagSet, verifyFlags bool, readOnly bool) (mu
if err != nil {
return nil, err
}

vc, err := github.New(github.Config{
Token: token,
BaseURL: gitBaseURL,
Expand Down
39 changes: 29 additions & 10 deletions internal/multigitter/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"math/rand"
"os"
"os/exec"
"regexp"
"sync"
"syscall"

Expand Down Expand Up @@ -54,12 +55,13 @@ type Runner struct {
BaseBranch string // The base branch of the PR, use default branch if not set
Assignees []string

Concurrent int
SkipPullRequest bool // If set, the script will run directly on the base-branch without creating any PR
SkipRepository []string // A list of repositories that run will skip

Fork bool // If set, create a fork and make the pull request from it
ForkOwner string // The owner of the new fork. If empty, the fork should happen on the logged in user
Concurrent int
SkipPullRequest bool // If set, the script will run directly on the base-branch without creating any PR
SkipRepository []string // A list of repositories that run will skip
RegExIncludeRepository *regexp.Regexp
RegExExcludeRepository *regexp.Regexp
Fork bool // If set, create a fork and make the pull request from it
josealdaco marked this conversation as resolved.
Show resolved Hide resolved
ForkOwner string // The owner of the new fork. If empty, the fork should happen on the logged in user

ConflictStrategy ConflictStrategy // Defines what will happen if a branch already exists

Expand Down Expand Up @@ -97,8 +99,7 @@ func (r *Runner) Run(ctx context.Context) error {
if err != nil {
return errors.Wrap(err, "could not fetch repositories")
}

repos = filterRepositories(repos, r.SkipRepository)
repos = filterRepositories(repos, r.SkipRepository, r.RegExIncludeRepository, r.RegExExcludeRepository)

if len(repos) == 0 {
log.Infof("No repositories found. Please make sure the user of the token has the correct access to the repos you want to change.")
Expand Down Expand Up @@ -152,15 +153,33 @@ func (r *Runner) Run(ctx context.Context) error {
return nil
}

func filterRepositories(repos []scm.Repository, skipRepositoryNames []string) []scm.Repository {
// Determines if Repository should be excluded based on provided Regular Expression
func excludeRepositoryFilter(repoName string, regExp *regexp.Regexp) bool {
if regExp == nil {
return false
}
return regExp.MatchString(repoName)
}

// Determines if Repository should be included based on provided Regular Expression
func matchesRepositoryFilter(repoName string, regExp *regexp.Regexp) bool {
if regExp == nil {
return true
}
return regExp.MatchString(repoName)
}

func filterRepositories(repos []scm.Repository, skipRepositoryNames []string, regExIncludeRepository *regexp.Regexp,
regExExcludeRepository *regexp.Regexp) []scm.Repository {
skipReposMap := map[string]struct{}{}
for _, skipRepo := range skipRepositoryNames {
skipReposMap[skipRepo] = struct{}{}
}

filteredRepos := make([]scm.Repository, 0, len(repos))
for _, r := range repos {
if _, shouldSkip := skipReposMap[r.FullName()]; !shouldSkip {
josealdaco marked this conversation as resolved.
Show resolved Hide resolved
if _, shouldSkip := skipReposMap[r.FullName()]; !shouldSkip && (matchesRepositoryFilter(r.FullName(),
regExIncludeRepository) && !excludeRepositoryFilter(r.FullName(), regExExcludeRepository)) {
filteredRepos = append(filteredRepos, r)
} else {
log.Infof("Skipping %s", r.FullName())
Expand Down
26 changes: 26 additions & 0 deletions tests/table_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,32 @@ func TestTable(t *testing.T) {
assert.False(t, branchExist(t, vcMock.Repositories[0].Path, "custom-branch-name"))
},
},
{
lindell marked this conversation as resolved.
Show resolved Hide resolved
name: "regex repository filtering",
vcCreate: func(t *testing.T) *vcmock.VersionController {
return &vcmock.VersionController{
Repositories: []vcmock.Repository{
createRepo(t, "owner", "repo1", "i like apples"),
createRepo(t, "owner", "repo-2", "i like oranges"),
createRepo(t, "owner", "repo-change", "i like carrots"),
createRepo(t, "owner", "repo-3", "i like carrots"),
},
}
},
args: []string{
"run",
"--repo-search", "repo",
"--repo-include", "^owner/repo-",
"--repo-exclude", "\\d$",
"--commit-message", "chore: foo",
"--dry-run",
changerBinaryPath,
},
verify: func(t *testing.T, vcMock *vcmock.VersionController, runData runData) {
require.Len(t, vcMock.PullRequests, 0)
assert.Contains(t, runData.logOut, "Running on 1 repositories")
},
},

{
name: "parallel",
Expand Down