From 5bb121911e9b60704154bbc89e238b97439e2bcf Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Mon, 20 Apr 2026 09:38:01 +0300 Subject: z4: fix verification lint cleanup --- cmd/comicforge/cli.go | 2 +- cmd/comicforge/cli_test.go | 18 ++++++++++++++---- internal/comic/artist.go | 2 +- internal/comic/narrator.go | 6 ++++-- internal/comic/runner.go | 12 ++++++++---- internal/comic/types.go | 13 +------------ internal/httpctx/httpctx_test.go | 2 +- internal/image/download.go | 8 ++++++-- internal/image/gemini.go | 4 +++- internal/image/gemini_test.go | 15 +++++++++++---- internal/image/types_test.go | 2 +- internal/provider/provider_test.go | 8 -------- internal/text/gemini.go | 2 +- internal/tts/gemini.go | 2 +- 14 files changed, 53 insertions(+), 43 deletions(-) diff --git a/cmd/comicforge/cli.go b/cmd/comicforge/cli.go index 270ed72..9da2eb3 100644 --- a/cmd/comicforge/cli.go +++ b/cmd/comicforge/cli.go @@ -77,7 +77,7 @@ func newRootCommandWithDeps(deps commandDeps) *cobra.Command { SilenceErrors: true, RunE: func(cmd *cobra.Command, args []string) error { if flags.version { - fmt.Fprintln(cmd.OutOrStdout(), version.Version) + _, _ = fmt.Fprintln(cmd.OutOrStdout(), version.Version) return nil } if strings.TrimSpace(flags.vocab) == "" { diff --git a/cmd/comicforge/cli_test.go b/cmd/comicforge/cli_test.go index 809a839..4cf49f8 100644 --- a/cmd/comicforge/cli_test.go +++ b/cmd/comicforge/cli_test.go @@ -9,6 +9,9 @@ import ( "strings" "testing" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + version "codeberg.org/snonux/comicforge/internal" "codeberg.org/snonux/comicforge/internal/comic" "codeberg.org/snonux/comicforge/internal/config" @@ -254,10 +257,7 @@ func TestRootCommandRejectsConflictingUltraFlags(t *testing.T) { func TestRootCommandProviderFlagsDefaultToGemini(t *testing.T) { cmd := newRootCommand() for _, flagName := range []string{"text-provider", "image-provider", "tts-provider"} { - flag := cmd.Flags().Lookup(flagName) - if flag == nil { - t.Fatalf("flag %q not found", flagName) - } + flag := mustLookupFlag(t, cmd, flagName) if got, want := flag.DefValue, provider.Gemini; got != want { t.Fatalf("%s default = %q, want %q", flagName, got, want) } @@ -317,6 +317,16 @@ func TestRootCommandSkipsTTSProviderWhenNarrationDisabled(t *testing.T) { } } +func mustLookupFlag(t *testing.T, cmd *cobra.Command, name string) *pflag.Flag { + t.Helper() + + flag := cmd.Flags().Lookup(name) + if flag == nil { + t.Fatalf("flag %q not found", name) + } + return flag +} + type recordingRunner struct { cfg *comic.RunnerConfig batchFile string diff --git a/internal/comic/artist.go b/internal/comic/artist.go index c58afa7..65fed3d 100644 --- a/internal/comic/artist.go +++ b/internal/comic/artist.go @@ -365,7 +365,7 @@ func buildPanelLayout(section string, pagePanels []string) string { if panel == "" { continue } - sb.WriteString(fmt.Sprintf("Панел %d: %s\n", i+1, panel)) + fmt.Fprintf(&sb, "Панел %d: %s\n", i+1, panel) } return sb.String() } diff --git a/internal/comic/narrator.go b/internal/comic/narrator.go index a75522a..a469c54 100644 --- a/internal/comic/narrator.go +++ b/internal/comic/narrator.go @@ -75,7 +75,9 @@ func (n *Narrator) Narrate(ctx context.Context, storyText, outputFile string) er if err != nil { return fmt.Errorf("create temp dir: %w", err) } - defer os.RemoveAll(tmpDir) + defer func() { + _ = os.RemoveAll(tmpDir) + }() var allPaths []string if introPath, ok := n.narrateIntro(ctx, storyText, tmpDir); ok { @@ -325,7 +327,7 @@ func concatenateMP3s(chunkPaths []string, outputFile, tmpDir string) error { listPath := filepath.Join(tmpDir, "concat_list.txt") var sb strings.Builder for _, path := range chunkPaths { - sb.WriteString(fmt.Sprintf("file '%s'\n", path)) + fmt.Fprintf(&sb, "file '%s'\n", path) } if err := os.WriteFile(listPath, []byte(sb.String()), 0o600); err != nil { return fmt.Errorf("write concat list: %w", err) diff --git a/internal/comic/runner.go b/internal/comic/runner.go index 7e4e9bb..5843aab 100644 --- a/internal/comic/runner.go +++ b/internal/comic/runner.go @@ -206,10 +206,10 @@ func (r *Runner) saveVocabularyFile(storyText string, entries []vocab.WordEntry, word = strings.TrimSpace(entry.Translation) } if entry.Translation != "" && entry.Word != "" { - sb.WriteString(fmt.Sprintf(" %s - %s\n", entry.Word, entry.Translation)) + fmt.Fprintf(&sb, " %s - %s\n", entry.Word, entry.Translation) continue } - sb.WriteString(fmt.Sprintf(" %s\n", word)) + fmt.Fprintf(&sb, " %s\n", word) } sb.WriteString("\n# Story Text\n\n") sb.WriteString(strings.TrimSpace(storyText)) @@ -269,12 +269,16 @@ func copyFile(src, dst string) error { if err != nil { return err } - defer in.Close() + defer func() { + _ = in.Close() + }() out, err := os.Create(dst) if err != nil { return err } - defer out.Close() + defer func() { + _ = out.Close() + }() _, err = io.Copy(out, in) return err } diff --git a/internal/comic/types.go b/internal/comic/types.go index c8ada2f..924739c 100644 --- a/internal/comic/types.go +++ b/internal/comic/types.go @@ -27,19 +27,8 @@ const ( ) const ( - storyGeminiModel = "gemini-2.5-flash" - storyTimeout = 120 * time.Second - // 8192 tokens for story-only generation (thinking + visible story). - storyMaxTokens = int32(8192) - - // 16384 total for the combined story+bible call, with thinking capped. - storyFullMaxTokens = int32(16384) - - // Caps the internal chain-of-thought so visible output is still produced. - storyFullThinkingBudget = int32(8192) - storyBibleSeparator = "---CHARACTER GUIDE---" storyTitleSeparator = "---COMIC TITLE---" storyPanelSeparator = "---PANEL SCRIPT---" @@ -240,7 +229,7 @@ func buildWordList(entries []WordEntry, header string) string { if word == "" { word = strings.TrimSpace(entry.Translation) } - sb.WriteString(fmt.Sprintf("- %s\n", word)) + fmt.Fprintf(&sb, "- %s\n", word) } return sb.String() } diff --git a/internal/httpctx/httpctx_test.go b/internal/httpctx/httpctx_test.go index ffad68c..e9f2a85 100644 --- a/internal/httpctx/httpctx_test.go +++ b/internal/httpctx/httpctx_test.go @@ -41,7 +41,7 @@ func TestWithTimeoutUnlessSet_NoDeadline(t *testing.T) { func TestWithTimeoutUnlessSet_NilUsesBackground(t *testing.T) { t.Parallel() - ctx, cancel := WithTimeoutUnlessSet(nil, 50*time.Millisecond) + ctx, cancel := WithTimeoutUnlessSet(context.Background(), 50*time.Millisecond) defer cancel() if err := ctx.Err(); err != nil { diff --git a/internal/image/download.go b/internal/image/download.go index a693121..aca32c5 100644 --- a/internal/image/download.go +++ b/internal/image/download.go @@ -70,7 +70,9 @@ func (d *Downloader) DownloadImage(ctx context.Context, result *SearchResult, ou if err != nil { return fmt.Errorf("create output file %q: %w", outputPath, err) } - defer unix.Close(parentFD) + defer func() { + _ = unix.Close(parentFD) + }() defer func() { if closeErr := file.Close(); err == nil && closeErr != nil { err = fmt.Errorf("close output file %q: %w", outputPath, closeErr) @@ -273,7 +275,9 @@ func (d *Downloader) writeSecureRelativeFile(outputPath, relPath string, content if err != nil { return err } - defer unix.Close(parentFD) + defer func() { + _ = unix.Close(parentFD) + }() defer func() { _ = file.Close() }() diff --git a/internal/image/gemini.go b/internal/image/gemini.go index d2f710c..e04b04e 100644 --- a/internal/image/gemini.go +++ b/internal/image/gemini.go @@ -207,7 +207,9 @@ func (c *GeminiProvider) GenerateImageWithReferences(ctx context.Context, prompt if err != nil { return err } - defer rc.Close() + defer func() { + _ = rc.Close() + }() data, err := io.ReadAll(rc) if err != nil { return fmt.Errorf("read image data: %w", err) diff --git a/internal/image/gemini_test.go b/internal/image/gemini_test.go index bbc9dcd..54480fc 100644 --- a/internal/image/gemini_test.go +++ b/internal/image/gemini_test.go @@ -16,10 +16,7 @@ import ( func TestNewGeminiProvider(t *testing.T) { t.Parallel() - client := NewGeminiProvider(&GeminiConfig{APIKey: "test-key"}) - if client == nil { - t.Fatal("expected client") - } + client := mustNewGeminiProvider(t, &GeminiConfig{APIKey: "test-key"}) if client.config == nil { t.Fatal("expected normalized config") } @@ -34,6 +31,16 @@ func TestNewGeminiProvider(t *testing.T) { } } +func mustNewGeminiProvider(t *testing.T, cfg *GeminiConfig) *GeminiProvider { + t.Helper() + + client := NewGeminiProvider(cfg) + if client == nil { + t.Fatal("expected client") + } + return client +} + func TestGeminiProvider_NoAPIKey(t *testing.T) { client := NewGeminiProvider(&GeminiConfig{}) diff --git a/internal/image/types_test.go b/internal/image/types_test.go index 3597ee3..9257392 100644 --- a/internal/image/types_test.go +++ b/internal/image/types_test.go @@ -53,7 +53,7 @@ func TestRegistryNewFromConfig(t *testing.T) { registry := NewRegistry[fakeConfig]() registry.Register(Gemini, func(cfg fakeConfig) (ImageProvider, error) { - return fakeProvider{name: cfg.name, token: cfg.token}, nil + return fakeProvider(cfg), nil }) provider, err := registry.NewFromConfig(fakeConfig{name: Gemini, token: "secret"}) diff --git a/internal/provider/provider_test.go b/internal/provider/provider_test.go index 147bf44..a7b4ae2 100644 --- a/internal/provider/provider_test.go +++ b/internal/provider/provider_test.go @@ -76,11 +76,3 @@ func (f fakeTextProvider) IsAvailable() error { return nil } func (f fakeTextProvider) GenerateText(_ context.Context, _ string) (string, error) { return "", nil } - -type fakeTTSProvider struct{} - -func (fakeTTSProvider) Name() string { return "missing" } -func (fakeTTSProvider) IsAvailable() error { return nil } -func (fakeTTSProvider) GenerateAudio(_ context.Context, _ string, _ string) error { - return nil -} diff --git a/internal/text/gemini.go b/internal/text/gemini.go index 94a8c16..3ad3b4c 100644 --- a/internal/text/gemini.go +++ b/internal/text/gemini.go @@ -45,7 +45,7 @@ func NewGeminiProvider(cfg *GeminiConfig) *GeminiProvider { } g.model = defaultOr(cfg.Model, DefaultModel) if strings.TrimSpace(cfg.APIKey) == "" { - g.err = fmt.Errorf("Google API key is required for text generation") + g.err = fmt.Errorf("google API key is required for text generation") return g } client, err := newGeminiClient(context.Background(), &genai.ClientConfig{ diff --git a/internal/tts/gemini.go b/internal/tts/gemini.go index 1836be4..736e1d1 100644 --- a/internal/tts/gemini.go +++ b/internal/tts/gemini.go @@ -43,7 +43,7 @@ func NewGeminiProvider(cfg *GeminiConfig) *GeminiProvider { g.model = defaultOr(cfg.Model, DefaultModel) g.voice = cfg.Voice if strings.TrimSpace(cfg.APIKey) == "" { - g.err = fmt.Errorf("Google API key is required for TTS") + g.err = fmt.Errorf("google API key is required for TTS") return g } client, err := genai.NewClient(context.Background(), &genai.ClientConfig{ -- cgit v1.2.3