func RunGenerateCRD()

in dev/tools/controllerbuilder/pkg/commands/generatetypes/generatetypescommand.go [91:193]


func RunGenerateCRD(ctx context.Context, o *GenerateCRDOptions) error {
	log := klog.FromContext(ctx)

	gv, err := schema.ParseGroupVersion(o.APIVersion)
	if err != nil {
		return fmt.Errorf("APIVersion %q is not valid: %w", o.APIVersion, err)
	}

	api, err := protoapi.LoadProto(o.GenerateOptions.ProtoSourcePath)
	if err != nil {
		return fmt.Errorf("loading proto: %w", err)
	}

	goPackage := strings.TrimSuffix(gv.Group, ".cnrm.cloud.google.com") + "/" + gv.Version

	scaffolder := &scaffold.APIScaffolder{
		BaseDir:         o.OutputAPIDirectory,
		GoPackage:       goPackage,
		Group:           gv.Group,
		Version:         gv.Version,
		PackageProtoTag: o.ServiceName,
	}
	if scaffolder.DocFileNotExist() {
		if err := scaffolder.AddDocFile(); err != nil {
			return fmt.Errorf("add doc.go file: %w", err)
		}
	}
	if scaffolder.GroupVersionFileNotExist() {
		if err := scaffolder.AddGroupVersionFile(); err != nil {
			return fmt.Errorf("add groupversion_info.go file: %w", err)
		}
	}

	typeGenerator := codegen.NewTypeGenerator(goPackage, o.OutputAPIDirectory, api)

	resourceAnnotations := make([]string, 0, len(o.Resources))
	for _, resource := range o.Resources {
		resourceProtoFullName := resource.ProtoMessageFullName(o.ServiceName)
		log.Info("visiting proto", "name", resourceProtoFullName)
		if err := typeGenerator.VisitProto(resourceProtoFullName); err != nil {
			return err
		}
		resourceAnnotations = append(resourceAnnotations, fmt.Sprintf("%s:%s", resource.Kind, resource.ProtoName))
	}

	generatedFileAnnotation := &annotations.FileAnnotation{
		Key: "+generated:types",
		Attributes: map[string][]string{
			"proto.service": {o.ServiceName},
			"krm.group":     {gv.Group},
			"krm.version":   {gv.Version},
			"resource":      resourceAnnotations,
		},
	}
	typeGenerator = typeGenerator.WithGeneratedFileAnnotation(generatedFileAnnotation)

	if err := typeGenerator.WriteVisitedMessages(); err != nil {
		return err
	}

	if err := typeGenerator.WriteOutputMessages(); err != nil {
		return err
	}

	for _, resource := range o.Resources { // A separate loop is needed to scaffold files AFTER all the visited messages have been generated.
		skipScaffold := o.SkipScaffoldFiles || resource.SkipScaffoldFiles
		if skipScaffold {
			log.Info("skipping scaffolding type, refs and identity files", "resource", resource.ProtoName)
		} else {
			if scaffolder.TypeFileExists(resource) {
				fmt.Printf("file %s already exists, skipping\n", scaffolder.PathToTypeFile(resource))
			} else {
				err := scaffolder.AddTypeFile(resource)
				if err != nil {
					return fmt.Errorf("add type file %s: %w", scaffolder.PathToTypeFile(resource), err)
				}
			}
			if scaffolder.RefsFileExist(resource) {
				fmt.Printf("file %s already exists, skipping\n", scaffolder.PathToRefsFile(resource))
			} else {
				err := scaffolder.AddRefsFile(resource)
				if err != nil {
					return fmt.Errorf("add refs file %s: %w", scaffolder.PathToRefsFile(resource), err)
				}
			}
			if scaffolder.IdentityFileExist(resource) {
				fmt.Printf("file %s already exists, skipping\n", scaffolder.PathToIdentityFile(resource))
			} else {
				err := scaffolder.AddIdentityFile(resource)
				if err != nil {
					return fmt.Errorf("add identity file %s: %w", scaffolder.PathToIdentityFile(resource), err)
				}
			}
		}
	}

	addCopyright := true
	if err := typeGenerator.WriteFiles(addCopyright); err != nil {
		return err
	}

	return nil
}