detached a3b6bfd

fix(cli): propagate rollback list writer errors

commit
a3b6bfdf9be06c5c225edb0eed7018106a5975b7
author
bnema · · 2026-02-11 20:51 UTC
parent
1c3348b
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +24 -6
M internal/adapters/in/cli/rollback.go +9 -5
@@ -109,19 +109,23 @@ func runRollbackList(ctx context.Context, w io.Writer, rollbackDomain string) er
109109 return err
110110 }
111111
112- printRollbackTags(w, rollbackDomain, currentTag, tags)
113- return nil
112+ return printRollbackTags(w, rollbackDomain, currentTag, tags)
114113 }
115114
116-func printRollbackTags(w io.Writer, rollbackDomain, currentTag string, tags []string) {
117- fmt.Fprintf(w, "Available tags for %s:\n", styles.Theme.Bold.Render(rollbackDomain))
115+func printRollbackTags(w io.Writer, rollbackDomain, currentTag string, tags []string) error {
116+ if _, err := fmt.Fprintf(w, "Available tags for %s:\n", styles.Theme.Bold.Render(rollbackDomain)); err != nil {
117+ return err
118+ }
118119 for _, tag := range tags {
119120 suffix := ""
120121 if tag == currentTag {
121122 suffix = " (current)"
122123 }
123- fmt.Fprintf(w, "- %s%s\n", tag, suffix)
124+ if _, err := fmt.Fprintf(w, "- %s%s\n", tag, suffix); err != nil {
125+ return err
126+ }
124127 }
128+ return nil
125129 }
126130
127131 func fetchAndSortTags(ctx context.Context, cp ControlPlane, imageName string) ([]string, error) {
@@ -109,19 +109,23 @@ func runRollbackList(ctx context.Context, w io.Writer, rollbackDomain string) er
109 return err109 return err
110 }110 }
111 111
112- printRollbackTags(w, rollbackDomain, currentTag, tags)112+ return printRollbackTags(w, rollbackDomain, currentTag, tags)
113- return nil
114 }113 }
115 114
116-func printRollbackTags(w io.Writer, rollbackDomain, currentTag string, tags []string) {115+func printRollbackTags(w io.Writer, rollbackDomain, currentTag string, tags []string) error {
117- fmt.Fprintf(w, "Available tags for %s:\n", styles.Theme.Bold.Render(rollbackDomain))116+ if _, err := fmt.Fprintf(w, "Available tags for %s:\n", styles.Theme.Bold.Render(rollbackDomain)); err != nil {
117+ return err
118+ }
118 for _, tag := range tags {119 for _, tag := range tags {
119 suffix := ""120 suffix := ""
120 if tag == currentTag {121 if tag == currentTag {
121 suffix = " (current)"122 suffix = " (current)"
122 }123 }
123- fmt.Fprintf(w, "- %s%s\n", tag, suffix)124+ if _, err := fmt.Fprintf(w, "- %s%s\n", tag, suffix); err != nil {
125+ return err
126+ }
124 }127 }
128+ return nil
125 }129 }
126 130
127 func fetchAndSortTags(ctx context.Context, cp ControlPlane, imageName string) ([]string, error) {131 func fetchAndSortTags(ctx context.Context, cp ControlPlane, imageName string) ([]string, error) {
M internal/adapters/in/cli/rollback_test.go +15 -1
@@ -2,11 +2,18 @@ package cli
22
33 import (
44 "bytes"
5+ "errors"
56 "testing"
67
78 "github.com/stretchr/testify/assert"
89 )
910
11+type failingWriter struct{}
12+
13+func (failingWriter) Write(_ []byte) (int, error) {
14+ return 0, errors.New("write failed")
15+}
16+
1017 func TestSortSemverTags(t *testing.T) {
1118 tests := []struct {
1219 name string
@@ -56,7 +63,8 @@ func TestValidateTagExists(t *testing.T) {
5663 func TestPrintRollbackTags_WritesToProvidedWriter(t *testing.T) {
5764 var buf bytes.Buffer
5865
59- printRollbackTags(&buf, "myapp.example.com", "v1.2.0", []string{"v1.2.0", "v1.1.0"})
66+ err := printRollbackTags(&buf, "myapp.example.com", "v1.2.0", []string{"v1.2.0", "v1.1.0"})
67+ assert.NoError(t, err)
6068
6169 output := buf.String()
6270 assert.Contains(t, output, "Available tags for")
@@ -64,3 +72,9 @@ func TestPrintRollbackTags_WritesToProvidedWriter(t *testing.T) {
6472 assert.Contains(t, output, "- v1.2.0 (current)")
6573 assert.Contains(t, output, "- v1.1.0")
6674 }
75+
76+func TestPrintRollbackTags_ReturnsWriteError(t *testing.T) {
77+ err := printRollbackTags(failingWriter{}, "myapp.example.com", "v1.2.0", []string{"v1.2.0"})
78+ assert.Error(t, err)
79+ assert.Contains(t, err.Error(), "write failed")
80+}
@@ -2,11 +2,18 @@ package cli
2 2
3 import (3 import (
4 "bytes"4 "bytes"
5+ "errors"
5 "testing"6 "testing"
6 7
7 "github.com/stretchr/testify/assert"8 "github.com/stretchr/testify/assert"
8 )9 )
9 10
11+type failingWriter struct{}
12+
13+func (failingWriter) Write(_ []byte) (int, error) {
14+ return 0, errors.New("write failed")
15+}
16+
10 func TestSortSemverTags(t *testing.T) {17 func TestSortSemverTags(t *testing.T) {
11 tests := []struct {18 tests := []struct {
12 name string19 name string
@@ -56,7 +63,8 @@ func TestValidateTagExists(t *testing.T) {
56 func TestPrintRollbackTags_WritesToProvidedWriter(t *testing.T) {63 func TestPrintRollbackTags_WritesToProvidedWriter(t *testing.T) {
57 var buf bytes.Buffer64 var buf bytes.Buffer
58 65
59- printRollbackTags(&buf, "myapp.example.com", "v1.2.0", []string{"v1.2.0", "v1.1.0"})66+ err := printRollbackTags(&buf, "myapp.example.com", "v1.2.0", []string{"v1.2.0", "v1.1.0"})
67+ assert.NoError(t, err)
60 68
61 output := buf.String()69 output := buf.String()
62 assert.Contains(t, output, "Available tags for")70 assert.Contains(t, output, "Available tags for")
@@ -64,3 +72,9 @@ func TestPrintRollbackTags_WritesToProvidedWriter(t *testing.T) {
64 assert.Contains(t, output, "- v1.2.0 (current)")72 assert.Contains(t, output, "- v1.2.0 (current)")
65 assert.Contains(t, output, "- v1.1.0")73 assert.Contains(t, output, "- v1.1.0")
66 }74 }
75+
76+func TestPrintRollbackTags_ReturnsWriteError(t *testing.T) {
77+ err := printRollbackTags(failingWriter{}, "myapp.example.com", "v1.2.0", []string{"v1.2.0"})
78+ assert.Error(t, err)
79+ assert.Contains(t, err.Error(), "write failed")
80+}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 7

Tags 106

↑↓ moveenter openesc close