mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-12-27 13:39:19 -05:00
ff00749191
This adds a new `doctor` check: `fix-push-mirrors-without-git-remote`. The new check looks for push mirrors that do not have their remotes configured in git. If automatic fixing is enabled, it will remove these push mirrors from the database. The check is not run by default, and thus, must be invoked manually. It should be usable in a half-migrated state, too, and as such, fixes #1800. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/1853 Co-authored-by: Gergely Nagy <forgejo@gergo.csillger.hu> Co-committed-by: Gergely Nagy <forgejo@gergo.csillger.hu> (cherry picked from commit9038e07ef3
) (cherry picked from commitb15bafcbc7
) (cherry picked from commit93ba05a2dd
) (cherry picked from commite418ea8082
) (cherry picked from commit321790a91e
) (cherry picked from commitf4e19d3323
) (cherry picked from commit4d9923dee8
) (cherry picked from commit049df69eda
) Conflicts: services/doctor/push_mirror_consistency.go https://codeberg.org/forgejo/forgejo/pulls/2214 (cherry picked from commitc79cba8d55
) (cherry picked from commitf3a3969c02
)
147 lines
5.6 KiB
Go
147 lines
5.6 KiB
Go
// Copyright 2021 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package integration
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"code.gitea.io/gitea/models/db"
|
|
repo_model "code.gitea.io/gitea/models/repo"
|
|
"code.gitea.io/gitea/models/unittest"
|
|
user_model "code.gitea.io/gitea/models/user"
|
|
gitea_context "code.gitea.io/gitea/modules/context"
|
|
"code.gitea.io/gitea/modules/git"
|
|
"code.gitea.io/gitea/modules/gitrepo"
|
|
"code.gitea.io/gitea/modules/setting"
|
|
doctor "code.gitea.io/gitea/services/doctor"
|
|
"code.gitea.io/gitea/services/migrations"
|
|
mirror_service "code.gitea.io/gitea/services/mirror"
|
|
repo_service "code.gitea.io/gitea/services/repository"
|
|
"code.gitea.io/gitea/tests"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestMirrorPush(t *testing.T) {
|
|
onGiteaRun(t, testMirrorPush)
|
|
}
|
|
|
|
func testMirrorPush(t *testing.T, u *url.URL) {
|
|
defer tests.PrepareTestEnv(t)()
|
|
|
|
setting.Migrations.AllowLocalNetworks = true
|
|
assert.NoError(t, migrations.Init())
|
|
|
|
user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
|
srcRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1})
|
|
|
|
mirrorRepo, err := repo_service.CreateRepositoryDirectly(db.DefaultContext, user, user, repo_service.CreateRepoOptions{
|
|
Name: "test-push-mirror",
|
|
})
|
|
assert.NoError(t, err)
|
|
|
|
ctx := NewAPITestContext(t, user.LowerName, srcRepo.Name)
|
|
|
|
doCreatePushMirror(ctx, fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(ctx.Username), url.PathEscape(mirrorRepo.Name)), user.LowerName, userPassword)(t)
|
|
doCreatePushMirror(ctx, fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(ctx.Username), url.PathEscape("does-not-matter")), user.LowerName, userPassword)(t)
|
|
|
|
mirrors, _, err := repo_model.GetPushMirrorsByRepoID(db.DefaultContext, srcRepo.ID, db.ListOptions{})
|
|
assert.NoError(t, err)
|
|
assert.Len(t, mirrors, 2)
|
|
|
|
ok := mirror_service.SyncPushMirror(context.Background(), mirrors[0].ID)
|
|
assert.True(t, ok)
|
|
|
|
srcGitRepo, err := gitrepo.OpenRepository(git.DefaultContext, srcRepo)
|
|
assert.NoError(t, err)
|
|
defer srcGitRepo.Close()
|
|
|
|
srcCommit, err := srcGitRepo.GetBranchCommit("master")
|
|
assert.NoError(t, err)
|
|
|
|
mirrorGitRepo, err := gitrepo.OpenRepository(git.DefaultContext, mirrorRepo)
|
|
assert.NoError(t, err)
|
|
defer mirrorGitRepo.Close()
|
|
|
|
mirrorCommit, err := mirrorGitRepo.GetBranchCommit("master")
|
|
assert.NoError(t, err)
|
|
|
|
assert.Equal(t, srcCommit.ID, mirrorCommit.ID)
|
|
|
|
// Test that we can "repair" push mirrors where the remote doesn't exist in git's state.
|
|
// To do that, we artificially remove the remote...
|
|
cmd := git.NewCommand(db.DefaultContext, "remote", "rm").AddDynamicArguments(mirrors[0].RemoteName)
|
|
_, _, err = cmd.RunStdString(&git.RunOpts{Dir: srcRepo.RepoPath()})
|
|
assert.NoError(t, err)
|
|
|
|
// ...then ensure that trying to get its remote address fails
|
|
_, err = repo_model.GetPushMirrorRemoteAddress(srcRepo.OwnerName, srcRepo.Name, mirrors[0].RemoteName)
|
|
assert.Error(t, err)
|
|
|
|
// ...and that we can fix it.
|
|
err = doctor.FixPushMirrorsWithoutGitRemote(db.DefaultContext, nil, true)
|
|
assert.NoError(t, err)
|
|
|
|
// ...and after fixing, we only have one remote
|
|
mirrors, _, err = repo_model.GetPushMirrorsByRepoID(db.DefaultContext, srcRepo.ID, db.ListOptions{})
|
|
assert.NoError(t, err)
|
|
assert.Len(t, mirrors, 1)
|
|
|
|
// ...one we can get the address of, and it's not the one we removed
|
|
remoteAddress, err := repo_model.GetPushMirrorRemoteAddress(srcRepo.OwnerName, srcRepo.Name, mirrors[0].RemoteName)
|
|
assert.NoError(t, err)
|
|
assert.Contains(t, remoteAddress, "does-not-matter")
|
|
|
|
// Cleanup
|
|
doRemovePushMirror(ctx, fmt.Sprintf("%s%s/%s", u.String(), url.PathEscape(ctx.Username), url.PathEscape(mirrorRepo.Name)), user.LowerName, userPassword, int(mirrors[0].ID))(t)
|
|
mirrors, _, err = repo_model.GetPushMirrorsByRepoID(db.DefaultContext, srcRepo.ID, db.ListOptions{})
|
|
assert.NoError(t, err)
|
|
assert.Len(t, mirrors, 0)
|
|
}
|
|
|
|
func doCreatePushMirror(ctx APITestContext, address, username, password 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",
|
|
})
|
|
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)))
|
|
|
|
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-remove",
|
|
"push_mirror_id": strconv.Itoa(pushMirrorID),
|
|
"push_mirror_address": address,
|
|
"push_mirror_username": username,
|
|
"push_mirror_password": password,
|
|
"push_mirror_interval": "0",
|
|
})
|
|
ctx.Session.MakeRequest(t, req, http.StatusSeeOther)
|
|
|
|
flashCookie := ctx.Session.GetCookie(gitea_context.CookieNameFlash)
|
|
assert.NotNil(t, flashCookie)
|
|
assert.Contains(t, flashCookie.Value, "success")
|
|
}
|
|
}
|