blob: b101adf0ac19df7efa7ff01377797dd757805e77 [file] [log] [blame]
package gitiles_common
import (
"bytes"
"context"
"errors"
"fmt"
"net/http"
"go.skia.org/infra/autoroll/go/config_vars"
"go.skia.org/infra/autoroll/go/repo_manager/common/version_file_common"
"go.skia.org/infra/autoroll/go/revision"
"go.skia.org/infra/go/gitiles"
"go.skia.org/infra/go/skerr"
)
// GitilesConfig provides configuration for GitilesRepo.
type GitilesConfig struct {
// Branch is the name of the git branch to be tracked by the GitilesRepo.
Branch *config_vars.Template `json:"branch"`
// RepoURL is the git repository URL.
RepoURL string `json:"repoURL"`
// Dependencies is an optional specification of dependencies to track.
// Revisions generated by the GitilesRepo will contain the pinned versions
// of these dependencies.
Dependencies []*version_file_common.VersionFileConfig `json:"dependencies,omitempty"`
}
// See documentation for util.Validator interface.
func (c *GitilesConfig) Validate() error {
if c.Branch == nil {
return errors.New("Branch is required.")
}
if err := c.Branch.Validate(); err != nil {
return skerr.Wrap(err)
}
if c.RepoURL == "" {
return errors.New("RepoURL is required.")
}
return nil
}
// GitilesRepo provides helpers for dealing with repos which use Gitiles.
type GitilesRepo struct {
*gitiles.Repo
branch *config_vars.Template
deps []*version_file_common.VersionFileConfig
}
// NewGitilesRepo returns a GitilesRepo instance.
func NewGitilesRepo(ctx context.Context, c GitilesConfig, reg *config_vars.Registry, client *http.Client) (*GitilesRepo, error) {
if err := c.Validate(); err != nil {
return nil, skerr.Wrap(err)
}
if err := reg.Register(c.Branch); err != nil {
return nil, skerr.Wrap(err)
}
repo := gitiles.NewRepo(c.RepoURL, client)
return &GitilesRepo{
Repo: repo,
branch: c.Branch,
deps: c.Dependencies,
}, nil
}
// Branch returns the resolved name of the branch tracked by this GitilesRepo.
func (r *GitilesRepo) Branch() string {
return r.branch.String()
}
// GetRevision returns a revision.Revision instance associated with the given
// revision ID, which may be a commit hash or fully-qualified ref name.
func (r *GitilesRepo) GetRevision(ctx context.Context, id string) (*revision.Revision, error) {
// Load the details for this revision.
details, err := r.Details(ctx, id)
if err != nil {
return nil, skerr.Wrapf(err, "Failed to retrieve revision %q", id)
}
rev := revision.FromLongCommit(fmt.Sprintf(gitiles.CommitURL, r.URL, "%s"), details)
// Optionally load any dependencies.
if len(r.deps) > 0 {
deps, err := version_file_common.GetPinnedRevs(ctx, r.deps, func(ctx context.Context, path string) (string, error) {
return r.GetFile(ctx, path, rev.Id)
})
if err != nil {
return nil, skerr.Wrap(err)
}
rev.Dependencies = deps
}
return rev, nil
}
// GetTipRevision returns a revision.Revision instance associated with the
// current tip of the branch tracked by this GitilesRepo.
func (r *GitilesRepo) GetTipRevision(ctx context.Context) (*revision.Revision, error) {
return r.GetRevision(ctx, r.branch.String())
}
// LogFirstParent returns a slice of revision.Revision instances in the given range.
func (r *GitilesRepo) LogFirstParent(ctx context.Context, from, to *revision.Revision) ([]*revision.Revision, error) {
commits, err := r.Repo.LogFirstParent(ctx, from.Id, to.Id)
if err != nil {
return nil, err
}
revs := make([]*revision.Revision, 0, len(commits))
for _, commit := range commits {
rev, err := r.GetRevision(ctx, commit.Hash)
if err != nil {
return nil, skerr.Wrapf(err, "Failed to retrieve revision")
}
revs = append(revs, rev)
}
return revs, nil
}
// GetFile retrieves the contents of the given file at the given ref.
func (r *GitilesRepo) GetFile(ctx context.Context, file, ref string) (string, error) {
var buf bytes.Buffer
if err := r.ReadFileAtRef(ctx, file, ref, &buf); err != nil {
return "", skerr.Wrap(err)
}
return buf.String(), nil
}