Skip to content
Open
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
fix(webhook): generate token for status update on failed compile
  • Loading branch information
ecrupper committed Mar 10, 2026
commit 19fc58482466ae292770ec1648acbbd465feff26
3 changes: 1 addition & 2 deletions api/build/cancel.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (

"github.com/go-vela/server/api/types"
"github.com/go-vela/server/cache"
"github.com/go-vela/server/compiler/types/yaml"
"github.com/go-vela/server/constants"
"github.com/go-vela/server/database"
"github.com/go-vela/server/internal/token"
Expand Down Expand Up @@ -161,7 +160,7 @@ func CancelBuild(c *gin.Context) {
var scmToken string

if b.GetRepo().GetInstallID() != 0 {
scmToken, _, err = scm.FromContext(c).GetNetrcPassword(ctx, database.FromContext(c), cache.FromContext(c), b, yaml.Git{})
scmToken, _, err = scm.FromContext(c).GetNetrcPassword(ctx, database.FromContext(c), cache.FromContext(c), b, nil, nil)
if err != nil {
l.Errorf("unable to generate new installation token for build %s: %v", entry, err)

Expand Down
16 changes: 15 additions & 1 deletion api/webhook/post.go
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,21 @@ func PostWebhook(c *gin.Context) {

util.HandleError(c, code, err)

err = scm.FromContext(c).Status(ctx, b, p.Token)
// send status update with proper token
var scmToken string

if repo.GetInstallID() != 0 {
scmToken, _, err = scm.FromContext(c).GetNetrcPassword(ctx, database.FromContext(c), cache.FromContext(c), b, nil, nil)
if err != nil {
l.Errorf("unable to generate installation token for failed compile status %s: %v", repo.GetFullName(), err)

return
}
} else {
scmToken = repo.GetOwner().GetToken()
}

err = scm.FromContext(c).Status(ctx, b, scmToken)
if err != nil {
l.Debugf("unable to set commit status for %s/%d: %v", repo.GetFullName(), b.GetNumber(), err)
}
Expand Down
2 changes: 1 addition & 1 deletion compiler/native/compile.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ func (c *Client) Compile(ctx context.Context, v any) (*pipeline.Build, *api.Pipe
// netrc can be provided directly using WithNetrc for situations like local exec
if c.netrc == nil && c.scm != nil {
// get the netrc password from the scm
netrc, exp, err := c.scm.GetNetrcPassword(ctx, c.db, c.cache, c.build, p.Git)
netrc, exp, err := c.scm.GetNetrcPassword(ctx, c.db, c.cache, c.build, p.Git.Repositories, p.Git.Permissions)
if err != nil {
return nil, nil, err
}
Expand Down
14 changes: 4 additions & 10 deletions scm/github/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (

api "github.com/go-vela/server/api/types"
"github.com/go-vela/server/cache"
"github.com/go-vela/server/compiler/types/yaml"
"github.com/go-vela/server/constants"
"github.com/go-vela/server/database"
)
Expand Down Expand Up @@ -488,7 +487,7 @@ func (c *Client) GetBranch(ctx context.Context, r *api.Repo, branch string) (str

// GetNetrcPassword returns a clone token using the repo's github app installation if it exists.
// If not, it defaults to the user OAuth token.
func (c *Client) GetNetrcPassword(ctx context.Context, db database.Interface, tknCache cache.Service, b *api.Build, g yaml.Git) (string, int64, error) {
func (c *Client) GetNetrcPassword(ctx context.Context, db database.Interface, tknCache cache.Service, b *api.Build, repos []string, perms map[string]string) (string, int64, error) {
r := b.GetRepo()
u := b.GetRepo().GetOwner()

Expand All @@ -506,11 +505,6 @@ func (c *Client) GetNetrcPassword(ctx context.Context, db database.Interface, tk

var err error

// repos that the token has access to
// providing no repos, nil, or empty slice will default the token permissions to the list
// of repos added to the installation
repos := g.Repositories

// enforce max number of repos allowed for token
//
// this prevents a large number of access checks for the repo owner
Expand Down Expand Up @@ -541,16 +535,16 @@ func (c *Client) GetNetrcPassword(ctx context.Context, db database.Interface, tk
permissions["deployments"] = constants.PermissionWrite
}

if len(g.Permissions) > 0 {
permissions = g.Permissions
if len(perms) > 0 {
permissions = perms

normalizePermissions(permissions)
}

// verify repo owner has `write` access to listed repositories before provisioning install token
//
// this prevents an app installed across the org from bypassing restrictions
for _, repo := range g.Repositories {
for _, repo := range repos {
if repo == r.GetName() {
continue
}
Expand Down
88 changes: 32 additions & 56 deletions scm/github/repo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import (
"github.com/google/go-github/v84/github"

api "github.com/go-vela/server/api/types"
"github.com/go-vela/server/compiler/types/yaml"
"github.com/go-vela/server/constants"
)

Expand Down Expand Up @@ -1277,52 +1276,41 @@ func TestGithub_GetNetrcPassword(t *testing.T) {
name string
repo *api.Repo
user *api.User
git yaml.Git
repos []string
perms map[string]string
appsTransport bool
wantToken string
wantExp int64
wantErr bool
}{
{
name: "installation token",
repo: installedRepo,
user: u,
git: yaml.Git{
Token: yaml.Token{
Repositories: []string{"Hello-World"},
Permissions: map[string]string{"contents": "read"},
},
},
name: "installation token",
repo: installedRepo,
user: u,
repos: []string{"Hello-World"},
perms: map[string]string{"contents": "read"},
appsTransport: true,
wantToken: "ghs_16C7e42F292c6912E7710c838347Ae178B4a",
wantExp: 1468275250,
wantErr: false,
},
{
name: "no app configured returns user oauth token",
repo: installedRepo,
user: u,
git: yaml.Git{
Token: yaml.Token{
Repositories: []string{"Hello-World"},
Permissions: map[string]string{"contents": "read"},
},
},
name: "no app configured returns user oauth token",
repo: installedRepo,
user: u,
repos: []string{"Hello-World"},
perms: map[string]string{"contents": "read"},
appsTransport: false,
wantToken: "bar",
wantExp: 0,
wantErr: false,
},
{
name: "repo not installed returns user oauth token",
repo: oauthRepo,
user: u,
git: yaml.Git{
Token: yaml.Token{
Repositories: []string{"Hello-World"},
Permissions: map[string]string{"contents": "read"},
},
},
name: "repo not installed returns user oauth token",
repo: oauthRepo,
user: u,
repos: []string{"Hello-World"},
perms: map[string]string{"contents": "read"},
appsTransport: true,
wantToken: "bar",
wantExp: 0,
Expand All @@ -1338,44 +1326,32 @@ func TestGithub_GetNetrcPassword(t *testing.T) {
wantErr: false,
},
{
name: "invalid permission resource",
repo: installedRepo,
user: u,
git: yaml.Git{
Token: yaml.Token{
Repositories: []string{"Hello-World"},
Permissions: map[string]string{"invalid": "read"},
},
},
name: "invalid permission resource",
repo: installedRepo,
user: u,
repos: []string{"Hello-World"},
perms: map[string]string{"invalid": "read"},
appsTransport: true,
wantToken: "bar",
wantExp: 0,
wantErr: false,
},
{
name: "invalid permission level",
repo: installedRepo,
user: u,
git: yaml.Git{
Token: yaml.Token{
Repositories: []string{"Hello-World"},
Permissions: map[string]string{"contents": "invalid"},
},
},
name: "invalid permission level",
repo: installedRepo,
user: u,
repos: []string{"Hello-World"},
perms: map[string]string{"contents": "invalid"},
appsTransport: true,
wantToken: "bar",
wantExp: 0,
wantErr: false,
},
{
name: "owner with inadequate permission to other repo",
repo: otherRepo,
user: badUser,
git: yaml.Git{
Token: yaml.Token{
Repositories: []string{"Hello-World"},
},
},
name: "owner with inadequate permission to other repo",
repo: otherRepo,
user: badUser,
repos: []string{"Hello-World"},
appsTransport: true,
wantToken: "bar",
wantExp: 0,
Expand All @@ -1395,7 +1371,7 @@ func TestGithub_GetNetrcPassword(t *testing.T) {
testBuild := new(api.Build)
testBuild.SetRepo(test.repo)

got, gotExp, err := client.GetNetrcPassword(t.Context(), nil, nil, testBuild, test.git)
got, gotExp, err := client.GetNetrcPassword(t.Context(), nil, nil, testBuild, test.repos, test.perms)
if (err != nil) != test.wantErr {
t.Errorf("GetNetrcPassword() error = %v, wantErr %v", err, test.wantErr)
return
Expand Down
3 changes: 1 addition & 2 deletions scm/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/go-vela/server/api/types/settings"
"github.com/go-vela/server/cache"
"github.com/go-vela/server/cache/models"
"github.com/go-vela/server/compiler/types/yaml"
"github.com/go-vela/server/database"
"github.com/go-vela/server/internal"
)
Expand Down Expand Up @@ -151,7 +150,7 @@ type Service interface {
GetHTMLURL(context.Context, *api.User, string, string, string, string) (string, error)
// GetNetrcPassword defines a function that returns the netrc
// password injected into build steps.
GetNetrcPassword(context.Context, database.Interface, cache.Service, *api.Build, yaml.Git) (string, int64, error)
GetNetrcPassword(context.Context, database.Interface, cache.Service, *api.Build, []string, map[string]string) (string, int64, error)
// SyncRepoWithInstallation defines a function that syncs
// a repo with the installation, if it exists.
SyncRepoWithInstallation(context.Context, *api.Repo) (*api.Repo, error)
Expand Down
Loading