Skip to content

Commit ee8bccc

Browse files
committed
Recognizes closed jira tickets
1 parent e62f3f0 commit ee8bccc

File tree

10 files changed

+647
-487
lines changed

10 files changed

+647
-487
lines changed

tools/flakeguard/README.md

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
1-
Flakeguard
2-
==========
1+
# Flakeguard
32

43
**Flakeguard** is a tool designed to help identify flaky tests within a Go project. Flaky tests are tests that intermittently fail without changes to the code, often due to race conditions or other non-deterministic behavior. Flakeguard assists by analyzing the impact of code changes on test packages and by running tests multiple times to determine stability.
54

65
In addition to detecting flaky tests, Flakeguard can also integrate with Jira to track known flaky tests. It maintains a local database of tests and their associated Jira tickets, allowing you to create and manage these tickets directly from the command line.
76

8-
Features
9-
--------
7+
## Features
108

119
* **Identify Impacted Tests:** Detects test packages that may be affected by changes in your Go project files.
1210
* **Run Tests for Flakiness:** Runs tests multiple times to determine their flakiness.
@@ -16,8 +14,7 @@ Features
1614
* **Jira Integration (Optional):** Create, review, and manage flaky test tickets in Jira.
1715
* **Local Database:** Store known flaky tests and their associated Jira tickets for easy reference.
1816

19-
Prerequisites
20-
-------------
17+
## Prerequisites
2118

2219
1. **Go:** Version 1.21 or later recommended.
2320
2. **Jira API Access (optional):** Required only if you want to use the Jira-related commands (`create-tickets`, `review-tickets`, `sync-jira`):
@@ -26,23 +23,24 @@ Prerequisites
2623
* `JIRA_API_KEY`: Your Jira API token (generate one in your Jira account settings).
2724
* `JIRA_PROJECT_KEY`: (Optional) The default Jira project key to use if `--jira-project` is not specified for `create-tickets`.
2825

29-
Installation
30-
------------
26+
## Installation
3127

3228
To install the `flakeguard` CLI, ensure you have Go installed. Then run:
3329

30+
```sh
3431
go install github.com/smartcontractkit/chainlink-testing-framework/tools/flakeguard@latest
32+
```
3533

3634
You can also clone the repository and build from source:
3735

36+
```sh
3837
git clone https://github.com/smartcontractkit/chainlink-testing-framework.git
3938
cd chainlink-testing-framework/tools/flakeguard
4039

4140
go build -o flakeguard main.go
41+
```
4242

43-
44-
Usage (Flaky Test Detection)
45-
----------------------------------
43+
## Usage (Flaky Test Detection)
4644

4745
Flakeguard provides two primary commands for local detection of flaky tests without involving Jira: `find` and `run`.
4846

@@ -56,8 +54,7 @@ The `find` command scans your Go project to determine which test packages may be
5654
After identifying packages of interest (via `flakeguard find` or otherwise), use the `run` command to execute tests multiple times to detect flakiness.
5755

5856

59-
Configuration Files (for Jira Integration)
60-
------------------------------------------
57+
## Configuration Files (for Jira Integration)
6158

6259
When using Flakeguard’s Jira integration and local database features, you may need these configuration files:
6360

@@ -83,8 +80,7 @@ When using Flakeguard’s Jira integration and local database features, you may
8380
* **Flag:** Use `--user-test-mapping-path` (default: `user_test_mapping.json`) when creating tickets.
8481

8582

86-
Usage (Jira Integration)
87-
------------------------
83+
## Usage (Jira Integration)
8884

8985
If you only need to identify flaky tests locally (via `find` and `run`) and do not intend to create or manage Jira tickets, you can skip these commands.
9086

@@ -113,7 +109,7 @@ Interactively process a CSV file of flaky tests, suggest assignees based on patt
113109

114110
**Example:**
115111

116-
```
112+
```sh
117113
go run main.go create-tickets \
118114
--jira-project=DX \
119115
--test-db-path=flaky_test_db.json \
@@ -147,7 +143,7 @@ Interactively review tickets in the local database. Fetches current status and P
147143

148144
**Examples:**
149145

