@@ -21,12 +21,9 @@ import (
21
21
"io"
22
22
"net/http"
23
23
"net/url"
24
- "os"
25
24
"path/filepath"
26
25
"runtime"
27
- "slices"
28
26
"strings"
29
- "time"
30
27
31
28
"github.com/spf13/cobra"
32
29
@@ -39,78 +36,10 @@ import (
39
36
pschema "github.com/pulumi/pulumi/pkg/v3/codegen/schema"
40
37
"github.com/pulumi/pulumi/sdk/v3/go/common/util/contract"
41
38
"github.com/pulumi/registry/tools/resourcedocsgen/pkg"
39
+ "github.com/pulumi/registry/tools/resourcedocsgen/pkg/registry/svc"
42
40
concpool "github.com/sourcegraph/conc/pool"
43
41
)
44
42
45
- // PackageMetadataProvider is an interface for providers that retrieve package metadata
46
- // from either the filesystem directory or the Pulumi Registry API.
47
- type PackageMetadataProvider interface {
48
- // GetPackageMetadata returns metadata for a specific package
49
- GetPackageMetadata (ctx context.Context , pkgName string ) (pkg.PackageMeta , error )
50
- // ListPackageMetadata returns metadata for all packages
51
- ListPackageMetadata (ctx context.Context ) ([]pkg.PackageMeta , error )
52
- }
53
-
54
- // FileSystemProvider implements PackageMetadataProvider using the local yaml data files
55
- // in the pulumi/registry repository.
56
- type fileSystemProvider struct {
57
- registryDir string
58
- }
59
-
60
- // RegistryAPIProvider implements PackageMetadataProvider using the Pulumi API
61
- // to retrieve package metadata.
62
- type registryAPIProvider struct {
63
- apiURL string
64
- }
65
-
66
- // PackageMetadata represents the API response structure for package metadata
67
- // from the Pulumi Registry API.
68
- // TODO: will eventually be available in the "github.com/pulumi/pulumi/sdk/v3/go/common/apitype" package.
69
- type PackageMetadata struct {
70
- Name string `json:"name"`
71
- Publisher string `json:"publisher"`
72
- Source string `json:"source"`
73
- Version string `json:"version"`
74
- Title string `json:"title,omitempty"`
75
- Description string `json:"description,omitempty"`
76
- LogoURL string `json:"logoUrl,omitempty"`
77
- RepoURL string `json:"repoUrl,omitempty"`
78
- Category string `json:"category,omitempty"`
79
- IsFeatured bool `json:"isFeatured"`
80
- PackageTypes []string `json:"packageTypes,omitempty"`
81
- PackageStatus string `json:"packageStatus"`
82
- SchemaURL string `json:"schemaURL"`
83
- CreatedAt time.Time `json:"createdAt"`
84
- }
85
-
86
- // PackageListResponse represents the API response structure for package lists
87
- type PackageListResponse struct {
88
- Packages []PackageMetadata `json:"packages"`
89
- }
90
-
91
- // NewFileSystemProvider creates a new PackageMetadataProvider that reads from the filesystem
92
- func NewFileSystemProvider (registryDir string ) PackageMetadataProvider {
93
- return & fileSystemProvider {
94
- registryDir : registryDir ,
95
- }
96
- }
97
-
98
- // NewAPIProvider creates a new PackageMetadataProvider that reads from the Pulumi API
99
- func NewAPIProvider (apiURL string ) PackageMetadataProvider {
100
- return & registryAPIProvider {
101
- apiURL : apiURL ,
102
- }
103
- }
104
-
105
- func getRepoSlug (repoURL string ) (string , error ) {
106
- u , err := url .Parse (repoURL )
107
- if err != nil {
108
- return "" , errors .Wrapf (err , "parsing repo url %s" , repoURL )
109
- }
110
-
111
- return u .Path , nil
112
- }
113
-
114
43
func genResourceDocsForPackageFromRegistryMetadata (
115
44
metadata pkg.PackageMeta , docsOutDir , packageTreeJSONOutDir string ,
116
45
) error {
@@ -195,12 +124,17 @@ func getSchemaFileURL(metadata pkg.PackageMeta) (string, error) {
195
124
return fmt .Sprintf ("https://raw.githubusercontent.com/%s/%s/%s" , repoSlug , metadata .Version , schemaFilePath ), nil
196
125
}
197
126
198
- func getRegistryPackagesPath (repoPath string ) string {
199
- return filepath .Join (repoPath , "themes" , "default" , "data" , "registry" , "packages" )
127
+ func getRepoSlug (repoURL string ) (string , error ) {
128
+ u , err := url .Parse (repoURL )
129
+ if err != nil {
130
+ return "" , errors .Wrapf (err , "parsing repo url %s" , repoURL )
131
+ }
132
+
133
+ return u .Path , nil
200
134
}
201
135
202
136
func genResourceDocsForAllRegistryPackages (
203
- provider PackageMetadataProvider ,
137
+ provider svc. PackageMetadataProvider ,
204
138
baseDocsOutDir , basePackageTreeJSONOutDir string ,
205
139
) error {
206
140
ctx := context .Background ()
@@ -226,25 +160,6 @@ func genResourceDocsForAllRegistryPackages(
226
160
return pool .Wait ()
227
161
}
228
162
229
- func convertAPIPackageToPackageMeta (apiPkg PackageMetadata ) (* pkg.PackageMeta , error ) {
230
- return & pkg.PackageMeta {
231
- Name : apiPkg .Name ,
232
- Publisher : apiPkg .Publisher ,
233
- Description : apiPkg .Description ,
234
- LogoURL : apiPkg .LogoURL ,
235
- RepoURL : apiPkg .RepoURL ,
236
- Category : pkg .PackageCategory (apiPkg .Category ),
237
- Featured : apiPkg .IsFeatured ,
238
- Native : slices .Contains (apiPkg .PackageTypes , "native" ),
239
- Component : slices .Contains (apiPkg .PackageTypes , "component" ),
240
- PackageStatus : pkg .PackageStatus (apiPkg .PackageStatus ),
241
- SchemaFileURL : apiPkg .SchemaURL ,
242
- Version : apiPkg .Version ,
243
- Title : apiPkg .Title ,
244
- UpdatedOn : apiPkg .CreatedAt .Unix (),
245
- }, nil
246
- }
247
-
248
163
func resourceDocsFromRegistryCmd () * cobra.Command {
249
164
var baseDocsOutDir string
250
165
var basePackageTreeJSONOutDir string
@@ -259,11 +174,11 @@ func resourceDocsFromRegistryCmd() *cobra.Command {
259
174
"Pass a package name in the registry as an optional arg to generate docs only for that package." ,
260
175
RunE : func (cmd * cobra.Command , args []string ) error {
261
176
ctx := cmd .Context ()
262
- var provider PackageMetadataProvider
177
+ var provider svc. PackageMetadataProvider
263
178
if useAPI {
264
- provider = NewAPIProvider (apiURL )
179
+ provider = svc . NewAPIProvider (apiURL )
265
180
} else {
266
- provider = NewFileSystemProvider (registryDir )
181
+ provider = svc . NewFileSystemProvider (registryDir )
267
182
}
268
183
269
184
if len (args ) > 0 {
@@ -307,144 +222,3 @@ func resourceDocsFromRegistryCmd() *cobra.Command {
307
222
308
223
return cmd
309
224
}
310
-
311
- // GetPackageMetadata implements PackageMetadataProvider for fileSystemProvider
312
- func (p * fileSystemProvider ) GetPackageMetadata (ctx context.Context , pkgName string ) (pkg.PackageMeta , error ) {
313
- metadataFilePath := filepath .Join (getRegistryPackagesPath (p .registryDir ), pkgName + ".yaml" )
314
- b , err := os .ReadFile (metadataFilePath )
315
- if err != nil {
316
- return pkg.PackageMeta {}, errors .Wrapf (err , "reading the metadata file %s" , metadataFilePath )
317
- }
318
-
319
- var metadata pkg.PackageMeta
320
- if err := yaml .Unmarshal (b , & metadata ); err != nil {
321
- return pkg.PackageMeta {}, errors .Wrapf (err , "unmarshalling the metadata file %s" , metadataFilePath )
322
- }
323
-
324
- return metadata , nil
325
- }
326
-
327
- // ListPackageMetadata implements PackageMetadataProvider for fileSystemProvider
328
- func (p * fileSystemProvider ) ListPackageMetadata (ctx context.Context ) ([]pkg.PackageMeta , error ) {
329
- registryPackagesPath := getRegistryPackagesPath (p .registryDir )
330
- files , err := os .ReadDir (registryPackagesPath )
331
- if err != nil {
332
- return nil , errors .Wrapf (err , "reading directory %s" , registryPackagesPath )
333
- }
334
-
335
- // Count YAML files to pre-allocate the slice mostly to appease the linter.
336
- var metadataCount int
337
- for _ , file := range files {
338
- if strings .HasSuffix (file .Name (), ".yaml" ) {
339
- metadataCount ++
340
- }
341
- }
342
-
343
- metadataList := make ([]pkg.PackageMeta , 0 , metadataCount )
344
- for _ , file := range files {
345
- if ! strings .HasSuffix (file .Name (), ".yaml" ) {
346
- continue
347
- }
348
-
349
- metadata , err := p .GetPackageMetadata (ctx , strings .TrimSuffix (file .Name (), ".yaml" ))
350
- if err != nil {
351
- return nil , err
352
- }
353
- metadataList = append (metadataList , metadata )
354
- }
355
-
356
- return metadataList , nil
357
- }
358
-
359
- // GetPackageMetadata implements PackageMetadataProvider for registryAPIProvider
360
- func (p * registryAPIProvider ) GetPackageMetadata (ctx context.Context , pkgName string ) (pkg.PackageMeta , error ) {
361
- req , err := http .NewRequestWithContext (ctx , http .MethodGet ,
362
- fmt .Sprintf ("%s/packages?name=%s" , p .apiURL , pkgName ), nil )
363
- if err != nil {
364
- return pkg.PackageMeta {}, errors .Wrapf (err , "creating request for package %s" , pkgName )
365
- }
366
-
367
- resp , err := http .DefaultClient .Do (req )
368
- if err != nil {
369
- return pkg.PackageMeta {}, errors .Wrapf (err , "fetching package metadata from API for %s" , pkgName )
370
- }
371
- defer resp .Body .Close ()
372
-
373
- if resp .StatusCode != http .StatusOK {
374
- return pkg.PackageMeta {}, errors .Errorf ("unexpected status code %d when fetching package metadata" , resp .StatusCode )
375
- }
376
-
377
- var response PackageListResponse
378
- if err := json .NewDecoder (resp .Body ).Decode (& response ); err != nil {
379
- return pkg.PackageMeta {}, errors .Wrap (err , "decoding API response" )
380
- }
381
-
382
- switch len (response .Packages ) {
383
- case 0 :
384
- return pkg.PackageMeta {}, errors .Errorf ("no package found with name %s" , pkgName )
385
- case 1 :
386
- metadata , err := convertAPIPackageToPackageMeta (response .Packages [0 ])
387
- if err != nil {
388
- return pkg.PackageMeta {}, err
389
- }
390
- return * metadata , nil
391
- default :
392
- return pkg.PackageMeta {}, errors .Errorf ("multiple packages found with name %s" , pkgName )
393
- }
394
- }
395
-
396
- // ListPackageMetadata implements PackageMetadataProvider for registryAPIProvider
397
- func (p * registryAPIProvider ) ListPackageMetadata (ctx context.Context ) ([]pkg.PackageMeta , error ) {
398
- var allPackages []pkg.PackageMeta
399
- // Maximum allowed by the API (must be less than 500). Request up to 499 to account for pagination
400
- // with minimum number of requests.
401
- const limit = 499
402
- continuationToken := ""
403
-
404
- for {
405
- url := fmt .Sprintf ("%s/packages?limit=%d" , p .apiURL , limit )
406
- if continuationToken != "" {
407
- url = fmt .Sprintf ("%s&continuationToken=%s" , url , continuationToken )
408
- }
409
-
410
- req , err := http .NewRequestWithContext (ctx , http .MethodGet , url , nil )
411
- if err != nil {
412
- return nil , errors .Wrap (err , "creating request for package list" )
413
- }
414
-
415
- resp , err := http .DefaultClient .Do (req )
416
- if err != nil {
417
- return nil , errors .Wrap (err , "fetching package list from API" )
418
- }
419
- defer resp .Body .Close ()
420
-
421
- if resp .StatusCode != http .StatusOK {
422
- return nil , errors .Errorf ("unexpected status code %d when fetching package list" , resp .StatusCode )
423
- }
424
-
425
- var response struct {
426
- Packages []PackageMetadata `json:"packages"`
427
- ContinuationToken * string `json:"continuationToken"`
428
- }
429
- if err := json .NewDecoder (resp .Body ).Decode (& response ); err != nil {
430
- return nil , errors .Wrap (err , "decoding API response" )
431
- }
432
-
433
- for _ , apiPkg := range response .Packages {
434
- metadata , err := convertAPIPackageToPackageMeta (apiPkg )
435
- if err != nil {
436
- return nil , err
437
- }
438
- allPackages = append (allPackages , * metadata )
439
- }
440
-
441
- // If there's no continuation token, we've reached the end
442
- if response .ContinuationToken == nil {
443
- break
444
- }
445
-
446
- continuationToken = * response .ContinuationToken
447
- }
448
-
449
- return allPackages , nil
450
- }
0 commit comments