mirror of
https://github.com/ivuorinen/gibidify.git
synced 2026-02-13 08:49:25 +00:00
chore: modernize workflows, security scanning, and linting configuration (#50)
* build: update Go 1.25, CI workflows, and build tooling - Upgrade to Go 1.25 - Add benchmark targets to Makefile - Implement parallel gosec execution - Lock tool versions for reproducibility - Add shellcheck directives to scripts - Update CI workflows with improved caching * refactor: migrate from golangci-lint to revive - Replace golangci-lint with revive for linting - Configure comprehensive revive rules - Fix all EditorConfig violations - Add yamllint and yamlfmt support - Remove deprecated .golangci.yml * refactor: rename utils to shared and deduplicate code - Rename utils package to shared - Add shared constants package - Deduplicate constants across packages - Address CodeRabbit review feedback * fix: resolve SonarQube issues and add safety guards - Fix all 73 SonarQube OPEN issues - Add nil guards for resourceMonitor, backpressure, metricsCollector - Implement io.Closer for headerFileReader - Propagate errors from processing helpers - Add metrics and templates packages - Improve error handling across codebase * test: improve test infrastructure and coverage - Add benchmarks for cli, fileproc, metrics - Improve test coverage for cli, fileproc, config - Refactor tests with helper functions - Add shared test constants - Fix test function naming conventions - Reduce cognitive complexity in benchmark tests * docs: update documentation and configuration examples - Update CLAUDE.md with current project state - Refresh README with new features - Add usage and configuration examples - Add SonarQube project configuration - Consolidate config.example.yaml * fix: resolve shellcheck warnings in scripts - Use ./*.go instead of *.go to prevent dash-prefixed filenames from being interpreted as options (SC2035) - Remove unreachable return statement after exit (SC2317) - Remove obsolete gibidiutils/ directory reference * chore(deps): upgrade go dependencies * chore(lint): megalinter fixes * fix: improve test coverage and fix file descriptor leaks - Add defer r.Close() to fix pipe file descriptor leaks in benchmark tests - Refactor TestProcessorConfigureFileTypes with helper functions and assertions - Refactor TestProcessorLogFinalStats with output capture and keyword verification - Use shared constants instead of literal strings (TestFilePNG, FormatMarkdown, etc.) - Reduce cognitive complexity by extracting helper functions * fix: align test comments with function names Remove underscores from test comments to match actual function names: - benchmark/benchmark_test.go (2 fixes) - fileproc/filetypes_config_test.go (4 fixes) - fileproc/filetypes_registry_test.go (6 fixes) - fileproc/processor_test.go (6 fixes) - fileproc/resource_monitor_types_test.go (4 fixes) - fileproc/writer_test.go (3 fixes) * fix: various test improvements and bug fixes - Remove duplicate maxCacheSize check in filetypes_registry_test.go - Shorten long comment in processor_test.go to stay under 120 chars - Remove flaky time.Sleep in collector_test.go, use >= 0 assertion - Close pipe reader in benchmark_test.go to fix file descriptor leak - Use ContinueOnError in flags_test.go to match ResetFlags behavior - Add nil check for p.ui in processor_workers.go before UpdateProgress - Fix resource_monitor_validation_test.go by setting hardMemoryLimitBytes directly * chore(yaml): add missing document start markers Add --- document start to YAML files to satisfy yamllint: - .github/workflows/codeql.yml - .github/workflows/build-test-publish.yml - .github/workflows/security.yml - .github/actions/setup/action.yml * fix: guard nil resourceMonitor and fix test deadlock - Guard resourceMonitor before CreateFileProcessingContext call - Add ui.UpdateProgress on emergency stop and path error returns - Fix potential deadlock in TestProcessFile using wg.Go with defer close
This commit is contained in:
259
shared/errors.go
Normal file
259
shared/errors.go
Normal file
@@ -0,0 +1,259 @@
|
||||
// Package shared provides common utility functions.
|
||||
package shared
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ErrorType represents the category of error.
|
||||
type ErrorType int
|
||||
|
||||
const (
|
||||
// ErrorTypeUnknown represents an unknown error type.
|
||||
ErrorTypeUnknown ErrorType = iota
|
||||
// ErrorTypeCLI represents command-line interface errors.
|
||||
ErrorTypeCLI
|
||||
// ErrorTypeFileSystem represents file system operation errors.
|
||||
ErrorTypeFileSystem
|
||||
// ErrorTypeProcessing represents file processing errors.
|
||||
ErrorTypeProcessing
|
||||
// ErrorTypeConfiguration represents configuration errors.
|
||||
ErrorTypeConfiguration
|
||||
// ErrorTypeIO represents input/output errors.
|
||||
ErrorTypeIO
|
||||
// ErrorTypeValidation represents validation errors.
|
||||
ErrorTypeValidation
|
||||
)
|
||||
|
||||
// String returns the string representation of the error type.
|
||||
func (e ErrorType) String() string {
|
||||
switch e {
|
||||
case ErrorTypeCLI:
|
||||
return "CLI"
|
||||
case ErrorTypeFileSystem:
|
||||
return "FileSystem"
|
||||
case ErrorTypeProcessing:
|
||||
return "Processing"
|
||||
case ErrorTypeConfiguration:
|
||||
return "Configuration"
|
||||
case ErrorTypeIO:
|
||||
return "IO"
|
||||
case ErrorTypeValidation:
|
||||
return "Validation"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// StructuredError represents a structured error with type, code, and context.
|
||||
type StructuredError struct {
|
||||
Type ErrorType
|
||||
Code string
|
||||
Message string
|
||||
Cause error
|
||||
Context map[string]any
|
||||
FilePath string
|
||||
Line int
|
||||
}
|
||||
|
||||
// Error implements the error interface.
|
||||
func (e *StructuredError) Error() string {
|
||||
if e.Cause != nil {
|
||||
return fmt.Sprintf("%s [%s]: %s: %v", e.Type, e.Code, e.Message, e.Cause)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s [%s]: %s", e.Type, e.Code, e.Message)
|
||||
}
|
||||
|
||||
// Unwrap returns the underlying cause error.
|
||||
func (e *StructuredError) Unwrap() error {
|
||||
return e.Cause
|
||||
}
|
||||
|
||||
// WithContext adds context information to the error.
|
||||
func (e *StructuredError) WithContext(key string, value any) *StructuredError {
|
||||
if e.Context == nil {
|
||||
e.Context = make(map[string]any)
|
||||
}
|
||||
e.Context[key] = value
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// WithFilePath adds file path information to the error.
|
||||
func (e *StructuredError) WithFilePath(filePath string) *StructuredError {
|
||||
e.FilePath = filePath
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// WithLine adds line number information to the error.
|
||||
func (e *StructuredError) WithLine(line int) *StructuredError {
|
||||
e.Line = line
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// NewStructuredError creates a new structured error.
|
||||
func NewStructuredError(errorType ErrorType, code, message, filePath string, context map[string]any) *StructuredError {
|
||||
return &StructuredError{
|
||||
Type: errorType,
|
||||
Code: code,
|
||||
Message: message,
|
||||
FilePath: filePath,
|
||||
Context: context,
|
||||
}
|
||||
}
|
||||
|
||||
// NewStructuredErrorf creates a new structured error with formatted message.
|
||||
func NewStructuredErrorf(errorType ErrorType, code, format string, args ...any) *StructuredError {
|
||||
return &StructuredError{
|
||||
Type: errorType,
|
||||
Code: code,
|
||||
Message: fmt.Sprintf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
// WrapError wraps an existing error with structured error information.
|
||||
func WrapError(err error, errorType ErrorType, code, message string) *StructuredError {
|
||||
return &StructuredError{
|
||||
Type: errorType,
|
||||
Code: code,
|
||||
Message: message,
|
||||
Cause: err,
|
||||
}
|
||||
}
|
||||
|
||||
// WrapErrorf wraps an existing error with formatted message.
|
||||
func WrapErrorf(err error, errorType ErrorType, code, format string, args ...any) *StructuredError {
|
||||
return &StructuredError{
|
||||
Type: errorType,
|
||||
Code: code,
|
||||
Message: fmt.Sprintf(format, args...),
|
||||
Cause: err,
|
||||
}
|
||||
}
|
||||
|
||||
// Common error codes for each type.
|
||||
const (
|
||||
// CodeCLIMissingSource CLI Error Codes.
|
||||
CodeCLIMissingSource = "MISSING_SOURCE"
|
||||
CodeCLIInvalidArgs = "INVALID_ARGS"
|
||||
|
||||
// CodeFSPathResolution FileSystem Error Codes.
|
||||
CodeFSPathResolution = "PATH_RESOLUTION"
|
||||
CodeFSPermission = "PERMISSION_DENIED"
|
||||
CodeFSNotFound = "NOT_FOUND"
|
||||
CodeFSAccess = "ACCESS_DENIED"
|
||||
|
||||
// CodeProcessingFileRead Processing Error Codes.
|
||||
CodeProcessingFileRead = "FILE_READ"
|
||||
CodeProcessingCollection = "COLLECTION"
|
||||
CodeProcessingTraversal = "TRAVERSAL"
|
||||
CodeProcessingEncode = "ENCODE"
|
||||
|
||||
// CodeConfigValidation Configuration Error Codes.
|
||||
CodeConfigValidation = "VALIDATION"
|
||||
CodeConfigMissing = "MISSING"
|
||||
|
||||
// CodeIOFileCreate IO Error Codes.
|
||||
CodeIOFileCreate = "FILE_CREATE"
|
||||
CodeIOFileWrite = "FILE_WRITE"
|
||||
CodeIOEncoding = "ENCODING"
|
||||
CodeIOWrite = "WRITE"
|
||||
CodeIORead = "READ"
|
||||
CodeIOClose = "CLOSE"
|
||||
|
||||
// Validation Error Codes.
|
||||
CodeValidationFormat = "FORMAT"
|
||||
CodeValidationFileType = "FILE_TYPE"
|
||||
CodeValidationSize = "SIZE_LIMIT"
|
||||
CodeValidationRequired = "REQUIRED"
|
||||
CodeValidationPath = "PATH_TRAVERSAL"
|
||||
|
||||
// Resource Limit Error Codes.
|
||||
CodeResourceLimitFiles = "FILE_COUNT_LIMIT"
|
||||
CodeResourceLimitTotalSize = "TOTAL_SIZE_LIMIT"
|
||||
CodeResourceLimitTimeout = "TIMEOUT"
|
||||
CodeResourceLimitMemory = "MEMORY_LIMIT"
|
||||
CodeResourceLimitConcurrency = "CONCURRENCY_LIMIT"
|
||||
CodeResourceLimitRate = "RATE_LIMIT"
|
||||
)
|
||||
|
||||
// Predefined error constructors for common error scenarios
|
||||
|
||||
// NewMissingSourceError creates a CLI error for missing source argument.
|
||||
func NewMissingSourceError() *StructuredError {
|
||||
return NewStructuredError(
|
||||
ErrorTypeCLI,
|
||||
CodeCLIMissingSource,
|
||||
"usage: gibidify -source <source_directory> [--destination <output_file>] "+
|
||||
"[--format=json|yaml|markdown (default: json)]",
|
||||
"",
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
// NewFileSystemError creates a file system error.
|
||||
func NewFileSystemError(code, message string) *StructuredError {
|
||||
return NewStructuredError(ErrorTypeFileSystem, code, message, "", nil)
|
||||
}
|
||||
|
||||
// NewProcessingError creates a processing error.
|
||||
func NewProcessingError(code, message string) *StructuredError {
|
||||
return NewStructuredError(ErrorTypeProcessing, code, message, "", nil)
|
||||
}
|
||||
|
||||
// NewIOError creates an IO error.
|
||||
func NewIOError(code, message string) *StructuredError {
|
||||
return NewStructuredError(ErrorTypeIO, code, message, "", nil)
|
||||
}
|
||||
|
||||
// NewValidationError creates a validation error.
|
||||
func NewValidationError(code, message string) *StructuredError {
|
||||
return NewStructuredError(ErrorTypeValidation, code, message, "", nil)
|
||||
}
|
||||
|
||||
// LogError logs an error with a consistent format if the error is not nil.
|
||||
// The operation parameter describes what was being attempted.
|
||||
// Additional context can be provided via the args parameter.
|
||||
func LogError(operation string, err error, args ...any) {
|
||||
if err != nil {
|
||||
msg := operation
|
||||
if len(args) > 0 {
|
||||
// Format the operation string with the provided arguments
|
||||
msg = fmt.Sprintf(operation, args...)
|
||||
}
|
||||
|
||||
logger := GetLogger()
|
||||
// Check if it's a structured error and log with additional context
|
||||
structErr := &StructuredError{}
|
||||
if errors.As(err, &structErr) {
|
||||
fields := map[string]any{
|
||||
"error_type": structErr.Type.String(),
|
||||
"error_code": structErr.Code,
|
||||
"context": structErr.Context,
|
||||
"file_path": structErr.FilePath,
|
||||
"line": structErr.Line,
|
||||
}
|
||||
logger.WithFields(fields).Errorf(ErrorFmtWithCause, msg, err)
|
||||
} else {
|
||||
logger.Errorf(ErrorFmtWithCause, msg, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LogErrorf logs an error with a formatted message if the error is not nil.
|
||||
// This is a convenience wrapper around LogError for cases where formatting is needed.
|
||||
func LogErrorf(err error, format string, args ...any) {
|
||||
if err != nil {
|
||||
LogError(format, err, args...)
|
||||
}
|
||||
}
|
||||
|
||||
// Test error variables.
|
||||
var (
|
||||
// ErrTestError is a generic test error.
|
||||
ErrTestError = errors.New(TestErrTestErrorMsg)
|
||||
)
|
||||
Reference in New Issue
Block a user