This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / appview / pulls / trigger_ci.go
3.1 kB 116 lines
1package pulls 2 3import ( 4 "fmt" 5 "net/http" 6 "strings" 7 8 "tangled.org/core/api/tangled" 9 "tangled.org/core/appview/db" 10 "tangled.org/core/appview/models" 11) 12 13// TriggerCi manually triggers a CI pipeline for a fork-based pull request. 14// authorized against and recorded under the target repo, but checked out 15// from the fork at the latest round's commit. 16func (s *Pulls) TriggerCi(w http.ResponseWriter, r *http.Request) { 17 l := s.logger.With("handler", "TriggerCi") 18 errorId := "pull-error" 19 20 fail := func(msg string, err error) { 21 if err != nil { 22 l.Error(msg, "err", err) 23 } else { 24 l.Error(msg) 25 } 26 s.pages.Notice(w, errorId, msg) 27 } 28 29 f, err := s.repoResolver.Resolve(r) 30 if err != nil { 31 fail("failed to resolve repository", err) 32 return 33 } 34 35 pull, ok := r.Context().Value("pull").(*models.Pull) 36 if !ok { 37 fail("failed to get pull", nil) 38 return 39 } 40 l = l.With("pull_id", pull.PullId) 41 42 if !pull.IsForkBased() { 43 fail("this pull request is not fork-based", nil) 44 return 45 } 46 47 if f.Spindle == "" { 48 fail("this repository has no spindle configured", nil) 49 return 50 } 51 52 latest := pull.LatestSubmission() 53 if latest.SourceRev == "" { 54 fail("cannot trigger ci: this round has no commit to run", nil) 55 return 56 } 57 58 workflowsChanged, changedFiles, err := s.workflowChangeFromSpindle(r, f, pull) 59 if err != nil { 60 // without the fingerprint comparison there is no way to tell fork 61 // workflows apart from reviewed ones, so refuse to run them 62 fail("failed to verify the workflow definitions of this round", err) 63 return 64 } 65 if workflowsChanged && r.URL.Query().Get("confirm") != "1" { 66 fail(fmt.Sprintf("workflow files changed in this round (%s); review before running", strings.Join(changedFiles, ", ")), nil) 67 return 68 } 69 70 forkRepo, err := db.GetRepoByDid(s.db, pull.PullSource.RepoDid.String()) 71 if err != nil { 72 fail("failed to resolve the fork this pull request comes from", err) 73 return 74 } 75 76 spindleClient, err := s.oauth.SpindleServiceClient(r, f.Spindle, tangled.CiTriggerPipelineNSID) 77 if err != nil { 78 fail("failed to authorize with spindle", err) 79 return 80 } 81 82 pullAt := pull.AtUri().String() 83 sourceBranch := pull.PullSource.Branch 84 targetBranch := pull.TargetBranch 85 out, err := tangled.CiTriggerPipeline( 86 r.Context(), 87 spindleClient, 88 &tangled.CiTriggerPipeline_Input{ 89 Repo: f.RepoDid, 90 Trigger: &tangled.CiTriggerPipeline_Input_Trigger{ 91 CiTrigger_PullRequest: &tangled.CiTrigger_PullRequest{ 92 Pull: &pullAt, 93 SourceBranch: &sourceBranch, 94 SourceRepo: &forkRepo.RepoDid, 95 SourceSha: latest.SourceRev, 96 TargetBranch: targetBranch, 97 }, 98 }, 99 }, 100 ) 101 if err != nil { 102 fail("spindle rejected the trigger", err) 103 return 104 } 105 l.Info("triggered ci for fork-based pull", "pipeline", out.Pipeline) 106 107 user := s.oauth.GetMultiAccountUser(r) 108 repoInfo := s.repoResolver.GetRepoInfo(r, user) 109 dest := fmt.Sprintf("/%s/pulls/%d/round/%d", repoInfo.FullName(), pull.PullId, pull.LastRoundNumber()) 110 if r.Header.Get("HX-Request") == "true" { 111 w.Header().Set("HX-Redirect", dest) 112 w.WriteHeader(http.StatusOK) 113 return 114 } 115 http.Redirect(w, r, dest, http.StatusSeeOther) 116}