github.com/projecteru2/core@v0.0.0-20240321043226-06bcc1c23f58/source/common/common.go (about) 1 package common 2 3 import ( 4 "context" 5 "io" 6 "net/http" 7 "net/url" 8 "os" 9 "path/filepath" 10 "strings" 11 12 "github.com/cockroachdb/errors" 13 "github.com/projecteru2/core/log" 14 "github.com/projecteru2/core/types" 15 16 gogit "github.com/go-git/go-git/v5" 17 "github.com/go-git/go-git/v5/plumbing" 18 gitssh "github.com/go-git/go-git/v5/plumbing/transport/ssh" 19 "golang.org/x/crypto/ssh" 20 ) 21 22 // GitScm is gitlab or github source code manager 23 type GitScm struct { 24 http.Client 25 Config types.GitConfig 26 AuthHeaders map[string]string 27 28 keyBytes []byte 29 } 30 31 // NewGitScm . 32 func NewGitScm(config types.GitConfig, authHeaders map[string]string) (*GitScm, error) { 33 b, err := os.ReadFile(config.PrivateKey) 34 return &GitScm{ 35 Config: config, 36 AuthHeaders: authHeaders, 37 keyBytes: b, 38 }, err 39 } 40 41 // SourceCode clone code from repository into path, by revision 42 func (g *GitScm) SourceCode(ctx context.Context, repository, path, revision string, submodule bool) error { 43 var repo *gogit.Repository 44 var err error 45 ctx, cancel := context.WithTimeout(ctx, g.Config.CloneTimeout) 46 defer cancel() 47 opts := &gogit.CloneOptions{ 48 URL: repository, 49 Progress: io.Discard, 50 } 51 logger := log.WithFunc("source.common.SourceCode") 52 53 switch { 54 case strings.Contains(repository, "https://"): 55 repo, err = gogit.PlainCloneContext(ctx, path, false, opts) 56 case strings.Contains(repository, "git@") || strings.Contains(repository, "gitlab@"): 57 signer, signErr := ssh.ParsePrivateKey(g.keyBytes) 58 if signErr != nil { 59 return signErr 60 } 61 splitRepo := strings.Split(repository, "@") 62 user, parseErr := url.Parse(splitRepo[0]) 63 if parseErr != nil { 64 return parseErr 65 } 66 // TODO check if it ok? 67 // gitssh.SetConfigHostKeyFields( // nolint 68 // &ssh.ClientConfig{ 69 // HostKeyCallback: ssh.InsecureIgnoreHostKey()}, // nolint 70 // user.Host) 71 72 auth := &gitssh.PublicKeys{ 73 User: user.Host + user.Path, 74 Signer: signer, 75 } 76 opts.Auth = auth 77 repo, err = gogit.PlainCloneContext(ctx, path, false, opts) 78 default: 79 return types.ErrInvaildSCMType 80 } 81 if err != nil { 82 return err 83 } 84 85 w, err := repo.Worktree() 86 if err != nil { 87 return err 88 } 89 90 hash, err := repo.ResolveRevision(plumbing.Revision(revision)) 91 if err != nil { 92 return err 93 } 94 95 if err = w.Checkout(&gogit.CheckoutOptions{Hash: *hash}); err != nil { 96 return err 97 } 98 99 logger.Infof(ctx, "Fetch repo %s", repository) 100 logger.Infof(ctx, "Checkout to commit %s", hash) 101 102 // Prepare submodules 103 if submodule { 104 s, err := w.Submodules() 105 if err != nil { 106 return err 107 } 108 return s.Update(&gogit.SubmoduleUpdateOptions{Init: true, Auth: opts.Auth}) 109 } 110 return err 111 } 112 113 // Artifact download the artifact to the path, then unzip it 114 func (g *GitScm) Artifact(ctx context.Context, artifact, path string) error { 115 req, err := http.NewRequest(http.MethodGet, artifact, nil) 116 if err != nil { 117 return err 118 } 119 120 for k, v := range g.AuthHeaders { 121 req.Header.Add(k, v) 122 } 123 124 log.WithFunc("source.common.Artifact").Infof(ctx, "Downloading artifacts from %q", artifact) 125 resp, err := g.Do(req) 126 if err != nil { 127 return err 128 } 129 defer resp.Body.Close() 130 if resp.StatusCode != 200 { 131 return errors.Wrapf(types.ErrDownloadArtifactsFailed, "code: %d", resp.StatusCode) 132 } 133 134 // extract files from zipfile 135 return unzipFile(resp.Body, path) 136 } 137 138 // Security remove the .git folder 139 func (g *GitScm) Security(path string) error { 140 return os.RemoveAll(filepath.Join(path, ".git")) 141 }