1package cmd
2
3import (
4 "fmt"
5 "strings"
6
7 "github.com/charmbracelet/soft-serve/git"
8 "github.com/charmbracelet/soft-serve/pkg/backend"
9 "github.com/charmbracelet/soft-serve/pkg/proto"
10 "github.com/charmbracelet/soft-serve/pkg/webhook"
11 gitm "github.com/gogs/git-module"
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 PersistentPreRunE: checkIfCollab,
152 RunE: func(cmd *cobra.Command, args []string) error {
153 ctx := cmd.Context()
154 be := backend.FromContext(ctx)
155 rn := strings.TrimSuffix(args[0], ".git")
156 rr, err := be.Repository(ctx, rn)
157 if err != nil {
158 return err
159 }
160
161 r, err := rr.Open()
162 if err != nil {
163 return err
164 }
165
166 branch := args[1]
167 branches, _ := r.Branches()
168 var exists bool
169 for _, b := range branches {
170 if branch == b {
171 exists = true
172 break
173 }
174 }
175
176 if !exists {
177 return git.ErrReferenceNotExist
178 }
179
180 head, err := r.HEAD()
181 if err != nil {
182 return err
183 }
184
185 if head.Name().Short() == branch {
186 return fmt.Errorf("cannot delete the default branch")
187 }
188
189 branchCommit, err := r.BranchCommit(branch)
190 if err != nil {
191 return err
192 }
193
194 if err := r.DeleteBranch(branch, gitm.DeleteBranchOptions{Force: true}); err != nil {
195 return err
196 }
197
198 wh, err := webhook.NewBranchTagEvent(ctx, proto.UserFromContext(ctx), rr, git.RefsHeads+branch, branchCommit.ID.String(), git.ZeroID)
199 if err != nil {
200 return err
201 }
202
203 return webhook.SendEvent(ctx, wh)
204 },
205 }
206
207 return cmd
208}