From 21a1d3e3167fc857a96139d7b1ec67e1f90003e8 Mon Sep 17 00:00:00 2001 From: Pedro Enrique Date: Wed, 28 May 2025 19:49:19 +0200 Subject: [PATCH 1/4] [AGENT-232] Fix issue with python project name --- cmd/agent.go | 28 +++++++++---------- cmd/profile.go | 2 +- cmd/project.go | 2 +- internal/bundler/bundler.go | 6 +--- internal/project/project.go | 8 ++++++ internal/templates/steps.go | 2 +- internal/templates/template.go | 5 +--- internal/util/strings.go | 29 ++++++++++---------- internal/util/strings_test.go | 50 +++++++++++++++++----------------- 9 files changed, 65 insertions(+), 67 deletions(-) diff --git a/cmd/agent.go b/cmd/agent.go index e753adee..0cddf28b 100644 --- a/cmd/agent.go +++ b/cmd/agent.go @@ -369,10 +369,6 @@ func getAgentList(logger logger.Logger, apiUrl string, apikey string, project pr return remoteAgents, err } -func normalAgentName(name string) string { - return util.SafeFilename(strings.ToLower(name)) -} - func reconcileAgentList(logger logger.Logger, cmd *cobra.Command, apiUrl string, apikey string, theproject project.ProjectContext) ([]string, map[string]agentListState) { remoteAgents, err := getAgentList(logger, apiUrl, apikey, theproject) if err != nil { @@ -395,7 +391,8 @@ func reconcileAgentList(logger logger.Logger, cmd *cobra.Command, apiUrl string, fileAgents := make(map[string]project.AgentConfig) fileAgentsByID := make(map[string]project.AgentConfig) for _, agent := range theproject.Project.Agents { - fileAgents[normalAgentName(agent.Name)] = agent + projectName := theproject.Project.SafeFilename() + fileAgents[projectName] = agent fileAgentsByID[agent.ID] = agent } @@ -405,10 +402,11 @@ func reconcileAgentList(logger logger.Logger, cmd *cobra.Command, apiUrl string, // perform the reconcilation state := make(map[string]agentListState) for _, agent := range remoteAgents { - state[normalAgentName(agent.Name)] = agentListState{ + projectName := theproject.Project.SafeFilename() + state[projectName] = agentListState{ Agent: &agent, - Filename: filepath.Join(agentSrcDir, util.SafeFilename(agent.Name), agentFilename), - FoundLocal: util.Exists(filepath.Join(agentSrcDir, util.SafeFilename(agent.Name), agentFilename)), + Filename: filepath.Join(agentSrcDir, projectName, agentFilename), + FoundLocal: util.Exists(filepath.Join(agentSrcDir, projectName, agentFilename)), FoundRemote: true, } } @@ -418,14 +416,14 @@ func reconcileAgentList(logger logger.Logger, cmd *cobra.Command, apiUrl string, } for _, filename := range localAgents { agentName := filepath.Base(filepath.Dir(filename)) - key := normalAgentName(agentName) + projectName := theproject.Project.SafeFilename() // var found bool // for _, agent := range remoteAgents { // if localAgent, ok := fileAgentsByID[agent.ID]; ok { // if localAgent.Name == agentName { // oldkey := normalAgentName(agent.Name) // agent.Name = localAgent.Name - // state[key] = agentListState{ + // state[projectName] = agentListState{ // Agent: &agent, // Filename: filename, // FoundLocal: true, @@ -443,8 +441,8 @@ func reconcileAgentList(logger logger.Logger, cmd *cobra.Command, apiUrl string, // continue // } if filepath.Base(filename) == agentFilename { - if found, ok := state[key]; ok { - state[key] = agentListState{ + if found, ok := state[projectName]; ok { + state[projectName] = agentListState{ Agent: found.Agent, Filename: filename, FoundLocal: true, @@ -452,15 +450,15 @@ func reconcileAgentList(logger logger.Logger, cmd *cobra.Command, apiUrl string, } continue } - if a, ok := fileAgents[key]; ok { - state[key] = agentListState{ + if a, ok := fileAgents[projectName]; ok { + state[projectName] = agentListState{ Agent: &agent.Agent{Name: a.Name, ID: a.ID, Description: a.Description}, Filename: filename, FoundLocal: true, FoundRemote: true, } } else { - state[key] = agentListState{ + state[projectName] = agentListState{ Agent: &agent.Agent{Name: agentName}, Filename: filename, FoundLocal: true, diff --git a/cmd/profile.go b/cmd/profile.go index da3582ee..7d13d283 100644 --- a/cmd/profile.go +++ b/cmd/profile.go @@ -154,7 +154,7 @@ Examples: } return nil }) - fp := filepath.Join(filepath.Dir(cfgFile), util.SafeFilename(name)+".yaml") + fp := filepath.Join(filepath.Dir(cfgFile), util.SafeProjectFilename(name, false)+".yaml") os.WriteFile(fp, []byte(fmt.Sprintf(`name: "%s"`, name)+"\n"), 0644) }, } diff --git a/cmd/project.go b/cmd/project.go index d9b27747..86f2e22a 100644 --- a/cmd/project.go +++ b/cmd/project.go @@ -481,7 +481,7 @@ Examples: provider = resp.Provider } } - projectDir := filepath.Join(cwd, util.SafeFilename(name)) + projectDir := filepath.Join(cwd, util.SafeProjectFilename(name, provider.Language == "python")) dir, _ := cmd.Flags().GetString("dir") if dir != "" { absDir, err := filepath.Abs(dir) diff --git a/internal/bundler/bundler.go b/internal/bundler/bundler.go index 95e3c126..e59039d7 100644 --- a/internal/bundler/bundler.go +++ b/internal/bundler/bundler.go @@ -336,11 +336,7 @@ func getAgents(theproject *project.Project, filename string) []AgentConfig { var agents []AgentConfig for _, agent := range theproject.Agents { var agentfilename string - if theproject.Bundler.Language == "python" { - agentfilename = util.SafePythonFilename(agent.Name) - } else { - agentfilename = util.SafeFilename(agent.Name) - } + agentfilename = theproject.SafeFilename() agents = append(agents, AgentConfig{ ID: agent.ID, Name: agent.Name, diff --git a/internal/project/project.go b/internal/project/project.go index ba8310a5..55085b38 100644 --- a/internal/project/project.go +++ b/internal/project/project.go @@ -153,6 +153,14 @@ type Project struct { Agents []AgentConfig `json:"agents" yaml:"agents" hc:"The agents that are part of this project"` } +func (p *Project) SafeFilename() string { + return util.SafeProjectFilename(p.Name, p.IsPython()) +} + +func (p *Project) IsPython() bool { + return p.Bundler.Language == "python" +} + // Load will load the project from a file in the given directory. func (p *Project) Load(dir string) error { fn := getFilename(dir) diff --git a/internal/templates/steps.go b/internal/templates/steps.go index 3526bccb..30ce6472 100644 --- a/internal/templates/steps.go +++ b/internal/templates/steps.go @@ -546,7 +546,7 @@ func resolveStep(ctx TemplateContext, step any) (Step, bool) { } var name string if val, ok := kv["name"].(string); ok { - name = util.SafeFilename(ctx.Interpolate(val).(string)) + name = util.SafeProjectFilename(ctx.Interpolate(val).(string), ctx.Template.Language == "python") } var version string if val, ok := kv["version"].(string); ok { diff --git a/internal/templates/template.go b/internal/templates/template.go index 1fb51377..8a0a8e54 100644 --- a/internal/templates/template.go +++ b/internal/templates/template.go @@ -29,10 +29,7 @@ type TemplateContext struct { func funcTemplates(t *template.Template, isPython bool) *template.Template { return t.Funcs(template.FuncMap{ "safe_filename": func(s string) string { - if isPython { - return util.SafePythonFilename(s) - } - return util.SafeFilename(s) + return util.SafeProjectFilename(s, isPython) }, }) } diff --git a/internal/util/strings.go b/internal/util/strings.go index de497b50..2d8ebd25 100644 --- a/internal/util/strings.go +++ b/internal/util/strings.go @@ -11,22 +11,21 @@ var beginsWithNumber = regexp.MustCompile(`^[0-9]+`) var removeStartingDashes = regexp.MustCompile(`^[-]+`) var removeEndingDashes = regexp.MustCompile(`[-]+$`) -func SafeFilename(name string) string { - return safeNameTransformer.ReplaceAllString(name, "-") -} - -func SafePythonFilename(name string) string { - if beginsWithNumber.MatchString(name) { - name = beginsWithNumber.ReplaceAllString(name, "") +func SafeProjectFilename(name string, python bool) string { + if python { + if beginsWithNumber.MatchString(name) { + name = beginsWithNumber.ReplaceAllString(name, "") + } + name = safePythonNameTransformer.ReplaceAllString(name, "_") + if removeStartingDashes.MatchString(name) { + name = removeStartingDashes.ReplaceAllString(name, "") + } + if removeEndingDashes.MatchString(name) { + name = removeEndingDashes.ReplaceAllString(name, "") + } + return name } - name = safePythonNameTransformer.ReplaceAllString(name, "_") - if removeStartingDashes.MatchString(name) { - name = removeStartingDashes.ReplaceAllString(name, "") - } - if removeEndingDashes.MatchString(name) { - name = removeEndingDashes.ReplaceAllString(name, "") - } - return name + return safeNameTransformer.ReplaceAllString(name, "-") } func Pluralize(count int, singular string, plural string) string { diff --git a/internal/util/strings_test.go b/internal/util/strings_test.go index 5f791299..44327d1c 100644 --- a/internal/util/strings_test.go +++ b/internal/util/strings_test.go @@ -7,30 +7,6 @@ import ( "github.com/stretchr/testify/assert" ) -func TestSafeFilename(t *testing.T) { - tests := []struct { - name string - input string - expected string - }{ - {"empty string", "", ""}, - {"no special characters", "filename", "filename"}, - {"with spaces", "file name", "file-name"}, - {"with special characters", "file@#$%^&*()name", "file---------name"}, - {"mixed case", "FileName", "FileName"}, - {"with numbers", "file123name", "file123name"}, - {"with underscores", "file_name", "file_name"}, - {"with hyphens", "file-name", "file-name"}, - } - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - result := SafeFilename(test.input) - assert.Equal(t, test.expected, result) - }) - } -} - func TestPluralize(t *testing.T) { tests := []struct { name string @@ -99,7 +75,31 @@ func TestSafePythonFilename(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - result := SafePythonFilename(test.input) + result := SafeProjectFilename(test.input, true) + assert.Equal(t, test.expected, result) + }) + } +} + +func TestSafeFilename(t *testing.T) { + tests := []struct { + name string + input string + expected string + }{ + {"empty string", "", ""}, + {"no special characters", "filename", "filename"}, + {"with spaces", "file name", "file-name"}, + {"with special characters", "file@#$%^&*()name", "file---------name"}, + {"mixed case", "FileName", "FileName"}, + {"with numbers", "file123name", "file123name"}, + {"with underscores", "file_name", "file_name"}, + {"with hyphens", "file-name", "file-name"}, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result := SafeProjectFilename(test.input, false) assert.Equal(t, test.expected, result) }) } From 23db0476c9252c9a9e55936c69e79ba528b9ff8a Mon Sep 17 00:00:00 2001 From: Pedro Enrique Date: Wed, 28 May 2025 19:52:34 +0200 Subject: [PATCH 2/4] lowecase it --- internal/util/strings.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/util/strings.go b/internal/util/strings.go index 2d8ebd25..ead53280 100644 --- a/internal/util/strings.go +++ b/internal/util/strings.go @@ -3,6 +3,7 @@ package util import ( "fmt" "regexp" + "strings" ) var safeNameTransformer = regexp.MustCompile(`[^a-zA-Z0-9_-]`) @@ -12,6 +13,7 @@ var removeStartingDashes = regexp.MustCompile(`^[-]+`) var removeEndingDashes = regexp.MustCompile(`[-]+$`) func SafeProjectFilename(name string, python bool) string { + name = strings.ToLower(name) if python { if beginsWithNumber.MatchString(name) { name = beginsWithNumber.ReplaceAllString(name, "") From 6f424397bf40995e3348af3d9bd00df1a7f35418 Mon Sep 17 00:00:00 2001 From: Pedro Enrique Date: Wed, 28 May 2025 20:06:28 +0200 Subject: [PATCH 3/4] thanks coderrabit! --- cmd/agent.go | 26 +++++++++++++------------- internal/bundler/bundler.go | 2 +- internal/project/project.go | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/cmd/agent.go b/cmd/agent.go index 0cddf28b..1ddd3f89 100644 --- a/cmd/agent.go +++ b/cmd/agent.go @@ -391,8 +391,8 @@ func reconcileAgentList(logger logger.Logger, cmd *cobra.Command, apiUrl string, fileAgents := make(map[string]project.AgentConfig) fileAgentsByID := make(map[string]project.AgentConfig) for _, agent := range theproject.Project.Agents { - projectName := theproject.Project.SafeFilename() - fileAgents[projectName] = agent + key := util.SafeProjectFilename(agent.Name, theproject.Project.IsPython()) + fileAgents[key] = agent fileAgentsByID[agent.ID] = agent } @@ -402,11 +402,11 @@ func reconcileAgentList(logger logger.Logger, cmd *cobra.Command, apiUrl string, // perform the reconcilation state := make(map[string]agentListState) for _, agent := range remoteAgents { - projectName := theproject.Project.SafeFilename() - state[projectName] = agentListState{ + key := util.SafeProjectFilename(agent.Name, theproject.Project.IsPython()) + state[key] = agentListState{ Agent: &agent, - Filename: filepath.Join(agentSrcDir, projectName, agentFilename), - FoundLocal: util.Exists(filepath.Join(agentSrcDir, projectName, agentFilename)), + Filename: filepath.Join(agentSrcDir, key, agentFilename), + FoundLocal: util.Exists(filepath.Join(agentSrcDir, key, agentFilename)), FoundRemote: true, } } @@ -416,14 +416,14 @@ func reconcileAgentList(logger logger.Logger, cmd *cobra.Command, apiUrl string, } for _, filename := range localAgents { agentName := filepath.Base(filepath.Dir(filename)) - projectName := theproject.Project.SafeFilename() + key := util.SafeProjectFilename(agentName, theproject.Project.IsPython()) // var found bool // for _, agent := range remoteAgents { // if localAgent, ok := fileAgentsByID[agent.ID]; ok { // if localAgent.Name == agentName { // oldkey := normalAgentName(agent.Name) // agent.Name = localAgent.Name - // state[projectName] = agentListState{ + // state[key] = agentListState{ // Agent: &agent, // Filename: filename, // FoundLocal: true, @@ -441,8 +441,8 @@ func reconcileAgentList(logger logger.Logger, cmd *cobra.Command, apiUrl string, // continue // } if filepath.Base(filename) == agentFilename { - if found, ok := state[projectName]; ok { - state[projectName] = agentListState{ + if found, ok := state[key]; ok { + state[key] = agentListState{ Agent: found.Agent, Filename: filename, FoundLocal: true, @@ -450,15 +450,15 @@ func reconcileAgentList(logger logger.Logger, cmd *cobra.Command, apiUrl string, } continue } - if a, ok := fileAgents[projectName]; ok { - state[projectName] = agentListState{ + if a, ok := fileAgents[key]; ok { + state[key] = agentListState{ Agent: &agent.Agent{Name: a.Name, ID: a.ID, Description: a.Description}, Filename: filename, FoundLocal: true, FoundRemote: true, } } else { - state[projectName] = agentListState{ + state[key] = agentListState{ Agent: &agent.Agent{Name: agentName}, Filename: filename, FoundLocal: true, diff --git a/internal/bundler/bundler.go b/internal/bundler/bundler.go index e59039d7..68be6cf7 100644 --- a/internal/bundler/bundler.go +++ b/internal/bundler/bundler.go @@ -336,7 +336,7 @@ func getAgents(theproject *project.Project, filename string) []AgentConfig { var agents []AgentConfig for _, agent := range theproject.Agents { var agentfilename string - agentfilename = theproject.SafeFilename() + agentfilename = util.SafeProjectFilename(agent.Name, theproject.IsPython()) agents = append(agents, AgentConfig{ ID: agent.ID, Name: agent.Name, diff --git a/internal/project/project.go b/internal/project/project.go index 55085b38..61426b62 100644 --- a/internal/project/project.go +++ b/internal/project/project.go @@ -158,7 +158,7 @@ func (p *Project) SafeFilename() string { } func (p *Project) IsPython() bool { - return p.Bundler.Language == "python" + return p.Bundler.Language == "python" || p.Bundler.Language == "py" } // Load will load the project from a file in the given directory. From ec931f4500b1760d1a0cf6c94ecd16c6510561ad Mon Sep 17 00:00:00 2001 From: Pedro Enrique Date: Wed, 28 May 2025 22:21:26 +0200 Subject: [PATCH 4/4] only lowercase agent names --- cmd/agent.go | 10 +++++++--- internal/util/strings.go | 2 -- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cmd/agent.go b/cmd/agent.go index 1ddd3f89..c72884bb 100644 --- a/cmd/agent.go +++ b/cmd/agent.go @@ -369,6 +369,10 @@ func getAgentList(logger logger.Logger, apiUrl string, apikey string, project pr return remoteAgents, err } +func normalAgentName(name string, isPython bool) string { + return util.SafeProjectFilename(strings.ToLower(name), isPython) +} + func reconcileAgentList(logger logger.Logger, cmd *cobra.Command, apiUrl string, apikey string, theproject project.ProjectContext) ([]string, map[string]agentListState) { remoteAgents, err := getAgentList(logger, apiUrl, apikey, theproject) if err != nil { @@ -391,7 +395,7 @@ func reconcileAgentList(logger logger.Logger, cmd *cobra.Command, apiUrl string, fileAgents := make(map[string]project.AgentConfig) fileAgentsByID := make(map[string]project.AgentConfig) for _, agent := range theproject.Project.Agents { - key := util.SafeProjectFilename(agent.Name, theproject.Project.IsPython()) + key := normalAgentName(agent.Name, theproject.Project.IsPython()) fileAgents[key] = agent fileAgentsByID[agent.ID] = agent } @@ -402,7 +406,7 @@ func reconcileAgentList(logger logger.Logger, cmd *cobra.Command, apiUrl string, // perform the reconcilation state := make(map[string]agentListState) for _, agent := range remoteAgents { - key := util.SafeProjectFilename(agent.Name, theproject.Project.IsPython()) + key := normalAgentName(agent.Name, theproject.Project.IsPython()) state[key] = agentListState{ Agent: &agent, Filename: filepath.Join(agentSrcDir, key, agentFilename), @@ -416,7 +420,7 @@ func reconcileAgentList(logger logger.Logger, cmd *cobra.Command, apiUrl string, } for _, filename := range localAgents { agentName := filepath.Base(filepath.Dir(filename)) - key := util.SafeProjectFilename(agentName, theproject.Project.IsPython()) + key := normalAgentName(agentName, theproject.Project.IsPython()) // var found bool // for _, agent := range remoteAgents { // if localAgent, ok := fileAgentsByID[agent.ID]; ok { diff --git a/internal/util/strings.go b/internal/util/strings.go index ead53280..2d8ebd25 100644 --- a/internal/util/strings.go +++ b/internal/util/strings.go @@ -3,7 +3,6 @@ package util import ( "fmt" "regexp" - "strings" ) var safeNameTransformer = regexp.MustCompile(`[^a-zA-Z0-9_-]`) @@ -13,7 +12,6 @@ var removeStartingDashes = regexp.MustCompile(`^[-]+`) var removeEndingDashes = regexp.MustCompile(`[-]+$`) func SafeProjectFilename(name string, python bool) string { - name = strings.ToLower(name) if python { if beginsWithNumber.MatchString(name) { name = beginsWithNumber.ReplaceAllString(name, "")