Skip to content

Commit c8189a6

Browse files
Copilotpelikhan
andcommitted
Address code review feedback: extract magic numbers and add optimizations
- Extract hardcoded values as named constants for better maintainability - Add early return for empty invalidProps parameter - Optimize similarity scoring with early exit for poor matches - All tests pass and functionality remains intact Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
1 parent 083ed92 commit c8189a6

8 files changed

Lines changed: 2256 additions & 2241 deletions

File tree

.github/workflows/ci-doctor.lock.yml

Lines changed: 817 additions & 818 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.github/workflows/dev.lock.yml

Lines changed: 689 additions & 689 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.github/workflows/tidy.lock.yml

Lines changed: 692 additions & 692 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/cli/logs.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ var ErrNoArtifacts = errors.New("no artifacts found for this run")
9191
// fetchJobStatuses gets job information for a workflow run and counts failed jobs
9292
func fetchJobStatuses(runID int64, verbose bool) (int, error) {
9393
args := []string{"api", fmt.Sprintf("repos/{owner}/{repo}/actions/runs/%d/jobs", runID), "--jq", ".jobs[] | {name: .name, status: .status, conclusion: .conclusion}"}
94-
94+
9595
if verbose {
9696
fmt.Println(console.FormatVerboseMessage(fmt.Sprintf("Fetching job statuses for run %d", runID)))
9797
}
@@ -113,15 +113,15 @@ func fetchJobStatuses(runID int64, verbose bool) (int, error) {
113113
if strings.TrimSpace(line) == "" {
114114
continue
115115
}
116-
116+
117117
var job JobInfo
118118
if err := json.Unmarshal([]byte(line), &job); err != nil {
119119
if verbose {
120120
fmt.Println(console.FormatVerboseMessage(fmt.Sprintf("Failed to parse job info: %s", line)))
121121
}
122122
continue
123123
}
124-
124+
125125
// Count jobs with failure conclusions as errors
126126
if job.Conclusion == "failure" || job.Conclusion == "cancelled" || job.Conclusion == "timed_out" {
127127
failedJobs++
@@ -130,7 +130,7 @@ func fetchJobStatuses(runID int64, verbose bool) (int, error) {
130130
}
131131
}
132132
}
133-
133+
134134
return failedJobs, nil
135135
}
136136

