rickub/clipublic Fork 0
main
Commits
Clone
git clone https://git.rickub.com/rickub/cli.git
git clone ssh://git@rickub.com/rickub/cli.git
pr.go · 326 lines · 7.7 KBGo Blame HistoryRaw
Initial import of the rickub CLI as a standalone public project 1a1d430 Olivier Girardot 9h ago1package cmd
2
3import (
4 "fmt"
5 "strconv"
6
7 "rickub.com/rickub/cli/internal/api"
8
9 "github.com/spf13/cobra"
10)
11
12var (
13 prRepo string
14 prState string
15 prBase string
16 prHead string
17 prTitle string
18 prBody string
19 prHeadOwner string
20 prHeadRepo string
21 prMergeMethod string
22)
23
24func init() {
25 prCmd := &cobra.Command{
26 Use: "pr",
27 Aliases: []string{"mr", "pull"},
28 Short: "Work with merge requests (pull requests)",
29 }
30 // --repo is shared by all subcommands; defaults to the cwd git remote.
31 prCmd.PersistentFlags().StringVarP(&prRepo, "repo", "R", "", "target repo as owner/repo (default: current git remote)")
32
33 listCmd := &cobra.Command{
34 Use: "list",
35 Short: "List merge requests",
36 Args: cobra.NoArgs,
37 RunE: runPRList,
38 }
39 listCmd.Flags().StringVar(&prState, "state", "open", "open | closed | merged | all")
40 addPaging(listCmd)
41
42 viewCmd := &cobra.Command{
43 Use: "view <number>",
44 Short: "Show a merge request with comments and reviews",
45 Args: cobra.ExactArgs(1),
46 RunE: runPRView,
47 }
48
49 createCmd := &cobra.Command{
50 Use: "create",
51 Short: "Open a merge request",
52 Args: cobra.NoArgs,
53 RunE: runPRCreate,
54 }
55 createCmd.Flags().StringVar(&prBase, "base", "", "base branch (required)")
56 createCmd.Flags().StringVar(&prHead, "head", "", "head branch (required)")
57 createCmd.Flags().StringVarP(&prTitle, "title", "t", "", "title (required)")
58 createCmd.Flags().StringVarP(&prBody, "body", "b", "", "description body")
59 createCmd.Flags().StringVar(&prHeadOwner, "head-owner", "", "fork owner for a cross-repo MR")
60 createCmd.Flags().StringVar(&prHeadRepo, "head-repo", "", "fork name for a cross-repo MR")
61
62 mergeCmd := &cobra.Command{
63 Use: "merge <number>",
64 Short: "Merge a merge request",
65 Args: cobra.ExactArgs(1),
66 RunE: runPRMerge,
67 }
68 mergeCmd.Flags().StringVar(&prMergeMethod, "method", "merge", "merge | squash | ff-only")
69
70 closeCmd := &cobra.Command{
71 Use: "close <number>",
72 Short: "Close a merge request",
73 Args: cobra.ExactArgs(1),
74 RunE: runPRClose,
75 }
76
77 reopenCmd := &cobra.Command{
78 Use: "reopen <number>",
79 Short: "Reopen a closed merge request",
80 Args: cobra.ExactArgs(1),
81 RunE: runPRReopen,
82 }
83
84 commentCmd := &cobra.Command{
85 Use: "comment <number>",
86 Short: "Comment on a merge request",
87 Args: cobra.ExactArgs(1),
88 RunE: runPRComment,
89 }
90 commentCmd.Flags().StringVarP(&prBody, "body", "b", "", "comment body (required)")
91
92 prCmd.AddCommand(listCmd, viewCmd, createCmd, mergeCmd, closeCmd, reopenCmd, commentCmd)
93 rootCmd.AddCommand(prCmd)
94}
95
96func prNumber(arg string) (int, error) {
97 n, err := strconv.Atoi(arg)
98 if err != nil || n <= 0 {
99 return 0, fmt.Errorf("invalid merge-request number %q", arg)
100 }
101 return n, nil
102}
103
104func runPRList(cmd *cobra.Command, _ []string) error {
105 client, err := newClient()
106 if err != nil {
107 return err
108 }
109 owner, repo, err := resolveRepo(prRepo)
110 if err != nil {
111 return err
112 }
113 page, err := client.ListMergeRequests(cmd.Context(), owner, repo, prState, pageFlag, perPageFlag)
114 if err != nil {
115 return err
116 }
117 if flagJSON {
118 return printJSON(cmd.OutOrStdout(), page)
119 }
120 if len(page.Items) == 0 {
121 fmt.Fprintln(cmd.OutOrStdout(), "No merge requests.")
122 return nil
123 }
124 tw := newTabw(cmd.OutOrStdout())
125 fmt.Fprintln(tw, "#\tSTATE\tTITLE\tHEAD→BASE\tAUTHOR")
126 for _, mr := range page.Items {
127 fmt.Fprintf(tw, "%d\t%s\t%s\t%s→%s\t%s\n", mr.Number, mr.State, mr.Title, mr.HeadBranch, mr.BaseBranch, mr.Author)
128 }
129 tw.Flush()
130 printPageFooter(cmd, page.Page)
131 return nil
132}
133
134func runPRView(cmd *cobra.Command, args []string) error {
135 client, err := newClient()
136 if err != nil {
137 return err
138 }
139 owner, repo, err := resolveRepo(prRepo)
140 if err != nil {
141 return err
142 }
143 n, err := prNumber(args[0])
144 if err != nil {
145 return err
146 }
147 mr, err := client.GetMergeRequest(cmd.Context(), owner, repo, n)
148 if err != nil {
149 return err
150 }
151 if flagJSON {
152 return printJSON(cmd.OutOrStdout(), mr)
153 }
154 out := cmd.OutOrStdout()
155 fmt.Fprintf(out, "#%d %s [%s]\n", mr.Number, mr.Title, mr.State)
156 fmt.Fprintf(out, "%s wants to merge %s → %s\n", mr.Author, mr.HeadBranch, mr.BaseBranch)
157 if mr.CrossRepo {
158 fmt.Fprintf(out, "cross-repo from %s/%s\n", mr.HeadOwner, mr.HeadRepo)
159 }
160 if mr.Body != "" {
161 fmt.Fprintf(out, "\n%s\n", mr.Body)
162 }
163 if len(mr.Reviewers) > 0 {
164 fmt.Fprintf(out, "\nReviewers: %s\n", joinSubjects(mr.Reviewers))
165 }
166 if len(mr.Assignees) > 0 {
167 fmt.Fprintf(out, "Assignees: %s\n", joinSubjects(mr.Assignees))
168 }
169 if len(mr.Reviews) > 0 {
170 fmt.Fprintln(out, "\nReviews:")
171 for _, r := range mr.Reviews {
172 fmt.Fprintf(out, " %s: %s\n", r.Reviewer, r.Verdict)
173 }
174 }
175 if len(mr.Comments) > 0 {
176 fmt.Fprintln(out, "\nComments:")
177 for _, c := range mr.Comments {
178 fmt.Fprintf(out, " %s (%s):\n %s\n", c.Author, humanTime(c.CreatedAt), c.Body)
179 }
180 }
181 return nil
182}
183
184func joinSubjects(subs []api.Subject) string {
185 out := ""
186 for i, s := range subs {
187 if i > 0 {
188 out += ", "
189 }
190 label := s.Name
191 if s.Type == "team" {
192 label = "@team/" + s.Name
193 }
194 out += label
195 }
196 return out
197}
198
199func runPRCreate(cmd *cobra.Command, _ []string) error {
200 client, err := newClient()
201 if err != nil {
202 return err
203 }
204 owner, repo, err := resolveRepo(prRepo)
205 if err != nil {
206 return err
207 }
208 if prBase == "" || prHead == "" || prTitle == "" {
209 return fmt.Errorf("--base, --head, and --title are required")
210 }
211 mr, err := client.CreateMergeRequest(cmd.Context(), owner, repo, api.MergeRequestCreate{
212 Base: prBase,
213 Head: prHead,
214 Title: prTitle,
215 Body: prBody,
216 HeadOwner: prHeadOwner,
217 HeadRepo: prHeadRepo,
218 })
219 if err != nil {
220 return err
221 }
222 if flagJSON {
223 return printJSON(cmd.OutOrStdout(), mr)
224 }
225 fmt.Fprintf(cmd.OutOrStdout(), "Opened merge request #%d: %s\n", mr.Number, mr.Title)
226 return nil
227}
228
229func runPRMerge(cmd *cobra.Command, args []string) error {
230 client, err := newClient()
231 if err != nil {
232 return err
233 }
234 owner, repo, err := resolveRepo(prRepo)
235 if err != nil {
236 return err
237 }
238 n, err := prNumber(args[0])
239 if err != nil {
240 return err
241 }
242 mr, err := client.MergeMergeRequest(cmd.Context(), owner, repo, n, prMergeMethod)
243 if err != nil {
244 return err
245 }
246 if flagJSON {
247 return printJSON(cmd.OutOrStdout(), mr)
248 }
249 fmt.Fprintf(cmd.OutOrStdout(), "Merged #%d (%s) → %s\n", mr.Number, prMergeMethod, dash(mr.MergeSHA))
250 return nil
251}
252
253func runPRClose(cmd *cobra.Command, args []string) error {
254 client, err := newClient()
255 if err != nil {
256 return err
257 }
258 owner, repo, err := resolveRepo(prRepo)
259 if err != nil {
260 return err
261 }
262 n, err := prNumber(args[0])
263 if err != nil {
264 return err
265 }
266 mr, err := client.CloseMergeRequest(cmd.Context(), owner, repo, n)
267 if err != nil {
268 return err
269 }
270 if flagJSON {
271 return printJSON(cmd.OutOrStdout(), mr)
272 }
273 fmt.Fprintf(cmd.OutOrStdout(), "Closed #%d\n", mr.Number)
274 return nil
275}
276
277func runPRReopen(cmd *cobra.Command, args []string) error {
278 client, err := newClient()
279 if err != nil {
280 return err
281 }
282 owner, repo, err := resolveRepo(prRepo)
283 if err != nil {
284 return err
285 }
286 n, err := prNumber(args[0])
287 if err != nil {
288 return err
289 }
290 mr, err := client.ReopenMergeRequest(cmd.Context(), owner, repo, n)
291 if err != nil {
292 return err
293 }
294 if flagJSON {
295 return printJSON(cmd.OutOrStdout(), mr)
296 }
297 fmt.Fprintf(cmd.OutOrStdout(), "Reopened #%d\n", mr.Number)
298 return nil
299}
300
301func runPRComment(cmd *cobra.Command, args []string) error {
302 client, err := newClient()
303 if err != nil {
304 return err
305 }
306 owner, repo, err := resolveRepo(prRepo)
307 if err != nil {
308 return err
309 }
310 n, err := prNumber(args[0])
311 if err != nil {
312 return err
313 }
314 if prBody == "" {
315 return fmt.Errorf("--body is required")
316 }
317 c, err := client.CommentMergeRequest(cmd.Context(), owner, repo, n, prBody)
318 if err != nil {
319 return err
320 }
321 if flagJSON {
322 return printJSON(cmd.OutOrStdout(), c)
323 }
324 fmt.Fprintf(cmd.OutOrStdout(), "Commented on #%d\n", n)
325 return nil
326}