@@ -30,6 +30,7 @@ var TicketsCmd = &cobra.Command{
3030
3131Actions:
3232 [s] mark as skipped (and optionally post a comment to the Jira ticket)
33+ [u] unskip a ticket
3334 [p] previous ticket
3435 [n] next ticket
3536 [q] quit
@@ -50,14 +51,13 @@ You can later extend this command to support additional actions.`,
5051 return nil
5152 }
5253
53- // Convert entries to model.FlakyTicket
54+ // Convert entries to model.FlakyTicket (using SkippedAt only).
5455 tickets := make ([]model.FlakyTicket , len (entries ))
5556 for i , entry := range entries {
5657 tickets [i ] = model.FlakyTicket {
5758 TestPackage : entry .TestPackage ,
5859 TestName : entry .TestName ,
5960 ExistingJiraKey : entry .JiraTicket ,
60- IsSkipped : entry .IsSkipped ,
6161 SkippedAt : entry .SkippedAt ,
6262 }
6363 }
@@ -74,7 +74,7 @@ You can later extend this command to support additional actions.`,
7474 m .JiraClient = jiraClient
7575 m .LocalDB = db
7676 m .JiraComment = jiraComment
77- m .DryRun = dryRun
77+ m .DryRun = ticketsDryRun
7878
7979 // 5) Run the TUI.
8080 finalModel , err := tea .NewProgram (m ).Run ()
@@ -111,6 +111,7 @@ type ticketModel struct {
111111 JiraComment bool
112112 DryRun bool
113113 quitting bool
114+ infoMessage string // new field for showing info to the user
114115}
115116
116117// initialTicketsModel creates an initial model.
@@ -143,45 +144,67 @@ func (m ticketModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
143144 if m .index > 0 {
144145 m .index --
145146 }
147+ // Clear info message on navigation.
148+ m .infoMessage = ""
146149 return m , nil
147150 case "n" :
148151 if m .index < len (m .tickets )- 1 {
149152 m .index ++
150153 }
154+ // Clear info message on navigation.
155+ m .infoMessage = ""
151156 return m , nil
152157 }
153158
154159 // Action: mark as skipped.
155160 if msg .String () == "s" {
156161 t := m .tickets [m .index ]
157- // Only mark if not already skipped.
158- if ! t . IsSkipped {
159- t .IsSkipped = true
162+ // Only mark as skipped if not already skipped.
163+ if t . SkippedAt . IsZero () {
164+ t .SkippedAt = time . Now ()
160165 // Optionally, post a comment to the Jira ticket if not in dry-run.
161166 if ! m .DryRun && m .JiraClient != nil && t .ExistingJiraKey != "" && m .JiraComment {
162167 comment := fmt .Sprintf ("Test marked as skipped on %s." , time .Now ().Format (time .RFC822 ))
163168 err := jirautils .PostCommentToTicket (m .JiraClient , t .ExistingJiraKey , comment )
164169 if err != nil {
165170 log .Error ().Err (err ).Msgf ("Failed to post comment to Jira ticket %s" , t .ExistingJiraKey )
171+ m .infoMessage = fmt .Sprintf ("Failed to post comment to Jira ticket %s" , t .ExistingJiraKey )
172+ } else {
173+ // Use the Jira link to display the comment location.
174+ m .infoMessage = fmt .Sprintf ("Skip comment posted to Jira ticket: %s" , jirautils .GetJiraLink (t .ExistingJiraKey ))
166175 }
167176 }
168177 // Update local DB state with the current time.
169178 m .tickets [m .index ] = t
170- m .LocalDB .UpdateTicketStatus (t .TestPackage , t .TestName , t .IsSkipped , time . Now () )
179+ m .LocalDB .UpdateTicketStatus (t .TestPackage , t .TestName , t .SkippedAt )
171180 }
172181 return m , nil
173182 }
174- }
175- return m , nil
176- }
177183
178- // getJiraLink returns the full Jira URL for a given ticket key if JIRA_DOMAIN is set.
179- func getJiraLink (ticketKey string ) string {
180- domain := os .Getenv ("JIRA_DOMAIN" )
181- if domain != "" {
182- return fmt .Sprintf ("https://%s/browse/%s" , domain , ticketKey )
184+ // Action: unskip a ticket.
185+ if msg .String () == "u" {
186+ t := m .tickets [m .index ]
187+ // Only unskip if the ticket is currently marked as skipped.
188+ if ! t .SkippedAt .IsZero () {
189+ t .SkippedAt = time.Time {} // reset to zero value
190+ // Optionally, post a comment to the Jira ticket if not in dry-run.
191+ if ! m .DryRun && m .JiraClient != nil && t .ExistingJiraKey != "" && m .JiraComment {
192+ comment := fmt .Sprintf ("Test unskipped on %s." , time .Now ().Format (time .RFC822 ))
193+ err := jirautils .PostCommentToTicket (m .JiraClient , t .ExistingJiraKey , comment )
194+ if err != nil {
195+ log .Error ().Err (err ).Msgf ("Failed to post unskip comment to Jira ticket %s" , t .ExistingJiraKey )
196+ m .infoMessage = fmt .Sprintf ("Failed to post unskip comment to Jira ticket %s" , t .ExistingJiraKey )
197+ } else {
198+ m .infoMessage = fmt .Sprintf ("Unskip comment posted to Jira ticket: %s" , jirautils .GetJiraLink (t .ExistingJiraKey ))
199+ }
200+ }
201+ m .tickets [m .index ] = t
202+ m .LocalDB .UpdateTicketStatus (t .TestPackage , t .TestName , t .SkippedAt )
203+ }
204+ return m , nil
205+ }
183206 }
184- return ticketKey
207+ return m , nil
185208}
186209
187210// View renders the current ticket and available actions.
@@ -194,23 +217,37 @@ func (m ticketModel) View() string {
194217 // Define styles.
195218 headerStyle := lipgloss .NewStyle ().Bold (true ).Foreground (lipgloss .Color ("205" ))
196219 infoStyle := lipgloss .NewStyle ().Foreground (lipgloss .Color ("10" ))
197- faintStyle := lipgloss .NewStyle ().Faint (true )
220+ labelStyle := lipgloss .NewStyle ().Bold (true ).Foreground (lipgloss .Color ("69" ))
221+ dryRunStyle := lipgloss .NewStyle ().Bold (true ).Foreground (lipgloss .Color ("208" ))
222+ actionStyle := lipgloss .NewStyle ().Bold (true ).Foreground (lipgloss .Color ("205" ))
223+
224+ var view string
225+
226+ // Show a dry-run indicator at the top.
227+ if m .DryRun {
228+ view += dryRunStyle .Render ("DRY RUN MODE" ) + "\n \n "
229+ }
198230
199231 // Build header and ticket details.
200- view : = headerStyle .Render (fmt .Sprintf ("Ticket [%d/%d]" , m .index + 1 , len (m .tickets ))) + "\n "
201- view += fmt .Sprintf ("Test: %s\n " , t .TestName )
202- view += fmt .Sprintf ("Package: %s\n " , t .TestPackage )
232+ view + = headerStyle .Render (fmt .Sprintf ("Ticket [%d/%d]\n " , m .index + 1 , len (m .tickets ))) + "\n "
233+ view += fmt .Sprintf ("%s %s\n " , labelStyle . Render ( "Test:" ) , t .TestName )
234+ view += fmt .Sprintf ("%s %s\n " , labelStyle . Render ( "Package:" ) , t .TestPackage )
203235 if t .ExistingJiraKey != "" {
204- view += fmt .Sprintf ("Jira: %s\n " , getJiraLink (t .ExistingJiraKey ))
236+ view += fmt .Sprintf ("%s %s\n " , labelStyle . Render ( "Jira:" ), jirautils . GetJiraLink (t .ExistingJiraKey ))
205237 }
206238
207239 // Show status with color.
208- if t . IsSkipped {
209- view += fmt .Sprintf ("Status: %s\n " , fmt .Sprintf ("Skipped At : %s\n " , faintStyle . Render ( t .SkippedAt .UTC ().Format (time .RFC822 ) )))
240+ if ! t . SkippedAt . IsZero () {
241+ view += fmt .Sprintf ("%s %s\n " , labelStyle . Render ( "Status:" ), fmt .Sprintf ("skipped at : %s" , t .SkippedAt .UTC ().Format (time .RFC822 )))
210242 } else {
211- view += fmt .Sprintf ("Status: %s\n " , infoStyle .Render ("Not Skipped" ))
243+ view += fmt .Sprintf ("%s %s\n " , labelStyle .Render ("Status:" ), infoStyle .Render ("not skipped" ))
244+ }
245+
246+ // Display any info message.
247+ if m .infoMessage != "" {
248+ view += "\n " + infoStyle .Render (m .infoMessage ) + "\n "
212249 }
213250
214- view += "\n Actions: [s] mark as skipped, [p] previous, [n] next, [q] quit"
251+ view += "\n " + actionStyle . Render ( "Actions:" ) + " [s] mark as skipped, [u] unskip , [p] previous, [n] next, [q] quit"
215252 return view
216253}
0 commit comments