Commit a29c053

Eric Bower  ·  2026-05-07 23:26:36 -0400 EDT
parent 8fc0a2e
fix: linter
1 files changed,  +64, -63
+64, -63
......@@ -85,7 +85,10 @@ func NewCfg() (*Cfg, string, bool) {
8585 // Split args so the subcommand (first non-flag arg) doesn't block
8686 // flags that appear after it: "pici runner --wait" works.
8787 flags, cmd, wantHelp := splitCommand(os.Args[1:])
88- flag.CommandLine.Parse(flags)
88+ if err := flag.CommandLine.Parse(flags); err != nil {
89+ fmt.Fprintf(os.Stderr, "failed to parse flags: %v\n", err)
90+ os.Exit(1)
91+ }
8992
9093 logger := newLogger("ci", logLevel)
9194 ctx, cancel := context.WithCancel(context.Background())
......@@ -381,7 +384,11 @@ func (w *WorkspaceTar) Setup() error {
381384 if err != nil {
382385 return fmt.Errorf("open tar: %w", err)
383386 }
384- defer f.Close()
387+ defer func() {
388+ if err := f.Close(); err != nil {
389+ log.Error("close tar", "err", err)
390+ }
391+ }()
385392
386393 // Extract tar to temp dir
387394 tr := tar.NewReader(f)
......@@ -410,10 +417,14 @@ func (w *WorkspaceTar) Setup() error {
410417 return fmt.Errorf("open %s: %w", target, err)
411418 }
412419 if _, err := io.Copy(tf, tr); err != nil {
413- tf.Close()
420+ if closeErr := tf.Close(); closeErr != nil {
421+ return fmt.Errorf("close %s: %w", target, closeErr)
422+ }
414423 return fmt.Errorf("write %s: %w", target, err)
415424 }
416- tf.Close()
425+ if err := tf.Close(); err != nil {
426+ return fmt.Errorf("close %s: %w", target, err)
427+ }
417428 }
418429 }
419430
......@@ -538,9 +549,9 @@ func eventHandler(cfg *Cfg, eventData *Event) error {
538549 jobID := generateJobID(eventData.Name, eventData.Workspace)
539550 log = log.With("job_id", jobID)
540551 eventBytes, _ := json.Marshal(eventData)
541- fmt.Fprintf(os.Stdout, "🚀 starting job ci.%s.%s\n", eventData.Name, jobID)
542- fmt.Fprintf(os.Stdout, " event: type=%s name=%s workspace=%s\n", eventData.Type, eventData.Name, eventData.Workspace)
543- fmt.Fprintf(os.Stdout, " %s\n", string(eventBytes))
552+ fmt.Fprintf(os.Stdout, "🚀 starting job ci.%s.%s\n", eventData.Name, jobID) //nolint:errcheck
553+ fmt.Fprintf(os.Stdout, " event: type=%s name=%s workspace=%s\n", eventData.Type, eventData.Name, eventData.Workspace) //nolint:errcheck
554+ fmt.Fprintf(os.Stdout, " %s\n", string(eventBytes)) //nolint:errcheck
544555
545556 wk := cfg.NewWorkspace(cfg, log, eventData.Workspace)
546557 eng := &JobEngine{
......@@ -556,11 +567,11 @@ func eventHandler(cfg *Cfg, eventData *Event) error {
556567 }
557568 }()
558569
559- fmt.Fprintf(os.Stdout, "📦 syncing workspace %s\n", eventData.Workspace)
570+ fmt.Fprintf(os.Stdout, "📦 syncing workspace %s\n", eventData.Workspace) //nolint:errcheck
560571 if err := eng.Setup(); err != nil {
561572 return fmt.Errorf("setup: %w", err)
562573 }
563- fmt.Fprintf(os.Stdout, "✅ workspace ready %s\n", eng.Wk.GetDir())
574+ fmt.Fprintf(os.Stdout, "✅ workspace ready %s\n", eng.Wk.GetDir()) //nolint:errcheck
564575
565576 // Store the event in the artifact directory so the monitor can access it
566577 eventDir := filepath.Join(cfg.ArtifactDir, eventData.Name, jobID)
......@@ -574,7 +585,8 @@ func eventHandler(cfg *Cfg, eventData *Event) error {
574585
575586 manifest, err := eng.FindManifest()
576587 if err != nil {
577- fmt.Fprintf(os.Stdout, "❌ %s\n\n", err)
588+ fmt.Fprintf(os.Stdout, "❌ %s\n\n", err) //nolint:errcheck
589+ //nolint:errcheck
578590 fmt.Fprint(os.Stdout, `Create a pico.sh script in your workspace root:
579591
580592 #!/usr/bin/env bash
......@@ -601,14 +613,14 @@ See: https://github.com/picosh/pici
601613 `)
602614 return err
603615 }
604- fmt.Fprintf(os.Stdout, "🔍 found %s\n", manifest)
616+ fmt.Fprintf(os.Stdout, "🔍 found %s\n", manifest) //nolint:errcheck
605617
606- fmt.Fprint(os.Stdout, "🏃 launching sessions...\n")
618+ fmt.Fprint(os.Stdout, "🏃 launching sessions...\n") //nolint:errcheck
607619 if err := eng.Run(manifest); err != nil {
608620 return fmt.Errorf("run: %w", err)
609621 }
610622
611- fmt.Fprintln(os.Stdout, "✅ job launched")
623+ fmt.Fprintln(os.Stdout, "✅ job launched") //nolint:errcheck
612624
613625 if cfg.Wait {
614626 if err := waitAndReport(cfg, log, eventData.Name, jobID); err != nil {
......@@ -618,9 +630,9 @@ See: https://github.com/picosh/pici
618630 }
619631
620632 session := fmt.Sprintf("ci.%s.%s.runner", eventData.Name, jobID)
621- fmt.Fprintf(os.Stdout, " zmx tail %s\n", session)
622- fmt.Fprintf(os.Stdout, " zmx history %s\n", session)
623- fmt.Fprintf(os.Stdout, " zmx attach %s\n", session)
633+ fmt.Fprintf(os.Stdout, " zmx tail %s\n", session) //nolint:errcheck
634+ fmt.Fprintf(os.Stdout, " zmx history %s\n", session) //nolint:errcheck
635+ fmt.Fprintf(os.Stdout, " zmx attach %s\n", session) //nolint:errcheck
624636 return nil
625637 }
626638
......@@ -636,8 +648,8 @@ func waitAndReport(cfg *Cfg, log *slog.Logger, name, jobID string) error {
636648 signal.Notify(sigCh, syscall.SIGINT)
637649 defer signal.Stop(sigCh)
638650
639- fmt.Fprintln(os.Stdout)
640- fmt.Fprint(os.Stdout, "⏳ waiting for completion...\n")
651+ fmt.Fprintln(os.Stdout) //nolint:errcheck
652+ fmt.Fprint(os.Stdout, "⏳ waiting for completion...\n") //nolint:errcheck
641653
642654 // Track state for each session
643655 known := make(map[string]*sessionState)
......@@ -650,7 +662,7 @@ func waitAndReport(cfg *Cfg, log *slog.Logger, name, jobID string) error {
650662 case <-cfg.Ctx.Done():
651663 return cfg.Ctx.Err()
652664 case <-sigCh:
653- fmt.Fprintln(os.Stdout, "\n⏹ cancelled")
665+ fmt.Fprintln(os.Stdout, "\n⏹ cancelled") //nolint:errcheck
654666 return nil
655667 case <-ticker.C:
656668 }
......@@ -717,21 +729,21 @@ func waitAndReport(cfg *Cfg, log *slog.Logger, name, jobID string) error {
717729
718730 // Overwrite previous lines with cursor-up, or print fresh
719731 if len(liveLines) > 0 {
720- // Move cursor up to overwrite previous lines
732+ // Move cursor up to overwrite previous lines //nolint:errcheck
721733 for range len(liveLines) {
722- fmt.Fprint(os.Stdout, "\033[A")
734+ fmt.Fprint(os.Stdout, "\033[A") //nolint:errcheck
723735 }
724736 // Clear each line
725- for i, line := range lines {
737+ for i, line := range lines { //nolint:errcheck
726738 if i > 0 {
727- fmt.Fprint(os.Stdout, "\n")
739+ fmt.Fprint(os.Stdout, "\n") //nolint:errcheck
728740 }
729- fmt.Fprint(os.Stdout, line+"\033[K")
741+ fmt.Fprint(os.Stdout, line+"\033[K") //nolint:errcheck
730742 }
731- fmt.Fprint(os.Stdout, "\n")
732- } else {
743+ fmt.Fprint(os.Stdout, "\n") //nolint:errcheck
744+ } else { //nolint:errcheck
733745 for _, line := range lines {
734- fmt.Fprintln(os.Stdout, line)
746+ fmt.Fprintln(os.Stdout, line) //nolint:errcheck
735747 }
736748 }
737749 liveLines = lines
......@@ -743,7 +755,7 @@ func waitAndReport(cfg *Cfg, log *slog.Logger, name, jobID string) error {
743755 }
744756
745757 // Print last 25 lines of history for failed sessions only
746- fmt.Fprintln(os.Stdout)
758+ fmt.Fprintln(os.Stdout) //nolint:errcheck
747759 for _, s := range jobSessions {
748760 state := known[s.Short]
749761 if state == nil || state.status != "failed" {
......@@ -751,28 +763,28 @@ func waitAndReport(cfg *Cfg, log *slog.Logger, name, jobID string) error {
751763 }
752764
753765 separator := strings.Repeat("\u2500", 50)
754- fmt.Fprintln(os.Stdout, separator)
755- fmt.Fprintf(os.Stdout, "Session: %s (exit %s)\n", s.Short, state.exitCode)
756- fmt.Fprintln(os.Stdout, separator)
757- fmt.Fprintln(os.Stdout)
766+ fmt.Fprintln(os.Stdout, separator) //nolint:errcheck
767+ fmt.Fprintf(os.Stdout, "Session: %s (exit %s)\n", s.Short, state.exitCode) //nolint:errcheck
768+ fmt.Fprintln(os.Stdout, separator) //nolint:errcheck
769+ fmt.Fprintln(os.Stdout) //nolint:errcheck
758770
759771 history, err := fetchHistoryPlain(s.Name)
760772 if err != nil {
761- fmt.Fprintf(os.Stdout, " (history unavailable: %v)\n", err)
773+ fmt.Fprintf(os.Stdout, " (history unavailable: %v)\n", err) //nolint:errcheck
762774 } else {
763775 lines := strings.Split(history, "\n")
764776 // Show last 25 lines
765777 if len(lines) > 25 {
766- fmt.Fprintf(os.Stdout, " ... (%d lines omitted)\n", len(lines)-25)
778+ fmt.Fprintf(os.Stdout, " ... (%d lines omitted)\n", len(lines)-25) //nolint:errcheck
767779 lines = lines[len(lines)-25:]
768780 }
769781 for _, line := range lines {
770782 if line != "" {
771- fmt.Fprintf(os.Stdout, " %s\n", line)
783+ fmt.Fprintf(os.Stdout, " %s\n", line) //nolint:errcheck
772784 }
773785 }
774786 }
775- fmt.Fprintln(os.Stdout)
787+ fmt.Fprintln(os.Stdout) //nolint:errcheck
776788 }
777789
778790 // Final summary
......@@ -780,9 +792,9 @@ func waitAndReport(cfg *Cfg, log *slog.Logger, name, jobID string) error {
780792 _, _, duration := computeJobTiming(jobSessions)
781793 icon := map[string]string{"success": "✅", "failed": "❌"}[status]
782794 if exitCode == 0 {
783- fmt.Fprintf(os.Stdout, "%s job finished: %s (%s)\n", icon, status, duration)
795+ fmt.Fprintf(os.Stdout, "%s job finished: %s (%s)\n", icon, status, duration) //nolint:errcheck
784796 } else {
785- fmt.Fprintf(os.Stdout, "%s job failed: exit %d (%s)\n", icon, exitCode, duration)
797+ fmt.Fprintf(os.Stdout, "%s job failed: exit %d (%s)\n", icon, exitCode, duration) //nolint:errcheck
786798 }
787799
788800 return nil
......@@ -822,7 +834,6 @@ type monitorJobState struct {
822834 sessionOrder []string // insertion order for deterministic output
823835 sessions map[string]*sessionState
824836 liveLines []string // last set of status lines printed (for overwrite)
825- published bool // final status already printed
826837 }
827838
828839 func runMonitor(cfg *Cfg) error {
......@@ -911,19 +922,19 @@ func renderJobRunning(output io.Writer, name, jobID string, group []SessionInfo,
911922 // Overwrite previous lines or print fresh
912923 if len(state.liveLines) > 0 {
913924 for range len(state.liveLines) {
914- fmt.Fprint(output, "\033[A")
925+ fmt.Fprint(output, "\033[A") //nolint:errcheck
915926 }
916927 for i, line := range lines {
917928 if i > 0 {
918- fmt.Fprint(output, "\n")
929+ fmt.Fprint(output, "\n") //nolint:errcheck
919930 }
920- fmt.Fprint(output, line+"\033[K")
931+ fmt.Fprint(output, line+"\033[K") //nolint:errcheck
921932 }
922- fmt.Fprint(output, "\n")
933+ fmt.Fprint(output, "\n") //nolint:errcheck
923934 } else {
924- fmt.Fprintf(output, " %-12s %s %s\n", name, "🚀", "running")
935+ fmt.Fprintf(output, " %-12s %s %s\n", name, "🚀", "running") //nolint:errcheck
925936 for _, line := range lines {
926- fmt.Fprintln(output, line)
937+ fmt.Fprintln(output, line) //nolint:errcheck
927938 }
928939 }
929940 state.liveLines = lines
......@@ -932,7 +943,7 @@ func renderJobRunning(output io.Writer, name, jobID string, group []SessionInfo,
932943 // renderJobFinal prints the final status for a completed job.
933944 func renderJobFinal(output io.Writer, name, jobID string, group []SessionInfo, duration, status string, success bool, workspace, artifactDir, artifactURL string) {
934945 icon := map[string]string{"success": "✅", "failed": "❌"}[status]
935- fmt.Fprintf(output, " %-12s %s %s (%s)\n", name, icon, status, duration)
946+ fmt.Fprintf(output, " %-12s %s %s (%s)\n", name, icon, status, duration) //nolint:errcheck
936947
937948 // Per-session summary
938949 for _, s := range group {
......@@ -941,20 +952,20 @@ func renderJobFinal(output io.Writer, name, jobID string, group []SessionInfo, d
941952 icon = "❌"
942953 }
943954 dur := fmtDuration(s.Created, s.Ended)
944- fmt.Fprintf(output, " %-12s %s done (%s)\n", s.Short, icon, dur)
955+ fmt.Fprintf(output, " %-12s %s done (%s)\n", s.Short, icon, dur) //nolint:errcheck
945956 }
946957
947958 // Context info
948- fmt.Fprint(output, "\n")
959+ fmt.Fprint(output, "\n") //nolint:errcheck
949960 if workspace != "" {
950- fmt.Fprintf(output, " workspace: %s\n", workspace)
961+ fmt.Fprintf(output, " workspace: %s\n", workspace) //nolint:errcheck
951962 }
952963 artifactPath := filepath.Join(artifactDir, name, jobID)
953- fmt.Fprintf(output, " artifacts: %s\n", artifactPath)
964+ fmt.Fprintf(output, " artifacts: %s\n", artifactPath) //nolint:errcheck
954965 if artifactURL != "" {
955- fmt.Fprintf(output, " url: %s\n", artifactURL)
966+ fmt.Fprintf(output, " url: %s\n", artifactURL) //nolint:errcheck
956967 }
957- fmt.Fprint(output, "\n")
968+ fmt.Fprint(output, "\n") //nolint:errcheck
958969 }
959970
960971 func monitorTick(cfg *Cfg, log *slog.Logger, output io.Writer, jobStates map[string]*monitorJobState) error {
......@@ -1094,7 +1105,7 @@ func monitorTick(cfg *Cfg, log *slog.Logger, output io.Writer, jobStates map[str
10941105
10951106 // d. Sync artifacts once per tick
10961107 if cfg.HumanOutput {
1097- fmt.Fprint(output, " 📦 syncing artifacts...\n")
1108+ fmt.Fprint(output, " 📦 syncing artifacts...\n") //nolint:errcheck
10981109 }
10991110 if err := syncArtifacts(cfg, log); err != nil {
11001111 log.Error("sync artifacts", "err", err)
......@@ -1389,16 +1400,6 @@ func allCompleted(sessions []SessionInfo) bool {
13891400 return true
13901401 }
13911402
1392-func countCompleted(sessions []SessionInfo) int {
1393- count := 0
1394- for _, s := range sessions {
1395- if s.Ended != "" {
1396- count++
1397- }
1398- }
1399- return count
1400-}
1401-
14021403 func runCmd(cmd *exec.Cmd, log *slog.Logger) error {
14031404 stdout, err := cmd.StdoutPipe()
14041405 if err != nil {