fix(api): remove git remote when deleting a push mirror (#40049)

Fixes https://github.com/go-gitea/gitea/issues/40037

`DELETE /repos/{owner}/{repo}/push_mirrors/{name}` only deleted the
`push_mirror` row and left the `remote_mirror_*` git remotes, which
embed the mirror credentials, in the repository and wiki config. Each
create/delete cycle through the API left more credentials behind.

- Remove the git remotes before deleting the row and record it in the
audit log, like the web settings handler.
- Return 404 for an unknown name, as documented, instead of 204.
- Ignore an already missing code remote, so the wiki remote is still
removed and such mirrors can be deleted through both the API and the web
settings.

---

AI disclosure: I used an AI coding assistant to help write this change
and the PR description. I reviewed the code and ran the tests myself.

---------

Signed-off-by: sarankumar1325 <120080287+sarankumar1325@users.noreply.github.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: wxiaoguang <wxiaoguang@gmail.com>
This commit is contained in:
sarankumar
2026-10-31 23:17:36 +05:30
committed by GitHub
parent 4ed6f89b60
commit a4d1711777
6 changed files with 116 additions and 113 deletions

View File

@@ -11,6 +11,7 @@ import (
"regexp"
"time"
audit_model "gitea.dev/models/audit"
"gitea.dev/models/db"
repo_model "gitea.dev/models/repo"
"gitea.dev/modules/git"
@@ -22,6 +23,7 @@ import (
"gitea.dev/modules/setting"
"gitea.dev/modules/timeutil"
"gitea.dev/modules/util"
"gitea.dev/services/audit"
"gitea.dev/services/migrations"
repo_service "gitea.dev/services/repository"
)
@@ -29,7 +31,14 @@ import (
var stripExitStatus = regexp.MustCompile(`exit status \d+ - `)
// AddPushMirrorRemote registers the push mirror remote.
func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr string) error {
func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr string) (retErr error) {
defer func() {
if retErr != nil {
if err := repo_model.DeleteRepoPushMirrorByID(ctx, m.RepoID, m.ID); err != nil {
log.Error("DeletePushMirrorByID %v", err)
}
}
}()
addRemoteAndConfig := func(storageRepo git.RepositoryFacade, addr string) error {
if err := git.ManagedRemoteAdd(ctx, storageRepo, m.RemoteName, addr, git.RemoteOptionMirrorPush); err != nil {
return err
@@ -59,7 +68,7 @@ func AddPushMirrorRemote(ctx context.Context, m *repo_model.PushMirror, addr str
// RemovePushMirrorRemote removes the push mirror remote.
func RemovePushMirrorRemote(ctx context.Context, m *repo_model.PushMirror) error {
_ = m.GetRepository(ctx)
if err := git.ManagedRemoteRemove(ctx, m.Repo, m.RemoteName); err != nil {
if err := git.ManagedRemoteRemove(ctx, m.Repo, m.RemoteName); err != nil && !git.IsRemoteNotExistError(err) {
return err
}
@@ -73,6 +82,25 @@ func RemovePushMirrorRemote(ctx context.Context, m *repo_model.PushMirror) error
return nil
}
// DeleteRepoPushMirrorByID removes the push mirror's git remotes, deletes the push mirror record
// and records the audit event. The web UI and the API share it.
func DeleteRepoPushMirrorByID(ctx context.Context, repoID, pushMirrorID int64) error {
pushMirror, err := repo_model.GetPushMirrorByRepoID(ctx, repoID, pushMirrorID)
if err != nil {
return err
}
if err := RemovePushMirrorRemote(ctx, pushMirror); err != nil {
return fmt.Errorf("RemovePushMirrorRemote: %w", err)
}
if err := repo_model.DeleteRepoPushMirrorByID(ctx, pushMirror.RepoID, pushMirror.ID); err != nil {
return fmt.Errorf("DeletePushMirrors: %w", err)
}
audit.Record(ctx, audit_model.RepositoryMirrorPushRemove, pushMirror.GetRepository(ctx), "mirror_id", pushMirror.ID, "remote_address", pushMirror.RemoteAddress)
return nil
}
// SyncPushMirror starts the sync of the push mirror and schedules the next run.
func SyncPushMirror(ctx context.Context, mirrorID int64) bool {
log.Trace("SyncPushMirror [mirror: %d]", mirrorID)