Skip to content

Commit

Permalink
Fix bug of branches API with tests(go-gitea#25578) (go-gitea#25579)
Browse files Browse the repository at this point in the history
Backport go-gitea#25578 

This PR added a repository's check when creating/deleting branches via
API. Mirror repository and archive repository cannot do that.
  • Loading branch information
lunny committed Jul 3, 2023
1 parent e5b684e commit 13ffa28
Show file tree
Hide file tree
Showing 26 changed files with 268 additions and 4 deletions.
49 changes: 49 additions & 0 deletions models/fixtures/mirror.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
-
id: 1
repo_id: 5
interval: 3600
enable_prune: false
updated_unix: 0
next_update_unix: 0
lfs_enabled: false
lfs_endpoint: ""

-
id: 2
repo_id: 25
interval: 3600
enable_prune: false
updated_unix: 0
next_update_unix: 0
lfs_enabled: false
lfs_endpoint: ""

-
id: 3
repo_id: 26
interval: 3600
enable_prune: false
updated_unix: 0
next_update_unix: 0
lfs_enabled: false
lfs_endpoint: ""

-
id: 4
repo_id: 27
interval: 3600
enable_prune: false
updated_unix: 0
next_update_unix: 0
lfs_enabled: false
lfs_endpoint: ""

-
id: 5
repo_id: 28
interval: 3600
enable_prune: false
updated_unix: 0
next_update_unix: 0
lfs_enabled: false
lfs_endpoint: ""
2 changes: 1 addition & 1 deletion models/fixtures/repository.yml
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@
num_projects: 0
num_closed_projects: 0
is_private: true
is_empty: true
is_empty: false
is_archived: false
is_mirror: true
status: 0
Expand Down
37 changes: 35 additions & 2 deletions routers/api/v1/repo/branch.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,21 @@ func DeleteBranch(ctx *context.APIContext) {
// "404":
// "$ref": "#/responses/notFound"

if ctx.Repo.Repository.IsEmpty {
ctx.Error(http.StatusNotFound, "", "Git Repository is empty.")
return
}

if ctx.Repo.Repository.IsArchived {
ctx.Error(http.StatusForbidden, "", "Git Repository is archived.")
return
}

if ctx.Repo.Repository.IsMirror {
ctx.Error(http.StatusForbidden, "", "Git Repository is a mirror.")
return
}

branchName := ctx.Params("*")

if err := repo_service.DeleteBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName); err != nil {
Expand Down Expand Up @@ -162,17 +177,30 @@ func CreateBranch(ctx *context.APIContext) {
// responses:
// "201":
// "$ref": "#/responses/Branch"
// "403":
// description: The branch is archived or a mirror.
// "404":
// description: The old branch does not exist.
// "409":
// description: The branch with the same name already exists.

opt := web.GetForm(ctx).(*api.CreateBranchRepoOption)
if ctx.Repo.Repository.IsEmpty {
ctx.Error(http.StatusNotFound, "", "Git Repository is empty.")
return
}

if ctx.Repo.Repository.IsArchived {
ctx.Error(http.StatusForbidden, "", "Git Repository is archived.")
return
}

if ctx.Repo.Repository.IsMirror {
ctx.Error(http.StatusForbidden, "", "Git Repository is a mirror.")
return
}

opt := web.GetForm(ctx).(*api.CreateBranchRepoOption)

var oldCommit *git.Commit
var err error

Expand Down Expand Up @@ -280,7 +308,12 @@ func ListBranches(ctx *context.APIContext) {

listOptions := utils.GetListOptions(ctx)

if !ctx.Repo.Repository.IsEmpty && ctx.Repo.GitRepo != nil {
if !ctx.Repo.Repository.IsEmpty {
if ctx.Repo.GitRepo == nil {
ctx.Error(http.StatusInternalServerError, "Load git repository failed", nil)
return
}

rules, err := git_model.FindRepoProtectedBranchRules(ctx, ctx.Repo.Repository.ID)
if err != nil {
ctx.Error(http.StatusInternalServerError, "FindMatchedProtectedBranchRules", err)
Expand Down
3 changes: 3 additions & 0 deletions templates/swagger/v1_json.tmpl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tests/gitea-repositories-meta/user3/repo5.git/HEAD
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ref: refs/heads/master
6 changes: 6 additions & 0 deletions tests/gitea-repositories-meta/user3/repo5.git/config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[core]
repositoryformatversion = 0
filemode = true
bare = true
ignorecase = true
precomposeunicode = true
1 change: 1 addition & 0 deletions tests/gitea-repositories-meta/user3/repo5.git/description
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Unnamed repository; edit this file 'description' to name the repository.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
ORI_DIR=`pwd`
SHELL_FOLDER=$(cd "$(dirname "$0")";pwd)
cd "$ORI_DIR"
for i in `ls "$SHELL_FOLDER/post-receive.d"`; do
sh "$SHELL_FOLDER/post-receive.d/$i"
done
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
"$GITEA_ROOT/gitea" hook --config="$GITEA_ROOT/$GITEA_CONF" post-receive
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
ORI_DIR=`pwd`
SHELL_FOLDER=$(cd "$(dirname "$0")";pwd)
cd "$ORI_DIR"
for i in `ls "$SHELL_FOLDER/pre-receive.d"`; do
sh "$SHELL_FOLDER/pre-receive.d/$i"
done
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
"$GITEA_ROOT/gitea" hook --config="$GITEA_ROOT/$GITEA_CONF" pre-receive
7 changes: 7 additions & 0 deletions tests/gitea-repositories-meta/user3/repo5.git/hooks/update
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
ORI_DIR=`pwd`
SHELL_FOLDER=$(cd "$(dirname "$0")";pwd)
cd "$ORI_DIR"
for i in `ls "$SHELL_FOLDER/update.d"`; do
sh "$SHELL_FOLDER/update.d/$i" $1 $2 $3
done
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/usr/bin/env bash
"$GITEA_ROOT/gitea" hook --config="$GITEA_ROOT/$GITEA_CONF" update $1 $2 $3
6 changes: 6 additions & 0 deletions tests/gitea-repositories-meta/user3/repo5.git/info/exclude
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# git ls-files --others --exclude-from=.git/info/exclude
# Lines that start with '#' are comments.
# For a project mostly in C, the following would be a good set of
# exclude patterns (uncomment them if you want to use them):
# *.[oa]
# *~
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
2a47ca4b614a9f5a43abbd5ad851a54a616ffee6
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
d22b4d4daa5be07329fcef6ed458f00cf3392da0
136 changes: 136 additions & 0 deletions tests/integration/api_repo_branch_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT

package integration

import (
"bytes"
"fmt"
"io"
"net/http"
"net/url"
"sort"
"testing"

auth_model "code.gitea.io/gitea/models/auth"
repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unittest"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/setting"
api "code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/tests"

"github.com/stretchr/testify/assert"
)

func TestAPIRepoBranchesPlain(t *testing.T) {
onGiteaRun(t, func(*testing.T, *url.URL) {
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3})
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
session := loginUser(t, user1.LowerName)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)

link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/user3/%s/branches", repo3.Name)) // a plain repo
link.RawQuery = url.Values{"token": {token}}.Encode()
resp := MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
bs, err := io.ReadAll(resp.Body)
assert.NoError(t, err)

var branches []*api.Branch
assert.NoError(t, json.Unmarshal(bs, &branches))
assert.Len(t, branches, 2)
sort.Slice(branches, func(i, j int) bool {
return branches[i].Name > branches[j].Name
})
assert.EqualValues(t, "test_branch", branches[0].Name)
assert.EqualValues(t, "master", branches[1].Name)

link2, _ := url.Parse(fmt.Sprintf("/api/v1/repos/user3/%s/branches/test_branch", repo3.Name))
link2.RawQuery = url.Values{"token": {token}}.Encode()
resp = MakeRequest(t, NewRequest(t, "GET", link2.String()), http.StatusOK)
bs, err = io.ReadAll(resp.Body)
assert.NoError(t, err)
var branch api.Branch
assert.NoError(t, json.Unmarshal(bs, &branch))
assert.EqualValues(t, "test_branch", branch.Name)

req := NewRequest(t, "POST", link.String())
req.Header.Add("Content-Type", "application/json")
req.Body = io.NopCloser(bytes.NewBufferString(`{"new_branch_name":"test_branch2", "old_branch_name": "test_branch", "old_ref_name":"refs/heads/test_branch"}`))
resp = MakeRequest(t, req, http.StatusCreated)
bs, err = io.ReadAll(resp.Body)
assert.NoError(t, err)
var branch2 api.Branch
assert.NoError(t, json.Unmarshal(bs, &branch2))
assert.EqualValues(t, "test_branch2", branch2.Name)
assert.EqualValues(t, branch.Commit.ID, branch2.Commit.ID)

resp = MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
bs, err = io.ReadAll(resp.Body)
assert.NoError(t, err)

branches = []*api.Branch{}
assert.NoError(t, json.Unmarshal(bs, &branches))
assert.Len(t, branches, 3)
sort.Slice(branches, func(i, j int) bool {
return branches[i].Name > branches[j].Name
})
assert.EqualValues(t, "test_branch2", branches[0].Name)
assert.EqualValues(t, "test_branch", branches[1].Name)
assert.EqualValues(t, "master", branches[2].Name)

link3, _ := url.Parse(fmt.Sprintf("/api/v1/repos/user3/%s/branches/test_branch2", repo3.Name))
MakeRequest(t, NewRequest(t, "DELETE", link3.String()), http.StatusNotFound)

link3.RawQuery = url.Values{"token": {token}}.Encode()
MakeRequest(t, NewRequest(t, "DELETE", link3.String()), http.StatusNoContent)
assert.NoError(t, err)
})
}

func TestAPIRepoBranchesMirror(t *testing.T) {
defer tests.PrepareTestEnv(t)()

repo5 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 5})
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
session := loginUser(t, user1.LowerName)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository)

link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/user3/%s/branches", repo5.Name)) // a mirror repo
link.RawQuery = url.Values{"token": {token}}.Encode()
resp := MakeRequest(t, NewRequest(t, "GET", link.String()), http.StatusOK)
bs, err := io.ReadAll(resp.Body)
assert.NoError(t, err)

var branches []*api.Branch
assert.NoError(t, json.Unmarshal(bs, &branches))
assert.Len(t, branches, 2)
sort.Slice(branches, func(i, j int) bool {
return branches[i].Name > branches[j].Name
})
assert.EqualValues(t, "test_branch", branches[0].Name)
assert.EqualValues(t, "master", branches[1].Name)

link2, _ := url.Parse(fmt.Sprintf("/api/v1/repos/user3/%s/branches/test_branch", repo5.Name))
link2.RawQuery = url.Values{"token": {token}}.Encode()
resp = MakeRequest(t, NewRequest(t, "GET", link2.String()), http.StatusOK)
bs, err = io.ReadAll(resp.Body)
assert.NoError(t, err)
var branch api.Branch
assert.NoError(t, json.Unmarshal(bs, &branch))
assert.EqualValues(t, "test_branch", branch.Name)

req := NewRequest(t, "POST", link.String())
req.Header.Add("Content-Type", "application/json")
req.Body = io.NopCloser(bytes.NewBufferString(`{"new_branch_name":"test_branch2", "old_branch_name": "test_branch", "old_ref_name":"refs/heads/test_branch"}`))
resp = MakeRequest(t, req, http.StatusForbidden)
bs, err = io.ReadAll(resp.Body)
assert.NoError(t, err)
assert.EqualValues(t, "{\"message\":\"Git Repository is a mirror.\",\"url\":\""+setting.AppURL+"api/swagger\"}\n", string(bs))

resp = MakeRequest(t, NewRequest(t, "DELETE", link2.String()), http.StatusForbidden)
bs, err = io.ReadAll(resp.Body)
assert.NoError(t, err)
assert.EqualValues(t, "{\"message\":\"Git Repository is a mirror.\",\"url\":\""+setting.AppURL+"api/swagger\"}\n", string(bs))
}
2 changes: 1 addition & 1 deletion tests/integration/empty_repo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func TestEmptyRepo(t *testing.T) {
"commit/1ae57b34ccf7e18373",
"graph",
}
emptyRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 5})
emptyRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 6})
assert.True(t, emptyRepo.IsEmpty)
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: emptyRepo.OwnerID})
for _, subPath := range subPaths {
Expand Down

0 comments on commit 13ffa28

Please sign in to comment.