Commit a29c053
Eric Bower
·
2026-05-07 23:26:36 -0400 EDT
parent 8fc0a2e
fix: linter
1 files changed,
+64,
-63
M
main.go
M
main.go
+64,
-63
1@@ -85,7 +85,10 @@ func NewCfg() (*Cfg, string, bool) {
2 // Split args so the subcommand (first non-flag arg) doesn't block
3 // flags that appear after it: "pici runner --wait" works.
4 flags, cmd, wantHelp := splitCommand(os.Args[1:])
5- flag.CommandLine.Parse(flags)
6+ if err := flag.CommandLine.Parse(flags); err != nil {
7+ fmt.Fprintf(os.Stderr, "failed to parse flags: %v\n", err)
8+ os.Exit(1)
9+ }
10
11 logger := newLogger("ci", logLevel)
12 ctx, cancel := context.WithCancel(context.Background())
13@@ -381,7 +384,11 @@ func (w *WorkspaceTar) Setup() error {
14 if err != nil {
15 return fmt.Errorf("open tar: %w", err)
16 }
17- defer f.Close()
18+ defer func() {
19+ if err := f.Close(); err != nil {
20+ log.Error("close tar", "err", err)
21+ }
22+ }()
23
24 // Extract tar to temp dir
25 tr := tar.NewReader(f)
26@@ -410,10 +417,14 @@ func (w *WorkspaceTar) Setup() error {
27 return fmt.Errorf("open %s: %w", target, err)
28 }
29 if _, err := io.Copy(tf, tr); err != nil {
30- tf.Close()
31+ if closeErr := tf.Close(); closeErr != nil {
32+ return fmt.Errorf("close %s: %w", target, closeErr)
33+ }
34 return fmt.Errorf("write %s: %w", target, err)
35 }
36- tf.Close()
37+ if err := tf.Close(); err != nil {
38+ return fmt.Errorf("close %s: %w", target, err)
39+ }
40 }
41 }
42
43@@ -538,9 +549,9 @@ func eventHandler(cfg *Cfg, eventData *Event) error {
44 jobID := generateJobID(eventData.Name, eventData.Workspace)
45 log = log.With("job_id", jobID)
46 eventBytes, _ := json.Marshal(eventData)
47- fmt.Fprintf(os.Stdout, "š starting job ci.%s.%s\n", eventData.Name, jobID)
48- fmt.Fprintf(os.Stdout, " event: type=%s name=%s workspace=%s\n", eventData.Type, eventData.Name, eventData.Workspace)
49- fmt.Fprintf(os.Stdout, " %s\n", string(eventBytes))
50+ fmt.Fprintf(os.Stdout, "š starting job ci.%s.%s\n", eventData.Name, jobID) //nolint:errcheck
51+ fmt.Fprintf(os.Stdout, " event: type=%s name=%s workspace=%s\n", eventData.Type, eventData.Name, eventData.Workspace) //nolint:errcheck
52+ fmt.Fprintf(os.Stdout, " %s\n", string(eventBytes)) //nolint:errcheck
53
54 wk := cfg.NewWorkspace(cfg, log, eventData.Workspace)
55 eng := &JobEngine{
56@@ -556,11 +567,11 @@ func eventHandler(cfg *Cfg, eventData *Event) error {
57 }
58 }()
59
60- fmt.Fprintf(os.Stdout, "š¦ syncing workspace %s\n", eventData.Workspace)
61+ fmt.Fprintf(os.Stdout, "š¦ syncing workspace %s\n", eventData.Workspace) //nolint:errcheck
62 if err := eng.Setup(); err != nil {
63 return fmt.Errorf("setup: %w", err)
64 }
65- fmt.Fprintf(os.Stdout, "ā
workspace ready %s\n", eng.Wk.GetDir())
66+ fmt.Fprintf(os.Stdout, "ā
workspace ready %s\n", eng.Wk.GetDir()) //nolint:errcheck
67
68 // Store the event in the artifact directory so the monitor can access it
69 eventDir := filepath.Join(cfg.ArtifactDir, eventData.Name, jobID)
70@@ -574,7 +585,8 @@ func eventHandler(cfg *Cfg, eventData *Event) error {
71
72 manifest, err := eng.FindManifest()
73 if err != nil {
74- fmt.Fprintf(os.Stdout, "ā %s\n\n", err)
75+ fmt.Fprintf(os.Stdout, "ā %s\n\n", err) //nolint:errcheck
76+ //nolint:errcheck
77 fmt.Fprint(os.Stdout, `Create a pico.sh script in your workspace root:
78
79 #!/usr/bin/env bash
80@@ -601,14 +613,14 @@ See: https://github.com/picosh/pici
81 `)
82 return err
83 }
84- fmt.Fprintf(os.Stdout, "š found %s\n", manifest)
85+ fmt.Fprintf(os.Stdout, "š found %s\n", manifest) //nolint:errcheck
86
87- fmt.Fprint(os.Stdout, "š launching sessions...\n")
88+ fmt.Fprint(os.Stdout, "š launching sessions...\n") //nolint:errcheck
89 if err := eng.Run(manifest); err != nil {
90 return fmt.Errorf("run: %w", err)
91 }
92
93- fmt.Fprintln(os.Stdout, "ā
job launched")
94+ fmt.Fprintln(os.Stdout, "ā
job launched") //nolint:errcheck
95
96 if cfg.Wait {
97 if err := waitAndReport(cfg, log, eventData.Name, jobID); err != nil {
98@@ -618,9 +630,9 @@ See: https://github.com/picosh/pici
99 }
100
101 session := fmt.Sprintf("ci.%s.%s.runner", eventData.Name, jobID)
102- fmt.Fprintf(os.Stdout, " zmx tail %s\n", session)
103- fmt.Fprintf(os.Stdout, " zmx history %s\n", session)
104- fmt.Fprintf(os.Stdout, " zmx attach %s\n", session)
105+ fmt.Fprintf(os.Stdout, " zmx tail %s\n", session) //nolint:errcheck
106+ fmt.Fprintf(os.Stdout, " zmx history %s\n", session) //nolint:errcheck
107+ fmt.Fprintf(os.Stdout, " zmx attach %s\n", session) //nolint:errcheck
108 return nil
109 }
110
111@@ -636,8 +648,8 @@ func waitAndReport(cfg *Cfg, log *slog.Logger, name, jobID string) error {
112 signal.Notify(sigCh, syscall.SIGINT)
113 defer signal.Stop(sigCh)
114
115- fmt.Fprintln(os.Stdout)
116- fmt.Fprint(os.Stdout, "ā³ waiting for completion...\n")
117+ fmt.Fprintln(os.Stdout) //nolint:errcheck
118+ fmt.Fprint(os.Stdout, "ā³ waiting for completion...\n") //nolint:errcheck
119
120 // Track state for each session
121 known := make(map[string]*sessionState)
122@@ -650,7 +662,7 @@ func waitAndReport(cfg *Cfg, log *slog.Logger, name, jobID string) error {
123 case <-cfg.Ctx.Done():
124 return cfg.Ctx.Err()
125 case <-sigCh:
126- fmt.Fprintln(os.Stdout, "\nā¹ cancelled")
127+ fmt.Fprintln(os.Stdout, "\nā¹ cancelled") //nolint:errcheck
128 return nil
129 case <-ticker.C:
130 }
131@@ -717,21 +729,21 @@ func waitAndReport(cfg *Cfg, log *slog.Logger, name, jobID string) error {
132
133 // Overwrite previous lines with cursor-up, or print fresh
134 if len(liveLines) > 0 {
135- // Move cursor up to overwrite previous lines
136+ // Move cursor up to overwrite previous lines //nolint:errcheck
137 for range len(liveLines) {
138- fmt.Fprint(os.Stdout, "\033[A")
139+ fmt.Fprint(os.Stdout, "\033[A") //nolint:errcheck
140 }
141 // Clear each line
142- for i, line := range lines {
143+ for i, line := range lines { //nolint:errcheck
144 if i > 0 {
145- fmt.Fprint(os.Stdout, "\n")
146+ fmt.Fprint(os.Stdout, "\n") //nolint:errcheck
147 }
148- fmt.Fprint(os.Stdout, line+"\033[K")
149+ fmt.Fprint(os.Stdout, line+"\033[K") //nolint:errcheck
150 }
151- fmt.Fprint(os.Stdout, "\n")
152- } else {
153+ fmt.Fprint(os.Stdout, "\n") //nolint:errcheck
154+ } else { //nolint:errcheck
155 for _, line := range lines {
156- fmt.Fprintln(os.Stdout, line)
157+ fmt.Fprintln(os.Stdout, line) //nolint:errcheck
158 }
159 }
160 liveLines = lines
161@@ -743,7 +755,7 @@ func waitAndReport(cfg *Cfg, log *slog.Logger, name, jobID string) error {
162 }
163
164 // Print last 25 lines of history for failed sessions only
165- fmt.Fprintln(os.Stdout)
166+ fmt.Fprintln(os.Stdout) //nolint:errcheck
167 for _, s := range jobSessions {
168 state := known[s.Short]
169 if state == nil || state.status != "failed" {
170@@ -751,28 +763,28 @@ func waitAndReport(cfg *Cfg, log *slog.Logger, name, jobID string) error {
171 }
172
173 separator := strings.Repeat("\u2500", 50)
174- fmt.Fprintln(os.Stdout, separator)
175- fmt.Fprintf(os.Stdout, "Session: %s (exit %s)\n", s.Short, state.exitCode)
176- fmt.Fprintln(os.Stdout, separator)
177- fmt.Fprintln(os.Stdout)
178+ fmt.Fprintln(os.Stdout, separator) //nolint:errcheck
179+ fmt.Fprintf(os.Stdout, "Session: %s (exit %s)\n", s.Short, state.exitCode) //nolint:errcheck
180+ fmt.Fprintln(os.Stdout, separator) //nolint:errcheck
181+ fmt.Fprintln(os.Stdout) //nolint:errcheck
182
183 history, err := fetchHistoryPlain(s.Name)
184 if err != nil {
185- fmt.Fprintf(os.Stdout, " (history unavailable: %v)\n", err)
186+ fmt.Fprintf(os.Stdout, " (history unavailable: %v)\n", err) //nolint:errcheck
187 } else {
188 lines := strings.Split(history, "\n")
189 // Show last 25 lines
190 if len(lines) > 25 {
191- fmt.Fprintf(os.Stdout, " ... (%d lines omitted)\n", len(lines)-25)
192+ fmt.Fprintf(os.Stdout, " ... (%d lines omitted)\n", len(lines)-25) //nolint:errcheck
193 lines = lines[len(lines)-25:]
194 }
195 for _, line := range lines {
196 if line != "" {
197- fmt.Fprintf(os.Stdout, " %s\n", line)
198+ fmt.Fprintf(os.Stdout, " %s\n", line) //nolint:errcheck
199 }
200 }
201 }
202- fmt.Fprintln(os.Stdout)
203+ fmt.Fprintln(os.Stdout) //nolint:errcheck
204 }
205
206 // Final summary
207@@ -780,9 +792,9 @@ func waitAndReport(cfg *Cfg, log *slog.Logger, name, jobID string) error {
208 _, _, duration := computeJobTiming(jobSessions)
209 icon := map[string]string{"success": "ā
", "failed": "ā"}[status]
210 if exitCode == 0 {
211- fmt.Fprintf(os.Stdout, "%s job finished: %s (%s)\n", icon, status, duration)
212+ fmt.Fprintf(os.Stdout, "%s job finished: %s (%s)\n", icon, status, duration) //nolint:errcheck
213 } else {
214- fmt.Fprintf(os.Stdout, "%s job failed: exit %d (%s)\n", icon, exitCode, duration)
215+ fmt.Fprintf(os.Stdout, "%s job failed: exit %d (%s)\n", icon, exitCode, duration) //nolint:errcheck
216 }
217
218 return nil
219@@ -822,7 +834,6 @@ type monitorJobState struct {
220 sessionOrder []string // insertion order for deterministic output
221 sessions map[string]*sessionState
222 liveLines []string // last set of status lines printed (for overwrite)
223- published bool // final status already printed
224 }
225
226 func runMonitor(cfg *Cfg) error {
227@@ -911,19 +922,19 @@ func renderJobRunning(output io.Writer, name, jobID string, group []SessionInfo,
228 // Overwrite previous lines or print fresh
229 if len(state.liveLines) > 0 {
230 for range len(state.liveLines) {
231- fmt.Fprint(output, "\033[A")
232+ fmt.Fprint(output, "\033[A") //nolint:errcheck
233 }
234 for i, line := range lines {
235 if i > 0 {
236- fmt.Fprint(output, "\n")
237+ fmt.Fprint(output, "\n") //nolint:errcheck
238 }
239- fmt.Fprint(output, line+"\033[K")
240+ fmt.Fprint(output, line+"\033[K") //nolint:errcheck
241 }
242- fmt.Fprint(output, "\n")
243+ fmt.Fprint(output, "\n") //nolint:errcheck
244 } else {
245- fmt.Fprintf(output, " %-12s %s %s\n", name, "š", "running")
246+ fmt.Fprintf(output, " %-12s %s %s\n", name, "š", "running") //nolint:errcheck
247 for _, line := range lines {
248- fmt.Fprintln(output, line)
249+ fmt.Fprintln(output, line) //nolint:errcheck
250 }
251 }
252 state.liveLines = lines
253@@ -932,7 +943,7 @@ func renderJobRunning(output io.Writer, name, jobID string, group []SessionInfo,
254 // renderJobFinal prints the final status for a completed job.
255 func renderJobFinal(output io.Writer, name, jobID string, group []SessionInfo, duration, status string, success bool, workspace, artifactDir, artifactURL string) {
256 icon := map[string]string{"success": "ā
", "failed": "ā"}[status]
257- fmt.Fprintf(output, " %-12s %s %s (%s)\n", name, icon, status, duration)
258+ fmt.Fprintf(output, " %-12s %s %s (%s)\n", name, icon, status, duration) //nolint:errcheck
259
260 // Per-session summary
261 for _, s := range group {
262@@ -941,20 +952,20 @@ func renderJobFinal(output io.Writer, name, jobID string, group []SessionInfo, d
263 icon = "ā"
264 }
265 dur := fmtDuration(s.Created, s.Ended)
266- fmt.Fprintf(output, " %-12s %s done (%s)\n", s.Short, icon, dur)
267+ fmt.Fprintf(output, " %-12s %s done (%s)\n", s.Short, icon, dur) //nolint:errcheck
268 }
269
270 // Context info
271- fmt.Fprint(output, "\n")
272+ fmt.Fprint(output, "\n") //nolint:errcheck
273 if workspace != "" {
274- fmt.Fprintf(output, " workspace: %s\n", workspace)
275+ fmt.Fprintf(output, " workspace: %s\n", workspace) //nolint:errcheck
276 }
277 artifactPath := filepath.Join(artifactDir, name, jobID)
278- fmt.Fprintf(output, " artifacts: %s\n", artifactPath)
279+ fmt.Fprintf(output, " artifacts: %s\n", artifactPath) //nolint:errcheck
280 if artifactURL != "" {
281- fmt.Fprintf(output, " url: %s\n", artifactURL)
282+ fmt.Fprintf(output, " url: %s\n", artifactURL) //nolint:errcheck
283 }
284- fmt.Fprint(output, "\n")
285+ fmt.Fprint(output, "\n") //nolint:errcheck
286 }
287
288 func monitorTick(cfg *Cfg, log *slog.Logger, output io.Writer, jobStates map[string]*monitorJobState) error {
289@@ -1094,7 +1105,7 @@ func monitorTick(cfg *Cfg, log *slog.Logger, output io.Writer, jobStates map[str
290
291 // d. Sync artifacts once per tick
292 if cfg.HumanOutput {
293- fmt.Fprint(output, " š¦ syncing artifacts...\n")
294+ fmt.Fprint(output, " š¦ syncing artifacts...\n") //nolint:errcheck
295 }
296 if err := syncArtifacts(cfg, log); err != nil {
297 log.Error("sync artifacts", "err", err)
298@@ -1389,16 +1400,6 @@ func allCompleted(sessions []SessionInfo) bool {
299 return true
300 }
301
302-func countCompleted(sessions []SessionInfo) int {
303- count := 0
304- for _, s := range sessions {
305- if s.Ended != "" {
306- count++
307- }
308- }
309- return count
310-}
311-
312 func runCmd(cmd *exec.Cmd, log *slog.Logger) error {
313 stdout, err := cmd.StdoutPipe()
314 if err != nil {