mirror of
https://github.com/hashicorp/vault.git
synced 2025-11-15 07:41:34 +01:00
* [VAULT-39890] pipeline(github): add list commit-statuses command * [VAULT-39890] pipeline(github): add check commit-status command * [VAULT-39890] actions(copy-pr): enforce license/cla before triggering copy workflow Signed-off-by: Ryan Cragun <me@ryan.ec> Co-authored-by: Ryan Cragun <me@ryan.ec>
160 lines
4.3 KiB
Go
160 lines
4.3 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package github
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"slices"
|
|
"strings"
|
|
|
|
libgithub "github.com/google/go-github/v74/github"
|
|
"github.com/jedib0t/go-pretty/v6/table"
|
|
)
|
|
|
|
// CheckCommitStatusReq is a request to list workflows runs. The fields represent
|
|
// various criteria we can use to filter.
|
|
type CheckCommitStatusReq struct {
|
|
Owner string
|
|
Repo string
|
|
Commit string
|
|
PR int
|
|
Context string
|
|
Creator string
|
|
State string
|
|
}
|
|
|
|
// CheckCommitStatusRes is a list workflows response.
|
|
type CheckCommitStatusRes struct {
|
|
CheckSuccessful bool `json:"check_success,omitempty"`
|
|
ExpectedContext string `json:"expected_context,omitempty"`
|
|
ExpectedState string `json:"expected_state,omitempty"`
|
|
ExpectedCreator string `json:"expected_creator,omitempty"`
|
|
Statuses []*CheckCommitStatus `json:"statuses,omitempty"`
|
|
}
|
|
|
|
type CheckCommitStatus struct {
|
|
Status *libgithub.RepoStatus `json:"statuses,omitempty"`
|
|
CheckSuccess bool `json:"success,omitempty"`
|
|
}
|
|
|
|
// String returns the response as a string
|
|
func (r *CheckCommitStatusRes) String() string {
|
|
b := strings.Builder{}
|
|
b.WriteString(fmt.Sprintf("success:%t", r.CheckSuccessful))
|
|
b.WriteString(" context:" + r.ExpectedContext)
|
|
b.WriteString(" state:" + r.ExpectedState)
|
|
b.WriteString(" creator:" + r.ExpectedCreator)
|
|
|
|
return b.String()
|
|
}
|
|
|
|
// Run runs the request to gather all instances of the workflow that match
|
|
// our filter criteria.
|
|
func (r *CheckCommitStatusReq) Run(ctx context.Context, client *libgithub.Client) (*CheckCommitStatusRes, error) {
|
|
var err error
|
|
res := &CheckCommitStatusRes{
|
|
CheckSuccessful: false,
|
|
ExpectedCreator: r.Creator,
|
|
ExpectedState: r.State,
|
|
ExpectedContext: r.Context,
|
|
Statuses: []*CheckCommitStatus{},
|
|
}
|
|
|
|
if err = r.validate(); err != nil {
|
|
return nil, fmt.Errorf("validating request: %w", err)
|
|
}
|
|
|
|
statusesReq := &ListCommitStatusesReq{
|
|
Owner: r.Owner,
|
|
Repo: r.Repo,
|
|
Commit: r.Commit,
|
|
PR: r.PR,
|
|
}
|
|
statuses, err := statusesReq.Run(ctx, client)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for _, status := range statuses.Statuses {
|
|
if status.GetContext() != r.Context {
|
|
continue
|
|
}
|
|
|
|
if r.Creator != "" && status.GetCreator().GetLogin() != r.Creator {
|
|
continue
|
|
}
|
|
|
|
// There can be many statuses with the same context and creator. Keep track
|
|
// of them all but only update our success if we get a match.
|
|
res.Statuses = append(res.Statuses, &CheckCommitStatus{
|
|
Status: status,
|
|
CheckSuccess: status.GetState() == r.State,
|
|
})
|
|
|
|
if status.GetState() == r.State {
|
|
res.CheckSuccessful = true
|
|
}
|
|
}
|
|
|
|
return res, nil
|
|
}
|
|
|
|
// validate ensures that we've been given the minimum filter arguments necessary to complete a
|
|
// request. It is always recommended that additional fitlers be given to reduce the response size
|
|
// and not exhaust API limits.
|
|
func (r *CheckCommitStatusReq) validate() error {
|
|
if r == nil {
|
|
return errors.New("failed to initialize request")
|
|
}
|
|
|
|
if r.Owner == "" {
|
|
return errors.New("no github organization has been provided")
|
|
}
|
|
|
|
if r.Repo == "" {
|
|
return errors.New("no github repository has been provided")
|
|
}
|
|
|
|
if r.Context == "" {
|
|
return errors.New("no status context has been provided")
|
|
}
|
|
|
|
if r.PR == 0 && r.Commit == "" {
|
|
return errors.New("no commit or Pull Request number has been provided")
|
|
}
|
|
|
|
allowedStates := []string{"error", "failure", "pending", "success"}
|
|
if !slices.Contains(allowedStates, r.State) {
|
|
return fmt.Errorf("invalid state, got: %s, expected one of: %v+", r.State, allowedStates)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ToTable marshals the response to a text table.
|
|
func (r *CheckCommitStatusRes) ToTable() table.Writer {
|
|
t := table.NewWriter()
|
|
t.Style().Options.DrawBorder = false
|
|
t.Style().Options.SeparateColumns = false
|
|
t.Style().Options.SeparateFooter = false
|
|
t.Style().Options.SeparateHeader = false
|
|
t.Style().Options.SeparateRows = false
|
|
t.AppendHeader(table.Row{"context", "creator", "date", "state", "check success"})
|
|
for _, status := range r.Statuses {
|
|
t.AppendRow(table.Row{
|
|
status.Status.GetContext(),
|
|
status.Status.GetCreator().GetLogin(),
|
|
status.Status.GetUpdatedAt(),
|
|
status.Status.GetState(),
|
|
status.CheckSuccess,
|
|
})
|
|
}
|
|
t.SuppressEmptyColumns()
|
|
t.SuppressTrailingSpaces()
|
|
|
|
return t
|
|
}
|