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(repo settings): approve build mechanism for pull_request events #328

Merged
merged 9 commits into from
Nov 27, 2023
18 changes: 18 additions & 0 deletions constants/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,21 @@ const (
// in Vela to control their pipeline being compiled as Starlark templates.
PipelineTypeStarlark = "starlark"
)

// Repo ApproveBuild types.
const (
// ApproveForkAlways defines the CI strategy of having a repo administrator approve
// all builds triggered from a forked PR.
ApproveForkAlways = "fork-always"

// ApproveForkNoWrite defines the CI strategy of having a repo administrator approve
// all builds triggered from a forked PR where the author does not have write access.
ApproveForkNoWrite = "fork-no-write"

// ApproveOnce defines the CI strategy of having a repo administrator approve
// all builds triggered from an outside contributor if this is their first time contributing.
ApproveOnce = "first-time"
Comment on lines +30 to +32
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A note on this: I am adding this constant as more of a To-Do. I don't have actual functionality for this coded up, but would create a follow-up story to implement this at a later point.


// ApproveNever defines the CI strategy of never having to approve CI builds from outside contributors.
ApproveNever = "never"
)
3 changes: 3 additions & 0 deletions constants/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ const (
// StatusPending defines the status type for build and step pending statuses.
StatusPending = "pending"

// StatusPendingApproval defines the status type for a build waiting to be approved to run.
StatusPendingApproval = "pending approval"

// StatusRunning defines the status type for build and step running statuses.
StatusRunning = "running"

Expand Down
19 changes: 18 additions & 1 deletion database/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ type Build struct {
Host sql.NullString `sql:"host"`
Runtime sql.NullString `sql:"runtime"`
Distribution sql.NullString `sql:"distribution"`
ApprovedAt sql.NullInt64 `sql:"approved_at"`
ApprovedBy sql.NullString `sql:"approved_by"`
}

// Crop prepares the Build type for inserting into the database by
Expand Down Expand Up @@ -92,7 +94,7 @@ func (b *Build) Crop() *Build {
// value for the field, the valid flag is set to
// false causing it to be NULL in the database.
//
//nolint:gocyclo // ignore cyclomatic complexity due to number of fields
//nolint:gocyclo,funlen // ignore cyclomatic complexity due to number of fields
func (b *Build) Nullify() *Build {
if b == nil {
return nil
Expand Down Expand Up @@ -248,6 +250,16 @@ func (b *Build) Nullify() *Build {
b.Distribution.Valid = false
}

// check if the ApprovedAt field should be false
if b.ApprovedAt.Int64 == 0 {
b.ApprovedAt.Valid = false
}

// check if the ApprovedBy field should be false
if len(b.ApprovedBy.String) == 0 {
b.ApprovedBy.Valid = false
}

return b
}

Expand Down Expand Up @@ -287,6 +299,8 @@ func (b *Build) ToLibrary() *library.Build {
build.SetHost(b.Host.String)
build.SetRuntime(b.Runtime.String)
build.SetDistribution(b.Distribution.String)
build.SetApprovedAt(b.ApprovedAt.Int64)
build.SetApprovedBy(b.ApprovedBy.String)

return build
}
Expand Down Expand Up @@ -328,6 +342,7 @@ func (b *Build) Validate() error {
b.Host = sql.NullString{String: sanitize(b.Host.String), Valid: b.Host.Valid}
b.Runtime = sql.NullString{String: sanitize(b.Runtime.String), Valid: b.Runtime.Valid}
b.Distribution = sql.NullString{String: sanitize(b.Distribution.String), Valid: b.Distribution.Valid}
b.ApprovedBy = sql.NullString{String: sanitize(b.ApprovedBy.String), Valid: b.ApprovedBy.Valid}

return nil
}
Expand Down Expand Up @@ -367,6 +382,8 @@ func BuildFromLibrary(b *library.Build) *Build {
Host: sql.NullString{String: b.GetHost(), Valid: true},
Runtime: sql.NullString{String: b.GetRuntime(), Valid: true},
Distribution: sql.NullString{String: b.GetDistribution(), Valid: true},
ApprovedAt: sql.NullInt64{Int64: b.GetApprovedAt(), Valid: true},
ApprovedBy: sql.NullString{String: b.GetApprovedBy(), Valid: true},
}

return build.Nullify()
Expand Down
6 changes: 6 additions & 0 deletions database/build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ func TestDatabase_Build_ToLibrary(t *testing.T) {
want.SetRuntime("docker")
want.SetDistribution("linux")
want.SetDeployPayload(raw.StringSliceMap{"foo": "test1", "bar": "test2"})
want.SetApprovedAt(1563474076)
want.SetApprovedBy("OctoCat")

// run test
got := testBuild().ToLibrary()
Expand Down Expand Up @@ -228,6 +230,8 @@ func TestDatabase_BuildFromLibrary(t *testing.T) {
b.SetRuntime("docker")
b.SetDistribution("linux")
b.SetDeployPayload(raw.StringSliceMap{"foo": "test1", "bar": "test2"})
b.SetApprovedAt(1563474076)
b.SetApprovedBy("OctoCat")

want := testBuild()

Expand Down Expand Up @@ -286,5 +290,7 @@ func testBuild() *Build {
Host: sql.NullString{String: "example.company.com", Valid: true},
Runtime: sql.NullString{String: "docker", Valid: true},
Distribution: sql.NullString{String: "linux", Valid: true},
ApprovedAt: sql.NullInt64{Int64: 1563474076, Valid: true},
ApprovedBy: sql.NullString{String: "OctoCat", Valid: true},
}
}
8 changes: 8 additions & 0 deletions database/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ type Repo struct {
AllowComment sql.NullBool `sql:"allow_comment"`
PipelineType sql.NullString `sql:"pipeline_type"`
PreviousName sql.NullString `sql:"previous_name"`
ApproveBuild sql.NullString `sql:"approve_build"`
}

// Decrypt will manipulate the existing repo hash by
Expand Down Expand Up @@ -198,6 +199,11 @@ func (r *Repo) Nullify() *Repo {
r.PreviousName.Valid = false
}

// check if the ApproveForkBuild field should be false
if len(r.ApproveBuild.String) == 0 {
r.ApproveBuild.Valid = false
}

return r
}

Expand Down Expand Up @@ -230,6 +236,7 @@ func (r *Repo) ToLibrary() *library.Repo {
repo.SetAllowComment(r.AllowComment.Bool)
repo.SetPipelineType(r.PipelineType.String)
repo.SetPreviousName(r.PreviousName.String)
repo.SetApproveBuild(r.ApproveBuild.String)

return repo
}
Expand Down Expand Up @@ -325,6 +332,7 @@ func RepoFromLibrary(r *library.Repo) *Repo {
AllowComment: sql.NullBool{Bool: r.GetAllowComment(), Valid: true},
PipelineType: sql.NullString{String: r.GetPipelineType(), Valid: true},
PreviousName: sql.NullString{String: r.GetPreviousName(), Valid: true},
ApproveBuild: sql.NullString{String: r.GetApproveBuild(), Valid: true},
}

return repo.Nullify()
Expand Down
5 changes: 5 additions & 0 deletions database/repo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"reflect"
"testing"

"github.com/go-vela/types/constants"
"github.com/go-vela/types/library"
)

Expand Down Expand Up @@ -118,6 +119,7 @@ func TestDatabase_Repo_Nullify(t *testing.T) {
Timeout: sql.NullInt64{Int64: 0, Valid: false},
Visibility: sql.NullString{String: "", Valid: false},
PipelineType: sql.NullString{String: "", Valid: false},
ApproveBuild: sql.NullString{String: "", Valid: false},
}

// setup tests
Expand Down Expand Up @@ -177,6 +179,7 @@ func TestDatabase_Repo_ToLibrary(t *testing.T) {
want.SetAllowComment(false)
want.SetPipelineType("yaml")
want.SetPreviousName("oldName")
want.SetApproveBuild(constants.ApproveNever)

// run test
got := testRepo().ToLibrary()
Expand Down Expand Up @@ -330,6 +333,7 @@ func TestDatabase_RepoFromLibrary(t *testing.T) {
r.SetAllowComment(false)
r.SetPipelineType("yaml")
r.SetPreviousName("oldName")
r.SetApproveBuild(constants.ApproveNever)

want := testRepo()

Expand Down Expand Up @@ -369,5 +373,6 @@ func testRepo() *Repo {
AllowComment: sql.NullBool{Bool: false, Valid: true},
PipelineType: sql.NullString{String: "yaml", Valid: true},
PreviousName: sql.NullString{String: "oldName", Valid: true},
ApproveBuild: sql.NullString{String: constants.ApproveNever, Valid: true},
}
}
60 changes: 60 additions & 0 deletions library/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ type Build struct {
Host *string `json:"host,omitempty"`
Runtime *string `json:"runtime,omitempty"`
Distribution *string `json:"distribution,omitempty"`
ApprovedAt *int64 `json:"approved_at,omitempty"`
ApprovedBy *string `json:"approved_by,omitempty"`
}

// Duration calculates and returns the total amount of
Expand Down Expand Up @@ -83,6 +85,8 @@ func (b *Build) Duration() string {
// provided from the fields of the Build type.
func (b *Build) Environment(workspace, channel string) map[string]string {
envs := map[string]string{
"VELA_BUILD_APPROVED_AT": ToString(b.GetApprovedAt()),
"VELA_BUILD_APPROVED_BY": ToString(b.GetApprovedBy()),
"VELA_BUILD_AUTHOR": ToString(b.GetAuthor()),
"VELA_BUILD_AUTHOR_EMAIL": ToString(b.GetEmail()),
"VELA_BUILD_BASE_REF": ToString(b.GetBaseRef()),
Expand Down Expand Up @@ -600,6 +604,32 @@ func (b *Build) GetDistribution() string {
return *b.Distribution
}

// GetApprovedAt returns the ApprovedAt field.
//
// When the provided Build type is nil, or the field within
// the type is nil, it returns the zero value for the field.
func (b *Build) GetApprovedAt() int64 {
// return zero value if Build type or ApprovedAt field is nil
if b == nil || b.ApprovedAt == nil {
return 0
}

return *b.ApprovedAt
}

// GetApprovedBy returns the ApprovedBy field.
//
// When the provided Build type is nil, or the field within
// the type is nil, it returns the zero value for the field.
func (b *Build) GetApprovedBy() string {
// return zero value if Build type or ApprovedBy field is nil
if b == nil || b.ApprovedBy == nil {
return ""
}

return *b.ApprovedBy
}

// SetID sets the ID field.
//
// When the provided Build type is nil, it
Expand Down Expand Up @@ -1003,11 +1033,39 @@ func (b *Build) SetDistribution(v string) {
b.Distribution = &v
}

// SetApprovedAt sets the ApprovedAt field.
//
// When the provided Build type is nil, it
// will set nothing and immediately return.
func (b *Build) SetApprovedAt(v int64) {
// return if Build type is nil
if b == nil {
return
}

b.ApprovedAt = &v
}

// SetApprovedBy sets the ApprovedBy field.
//
// When the provided Build type is nil, it
// will set nothing and immediately return.
func (b *Build) SetApprovedBy(v string) {
// return if Build type is nil
if b == nil {
return
}

b.ApprovedBy = &v
}

// String implements the Stringer interface for the Build type.
//
//nolint:dupl // this is duplicated in the test
func (b *Build) String() string {
return fmt.Sprintf(`{
ApprovedAt: %d,
ApprovedBy: %s,
Author: %s,
BaseRef: %s,
Branch: %s,
Expand Down Expand Up @@ -1040,6 +1098,8 @@ func (b *Build) String() string {
Status: %s,
Title: %s,
}`,
b.GetApprovedAt(),
b.GetApprovedBy(),
b.GetAuthor(),
b.GetBaseRef(),
b.GetBranch(),
Expand Down
Loading