pkg/parser/schema.go

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ func validateWithSchemaAndLocation(frontmatter map[string]any, schemaJSON, conte
212212

213213
// Rewrite "additional properties not allowed" errors to be more friendly
214214
message := rewriteAdditionalPropertiesError(primaryPath.Message)
215-
215+
216216
// Add schema-based suggestions
217217
suggestions := generateSchemaBasedSuggestions(schemaJSON, primaryPath.Message, primaryPath.Path)
218218
if suggestions != "" {
@@ -240,7 +240,7 @@ func validateWithSchemaAndLocation(frontmatter map[string]any, schemaJSON, conte
240240

241241
// Rewrite "additional properties not allowed" errors to be more friendly
242242
message := rewriteAdditionalPropertiesError(errorMsg)
243-
243+
244244
// Add schema-based suggestions for fallback case
245245
suggestions := generateSchemaBasedSuggestions(schemaJSON, errorMsg, "")
246246
if suggestions != "" {
@@ -393,6 +393,14 @@ func findFrontmatterBounds(lines []string) (startIdx int, endIdx int, frontmatte
393393
return startIdx, endIdx, frontmatterContent
394394
}
395395

396+
// Constants for suggestion limits and field generation
397+
const (
398+
maxClosestMatches = 3 // Maximum number of closest matches to find
399+
maxSuggestions = 5 // Maximum number of suggestions to show
400+
maxAcceptedFields = 10 // Maximum number of accepted fields to display
401+
maxExampleFields = 3 // Maximum number of fields to include in example JSON
402+
)
403+
396404
// generateSchemaBasedSuggestions generates helpful suggestions based on the schema and error type
397405
func generateSchemaBasedSuggestions(schemaJSON, errorMessage, jsonPath string) string {
398406
// Parse the schema to extract information for suggestions
@@ -405,7 +413,7 @@ func generateSchemaBasedSuggestions(schemaJSON, errorMessage, jsonPath string) s
405413
if strings.Contains(strings.ToLower(errorMessage), "additional propert") && strings.Contains(strings.ToLower(errorMessage), "not allowed") {
406414
invalidProps := extractAdditionalPropertyNames(errorMessage)
407415
acceptedFields := extractAcceptedFieldsFromSchema(schemaDoc, jsonPath)
408-
416+
409417
if len(acceptedFields) > 0 {
410418
return generateFieldSuggestions(invalidProps, acceptedFields)
411419
}
@@ -510,12 +518,12 @@ func resolveSchemaWithOneOf(schema map[string]any) map[string]any {
510518

511519
// generateFieldSuggestions creates a helpful suggestion message for invalid field names
512520
func generateFieldSuggestions(invalidProps, acceptedFields []string) string {
513-
if len(acceptedFields) == 0 {
521+
if len(acceptedFields) == 0 || len(invalidProps) == 0 {
514522
return ""
515523
}
516524

517525
var suggestion strings.Builder
518-
526+
519527
if len(invalidProps) == 1 {
520528
suggestion.WriteString("Did you mean one of: ")
521529
} else {
@@ -525,26 +533,26 @@ func generateFieldSuggestions(invalidProps, acceptedFields []string) string {
525533
// Find closest matches using simple string distance
526534
var suggestions []string
527535
for _, invalidProp := range invalidProps {
528-
closest := findClosestMatches(invalidProp, acceptedFields, 3)
536+
closest := findClosestMatches(invalidProp, acceptedFields, maxClosestMatches)
529537
suggestions = append(suggestions, closest...)
530538
}
531539

532540
// If we have specific suggestions, show them first
533541
if len(suggestions) > 0 {
534542
// Remove duplicates
535543
uniqueSuggestions := removeDuplicates(suggestions)
536-
if len(uniqueSuggestions) <= 5 {
544+
if len(uniqueSuggestions) <= maxSuggestions {
537545
suggestion.WriteString(strings.Join(uniqueSuggestions, ", "))
538546
} else {
539-
suggestion.WriteString(strings.Join(uniqueSuggestions[:5], ", "))
547+
suggestion.WriteString(strings.Join(uniqueSuggestions[:maxSuggestions], ", "))
540548
suggestion.WriteString(", ...")
541549
}
542550
} else {
543551
// Show all accepted fields if no close matches
544-
if len(acceptedFields) <= 10 {
552+
if len(acceptedFields) <= maxAcceptedFields {
545553
suggestion.WriteString(strings.Join(acceptedFields, ", "))
546554
} else {
547-
suggestion.WriteString(strings.Join(acceptedFields[:10], ", "))
555+
suggestion.WriteString(strings.Join(acceptedFields[:maxAcceptedFields], ", "))
548556
suggestion.WriteString(", ...")
549557
}
550558
}
@@ -565,7 +573,7 @@ func findClosestMatches(target string, candidates []string, maxResults int) []st
565573
for _, candidate := range candidates {
566574
candidateLower := strings.ToLower(candidate)
567575
score := calculateSimilarityScore(targetLower, candidateLower)
568-
576+
569577
// Only include if there's some similarity
570578
if score > 0 {
571579
matches = append(matches, match{value: candidate, score: score})
@@ -588,32 +596,41 @@ func findClosestMatches(target string, candidates []string, maxResults int) []st
588596

589597
// calculateSimilarityScore calculates a simple similarity score between two strings
590598
func calculateSimilarityScore(a, b string) int {
599+
// Early exit for obviously poor matches (length difference > 2x shorter string length)
600+
minLen := len(a)
601+
if len(b) < minLen {
602+
minLen = len(b)
603+
}
604+
lengthDiff := abs(len(a) - len(b))
605+
if lengthDiff > minLen*2 && minLen > 0 {
606+
return 0
607+
}
608+
591609
// Simple heuristics for string similarity
592610
score := 0
593-
611+
594612
// Bonus for substring matches
595613
if strings.Contains(b, a) || strings.Contains(a, b) {
596614
score += 10
597615
}
598-
616+
599617
// Bonus for common prefixes
600618
commonPrefix := 0
601619
for i := 0; i < len(a) && i < len(b) && a[i] == b[i]; i++ {
602620
commonPrefix++
603621
}
604622
score += commonPrefix * 2
605-
623+
606624
// Bonus for common suffixes
607625
commonSuffix := 0
608626
for i := 0; i < len(a) && i < len(b) && a[len(a)-1-i] == b[len(b)-1-i]; i++ {
609627
commonSuffix++
610628
}
611629
score += commonSuffix * 2
612-
630+
613631
// Penalty for length difference
614-
lengthDiff := abs(len(a) - len(b))
615632
score -= lengthDiff
616-
633+
617634
return score
618635
}
619636

@@ -694,11 +711,10 @@ func generateExampleFromSchema(schema map[string]any) any {
694711

695712
// Add a few example properties (prioritize required ones)
696713
count := 0
697-
maxFields := 3
698714

699715
// First, add required fields
700716
for propName, propSchema := range properties {
701-
if requiredFields[propName] && count < maxFields {
717+
if requiredFields[propName] && count < maxExampleFields {
702718
if propSchemaMap, ok := propSchema.(map[string]any); ok {
703719
result[propName] = generateExampleFromSchema(propSchemaMap)
704720
count++
@@ -708,7 +724,7 @@ func generateExampleFromSchema(schema map[string]any) any {
708724

709725
// Then add some optional fields if we have room
710726
for propName, propSchema := range properties {
711-
if !requiredFields[propName] && count < maxFields {
727+
if !requiredFields[propName] && count < maxExampleFields {
712728
if propSchemaMap, ok := propSchema.(map[string]any); ok {
713729
result[propName] = generateExampleFromSchema(propSchemaMap)
714730
count++
@@ -726,14 +742,14 @@ func generateExampleFromSchema(schema map[string]any) any {
726742
func removeDuplicates(strings []string) []string {
727743
seen := make(map[string]bool)
728744
var result []string
729-
745+
730746
for _, str := range strings {
731747
if !seen[str] {
732748
seen[str] = true
733749
result = append(result, str)
734750
}
735751
}
736-
752+
737753
return result
738754
}
739755

pkg/parser/schema_suggestions_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,4 +317,4 @@ func TestGenerateExampleJSONForPath(t *testing.T) {
317317
}
318318
})
319319
}
320-
}
320+
}

pkg/workflow/metrics.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -264,25 +264,25 @@ type ErrorWarningCounts struct {
264264
// This is more accurate than simple string matching and uses the same logic as validate_errors.cjs
265265
func CountErrorsAndWarningsWithPatterns(logContent string, patterns []ErrorPattern) ErrorWarningCounts {
266266
counts := ErrorWarningCounts{}
267-
267+
268268
if len(patterns) == 0 {
269269
return counts
270270
}
271-
271+
272272
lines := strings.Split(logContent, "\n")
273-
273+
274274
for _, pattern := range patterns {
275275
regex, err := regexp.Compile(pattern.Pattern)
276276
if err != nil {
277277
// Skip invalid patterns
278278
continue
279279
}
280-
280+
281281
for _, line := range lines {
282282
matches := regex.FindAllStringSubmatch(line, -1)
283283
for _, match := range matches {
284284
level := extractLevelFromMatch(match, pattern)
285-
285+
286286
if strings.ToLower(level) == "error" {
287287
counts.ErrorCount++
288288
} else if strings.ToLower(level) == "warning" || strings.ToLower(level) == "warn" {
@@ -291,7 +291,7 @@ func CountErrorsAndWarningsWithPatterns(logContent string, patterns []ErrorPatte
291291
}
292292
}
293293
}
294-
294+
295295
return counts
296296
}
297297

@@ -301,26 +301,26 @@ func extractLevelFromMatch(match []string, pattern ErrorPattern) string {
301301
if pattern.LevelGroup > 0 && pattern.LevelGroup < len(match) && match[pattern.LevelGroup] != "" {
302302
levelText := strings.ToLower(match[pattern.LevelGroup])
303303
// Normalize common error/warning keywords
304-
if strings.Contains(levelText, "err") || strings.Contains(levelText, "error") ||
305-
strings.Contains(levelText, "fail") || strings.Contains(levelText, "fatal") {
304+
if strings.Contains(levelText, "err") || strings.Contains(levelText, "error") ||
305+
strings.Contains(levelText, "fail") || strings.Contains(levelText, "fatal") {
306306
return "error"
307307
} else if strings.Contains(levelText, "warn") || strings.Contains(levelText, "warning") {
308308
return "warning"
309309
}
310310
// Return the original level text if it doesn't match common patterns
311311
return match[pattern.LevelGroup]
312312
}
313-
313+
314314
// Try to infer level from the full match content
315315
if len(match) > 0 {
316316
fullMatch := strings.ToLower(match[0])
317317
if strings.Contains(fullMatch, "error") || strings.Contains(fullMatch, "err") ||
318-
strings.Contains(fullMatch, "fail") || strings.Contains(fullMatch, "fatal") {
318+
strings.Contains(fullMatch, "fail") || strings.Contains(fullMatch, "fatal") {
319319
return "error"
320320
} else if strings.Contains(fullMatch, "warn") || strings.Contains(fullMatch, "warning") {
321321
return "warning"
322322
}
323323
}
324-
324+
325325
return "unknown"
326326
}

pkg/workflow/pattern_error_counting_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ Another warning message`,
116116
},
117117
},
118118
expectedErrors: 2, // npm ERR! + random error
119-
expectedWarns: 2, // npm WARN + warning message
119+
expectedWarns: 2, // npm WARN + warning message
120120
},
121121
{
122122
name: "invalid regex pattern",
@@ -150,11 +150,11 @@ warning: be careful`,
150150
for _, tt := range tests {
151151
t.Run(tt.name, func(t *testing.T) {
152152
counts := CountErrorsAndWarningsWithPatterns(tt.logContent, tt.patterns)
153-
153+
154154
if counts.ErrorCount != tt.expectedErrors {
155155
t.Errorf("Expected %d errors, got %d", tt.expectedErrors, counts.ErrorCount)
156156
}
157-
157+
158158
if counts.WarningCount != tt.expectedWarns {
159159
t.Errorf("Expected %d warnings, got %d", tt.expectedWarns, counts.WarningCount)
160160
}
@@ -215,4 +215,4 @@ func TestExtractLevelFromMatch(t *testing.T) {
215215
}
216216
})
217217
}
218-
}
218+
}

0 commit comments

Comments
 (0)