2017-10-15 17:24:40 +00:00
|
|
|
package git
|
|
|
|
|
|
|
|
import (
|
2021-05-24 05:27:07 +00:00
|
|
|
"context"
|
2019-09-09 22:56:16 +00:00
|
|
|
"crypto/tls"
|
|
|
|
"net/http"
|
2021-05-24 05:27:07 +00:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2021-08-17 01:12:07 +00:00
|
|
|
"strings"
|
2019-09-09 22:56:16 +00:00
|
|
|
"time"
|
2018-03-15 21:22:05 +00:00
|
|
|
|
2021-06-15 21:11:35 +00:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
|
2021-05-24 05:27:07 +00:00
|
|
|
"github.com/go-git/go-git/v5"
|
2021-08-17 01:12:07 +00:00
|
|
|
"github.com/go-git/go-git/v5/config"
|
2021-05-24 05:27:07 +00:00
|
|
|
"github.com/go-git/go-git/v5/plumbing"
|
|
|
|
"github.com/go-git/go-git/v5/plumbing/transport/client"
|
|
|
|
githttp "github.com/go-git/go-git/v5/plumbing/transport/http"
|
2021-08-17 01:12:07 +00:00
|
|
|
"github.com/go-git/go-git/v5/storage/memory"
|
2017-10-15 17:24:40 +00:00
|
|
|
)
|
|
|
|
|
2021-08-17 01:12:07 +00:00
|
|
|
type fetchOptions struct {
|
|
|
|
repositoryUrl string
|
|
|
|
username string
|
|
|
|
password string
|
|
|
|
referenceName string
|
|
|
|
}
|
|
|
|
|
2021-05-24 05:27:07 +00:00
|
|
|
type cloneOptions struct {
|
|
|
|
repositoryUrl string
|
|
|
|
username string
|
|
|
|
password string
|
|
|
|
referenceName string
|
|
|
|
depth int
|
|
|
|
}
|
|
|
|
|
|
|
|
type downloader interface {
|
|
|
|
download(ctx context.Context, dst string, opt cloneOptions) error
|
2021-08-17 01:12:07 +00:00
|
|
|
latestCommitID(ctx context.Context, opt fetchOptions) (string, error)
|
2021-05-24 05:27:07 +00:00
|
|
|
}
|
|
|
|
|
2021-06-15 21:11:35 +00:00
|
|
|
type gitClient struct {
|
2021-05-24 05:27:07 +00:00
|
|
|
preserveGitDirectory bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c gitClient) download(ctx context.Context, dst string, opt cloneOptions) error {
|
|
|
|
gitOptions := git.CloneOptions{
|
|
|
|
URL: opt.repositoryUrl,
|
|
|
|
Depth: opt.depth,
|
2021-08-17 01:12:07 +00:00
|
|
|
Auth: getAuth(opt.username, opt.password),
|
2021-05-24 05:27:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if opt.referenceName != "" {
|
|
|
|
gitOptions.ReferenceName = plumbing.ReferenceName(opt.referenceName)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err := git.PlainCloneContext(ctx, dst, false, &gitOptions)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "failed to clone git repository")
|
|
|
|
}
|
|
|
|
|
|
|
|
if !c.preserveGitDirectory {
|
|
|
|
os.RemoveAll(filepath.Join(dst, ".git"))
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-08-17 01:12:07 +00:00
|
|
|
func (c gitClient) latestCommitID(ctx context.Context, opt fetchOptions) (string, error) {
|
|
|
|
remote := git.NewRemote(memory.NewStorage(), &config.RemoteConfig{
|
|
|
|
Name: "origin",
|
|
|
|
URLs: []string{opt.repositoryUrl},
|
|
|
|
})
|
|
|
|
|
|
|
|
listOptions := &git.ListOptions{
|
|
|
|
Auth: getAuth(opt.username, opt.password),
|
|
|
|
}
|
|
|
|
|
|
|
|
refs, err := remote.List(listOptions)
|
|
|
|
if err != nil {
|
|
|
|
return "", errors.Wrap(err, "failed to list repository refs")
|
|
|
|
}
|
|
|
|
|
2022-05-15 21:35:11 +00:00
|
|
|
referenceName := opt.referenceName
|
|
|
|
if referenceName == "" {
|
|
|
|
for _, ref := range refs {
|
|
|
|
if strings.EqualFold(ref.Name().String(), "HEAD") {
|
|
|
|
referenceName = ref.Target().String()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-17 01:12:07 +00:00
|
|
|
for _, ref := range refs {
|
2022-05-15 21:35:11 +00:00
|
|
|
if strings.EqualFold(ref.Name().String(), referenceName) {
|
2021-08-17 01:12:07 +00:00
|
|
|
return ref.Hash().String(), nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return "", errors.Errorf("could not find ref %q in the repository", opt.referenceName)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getAuth(username, password string) *githttp.BasicAuth {
|
|
|
|
if password != "" {
|
|
|
|
if username == "" {
|
|
|
|
username = "token"
|
|
|
|
}
|
|
|
|
|
|
|
|
return &githttp.BasicAuth{
|
|
|
|
Username: username,
|
|
|
|
Password: password,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-10-15 17:24:40 +00:00
|
|
|
// Service represents a service for managing Git.
|
2019-09-09 22:56:16 +00:00
|
|
|
type Service struct {
|
|
|
|
httpsCli *http.Client
|
2021-05-24 05:27:07 +00:00
|
|
|
azure downloader
|
|
|
|
git downloader
|
2019-09-09 22:56:16 +00:00
|
|
|
}
|
2017-10-15 17:24:40 +00:00
|
|
|
|
|
|
|
// NewService initializes a new service.
|
2019-09-09 22:56:16 +00:00
|
|
|
func NewService() *Service {
|
|
|
|
httpsCli := &http.Client{
|
|
|
|
Transport: &http.Transport{
|
|
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
2021-08-12 11:37:48 +00:00
|
|
|
Proxy: http.ProxyFromEnvironment,
|
2019-09-09 22:56:16 +00:00
|
|
|
},
|
|
|
|
Timeout: 300 * time.Second,
|
|
|
|
}
|
2017-10-15 17:24:40 +00:00
|
|
|
|
2019-09-09 22:56:16 +00:00
|
|
|
client.InstallProtocol("https", githttp.NewClient(httpsCli))
|
|
|
|
|
|
|
|
return &Service{
|
|
|
|
httpsCli: httpsCli,
|
2021-05-24 05:27:07 +00:00
|
|
|
azure: NewAzureDownloader(httpsCli),
|
|
|
|
git: gitClient{},
|
2019-09-09 22:56:16 +00:00
|
|
|
}
|
2017-10-15 17:24:40 +00:00
|
|
|
}
|
|
|
|
|
2021-06-15 21:11:35 +00:00
|
|
|
// CloneRepository clones a git repository using the specified URL in the specified
|
2017-10-15 17:24:40 +00:00
|
|
|
// destination folder.
|
2021-06-15 21:11:35 +00:00
|
|
|
func (service *Service) CloneRepository(destination, repositoryURL, referenceName, username, password string) error {
|
|
|
|
options := cloneOptions{
|
2021-05-24 05:27:07 +00:00
|
|
|
repositoryUrl: repositoryURL,
|
|
|
|
username: username,
|
|
|
|
password: password,
|
|
|
|
referenceName: referenceName,
|
|
|
|
depth: 1,
|
2021-06-15 21:11:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return service.cloneRepository(destination, options)
|
2018-03-15 21:22:05 +00:00
|
|
|
}
|
|
|
|
|
2021-05-24 05:27:07 +00:00
|
|
|
func (service *Service) cloneRepository(destination string, options cloneOptions) error {
|
|
|
|
if isAzureUrl(options.repositoryUrl) {
|
|
|
|
return service.azure.download(context.TODO(), destination, options)
|
2018-07-24 14:11:35 +00:00
|
|
|
}
|
|
|
|
|
2021-05-24 05:27:07 +00:00
|
|
|
return service.git.download(context.TODO(), destination, options)
|
2017-10-15 17:24:40 +00:00
|
|
|
}
|
2021-08-17 01:12:07 +00:00
|
|
|
|
|
|
|
// LatestCommitID returns SHA1 of the latest commit of the specified reference
|
|
|
|
func (service *Service) LatestCommitID(repositoryURL, referenceName, username, password string) (string, error) {
|
|
|
|
options := fetchOptions{
|
|
|
|
repositoryUrl: repositoryURL,
|
|
|
|
username: username,
|
|
|
|
password: password,
|
|
|
|
referenceName: referenceName,
|
|
|
|
}
|
|
|
|
|
|
|
|
if isAzureUrl(options.repositoryUrl) {
|
|
|
|
return service.azure.latestCommitID(context.TODO(), options)
|
|
|
|
}
|
|
|
|
|
|
|
|
return service.git.latestCommitID(context.TODO(), options)
|
|
|
|
}
|