|
15 | 15 | package gcloud |
16 | 16 |
|
17 | 17 | import ( |
| 18 | + "bytes" |
18 | 19 | "context" |
19 | 20 | "fmt" |
20 | 21 | "os" |
21 | 22 | "path/filepath" |
22 | 23 | "strings" |
| 24 | + "text/template" |
23 | 25 |
|
24 | 26 | "github.com/googleapis/librarian/internal/sidekick/api" |
25 | 27 | "github.com/googleapis/librarian/internal/surfer/gcloud/utils" |
26 | 28 | "github.com/googleapis/librarian/internal/yaml" |
| 29 | + "github.com/iancoleman/strcase" |
27 | 30 | ) |
28 | 31 |
|
29 | 32 | // partialsHeader is the directive that tells gcloud to look in the `_partials` directory |
@@ -69,6 +72,21 @@ func generateService(service *api.Service, overrides *Config, model *api.API, ou |
69 | 72 | // `{outdir}/{shortServiceName}/` |
70 | 73 | surfaceDir := filepath.Join(output, shortServiceName) |
71 | 74 |
|
| 75 | + if err := os.MkdirAll(surfaceDir, 0755); err != nil { |
| 76 | + return fmt.Errorf("failed to create surface directory for %q: %w", shortServiceName, err) |
| 77 | + } |
| 78 | + |
| 79 | + track := strings.ToUpper(utils.InferTrackFromPackage(service.Package)) |
| 80 | + data := commandGroupData{ |
| 81 | + ServiceTitle: utils.GetServiceTitle(model, shortServiceName), |
| 82 | + ClassNamePrefix: strcase.ToCamel(shortServiceName), |
| 83 | + Tracks: []string{track}, |
| 84 | + } |
| 85 | + |
| 86 | + if err := writeCommandGroupFile(surfaceDir, data); err != nil { |
| 87 | + return fmt.Errorf("failed to write command group file for service %q: %w", shortServiceName, err) |
| 88 | + } |
| 89 | + |
72 | 90 | // gcloud commands are resource-centric commands (e.g., `gcloud parallelstore instances create`), |
73 | 91 | // so we first need to group all the API methods by the resource they operate on. |
74 | 92 | // We'll create a map where the key is the resource's collection ID (e.g., "instances") |
@@ -106,10 +124,35 @@ func generateService(service *api.Service, overrides *Config, model *api.API, ou |
106 | 124 | // For a given collectionID like "instances", this function will create a directory |
107 | 125 | // `instances/` and populate it with `create.yaml`, `delete.yaml`, etc. |
108 | 126 | func generateResourceCommands(collectionID string, methods []*api.Method, baseDir string, overrides *Config, model *api.API, service *api.Service) error { |
| 127 | + if len(methods) == 0 { |
| 128 | + return nil |
| 129 | + } |
| 130 | + |
109 | 131 | // The main directory for the resource is named after its collection ID. |
110 | 132 | // Example: `{baseDir}/instances` |
111 | 133 | resourceDir := filepath.Join(baseDir, collectionID) |
112 | 134 |
|
| 135 | + if err := os.MkdirAll(resourceDir, 0755); err != nil { |
| 136 | + return fmt.Errorf("failed to create resource directory for %q: %w", collectionID, err) |
| 137 | + } |
| 138 | + |
| 139 | + singular := utils.GetSingularResourceNameForMethod(methods[0], model) |
| 140 | + |
| 141 | + // We determine the short service name from the default host to use as a fallback title. |
| 142 | + shortServiceName, _, _ := strings.Cut(service.DefaultHost, ".") |
| 143 | + |
| 144 | + track := strings.ToUpper(utils.InferTrackFromPackage(service.Package)) |
| 145 | + data := commandGroupData{ |
| 146 | + ServiceTitle: utils.GetServiceTitle(model, shortServiceName), |
| 147 | + ResourceSingular: singular, |
| 148 | + ClassNamePrefix: strcase.ToCamel(collectionID), |
| 149 | + Tracks: []string{track}, |
| 150 | + } |
| 151 | + |
| 152 | + if err := writeCommandGroupFile(resourceDir, data); err != nil { |
| 153 | + return fmt.Errorf("failed to write command group file for resource %q: %w", collectionID, err) |
| 154 | + } |
| 155 | + |
113 | 156 | // Gcloud commands are defined in a `_partials` directory. This allows |
114 | 157 | // for sharing command definitions across different release tracks (GA, Beta, Alpha). |
115 | 158 | partialsDir := filepath.Join(resourceDir, "_partials") |
@@ -165,3 +208,36 @@ func generateResourceCommands(collectionID string, methods []*api.Method, baseDi |
165 | 208 | } |
166 | 209 | return nil |
167 | 210 | } |
| 211 | + |
| 212 | +func writeCommandGroupFile(dir string, data commandGroupData) error { |
| 213 | + var buf bytes.Buffer |
| 214 | + if err := commandGroupTemplate.Execute(&buf, data); err != nil { |
| 215 | + return err |
| 216 | + } |
| 217 | + path := filepath.Join(dir, "__init__.py") |
| 218 | + return os.WriteFile(path, buf.Bytes(), 0644) |
| 219 | +} |
| 220 | + |
| 221 | +var commandGroupTemplate = template.Must(template.New("__init__.py").Funcs(template.FuncMap{ |
| 222 | + "toCamel": strcase.ToCamel, |
| 223 | +}).Parse(`# NOTE: This file is autogenerated and should not be edited by hand. |
| 224 | +"""Manage {{.ServiceTitle}}{{if .ResourceSingular}} {{.ResourceSingular}}{{end}} resources.""" |
| 225 | +
|
| 226 | +from googlecloudsdk.calliope import base |
| 227 | +
|
| 228 | +
|
| 229 | +{{range .Tracks}}@base.ReleaseTracks(base.ReleaseTrack.{{.}}) |
| 230 | +@base.Autogenerated |
| 231 | +@base.Hidden |
| 232 | +class {{$.ClassNamePrefix}}{{. | toCamel}}(base.Group): |
| 233 | + """Manage {{$.ServiceTitle}}{{if $.ResourceSingular}} {{$.ResourceSingular}}{{end}} resources.""" |
| 234 | +
|
| 235 | +
|
| 236 | +{{end}}`)) |
| 237 | + |
| 238 | +type commandGroupData struct { |
| 239 | + ServiceTitle string |
| 240 | + ResourceSingular string |
| 241 | + ClassNamePrefix string |
| 242 | + Tracks []string |
| 243 | +} |
0 commit comments