Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add types, fix errors, add conditional status report #1071

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions api/step/plan.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,18 @@ func planStep(ctx context.Context, database database.Interface, scm scm.Service,
s.SetReportAs(c.ReportAs)
s.SetCreated(time.Now().UTC().Unix())

id, err := scm.CreateChecks(ctx, r, b.GetCommit(), s.GetName())
if err != nil {
// TODO: make this error more meaningful
return nil, err
}
if c.ReportStatus {
id, err := scm.CreateChecks(ctx, r, b.GetCommit(), s.GetName(), b.GetEvent())
if err != nil {
// TODO: make this error more meaningful
return nil, err
}

// TODO: have to store the check ID somewhere
s.SetCheckID(id)
s.SetCheckID(id)
}

// send API call to create the step
s, err = database.CreateStep(ctx, s)
s, err := database.CreateStep(ctx, s)
if err != nil {
return nil, fmt.Errorf("unable to create step %s: %w", s.GetName(), err)
}
Expand Down
14 changes: 9 additions & 5 deletions api/step/update.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,13 +154,17 @@ func UpdateStep(c *gin.Context) {
return
}

err = scm.FromContext(c).UpdateChecks(ctx, r, s, b.GetCommit())
if err != nil {
retErr := fmt.Errorf("unable to set step check %s: %w", entry, err)
if s.GetCheckID() != 0 {
s.SetReport(input.GetReport())

util.HandleError(c, http.StatusInternalServerError, retErr)
err = scm.FromContext(c).UpdateChecks(ctx, r, s, b.GetCommit(), b.GetEvent())
if err != nil {
retErr := fmt.Errorf("unable to set step check %s: %w", entry, err)

return
util.HandleError(c, http.StatusInternalServerError, retErr)

return
}
}

c.JSON(http.StatusOK, s)
Expand Down
27 changes: 27 additions & 0 deletions api/types/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type Repo struct {
PipelineType *string `json:"pipeline_type,omitempty"`
PreviousName *string `json:"previous_name,omitempty"`
ApproveBuild *string `json:"approve_build,omitempty"`
InstallID *int64 `json:"install_id,omitempty"`
}

// Environment returns a list of environment variables
Expand Down Expand Up @@ -345,6 +346,19 @@ func (r *Repo) GetApproveBuild() string {
return *r.ApproveBuild
}

// GetInstallID returns the InstallID field.
//
// When the provided Repo type is nil, or the field within
// the type is nil, it returns the zero value for the field.
func (r *Repo) GetInstallID() int64 {
// return zero value if Repo type or InstallID field is nil
if r == nil || r.InstallID == nil {
return 0
}

return *r.InstallID
}

// SetID sets the ID field.
//
// When the provided Repo type is nil, it
Expand Down Expand Up @@ -618,6 +632,19 @@ func (r *Repo) SetApproveBuild(v string) {
r.ApproveBuild = &v
}

// SetInstallID sets the InstallID field.
//
// When the provided Repo type is nil, it
// will set nothing and immediately return.
func (r *Repo) SetInstallID(v int64) {
// return if Repo type is nil
if r == nil {
return
}

r.InstallID = &v
}

// String implements the Stringer interface for the Repo type.
func (r *Repo) String() string {
return fmt.Sprintf(`{
Expand Down
2 changes: 2 additions & 0 deletions database/repo/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ repos (
pipeline_type TEXT,
previous_name VARCHAR(100),
approve_build VARCHAR(20),
install_id INTEGER,
UNIQUE(full_name)
);
`
Expand Down Expand Up @@ -65,6 +66,7 @@ repos (
pipeline_type TEXT,
previous_name TEXT,
approve_build TEXT,
install_id INTEGER,
UNIQUE(full_name)
);
`
Expand Down
2 changes: 2 additions & 0 deletions database/step/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ steps (
host VARCHAR(250),
runtime VARCHAR(250),
distribution VARCHAR(250),
check_id INTEGER,
report_as VARCHAR(250),
UNIQUE(build_id, number)
);
Expand All @@ -56,6 +57,7 @@ steps (
host TEXT,
runtime TEXT,
distribution TEXT,
check_id INTEGER,
report_as TEXT,
UNIQUE(build_id, number)
);
Expand Down
41 changes: 34 additions & 7 deletions scm/github/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ package github

import (
"context"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"fmt"
"net/http"
"net/url"
Expand Down Expand Up @@ -118,11 +121,24 @@ func New(opts ...ClientOpt) (*client, error) {

if c.config.GithubAppID != 0 && len(c.config.GithubAppPrivateKey) > 0 {
c.Logger.Infof("sourcing private key from path: %s", c.config.GithubAppPrivateKey)
transport, err := ghinstallation.NewAppsTransportKeyFromFile(http.DefaultTransport, c.config.GithubAppID, c.config.GithubAppPrivateKey)

decodedPEM, err := base64.StdEncoding.DecodeString(c.config.GithubAppPrivateKey)
if err != nil {
return nil, err
return nil, fmt.Errorf("error decoding base64: %w", err)
}

block, _ := pem.Decode(decodedPEM)
if block == nil {
return nil, fmt.Errorf("failed to parse PEM block containing the key")
}

privateKey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return nil, fmt.Errorf("failed to parse RSA private key: %w", err)
}

transport := ghinstallation.NewAppsTransportFromPrivateKey(http.DefaultTransport, c.config.GithubAppID, privateKey)

transport.BaseURL = c.config.API
c.AppsTransport = transport
}
Expand Down Expand Up @@ -184,17 +200,28 @@ func (c *client) newClientToken(token string) *github.Client {
}

// helper function to return the GitHub App token.
func (c *client) newGithubAppToken(r *api.Repo) *github.Client {
func (c *client) newGithubAppToken(r *api.Repo) (*github.Client, error) {
// create a github client based off the existing GitHub App configuration
client, err := github.NewClient(&http.Client{Transport: c.AppsTransport}).WithEnterpriseURLs(c.config.API, c.config.API)
if err != nil {
panic(err)
return nil, err
}

// if repo has an install ID, use it to create an installation token
if r.GetInstallID() != 0 {
// create installation token for the repo
t, _, err := client.Apps.CreateInstallationToken(context.Background(), r.GetInstallID(), &github.InstallationTokenOptions{})
if err != nil {
panic(err)
}

return c.newClientToken(t.GetToken()), nil
}

// list all installations (a.k.a. orgs) where the GitHub App is installed
installations, _, err := client.Apps.ListInstallations(context.Background(), &github.ListOptions{})
if err != nil {
panic(err)
return nil, err
}

var id int64
Expand All @@ -208,7 +235,7 @@ func (c *client) newGithubAppToken(r *api.Repo) *github.Client {

// failsafe in case the repo does not belong to an org where the GitHub App is installed
if id == 0 {
panic(err)
return nil, err
}

// create installation token for the repo
Expand All @@ -217,5 +244,5 @@ func (c *client) newGithubAppToken(r *api.Repo) *github.Client {
panic(err)
}

return c.newClientToken(t.GetToken())
return c.newClientToken(t.GetToken()), nil
}
48 changes: 41 additions & 7 deletions scm/github/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -662,12 +662,15 @@ func (c *client) GetBranch(ctx context.Context, r *api.Repo, branch string) (str
}

// CreateChecks defines a function that does stuff...
func (c *client) CreateChecks(ctx context.Context, r *api.Repo, commit, step string) (int64, error) {
func (c *client) CreateChecks(ctx context.Context, r *api.Repo, commit, step, event string) (int64, error) {
// create client from GitHub App
client := c.newGithubAppToken(r)
client, err := c.newGithubAppToken(r)
if err != nil {
return 0, err
}

opts := github.CreateCheckRunOptions{
Name: fmt.Sprintf("vela-%s-%s", commit, step),
Name: fmt.Sprintf("vela-%s-%s", event, step),
HeadSHA: commit,
}

Expand All @@ -680,9 +683,12 @@ func (c *client) CreateChecks(ctx context.Context, r *api.Repo, commit, step str
}

// UpdateChecks defines a function that does stuff...
func (c *client) UpdateChecks(ctx context.Context, r *api.Repo, s *library.Step, commit string) error {
func (c *client) UpdateChecks(ctx context.Context, r *api.Repo, s *library.Step, commit, event string) error {
// create client from GitHub App
client := c.newGithubAppToken(r)
client, err := c.newGithubAppToken(r)
if err != nil {
return err
}

var (
conclusion string
Expand Down Expand Up @@ -719,13 +725,41 @@ func (c *client) UpdateChecks(ctx context.Context, r *api.Repo, s *library.Step,
status = "completed"
}

var annotations []*github.CheckRunAnnotation

for _, reportAnnotation := range s.GetReport().GetAnnotations() {
annotation := &github.CheckRunAnnotation{
Path: github.String(reportAnnotation.GetPath()),
StartLine: github.Int(reportAnnotation.GetStartLine()),
EndLine: github.Int(reportAnnotation.GetEndLine()),
StartColumn: github.Int(reportAnnotation.GetStartColumn()),
EndColumn: github.Int(reportAnnotation.GetEndColumn()),
AnnotationLevel: github.String(reportAnnotation.GetAnnotationLevel()),
Message: github.String(reportAnnotation.GetMessage()),
Title: github.String(reportAnnotation.GetTitle()),
RawDetails: github.String(reportAnnotation.GetRawDetails()),
}

annotations = append(annotations, annotation)
}

output := &github.CheckRunOutput{
Title: github.String(s.GetReport().GetTitle()),
Summary: github.String(s.GetReport().GetSummary()),
Text: github.String(s.GetReport().GetText()),
AnnotationsCount: github.Int(s.GetReport().GetAnnotationsCount()),
AnnotationsURL: github.String(s.GetReport().GetAnnotationsURL()),
Annotations: annotations,
}

opts := github.UpdateCheckRunOptions{
Name: fmt.Sprintf("vela-%s-%s", commit, s.GetName()),
Name: fmt.Sprintf("vela-%s-%s", event, s.GetName()),
Conclusion: github.String(conclusion),
Status: github.String(status),
Output: output,
}

_, _, err := client.Checks.UpdateCheckRun(ctx, r.GetOrg(), r.GetName(), s.GetCheckID(), opts)
_, _, err = client.Checks.UpdateCheckRun(ctx, r.GetOrg(), r.GetName(), s.GetCheckID(), opts)
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions scm/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ type Service interface {
GetHTMLURL(context.Context, *api.User, string, string, string, string) (string, error)

// TODO: add comments
CreateChecks(context.Context, *api.Repo, string, string) (int64, error)
UpdateChecks(context.Context, *api.Repo, *library.Step, string) error
CreateChecks(context.Context, *api.Repo, string, string, string) (int64, error)
UpdateChecks(context.Context, *api.Repo, *library.Step, string, string) error

// Webhook SCM Interface Functions

Expand Down
Loading