Class: Sxn::Config::ConfigValidator

Inherits:
Object
  • Object
show all
Defined in:
lib/sxn/config/config_validator.rb

Overview

Validates configuration structure and values

Features:

  • Schema validation for configuration structure

  • Value validation with helpful error messages

  • Support for configuration migrations from older versions

  • Type checking and constraint validation

Constant Summary collapse

CURRENT_VERSION =

Current schema version

1
SCHEMA =

Configuration schema definition

{
  "version" => {
    type: :integer,
    required: true,
    min: 1,
    max: CURRENT_VERSION
  },
  "sessions_folder" => {
    type: :string,
    required: true,
    min_length: 1
  },
  "current_session" => {
    type: :string,
    required: false
  },
  "projects" => {
    type: :hash,
    required: true,
    default: {},
    value_schema: {
      "path" => {
        type: :string,
        required: true,
        min_length: 1
      },
      "type" => {
        type: :string,
        required: false,
        allowed_values: %w[rails ruby javascript typescript react nextjs vue angular unknown]
      },
      "default_branch" => {
        type: :string,
        required: false,
        default: "main"
      },
      "package_manager" => {
        type: :string,
        required: false,
        allowed_values: %w[npm yarn pnpm]
      },
      "rules" => {
        type: :hash,
        required: false,
        default: {},
        value_schema: {
          "copy_files" => {
            type: :array,
            required: false,
            item_schema: {
              "source" => {
                type: :string,
                required: true,
                min_length: 1
              },
              "strategy" => {
                type: :string,
                required: false,
                default: "copy",
                allowed_values: %w[copy symlink]
              },
              "permissions" => {
                type: :integer,
                required: false,
                min: 0,
                max: 0o777
              },
              "encrypt" => {
                type: :boolean,
                required: false,
                default: false
              }
            }
          },
          "setup_commands" => {
            type: :array,
            required: false,
            item_schema: {
              "command" => {
                type: :array,
                required: true,
                min_length: 1,
                item_type: :string
              },
              "environment" => {
                type: :hash,
                required: false,
                value_type: :string
              },
              "condition" => {
                type: :string,
                required: false,
                allowed_values: %w[always db_not_exists file_not_exists]
              }
            }
          },
          "templates" => {
            type: :array,
            required: false,
            item_schema: {
              "source" => {
                type: :string,
                required: true,
                min_length: 1
              },
              "destination" => {
                type: :string,
                required: true,
                min_length: 1
              },
              "process" => {
                type: :boolean,
                required: false,
                default: true
              },
              "engine" => {
                type: :string,
                required: false,
                default: "liquid",
                allowed_values: %w[liquid erb mustache]
              }
            }
          }
        }
      }
    }
  },
  "settings" => {
    type: :hash,
    required: false,
    default: {},
    value_schema: {
      "auto_cleanup" => {
        type: :boolean,
        required: false,
        default: true
      },
      "max_sessions" => {
        type: :integer,
        required: false,
        default: 10,
        min: 1,
        max: 100
      },
      "worktree_cleanup_days" => {
        type: :integer,
        required: false,
        default: 30,
        min: 1,
        max: 365
      },
      "default_rules" => {
        type: :hash,
        required: false,
        default: {},
        value_schema: {
          "templates" => {
            type: :array,
            required: false,
            item_schema: {
              "source" => {
                type: :string,
                required: true,
                min_length: 1
              },
              "destination" => {
                type: :string,
                required: true,
                min_length: 1
              }
            }
          }
        }
      }
    }
  }
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeConfigValidator

Returns a new instance of ConfigValidator.



200
201
202
# File 'lib/sxn/config/config_validator.rb', line 200

def initialize
  @errors = []
end

Instance Attribute Details

#errorsObject (readonly)

Returns the value of attribute errors.



198
199
200
# File 'lib/sxn/config/config_validator.rb', line 198

def errors
  @errors
end

Instance Method Details

#format_errorsString

Get formatted error messages

Returns:

  • (String)

    Formatted error messages



244
245
246
247
248
249
250
# File 'lib/sxn/config/config_validator.rb', line 244

def format_errors
  return "No errors" if @errors.empty?

  @errors.map.with_index(1) do |error, index|
    "  #{index}. #{error}"
  end.join("\n")
end

#migrate_config(config) ⇒ Hash

Migrate configuration from older versions

Parameters:

  • config (Hash)

    Configuration to migrate

Returns:

  • (Hash)

    Migrated configuration



255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/sxn/config/config_validator.rb', line 255

def migrate_config(config)
  return config unless config.is_a?(Hash)

  version = config["version"] || 0
  migrated_config = config.dup

  # Check if this is a v0 config that got merged with system defaults (version = 1)
  # but still has v0 structure (projects without paths)
  # Handle invalid version types safely
  needs_v0_migration = (version.is_a?(Integer) && version.zero?) || needs_v0_to_v1_migration?(config)

  case version
  when 0
    # Migrate from unversioned to version 1
    migrated_config = migrate_v0_to_v1(migrated_config)
  when 1
    if needs_v0_migration
      # This is a v0 config that was merged with v1 defaults, migrate it
      migrated_config = migrate_v0_to_v1(migrated_config)
    end
  end

  migrated_config
end

#valid?(config) ⇒ Boolean

Validate configuration against schema

Parameters:

  • config (Hash)

    Configuration to validate

Returns:

  • (Boolean)

    True if valid



207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/sxn/config/config_validator.rb', line 207

def valid?(config)
  @errors = []

  unless config.is_a?(Hash)
    @errors << "Configuration must be a hash, got #{config.class}"
    return false
  end

  validate_against_schema(config, SCHEMA, "")

  @errors.empty?
end

#validate_and_migrate(config) ⇒ Hash

Validate and migrate configuration if needed

Parameters:

  • config (Hash)

    Configuration to validate and migrate

Returns:

  • (Hash)

    Validated and migrated configuration

Raises:



224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/sxn/config/config_validator.rb', line 224

def validate_and_migrate(config)
  # First, migrate if needed
  migrated_config = migrate_config(config)

  # Debug output
  # puts "DEBUG: Original config: #{config.inspect}"
  # puts "DEBUG: Migrated config: #{migrated_config.inspect}"

  # Then validate the migrated config
  unless valid?(migrated_config)
    error_message = format_errors
    raise ConfigurationError, "Configuration validation failed:\n#{error_message}"
  end

  # Apply defaults for missing values
  apply_defaults(migrated_config)
end