• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mindersec / minder / 12160307325

04 Dec 2024 01:02PM UTC coverage: 55.187% (-0.03%) from 55.218%
12160307325

push

github

web-flow
Simplify propagation of action settings (#5132)

* Simplify propagation of action settings

Currently, action settings are passed around via a complex dance of
getter/setter functions that span multiple places in the executor.

However, this is not necessary, as we've already read these settings at
the beginning of execution, and can just set them in place where we need
them.

This simplifies this logic and leverages the aggregate profile structure
to set this. It also enforces the setting as part of the action driver
initiatlization.

Signed-off-by: Juan Antonio Osorio <ozz@stacklok.com>

* Fix unit tests

Signed-off-by: Juan Antonio Osorio <ozz@stacklok.com>

* Fix linter issues

Signed-off-by: Juan Antonio Osorio <ozz@stacklok.com>

---------

Signed-off-by: Juan Antonio Osorio <ozz@stacklok.com>

29 of 44 new or added lines in 10 files covered. (65.91%)

6 existing lines in 5 files now uncovered.

16458 of 29822 relevant lines covered (55.19%)

38.73 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

60.38
/internal/engine/actions/remediate/pull_request/pull_request.go
1
// SPDX-FileCopyrightText: Copyright 2023 The Minder Authors
2
// SPDX-License-Identifier: Apache-2.0
3

4
// Package pull_request provides the pull request remediation engine
5
package pull_request
6

7
import (
8
        "bytes"
9
        "context"
10
        "encoding/json"
11
        "errors"
12
        "fmt"
13
        "os"
14
        "strings"
15
        "text/template"
16
        "time"
17

18
        "github.com/go-git/go-git/v5"
19
        "github.com/go-git/go-git/v5/config"
20
        "github.com/go-git/go-git/v5/plumbing"
21
        "github.com/go-git/go-git/v5/plumbing/object"
22
        "github.com/google/go-github/v63/github"
23
        "github.com/rs/zerolog"
24
        "google.golang.org/protobuf/reflect/protoreflect"
25

26
        "github.com/mindersec/minder/internal/db"
27
        enginerr "github.com/mindersec/minder/internal/engine/errors"
28
        "github.com/mindersec/minder/internal/engine/interfaces"
29
        "github.com/mindersec/minder/internal/util"
30
        pb "github.com/mindersec/minder/pkg/api/protobuf/go/minder/v1"
31
        engifv1 "github.com/mindersec/minder/pkg/engine/v1/interfaces"
32
        "github.com/mindersec/minder/pkg/profiles/models"
33
        provifv1 "github.com/mindersec/minder/pkg/providers/v1"
34
)
35

36
const (
37
        // RemediateType is the type of the REST remediation engine
38
        RemediateType = "pull_request"
39
)
40

41
const (
42
        // if no Mode is specified, create a regular file with 0644 UNIX permissions
43
        ghModeNonExecFile = "100644"
44
        dflBranchBaseName = "minder"
45
        dflBranchTo       = "main"
46
)
47

48
const (
49
        prTemplateName = "prBody"
50
        prBodyTmplStr  = "{{.PrText}}"
51
)
52

53
const (
54
        // TitleMaxLength is the maximum number of bytes for the title
55
        TitleMaxLength = 256
56

57
        // BodyMaxLength is the maximum number of bytes for the body
58
        BodyMaxLength = 5120
59
)
60

61
type pullRequestMetadata struct {
62
        Number int `json:"pr_number,omitempty"`
63
}
64

65
// Remediator is the remediation engine for the Pull Request remediation type
66
type Remediator struct {
67
        ghCli      provifv1.GitHub
68
        actionType interfaces.ActionType
69
        setting    models.ActionOpt
70

71
        prCfg                *pb.RuleType_Definition_Remediate_PullRequestRemediation
72
        modificationRegistry modificationRegistry
73

74
        titleTemplate *util.SafeTemplate
75
        bodyTemplate  *util.SafeTemplate
76
}
77

78
type paramsPR struct {
79
        ingested   *engifv1.Result
80
        repo       *pb.Repository
81
        title      string
82
        modifier   fsModifier
83
        body       string
84
        metadata   *pullRequestMetadata
85
        prevStatus *db.ListRuleEvaluationsByProfileIdRow
86
}
87

88
// NewPullRequestRemediate creates a new PR remediation engine
89
func NewPullRequestRemediate(
90
        actionType interfaces.ActionType,
91
        prCfg *pb.RuleType_Definition_Remediate_PullRequestRemediation,
92
        ghCli provifv1.GitHub,
93
        setting models.ActionOpt,
94
) (*Remediator, error) {
8✔
95
        err := prCfg.Validate()
8✔
96
        if err != nil {
8✔
97
                return nil, fmt.Errorf("pull request remediation config is invalid: %w", err)
×
98
        }
×
99

100
        titleTmpl, err := util.NewSafeHTMLTemplate(&prCfg.Title, "title")
8✔
101
        if err != nil {
8✔
102
                return nil, fmt.Errorf("cannot parse title template: %w", err)
×
103
        }
×
104

105
        bodyTmpl, err := util.NewSafeHTMLTemplate(&prCfg.Body, "body")
8✔
106
        if err != nil {
8✔
107
                return nil, fmt.Errorf("cannot parse body template: %w", err)
×
108
        }
×
109

110
        modRegistry := newModificationRegistry()
8✔
111
        modRegistry.registerBuiltIn()
8✔
112

8✔
113
        return &Remediator{
8✔
114
                ghCli:                ghCli,
8✔
115
                prCfg:                prCfg,
8✔
116
                actionType:           actionType,
8✔
117
                modificationRegistry: modRegistry,
8✔
118
                setting:              setting,
8✔
119

8✔
120
                titleTemplate: titleTmpl,
8✔
121
                bodyTemplate:  bodyTmpl,
8✔
122
        }, nil
8✔
123
}
124

125
// PrTemplateParams is the parameters for the PR templates
126
type PrTemplateParams struct {
127
        // Entity is the entity to be evaluated
128
        Entity any
129
        // Profile are the parameters to be used in the template
130
        Profile map[string]any
131
        // Params are the rule instance parameters
132
        Params map[string]any
133
}
134

135
// Class returns the action type of the remediation engine
136
func (r *Remediator) Class() interfaces.ActionType {
×
137
        return r.actionType
×
138
}
×
139

140
// Type returns the action subtype of the remediation engine
141
func (_ *Remediator) Type() string {
×
142
        return RemediateType
×
143
}
×
144

145
// GetOnOffState returns the alert action state read from the profile
NEW
146
func (r *Remediator) GetOnOffState() models.ActionOpt {
×
NEW
147
        return models.ActionOptOrDefault(r.setting, models.ActionOptOff)
×
UNCOV
148
}
×
149

150
// Do perform the remediation
151
func (r *Remediator) Do(
152
        ctx context.Context,
153
        cmd interfaces.ActionCmd,
154
        ent protoreflect.ProtoMessage,
155
        params interfaces.ActionsParams,
156
        metadata *json.RawMessage,
157
) (json.RawMessage, error) {
8✔
158
        p, err := r.getParamsForPRRemediation(ctx, ent, params, metadata)
8✔
159
        if err != nil {
8✔
160
                return nil, fmt.Errorf("cannot get PR remediation params: %w", err)
×
161
        }
×
162
        var remErr error
8✔
163
        switch r.setting {
8✔
164
        case models.ActionOptOn:
8✔
165
                return r.run(ctx, cmd, p)
8✔
166
        case models.ActionOptDryRun:
×
167
                return r.dryRun(ctx, cmd, p)
×
168
        case models.ActionOptOff, models.ActionOptUnknown:
×
169
                remErr = errors.New("unexpected action")
×
170
        }
171
        return nil, remErr
×
172
}
173

174
func (r *Remediator) getParamsForPRRemediation(
175
        ctx context.Context,
176
        ent protoreflect.ProtoMessage,
177
        params interfaces.ActionsParams,
178
        metadata *json.RawMessage,
179
) (*paramsPR, error) {
8✔
180
        logger := zerolog.Ctx(ctx)
8✔
181

8✔
182
        repo, ok := ent.(*pb.Repository)
8✔
183
        if !ok {
8✔
184
                return nil, fmt.Errorf("expected repository, got %T", ent)
×
185
        }
×
186

187
        tmplParams := &PrTemplateParams{
8✔
188
                Entity:  ent,
8✔
189
                Profile: params.GetRule().Def,
8✔
190
                Params:  params.GetRule().Params,
8✔
191
        }
8✔
192

8✔
193
        ingested := params.GetIngestResult()
8✔
194
        if ingested == nil || ingested.Fs == nil || ingested.Storer == nil {
8✔
195
                return nil, errors.New("ingested filesystem is nil or no git repo was ingested")
×
196
        }
×
197

198
        title, err := r.titleTemplate.Render(ctx, tmplParams, TitleMaxLength)
8✔
199
        if err != nil {
8✔
200
                return nil, fmt.Errorf("cannot execute title template: %w", err)
×
201
        }
×
202

203
        modification, err := r.modificationRegistry.getModification(getMethod(r.prCfg), &modificationConstructorParams{
8✔
204
                prCfg: r.prCfg,
8✔
205
                ghCli: r.ghCli,
8✔
206
                bfs:   ingested.Fs,
8✔
207
                def:   params.GetRule().Def,
8✔
208
        })
8✔
209
        if err != nil {
8✔
210
                return nil, fmt.Errorf("cannot get modification: %w", err)
×
211
        }
×
212

213
        err = modification.createFsModEntries(ctx, params)
8✔
214
        if err != nil {
8✔
215
                return nil, fmt.Errorf("cannot create PR entries: %w", err)
×
216
        }
×
217

218
        prFullBodyText, err := r.getPrBodyText(ctx, tmplParams)
8✔
219
        if err != nil {
8✔
220
                return nil, fmt.Errorf("cannot create PR full body text: %w", err)
×
221
        }
×
222

223
        // Unmarshal the existing remediation metadata, if any
224
        meta := &pullRequestMetadata{}
8✔
225
        if metadata != nil {
8✔
226
                err := json.Unmarshal(*metadata, meta)
×
227
                if err != nil {
×
228
                        // There's nothing saved apparently, so no need to fail here, but do log the error
×
229
                        logger.Debug().Msgf("error unmarshalling remediation metadata: %v", err)
×
230
                }
×
231
        }
232
        return &paramsPR{
8✔
233
                ingested:   ingested,
8✔
234
                repo:       repo,
8✔
235
                title:      title,
8✔
236
                modifier:   modification,
8✔
237
                body:       prFullBodyText,
8✔
238
                metadata:   meta,
8✔
239
                prevStatus: params.GetEvalStatusFromDb(),
8✔
240
        }, nil
8✔
241
}
242

243
func (r *Remediator) dryRun(
244
        ctx context.Context,
245
        cmd interfaces.ActionCmd,
246
        p *paramsPR,
247
) (json.RawMessage, error) {
×
248
        logger := zerolog.Ctx(ctx).Info().Str("repo", p.repo.String())
×
249
        // Process the command
×
250
        switch cmd {
×
251
        case interfaces.ActionCmdOn:
×
252
                // TODO: jsonize too
×
253
                logger.Msgf("title:\n%s\n", p.title)
×
254
                logger.Msgf("body:\n%s\n", p.body)
×
255

×
256
                err := p.modifier.writeSummary(os.Stdout)
×
257
                if err != nil {
×
258
                        logger.Msgf("cannot write summary: %s\n", err)
×
259
                }
×
260
                return nil, nil
×
261
        case interfaces.ActionCmdOff:
×
262
                if p.metadata == nil || p.metadata.Number == 0 {
×
263
                        // We cannot do anything without a PR number, so we assume that closing this is a success
×
264
                        return nil, fmt.Errorf("no pull request number provided: %w", enginerr.ErrActionSkipped)
×
265
                }
×
266
                endpoint := fmt.Sprintf("repos/%v/%v/pulls/%d", p.repo.GetOwner(), p.repo.GetName(), p.metadata.Number)
×
267
                body := "{\"state\": \"closed\"}"
×
268
                curlCmd, err := util.GenerateCurlCommand(ctx, "PATCH", r.ghCli.GetBaseURL(), endpoint, body)
×
269
                if err != nil {
×
270
                        return nil, fmt.Errorf("cannot generate curl command to close a pull request: %w", err)
×
271
                }
×
272
                logger.Msgf("run the following curl command: \n%s\n", curlCmd)
×
273
                return nil, nil
×
274
        case interfaces.ActionCmdDoNothing:
×
275
                return r.runDoNothing(ctx, p)
×
276
        }
277
        return nil, nil
×
278
}
279
func (r *Remediator) runOn(
280
        ctx context.Context,
281
        p *paramsPR,
282
) (json.RawMessage, error) {
8✔
283
        logger := zerolog.Ctx(ctx).With().Str("repo", p.repo.String()).Logger()
8✔
284
        repo, err := git.Open(p.ingested.Storer, p.ingested.Fs)
8✔
285
        if err != nil {
8✔
286
                return nil, fmt.Errorf("cannot open git repo: %w", err)
×
287
        }
×
288

289
        wt, err := repo.Worktree()
8✔
290
        if err != nil {
8✔
291
                return nil, fmt.Errorf("cannot get worktree: %w", err)
×
292
        }
×
293

294
        logger.Debug().Msg("Getting authenticated user details")
8✔
295
        email, err := r.ghCli.GetPrimaryEmail(ctx)
8✔
296
        if err != nil {
8✔
297
                return nil, fmt.Errorf("cannot get primary email: %w", err)
×
298
        }
×
299

300
        currentHeadReference, err := repo.Head()
8✔
301
        if err != nil {
8✔
302
                return nil, fmt.Errorf("cannot get current HEAD: %w", err)
×
303
        }
×
304
        currHeadName := currentHeadReference.Name()
8✔
305

8✔
306
        // This resets the worktree so we don't corrupt the ingest cache (at least the main/originally-fetched branch).
8✔
307
        // This also makes sure, all new remediations check out from main branch rather than prev remediation branch.
8✔
308
        defer checkoutToOriginallyFetchedBranch(&logger, wt, currHeadName)
8✔
309

8✔
310
        logger.Debug().Str("branch", branchBaseName(p.title)).Msg("Checking out branch")
8✔
311
        err = wt.Checkout(&git.CheckoutOptions{
8✔
312
                Branch: plumbing.NewBranchReferenceName(branchBaseName(p.title)),
8✔
313
                Create: true,
8✔
314
        })
8✔
315
        if err != nil {
8✔
316
                return nil, fmt.Errorf("cannot checkout branch: %w", err)
×
317
        }
×
318

319
        logger.Debug().Msg("Creating file entries")
8✔
320
        changeEntries, err := p.modifier.modifyFs()
8✔
321
        if err != nil {
8✔
322
                return nil, fmt.Errorf("cannot modifyFs: %w", err)
×
323
        }
×
324

325
        logger.Debug().Msg("Staging changes")
8✔
326
        for _, entry := range changeEntries {
19✔
327
                if _, err := wt.Add(entry.Path); err != nil {
11✔
328
                        return nil, fmt.Errorf("cannot add file %s: %w", entry.Path, err)
×
329
                }
×
330
        }
331

332
        logger.Debug().Msg("Committing changes")
8✔
333
        _, err = wt.Commit(p.title, &git.CommitOptions{
8✔
334
                Author: &object.Signature{
8✔
335
                        Name:  userNameForCommit(ctx, r.ghCli),
8✔
336
                        Email: email,
8✔
337
                        When:  time.Now(),
8✔
338
                },
8✔
339
        })
8✔
340
        if err != nil {
8✔
341
                return nil, fmt.Errorf("cannot commit: %w", err)
×
342
        }
×
343

344
        refspec := refFromBranch(branchBaseName(p.title))
8✔
345

8✔
346
        l := logger.With().Str("branchBaseName", branchBaseName(p.title)).Logger()
8✔
347

8✔
348
        // Check if a PR already exists for this branch
8✔
349
        prNumber := getPRNumberFromBranch(ctx, r.ghCli, p.repo, branchBaseName(p.title))
8✔
350

8✔
351
        // If no PR exists, push the branch and create a PR
8✔
352
        if prNumber == 0 {
15✔
353
                err = pushBranch(ctx, repo, refspec, r.ghCli)
7✔
354
                if err != nil {
7✔
355
                        return nil, fmt.Errorf("cannot push branch: %w", err)
×
356
                }
×
357

358
                pr, err := r.ghCli.CreatePullRequest(
7✔
359
                        ctx, p.repo.GetOwner(), p.repo.GetName(),
7✔
360
                        p.title, p.body,
7✔
361
                        refspec,
7✔
362
                        dflBranchTo,
7✔
363
                )
7✔
364
                if err != nil {
8✔
365
                        return nil, fmt.Errorf("cannot create pull request: %w, %w", err, enginerr.ErrActionFailed)
1✔
366
                }
1✔
367
                // Return the new PR number
368
                prNumber = pr.GetNumber()
6✔
369
                l = l.With().Str("pr_origin", "newly_created").Logger()
6✔
370
        } else {
1✔
371
                l = l.With().Str("pr_origin", "already_existed").Logger()
1✔
372
        }
1✔
373

374
        newMeta, err := json.Marshal(pullRequestMetadata{Number: prNumber})
7✔
375
        if err != nil {
7✔
376
                return nil, fmt.Errorf("error marshalling pull request remediation metadata json: %w", err)
×
377
        }
×
378
        // Success - return the new metadata for storing the pull request number
379
        l.Info().Int("pr_number", prNumber).Msg("pull request remediation completed")
7✔
380
        return newMeta, enginerr.ErrActionPending
7✔
381
}
382

383
func getPRNumberFromBranch(
384
        ctx context.Context,
385
        cli provifv1.GitHub,
386
        repo *pb.Repository,
387
        branchName string,
388
) int {
8✔
389
        opts := &github.PullRequestListOptions{
8✔
390
                // TODO: This is not working as expected, need to fix this
8✔
391
                // Head: fmt.Sprintf("%s:%s", repo.GetOwner(), branchName),
8✔
392
                State: "open",
8✔
393
        }
8✔
394
        openPrs, err := cli.ListPullRequests(ctx, repo.GetOwner(), repo.GetName(), opts)
8✔
395
        if err != nil {
8✔
396
                return 0
×
397
        }
×
398
        for _, pr := range openPrs {
9✔
399
                if pr.GetHead().GetRef() == branchName {
2✔
400
                        return pr.GetNumber()
1✔
401
                }
1✔
402
        }
403
        return 0
7✔
404
}
405

406
func (r *Remediator) runOff(
407
        ctx context.Context,
408
        p *paramsPR,
409
) (json.RawMessage, error) {
×
410
        logger := zerolog.Ctx(ctx).With().Str("repo", p.repo.String()).Logger()
×
411

×
412
        if p.metadata == nil || p.metadata.Number == 0 {
×
413
                // We cannot do anything without a PR number, so we assume that closing this is a success
×
414
                return nil, fmt.Errorf("no pull request number provided: %w", enginerr.ErrActionSkipped)
×
415
        }
×
416

417
        pr, err := r.ghCli.ClosePullRequest(ctx, p.repo.GetOwner(), p.repo.GetName(), p.metadata.Number)
×
418
        if err != nil {
×
419
                return nil, fmt.Errorf("error closing pull request %d: %w, %w", p.metadata.Number, err, enginerr.ErrActionFailed)
×
420
        }
×
421
        logger.Info().Int("pr_number", pr.GetNumber()).Msg("pull request closed")
×
422
        return nil, enginerr.ErrActionSkipped
×
423
}
424

425
func (r *Remediator) run(
426
        ctx context.Context,
427
        cmd interfaces.ActionCmd,
428
        p *paramsPR,
429
) (json.RawMessage, error) {
8✔
430
        // Process the command
8✔
431
        switch cmd {
8✔
432
        case interfaces.ActionCmdOn:
8✔
433
                return r.runOn(ctx, p)
8✔
434
        case interfaces.ActionCmdOff:
×
435
                return r.runOff(ctx, p)
×
436
        case interfaces.ActionCmdDoNothing:
×
437
                return r.runDoNothing(ctx, p)
×
438
        }
439
        return nil, enginerr.ErrActionSkipped
×
440
}
441

442
func pushBranch(ctx context.Context, repo *git.Repository, refspec string, gh provifv1.GitHub) error {
7✔
443
        var b bytes.Buffer
7✔
444
        pushOptions := &git.PushOptions{
7✔
445
                RemoteName: guessRemote(repo),
7✔
446
                Force:      true,
7✔
447
                RefSpecs: []config.RefSpec{
7✔
448
                        config.RefSpec(
7✔
449
                                fmt.Sprintf("+%s:%s", refspec, refspec),
7✔
450
                        ),
7✔
451
                },
7✔
452
                Progress: &b,
7✔
453
        }
7✔
454
        err := gh.AddAuthToPushOptions(ctx, pushOptions)
7✔
455
        if err != nil {
7✔
456
                return fmt.Errorf("cannot add auth to push options: %w", err)
×
457
        }
×
458

459
        err = repo.PushContext(ctx, pushOptions)
7✔
460
        if err != nil {
7✔
461
                return fmt.Errorf("cannot push: %w", err)
×
462
        }
×
463
        zerolog.Ctx(ctx).Debug().Msgf("Push output: %s", b.String())
7✔
464
        return nil
7✔
465
}
466

467
func guessRemote(gitRepo *git.Repository) string {
7✔
468
        remotes, err := gitRepo.Remotes()
7✔
469
        if err != nil {
7✔
470
                return ""
×
471
        }
×
472

473
        if len(remotes) == 0 {
7✔
474
                return ""
×
475
        }
×
476

477
        for _, remote := range remotes {
14✔
478
                if remote.Config().Name == "origin" {
14✔
479
                        return remote.Config().Name
7✔
480
                }
7✔
481
        }
482

483
        return remotes[0].Config().Name
×
484
}
485

486
func refFromBranch(branchFrom string) string {
16✔
487
        return fmt.Sprintf("refs/heads/%s", branchFrom)
16✔
488
}
16✔
489

490
func branchBaseName(prTitle string) string {
48✔
491
        baseName := dflBranchBaseName
48✔
492
        normalizedPrTitle := strings.ReplaceAll(strings.ToLower(prTitle), " ", "_")
48✔
493
        return fmt.Sprintf("%s_%s", baseName, normalizedPrTitle)
48✔
494
}
48✔
495

496
func userNameForCommit(ctx context.Context, gh provifv1.GitHub) string {
8✔
497
        var name string
8✔
498

8✔
499
        // we ignore errors here, as we can still create a commit without a name
8✔
500
        // and errors are checked when getting the primary email
8✔
501
        name, _ = gh.GetName(ctx)
8✔
502
        if name == "" {
8✔
503
                name, _ = gh.GetLogin(ctx)
×
504
        }
×
505
        return name
8✔
506
}
507

508
func (r *Remediator) getPrBodyText(ctx context.Context, tmplParams *PrTemplateParams) (string, error) {
8✔
509
        body := new(bytes.Buffer)
8✔
510
        if err := r.bodyTemplate.Execute(ctx, body, tmplParams, BodyMaxLength); err != nil {
8✔
511
                return "", fmt.Errorf("cannot execute body template: %w", err)
×
512
        }
×
513

514
        prFullBodyText, err := createReviewBody(body.String())
8✔
515
        if err != nil {
8✔
516
                return "", fmt.Errorf("cannot create PR full body text: %w", err)
×
517
        }
×
518

519
        return prFullBodyText, nil
8✔
520
}
521

522
func getMethod(prCfg *pb.RuleType_Definition_Remediate_PullRequestRemediation) string {
8✔
523
        if prCfg.Method == "" {
8✔
524
                return minderContentModification
×
525
        }
×
526

527
        return prCfg.Method
8✔
528
}
529

530
func createReviewBody(prText string) (string, error) {
8✔
531
        tmpl, err := template.New(prTemplateName).Option("missingkey=error").Parse(prBodyTmplStr)
8✔
532
        if err != nil {
8✔
533
                return "", err
×
534
        }
×
535

536
        data := struct {
8✔
537
                PrText string
8✔
538
        }{
8✔
539
                PrText: prText,
8✔
540
        }
8✔
541

8✔
542
        // Execute the template
8✔
543
        var buf bytes.Buffer
8✔
544
        if err := tmpl.Execute(&buf, data); err != nil {
8✔
545
                return "", err
×
546
        }
×
547

548
        return buf.String(), nil
8✔
549
}
550

551
func checkoutToOriginallyFetchedBranch(
552
        logger *zerolog.Logger,
553
        wt *git.Worktree,
554
        originallyFetchedBranch plumbing.ReferenceName,
555
) {
8✔
556
        err := wt.Checkout(&git.CheckoutOptions{
8✔
557
                Branch: originallyFetchedBranch,
8✔
558
        })
8✔
559
        if err != nil {
8✔
560
                logger.Err(err).Msg(
×
561
                        "unable to checkout to the previous head, this can corrupt the ingest cache, should not happen",
×
562
                )
×
563
        } else {
8✔
564
                logger.Info().Msg(fmt.Sprintf("checked out back to %s branch", originallyFetchedBranch))
8✔
565
        }
8✔
566
}
567

568
// runDoNothing returns the previous remediation status
569
func (_ *Remediator) runDoNothing(ctx context.Context, p *paramsPR) (json.RawMessage, error) {
×
570
        logger := zerolog.Ctx(ctx).With().Str("repo", p.repo.String()).Logger()
×
571

×
572
        logger.Debug().Msg("Running do nothing")
×
573

×
574
        // Return the previous remediation status.
×
575
        err := enginerr.RemediationStatusAsError(p.prevStatus)
×
576
        // If there is a valid remediation metadata, return it too
×
577
        if p.prevStatus != nil {
×
578
                return p.prevStatus.RemMetadata, err
×
579
        }
×
580
        // If there is no remediation metadata, return nil as the metadata and the error
581
        return nil, err
×
582
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc