POC/Provisioning/Jobs: Move PR options to their own object and add resource summary (#99489)
This commit is contained in:
1 parent
bd201abc63
commit
5cdaf77bc9
12 files changed
+402
-90
No files matched your search
@@ -63,6 +63,14 @@ type JobSpec struct {
|
||||
// The the repository reference (for now also in labels)
|
||||
Repository string `json:"repository"`
|
||||
|
||||
// Pull request options
|
||||
PullRequest *PullRequestOptions `json:"pr,omitempty"`
|
||||
|
||||
// Required when the action is `export`
|
||||
Export *ExportOptions `json:"export,omitempty"`
|
||||
}
|
||||
|
||||
type PullRequestOptions struct {
|
||||
// The branch of commit hash
|
||||
Ref string `json:"ref,omitempty"`
|
||||
|
||||
@@ -72,9 +80,6 @@ type JobSpec struct {
|
||||
|
||||
// URL to the originator (eg, PR URL)
|
||||
URL string `json:"url,omitempty"`
|
||||
|
||||
// Required when the action is `export`
|
||||
Export *ExportOptions `json:"export,omitempty"`
|
||||
}
|
||||
|
||||
type ExportOptions struct {
|
||||
@@ -101,6 +106,25 @@ type JobStatus struct {
|
||||
|
||||
// Optional value 0-100 that can be set while running
|
||||
Progress float64 `json:"progress,omitempty"`
|
||||
|
||||
// Summary of processed actions
|
||||
Summary []JobResourceSummary `json:"summary,omitempty"`
|
||||
}
|
||||
|
||||
type JobResourceSummary struct {
|
||||
Group string `json:"group,omitempty"`
|
||||
Resource string `json:"resource,omitempty"`
|
||||
|
||||
Create int64 `json:"create,omitempty"`
|
||||
Update int64 `json:"update,omitempty"`
|
||||
Delete int64 `json:"delete,omitempty"`
|
||||
|
||||
// No action required (useful for sync)
|
||||
Noop int64 `json:"noop,omitempty"`
|
||||
|
||||
// Report errors for this resource type
|
||||
// This may not be an exhaustive list and recommend looking at the logs for more info
|
||||
Errors []string `json:"errors,omitempty"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
@@ -283,9 +283,35 @@ func (in *JobList) DeepCopyObject() runtime.Object {
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *JobResourceSummary) DeepCopyInto(out *JobResourceSummary) {
|
||||
*out = *in
|
||||
if in.Errors != nil {
|
||||
in, out := &in.Errors, &out.Errors
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JobResourceSummary.
|
||||
func (in *JobResourceSummary) DeepCopy() *JobResourceSummary {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(JobResourceSummary)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *JobSpec) DeepCopyInto(out *JobSpec) {
|
||||
*out = *in
|
||||
if in.PullRequest != nil {
|
||||
in, out := &in.PullRequest, &out.PullRequest
|
||||
*out = new(PullRequestOptions)
|
||||
**out = **in
|
||||
}
|
||||
if in.Export != nil {
|
||||
in, out := &in.Export, &out.Export
|
||||
*out = new(ExportOptions)
|
||||
@@ -312,6 +338,13 @@ func (in *JobStatus) DeepCopyInto(out *JobStatus) {
|
||||
*out = make([]string, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
if in.Summary != nil {
|
||||
in, out := &in.Summary, &out.Summary
|
||||
*out = make([]JobResourceSummary, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -357,6 +390,22 @@ func (in *LocalRepositoryConfig) DeepCopy() *LocalRepositoryConfig {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *PullRequestOptions) DeepCopyInto(out *PullRequestOptions) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PullRequestOptions.
|
||||
func (in *PullRequestOptions) DeepCopy() *PullRequestOptions {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(PullRequestOptions)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Repository) DeepCopyInto(out *Repository) {
|
||||
*out = *in
|
||||
|
||||
@@ -26,10 +26,12 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
|
||||
"github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.HistoryList": schema_pkg_apis_provisioning_v0alpha1_HistoryList(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.Job": schema_pkg_apis_provisioning_v0alpha1_Job(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.JobList": schema_pkg_apis_provisioning_v0alpha1_JobList(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.JobResourceSummary": schema_pkg_apis_provisioning_v0alpha1_JobResourceSummary(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.JobSpec": schema_pkg_apis_provisioning_v0alpha1_JobSpec(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.JobStatus": schema_pkg_apis_provisioning_v0alpha1_JobStatus(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.LintIssue": schema_pkg_apis_provisioning_v0alpha1_LintIssue(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.LocalRepositoryConfig": schema_pkg_apis_provisioning_v0alpha1_LocalRepositoryConfig(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.PullRequestOptions": schema_pkg_apis_provisioning_v0alpha1_PullRequestOptions(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.Repository": schema_pkg_apis_provisioning_v0alpha1_Repository(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.RepositoryList": schema_pkg_apis_provisioning_v0alpha1_RepositoryList(ref),
|
||||
"github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.RepositorySpec": schema_pkg_apis_provisioning_v0alpha1_RepositorySpec(ref),
|
||||
@@ -579,6 +581,70 @@ func schema_pkg_apis_provisioning_v0alpha1_JobList(ref common.ReferenceCallback)
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_provisioning_v0alpha1_JobResourceSummary(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"group": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"resource": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"create": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"integer"},
|
||||
Format: "int64",
|
||||
},
|
||||
},
|
||||
"update": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"integer"},
|
||||
Format: "int64",
|
||||
},
|
||||
},
|
||||
"delete": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"integer"},
|
||||
Format: "int64",
|
||||
},
|
||||
},
|
||||
"noop": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "No action required (useful for sync)",
|
||||
Type: []string{"integer"},
|
||||
Format: "int64",
|
||||
},
|
||||
},
|
||||
"errors": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Report errors for this resource type This may not be an exhaustive list and recommend looking at the logs for more info",
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: "",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_provisioning_v0alpha1_JobSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
@@ -602,31 +668,10 @@ func schema_pkg_apis_provisioning_v0alpha1_JobSpec(ref common.ReferenceCallback)
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"ref": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "The branch of commit hash",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"pr": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Pull request number (when appropriate)",
|
||||
Type: []string{"integer"},
|
||||
Format: "int32",
|
||||
},
|
||||
},
|
||||
"hash": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"url": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "URL to the originator (eg, PR URL)",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
Description: "Pull request options",
|
||||
Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.PullRequestOptions"),
|
||||
},
|
||||
},
|
||||
"export": {
|
||||
@@ -640,7 +685,7 @@ func schema_pkg_apis_provisioning_v0alpha1_JobSpec(ref common.ReferenceCallback)
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ExportOptions"},
|
||||
"github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.ExportOptions", "github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.PullRequestOptions"},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -698,9 +743,25 @@ func schema_pkg_apis_provisioning_v0alpha1_JobStatus(ref common.ReferenceCallbac
|
||||
Format: "double",
|
||||
},
|
||||
},
|
||||
"summary": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Summary of processed actions",
|
||||
Type: []string{"array"},
|
||||
Items: &spec.SchemaOrArray{
|
||||
Schema: &spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Default: map[string]interface{}{},
|
||||
Ref: ref("github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.JobResourceSummary"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1.JobResourceSummary"},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -758,6 +819,45 @@ func schema_pkg_apis_provisioning_v0alpha1_LocalRepositoryConfig(ref common.Refe
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_provisioning_v0alpha1_PullRequestOptions(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"ref": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "The branch of commit hash",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"pr": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Pull request number (when appropriate)",
|
||||
Type: []string{"integer"},
|
||||
Format: "int32",
|
||||
},
|
||||
},
|
||||
"hash": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"url": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "URL to the originator (eg, PR URL)",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func schema_pkg_apis_provisioning_v0alpha1_Repository(ref common.ReferenceCallback) common.OpenAPIDefinition {
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,HealthStatus,Message
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,HistoryItem,Authors
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,JobResourceSummary,Errors
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,JobStatus,Errors
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,JobStatus,Summary
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,RepositoryStatus,Stats
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,ResourceWrapper,Errors
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,ResourceWrapper,Lint
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,SyncStatus,Message
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,TestResults,Errors
|
||||
API rule violation: list_type_missing,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,WebhookStatus,SubscribedEvents
|
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,JobSpec,PullRequest
|
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,RepositorySpec,GitHub
|
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,SyncStatus,JobID
|
||||
API rule violation: names_match,github.com/grafana/grafana/pkg/apis/provisioning/v0alpha1,WebhookResponse,Message
|
||||
@@ -23,6 +23,11 @@ func (s *dummyExporter) Export(ctx context.Context,
|
||||
logger.Info("start export", "folder", options.Folder)
|
||||
|
||||
size := 5 + rand.IntN(15)
|
||||
|
||||
dashboards := provisioning.JobResourceSummary{
|
||||
Group: "dashboard.grafana.app",
|
||||
Resource: "dashboards",
|
||||
}
|
||||
status := provisioning.JobStatus{
|
||||
State: provisioning.JobStateWorking,
|
||||
Message: "exporting..." + repo.Config().Spec.Title,
|
||||
@@ -39,6 +44,23 @@ func (s *dummyExporter) Export(ctx context.Context,
|
||||
sleep := time.Duration(400+rand.IntN(800)) * time.Millisecond
|
||||
status.Message, _ = util.GetRandomString(rand.IntN(10 + 15))
|
||||
time.Sleep(sleep)
|
||||
|
||||
for j := 0; j < (4 + rand.IntN(10)); j++ {
|
||||
switch rand.IntN(9) {
|
||||
case 0, 1, 2:
|
||||
dashboards.Create++
|
||||
case 3, 4:
|
||||
dashboards.Update++
|
||||
case 5:
|
||||
dashboards.Delete++
|
||||
case 6, 7, 8:
|
||||
dashboards.Noop++
|
||||
}
|
||||
}
|
||||
status.Summary = []provisioning.JobResourceSummary{
|
||||
dashboards,
|
||||
}
|
||||
|
||||
err = progress(status)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -15,6 +15,14 @@ import (
|
||||
"github.com/grafana/grafana/pkg/registry/apis/provisioning/resources"
|
||||
)
|
||||
|
||||
type PullRequestWorker interface {
|
||||
ProcessPullRequest(ctx context.Context,
|
||||
repo repository.Repository,
|
||||
options provisioning.PullRequestOptions,
|
||||
progress func(provisioning.JobStatus) error,
|
||||
) (*provisioning.JobStatus, error)
|
||||
}
|
||||
|
||||
// resourcePreview represents a resource that has changed in a pull request.
|
||||
type resourcePreview struct {
|
||||
Filename string
|
||||
@@ -65,7 +73,7 @@ type PreviewRenderer interface {
|
||||
RenderDashboardPreview(ctx context.Context, path string, ref string) (string, error)
|
||||
}
|
||||
|
||||
type PullRequestCommenter struct {
|
||||
type pullRequestCommenter struct {
|
||||
repo PullRequestRepo
|
||||
parser *resources.Parser
|
||||
lintTemplate *template.Template
|
||||
@@ -79,7 +87,7 @@ func NewPullRequestCommenter(
|
||||
parser *resources.Parser,
|
||||
renderer PreviewRenderer,
|
||||
baseURL *url.URL,
|
||||
) (*PullRequestCommenter, error) {
|
||||
) (PullRequestWorker, error) {
|
||||
lintTemplate, err := template.New("comment").Parse(lintDashboardIssuesTemplate)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("parse lint comment template: %w", err)
|
||||
@@ -89,7 +97,7 @@ func NewPullRequestCommenter(
|
||||
return nil, fmt.Errorf("parse previews comment template: %w", err)
|
||||
}
|
||||
|
||||
return &PullRequestCommenter{
|
||||
return &pullRequestCommenter{
|
||||
repo: repo,
|
||||
parser: parser,
|
||||
lintTemplate: lintTemplate,
|
||||
@@ -99,40 +107,51 @@ func NewPullRequestCommenter(
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *PullRequestCommenter) Process(ctx context.Context, job provisioning.Job) error {
|
||||
if job.Spec.Action != provisioning.JobActionPullRequest {
|
||||
return errors.New("job is not a pull request")
|
||||
}
|
||||
func (c *pullRequestCommenter) ProcessPullRequest(ctx context.Context,
|
||||
repo repository.Repository,
|
||||
options provisioning.PullRequestOptions,
|
||||
progress func(provisioning.JobStatus) error,
|
||||
) (*provisioning.JobStatus, error) {
|
||||
cfg := c.repo.Config().Spec
|
||||
|
||||
// TODO: clean specification to have better options
|
||||
if !(cfg.Linting && cfg.GitHub.PullRequestLinter) &&
|
||||
!cfg.GitHub.GenerateDashboardPreviews {
|
||||
return nil
|
||||
return &provisioning.JobStatus{
|
||||
State: provisioning.JobStateSuccess,
|
||||
Message: "linting and previews are not required",
|
||||
}, nil
|
||||
}
|
||||
|
||||
logger := logging.FromContext(ctx).With("pr", job.Spec.PR)
|
||||
logger := logging.FromContext(ctx).With("pr", options.PR)
|
||||
logger.Info("process pull request")
|
||||
defer logger.Info("pull request processed")
|
||||
|
||||
spec := job.Spec
|
||||
base := cfg.GitHub.Branch
|
||||
ref := spec.Hash
|
||||
ref := options.Hash
|
||||
|
||||
// list pull requests changes files
|
||||
files, err := c.repo.CompareFiles(ctx, base, ref)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list pull request files: %w", err)
|
||||
return &provisioning.JobStatus{
|
||||
State: provisioning.JobStateError,
|
||||
Message: fmt.Sprintf("failed to list pull request files: %s", err.Error()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// clear all previous comments
|
||||
if err := c.repo.ClearAllPullRequestFileComments(ctx, spec.PR); err != nil {
|
||||
return fmt.Errorf("failed to clear pull request comments: %w", err)
|
||||
if err := c.repo.ClearAllPullRequestFileComments(ctx, options.PR); err != nil {
|
||||
return &provisioning.JobStatus{
|
||||
State: provisioning.JobStateError,
|
||||
Message: fmt.Sprintf("failed to clear pull request comments: %+v", err),
|
||||
}, nil
|
||||
}
|
||||
|
||||
if len(files) == 0 {
|
||||
logger.Info("no files to process")
|
||||
return nil
|
||||
return &provisioning.JobStatus{
|
||||
State: provisioning.JobStateSuccess,
|
||||
Message: "no files to process",
|
||||
}, nil
|
||||
}
|
||||
|
||||
previews := make([]resourcePreview, 0, len(files))
|
||||
@@ -145,7 +164,10 @@ func (c *PullRequestCommenter) Process(ctx context.Context, job provisioning.Job
|
||||
logger := logger.With("file", f.Path)
|
||||
fileInfo, err := c.repo.Read(ctx, f.Path, ref)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read file %s: %w", f.Path, err)
|
||||
return &provisioning.JobStatus{
|
||||
State: provisioning.JobStateError,
|
||||
Message: fmt.Sprintf("failed to read file %s: %+v", f.Path, err),
|
||||
}, nil
|
||||
}
|
||||
|
||||
parsed, err := c.parser.Parse(ctx, fileInfo, true)
|
||||
@@ -161,11 +183,11 @@ func (c *PullRequestCommenter) Process(ctx context.Context, job provisioning.Job
|
||||
if cfg.Linting && cfg.GitHub.PullRequestLinter && len(parsed.Lint) > 0 && f.Action != repository.FileActionDeleted {
|
||||
var buf bytes.Buffer
|
||||
if err := c.lintTemplate.Execute(&buf, parsed.Lint); err != nil {
|
||||
return fmt.Errorf("execute lint comment template: %w", err)
|
||||
return nil, fmt.Errorf("execute lint comment template: %w", err)
|
||||
}
|
||||
|
||||
if err := c.repo.CommentPullRequestFile(ctx, spec.PR, f.Path, ref, buf.String()); err != nil {
|
||||
return fmt.Errorf("comment pull request file %s: %w", f.Path, err)
|
||||
if err := c.repo.CommentPullRequestFile(ctx, options.PR, f.Path, ref, buf.String()); err != nil {
|
||||
return nil, fmt.Errorf("comment pull request file %s: %w", f.Path, err)
|
||||
}
|
||||
|
||||
logger.Info("lint comment added")
|
||||
@@ -180,23 +202,23 @@ func (c *PullRequestCommenter) Process(ctx context.Context, job provisioning.Job
|
||||
|
||||
switch f.Action {
|
||||
case repository.FileActionCreated:
|
||||
preview.PreviewURL = c.previewURL(ref, f.Path, spec.URL)
|
||||
preview.PreviewURL = c.previewURL(ref, f.Path, options.URL)
|
||||
case repository.FileActionUpdated:
|
||||
preview.OriginalURL = c.previewURL(base, f.Path, spec.URL)
|
||||
preview.PreviewURL = c.previewURL(ref, f.Path, spec.URL)
|
||||
preview.OriginalURL = c.previewURL(base, f.Path, options.URL)
|
||||
preview.PreviewURL = c.previewURL(ref, f.Path, options.URL)
|
||||
case repository.FileActionRenamed:
|
||||
preview.OriginalURL = c.previewURL(base, f.PreviousPath, spec.URL)
|
||||
preview.PreviewURL = c.previewURL(ref, f.Path, spec.URL)
|
||||
preview.OriginalURL = c.previewURL(base, f.PreviousPath, options.URL)
|
||||
preview.PreviewURL = c.previewURL(ref, f.Path, options.URL)
|
||||
case repository.FileActionDeleted:
|
||||
preview.OriginalURL = c.previewURL(base, f.Path, spec.URL)
|
||||
preview.OriginalURL = c.previewURL(base, f.Path, options.URL)
|
||||
default:
|
||||
return fmt.Errorf("unknown file action: %s", f.Action)
|
||||
return nil, fmt.Errorf("unknown file action: %s", f.Action)
|
||||
}
|
||||
|
||||
if cfg.GitHub.GenerateDashboardPreviews && f.Action != repository.FileActionDeleted {
|
||||
screenshotURL, err := c.renderer.RenderDashboardPreview(ctx, f.Path, ref)
|
||||
if err != nil {
|
||||
return fmt.Errorf("render dashboard preview: %w", err)
|
||||
return nil, fmt.Errorf("render dashboard preview: %w", err)
|
||||
}
|
||||
preview.PreviewScreenshotURL = screenshotURL
|
||||
|
||||
@@ -209,21 +231,24 @@ func (c *PullRequestCommenter) Process(ctx context.Context, job provisioning.Job
|
||||
if len(previews) > 0 && cfg.GitHub.GenerateDashboardPreviews {
|
||||
var buf bytes.Buffer
|
||||
if err := c.prevTemplate.Execute(&buf, previews); err != nil {
|
||||
return fmt.Errorf("execute previews comment template: %w", err)
|
||||
return nil, fmt.Errorf("execute previews comment template: %w", err)
|
||||
}
|
||||
|
||||
if err := c.repo.CommentPullRequest(ctx, spec.PR, buf.String()); err != nil {
|
||||
return fmt.Errorf("comment pull request: %w", err)
|
||||
if err := c.repo.CommentPullRequest(ctx, options.PR, buf.String()); err != nil {
|
||||
return nil, fmt.Errorf("comment pull request: %w", err)
|
||||
}
|
||||
|
||||
logger.Info("previews comment added", "number", len(previews))
|
||||
}
|
||||
|
||||
return nil
|
||||
return &provisioning.JobStatus{
|
||||
State: provisioning.JobStateSuccess,
|
||||
Message: "finished", // can update with useful feedback
|
||||
}, nil
|
||||
}
|
||||
|
||||
// previewURL returns the URL to preview the file in Grafana
|
||||
func (c *PullRequestCommenter) previewURL(ref, filePath, pullRequestURL string) string {
|
||||
func (c *pullRequestCommenter) previewURL(ref, filePath, pullRequestURL string) string {
|
||||
// Copy the baseURL to modify path and query
|
||||
baseURL := *c.baseURL
|
||||
baseURL = *baseURL.JoinPath("/admin/provisioning", c.repo.Config().GetName(), "dashboard/preview", filePath)
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apiserver/pkg/endpoints/request"
|
||||
|
||||
@@ -159,14 +160,17 @@ func (g *JobWorker) Process(ctx context.Context, job provisioning.Job, progress
|
||||
id: id,
|
||||
}
|
||||
|
||||
options := job.Spec.PullRequest
|
||||
if options == nil {
|
||||
return nil, apierrors.NewBadRequest("missing spec.pr")
|
||||
}
|
||||
|
||||
commenter, err := NewPullRequestCommenter(prRepo, parser, renderer, baseURL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error creating pull request commenter: %w", err)
|
||||
}
|
||||
return commenter.ProcessPullRequest(ctx, repo, *options, progress)
|
||||
|
||||
if err := commenter.Process(ctx, job); err != nil {
|
||||
return nil, fmt.Errorf("error processing pull request: %w", err)
|
||||
}
|
||||
case provisioning.JobActionExport:
|
||||
if job.Spec.Export == nil {
|
||||
return &provisioning.JobStatus{
|
||||
|
||||
@@ -522,10 +522,12 @@ func (r *githubRepository) parsePullRequestEvent(event *github.PullRequestEvent)
|
||||
Job: &provisioning.JobSpec{
|
||||
Repository: r.Config().GetName(),
|
||||
Action: provisioning.JobActionPullRequest,
|
||||
URL: pr.GetHTMLURL(),
|
||||
PR: pr.GetNumber(),
|
||||
Ref: pr.GetHead().GetRef(),
|
||||
Hash: pr.GetHead().GetSHA(),
|
||||
PullRequest: &provisioning.PullRequestOptions{
|
||||
URL: pr.GetHTMLURL(),
|
||||
PR: pr.GetNumber(),
|
||||
Ref: pr.GetHead().GetRef(),
|
||||
Hash: pr.GetHead().GetSHA(),
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -68,10 +68,12 @@ func TestParseWebhooks(t *testing.T) {
|
||||
Job: &provisioning.JobSpec{
|
||||
Repository: "unit-test-repo",
|
||||
Action: provisioning.JobActionPullRequest,
|
||||
Ref: "dashboard/1733653266690",
|
||||
Hash: "ab5446a53df9e5f8bdeed52250f51fad08e822bc",
|
||||
PR: 12,
|
||||
URL: "https://github.com/grafana/git-ui-sync-demo/pull/12",
|
||||
PullRequest: &provisioning.PullRequestOptions{
|
||||
Ref: "dashboard/1733653266690",
|
||||
Hash: "ab5446a53df9e5f8bdeed52250f51fad08e822bc",
|
||||
PR: 12,
|
||||
URL: "https://github.com/grafana/git-ui-sync-demo/pull/12",
|
||||
},
|
||||
},
|
||||
}},
|
||||
{"push", "different_branch", provisioning.WebhookResponse{
|
||||
|
||||
@@ -43,7 +43,7 @@ func TestIntegrationProvisioning(t *testing.T) {
|
||||
AppModeProduction: false, // required for experimental APIs
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagProvisioning,
|
||||
featuremgmt.FlagKubernetesFolders, // Required for tests that deal with folders.
|
||||
featuremgmt.FlagKubernetesFoldersServiceV2, // Required for tests that deal with folders.
|
||||
featuremgmt.FlagUnifiedStorageSearch,
|
||||
},
|
||||
PermittedProvisioningPaths: ".|" + provisioningPath,
|
||||
|
||||
+82
-15
@@ -3782,10 +3782,47 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.JobResourceSummary": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"create": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"delete": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"errors": {
|
||||
"description": "Report errors for this resource type This may not be an exhaustive list and recommend looking at the logs for more info",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
}
|
||||
},
|
||||
"group": {
|
||||
"type": "string"
|
||||
},
|
||||
"noop": {
|
||||
"description": "No action required (useful for sync)",
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
},
|
||||
"resource": {
|
||||
"type": "string"
|
||||
},
|
||||
"update": {
|
||||
"type": "integer",
|
||||
"format": "int64"
|
||||
}
|
||||
}
|
||||
},
|
||||
"com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.JobSpec": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"action"
|
||||
"action",
|
||||
"repository"
|
||||
],
|
||||
"properties": {
|
||||
"action": {
|
||||
@@ -3806,21 +3843,18 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"hash": {
|
||||
"type": "string"
|
||||
},
|
||||
"pr": {
|
||||
"description": "Pull request number (when appropriate)",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
"description": "Pull request options",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.PullRequestOptions"
|
||||
}
|
||||
]
|
||||
},
|
||||
"ref": {
|
||||
"description": "The branch of commit hash",
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
"description": "URL to the originator (eg, PR URL)",
|
||||
"type": "string"
|
||||
"repository": {
|
||||
"description": "The the repository reference (for now also in labels)",
|
||||
"type": "string",
|
||||
"default": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -3843,7 +3877,7 @@
|
||||
"type": "string"
|
||||
},
|
||||
"progress": {
|
||||
"description": "Optional value 0-1 that can be set while running",
|
||||
"description": "Optional value 0-100 that can be set while running",
|
||||
"type": "number",
|
||||
"format": "double"
|
||||
},
|
||||
@@ -3860,6 +3894,18 @@
|
||||
"success",
|
||||
"working"
|
||||
]
|
||||
},
|
||||
"summary": {
|
||||
"description": "Summary of processed actions",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"default": {},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.JobResourceSummary"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -3901,6 +3947,27 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.PullRequestOptions": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"hash": {
|
||||
"type": "string"
|
||||
},
|
||||
"pr": {
|
||||
"description": "Pull request number (when appropriate)",
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"ref": {
|
||||
"description": "The branch of commit hash",
|
||||
"type": "string"
|
||||
},
|
||||
"url": {
|
||||
"description": "URL to the originator (eg, PR URL)",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"com.github.grafana.grafana.pkg.apis.provisioning.v0alpha1.Repository": {
|
||||
"description": "When this code is changed, make sure to update the code generation. As of writing, this can be done via the hack dir in the root of the repo: ./hack/update-codegen.sh provisioning If you've opened the generated files in this dir at some point in VSCode, you may also have to re-open them to clear errors.",
|
||||
"type": "object",
|
||||
|
||||
@@ -435,14 +435,27 @@ export type ExportOptions = {
|
||||
history?: boolean;
|
||||
prefix?: string;
|
||||
};
|
||||
export type JobSpec = {
|
||||
action: 'export' | 'pr' | 'sync';
|
||||
export?: ExportOptions;
|
||||
export type PullRequestOptions = {
|
||||
hash?: string;
|
||||
pr?: number;
|
||||
ref?: string;
|
||||
url?: string;
|
||||
};
|
||||
export type JobSpec = {
|
||||
action: 'export' | 'pr' | 'sync';
|
||||
export?: ExportOptions;
|
||||
pr?: PullRequestOptions;
|
||||
repository: string;
|
||||
};
|
||||
export type JobResourceSummary = {
|
||||
create?: number;
|
||||
delete?: number;
|
||||
errors?: string[];
|
||||
group?: string;
|
||||
noop?: number;
|
||||
resource?: string;
|
||||
update?: number;
|
||||
};
|
||||
export type JobStatus = {
|
||||
errors?: string[];
|
||||
finished?: number;
|
||||
@@ -450,6 +463,7 @@ export type JobStatus = {
|
||||
progress?: number;
|
||||
started?: number;
|
||||
state?: 'error' | 'pending' | 'success' | 'working';
|
||||
summary?: JobResourceSummary[];
|
||||
};
|
||||
export type Job = {
|
||||
apiVersion?: string;
|
||||
|
||||
Reference in new issue
Block a user