From 737843704edb159234753fb527a309396e57e69f Mon Sep 17 00:00:00 2001 From: Srikrishna Iyer Date: Sun, 6 Sep 2026 11:41:47 +0530 Subject: [PATCH] Add support for Go build tags when loading CRD packages --- README.md | 14 ++++++++++++-- config/config.go | 1 + main.go | 1 + processor/build_tags_test.go | 25 +++++++++++++++++++++++++ processor/processor.go | 19 ++++++++++++++++--- 5 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 processor/build_tags_test.go diff --git a/README.md b/README.md index 0e63706..eb295e8 100644 --- a/README.md +++ b/README.md @@ -41,8 +41,8 @@ crd-ref-docs \ --templates-dir=templates/asciidoctor ``` -Default output mode writes all data to a single output file. -You can choose between single mode and group mode by specifying the output mode. +Default output mode writes all data to a single output file. +You can choose between single mode and group mode by specifying the output mode. In group mode, separate files are created for each API group, ensuring that the specified output path is an existing directory. ``` crd-ref-docs \ @@ -52,6 +52,16 @@ crd-ref-docs \ --output-mode=group ``` +To generate documentation for source files that rely on Go build tags (e.g. `//go:build enterprise`), pass the tags with `--build-tags`. The flag may be repeated for multiple tags. Autogenerated files (e.g. `zz_generated.deepcopy.go`) are always excluded regardless of the tags supplied: + +``` +crd-ref-docs \ + --source-path=$GOPATH/src/github.com/elastic/cloud-on-k8s/pkg/apis \ + --config=config.yaml \ + --build-tags=enterprise \ + --build-tags=premium +``` + ### Configuration Configuration options such as types and fields to exclude from the documentation can be specified using a YAML file. diff --git a/config/config.go b/config/config.go index 1ce90dd..ec22abc 100644 --- a/config/config.go +++ b/config/config.go @@ -100,6 +100,7 @@ type Flags struct { OutputMode string MaxDepth int TemplateKeyValues KeyValueFlags + BuildTags []string } func Load(flags Flags) (*Config, error) { diff --git a/main.go b/main.go index 3e5d4a6..f4f16a9 100644 --- a/main.go +++ b/main.go @@ -54,6 +54,7 @@ func main() { cmd.Flags().StringVar(&args.OutputMode, "output-mode", "single", "Output mode to generate a single file or one file per group ('group' or 'single')") cmd.Flags().IntVar(&args.MaxDepth, "max-depth", 10, "Maximum recursion level for type discovery") cmd.Flags().Var(&args.TemplateKeyValues, "template-value", "Can be used in template to pass in a version number or similar information. Example: --template-value=key1=value1 and {{ markdownTemplateValue \"k1\" }}") + cmd.Flags().StringArrayVar(&args.BuildTags, "build-tags", nil, "Go build tags to pass when loading source packages (may be specified multiple times, e.g. --build-tags=enterprise --build-tags=premium)") if err := cmd.Execute(); err != nil { os.Exit(1) diff --git a/processor/build_tags_test.go b/processor/build_tags_test.go new file mode 100644 index 0000000..c6b762e --- /dev/null +++ b/processor/build_tags_test.go @@ -0,0 +1,25 @@ +package processor + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestBuildTagsFlag(t *testing.T) { + testCases := []struct { + name string + buildTags []string + want string + }{ + {name: "no tags", buildTags: nil, want: "-tags=ignore_autogenerated"}, + {name: "single tag", buildTags: []string{"enterprise"}, want: "-tags=ignore_autogenerated,enterprise"}, + {name: "multiple tags", buildTags: []string{"enterprise", "premium"}, want: "-tags=ignore_autogenerated,enterprise,premium"}, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + require.Equal(t, tc.want, buildTagsFlag(tc.buildTags)) + }) + } +} diff --git a/processor/processor.go b/processor/processor.go index 0b5d1f6..04340f3 100644 --- a/processor/processor.go +++ b/processor/processor.go @@ -64,7 +64,7 @@ func Process(config *config.Config) ([]types.GroupVersionDetails, error) { return nil, err } // locate the packages annotated with group names - if err := p.findAPITypes(config.SourcePath); err != nil { + if err := p.findAPITypes(config.SourcePath, config.Flags.BuildTags); err != nil { return nil, fmt.Errorf("failed to find API types in directory %s:%w", config.SourcePath, err) } @@ -158,8 +158,11 @@ type processor struct { references map[string]map[string]struct{} } -func (p *processor) findAPITypes(directory string) error { - cfg := &packages.Config{Dir: directory} +func (p *processor) findAPITypes(directory string, buildTags []string) error { + cfg := &packages.Config{ + Dir: directory, + BuildFlags: []string{buildTagsFlag(buildTags)}, + } pkgs, err := loader.LoadRootsWithConfig(cfg, "./...") if err != nil { return err @@ -227,6 +230,16 @@ func (p *processor) findAPITypes(directory string) error { return nil } +// buildTagsFlag builds the "-tags" value passed to the go tool when loading +// source packages. The go tool only honors the last "-tags" flag it sees, and +// loader.LoadRootsWithConfig always prepends its own "-tags=ignore_autogenerated" +// ahead of ours, so ignore_autogenerated is folded into our own -tags value here +// to make sure it always takes effect, regardless of what tags the caller supplies. +func buildTagsFlag(buildTags []string) string { + tags := append([]string{"ignore_autogenerated"}, buildTags...) + return fmt.Sprintf("-tags=%s", strings.Join(tags, ",")) +} + func (p *processor) extractGroupVersionIfExists(collector *markers.Collector, pkg *loader.Package) *groupVersionInfo { markerValues, err := markers.PackageMarkers(collector, pkg) if err != nil {