1 Star 0 Fork 0

h79/goutils

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
git.go 7.73 KB
一键复制 编辑 原始数据 按行查看 历史
huqiuyun 提交于 2023-09-02 15:19 . git cmd
package git
import (
"crypto/tls"
"errors"
"gitee.com/h79/goutils/common/file"
"gitee.com/h79/goutils/common/random"
"net/http"
"os"
"path/filepath"
"strings"
"time"
"bytes"
"log"
"os/exec"
"runtime"
"golang.org/x/crypto/ssh"
"gopkg.in/src-d/go-git.v4"
"gopkg.in/src-d/go-git.v4/config"
"gopkg.in/src-d/go-git.v4/plumbing"
"gopkg.in/src-d/go-git.v4/plumbing/object"
"gopkg.in/src-d/go-git.v4/plumbing/transport"
"gopkg.in/src-d/go-git.v4/plumbing/transport/client"
githttp "gopkg.in/src-d/go-git.v4/plumbing/transport/http"
gitssh "gopkg.in/src-d/go-git.v4/plumbing/transport/ssh"
)
const branchNamePrefix = "refs/heads/auto-"
type Git struct {
Url string `json:"url"`
SshKey string `json:"ssh_key"`
SshKeySalt string `json:"ssh_key_salt"`
Path string `json:"path"`
Branch string `json:"branch"`
Username string `json:"username"`
Password string `json:"password"`
DirUser string `json:"dir_user"`
}
func (g *Git) Validate() error {
if g.Path == "" {
return errors.New("git path param error")
}
if g.Url == "" {
return errors.New("git url param error")
}
if g.IsHTTP() {
if (g.Username != "" && g.Password == "") ||
(g.Username == "" && g.Password != "") {
return errors.New("git param username and password error")
}
} else if g.SshKey == "" {
return errors.New("git ssh_key param error")
}
return nil
}
func (g *Git) Checkout(name string) (err error) {
r, err := git.PlainOpen(g.Path)
if err != nil {
return
}
w, err := r.Worktree()
if err != nil {
return
}
err = w.Checkout(&git.CheckoutOptions{
Branch: plumbing.ReferenceName("refs/heads/" + name),
Force: true,
})
return
}
func (g *Git) CleanBranch() (err error) {
//var hash string
//var hashObj plumbing.Hash
var r *git.Repository
//var ref *plumbing.Reference
if r, err = git.PlainOpen(g.Path); err != nil {
return
}
refs, err := r.References()
if err != nil {
return
}
h, err := r.Head()
if err != nil {
return
}
headBranchName := h.Name().String()
err = refs.ForEach(func(ref0 *plumbing.Reference) (e error) {
if headBranchName != ref0.Name().String() && strings.HasPrefix(ref0.Name().String(), "refs/heads/") {
e = r.Storer.RemoveReference(ref0.Name())
return
}
return nil
})
return
}
func (g *Git) CreateBranchName() (name string, err error) {
var hash string
// var hashObj plumbing.Hash
var ref *plumbing.Reference
hash, _, _, ref, err = g.GetHash()
if err != nil {
return
}
f := ""
if ref != nil {
f = filepath.Base(ref.Name().Short())
} else {
f = hash
}
name = branchNamePrefix + f + "-" + random.GenerateString(8)
return
}
func (g *Git) Publish() (commitId string, err error) {
defer func() {
go func() {
switch runtime.GOOS {
case "windows":
default:
cmd := exec.Command("chown", "-R", g.DirUser, g.Path)
var out bytes.Buffer
cmd.Stderr = &out
err = cmd.Run()
if err != nil {
log.Println(out.String())
}
}
}()
}()
if file.DirEmpty(g.Path) {
err = nil
_, err = g.Clone()
} else {
p := filepath.Join(g.Path, ".git")
if _, err := os.Stat(p); err != nil || file.DirEmpty(p) {
return "", errors.New("git: publish error, target path is not a git repository")
}
_, err = git.PlainOpen(g.Path)
if err == nil {
_, err = g.Fetch()
}
}
if err != nil {
return
}
branchShortName := ""
branchShortName, _, err = g.CreateBranch()
if err != nil {
return
}
//output := ""
err = g.Checkout(branchShortName)
if err != nil {
return
}
err = g.CleanBranch()
if err != nil {
return
}
// get commit_id
commitId, err = g.LastCommitId()
return
}
// CreateBranch 创建分支
func (g *Git) CreateBranch() (branchShortName, branchName string, err error) {
_, hashObj, r, _, err := g.GetHash()
if err != nil {
return
}
branchName, err = g.CreateBranchName()
if err != nil {
return
}
branchShortName = filepath.Base(branchName)
err = r.Storer.SetReference(plumbing.NewHashReference(plumbing.ReferenceName(branchName), hashObj))
return
}
// GetHash 获取 hash
func (g *Git) GetHash() (hash string, hashObj plumbing.Hash, r *git.Repository, ref *plumbing.Reference, err error) {
if r, err = git.PlainOpen(g.Path); err != nil {
return
}
refs, err := r.References()
if err != nil {
return
}
_ = refs.ForEach(func(ref0 *plumbing.Reference) (e error) {
if !strings.HasPrefix(ref0.Name().String(), "refs/heads/") {
if g.Branch == filepath.Base(ref0.Name().String()) {
hash = ref0.Hash().String()
ref = ref0
hashObj = ref0.Hash()
return errors.New("")
}
}
return nil
})
if hash == "" {
var c *object.Commit
c, err = r.CommitObject(plumbing.NewHash(g.Branch))
if err == nil {
hash = g.Branch
hashObj = c.Hash
return
}
}
return
}
// Fetch 拉取代码
func (g *Git) Fetch() (r *git.Repository, err error) {
if err = g.Validate(); err != nil {
return
}
if r, err = git.PlainOpen(g.Path); err != nil {
return
}
rconfig, err := r.Storer.Config()
if err != nil {
return
}
remoteConfig := &config.RemoteConfig{
Name: git.DefaultRemoteName,
URLs: []string{g.Url},
Fetch: []config.RefSpec{"+refs/heads/*:refs/remotes/" + git.DefaultRemoteName + "/*"},
}
rconfig.Remotes = map[string]*config.RemoteConfig{
git.DefaultRemoteName: remoteConfig,
}
err = r.Storer.SetConfig(rconfig)
if err != nil {
return
}
var opt git.FetchOptions
if opt, err = g.FetchOptions(); err != nil {
return
}
if err = r.Fetch(&opt); err == git.NoErrAlreadyUpToDate {
err = nil
}
return
}
func (g *Git) Clone() (r *git.Repository, err error) {
if err = g.Validate(); err != nil {
return
}
var opt git.CloneOptions
opt, err = g.CloneOptions()
if err != nil {
return
}
r, err = git.PlainClone(g.Path, false, &opt)
return
}
func (g *Git) LastCommitId() (hash string, err error) {
if err = g.Validate(); err != nil {
return
}
var r *git.Repository
if r, err = git.PlainOpen(g.Path); err != nil {
return
}
commitIter, err := r.Log(&git.LogOptions{})
if err != nil {
return
}
commit, err := commitIter.Next()
if err != nil {
return
}
hash = commit.Hash.String()
return
}
func (g *Git) CloneOptions() (opt git.CloneOptions, err error) {
opt.URL = g.Url
if g.IsNeedAuth() {
opt.Auth, err = g.GetAuth()
if err != nil {
return
}
}
return
}
func (g *Git) FetchOptions() (opt git.FetchOptions, err error) {
opt.RemoteName = git.DefaultRemoteName
opt.RefSpecs = []config.RefSpec{"+refs/heads/*:refs/remotes/" + git.DefaultRemoteName + "/*"}
if g.IsNeedAuth() {
opt.Auth, err = g.GetAuth()
if err != nil {
return
}
}
return
}
func (g *Git) GetAuth() (auth transport.AuthMethod, err error) {
var signer ssh.Signer
if g.IsHTTP() {
customClient := &http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
Timeout: 30 * time.Minute,
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
client.InstallProtocol("https", githttp.NewClient(customClient))
client.InstallProtocol("http", githttp.NewClient(customClient))
auth = &githttp.BasicAuth{
Username: g.Username,
Password: g.Password,
}
} else {
if g.SshKeySalt != "" {
signer, err = ssh.ParsePrivateKeyWithPassphrase([]byte(g.SshKey), []byte(g.SshKeySalt))
} else {
signer, err = ssh.ParsePrivateKey([]byte(g.SshKey))
}
if err != nil {
return
}
auth = &gitssh.PublicKeys{
User: "git",
Signer: signer,
HostKeyCallbackHelper: gitssh.HostKeyCallbackHelper{
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
},
}
}
return
}
func (g *Git) IsHTTP() bool {
return strings.HasPrefix(g.Url, "http://") || strings.HasPrefix(g.Url, "https://")
}
func (g *Git) IsNeedAuth() bool {
if g.IsHTTP() {
return g.Username != "" && g.Password != ""
}
return g.SshKey != ""
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/h79/goutils.git
git@gitee.com:h79/goutils.git
h79
goutils
goutils
v1.20.103

搜索帮助