Skip to content

Commit ce3dd04

Browse files
lunnyzeripath
andauthored
Fix some mirror bugs (#18649)
* Fix some mirror bugs * Remove unnecessary code * Fix lint * rename stdard url * Allow more charactors in git ssh protocol url * improve the detection * support ipv6 for git url parse * Fix bug * Fix template * Fix bug * fix template * Fix tmpl * Fix tmpl * Fix parse ssh with interface * Rename functions name Co-authored-by: zeripath <[email protected]>
1 parent 88f2e45 commit ce3dd04

File tree

10 files changed

+314
-32
lines changed

10 files changed

+314
-32
lines changed

models/repo/mirror.go

-6
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,6 @@ import (
1919
// ErrMirrorNotExist mirror does not exist error
2020
var ErrMirrorNotExist = errors.New("Mirror does not exist")
2121

22-
// RemoteMirrorer defines base methods for pull/push mirrors.
23-
type RemoteMirrorer interface {
24-
GetRepository() *Repository
25-
GetRemoteName() string
26-
}
27-
2822
// Mirror represents mirror information of a repository.
2923
type Mirror struct {
3024
ID int64 `xorm:"pk autoincr"`

modules/git/remote.go

+15-5
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ package git
66

77
import (
88
"context"
9-
"net/url"
9+
10+
giturl "code.gitea.io/gitea/modules/git/url"
1011
)
1112

12-
// GetRemoteAddress returns the url of a specific remote of the repository.
13-
func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (*url.URL, error) {
13+
// GetRemoteAddress returns remote url of git repository in the repoPath with special remote name
14+
func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (string, error) {
1415
var cmd *Command
1516
if CheckGitVersionAtLeast("2.7") == nil {
1617
cmd = NewCommand(ctx, "remote", "get-url", remoteName)
@@ -20,11 +21,20 @@ func GetRemoteAddress(ctx context.Context, repoPath, remoteName string) (*url.UR
2021

2122
result, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
2223
if err != nil {
23-
return nil, err
24+
return "", err
2425
}
2526

2627
if len(result) > 0 {
2728
result = result[:len(result)-1]
2829
}
29-
return url.Parse(result)
30+
return result, nil
31+
}
32+
33+
// GetRemoteURL returns the url of a specific remote of the repository.
34+
func GetRemoteURL(ctx context.Context, repoPath, remoteName string) (*giturl.GitURL, error) {
35+
addr, err := GetRemoteAddress(ctx, repoPath, remoteName)
36+
if err != nil {
37+
return nil, err
38+
}
39+
return giturl.Parse(addr)
3040
}

modules/git/url/url.go

+90
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Copyright 2022 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package url
6+
7+
import (
8+
"fmt"
9+
stdurl "net/url"
10+
"strings"
11+
)
12+
13+
// ErrWrongURLFormat represents an error with wrong url format
14+
type ErrWrongURLFormat struct {
15+
URL string
16+
}
17+
18+
func (err ErrWrongURLFormat) Error() string {
19+
return fmt.Sprintf("git URL %s format is wrong", err.URL)
20+
}
21+
22+
// GitURL represents a git URL
23+
type GitURL struct {
24+
*stdurl.URL
25+
extraMark int // 0 no extra 1 scp 2 file path with no prefix
26+
}
27+
28+
// String returns the URL's string
29+
func (u *GitURL) String() string {
30+
switch u.extraMark {
31+
case 0:
32+
return u.URL.String()
33+
case 1:
34+
return fmt.Sprintf("%s@%s:%s", u.User.Username(), u.Host, u.Path)
35+
case 2:
36+
return u.Path
37+
default:
38+
return ""
39+
}
40+
}
41+
42+
// Parse parse all kinds of git URL
43+
func Parse(remote string) (*GitURL, error) {
44+
if strings.Contains(remote, "://") {
45+
u, err := stdurl.Parse(remote)
46+
if err != nil {
47+
return nil, err
48+
}
49+
return &GitURL{URL: u}, nil
50+
} else if strings.Contains(remote, "@") && strings.Contains(remote, ":") {
51+
url := stdurl.URL{
52+
Scheme: "ssh",
53+
}
54+
squareBrackets := false
55+
lastIndex := -1
56+
FOR:
57+
for i := 0; i < len(remote); i++ {
58+
switch remote[i] {
59+
case '@':
60+
url.User = stdurl.User(remote[:i])
61+
lastIndex = i + 1
62+
case ':':
63+
if !squareBrackets {
64+
url.Host = strings.ReplaceAll(remote[lastIndex:i], "%25", "%")
65+
if len(remote) <= i+1 {
66+
return nil, ErrWrongURLFormat{URL: remote}
67+
}
68+
url.Path = remote[i+1:]
69+
break FOR
70+
}
71+
case '[':
72+
squareBrackets = true
73+
case ']':
74+
squareBrackets = false
75+
}
76+
}
77+
return &GitURL{
78+
URL: &url,
79+
extraMark: 1,
80+
}, nil
81+
}
82+
83+
return &GitURL{
84+
URL: &stdurl.URL{
85+
Scheme: "file",
86+
Path: remote,
87+
},
88+
extraMark: 2,
89+
}, nil
90+
}

modules/git/url/url_test.go

+167
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
// Copyright 2022 The Gitea Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package url
6+
7+
import (
8+
"net/url"
9+
"testing"
10+
11+
"github.com/stretchr/testify/assert"
12+
)
13+
14+
func TestParseGitURLs(t *testing.T) {
15+
kases := []struct {
16+
kase string
17+
expected *GitURL
18+
}{
19+
{
20+
kase: "[email protected]:go-gitea/gitea.git",
21+
expected: &GitURL{
22+
URL: &url.URL{
23+
Scheme: "ssh",
24+
User: url.User("git"),
25+
Host: "127.0.0.1",
26+
Path: "go-gitea/gitea.git",
27+
},
28+
extraMark: 1,
29+
},
30+
},
31+
{
32+
kase: "git@[fe80:14fc:cec5:c174:d88%2510]:go-gitea/gitea.git",
33+
expected: &GitURL{
34+
URL: &url.URL{
35+
Scheme: "ssh",
36+
User: url.User("git"),
37+
Host: "[fe80:14fc:cec5:c174:d88%10]",
38+
Path: "go-gitea/gitea.git",
39+
},
40+
extraMark: 1,
41+
},
42+
},
43+
{
44+
kase: "git@[::1]:go-gitea/gitea.git",
45+
expected: &GitURL{
46+
URL: &url.URL{
47+
Scheme: "ssh",
48+
User: url.User("git"),
49+
Host: "[::1]",
50+
Path: "go-gitea/gitea.git",
51+
},
52+
extraMark: 1,
53+
},
54+
},
55+
{
56+
kase: "[email protected]:go-gitea/gitea.git",
57+
expected: &GitURL{
58+
URL: &url.URL{
59+
Scheme: "ssh",
60+
User: url.User("git"),
61+
Host: "github.com",
62+
Path: "go-gitea/gitea.git",
63+
},
64+
extraMark: 1,
65+
},
66+
},
67+
{
68+
kase: "ssh://[email protected]/go-gitea/gitea.git",
69+
expected: &GitURL{
70+
URL: &url.URL{
71+
Scheme: "ssh",
72+
User: url.User("git"),
73+
Host: "github.com",
74+
Path: "/go-gitea/gitea.git",
75+
},
76+
extraMark: 0,
77+
},
78+
},
79+
{
80+
kase: "ssh://git@[::1]/go-gitea/gitea.git",
81+
expected: &GitURL{
82+
URL: &url.URL{
83+
Scheme: "ssh",
84+
User: url.User("git"),
85+
Host: "[::1]",
86+
Path: "/go-gitea/gitea.git",
87+
},
88+
extraMark: 0,
89+
},
90+
},
91+
{
92+
kase: "/repositories/go-gitea/gitea.git",
93+
expected: &GitURL{
94+
URL: &url.URL{
95+
Scheme: "file",
96+
Path: "/repositories/go-gitea/gitea.git",
97+
},
98+
extraMark: 2,
99+
},
100+
},
101+
{
102+
kase: "file:///repositories/go-gitea/gitea.git",
103+
expected: &GitURL{
104+
URL: &url.URL{
105+
Scheme: "file",
106+
Path: "/repositories/go-gitea/gitea.git",
107+
},
108+
extraMark: 0,
109+
},
110+
},
111+
{
112+
kase: "https://github.com/go-gitea/gitea.git",
113+
expected: &GitURL{
114+
URL: &url.URL{
115+
Scheme: "https",
116+
Host: "github.com",
117+
Path: "/go-gitea/gitea.git",
118+
},
119+
extraMark: 0,
120+
},
121+
},
122+
{
123+
kase: "https://git:[email protected]/go-gitea/gitea.git",
124+
expected: &GitURL{
125+
URL: &url.URL{
126+
Scheme: "https",
127+
Host: "github.com",
128+
User: url.UserPassword("git", "git"),
129+
Path: "/go-gitea/gitea.git",
130+
},
131+
extraMark: 0,
132+
},
133+
},
134+
{
135+
kase: "https://[fe80:14fc:cec5:c174:d88%2510]:20/go-gitea/gitea.git",
136+
expected: &GitURL{
137+
URL: &url.URL{
138+
Scheme: "https",
139+
Host: "[fe80:14fc:cec5:c174:d88%10]:20",
140+
Path: "/go-gitea/gitea.git",
141+
},
142+
extraMark: 0,
143+
},
144+
},
145+
146+
{
147+
kase: "git://github.com/go-gitea/gitea.git",
148+
expected: &GitURL{
149+
URL: &url.URL{
150+
Scheme: "git",
151+
Host: "github.com",
152+
Path: "/go-gitea/gitea.git",
153+
},
154+
extraMark: 0,
155+
},
156+
},
157+
}
158+
159+
for _, kase := range kases {
160+
t.Run(kase.kase, func(t *testing.T) {
161+
u, err := Parse(kase.kase)
162+
assert.NoError(t, err)
163+
assert.EqualValues(t, kase.expected.extraMark, u.extraMark)
164+
assert.EqualValues(t, *kase.expected, *u)
165+
})
166+
}
167+
}

modules/templates/helper.go

+23-7
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"code.gitea.io/gitea/modules/base"
3333
"code.gitea.io/gitea/modules/emoji"
3434
"code.gitea.io/gitea/modules/git"
35+
giturl "code.gitea.io/gitea/modules/git/url"
3536
"code.gitea.io/gitea/modules/json"
3637
"code.gitea.io/gitea/modules/log"
3738
"code.gitea.io/gitea/modules/markup"
@@ -971,20 +972,35 @@ type remoteAddress struct {
971972
Password string
972973
}
973974

974-
func mirrorRemoteAddress(ctx context.Context, m repo_model.RemoteMirrorer) remoteAddress {
975+
func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string) remoteAddress {
975976
a := remoteAddress{}
977+
if !m.IsMirror {
978+
return a
979+
}
980+
981+
remoteURL := m.OriginalURL
982+
if remoteURL == "" {
983+
var err error
984+
remoteURL, err = git.GetRemoteAddress(ctx, m.RepoPath(), remoteName)
985+
if err != nil {
986+
log.Error("GetRemoteURL %v", err)
987+
return a
988+
}
989+
}
976990

977-
u, err := git.GetRemoteAddress(ctx, m.GetRepository().RepoPath(), m.GetRemoteName())
991+
u, err := giturl.Parse(remoteURL)
978992
if err != nil {
979-
log.Error("GetRemoteAddress %v", err)
993+
log.Error("giturl.Parse %v", err)
980994
return a
981995
}
982996

983-
if u.User != nil {
984-
a.Username = u.User.Username()
985-
a.Password, _ = u.User.Password()
997+
if u.Scheme != "ssh" && u.Scheme != "file" {
998+
if u.User != nil {
999+
a.Username = u.User.Username()
1000+
a.Password, _ = u.User.Password()
1001+
}
1002+
u.User = nil
9861003
}
987-
u.User = nil
9881004
a.Address = u.String()
9891005

9901006
return a

routers/web/repo/setting.go

+8-6
Original file line numberDiff line numberDiff line change
@@ -215,22 +215,24 @@ func SettingsPost(ctx *context.Context) {
215215
return
216216
}
217217

218-
u, _ := git.GetRemoteAddress(ctx, ctx.Repo.Repository.RepoPath(), ctx.Repo.Mirror.GetRemoteName())
218+
u, err := git.GetRemoteURL(ctx, ctx.Repo.Repository.RepoPath(), ctx.Repo.Mirror.GetRemoteName())
219+
if err != nil {
220+
ctx.Data["Err_MirrorAddress"] = true
221+
handleSettingRemoteAddrError(ctx, err, form)
222+
return
223+
}
219224
if u.User != nil && form.MirrorPassword == "" && form.MirrorUsername == u.User.Username() {
220225
form.MirrorPassword, _ = u.User.Password()
221226
}
222227

223-
address, err := forms.ParseRemoteAddr(form.MirrorAddress, form.MirrorUsername, form.MirrorPassword)
224-
if err == nil {
225-
err = migrations.IsMigrateURLAllowed(address, ctx.Doer)
226-
}
228+
err = migrations.IsMigrateURLAllowed(u.String(), ctx.Doer)
227229
if err != nil {
228230
ctx.Data["Err_MirrorAddress"] = true
229231
handleSettingRemoteAddrError(ctx, err, form)
230232
return
231233
}
232234

233-
if err := mirror_service.UpdateAddress(ctx, ctx.Repo.Mirror, address); err != nil {
235+
if err := mirror_service.UpdateAddress(ctx, ctx.Repo.Mirror, u.String()); err != nil {
234236
ctx.ServerError("UpdateAddress", err)
235237
return
236238
}

0 commit comments

Comments
 (0)