Skip to content

Commit 819e322

Browse files
committed
Add protect and unprotect for edit branch command
1 parent 6883f7e commit 819e322

File tree

6 files changed

+330
-2
lines changed

6 files changed

+330
-2
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,8 @@ Contributors are welcomed with love! Please read [CONTRIBUTING.md](./CONTRIBUTIN
204204
* [x] `describe branch [branch name] [--project] [flags]`
205205
* [x] `new branch [branch name] [--project] [flags]`
206206
* [x] `delete branch [branch name] [--project]`
207-
* [ ] `protect-branch [branch name] [--project] [flags]`
208-
* [ ] `unprotect-branch [branch name] [--project]`
207+
* [x] `edit branch [branch name] [--project] [--protect] [flags]`
208+
* [x] `edit branch [branch name] [--project] [--unprotect] [flags]`
209209

210210
### Project/Repository Tags
211211

cmd/edit_branch.go

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
// Copyright © 2018 github.com/devopsctl authors
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a copy
4+
// of this software and associated documentation files (the "Software"), to deal
5+
// in the Software without restriction, including without limitation the rights
6+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
// copies of the Software, and to permit persons to whom the Software is
8+
// furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
// THE SOFTWARE.
20+
21+
package cmd
22+
23+
import (
24+
"github.com/spf13/cobra"
25+
gitlab "github.com/xanzy/go-gitlab"
26+
)
27+
28+
var editBranchCmd = &cobra.Command{
29+
Use: "branch",
30+
Aliases: []string{"b"},
31+
Short: "Protect or unprotect a repositort branch",
32+
Example: `# protect a branch
33+
gitlabctl edit branch master -p devopsctl/gitlabctl --protect
34+
35+
# unprotect a branch
36+
gitlabctl edit branch master -p devopsctl/gitlabctl --unprotect`,
37+
SilenceErrors: true,
38+
SilenceUsage: true,
39+
DisableAutoGenTag: true,
40+
Args: cobra.ExactArgs(1),
41+
PreRunE: func(cmd *cobra.Command, args []string) error {
42+
if err := validateFlagCombination(cmd, "protect", "dev-can-merge", "dev-can-push"); err != nil {
43+
return err
44+
}
45+
if cmd.Flag("protect").Changed && cmd.Flag("unprotect").Changed {
46+
return newUsedTooManyFlagError("protect", "unprotect")
47+
}
48+
return nil
49+
},
50+
RunE: func(cmd *cobra.Command, args []string) error {
51+
if cmd.Flag("protect").Changed {
52+
return runProtectBranch(cmd, args[0])
53+
}
54+
return runUnProtectBranch(args[0], getFlagString(cmd, "project"), getFlagString(cmd, "out"))
55+
},
56+
}
57+
58+
func init() {
59+
editCmd.AddCommand(editBranchCmd)
60+
addProjectFlag(editBranchCmd)
61+
verifyMarkFlagRequired(editBranchCmd, "project")
62+
editBranchCmd.Flags().Bool("unprotect", false,
63+
"Remove protection of a branch")
64+
editBranchCmd.Flags().Bool("protect", false,
65+
"Protect a branch")
66+
editBranchCmd.Flags().Bool("dev-can-push", false,
67+
"Used with '--protect'. Flag if developers can push to the branch")
68+
editBranchCmd.Flags().Bool("dev-can-merge", false,
69+
"Used with '--protect'. Flag if developers can merge to the branch")
70+
}
71+
72+
func runProtectBranch(cmd *cobra.Command, branch string) error {
73+
opts := new(gitlab.ProtectBranchOptions)
74+
if cmd.Flag("dev-can-push").Changed {
75+
opts.DevelopersCanPush = gitlab.Bool(getFlagBool(cmd, "dev-can-push"))
76+
}
77+
if cmd.Flag("dev-can-merge").Changed {
78+
opts.DevelopersCanMerge = gitlab.Bool(getFlagBool(cmd, "dev-can-merge"))
79+
}
80+
branchInfo, err := protectBranch(branch, getFlagString(cmd, "project"), opts)
81+
if err != nil {
82+
return err
83+
}
84+
printBranchOut(getFlagString(cmd, "out"), branchInfo)
85+
return nil
86+
}
87+
88+
func protectBranch(branch, project string, opts *gitlab.ProtectBranchOptions) (*gitlab.Branch, error) {
89+
git, err := newGitlabClient()
90+
if err != nil {
91+
return nil, err
92+
}
93+
protectedBranch, _, err := git.Branches.ProtectBranch(project, branch, opts)
94+
if err != nil {
95+
return nil, err
96+
}
97+
return protectedBranch, nil
98+
}
99+
100+
func runUnProtectBranch(branch, project, out string) error {
101+
branchInfo, err := unProtectBranch(branch, project)
102+
if err != nil {
103+
return err
104+
}
105+
printBranchOut(out, branchInfo)
106+
return nil
107+
}
108+
109+
func unProtectBranch(branch, project string) (*gitlab.Branch, error) {
110+
git, err := newGitlabClient()
111+
if err != nil {
112+
return nil, err
113+
}
114+
branchInfo, _, err := git.Branches.UnprotectBranch(project, branch)
115+
if err != nil {
116+
return nil, err
117+
}
118+
return branchInfo, nil
119+
}

cmd/edit_branch_test.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
// Copyright © 2018 github.com/devopsctl authors
2+
//
3+
// Permission is hereby granted, free of charge, to any person obtaining a copy
4+
// of this software and associated documentation files (the "Software"), to deal
5+
// in the Software without restriction, including without limitation the rights
6+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
// copies of the Software, and to permit persons to whom the Software is
8+
// furnished to do so, subject to the following conditions:
9+
//
10+
// The above copyright notice and this permission notice shall be included in
11+
// all copies or substantial portions of the Software.
12+
//
13+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
// THE SOFTWARE.
20+
21+
package cmd
22+
23+
import (
24+
"fmt"
25+
"testing"
26+
27+
gitlab "github.com/xanzy/go-gitlab"
28+
)
29+
30+
func TestProtectBranch(t *testing.T) {
31+
tt := []struct {
32+
name string
33+
flagsMap map[string]string
34+
args []string
35+
expect testResult
36+
expectOut []string
37+
}{
38+
{
39+
name: "protect an existing branch",
40+
flagsMap: map[string]string{
41+
"project": "12",
42+
"protect": "true",
43+
"dev-can-push": "true",
44+
"dev-can-merge": "true",
45+
"out": "json",
46+
},
47+
args: []string{"release-abc"}, // to be created in SETUP
48+
expect: pass,
49+
expectOut: []string{
50+
`"protected": true`,
51+
`"developers_can_push": true`,
52+
`"developers_can_merge": true`,
53+
},
54+
},
55+
{
56+
name: "protect non existing branch fails",
57+
flagsMap: map[string]string{
58+
"project": "12",
59+
"protect": "true",
60+
},
61+
args: []string{"xxxx"},
62+
expect: fail,
63+
expectOut: []string{`message: 404 Branch Not Found`},
64+
},
65+
{
66+
name: "unprotect an existing branch",
67+
flagsMap: map[string]string{
68+
"project": "12",
69+
"unprotect": "true",
70+
"out": "json",
71+
},
72+
args: []string{"master"},
73+
expect: pass,
74+
expectOut: []string{
75+
`"protected": false`,
76+
`"developers_can_push": false`,
77+
`"developers_can_merge": false`,
78+
},
79+
},
80+
{
81+
name: "invalid usage of flags fails",
82+
flagsMap: map[string]string{
83+
"project": "12",
84+
"dev-can-merge": "true",
85+
},
86+
args: []string{"master"},
87+
expect: fail,
88+
expectOut: []string{"flag can only be used"},
89+
},
90+
}
91+
92+
for _, tc := range tt {
93+
t.Run(tc.name, func(t *testing.T) {
94+
execT := execTestCmdFlags{
95+
t: t,
96+
cmd: editBranchCmd,
97+
flagsMap: tc.flagsMap,
98+
args: tc.args,
99+
}
100+
// SETUP for positive test
101+
if tc.expect == pass {
102+
// force to unchanged the flag due to concurrency issue when testing
103+
if _, ok := tc.flagsMap["protect"]; ok {
104+
execT.cmd.Flag("unprotect").Changed = false
105+
}
106+
if _, ok := tc.flagsMap["unprotect"]; ok {
107+
execT.cmd.Flag("protect").Changed = false
108+
}
109+
// force to create the branch to protect or unprotect
110+
if _, err := newBranch(tc.flagsMap["project"],
111+
&gitlab.CreateBranchOptions{
112+
Branch: gitlab.String(tc.args[0]),
113+
Ref: gitlab.String("master"),
114+
}); err != nil {
115+
tInfo(err)
116+
}
117+
}
118+
stdout, execResult := execT.executeCommand()
119+
assertEqualResult(t, execResult, tc.expect,
120+
printFlagsTable(tc.flagsMap, stdout))
121+
fmt.Printf("--- OUTPUT: %s\n", stdout)
122+
assertOutContains(t, stdout, tc.expectOut...)
123+
})
124+
}
125+
126+
}

docs/gitlabctl_edit.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ Update or patch a Gitlab resource
2222
### SEE ALSO
2323

2424
* [gitlabctl](gitlabctl.md) - Gitlab command-line interface
25+
* [gitlabctl edit branch](gitlabctl_edit_branch.md) - Protect or unprotect a repositort branch
2526
* [gitlabctl edit group](gitlabctl_edit_group.md) - Update a group by specifying the group id or path and using flags for fields to modify
2627
* [gitlabctl edit member](gitlabctl_edit_member.md) - Edit a member by specifying the member name as the first argument
2728
* [gitlabctl edit project](gitlabctl_edit_project.md) - Edit a project by specifying the project id or path and using flags for fields to modify

docs/gitlabctl_edit_branch.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
## gitlabctl edit branch
2+
3+
Protect or unprotect a repositort branch
4+
5+
### Synopsis
6+
7+
Protect or unprotect a repositort branch
8+
9+
```
10+
gitlabctl edit branch [flags]
11+
```
12+
13+
### Examples
14+
15+
```
16+
# protect a branch
17+
gitlabctl edit branch master -p devopsctl/gitlabctl --protect
18+
19+
# unprotect a branch
20+
gitlabctl edit branch master -p devopsctl/gitlabctl --unprotect
21+
```
22+
23+
### Options
24+
25+
```
26+
--dev-can-merge Flag if developers can merge to the branch (default true)
27+
--dev-can-push Flag if developers can push to the branch (default true)
28+
-h, --help help for branch
29+
-p, --project string The name or ID of the project
30+
--protect Protect a branch (default true)
31+
--unprotect Remove protection of a branch (default true)
32+
```
33+
34+
### Options inherited from parent commands
35+
36+
```
37+
--config string config file (default is $HOME/.gitlabctl.yaml)
38+
-o, --out string Print the command output to the desired format. (json, yaml, simple) (default "simple")
39+
```
40+
41+
### SEE ALSO
42+
43+
* [gitlabctl edit](gitlabctl_edit.md) - Update or patch a Gitlab resource
44+

docs/gitlabctl_protect-branch.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
## gitlabctl protect-branch
2+
3+
Protect a repository branch
4+
5+
### Synopsis
6+
7+
Protect a repository branch
8+
9+
```
10+
gitlabctl protect-branch [flags]
11+
```
12+
13+
### Examples
14+
15+
```
16+
gitlabctl protect-branch master --project=devopsctl/gitlabctl
17+
```
18+
19+
### Options
20+
21+
```
22+
--dev-can-merge Flag if developers can merge to the branch (default true)
23+
--dev-can-push Flag if developers can push to the branch (default true)
24+
-h, --help help for protect-branch
25+
-o, --out string Print the command output to the desired format. (json, yaml, simple) (default "simple")
26+
-p, --project string The name or ID of the project
27+
```
28+
29+
### Options inherited from parent commands
30+
31+
```
32+
--config string config file (default is $HOME/.gitlabctl.yaml)
33+
```
34+
35+
### SEE ALSO
36+
37+
* [gitlabctl](gitlabctl.md) - Gitlab command-line interface
38+

0 commit comments

Comments
 (0)