diff --git a/staging/src/k8s.io/code-generator/cmd/deepcopy-gen/gen/run.go b/staging/src/k8s.io/code-generator/cmd/deepcopy-gen/gen/run.go new file mode 100644 index 0000000000000..bbc228baf66fe --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/deepcopy-gen/gen/run.go @@ -0,0 +1,46 @@ +/* + Copyright 2023 The Kubernetes Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except In compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to In writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package gen + +import ( + "github.com/spf13/pflag" + generatorargs "k8s.io/code-generator/cmd/deepcopy-gen/args" + "k8s.io/gengo/examples/deepcopy-gen/generators" +) + +// DeepCopy runs the deepcopy-gen with given flagset and args. +func DeepCopy(fs *pflag.FlagSet, args []string) error { + genericArgs, customArgs := generatorargs.NewDefaults() + + genericArgs.AddFlags(fs) + customArgs.AddFlags(fs) + + if err := fs.Parse(args); err != nil { + return err + } + + if err := generatorargs.Validate(genericArgs); err != nil { + return err + } + + // Run it. + return genericArgs.Execute( + generators.NameSystems(), + generators.DefaultNameSystem(), + generators.Packages, + ) +} diff --git a/staging/src/k8s.io/code-generator/cmd/deepcopy-gen/main.go b/staging/src/k8s.io/code-generator/cmd/deepcopy-gen/main.go index 5622c1a1bebdf..8071540092769 100644 --- a/staging/src/k8s.io/code-generator/cmd/deepcopy-gen/main.go +++ b/staging/src/k8s.io/code-generator/cmd/deepcopy-gen/main.go @@ -47,34 +47,19 @@ package main import ( "flag" + "k8s.io/code-generator/cmd/defaulter-gen/gen" + "os" "github.com/spf13/pflag" - "k8s.io/gengo/examples/deepcopy-gen/generators" "k8s.io/klog/v2" - - generatorargs "k8s.io/code-generator/cmd/deepcopy-gen/args" ) func main() { klog.InitFlags(nil) - genericArgs, customArgs := generatorargs.NewDefaults() - - genericArgs.AddFlags(pflag.CommandLine) - customArgs.AddFlags(pflag.CommandLine) flag.Set("logtostderr", "true") pflag.CommandLine.AddGoFlagSet(flag.CommandLine) - pflag.Parse() - - if err := generatorargs.Validate(genericArgs); err != nil { - klog.Fatalf("Error: %v", err) - } - // Run it. - if err := genericArgs.Execute( - generators.NameSystems(), - generators.DefaultNameSystem(), - generators.Packages, - ); err != nil { + if err := gen.Defaulter(pflag.CommandLine, os.Args[1:]); err != nil { klog.Fatalf("Error: %v", err) } klog.V(2).Info("Completed successfully.") diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/gen/run.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/gen/run.go new file mode 100644 index 0000000000000..0087c4b156238 --- /dev/null +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/gen/run.go @@ -0,0 +1,46 @@ +/* + Copyright 2023 The Kubernetes Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except In compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to In writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package gen + +import ( + "github.com/spf13/pflag" + generatorargs "k8s.io/code-generator/cmd/defaulter-gen/args" + "k8s.io/gengo/examples/defaulter-gen/generators" +) + +// Defaulter runs the defaulter-gen with given flagset and args. +func Defaulter(fs *pflag.FlagSet, args []string) error { + genericArgs, customArgs := generatorargs.NewDefaults() + + genericArgs.AddFlags(fs) + customArgs.AddFlags(fs) + + if err := fs.Parse(args); err != nil { + return err + } + + if err := generatorargs.Validate(genericArgs); err != nil { + return err + } + + // Run it. + return genericArgs.Execute( + generators.NameSystems(), + generators.DefaultNameSystem(), + generators.Packages, + ) +} diff --git a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/main.go b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/main.go index 23d5d3271bb07..9841b607c1822 100644 --- a/staging/src/k8s.io/code-generator/cmd/defaulter-gen/main.go +++ b/staging/src/k8s.io/code-generator/cmd/defaulter-gen/main.go @@ -43,34 +43,19 @@ package main import ( "flag" + "k8s.io/code-generator/cmd/defaulter-gen/gen" + "os" "github.com/spf13/pflag" - "k8s.io/gengo/examples/defaulter-gen/generators" "k8s.io/klog/v2" - - generatorargs "k8s.io/code-generator/cmd/defaulter-gen/args" ) func main() { klog.InitFlags(nil) - genericArgs, customArgs := generatorargs.NewDefaults() - - genericArgs.AddFlags(pflag.CommandLine) - customArgs.AddFlags(pflag.CommandLine) flag.Set("logtostderr", "true") pflag.CommandLine.AddGoFlagSet(flag.CommandLine) - pflag.Parse() - - if err := generatorargs.Validate(genericArgs); err != nil { - klog.Fatalf("Error: %v", err) - } - // Run it. - if err := genericArgs.Execute( - generators.NameSystems(), - generators.DefaultNameSystem(), - generators.Packages, - ); err != nil { + if err := gen.Defaulter(pflag.CommandLine, os.Args[1:]); err != nil { klog.Fatalf("Error: %v", err) } klog.V(2).Info("Completed successfully.") diff --git a/staging/src/k8s.io/code-generator/codegen.go b/staging/src/k8s.io/code-generator/codegen.go index 6d1f7fff668e9..b475ef728b959 100644 --- a/staging/src/k8s.io/code-generator/codegen.go +++ b/staging/src/k8s.io/code-generator/codegen.go @@ -17,13 +17,17 @@ package main import ( + "flag" "k8s.io/code-generator/internal/codegen" "k8s.io/code-generator/internal/codegen/execution" + "k8s.io/klog/v2" ) var options []execution.Option func main() { + klog.InitFlags(nil) + _ = flag.Set("logtostderr", "true") codegen.Run(options...) } diff --git a/staging/src/k8s.io/code-generator/internal/codegen/command/client/cmd.go b/staging/src/k8s.io/code-generator/internal/codegen/command/client/cmd.go index 4df75d59c00cc..a98f404f9884c 100644 --- a/staging/src/k8s.io/code-generator/internal/codegen/command/client/cmd.go +++ b/staging/src/k8s.io/code-generator/internal/codegen/command/client/cmd.go @@ -17,6 +17,7 @@ package client import ( + goflag "flag" "github.com/spf13/pflag" "k8s.io/code-generator/internal/codegen/execution" "k8s.io/code-generator/pkg/codegen/client" @@ -39,7 +40,8 @@ func (c Command) Matches(ex *execution.Vars) bool { func (c Command) Run(ex *execution.Vars) { args := &client.Args{} - fs := pflag.NewFlagSet(ex.Args[0], pflag.ExitOnError) + fs := pflag.NewFlagSet(ex.Args[0], pflag.ContinueOnError) + fs.AddGoFlagSet(goflag.CommandLine) // make sure we get the klog flags defineFlags(fs, args) if err := fs.Parse(ex.Args[1:]); err != nil { ex.PrintErrln("Error parsing arguments:", err) diff --git a/staging/src/k8s.io/code-generator/internal/codegen/command/client/cmd_test.go b/staging/src/k8s.io/code-generator/internal/codegen/command/client/cmd_test.go index c38924378d885..5fc869e613999 100644 --- a/staging/src/k8s.io/code-generator/internal/codegen/command/client/cmd_test.go +++ b/staging/src/k8s.io/code-generator/internal/codegen/command/client/cmd_test.go @@ -17,9 +17,11 @@ package client_test import ( + "bytes" "k8s.io/code-generator/internal/codegen/command/client" "k8s.io/code-generator/internal/codegen/execution" pkgclient "k8s.io/code-generator/pkg/codegen/client" + "math" "testing" ) @@ -64,6 +66,31 @@ func TestCommandRun(t *testing.T) { } } +func TestCommandRunInvalidArgs(t *testing.T) { + t.Parallel() + var errstream bytes.Buffer + retcode := math.MinInt32 + gen := &testGen{} + cmd := client.Command{Gen: gen} + ex := execution.New(func(v *execution.Vars) { + v.Args = []string{cmd.Name(), "--input-foo", "foo"} + v.Err = &errstream + v.Exit = func(code int) { + retcode = code + } + }) + cmd.Run(ex) + if len(gen.calls) != 0 { + t.Errorf("expected gen to be called zero times, got %d", len(gen.calls)) + } + if retcode != 8 { + t.Errorf("expected exit code 8, got %d", retcode) + } + if errstream.String() == "" { + t.Errorf("expected error message to be printed") + } +} + type testGen struct { calls []pkgclient.Args } diff --git a/staging/src/k8s.io/code-generator/internal/codegen/command/helpers/cmd.go b/staging/src/k8s.io/code-generator/internal/codegen/command/helpers/cmd.go index 4a23607d9ccf8..ee34c32426635 100644 --- a/staging/src/k8s.io/code-generator/internal/codegen/command/helpers/cmd.go +++ b/staging/src/k8s.io/code-generator/internal/codegen/command/helpers/cmd.go @@ -17,6 +17,7 @@ package helpers import ( + goflag "flag" "github.com/spf13/pflag" "k8s.io/code-generator/internal/codegen/execution" "k8s.io/code-generator/pkg/codegen/helpers" @@ -38,13 +39,15 @@ func (c Command) Matches(ex *execution.Vars) bool { func (c Command) Run(ex *execution.Vars) { args := &helpers.Args{} - fs := pflag.NewFlagSet(ex.Args[0], pflag.ExitOnError) + fs := pflag.NewFlagSet(ex.Args[0], pflag.ContinueOnError) + fs.AddGoFlagSet(goflag.CommandLine) // make sure we get the klog flags defineFlags(fs, args) if err := fs.Parse(ex.Args[1:]); err != nil { - ex.PrintErrln("Error parsing arguments:", err) - ex.PrintErrln() - ex.PrintErrln(c.Help()) - ex.Exit(10) + c.printErrorWithUsage(ex, "Error parsing arguments:", err) + return + } + if err := args.Validate(); err != nil { + c.printErrorWithUsage(ex, err) return } gen := c.createOrGetGenerator(ex) @@ -55,6 +58,13 @@ func (c Command) Run(ex *execution.Vars) { } } +func (c Command) printErrorWithUsage(ex *execution.Vars, i ...any) { + ex.PrintErrln(i...) + ex.PrintErrln() + ex.PrintErrln(c.Help()) + ex.Exit(10) +} + func (c Command) createOrGetGenerator(pr printer.Printer) Generator { if c.Gen == nil { return &helpers.Generator{Printer: pr} diff --git a/staging/src/k8s.io/code-generator/internal/codegen/command/helpers/cmd_test.go b/staging/src/k8s.io/code-generator/internal/codegen/command/helpers/cmd_test.go index 7f7ba642625c0..a151ee927346e 100644 --- a/staging/src/k8s.io/code-generator/internal/codegen/command/helpers/cmd_test.go +++ b/staging/src/k8s.io/code-generator/internal/codegen/command/helpers/cmd_test.go @@ -17,9 +17,11 @@ package helpers_test import ( + "bytes" "k8s.io/code-generator/internal/codegen/command/helpers" "k8s.io/code-generator/internal/codegen/execution" pkghelpers "k8s.io/code-generator/pkg/codegen/helpers" + "math" "testing" ) @@ -64,6 +66,31 @@ func TestCommandRun(t *testing.T) { } } +func TestCommandRunInvalidArgs(t *testing.T) { + t.Parallel() + var errstream bytes.Buffer + retcode := math.MinInt32 + gen := &testGen{} + cmd := helpers.Command{Gen: gen} + ex := execution.New(func(v *execution.Vars) { + v.Args = []string{cmd.Name(), "--input-foo", "foo"} + v.Err = &errstream + v.Exit = func(code int) { + retcode = code + } + }) + cmd.Run(ex) + if len(gen.calls) != 0 { + t.Errorf("expected gen to be called zero times, got %d", len(gen.calls)) + } + if retcode != 10 { + t.Errorf("expected exit code 10, got %d", retcode) + } + if errstream.String() == "" { + t.Errorf("expected error message to be printed") + } +} + type testGen struct { calls []pkghelpers.Args } diff --git a/staging/src/k8s.io/code-generator/internal/codegen/command/openapi/cmd.go b/staging/src/k8s.io/code-generator/internal/codegen/command/openapi/cmd.go index 55a3db7293cb5..7b86e59990fdc 100644 --- a/staging/src/k8s.io/code-generator/internal/codegen/command/openapi/cmd.go +++ b/staging/src/k8s.io/code-generator/internal/codegen/command/openapi/cmd.go @@ -17,6 +17,7 @@ package openapi import ( + goflag "flag" "github.com/spf13/pflag" "k8s.io/code-generator/internal/codegen/execution" "k8s.io/code-generator/pkg/codegen/openapi" @@ -39,7 +40,8 @@ func (c Command) Matches(ex *execution.Vars) bool { func (c Command) Run(ex *execution.Vars) { args := &openapi.Args{} - fs := pflag.NewFlagSet(ex.Args[0], pflag.ExitOnError) + fs := pflag.NewFlagSet(ex.Args[0], pflag.ContinueOnError) + fs.AddGoFlagSet(goflag.CommandLine) // make sure we get the klog flags defineFlags(fs, args) if err := fs.Parse(ex.Args[1:]); err != nil { ex.PrintErrln("Error parsing arguments:", err) diff --git a/staging/src/k8s.io/code-generator/internal/codegen/command/openapi/cmd_test.go b/staging/src/k8s.io/code-generator/internal/codegen/command/openapi/cmd_test.go index 9def39a3c1f5f..9d362ee95759f 100644 --- a/staging/src/k8s.io/code-generator/internal/codegen/command/openapi/cmd_test.go +++ b/staging/src/k8s.io/code-generator/internal/codegen/command/openapi/cmd_test.go @@ -17,9 +17,11 @@ package openapi_test import ( + "bytes" "k8s.io/code-generator/internal/codegen/command/openapi" "k8s.io/code-generator/internal/codegen/execution" pkgopenapi "k8s.io/code-generator/pkg/codegen/openapi" + "math" "testing" ) @@ -64,6 +66,31 @@ func TestCommandRun(t *testing.T) { } } +func TestCommandRunInvalidArgs(t *testing.T) { + t.Parallel() + var errstream bytes.Buffer + retcode := math.MinInt32 + gen := &testGen{} + cmd := openapi.Command{Gen: gen} + ex := execution.New(func(v *execution.Vars) { + v.Args = []string{cmd.Name(), "--input-foo", "foo"} + v.Err = &errstream + v.Exit = func(code int) { + retcode = code + } + }) + cmd.Run(ex) + if len(gen.calls) != 0 { + t.Errorf("expected gen to be called zero times, got %d", len(gen.calls)) + } + if retcode != 12 { + t.Errorf("expected exit code 12, got %d", retcode) + } + if errstream.String() == "" { + t.Errorf("expected error message to be printed") + } +} + type testGen struct { calls []pkgopenapi.Args } diff --git a/staging/src/k8s.io/code-generator/pkg/codegen/helpers/args.go b/staging/src/k8s.io/code-generator/pkg/codegen/helpers/args.go index a51aacf25e86a..cd5940117c85e 100644 --- a/staging/src/k8s.io/code-generator/pkg/codegen/helpers/args.go +++ b/staging/src/k8s.io/code-generator/pkg/codegen/helpers/args.go @@ -16,10 +16,72 @@ package helpers +import ( + "errors" + "os" + "path" + "path/filepath" +) + +var ( + // ErrInputPkgRootIsRequired is returned when the input package root is not specified. + ErrInputPkgRootIsRequired = errors.New("--input-pkg-root is required to be a valid package") + // ErrOutputBaseIsRequired is returned when the output base is not specified. + ErrOutputBaseIsRequired = errors.New("--output-base is required to be a valid directory") + // ErrBoilerplateIsntReadable is returned when the boilerplate file is not readable. + ErrBoilerplateIsntReadable = errors.New("--boilerplate needs to point to a readable file") +) + // Args are the arguments for the helper generator. type Args struct { InputPkgRoot string `doc:"The root package under which to search for files which request code to be generated. This must be Go package syntax, e.g. \"k8s.io/foo/bar\"."` OutputBase string `doc:"The root directory under which to emit code. The concatenation of + must be valid."` Boilerplate string `doc:"An optional override for the header file to insert into generated files."` ExtraPeerDirs []string `doc:"An optional list (this flag may be specified multiple times) of \"extra\" directories to consider during conversion generation."` + + // root is the absolute path to the output base + input package root. + root string +} + +// Validate the arguments. +func (a *Args) Validate() error { + if len(a.InputPkgRoot) == 0 { + return ErrInputPkgRootIsRequired + } + if len(a.OutputBase) == 0 || !isDir(a.OutputBase) { + return ErrOutputBaseIsRequired + } + if fp, err := filepath.Abs(a.OutputBase); err != nil { + return err + } else { + a.OutputBase = fp + } + root := path.Join(a.OutputBase, a.InputPkgRoot) + if !isDir(root) { + return ErrInputPkgRootIsRequired + + } + a.root = root + if len(a.Boilerplate) > 0 && !isReadable(a.Boilerplate) { + return ErrBoilerplateIsntReadable + } + if fp, err := filepath.Abs(a.Boilerplate); err != nil { + return err + } else { + a.Boilerplate = fp + } + return nil +} + +func isReadable(path string) bool { + _, err := os.Stat(path) + return err == nil +} + +func isDir(path string) bool { + info, err := os.Stat(path) + if err != nil { + return false + } + return info.IsDir() } diff --git a/staging/src/k8s.io/code-generator/pkg/codegen/helpers/deepcopy.go b/staging/src/k8s.io/code-generator/pkg/codegen/helpers/deepcopy.go new file mode 100644 index 0000000000000..1401e3acd288f --- /dev/null +++ b/staging/src/k8s.io/code-generator/pkg/codegen/helpers/deepcopy.go @@ -0,0 +1,162 @@ +/* + Copyright 2023 The Kubernetes Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except In compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to In writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package helpers + +import ( + goflag "flag" + "fmt" + "github.com/spf13/pflag" + "k8s.io/code-generator/cmd/deepcopy-gen/gen" + "k8s.io/code-generator/pkg/osbin/git" + "k8s.io/code-generator/pkg/osbin/golang" + "k8s.io/klog/v2" + "os" + "path" +) + +func generateDeepCopy(args *Args) error { + pkgs, err := collectDeepCopyPackages(args) + if err != nil { + return err + } + klog.V(2).Infof("Found %d packages with deepcopy-gen tags", + len(pkgs)) + if len(pkgs) >= 0 { + if err = deleteDeepCopyHelpers(args); err != nil { + return err + } + } + return generateDeepCopyHelpers(args, pkgs) +} + +func deleteDeepCopyHelpers(args *Args) error { + pathspec := fmt.Sprintf(":(glob)%s/**/zz_generated.deepcopy.go", args.root) + if genFiles, err := git.Find(pathspec); err != nil { + return err + } else { + klog.V(2).Infof("Deleting %d existing deepcopy helpers", + len(genFiles)) + for _, file := range genFiles { + if _, oserr := os.Stat(file); oserr != nil && os.IsNotExist(oserr) { + continue + } + klog.V(4).Infof("Deleting %s", file) + if err = os.Remove(file); err != nil { + return err + } + } + } + return nil +} + +func generateDeepCopyHelpers(args *Args, pkgs []string) error { + if len(pkgs) == 0 { + return nil + } + klog.V(2).Infof("Generating deepcopy helpers for %d packages", len(pkgs)) + + klog.V(2) + dcargs := []string{ + "-O", "zz_generated.deepcopy", + "--output-base", args.OutputBase, + } + if args.Boilerplate != "" { + dcargs = append(dcargs, "--go-header-file", args.Boilerplate) + } + for _, pkg := range pkgs { + dcargs = append(dcargs, "--input-dirs", pkg) + } + fs := pflag.NewFlagSet("deepcopy-gen", pflag.ContinueOnError) + fs.AddGoFlagSet(goflag.CommandLine) // make sure we get the klog flags + klog.V(2).Infof("Running deepcopy-gen with args %q", dcargs) + return withinDirectory(args.root, func() error { + return asBinary("deepcopy-gen", func() error { + return gen.DeepCopy(fs, dcargs) + }) + }) +} + +func collectDeepCopyPackages(args *Args) ([]string, error) { + var inputPkgs = make(map[string]bool, 1) + if files, err := git.Grep( + "+k8s:deepcopy-gen=", + fmt.Sprintf(":(glob)%s/**/*.go", args.root), + git.WithList, + ); err != nil { + return nil, err + } else { + klog.V(3).Infof("Found %d files with deepcopy-gen tags", + len(files)) + + for _, file := range files { + if p, errr := resolvePackage(file); errr != nil { + klog.Errorf("Error finding package for %s: %s", file, errr) + return nil, errr + } else { + klog.V(4).Infof("Found package %s", p) + inputPkgs[p] = true + } + } + } + var pkgs []string + for p := range inputPkgs { + pkgs = append(pkgs, p) + } + return pkgs, nil +} + +func resolvePackage(file string) (string, error) { + dir := path.Dir(file) + if wd, err := os.Getwd(); err != nil { + return "", err + } else { + defer os.Chdir(wd) + } + if err := os.Chdir(dir); err != nil { + return "", err + } + if p, err := golang.PackageOf("."); err != nil { + klog.Errorf("Error finding package for %s: %s", dir, err) + return "", err + } else { + klog.V(4).Infof("Found %s", p) + return p, nil + } +} + +func withinDirectory(dir string, fn func() error) error { + if wd, err := os.Getwd(); err != nil { + return err + } else { + defer func() { + _ = os.Chdir(wd) + }() + } + if err := os.Chdir(dir); err != nil { + return err + } + return fn() +} + +func asBinary(binName string, fn func() error) error { + curr := os.Args[0] + defer func() { + os.Args[0] = curr + }() + os.Args[0] = binName + return fn() +} diff --git a/staging/src/k8s.io/code-generator/pkg/codegen/helpers/gen.go b/staging/src/k8s.io/code-generator/pkg/codegen/helpers/gen.go index 3a1696a935116..54404853ede26 100644 --- a/staging/src/k8s.io/code-generator/pkg/codegen/helpers/gen.go +++ b/staging/src/k8s.io/code-generator/pkg/codegen/helpers/gen.go @@ -17,8 +17,9 @@ package helpers import ( - "errors" "k8s.io/code-generator/pkg/printer" + "k8s.io/klog/v2" + "os" ) type Generator struct { @@ -26,6 +27,16 @@ type Generator struct { } func (g *Generator) Generate(args *Args) error { - // TODO: implement me - return errors.New("not yet implemented") + // These tools all assume out-dir == in-dir. + if err := os.MkdirAll(args.root, 0x755); err != nil { + return err + } + klog.V(2).Infof("Generating helpers for %s in %s", + args.InputPkgRoot, args.root) + + if err := generateDeepCopy(args); err != nil { + return err + } + + return nil } diff --git a/staging/src/k8s.io/code-generator/pkg/flags.go b/staging/src/k8s.io/code-generator/pkg/flags.go new file mode 100644 index 0000000000000..0ef775ffaf368 --- /dev/null +++ b/staging/src/k8s.io/code-generator/pkg/flags.go @@ -0,0 +1,28 @@ +/* + Copyright 2023 The Kubernetes Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except In compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to In writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package pkg + +import "os" + +// Verbose returns the value of the KUBE_VERBOSE environment variable. +func Verbose() string { + v := os.Getenv("KUBE_VERBOSE") + if v == "" { + v = "0" + } + return v +} diff --git a/staging/src/k8s.io/code-generator/pkg/osbin/git/find.go b/staging/src/k8s.io/code-generator/pkg/osbin/git/find.go new file mode 100644 index 0000000000000..476b2e1f9ad39 --- /dev/null +++ b/staging/src/k8s.io/code-generator/pkg/osbin/git/find.go @@ -0,0 +1,64 @@ +/* + Copyright 2023 The Kubernetes Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except In compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to In writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package git + +import ( + "io" + "k8s.io/klog/v2" + "os" + "os/exec" + "strings" +) + +// FindOpt is a functional option for the Find func. +type FindOpt func(*FindOpts) + +// FindOpts are options for the Find func. +type FindOpts struct { + Err io.Writer +} + +// Find runs git ls-files with the given options, and returns the output or +// the exit error. +// +// TODO: Consider rewriting this in native Go. +func Find(pathspec string, opts ...FindOpt) ([]string, error) { + o := &FindOpts{} + for _, opt := range opts { + opt(o) + } + args := []string{ + // Similar to find but faster and easier to understand. We want to + // include modified and untracked files because this might be running + // against code which is not tracked by git yet. + "ls-files", + "--cached", "--modified", "--others", + "--exclude-standard", "-z", + "--", pathspec, + } + cmd := exec.Command("git", args...) + if o.Err != nil { + cmd.Stderr = o.Err + } + cmd.Env = append(os.Environ(), "LANG=C.UTF-8") + klog.V(2).Infof("Running: %q", cmd) + out, err := cmd.Output() + if err != nil { + return nil, err + } + return strings.Split(string(out), "\x00"), nil +} diff --git a/staging/src/k8s.io/code-generator/pkg/osbin/git/grep.go b/staging/src/k8s.io/code-generator/pkg/osbin/git/grep.go new file mode 100644 index 0000000000000..fba7fb62311c8 --- /dev/null +++ b/staging/src/k8s.io/code-generator/pkg/osbin/git/grep.go @@ -0,0 +1,72 @@ +/* + Copyright 2023 The Kubernetes Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except In compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to In writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package git + +import ( + "io" + "k8s.io/klog/v2" + "os" + "os/exec" + "strings" +) + +// GrepOpt is an functional option for the git grep command. +type GrepOpt func(*GrepOpts) + +// GrepOpts are options for the git grep command. +type GrepOpts struct { + List bool + Err io.Writer +} + +// WithList is a GrepOpt that sets the --files-with-matches flag. +func WithList(o *GrepOpts) { + o.List = true +} + +// Grep runs the git grep command, and returns the output or the exit error. +// +// TODO: Consider rewriting this in native Go. +func Grep(pattern, pathspec string, opts ...GrepOpt) ([]string, error) { + o := &GrepOpts{} + for _, opt := range opts { + opt(o) + } + args := []string{ + // We want to include modified and untracked files because this might be + // running against code which is not tracked by git yet. + "grep", "--no-color", "--untracked", "--null", + } + if o.List { + args = append(args, "--files-with-matches") + } + args = append(args, + "-e", pattern, + "--", pathspec, + ) + cmd := exec.Command("git", args...) + if o.Err != nil { + cmd.Stderr = o.Err + } + cmd.Env = append(os.Environ(), "LANG=C.UTF-8") + klog.V(2).Infof("Running: %q", cmd) + out, err := cmd.Output() + if err != nil { + return nil, err + } + return strings.Split(string(out), "\x00"), nil +} diff --git a/staging/src/k8s.io/code-generator/pkg/osbin/golang/packageof.go b/staging/src/k8s.io/code-generator/pkg/osbin/golang/packageof.go new file mode 100644 index 0000000000000..9d0437c1829b2 --- /dev/null +++ b/staging/src/k8s.io/code-generator/pkg/osbin/golang/packageof.go @@ -0,0 +1,39 @@ +/* + Copyright 2023 The Kubernetes Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except In compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to In writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package golang + +import ( + "errors" + "k8s.io/klog/v2" + "os" + "os/exec" + "strings" +) + +// PackageOf returns the package name of the given path. +func PackageOf(path string) (string, error) { + c := exec.Command("go", "list", "-find", path) + c.Env = append(os.Environ(), "GO111MODULE=on") + out, err := c.Output() + if err != nil { + var exitErr *exec.ExitError + errors.As(err, &exitErr) + klog.Errorf("Stderr ==> %s", exitErr.Stderr) + return "", err + } + return strings.Trim(string(out), "\n"), nil +} diff --git a/staging/src/k8s.io/code-generator/pkg/osbin/golang/packageof_test.go b/staging/src/k8s.io/code-generator/pkg/osbin/golang/packageof_test.go new file mode 100644 index 0000000000000..7c3c83bf49a5c --- /dev/null +++ b/staging/src/k8s.io/code-generator/pkg/osbin/golang/packageof_test.go @@ -0,0 +1,33 @@ +/* + Copyright 2023 The Kubernetes Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except In compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to In writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package golang_test + +import ( + "k8s.io/code-generator/pkg/osbin/golang" + "testing" +) + +func TestPackageOf(t *testing.T) { + pkg, err := golang.PackageOf(".") + if err != nil { + t.Errorf("no error expected, got %v", err) + } + want := "k8s.io/code-generator/pkg/osbin/go" + if pkg != want { + t.Errorf("Missmatch\nwant %#v\n got %#v", want, pkg) + } +}