Skip to content

Commit

Permalink
feat: Assign Reviewers from Backstage Owners in GitLab  (#3)
Browse files Browse the repository at this point in the history
* Install a backstage go client

* Add flags for Backstage instance

* Initialize backstage client in gitlab client

* Client tweaks and ensure flags get passed to the evaluate and server command

* Implement Backstage queries for members

* Default Backstage namespace is always default

* Backstage client package

* Also remove namespace from here

* Changes to Backstage client initialization

* Refactor Backstage client integration

* Make source required for assign_reviewer

* Remove unnecessary break

* Use go-vcr for backstage client tests

* Add Backstage client test for getting a user

* Add Backstage client test for getting members of group

* Add Backstage client test for getting owners by project name

* Break out test helpers into testutils and add test for backstage assign action

* Fix test

* Update action schema

* Docs
  • Loading branch information
bbckr authored Dec 11, 2024
1 parent d2ac59e commit c88a0e4
Show file tree
Hide file tree
Showing 29 changed files with 1,152 additions and 9 deletions.
21 changes: 21 additions & 0 deletions cmd/conventions.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package cmd

import "github.com/urfave/cli/v2"

const (
FlagAPIToken = "api-token"
FlagBackstageURL = "backstage-url"
FlagBackstageToken = "backstage-token"
FlagCommitSHA = "commit"
FlagConfigFile = "config"
FlagDryRun = "dry-run"
Expand All @@ -20,3 +24,20 @@ const (
FlagPeriodicEvaluationOnlyProjectsWithMembership = "periodic-evaluation-only-project-membership"
FlagWebhookSecret = "webhook-secret"
)

var (
StringFlagBackstageURL = &cli.StringFlag{
Name: FlagBackstageURL,
Usage: "The Backstage base URL",
EnvVars: []string{
"BACKSTAGE_URL", // Backstage catalog integration
},
}
StringFlagBackstageToken = &cli.StringFlag{
Name: FlagBackstageToken,
Usage: "The Backstage static token with access to the catalog plugin", // https://backstage.io/docs/auth/service-to-service-auth/#static-tokens
EnvVars: []string{
"BACKSTAGE_TOKEN", // Backstage catalog integration
},
}
)
4 changes: 4 additions & 0 deletions cmd/gitlab.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ var GitLab = &cli.Command{
"CI_COMMIT_SHA", // GitLab CI
},
},
StringFlagBackstageURL,
StringFlagBackstageToken,
},
},
{
Expand Down Expand Up @@ -184,6 +186,8 @@ var GitLab = &cli.Command{
"SCM_ENGINE_PERIODIC_EVALUATION_ONLY_PROJECTS_WITH_MEMBERSHIP",
},
},
StringFlagBackstageURL,
StringFlagBackstageToken,
},
},
},
Expand Down
4 changes: 4 additions & 0 deletions cmd/gitlab_evaluate.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ func Evaluate(cCtx *cli.Context) error {
ctx = state.WithToken(ctx, cCtx.String(FlagAPIToken))
ctx = state.WithUpdatePipeline(ctx, cCtx.Bool(FlagUpdatePipeline), cCtx.String(FlagUpdatePipelineURL))

// Optional Backstage catalog integration
ctx = state.WithBackstageURL(ctx, cCtx.String(FlagBackstageURL))
ctx = state.WithBackstageToken(ctx, cCtx.String(FlagBackstageToken))

cfg, err := config.LoadFile(state.ConfigFilePath(ctx))
if err != nil {
return err
Expand Down
4 changes: 4 additions & 0 deletions cmd/gitlab_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ func Server(cCtx *cli.Context) error {
ctx = state.WithConfigFilePath(ctx, cCtx.String(FlagConfigFile))
ctx = state.WithUpdatePipeline(ctx, cCtx.Bool(FlagUpdatePipeline), cCtx.String(FlagUpdatePipelineURL))

// Optional Backstage catalog integration
ctx = state.WithBackstageURL(ctx, cCtx.String(FlagBackstageURL))
ctx = state.WithBackstageToken(ctx, cCtx.String(FlagBackstageToken))

// Add logging context key/value pairs
ctx = slogctx.With(ctx, slog.String("gitlab_url", cCtx.String(FlagSCMBaseURL)))
ctx = slogctx.With(ctx, slog.Duration("server_timeout", cCtx.Duration(FlagServerTimeout)))
Expand Down
8 changes: 7 additions & 1 deletion cmd/shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"time"

"github.com/jippi/scm-engine/pkg/config"
"github.com/jippi/scm-engine/pkg/integration/backstage"
"github.com/jippi/scm-engine/pkg/scm"
"github.com/jippi/scm-engine/pkg/scm/github"
"github.com/jippi/scm-engine/pkg/scm/gitlab"
Expand All @@ -20,12 +21,17 @@ import (
var sid = shortid.MustNew(1, shortid.DefaultABC, 2342)

func getClient(ctx context.Context) (scm.Client, error) {
backstageClient, err := backstage.NewClient(ctx, state.BackstageURL(ctx), state.BackstageToken(ctx), nil)
if err != nil {
slogctx.Warn(ctx, "Backstage client is not available, actions requiring it will be skipped", slog.Any("error", err))
}

switch state.Provider(ctx) {
case "github":
return github.NewClient(ctx), nil

case "gitlab":
return gitlab.NewClient(ctx)
return gitlab.NewClient(ctx, backstageClient)

default:
return nil, fmt.Errorf("unknown provider %q - we only support 'github' and 'gitlab'", state.Provider(ctx))
Expand Down
45 changes: 45 additions & 0 deletions docs/gitlab/examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,3 +186,48 @@ actions:
/* Remove duplicate values from the output */
| uniq()
```

## Assign reviewers from CODEOWNERS

The `codeowners` source relies on the GitLab [CODE OWNERS](https://docs.gitlab.com/ee/user/project/codeowners/) feature.

For a reviewer to be assigned to a Merge Request, the project must have a CODEOWNERS file and the groups/users mentioned in the file must have [direct membership](https://gitlab.com/gitlab-org/gitlab/-/issues/288851/) to the project.

```yaml
# yaml-language-server: $schema=https://jippi.github.io/scm-engine/scm-engine.schema.json

actions:
- name: "assign"
if: |1
--8<-- "docs/gitlab/snippets/assign-merge-request/assign-if.expr"
then:
- action: assign_reviewers
source: codeowners
limit: 1
mode: random
```

## Assign reviewers from Backstage

The `backstage` source relies on the [Backstage Catalog](https://backstage.io/docs/features/software-catalog/) to derive ownership information.

This assumes [System](https://backstage.io/docs/features/software-catalog/descriptor-format#kind-system) and [User](https://backstage.io/docs/features/software-catalog/descriptor-format/#kind-user) in Backstage can be mapped directly to a GitLab project and user.

For a Backstage System entity to be mapped to a GitLab project, the system must have same name as the GitLab project or the `gitlab.com/project` annotation is set to the GitLab project name.

For a Backstage User entity to be mapped to a GitLab user, the user must have the `gitlab.com/user_id` annotation set to the numeric ID of the user. A plugin that can be used to automatically set this annotation is [@seatgeek/backstage-plugin-gitlab-catalog-backend](https://github.com/seatgeek/backstage-plugins/tree/main/plugins/gitlab-catalog-backend).


```yaml
# yaml-language-server: $schema=https://jippi.github.io/scm-engine/scm-engine.schema.json

actions:
- name: "assign"
if: |1
--8<-- "docs/gitlab/snippets/assign-merge-request/assign-if.expr"
then:
- action: assign_reviewers
source: backstage
limit: 1
mode: random
```
2 changes: 2 additions & 0 deletions docs/gitlab/snippets/assign-merge-request/assign-if.expr
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
merge_request.state_is("opened")
&& not merge_request.approved
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/99designs/gqlgen v0.17.57
github.com/aquilax/truncate v1.0.0
github.com/charmbracelet/lipgloss v1.0.0
github.com/datolabs-io/go-backstage/v3 v3.0.0
github.com/davecgh/go-spew v1.1.1
github.com/expr-lang/expr v1.16.9
github.com/fatih/structtag v1.2.0
Expand All @@ -31,7 +32,9 @@ require (
github.com/xanzy/go-gitlab v0.114.0
github.com/xhit/go-str2duration/v2 v2.1.0
golang.org/x/oauth2 v0.24.0
gopkg.in/dnaeon/go-vcr.v4 v4.0.2
gopkg.in/yaml.v3 v3.0.1
gotest.tools/v3 v3.5.1
)

require (
Expand All @@ -44,6 +47,7 @@ require (
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/fatih/color v1.17.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/hashicorp/errwrap v1.0.0 // indirect
Expand Down
10 changes: 10 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NA
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/datolabs-io/go-backstage/v3 v3.0.0 h1:AaaA5PRhriPp+WM3siGEV5YLlV0IxXe2XIftsoi9wYg=
github.com/datolabs-io/go-backstage/v3 v3.0.0/go.mod h1:xzfVJBuLKDbYXuYWmlimS9fX13OQYkYyl70UbQMeXXU=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
Expand Down Expand Up @@ -54,6 +56,10 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/guregu/null/v5 v5.0.0 h1:PRxjqyOekS11W+w/7Vfz6jgJE/BCwELWtgvOJzddimw=
github.com/guregu/null/v5 v5.0.0/go.mod h1:SjupzNy+sCPtwQTKWhUCqjhVCO69hpsl2QsZrWHjlwU=
github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE=
github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
Expand Down Expand Up @@ -156,9 +162,13 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/dnaeon/go-vcr.v4 v4.0.2 h1:7T5VYf2ifyK01ETHbJPl5A6XTpUljD4Trw3GEDcdedk=
gopkg.in/dnaeon/go-vcr.v4 v4.0.2/go.mod h1:65yxh9goQVrudqofKtHA4JNFWd6XZRkWfKN4YpMx7KI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
modernc.org/b/v2 v2.1.0 h1:kMD/G43EYnsFJI/0qK1F1X659XlSs41bp01MUDidHC0=
modernc.org/b/v2 v2.1.0/go.mod h1:fQhHWDXrchyUSLjQYCslV/4uw04PW1LeiZ25D4SNmeo=
modernc.org/mathutil v1.4.1 h1:ij3fYGe8zBF4Vu+g0oT7mB06r8sqGWKuJu1yXeR4by8=
Expand Down
2 changes: 1 addition & 1 deletion pkg/config/action_step.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ type AssignReviewers struct {
BaseAction

// The source of the reviewers
Source *string `json:"source,omitempty" yaml:"source,omitempty" jsonschema:"enum=codeowners"`
Source string `json:"source" yaml:"source,omitempty" jsonschema:"enum=codeowners,enum=backstage"`
// The max number of reviewers to assign
Limit int `json:"limit,omitempty" yaml:"limit,omitempty"`
// The mode of assigning reviewers
Expand Down
Loading

0 comments on commit c88a0e4

Please sign in to comment.