Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ snapshot:
version_template: '{{ .Version }}-next'

changelog:
# Disable auto-generated changelog since CHANGELOG.md is maintained manually
disable: true

release:
Expand Down
4 changes: 3 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,14 @@ kairo/
- **Line Length:** 120 characters (MD013)
- **Indentation:** Tabs (Go standard)
- **Naming:** Go conventions (PascalCase for exported, camelCase for unexported)
- **Error Handling:** Typed errors from `internal/errors` package
- **Error Handling:** Always use typed errors from `internal/errors` package
- **Formatting:** `gofmt -w .` (run before committing)
- **Vetting:** `go vet ./...` (run before committing)

### Error Handling Pattern

When handling errors, always use an error type from the `kairoerrors` package. Never use plain `errors.New()` or `fmt.Errorf()` without a typed error.

```go
import kairoerrors "github.com/dkmnx/kairo/internal/errors"

Expand Down
7 changes: 5 additions & 2 deletions cmd/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strings"

"github.com/dkmnx/kairo/internal/audit"
kairoerrors "github.com/dkmnx/kairo/internal/errors"
"github.com/dkmnx/kairo/internal/ui"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -122,7 +123,8 @@ var auditExportCmd = &cobra.Command{
}

if exportOutput == "" {
return fmt.Errorf("--output is required for export")
return kairoerrors.NewError(kairoerrors.ConfigError,
"--output is required for export")
}

if _, err := os.Stat(dir); os.IsNotExist(err) {
Expand Down Expand Up @@ -318,5 +320,6 @@ func exportAuditLog(entries []audit.AuditEntry, outputPath, format string) error
return nil
}

return fmt.Errorf("unsupported format: %s (supported: csv, json)", format)
return kairoerrors.NewError(kairoerrors.ConfigError,
fmt.Sprintf("unsupported format: %s (supported: csv, json)", format))
}
9 changes: 5 additions & 4 deletions cmd/audit_helpers.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
package cmd

import (
"fmt"

"github.com/dkmnx/kairo/internal/audit"
kairoerrors "github.com/dkmnx/kairo/internal/errors"
)

// logAuditEvent logs an audit event using the provided logging function.
Expand All @@ -27,12 +26,14 @@ import (
func logAuditEvent(configDir string, logFunc func(*audit.Logger) error) error {
logger, err := audit.NewLogger(configDir)
if err != nil {
return fmt.Errorf("failed to create audit logger: %w", err)
return kairoerrors.WrapError(kairoerrors.ConfigError,
"failed to create audit logger", err)
}
defer logger.Close()

if err := logFunc(logger); err != nil {
return fmt.Errorf("failed to log audit event: %w", err)
return kairoerrors.WrapError(kairoerrors.ConfigError,
"failed to log audit event", err)
}
return nil
}
2 changes: 1 addition & 1 deletion cmd/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ func TestProviderConfigSaveLoad(t *testing.T) {

func TestGetConfigDir(t *testing.T) {
// Reset configDir to avoid pollution from other tests
configDir = ""
setConfigDir("")

home, err := os.UserHomeDir()
if err != nil {
Expand Down
26 changes: 18 additions & 8 deletions cmd/config_tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"os"
"path/filepath"
"time"

kairoerrors "github.com/dkmnx/kairo/internal/errors"
)

// createConfigBackup creates a backup of the current configuration file.
Expand All @@ -16,15 +18,17 @@ func createConfigBackup(configDir string) (string, error) {
// Read the current config file
data, err := os.ReadFile(configPath)
if err != nil {
return "", fmt.Errorf("failed to read config for backup: %w", err)
return "", kairoerrors.WrapError(kairoerrors.ConfigError,
"failed to read config for backup", err)
}

// Create backup filename with timestamp
backupPath := getBackupPath(configDir)

// Write the backup
if err := os.WriteFile(backupPath, data, 0600); err != nil {
return "", fmt.Errorf("failed to write backup file: %w", err)
return "", kairoerrors.WrapError(kairoerrors.FileSystemError,
"failed to write backup file", err)
}

return backupPath, nil
Expand All @@ -36,19 +40,22 @@ func createConfigBackup(configDir string) (string, error) {
func rollbackConfig(configDir, backupPath string) error {
// Verify backup exists
if _, err := os.Stat(backupPath); os.IsNotExist(err) {
return fmt.Errorf("backup file not found: %s", backupPath)
return kairoerrors.NewError(kairoerrors.ConfigError,
fmt.Sprintf("backup file not found: %s", backupPath))
}

// Read backup data
data, err := os.ReadFile(backupPath)
if err != nil {
return fmt.Errorf("failed to read backup file: %w", err)
return kairoerrors.WrapError(kairoerrors.ConfigError,
"failed to read backup file", err)
}

// Write to config file
configPath := getConfigPath(configDir)
if err := os.WriteFile(configPath, data, 0600); err != nil {
return fmt.Errorf("failed to restore config from backup: %w", err)
return kairoerrors.WrapError(kairoerrors.ConfigError,
"failed to restore config from backup", err)
}

return nil
Expand Down Expand Up @@ -79,7 +86,8 @@ func withConfigTransaction(configDir string, fn func(txDir string) error) error
// Create backup before transaction
backupPath, err := createConfigBackup(configDir)
if err != nil {
return fmt.Errorf("failed to create transaction backup: %w", err)
return kairoerrors.WrapError(kairoerrors.ConfigError,
"failed to create transaction backup", err)
}

// Execute the transaction function
Expand All @@ -89,9 +97,11 @@ func withConfigTransaction(configDir string, fn func(txDir string) error) error
if err != nil {
if rbErr := rollbackConfig(configDir, backupPath); rbErr != nil {
// Rollback failed - this is a critical situation
return fmt.Errorf("transaction failed and rollback also failed: tx_err=%w, rollback_err=%w", err, rbErr)
return kairoerrors.WrapError(kairoerrors.ConfigError,
fmt.Sprintf("transaction failed and rollback also failed: tx_err=%v, rollback_err=%v", err, rbErr), rbErr)
}
return fmt.Errorf("transaction failed, changes rolled back: %w", err)
return kairoerrors.WrapError(kairoerrors.ConfigError,
"transaction failed, changes rolled back", err)
}

// Transaction succeeded - clean up the backup file
Expand Down
Loading
Loading