1
1
mirror of https://github.com/drone-plugins/github-actions synced 2026-03-07 10:01:33 +01:00
github.com-drone-plugins-gi.../cloner/default.go
OP (oppenheimer) 11ed8bab1c
feat: [CI-15681]: Enhance Drone GitHub Action Plugin with Workflow Output Parsing (#18)
* feat: [CI-15681]: Enhance Drone GitHub Actions Plugin with Workflow Output Parsing

* formatted parse_test.go

* Removed 'drone/plugin' dependencies to reduce the binary size and copied the relevant code to this repo

* Removed windows code

* Updated plugin.go
2025-01-10 20:01:59 +05:30

145 lines
3.4 KiB
Go

// Copyright 2022 Harness Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package cloner
import (
"context"
"errors"
"io"
"os"
"regexp"
"strings"
"time"
"github.com/cenkalti/backoff/v4"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/transport/http"
)
const (
maxRetries = 3
backoffInterval = time.Second * 1
)
// New returns a new cloner.
func New(depth int, stdout io.Writer) Cloner {
c := &cloner{
depth: depth,
stdout: stdout,
}
if token := os.Getenv("GITHUB_TOKEN"); token != "" {
c.username = "token"
c.password = token
}
return c
}
// NewDefault returns a cloner with default settings.
func NewDefault() Cloner {
return New(1, os.Stdout)
}
// default cloner using the built-in Git client.
type cloner struct {
depth int
username string
password string
stdout io.Writer
}
// Clone the repository using the built-in Git client.
func (c *cloner) Clone(ctx context.Context, params Params) error {
opts := &git.CloneOptions{
RemoteName: "origin",
Progress: c.stdout,
URL: params.Repo,
Tags: git.NoTags,
}
// set the reference name if provided
if params.Ref != "" {
opts.ReferenceName = plumbing.ReferenceName(expandRef(params.Ref))
}
// set depth if cloning the head commit of a branch as
// opposed to a specific commit sha
if params.Sha == "" {
opts.Depth = c.depth
}
if c.username != "" && c.password != "" {
opts.Auth = &http.BasicAuth{
Username: c.username,
Password: c.password,
}
}
// clone the repository
var (
r *git.Repository
err error
)
retryStrategy := backoff.NewExponentialBackOff()
retryStrategy.InitialInterval = backoffInterval
retryStrategy.MaxInterval = backoffInterval * 5 // Maximum delay
retryStrategy.MaxElapsedTime = backoffInterval * 60 // Maximum time to retry (1min)
b := backoff.WithMaxRetries(retryStrategy, uint64(maxRetries))
err = backoff.Retry(func() error {
r, err = git.PlainClone(params.Dir, false, opts)
if err == nil {
return nil
}
if (errors.Is(plumbing.ErrReferenceNotFound, err) || matchRefNotFoundErr(err)) &&
!strings.HasPrefix(params.Ref, "refs/") {
originalRefName := opts.ReferenceName
// If params.Ref is provided without refs/*, then we are assuming it to either refs/heads/ or refs/tags.
// Try clone again with inverse ref.
if opts.ReferenceName.IsBranch() {
opts.ReferenceName = plumbing.ReferenceName("refs/tags/" + params.Ref)
} else if opts.ReferenceName.IsTag() {
opts.ReferenceName = plumbing.ReferenceName("refs/heads/" + params.Ref)
} else {
return err // Return err if the reference name is invalid
}
r, err = git.PlainClone(params.Dir, false, opts)
if err == nil {
return nil
}
// Change reference name back to original
opts.ReferenceName = originalRefName
}
return err
}, b)
// If error not nil, then return it
if err != nil {
return err
}
if params.Sha == "" {
return nil
}
// checkout the sha
w, err := r.Worktree()
if err != nil {
return err
}
return w.Checkout(&git.CheckoutOptions{
Hash: plumbing.NewHash(params.Sha),
})
}
func matchRefNotFoundErr(err error) bool {
if err == nil {
return false
}
pattern := `couldn't find remote ref.*`
regex := regexp.MustCompile(pattern)
return regex.MatchString(err.Error())
}