Skip to content

Commit dbcaa3e

Browse files
committed
refactor: unified path handle for windows and unix like system
1 parent 66fa57e commit dbcaa3e

File tree

7 files changed

+476
-24
lines changed

7 files changed

+476
-24
lines changed

pkg/plugin_packager/decoder/fs.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -126,11 +126,13 @@ func (d *FSPluginDecoder) Close() error {
126126
}
127127

128128
func (d *FSPluginDecoder) Stat(filename string) (fs.FileInfo, error) {
129-
return os.Stat(filepath.Join(d.root, filename))
129+
// Convert logical forward-slash paths to OS-native separators at the boundary.
130+
return os.Stat(filepath.Join(d.root, filepath.FromSlash(filename)))
130131
}
131132

132133
func (d *FSPluginDecoder) ReadFile(filename string) ([]byte, error) {
133-
return os.ReadFile(filepath.Join(d.root, filename))
134+
// Convert logical forward-slash paths to OS-native separators at the boundary.
135+
return os.ReadFile(filepath.Join(d.root, filepath.FromSlash(filename)))
134136
}
135137

136138
func (d *FSPluginDecoder) ReadDir(dirname string) ([]string, error) {
@@ -158,7 +160,8 @@ func (d *FSPluginDecoder) ReadDir(dirname string) ([]string, error) {
158160
}
159161

160162
func (d *FSPluginDecoder) FileReader(filename string) (io.ReadCloser, error) {
161-
return os.Open(filepath.Join(d.root, filename))
163+
// Convert logical forward-slash paths to OS-native separators at the boundary.
164+
return os.Open(filepath.Join(d.root, filepath.FromSlash(filename)))
162165
}
163166

164167
func (d *FSPluginDecoder) Signature() (string, error) {

pkg/plugin_packager/decoder/helper.go

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"errors"
55
"fmt"
66
"os"
7+
"path"
78
"path/filepath"
89
"regexp"
910
"strings"
@@ -38,7 +39,8 @@ func (p *PluginDecoderHelper) Manifest(decoder PluginDecoder) (plugin_entities.P
3839
// try to load plugins
3940
plugins := dec.Plugins
4041
for _, tool := range plugins.Tools {
41-
// read yaml
42+
// read YAML
43+
tool = normalizeLogicalPath(tool)
4244
pluginYaml, err := decoder.ReadFile(tool)
4345
if err != nil {
4446
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to read tool file: %s", tool))
@@ -50,15 +52,16 @@ func (p *PluginDecoderHelper) Manifest(decoder PluginDecoder) (plugin_entities.P
5052
}
5153

5254
// read tools
53-
for _, tool_file := range pluginDec.ToolFiles {
54-
toolFileContent, err := decoder.ReadFile(tool_file)
55+
for _, toolFile := range pluginDec.ToolFiles {
56+
toolFile = normalizeLogicalPath(toolFile)
57+
toolFileContent, err := decoder.ReadFile(toolFile)
5558
if err != nil {
56-
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to read tool file: %s", tool_file))
59+
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to read tool file: %s", toolFile))
5760
}
5861

5962
toolFileDec, err := parser.UnmarshalYamlBytes[plugin_entities.ToolDeclaration](toolFileContent)
6063
if err != nil {
61-
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to unmarshal tool file: %s", tool_file))
64+
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to unmarshal tool file: %s", toolFile))
6265
}
6366

6467
pluginDec.Tools = append(pluginDec.Tools, toolFileDec)
@@ -69,6 +72,7 @@ func (p *PluginDecoderHelper) Manifest(decoder PluginDecoder) (plugin_entities.P
6972

7073
for _, endpoint := range plugins.Endpoints {
7174
// read yaml
75+
endpoint = normalizeLogicalPath(endpoint)
7276
pluginYaml, err := decoder.ReadFile(endpoint)
7377
if err != nil {
7478
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to read endpoint file: %s", endpoint))
@@ -82,15 +86,16 @@ func (p *PluginDecoderHelper) Manifest(decoder PluginDecoder) (plugin_entities.P
8286
// read detailed endpoints
8387
endpointsFiles := pluginDec.EndpointFiles
8488

85-
for _, endpoint_file := range endpointsFiles {
86-
endpointFileContent, err := decoder.ReadFile(endpoint_file)
89+
for _, endpointFile := range endpointsFiles {
90+
endpointFile = normalizeLogicalPath(endpointFile)
91+
endpointFileContent, err := decoder.ReadFile(endpointFile)
8792
if err != nil {
88-
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to read endpoint file: %s", endpoint_file))
93+
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to read endpoint file: %s", endpointFile))
8994
}
9095

9196
endpointFileDec, err := parser.UnmarshalYamlBytes[plugin_entities.EndpointDeclaration](endpointFileContent)
9297
if err != nil {
93-
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to unmarshal endpoint file: %s", endpoint_file))
98+
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to unmarshal endpoint file: %s", endpointFile))
9499
}
95100

96101
pluginDec.Endpoints = append(pluginDec.Endpoints, endpointFileDec)
@@ -101,6 +106,7 @@ func (p *PluginDecoderHelper) Manifest(decoder PluginDecoder) (plugin_entities.P
101106

102107
for _, model := range plugins.Models {
103108
// read yaml
109+
model = normalizeLogicalPath(model)
104110
pluginYaml, err := decoder.ReadFile(model)
105111
if err != nil {
106112
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to read model file: %s", model))
@@ -117,6 +123,7 @@ func (p *PluginDecoderHelper) Manifest(decoder PluginDecoder) (plugin_entities.P
117123

118124
llmFileName, ok := pluginDec.PositionFiles["llm"]
119125
if ok {
126+
llmFileName = normalizeLogicalPath(llmFileName)
120127
llmFile, err := decoder.ReadFile(llmFileName)
121128
if err != nil {
122129
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to read llm position file: %s", llmFileName))
@@ -132,6 +139,7 @@ func (p *PluginDecoderHelper) Manifest(decoder PluginDecoder) (plugin_entities.P
132139

133140
textEmbeddingFileName, ok := pluginDec.PositionFiles["text_embedding"]
134141
if ok {
142+
textEmbeddingFileName = normalizeLogicalPath(textEmbeddingFileName)
135143
textEmbeddingFile, err := decoder.ReadFile(textEmbeddingFileName)
136144
if err != nil {
137145
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to read text embedding position file: %s", textEmbeddingFileName))
@@ -147,6 +155,7 @@ func (p *PluginDecoderHelper) Manifest(decoder PluginDecoder) (plugin_entities.P
147155

148156
rerankFileName, ok := pluginDec.PositionFiles["rerank"]
149157
if ok {
158+
rerankFileName = normalizeLogicalPath(rerankFileName)
150159
rerankFile, err := decoder.ReadFile(rerankFileName)
151160
if err != nil {
152161
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to read rerank position file: %s", rerankFileName))
@@ -162,6 +171,7 @@ func (p *PluginDecoderHelper) Manifest(decoder PluginDecoder) (plugin_entities.P
162171

163172
ttsFileName, ok := pluginDec.PositionFiles["tts"]
164173
if ok {
174+
ttsFileName = normalizeLogicalPath(ttsFileName)
165175
ttsFile, err := decoder.ReadFile(ttsFileName)
166176
if err != nil {
167177
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to read tts position file: %s", ttsFileName))
@@ -177,6 +187,7 @@ func (p *PluginDecoderHelper) Manifest(decoder PluginDecoder) (plugin_entities.P
177187

178188
speech2textFileName, ok := pluginDec.PositionFiles["speech2text"]
179189
if ok {
190+
speech2textFileName = normalizeLogicalPath(speech2textFileName)
180191
speech2textFile, err := decoder.ReadFile(speech2textFileName)
181192
if err != nil {
182193
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to read speech2text position file: %s", speech2textFileName))
@@ -192,6 +203,7 @@ func (p *PluginDecoderHelper) Manifest(decoder PluginDecoder) (plugin_entities.P
192203

193204
moderationFileName, ok := pluginDec.PositionFiles["moderation"]
194205
if ok {
206+
moderationFileName = normalizeLogicalPath(moderationFileName)
195207
moderationFile, err := decoder.ReadFile(moderationFileName)
196208
if err != nil {
197209
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to read moderation position file: %s", moderationFileName))
@@ -208,21 +220,22 @@ func (p *PluginDecoderHelper) Manifest(decoder PluginDecoder) (plugin_entities.P
208220

209221
// read models
210222
if err := decoder.Walk(func(filename, dir string) error {
211-
modelPatterns := pluginDec.ModelFiles
212-
// using glob to match if dir/filename is in models
213-
modelFileName := filepath.Join(dir, filename)
214-
if strings.HasSuffix(modelFileName, "_position.yaml") {
223+
// Normalize walked relative path to forward slashes so matching is OS-independent
224+
rel := normalizeLogicalPath(filepath.ToSlash(filepath.Join(dir, filename)))
225+
if strings.HasSuffix(rel, "_position.yaml") {
215226
return nil
216227
}
217228

218-
for _, model_pattern := range modelPatterns {
219-
matched, err := filepath.Match(model_pattern, modelFileName)
229+
// Normalize patterns to forward slashes and use POSIX-style matching
230+
for _, modelPattern := range pluginDec.ModelFiles {
231+
pat := normalizeLogicalPath(modelPattern)
232+
matched, err := path.Match(pat, rel)
220233
if err != nil {
221234
return err
222235
}
223236
if matched {
224-
// read model file
225-
modelFile, err := decoder.ReadFile(modelFileName)
237+
// Read using forward-slash path so both zip and fs decoders work
238+
modelFile, err := decoder.ReadFile(rel)
226239
if err != nil {
227240
return err
228241
}
@@ -233,6 +246,7 @@ func (p *PluginDecoderHelper) Manifest(decoder PluginDecoder) (plugin_entities.P
233246
}
234247

235248
pluginDec.Models = append(pluginDec.Models, modelDec)
249+
break
236250
}
237251
}
238252

@@ -246,6 +260,7 @@ func (p *PluginDecoderHelper) Manifest(decoder PluginDecoder) (plugin_entities.P
246260

247261
for _, agentStrategy := range plugins.AgentStrategies {
248262
// read yaml
263+
agentStrategy = normalizeLogicalPath(agentStrategy)
249264
pluginYaml, err := decoder.ReadFile(agentStrategy)
250265
if err != nil {
251266
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to read agent strategy file: %s", agentStrategy))
@@ -257,6 +272,7 @@ func (p *PluginDecoderHelper) Manifest(decoder PluginDecoder) (plugin_entities.P
257272
}
258273

259274
for _, strategyFile := range pluginDec.StrategyFiles {
275+
strategyFile = normalizeLogicalPath(strategyFile)
260276
strategyFileContent, err := decoder.ReadFile(strategyFile)
261277
if err != nil {
262278
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to read agent strategy file: %s", strategyFile))
@@ -275,6 +291,7 @@ func (p *PluginDecoderHelper) Manifest(decoder PluginDecoder) (plugin_entities.P
275291

276292
for _, datasource := range plugins.Datasources {
277293
// read yaml
294+
datasource = normalizeLogicalPath(datasource)
278295
pluginYaml, err := decoder.ReadFile(datasource)
279296
if err != nil {
280297
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to read datasource file: %s", datasource))
@@ -286,6 +303,7 @@ func (p *PluginDecoderHelper) Manifest(decoder PluginDecoder) (plugin_entities.P
286303
}
287304

288305
for _, datasourceFile := range pluginDec.DatasourceFiles {
306+
datasourceFile = normalizeLogicalPath(datasourceFile)
289307
datasourceFileContent, err := decoder.ReadFile(datasourceFile)
290308
if err != nil {
291309
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to read datasource file: %s", datasourceFile))
@@ -304,6 +322,7 @@ func (p *PluginDecoderHelper) Manifest(decoder PluginDecoder) (plugin_entities.P
304322

305323
for _, trigger := range plugins.Triggers {
306324
// read yaml
325+
trigger = normalizeLogicalPath(trigger)
307326
pluginYaml, err := decoder.ReadFile(trigger)
308327
if err != nil {
309328
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to read trigger file: %s", trigger))
@@ -315,15 +334,16 @@ func (p *PluginDecoderHelper) Manifest(decoder PluginDecoder) (plugin_entities.P
315334
}
316335

317336
// read events
318-
for _, event_file := range pluginDec.EventFiles {
319-
eventFileContent, err := decoder.ReadFile(event_file)
337+
for _, eventFile := range pluginDec.EventFiles {
338+
eventFile = normalizeLogicalPath(eventFile)
339+
eventFileContent, err := decoder.ReadFile(eventFile)
320340
if err != nil {
321-
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to read event file: %s", event_file))
341+
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to read event file: %s", eventFile))
322342
}
323343

324344
eventFileDec, err := parser.UnmarshalYamlBytes[plugin_entities.EventDeclaration](eventFileContent)
325345
if err != nil {
326-
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to unmarshal event file: %s", event_file))
346+
return plugin_entities.PluginDeclaration{}, errors.Join(err, fmt.Errorf("failed to unmarshal event file: %s", eventFile))
327347
}
328348

329349
pluginDec.Events = append(pluginDec.Events, eventFileDec)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package decoder
2+
3+
import (
4+
"io"
5+
"os"
6+
"path/filepath"
7+
"testing"
8+
)
9+
10+
func TestFSDecoder_FromSlashBoundary(t *testing.T) {
11+
root := t.TempDir()
12+
// Create nested file using OS-native separators
13+
p := filepath.Join(root, "dir", "sub", "file.txt")
14+
if err := os.MkdirAll(filepath.Dir(p), 0o755); err != nil { t.Fatal(err) }
15+
if err := os.WriteFile(p, []byte("ok"), 0o644); err != nil { t.Fatal(err) }
16+
17+
dec := &FSPluginDecoder{root: root}
18+
if err := dec.Open(); err != nil { t.Fatalf("init open: %v", err) }
19+
20+
// Use forward-slash logical path; decoder should convert via FromSlash at boundary
21+
b, err := dec.ReadFile("dir/sub/file.txt")
22+
if err != nil { t.Fatalf("ReadFile with forward slashes: %v", err) }
23+
if string(b) != "ok" { t.Fatalf("unexpected content: %q", string(b)) }
24+
25+
// Stat also accepts forward slashes
26+
if _, err := dec.Stat("dir/sub/file.txt"); err != nil { t.Fatalf("Stat with forward slashes: %v", err) }
27+
28+
// FileReader also accepts forward slashes
29+
r, err := dec.FileReader("dir/sub/file.txt")
30+
if err != nil { t.Fatalf("FileReader with forward slashes: %v", err) }
31+
defer r.Close()
32+
data, _ := io.ReadAll(r)
33+
if string(data) != "ok" { t.Fatalf("unexpected reader content: %q", string(data)) }
34+
}

0 commit comments

Comments
 (0)