summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/release/release.go6
-rw-r--r--internal/release/release_ai_tool_test.go64
-rw-r--r--internal/showcase/showcase.go6
-rw-r--r--internal/showcase/showcase_test.go42
4 files changed, 116 insertions, 2 deletions
diff --git a/internal/release/release.go b/internal/release/release.go
index d0bc3cf..9af3392 100644
--- a/internal/release/release.go
+++ b/internal/release/release.go
@@ -362,6 +362,10 @@ func (m *Manager) executeAICommand(cmd *exec.Cmd, toolName string) (string, erro
return content, nil
}
+func (m *Manager) availableReleaseNotesTools(lookPath aitool.LookPathFunc) []aitool.Tool {
+ return aitool.AvailableChain(m.aiTool, lookPath)
+}
+
// GenerateAIReleaseNotes generates prose release notes using an AI tool, with fallback.
func (m *Manager) GenerateAIReleaseNotes(repoPath, repoName, tag string, allTags []string, commits []string) (string, error) {
// Find the previous tag
@@ -417,7 +421,7 @@ func (m *Manager) GenerateAIReleaseNotes(repoPath, repoName, tag string, allTags
var releaseNotes string
- for _, tool := range aitool.AvailableChain("opencode", nil) {
+ for _, tool := range m.availableReleaseNotesTools(nil) {
switch tool {
case aitool.ToolOpencode:
fmt.Println(" Running ollama launch opencode ...")
diff --git a/internal/release/release_ai_tool_test.go b/internal/release/release_ai_tool_test.go
new file mode 100644
index 0000000..1d15280
--- /dev/null
+++ b/internal/release/release_ai_tool_test.go
@@ -0,0 +1,64 @@
+package release
+
+import (
+ "os/exec"
+ "reflect"
+ "testing"
+
+ "codeberg.org/snonux/gitsyncer/internal/aitool"
+)
+
+func TestAvailableReleaseNotesTools_DefaultChainWithFallback(t *testing.T) {
+ t.Parallel()
+
+ manager := NewManager("")
+ got := manager.availableReleaseNotesTools(fakeLookPathRelease("ollama", "claude"))
+ want := []aitool.Tool{aitool.ToolOpencode, aitool.ToolClaude}
+
+ if !reflect.DeepEqual(got, want) {
+ t.Fatalf("availableReleaseNotesTools() = %#v, want %#v", got, want)
+ }
+}
+
+func TestAvailableReleaseNotesTools_HonorsConfiguredPreferenceWithFallback(t *testing.T) {
+ t.Parallel()
+
+ manager := NewManager("")
+ manager.SetAITool("hexai")
+
+ got := manager.availableReleaseNotesTools(fakeLookPathRelease("claude", "amp"))
+ want := []aitool.Tool{aitool.ToolClaude, aitool.ToolAmp}
+
+ if !reflect.DeepEqual(got, want) {
+ t.Fatalf("availableReleaseNotesTools() = %#v, want %#v", got, want)
+ }
+}
+
+func TestAvailableReleaseNotesTools_AmpChainOnly(t *testing.T) {
+ t.Parallel()
+
+ manager := NewManager("")
+ manager.SetAITool("amp")
+
+ got := manager.availableReleaseNotesTools(fakeLookPathRelease("ollama", "amp"))
+ want := []aitool.Tool{aitool.ToolAmp}
+
+ if !reflect.DeepEqual(got, want) {
+ t.Fatalf("availableReleaseNotesTools() = %#v, want %#v", got, want)
+ }
+}
+
+func fakeLookPathRelease(tools ...string) func(string) (string, error) {
+ available := make(map[string]struct{}, len(tools))
+ for _, tool := range tools {
+ available[tool] = struct{}{}
+ }
+
+ return func(file string) (string, error) {
+ if _, ok := available[file]; ok {
+ return "/usr/bin/" + file, nil
+ }
+
+ return "", exec.ErrNotFound
+ }
+}
diff --git a/internal/showcase/showcase.go b/internal/showcase/showcase.go
index b9e1fe1..7b80de6 100644
--- a/internal/showcase/showcase.go
+++ b/internal/showcase/showcase.go
@@ -231,7 +231,11 @@ func findReadmeContent(repoPath string) ([]byte, string, bool) {
}
func selectSummaryTool(aiTool string) string {
- return string(aitool.FirstAvailable(aiTool, nil))
+ return selectSummaryToolWithLookPath(aiTool, nil)
+}
+
+func selectSummaryToolWithLookPath(aiTool string, lookPath aitool.LookPathFunc) string {
+ return string(aitool.FirstAvailable(aiTool, lookPath))
}
func runSummaryTool(selectedTool, prompt, repoPath, readmeFile string, readmeContent []byte, readmeFound bool) string {
diff --git a/internal/showcase/showcase_test.go b/internal/showcase/showcase_test.go
index cd571f8..ca40293 100644
--- a/internal/showcase/showcase_test.go
+++ b/internal/showcase/showcase_test.go
@@ -214,6 +214,33 @@ func TestFallbackSummary_SkipsHeadingOnlyParagraphs(t *testing.T) {
}
}
+func TestSelectSummaryTool_DefaultPrefersOpencode(t *testing.T) {
+ t.Parallel()
+
+ tool := selectSummaryToolWithLookPath("", fakeLookPathShowcase("ollama", "claude", "amp"))
+ if tool != "opencode" {
+ t.Fatalf("selectSummaryToolWithLookPath() = %q, want %q", tool, "opencode")
+ }
+}
+
+func TestSelectSummaryTool_HonorsConfiguredToolWithFallback(t *testing.T) {
+ t.Parallel()
+
+ tool := selectSummaryToolWithLookPath("hexai", fakeLookPathShowcase("claude", "amp"))
+ if tool != "claude" {
+ t.Fatalf("selectSummaryToolWithLookPath() = %q, want %q", tool, "claude")
+ }
+}
+
+func TestSelectSummaryTool_UsesAmpOnlyChain(t *testing.T) {
+ t.Parallel()
+
+ tool := selectSummaryToolWithLookPath("amp", fakeLookPathShowcase("ollama", "amp"))
+ if tool != "amp" {
+ t.Fatalf("selectSummaryToolWithLookPath() = %q, want %q", tool, "amp")
+ }
+}
+
func TestExtractUsefulSummary_SkipsNonProseParagraphs(t *testing.T) {
t.Parallel()
@@ -373,3 +400,18 @@ func TestPrepareStatsRepoPath_UsesRemoteTrackingBranchWhenLocalBranchMissing(t *
t.Fatalf("current branch = %q, want %q", currentBranch, "main")
}
}
+
+func fakeLookPathShowcase(tools ...string) func(string) (string, error) {
+ available := make(map[string]struct{}, len(tools))
+ for _, tool := range tools {
+ available[tool] = struct{}{}
+ }
+
+ return func(file string) (string, error) {
+ if _, ok := available[file]; ok {
+ return "/usr/bin/" + file, nil
+ }
+
+ return "", exec.ErrNotFound
+ }
+}