state

package
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 2, 2025 License: MIT Imports: 10 Imported by: 0

README

State Package

Package state provides state management, file tracking, manifest handling, cleanup operations, and versioning capabilities for weft. It offers configurable tracking modes, orphan file detection, automatic cleanup, manifest versioning, and migration support for maintaining consistent output state across template generations.

Architecture

The state package is organized into four main components:

  • File Tracking (tracking.go): State tracking for generated files with change detection
  • Manifest Management (manifest.go): File manifest creation, loading, and validation
  • Cleanup Operations (cleanup.go): Orphan file detection and cleanup with multiple modes
  • Version Management (version.go): Manifest versioning, migration, and backup handling

Basic Usage

Create State Tracker and Enable File Tracking
tracker := state.NewStateTracker("/path/to/output", state.TrackingModeEnabled)
Track Generated Files
metadata := map[string]string{"author": "weft", "template_version": "1.0"}
err := tracker.TrackFile("output/file.go", "templates/file.go.tmpl", metadata)

Manifest Management

The package provides comprehensive manifest operations for tracking generated files:

// Create manifest manager
manifestManager := state.NewManifestManager("/path/to/output")

// Load existing manifest
manifest, err := manifestManager.LoadManifest()

// Add entries to manifest
err = manifestManager.AddEntry(manifest, "output/file.go", "templates/file.go.tmpl", metadata)

// Save manifest
err = manifestManager.SaveManifest(manifest)
Register File Tracking
tracker := state.NewStateTracker("/output", state.TrackingModeEnabled)
err := tracker.TrackFile("generated/config.go", "templates/config.go.tmpl", nil)

Configuration

The package supports various tracking modes and cleanup options:

// Tracking modes
tracker := state.NewStateTracker("/output", state.TrackingModeStrict)

// Cleanup configuration
cleanupManager := state.NewCleanupManager(tracker,
    state.WithCleanupMode(state.CleanupModeInteractive),
    state.WithBackupDirectory("/backups"),
    state.WithIgnorePatterns([]string{"*.tmp", "*.log"}),
)

File State Tracking

Monitor and analyze file states across generations:

// Check individual file state
fileState, err := tracker.GetFileState("output/config.go")
switch fileState {
case state.FileStateGenerated:
    fmt.Println("File is up-to-date")
case state.FileStateModified:
    fmt.Println("File has been modified since generation")
case state.FileStateOrphan:
    fmt.Println("File exists but is not tracked")
}
File State Detection
  • Generated: File matches manifest entry (hash, size, modtime)
  • Modified: File differs from manifest entry
  • Deleted: File in manifest but missing from filesystem
  • Orphan: File exists but not in manifest
  • Unknown: File state cannot be determined

Tracking Modes

The package supports multiple tracking modes:

Mode Description
TrackingModeDisabled No file tracking performed
TrackingModeEnabled Standard file tracking and manifest management
TrackingModeStrict Enhanced tracking with strict validation
Setting Tracking Modes
tracker := state.NewStateTracker("/output", state.TrackingModeEnabled)

// Check if file is tracked
isTracked, err := tracker.IsFileTracked("output/config.go")

// Get all tracked files
trackedFiles, err := tracker.GetTrackedFiles()

Cleanup Operations

Available Cleanup Modes
Mode Description Example
CleanupModeAuto Automatically delete orphan files state.CleanupModeAuto
CleanupModeInteractive Prompt user for each orphan state.CleanupModeInteractive
CleanupModeReport Generate report without cleanup state.CleanupModeReport
CleanupModeDisabled Disable cleanup operations state.CleanupModeDisabled
Usage Examples
// Find orphan files
orphans, err := cleanupManager.FindOrphans()
for _, orphan := range orphans {
    fmt.Printf("Orphan: %s (%d bytes)\n", orphan.Path, orphan.Size)
}

// Perform cleanup
summary, err := cleanupManager.CleanupOrphans()
cleanupManager.PrintSummary(summary)

// Get report without cleanup
report, err := cleanupManager.GetOrphanReport()
fmt.Println(report)

Manifest Structure

