Skip to content

Conversation

@jimmy201602
Copy link

@jimmy201602 jimmy201602 commented Mar 22, 2022

add safe pull mirror option to avoid original repository delete

close #14076

@lunny lunny added the type/enhancement An improvement of existing functionality label Mar 22, 2022
@lunny lunny added this to the 1.17.0 milestone Mar 22, 2022
can_write_info=写入
key_state_desc=7 天内使用过该密钥
token_state_desc=7 天内使用过该密钥
key_state_desc=7 天内使用过该密钥
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please revert changes to translation file

@GiteaBot GiteaBot added the lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. label Mar 22, 2022
@lafriks
Copy link
Member

lafriks commented Mar 22, 2022

Also need to add database migration to add Sync changed struct with database table structure


// v211 -> v212
NewMigration("Create ForeignReference table", createForeignReferenceTable),

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change


if safeMirror {
// detect can safe mirror
canUpdate, err := detectCanUpdateMirror(ctx, m, gitArgs)
Copy link
Member

@6543 6543 Mar 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will be resource intense on huge repos .. e.g. Linux kernel

  • git binary do not provide a option to filter the update ... :/

mirror_password_blank_placeholder = (Unset)
mirror_password_help = Change the username to erase a stored password.
mirror_enable_safe = Safe Mirror
mirror_enable_safe_desc = Enable safe mirror to avoid delete local repository data
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
mirror_enable_safe_desc = Enable safe mirror to avoid delete local repository data
mirror_enable_safe_desc = Enable safe mirroring to avoid deleting data from the local repository

Comment on lines 74 to 79
mirrors, err := repo_model.GetMirrorByRepoID(ctx.Repo.Repository.ID)
if err != nil {
ctx.Data["EnableSafeMirror"] = false
} else {
ctx.Data["EnableSafeMirror"] = mirrors.EnableSafeMirror
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
mirrors, err := repo_model.GetMirrorByRepoID(ctx.Repo.Repository.ID)
if err != nil {
ctx.Data["EnableSafeMirror"] = false
} else {
ctx.Data["EnableSafeMirror"] = mirrors.EnableSafeMirror
}
mirror, err := repo_model.GetMirrorByRepoID(ctx.Repo.Repository.ID)
if err != nil {
ctx.Data["EnableSafeMirror"] = false
} else {
ctx.Data["EnableSafeMirror"] = mirror.EnableSafeMirror
}

Copy link
Contributor

@Gusted Gusted Mar 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we should check if err is ErrMirrorNotExist and allow such error(without returning a 404), otherwise log the error and render a 404 page, because it would then be a database error.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
mirrors, err := repo_model.GetMirrorByRepoID(ctx.Repo.Repository.ID)
if err != nil {
ctx.Data["EnableSafeMirror"] = false
} else {
ctx.Data["EnableSafeMirror"] = mirrors.EnableSafeMirror
}
mirror, err := repo_model.GetMirrorByRepoID(ctx.Repo.Repository.ID)
ctx.Data["EnableSafeMirror"] = err == nil && mirror.EnableSafeMirror

func runSync(ctx context.Context, m *repo_model.Mirror) ([]*mirrorSyncResult, bool) {
repoPath := m.Repo.RepoPath()
wikiPath := m.Repo.WikiPath()
safeMirror := m.EnableSafeMirror
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
safeMirror := m.EnableSafeMirror

log.Error("SyncMirrors [repo: %-v]: GetRemoteAddress Error %v", m.Repo, remoteErr)
}

if safeMirror {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if safeMirror {
if m.EnableSafeMirror {

Comment on lines 210 to 219
canUpdate, err := detectCanUpdateMirror(ctx, m, gitArgs)
newRepoPath := fmt.Sprintf("%s_update", repoPath)
// delete the temp directory
errDelete := util.RemoveAll(newRepoPath)
if errDelete != nil {
log.Error("DeleteRepositoryTempDirectoryError: %v", errDelete)
}
if err != nil {
log.Error("CheckRepositoryCanSafeMirrorError: %v", err)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is the error checked later here? In 100% cases you want to check the error immediately.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, I'd say that every single method call inside these error logs is wrong.

Comment on lines 48 to 49
desc := fmt.Sprintf("Failed to get mirror repository can update '%s': %s", newRepoPath, stderrMessage)
log.Error("GetMirrorCanUpdate [repo: %-v]: failed to get mirror repository can update:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
desc := fmt.Sprintf("Failed to get mirror repository can update '%s': %s", newRepoPath, stderrMessage)
log.Error("GetMirrorCanUpdate [repo: %-v]: failed to get mirror repository can update:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err)
log.Error("GetMirrorCanUpdate [repo: %-v]: failed to get mirror repository can update:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err)
desc := fmt.Sprintf("Failed to get mirror repository can update '%s': %s", newRepoPath, stderrMessage)

More logical to re-order this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
desc := fmt.Sprintf("Failed to get mirror repository can update '%s': %s", newRepoPath, stderrMessage)
log.Error("GetMirrorCanUpdate [repo: %-v]: failed to get mirror repository can update:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err)
log.Error("getGitCommandStdoutStderr [repo: %-v]: failed to check if mirror can be updated:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err)
desc := fmt.Sprintf("Failed to check if mirror '%s' can be updated: %s", newRepoPath, stderrMessage)

log.Error("GetMirrorCanUpdate [repo: %-v]: failed to get mirror repository can update:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err)
if err = admin_model.CreateRepositoryNotice(desc); err != nil {
log.Error("GetMirrorCanUpdateNotice: %v", err)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to return here with a error.

Comment on lines +65 to +67
remoteAddr, remoteErr := git.GetRemoteAddress(ctx, newRepoPath, m.GetRemoteName())
if remoteErr != nil {
log.Error("GetMirrorCanUpdate [repo: %-v]: GetRemoteAddress Error %v", m.Repo, remoteErr)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
remoteAddr, remoteErr := git.GetRemoteAddress(ctx, newRepoPath, m.GetRemoteName())
if remoteErr != nil {
log.Error("GetMirrorCanUpdate [repo: %-v]: GetRemoteAddress Error %v", m.Repo, remoteErr)
remoteAddr, err := git.GetRemoteAddress(ctx, newRepoPath, m.GetRemoteName())
if err != nil {
log.Error("GetMirrorCanUpdate [repo: %-v]: GetRemoteAddress Error %v", m.Repo, err)

}

// detect user can update the mirror
func detectCanUpdateMirror(ctx context.Context, m *repo_model.Mirror, gitArgs []string) (bool, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function needs some more comments in order to not have a 10 minutes hard time understanding how this function is achieving it's "detection method".

Comment on lines +173 to +195
if repoCommitCount > newRepoCommitCount {
return false, nil
} else if repoCommitCount == newRepoCommitCount {
// noting to happen
return true, nil
} else {
// compare commit id
skipcout := newRepoCommitCount - repoCommitCount
gitNewRepoLastCommitIDArgs := []string{"log", "-1", fmt.Sprintf("--skip=%d", skipcout), "--format='%H'"}
stdoutNewRepoCommitID, _, err := getGitCommandStdoutStderr(ctx, m, gitNewRepoLastCommitIDArgs, newRepoPath)
if err != nil {
return false, err
}
gitRepoLastCommitIDArgs := []string{"log", "--format='%H'", "-n", "1"}
stdoutRepoCommitID, _, err := getGitCommandStdoutStderr(ctx, m, gitRepoLastCommitIDArgs, repoPath)
if err != nil {
return false, err
}
if stdoutNewRepoCommitID != stdoutRepoCommitID {
return false, fmt.Errorf("Old repo commit id: %s not match new repo id: %s", stdoutRepoCommitID, stdoutNewRepoCommitID)
}
}
return true, nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for the sake of understanding here, it's more or less trying to detect if a force-push has happen on the upstream's repo?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is the purpose. However, I think copying the repo again and again is not ideal.

A better solution could be: track all branches locally, fetch remote, compare the merge-base, if the merge-base is not the locally tracked one, then there is a force-push.

And there might be other better solutions, but copying the repo is not the proper way.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, I'm also looking for an ideal solution to figure out this sitution.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you seem my option with the reference-transaction hook ? #14076 (comment)
You'll also need an option to allow/disallow force-push on specific branches otherwise mirrors will break quickly (people force-push on PR branches)

"xorm.io/xorm"
)

func addColorColToMirror(x *xorm.Engine) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that method name is not correct anymore?

mirror_password_blank_placeholder = (Unset)
mirror_password_help = Change the username to erase a stored password.
mirror_enable_safe = Safe Mirror
mirror_enable_safe_desc = Enable safe mirror to avoid delete local repository data
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
mirror_enable_safe_desc = Enable safe mirror to avoid delete local repository data
mirror_enable_safe_desc = Enable safe mirroring to avoid deleting local data such as branches or tags once they are deleted remotely

NewMigration("Create ForeignReference table", createForeignReferenceTable),

// v212 -> v213
NewMigration("Add new safe mirror option", addColorColToMirror),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
NewMigration("Add new safe mirror option", addColorColToMirror),
NewMigration("Add safe pull mirrors", addColorColToMirror),

Comment on lines +24 to +25
stdoutBuilder := strings.Builder{}
stderrBuilder := strings.Builder{}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment on lines 74 to 79
mirrors, err := repo_model.GetMirrorByRepoID(ctx.Repo.Repository.ID)
if err != nil {
ctx.Data["EnableSafeMirror"] = false
} else {
ctx.Data["EnableSafeMirror"] = mirrors.EnableSafeMirror
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
mirrors, err := repo_model.GetMirrorByRepoID(ctx.Repo.Repository.ID)
if err != nil {
ctx.Data["EnableSafeMirror"] = false
} else {
ctx.Data["EnableSafeMirror"] = mirrors.EnableSafeMirror
}
mirror, err := repo_model.GetMirrorByRepoID(ctx.Repo.Repository.ID)
ctx.Data["EnableSafeMirror"] = err == nil && mirror.EnableSafeMirror

}
// can not safe mirror
if !canUpdate {
log.Error("CheckSyncMirrors [repo: %-v]: can not safe mirror...", m.Repo)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
log.Error("CheckSyncMirrors [repo: %-v]: can not safe mirror...", m.Repo)
log.Error("CheckSyncMirrors [repo: %-v]: cannot sync safe mirror...", m.Repo)

Comment on lines 211 to 213
newRepoPath := fmt.Sprintf("%s_update", repoPath)
// delete the temp directory
errDelete := util.RemoveAll(newRepoPath)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What temp directory?
As that wasn't present previously, I'd assume that it is constructed within detectCanUpdateMirror.
In that case I'd recommend a defer inside the method instead.

)

// get git command running stdout and stderr
func getGitCommandStdoutStderr(ctx context.Context, m *repo_model.Mirror, gitArgs []string, newRepoPath string) (string, string, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And also a better comment, please.
The comment does not help to understand what this function is doing.
Also, I just noticed that the comment does not start with the obligatory method name
(// getGitCommandStdoutStderr ...)

desc := fmt.Sprintf("Failed to get mirror repository can update '%s': %s", newRepoPath, stderrMessage)
log.Error("GetMirrorCanUpdate [repo: %-v]: failed to get mirror repository can update:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err)
if err = admin_model.CreateRepositoryNotice(desc); err != nil {
log.Error("GetMirrorCanUpdateNotice: %v", err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
log.Error("GetMirrorCanUpdateNotice: %v", err)
log.Error("CreateRepositoryNotice: %v", err)

Comment on lines 48 to 49
desc := fmt.Sprintf("Failed to get mirror repository can update '%s': %s", newRepoPath, stderrMessage)
log.Error("GetMirrorCanUpdate [repo: %-v]: failed to get mirror repository can update:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
desc := fmt.Sprintf("Failed to get mirror repository can update '%s': %s", newRepoPath, stderrMessage)
log.Error("GetMirrorCanUpdate [repo: %-v]: failed to get mirror repository can update:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err)
log.Error("getGitCommandStdoutStderr [repo: %-v]: failed to check if mirror can be updated:\nStdout: %s\nStderr: %s\nErr: %v", m.Repo, stdoutMessage, stderrMessage, err)
desc := fmt.Sprintf("Failed to check if mirror '%s' can be updated: %s", newRepoPath, stderrMessage)

@wxiaoguang wxiaoguang changed the title add safe pull mirror option to avoid original repository delete Add safe pull mirror option to avoid original repository deletion by force-push Mar 24, 2022
@jimmy201602
Copy link
Author

jimmy201602 commented Mar 24, 2022

I will fix these errors later.

@lunny
Copy link
Member

lunny commented Jun 3, 2022

Please resolve the conflicts.

@kotovalexarian
Copy link

What is the state of this feature?

I think that this would be better as part of branch protection, not as a setting of the whole repo. It is not very useful for all branches (such as feature branches).

@lunny lunny modified the milestones: 1.18.0, 1.19.0 Oct 17, 2022
@lunny lunny removed this from the 1.19.0 milestone Feb 1, 2023
@wxiaoguang
Copy link
Contributor

It seems that this PR got stale after first round review. And the approach has some problems, eg: err := util.CopyDir(repoPath, newRepoPath), copying large repositories again and again would cause performance problems.

@wxiaoguang wxiaoguang added the issue/needs-feedback For bugs, we need more details. For features, the feature must be described in more detail label Apr 24, 2023
@wxiaoguang
Copy link
Contributor

It has been stale for a long time and I can't think of a way to handling it other than closing it. Feel free to reopen if there's any new progress and I could also help.

@wxiaoguang wxiaoguang closed this May 1, 2023
@go-gitea go-gitea locked as resolved and limited conversation to collaborators Aug 1, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

issue/needs-feedback For bugs, we need more details. For features, the feature must be described in more detail lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. type/enhancement An improvement of existing functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow "safe" mirroring