Skip to content

Commit fff5c2d

Browse files
committed
Remove duplication from config.Resolv
Signed-off-by: David Gageot <david.gageot@docker.com>
1 parent af20cc4 commit fff5c2d

5 files changed

Lines changed: 58 additions & 62 deletions

File tree

.dockerignore

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,5 @@
77
!./**/*.css
88
!./**/*.go
99
!./**/*.txt
10-
!/pkg/config/default-agent.yaml
11-
!/pkg/config/coder-agent.yaml
10+
!/pkg/config/builtin-agents/*.yaml
1211
!/pkg/tui/styles/themes/*.yaml

pkg/config/resolve.go

Lines changed: 56 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import (
55
_ "embed"
66
"fmt"
77
"log/slog"
8+
"maps"
89
"os"
910
"path/filepath"
11+
"slices"
1012
"strings"
1113

1214
"github.com/google/go-containerregistry/pkg/name"
@@ -16,10 +18,10 @@ import (
1618
"github.com/docker/cagent/pkg/userconfig"
1719
)
1820

19-
//go:embed default-agent.yaml
21+
//go:embed builtin-agents/default.yaml
2022
var defaultAgent []byte
2123

22-
//go:embed coder-agent.yaml
24+
//go:embed builtin-agents/coder.yaml
2325
var coderAgent []byte
2426

2527
// builtinAgents maps built-in agent names to their embedded YAML configurations.
@@ -30,11 +32,7 @@ var builtinAgents = map[string][]byte{
3032

3133
// BuiltinAgentNames returns the names of all built-in agents.
3234
func BuiltinAgentNames() []string {
33-
names := make([]string, 0, len(builtinAgents))
34-
for name := range builtinAgents {
35-
names = append(names, name)
36-
}
37-
return names
35+
return slices.Sorted(maps.Keys(builtinAgents))
3836
}
3937

4038
// ResolveAlias resolves an agent reference and returns the alias if it exists and has options.
@@ -70,61 +68,24 @@ func GetUserSettings() *userconfig.Settings {
7068
// when fetching from GitHub URLs.
7169
// For OCI references, always checks remote for updates but falls back to local cache if offline.
7270
func ResolveSources(agentsPath string, envProvider environment.Provider) (Sources, error) {
73-
// Handle URL references first (before resolve() which converts to absolute path)
74-
if IsURLReference(agentsPath) {
75-
return map[string]Source{
76-
agentsPath: NewURLSource(agentsPath, envProvider),
77-
}, nil
78-
}
79-
8071
resolvedPath, err := resolve(agentsPath)
8172
if err != nil {
73+
// resolve() only fails for non-OCI, non-URL, non-builtin references
74+
// that can't be made absolute. Try OCI as last resort.
8275
if IsOCIReference(agentsPath) {
83-
return map[string]Source{
84-
reference.OciRefToFilename(agentsPath): NewOCISource(agentsPath),
85-
}, nil
76+
return singleSource(reference.OciRefToFilename(agentsPath), NewOCISource(agentsPath)), nil
8677
}
8778
return nil, err
8879
}
8980

90-
if data, ok := builtinAgents[resolvedPath]; ok {
91-
return map[string]Source{
92-
resolvedPath: NewBytesSource(resolvedPath, data),
93-
}, nil
94-
}
95-
96-
if isLocalFile(resolvedPath) {
97-
return map[string]Source{
98-
fileNameWithoutExt(resolvedPath): NewFileSource(resolvedPath),
99-
}, nil
100-
}
101-
81+
// Only directories need special handling to enumerate YAML files.
10282
if dirExists(resolvedPath) {
103-
sources := make(Sources)
104-
entries, err := os.ReadDir(resolvedPath)
105-
if err != nil {
106-
return nil, fmt.Errorf("reading agents directory %s: %w", resolvedPath, err)
107-
}
108-
for _, entry := range entries {
109-
if entry.IsDir() {
110-
continue
111-
}
112-
ext := strings.ToLower(filepath.Ext(entry.Name()))
113-
if ext != ".yaml" && ext != ".yml" {
114-
continue
115-
}
116-
a := filepath.Join(resolvedPath, entry.Name())
117-
sources[fileNameWithoutExt(a)], err = Resolve(a, envProvider)
118-
if err != nil {
119-
return nil, err
120-
}
121-
}
122-
return sources, nil
83+
return resolveDirectory(resolvedPath, envProvider)
12384
}
12485

125-
return map[string]Source{
126-
reference.OciRefToFilename(resolvedPath): NewOCISource(resolvedPath),
127-
}, nil
86+
// For all other reference types, delegate to resolveOne.
87+
key, source := resolveOne(resolvedPath, envProvider)
88+
return singleSource(key, source), nil
12889
}
12990

13091
// Resolve resolves an agent file reference (local file, URL, or OCI image) to a source.
@@ -140,19 +101,55 @@ func Resolve(agentFilename string, envProvider environment.Provider) (Source, er
140101
return nil, err
141102
}
142103

143-
if data, ok := builtinAgents[resolvedPath]; ok {
144-
return NewBytesSource(resolvedPath, data), nil
104+
_, source := resolveOne(resolvedPath, envProvider)
105+
return source, nil
106+
}
107+
108+
// resolveOne maps a resolved path to the appropriate Source and a key for use
109+
// in Sources maps. The path must already be resolved via resolve().
110+
// This is the single place that decides which source type a reference maps to.
111+
// To add a new source type, add a case here.
112+
func resolveOne(resolvedPath string, envProvider environment.Provider) (string, Source) {
113+
switch {
114+
case builtinAgents[resolvedPath] != nil:
115+
return resolvedPath, NewBytesSource(resolvedPath, builtinAgents[resolvedPath])
116+
case IsURLReference(resolvedPath):
117+
return resolvedPath, NewURLSource(resolvedPath, envProvider)
118+
case isLocalFile(resolvedPath):
119+
return fileNameWithoutExt(resolvedPath), NewFileSource(resolvedPath)
120+
default:
121+
return reference.OciRefToFilename(resolvedPath), NewOCISource(resolvedPath)
145122
}
123+
}
146124

147-
if IsURLReference(resolvedPath) {
148-
return NewURLSource(resolvedPath, envProvider), nil
125+
// resolveDirectory enumerates YAML files in a directory and resolves each one.
126+
func resolveDirectory(dirPath string, envProvider environment.Provider) (Sources, error) {
127+
entries, err := os.ReadDir(dirPath)
128+
if err != nil {
129+
return nil, fmt.Errorf("reading agents directory %s: %w", dirPath, err)
149130
}
150131

151-
if isLocalFile(resolvedPath) {
152-
return NewFileSource(resolvedPath), nil
132+
sources := make(Sources)
133+
for _, entry := range entries {
134+
if entry.IsDir() {
135+
continue
136+
}
137+
ext := strings.ToLower(filepath.Ext(entry.Name()))
138+
if ext != ".yaml" && ext != ".yml" {
139+
continue
140+
}
141+
a := filepath.Join(dirPath, entry.Name())
142+
sources[fileNameWithoutExt(a)], err = Resolve(a, envProvider)
143+
if err != nil {
144+
return nil, err
145+
}
153146
}
147+
return sources, nil
148+
}
154149

155-
return NewOCISource(resolvedPath), nil
150+
// singleSource wraps a single source in a Sources map.
151+
func singleSource(key string, source Source) Sources {
152+
return Sources{key: source}
156153
}
157154

158155
// resolve resolves an agent reference, handling aliases and defaults

pkg/teamloader/teamloader_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ func TestLoadExamples(t *testing.T) {
139139
func TestLoadDefaultAgent(t *testing.T) {
140140
t.Parallel()
141141

142-
agentSource, err := config.Resolve("../../pkg/config/default-agent.yaml", nil)
142+
agentSource, err := config.Resolve("default", nil)
143143
require.NoError(t, err)
144144

145145
runConfig := &config.RuntimeConfig{

0 commit comments

Comments
 (0)