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

mindersec / minder / 25342198270

04 May 2026 08:36PM UTC coverage: 60.442% (+2.1%) from 58.3%
25342198270

Pull #6253

github

web-flow
Merge 537c7f40e into 1347d06af
Pull Request #6253: Update roadmap for 2026 (and possibly beyond)

20396 of 33745 relevant lines covered (60.44%)

38.95 hits per line

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

61.13
/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/proto"
25

26
        dbadapter "github.com/mindersec/minder/internal/adapters/db"
27
        "github.com/mindersec/minder/internal/db"
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
        enginerr "github.com/mindersec/minder/pkg/engine/errors"
32
        engifv1 "github.com/mindersec/minder/pkg/engine/v1/interfaces"
33
        "github.com/mindersec/minder/pkg/profiles/models"
34
        provifv1 "github.com/mindersec/minder/pkg/providers/v1"
35
)
36

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

42
const (
43
        // if no Mode is specified, create a regular file with 0644 UNIX permissions
44
        ghModeNonExecFile = "100644"
45
        dflBranchBaseName = "minder"
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.Ingested
80
        repo       *pb.Repository
81
        title      string
82
        ruleName   string
83
        modifier   fsModifier
84
        body       string
85
        metadata   *pullRequestMetadata
86
        prevStatus *db.ListRuleEvaluationsByProfileIdRow
87
}
88

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

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

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

111
        modRegistry := newModificationRegistry()
9✔
112
        modRegistry.registerBuiltIn()
9✔
113

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

9✔
121
                titleTemplate: titleTmpl,
9✔
122
                bodyTemplate:  bodyTmpl,
9✔
123
        }, nil
9✔
124
}
125

126
// PrTemplateParams is the parameters for the PR templates
127
type PrTemplateParams struct {
128
        // Entity is the entity to be evaluated
129
        Entity any
130
        // Profile are the parameters to be used in the template
131
        Profile map[string]any
132
        // Params are the rule instance parameters
133
        Params map[string]any
134
        // EvalResultOutput is the data output by the rule evaluation engine
135
        EvalResultOutput any
136
}
137

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

143
// Type returns the action subtype of the remediation engine
144
func (*Remediator) Type() string {
×
145
        return RemediateType
×
146
}
×
147

148
// GetOnOffState returns the alert action state read from the profile
149
func (r *Remediator) GetOnOffState() models.ActionOpt {
×
150
        return models.ActionOptOrDefault(r.setting, models.ActionOptOff)
×
151
}
×
152

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

177
func (r *Remediator) getParamsForPRRemediation(
178
        ctx context.Context,
179
        ent proto.Message,
180
        params interfaces.ActionsParams,
181
        metadata *json.RawMessage,
182
) (*paramsPR, error) {
9✔
183
        logger := zerolog.Ctx(ctx)
9✔
184

9✔
185
        repo, ok := ent.(*pb.Repository)
9✔
186
        if !ok {
9✔
187
                return nil, fmt.Errorf("expected repository, got %T", ent)
×
188
        }
×
189

190
        tmplParams := &PrTemplateParams{
9✔
191
                Entity:  ent,
9✔
192
                Profile: params.GetRule().Def,
9✔
193
                Params:  params.GetRule().Params,
9✔
194
        }
9✔
195

9✔
196
        if params.GetEvalResult() != nil {
18✔
197
                tmplParams.EvalResultOutput = params.GetEvalResult().Output
9✔
198
        }
9✔
199

200
        ingested := params.GetIngestResult()
9✔
201
        if ingested == nil || ingested.Fs == nil || ingested.Storer == nil {
9✔
202
                return nil, errors.New("ingested filesystem is nil or no git repo was ingested")
×
203
        }
×
204

205
        title, err := r.titleTemplate.Render(ctx, tmplParams, TitleMaxLength)
9✔
206
        if err != nil {
9✔
207
                return nil, fmt.Errorf("cannot execute title template: %w", err)
×
208
        }
×
209

210
        modification, err := r.modificationRegistry.getModification(getMethod(r.prCfg), &modificationConstructorParams{
9✔
211
                prCfg: r.prCfg,
9✔
212
                ghCli: r.ghCli,
9✔
213
                bfs:   ingested.Fs,
9✔
214
                def:   params.GetRule().Def,
9✔
215
        })
9✔
216
        if err != nil {
9✔
217
                return nil, fmt.Errorf("cannot get modification: %w", err)
×
218
        }
×
219

220
        err = modification.createFsModEntries(ctx, ent, params)
9✔
221
        if err != nil {
9✔
222
                return nil, fmt.Errorf("cannot create PR entries: %w", err)
×
223
        }
×
224

225
        prFullBodyText, err := r.getPrBodyText(ctx, tmplParams)
9✔
226
        if err != nil {
9✔
227
                return nil, fmt.Errorf("cannot create PR full body text: %w", err)
×
228
        }
×
229

230
        // Unmarshal the existing remediation metadata, if any
231
        meta := &pullRequestMetadata{}
9✔
232
        if metadata != nil {
9✔
233
                err := json.Unmarshal(*metadata, meta)
×
234
                if err != nil {
×
235
                        // There's nothing saved apparently, so no need to fail here, but do log the error
×
236
                        logger.Debug().Msgf("error unmarshalling remediation metadata: %v", err)
×
237
                }
×
238
        }
239
        return &paramsPR{
9✔
240
                ingested:   ingested,
9✔
241
                repo:       repo,
9✔
242
                title:      title,
9✔
243
                ruleName:   params.GetRule().Name,
9✔
244
                modifier:   modification,
9✔
245
                body:       prFullBodyText,
9✔
246
                metadata:   meta,
9✔
247
                prevStatus: params.GetEvalStatusFromDb(),
9✔
248
        }, nil
9✔
249
}
250

