-
Notifications
You must be signed in to change notification settings - Fork 574
✨ Add multi-repo scanning: --repos, --org, and optional --combined output #4793
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
base: main
Are you sure you want to change the base?
Conversation
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## main #4793 +/- ##
==========================================
+ Coverage 66.80% 67.48% +0.68%
==========================================
Files 230 250 +20
Lines 16602 19367 +2765
==========================================
+ Hits 11091 13070 +1979
- Misses 4808 5422 +614
- Partials 703 875 +172 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did a partial review of a few things before I ran out of time, I need to dive more into how you build the repo list, scan repos, and present the results. Feel free to address that feedback now or wait until I have more time for full review
// TransportFactory is used to create an http.RoundTripper. It defaults to | ||
// calling NewTransport but can be overridden in tests to provide a custom | ||
// RoundTripper that redirects requests to a test server. | ||
var TransportFactory = NewTransport |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont think we need this. Most mocking in this repo is done with dependency injection, so whatever uses the round tripper should have one injected in a test.
E.g.
scorecard/clients/githubrepo/webhook_test.go
Lines 28 to 42 in 2864c76
type stubTripper struct { | |
responsePath string | |
} | |
func (s stubTripper) RoundTrip(_ *http.Request) (*http.Response, error) { | |
f, err := os.Open(s.responsePath) | |
if err != nil { | |
return nil, err | |
} | |
return &http.Response{ | |
Status: "200 OK", | |
StatusCode: http.StatusOK, | |
Body: f, | |
}, nil | |
} |
scorecard/clients/gitlabrepo/search_test.go
Lines 172 to 176 in 2864c76
httpClient := &http.Client{ | |
Transport: stubTripper{ | |
responsePath: tt.responsePath, | |
}, | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And then when you make a client you can still call this by default, but if the test gives you a round tripper you use it instead.
scorecard/clients/githubrepo/client.go
Lines 347 to 359 in 44f521d
func NewRepoClient(ctx context.Context, opts ...Option) (clients.RepoClient, error) { | |
var config repoClientConfig | |
for _, option := range opts { | |
if err := option(&config); err != nil { | |
return nil, err | |
} | |
} | |
if config.rt == nil { | |
logger := log.NewLogger(log.DefaultLevel) | |
config.rt = roundtripper.NewTransport(ctx, logger) | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was also changed; still not sure if my implementation is the expected one.
go.mod
Outdated
github.com/google/go-github/v53 v53.2.0 | ||
github.com/google/go-github/v60 v60.0.0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why introduce a second version when the function you need is available in the version we already use?
https://pkg.go.dev/github.com/google/go-github/v53/github#RepositoriesService.ListByOrg
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right. Changed. ✅
clients/githubrepo/org.go
Outdated
// ListOrgRepos lists all non-archived repositories for a GitHub organization. | ||
func ListOrgRepos(ctx context.Context, org string) ([]string, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we move org.go
and org_test.go
to cmd/internal/org/org.go
and cmd/internal/org/org_test.go
.
Any code we can keep out of the public is good.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not quite sure I understand this. But if you think that's the correct place for it, I'll move it... My logic was to keep all related github code together....
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved ✅
cmd/internal/org/org.go
Outdated
// parseOrgName extracts the organization name from a GitHub URL or returns the input if already an org name. | ||
func parseOrgName(input string) string { | ||
// Remove "github.com/" prefix if present | ||
const prefix = "github.com/" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
other Scorecard arguments accept URLs with schmes. So I would want you to test this works with something like:
http://github.com/gabrielsoltz
https://github.com/gabrielsoltz
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Improved and added test cases ✅
// See the License for the specific language governing permissions and | ||
// limitations under the License. | ||
|
||
// Package cmd implements Scorecard command-line. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please dont delete comments unrelated to your change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
restored ✅
cmd/root.go
Outdated
// Shared setup (unchanged) | ||
pol, err := policy.ParseFromFile(o.PolicyFile) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unchanged from what?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on the previous behavior before my change, I'll remove that comment now because I see it's confusing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Deleted that part of the comment ✅
|
||
// Read docs. | ||
checkDocs, err := docs.Read() | ||
if err != nil { | ||
return fmt.Errorf("cannot read yaml file: %w", err) | ||
} | ||
|
||
var requiredRequestTypes []checker.RequestType | ||
// if local option not set add file based | ||
if o.Local != "" { | ||
requiredRequestTypes = append(requiredRequestTypes, checker.FileBased) | ||
} | ||
// if commit option set to anything other than HEAD add commit based | ||
if !strings.EqualFold(o.Commit, clients.HeadSHA) { | ||
requiredRequestTypes = append(requiredRequestTypes, checker.CommitBased) | ||
} | ||
// this call to policy is different from the one in scorecard.Run | ||
// this one is concerned with a policy file, while the scorecard.Run call is | ||
// more concerned with the supported request types | ||
|
||
enabledChecks, err := policy.GetEnabled(pol, o.Checks(), requiredRequestTypes) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please dont delete comments unrelated to your change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for this, I restored them ✅
Also, this repo requires DCO. Lines 10 to 14 in 2864c76
For instructions on how to fix it: |
Signed-off-by: Gabriel Alejandro Soltz <[email protected]>
Signed-off-by: Gabriel Alejandro Soltz <[email protected]>
Signed-off-by: Gabriel Alejandro Soltz <[email protected]>
Signed-off-by: Gabriel Alejandro Soltz <[email protected]>
Signed-off-by: Gabriel Alejandro Soltz <[email protected]>
Signed-off-by: Gabriel Alejandro Soltz <[email protected]>
Signed-off-by: Gabriel Alejandro Soltz <[email protected]>
Signed-off-by: Gabriel Alejandro Soltz <[email protected]>
Signed-off-by: Gabriel Alejandro Soltz <[email protected]>
3ab90d2
to
75c4984
Compare
Signed-off-by: Gabriel Alejandro Soltz <[email protected]>
Signed-off-by: Gabriel Alejandro Soltz <[email protected]>
Signed-off-by: Gabriel Alejandro Soltz <[email protected]>
Signed-off-by: Gabriel Alejandro Soltz <[email protected]>
Signed-off-by: Gabriel Alejandro Soltz <[email protected]>
Signed-off-by: Gabriel Alejandro Soltz <[email protected]>
Just wanted to update you that this is still on my radar! We are trying to cut a 5.3.0 release this week, so my attention has been on that. But once that's cut this week, I will make time to finish. my review, and happy to cut a 5.4.0 or 5.3.1 shortly after |
What kind of change does this PR introduce?
This PR introduces support for scanning multiple repositories in a single invocation, while preserving existing behavior by default. The only user-visible change for single-repo usage is a small improvement to the “Starting/Finished” banners (they now include the repo label).
New flags
--repos
: comma-separated list of repositories to scan (e.g., --repos=owner1/repo1,github.com/owner2/repo2).--org
: GitHub organization handle (e.g., --org=github.com/ossf or ossf).--combined
: after scanning N repos, print a single combined table with all checks together with two new extra columns, REPO and AGGREGATED SCORE.Precedence when multiple inputs are provided: --repos ➜ --org ➜ --local ➜ --repo (or repo resolved from package managers).
UX / output changes
Before (single repo):
Now (single or multi-repo):
This makes it obvious which repo each check belongs to—especially important when scanning many repos.
No other output changes occur unless
--combined
is used.When
--combined
is set, a final COMBINED RESULTS section is emitted after all per-repo results, e.g.:Implementation details
buildRepoURLs(ctx, *options.Options) ([]string, error)
:Examples
Scan a list
Scan all non-archived repos in an org
Aggregate across many repos
PR title follows the guidelines defined in our pull request documentation
Tests for the changes have been added (for bug fixes/features)
Which issue(s) this PR fixes
Fixes #4792
Special notes for your reviewer
Does this PR introduce a user-facing change?
For user-facing changes, please add a concise, human-readable release note to
the
release-note
(In particular, describe what changes users might need to make in their
application as a result of this pull request.)