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

implement go-licenses/v2 #67

Closed
wants to merge 10 commits into from
Closed

implement go-licenses/v2 #67

wants to merge 10 commits into from

Conversation

Bobgy
Copy link
Collaborator

@Bobgy Bobgy commented Jun 16, 2021

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):

@google-cla google-cla bot added the cla: yes Contributor license agreement signed (https://cla.developers.google.com) label Jun 16, 2021
@Bobgy Bobgy changed the title feat!: initial PR for go-licenses/v2 go-licenses/v2 Jun 16, 2021
@Bobgy Bobgy changed the title go-licenses/v2 implement go-licenses/v2 Jun 16, 2021
@Bobgy
Copy link
Collaborator Author

Bobgy commented Jun 16, 2021

Hi @wlynch, I finally got some time to fully clean up and migrate the tool here : )
Feedbacks welcomed!

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.

@Bobgy Bobgy mentioned this pull request Jun 17, 2021
Copy link
Contributor

@wlynch wlynch left a 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()
Copy link
Contributor

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/tools@v0.1.3/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.

Copy link
Collaborator Author

@Bobgy Bobgy Jun 19, 2021

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?

v2/deps/go_binary.go Show resolved Hide resolved
v2/deps/go_binary_test.go Show resolved Hide resolved
v2/deps/go_binary.go Show resolved Hide resolved
v2/goutils/list.go Show resolved Hide resolved
Comment on lines +122 to +124
errorArgs := []interface{}{"module", goModule.ImportPath}
errorArgs = append(errorArgs, args...)
klog.ErrorS(err, "Failed", errorArgs...)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this be equivalent?

Suggested change
errorArgs := []interface{}{"module", goModule.ImportPath}
errorArgs = append(errorArgs, args...)
klog.ErrorS(err, "Failed", errorArgs...)
klog.ErrorS(err, "Failed", "module", goModule.ImportPath, args...)

Copy link
Collaborator Author

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)
Copy link
Contributor

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.

Copy link
Collaborator Author

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
}
Copy link
Contributor

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.

Copy link
Collaborator Author

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) {
Copy link
Contributor

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?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0, good catch!

Comment on lines +49 to +54
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)
Copy link
Contributor

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.

Copy link
Collaborator Author

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?

@Bobgy
Copy link
Collaborator Author

Bobgy commented Jun 19, 2021

@wlynch thank you so much for putting time to review this thoroughly : )

I understand your concerns, so let me confirm -- we'll proceed like:

  1. We won't merge this PR. We break the PR up into smaller chunks one by one and get them reviewed. Additionally, I will create a tracking issue to track progress.

  2. For existing comments, I will address the quick ones right away, and try to triage others. I'd use P0 as merge blockers, and then P1 and P2.

How does the structure sound to you?

Co-authored-by: Billy Lynch <wlynch92@gmail.com>
@Bobgy
Copy link
Collaborator Author

Bobgy commented Jun 20, 2021

Close, in favor of #70

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes Contributor license agreement signed (https://cla.developers.google.com)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants