You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@answer.apache.org by li...@apache.org on 2026/04/15 08:15:50 UTC
(answer) 02/02: fix(build): enhance module replacement handling for v2+ versions and implement local cloning
This is an automated email from the ASF dual-hosted git repository.
linkinstar pushed a commit to branch fix/2.0.1/chat
in repository https://gitbox.apache.org/repos/asf/answer.git
commit 9da00a807db70ebd9033dc8298707d0a1e68ce52
Author: LinkinStars <li...@foxmail.com>
AuthorDate: Wed Apr 15 16:15:13 2026 +0800
fix(build): enhance module replacement handling for v2+ versions and implement local cloning
---
internal/cli/build.go | 74 ++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 71 insertions(+), 3 deletions(-)
diff --git a/internal/cli/build.go b/internal/cli/build.go
index efc876e3..483219d7 100644
--- a/internal/cli/build.go
+++ b/internal/cli/build.go
@@ -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) {