From 0aba87080902fe040f433ba96fe6b92415810a94 Mon Sep 17 00:00:00 2001 From: Christian Winther Date: Sat, 11 May 2024 22:26:13 +0200 Subject: [PATCH 01/10] feat: implement CI pipeline status reporting when evaluating MR --- cmd/cmd_evaluate.go | 19 ++++++++++++----- cmd/cmd_server.go | 22 +++++++++++-------- cmd/conventions.go | 2 ++ cmd/shared.go | 16 +++++++++++--- docs/commands/evaluate.md | 10 +++++---- docs/commands/server.md | 3 ++- docs/gitlab/setup.md | 26 +++++++++++----------- main.go | 35 ++++++++++++++++++++++++------ pkg/scm/gitlab/client.go | 45 +++++++++++++++++++++++++++++++++++++++ pkg/scm/interfaces.go | 2 ++ pkg/scm/types.go | 3 ++- pkg/state/context.go | 40 +++++++++++++++++++++++----------- 12 files changed, 168 insertions(+), 55 deletions(-) diff --git a/cmd/cmd_evaluate.go b/cmd/cmd_evaluate.go index ecd04a7..dbd6136 100644 --- a/cmd/cmd_evaluate.go +++ b/cmd/cmd_evaluate.go @@ -11,8 +11,10 @@ import ( ) func Evaluate(cCtx *cli.Context) error { - ctx := state.ContextWithProjectID(cCtx.Context, cCtx.String(FlagSCMProject)) - ctx = state.ContextWithDryRun(ctx, cCtx.Bool(FlagDryRun)) + ctx := state.WithProjectID(cCtx.Context, cCtx.String(FlagSCMProject)) + ctx = state.WithCommitSHA(ctx, cCtx.String(FlagCommitSHA)) + ctx = state.WithDryRun(ctx, cCtx.Bool(FlagDryRun)) + ctx = state.WithUpdatePipeline(ctx, cCtx.Bool(FlagUpdatePipeline)) cfg, err := config.LoadFile(cCtx.String(FlagConfigFile)) if err != nil { @@ -33,14 +35,19 @@ func Evaluate(cCtx *cli.Context) error { } for _, mr := range res { - if err := ProcessMR(ctx, client, cfg, mr.ID, nil); err != nil { + ctx := state.ContextWithMergeRequestID(ctx, mr.ID) + ctx = state.WithCommitSHA(ctx, mr.SHA) + + if err := ProcessMR(ctx, client, cfg, nil); err != nil { return err } } // If the flag is set, use that for evaluation case cCtx.String(FlagMergeRequestID) != "": - return ProcessMR(ctx, client, cfg, cCtx.String(FlagMergeRequestID), nil) + ctx = state.ContextWithMergeRequestID(ctx, cCtx.String(FlagMergeRequestID)) + + return ProcessMR(ctx, client, cfg, nil) // If no flag is set, we require arguments case cCtx.Args().Len() == 0: @@ -48,7 +55,9 @@ func Evaluate(cCtx *cli.Context) error { default: for _, mr := range cCtx.Args().Slice() { - if err := ProcessMR(ctx, client, cfg, mr, nil); err != nil { + ctx = state.ContextWithMergeRequestID(ctx, mr) + + if err := ProcessMR(ctx, client, cfg, nil); err != nil { return err } } diff --git a/cmd/cmd_server.go b/cmd/cmd_server.go index 5a3ddf9..8846118 100644 --- a/cmd/cmd_server.go +++ b/cmd/cmd_server.go @@ -112,31 +112,34 @@ func Server(cCtx *cli.Context) error { //nolint:unparam } // Initialize context - ctx = state.ContextWithProjectID(ctx, payload.Project.PathWithNamespace) + ctx = state.WithProjectID(ctx, payload.Project.PathWithNamespace) // Grab event specific information var ( - id string - ref string + id string + gitSha string ) switch payload.EventType { case "merge_request": id = strconv.Itoa(payload.ObjectAttributes.IID) - ref = payload.ObjectAttributes.LastCommit.ID + gitSha = payload.ObjectAttributes.LastCommit.ID case "note": id = strconv.Itoa(payload.MergeRequest.IID) - ref = payload.MergeRequest.LastCommit.ID + gitSha = payload.MergeRequest.LastCommit.ID default: errHandler(ctx, w, http.StatusInternalServerError, fmt.Errorf("unknown event type: %s", payload.EventType)) } - ctx = slogctx.With(ctx, slog.String("event_type", payload.EventType), slog.String("merge_request_id", id), slog.String("sha_reference", ref)) + // Build context for rest of the pipeline + ctx = state.WithCommitSHA(ctx, gitSha) + ctx = state.ContextWithMergeRequestID(ctx, id) + ctx = slogctx.With(ctx, slog.String("event_type", payload.EventType)) // Get the remote config file - file, err := client.MergeRequests().GetRemoteConfig(ctx, cCtx.String(FlagConfigFile), ref) + file, err := client.MergeRequests().GetRemoteConfig(ctx, cCtx.String(FlagConfigFile), gitSha) if err != nil { errHandler(ctx, w, http.StatusOK, fmt.Errorf("could not read remote config file: %w", err)) @@ -160,7 +163,7 @@ func Server(cCtx *cli.Context) error { //nolint:unparam } // Process the MR - if err := ProcessMR(ctx, client, cfg, id, fullEventPayload); err != nil { + if err := ProcessMR(ctx, client, cfg, fullEventPayload); err != nil { errHandler(ctx, w, http.StatusOK, err) return @@ -176,7 +179,8 @@ func Server(cCtx *cli.Context) error { //nolint:unparam ReadTimeout: 5 * time.Second, WriteTimeout: 5 * time.Second, BaseContext: func(l net.Listener) context.Context { - ctx := state.ContextWithDryRun(cCtx.Context, cCtx.Bool(FlagDryRun)) + ctx := state.WithDryRun(cCtx.Context, cCtx.Bool(FlagDryRun)) + ctx = state.WithUpdatePipeline(ctx, cCtx.Bool(FlagUpdatePipeline)) return ctx }, diff --git a/cmd/conventions.go b/cmd/conventions.go index 81f31ea..94ce71e 100644 --- a/cmd/conventions.go +++ b/cmd/conventions.go @@ -5,6 +5,8 @@ const ( FlagConfigFile = "config" FlagDryRun = "dry-run" FlagMergeRequestID = "id" + FlagUpdatePipeline = "update-pipeline" + FlagCommitSHA = "commit" FlagSCMBaseURL = "base-url" FlagSCMProject = "project" FlagServerListen = "listen" diff --git a/cmd/shared.go b/cmd/shared.go index fd4a079..88e1fdd 100644 --- a/cmd/shared.go +++ b/cmd/shared.go @@ -2,6 +2,7 @@ package cmd import ( "context" + "fmt" "log/slog" "net/http" @@ -11,10 +12,19 @@ import ( slogctx "github.com/veqryn/slog-context" ) -func ProcessMR(ctx context.Context, client scm.Client, cfg *config.Config, mr string, event any) error { - ctx = state.ContextWithMergeRequestID(ctx, mr) +func ProcessMR(ctx context.Context, client scm.Client, cfg *config.Config, event any) (err error) { + // Stop the pipeline when we leave this func + defer func() { + if stopErr := client.Stop(ctx, err); err != nil { + slogctx.Error(ctx, "Failed to update pipeline", slog.Any("error", stopErr)) + } + }() + + // Start the pipeline + if err = client.Start(ctx); err != nil { + return fmt.Errorf("failed to update pipeline monitor: %w", err) + } - // for mr := 900; mr <= 1000; mr++ { slogctx.Info(ctx, "Processing MR") remoteLabels, err := client.Labels().List(ctx) diff --git a/docs/commands/evaluate.md b/docs/commands/evaluate.md index d9e1584..cf35f29 100644 --- a/docs/commands/evaluate.md +++ b/docs/commands/evaluate.md @@ -12,12 +12,14 @@ NAME: scm-engine evaluate - Evaluate a Merge Request USAGE: - scm-engine evaluate [command options] [id, id, ...] + scm-engine evaluate [command options] [mr_id, mr_id, ...] OPTIONS: - --project value GitLab project (example: 'gitlab-org/gitlab') [$GITLAB_PROJECT, $CI_PROJECT_PATH] - --id value, --merge-request-id value, --pull-request-id value The pull/merge to process, if not provided as a CLI flag [$CI_MERGE_REQUEST_IID] - --help, -h show help + --project value GitLab project (example: 'gitlab-org/gitlab') [$GITLAB_PROJECT, $CI_PROJECT_PATH] + --id value The pull/merge ID to process, if not provided as a CLI flag [$CI_MERGE_REQUEST_IID] + --commit value The git commit sha [$CI_COMMIT_SHA] + --update-pipeline Update the CI pipeline status with progress (default: true) [$SCM_ENGINE_SKIP_PIPELINE] + --help, -h show help GLOBAL OPTIONS: --config value Path to the scm-engine config file (default: ".scm-engine.yml") [$SCM_ENGINE_CONFIG_FILE] diff --git a/docs/commands/server.md b/docs/commands/server.md index a1ff5e0..18d7ccb 100644 --- a/docs/commands/server.md +++ b/docs/commands/server.md @@ -25,7 +25,8 @@ USAGE: OPTIONS: --webhook-secret value Used to validate received payloads. Sent with the request in the X-Gitlab-Token HTTP header [$SCM_ENGINE_WEBHOOK_SECRET] - --listen value Port the HTTP server should listen on (default: "0.0.0.0:3000") [$SCM_ENGINE_LISTEN] + --listen value IP + Port that the HTTP server should listen on (default: "0.0.0.0:3000") [$SCM_ENGINE_LISTEN] + --update-pipeline Update the CI pipeline status with progress (default: true) [$SCM_ENGINE_SKIP_PIPELINE] --help, -h show help GLOBAL OPTIONS: diff --git a/docs/gitlab/setup.md b/docs/gitlab/setup.md index c1e1f5e..da77d50 100644 --- a/docs/gitlab/setup.md +++ b/docs/gitlab/setup.md @@ -4,14 +4,14 @@ Using `scm-engine` as a webhook server allows for richer feature set compared to [GitLab CI pipeline](#gitlab-ci-pipeline) mode -- `+` Reacting to comments -- `+` Access to webhook event data in scripts via `webhook_event.*` (see [server docs](../commands/server.md) for more information) -- `+` A single `scm-engine` instance (and single token) for your GitLab project, group, or instance depending on where you configure the webhook. -- `+` Each Project still have their own `.scm-engine.yml` file, it's downloaded via the API when the server is processing a webhook event. -- `+` A single "bot" identity across your projects. -- `+` Turn key once configured; if a project want to use `scm-engine` they just need to create the `.scm-engine.yml` file in their project. -- `+` Real-time reactions to changes -- `-` No intuitive access to [`evaluation` logs](../commands/evaluate.md) within GitLab (you can see them in the server logs or in the webhook failure log) +- [X] Real-time reactions to changes. +- [X] Reacting to comments. +- [X] Access to webhook event data in scripts via `webhook_event.*` (see [server docs](../commands/server.md) for more information). +- [X] A single `scm-engine` instance (and single token) for your GitLab project, group, or instance depending on where you configure the webhook. +- [X] Each Project still have their own `.scm-engine.yml` file, it's downloaded via the API when the server is processing a webhook event. +- [X] A single "bot" identity across your projects. +- [X] Turn key once configured; if a project want to use `scm-engine` they just need to create the `.scm-engine.yml` file in their project. +- [ ] No intuitive access to [`evaluation` logs](../commands/evaluate.md) within GitLab (you can see them in the server logs or in the webhook failure log). **Setup**: @@ -22,11 +22,11 @@ Using `scm-engine` as a webhook server allows for richer feature set compared to Using `scm-engine` within a GitLab CI pipeline is straight forward - every time a CI pipeline runs, `scm-engine` will [evaluate](../commands/evaluate.md) the Merge Request. -- `+` Simple & quick installation. -- `+` Limited access token permissions. -- `+` Easy access to [`evaluation` logs](../commands/evaluate.md) within the GitLab CI job. -- `-` Can't react to comments; only works within a CI pipeline. -- `-` Higher latency for reacting to changes depending on how fast CI jobs run (and where in the pipeline it runs). +- [X] Simple & quick installation. +- [X] Limited access token permissions. +- [X] Easy access to [`evaluation` logs](../commands/evaluate.md) within the GitLab CI job. +- [ ] Can't react to comments; only works within a CI pipeline. +- [ ] Higher latency for reacting to changes depending on how fast CI jobs run (and where in the pipeline it runs). **Setup**: diff --git a/main.go b/main.go index 04db5eb..837c5a8 100644 --- a/main.go +++ b/main.go @@ -79,7 +79,7 @@ func main() { Name: "evaluate", Usage: "Evaluate a Merge Request", Args: true, - ArgsUsage: " [id, id, ...]", + ArgsUsage: " [mr_id, mr_id, ...]", Action: cmd.Evaluate, Flags: []cli.Flag{ &cli.StringFlag{ @@ -92,16 +92,29 @@ func main() { }, }, &cli.StringFlag{ - Name: cmd.FlagMergeRequestID, - Usage: "The pull/merge to process, if not provided as a CLI flag", - Aliases: []string{ - "merge-request-id", // GitLab naming - "pull-request-id", // GitHub naming - }, + Name: cmd.FlagMergeRequestID, + Usage: "The pull/merge ID to process, if not provided as a CLI flag", + Required: true, EnvVars: []string{ "CI_MERGE_REQUEST_IID", // GitLab CI }, }, + &cli.StringFlag{ + Name: cmd.FlagCommitSHA, + Usage: "The git commit sha", + Required: true, + EnvVars: []string{ + "CI_COMMIT_SHA", // GitLab CI + }, + }, + &cli.BoolFlag{ + Name: cmd.FlagUpdatePipeline, + Usage: "Update the CI pipeline status with progress", + Value: true, + EnvVars: []string{ + "SCM_ENGINE_SKIP_PIPELINE", + }, + }, }, }, { @@ -124,6 +137,14 @@ func main() { "SCM_ENGINE_LISTEN", }, }, + &cli.BoolFlag{ + Name: cmd.FlagUpdatePipeline, + Usage: "Update the CI pipeline status with progress", + Value: true, + EnvVars: []string{ + "SCM_ENGINE_SKIP_PIPELINE", + }, + }, }, }, }, diff --git a/pkg/scm/gitlab/client.go b/pkg/scm/gitlab/client.go index 34bbb25..dcb07ab 100644 --- a/pkg/scm/gitlab/client.go +++ b/pkg/scm/gitlab/client.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/jippi/scm-engine/pkg/scm" + "github.com/jippi/scm-engine/pkg/state" go_gitlab "github.com/xanzy/go-gitlab" ) @@ -59,6 +60,50 @@ func (client *Client) EvalContext(ctx context.Context) (scm.EvalContext, error) return res, nil } +// Start pipeline +func (client *Client) Start(ctx context.Context) error { + if !state.ShouldUpdatePipeline(ctx) { + return nil + } + + _, _, err := client.wrapped.Commits.SetCommitStatus(state.ProjectID(ctx), state.CommitSHA(ctx), &go_gitlab.SetCommitStatusOptions{ + State: go_gitlab.Running, + Name: go_gitlab.Ptr("scm-engine"), + Description: go_gitlab.Ptr("Currently evaluating MR"), + }) + if err != nil { + return err + } + + return nil +} + +// Stop pipeline +func (client *Client) Stop(ctx context.Context, err error) error { + if !state.ShouldUpdatePipeline(ctx) { + return nil + } + + status := go_gitlab.Success + message := "OK" + + if err != nil { + status = go_gitlab.Failed + message = err.Error() + } + + _, _, err = client.wrapped.Commits.SetCommitStatus(state.ProjectID(ctx), state.CommitSHA(ctx), &go_gitlab.SetCommitStatusOptions{ + State: status, + Name: go_gitlab.Ptr("scm-engine"), + Description: go_gitlab.Ptr(message), + }) + if err != nil { + return err + } + + return nil +} + func graphqlBaseURL(inputURL *url.URL) string { var buf strings.Builder if inputURL.Scheme != "" { diff --git a/pkg/scm/interfaces.go b/pkg/scm/interfaces.go index 7ba9140..c65947a 100644 --- a/pkg/scm/interfaces.go +++ b/pkg/scm/interfaces.go @@ -10,6 +10,8 @@ type Client interface { MergeRequests() MergeRequestClient EvalContext(ctx context.Context) (EvalContext, error) ApplyStep(ctx context.Context, update *UpdateMergeRequestOptions, step EvaluationActionStep) error + Start(ctx context.Context) error + Stop(ctx context.Context, err error) error } type LabelClient interface { diff --git a/pkg/scm/types.go b/pkg/scm/types.go index 55df2e6..0e54b90 100644 --- a/pkg/scm/types.go +++ b/pkg/scm/types.go @@ -101,7 +101,8 @@ type ListMergeRequestsOptions struct { } type ListMergeRequest struct { - ID string `expr:"id" graphql:"id"` + ID string `expr:"id" graphql:"id"` + SHA string `expr:"sha" graphql:"sha"` } // Response is a GitLab API response. This wraps the standard http.Response diff --git a/pkg/state/context.go b/pkg/state/context.go index 1e1d74b..abcb179 100644 --- a/pkg/state/context.go +++ b/pkg/state/context.go @@ -14,36 +14,44 @@ const ( projectID contextKey = iota dryRun mergeRequestID + commitSha + updatePipeline ) -func NewContext(project, mr string) context.Context { - ctx := context.Background() - ctx = ContextWithProjectID(ctx, project) - ctx = ContextWithMergeRequestID(ctx, mr) - - return ctx -} - func ProjectID(ctx context.Context) string { return ctx.Value(projectID).(string) //nolint:forcetypeassert } -func ContextWithProjectID(ctx context.Context, value string) context.Context { +func CommitSHA(ctx context.Context) string { + return ctx.Value(commitSha).(string) //nolint:forcetypeassert +} + +func WithProjectID(ctx context.Context, value string) context.Context { ctx = slogctx.With(ctx, slog.String("project_id", value)) ctx = context.WithValue(ctx, projectID, value) return ctx } -func ContextWithDryRun(ctx context.Context, dry bool) context.Context { +func WithDryRun(ctx context.Context, dry bool) context.Context { ctx = slogctx.With(ctx, slog.Bool("dry_run", dry)) ctx = context.WithValue(ctx, dryRun, dry) return ctx } -func IsDryRun(ctx context.Context) bool { - return ctx.Value(dryRun).(bool) //nolint:forcetypeassert +func WithUpdatePipeline(ctx context.Context, update bool) context.Context { + ctx = slogctx.With(ctx, slog.Bool("update_pipeline", update)) + ctx = context.WithValue(ctx, updatePipeline, update) + + return ctx +} + +func WithCommitSHA(ctx context.Context, sha string) context.Context { + ctx = slogctx.With(ctx, slog.String("git_commit_sha", sha)) + ctx = context.WithValue(ctx, commitSha, sha) + + return ctx } func ContextWithMergeRequestID(ctx context.Context, id string) context.Context { @@ -53,6 +61,14 @@ func ContextWithMergeRequestID(ctx context.Context, id string) context.Context { return ctx } +func IsDryRun(ctx context.Context) bool { + return ctx.Value(dryRun).(bool) //nolint:forcetypeassert +} + +func ShouldUpdatePipeline(ctx context.Context) bool { + return ctx.Value(updatePipeline).(bool) //nolint:forcetypeassert +} + func MergeRequestID(ctx context.Context) string { return ctx.Value(mergeRequestID).(string) //nolint:forcetypeassert } From 1c994f2224939bd31cc5bac375d8aa5db3e0b3c5 Mon Sep 17 00:00:00 2001 From: Christian Winther Date: Sat, 11 May 2024 22:28:47 +0200 Subject: [PATCH 02/10] fix: fix mandatory flag --- main.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index 837c5a8..30de5a3 100644 --- a/main.go +++ b/main.go @@ -92,9 +92,8 @@ func main() { }, }, &cli.StringFlag{ - Name: cmd.FlagMergeRequestID, - Usage: "The pull/merge ID to process, if not provided as a CLI flag", - Required: true, + Name: cmd.FlagMergeRequestID, + Usage: "The pull/merge ID to process, if not provided as a CLI flag", EnvVars: []string{ "CI_MERGE_REQUEST_IID", // GitLab CI }, From f4a8e3ab7a1b89feee3fb4352652ad11fafa6160 Mon Sep 17 00:00:00 2001 From: Christian Winther Date: Sat, 11 May 2024 22:29:22 +0200 Subject: [PATCH 03/10] fix: semgrep finding --- pkg/scm/gitlab/client.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/pkg/scm/gitlab/client.go b/pkg/scm/gitlab/client.go index dcb07ab..3507f45 100644 --- a/pkg/scm/gitlab/client.go +++ b/pkg/scm/gitlab/client.go @@ -71,11 +71,8 @@ func (client *Client) Start(ctx context.Context) error { Name: go_gitlab.Ptr("scm-engine"), Description: go_gitlab.Ptr("Currently evaluating MR"), }) - if err != nil { - return err - } - return nil + return err } // Stop pipeline @@ -97,11 +94,8 @@ func (client *Client) Stop(ctx context.Context, err error) error { Name: go_gitlab.Ptr("scm-engine"), Description: go_gitlab.Ptr(message), }) - if err != nil { - return err - } - return nil + return err } func graphqlBaseURL(inputURL *url.URL) string { From c673e291277375e077c177f8e395ddba3a6d17b5 Mon Sep 17 00:00:00 2001 From: Christian Winther Date: Sat, 11 May 2024 22:29:58 +0200 Subject: [PATCH 04/10] ci: move semgrep to lint instead of security --- .github/workflows/lint.yml | 22 ++++++++++++++++++++++ .github/workflows/security.yml | 22 ---------------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 61e1ac8..83f0bb3 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -68,3 +68,25 @@ jobs: - uses: github/codeql-action/autobuild@v3 - uses: github/codeql-action/analyze@v3 + + # ------------------------------ + + semgrep: + runs-on: ubuntu-latest + name: semgrep + container: + image: returntocorp/semgrep + steps: + - uses: actions/checkout@v4 + + - uses: actions/checkout@v4 + with: + repository: dgryski/semgrep-go + path: rules + + - uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: semgrep + run: semgrep scan --error --enable-nosem -f ./rules . diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index fb5a889..32caa17 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -40,28 +40,6 @@ jobs: # ------------------------------ - semgrep: - runs-on: ubuntu-latest - name: semgrep - container: - image: returntocorp/semgrep - steps: - - uses: actions/checkout@v4 - - - uses: actions/checkout@v4 - with: - repository: dgryski/semgrep-go - path: rules - - - uses: actions/setup-go@v5 - with: - go-version-file: go.mod - - - name: semgrep - run: semgrep scan --error --enable-nosem -f ./rules . - - # ------------------------------ - gitleaks: runs-on: ubuntu-latest name: gitleaks From f81931545c8842438f0cd71570e0764c75050f74 Mon Sep 17 00:00:00 2001 From: Christian Winther Date: Sat, 11 May 2024 22:30:40 +0200 Subject: [PATCH 05/10] ci: use '--all' flag for integration test --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index afbd64e..825250a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -47,7 +47,7 @@ jobs: run: ./scm-engine -h - name: Test scm-engine against test GitLab project - run: ./scm-engine evaluate 1 + run: ./scm-engine evaluate --all env: SCM_ENGINE_TOKEN: "${{ secrets.GITLAB_INTEGRATION_TEST_API_TOKEN }}" SCM_ENGINE_CONFIG_FILE: ".scm-engine.example.yml" From a26f1b52862af83b90958efb010609642520820b Mon Sep 17 00:00:00 2001 From: Christian Winther Date: Sat, 11 May 2024 22:32:24 +0200 Subject: [PATCH 06/10] ci: use 'all' arg instead of '--all' flag --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 825250a..737bbfd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -47,7 +47,7 @@ jobs: run: ./scm-engine -h - name: Test scm-engine against test GitLab project - run: ./scm-engine evaluate --all + run: ./scm-engine evaluate all env: SCM_ENGINE_TOKEN: "${{ secrets.GITLAB_INTEGRATION_TEST_API_TOKEN }}" SCM_ENGINE_CONFIG_FILE: ".scm-engine.example.yml" From df3eb37849b0fade010c1221d814ca316cef9780 Mon Sep 17 00:00:00 2001 From: Christian Winther Date: Sat, 11 May 2024 22:35:03 +0200 Subject: [PATCH 07/10] fix: 'commit' is not required in all cases --- main.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/main.go b/main.go index 30de5a3..4021580 100644 --- a/main.go +++ b/main.go @@ -99,9 +99,8 @@ func main() { }, }, &cli.StringFlag{ - Name: cmd.FlagCommitSHA, - Usage: "The git commit sha", - Required: true, + Name: cmd.FlagCommitSHA, + Usage: "The git commit sha", EnvVars: []string{ "CI_COMMIT_SHA", // GitLab CI }, From 7a51c1dbaad678ccabad0bdcd2245e5e05c7435e Mon Sep 17 00:00:00 2001 From: Christian Winther Date: Sat, 11 May 2024 22:48:58 +0200 Subject: [PATCH 08/10] fix: ensure 'evaluate all' works correctly with git sha --- pkg/scm/gitlab/client_merge_request.go | 17 +++++++++++++---- schema/gitlab.schema.graphqls | 11 ++++++----- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/pkg/scm/gitlab/client_merge_request.go b/pkg/scm/gitlab/client_merge_request.go index 5941a22..9ed94ce 100644 --- a/pkg/scm/gitlab/client_merge_request.go +++ b/pkg/scm/gitlab/client_merge_request.go @@ -87,10 +87,19 @@ func (client *MergeRequestClient) List(ctx context.Context, options *scm.ListMer return nil, err } - hits := []scm.ListMergeRequest{} - for _, x := range result.Project.MergeRequests.Nodes { - hits = append(hits, scm.ListMergeRequest{ID: x.ID}) + results := []scm.ListMergeRequest{} + + for _, mergeRequest := range result.Project.MergeRequests.Nodes { + // If there are no DiffHeadSha; there are no commits on the MR; so don't process it + if mergeRequest.DiffHeadSha == nil { + continue + } + + results = append(results, scm.ListMergeRequest{ + ID: mergeRequest.ID, + SHA: *mergeRequest.DiffHeadSha, + }) } - return hits, nil + return results, nil } diff --git a/schema/gitlab.schema.graphqls b/schema/gitlab.schema.graphqls index 65f7e51..4e7384a 100644 --- a/schema/gitlab.schema.graphqls +++ b/schema/gitlab.schema.graphqls @@ -38,7 +38,7 @@ type Context { CurrentUser: ContextUser! "Information about the event that triggered the evaluation. Empty when not using webhook server." - WebhookEvent: Any @generated + WebhookEvent: Any @generated @expr(key: "webhook_event") } enum MergeRequestState { @@ -98,7 +98,8 @@ type ListMergeRequestsProjectMergeRequestNodes { } type ListMergeRequestsProjectMergeRequest { - ID: String! @graphql(key: "iid") @internal + ID: String! @graphql(key: "iid") @internal + DiffHeadSha: String @graphql(key: "diffHeadSha") @internal } # https://docs.gitlab.com/ee/api/graphql/reference/#project @@ -135,9 +136,9 @@ type ContextProject { "Labels available on this project" Labels: [ContextLabel!] @generated - ResponseLabels: ContextLabelNode @internal @graphql(key: "labels(first: 200)") - MergeRequest: ContextMergeRequest @internal @graphql(key: "mergeRequest(iid: $mr_id)") - ResponseGroup: ContextGroup @internal @graphql(key: "group") + ResponseLabels: ContextLabelNode @internal @graphql(key: "labels(first: 200)") + MergeRequest: ContextMergeRequest @internal @graphql(key: "mergeRequest(iid: $mr_id)") + ResponseGroup: ContextGroup @internal @graphql(key: "group") } # https://docs.gitlab.com/ee/api/graphql/reference/#group From c8059f4255ee2a5c68374a4288a91e95a0c324ce Mon Sep 17 00:00:00 2001 From: Christian Winther Date: Sat, 11 May 2024 22:51:33 +0200 Subject: [PATCH 09/10] fix: don't update pipelines in DRY mode either --- pkg/state/context.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/state/context.go b/pkg/state/context.go index abcb179..fae101f 100644 --- a/pkg/state/context.go +++ b/pkg/state/context.go @@ -66,7 +66,7 @@ func IsDryRun(ctx context.Context) bool { } func ShouldUpdatePipeline(ctx context.Context) bool { - return ctx.Value(updatePipeline).(bool) //nolint:forcetypeassert + return !IsDryRun(ctx) && ctx.Value(updatePipeline).(bool) //nolint:forcetypeassert } func MergeRequestID(ctx context.Context) string { From 97eb963539c4f2d536e6826b31b27d71777433ee Mon Sep 17 00:00:00 2001 From: Christian Winther Date: Sat, 11 May 2024 23:00:00 +0200 Subject: [PATCH 10/10] fix: update --update-pipeline env var --- docs/commands/evaluate.md | 2 +- docs/commands/server.md | 2 +- main.go | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/commands/evaluate.md b/docs/commands/evaluate.md index cf35f29..86f2b64 100644 --- a/docs/commands/evaluate.md +++ b/docs/commands/evaluate.md @@ -18,7 +18,7 @@ OPTIONS: --project value GitLab project (example: 'gitlab-org/gitlab') [$GITLAB_PROJECT, $CI_PROJECT_PATH] --id value The pull/merge ID to process, if not provided as a CLI flag [$CI_MERGE_REQUEST_IID] --commit value The git commit sha [$CI_COMMIT_SHA] - --update-pipeline Update the CI pipeline status with progress (default: true) [$SCM_ENGINE_SKIP_PIPELINE] + --update-pipeline Update the CI pipeline status with progress (default: true) [$SCM_ENGINE_UPDATE_PIPELINE] --help, -h show help GLOBAL OPTIONS: diff --git a/docs/commands/server.md b/docs/commands/server.md index 18d7ccb..039e7bf 100644 --- a/docs/commands/server.md +++ b/docs/commands/server.md @@ -26,7 +26,7 @@ USAGE: OPTIONS: --webhook-secret value Used to validate received payloads. Sent with the request in the X-Gitlab-Token HTTP header [$SCM_ENGINE_WEBHOOK_SECRET] --listen value IP + Port that the HTTP server should listen on (default: "0.0.0.0:3000") [$SCM_ENGINE_LISTEN] - --update-pipeline Update the CI pipeline status with progress (default: true) [$SCM_ENGINE_SKIP_PIPELINE] + --update-pipeline Update the CI pipeline status with progress (default: true) [$SCM_ENGINE_UPDATE_PIPELINE] --help, -h show help GLOBAL OPTIONS: diff --git a/main.go b/main.go index 4021580..b8905f9 100644 --- a/main.go +++ b/main.go @@ -72,6 +72,7 @@ func main() { &cli.BoolFlag{ Name: cmd.FlagDryRun, Usage: "Dry run, don't actually _do_ actions, just print them", + Value: false, }, }, Commands: []*cli.Command{ @@ -110,7 +111,7 @@ func main() { Usage: "Update the CI pipeline status with progress", Value: true, EnvVars: []string{ - "SCM_ENGINE_SKIP_PIPELINE", + "SCM_ENGINE_UPDATE_PIPELINE", }, }, }, @@ -140,7 +141,7 @@ func main() { Usage: "Update the CI pipeline status with progress", Value: true, EnvVars: []string{ - "SCM_ENGINE_SKIP_PIPELINE", + "SCM_ENGINE_UPDATE_PIPELINE", }, }, },