Skip to content

Commit d2d6079

Browse files
authored
feat: Add --skip-archived-repos flag (#33)
1 parent 1241a03 commit d2d6079

File tree

10 files changed

+71
-2
lines changed

10 files changed

+71
-2
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,7 @@ echo "gruntwork-io/terragrunt gruntwork-io/terratest" | git-xargs \
332332
| `--github-org` | If you want to target every repo in a Github org that your GITHUB_OAUTH_TOKEN has access to, pass the name of the Organization with this flag, to page through every repo via the Github API and target it | String | No |
333333
| `--commit-message` | The commit message to use when creating commits. If you supply this flag, but neither the optional `--pull-request-title` or `--pull-request-description` flags, then the commit message value will be used for all three. | String | No |
334334
| `--skip-pull-requests` | If you don't want any pull requests opened, but would rather have your changes committed directly to your specified branch, pass this flag. Note that it won't work if your Github repo is configured with branch protections on the branch you're trying to commit directly to! | Boolean | No |
335+
| `--skip-archived-repos`| If you want to exclude archived (read-only) repositories from the list of targeted repos, pass this flag. | Boolean | No |
335336
| `--dry-run` | If you are in the process of testing out `git-xargs` or your intial set of targeted repos, but you don't want to make any changes via the Github API (pushing your local changes or opening pull requests) you can pass the dry-run branch. This is useful because the output report will still tell you which repos would have been affected, without actually making changes via the Github API to your remote repositories. | Boolean | No |
336337
337338
## Best practices, tips and tricks

cmd/git-xargs.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ func parseGitXargsConfig(c *cli.Context) (*config.GitXargsConfig, error) {
2222
config := config.NewGitXargsConfig()
2323
config.DryRun = c.Bool("dry-run")
2424
config.SkipPullRequests = c.Bool("skip-pull-requests")
25+
config.SkipArchivedRepos = c.Bool("skip-archived-repos")
2526
config.BranchName = c.String("branch-name")
2627
config.CommitMessage = c.String("commit-message")
2728
config.PullRequestTitle = c.String("pull-request-title")

common/common.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ const (
66
GithubOrgFlagName = "github-org"
77
DryRunFlagName = "dry-run"
88
SkipPullRequestsFlagName = "skip-pull-requests"
9+
SkipArchivedReposFlagName = "skip-archived-repos"
910
RepoFlagName = "repo"
1011
ReposFileFlagName = "repos"
1112
CommitMessageFlagName = "commit-message"
@@ -30,6 +31,10 @@ var (
3031
Name: SkipPullRequestsFlagName,
3132
Usage: "When skip-pull-requests is set to true, no pull requests will be opened. All changes will be committed and pushed to the specified branch directly.",
3233
}
34+
GenericSkipArchivedReposFlag = cli.BoolFlag{
35+
Name: SkipArchivedReposFlagName,
36+
Usage: "Used in conjunction with github-org, will exclude archived repositories.",
37+
}
3338
GenericRepoFlag = cli.StringSliceFlag{
3439
Name: RepoFlagName,
3540
Usage: "A single repo name to run the command on in the format of <github-organization/repo-name>. Can be invoked multiple times with different repo names",

config/config.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
type GitXargsConfig struct {
1515
DryRun bool
1616
SkipPullRequests bool
17+
SkipArchivedRepos bool
1718
BranchName string
1819
CommitMessage string
1920
PullRequestTitle string
@@ -33,6 +34,7 @@ func NewGitXargsConfig() *GitXargsConfig {
3334
return &GitXargsConfig{
3435
DryRun: false,
3536
SkipPullRequests: false,
37+
SkipArchivedRepos: false,
3638
BranchName: "",
3739
CommitMessage: common.DefaultCommitMessage,
3840
PullRequestTitle: common.DefaultPullRequestTitle,

main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ func setupApp() *cli.App {
6464
common.GenericGithubOrgFlag,
6565
common.GenericDryRunFlag,
6666
common.GenericSkipPullRequestFlag,
67+
common.GenericSkipArchivedReposFlag,
6768
common.GenericRepoFlag,
6869
common.GenericRepoFileFlag,
6970
common.GenericBranchFlag,

mocks/mocks.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,14 @@ var ownerName = "gruntwork-io"
1414
var repoName1 = "terragrunt"
1515
var repoName2 = "terratest"
1616
var repoName3 = "fetch"
17+
var repoName4 = "terraform-kubernetes-helm"
1718

1819
var repoURL1 = "https://github.com/gruntwork-io/terragrunt"
1920
var repoURL2 = "https://github.com/gruntwork-io/terratest"
2021
var repoURL3 = "https://github.com/gruntwork-io/fetch"
22+
var repoURL4 = "https://github.com/gruntwork-io/terraform-kubernetes-helm"
23+
24+
var archivedFlag = true
2125

2226
var MockGithubRepositories = []*github.Repository{
2327
&github.Repository{
@@ -41,6 +45,14 @@ var MockGithubRepositories = []*github.Repository{
4145
Name: &repoName3,
4246
HTMLURL: &repoURL3,
4347
},
48+
&github.Repository{
49+
Owner: &github.User{
50+
Login: &ownerName,
51+
},
52+
Name: &repoName4,
53+
HTMLURL: &repoURL4,
54+
Archived: &archivedFlag,
55+
},
4456
}
4557

4658
// This mocks the PullRequest service in go-github that is used in production to call the associated Github endpoint

repository/fetch-repos.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,11 +84,31 @@ func getReposByOrg(config *config.GitXargsConfig) ([]*github.Repository, error)
8484
}
8585

8686
for {
87+
var reposToAdd []*github.Repository
8788
repos, resp, err := config.GithubClient.Repositories.ListByOrg(context.Background(), config.GithubOrg, opt)
8889
if err != nil {
8990
return allRepos, errors.WithStackTrace(err)
9091
}
91-
allRepos = append(allRepos, repos...)
92+
93+
// github.RepositoryListByOrgOptions doesn't seem to be able to filter out archived repos
94+
// So re-slice the repos list if --skip-archived-repos is passed and the repository is in archived/read-only state
95+
for i, repo := range repos {
96+
if config.SkipArchivedRepos && repo.GetArchived() {
97+
logger.WithFields(logrus.Fields{
98+
"Name": repo.GetFullName(),
99+
}).Debug("Skipping archived repository")
100+
101+
// Track repos to skip because of archived status for our final run report
102+
config.Stats.TrackSingle(stats.ReposArchivedSkipped, repo)
103+
104+
reposToAdd = append(repos[:i], repos[i+1:]...)
105+
} else {
106+
reposToAdd = repos
107+
}
108+
}
109+
110+
allRepos = append(allRepos, reposToAdd...)
111+
92112
if resp.NextPage == 0 {
93113
break
94114
}

repository/fetch-repos_test.go

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func TestGetFileDefinedRepos(t *testing.T) {
3434

3535
githubRepos, reposLookupErr := getFileDefinedRepos(config.GithubClient, allowedRepos, config.Stats)
3636

37-
assert.Equal(t, len(githubRepos), len(mocks.MockGithubRepositories))
37+
assert.Equal(t, len(githubRepos), len(allowedRepos))
3838
assert.NoError(t, reposLookupErr)
3939
}
4040

@@ -51,3 +51,18 @@ func TestGetReposByOrg(t *testing.T) {
5151
assert.Equal(t, len(githubRepos), len(mocks.MockGithubRepositories))
5252
assert.NoError(t, reposByOrgLookupErr)
5353
}
54+
55+
// TestSkipArchivedRepos ensures that you can filter out archived repositories
56+
func TestSkipArchivedRepos(t *testing.T) {
57+
t.Parallel()
58+
59+
config := config.NewGitXargsTestConfig()
60+
config.GithubOrg = "gruntwork-io"
61+
config.SkipArchivedRepos = true
62+
config.GithubClient = mocks.ConfigureMockGithubClient()
63+
64+
githubRepos, reposByOrgLookupErr := getReposByOrg(config)
65+
66+
assert.Equal(t, len(githubRepos), len(mocks.MockGithubRepositories) -1)
67+
assert.NoError(t, reposByOrgLookupErr)
68+
}

stats/stats.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ const (
1414
DryRunSet types.Event = "dry-run-set-no-changes-made"
1515
// ReposSelected denotes all the repositories that were targeted for processing by this tool AFTER filtering was applied to determine valid repos
1616
ReposSelected types.Event = "repos-selected-pre-processing"
17+
// ReposArchivedSkipped denotes all the repositories that were skipped from the list of repos to clone because the skip-archived-repos was set to true
18+
ReposArchivedSkipped types.Event = "repos-archived-skipped"
1719
// TargetBranchNotFound denotes the special branch used by this tool to make changes on was not found on lookup, suggesting it should be created
1820
TargetBranchNotFound types.Event = "target-branch-not-found"
1921
// TargetBranchAlreadyExists denotes the special branch used by this tool was already found (so it was likely already created by a previous run)
@@ -70,6 +72,7 @@ var allEvents = []types.AnnotatedEvent{
7072
{Event: FetchedViaGithubAPI, Description: "Repos successfully fetched via Github API"},
7173
{Event: DryRunSet, Description: "Repos that were not modified in any way because this was a dry-run"},
7274
{Event: ReposSelected, Description: "All repos that were targeted for processing AFTER filtering missing / malformed repos"},
75+
{Event: ReposArchivedSkipped, Description: "All repos that were filtered out with the --skip-archived-repos flag"},
7376
{Event: TargetBranchNotFound, Description: "Repos whose target branch was not found"},
7477
{Event: TargetBranchAlreadyExists, Description: "Repos whose target branch already existed"},
7578
{Event: TargetBranchLookupErr, Description: "Repos whose target branches could not be looked up due to an API error"},
@@ -96,6 +99,7 @@ var allEvents = []types.AnnotatedEvent{
9699
// RunStats will be a stats-tracker class that keeps score of which repos were touched, which were considered for update, which had branches made, PRs made, which were missing workflows or contexts, or had out of date workflows syntax values, etc
97100
type RunStats struct {
98101
repos map[types.Event][]*github.Repository
102+
skippedArchivedRepos map[types.Event][]*github.Repository
99103
pulls map[string]string
100104
command []string
101105
fileProvidedRepos []*types.AllowedRepo
@@ -112,6 +116,7 @@ func NewStatsTracker() *RunStats {
112116

113117
t := &RunStats{
114118
repos: make(map[types.Event][]*github.Repository),
119+
skippedArchivedRepos: make(map[types.Event][]*github.Repository),
115120
pulls: make(map[string]string),
116121
command: []string{},
117122
fileProvidedRepos: fileProvidedRepos,
@@ -134,6 +139,11 @@ func (r *RunStats) GetRepos() map[types.Event][]*github.Repository {
134139
return r.repos
135140
}
136141

142+
// GetSkippedArchivedRepos returns the inner map of events to *github.Repositories that are excluded from the targeted repos list
143+
func (r *RunStats) GetSkippedArchivedRepos() map[types.Event][]*github.Repository {
144+
return r.skippedArchivedRepos
145+
}
146+
137147
// GetPullRequests returns the inner representation of the pull requests that were opened during the lifecycle of a given run
138148
func (r *RunStats) GetPullRequests() map[string]string {
139149
return r.pulls
@@ -211,6 +221,7 @@ func (r *RunStats) TrackMultiple(event types.Event, repos []*github.Repository)
211221
func (r *RunStats) GenerateRunReport() *types.RunReport {
212222
return &types.RunReport{
213223
Repos: r.GetRepos(),
224+
SkippedRepos: r.GetSkippedArchivedRepos(),
214225
Command: r.command,
215226
RuntimeSeconds: r.GetTotalRunSeconds(), FileProvidedRepos: r.GetFileProvidedRepos(),
216227
PullRequests: r.GetPullRequests(),

types/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type ReducedRepo struct {
1717

1818
type RunReport struct {
1919
Repos map[Event][]*github.Repository
20+
SkippedRepos map[Event][]*github.Repository
2021
Command []string
2122
RuntimeSeconds int
2223
FileProvidedRepos []*AllowedRepo

0 commit comments

Comments
 (0)