Skip to content

Commit 31816e1

Browse files
authored
feat(images): add image list and imageconfigs (#416)
1 parent 6daefdc commit 31816e1

12 files changed

Lines changed: 672 additions & 1 deletion

internal/app/azldev/cmds/image/image.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ func OnAppInit(app *azldev.App) {
1818
app.AddTopLevelCommand(cmd)
1919
customizeOnAppInit(app, cmd)
2020
injectFilesOnAppInit(app, cmd)
21+
listOnAppInit(app, cmd)
2122
}
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
package image
5+
6+
import (
7+
"fmt"
8+
"path/filepath"
9+
"sort"
10+
"strings"
11+
12+
"github.com/gim-home/azldev-preview/internal/app/azldev"
13+
"github.com/samber/lo"
14+
"github.com/spf13/cobra"
15+
)
16+
17+
// Options for listing images within the environment.
18+
type ListImageOptions struct {
19+
// Name patterns to filter images. Supports glob patterns (*, ?, []).
20+
ImageNamePatterns []string
21+
}
22+
23+
// ImageListResult represents an image in the list output.
24+
type ImageListResult struct {
25+
// Name of the image.
26+
Name string `json:"name" table:",sortkey"`
27+
28+
// Description of the image.
29+
Description string `json:"description"`
30+
31+
// Definition contains the image definition details (hidden from table output).
32+
Definition ImageDefinitionResult `json:"definition" table:"-"`
33+
}
34+
35+
// ImageDefinitionResult represents the definition details for an image.
36+
type ImageDefinitionResult struct {
37+
// Type indicates the type of image definition (e.g., "kiwi").
38+
Type string `json:"type"`
39+
40+
// Path points to the image definition file.
41+
Path string `json:"path"`
42+
}
43+
44+
func listOnAppInit(_ *azldev.App, parentCmd *cobra.Command) {
45+
parentCmd.AddCommand(NewImageListCommand())
46+
}
47+
48+
// Constructs a [cobra.Command] for "image list" CLI subcommand.
49+
func NewImageListCommand() *cobra.Command {
50+
options := &ListImageOptions{}
51+
52+
cmd := &cobra.Command{
53+
Use: "list [image-name-pattern...]",
54+
Short: "List images in this project",
55+
Long: `List images defined in this project's configuration.
56+
57+
Image name patterns support glob syntax (*, ?, []).
58+
If no patterns are provided, all images are listed.`,
59+
RunE: azldev.RunFuncWithExtraArgs(func(env *azldev.Env, args []string) (interface{}, error) {
60+
options.ImageNamePatterns = append(args, options.ImageNamePatterns...)
61+
62+
return ListImages(env, options)
63+
}),
64+
ValidArgsFunction: generateImageNameCompletions,
65+
}
66+
67+
azldev.ExportAsMCPTool(cmd)
68+
69+
return cmd
70+
}
71+
72+
// ListImages lists images in the env, in accordance with options. Returns the found images.
73+
func ListImages(env *azldev.Env, options *ListImageOptions) ([]ImageListResult, error) {
74+
cfg := env.Config()
75+
if cfg == nil {
76+
return nil, nil
77+
}
78+
79+
// Collect all image names, sorted.
80+
imageNames := lo.Keys(cfg.Images)
81+
sort.Strings(imageNames)
82+
83+
// If no patterns provided, match all images.
84+
patterns := options.ImageNamePatterns
85+
if len(patterns) == 0 {
86+
patterns = []string{"*"}
87+
}
88+
89+
// Filter images by patterns and build results.
90+
results := make([]ImageListResult, 0, len(imageNames))
91+
92+
for _, name := range imageNames {
93+
matched, err := matchesAnyPattern(name, patterns)
94+
if err != nil {
95+
return nil, err
96+
}
97+
98+
if !matched {
99+
continue
100+
}
101+
102+
imageConfig := cfg.Images[name]
103+
results = append(results, ImageListResult{
104+
Name: name,
105+
Description: imageConfig.Description,
106+
Definition: ImageDefinitionResult{
107+
Type: string(imageConfig.Definition.DefinitionType),
108+
Path: imageConfig.Definition.Path,
109+
},
110+
})
111+
}
112+
113+
return results, nil
114+
}
115+
116+
// matchesAnyPattern returns true if name matches any of the given glob patterns.
117+
func matchesAnyPattern(name string, patterns []string) (bool, error) {
118+
for _, pattern := range patterns {
119+
matched, err := filepath.Match(pattern, name)
120+
if err != nil {
121+
return false, fmt.Errorf("matching pattern %#q against image name %#q:\n%w", pattern, name, err)
122+
}
123+
124+
if matched {
125+
return true, nil
126+
}
127+
}
128+
129+
return false, nil
130+
}
131+
132+
// generateImageNameCompletions generates shell completions for image names.
133+
func generateImageNameCompletions(
134+
cmd *cobra.Command, _ []string, toComplete string,
135+
) ([]string, cobra.ShellCompDirective) {
136+
env, err := azldev.GetEnvFromCommand(cmd)
137+
if err != nil {
138+
return nil, cobra.ShellCompDirectiveError
139+
}
140+
141+
cfg := env.Config()
142+
if cfg == nil {
143+
return nil, cobra.ShellCompDirectiveError
144+
}
145+
146+
// Collect image names that match the prefix.
147+
imageNames := lo.Keys(cfg.Images)
148+
completions := lo.Filter(imageNames, func(name string, _ int) bool {
149+
return strings.HasPrefix(name, toComplete)
150+
})
151+
152+
sort.Strings(completions)
153+
154+
return completions, cobra.ShellCompDirectiveNoFileComp
155+
}

0 commit comments

Comments
 (0)