Manifest Entry Information
// Access manifest entries
entries := manifestManager.ListEntries(manifest)
for _, entry := range entries {
    fmt.Printf("File: %s\n", entry.Path)
    fmt.Printf("  Hash: %s\n", entry.Hash)
    fmt.Printf("  Size: %d bytes\n", entry.Size)
    fmt.Printf("  Template: %s\n", entry.TemplatePath)
    fmt.Printf("  Generated: %s\n", entry.ModTime.Format("2006-01-02 15:04:05"))
}
Change Detection
// Check if file has changed
hasChanged, err := manifestManager.HasChanged(manifest, "output/config.go")
if hasChanged {
    fmt.Println("File needs regeneration")
}

// Get specific files by state
modifiedFiles, err := tracker.GetModifiedFiles()
deletedFiles, err := tracker.GetDeletedFiles()
orphanFiles, err := tracker.GetOrphanedFiles()

Version Management

Version Operations
versionManager := state.NewVersionManager("/output", "/backups")

// Get version information
versionInfo, err := versionManager.GetVersionInfo()
fmt.Printf("Version: %s\n", versionInfo.Version)
fmt.Printf("Created: %s\n", versionInfo.CreatedAt.Format("2006-01-02 15:04:05"))
fmt.Printf("Generator: %s\n", versionInfo.Generator)
Migration Support
// Check if migration is needed
if versionManager.RequiresMigration(manifest) {
    result, err := versionManager.MigrateManifest(manifest)
    if result.Success {
        fmt.Printf("Migrated from %s to %s\n", result.FromVersion, result.ToVersion)
        if result.BackupPath != "" {
            fmt.Printf("Backup created: %s\n", result.BackupPath)
        }
    }
}
Backup Management
// List available backups
backups, err := versionManager.ListBackups()
for _, backup := range backups {
    fmt.Printf("Backup: %s\n", backup)
}

// Restore from backup
err = versionManager.RestoreBackup("manifest-20240101-120000.json")

// Clean up old backups (keep 5 most recent)
err = versionManager.CleanupOldBackups(5)

Performance Considerations

Optimization Tips
  1. Selective Tracking: Use TrackingModeDisabled when state management isn't needed
  2. Efficient Cleanup: Use CleanupModeReport for analysis without filesystem operations
  3. Batch Operations: Process multiple files in single manifest operations
  4. Hash Caching: File hashes are calculated only when needed for change detection
Performance Settings
// Lightweight tracking for performance-critical scenarios
tracker := state.NewStateTracker("/output", state.TrackingModeEnabled)

// Efficient cleanup with ignore patterns
cleanupManager := state.NewCleanupManager(tracker,
    state.WithCleanupMode(state.CleanupModeReport),
    state.WithIgnorePatterns([]string{"*.tmp", "*.log", "node_modules/*"}),
)
Memory Management
// Refresh manifest to update file states
err := tracker.RefreshManifest()

// Selective file tracking
isTracked, err := tracker.IsFileTracked("specific/file.go")
if !isTracked {
    err = tracker.TrackFile("specific/file.go", "template.go.tmpl", nil)
}

Security Notes

File Path Protection
  • All file paths are validated and sanitized before operations
  • Relative path traversal attempts are prevented
  • Backup operations create secure temporary files
  • Manifest files are written atomically to prevent corruption
Security Best Practices
  1. Validate Output Paths before tracking files
  2. Secure Backup Locations outside public directories
  3. Regular Cleanup of sensitive orphan files
  4. Manifest Integrity validation before migrations
Path Validation
// Safe file operations with automatic path validation
// Paths are automatically resolved and validated:
// - No directory traversal (../) attempts
// - Absolute paths within output root only
// - Proper file permissions on created directories

Thread Safety

All public functions and types in this package are thread-safe and can be used concurrently from multiple goroutines.

Concurrent Usage Examples
// Safe concurrent state tracking
tracker := state.NewStateTracker("/output", state.TrackingModeEnabled)

go func() {
    tracker.TrackFile("file1.go", "template1.go.tmpl", nil)
}()

