Merge "Implement fetching remote refs for document pull"

This commit is contained in:
Zuul 2021-08-10 22:07:46 +00:00 committed by Gerrit Code Review
commit f74a935df6
6 changed files with 62 additions and 21 deletions

View File

@ -67,7 +67,7 @@ type ErrMutuallyExclusiveCheckout struct {
}
func (e ErrMutuallyExclusiveCheckout) Error() string {
return "Checkout mutually exclusive, use either: commit-hash, branch or tag."
return "Checkout mutually exclusive, use either: commit-hash, branch, tag, or ref."
}
// ErrRepositoryNotFound is returned if repository is empty

View File

@ -22,8 +22,6 @@ import (
"k8s.io/cli-runtime/pkg/printers"
"sigs.k8s.io/yaml"
"opendev.org/airship/airshipctl/pkg/errors"
)
// ContextOptions holds all configurable options for context
@ -44,7 +42,6 @@ type ManifestOptions struct {
Branch string
CommitHash string
Tag string
RemoteRef string
Force bool
IsPhase bool
TargetPath string
@ -152,9 +149,6 @@ func (o *ManifestOptions) Validate() error {
if o.Name == "" {
return ErrMissingManifestName{}
}
if o.RemoteRef != "" {
return errors.ErrNotImplemented{What: "repository checkout by RemoteRef"}
}
if o.IsPhase && o.RepoName == "" {
return ErrMissingRepositoryName{}
}

View File

@ -18,6 +18,7 @@ import (
"fmt"
"github.com/go-git/go-git/v5"
gitconfig "github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/transport"
"github.com/go-git/go-git/v5/plumbing/transport/http"
@ -49,6 +50,8 @@ type Repository struct {
Auth *RepoAuth `json:"auth,omitempty"`
// CheckoutOptions holds options to checkout repository
CheckoutOptions *RepoCheckout `json:"checkout,omitempty"`
// FetchOptions holds options for fetching remote refs
FetchOptions *RepoFetch `json:"fetch,omitempty"`
}
// RepoAuth struct describes method of authentication against given repository
@ -77,17 +80,27 @@ type RepoCheckout struct {
Branch string `json:"branch"`
// Tag is the tag name to checkout
Tag string `json:"tag"`
// RemoteRef is not supported currently TODO
// RemoteRef is used for remote checkouts such as gerrit change requests/github pull request
// Ref is the ref to checkout
// for example refs/changes/04/691202/5
// TODO Add support for fetching remote refs
RemoteRef string `json:"remoteRef,omitempty"`
Ref string `json:"ref,omitempty"`
// ForceCheckout is a boolean to indicate whether to use the `--force` option when checking out
ForceCheckout bool `json:"force"`
// LocalBranch is a boolean to indicate whether the Branch is local one. False by default
LocalBranch bool `json:"localBranch"`
}
// RepoFetch holds information on which remote ref to fetch
type RepoFetch struct {
// RemoteRefSpec is used for remote fetches such as gerrit change
// requests and github pull requests. The format of the refspec is an
// optional +, followed by <src>:<dst>, where <src> is the pattern for
// references on the remote side and <dst> is where those references
// will be written locally. The + tells Git to update the reference
// even if it isn't a fast-forward.
// eg.: refs/changes/04/691202/5:refs/changes/04/691202/5
RemoteRefSpec string `json:"remoteRefSpec,omitempty"`
}
// RepoCheckout methods
func (c *RepoCheckout) String() string {
@ -102,7 +115,7 @@ func (c *RepoCheckout) String() string {
// repository checkout and returns Error for incorrect values
// returns nil when there are no errors
func (c *RepoCheckout) Validate() error {
possibleValues := []string{c.CommitHash, c.Branch, c.Tag, c.RemoteRef}
possibleValues := []string{c.CommitHash, c.Branch, c.Tag, c.Ref}
var count int
for _, val := range possibleValues {
if val != "" {
@ -112,8 +125,14 @@ func (c *RepoCheckout) Validate() error {
if count > 1 {
return ErrMutuallyExclusiveCheckout{}
}
if c.RemoteRef != "" {
return errors.ErrNotImplemented{What: "repository checkout by RemoteRef"}
return nil
}
// Validate verifies that the remote refspec is valid. If a remote refspec was
// not supplied, Validate does nothing.
func (rf *RepoFetch) Validate() error {
if rf.RemoteRefSpec != "" {
return gitconfig.RefSpec(rf.RemoteRefSpec).Validate()
}
return nil
}
@ -238,6 +257,8 @@ func (repo *Repository) ToCheckoutOptions() *git.CheckoutOptions {
co.Branch = plumbing.NewTagReferenceName(repo.CheckoutOptions.Tag)
case repo.CheckoutOptions.CommitHash != "":
co.Hash = plumbing.NewHash(repo.CheckoutOptions.CommitHash)
case repo.CheckoutOptions.Ref != "":
co.Branch = plumbing.ReferenceName(repo.CheckoutOptions.Ref)
}
}
return co
@ -257,7 +278,14 @@ func (repo *Repository) ToCloneOptions(auth transport.AuthMethod) *git.CloneOpti
// ToFetchOptions returns an instance of git.FetchOptions for given authentication
// FetchOptions describes how a fetch should be performed
func (repo *Repository) ToFetchOptions(auth transport.AuthMethod) *git.FetchOptions {
return &git.FetchOptions{Auth: auth}
var refSpecs []gitconfig.RefSpec
if repo.FetchOptions != nil && repo.FetchOptions.RemoteRefSpec != "" {
refSpecs = []gitconfig.RefSpec{gitconfig.RefSpec(repo.FetchOptions.RemoteRefSpec)}
}
return &git.FetchOptions{
Auth: auth,
RefSpecs: refSpecs,
}
}
// URL returns the repository URL in a string format

View File

@ -31,7 +31,6 @@ func Pull(cfgFactory config.Factory, noCheckout bool) error {
}
func cloneRepositories(cfg *config.Config, noCheckout bool) error {
// Clone main repository
currentManifest, err := cfg.CurrentContextManifest()
log.Debugf("Reading current context manifest information from %s", cfg.LoadedConfigPath())
if err != nil {
@ -39,17 +38,17 @@ func cloneRepositories(cfg *config.Config, noCheckout bool) error {
}
// Clone repositories
for repoName, extraRepoConfig := range currentManifest.Repositories {
err := extraRepoConfig.Validate()
for repoName, repoConfig := range currentManifest.Repositories {
err := repoConfig.Validate()
if err != nil {
return err
}
repository, err := repo.NewRepository(currentManifest.GetTargetPath(), extraRepoConfig)
repository, err := repo.NewRepository(currentManifest.GetTargetPath(), repoConfig)
if err != nil {
return err
}
log.Printf("Downloading %s repository %s from %s into %s",
repoName, repository.Name, extraRepoConfig.URL(), currentManifest.GetTargetPath())
repoName, repository.Name, repoConfig.URL(), currentManifest.GetTargetPath())
err = repository.Download(noCheckout)
if err != nil {
return err

View File

@ -115,6 +115,19 @@ func (repo *Repository) Checkout() error {
return tree.Checkout(co)
}
// Fetch fetches remote refs
func (repo *Repository) Fetch() error {
if !repo.Driver.IsOpen() {
return ErrNoOpenRepo{}
}
auth, err := repo.ToAuth()
if err != nil {
return fmt.Errorf("failed to build auth options for repository %v: %w", repo.Name, err)
}
fo := repo.ToFetchOptions(auth)
return repo.Driver.Fetch(fo)
}
// Open the repository
func (repo *Repository) Open() error {
log.Debugf("Attempting to open repository %s", repo.Name)
@ -155,5 +168,11 @@ func (repo *Repository) Download(noCheckout bool) error {
if noCheckout {
return nil
}
err := repo.Fetch()
if err != nil && err != git.NoErrAlreadyUpToDate {
return fmt.Errorf("failed to fetch refs for repository %v: %w", repo.Name, err)
}
return repo.Checkout()
}

View File

@ -63,7 +63,8 @@ func TestDownload(t *testing.T) {
CloneOptions: &git.CloneOptions{
URL: fx.DotGit().Root(),
},
URLString: fx.DotGit().Root(),
FetchOptions: &git.FetchOptions{Auth: nil},
URLString: fx.DotGit().Root(),
}
fs := memfs.New()