diff --git a/cmd/validate/main.go b/cmd/validate/main.go index 1e0459da..f89c7cdc 100644 --- a/cmd/validate/main.go +++ b/cmd/validate/main.go @@ -51,6 +51,10 @@ func run(name string) error { return err } + if err := isRunVolumesValid(name); err != nil { + return err + } + if err := IsLicenseValid(name); err != nil { return err } @@ -146,6 +150,53 @@ func isConfigEnvValid(name string) error { return nil } +// Check volume parameter usage is valid +func isRunVolumesValid(name string) error { + server, err := readServerYaml(name) + if err != nil { + return err + } + + for _, vol := range server.Run.Volumes { + if !strings.Contains(vol, "{{") { + continue + } + // Extract all template parameters from the volume string + start := strings.Index(vol, "{{") + for start != -1 { + end := strings.Index(vol[start:], "}}") + if end == -1 { + break + } + end += start + 2 + param := vol[start:end] + + // Skip if it's a filter expression (contains |) + paramContent := param[2 : len(param)-2] + if strings.Contains(paramContent, "|") { + // Extract just the parameter name before the first | + paramName := strings.Split(paramContent, "|")[0] + if !strings.HasPrefix(paramName, server.Name+".") { + return fmt.Errorf("volume parameter must be prefixed with %q but found %q in: %q", server.Name+".", paramName, vol) + } + } else { + // Regular parameter without filters + if !strings.HasPrefix(paramContent, server.Name+".") { + return fmt.Errorf("volume parameter must be prefixed with %q but found %q in: %q", server.Name+".", paramContent, vol) + } + } + + start = strings.Index(vol[end:], "{{") + if start != -1 { + start += end + } + } + } + + fmt.Println("✅ Run volumes are valid") + return nil +} + // check if the license is valid // the license must be valid func IsLicenseValid(name string) error { diff --git a/cmd/validate/main_test.go b/cmd/validate/main_test.go index a66369be..31aa7bc9 100644 --- a/cmd/validate/main_test.go +++ b/cmd/validate/main_test.go @@ -1,6 +1,8 @@ package main import ( + "os" + "path/filepath" "testing" ) @@ -66,6 +68,21 @@ func Test_isNameValid(t *testing.T) { } func Test_areSecretsValid(t *testing.T) { + // Change to repo root for tests + originalWd, err := os.Getwd() + if err != nil { + t.Fatalf("Failed to get current directory: %v", err) + } + repoRoot := filepath.Join(originalWd, "..", "..") + if err := os.Chdir(repoRoot); err != nil { + t.Fatalf("Failed to change to repo root: %v", err) + } + defer func() { + if err := os.Chdir(originalWd); err != nil { + t.Errorf("Failed to restore original directory: %v", err) + } + }() + type args struct { name string } @@ -104,3 +121,58 @@ func Test_areSecretsValid(t *testing.T) { }) } } + +func Test_isRunVolumesValid(t *testing.T) { + // Change to repo root for tests + originalWd, err := os.Getwd() + if err != nil { + t.Fatalf("Failed to get current directory: %v", err) + } + repoRoot := filepath.Join(originalWd, "..", "..") + if err := os.Chdir(repoRoot); err != nil { + t.Fatalf("Failed to change to repo root: %v", err) + } + defer func() { + if err := os.Chdir(originalWd); err != nil { + t.Errorf("Failed to restore original directory: %v", err) + } + }() + + type args struct { + name string + } + tests := []struct { + name string + args args + wantError bool + }{ + { + name: "valid volume with parameter", + args: args{ + name: "arxiv-mcp-server", + }, + wantError: false, + }, + { + name: "valid volume with filter", + args: args{ + name: "markdownify", + }, + wantError: false, + }, + { + name: "no volumes", + args: args{ + name: "astra-db", + }, + wantError: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isRunVolumesValid(tt.args.name); (got != nil) != tt.wantError { + t.Errorf("isRunVolumesValid() = %v, want %v", got, tt.wantError) + } + }) + } +}