diff options
| -rw-r--r-- | internal/release/release.go | 6 | ||||
| -rw-r--r-- | internal/release/release_ai_tool_test.go | 64 | ||||
| -rw-r--r-- | internal/showcase/showcase.go | 6 | ||||
| -rw-r--r-- | internal/showcase/showcase_test.go | 42 |
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 + } +} |
