Skip to content

Commit c5d91f5

Browse files
authored
Merge pull request #3011 from actiontech/support_git_clone_protocol
Support git clone protocol
2 parents c69a58d + 0fbe0db commit c5d91f5

File tree

3 files changed

+107
-48
lines changed

3 files changed

+107
-48
lines changed

sqle/api/controller/v1/configuration.go

Lines changed: 104 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
package v1
22

33
import (
4+
"context"
45
"crypto/rsa"
56
"fmt"
67
"net/http"
8+
"os"
79
"strconv"
810

11+
"github.com/actiontech/sqle/sqle/errors"
912
"github.com/actiontech/sqle/sqle/utils"
1013
"github.com/go-git/go-git/v5/plumbing"
1114
"github.com/go-git/go-git/v5/plumbing/storer"
@@ -15,6 +18,11 @@ import (
1518

1619
"github.com/actiontech/sqle/sqle/model"
1720

21+
goGit "github.com/go-git/go-git/v5"
22+
"github.com/go-git/go-git/v5/plumbing/transport"
23+
goGitTransport "github.com/go-git/go-git/v5/plumbing/transport/http"
24+
25+
sshTransport "github.com/go-git/go-git/v5/plumbing/transport/ssh"
1826
"github.com/labstack/echo/v4"
1927
"golang.org/x/crypto/ssh"
2028
)
@@ -442,7 +450,7 @@ func testGitConnectionV1(c echo.Context) error {
442450
if err := controller.BindAndValidateReq(c, request); err != nil {
443451
return controller.JSONBaseErrorReq(c, err)
444452
}
445-
repository, _, cleanup, err := utils.CloneGitRepository(c.Request().Context(), request.GitHttpUrl, request.GitUserName, request.GitUserPassword)
453+
repository, _, cleanup, err := CloneGitRepository(c.Request().Context(), request.GitHttpUrl, request.GitUserName, request.GitUserPassword, "")
446454
if err != nil {
447455
return c.JSON(http.StatusOK, &TestGitConnectionResV1{
448456
BaseRes: controller.NewBaseReq(nil),
@@ -469,6 +477,15 @@ func testGitConnectionV1(c echo.Context) error {
469477
})
470478
}
471479
branches, err := getBranches(references)
480+
if err != nil {
481+
return c.JSON(http.StatusOK, &TestGitConnectionResV1{
482+
BaseRes: controller.NewBaseReq(nil),
483+
Data: TestGitConnectionResDataV1{
484+
IsConnectedSuccess: false,
485+
ErrorMessage: err.Error(),
486+
},
487+
})
488+
}
472489
return c.JSON(http.StatusOK, &TestGitConnectionResV1{
473490
BaseRes: controller.NewBaseReq(nil),
474491
Data: TestGitConnectionResDataV1{
@@ -478,6 +495,82 @@ func testGitConnectionV1(c echo.Context) error {
478495
})
479496
}
480497

498+
func CloneGitRepository(ctx context.Context, url, username, password, branch string) (repository *goGit.Repository, directory string, cleanup func() error, err error) {
499+
// 创建一个临时目录用于存放克隆的仓库
500+
directory, err = os.MkdirTemp("./", "git-repo-")
501+
if err != nil {
502+
return nil, "", nil, err
503+
}
504+
// 定义清理函数,用于删除临时目录
505+
cleanup = func() error {
506+
return os.RemoveAll(directory)
507+
}
508+
509+
cloneOpts := &goGit.CloneOptions{
510+
URL: url,
511+
}
512+
// TODO use branch name to clone single branch on the repo
513+
// if branch != "" {
514+
// cloneOpts.ReferenceName = plumbing.ReferenceName(branch)
515+
// }
516+
ep, err := transport.NewEndpoint(url)
517+
if err != nil {
518+
return nil, "", nil, err
519+
}
520+
521+
switch {
522+
case ep.Protocol == "http" || ep.Protocol == "https":
523+
// http协议下:
524+
// 1. 账号密码登录
525+
// username/password
526+
// 2. token 方式
527+
// 对于 GitLab/github,用户名可以是任意非空字符串,建议填oauth2
528+
// gitlab:
529+
// oauth2/token
530+
// github:
531+
// oauth2/token
532+
533+
cloneOpts.Auth = &goGitTransport.BasicAuth{
534+
Username: username,
535+
Password: password,
536+
}
537+
case ep.Protocol == "ssh":
538+
// ssh 协议
539+
// 前置条件:
540+
// 1. 生成密钥
541+
// 2. 查看公钥
542+
// 3. 仓库配置密钥
543+
// 不支持该步骤,用户手动执行
544+
storage := model.GetStorage()
545+
privateKey, exists, err := storage.GetSystemVariableByKey(model.SystemVariableSSHPrimaryKey)
546+
if err != nil {
547+
return nil, directory, cleanup, err
548+
}
549+
if !exists {
550+
return nil, directory, cleanup, errors.New(errors.DataNotExist, fmt.Errorf("git ssh private key not found"))
551+
}
552+
publicKeys, err := sshTransport.NewPublicKeys("git", []byte(privateKey.Value), "")
553+
if err != nil {
554+
return nil, directory, cleanup, fmt.Errorf("failed to load SSH key: %w", err)
555+
}
556+
publicKeys.HostKeyCallback = ssh.InsecureIgnoreHostKey()
557+
cloneOpts.Auth = publicKeys
558+
case ep.Protocol == "git":
559+
// git协议
560+
// 不需要校验权限
561+
// case IsFile TODO
562+
default:
563+
return nil, "", cleanup, errors.New(errors.DataInvalid, fmt.Errorf("url is not a git url"))
564+
}
565+
566+
repository, err = goGit.PlainCloneContext(ctx, directory, false, cloneOpts)
567+
if err != nil {
568+
return nil, directory, cleanup, err
569+
}
570+
571+
return repository, directory, cleanup, nil
572+
}
573+
481574
func getBranches(references storer.ReferenceIter) ([]string, error) {
482575
branches := make([]string, 0)
483576
err := references.ForEach(func(ref *plumbing.Reference) error {
@@ -558,18 +651,24 @@ type SSHPublicKeyInfo struct {
558651
// @Tags configuration
559652
// @Id getSSHPublicKey
560653
// @Security ApiKeyAuth
561-
// @Success 200 {object} v1.SSHPublicKeyInfo
654+
// @Success 200 {object} v1.SSHPublicKeyInfoV1Rsp
562655
// @Router /v1/configurations/ssh_key [get]
563656
func GetSSHPublicKey(c echo.Context) error {
564657
storage := model.GetStorage()
565658
systemVariables, exists, err := storage.GetSystemVariableByKey(model.SystemVariableSSHPrimaryKey)
566659
if err != nil {
567-
c.Logger().Errorf("failed to get ssh public key: %v", err)
568660
return controller.JSONBaseErrorReq(c, err)
569661
}
662+
if !exists {
663+
return c.JSON(http.StatusOK, SSHPublicKeyInfoV1Rsp{
664+
BaseRes: controller.NewBaseReq(nil),
665+
Data: SSHPublicKeyInfo{
666+
PublicKey: "",
667+
},
668+
})
669+
}
570670
privateKey, err := ssh.ParseRawPrivateKey([]byte(systemVariables.Value))
571671
if err != nil {
572-
c.Logger().Errorf("failed to parse SSH private key: %v", err)
573672
return controller.JSONBaseErrorReq(c, err)
574673
}
575674
rsaPrivateKey, ok := privateKey.(*rsa.PrivateKey)
@@ -584,12 +683,7 @@ func GetSSHPublicKey(c echo.Context) error {
584683
return c.JSON(http.StatusOK,
585684
SSHPublicKeyInfoV1Rsp{
586685
Data: SSHPublicKeyInfo{
587-
PublicKey: func() string {
588-
if !exists {
589-
return ""
590-
}
591-
return publicKeyStr
592-
}(),
686+
PublicKey: publicKeyStr,
593687
},
594688
})
595689
}

sqle/api/controller/v1/sql_audit_record.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"encoding/json"
77
e "errors"
88
"fmt"
9-
"github.com/go-git/go-git/v5/plumbing"
109
"io"
1110
"io/fs"
1211
"net/http"
@@ -15,6 +14,8 @@ import (
1514
"strings"
1615
"time"
1716

17+
"github.com/go-git/go-git/v5/plumbing"
18+
1819
javaParser "github.com/actiontech/java-sql-extractor/parser"
1920
xmlParser "github.com/actiontech/mybatis-mapper-2-sql"
2021
"github.com/actiontech/sqle/sqle/api/controller"
@@ -441,7 +442,7 @@ func parseXMLsWithFilePath(xmlContents []xmlParser.XmlFile) ([]SQLFromXML, error
441442
// todo 此处跳过了不支持的编码格式文件
442443
func getSqlsFromGit(c echo.Context) (sqlsFromSQLFiles, sqlsFromJavaFiles []SQLsFromSQLFile, sqlsFromXMLs []SQLFromXML, exist bool, err error) {
443444
// clone from git
444-
repository, directory, cleanup, err := utils.CloneGitRepository(c.Request().Context(), c.FormValue(GitHttpURL), c.FormValue(GitUserName), c.FormValue(GitPassword))
445+
repository, directory, cleanup, err := CloneGitRepository(c.Request().Context(), c.FormValue(GitHttpURL), c.FormValue(GitUserName), c.FormValue(GitPassword), c.FormValue(GitBranchName))
445446
if err != nil {
446447
return nil, nil, nil, false, err
447448
}

sqle/utils/util.go

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package utils
22

33
import (
44
"bytes"
5-
"context"
65
"crypto/md5"
76
"crypto/rand"
87
"crypto/rsa"
@@ -15,7 +14,6 @@ import (
1514
"io"
1615
"math"
1716
"net/url"
18-
"os"
1917
"regexp"
2018
"sort"
2119
"strconv"
@@ -26,9 +24,6 @@ import (
2624
"unicode/utf8"
2725
"unsafe"
2826

29-
sqleErrors "github.com/actiontech/sqle/sqle/errors"
30-
goGit "github.com/go-git/go-git/v5"
31-
goGitTransport "github.com/go-git/go-git/v5/plumbing/transport/http"
3227
"golang.org/x/crypto/ssh"
3328

3429
"github.com/actiontech/sqle/sqle/log"
@@ -464,37 +459,6 @@ func IntersectionStringSlice(slice1, slice2 []string) []string {
464459
}
465460
return intersection
466461
}
467-
468-
func CloneGitRepository(ctx context.Context, url, username, password string) (repository *goGit.Repository, directory string, cleanup func() error, err error) {
469-
if !IsGitHttpURL(url) {
470-
return nil, "", nil, sqleErrors.New(sqleErrors.DataInvalid, fmt.Errorf("url is not a git url"))
471-
}
472-
// 创建一个临时目录用于存放克隆的仓库
473-
directory, err = os.MkdirTemp("./", "git-repo-")
474-
if err != nil {
475-
return nil, "", nil, err
476-
}
477-
// 定义清理函数,用于删除临时目录
478-
cleanup = func() error {
479-
return os.RemoveAll(directory)
480-
}
481-
cloneOpts := &goGit.CloneOptions{
482-
URL: url,
483-
}
484-
if username != "" {
485-
cloneOpts.Auth = &goGitTransport.BasicAuth{
486-
Username: username,
487-
Password: password,
488-
}
489-
}
490-
repository, err = goGit.PlainCloneContext(ctx, directory, false, cloneOpts)
491-
if err != nil {
492-
err = cleanup()
493-
return nil, directory, nil, err
494-
}
495-
return repository, directory, cleanup, nil
496-
}
497-
498462
func GeneratePublicKeyFromPrivateKey(privateKey *rsa.PrivateKey) (string, error) {
499463
publicKey, err := ssh.NewPublicKey(&privateKey.PublicKey)
500464
if err != nil {

0 commit comments

Comments
 (0)