Skip to content

Commit 1df000e

Browse files
committed
feat: Add Go vendor directory support
WHAT: - Detect vendor/modules.txt to identify projects using vendoring - Automatically run 'go mod vendor' after 'go mod tidy' when vendor exists - This keeps vendor/ directory in sync with go.mod/go.sum after dependency updates WHY: - Renovate and Dependabot both support vendor directories - When dependencies are updated, vendor/ needs to be regenerated - Following industry best practices from Renovate's postUpdateOptions HOW IT WORKS: 1. After running 'go get package@version' 2. Run 'go mod tidy' (already existed) 3. NEW: Check for vendor/modules.txt 4. NEW: If found, run 'go mod vendor' to update vendor/ directory TESTING: - Test repo: frogbot-test-multi-go (has vendor/ in both projects) - Expected: After fix, vendor/ directory should be updated in PR REFERENCES: - Renovate: config.postUpdateOptions includes 'gomodTidy' - Dependabot: Automatically detects and updates vendor directories - Analysis: dependency-update-tools-analysis-mermaid.md
1 parent 9d99448 commit 1df000e

1 file changed

Lines changed: 41 additions & 0 deletions

File tree

packagehandlers/gopackagehandler.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"os"
66
"os/exec"
7+
"path/filepath"
78
"strings"
89

910
"github.com/jfrog/frogbot/v2/utils"
@@ -66,6 +67,7 @@ func (gpu *GoPackageUpdater) updateDependency(vulnDetails *utils.VulnerabilityDe
6667
}
6768

6869
func (gpu *GoPackageUpdater) tidyLockfiles(env []string) error {
70+
// Run go mod tidy to clean up go.mod and go.sum
6971
cmd := exec.Command("go", "mod", "tidy")
7072
cmd.Env = env
7173
log.Debug("Running 'go mod tidy'")
@@ -79,5 +81,44 @@ func (gpu *GoPackageUpdater) tidyLockfiles(env []string) error {
7981
if err != nil {
8082
return fmt.Errorf("go mod tidy failed: %s\n%s", err.Error(), output)
8183
}
84+
85+
// If vendor directory exists, update it (like Renovate/Dependabot)
86+
if gpu.hasVendorDirectory() {
87+
if err := gpu.updateVendor(env); err != nil {
88+
return err
89+
}
90+
}
91+
92+
return nil
93+
}
94+
95+
// hasVendorDirectory checks if vendor/modules.txt exists
96+
// This is the standard way to detect if a Go project uses vendoring
97+
func (gpu *GoPackageUpdater) hasVendorDirectory() bool {
98+
vendorModulesPath := filepath.Join("vendor", "modules.txt")
99+
if _, err := os.Stat(vendorModulesPath); err == nil {
100+
log.Debug(fmt.Sprintf("Detected vendor directory at: %s", vendorModulesPath))
101+
return true
102+
}
103+
return false
104+
}
105+
106+
// updateVendor runs 'go mod vendor' to update the vendor directory
107+
func (gpu *GoPackageUpdater) updateVendor(env []string) error {
108+
vendorCmd := exec.Command("go", "mod", "vendor")
109+
vendorCmd.Env = env
110+
log.Debug("Running 'go mod vendor' to update vendored dependencies")
111+
112+
//#nosec G204 -- False positive - the subprocess only runs after the user's approval.
113+
vendorOutput, err := vendorCmd.CombinedOutput()
114+
if len(vendorOutput) > 0 {
115+
log.Debug(fmt.Sprintf("go mod vendor output:\n%s", string(vendorOutput)))
116+
}
117+
118+
if err != nil {
119+
return fmt.Errorf("go mod vendor failed: %s\n%s", err.Error(), vendorOutput)
120+
}
121+
122+
log.Debug("Successfully updated vendor directory")
82123
return nil
83124
}

0 commit comments

Comments
 (0)