fix(build): enhance module replacement handling for v2+ versions and implement local cloning

This commit is contained in:
LinkinStars
2026-04-15 16:15:13 +08:00
parent cfc3e54f30
commit 9da00a807d
+71 -3
View File
@@ -200,9 +200,27 @@ func createMainGoFile(b *buildingMaterial) (err error) {
// downloadGoModFile run go mod commands to download dependencies
func downloadGoModFile(b *buildingMaterial) (err error) {
// If user specify a module replacement, use it. Otherwise, use the latest version.
if len(b.answerModuleReplacement) > 0 {
replacement := fmt.Sprintf("%s=%s", "github.com/apache/answer", b.answerModuleReplacement)
answerReplacement := b.answerModuleReplacement
// If no replacement specified and current binary is v2+, auto-determine replacement.
// This is needed because go mod tidy would otherwise resolve github.com/apache/answer
// to the latest v1.x version, causing v2+ features (e.g. AI/MCP) to disappear.
if len(answerReplacement) == 0 && b.originalAnswerInfo.Version != "" {
ver, verErr := semver.NewVersion(strings.TrimPrefix(b.originalAnswerInfo.Version, "v"))
if verErr == nil && ver.Major() >= 2 {
answerReplacement = fmt.Sprintf("github.com/apache/answer@%s", b.originalAnswerInfo.Version)
}
}
if len(answerReplacement) > 0 {
// For v2+ versioned module paths (e.g. github.com/apache/answer@v2.0.0),
// go mod tidy rejects the version because the module path lacks a /v2 suffix.
// Work around this by cloning the repo locally and using a local path replacement.
localPath, resolveErr := resolveAnswerModuleReplacement(answerReplacement, b.tmpDir)
if resolveErr != nil {
return resolveErr
}
replacement := fmt.Sprintf("%s=%s", "github.com/apache/answer", localPath)
err = b.newExecCmd("go", "mod", "edit", "-replace", replacement).Run()
if err != nil {
return err
@@ -221,6 +239,56 @@ func downloadGoModFile(b *buildingMaterial) (err error) {
return
}
// resolveAnswerModuleReplacement resolves the ANSWER_MODULE value to a usable local path or
// remote replacement string. For v2+ versioned module paths (e.g. github.com/apache/answer@v2.0.0),
// Go module system rejects the version because the module path has no /v2 suffix. In that case
// the repository is cloned locally and the local path is returned instead.
func resolveAnswerModuleReplacement(replacement, tmpDir string) (string, error) {
// Local paths can be used as-is.
if strings.HasPrefix(replacement, "/") || strings.HasPrefix(replacement, "./") || strings.HasPrefix(replacement, "../") {
return replacement, nil
}
// Parse module@version format.
moduleName, version, hasVersion := strings.Cut(replacement, "@")
if !hasVersion {
return replacement, nil
}
// Only handle v2+ versions on module paths without the /vN suffix.
ver, err := semver.StrictNewVersion(strings.TrimPrefix(version, "v"))
if err != nil || ver.Major() < 2 {
return replacement, nil
}
if strings.HasSuffix(moduleName, fmt.Sprintf("/v%d", ver.Major())) {
return replacement, nil
}
// Clone the repo to a local directory and return its path.
gitURL := "https://" + moduleName
tag := "v" + strings.TrimPrefix(version, "v")
localPath := filepath.Join(filepath.Dir(tmpDir), fmt.Sprintf("answer_src_%s", strings.ReplaceAll(version, ".", "_")))
if _, statErr := os.Stat(localPath); statErr == nil {
fmt.Printf("[build] using cached local clone at %s\n", localPath)
return localPath, nil
}
fmt.Printf("[build] v2+ module detected, cloning %s@%s to local path %s...\n", moduleName, version, localPath)
cloneCmd := exec.Command("git", "clone", "--depth=1", "--branch="+tag, gitURL, localPath)
cloneCmd.Stdout = os.Stdout
cloneCmd.Stderr = os.Stderr
if err = cloneCmd.Run(); err != nil {
return "", fmt.Errorf(
"failed to clone %s@%s: %w\nTip: set ANSWER_MODULE to a local checkout path instead, e.g. ANSWER_MODULE=/path/to/answer",
moduleName, version, err,
)
}
fmt.Printf("[build] successfully cloned to %s\n", localPath)
return localPath, nil
}
// movePluginToVendor move plugin to vendor dir
// Traverse the plugins, and if the plugin path is not github.com/apache/answer-plugins, move the contents of the current plugin to the vendor/github.com/apache/answer-plugins/ directory.
func movePluginToVendor(b *buildingMaterial) (err error) {