go func() {
    tracker.TrackFile("file2.go", "template2.go.tmpl", nil)
}()

// Concurrent cleanup operations
go func() {
    summary, _ := cleanupManager.CleanupOrphans()
    cleanupManager.PrintSummary(summary)
}()

Advanced Features

Custom Cleanup Logic
cleanupManager := state.NewCleanupManager(tracker,
    state.WithCleanupMode(state.CleanupModeInteractive),
    state.WithBackupDirectory("/secure/backups"),
    state.WithIgnorePatterns([]string{
        "*.log",           // Ignore log files
        "tmp/*",           // Ignore temp directory
        "*.backup",        // Ignore backup files
        "node_modules/*",  // Ignore dependencies
    }),
)

// Generate detailed orphan report
report, err := cleanupManager.GetOrphanReport()
fmt.Println(report)
Manifest Validation
versionManager := state.NewVersionManager("/output", "/backups")

// Validate manifest structure
if err := versionManager.ValidateManifest(manifest); err != nil {
    fmt.Printf("Manifest validation failed: %v\n", err)
}

// Version comparison
comparison := state.CompareVersions("1.0", "1.1")
switch comparison {
case -1:
    fmt.Println("Version 1.0 is older than 1.1")
case 0:
    fmt.Println("Versions are equal")
case 1:
    fmt.Println("Version 1.0 is newer than 1.1")
}
Error Recovery
// Implement error recovery patterns
func processWithStateTracking() error {
    tracker := state.NewStateTracker("/output", state.TrackingModeEnabled)
    
    // Track file generation
    if err := tracker.TrackFile("output.go", "template.go.tmpl", nil); err != nil {
        // Handle tracking errors gracefully
        fmt.Printf("Warning: Could not track file: %v\n", err)
        // Continue processing...
    }
    
    return nil
}

Integration Examples

With Template Generators
func generateTemplate(templatePath, outputPath string, data interface{}) error {
    tracker := state.NewStateTracker("/output", state.TrackingModeEnabled)
    
    // Generate template content
    content, err := executeTemplate(templatePath, data)
    if err != nil {
        return err
    }
    
    // Write output file
    if err := writeFile(outputPath, content); err != nil {
        return err
    }
    
    // Track generated file
    metadata := map[string]string{
        "generated_at": time.Now().Format(time.RFC3339),
        "template_version": "1.0",
    }
    
    return tracker.TrackFile(outputPath, templatePath, metadata)
}
With Build Systems
func buildPhaseCleanup() error {
    tracker := state.NewStateTracker("./dist", state.TrackingModeEnabled)
    
    cleanupManager := state.NewCleanupManager(tracker,
        state.WithCleanupMode(state.CleanupModeAuto),
        state.WithIgnorePatterns([]string{"*.log"}),
    )
    
    // Clean up orphan files before build
    summary, err := cleanupManager.CleanupOrphans()
    if err != nil {
        return fmt.Errorf("cleanup failed: %w", err)
    }
    
    fmt.Printf("Cleanup: %d files deleted, %d bytes freed\n", 
        summary.FilesDeleted, summary.TotalSizeFreed)
    
    return nil
}
With CI/CD Pipelines
func ciPostProcess() error {
    versionManager := state.NewVersionManager("./output", "./backups")
    
    // Get version information for reporting
    versionInfo, err := versionManager.GetVersionInfo()
    if err != nil {
        return err
    }
    
    fmt.Printf("Generated files version: %s\n", versionInfo.Version)
    fmt.Printf("Generation time: %s\n", versionInfo.CreatedAt.Format(time.RFC3339))
    
    // Clean up old backups in CI environment
    return versionManager.CleanupOldBackups(3)
}

Documentation

Overview

Package state provides a manifest for tracking the state of generated files.

Index

Constants

View Source
const (
	CurrentManifestVersion = "1.0"
	ManifestVersionKey     = "version"
)

Variables

This section is empty.

Functions

func CompareVersions

func CompareVersions(v1, v2 string) int

Types

type CleanupAction

type CleanupAction int
const (
	CleanupActionDelete CleanupAction = iota
	CleanupActionSkip
	CleanupActionBackup
)

