Skip to content

Commit 2705892

Browse files
committed
feat: add support for evaluating all open MRs
1 parent 3f47d5c commit 2705892

8 files changed

+132
-9
lines changed

.gitlab-ci.yml

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

.scm-engine.example.yml

+29-5
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,44 @@
22

33
actions:
44
- name: Warn that the ticket if older than 30 days
5-
if: merge_request.state != "closed" && merge_request.time_since_last_commit > duration("30d") && not merge_request.has_label("do-not-close")
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")
614
then:
715
- action: comment
8-
message: "Hey, this MR is old, we will close it in 15 days if no activity has happened. If you want to disable this behavior, add the label 'do-not-close' on the MR."
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.
922
1023
- name: Close ticket if older than 45 days
11-
if: merge_request.state != "closed" && merge_request.time_since_last_commit > duration("45d") && not merge_request.has_label("do-not-close")
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")
1228
then:
1329
- action: close
1430
- action: comment
15-
message: "As promised, we're closing the MR due to inactivity, bye bye"
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.
1637
1738
- name: Approve MR if the 'break-glass-approve' label is configured
18-
if: merge_request.state != "closed" && not merge_request.approved && merge_request.has_label("break-glass-approve")
39+
if: |
40+
merge_request.state != "closed"
41+
&& not merge_request.approved
42+
&& merge_request.has_label("break-glass-approve")
1943
then:
2044
- action: approve
2145
- action: comment

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
@@ -19,6 +19,7 @@ type LabelClient interface {
1919

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

2425
type EvalContext interface {

pkg/scm/types.go

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

97+
type ListMergeRequestsOptions struct {
98+
ListOptions
99+
State string
100+
First int
101+
}
102+
103+
type ListMergeRequest struct {
104+
ID string `expr:"id" graphql:"id"`
105+
}
106+
97107
// Response is a GitLab API response. This wraps the standard http.Response
98108
// returned from GitLab and provides convenient access to things like
99109
// 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)