Skip to content

Commit 54df1a4

Browse files
committed
feat: add support for evaluating all open MRs
1 parent ab3edcc commit 54df1a4

8 files changed

+149
-5
lines changed

.gitlab-ci.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
run::cli:
2-
image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/golang:1.22.2
1+
scm-engine::evaluate:
2+
image: ${CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX}/golang:1.22.3
33
rules:
44
- if: $CI_PIPELINE_SOURCE == 'merge_request_event'
55
script:

.scm-engine.example.yml

+45
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,50 @@
11
# See: https://getbootstrap.com/docs/5.3/customize/color/#all-colors
22

3+
actions:
4+
- name: Warn that the ticket if older than 30 days
5+
if: |
6+
// ignore MRs already closed
7+
merge_request.state != "closed"
8+
// if last commit happened more than 30 days ago
9+
&& merge_request.time_since_last_commit > duration("30d")
10+
// but still less than 45 days ago (where we close the MR)
11+
&& merge_request.time_since_last_commit < duration("45d")
12+
// and the label to disable this feature is not on the MR
13+
&& not merge_request.has_label("do-not-close")
14+
then:
15+
- action: comment
16+
message: |
17+
Hello!
18+
19+
This Merge Request have not seen any commit activity for 30 days. In an effort to keep our project clean we will automatically close the Merge request after 45 days.
20+
21+
You can add the "do-not-close" label to the Merge Request to disable this behavior.
22+
23+
- name: Close ticket if older than 45 days
24+
if: |
25+
merge_request.state != "closed"
26+
&& merge_request.time_since_last_commit > duration("45d")
27+
&& not merge_request.has_label("do-not-close")
28+
then:
29+
- action: close
30+
- action: comment
31+
message: |
32+
Hello!
33+
34+
This Merge Request have not seen any commit activity for 45 days. In an effort to keep our project clean we will automatically close the Merge request.
35+
36+
You can add the "do-not-close" label to the Merge Request to disable this behavior.
37+
38+
- name: Approve MR if the 'break-glass-approve' label is configured
39+
if: |
40+
merge_request.state != "closed"
41+
&& not merge_request.approved
42+
&& merge_request.has_label("break-glass-approve")
43+
then:
44+
- action: approve
45+
- action: comment
46+
message: "Approving the MR since it has the 'break-glass-approve' label. Talk to ITGC about this!"
47+
348
label:
449
- name: lang/go
550
color: "$indigo"

cmd/cmd_evaluate.go

+16-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55

66
"github.com/jippi/scm-engine/pkg/config"
7+
"github.com/jippi/scm-engine/pkg/scm"
78
"github.com/jippi/scm-engine/pkg/scm/gitlab"
89
"github.com/jippi/scm-engine/pkg/state"
910
"github.com/urfave/cli/v2"
@@ -23,6 +24,19 @@ func Evaluate(cCtx *cli.Context) error {
2324
}
2425

2526
switch {
27+
// If first arg is 'all' we will find all opened MRs and apply the rules to them
28+
case cCtx.Args().First() == "all":
29+
res, err := client.MergeRequests().List(ctx, &scm.ListMergeRequestsOptions{State: "opened", First: 100})
30+
if err != nil {
31+
return err
32+
}
33+
34+
for _, mr := range res {
35+
if err := ProcessMR(ctx, client, cfg, mr.ID); err != nil {
36+
return err
37+
}
38+
}
39+
2640
// If the flag is set, use that for evaluation
2741
case cCtx.String(FlagMergeRequestID) != "":
2842
return ProcessMR(ctx, client, cfg, cCtx.String(FlagMergeRequestID))
@@ -37,7 +51,7 @@ func Evaluate(cCtx *cli.Context) error {
3751
return err
3852
}
3953
}
40-
41-
return nil
4254
}
55+
56+
return nil
4357
}

pkg/scm/gitlab/client_merge_request.go

+35
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ import (
55
"fmt"
66
"net/http"
77

8+
"github.com/hasura/go-graphql-client"
89
"github.com/jippi/scm-engine/pkg/scm"
910
"github.com/jippi/scm-engine/pkg/state"
1011
go_gitlab "github.com/xanzy/go-gitlab"
12+
"golang.org/x/oauth2"
1113
)
1214