func (CleanupAction) String

func (ca CleanupAction) String() string

type CleanupManager

type CleanupManager struct {
	// contains filtered or unexported fields
}

func NewCleanupManager

func NewCleanupManager(stateTracker *StateTracker, opts ...CleanupOption) *CleanupManager

func (*CleanupManager) CleanupOrphans

func (cm *CleanupManager) CleanupOrphans() (*CleanupSummary, error)

func (*CleanupManager) FindOrphans

func (cm *CleanupManager) FindOrphans() ([]OrphanFile, error)

func (*CleanupManager) GetOrphanReport

func (cm *CleanupManager) GetOrphanReport() (string, error)

func (*CleanupManager) PrintSummary

func (cm *CleanupManager) PrintSummary(summary *CleanupSummary)

type CleanupMode

type CleanupMode int
const (
	CleanupModeAuto CleanupMode = iota
	CleanupModeInteractive
	CleanupModeReport
	CleanupModeDisabled
)

func (CleanupMode) String

func (cm CleanupMode) String() string

type CleanupOption

type CleanupOption func(*CleanupManager)

func WithBackupDirectory

func WithBackupDirectory(dir string) CleanupOption

func WithCleanupMode

func WithCleanupMode(mode CleanupMode) CleanupOption

func WithIgnorePatterns

func WithIgnorePatterns(patterns []string) CleanupOption

type CleanupResult

type CleanupResult struct {
	Action     CleanupAction `json:"action"`
	File       OrphanFile    `json:"file"`
	Success    bool          `json:"success"`
	Error      string        `json:"error,omitempty"`
	BackupPath string        `json:"backup_path,omitempty"`
}

type CleanupSummary

type CleanupSummary struct {
	Mode           CleanupMode     `json:"mode"`
	OrphansFound   int             `json:"orphans_found"`
	FilesDeleted   int             `json:"files_deleted"`
	FilesSkipped   int             `json:"files_skipped"`
	FilesBackedUp  int             `json:"files_backed_up"`
	Errors         int             `json:"errors"`
	Results        []CleanupResult `json:"results"`
	TotalSizeFreed int64           `json:"total_size_freed"`
	ExecutionTime  time.Duration   `json:"execution_time"`
}

type FileState

type FileState int
const (
	FileStateUnknown FileState = iota
	FileStateGenerated
	FileStateModified
	FileStateDeleted
	FileStateOrphan
)

func (FileState) String

func (fs FileState) String() string

type Manifest

type Manifest struct {
	Version    string                   `json:"version"`
	Generated  time.Time                `json:"generated"`
	Generator  string                   `json:"generator"`
	OutputRoot string                   `json:"output_root"`
	Entries    map[string]ManifestEntry `json:"entries"`
	Metadata   map[string]string        `json:"metadata,omitempty"`
}

type ManifestEntry

type ManifestEntry struct {
	Path         string            `json:"path"`
	Hash         string            `json:"hash"`
	Size         int64             `json:"size"`
	ModTime      time.Time         `json:"mod_time"`
	GeneratedBy  string            `json:"generated_by"`
	TemplatePath string            `json:"template_path"`
	Metadata     map[string]string `json:"metadata,omitempty"`
}

type ManifestManager

type ManifestManager struct {
	// contains filtered or unexported fields
}

func NewManifestManager

func NewManifestManager(outputRoot string) *ManifestManager

func (*ManifestManager) AddEntry

func (mm *ManifestManager) AddEntry(manifest *Manifest, path, templatePath string, metadata map[string]string) error

func (*ManifestManager) GetEntry

func (mm *ManifestManager) GetEntry(manifest *Manifest, path string) (ManifestEntry, bool)

func (*ManifestManager) HasChanged

func (mm *ManifestManager) HasChanged(manifest *Manifest, path string) (bool, error)

func (*ManifestManager) ListEntries

func (mm *ManifestManager) ListEntries(manifest *Manifest) []ManifestEntry

func (*ManifestManager) LoadManifest

func (mm *ManifestManager) LoadManifest() (*Manifest, error)

