diff options
| author | Aleksandr Nogikh <nogikh@google.com> | 2023-07-17 18:08:56 +0200 |
|---|---|---|
| committer | Aleksandr Nogikh <nogikh@google.com> | 2023-07-25 08:23:29 +0000 |
| commit | e06c669f49a06146914b04a1fbbdd21a0bf1d7b1 (patch) | |
| tree | dc4a1b32a67606741f49d7cbf6a07055b919b86c | |
| parent | 67db6ccb7357714a0299fe9f3d192a26bd32ac4e (diff) | |
all: support cross-tree fix bisection
If tree origin assessment code has identified that the bug is not
reproducible in a tree from which we merge/cherry-pick commits, use fix
bisection to identify that commit. This can e.g. be used to find fixing
commits that were not backported from Linux kernel mainline into LTS
branches.
In case of bisection errors, re-do such jobs every 30 days.
Remember in the Bug structure whether there's a fix candidate and return
the details in the full bug info API query.
| -rw-r--r-- | dashboard/app/config.go | 2 | ||||
| -rw-r--r-- | dashboard/app/entities.go | 14 | ||||
| -rw-r--r-- | dashboard/app/jobs.go | 82 | ||||
| -rw-r--r-- | dashboard/app/reporting.go | 19 | ||||
| -rw-r--r-- | dashboard/app/tree.go | 153 | ||||
| -rw-r--r-- | dashboard/app/tree_test.go | 116 | ||||
| -rw-r--r-- | dashboard/dashapi/dashapi.go | 28 | ||||
| -rw-r--r-- | syz-ci/jobs.go | 31 |
8 files changed, 404 insertions, 41 deletions
diff --git a/dashboard/app/config.go b/dashboard/app/config.go index 1ee059b80..88230f9c6 100644 --- a/dashboard/app/config.go +++ b/dashboard/app/config.go @@ -263,6 +263,8 @@ type KernelRepoLink struct { Alias string // Whether commits from the other repository merged or cherry-picked. Merge bool + // Whether syzbot should try to fix bisect the bug in the Alias tree. + BisectFixes bool } type CCConfig struct { diff --git a/dashboard/app/entities.go b/dashboard/app/entities.go index 812d31e59..b64e66c03 100644 --- a/dashboard/app/entities.go +++ b/dashboard/app/entities.go @@ -123,6 +123,8 @@ type Bug struct { Labels []BugLabel DiscussionInfo []BugDiscussionInfo TreeTests BugTreeTestInfo + // FixCandidateJob holds the key of the latest successful cross-tree fix bisection job. + FixCandidateJob string } type BugTreeTestInfo struct { @@ -559,6 +561,10 @@ type Job struct { MergeBaseRepo string MergeBaseBranch string + // By default, bisection starts from the revision of the associated crash. + // The BisectFrom field can override this. + BisectFrom string + // Result of execution: CrashTitle string // if empty, we did not hit crash during testing CrashLog int64 // reference to CrashLog text entity @@ -573,6 +579,10 @@ type Job struct { InvalidatedBy string // user who marked this bug as invalid, empty by default } +func (job *Job) IsBisection() bool { + return job.Type == JobBisectCause || job.Type == JobBisectFix +} + func (job *Job) IsFinished() bool { return !job.Finished.IsZero() } @@ -610,6 +620,10 @@ func (job *Job) isUnreliableBisect() bool { job.Flags&dashapi.BisectResultIgnore != 0 } +func (job *Job) IsCrossTree() bool { + return job.MergeBaseRepo != "" && job.IsBisection() +} + // Text holds text blobs (crash logs, reports, reproducers, etc). type Text struct { Namespace string diff --git a/dashboard/app/jobs.go b/dashboard/app/jobs.go index 2567cb30e..cf5698e99 100644 --- a/dashboard/app/jobs.go +++ b/dashboard/app/jobs.go @@ -435,6 +435,7 @@ func jobFromBugSample(c context.Context, managers map[string]dashapi.ManagerJobs map[string]dashapi.ManagerJobs) (*Job, *db.Key, error){ createPatchRetestingJobs, createTreeTestJobs, + createTreeBisectionJobs, } r.Shuffle(len(funcs), func(i, j int) { funcs[i], funcs[j] = funcs[j], funcs[i] }) for _, f := range funcs { @@ -446,6 +447,35 @@ func jobFromBugSample(c context.Context, managers map[string]dashapi.ManagerJobs return nil, nil, nil } +func createTreeBisectionJobs(c context.Context, bugs []*Bug, bugKeys []*db.Key, + managers map[string]dashapi.ManagerJobs) (*Job, *db.Key, error) { + log.Infof(c, "createTreeBisectionJobs is called for %d bugs", len(bugs)) + const maxProcess = 3 + processed := 0 + for _, bug := range bugs { + if bug.FixCandidateJob != "" { + continue + } + if processed >= maxProcess { + break + } + any := false + for _, mgr := range bug.HappenedOn { + newMgr, _ := activeManager(mgr, bug.Namespace) + any = any || managers[newMgr].BisectFix + } + if !any { + continue + } + processed++ + job, key, err := crossTreeBisection(c, bug, managers) + if job != nil || err != nil { + return job, key, err + } + } + return nil, nil, nil +} + func createTreeTestJobs(c context.Context, bugs []*Bug, bugKeys []*db.Key, managers map[string]dashapi.ManagerJobs) (*Job, *db.Key, error) { takeBugs := 5 @@ -814,20 +844,23 @@ func createJobResp(c context.Context, job *Job, jobKey *db.Key) (*dashapi.JobPol return nil, true, nil } resp := &dashapi.JobPollResp{ - ID: jobID, - Manager: job.Manager, - KernelRepo: job.KernelRepo, - KernelBranch: job.KernelBranch, - MergeBaseRepo: job.MergeBaseRepo, - MergeBaseBranch: job.MergeBaseBranch, - KernelCommit: build.KernelCommit, - KernelCommitTitle: build.KernelCommitTitle, - KernelConfig: kernelConfig, - SyzkallerCommit: build.SyzkallerCommit, - Patch: patch, - ReproOpts: crash.ReproOpts, - ReproSyz: reproSyz, - ReproC: reproC, + ID: jobID, + Manager: job.Manager, + KernelRepo: job.KernelRepo, + KernelBranch: job.KernelBranch, + MergeBaseRepo: job.MergeBaseRepo, + MergeBaseBranch: job.MergeBaseBranch, + KernelCommit: job.BisectFrom, + KernelConfig: kernelConfig, + SyzkallerCommit: build.SyzkallerCommit, + Patch: patch, + ReproOpts: crash.ReproOpts, + ReproSyz: reproSyz, + ReproC: reproC, + } + if resp.KernelCommit == "" { + resp.KernelCommit = build.KernelCommit + resp.KernelCommitTitle = build.KernelCommitTitle } switch job.Type { case JobTestPatch: @@ -1057,12 +1090,20 @@ func doneJob(c context.Context, req *dashapi.JobDoneReq) error { if err != nil { return err } + return postJob(c, jobKey, job) +} + +func postJob(c context.Context, jobKey *db.Key, job *Job) error { if job.TreeOrigin { - err = treeOriginJobDone(c, jobKey, job) + err := treeOriginJobDone(c, jobKey, job) if err != nil { - return fmt.Errorf("job %v: failed to execute tree origin handlers: %w", jobID, err) + return fmt.Errorf("job %v: failed to execute tree origin handlers: %w", jobKey, err) } } + err := doneCrossTreeBisection(c, jobKey, job) + if err != nil { + return fmt.Errorf("job %s: cross tree bisection handlers failed: %w", jobKey, err) + } return nil } @@ -1155,6 +1196,11 @@ func pollCompletedJobs(c context.Context, typ string) ([]*dashapi.BugReport, err if job.Type == JobBisectFix && len(job.Commits) != 1 { continue } + // Don't report cross-tree bisection results for now as we need to first understand + // how reliable their results are. + if job.IsCrossTree() { + continue + } // If the bug is already known to be fixed, invalid or duplicate, do not report the bisection results. if job.Type == JobBisectCause || job.Type == JobBisectFix { bug := new(Bug) @@ -1619,6 +1665,10 @@ func (b *bugJobs) bestBisection() *bugJob { if j.job.InvalidatedBy != "" { continue } + if j.job.MergeBaseRepo != "" { + // It was a cross-tree bisection. + continue + } return j } return nil diff --git a/dashboard/app/reporting.go b/dashboard/app/reporting.go index 599eb1a79..3917adb56 100644 --- a/dashboard/app/reporting.go +++ b/dashboard/app/reporting.go @@ -1358,6 +1358,13 @@ func loadFullBugInfo(c context.Context, bug *Bug, bugKey *db.Key, return nil, err } } + // Query the fix candidate. + if bug.FixCandidateJob != "" { + ret.FixCandidate, err = prepareFixCandidateReport(c, bug, reporting) + if err != nil { + return nil, err + } + } // Query similar bugs. similar, err := loadSimilarBugs(c, bug) if err != nil { @@ -1402,6 +1409,18 @@ func loadFullBugInfo(c context.Context, bug *Bug, bugKey *db.Key, return ret, nil } +func prepareFixCandidateReport(c context.Context, bug *Bug, reporting *Reporting) (*dashapi.BugReport, error) { + job, jobKey, err := fetchJob(c, bug.FixCandidateJob) + if err != nil { + return nil, err + } + ret, err := createBugReportForJob(c, job, jobKey, reporting.Config) + if err != nil { + return nil, fmt.Errorf("failed to create the job bug report: %w", err) + } + return ret, nil +} + func prepareBisectionReport(c context.Context, bug *Bug, jobType JobType, reporting *Reporting) (*dashapi.BugReport, error) { bisectionJob, err := queryBestBisection(c, bug, jobType) diff --git a/dashboard/app/tree.go b/dashboard/app/tree.go index 2331ab9d2..07c289ea7 100644 --- a/dashboard/app/tree.go +++ b/dashboard/app/tree.go @@ -713,6 +713,128 @@ func treeTestJobs(c context.Context, bug *Bug) ([]*dashapi.JobInfo, error) { return ret, nil } +// Create a cross-tree bisection job (if needed). +func crossTreeBisection(c context.Context, bug *Bug, + managers map[string]dashapi.ManagerJobs) (*Job, *db.Key, error) { + repoGraph, err := makeRepoGraph(getKernelRepos(c, bug.Namespace)) + if err != nil { + return nil, nil, err + } + bugJobs := &lazyJobList{ + c: c, + bug: bug, + jobType: JobBisectFix, + } + var job *Job + var jobKey *db.Key + err = repoGraph.forEachEdge(func(from, to *repoNode, info KernelRepoLink) error { + if jobKey != nil { + return nil + } + if !info.BisectFixes { + return nil + } + log.Infof(c, "%s: considering cross-tree bisection %s/%s", + bug.displayTitle(), from.repo.Alias, to.repo.Alias) + _, crashJob := bug.findResult(c, to.repo, wantNewAny{}, runOnHEAD{}) + if crashJob.CrashTitle == "" { + // The bug is already fixed on the target tree. + return nil + } + crashBuild, err := loadBuild(c, bug.Namespace, crashJob.BuildID) + if err != nil { + return err + } + manager, _ := activeManager(crashJob.Manager, crashJob.Namespace) + if !managers[manager].BisectFix { + return nil + } + _, successJob := bug.findResult(c, from.repo, wantNewAny{}, runOnHEAD{}) + if successJob.CrashTitle != "" { + // The kernel tree is still crashed by the repro. + return nil + } + newJob := &Job{ + Type: JobBisectFix, + Created: timeNow(c), + Namespace: bug.Namespace, + Manager: crashJob.Manager, + BisectFrom: crashBuild.KernelCommit, + KernelRepo: from.repo.URL, + KernelBranch: from.repo.Branch, + MergeBaseRepo: to.repo.URL, + MergeBaseBranch: to.repo.Branch, + BugTitle: bug.displayTitle(), + CrashID: crashJob.CrashID, + } + // It's expected that crossTreeBisection is not concurrently called with the same + // manager list. + prevJob, err := bugJobs.lastMatch(newJob) + if err != nil { + return err + } + const repeatPeriod = time.Hour * 24 * 30 + if prevJob != nil && (prevJob.Error == 0 || + prevJob.Finished.After(timeNow(c).Add(-repeatPeriod))) { + // The job is already pending or failed recently. Skip. + return nil + } + job = newJob + jobKey, err = saveJob(c, newJob, bug.key(c)) + return err + }) + return job, jobKey, err +} + +type lazyJobList struct { + c context.Context + bug *Bug + jobType JobType + jobs *bugJobs +} + +func (list *lazyJobList) lastMatch(job *Job) (*Job, error) { + if list.jobs == nil { + var err error + list.jobs, err = queryBugJobs(list.c, list.bug, list.jobType) + if err != nil { + return nil, err + } + } + var best *Job + for _, item := range list.jobs.all() { + otherJob := item.job + same := otherJob.Manager == job.Manager && + otherJob.KernelRepo == job.KernelRepo && + otherJob.KernelBranch == job.KernelBranch && + otherJob.CrashID == job.CrashID && + otherJob.MergeBaseRepo == job.MergeBaseRepo && + otherJob.MergeBaseBranch == job.MergeBaseBranch + if !same { + continue + } + if best == nil || best.Created.Before(otherJob.Created) { + best = otherJob + } + } + return best, nil +} + +func doneCrossTreeBisection(c context.Context, jobKey *db.Key, job *Job) error { + if job.Type != JobBisectFix || job.MergeBaseRepo == "" { + // Not a cross tree bisection. + return nil + } + if job.Error != 0 || job.isUnreliableBisect() || len(job.Commits) != 1 { + // The result is not interesting. + return nil + } + return updateSingleBug(c, jobKey.Parent(), func(bug *Bug) error { + bug.FixCandidateJob = jobKey.Encode() + return nil + }) +} + type repoNode struct { repo KernelRepo edges []repoEdge @@ -720,7 +842,7 @@ type repoNode struct { type repoEdge struct { in bool - merge bool + info KernelRepoLink other *repoNode } @@ -743,8 +865,8 @@ func makeRepoGraph(repos []KernelRepo) (*repoGraph, error) { if g.nodes[link.Alias] == nil { return nil, fmt.Errorf("no repo with alias %q", link.Alias) } - g.nodes[repo.Alias].addEdge(true, link.Merge, g.nodes[link.Alias]) - g.nodes[link.Alias].addEdge(false, link.Merge, g.nodes[repo.Alias]) + g.nodes[repo.Alias].addEdge(true, link, g.nodes[link.Alias]) + g.nodes[link.Alias].addEdge(false, link, g.nodes[repo.Alias]) } } for alias, node := range g.nodes { @@ -774,6 +896,21 @@ func (g *repoGraph) nodeByAlias(alias string) *repoNode { return nil } +func (g *repoGraph) forEachEdge(cb func(from, to *repoNode, info KernelRepoLink) error) error { + for _, node := range g.nodes { + for _, e := range node.edges { + if !e.in { + continue + } + err := cb(e.other, node, e.info) + if err != nil { + return err + } + } + } + return nil +} + // reachable returns a map *repoNode -> bool (whether commits are merged). func (n *repoNode) reachable(in bool) map[*repoNode]bool { ret := map[*repoNode]bool{} @@ -787,14 +924,14 @@ func (n *repoNode) reachableMerged(in, onlyMerge bool, ret map[*repoNode]bool) { var dfs func(*repoNode, bool) dfs = func(node *repoNode, merge bool) { for _, edge := range node.edges { - if edge.in != in || onlyMerge && !edge.merge { + if edge.in != in || onlyMerge && !edge.info.Merge { continue } if _, ok := ret[edge.other]; ok { continue } - ret[edge.other] = merge && edge.merge - dfs(edge.other, merge && edge.merge) + ret[edge.other] = merge && edge.info.Merge + dfs(edge.other, merge && edge.info.Merge) } } dfs(n, true) @@ -808,10 +945,10 @@ func (n *repoNode) allReachable() map[*repoNode]bool { return ret } -func (n *repoNode) addEdge(in, merge bool, other *repoNode) { +func (n *repoNode) addEdge(in bool, info KernelRepoLink, other *repoNode) { n.edges = append(n.edges, repoEdge{ in: in, - merge: merge, + info: info, other: other, }) } diff --git a/dashboard/app/tree_test.go b/dashboard/app/tree_test.go index 79f2dfe08..8fef51865 100644 --- a/dashboard/app/tree_test.go +++ b/dashboard/app/tree_test.go @@ -153,6 +153,117 @@ func TestTreeOriginLts(t *testing.T) { ctx.ctx.expectNoEmail() } +func TestTreeOriginLtsBisection(t *testing.T) { + c := NewCtx(t) + defer c.Close() + + ctx := setUpTreeTest(c, downstreamUpstreamRepos) + ctx.uploadBug(`https://downstream.repo/repo`, `master`, dashapi.ReproLevelC) + ctx.entries = []treeTestEntry{ + { + alias: `downstream`, + results: []treeTestEntryPeriod{{fromDay: 0, result: treeTestCrash}}, + }, + { + alias: `lts`, + mergeAlias: `downstream`, + results: []treeTestEntryPeriod{ + { + fromDay: 0, + result: treeTestCrash, + commit: "badc0ffee", + }, + }, + }, + { + alias: `upstream`, + results: []treeTestEntryPeriod{{fromDay: 0, result: treeTestOK}}, + }, + } + ctx.jobTestDays = []int{10} + ctx.moveToDay(10) + ctx.ensureLabels(`origin:lts`) + ctx.reportToEmail() + ctx.ctx.advanceTime(time.Hour) + + // Expect a cross tree bisection request. + job := ctx.client.pollSpecificJobs(ctx.manager, dashapi.ManagerJobs{BisectFix: true}) + assert.Equal(t, dashapi.JobBisectFix, job.Type) + assert.Equal(t, "https://upstream.repo/repo", job.KernelRepo) + assert.Equal(t, "upstream-master", job.KernelBranch) + assert.Equal(t, "https://lts.repo/repo", job.MergeBaseRepo) + assert.Equal(t, "lts-master", job.MergeBaseBranch) + assert.Equal(t, "badc0ffee", job.KernelCommit) + ctx.ctx.advanceTime(time.Hour) + + // Make sure we don't create the same job twice. + job2 := ctx.client.pollSpecificJobs(ctx.manager, dashapi.ManagerJobs{BisectFix: true}) + assert.Equal(t, "", job2.ID) + ctx.ctx.advanceTime(time.Hour) + + // Let the bisection fail. + done := &dashapi.JobDoneReq{ + ID: job.ID, + Log: []byte("bisect log"), + Error: []byte("bisect error"), + } + c.expectOK(ctx.client.JobDone(done)) + ctx.ctx.advanceTime(time.Hour) + + // Ensure there are no new bisection requests. + job = ctx.client.pollSpecificJobs(ctx.manager, dashapi.ManagerJobs{BisectFix: true}) + assert.Equal(t, job.ID, "") + + // Wait for the cooldown and request the job once more. + ctx.ctx.advanceTime(15 * 24 * time.Hour) + ctx.uploadBug(`https://downstream.repo/repo`, `master`, dashapi.ReproLevelC) + ctx.ctx.advanceTime(15 * 24 * time.Hour) + job = ctx.client.pollSpecificJobs(ctx.manager, dashapi.ManagerJobs{BisectFix: true}) + assert.Equal(t, job.KernelRepo, "https://upstream.repo/repo") + assert.Equal(t, job.KernelCommit, "badc0ffee") + + // This time pretend we have found the commit. + build := testBuild(2) + build.KernelRepo = job.KernelRepo + build.KernelBranch = job.KernelBranch + build.KernelCommit = "deadf00d" + done = &dashapi.JobDoneReq{ + ID: job.ID, + Build: *build, + Log: []byte("bisect log 2"), + CrashTitle: "bisect crash title", + CrashLog: []byte("bisect crash log"), + CrashReport: []byte("bisect crash report"), + Commits: []dashapi.Commit{ + { + Hash: "deadf00d", + Title: "kernel: fix a bug", + }, + }, + } + done.Build.ID = job.ID + ctx.ctx.advanceTime(time.Hour) + c.expectOK(ctx.client.JobDone(done)) + + // Ensure the job is no longer created. + ctx.ctx.advanceTime(time.Hour) + job = ctx.client.pollSpecificJobs(ctx.manager, dashapi.ManagerJobs{BisectFix: true}) + assert.Equal(t, job.ID, "") + + // No emails are to be sent (for now). + ctx.ctx.expectNoEmail() + + info := ctx.fullBugInfo() + assert.NotNil(t, info.FixCandidate) + fix := info.FixCandidate + assert.Equal(t, "upstream", fix.KernelRepoAlias) + assert.NotNil(t, fix.BisectFix) + assert.NotNil(t, fix.BisectFix.Commit) + commit := fix.BisectFix.Commit + assert.Equal(t, "deadf00d", commit.Hash) + assert.Equal(t, "kernel: fix a bug", commit.Title) +} + func TestTreeOriginErrors(t *testing.T) { c := NewCtx(t) defer c.Close() @@ -220,8 +331,9 @@ var downstreamUpstreamRepos = []KernelRepo{ LabelIntroduced: `lts`, CommitInflow: []KernelRepoLink{ { - Alias: `upstream`, - Merge: false, + Alias: `upstream`, + Merge: false, + BisectFixes: true, }, }, }, diff --git a/dashboard/dashapi/dashapi.go b/dashboard/dashapi/dashapi.go index af4b89e32..28a805edd 100644 --- a/dashboard/dashapi/dashapi.go +++ b/dashboard/dashapi/dashapi.go @@ -176,13 +176,16 @@ func (m ManagerJobs) Any() bool { } type JobPollResp struct { - ID string - Type JobType - Manager string - KernelRepo string - KernelBranch string - MergeBaseRepo string - MergeBaseBranch string + ID string + Type JobType + Manager string + KernelRepo string + // KernelBranch is used for patch testing and serves as the current HEAD + // for bisections. + KernelBranch string + MergeBaseRepo string + MergeBaseBranch string + // Bisection starts from KernelCommit. KernelCommit string KernelCommitTitle string KernelConfig []byte @@ -785,11 +788,12 @@ type LoadFullBugReq struct { } type FullBugInfo struct { - SimilarBugs []*SimilarBugInfo - BisectCause *BugReport - BisectFix *BugReport - Crashes []*BugReport - TreeJobs []*JobInfo + SimilarBugs []*SimilarBugInfo + BisectCause *BugReport + BisectFix *BugReport + Crashes []*BugReport + TreeJobs []*JobInfo + FixCandidate *BugReport } type SimilarBugInfo struct { diff --git a/syz-ci/jobs.go b/syz-ci/jobs.go index a0f838e7b..cc7c12908 100644 --- a/syz-ci/jobs.go +++ b/syz-ci/jobs.go @@ -397,7 +397,10 @@ func (jp *JobProcessor) process(job *Job) *dashapi.JobDoneReq { jp.Errorf("%s", err) return job.resp } - + if req.KernelRepo == "" { + req.KernelRepo = mgr.mgrcfg.Repo + req.KernelRepo = mgr.mgrcfg.Branch + } required := []struct { name string ok bool @@ -455,6 +458,10 @@ func (jp *JobProcessor) bisect(job *Job, mgrcfg *mgrconfig.Config) error { return fmt.Errorf("failed to read baseline config: %w", err) } } + err := jp.prepareBisectionRepo(mgrcfg, req) + if err != nil { + return err + } trace := new(bytes.Buffer) cfg := &bisect.Config{ Trace: &debugtracer.GenericTracer{ @@ -485,8 +492,8 @@ func (jp *JobProcessor) bisect(job *Job, mgrcfg *mgrconfig.Config) error { Linker: mgr.mgrcfg.Linker, Ccache: jp.cfg.Ccache, Kernel: bisect.KernelConfig{ - Repo: mgr.mgrcfg.Repo, - Branch: mgr.mgrcfg.Branch, + Repo: req.KernelRepo, + Branch: req.KernelBranch, Commit: req.KernelCommit, CommitTitle: req.KernelCommitTitle, Cmdline: mgr.mgrcfg.KernelCmdline, @@ -505,6 +512,7 @@ func (jp *JobProcessor) bisect(job *Job, mgrcfg *mgrconfig.Config) error { Syz: req.ReproSyz, C: req.ReproC, }, + CrossTree: req.MergeBaseRepo != "", Manager: mgrcfg, BuildSemaphore: buildSem, TestSemaphore: testSem, @@ -656,6 +664,23 @@ func (jp *JobProcessor) testPatch(job *Job, mgrcfg *mgrconfig.Config) error { return nil } +func (jp *JobProcessor) prepareBisectionRepo(mgrcfg *mgrconfig.Config, req *dashapi.JobPollResp) error { + if req.MergeBaseRepo == "" { + // No need to. + return nil + } + repo, err := vcs.NewRepo(mgrcfg.TargetOS, mgrcfg.Type, mgrcfg.KernelSrc) + if err != nil { + return fmt.Errorf("failed to create kernel repo: %w", err) + } + _, err = checkoutKernelOrCommit(repo, req.MergeBaseRepo, req.MergeBaseBranch) + if err != nil { + return fmt.Errorf("failed to checkout the merge base repo %v on %v: %w", + req.MergeBaseRepo, req.MergeBaseBranch, err) + } + return nil +} + func (jp *JobProcessor) checkoutJobCommit(job *Job, repo vcs.Repo) (*vcs.Commit, error) { req, resp := job.req, job.resp var kernelCommit *vcs.Commit |