251
func (r *Remediator) dryRun(
252
        ctx context.Context,
253
        cmd interfaces.ActionCmd,
254
        p *paramsPR,
255
) (json.RawMessage, error) {
×
256
        logger := zerolog.Ctx(ctx).Info().Str("repo", p.repo.String())
×
257
        // Process the command
×
258
        switch cmd {
×
259
        case interfaces.ActionCmdOn:
×
260
                // TODO: jsonize too
×
261
                logger.Msgf("title:\n%s\n", p.title)
×
262
                logger.Msgf("body:\n%s\n", p.body)
×
263

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

297
        wt, err := repo.Worktree()
9✔
298
        if err != nil {
9✔
299
                return nil, fmt.Errorf("cannot get worktree: %w", err)
×
300
        }
×
301

302
        logger.Debug().Msg("Getting authenticated user details")
9✔
303
        email, err := r.ghCli.GetPrimaryEmail(ctx)
9✔
304
        if err != nil {
9✔
305
                return nil, fmt.Errorf("cannot get primary email: %w", err)
×
306
        }
×
307

308
        currentHeadReference, err := repo.Head()
9✔
309
        if err != nil {
9✔
310
                return nil, fmt.Errorf("cannot get current HEAD: %w", err)
×
311
        }
×
312
        currHeadName := currentHeadReference.Name()
9✔
313

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

9✔
318
        logger.Debug().Str("branch", branchBaseName(p.title, p.ruleName)).Msg("Checking out branch")
9✔
319
        err = wt.Checkout(&git.CheckoutOptions{
9✔
320
                Branch: plumbing.NewBranchReferenceName(branchBaseName(p.title, p.ruleName)),
9✔
321
                Create: true,
9✔
322
        })
9✔
323
        if err != nil {
9✔
324
                return nil, fmt.Errorf("cannot checkout branch: %w", err)
×
325
        }
×
326

327
        logger.Debug().Msg("Creating file entries")
9✔
328
        changeEntries, err := p.modifier.modifyFs()
9✔
329
        if err != nil {
9✔
330
                return nil, fmt.Errorf("cannot modifyFs: %w", err)
×
331
        }
×
332

333
        logger.Debug().Msg("Staging changes")
9✔
334
        for _, entry := range changeEntries {
23✔
335
                if _, err := wt.Add(entry.Path); err != nil {
14✔
336
                        return nil, fmt.Errorf("cannot add file %s: %w", entry.Path, err)
×
337
                }
×
338
        }
339

340
        logger.Debug().Msg("Committing changes")
9✔
341
        _, err = wt.Commit(p.title, &git.CommitOptions{
9✔
342
                Author: &object.Signature{
9✔
343
                        Name:  userNameForCommit(ctx, r.ghCli),
9✔
344
                        Email: email,
9✔
345
                        When:  time.Now(),
9✔
346
                },
9✔
347
        })
9✔
348
        if err != nil {
9✔
349
                return nil, fmt.Errorf("cannot commit: %w", err)
×
350
        }
×
351

352
        refspec := refFromBranch(branchBaseName(p.title, p.ruleName))
9✔
353

9✔
354
        l := logger.With().Str("branchBaseName", branchBaseName(p.title, p.ruleName)).Logger()
9✔
355

9✔
356
        // Check if a PR already exists for this branch
9✔
357
        prNumber := getPRNumberFromBranch(ctx, r.ghCli, p.repo, branchBaseName(p.title, p.ruleName))
9✔
358

9✔
359
        // If no PR exists, push the branch and create a PR
9✔
360
        if prNumber == 0 {
17✔
361
                err = pushBranch(ctx, repo, refspec, r.ghCli)
8✔
362
                if err != nil {
8✔
363
                        return nil, fmt.Errorf("cannot push branch: %w", err)
×
364
                }
×
365

366
                pr, err := r.ghCli.CreatePullRequest(
8✔
367
                        ctx, p.repo.GetOwner(), p.repo.GetName(),
8✔
368
                        p.title, p.body,
8✔
369
                        refspec,
8✔
370
                        currHeadName.Short(),
8✔
371
                )
8✔
372
                if err != nil {
9✔
373
                        return nil, fmt.Errorf("cannot create pull request: %w, %w", err, enginerr.ErrActionFailed)
1✔
374
                }
1✔
375
                // Return the new PR number
376
                prNumber = pr.GetNumber()
7✔
377
                l = l.With().Str("pr_origin", "newly_created").Logger()
7✔
378
        } else {
1✔
379
                l = l.With().Str("pr_origin", "already_existed").Logger()
1✔
380
        }
1✔
381

382
        newMeta, err := json.Marshal(pullRequestMetadata{Number: prNumber})
8✔
383
        if err != nil {
8✔
384
                return nil, fmt.Errorf("error marshalling pull request remediation metadata json: %w", err)
×
385
        }
×
386
        // Success - return the new metadata for storing the pull request number
387
        l.Info().Int("pr_number", prNumber).Msg("pull request remediation completed")
8✔
388
        return newMeta, enginerr.ErrActionPending
8✔
389
}
390

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

414
func (r *Remediator) runOff(
415
        ctx context.Context,
416
        p *paramsPR,
417
) (json.RawMessage, error) {
×
418
        logger := zerolog.Ctx(ctx).With().Str("repo", p.repo.String()).Logger()
×
419

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

425
        pr, err := r.ghCli.ClosePullRequest(ctx, p.repo.GetOwner(), p.repo.GetName(), p.metadata.Number)
×
426
        if err != nil {
×
427
                return nil, fmt.Errorf("error closing pull request %d: %w, %w", p.metadata.Number, err, enginerr.ErrActionFailed)
×
428
        }
×
429
        logger.Info().Int("pr_number", pr.GetNumber()).Msg("pull request closed")
×
430
        return nil, enginerr.ErrActionSkipped
×
431
}
432

433
func (r *Remediator) run(
434
        ctx context.Context,
435
        cmd interfaces.ActionCmd,
436
        p *paramsPR,
437
) (json.RawMessage, error) {
9✔
438
        // Process the command
9✔
439
        switch cmd {
9✔
440
        case interfaces.ActionCmdOn:
9✔
441
                return r.runOn(ctx, p)
9✔
442
        case interfaces.ActionCmdOff:
×
443
                return r.runOff(ctx, p)
×
444
        case interfaces.ActionCmdDoNothing:
×
445
                return r.runDoNothing(ctx, p)
×
446
        }
447
        return nil, enginerr.ErrActionSkipped
×
448
}
449

450
func pushBranch(ctx context.Context, repo *git.Repository, refspec string, gh provifv1.GitHub) error {
8✔
451
        var b bytes.Buffer
8✔
452
        pushOptions := &git.PushOptions{
8✔
453
                RemoteName: guessRemote(repo),
8✔
454
                Force:      true,
8✔
455
                RefSpecs: []config.RefSpec{
8✔
456
                        config.RefSpec(
8✔
457
                                fmt.Sprintf("+%s:%s", refspec, refspec),
8✔
458
                        ),
8✔
459
                },
8✔
460
                Progress: &b,
8✔
461
        }
8✔
462
        err := gh.AddAuthToPushOptions(ctx, pushOptions)
8✔
463
        if err != nil {
8✔
464
                return fmt.Errorf("cannot add auth to push options: %w", err)
×
465
        }
×
466

467
        err = repo.PushContext(ctx, pushOptions)
8✔
468
        if err != nil {
8✔
469
                return fmt.Errorf("cannot push: %w", err)
×
470
        }
×
471
        zerolog.Ctx(ctx).Debug().Msgf("Push output: %s", b.String())
8✔
472
        return nil
8✔
473
}
474

475
func guessRemote(gitRepo *git.Repository) string {
8✔
476
        remotes, err := gitRepo.Remotes()
8✔
477
        if err != nil {
8✔
478
                return ""
×
479
        }
×
480

481
        if len(remotes) == 0 {
8✔
482
                return ""
×
483
        }
×
484

485
        for _, remote := range remotes {
16✔
486
                if remote.Config().Name == "origin" {
16✔
487
                        return remote.Config().Name
8✔
488
                }
8✔
489
        }
490

491
        return remotes[0].Config().Name
×
492
}
493

494
func refFromBranch(branchFrom string) string {
18✔
495
        return fmt.Sprintf("refs/heads/%s", branchFrom)
18✔
496
}
18✔
497

498
func branchBaseName(prTitle, ruleName string) string {
53✔
499
        baseName := dflBranchBaseName
53✔
500
        normalizedPrTitle := strings.ReplaceAll(strings.ToLower(prTitle), " ", "_")
53✔
501
        if ruleName == "" {
101✔
502
                return fmt.Sprintf("%s_%s", baseName, normalizedPrTitle)
48✔
503
        }
48✔
504
        normalizedRuleName := strings.ReplaceAll(strings.ToLower(ruleName), " ", "_")
5✔
505
        return fmt.Sprintf("%s_%s_%s", baseName, normalizedRuleName, normalizedPrTitle)
5✔
506
}
507

508
func userNameForCommit(ctx context.Context, gh provifv1.GitHub) string {
9✔
509
        var name string
9✔
510

9✔
511
        // we ignore errors here, as we can still create a commit without a name
9✔
512
        // and errors are checked when getting the primary email
9✔
513
        name, _ = gh.GetName(ctx)
9✔
514
        if name == "" {
9✔
515
                name, _ = gh.GetLogin(ctx)
×
516
        }
×
517
        return name
9✔
518
}
519

520
func (r *Remediator) getPrBodyText(ctx context.Context, tmplParams *PrTemplateParams) (string, error) {
9✔
521
        body := new(bytes.Buffer)
9✔
522
        if err := r.bodyTemplate.Execute(ctx, body, tmplParams, BodyMaxLength); err != nil {
9✔
523
                return "", fmt.Errorf("cannot execute body template: %w", err)
×
524
        }
×
525

526
        prFullBodyText, err := createReviewBody(body.String())
9✔
527
        if err != nil {
9✔
528
                return "", fmt.Errorf("cannot create PR full body text: %w", err)
×
529
        }
×
530

531
        return prFullBodyText, nil
9✔
532
}
533

534
func getMethod(prCfg *pb.RuleType_Definition_Remediate_PullRequestRemediation) string {
9✔
535
        if prCfg.Method == "" {
9✔
536
                return minderContentModification
×
537
        }
×
538

539
        return prCfg.Method
9✔
540
}
541

542
func createReviewBody(prText string) (string, error) {
9✔
543
        tmpl, err := template.New(prTemplateName).Option("missingkey=error").Parse(prBodyTmplStr)
9✔
544
        if err != nil {
9✔
545
                return "", err
×
546
        }
×
547

548
        data := struct {
9✔
549
                PrText string
9✔
550
        }{
9✔
551
                PrText: prText,
9✔
552
        }
9✔
553

9✔
554
        // Execute the template
9✔
555
        var buf bytes.Buffer
9✔
556
        if err := tmpl.Execute(&buf, data); err != nil {
9✔
557
                return "", err
×
558
        }
×
559

560
        return buf.String(), nil
9✔
561
}
562

563
func checkoutToOriginallyFetchedBranch(
564
        logger *zerolog.Logger,
565
        wt *git.Worktree,
566
        originallyFetchedBranch plumbing.ReferenceName,
567
) {
9✔
568
        err := wt.Checkout(&git.CheckoutOptions{
9✔
569
                Branch: originallyFetchedBranch,
9✔
570
        })
9✔
571
        if err != nil {
9✔
572
                logger.Err(err).Msg(
×
573
                        "unable to checkout to the previous head, this can corrupt the ingest cache, should not happen",
×
574
                )
×
575
        } else {
9✔
576
                logger.Info().Msg(fmt.Sprintf("checked out back to %s branch", originallyFetchedBranch))
9✔
577
        }
9✔
578
}
579

580
// runDoNothing returns the previous remediation status
581
func (*Remediator) runDoNothing(ctx context.Context, p *paramsPR) (json.RawMessage, error) {
×
582
        logger := zerolog.Ctx(ctx).With().Str("repo", p.repo.String()).Logger()
×
583

×
584
        logger.Debug().Msg("Running do nothing")
×
585

×
586
        // Return the previous remediation status.
×
587
        err := dbadapter.RemediationStatusAsError(p.prevStatus)
×
588
        // If there is a valid remediation metadata, return it too
×
589
        if p.prevStatus != nil {
×
590
                return p.prevStatus.RemMetadata, err
×
591
        }
×
592
        // If there is no remediation metadata, return nil as the metadata and the error
593
        return nil, err
×
594
}
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