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
| ... | ... | @@ -85,7 +85,10 @@ func NewCfg() (*Cfg, string, bool) { | |
| 85 | 85 | // Split args so the subcommand (first non-flag arg) doesn't block | |
| 86 | 86 | // flags that appear after it: "pici runner --wait" works. | |
| 87 | 87 | 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 | + | } | |
| 89 | 92 | ||
| 90 | 93 | logger := newLogger("ci", logLevel) | |
| 91 | 94 | ctx, cancel := context.WithCancel(context.Background()) |
| ... | ... | @@ -381,7 +384,11 @@ func (w *WorkspaceTar) Setup() error { | |
| 381 | 384 | if err != nil { | |
| 382 | 385 | return fmt.Errorf("open tar: %w", err) | |
| 383 | 386 | } | |
| 384 | - | defer f.Close() | |
| 387 | + | defer func() { | |
| 388 | + | if err := f.Close(); err != nil { | |
| 389 | + | log.Error("close tar", "err", err) | |
| 390 | + | } | |
| 391 | + | }() | |
| 385 | 392 | ||
| 386 | 393 | // Extract tar to temp dir | |
| 387 | 394 | tr := tar.NewReader(f) |
| ... | ... | @@ -410,10 +417,14 @@ func (w *WorkspaceTar) Setup() error { | |
| 410 | 417 | return fmt.Errorf("open %s: %w", target, err) | |
| 411 | 418 | } | |
| 412 | 419 | 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 | + | } | |
| 414 | 423 | return fmt.Errorf("write %s: %w", target, err) | |
| 415 | 424 | } | |
| 416 | - | tf.Close() | |
| 425 | + | if err := tf.Close(); err != nil { | |
| 426 | + | return fmt.Errorf("close %s: %w", target, err) | |
| 427 | + | } | |
| 417 | 428 | } | |
| 418 | 429 | } | |
| 419 | 430 |
| ... | ... | @@ -538,9 +549,9 @@ func eventHandler(cfg *Cfg, eventData *Event) error { | |
| 538 | 549 | jobID := generateJobID(eventData.Name, eventData.Workspace) | |
| 539 | 550 | log = log.With("job_id", jobID) | |
| 540 | 551 | 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 | |
| 544 | 555 | ||
| 545 | 556 | wk := cfg.NewWorkspace(cfg, log, eventData.Workspace) | |
| 546 | 557 | eng := &JobEngine{ |
| ... | ... | @@ -556,11 +567,11 @@ func eventHandler(cfg *Cfg, eventData *Event) error { | |
| 556 | 567 | } | |
| 557 | 568 | }() | |
| 558 | 569 | ||
| 559 | - | fmt.Fprintf(os.Stdout, "📦 syncing workspace %s\n", eventData.Workspace) | |
| 570 | + | fmt.Fprintf(os.Stdout, "📦 syncing workspace %s\n", eventData.Workspace) //nolint:errcheck | |
| 560 | 571 | if err := eng.Setup(); err != nil { | |
| 561 | 572 | return fmt.Errorf("setup: %w", err) | |
| 562 | 573 | } | |
| 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 | |
| 564 | 575 | ||
| 565 | 576 | // Store the event in the artifact directory so the monitor can access it | |
| 566 | 577 | eventDir := filepath.Join(cfg.ArtifactDir, eventData.Name, jobID) |
| ... | ... | @@ -574,7 +585,8 @@ func eventHandler(cfg *Cfg, eventData *Event) error { | |
| 574 | 585 | ||
| 575 | 586 | manifest, err := eng.FindManifest() | |
| 576 | 587 | 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 | |
| 578 | 590 | fmt.Fprint(os.Stdout, `Create a pico.sh script in your workspace root: | |
| 579 | 591 | ||
| 580 | 592 | #!/usr/bin/env bash |
| ... | ... | @@ -601,14 +613,14 @@ See: https://github.com/picosh/pici | |
| 601 | 613 | `) | |
| 602 | 614 | return err | |
| 603 | 615 | } | |
| 604 | - | fmt.Fprintf(os.Stdout, "🔍 found %s\n", manifest) | |
| 616 | + | fmt.Fprintf(os.Stdout, "🔍 found %s\n", manifest) //nolint:errcheck | |
| 605 | 617 | ||
| 606 | - | fmt.Fprint(os.Stdout, "🏃 launching sessions...\n") | |
| 618 | + | fmt.Fprint(os.Stdout, "🏃 launching sessions...\n") //nolint:errcheck | |
| 607 | 619 | if err := eng.Run(manifest); err != nil { | |
| 608 | 620 | return fmt.Errorf("run: %w", err) | |
| 609 | 621 | } | |
| 610 | 622 | ||
| 611 | - | fmt.Fprintln(os.Stdout, "✅ job launched") | |
| 623 | + | fmt.Fprintln(os.Stdout, "✅ job launched") //nolint:errcheck | |
| 612 | 624 | ||
| 613 | 625 | if cfg.Wait { | |
| 614 | 626 | if err := waitAndReport(cfg, log, eventData.Name, jobID); err != nil { |
| ... | ... | @@ -618,9 +630,9 @@ See: https://github.com/picosh/pici | |
| 618 | 630 | } | |
| 619 | 631 | ||
| 620 | 632 | 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 | |
| 624 | 636 | return nil | |
| 625 | 637 | } | |
| 626 | 638 |
| ... | ... | @@ -636,8 +648,8 @@ func waitAndReport(cfg *Cfg, log *slog.Logger, name, jobID string) error { | |
| 636 | 648 | signal.Notify(sigCh, syscall.SIGINT) | |
| 637 | 649 | defer signal.Stop(sigCh) | |
| 638 | 650 | ||
| 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 | |
| 641 | 653 | ||
| 642 | 654 | // Track state for each session | |
| 643 | 655 | known := make(map[string]*sessionState) |
| ... | ... | @@ -650,7 +662,7 @@ func waitAndReport(cfg *Cfg, log *slog.Logger, name, jobID string) error { | |
| 650 | 662 | case <-cfg.Ctx.Done(): | |
| 651 | 663 | return cfg.Ctx.Err() | |
| 652 | 664 | case <-sigCh: | |
| 653 | - | fmt.Fprintln(os.Stdout, "\n⏹ cancelled") | |
| 665 | + | fmt.Fprintln(os.Stdout, "\n⏹ cancelled") //nolint:errcheck | |
| 654 | 666 | return nil | |
| 655 | 667 | case <-ticker.C: | |
| 656 | 668 | } |
| ... | ... | @@ -717,21 +729,21 @@ func waitAndReport(cfg *Cfg, log *slog.Logger, name, jobID string) error { | |
| 717 | 729 | ||
| 718 | 730 | // Overwrite previous lines with cursor-up, or print fresh | |
| 719 | 731 | if len(liveLines) > 0 { | |
| 720 | - | // Move cursor up to overwrite previous lines | |
| 732 | + | // Move cursor up to overwrite previous lines //nolint:errcheck | |
| 721 | 733 | for range len(liveLines) { | |
| 722 | - | fmt.Fprint(os.Stdout, "\033[A") | |
| 734 | + | fmt.Fprint(os.Stdout, "\033[A") //nolint:errcheck | |
| 723 | 735 | } | |
| 724 | 736 | // Clear each line | |
| 725 | - | for i, line := range lines { | |
| 737 | + | for i, line := range lines { //nolint:errcheck | |
| 726 | 738 | if i > 0 { | |
| 727 | - | fmt.Fprint(os.Stdout, "\n") | |
| 739 | + | fmt.Fprint(os.Stdout, "\n") //nolint:errcheck | |
| 728 | 740 | } | |
| 729 | - | fmt.Fprint(os.Stdout, line+"\033[K") | |
| 741 | + | fmt.Fprint(os.Stdout, line+"\033[K") //nolint:errcheck | |
| 730 | 742 | } | |
| 731 | - | fmt.Fprint(os.Stdout, "\n") | |
| 732 | - | } else { | |
| 743 | + | fmt.Fprint(os.Stdout, "\n") //nolint:errcheck | |
| 744 | + | } else { //nolint:errcheck | |
| 733 | 745 | for _, line := range lines { | |
| 734 | - | fmt.Fprintln(os.Stdout, line) | |
| 746 | + | fmt.Fprintln(os.Stdout, line) //nolint:errcheck | |
| 735 | 747 | } | |
| 736 | 748 | } | |
| 737 | 749 | liveLines = lines |
| ... | ... | @@ -743,7 +755,7 @@ func waitAndReport(cfg *Cfg, log *slog.Logger, name, jobID string) error { | |
| 743 | 755 | } | |
| 744 | 756 | ||
| 745 | 757 | // Print last 25 lines of history for failed sessions only | |
| 746 | - | fmt.Fprintln(os.Stdout) | |
| 758 | + | fmt.Fprintln(os.Stdout) //nolint:errcheck | |
| 747 | 759 | for _, s := range jobSessions { | |
| 748 | 760 | state := known[s.Short] | |
| 749 | 761 | if state == nil || state.status != "failed" { |
| ... | ... | @@ -751,28 +763,28 @@ func waitAndReport(cfg *Cfg, log *slog.Logger, name, jobID string) error { | |
| 751 | 763 | } | |
| 752 | 764 | ||
| 753 | 765 | 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 | |
| 758 | 770 | ||
| 759 | 771 | history, err := fetchHistoryPlain(s.Name) | |
| 760 | 772 | 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 | |
| 762 | 774 | } else { | |
| 763 | 775 | lines := strings.Split(history, "\n") | |
| 764 | 776 | // Show last 25 lines | |
| 765 | 777 | 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 | |
| 767 | 779 | lines = lines[len(lines)-25:] | |
| 768 | 780 | } | |
| 769 | 781 | for _, line := range lines { | |
| 770 | 782 | if line != "" { | |
| 771 | - | fmt.Fprintf(os.Stdout, " %s\n", line) | |
| 783 | + | fmt.Fprintf(os.Stdout, " %s\n", line) //nolint:errcheck | |
| 772 | 784 | } | |
| 773 | 785 | } | |
| 774 | 786 | } | |
| 775 | - | fmt.Fprintln(os.Stdout) | |
| 787 | + | fmt.Fprintln(os.Stdout) //nolint:errcheck | |
| 776 | 788 | } | |
| 777 | 789 | ||
| 778 | 790 | // Final summary |
| ... | ... | @@ -780,9 +792,9 @@ func waitAndReport(cfg *Cfg, log *slog.Logger, name, jobID string) error { | |
| 780 | 792 | _, _, duration := computeJobTiming(jobSessions) | |
| 781 | 793 | icon := map[string]string{"success": "✅", "failed": "❌"}[status] | |
| 782 | 794 | 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 | |
| 784 | 796 | } 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 | |
| 786 | 798 | } | |
| 787 | 799 | ||
| 788 | 800 | return nil |
| ... | ... | @@ -822,7 +834,6 @@ type monitorJobState struct { | |
| 822 | 834 | sessionOrder []string // insertion order for deterministic output | |
| 823 | 835 | sessions map[string]*sessionState | |
| 824 | 836 | liveLines []string // last set of status lines printed (for overwrite) | |
| 825 | - | published bool // final status already printed | |
| 826 | 837 | } | |
| 827 | 838 | ||
| 828 | 839 | func runMonitor(cfg *Cfg) error { |
| ... | ... | @@ -911,19 +922,19 @@ func renderJobRunning(output io.Writer, name, jobID string, group []SessionInfo, | |
| 911 | 922 | // Overwrite previous lines or print fresh | |
| 912 | 923 | if len(state.liveLines) > 0 { | |
| 913 | 924 | for range len(state.liveLines) { | |
| 914 | - | fmt.Fprint(output, "\033[A") | |
| 925 | + | fmt.Fprint(output, "\033[A") //nolint:errcheck | |
| 915 | 926 | } | |
| 916 | 927 | for i, line := range lines { | |
| 917 | 928 | if i > 0 { | |
| 918 | - | fmt.Fprint(output, "\n") | |
| 929 | + | fmt.Fprint(output, "\n") //nolint:errcheck | |
| 919 | 930 | } | |
| 920 | - | fmt.Fprint(output, line+"\033[K") | |
| 931 | + | fmt.Fprint(output, line+"\033[K") //nolint:errcheck | |
| 921 | 932 | } | |
| 922 | - | fmt.Fprint(output, "\n") | |
| 933 | + | fmt.Fprint(output, "\n") //nolint:errcheck | |
| 923 | 934 | } else { | |
| 924 | - | fmt.Fprintf(output, " %-12s %s %s\n", name, "🚀", "running") | |
| 935 | + | fmt.Fprintf(output, " %-12s %s %s\n", name, "🚀", "running") //nolint:errcheck | |
| 925 | 936 | for _, line := range lines { | |
| 926 | - | fmt.Fprintln(output, line) | |
| 937 | + | fmt.Fprintln(output, line) //nolint:errcheck | |
| 927 | 938 | } | |
| 928 | 939 | } | |
| 929 | 940 | state.liveLines = lines |
| ... | ... | @@ -932,7 +943,7 @@ func renderJobRunning(output io.Writer, name, jobID string, group []SessionInfo, | |
| 932 | 943 | // renderJobFinal prints the final status for a completed job. | |
| 933 | 944 | func renderJobFinal(output io.Writer, name, jobID string, group []SessionInfo, duration, status string, success bool, workspace, artifactDir, artifactURL string) { | |
| 934 | 945 | 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 | |
| 936 | 947 | ||
| 937 | 948 | // Per-session summary | |
| 938 | 949 | for _, s := range group { |
| ... | ... | @@ -941,20 +952,20 @@ func renderJobFinal(output io.Writer, name, jobID string, group []SessionInfo, d | |
| 941 | 952 | icon = "❌" | |
| 942 | 953 | } | |
| 943 | 954 | 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 | |
| 945 | 956 | } | |
| 946 | 957 | ||
| 947 | 958 | // Context info | |
| 948 | - | fmt.Fprint(output, "\n") | |
| 959 | + | fmt.Fprint(output, "\n") //nolint:errcheck | |
| 949 | 960 | if workspace != "" { | |
| 950 | - | fmt.Fprintf(output, " workspace: %s\n", workspace) | |
| 961 | + | fmt.Fprintf(output, " workspace: %s\n", workspace) //nolint:errcheck | |
| 951 | 962 | } | |
| 952 | 963 | artifactPath := filepath.Join(artifactDir, name, jobID) | |
| 953 | - | fmt.Fprintf(output, " artifacts: %s\n", artifactPath) | |
| 964 | + | fmt.Fprintf(output, " artifacts: %s\n", artifactPath) //nolint:errcheck | |
| 954 | 965 | if artifactURL != "" { | |
| 955 | - | fmt.Fprintf(output, " url: %s\n", artifactURL) | |
| 966 | + | fmt.Fprintf(output, " url: %s\n", artifactURL) //nolint:errcheck | |
| 956 | 967 | } | |
| 957 | - | fmt.Fprint(output, "\n") | |
| 968 | + | fmt.Fprint(output, "\n") //nolint:errcheck | |
| 958 | 969 | } | |
| 959 | 970 | ||
| 960 | 971 | 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 | |
| 1094 | 1105 | ||
| 1095 | 1106 | // d. Sync artifacts once per tick | |
| 1096 | 1107 | if cfg.HumanOutput { | |
| 1097 | - | fmt.Fprint(output, " 📦 syncing artifacts...\n") | |
| 1108 | + | fmt.Fprint(output, " 📦 syncing artifacts...\n") //nolint:errcheck | |
| 1098 | 1109 | } | |
| 1099 | 1110 | if err := syncArtifacts(cfg, log); err != nil { | |
| 1100 | 1111 | log.Error("sync artifacts", "err", err) |
| ... | ... | @@ -1389,16 +1400,6 @@ func allCompleted(sessions []SessionInfo) bool { | |
| 1389 | 1400 | return true | |
| 1390 | 1401 | } | |
| 1391 | 1402 | ||
| 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 | - | ||
| 1402 | 1403 | func runCmd(cmd *exec.Cmd, log *slog.Logger) error { | |
| 1403 | 1404 | stdout, err := cmd.StdoutPipe() | |
| 1404 | 1405 | if err != nil { |