mirror of
https://codeberg.org/davrot/forgejo.git
synced 2025-07-22 16:00:03 +02:00
Compare commits
2 commits
702896a512
...
aaa1f333bf
Author | SHA1 | Date | |
---|---|---|---|
![]() |
aaa1f333bf | ||
![]() |
9dfdacf54f |
16 changed files with 1089 additions and 10 deletions
|
@ -111,6 +111,7 @@ var migrations = []*Migration{
|
|||
NewMigration("Noop because of https://codeberg.org/forgejo/forgejo/issues/8373", NoopAddIndexToActionRunStopped),
|
||||
// v35 -> v36
|
||||
NewMigration("Fix wiki unit default permission", FixWikiUnitDefaultPermission),
|
||||
NewMigration("Add `branch_filter` to `push_mirror` table", AddPushMirrorBranchFilter),
|
||||
}
|
||||
|
||||
// GetCurrentDBVersion returns the current Forgejo database version.
|
||||
|
|
16
models/forgejo_migrations/v37.go
Normal file
16
models/forgejo_migrations/v37.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2025 The Forgejo Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package forgejo_migrations
|
||||
|
||||
import (
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
func AddPushMirrorBranchFilter(x *xorm.Engine) error {
|
||||
type PushMirror struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
BranchFilter string `xorm:"VARCHAR(255)"`
|
||||
}
|
||||
return x.Sync2(new(PushMirror))
|
||||
}
|
|
@ -32,6 +32,7 @@ type PushMirror struct {
|
|||
Repo *Repository `xorm:"-"`
|
||||
RemoteName string
|
||||
RemoteAddress string `xorm:"VARCHAR(2048)"`
|
||||
BranchFilter string `xorm:"VARCHAR(2048)"`
|
||||
|
||||
// A keypair formatted in OpenSSH format.
|
||||
PublicKey string `xorm:"VARCHAR(100)"`
|
||||
|
@ -122,6 +123,11 @@ func UpdatePushMirrorInterval(ctx context.Context, m *PushMirror) error {
|
|||
return err
|
||||
}
|
||||
|
||||
func UpdatePushMirrorBranchFilter(ctx context.Context, m *PushMirror) error {
|
||||
_, err := db.GetEngine(ctx).ID(m.ID).Cols("branch_filter").Update(m)
|
||||
return err
|
||||
}
|
||||
|
||||
var DeletePushMirrors = deletePushMirrors
|
||||
|
||||
func deletePushMirrors(ctx context.Context, opts PushMirrorOptions) error {
|
||||
|
|
|
@ -75,3 +75,139 @@ func TestPushMirrorPrivatekey(t *testing.T) {
|
|||
assert.Empty(t, actualPrivateKey)
|
||||
})
|
||||
}
|
||||
|
||||
func TestPushMirrorBranchFilter(t *testing.T) {
|
||||
require.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
t.Run("Create push mirror with branch filter", func(t *testing.T) {
|
||||
m := &repo_model.PushMirror{
|
||||
RepoID: 1,
|
||||
RemoteName: "test-branch-filter",
|
||||
BranchFilter: "main,develop",
|
||||
}
|
||||
unittest.AssertSuccessfulInsert(t, m)
|
||||
assert.NotZero(t, m.ID)
|
||||
assert.Equal(t, "main,develop", m.BranchFilter)
|
||||
})
|
||||
|
||||
t.Run("Create push mirror with empty branch filter", func(t *testing.T) {
|
||||
m := &repo_model.PushMirror{
|
||||
RepoID: 1,
|
||||
RemoteName: "test-empty-filter",
|
||||
BranchFilter: "",
|
||||
}
|
||||
unittest.AssertSuccessfulInsert(t, m)
|
||||
assert.NotZero(t, m.ID)
|
||||
assert.Empty(t, m.BranchFilter)
|
||||
})
|
||||
|
||||
t.Run("Create push mirror without branch filter", func(t *testing.T) {
|
||||
m := &repo_model.PushMirror{
|
||||
RepoID: 1,
|
||||
RemoteName: "test-no-filter",
|
||||
// BranchFilter: "",
|
||||
}
|
||||
unittest.AssertSuccessfulInsert(t, m)
|
||||
assert.NotZero(t, m.ID)
|
||||
assert.Empty(t, m.BranchFilter)
|
||||
})
|
||||
|
||||
t.Run("Update branch filter", func(t *testing.T) {
|
||||
m := &repo_model.PushMirror{
|
||||
RepoID: 1,
|
||||
RemoteName: "test-update",
|
||||
BranchFilter: "main",
|
||||
}
|
||||
unittest.AssertSuccessfulInsert(t, m)
|
||||
|
||||
m.BranchFilter = "main,develop"
|
||||
require.NoError(t, repo_model.UpdatePushMirrorBranchFilter(db.DefaultContext, m))
|
||||
|
||||
updated := unittest.AssertExistsAndLoadBean(t, &repo_model.PushMirror{ID: m.ID})
|
||||
assert.Equal(t, "main,develop", updated.BranchFilter)
|
||||
})
|
||||
|
||||
t.Run("Retrieve push mirror with branch filter", func(t *testing.T) {
|
||||
original := &repo_model.PushMirror{
|
||||
RepoID: 1,
|
||||
RemoteName: "test-retrieve",
|
||||
BranchFilter: "main,develop",
|
||||
}
|
||||
unittest.AssertSuccessfulInsert(t, original)
|
||||
|
||||
retrieved := unittest.AssertExistsAndLoadBean(t, &repo_model.PushMirror{ID: original.ID})
|
||||
assert.Equal(t, original.BranchFilter, retrieved.BranchFilter)
|
||||
assert.Equal(t, "main,develop", retrieved.BranchFilter)
|
||||
})
|
||||
|
||||
t.Run("GetPushMirrorsByRepoID includes branch filter", func(t *testing.T) {
|
||||
mirrors := []*repo_model.PushMirror{
|
||||
{
|
||||
RepoID: 2,
|
||||
RemoteName: "mirror-1",
|
||||
BranchFilter: "main",
|
||||
},
|
||||
{
|
||||
RepoID: 2,
|
||||
RemoteName: "mirror-2",
|
||||
BranchFilter: "develop,feature-*",
|
||||
},
|
||||
{
|
||||
RepoID: 2,
|
||||
RemoteName: "mirror-3",
|
||||
BranchFilter: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, mirror := range mirrors {
|
||||
unittest.AssertSuccessfulInsert(t, mirror)
|
||||
}
|
||||
|
||||
retrieved, count, err := repo_model.GetPushMirrorsByRepoID(db.DefaultContext, 2, db.ListOptions{})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(3), count)
|
||||
assert.Len(t, retrieved, 3)
|
||||
|
||||
filterMap := make(map[string]string)
|
||||
for _, mirror := range retrieved {
|
||||
filterMap[mirror.RemoteName] = mirror.BranchFilter
|
||||
}
|
||||
|
||||
assert.Equal(t, "main", filterMap["mirror-1"])
|
||||
assert.Equal(t, "develop,feature-*", filterMap["mirror-2"])
|
||||
assert.Empty(t, filterMap["mirror-3"])
|
||||
})
|
||||
|
||||
t.Run("GetPushMirrorsSyncedOnCommit includes branch filter", func(t *testing.T) {
|
||||
mirrors := []*repo_model.PushMirror{
|
||||
{
|
||||
RepoID: 3,
|
||||
RemoteName: "sync-mirror-1",
|
||||
BranchFilter: "main,develop",
|
||||
SyncOnCommit: true,
|
||||
},
|
||||
{
|
||||
RepoID: 3,
|
||||
RemoteName: "sync-mirror-2",
|
||||
BranchFilter: "feature-*",
|
||||
SyncOnCommit: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, mirror := range mirrors {
|
||||
unittest.AssertSuccessfulInsert(t, mirror)
|
||||
}
|
||||
|
||||
retrieved, err := repo_model.GetPushMirrorsSyncedOnCommit(db.DefaultContext, 3)
|
||||
require.NoError(t, err)
|
||||
assert.Len(t, retrieved, 2)
|
||||
|
||||
filterMap := make(map[string]string)
|
||||
for _, mirror := range retrieved {
|
||||
filterMap[mirror.RemoteName] = mirror.BranchFilter
|
||||
}
|
||||
|
||||
assert.Equal(t, "main,develop", filterMap["sync-mirror-1"])
|
||||
assert.Equal(t, "feature-*", filterMap["sync-mirror-2"])
|
||||
})
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ type CreatePushMirrorOption struct {
|
|||
Interval string `json:"interval"`
|
||||
SyncOnCommit bool `json:"sync_on_commit"`
|
||||
UseSSH bool `json:"use_ssh"`
|
||||
BranchFilter string `json:"branch_filter"`
|
||||
}
|
||||
|
||||
// PushMirror represents information of a push mirror
|
||||
|
@ -29,4 +30,6 @@ type PushMirror struct {
|
|||
Interval string `json:"interval"`
|
||||
SyncOnCommit bool `json:"sync_on_commit"`
|
||||
PublicKey string `json:"public_key"`
|
||||
|
||||
BranchFilter string `json:"branch_filter"`
|
||||
}
|
||||
|
|
|
@ -55,6 +55,8 @@
|
|||
"repo.form.cannot_create": "All spaces in which you can create repositories have reached the limit of repositories.",
|
||||
"repo.issue_indexer.title": "Issue Indexer",
|
||||
"search.milestone_kind": "Search milestones…",
|
||||
"repo.settings.push_mirror.branch_filter.label": "Branch filter (optional)",
|
||||
"repo.settings.push_mirror.branch_filter.description": "Branches to be mirrored. Leave blank to mirror all branches. See <a href=\"%[1]s\">%[2]s</a> documentation for syntax. Examples: <code>main, release/*</code>",
|
||||
"incorrect_root_url": "This Forgejo instance is configured to be served on \"%s\". You are currently viewing Forgejo through a different URL, which may cause parts of the application to break. The canonical URL is controlled by Forgejo admins via the ROOT_URL setting in the app.ini.",
|
||||
"themes.names.forgejo-auto": "Forgejo (follow system theme)",
|
||||
"themes.names.forgejo-light": "Forgejo light",
|
||||
|
|
|
@ -389,6 +389,7 @@ func CreatePushMirror(ctx *context.APIContext, mirrorOption *api.CreatePushMirro
|
|||
Interval: interval,
|
||||
SyncOnCommit: mirrorOption.SyncOnCommit,
|
||||
RemoteAddress: remoteAddress,
|
||||
BranchFilter: mirrorOption.BranchFilter,
|
||||
}
|
||||
|
||||
var plainPrivateKey []byte
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
package setting
|
||||
|
||||
import (
|
||||
go_context "context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
@ -589,6 +590,23 @@ func SettingsPost(ctx *context.Context) {
|
|||
ctx.ServerError("UpdatePushMirrorInterval", err)
|
||||
return
|
||||
}
|
||||
|
||||
if m.BranchFilter != form.PushMirrorBranchFilter {
|
||||
// replace `remote.<remote>.push` in config and db
|
||||
m.BranchFilter = form.PushMirrorBranchFilter
|
||||
if err := db.WithTx(ctx, func(ctx go_context.Context) error {
|
||||
// Update the DB
|
||||
if err = repo_model.UpdatePushMirrorBranchFilter(ctx, m); err != nil {
|
||||
return err
|
||||
}
|
||||
// Update the repo config
|
||||
return mirror_service.UpdatePushMirrorBranchFilter(ctx, m)
|
||||
}); err != nil {
|
||||
ctx.ServerError("UpdatePushMirrorBranchFilter", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Background why we are adding it to Queue
|
||||
// If we observed its implementation in the context of `push-mirror-sync` where it
|
||||
// is evident that pushing to the queue is necessary for updates.
|
||||
|
@ -684,6 +702,7 @@ func SettingsPost(ctx *context.Context) {
|
|||
SyncOnCommit: form.PushMirrorSyncOnCommit,
|
||||
Interval: interval,
|
||||
RemoteAddress: remoteAddress,
|
||||
BranchFilter: form.PushMirrorBranchFilter,
|
||||
}
|
||||
|
||||
var plainPrivateKey []byte
|
||||
|
|
|
@ -23,5 +23,6 @@ func ToPushMirror(ctx context.Context, pm *repo_model.PushMirror) (*api.PushMirr
|
|||
Interval: pm.Interval.String(),
|
||||
SyncOnCommit: pm.SyncOnCommit,
|
||||
PublicKey: pm.GetPublicKey(),
|
||||
BranchFilter: pm.BranchFilter,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -141,6 +141,7 @@ type RepoSettingForm struct {
|
|||
PushMirrorSyncOnCommit bool
|
||||
PushMirrorInterval string
|
||||
PushMirrorUseSSH bool
|
||||
PushMirrorBranchFilter string `binding:"MaxSize(2048)" preprocess:"TrimSpace"`
|
||||
Private bool
|
||||
Template bool
|
||||
EnablePrune bool
|
||||
|
|
|
@ -33,19 +33,22 @@ var AddPushMirrorRemote = addPushMirrorRemote
|
|||
|
||||
func addPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr string) error {
|
||||
addRemoteAndConfig := func(addr, path string) error {
|
||||
cmd := git.NewCommand(ctx, "remote", "add", "--mirror=push").AddDynamicArguments(m.RemoteName, addr)
|
||||
if strings.Contains(addr, "://") && strings.Contains(addr, "@") {
|
||||
cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, util.SanitizeCredentialURLs(addr), path))
|
||||
var cmd *git.Command
|
||||
if m.BranchFilter == "" {
|
||||
cmd = git.NewCommand(ctx, "remote", "add", "--mirror").AddDynamicArguments(m.RemoteName, addr)
|
||||
} else {
|
||||
cmd.SetDescription(fmt.Sprintf("remote add %s --mirror=push %s [repo_path: %s]", m.RemoteName, addr, path))
|
||||
cmd = git.NewCommand(ctx, "remote", "add").AddDynamicArguments(m.RemoteName, addr)
|
||||
}
|
||||
if strings.Contains(addr, "://") && strings.Contains(addr, "@") {
|
||||
cmd.SetDescription(fmt.Sprintf("remote add %s %s [repo_path: %s]", m.RemoteName, util.SanitizeCredentialURLs(addr), path))
|
||||
} else {
|
||||
cmd.SetDescription(fmt.Sprintf("remote add %s %s [repo_path: %s]", m.RemoteName, addr, path))
|
||||
}
|
||||
if _, _, err := cmd.RunStdString(&git.RunOpts{Dir: path}); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*").RunStdString(&git.RunOpts{Dir: path}); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*").RunStdString(&git.RunOpts{Dir: path}); err != nil {
|
||||
err := addRemotePushRefSpecs(ctx, path, m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -67,6 +70,49 @@ func addPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr str
|
|||
return nil
|
||||
}
|
||||
|
||||
func addRemotePushRefSpecs(ctx context.Context, path string, m *repo_model.PushMirror) error {
|
||||
if m.BranchFilter == "" {
|
||||
// If there is no branch filter, set the push refspecs to mirror all branches and tags.
|
||||
if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/heads/*:refs/heads/*").RunStdString(&git.RunOpts{Dir: path}); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
branches := strings.SplitSeq(m.BranchFilter, ",")
|
||||
for branch := range branches {
|
||||
branch = strings.TrimSpace(branch)
|
||||
if branch == "" {
|
||||
continue
|
||||
}
|
||||
refspec := fmt.Sprintf("+refs/heads/%s:refs/heads/%s", branch, branch)
|
||||
if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", refspec).RunStdString(&git.RunOpts{Dir: path}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if _, _, err := git.NewCommand(ctx, "config", "--add").AddDynamicArguments("remote."+m.RemoteName+".push", "+refs/tags/*:refs/tags/*").RunStdString(&git.RunOpts{Dir: path}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func UpdatePushMirrorBranchFilter(ctx context.Context, m *repo_model.PushMirror) error {
|
||||
path := m.Repo.RepoPath()
|
||||
|
||||
// First, remove all existing push refspecs for this remote
|
||||
cmd := git.NewCommand(ctx, "config", "--unset-all").AddDynamicArguments("remote." + m.RemoteName + ".push")
|
||||
if _, _, err := cmd.RunStdString(&git.RunOpts{Dir: path}); err != nil {
|
||||
// Ignore error if the key doesn't exist
|
||||
if !strings.Contains(err.Error(), "does not exist") {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err := addRemotePushRefSpecs(ctx, path, m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// RemovePushMirrorRemote removes the push mirror remote.
|
||||
func RemovePushMirrorRemote(ctx context.Context, m *repo_model.PushMirror) error {
|
||||
cmd := git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(m.RemoteName)
|
||||
|
@ -212,7 +258,6 @@ func runPushSync(ctx context.Context, m *repo_model.PushMirror) error {
|
|||
|
||||
return util.SanitizeErrorCredentialURLs(err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -254,6 +254,7 @@
|
|||
data-modal-push-mirror-edit-id="{{.ID}}"
|
||||
data-modal-push-mirror-edit-interval="{{.Interval}}"
|
||||
data-modal-push-mirror-edit-address="{{.RemoteAddress}}"
|
||||
data-modal-push-mirror-edit-branch-filter="{{.BranchFilter}}"
|
||||
>
|
||||
{{svg "octicon-pencil" 14}}
|
||||
</button>
|
||||
|
@ -288,6 +289,11 @@
|
|||
<input id="push_mirror_address" name="push_mirror_address" value="{{.push_mirror_address}}" required>
|
||||
<p class="help">{{ctx.Locale.Tr "repo.mirror_address_desc"}}</p>
|
||||
</div>
|
||||
<div class="field {{if .Err_PushMirrorBranchFilter}}error{{end}}">
|
||||
<label for="push_mirror_branch_filter">{{ctx.Locale.Tr "repo.settings.push_mirror.branch_filter.label"}}</label>
|
||||
<input id="push_mirror_branch_filter" name="push_mirror_branch_filter" value="{{.push_mirror_branch_filter}}" maxlength="2048">
|
||||
<p class="help">{{ctx.Locale.Tr "repo.settings.push_mirror.branch_filter.description" "https://forgejo.org/docs/latest/user/repo-mirror/#branch-filter" "forgejo"}}</p>
|
||||
</div>
|
||||
<details class="ui optional field" {{if or .Err_PushMirrorAuth .push_mirror_username}}open{{end}}>
|
||||
<summary class="tw-p-1">
|
||||
{{ctx.Locale.Tr "repo.need_auth"}}
|
||||
|
|
|
@ -15,7 +15,16 @@
|
|||
</div>
|
||||
<div class="inline field">
|
||||
<label for="push-mirror-edit-interval">{{ctx.Locale.Tr "repo.mirror_interval" .MinimumMirrorInterval}}</label>
|
||||
<input id="push-mirror-edit-interval" name="push_mirror_interval" autofocus>
|
||||
<div class="ui small input">
|
||||
<input id="push-mirror-edit-interval" name="push_mirror_interval" autofocus>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="push-mirror-edit-branch-filter">{{ctx.Locale.Tr "repo.settings.push_mirror.branch_filter.label"}}</label>
|
||||
<div class="ui small input">
|
||||
<input id="push-mirror-edit-branch-filter" name="push_mirror_branch_filter" maxlength="2048">
|
||||
</div>
|
||||
<p class="help">{{ctx.Locale.Tr "repo.settings.push_mirror.branch_filter.description" "https://forgejo.org/docs/latest/user/repo-mirror/#branch-filter" "forgejo"}}</p>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="ui small basic cancel button">
|
||||
|
|
8
templates/swagger/v1_json.tmpl
generated
8
templates/swagger/v1_json.tmpl
generated
|
@ -23111,6 +23111,10 @@
|
|||
"type": "object",
|
||||
"title": "CreatePushMirrorOption represents need information to create a push mirror of a repository.",
|
||||
"properties": {
|
||||
"branch_filter": {
|
||||
"type": "string",
|
||||
"x-go-name": "BranchFilter"
|
||||
},
|
||||
"interval": {
|
||||
"type": "string",
|
||||
"x-go-name": "Interval"
|
||||
|
@ -26994,6 +26998,10 @@
|
|||
"description": "PushMirror represents information of a push mirror",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"branch_filter": {
|
||||
"type": "string",
|
||||
"x-go-name": "BranchFilter"
|
||||
},
|
||||
"created": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
|
|
|
@ -14,6 +14,7 @@ import (
|
|||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -142,6 +143,159 @@ func testAPIPushMirror(t *testing.T, u *url.URL) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestAPIPushMirrorBranchFilter(t *testing.T) {
|
||||
onGiteaRun(t, testAPIPushMirrorBranchFilter)
|
||||
}
|
||||
|
||||
func testAPIPushMirrorBranchFilter(t *testing.T, u *url.URL) {
|
||||
defer test.MockVariableValue(&setting.Migrations.AllowLocalNetworks, true)()
|
||||
defer test.MockVariableValue(&setting.Mirror.Enabled, true)()
|
||||
defer test.MockProtect(&mirror_service.AddPushMirrorRemote)()
|
||||
defer test.MockProtect(&repo_model.DeletePushMirrors)()
|
||||
|
||||
require.NoError(t, migrations.Init())
|
||||
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
srcRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: srcRepo.OwnerID})
|
||||
session := loginUser(t, user.Name)
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAll)
|
||||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/push_mirrors", owner.Name, srcRepo.Name)
|
||||
|
||||
mirrorRepo, _, f := tests.CreateDeclarativeRepo(t, user, "", []unit.Type{unit.TypeCode}, nil, nil)
|
||||
defer f()
|
||||
remoteAddress := fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(user.Name), url.PathEscape(mirrorRepo.Name))
|
||||
|
||||
t.Run("Create push mirror with branch filter", func(t *testing.T) {
|
||||
req := NewRequestWithJSON(t, "POST", urlStr, &api.CreatePushMirrorOption{
|
||||
RemoteAddress: remoteAddress,
|
||||
Interval: "8h",
|
||||
BranchFilter: "main,develop",
|
||||
}).AddTokenAuth(token)
|
||||
|
||||
MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
// Verify the push mirror was created with branch filter
|
||||
req = NewRequest(t, "GET", urlStr).AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
var pushMirrors []*api.PushMirror
|
||||
DecodeJSON(t, resp, &pushMirrors)
|
||||
require.Len(t, pushMirrors, 1)
|
||||
assert.Equal(t, "main,develop", pushMirrors[0].BranchFilter)
|
||||
|
||||
// Cleanup
|
||||
req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s", urlStr, pushMirrors[0].RemoteName)).AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
})
|
||||
|
||||
t.Run("Create push mirror with empty branch filter", func(t *testing.T) {
|
||||
req := NewRequestWithJSON(t, "POST", urlStr, &api.CreatePushMirrorOption{
|
||||
RemoteAddress: remoteAddress,
|
||||
Interval: "8h",
|
||||
BranchFilter: "",
|
||||
}).AddTokenAuth(token)
|
||||
|
||||
MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
// Verify the push mirror was created with empty branch filter
|
||||
req = NewRequest(t, "GET", urlStr).AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
var pushMirrors []*api.PushMirror
|
||||
DecodeJSON(t, resp, &pushMirrors)
|
||||
require.Len(t, pushMirrors, 1)
|
||||
assert.Empty(t, pushMirrors[0].BranchFilter)
|
||||
|
||||
// Cleanup
|
||||
req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s", urlStr, pushMirrors[0].RemoteName)).AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
})
|
||||
|
||||
t.Run("Create push mirror without branch filter parameter", func(t *testing.T) {
|
||||
req := NewRequestWithJSON(t, "POST", urlStr, &api.CreatePushMirrorOption{
|
||||
RemoteAddress: remoteAddress,
|
||||
Interval: "8h",
|
||||
// BranchFilter: ""
|
||||
}).AddTokenAuth(token)
|
||||
|
||||
MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
// Verify the push mirror defaults to empty branch filter
|
||||
req = NewRequest(t, "GET", urlStr).AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
var pushMirrors []*api.PushMirror
|
||||
DecodeJSON(t, resp, &pushMirrors)
|
||||
require.Len(t, pushMirrors, 1)
|
||||
assert.Empty(t, pushMirrors[0].BranchFilter)
|
||||
|
||||
// Cleanup
|
||||
req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s", urlStr, pushMirrors[0].RemoteName)).AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
})
|
||||
|
||||
t.Run("Retrieve multiple push mirrors with different branch filters", func(t *testing.T) {
|
||||
// Create multiple push mirrors with different branch filters
|
||||
testCases := []struct {
|
||||
name string
|
||||
branchFilter string
|
||||
}{
|
||||
{"mirror-1", "main"},
|
||||
{"mirror-2", "develop,feature-*"},
|
||||
{"mirror-3", ""},
|
||||
}
|
||||
|
||||
// Create mirrors
|
||||
mirrorCleanups := []func(){}
|
||||
defer func() {
|
||||
for _, mirror := range mirrorCleanups {
|
||||
mirror()
|
||||
}
|
||||
}()
|
||||
for _, tc := range testCases {
|
||||
mirrorRepo, _, f := tests.CreateDeclarativeRepo(t, user, tc.name, []unit.Type{unit.TypeCode}, nil, nil)
|
||||
mirrorCleanups = append(mirrorCleanups, f)
|
||||
|
||||
remoteAddr := fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(user.Name), url.PathEscape(mirrorRepo.Name))
|
||||
req := NewRequestWithJSON(t, "POST", urlStr, &api.CreatePushMirrorOption{
|
||||
RemoteAddress: remoteAddr,
|
||||
Interval: "8h",
|
||||
BranchFilter: tc.branchFilter,
|
||||
}).AddTokenAuth(token)
|
||||
|
||||
MakeRequest(t, req, http.StatusOK)
|
||||
}
|
||||
|
||||
// Retrieve all mirrors and verify branch filters
|
||||
req := NewRequest(t, "GET", urlStr).AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
var pushMirrors []*api.PushMirror
|
||||
DecodeJSON(t, resp, &pushMirrors)
|
||||
require.Len(t, pushMirrors, 3)
|
||||
|
||||
// Create a map for easier verification
|
||||
filterMap := make(map[string]string)
|
||||
var createdMirrors []*api.PushMirror
|
||||
for _, mirror := range pushMirrors {
|
||||
for _, tc := range testCases {
|
||||
if strings.Contains(mirror.RemoteAddress, tc.name) {
|
||||
filterMap[tc.name] = mirror.BranchFilter
|
||||
createdMirrors = append(createdMirrors, mirror)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assert.Equal(t, "main", filterMap["mirror-1"])
|
||||
assert.Equal(t, "develop,feature-*", filterMap["mirror-2"])
|
||||
assert.Empty(t, filterMap["mirror-3"])
|
||||
|
||||
// Cleanup
|
||||
for _, mirror := range createdMirrors {
|
||||
req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s", urlStr, mirror.RemoteName)).AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestAPIPushMirrorSSH(t *testing.T) {
|
||||
_, err := exec.LookPath("ssh")
|
||||
if err != nil {
|
||||
|
|
|
@ -13,10 +13,12 @@ import (
|
|||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
asymkey_model "forgejo.org/models/asymkey"
|
||||
auth_model "forgejo.org/models/auth"
|
||||
"forgejo.org/models/db"
|
||||
repo_model "forgejo.org/models/repo"
|
||||
"forgejo.org/models/unit"
|
||||
|
@ -26,6 +28,7 @@ import (
|
|||
"forgejo.org/modules/gitrepo"
|
||||
"forgejo.org/modules/optional"
|
||||
"forgejo.org/modules/setting"
|
||||
api "forgejo.org/modules/structs"
|
||||
"forgejo.org/modules/test"
|
||||
gitea_context "forgejo.org/services/context"
|
||||
doctor "forgejo.org/services/doctor"
|
||||
|
@ -34,6 +37,7 @@ import (
|
|||
repo_service "forgejo.org/services/repository"
|
||||
"forgejo.org/tests"
|
||||
|
||||
"github.com/PuerkitoBio/goquery"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -134,6 +138,27 @@ func doCreatePushMirror(ctx APITestContext, address, username, password string)
|
|||
}
|
||||
}
|
||||
|
||||
func doCreatePushMirrorWithBranchFilter(ctx APITestContext, address, username, password, branchFilter string) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
csrf := GetCSRF(t, ctx.Session, fmt.Sprintf("/%s/%s/settings", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame)))
|
||||
|
||||
req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame)), map[string]string{
|
||||
"_csrf": csrf,
|
||||
"action": "push-mirror-add",
|
||||
"push_mirror_address": address,
|
||||
"push_mirror_username": username,
|
||||
"push_mirror_password": password,
|
||||
"push_mirror_interval": "0",
|
||||
"push_mirror_branch_filter": branchFilter,
|
||||
})
|
||||
ctx.Session.MakeRequest(t, req, http.StatusSeeOther)
|
||||
|
||||
flashCookie := ctx.Session.GetCookie(gitea_context.CookieNameFlash)
|
||||
assert.NotNil(t, flashCookie)
|
||||
assert.Contains(t, flashCookie.Value, "success")
|
||||
}
|
||||
}
|
||||
|
||||
func doRemovePushMirror(ctx APITestContext, address, username, password string, pushMirrorID int) func(t *testing.T) {
|
||||
return func(t *testing.T) {
|
||||
csrf := GetCSRF(t, ctx.Session, fmt.Sprintf("/%s/%s/settings", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame)))
|
||||
|
@ -322,6 +347,196 @@ func TestSSHPushMirror(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestPushMirrorBranchFilterWebUI(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
defer test.MockVariableValue(&setting.Migrations.AllowLocalNetworks, true)()
|
||||
defer test.MockVariableValue(&setting.Mirror.Enabled, true)()
|
||||
require.NoError(t, migrations.Init())
|
||||
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
srcRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
sess := loginUser(t, user.Name)
|
||||
|
||||
mirrorRepo, _, f := tests.CreateDeclarativeRepo(t, user, "", []unit.Type{unit.TypeCode}, nil, nil)
|
||||
defer f()
|
||||
|
||||
ctx := NewAPITestContext(t, user.LowerName, srcRepo.Name)
|
||||
ctx.Session = sess
|
||||
remoteAddress := fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(user.Name), url.PathEscape(mirrorRepo.Name))
|
||||
|
||||
t.Run("Create push mirror with branch filter via web UI", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
doCreatePushMirrorWithBranchFilter(ctx, remoteAddress, user.LowerName, userPassword, "main,develop")(t)
|
||||
|
||||
mirrors, _, err := repo_model.GetPushMirrorsByRepoID(db.DefaultContext, srcRepo.ID, db.ListOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, mirrors, 1)
|
||||
assert.Equal(t, "main,develop", mirrors[0].BranchFilter)
|
||||
assert.Equal(t, remoteAddress, mirrors[0].RemoteAddress)
|
||||
})
|
||||
|
||||
t.Run("Create push mirror with empty branch filter via web UI", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
remoteAddress2 := fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(user.Name), url.PathEscape("foo"))
|
||||
doCreatePushMirrorWithBranchFilter(ctx, remoteAddress2, user.LowerName, userPassword, "")(t)
|
||||
|
||||
mirrors, _, err := repo_model.GetPushMirrorsByRepoID(db.DefaultContext, srcRepo.ID, db.ListOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, mirrors, 2)
|
||||
|
||||
var emptyMirror *repo_model.PushMirror
|
||||
for _, mirror := range mirrors {
|
||||
if mirror.RemoteAddress == remoteAddress2 {
|
||||
emptyMirror = mirror
|
||||
break
|
||||
}
|
||||
}
|
||||
require.NotNil(t, emptyMirror)
|
||||
assert.Empty(t, emptyMirror.BranchFilter)
|
||||
})
|
||||
|
||||
t.Run("Verify branch filter field is visible in settings page", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/settings", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame)))
|
||||
resp := sess.MakeRequest(t, req, http.StatusOK)
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
|
||||
htmlDoc.AssertElement(t, "input#push_mirror_branch_filter", true)
|
||||
})
|
||||
|
||||
t.Run("Verify existing branch filter values are pre-populated", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/settings", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame)))
|
||||
resp := sess.MakeRequest(t, req, http.StatusOK)
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
|
||||
// Find all edit buttons for push mirrors
|
||||
editButtons := htmlDoc.Find("button[data-modal='#push-mirror-edit-modal']")
|
||||
assert.Equal(t, 2, editButtons.Length(), "Should have 2 push mirror edit buttons")
|
||||
|
||||
// Get the created mirrors from database to match with UI elements
|
||||
mirrors, _, err := repo_model.GetPushMirrorsByRepoID(db.DefaultContext, srcRepo.ID, db.ListOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, mirrors, 2)
|
||||
|
||||
// Create a map of remote address to branch filter for easy lookup
|
||||
expectedFilters := make(map[string]string)
|
||||
for _, mirror := range mirrors {
|
||||
expectedFilters[mirror.RemoteAddress] = mirror.BranchFilter
|
||||
}
|
||||
|
||||
// Check each edit button has the correct branch filter data attribute
|
||||
editButtons.Each(func(i int, s *goquery.Selection) {
|
||||
remoteAddress, exists := s.Attr("data-modal-push-mirror-edit-address")
|
||||
assert.True(t, exists, "Edit button should have remote address data attribute")
|
||||
|
||||
branchFilter, exists := s.Attr("data-modal-push-mirror-edit-branch-filter")
|
||||
assert.True(t, exists, "Edit button should have branch filter data attribute")
|
||||
|
||||
expectedFilter, found := expectedFilters[remoteAddress]
|
||||
assert.True(t, found, "Remote address should match one of the created mirrors")
|
||||
assert.Equal(t, expectedFilter, branchFilter, "Branch filter should match the expected value for remote %s", remoteAddress)
|
||||
})
|
||||
|
||||
// Verify the edit modal has the correct input field for branch filter editing
|
||||
htmlDoc.AssertElement(t, "#push-mirror-edit-modal input[name='push_mirror_branch_filter']", true)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestPushMirrorBranchFilterIntegration(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
defer test.MockVariableValue(&setting.Migrations.AllowLocalNetworks, true)()
|
||||
defer test.MockVariableValue(&setting.Mirror.Enabled, true)()
|
||||
require.NoError(t, migrations.Init())
|
||||
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
srcRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
sess := loginUser(t, user.Name)
|
||||
token := getTokenForLoggedInUser(t, sess, auth_model.AccessTokenScopeAll)
|
||||
|
||||
ctx := NewAPITestContext(t, user.LowerName, srcRepo.Name)
|
||||
ctx.Session = sess
|
||||
remoteAddress := fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(user.Name), url.PathEscape("foo"))
|
||||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/push_mirrors", user.LowerName, srcRepo.Name)
|
||||
|
||||
t.Run("Web UI to API integration", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
// Create push mirror with branch filter via web UI
|
||||
doCreatePushMirrorWithBranchFilter(ctx, remoteAddress, user.LowerName, userPassword, "main,develop")(t)
|
||||
|
||||
// Verify it appears in API responses
|
||||
req := NewRequest(t, "GET", urlStr).AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
var pushMirrors []*api.PushMirror
|
||||
DecodeJSON(t, resp, &pushMirrors)
|
||||
require.Len(t, pushMirrors, 1)
|
||||
assert.Equal(t, "main,develop", pushMirrors[0].BranchFilter)
|
||||
assert.Equal(t, remoteAddress, pushMirrors[0].RemoteAddress)
|
||||
|
||||
// Verify it's stored correctly in database
|
||||
mirrors, _, err := repo_model.GetPushMirrorsByRepoID(db.DefaultContext, srcRepo.ID, db.ListOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, mirrors, 1)
|
||||
assert.Equal(t, "main,develop", mirrors[0].BranchFilter)
|
||||
})
|
||||
|
||||
t.Run("API to Web UI integration", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
// Create another mirror repo for this test
|
||||
mirrorRepo2, err := repo_service.CreateRepositoryDirectly(db.DefaultContext, user, user, repo_service.CreateRepoOptions{
|
||||
Name: "test-api-to-ui",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
remoteAddress2 := fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(user.Name), url.PathEscape(mirrorRepo2.Name))
|
||||
|
||||
// Create push mirror with branch filter via API
|
||||
req := NewRequestWithJSON(t, "POST", urlStr, &api.CreatePushMirrorOption{
|
||||
RemoteAddress: remoteAddress2,
|
||||
Interval: "8h",
|
||||
BranchFilter: "feature-*,hotfix-*",
|
||||
}).AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
// Verify it's stored in database
|
||||
mirrors, _, err := repo_model.GetPushMirrorsByRepoID(db.DefaultContext, srcRepo.ID, db.ListOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, mirrors, 2) // Should have 2 mirrors now
|
||||
|
||||
// Find the mirror created via API
|
||||
var apiMirror *repo_model.PushMirror
|
||||
for _, mirror := range mirrors {
|
||||
if mirror.RemoteAddress == remoteAddress2 {
|
||||
apiMirror = mirror
|
||||
break
|
||||
}
|
||||
}
|
||||
require.NotNil(t, apiMirror)
|
||||
assert.Equal(t, "feature-*,hotfix-*", apiMirror.BranchFilter)
|
||||
|
||||
// Verify it appears in web UI with correct branch filter data
|
||||
req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s/settings", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame)))
|
||||
resp := sess.MakeRequest(t, req, http.StatusOK)
|
||||
assert.Equal(t, http.StatusOK, resp.Code)
|
||||
|
||||
// Check that the edit button has the correct data attributes for branch filter
|
||||
doc := NewHTMLParser(t, resp.Body)
|
||||
editButton := doc.Find(fmt.Sprintf(`button[data-modal-push-mirror-edit-address="%s"]`, remoteAddress2))
|
||||
require.Equal(t, 1, editButton.Length(), "Should find exactly one edit button for the API-created mirror")
|
||||
|
||||
branchFilterAttr, exists := editButton.Attr("data-modal-push-mirror-edit-branch-filter")
|
||||
require.True(t, exists, "Edit button should have branch filter data attribute")
|
||||
assert.Equal(t, "feature-*,hotfix-*", branchFilterAttr, "Branch filter data attribute should match what was set via API")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestPushMirrorSettings(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
defer test.MockVariableValue(&setting.Migrations.AllowLocalNetworks, true)()
|
||||
|
@ -400,3 +615,459 @@ func TestPushMirrorSettings(t *testing.T) {
|
|||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestPushMirrorBranchFilterSyncOperations(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
defer test.MockVariableValue(&setting.Migrations.AllowLocalNetworks, true)()
|
||||
defer test.MockVariableValue(&setting.Mirror.Enabled, true)()
|
||||
require.NoError(t, migrations.Init())
|
||||
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
srcRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
sess := loginUser(t, user.Name)
|
||||
|
||||
// Create test repository with multiple branches
|
||||
testRepoPath := srcRepo.RepoPath()
|
||||
|
||||
// Create additional branches in source repository
|
||||
_, _, err := git.NewCommand(git.DefaultContext, "update-ref", "refs/heads/develop", "refs/heads/master").RunStdString(&git.RunOpts{Dir: testRepoPath})
|
||||
require.NoError(t, err)
|
||||
_, _, err = git.NewCommand(git.DefaultContext, "update-ref", "refs/heads/feature-auth", "refs/heads/master").RunStdString(&git.RunOpts{Dir: testRepoPath})
|
||||
require.NoError(t, err)
|
||||
_, _, err = git.NewCommand(git.DefaultContext, "update-ref", "refs/heads/feature-ui", "refs/heads/master").RunStdString(&git.RunOpts{Dir: testRepoPath})
|
||||
require.NoError(t, err)
|
||||
_, _, err = git.NewCommand(git.DefaultContext, "update-ref", "refs/heads/hotfix-123", "refs/heads/master").RunStdString(&git.RunOpts{Dir: testRepoPath})
|
||||
require.NoError(t, err)
|
||||
|
||||
ctx := NewAPITestContext(t, user.LowerName, srcRepo.Name)
|
||||
ctx.Session = sess
|
||||
|
||||
t.Run("Create push mirror with branch filter and trigger sync", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
// Create mirror repository
|
||||
mirrorRepo, err := repo_service.CreateRepositoryDirectly(db.DefaultContext, user, user, repo_service.CreateRepoOptions{
|
||||
Name: "test-sync-branch-filter",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
remoteAddress := fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(user.Name), url.PathEscape(mirrorRepo.Name))
|
||||
|
||||
// Create push mirror with specific branch filter via web UI
|
||||
branchFilter := "master,develop,feature-auth"
|
||||
doCreatePushMirrorWithBranchFilter(ctx, remoteAddress, user.LowerName, userPassword, branchFilter)(t)
|
||||
|
||||
// Verify the push mirror was created with branch filter
|
||||
mirrors, _, err := repo_model.GetPushMirrorsByRepoID(db.DefaultContext, srcRepo.ID, db.ListOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, mirrors, 1)
|
||||
assert.Equal(t, branchFilter, mirrors[0].BranchFilter)
|
||||
|
||||
// Verify git remote configuration includes correct refspecs for filtered branches
|
||||
output, _, err := git.NewCommand(git.DefaultContext, "config", "--get-all").AddDynamicArguments(fmt.Sprintf("remote.%s.push", mirrors[0].RemoteName)).RunStdString(&git.RunOpts{Dir: testRepoPath})
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, output, "+refs/heads/master:refs/heads/master")
|
||||
assert.Contains(t, output, "+refs/heads/develop:refs/heads/develop")
|
||||
assert.Contains(t, output, "+refs/heads/feature-auth:refs/heads/feature-auth")
|
||||
assert.NotContains(t, output, "+refs/heads/feature-ui:refs/heads/feature-ui")
|
||||
assert.NotContains(t, output, "+refs/heads/hotfix-123:refs/heads/hotfix-123")
|
||||
assert.Contains(t, output, "+refs/tags/*:refs/tags/*") // Tags should always be pushed
|
||||
|
||||
// Trigger sync operation
|
||||
ok := mirror_service.SyncPushMirror(db.DefaultContext, mirrors[0].ID)
|
||||
assert.True(t, ok)
|
||||
|
||||
// Verify only filtered branches were pushed to mirror
|
||||
mirrorGitRepo, err := gitrepo.OpenRepository(git.DefaultContext, mirrorRepo)
|
||||
require.NoError(t, err)
|
||||
defer mirrorGitRepo.Close()
|
||||
|
||||
// Check that filtered branches exist in mirror
|
||||
_, err = mirrorGitRepo.GetBranchCommit("master")
|
||||
require.NoError(t, err, "master branch should exist in mirror")
|
||||
_, err = mirrorGitRepo.GetBranchCommit("develop")
|
||||
require.NoError(t, err, "develop branch should exist in mirror")
|
||||
_, err = mirrorGitRepo.GetBranchCommit("feature-auth")
|
||||
require.NoError(t, err, "feature-auth branch should exist in mirror")
|
||||
|
||||
// Check that non-filtered branches don't exist in mirror
|
||||
_, err = mirrorGitRepo.GetBranchCommit("feature-ui")
|
||||
require.Error(t, err, "feature-ui branch should not exist in mirror")
|
||||
_, err = mirrorGitRepo.GetBranchCommit("hotfix-123")
|
||||
require.Error(t, err, "hotfix-123 branch should not exist in mirror")
|
||||
})
|
||||
|
||||
t.Run("Update branch filter and verify git remote settings are updated", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
// Get the existing mirror
|
||||
mirrors, _, err := repo_model.GetPushMirrorsByRepoID(db.DefaultContext, srcRepo.ID, db.ListOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, mirrors, 1)
|
||||
mirror := mirrors[0]
|
||||
mirror.Repo = srcRepo
|
||||
|
||||
// Update branch filter to include different branches
|
||||
mirror.BranchFilter = "master,feature-ui,hotfix-123"
|
||||
err = repo_model.UpdatePushMirror(db.DefaultContext, mirror)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Update git remote configuration
|
||||
err = mirror_service.UpdatePushMirrorBranchFilter(db.DefaultContext, mirror)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify git remote configuration was updated
|
||||
output, _, err := git.NewCommand(git.DefaultContext, "config", "--get-all").AddDynamicArguments(fmt.Sprintf("remote.%s.push", mirror.RemoteName)).RunStdString(&git.RunOpts{Dir: testRepoPath})
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, output, "+refs/heads/master:refs/heads/master")
|
||||
assert.Contains(t, output, "+refs/heads/feature-ui:refs/heads/feature-ui")
|
||||
assert.Contains(t, output, "+refs/heads/hotfix-123:refs/heads/hotfix-123")
|
||||
assert.NotContains(t, output, "+refs/heads/develop:refs/heads/develop")
|
||||
assert.NotContains(t, output, "+refs/heads/feature-auth:refs/heads/feature-auth")
|
||||
assert.Contains(t, output, "+refs/tags/*:refs/tags/*") // Tags should always be pushed
|
||||
|
||||
// Trigger sync operation with updated filter
|
||||
ok := mirror_service.SyncPushMirror(db.DefaultContext, mirror.ID)
|
||||
assert.True(t, ok)
|
||||
})
|
||||
|
||||
t.Run("Test empty branch filter pushes all branches", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
// Create another mirror repository for this test
|
||||
mirrorRepo2, err := repo_service.CreateRepositoryDirectly(db.DefaultContext, user, user, repo_service.CreateRepoOptions{
|
||||
Name: "test-sync-empty-filter",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
remoteAddress2 := fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(user.Name), url.PathEscape(mirrorRepo2.Name))
|
||||
|
||||
// Create push mirror with empty branch filter
|
||||
doCreatePushMirrorWithBranchFilter(ctx, remoteAddress2, user.LowerName, userPassword, "")(t)
|
||||
|
||||
// Get the new mirror
|
||||
mirrors, _, err := repo_model.GetPushMirrorsByRepoID(db.DefaultContext, srcRepo.ID, db.ListOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, mirrors, 2) // Should have 2 mirrors now
|
||||
|
||||
var emptyFilterMirror *repo_model.PushMirror
|
||||
for _, mirror := range mirrors {
|
||||
if mirror.RemoteAddress == remoteAddress2 {
|
||||
emptyFilterMirror = mirror
|
||||
break
|
||||
}
|
||||
}
|
||||
require.NotNil(t, emptyFilterMirror)
|
||||
assert.Empty(t, emptyFilterMirror.BranchFilter)
|
||||
|
||||
// Verify git remote configuration for empty filter (should mirror all branches)
|
||||
output, _, err := git.NewCommand(git.DefaultContext, "config", "--get-all").AddDynamicArguments(fmt.Sprintf("remote.%s.push", emptyFilterMirror.RemoteName)).RunStdString(&git.RunOpts{Dir: testRepoPath})
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, output, "+refs/heads/*:refs/heads/*") // Should mirror all branches
|
||||
assert.Contains(t, output, "+refs/tags/*:refs/tags/*")
|
||||
|
||||
// Trigger sync operation
|
||||
ok := mirror_service.SyncPushMirror(db.DefaultContext, emptyFilterMirror.ID)
|
||||
assert.True(t, ok)
|
||||
|
||||
// Verify all branches were pushed to mirror
|
||||
mirrorGitRepo2, err := gitrepo.OpenRepository(git.DefaultContext, mirrorRepo2)
|
||||
require.NoError(t, err)
|
||||
defer mirrorGitRepo2.Close()
|
||||
|
||||
// Check that all branches exist in mirror
|
||||
_, err = mirrorGitRepo2.GetBranchCommit("master")
|
||||
require.NoError(t, err, "master branch should exist in mirror")
|
||||
_, err = mirrorGitRepo2.GetBranchCommit("develop")
|
||||
require.NoError(t, err, "develop branch should exist in mirror")
|
||||
_, err = mirrorGitRepo2.GetBranchCommit("feature-auth")
|
||||
require.NoError(t, err, "feature-auth branch should exist in mirror")
|
||||
_, err = mirrorGitRepo2.GetBranchCommit("feature-ui")
|
||||
require.NoError(t, err, "feature-ui branch should exist in mirror")
|
||||
_, err = mirrorGitRepo2.GetBranchCommit("hotfix-123")
|
||||
require.NoError(t, err, "hotfix-123 branch should exist in mirror")
|
||||
})
|
||||
|
||||
t.Run("Test glob pattern branch filter in sync operations", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
// Create another mirror repository for this test
|
||||
mirrorRepo3, err := repo_service.CreateRepositoryDirectly(db.DefaultContext, user, user, repo_service.CreateRepoOptions{
|
||||
Name: "test-sync-glob-filter",
|
||||
})
|
||||
require.NoError(t, err)
|
||||
remoteAddress3 := fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(user.Name), url.PathEscape(mirrorRepo3.Name))
|
||||
|
||||
// Create push mirror with glob pattern branch filter
|
||||
globFilter := "master,feature-*"
|
||||
doCreatePushMirrorWithBranchFilter(ctx, remoteAddress3, user.LowerName, userPassword, globFilter)(t)
|
||||
|
||||
// Get the new mirror
|
||||
mirrors, _, err := repo_model.GetPushMirrorsByRepoID(db.DefaultContext, srcRepo.ID, db.ListOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, mirrors, 3) // Should have 3 mirrors now
|
||||
|
||||
var globMirror *repo_model.PushMirror
|
||||
for _, mirror := range mirrors {
|
||||
if mirror.RemoteAddress == remoteAddress3 {
|
||||
globMirror = mirror
|
||||
break
|
||||
}
|
||||
}
|
||||
require.NotNil(t, globMirror)
|
||||
assert.Equal(t, globFilter, globMirror.BranchFilter)
|
||||
|
||||
// Verify git remote configuration includes glob pattern branches
|
||||
output, _, err := git.NewCommand(git.DefaultContext, "config", "--get-all").AddDynamicArguments(fmt.Sprintf("remote.%s.push", globMirror.RemoteName)).RunStdString(&git.RunOpts{Dir: testRepoPath})
|
||||
require.NoError(t, err)
|
||||
assert.Contains(t, output, "+refs/heads/master:refs/heads/master")
|
||||
assert.Contains(t, output, "+refs/heads/feature-*:refs/heads/feature-*")
|
||||
assert.NotContains(t, output, "+refs/heads/develop:refs/heads/develop")
|
||||
assert.NotContains(t, output, "+refs/heads/hotfix-123:refs/heads/hotfix-123")
|
||||
assert.Contains(t, output, "+refs/tags/*:refs/tags/*")
|
||||
|
||||
// Trigger sync operation
|
||||
ok := mirror_service.SyncPushMirror(db.DefaultContext, globMirror.ID)
|
||||
assert.True(t, ok)
|
||||
|
||||
// Verify only matching branches were pushed to mirror
|
||||
mirrorGitRepo3, err := gitrepo.OpenRepository(git.DefaultContext, mirrorRepo3)
|
||||
require.NoError(t, err)
|
||||
defer mirrorGitRepo3.Close()
|
||||
|
||||
// Check that matching branches exist in mirror
|
||||
_, err = mirrorGitRepo3.GetBranchCommit("master")
|
||||
require.NoError(t, err, "master branch should exist in mirror")
|
||||
_, err = mirrorGitRepo3.GetBranchCommit("feature-auth")
|
||||
require.NoError(t, err, "feature-auth branch should exist in mirror")
|
||||
_, err = mirrorGitRepo3.GetBranchCommit("feature-ui")
|
||||
require.NoError(t, err, "feature-ui branch should exist in mirror")
|
||||
|
||||
// Check that non-matching branches don't exist in mirror
|
||||
_, err = mirrorGitRepo3.GetBranchCommit("develop")
|
||||
require.Error(t, err, "develop branch should not exist in mirror")
|
||||
_, err = mirrorGitRepo3.GetBranchCommit("hotfix-123")
|
||||
require.Error(t, err, "hotfix-123 branch should not exist in mirror")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestPushMirrorWebUIToAPIIntegration(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
defer test.MockVariableValue(&setting.Migrations.AllowLocalNetworks, true)()
|
||||
defer test.MockVariableValue(&setting.Mirror.Enabled, true)()
|
||||
require.NoError(t, migrations.Init())
|
||||
|
||||
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
srcRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
||||
session := loginUser(t, user.Name)
|
||||
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeAll)
|
||||
|
||||
mirrorRepo, _, f := tests.CreateDeclarativeRepo(t, user, "", []unit.Type{unit.TypeCode}, nil, nil)
|
||||
defer f()
|
||||
|
||||
ctx := NewAPITestContext(t, user.LowerName, srcRepo.Name)
|
||||
ctx.Session = session
|
||||
remoteAddress := fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(user.Name), url.PathEscape(mirrorRepo.Name))
|
||||
urlStr := fmt.Sprintf("/api/v1/repos/%s/%s/push_mirrors", user.Name, srcRepo.Name)
|
||||
|
||||
t.Run("Set branch filter via web UI and verify in API response", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
// Create push mirror with branch filter via web UI
|
||||
branchFilter := "main,develop,feature-*"
|
||||
doCreatePushMirrorWithBranchFilter(ctx, remoteAddress, user.LowerName, userPassword, branchFilter)(t)
|
||||
|
||||
// Verify via API that branch filter is set correctly
|
||||
req := NewRequest(t, "GET", urlStr).AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
var pushMirrors []*api.PushMirror
|
||||
DecodeJSON(t, resp, &pushMirrors)
|
||||
|
||||
require.Len(t, pushMirrors, 1)
|
||||
assert.Equal(t, branchFilter, pushMirrors[0].BranchFilter, "Branch filter set via web UI should appear in API response")
|
||||
assert.Equal(t, remoteAddress, pushMirrors[0].RemoteAddress)
|
||||
|
||||
// Store mirror info for cleanup
|
||||
mirrorRemoteName := pushMirrors[0].RemoteName
|
||||
|
||||
// Cleanup
|
||||
req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s", urlStr, mirrorRemoteName)).AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
})
|
||||
|
||||
t.Run("Set empty branch filter via web UI and verify in API response", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
// Create another mirror repo for this test
|
||||
mirrorRepo2, _, f := tests.CreateDeclarativeRepo(t, user, "", []unit.Type{unit.TypeCode}, nil, nil)
|
||||
defer f()
|
||||
remoteAddress2 := fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(user.Name), url.PathEscape(mirrorRepo2.Name))
|
||||
|
||||
// Create push mirror with empty branch filter via web UI
|
||||
doCreatePushMirrorWithBranchFilter(ctx, remoteAddress2, user.LowerName, userPassword, "")(t)
|
||||
|
||||
// Verify via API that branch filter is empty
|
||||
req := NewRequest(t, "GET", urlStr).AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
var pushMirrors []*api.PushMirror
|
||||
DecodeJSON(t, resp, &pushMirrors)
|
||||
|
||||
require.Len(t, pushMirrors, 1)
|
||||
assert.Empty(t, pushMirrors[0].BranchFilter, "Empty branch filter set via web UI should appear as empty in API response")
|
||||
assert.Equal(t, remoteAddress2, pushMirrors[0].RemoteAddress)
|
||||
|
||||
// Cleanup
|
||||
req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s", urlStr, pushMirrors[0].RemoteName)).AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
})
|
||||
|
||||
t.Run("Set complex branch filter via web UI and verify in API response", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
// Create another mirror repo for this test
|
||||
mirrorRepo3, _, f := tests.CreateDeclarativeRepo(t, user, "", []unit.Type{unit.TypeCode}, nil, nil)
|
||||
defer f()
|
||||
remoteAddress3 := fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(user.Name), url.PathEscape(mirrorRepo3.Name))
|
||||
|
||||
// Create push mirror with complex branch filter via web UI
|
||||
complexFilter := "main,release/v*,hotfix-*,feature-auth,feature-ui"
|
||||
doCreatePushMirrorWithBranchFilter(ctx, remoteAddress3, user.LowerName, userPassword, complexFilter)(t)
|
||||
|
||||
// Verify via API that complex branch filter is preserved
|
||||
req := NewRequest(t, "GET", urlStr).AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
var pushMirrors []*api.PushMirror
|
||||
DecodeJSON(t, resp, &pushMirrors)
|
||||
|
||||
require.Len(t, pushMirrors, 1)
|
||||
assert.Equal(t, complexFilter, pushMirrors[0].BranchFilter, "Complex branch filter set via web UI should be preserved in API response")
|
||||
assert.Equal(t, remoteAddress3, pushMirrors[0].RemoteAddress)
|
||||
|
||||
// Cleanup
|
||||
req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s", urlStr, pushMirrors[0].RemoteName)).AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
})
|
||||
|
||||
t.Run("Update branch filter via API and verify in web UI", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
// Create another mirror repo for this test
|
||||
mirrorRepo4, _, f := tests.CreateDeclarativeRepo(t, user, "", []unit.Type{unit.TypeCode}, nil, nil)
|
||||
defer f()
|
||||
remoteAddress4 := fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(user.Name), url.PathEscape(mirrorRepo4.Name))
|
||||
|
||||
// First create a push mirror via API with initial branch filter
|
||||
initialFilter := "main"
|
||||
req := NewRequestWithJSON(t, "POST", urlStr, &api.CreatePushMirrorOption{
|
||||
RemoteAddress: remoteAddress4,
|
||||
Interval: "8h",
|
||||
BranchFilter: initialFilter,
|
||||
}).AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
// Get the created mirror info
|
||||
req = NewRequest(t, "GET", urlStr).AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
var pushMirrors []*api.PushMirror
|
||||
DecodeJSON(t, resp, &pushMirrors)
|
||||
require.Len(t, pushMirrors, 1)
|
||||
assert.Equal(t, initialFilter, pushMirrors[0].BranchFilter)
|
||||
mirrorRemoteName := pushMirrors[0].RemoteName
|
||||
|
||||
// Get the actual mirror from database to get the ID
|
||||
dbMirrors, _, err := repo_model.GetPushMirrorsByRepoID(db.DefaultContext, srcRepo.ID, db.ListOptions{})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, dbMirrors, 1)
|
||||
|
||||
// Update branch filter via web form (using existing repo settings endpoint)
|
||||
updatedFilter := "main,develop,feature-*"
|
||||
req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings", url.PathEscape(user.Name), url.PathEscape(srcRepo.Name)), map[string]string{
|
||||
"_csrf": GetCSRF(t, session, fmt.Sprintf("/%s/%s/settings", url.PathEscape(user.Name), url.PathEscape(srcRepo.Name))),
|
||||
"action": "push-mirror-update",
|
||||
"push_mirror_id": fmt.Sprintf("%d", dbMirrors[0].ID),
|
||||
"push_mirror_interval": "8h",
|
||||
"push_mirror_branch_filter": updatedFilter,
|
||||
})
|
||||
session.MakeRequest(t, req, http.StatusSeeOther)
|
||||
|
||||
// Verify the branch filter was updated via API
|
||||
req = NewRequest(t, "GET", urlStr).AddTokenAuth(token)
|
||||
resp = MakeRequest(t, req, http.StatusOK)
|
||||
DecodeJSON(t, resp, &pushMirrors)
|
||||
require.Len(t, pushMirrors, 1)
|
||||
assert.Equal(t, updatedFilter, pushMirrors[0].BranchFilter, "Branch filter should be updated via web form")
|
||||
|
||||
// Verify the branch filter is visible in the web UI settings page
|
||||
req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s/settings", url.PathEscape(user.Name), url.PathEscape(srcRepo.Name)))
|
||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
editButton := htmlDoc.Find(fmt.Sprintf(`button[data-modal-push-mirror-edit-address="%s"]`, remoteAddress4))
|
||||
require.Equal(t, 1, editButton.Length(), "Should find exactly one edit button for the updated mirror")
|
||||
|
||||
branchFilterAttr, exists := editButton.Attr("data-modal-push-mirror-edit-branch-filter")
|
||||
require.True(t, exists, "Edit button should have branch filter data attribute")
|
||||
assert.Equal(t, updatedFilter, branchFilterAttr, "Branch filter data attribute should match the updated value")
|
||||
|
||||
// Cleanup
|
||||
req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s", urlStr, mirrorRemoteName)).AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusNoContent)
|
||||
})
|
||||
|
||||
t.Run("Multiple mirrors with different branch filters - UI to API consistency", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
// Create multiple mirror repos
|
||||
testCases := []struct {
|
||||
name string
|
||||
branchFilter string
|
||||
}{
|
||||
{"multi-test-1", "main,develop"},
|
||||
{"multi-test-2", "feature-*,hotfix-*"},
|
||||
{"multi-test-3", ""},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
remoteAddress := fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(user.Name), url.PathEscape(tc.name))
|
||||
req := NewRequestWithJSON(t, "POST", urlStr, &api.CreatePushMirrorOption{
|
||||
RemoteAddress: remoteAddress,
|
||||
Interval: "8h",
|
||||
BranchFilter: tc.branchFilter,
|
||||
}).AddTokenAuth(token)
|
||||
MakeRequest(t, req, http.StatusOK)
|
||||
}
|
||||
|
||||
// Verify all mirrors and their branch filters via API
|
||||
req := NewRequest(t, "GET", urlStr).AddTokenAuth(token)
|
||||
resp := MakeRequest(t, req, http.StatusOK)
|
||||
var pushMirrors []*api.PushMirror
|
||||
DecodeJSON(t, resp, &pushMirrors)
|
||||
require.Len(t, pushMirrors, 3)
|
||||
|
||||
// Create a map for easier verification
|
||||
filterMap := make(map[string]string)
|
||||
for _, mirror := range pushMirrors {
|
||||
for _, tc := range testCases {
|
||||
if strings.Contains(mirror.RemoteAddress, tc.name) {
|
||||
filterMap[tc.name] = mirror.BranchFilter
|
||||
// createdMirrors = append(createdMirrors, mirror.RemoteName)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Verify each branch filter is correctly preserved
|
||||
assert.Equal(t, "main,develop", filterMap["multi-test-1"], "First mirror branch filter should match")
|
||||
assert.Equal(t, "feature-*,hotfix-*", filterMap["multi-test-2"], "Second mirror branch filter should match")
|
||||
assert.Empty(t, filterMap["multi-test-3"], "Third mirror branch filter should be empty")
|
||||
})
|
||||
|
||||
t.Run("Verify branch filter field exists in web UI form", func(t *testing.T) {
|
||||
defer tests.PrintCurrentTest(t)()
|
||||
|
||||
// Access the push mirror settings page
|
||||
req := NewRequest(t, "GET", fmt.Sprintf("/%s/%s/settings", url.PathEscape(user.Name), url.PathEscape(srcRepo.Name)))
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
|
||||
htmlDoc.AssertElement(t, "#push_mirror_branch_filter", true)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue