@@ -2,17 +2,22 @@ package cmd
22
33import (
44 "context"
5+ "errors"
56 "fmt"
7+ "io"
68 "os"
9+ "strings"
710 "time"
811
912 "github.com/go-git/go-git/v5"
1013 "github.com/go-git/go-git/v5/plumbing"
1114 "github.com/google/go-github/v72/github"
12- "github.com/smartcontractkit/chainlink-testing-framework/tools/flakeguard/golang"
13- "github.com/smartcontractkit/chainlink-testing-framework/tools/flakeguard/localdb"
1415 "github.com/spf13/cobra"
1516 "golang.org/x/oauth2"
17+
18+ flake_git "github.com/smartcontractkit/chainlink-testing-framework/tools/flakeguard/git"
19+ "github.com/smartcontractkit/chainlink-testing-framework/tools/flakeguard/golang"
20+ "github.com/smartcontractkit/chainlink-testing-framework/tools/flakeguard/localdb"
1621)
1722
1823var (
@@ -29,55 +34,118 @@ var MakePRCmd = &cobra.Command{
2934func makePR (cmd * cobra.Command , args []string ) error {
3035 repo , err := git .PlainOpen (repoPath )
3136 if err != nil {
32- return err
37+ return fmt . Errorf ( "failed to open repo: %w" , err )
3338 }
3439
3540 db , err := localdb .LoadDBWithPath (localDBPath )
3641 if err != nil {
37- return err
42+ return fmt . Errorf ( "failed to load local db: %w" , err )
3843 }
3944
4045 currentlyFlakyEntries := db .GetAllCurrentlyFlakyEntries ()
4146
42- branchName := fmt .Sprintf ("flakeguard-skip-%s" , time .Now ().Format ("20060102150405" ))
47+ owner , repoName , defaultBranch , err := flake_git .GetOwnerRepoDefaultBranchFromLocalRepo (repoPath )
48+ if err != nil {
49+ return fmt .Errorf ("failed to get repo info: %w" , err )
50+ }
51+
4352 targetRepoWorktree , err := repo .Worktree ()
4453 if err != nil {
45- return err
54+ return fmt . Errorf ( "failed to open repo's worktree: %w" , err )
4655 }
56+
57+ // First checkout default branch and pull latest
58+ err = targetRepoWorktree .Checkout (& git.CheckoutOptions {
59+ Branch : plumbing .NewBranchReferenceName (defaultBranch ),
60+ })
61+ if err != nil {
62+ if errors .Is (err , git .ErrUnstagedChanges ) {
63+ fmt .Println ("Local repo has unstaged changes, please commit or stash them before running this command" )
64+ }
65+ return fmt .Errorf ("failed to checkout default branch %s: %w" , defaultBranch , err )
66+ }
67+
68+ fmt .Print ("Fetching latest changes from default branch, tap your yubikey if it's blinking..." )
69+ err = repo .Fetch (& git.FetchOptions {})
70+ if err != nil && err != git .NoErrAlreadyUpToDate {
71+ return fmt .Errorf ("failed to fetch latest: %w" , err )
72+ }
73+ fmt .Println (" ✅" )
74+
75+ fmt .Print ("Pulling latest changes from default branch, tap your yubikey if it's blinking..." )
76+ err = targetRepoWorktree .Pull (& git.PullOptions {})
77+ if err != nil && err != git .NoErrAlreadyUpToDate {
78+ return fmt .Errorf ("failed to pull latest changes: %w" , err )
79+ }
80+ fmt .Println (" ✅" )
81+
82+ // Create and checkout new branch
83+ branchName := fmt .Sprintf ("flakeguard-skip-%s" , time .Now ().Format ("20060102150405" ))
4784 err = targetRepoWorktree .Checkout (& git.CheckoutOptions {
4885 Branch : plumbing .NewBranchReferenceName (branchName ),
4986 Create : true ,
5087 })
5188 if err != nil {
52- return err
89+ return fmt .Errorf ("failed to checkout new branch: %w" , err )
90+ }
91+
92+ cleanUpBranch := true
93+ defer func () {
94+ if cleanUpBranch {
95+ fmt .Printf ("Cleaning up branch %s..." , branchName )
96+ err = targetRepoWorktree .Checkout (& git.CheckoutOptions {
97+ Branch : plumbing .NewBranchReferenceName (defaultBranch ),
98+ })
99+ if err != nil {
100+ fmt .Printf ("Failed to clean up branch: %v\n " , err )
101+ }
102+ err = repo .Storer .RemoveReference (plumbing .NewBranchReferenceName (branchName ))
103+ if err != nil {
104+ fmt .Printf ("Failed to remove branch: %v\n " , err )
105+ }
106+ fmt .Println (" ✅" )
107+ }
108+ }()
109+
110+ if len (currentlyFlakyEntries ) == 0 {
111+ fmt .Println ("No flaky tests found!" )
112+ return nil
53113 }
54114
55- testsToSkip := []golang.SkipTest {}
115+ jiraTickets := []string {}
116+ testsToSkip := []* golang.SkipTest {}
56117 for _ , entry := range currentlyFlakyEntries {
57- testsToSkip = append (testsToSkip , golang.SkipTest {
58- Package : entry .TestPackage ,
59- Name : entry .TestName ,
118+ testsToSkip = append (testsToSkip , & golang.SkipTest {
119+ Package : entry .TestPackage ,
120+ Name : entry .TestName ,
121+ JiraTicket : entry .JiraTicket ,
60122 })
123+ jiraTickets = append (jiraTickets , entry .JiraTicket )
61124 }
62125
63126 err = golang .SkipTests (repoPath , testsToSkip )
64127 if err != nil {
65- return err
128+ return fmt . Errorf ( "failed to modify code to skip tests: %w" , err )
66129 }
67130
68131 _ , err = targetRepoWorktree .Add ("." )
69132 if err != nil {
70- return err
133+ return fmt . Errorf ( "failed to add changes: %w" , err )
71134 }
72- _ , err = targetRepoWorktree .Commit ("Skips flaky tests" , & git.CommitOptions {})
135+
136+ fmt .Print ("Committing changes, tap your yubikey if it's blinking..." )
137+ commitHash , err := targetRepoWorktree .Commit (fmt .Sprintf ("Skips flaky %d tests" , len (testsToSkip )), & git.CommitOptions {})
73138 if err != nil {
74- return err
139+ return fmt . Errorf ( "failed to commit changes: %w" , err )
75140 }
141+ fmt .Println (" ✅" )
76142
143+ fmt .Print ("Pushing changes to remote, tap your yubikey if it's blinking..." )
77144 err = repo .Push (& git.PushOptions {})
78145 if err != nil {
79- return err
146+ return fmt . Errorf ( "failed to push changes: %w" , err )
80147 }
148+ fmt .Println (" ✅" )
81149
82150 ctx := context .Background ()
83151 ts := oauth2 .StaticTokenSource (
@@ -86,24 +154,71 @@ func makePR(cmd *cobra.Command, args []string) error {
86154 tc := oauth2 .NewClient (ctx , ts )
87155 client := github .NewClient (tc )
88156
89- owner := "your-org"
90- repoName := "your-repo"
157+ var (
158+ skippedTestsPRBody strings.Builder
159+ alreadySkippedTestsPRBody strings.Builder
160+ )
161+
162+ for _ , test := range testsToSkip {
163+ if test .Skipped {
164+ skippedTestsPRBody .WriteString (fmt .Sprintf ("- Package: `%s`\n " , test .Package ))
165+ skippedTestsPRBody .WriteString (fmt .Sprintf (" Test: `%s`\n " , test .Name ))
166+ skippedTestsPRBody .WriteString (fmt .Sprintf (" Ticket: [%s](https://%s/browse/%s)\n " , test .JiraTicket , os .Getenv ("JIRA_DOMAIN" ), test .JiraTicket ))
167+ skippedTestsPRBody .WriteString (fmt .Sprintf (" [View skip in PR](https://github.com/%s/%s/pull/%s/files#diff-%sL%d)\n \n " , owner , repoName , branchName , commitHash , test .Line ))
168+ } else {
169+ alreadySkippedTestsPRBody .WriteString (fmt .Sprintf ("- Package: `%s`\n " , test .Package ))
170+ alreadySkippedTestsPRBody .WriteString (fmt .Sprintf (" Test: `%s`\n " , test .Name ))
171+ alreadySkippedTestsPRBody .WriteString (fmt .Sprintf (" Ticket: [%s](https://%s/browse/%s)\n " , test .JiraTicket , os .Getenv ("JIRA_DOMAIN" ), test .JiraTicket ))
172+ }
173+ }
174+
91175 pr := & github.NewPullRequest {
92- Title : github .Ptr (" Skip flaky tests" ),
176+ Title : github .Ptr (fmt . Sprintf ( "[%s] Flakeguard: Skip flaky tests", strings . Join ( jiraTickets , "] [" )) ),
93177 Head : github .Ptr (branchName ),
94- Base : github .Ptr ("main" ),
95- Body : github .Ptr ("This PR skips flaky tests." ),
178+ Base : github .Ptr (defaultBranch ),
179+ Body : github .Ptr (fmt . Sprintf ( "## Tests Skipped \n \n %s \n \n ## Tests Already Skipped \n \n %s" , skippedTestsPRBody . String (), alreadySkippedTestsPRBody . String ()) ),
96180 MaintainerCanModify : github .Ptr (true ),
97181 }
98- _ , _ , err = client .PullRequests .Create (ctx , owner , repoName , pr )
182+
183+ fmt .Println ("PR Preview:" )
184+ fmt .Println ("================================================" )
185+ fmt .Println (pr .Title )
186+ fmt .Println ("--------------------------------" )
187+ fmt .Printf ("Merging '%s' into '%s'\n " , branchName , defaultBranch )
188+ fmt .Println (pr .Body )
189+ fmt .Println ("================================================" )
190+
191+ fmt .Printf ("To preview the code changes in the GitHub UI, visit: https://github.com/%s/%s/compare/%s...%s\n " , owner , repoName , defaultBranch , branchName )
192+ fmt .Print ("Would you like to create the PR automatically from the CLI? (y/N): " )
193+
194+ var confirm string
195+ _ , err = fmt .Scanln (& confirm )
99196 if err != nil {
100197 return err
101198 }
102199
103- fmt .Println ("PR created!" )
200+ if strings .ToLower (confirm ) != "y" {
201+ fmt .Println ("Exiting. Please use the GitHub UI to create the PR." )
202+ return nil
203+ }
204+
205+ createdPR , resp , err := client .PullRequests .Create (ctx , owner , repoName , pr )
206+ if err != nil {
207+ return err
208+ }
209+ if resp .StatusCode != 201 {
210+ body , err := io .ReadAll (resp .Body )
211+ if err != nil {
212+ return fmt .Errorf ("failed to read github response body while trying to create PR: %s\n %w" , resp .Status , err )
213+ }
214+ return fmt .Errorf ("failed to create PR, got bad status: %s\n %s" , resp .Status , string (body ))
215+ }
216+
217+ cleanUpBranch = false
218+ fmt .Printf ("PR created! https://github.com/%s/%s/pull/%d\n " , owner , repoName , createdPR .GetNumber ())
104219 return nil
105220}
106221
107222func init () {
108- MakePRCmd .Flags ().StringVarP (& repoPath , "repo " , "r" , "." , "Path to the repository to make the PR in " )
223+ MakePRCmd .Flags ().StringVarP (& repoPath , "repoPath " , "r" , "." , "Local path to the repository to make the PR for " )
109224}
0 commit comments