fix(cli): resolve golangci-lint errcheck and noctx violations

- Replace client.Head/client.Get with client.Do(http.NewRequestWithContext) to satisfy the noctx linter (3 fixes) - Add `_ =` to all best-effort Close/Remove/Rename calls to satisfy errcheck (13 fixes) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Yechan Kim committed Apr 16, 2026 at 20:24 UTC fb6ab8466abf0bd077c5bed26a87bd17673d7b50
2 files changed +38 -22
cmd/portal-tunnel/update.go
+34 -18
@@ -1,6 +1,7 @@
1 package main
2
3 import (
4 + "context"
5 "crypto/sha256"
6 "encoding/hex"
7 "fmt"
@@ -55,17 +56,17 @@ func runUpdateCommand(args []string) error {
56 if err != nil {
57 return fmt.Errorf("failed to create temp file: %w", err)
58 }
58 - defer os.Remove(tmpFile.Name())
59 + defer func() { _ = os.Remove(tmpFile.Name()) }()
60
61 if err := downloadBinary(binURL, tmpFile); err != nil {
61 - tmpFile.Close()
62 + _ = tmpFile.Close()
63 return fmt.Errorf("failed to download binary: %w", err)
64 }
65 if err := tmpFile.Sync(); err != nil {
65 - tmpFile.Close()
66 + _ = tmpFile.Close()
67 return fmt.Errorf("failed to sync downloaded binary: %w", err)
68 }
68 - tmpFile.Close()
69 + _ = tmpFile.Close()
70
71 checksumURL, _ := installer.OfficialAssetURL(slug, true)
72 if err := verifyChecksum(tmpFile.Name(), checksumURL); err != nil {
@@ -90,11 +91,16 @@ func detectLatestVersion(latestURL string) (string, error) {
91 },
92 }
93
93 - resp, err := client.Head(latestURL)
94 + req, err := http.NewRequestWithContext(context.Background(), http.MethodHead, latestURL, nil)
95 + if err != nil {
96 + return "", fmt.Errorf("failed to create request: %w", err)
97 + }
98 +
99 + resp, err := client.Do(req)
100 if err != nil {
101 return "", fmt.Errorf("HEAD request failed: %w", err)
102 }
97 - resp.Body.Close()
103 + _ = resp.Body.Close()
104
105 location := resp.Header.Get("Location")
106 if location == "" {
@@ -124,11 +130,16 @@ func detectLatestVersion(latestURL string) (string, error) {
130 func downloadBinary(binURL string, dst *os.File) error {
131 client := &http.Client{Timeout: 120 * time.Second}
132
127 - resp, err := client.Get(binURL)
133 + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, binURL, nil)
134 + if err != nil {
135 + return fmt.Errorf("failed to create request: %w", err)
136 + }
137 +
138 + resp, err := client.Do(req)
139 if err != nil {
140 return err
141 }
131 - defer resp.Body.Close()
142 + defer func() { _ = resp.Body.Close() }()
143
144 if resp.StatusCode != http.StatusOK {
145 return fmt.Errorf("unexpected status %d", resp.StatusCode)
@@ -141,11 +152,16 @@ func downloadBinary(binURL string, dst *os.File) error {
152 func verifyChecksum(filePath, checksumURL string) error {
153 client := &http.Client{Timeout: 10 * time.Second}
154
144 - resp, err := client.Get(checksumURL)
155 + req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, checksumURL, nil)
156 + if err != nil {
157 + return fmt.Errorf("failed to create request: %w", err)
158 + }
159 +
160 + resp, err := client.Do(req)
161 if err != nil {
162 return fmt.Errorf("failed to download checksum: %w", err)
163 }
148 - defer resp.Body.Close()
164 + defer func() { _ = resp.Body.Close() }()
165
166 if resp.StatusCode != http.StatusOK {
167 return fmt.Errorf("checksum download returned status %d", resp.StatusCode)
@@ -165,7 +181,7 @@ func verifyChecksum(filePath, checksumURL string) error {
181 if err != nil {
182 return fmt.Errorf("failed to open downloaded file: %w", err)
183 }
168 - defer f.Close()
184 + defer func() { _ = f.Close() }()
185
186 h := sha256.New()
187 if _, err := io.Copy(h, f); err != nil {
@@ -186,8 +202,8 @@ func checkWritable(dir string) error {
202 return fmt.Errorf("directory %s is not writable: %w", dir, err)
203 }
204 name := f.Name()
189 - f.Close()
190 - os.Remove(name)
205 + _ = f.Close()
206 + _ = os.Remove(name)
207 return nil
208 }
209
@@ -213,13 +229,13 @@ func replaceBinaryUnix(srcPath, dstPath string) error {
229 if err != nil {
230 return err
231 }
216 - defer src.Close()
232 + defer func() { _ = src.Close() }()
233
234 dst, err := os.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0755)
235 if err != nil {
236 return fmt.Errorf("failed to open destination: %w", err)
237 }
222 - defer dst.Close()
238 + defer func() { _ = dst.Close() }()
239
240 if _, err := io.Copy(dst, src); err != nil {
241 return fmt.Errorf("failed to copy binary: %w", err)
@@ -231,7 +247,7 @@ func replaceBinaryWindows(srcPath, dstPath string) error {
247 oldPath := dstPath + ".old"
248
249 // Remove leftover .old file from a previous update.
234 - os.Remove(oldPath)
250 + _ = os.Remove(oldPath)
251
252 // Rename the running binary out of the way, then move the new one in.
253 if err := os.Rename(dstPath, oldPath); err != nil {
@@ -240,11 +256,11 @@ func replaceBinaryWindows(srcPath, dstPath string) error {
256
257 if err := os.Rename(srcPath, dstPath); err != nil {
258 // Attempt to restore the old binary on failure.
243 - os.Rename(oldPath, dstPath)
259 + _ = os.Rename(oldPath, dstPath)
260 return fmt.Errorf("failed to place new binary: %w", err)
261 }
262
263 // Best-effort cleanup of the old binary.
248 - os.Remove(oldPath)
264 + _ = os.Remove(oldPath)
265 return nil
266 }
cmd/portal-tunnel/update_check.go
+4 -4
@@ -129,11 +129,11 @@ func writeUpdateCache(path string, c updateCache) {
129 tmpName := tmp.Name()
130
131 if _, err := tmp.Write(data); err != nil {
132 - tmp.Close()
133 - os.Remove(tmpName)
132 + _ = tmp.Close()
133 + _ = os.Remove(tmpName)
134 return
135 }
136 - tmp.Close()
136 + _ = tmp.Close()
137
138 - os.Rename(tmpName, path)
138 + _ = os.Rename(tmpName, path)
139 }