1package cmd
2
3import (
4 "fmt"
5 "strings"
6
7 gitm "github.com/aymanbagabas/git-module"
8 "github.com/charmbracelet/soft-serve/git"
9 "github.com/charmbracelet/soft-serve/pkg/backend"
10 "github.com/charmbracelet/soft-serve/pkg/proto"
11 "github.com/charmbracelet/soft-serve/pkg/webhook"
12 "github.com/spf13/cobra"
13)
14
15func branchCommand() *cobra.Command {
16 cmd := &cobra.Command{
17 Use: "branch",
18 Short: "Manage repository branches",
19 }
20
21 cmd.AddCommand(
22 branchListCommand(),
23 branchDefaultCommand(),
24 branchDeleteCommand(),
25 )
26
27 return cmd
28}
29
30func branchListCommand() *cobra.Command {
31 cmd := &cobra.Command{
32 Use: "list REPOSITORY",
33 Short: "List repository branches",
34 Args: cobra.ExactArgs(1),
35 PersistentPreRunE: checkIfReadable,
36 RunE: func(cmd *cobra.Command, args []string) error {
37 ctx := cmd.Context()
38 be := backend.FromContext(ctx)
39 rn := strings.TrimSuffix(args[0], ".git")
40 rr, err := be.Repository(ctx, rn)
41 if err != nil {
42 return err
43 }
44
45 r, err := rr.Open()
46 if err != nil {
47 return err
48 }
49
50 branches, _ := r.Branches()
51 for _, b := range branches {
52 cmd.Println(b)
53 }
54
55 return nil
56 },
57 }
58
59 return cmd
60}
61
62func branchDefaultCommand() *cobra.Command {
63 cmd := &cobra.Command{
64 Use: "default REPOSITORY [BRANCH]",
65 Short: "Set or get the default branch",
66 Args: cobra.RangeArgs(1, 2),
67 RunE: func(cmd *cobra.Command, args []string) error {
68 ctx := cmd.Context()
69 be := backend.FromContext(ctx)
70 rn := strings.TrimSuffix(args[0], ".git")
71 switch len(args) {
72 case 1:
73 if err := checkIfReadable(cmd, args); err != nil {
74 return err
75 }
76 rr, err := be.Repository(ctx, rn)
77 if err != nil {
78 return err
79 }
80
81 r, err := rr.Open()
82 if err != nil {
83 return err
84 }
85
86 head, err := r.HEAD()
87 if err != nil {
88 return err
89 }
90
91 cmd.Println(head.Name().Short())
92 case 2:
93 if err := checkIfCollab(cmd, args); err != nil {
94 return err
95 }
96
97 rr, err := be.Repository(ctx, rn)
98 if err != nil {
99 return err
100 }
101
102 r, err := rr.Open()
103 if err != nil {
104 return err
105 }
106
107 branch := args[1]
108 branches, _ := r.Branches()
109 var exists bool
110 for _, b := range branches {
111 if branch == b {
112 exists = true
113 break
114 }
115 }
116
117 if !exists {
118 return git.ErrReferenceNotExist
119 }
120
121 if _, err := r.SymbolicRef(git.HEAD, gitm.RefsHeads+branch, gitm.SymbolicRefOptions{
122 CommandOptions: gitm.CommandOptions{
123 Context: ctx,
124 },
125 }); err != nil {
126 return err
127 }
128
129 // TODO: move this to backend?
130 user := proto.UserFromContext(ctx)
131 wh, err := webhook.NewRepositoryEvent(ctx, user, rr, webhook.RepositoryEventActionDefaultBranchChange)
132 if err != nil {
133 return err
134 }
135
136 return webhook.SendEvent(ctx, wh)
137 }
138
139 return nil
140 },
141 }
142
143 return cmd
144}
145
146func branchDeleteCommand() *cobra.Command {
147 cmd := &cobra.Command{
148 Use: "delete REPOSITORY BRANCH",
149 Aliases: []string{"remove", "rm", "del"},
150 Short: "Delete a branch",
151 Args: cobra.ExactArgs(2),
152 PersistentPreRunE: checkIfCollab,
153 RunE: func(cmd *cobra.Command, args []string) error {
154 ctx := cmd.Context()
155 be := backend.FromContext(ctx)
156 rn := strings.TrimSuffix(args[0], ".git")
157 rr, err := be.Repository(ctx, rn)
158 if err != nil {
159 return err
160 }
161
162 r, err := rr.Open()
163 if err != nil {
164 return err
165 }
166
167 branch := args[1]
168 branches, _ := r.Branches()
169 var exists bool
170 for _, b := range branches {
171 if branch == b {
172 exists = true
173 break
174 }
175 }
176
177 if !exists {
178 return git.ErrReferenceNotExist
179 }
180
181 head, err := r.HEAD()
182 if err != nil {
183 return err
184 }
185
186 if head.Name().Short() == branch {
187 return fmt.Errorf("cannot delete the default branch")
188 }
189
190 branchCommit, err := r.BranchCommit(branch)
191 if err != nil {
192 return err
193 }
194
195 if err := r.DeleteBranch(branch, gitm.DeleteBranchOptions{Force: true}); err != nil {
196 return err
197 }
198
199 wh, err := webhook.NewBranchTagEvent(ctx, proto.UserFromContext(ctx), rr, git.RefsHeads+branch, branchCommit.ID.String(), git.ZeroID)
200 if err != nil {
201 return err
202 }
203
204 return webhook.SendEvent(ctx, wh)
205 },
206 }
207
208 return cmd
209}