-
Notifications
You must be signed in to change notification settings - Fork 636
Github operator build update #4305
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
Merged
benjaminjb
merged 6 commits into
CrunchyData:main
from
benjaminjb:benjb/public-operator-builds
Sep 30, 2025
Merged
Changes from 2 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
c17c6d0
Update GitHub builds
benjaminjb 4c7a633
Clean up golangci lint errors
benjaminjb 58bdb34
Apply suggestions from code review
benjaminjb 855c9fb
PR feedback
benjaminjb 7469b1e
Remove unnecessary build constraint
benjaminjb 65272e1
rm licenses/.gitignore
benjaminjb File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,4 +3,5 @@ | |
| /.git | ||
| /bin | ||
| /hack | ||
| !/hack/extract-licenses.go | ||
| !/hack/tools/queries | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,235 @@ | ||
| //go:build go1.21 | ||
|
|
||
| package main | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "context" | ||
| "encoding/csv" | ||
| "encoding/json" | ||
| "errors" | ||
| "flag" | ||
| "fmt" | ||
| "io" | ||
| "io/fs" | ||
| "os" | ||
| "os/exec" | ||
| "os/signal" | ||
| "path/filepath" | ||
| "slices" | ||
| "strings" | ||
| "syscall" | ||
| ) | ||
|
|
||
| func main() { | ||
| flags := flag.NewFlagSet(os.Args[0], flag.ExitOnError) | ||
| flags.Usage = func() { | ||
| fmt.Fprintln(flags.Output(), strings.TrimSpace(` | ||
| Usage: `+flags.Name()+` {directory} {executables...} | ||
|
|
||
| This program downloads and extracts the licenses of Go modules used to build | ||
| Go executables. | ||
|
|
||
| The first argument is a directory that will receive license files. It will be | ||
| created if it does not exist. This program will overwrite existing files but | ||
| not delete them. Remaining arguments must be Go executables. | ||
|
|
||
| Go modules are downloaded to the Go module cache which can be changed via | ||
| the environment: https://go.dev/ref/mod#module-cache`, | ||
| )) | ||
| } | ||
| if _ = flags.Parse(os.Args[1:]); flags.NArg() < 2 || slices.ContainsFunc( | ||
| os.Args, func(arg string) bool { return arg == "-help" || arg == "--help" }, | ||
| ) { | ||
| flags.Usage() | ||
| os.Exit(2) | ||
| } | ||
|
|
||
| ctx, cancel := context.WithCancel(context.Background()) | ||
| signals := make(chan os.Signal, 1) | ||
| signal.Notify(signals, os.Interrupt, syscall.SIGTERM) | ||
| go func() { <-signals; cancel() }() | ||
|
|
||
| // Create the target directory. | ||
| if err := os.MkdirAll(flags.Arg(0), 0o755); err != nil { | ||
| fmt.Fprintln(os.Stderr, err) | ||
| os.Exit(1) | ||
| } | ||
|
|
||
| // Extract module information from remaining arguments. | ||
| modules := identifyModules(ctx, flags.Args()[1:]...) | ||
|
|
||
| // Ignore packages from Crunchy Data. Most are not available in any [proxy], | ||
| // and we handle their licenses elsewhere. | ||
| // | ||
| // This is also a quick fix to avoid the [replace] directive in our projects. | ||
| // The logic below cannot handle them. Showing xxhash versus a replace: | ||
| // | ||
| // dep github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= | ||
| // dep github.com/crunchydata/postgres-operator v0.0.0-00010101000000-000000000000 | ||
| // => ./postgres-operator (devel) | ||
| // | ||
| // [proxy]: https://go.dev/ref/mod#module-proxy | ||
| // [replace]: https://go.dev/ref/mod#go-mod-file-replace | ||
| modules = slices.DeleteFunc(modules, func(s string) bool { | ||
| return strings.HasPrefix(s, "github.com/crunchydata/") | ||
| }) | ||
|
|
||
| // Download modules to the Go module cache. | ||
| directories := downloadModules(ctx, modules...) | ||
|
|
||
| // Gather license files from every module into the target directory. | ||
| for module, directory := range directories { | ||
| for _, license := range findLicenses(directory) { | ||
| relative := module + strings.TrimPrefix(license, directory) | ||
| destination := filepath.Join(flags.Arg(0), relative) | ||
|
|
||
| var data []byte | ||
| err := ctx.Err() | ||
|
|
||
| if err == nil { | ||
| err = os.MkdirAll(filepath.Dir(destination), 0o755) | ||
| } | ||
| if err == nil { | ||
| data, err = os.ReadFile(license) | ||
| } | ||
| if err == nil { | ||
| //nolint:gosec // gosec warns on permissions more open than 600 | ||
| // but we need these licenses to be readable by all | ||
| err = os.WriteFile(destination, data, 0o644) | ||
cbandy marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| if err == nil { | ||
| //nolint:forbidigo // This is an intentional print to console to inform the user | ||
| fmt.Println(license, "=>", destination) | ||
benjaminjb marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| } | ||
| if err != nil { | ||
| fmt.Fprintln(os.Stderr, err) | ||
| os.Exit(1) | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| func downloadModules(ctx context.Context, modules ...string) map[string]string { | ||
| var stdout bytes.Buffer | ||
|
|
||
| // Download modules and read their details into a series of JSON objects. | ||
| // - https://go.dev/ref/mod#go-mod-download | ||
| //nolint:gosec // Suppressing unnecessary warning re: potentially tainted inputs (G204) | ||
| cmd := exec.CommandContext(ctx, os.Getenv("GO"), append([]string{"mod", "download", "-json"}, modules...)...) | ||
benjaminjb marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if cmd.Path == "" { | ||
| cmd.Path, cmd.Err = exec.LookPath("go") | ||
| } | ||
| cmd.Stderr = os.Stderr | ||
| cmd.Stdout = &stdout | ||
| if err := cmd.Run(); err != nil { | ||
| fmt.Fprintln(os.Stderr, err) | ||
| os.Exit(cmd.ProcessState.ExitCode()) | ||
| } | ||
|
|
||
| decoder := json.NewDecoder(&stdout) | ||
| results := make(map[string]string, len(modules)) | ||
|
|
||
| // NOTE: The directory in the cache is a normalized spelling of the module path; | ||
| // ask Go for the directory; do not try to spell it yourself. | ||
| // - https://go.dev/ref/mod#module-cache | ||
| // - https://go.dev/ref/mod#module-path | ||
| for { | ||
| var module struct { | ||
| Path string `json:"path,omitempty"` | ||
| Version string `json:"version,omitempty"` | ||
| Dir string `json:"dir,omitempty"` | ||
| } | ||
| err := decoder.Decode(&module) | ||
|
|
||
| if err == nil { | ||
| results[module.Path+"@"+module.Version] = module.Dir | ||
| continue | ||
| } | ||
| if errors.Is(err, io.EOF) { | ||
| break | ||
| } | ||
|
|
||
| fmt.Fprintln(os.Stderr, err) | ||
| os.Exit(1) | ||
| } | ||
|
|
||
| return results | ||
| } | ||
|
|
||
| func findLicenses(directory string) []string { | ||
| var results []string | ||
|
|
||
| // Syft maintains a list of license filenames that began as a list maintained by | ||
| // Go. We gather a similar list by matching on "copying" and "license" filenames. | ||
| // - https://pkg.go.dev/github.com/anchore/syft@v1.3.0/internal/licenses#FileNames | ||
| // | ||
| // Ignore Go files and anything in the special "testdata" directory. | ||
| // - https://go.dev/cmd/go | ||
| err := filepath.WalkDir(directory, func(path string, d fs.DirEntry, err error) error { | ||
| if d.IsDir() && d.Name() == "testdata" { | ||
| return fs.SkipDir | ||
| } | ||
| if d.IsDir() || strings.HasSuffix(path, ".go") { | ||
| return err | ||
| } | ||
|
|
||
| lower := strings.ToLower(d.Name()) | ||
| if strings.Contains(lower, "copying") || strings.Contains(lower, "license") { | ||
| results = append(results, path) | ||
| } | ||
|
|
||
| return err | ||
| }) | ||
|
|
||
| if err != nil { | ||
| fmt.Fprintln(os.Stderr, err) | ||
| os.Exit(1) | ||
| } | ||
|
|
||
| return results | ||
| } | ||
|
|
||
| func identifyModules(ctx context.Context, executables ...string) []string { | ||
| var stdout bytes.Buffer | ||
|
|
||
| // Use `go version -m` to read the embedded module information as a text table. | ||
| // - https://go.dev/ref/mod#go-version-m | ||
| //nolint:gosec // Suppressing unnecessary warning re: potentially tainted inputs (G204) | ||
| cmd := exec.CommandContext(ctx, os.Getenv("GO"), append([]string{"version", "-m"}, executables...)...) | ||
| if cmd.Path == "" { | ||
| cmd.Path, cmd.Err = exec.LookPath("go") | ||
| } | ||
| cmd.Stderr = os.Stderr | ||
| cmd.Stdout = &stdout | ||
| if err := cmd.Run(); err != nil { | ||
| fmt.Fprintln(os.Stderr, err) | ||
| os.Exit(cmd.ProcessState.ExitCode()) | ||
| } | ||
|
|
||
| // Parse the tab-separated table without checking row lengths. | ||
| reader := csv.NewReader(&stdout) | ||
| reader.Comma = '\t' | ||
| reader.FieldsPerRecord = -1 | ||
|
|
||
| lines, _ := reader.ReadAll() | ||
| result := make([]string, 0, len(lines)) | ||
|
|
||
| for _, fields := range lines { | ||
| if len(fields) > 3 && fields[1] == "dep" { | ||
| result = append(result, fields[2]+"@"+fields[3]) | ||
| } | ||
| if len(fields) > 4 && fields[1] == "mod" && fields[4] != "" { | ||
| result = append(result, fields[2]+"@"+fields[3]) | ||
| } | ||
| } | ||
|
|
||
| // The `go version -m` command returns no information for empty files, and it | ||
| // is possible for a Go executable to have no main module and no dependencies. | ||
| if len(result) == 0 { | ||
| fmt.Fprintf(os.Stderr, "no Go modules in %v\n", executables) | ||
| os.Exit(0) | ||
| } | ||
|
|
||
| return result | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.