1315
var _ scm.MergeRequestClient = (*MergeRequestClient)(nil)
@@ -43,3 +45,36 @@ func (client *MergeRequestClient) Update(ctx context.Context, opt *scm.UpdateMer
4345

4446
return convertResponse(resp), err
4547
}
48+
49+
func (client *MergeRequestClient) List(ctx context.Context, options *scm.ListMergeRequestsOptions) ([]scm.ListMergeRequest, error) {
50+
httpClient := oauth2.NewClient(
51+
ctx,
52+
oauth2.StaticTokenSource(
53+
&oauth2.Token{
54+
AccessToken: client.client.token,
55+
},
56+
),
57+
)
58+
59+
graphqlClient := graphql.NewClient(graphqlBaseURL(client.client.wrapped.BaseURL())+"/api/graphql", httpClient)
60+
61+
var (
62+
result *ListMergeRequestsQuery
63+
variables = map[string]any{
64+
"project_id": graphql.ID(state.ProjectIDFromContext(ctx)),
65+
"state": MergeRequestState(options.State),
66+
"first": options.First,
67+
}
68+
)
69+
70+
if err := graphqlClient.Query(ctx, &result, variables); err != nil {
71+
return nil, err
72+
}
73+
74+
hits := []scm.ListMergeRequest{}
75+
for _, x := range result.Project.MergeRequests.Nodes {
76+
hits = append(hits, scm.ListMergeRequest{ID: x.ID})
77+
}
78+
79+
return hits, nil
80+
}

pkg/scm/interfaces.go

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ type LabelClient interface {
1818

1919
type MergeRequestClient interface {
2020
Update(ctx context.Context, opt *UpdateMergeRequestOptions) (*Response, error)
21+
List(ctx context.Context, options *ListMergeRequestsOptions) ([]ListMergeRequest, error)
2122
}
2223

2324
type EvalContext interface {

pkg/scm/types.go

+10
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,16 @@ type ListOptions struct {
9393
Sort string `json:"sort,omitempty" url:"sort,omitempty"`
9494
}
9595

96+
type ListMergeRequestsOptions struct {
97+
ListOptions
98+
State string
99+
First int
100+
}
101+
102+
type ListMergeRequest struct {
103+
ID string `expr:"id" graphql:"id"`
104+
}
105+
96106
// Response is a GitLab API response. This wraps the standard http.Response
97107
// returned from GitLab and provides convenient access to things like
98108
// pagination links.

schema/gitlab.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,12 @@ func main() {
6767
func nest(props []*Property) {
6868
for _, field := range props {
6969
if field.IsCustomType {
70-
for _, nested := range PropMap[field.Type].Attributes {
70+
attr, ok := PropMap[field.Type]
71+
if !ok {
72+
continue
73+
}
74+
75+
for _, nested := range attr.Attributes {
7176
field.AddAttribute(&Property{
7277
Name: nested.Name,
7378
Description: nested.Description,
@@ -149,6 +154,8 @@ func mutateHook(b *modelgen.ModelBuild) *modelgen.ModelBuild {
149154
Description: model.Description,
150155
}
151156

157+
fmt.Println("model", modelProperty.Name)
158+
152159
for _, field := range model.Fields {
153160
tags, err := structtag.Parse(field.Tag)
154161
if err != nil {

schema/gitlab.schema.graphqls

+32
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ scalar Time
2121
# Add time.Duration support
2222
scalar Duration
2323

24+
2425
type Context {
2526
"The project the Merge Request belongs to"
2627
Project: ContextProject @graphql(key: "project(fullPath: $project_id)")
@@ -32,6 +33,37 @@ type Context {
3233
MergeRequest: ContextMergeRequest @generated
3334
}
3435

36+
enum MergeRequestState {
37+
all
38+
closed
39+
locked
40+
merged
41+
opened
42+
}
43+
44+
input ListMergeRequestsQueryInput {
45+
project_id: ID!
46+
state: MergeRequestState! = "opened"
47+
first: Int! = 100
48+
}
49+
50+
type ListMergeRequestsQuery {
51+
"The project the Merge Request belongs to"
52+
Project: ListMergeRequestsProject @graphql(key: "project(fullPath: $project_id)")
53+
}
54+
55+
type ListMergeRequestsProject {
56+
MergeRequests: ListMergeRequestsProjectMergeRequestNodes @graphql(key: "mergeRequests(state: $state, first: $first)") @internal
57+
}
58+
59+
type ListMergeRequestsProjectMergeRequestNodes {
60+
Nodes: [ListMergeRequestsProjectMergeRequest!]
61+
}
62+
63+
type ListMergeRequestsProjectMergeRequest {
64+
ID: String! @graphql(key: "iid") @internal
65+
}
66+
3567
type ContextProject {
3668
#
3769
# Native GraphQL fields - https://docs.gitlab.com/ee/api/graphql/reference/#project

0 commit comments

Comments
 (0)