150-
```
146+
```sh
151147
go run main.go review-tickets --test-db-path ".flaky_test_db.json" --dry-run=false --user-mapping-path "user_mapping.json"
152148
```
153149

@@ -166,6 +162,6 @@ Scans Jira for all tickets matching a specific label and ensures they exist in t
166162

167163
**Example:**
168164

169-
```
165+
```sh
170166
go run main.go sync-jira --test-db-path=.flaky_test_db.json
171167
```

tools/flakeguard/cmd/create_tickets.go

Lines changed: 28 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -117,12 +117,16 @@ Features:
117117

118118
// Check local DB first for existing ticket
119119
if entry, found := db.GetEntry(ft.TestPackage, ft.TestName); found {
120-
log.Debug().Str("test", ft.TestName).Str("db_ticket", entry.JiraTicket).Str("db_assignee", entry.AssigneeID).Time("db_skipped_at", entry.SkippedAt).Msg("Found existing entry in local DB")
121-
122-
if entry.JiraTicket != "" {
123-
ft.ExistingJiraKey = entry.JiraTicket
124-
ft.ExistingTicketSource = "localdb"
125-
}
120+
log.Debug().
121+
Str("test", ft.TestName).
122+
Str("db_ticket", entry.JiraTicket).
123+
Str("db_assignee", entry.AssigneeID).
124+
Time("db_skipped_at", entry.SkippedAt).
125+
Msg("Found existing entry in local DB")
126+
127+
ft.RelatedJiraTickets = entry.PastJiraTickets
128+
ft.ExistingJiraKey = entry.JiraTicket
129+
ft.ExistingTicketSource = "localdb"
126130
// Always assign SkippedAt and AssigneeID from the DB entry if found
127131
ft.SkippedAt = entry.SkippedAt
128132
if entry.AssigneeID != "" {
@@ -170,35 +174,6 @@ Features:
170174
client = nil
171175
}
172176

173-
if client != nil {
174-
processedCount := 0
175-
totalToSearch := 0
176-
for _, t := range tickets {
177-
if t.ExistingJiraKey == "" {
178-
totalToSearch++
179-
}
180-
}
181-
182-
for i := range tickets {
183-
t := &tickets[i]
184-
if t.ExistingJiraKey == "" {
185-
key, searchErr := findExistingTicket(client, jiraSearchLabel, *t)
186-
processedCount++
187-
if searchErr != nil {
188-
log.Warn().Err(searchErr).Str("summary", t.Summary).Msg("Jira search failed for test")
189-
} else if key != "" {
190-
log.Info().Str("test", t.TestName).Str("found_key", key).Str("label", jiraSearchLabel).Msg("Found existing ticket in Jira via search")
191-
t.ExistingJiraKey = key
192-
t.ExistingTicketSource = "jira"
193-
errDb := db.UpsertEntry(t.TestPackage, t.TestName, key, t.SkippedAt, t.AssigneeId)
194-
if errDb != nil {
195-
log.Error().Err(errDb).Str("key", key).Msg("Failed to update local DB after finding ticket in Jira!")
196-
}
197-
}
198-
}
199-
}
200-
}
201-
202177
m := initialCreateModel(tickets, userMap)
203178
m.DryRun = dryRun
204179
m.JiraProject = jiraProject
@@ -763,7 +738,19 @@ func updateConfirm(m createModel) (tea.Model, tea.Cmd) {
763738
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
764739
defer cancel()
765740

766-
issueKey, err := jirautils.CreateTicketInJira(ctx, m.JiraClient, t.Summary, t.Description, m.JiraProject, m.JiraIssueType, assigneeForJira, t.Priority, []string{jiraSearchLabel}, pillarName)
741+
issueKey, err := jirautils.CreateTicketInJira(
742+
ctx,
743+
m.JiraClient,
744+
t.Summary,
745+
t.Description,
746+
m.JiraProject,
747+
m.JiraIssueType,
748+
assigneeForJira,
749+
t.Priority,
750+
t.RelatedJiraTickets,
751+
[]string{},
752+
pillarName,
753+
)
767754

768755
if err != nil {
769756
errMsg := fmt.Sprintf("Failed to create Jira ticket for %q", t.Summary)
@@ -1034,6 +1021,11 @@ func viewNormal(m createModel) string {
10341021
sb.WriteString(descHeaderStyle.Render("Proposed Description:") + "\n")
10351022
sb.WriteString(descBodyStyle.Render(t.Description) + "\n")
10361023

1024+
if len(t.RelatedJiraTickets) > 0 {
1025+
sb.WriteString(descHeaderStyle.Render("Related Jira Tickets:") + "\n")
1026+
sb.WriteString(descBodyStyle.Render(strings.Join(t.RelatedJiraTickets, ", ")) + "\n")
1027+
}
1028+
10371029
// Help Line / Actions
10381030
sb.WriteString(helpStyle.Render(buildHelpLine(m)))
10391031

tools/flakeguard/cmd/make_pr.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
package cmd
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"os"
7+
"time"
8+
9+
"github.com/go-git/go-git/v5"
10+
"github.com/go-git/go-git/v5/plumbing"
11+
"github.com/google/go-github/v72/github"
12+
"github.com/smartcontractkit/chainlink-testing-framework/tools/flakeguard/localdb"
13+
"github.com/spf13/cobra"
14+
"golang.org/x/oauth2"
15+
)
16+
17+
var (
18+
repoPath string
19+
localDBPath string
20+
)
21+
22+
var MakePRCmd = &cobra.Command{
23+
Use: "make-pr",
24+
Short: "Make a PR to skip identified flaky tests",
25+
RunE: makePR,
26+
}
27+
28+
func makePR(cmd *cobra.Command, args []string) error {
29+
repo, err := git.PlainOpen(repoPath)
30+
if err != nil {
31+
return err
32+
}
33+
34+
db, err := localdb.LoadDBWithPath(localDBPath)
35+
if err != nil {
36+
return err
37+
}
38+
39+
entries := db.GetAllEntries()
40+
41+
branchName := fmt.Sprintf("flakeguard-skip-%s", time.Now().Format("20060102150405"))
42+
w, err := repo.Worktree()
43+
if err != nil {
44+
return err
45+
}
46+
err = w.Checkout(&git.CheckoutOptions{
47+
Branch: plumbing.NewBranchReferenceName(branchName),
48+
Create: true,
49+
})
50+
if err != nil {
51+
return err
52+
}
53+
54+
for _, entry := range entries {
55+
// Find the test function in the codebase and skip it
56+
57+
entry.SkippedAt = time.Now()
58+
db.UpsertEntry(entry.TestPackage, entry.TestName, entry.JiraTicket, entry.SkippedAt, entry.AssigneeID)
59+
60+
}
61+
62+
_, err = w.Add(".")
63+
if err != nil {
64+
return err
65+
}
66+
_, err = w.Commit("Skips flaky tests", &git.CommitOptions{})
67+
if err != nil {
68+
return err
69+
}
70+
71+
err = repo.Push(&git.PushOptions{})
72+
if err != nil {
73+
return err
74+
}
75+
76+
ctx := context.Background()
77+
ts := oauth2.StaticTokenSource(
78+
&oauth2.Token{AccessToken: os.Getenv("GITHUB_TOKEN")},
79+
)
80+
tc := oauth2.NewClient(ctx, ts)
81+
client := github.NewClient(tc)
82+
83+
owner := "your-org"
84+
repoName := "your-repo"
85+
pr := &github.NewPullRequest{
86+
Title: github.Ptr("Skip flaky tests"),
87+
Head: github.Ptr(branchName),
88+
Base: github.Ptr("main"),
89+
Body: github.Ptr("This PR skips flaky tests."),
90+
MaintainerCanModify: github.Ptr(true),
91+
}
92+
_, _, err = client.PullRequests.Create(ctx, owner, repoName, pr)
93+
if err != nil {
94+
return err
95+
}
96+
97+
fmt.Println("PR created!")
98+
return nil
99+
}
100+
101+
func init() {
102+
MakePRCmd.Flags().StringVarP(&repoPath, "repo", "r", ".", "Path to the repository to make the PR in")
103+
}

0 commit comments

Comments
 (0)