From 13427972494a2e8ecba72c0d93f25167cd993050 Mon Sep 17 00:00:00 2001 From: Benny Yen Date: Sun, 26 Apr 2026 11:52:22 +0800 Subject: [PATCH 1/2] chore(lint): clear pre-existing CI lint baseline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace `sb.WriteString(fmt.Sprintf(...))` with `fmt.Fprintf(&sb, ...)` in `confirm.go` and `model.go` (staticcheck QF1012). - Cancel any previous hook context before starting a new one in `startNextHook`. The cancel func was being silently overwritten when hooks completed normally, leaking the prior context — calling it first also clears gosec G118. These were flagged by newer golangci-lint releases; the workflow uses `version: latest` so rule churn started failing CI on previously-passing code. Co-Authored-By: Claude Opus 4.7 (1M context) --- internal/tui/confirm.go | 34 ++++++++++++++++------------------ internal/tui/model.go | 14 ++++++++++---- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/internal/tui/confirm.go b/internal/tui/confirm.go index c72240f..dfabf0d 100644 --- a/internal/tui/confirm.go +++ b/internal/tui/confirm.go @@ -23,18 +23,18 @@ func RenderConfirmation( sb.WriteString("\n\n") // Summary - sb.WriteString(fmt.Sprintf(" Version: %s %s %s\n", + fmt.Fprintf(&sb, " Version: %s %s %s\n", CurrentVersionStyle.Render(prevVersion), IconArrow, NewVersionStyle.Render(newVersion), - )) - sb.WriteString(fmt.Sprintf(" Tag: %s\n", NewVersionStyle.Render(tagName))) - sb.WriteString(fmt.Sprintf(" Commits: %d\n", commitCount)) + ) + fmt.Fprintf(&sb, " Tag: %s\n", NewVersionStyle.Render(tagName)) + fmt.Fprintf(&sb, " Commits: %d\n", commitCount) if noPush { - sb.WriteString(fmt.Sprintf(" Push: %s\n", WarningStyle.Render("disabled (--no-push)"))) + fmt.Fprintf(&sb, " Push: %s\n", WarningStyle.Render("disabled (--no-push)")) } else { - sb.WriteString(fmt.Sprintf(" Remote: %s\n", remoteName)) + fmt.Fprintf(&sb, " Remote: %s\n", remoteName) } if dryRun { @@ -50,11 +50,11 @@ func RenderConfirmation( cancelLabel := "No, cancel" if selected == 0 { - sb.WriteString(fmt.Sprintf(" %s %s\n", IconSelected, SelectedStyle.Render(confirmLabel))) - sb.WriteString(fmt.Sprintf(" %s\n", UnselectedStyle.Render(cancelLabel))) + fmt.Fprintf(&sb, " %s %s\n", IconSelected, SelectedStyle.Render(confirmLabel)) + fmt.Fprintf(&sb, " %s\n", UnselectedStyle.Render(cancelLabel)) } else { - sb.WriteString(fmt.Sprintf(" %s\n", UnselectedStyle.Render(confirmLabel))) - sb.WriteString(fmt.Sprintf(" %s %s\n", IconSelected, SelectedStyle.Render(cancelLabel))) + fmt.Fprintf(&sb, " %s\n", UnselectedStyle.Render(confirmLabel)) + fmt.Fprintf(&sb, " %s %s\n", IconSelected, SelectedStyle.Render(cancelLabel)) } return sb.String() @@ -67,15 +67,13 @@ func RenderSuccess(result *ExecutionSummary) string { sb.WriteString(SuccessStyle.Render(fmt.Sprintf("%s Success!", IconCheck))) sb.WriteString("\n\n") - sb.WriteString(fmt.Sprintf(" Created tag: %s\n", NewVersionStyle.Render(result.TagName))) - sb.WriteString( - fmt.Sprintf(" Commit: %s\n", CommitHashStyle.Render(result.CommitHash[:7])), - ) + fmt.Fprintf(&sb, " Created tag: %s\n", NewVersionStyle.Render(result.TagName)) + fmt.Fprintf(&sb, " Commit: %s\n", CommitHashStyle.Render(result.CommitHash[:7])) if result.Pushed { - sb.WriteString(fmt.Sprintf(" Pushed to: %s\n", result.Remote)) + fmt.Fprintf(&sb, " Pushed to: %s\n", result.Remote) } else { - sb.WriteString(fmt.Sprintf(" Push: %s\n", WarningStyle.Render("skipped"))) + fmt.Fprintf(&sb, " Push: %s\n", WarningStyle.Render("skipped")) } // Display post-push hook warnings if any @@ -84,7 +82,7 @@ func RenderSuccess(result *ExecutionSummary) string { sb.WriteString(WarningStyle.Render(" Post-push hook warnings:")) sb.WriteString("\n") for _, warning := range result.PostPushWarnings { - sb.WriteString(fmt.Sprintf(" %s %s\n", IconCross, warning)) + fmt.Fprintf(&sb, " %s %s\n", IconCross, warning) } } @@ -100,7 +98,7 @@ func RenderError(err error) string { sb.WriteString(ErrorStyle.Render(fmt.Sprintf("%s Error", IconCross))) sb.WriteString("\n\n") - sb.WriteString(fmt.Sprintf(" %s\n", err.Error())) + fmt.Fprintf(&sb, " %s\n", err.Error()) sb.WriteString("\n") sb.WriteString(HelpStyle.Render("Press any key to exit")) diff --git a/internal/tui/model.go b/internal/tui/model.go index 20db008..2b30291 100644 --- a/internal/tui/model.go +++ b/internal/tui/model.go @@ -666,9 +666,9 @@ func (m Model) View() string { func (m Model) renderVersionSelectView() string { var sb strings.Builder - sb.WriteString(fmt.Sprintf("Current version: %s\n\n", + fmt.Fprintf(&sb, "Current version: %s\n\n", CurrentVersionStyle.Render(m.currentVersion.StringWithPrefix(m.config.Prefix)), - )) + ) // Dual-pane layout: commits pane (top) + version pane (bottom) @@ -733,7 +733,7 @@ func (m Model) renderCustomInputView() string { sb.WriteString(SubtitleStyle.Render("Enter custom version:")) sb.WriteString("\n\n") - sb.WriteString(fmt.Sprintf(" %s%s\n", m.config.Prefix, m.customInput.View())) + fmt.Fprintf(&sb, " %s%s\n", m.config.Prefix, m.customInput.View()) if m.err != nil { sb.WriteString("\n") @@ -880,7 +880,13 @@ func (m *Model) startNextHook() tea.Cmd { DryRun: m.config.DryRun, } - // Create cancellable context for hook execution + // Tear down any previous hook's context before starting a new one — the + // cancel func is otherwise leaked when hooks complete normally and the + // field is overwritten on the next call. + if m.hookCancelFunc != nil { + m.hookCancelFunc() + } + ctx, cancel := context.WithCancel(context.Background()) m.hookCancelFunc = cancel From b62b74972fc658d7d8ad7331bf1c1fdb4692c545 Mon Sep 17 00:00:00 2001 From: Benny Yen Date: Sun, 26 Apr 2026 12:02:37 +0800 Subject: [PATCH 2/2] chore: condense hook cancel comment to one line per CLAUDE.md Co-Authored-By: Claude Opus 4.7 (1M context) --- internal/tui/model.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/tui/model.go b/internal/tui/model.go index 2b30291..71d05a8 100644 --- a/internal/tui/model.go +++ b/internal/tui/model.go @@ -880,9 +880,7 @@ func (m *Model) startNextHook() tea.Cmd { DryRun: m.config.DryRun, } - // Tear down any previous hook's context before starting a new one — the - // cancel func is otherwise leaked when hooks complete normally and the - // field is overwritten on the next call. + // Call previous cancel before overwriting — hooks completing normally don't cancel their own context. if m.hookCancelFunc != nil { m.hookCancelFunc() }