func (*ManifestManager) RemoveEntry

func (mm *ManifestManager) RemoveEntry(manifest *Manifest, path string)

func (*ManifestManager) SaveManifest

func (mm *ManifestManager) SaveManifest(manifest *Manifest) error

type MigrationResult

type MigrationResult struct {
	FromVersion string `json:"from_version"`
	ToVersion   string `json:"to_version"`
	Success     bool   `json:"success"`
	Error       string `json:"error,omitempty"`
	BackupPath  string `json:"backup_path,omitempty"`
}

type OrphanFile

type OrphanFile struct {
	Path        string    `json:"path"`
	Size        int64     `json:"size"`
	ModTime     time.Time `json:"mod_time"`
	IsDirectory bool      `json:"is_directory"`
	Reason      string    `json:"reason"`
}

type StateTracker

type StateTracker struct {
	// contains filtered or unexported fields
}

func NewStateTracker

func NewStateTracker(outputRoot string, mode TrackingMode) *StateTracker

func (*StateTracker) GetDeletedFiles

func (st *StateTracker) GetDeletedFiles() ([]string, error)

func (*StateTracker) GetFileState

func (st *StateTracker) GetFileState(path string) (FileState, error)

func (*StateTracker) GetModifiedFiles

func (st *StateTracker) GetModifiedFiles() ([]string, error)

func (*StateTracker) GetOrphanedFiles

func (st *StateTracker) GetOrphanedFiles() ([]string, error)

func (*StateTracker) GetTrackedFiles

func (st *StateTracker) GetTrackedFiles() ([]TrackedFile, error)

func (*StateTracker) IsFileTracked

func (st *StateTracker) IsFileTracked(path string) (bool, error)

func (*StateTracker) RefreshManifest

func (st *StateTracker) RefreshManifest() error

func (*StateTracker) TrackFile

func (st *StateTracker) TrackFile(path, templatePath string, metadata map[string]string) error

func (*StateTracker) UntrackFile

func (st *StateTracker) UntrackFile(path string) error

type TrackedFile

type TrackedFile struct {
	Path         string            `json:"path"`
	State        FileState         `json:"state"`
	LastSeen     time.Time         `json:"last_seen"`
	TemplatePath string            `json:"template_path"`
	Hash         string            `json:"hash"`
	Size         int64             `json:"size"`
	Metadata     map[string]string `json:"metadata,omitempty"`
}

type TrackingMode

type TrackingMode int
const (
	TrackingModeDisabled TrackingMode = iota
	TrackingModeEnabled
	TrackingModeStrict
)

type VersionInfo

type VersionInfo struct {
	Version     string            `json:"version"`
	CreatedAt   time.Time         `json:"created_at"`
	Generator   string            `json:"generator"`
	Description string            `json:"description,omitempty"`
	Metadata    map[string]string `json:"metadata,omitempty"`
}

type VersionManager

type VersionManager struct {
	// contains filtered or unexported fields
}

func NewVersionManager

func NewVersionManager(outputRoot, backupDir string) *VersionManager

func (*VersionManager) CleanupOldBackups

func (vm *VersionManager) CleanupOldBackups(keepCount int) error

func (*VersionManager) GetManifestVersion

func (vm *VersionManager) GetManifestVersion(manifest *Manifest) string

func (*VersionManager) GetVersionInfo

func (vm *VersionManager) GetVersionInfo() (*VersionInfo, error)

func (*VersionManager) IsVersionSupported

func (vm *VersionManager) IsVersionSupported(version string) bool

func (*VersionManager) ListBackups

func (vm *VersionManager) ListBackups() ([]string, error)

func (*VersionManager) MigrateManifest

func (vm *VersionManager) MigrateManifest(manifest *Manifest) (*MigrationResult, error)

func (*VersionManager) RequiresMigration

func (vm *VersionManager) RequiresMigration(manifest *Manifest) bool

func (*VersionManager) RestoreBackup

func (vm *VersionManager) RestoreBackup(backupName string) error

func (*VersionManager) ValidateManifest

func (vm *VersionManager) ValidateManifest(manifest *Manifest) error

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL