From 8f023b8abec8851213533f748b38499ee73796ff Mon Sep 17 00:00:00 2001 From: misraved Date: Thu, 31 Jul 2025 01:55:07 +0530 Subject: [PATCH 1/5] Update sqlite build code to respect all plugin dependencies --- generate/generator.go | 121 ++++++++++++++++++++++++++++++++---------- 1 file changed, 94 insertions(+), 27 deletions(-) diff --git a/generate/generator.go b/generate/generator.go index cc62b22..229d73d 100644 --- a/generate/generator.go +++ b/generate/generator.go @@ -3,8 +3,10 @@ package main import ( + "encoding/json" "fmt" "os" + "os/exec" "path" "path/filepath" "strings" @@ -19,7 +21,84 @@ type RenderData struct { PluginVersion string } -func RenderDir(templatePath, root, pluginAlias, pluginGithubUrl, pluginVersion string) { +type ModuleInfo struct { + Path string `json:"Path"` + Version string `json:"Version"` + Dir string `json:"Dir"` +} + +func getPluginReplaceDirectives(pluginGithubUrl string) (string, error) { + // Create a temporary directory + tmpDir, err := os.MkdirTemp("", "plugin-mod") + if err != nil { + return "", fmt.Errorf("failed to create temp dir: %v", err) + } + defer os.RemoveAll(tmpDir) + + // Initialize a go module + cmd := exec.Command("go", "mod", "init", "temp") + cmd.Dir = tmpDir + if err := cmd.Run(); err != nil { + return "", fmt.Errorf("failed to init module: %v", err) + } + + // Get the latest version of the plugin + cmd = exec.Command("go", "get", pluginGithubUrl) + cmd.Dir = tmpDir + if err := cmd.Run(); err != nil { + return "", fmt.Errorf("failed to get plugin: %v", err) + } + + // Get the module info + cmd = exec.Command("go", "list", "-m", "-json", pluginGithubUrl) + cmd.Dir = tmpDir + output, err := cmd.Output() + if err != nil { + return "", fmt.Errorf("failed to get module info: %v", err) + } + + var info ModuleInfo + if err := json.Unmarshal(output, &info); err != nil { + return "", fmt.Errorf("failed to parse module info: %v", err) + } + + // Read the go.mod file + goModPath := filepath.Join(info.Dir, "go.mod") + content, err := os.ReadFile(goModPath) + if err != nil { + return "", fmt.Errorf("failed to read go.mod: %v", err) + } + + // Extract replace directives + var replaces []string + lines := strings.Split(string(content), "\n") + inReplace := false + for _, line := range lines { + line = strings.TrimSpace(line) + if strings.HasPrefix(line, "replace (") { + inReplace = true + continue + } + if inReplace { + if line == ")" { + inReplace = false + continue + } + if line != "" { + replaces = append(replaces, line) + } + } else if strings.HasPrefix(line, "replace ") { + replaces = append(replaces, strings.TrimPrefix(line, "replace ")) + } + } + + if len(replaces) > 0 { + return "\n// Replace directives from plugin\nreplace (\n\t" + strings.Join(replaces, "\n\t") + "\n)\n", nil + } + return "", nil +} + +func RenderDir(templatePath, root, pluginAlias, pluginGithubUrl string) { var targetFilePath string err := filepath.Walk(templatePath, func(filePath string, info os.FileInfo, err error) error { if err != nil { @@ -29,27 +108,19 @@ func RenderDir(templatePath, root, pluginAlias, pluginGithubUrl, pluginVersion s fmt.Println("filePath:", filePath) if info.IsDir() { - // fmt.Println("not a file, continuing...\n") return nil } relativeFilePath := strings.TrimPrefix(filePath, root) - // fmt.Println("relative path:", relativeFilePath) ext := filepath.Ext(filePath) - // fmt.Println("extension:", ext) if ext != templateExt { - // fmt.Println("not tmpl, continuing...\n") return nil } templateFileName := strings.TrimPrefix(relativeFilePath, "/templates/") - // fmt.Println("template fileName:", templateFileName) fileName := strings.TrimSuffix(templateFileName, ext) - // fmt.Println("actual fileName:", fileName) - targetFilePath = path.Join(root, fileName) - // fmt.Println("targetFilePath:", targetFilePath) // read template file templateContent, err := os.ReadFile(filePath) @@ -68,7 +139,6 @@ func RenderDir(templatePath, root, pluginAlias, pluginGithubUrl, pluginVersion s data := RenderData{ Plugin: pluginAlias, PluginGithubUrl: pluginGithubUrl, - PluginVersion: pluginVersion, } // execute the template with the data @@ -77,13 +147,22 @@ func RenderDir(templatePath, root, pluginAlias, pluginGithubUrl, pluginVersion s return err } + content := renderedContent.String() + + // If this is go.mod, append any replace directives from the plugin + if strings.HasSuffix(targetFilePath, "go.mod") { + if replaces, err := getPluginReplaceDirectives(pluginGithubUrl); err == nil && replaces != "" { + content += replaces + } + } + if err := os.MkdirAll(filepath.Dir(targetFilePath), 0755); err != nil { fmt.Printf("Error creating directory: %v\n", err) return err } // write the rendered content to the target file - if err := os.WriteFile(targetFilePath, []byte(renderedContent.String()), 0644); err != nil { + if err := os.WriteFile(targetFilePath, []byte(content), 0644); err != nil { fmt.Printf("Error writing to target file: %v\n", err) return err } @@ -100,35 +179,23 @@ func RenderDir(templatePath, root, pluginAlias, pluginGithubUrl, pluginVersion s func main() { // Check if the correct number of command-line arguments are provided if len(os.Args) < 4 { - fmt.Println("Usage: go run generator.go [plugin_version] [pluginGithubUrl]") + fmt.Println("Usage: go run generator.go [pluginGithubUrl]") return } templatePath := os.Args[1] root := os.Args[2] plugin := os.Args[3] - var pluginVersion string var pluginGithubUrl string - // Check if pluginVersion is provided as a command-line argument - if len(os.Args) >= 5 { - pluginVersion = os.Args[4] - } - // Check if PluginGithubUrl is provided as a command-line argument - if len(os.Args) >= 6 { - pluginGithubUrl = os.Args[5] + if len(os.Args) >= 5 { + pluginGithubUrl = os.Args[4] } else { // If PluginGithubUrl is not provided, generate it based on PluginAlias pluginGithubUrl = "github.com/turbot/steampipe-plugin-" + plugin } - // If pluginVersion is provided but pluginGithubUrl is not, error out - if pluginVersion != "" && pluginGithubUrl == "" { - fmt.Println("Error: plugin_github_url is required when plugin_version is specified") - return - } - // Convert relative paths to absolute paths absTemplatePath, err := filepath.Abs(templatePath) if err != nil { @@ -142,5 +209,5 @@ func main() { return } - RenderDir(absTemplatePath, absRoot, plugin, pluginGithubUrl, pluginVersion) + RenderDir(absTemplatePath, absRoot, plugin, pluginGithubUrl) } From 91ea646deb4ca09455fda82155f5ef4ee5ae9389 Mon Sep 17 00:00:00 2001 From: misraved Date: Thu, 31 Jul 2025 02:18:01 +0530 Subject: [PATCH 2/5] Apply copilot suggestions --- Makefile | 10 +++++- generate/generator.go | 84 +++++++++++++++++++++++++++++++++++++++---- 2 files changed, 86 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index ad8930a..369c2df 100644 --- a/Makefile +++ b/Makefile @@ -31,6 +31,10 @@ build: validate_plugin validate_version go get $(plugin_github_url)@$(plugin_version); \ fi && \ go mod tidy && \ + if [ -f replace.mod ]; then \ + cat replace.mod >> go.mod && \ + go mod tidy; \ + fi && \ $(MAKE) -f out/Makefile build # Copy the created binary from the work directory @@ -57,7 +61,11 @@ render: validate_plugin validate_version echo "go get $(plugin_github_url)@$(plugin_version)" && \ go get $(plugin_github_url)@$(plugin_version); \ fi && \ - go mod tidy + go mod tidy && \ + if [ -f replace.mod ]; then \ + cat replace.mod >> go.mod && \ + go mod tidy; \ + fi # Note: The work directory will contain the full code tree with rendered changes diff --git a/generate/generator.go b/generate/generator.go index 229d73d..1bcf56a 100644 --- a/generate/generator.go +++ b/generate/generator.go @@ -9,6 +9,7 @@ import ( "os/exec" "path" "path/filepath" + "regexp" "strings" "text/template" ) @@ -27,7 +28,37 @@ type ModuleInfo struct { Dir string `json:"Dir"` } +// validateGithubUrl validates that the URL is a valid GitHub repository URL +func validateGithubUrl(urlStr string) error { + if !strings.HasPrefix(urlStr, "github.com/") { + return fmt.Errorf("URL must be a GitHub repository URL (github.com/...)") + } + parts := strings.Split(urlStr, "/") + if len(parts) != 3 { + return fmt.Errorf("URL must be in format github.com/owner/repo") + } + return nil +} + +// validateReplaceDirective validates a replace directive format +func validateReplaceDirective(directive string) error { + // Basic format validation for replace directives + // Should be in format: module => replacement [version] + re := regexp.MustCompile(`^[\w\-\.\/]+(\/[\w\-\.]+)* => [\w\-\.\/]+(\/[\w\-\.]+)*( v\d+\.\d+\.\d+(-[\w\-\.]+)*)?$`) + if !re.MatchString(strings.TrimSpace(directive)) { + return fmt.Errorf("invalid replace directive format: %s", directive) + } + return nil +} + func getPluginReplaceDirectives(pluginGithubUrl string) (string, error) { + // Validate GitHub URL + if err := validateGithubUrl(pluginGithubUrl); err != nil { + return "", fmt.Errorf("invalid GitHub URL: %v", err) + } + + fmt.Printf("Getting replace directives for plugin: %s\n", pluginGithubUrl) + // Create a temporary directory tmpDir, err := os.MkdirTemp("", "plugin-mod") if err != nil { @@ -35,6 +66,8 @@ func getPluginReplaceDirectives(pluginGithubUrl string) (string, error) { } defer os.RemoveAll(tmpDir) + fmt.Printf("Created temp dir: %s\n", tmpDir) + // Initialize a go module cmd := exec.Command("go", "mod", "init", "temp") cmd.Dir = tmpDir @@ -42,26 +75,34 @@ func getPluginReplaceDirectives(pluginGithubUrl string) (string, error) { return "", fmt.Errorf("failed to init module: %v", err) } + fmt.Printf("Initialized temp module\n") + // Get the latest version of the plugin - cmd = exec.Command("go", "get", pluginGithubUrl) + cmd = exec.Command("go", "get", "--", pluginGithubUrl) cmd.Dir = tmpDir - if err := cmd.Run(); err != nil { - return "", fmt.Errorf("failed to get plugin: %v", err) + if output, err := cmd.CombinedOutput(); err != nil { + return "", fmt.Errorf("failed to get plugin: %v\nOutput: %s", err, output) } + fmt.Printf("Got plugin\n") + // Get the module info - cmd = exec.Command("go", "list", "-m", "-json", pluginGithubUrl) + cmd = exec.Command("go", "list", "-m", "-json", "--", pluginGithubUrl) cmd.Dir = tmpDir output, err := cmd.Output() if err != nil { return "", fmt.Errorf("failed to get module info: %v", err) } + fmt.Printf("Got module info: %s\n", output) + var info ModuleInfo if err := json.Unmarshal(output, &info); err != nil { return "", fmt.Errorf("failed to parse module info: %v", err) } + fmt.Printf("Module dir: %s\n", info.Dir) + // Read the go.mod file goModPath := filepath.Join(info.Dir, "go.mod") content, err := os.ReadFile(goModPath) @@ -69,6 +110,8 @@ func getPluginReplaceDirectives(pluginGithubUrl string) (string, error) { return "", fmt.Errorf("failed to read go.mod: %v", err) } + fmt.Printf("Read go.mod: %s\n", content) + // Extract replace directives var replaces []string lines := strings.Split(string(content), "\n") @@ -85,15 +128,39 @@ func getPluginReplaceDirectives(pluginGithubUrl string) (string, error) { continue } if line != "" { + // Validate replace directive + if err := validateReplaceDirective(line); err != nil { + return "", fmt.Errorf("invalid replace directive in plugin go.mod: %v", err) + } replaces = append(replaces, line) } } else if strings.HasPrefix(line, "replace ") { - replaces = append(replaces, strings.TrimPrefix(line, "replace ")) + directive := strings.TrimPrefix(line, "replace ") + // Validate replace directive + if err := validateReplaceDirective(directive); err != nil { + return "", fmt.Errorf("invalid replace directive in plugin go.mod: %v", err) + } + replaces = append(replaces, directive) } } + fmt.Printf("Found replace directives: %v\n", replaces) + if len(replaces) > 0 { - return "\n// Replace directives from plugin\nreplace (\n\t" + strings.Join(replaces, "\n\t") + "\n)\n", nil + // Write replace directives to a file + var content strings.Builder + content.WriteString("\n// Replace directives from plugin\nreplace (\n") + for _, r := range replaces { + content.WriteString("\t") + content.WriteString(r) + content.WriteString("\n") + } + content.WriteString(")\n") + + // Write to replace.mod file + if err := os.WriteFile("replace.mod", []byte(content.String()), 0644); err != nil { + return "", fmt.Errorf("failed to write replace directives: %v", err) + } } return "", nil } @@ -151,7 +218,10 @@ func RenderDir(templatePath, root, pluginAlias, pluginGithubUrl string) { // If this is go.mod, append any replace directives from the plugin if strings.HasSuffix(targetFilePath, "go.mod") { - if replaces, err := getPluginReplaceDirectives(pluginGithubUrl); err == nil && replaces != "" { + replaces, err := getPluginReplaceDirectives(pluginGithubUrl) + if err != nil { + fmt.Printf("Error getting plugin replace directives: %v\n", err) + } else if replaces != "" { content += replaces } } From d063c79320cb283c1a4a9b82c5c50075c06e4421 Mon Sep 17 00:00:00 2001 From: misraved Date: Thu, 31 Jul 2025 02:28:13 +0530 Subject: [PATCH 3/5] Address copilot review comments --- generate/generator.go | 174 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 144 insertions(+), 30 deletions(-) diff --git a/generate/generator.go b/generate/generator.go index 1bcf56a..a8d9760 100644 --- a/generate/generator.go +++ b/generate/generator.go @@ -20,6 +20,8 @@ type RenderData struct { Plugin string PluginGithubUrl string PluginVersion string + GoVersion string + GoToolchain string } type ModuleInfo struct { @@ -28,6 +30,43 @@ type ModuleInfo struct { Dir string `json:"Dir"` } +type GoVersionInfo struct { + Version string // Go version (e.g., "1.23.1") + Toolchain string // Go toolchain (e.g., "go1.24.1") +} + +// extractGoVersionInfo extracts Go version and toolchain information from go.mod content +func extractGoVersionInfo(content string) GoVersionInfo { + var info GoVersionInfo + lines := strings.Split(content, "\n") + for _, line := range lines { + line = strings.TrimSpace(line) + if strings.HasPrefix(line, "go ") { + info.Version = strings.TrimSpace(strings.TrimPrefix(line, "go")) + } else if strings.HasPrefix(line, "toolchain ") { + info.Toolchain = strings.TrimSpace(strings.TrimPrefix(line, "toolchain")) + } + } + return info +} + +// validateGoVersionInfo validates the Go version format +func validateGoVersionInfo(info GoVersionInfo) error { + if info.Version != "" { + versionRegex := regexp.MustCompile(`^\d+\.\d+(\.\d+)?$`) + if !versionRegex.MatchString(info.Version) { + return fmt.Errorf("invalid Go version format: %s", info.Version) + } + } + if info.Toolchain != "" { + toolchainRegex := regexp.MustCompile(`^go\d+\.\d+\.\d+$`) + if !toolchainRegex.MatchString(info.Toolchain) { + return fmt.Errorf("invalid Go toolchain format: %s", info.Toolchain) + } + } + return nil +} + // validateGithubUrl validates that the URL is a valid GitHub repository URL func validateGithubUrl(urlStr string) error { if !strings.HasPrefix(urlStr, "github.com/") { @@ -40,21 +79,69 @@ func validateGithubUrl(urlStr string) error { return nil } +// validateModulePath validates a Go module path format +func validateModulePath(path string) error { + // Module path should be alphanumeric with possible dots, dashes, and slashes + re := regexp.MustCompile(`^[\w\-\.]+(/[\w\-\.]+)*$`) + if !re.MatchString(path) { + return fmt.Errorf("invalid module path format: %s", path) + } + return nil +} + +// validateVersion validates a Go module version format +func validateVersion(version string) error { + if version == "" { + return nil // Version is optional + } + // Version should be in format v1.2.3 or v1.2.3-beta.1 + re := regexp.MustCompile(`^v\d+\.\d+\.\d+(-[\w\-\.]+)*$`) + if !re.MatchString(version) { + return fmt.Errorf("invalid version format: %s", version) + } + return nil +} + // validateReplaceDirective validates a replace directive format func validateReplaceDirective(directive string) error { - // Basic format validation for replace directives - // Should be in format: module => replacement [version] - re := regexp.MustCompile(`^[\w\-\.\/]+(\/[\w\-\.]+)* => [\w\-\.\/]+(\/[\w\-\.]+)*( v\d+\.\d+\.\d+(-[\w\-\.]+)*)?$`) - if !re.MatchString(strings.TrimSpace(directive)) { - return fmt.Errorf("invalid replace directive format: %s", directive) + // Split into original module and replacement + parts := strings.Split(strings.TrimSpace(directive), "=>") + if len(parts) != 2 { + return fmt.Errorf("invalid replace directive format (should be 'module => replacement [version]'): %s", directive) } + + // Validate original module path + origModule := strings.TrimSpace(parts[0]) + if err := validateModulePath(origModule); err != nil { + return fmt.Errorf("invalid original module: %v", err) + } + + // Split replacement into module and version + replacement := strings.TrimSpace(parts[1]) + repParts := strings.Fields(replacement) + if len(repParts) == 0 || len(repParts) > 2 { + return fmt.Errorf("invalid replacement format (should be 'module [version]'): %s", replacement) + } + + // Validate replacement module path + if err := validateModulePath(repParts[0]); err != nil { + return fmt.Errorf("invalid replacement module: %v", err) + } + + // Validate version if present + if len(repParts) == 2 { + if err := validateVersion(repParts[1]); err != nil { + return fmt.Errorf("invalid replacement version: %v", err) + } + } + return nil } -func getPluginReplaceDirectives(pluginGithubUrl string) (string, error) { +func getPluginReplaceDirectives(pluginGithubUrl string, targetDir string) (string, GoVersionInfo, error) { // Validate GitHub URL if err := validateGithubUrl(pluginGithubUrl); err != nil { - return "", fmt.Errorf("invalid GitHub URL: %v", err) + return "", GoVersionInfo{}, fmt.Errorf("invalid GitHub URL: %v", err) } fmt.Printf("Getting replace directives for plugin: %s\n", pluginGithubUrl) @@ -62,7 +149,7 @@ func getPluginReplaceDirectives(pluginGithubUrl string) (string, error) { // Create a temporary directory tmpDir, err := os.MkdirTemp("", "plugin-mod") if err != nil { - return "", fmt.Errorf("failed to create temp dir: %v", err) + return "", GoVersionInfo{}, fmt.Errorf("failed to create temp dir: %v", err) } defer os.RemoveAll(tmpDir) @@ -72,33 +159,42 @@ func getPluginReplaceDirectives(pluginGithubUrl string) (string, error) { cmd := exec.Command("go", "mod", "init", "temp") cmd.Dir = tmpDir if err := cmd.Run(); err != nil { - return "", fmt.Errorf("failed to init module: %v", err) + return "", GoVersionInfo{}, fmt.Errorf("failed to init module: %v", err) } fmt.Printf("Initialized temp module\n") + // Extract owner and repo from URL for safer command execution + urlParts := strings.Split(pluginGithubUrl, "/") + if len(urlParts) != 3 { + return "", GoVersionInfo{}, fmt.Errorf("invalid GitHub URL format") + } + safeUrl := fmt.Sprintf("github.com/%s/%s", urlParts[1], urlParts[2]) + // Get the latest version of the plugin - cmd = exec.Command("go", "get", "--", pluginGithubUrl) + cmd = exec.Command("go", "get") cmd.Dir = tmpDir + cmd.Args = append(cmd.Args, "--", safeUrl) // Ensure URL is treated as literal if output, err := cmd.CombinedOutput(); err != nil { - return "", fmt.Errorf("failed to get plugin: %v\nOutput: %s", err, output) + return "", GoVersionInfo{}, fmt.Errorf("failed to get plugin: %v\nOutput: %s", err, output) } fmt.Printf("Got plugin\n") // Get the module info - cmd = exec.Command("go", "list", "-m", "-json", "--", pluginGithubUrl) + cmd = exec.Command("go", "list", "-m", "-json") cmd.Dir = tmpDir + cmd.Args = append(cmd.Args, "--", safeUrl) // Ensure URL is treated as literal output, err := cmd.Output() if err != nil { - return "", fmt.Errorf("failed to get module info: %v", err) + return "", GoVersionInfo{}, fmt.Errorf("failed to get module info: %v", err) } fmt.Printf("Got module info: %s\n", output) var info ModuleInfo if err := json.Unmarshal(output, &info); err != nil { - return "", fmt.Errorf("failed to parse module info: %v", err) + return "", GoVersionInfo{}, fmt.Errorf("failed to parse module info: %v", err) } fmt.Printf("Module dir: %s\n", info.Dir) @@ -107,11 +203,17 @@ func getPluginReplaceDirectives(pluginGithubUrl string) (string, error) { goModPath := filepath.Join(info.Dir, "go.mod") content, err := os.ReadFile(goModPath) if err != nil { - return "", fmt.Errorf("failed to read go.mod: %v", err) + return "", GoVersionInfo{}, fmt.Errorf("failed to read go.mod: %v", err) } fmt.Printf("Read go.mod: %s\n", content) + // Extract Go version info + goInfo := extractGoVersionInfo(string(content)) + if err := validateGoVersionInfo(goInfo); err != nil { + return "", GoVersionInfo{}, fmt.Errorf("invalid Go version info in plugin go.mod: %v", err) + } + // Extract replace directives var replaces []string lines := strings.Split(string(content), "\n") @@ -130,7 +232,7 @@ func getPluginReplaceDirectives(pluginGithubUrl string) (string, error) { if line != "" { // Validate replace directive if err := validateReplaceDirective(line); err != nil { - return "", fmt.Errorf("invalid replace directive in plugin go.mod: %v", err) + return "", GoVersionInfo{}, fmt.Errorf("invalid replace directive in plugin go.mod: %v", err) } replaces = append(replaces, line) } @@ -138,7 +240,7 @@ func getPluginReplaceDirectives(pluginGithubUrl string) (string, error) { directive := strings.TrimPrefix(line, "replace ") // Validate replace directive if err := validateReplaceDirective(directive); err != nil { - return "", fmt.Errorf("invalid replace directive in plugin go.mod: %v", err) + return "", GoVersionInfo{}, fmt.Errorf("invalid replace directive in plugin go.mod: %v", err) } replaces = append(replaces, directive) } @@ -146,8 +248,9 @@ func getPluginReplaceDirectives(pluginGithubUrl string) (string, error) { fmt.Printf("Found replace directives: %v\n", replaces) + var replaceContent string if len(replaces) > 0 { - // Write replace directives to a file + // Create replace directives content var content strings.Builder content.WriteString("\n// Replace directives from plugin\nreplace (\n") for _, r := range replaces { @@ -156,13 +259,16 @@ func getPluginReplaceDirectives(pluginGithubUrl string) (string, error) { content.WriteString("\n") } content.WriteString(")\n") + replaceContent = content.String() - // Write to replace.mod file - if err := os.WriteFile("replace.mod", []byte(content.String()), 0644); err != nil { - return "", fmt.Errorf("failed to write replace directives: %v", err) + // Write to replace.mod file in the target directory + replaceModPath := filepath.Join(targetDir, "replace.mod") + if err := os.WriteFile(replaceModPath, []byte(replaceContent), 0644); err != nil { + return "", GoVersionInfo{}, fmt.Errorf("failed to write replace directives: %v", err) } } - return "", nil + + return replaceContent, goInfo, nil } func RenderDir(templatePath, root, pluginAlias, pluginGithubUrl string) { @@ -202,10 +308,23 @@ func RenderDir(templatePath, root, pluginAlias, pluginGithubUrl string) { // create a buffer to render the template var renderedContent strings.Builder + // If this is go.mod, get plugin info first + var goInfo GoVersionInfo + var replaces string + if strings.HasSuffix(targetFilePath, "go.mod") { + var err error + replaces, goInfo, err = getPluginReplaceDirectives(pluginGithubUrl, root) + if err != nil { + fmt.Printf("Error getting plugin replace directives: %v\n", err) + } + } + // define the data to be used in the template data := RenderData{ Plugin: pluginAlias, PluginGithubUrl: pluginGithubUrl, + GoVersion: goInfo.Version, + GoToolchain: goInfo.Toolchain, } // execute the template with the data @@ -216,14 +335,9 @@ func RenderDir(templatePath, root, pluginAlias, pluginGithubUrl string) { content := renderedContent.String() - // If this is go.mod, append any replace directives from the plugin - if strings.HasSuffix(targetFilePath, "go.mod") { - replaces, err := getPluginReplaceDirectives(pluginGithubUrl) - if err != nil { - fmt.Printf("Error getting plugin replace directives: %v\n", err) - } else if replaces != "" { - content += replaces - } + // If this is go.mod, append any replace directives + if strings.HasSuffix(targetFilePath, "go.mod") && replaces != "" { + content += replaces } if err := os.MkdirAll(filepath.Dir(targetFilePath), 0755); err != nil { From 1a1bcb7c707e95a140fd445817565410042ab925 Mon Sep 17 00:00:00 2001 From: misraved Date: Thu, 31 Jul 2025 02:46:13 +0530 Subject: [PATCH 4/5] Apply copilot suggestions --- generate/generator.go | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/generate/generator.go b/generate/generator.go index a8d9760..2e92883 100644 --- a/generate/generator.go +++ b/generate/generator.go @@ -82,7 +82,7 @@ func validateGithubUrl(urlStr string) error { // validateModulePath validates a Go module path format func validateModulePath(path string) error { // Module path should be alphanumeric with possible dots, dashes, and slashes - re := regexp.MustCompile(`^[\w\-\.]+(/[\w\-\.]+)*$`) + re := regexp.MustCompile(`^[a-zA-Z0-9._/-]+$`) if !re.MatchString(path) { return fmt.Errorf("invalid module path format: %s", path) } @@ -94,8 +94,8 @@ func validateVersion(version string) error { if version == "" { return nil // Version is optional } - // Version should be in format v1.2.3 or v1.2.3-beta.1 - re := regexp.MustCompile(`^v\d+\.\d+\.\d+(-[\w\-\.]+)*$`) + // Version should be in format v1.2.3 or v1.2.3-beta.1 or v1.2.3+build.1 + re := regexp.MustCompile(`^v\d+\.\d+\.\d+(-[\w\-\.]+)?(\+[\w\-\.]+)?$`) if !re.MatchString(version) { return fmt.Errorf("invalid version format: %s", version) } @@ -174,7 +174,7 @@ func getPluginReplaceDirectives(pluginGithubUrl string, targetDir string) (strin // Get the latest version of the plugin cmd = exec.Command("go", "get") cmd.Dir = tmpDir - cmd.Args = append(cmd.Args, "--", safeUrl) // Ensure URL is treated as literal + cmd.Args = append(cmd.Args, safeUrl) // Append URL directly if output, err := cmd.CombinedOutput(); err != nil { return "", GoVersionInfo{}, fmt.Errorf("failed to get plugin: %v\nOutput: %s", err, output) } @@ -184,7 +184,7 @@ func getPluginReplaceDirectives(pluginGithubUrl string, targetDir string) (strin // Get the module info cmd = exec.Command("go", "list", "-m", "-json") cmd.Dir = tmpDir - cmd.Args = append(cmd.Args, "--", safeUrl) // Ensure URL is treated as literal + cmd.Args = append(cmd.Args, safeUrl) // Append the URL directly output, err := cmd.Output() if err != nil { return "", GoVersionInfo{}, fmt.Errorf("failed to get module info: %v", err) @@ -272,6 +272,13 @@ func getPluginReplaceDirectives(pluginGithubUrl string, targetDir string) (strin } func RenderDir(templatePath, root, pluginAlias, pluginGithubUrl string) { + // Cache for plugin info + var ( + cachedReplaces string + cachedGoInfo GoVersionInfo + cachedUrl string + ) + var targetFilePath string err := filepath.Walk(templatePath, func(filePath string, info os.FileInfo, err error) error { if err != nil { @@ -312,11 +319,16 @@ func RenderDir(templatePath, root, pluginAlias, pluginGithubUrl string) { var goInfo GoVersionInfo var replaces string if strings.HasSuffix(targetFilePath, "go.mod") { - var err error - replaces, goInfo, err = getPluginReplaceDirectives(pluginGithubUrl, root) - if err != nil { - fmt.Printf("Error getting plugin replace directives: %v\n", err) + if cachedUrl != pluginGithubUrl { + var err error + cachedReplaces, cachedGoInfo, err = getPluginReplaceDirectives(pluginGithubUrl, root) + if err != nil { + fmt.Printf("Error getting plugin replace directives: %v\n", err) + } + cachedUrl = pluginGithubUrl } + replaces = cachedReplaces + goInfo = cachedGoInfo } // define the data to be used in the template From 86f12c115e39f0a1a1feb2e39183e2785da09d0e Mon Sep 17 00:00:00 2001 From: misraved Date: Thu, 31 Jul 2025 03:06:49 +0530 Subject: [PATCH 5/5] Apply copilot review suggestions --- generate/generator.go | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/generate/generator.go b/generate/generator.go index 2e92883..1cea649 100644 --- a/generate/generator.go +++ b/generate/generator.go @@ -59,7 +59,7 @@ func validateGoVersionInfo(info GoVersionInfo) error { } } if info.Toolchain != "" { - toolchainRegex := regexp.MustCompile(`^go\d+\.\d+\.\d+$`) + toolchainRegex := regexp.MustCompile(`^go\d+\.\d+(\.\d+)?(rc\d+|beta\d+)?$`) if !toolchainRegex.MatchString(info.Toolchain) { return fmt.Errorf("invalid Go toolchain format: %s", info.Toolchain) } @@ -81,8 +81,9 @@ func validateGithubUrl(urlStr string) error { // validateModulePath validates a Go module path format func validateModulePath(path string) error { - // Module path should be alphanumeric with possible dots, dashes, and slashes - re := regexp.MustCompile(`^[a-zA-Z0-9._/-]+$`) + // Module path should be lowercase alphanumeric with dots, hyphens, and slashes + // Each component must be valid and follow Go module naming conventions + re := regexp.MustCompile(`^[a-z0-9]+(?:[-./][a-z0-9]+)*$`) if !re.MatchString(path) { return fmt.Errorf("invalid module path format: %s", path) } @@ -94,8 +95,12 @@ func validateVersion(version string) error { if version == "" { return nil // Version is optional } - // Version should be in format v1.2.3 or v1.2.3-beta.1 or v1.2.3+build.1 - re := regexp.MustCompile(`^v\d+\.\d+\.\d+(-[\w\-\.]+)?(\+[\w\-\.]+)?$`) + // Version should be in format: + // - v1.2.3 + // - v1.2.3-beta.1 + // - v1.2.3+build.1 + // - v0.0.0-20231201123456-abcdef123456 (pseudo-versions) + re := regexp.MustCompile(`^v\d+(\.\d+){0,2}(-\d{14}-[a-f0-9]{12}|(-[\w\-\.]+)?(\+[\w\-\.]+)?)?$`) if !re.MatchString(version) { return fmt.Errorf("invalid version format: %s", version) }