-
Notifications
You must be signed in to change notification settings - Fork 125
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
implement go-licenses/v2 #67
Conversation
Hi @wlynch, I finally got some time to fully clean up and migrate the tool here : ) Because the initial PR is huge, I'd hope we focus on the P0 blockers first. We can log any non-blocking items as follow up issues. |
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.
Thanks for the PR!
Unfortunately this is a very large change. I've left some initial comments as a first pass, but we'll likely need to do multiple iterations to resolve everything. Something that would greatly help me out for reviewing is if we could break this up into separate PRs. Here's some individual chunks I see that might be good places to break this apart:
- Using module cache for license source.
- Subfile license checking.
- Config file customization.
- Config file overrides.
- Single licenses.txt output.
- Licenses from binary
Thanks!
} | ||
|
||
func ListModules() ([]Module, error) { | ||
out, err := exec.Command("go", "list", "-m", "-json", "all").Output() |
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.
Prefer using https://pkg.go.dev/golang.org/x/tools over invoking the CLI.
In particular, you probably want https://pkg.go.dev/golang.org/x/[email protected]/go/packages. This is what v1 already does - the missing piece is using the Module
field to know where to access the module cache for source files.
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.
P2
Because this is already abstracted in a package, can be transparently improved with minimum side effect in the future.
After taking a quick look, https://cs.opensource.google/go/x/tools/+/refs/tags/v0.1.3:go/packages/golist.go
packages also use go list under the hood it seems.
Note that the json flag makes output programmatically parsable, so I don't worry much about using the CLI directly.
Do you have concrete scenarios this may be problematic?
errorArgs := []interface{}{"module", goModule.ImportPath} | ||
errorArgs = append(errorArgs, args...) | ||
klog.ErrorS(err, "Failed", errorArgs...) |
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.
Would this be equivalent?
errorArgs := []interface{}{"module", goModule.ImportPath} | |
errorArgs = append(errorArgs, args...) | |
klog.ErrorS(err, "Failed", errorArgs...) | |
klog.ErrorS(err, "Failed", "module", goModule.ImportPath, args...) |
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.
Unfortunately, that's a compile error, because when using args..., golang only allows passing an array, it's not supported to specify some arguments first and then the args...
} | ||
} | ||
if errorCount > 0 { | ||
return fmt.Errorf("Failed to scan licenses for %v module(s)", errorCount) |
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 is suppressing the error(s) that encountered, which is going to make it difficult for users to see what failed and if they can do anything about it. Consider including the error here.
It might also be worthwhile to try and process all modules prior to writing the output to a file, this way we don't accidentally write partial output.
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.
Note that the default logging level is warning and users will easily see the entire list of errors because I added klog logging as I encounter each error (and collect them at the same time for a summary at the end).
When I examine licenses, the partially written file is super useful, because I don't need to wait the entire command to finish.
Can you elaborate more why the partially written file can be harmful?
} | ||
klog.InfoS("Done: scan licenses of dependencies", "licenseCount", licenseCount, "moduleCount", len(goModules)) | ||
return nil | ||
} |
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 func would definitely benefit from some tests. I'd recommend breaking this logic out into it's own func that takes in a io.Writer
.
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.
P1
Agree, I started everything in this file and gradually extracted stuff out. Ideally, all the logic should not be in cmd folder.
return records, nil | ||
} | ||
|
||
func LoadLicenseDict(r io.Reader) (LicenseDict, 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.
AFAICT this isn't used?
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.
P0, good catch!
info, err := loadInfo(config.Module.Csv.Path) | ||
if err != nil { | ||
klog.ErrorS(err, "Failed: load license info csv") | ||
os.Exit(1) | ||
} | ||
err = complyWithLicenses(info, *config) |
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.
Requiring a csv to be generated and save is changing the semantic of the save
command (e.g. save
is now a 2 step process instead of 1).
I'd prefer to keep similar semantics for save
, though I think adding a flag to read from csv could be a reasonable alternative.
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.
P1
Yes, I was aware this was one of the major differences. Let me have a look at the existing behavior and see how save can be done without csv.
In all my cases, I needed to configure some overrides before actually going to the save step. I am curious why that's different for go-licenses v1. Is it because it doesn't scan the full folder?
@wlynch thank you so much for putting time to review this thoroughly : ) I understand your concerns, so let me confirm -- we'll proceed like:
How does the structure sound to you? |
Co-authored-by: Billy Lynch <[email protected]>
Close, in favor of #70 |
Closed in favor of #70
this PR is a port from https://github.com/Bobgy/go-mod-licenses
Refer to the README included for comparisons with similar tools including go-licenses v1.
This is still under development, see the roadmap & TODOs section for what needs to happen before finalizing a stable release.
This new tool should be able to resolve:
and might help the following (but needs confirmation):