Skip to content

Commit e2a92c3

Browse files
committed
Update: allow push to certain branches
This extends MasterOnly and makes it configurable. The idea is to set arbitrary branch names which are allowed to be pushed to and otherwise return an error. Resolves: #33
1 parent 33d10fa commit e2a92c3

File tree

4 files changed

+121
-6
lines changed

4 files changed

+121
-6
lines changed

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/gofrs/uuid v4.0.0+incompatible
77
github.com/stretchr/testify v1.7.0
88
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a
9+
golang.org/x/exp v0.0.0-20230203172020-98cc5a0785f9
910
)
1011

1112
require (

go.sum

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5Cc
1010
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
1111
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc=
1212
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
13+
golang.org/x/exp v0.0.0-20230203172020-98cc5a0785f9 h1:frX3nT9RkKybPnjyI+yvZh6ZucTZatCCEm9D47sZ2zo=
14+
golang.org/x/exp v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
1315
golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
1416
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
1517
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=

receiver.go

+24-6
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,17 @@ import (
99
"strings"
1010

1111
"github.com/gofrs/uuid"
12+
"golang.org/x/exp/slices"
1213
)
1314

1415
const ZeroSHA = "0000000000000000000000000000000000000000"
1516

1617
type Receiver struct {
17-
Debug bool
18-
MasterOnly bool
19-
TmpDir string
20-
HandlerFunc func(*HookInfo, string) error
18+
Debug bool
19+
MasterOnly bool
20+
AllowedBranches []string
21+
TmpDir string
22+
HandlerFunc func(*HookInfo, string) error
2123
}
2224

2325
func ReadCommitMessage(sha string) (string, error) {
@@ -45,14 +47,30 @@ func IsForcePush(hook *HookInfo) (bool, error) {
4547
return base != hook.OldRev, nil
4648
}
4749

50+
func (r *Receiver) CheckAllowedBranch(hook *HookInfo) error {
51+
if r.MasterOnly { // for BC
52+
r.AllowedBranches = append(r.AllowedBranches, "refs/heads/master")
53+
}
54+
55+
if len(r.AllowedBranches) == 0 {
56+
return nil
57+
}
58+
59+
if !slices.Contains(r.AllowedBranches, hook.Ref) {
60+
return fmt.Errorf("cannot push branch, allowed branches: %s", strings.Join(r.AllowedBranches, ", "))
61+
}
62+
63+
return nil
64+
}
65+
4866
func (r *Receiver) Handle(reader io.Reader) error {
4967
hook, err := ReadHookInput(reader)
5068
if err != nil {
5169
return err
5270
}
5371

54-
if r.MasterOnly && hook.Ref != "refs/heads/master" {
55-
return fmt.Errorf("cant push to non-master branch")
72+
if err = r.CheckAllowedBranch(hook); err != nil {
73+
return err
5674
}
5775

5876
id, err := uuid.NewV4()

receiver_test.go

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package gitkit_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/sosedoff/gitkit"
7+
"github.com/stretchr/testify/assert"
8+
)
9+
10+
type gitReceiveMock struct {
11+
name string
12+
masterOnly bool
13+
allowedBranches []string
14+
ref string
15+
isErr bool
16+
}
17+
18+
func TestMasterOnly(t *testing.T) {
19+
testCases := []gitReceiveMock{
20+
{
21+
name: "push to master, no error",
22+
masterOnly: true,
23+
ref: "refs/heads/master",
24+
isErr: false,
25+
},
26+
{
27+
name: "push to a branch, should trigger error",
28+
masterOnly: true,
29+
ref: "refs/heads/branch",
30+
isErr: true,
31+
},
32+
}
33+
34+
for _, tc := range testCases {
35+
r := &gitkit.Receiver{
36+
MasterOnly: tc.masterOnly,
37+
}
38+
39+
err := r.CheckAllowedBranch(&gitkit.HookInfo{
40+
Ref: tc.ref,
41+
})
42+
43+
if !tc.isErr {
44+
assert.NoError(t, err, "expected no error: %s", tc.name)
45+
} else {
46+
assert.Error(t, err, "expected an error: %s", tc.name)
47+
}
48+
}
49+
}
50+
51+
func TestAllowedBranches(t *testing.T) {
52+
testCases := []gitReceiveMock{
53+
{
54+
name: "push to master, no error",
55+
allowedBranches: []string{"refs/heads/master"},
56+
ref: "refs/heads/master",
57+
isErr: false,
58+
},
59+
{
60+
name: "push to a branch, should trigger error",
61+
allowedBranches: []string{"refs/heads/master"},
62+
ref: "refs/heads/some-branch",
63+
isErr: true,
64+
},
65+
{
66+
name: "push to another-branch",
67+
allowedBranches: []string{"refs/heads/another-branch"},
68+
ref: "refs/heads/another-branch",
69+
isErr: false,
70+
},
71+
{
72+
name: "push to main and only allow main",
73+
allowedBranches: []string{"refs/heads/main"},
74+
ref: "refs/heads/main",
75+
isErr: false,
76+
},
77+
}
78+
79+
for _, tc := range testCases {
80+
r := &gitkit.Receiver{
81+
AllowedBranches: tc.allowedBranches,
82+
}
83+
84+
err := r.CheckAllowedBranch(&gitkit.HookInfo{
85+
Ref: tc.ref,
86+
})
87+
88+
if !tc.isErr {
89+
assert.NoError(t, err, "expected no error: %s", tc.name)
90+
} else {
91+
assert.Error(t, err, "expected an error: %s", tc.name)
92+
}
93+
}
94+
}

0 commit comments

Comments
 (0)