| Initial import of the rickub CLI as a standalone public project 1a1d430 Olivier Girardot 9h ago | 1 | package cmd |
| 2 | |
| 3 | import ( |
| 4 | "fmt" |
| 5 | "strings" |
| 6 | |
| 7 | "github.com/spf13/cobra" |
| 8 | ) |
| 9 | |
| 10 | var ( |
| 11 | milestoneRepo string |
| 12 | milestoneState string |
| 13 | milestoneTitle string |
| 14 | milestoneDue string |
| 15 | milestoneDescription string |
| 16 | ) |
| 17 | |
| 18 | func init() { |
| 19 | msCmd := &cobra.Command{ |
| 20 | Use: "milestone", |
| 21 | Aliases: []string{"milestones"}, |
| 22 | Short: "Work with milestones", |
| 23 | } |
| 24 | msCmd.PersistentFlags().StringVarP(&milestoneRepo, "repo", "R", "", "target repo as owner/repo (default: current git remote)") |
| 25 | |
| 26 | listCmd := &cobra.Command{ |
| 27 | Use: "list", |
| 28 | Short: "List milestones", |
| 29 | Args: cobra.NoArgs, |
| 30 | RunE: runMilestoneList, |
| 31 | } |
| 32 | listCmd.Flags().StringVar(&milestoneState, "state", "open", "open | closed | all") |
| 33 | |
| 34 | createCmd := &cobra.Command{ |
| 35 | Use: "create", |
| 36 | Short: "Create a milestone", |
| 37 | Args: cobra.NoArgs, |
| 38 | RunE: runMilestoneCreate, |
| 39 | } |
| 40 | createCmd.Flags().StringVarP(&milestoneTitle, "title", "t", "", "title (required)") |
| 41 | createCmd.Flags().StringVarP(&milestoneDescription, "description", "d", "", "description") |
| 42 | createCmd.Flags().StringVar(&milestoneDue, "due", "", "due date, YYYY-MM-DD") |
| 43 | |
| 44 | closeCmd := &cobra.Command{ |
| 45 | Use: "close <id>", |
| 46 | Short: "Close a milestone (by id; see `milestone list`)", |
| 47 | Args: cobra.ExactArgs(1), |
| 48 | RunE: func(cmd *cobra.Command, args []string) error { return runMilestoneSetState(cmd, args, "closed") }, |
| 49 | } |
| 50 | |
| 51 | reopenCmd := &cobra.Command{ |
| 52 | Use: "reopen <id>", |
| 53 | Short: "Reopen a closed milestone", |
| 54 | Args: cobra.ExactArgs(1), |
| 55 | RunE: func(cmd *cobra.Command, args []string) error { return runMilestoneSetState(cmd, args, "open") }, |
| 56 | } |
| 57 | |
| 58 | deleteCmd := &cobra.Command{ |
| 59 | Use: "delete <id>", |
| 60 | Short: "Delete a milestone (issues keep going, milestone cleared)", |
| 61 | Args: cobra.ExactArgs(1), |
| 62 | RunE: runMilestoneDelete, |
| 63 | } |
| 64 | |
| 65 | msCmd.AddCommand(listCmd, createCmd, closeCmd, reopenCmd, deleteCmd) |
| 66 | rootCmd.AddCommand(msCmd) |
| 67 | } |
| 68 | |
| 69 | func runMilestoneList(cmd *cobra.Command, _ []string) error { |
| 70 | client, err := newClient() |
| 71 | if err != nil { |
| 72 | return err |
| 73 | } |
| 74 | owner, repo, err := resolveRepo(milestoneRepo) |
| 75 | if err != nil { |
| 76 | return err |
| 77 | } |
| 78 | mses, err := client.ListMilestones(cmd.Context(), owner, repo, milestoneState) |
| 79 | if err != nil { |
| 80 | return err |
| 81 | } |
| 82 | if flagJSON { |
| 83 | return printJSON(cmd.OutOrStdout(), mses) |
| 84 | } |
| 85 | if len(mses) == 0 { |
| 86 | fmt.Fprintln(cmd.OutOrStdout(), "No milestones.") |
| 87 | return nil |
| 88 | } |
| 89 | tw := newTabw(cmd.OutOrStdout()) |
| 90 | fmt.Fprintln(tw, "ID\tSTATE\tDUE\tPROGRESS\tTITLE") |
| 91 | for _, m := range mses { |
| 92 | progress := fmt.Sprintf("%d/%d", m.ClosedCount, m.OpenCount+m.ClosedCount) |
| 93 | fmt.Fprintf(tw, "%s\t%s\t%s\t%s\t%s\n", m.ID, m.State, dash(deref(m.DueOn)), progress, m.Title) |
| 94 | } |
| 95 | tw.Flush() |
| 96 | return nil |
| 97 | } |
| 98 | |
| 99 | func deref(s *string) string { |
| 100 | if s == nil { |
| 101 | return "" |
| 102 | } |
| 103 | return *s |
| 104 | } |
| 105 | |
| 106 | func runMilestoneCreate(cmd *cobra.Command, _ []string) error { |
| 107 | if strings.TrimSpace(milestoneTitle) == "" { |
| 108 | return fmt.Errorf("--title is required") |
| 109 | } |
| 110 | client, err := newClient() |
| 111 | if err != nil { |
| 112 | return err |
| 113 | } |
| 114 | owner, repo, err := resolveRepo(milestoneRepo) |
| 115 | if err != nil { |
| 116 | return err |
| 117 | } |
| 118 | m, err := client.CreateMilestone(cmd.Context(), owner, repo, milestoneTitle, milestoneDescription, milestoneDue) |
| 119 | if err != nil { |
| 120 | return err |
| 121 | } |
| 122 | if flagJSON { |
| 123 | return printJSON(cmd.OutOrStdout(), m) |
| 124 | } |
| 125 | fmt.Fprintf(cmd.OutOrStdout(), "Created milestone %s (%s)\n", m.Title, m.ID) |
| 126 | return nil |
| 127 | } |
| 128 | |
| 129 | func runMilestoneSetState(cmd *cobra.Command, args []string, state string) error { |
| 130 | client, err := newClient() |
| 131 | if err != nil { |
| 132 | return err |
| 133 | } |
| 134 | owner, repo, err := resolveRepo(milestoneRepo) |
| 135 | if err != nil { |
| 136 | return err |
| 137 | } |
| 138 | m, err := client.UpdateMilestone(cmd.Context(), owner, repo, args[0], map[string]any{"state": state}) |
| 139 | if err != nil { |
| 140 | return err |
| 141 | } |
| 142 | if flagJSON { |
| 143 | return printJSON(cmd.OutOrStdout(), m) |
| 144 | } |
| 145 | fmt.Fprintf(cmd.OutOrStdout(), "Milestone %s is now %s.\n", m.Title, m.State) |
| 146 | return nil |
| 147 | } |
| 148 | |
| 149 | func runMilestoneDelete(cmd *cobra.Command, args []string) error { |
| 150 | client, err := newClient() |
| 151 | if err != nil { |
| 152 | return err |
| 153 | } |
| 154 | owner, repo, err := resolveRepo(milestoneRepo) |
| 155 | if err != nil { |
| 156 | return err |
| 157 | } |
| 158 | if err := client.DeleteMilestone(cmd.Context(), owner, repo, args[0]); err != nil { |
| 159 | return err |
| 160 | } |
| 161 | fmt.Fprintf(cmd.OutOrStdout(), "Deleted milestone %s.\n", args[0]) |
| 162 | return nil |
| 163 | } |