Provisioning: Move repository package to provisioning app (#110228)
* Move repository package to apps
* Move operators to grafana/grafana
* Go mod tidy
* Own package by git sync team for now
* Merged
* Do not use settings in local extra
* Remove dependency on webhook extra
* Hack to work around issue with secure contracts
* Sync Go modules
* Revert "Move operators to grafana/grafana"
This reverts commit 9f19b30a2e.
This commit is contained in:
@@ -0,0 +1,236 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
|
||||
provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1"
|
||||
"github.com/grafana/grafana/apps/provisioning/pkg/repository"
|
||||
"github.com/grafana/grafana/apps/provisioning/pkg/repository/git"
|
||||
"github.com/grafana/grafana/apps/provisioning/pkg/safepath"
|
||||
common "github.com/grafana/grafana/pkg/apimachinery/apis/common/v0alpha1"
|
||||
)
|
||||
|
||||
// Make sure all public functions of this struct call the (*githubRepository).logger function, to ensure the GH repo details are included.
|
||||
type githubRepository struct {
|
||||
git.GitRepository
|
||||
config *provisioning.Repository
|
||||
gh Client // assumes github.com base URL
|
||||
|
||||
owner string
|
||||
repo string
|
||||
}
|
||||
|
||||
// GithubRepository is an interface that combines all repository capabilities
|
||||
// needed for GitHub repositories.
|
||||
|
||||
//go:generate mockery --name GithubRepository --structname MockGithubRepository --inpackage --filename github_repository_mock.go --with-expecter
|
||||
type GithubRepository interface {
|
||||
repository.Repository
|
||||
repository.Versioned
|
||||
repository.Writer
|
||||
repository.Reader
|
||||
repository.RepositoryWithURLs
|
||||
repository.StageableRepository
|
||||
Owner() string
|
||||
Repo() string
|
||||
Client() Client
|
||||
}
|
||||
|
||||
func NewRepository(
|
||||
ctx context.Context,
|
||||
config *provisioning.Repository,
|
||||
gitRepo git.GitRepository,
|
||||
factory *Factory,
|
||||
token common.RawSecureValue,
|
||||
) (GithubRepository, error) {
|
||||
owner, repo, err := ParseOwnerRepoGithub(config.Spec.GitHub.URL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse owner and repo: %w", err)
|
||||
}
|
||||
|
||||
return &githubRepository{
|
||||
config: config,
|
||||
GitRepository: gitRepo,
|
||||
gh: factory.New(ctx, token), // TODO, baseURL from config
|
||||
owner: owner,
|
||||
repo: repo,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r *githubRepository) Owner() string {
|
||||
return r.owner
|
||||
}
|
||||
|
||||
func (r *githubRepository) Repo() string {
|
||||
return r.repo
|
||||
}
|
||||
|
||||
func (r *githubRepository) Client() Client {
|
||||
return r.gh
|
||||
}
|
||||
|
||||
// Validate implements provisioning.Repository.
|
||||
func (r *githubRepository) Validate() (list field.ErrorList) {
|
||||
cfg := r.Config()
|
||||
gh := cfg.Spec.GitHub
|
||||
if gh == nil {
|
||||
list = append(list, field.Required(field.NewPath("spec", "github"), "a github config is required"))
|
||||
return list
|
||||
}
|
||||
if gh.URL == "" {
|
||||
list = append(list, field.Required(field.NewPath("spec", "github", "url"), "a github url is required"))
|
||||
} else {
|
||||
_, _, err := ParseOwnerRepoGithub(gh.URL)
|
||||
if err != nil {
|
||||
list = append(list, field.Invalid(field.NewPath("spec", "github", "url"), gh.URL, err.Error()))
|
||||
} else if !strings.HasPrefix(gh.URL, "https://github.com/") {
|
||||
list = append(list, field.Invalid(field.NewPath("spec", "github", "url"), gh.URL, "URL must start with https://github.com/"))
|
||||
}
|
||||
}
|
||||
|
||||
if len(list) > 0 {
|
||||
return list
|
||||
}
|
||||
|
||||
return r.GitRepository.Validate()
|
||||
}
|
||||
|
||||
func ParseOwnerRepoGithub(giturl string) (owner string, repo string, err error) {
|
||||
giturl = strings.TrimSuffix(giturl, ".git")
|
||||
giturl = strings.TrimSuffix(giturl, "/")
|
||||
|
||||
parsed, e := url.Parse(giturl)
|
||||
if e != nil {
|
||||
err = e
|
||||
return
|
||||
}
|
||||
parts := strings.Split(parsed.Path, "/")
|
||||
if len(parts) < 3 {
|
||||
err = fmt.Errorf("unable to parse repo+owner from url")
|
||||
return
|
||||
}
|
||||
return parts[1], parts[2], nil
|
||||
}
|
||||
|
||||
// Test implements provisioning.Repository.
|
||||
func (r *githubRepository) Test(ctx context.Context) (*provisioning.TestResults, error) {
|
||||
url := r.config.Spec.GitHub.URL
|
||||
_, _, err := ParseOwnerRepoGithub(url)
|
||||
if err != nil {
|
||||
return repository.FromFieldError(field.Invalid(
|
||||
field.NewPath("spec", "github", "url"), url, err.Error())), nil
|
||||
}
|
||||
|
||||
return r.GitRepository.Test(ctx)
|
||||
}
|
||||
|
||||
func (r *githubRepository) History(ctx context.Context, path, ref string) ([]provisioning.HistoryItem, error) {
|
||||
if ref == "" {
|
||||
ref = r.config.Spec.GitHub.Branch
|
||||
}
|
||||
|
||||
finalPath := safepath.Join(r.config.Spec.GitHub.Path, path)
|
||||
commits, err := r.gh.Commits(ctx, r.owner, r.repo, finalPath, ref)
|
||||
if err != nil {
|
||||
if errors.Is(err, ErrResourceNotFound) {
|
||||
return nil, repository.ErrFileNotFound
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("get commits: %w", err)
|
||||
}
|
||||
|
||||
ret := make([]provisioning.HistoryItem, 0, len(commits))
|
||||
for _, commit := range commits {
|
||||
authors := make([]provisioning.Author, 0)
|
||||
if commit.Author != nil {
|
||||
authors = append(authors, provisioning.Author{
|
||||
Name: commit.Author.Name,
|
||||
Username: commit.Author.Username,
|
||||
AvatarURL: commit.Author.AvatarURL,
|
||||
})
|
||||
}
|
||||
|
||||
if commit.Committer != nil && commit.Author != nil && commit.Author.Name != commit.Committer.Name {
|
||||
authors = append(authors, provisioning.Author{
|
||||
Name: commit.Committer.Name,
|
||||
Username: commit.Committer.Username,
|
||||
AvatarURL: commit.Committer.AvatarURL,
|
||||
})
|
||||
}
|
||||
|
||||
ret = append(ret, provisioning.HistoryItem{
|
||||
Ref: commit.Ref,
|
||||
Message: commit.Message,
|
||||
Authors: authors,
|
||||
CreatedAt: commit.CreatedAt.UnixMilli(),
|
||||
})
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// ListRefs list refs from the git repository and add the ref URL to the ref item
|
||||
func (r *githubRepository) ListRefs(ctx context.Context) ([]provisioning.RefItem, error) {
|
||||
refs, err := r.GitRepository.ListRefs(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("list refs: %w", err)
|
||||
}
|
||||
|
||||
for i := range refs {
|
||||
refs[i].RefURL = fmt.Sprintf("%s/tree/%s", r.config.Spec.GitHub.URL, refs[i].Name)
|
||||
}
|
||||
|
||||
return refs, nil
|
||||
}
|
||||
|
||||
// ResourceURLs implements RepositoryWithURLs.
|
||||
func (r *githubRepository) ResourceURLs(ctx context.Context, file *repository.FileInfo) (*provisioning.RepositoryURLs, error) {
|
||||
cfg := r.config.Spec.GitHub
|
||||
if file.Path == "" || cfg == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
ref := file.Ref
|
||||
if ref == "" {
|
||||
ref = cfg.Branch
|
||||
}
|
||||
|
||||
urls := &provisioning.RepositoryURLs{
|
||||
RepositoryURL: cfg.URL,
|
||||
SourceURL: fmt.Sprintf("%s/blob/%s/%s", cfg.URL, ref, file.Path),
|
||||
}
|
||||
|
||||
if ref != cfg.Branch {
|
||||
urls.CompareURL = fmt.Sprintf("%s/compare/%s...%s", cfg.URL, cfg.Branch, ref)
|
||||
|
||||
// Create a new pull request
|
||||
urls.NewPullRequestURL = fmt.Sprintf("%s?quick_pull=1&labels=grafana", urls.CompareURL)
|
||||
}
|
||||
|
||||
return urls, nil
|
||||
}
|
||||
|
||||
// RefURLs implements RepositoryWithURLs.
|
||||
func (r *githubRepository) RefURLs(ctx context.Context, ref string) (*provisioning.RepositoryURLs, error) {
|
||||
cfg := r.config.Spec.GitHub
|
||||
if cfg == nil || ref == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
urls := &provisioning.RepositoryURLs{
|
||||
SourceURL: fmt.Sprintf("%s/tree/%s", cfg.URL, ref),
|
||||
}
|
||||
|
||||
if ref != cfg.Branch {
|
||||
urls.CompareURL = fmt.Sprintf("%s/compare/%s...%s", cfg.URL, cfg.Branch, ref)
|
||||
urls.NewPullRequestURL = fmt.Sprintf("%s?quick_pull=1&labels=grafana", urls.CompareURL)
|
||||
}
|
||||
|
||||
return urls, nil
|
||||
}
|
||||
Reference in New Issue
Block a user