vagrant-orbstack-provider 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'vagrant-orbstack'
4
+
5
+ module VagrantPlugins
6
+ module OrbStack
7
+ # Configuration class for OrbStack provider.
8
+ #
9
+ # This class defines configuration options available in Vagrantfiles
10
+ # for customizing OrbStack machine creation and behavior.
11
+ #
12
+ # @example Basic configuration
13
+ # Vagrant.configure("2") do |config|
14
+ # config.vm.provider :orbstack do |os|
15
+ # os.distro = "ubuntu"
16
+ # os.version = "22.04"
17
+ # os.machine_name = "my-dev-env"
18
+ # end
19
+ # end
20
+ #
21
+ # @api public
22
+ class Config < Vagrant.plugin('2', :config)
23
+ # @!attribute [rw] distro
24
+ # Linux distribution to use for the machine
25
+ # @return [String, nil] Distribution name (e.g., "ubuntu", "debian")
26
+ # @api public
27
+ #
28
+ # @!attribute [rw] version
29
+ # Distribution version to use
30
+ # @return [String, nil] Version string (e.g., "22.04")
31
+ # @api public
32
+ #
33
+ # @!attribute [rw] machine_name
34
+ # Custom name for the OrbStack machine
35
+ # @return [String, nil] Machine name
36
+ # @api public
37
+ #
38
+ # @!attribute [rw] ssh_username
39
+ # Custom SSH username for connecting to the machine
40
+ # @return [String, nil] SSH username (defaults to OrbStack's default if nil)
41
+ # @api public
42
+ #
43
+ # @!attribute [rw] forward_agent
44
+ # Enable SSH agent forwarding
45
+ # @return [Boolean, nil] Whether to forward SSH agent (defaults to false)
46
+ # @api public
47
+ attr_accessor :distro
48
+ attr_accessor :version, :machine_name, :ssh_username, :forward_agent
49
+
50
+ # Error message constants for validation
51
+ DISTRO_EMPTY_ERROR = 'distro cannot be empty'
52
+ MACHINE_NAME_FORMAT_ERROR = 'machine_name must contain only alphanumeric characters and hyphens'
53
+ SSH_USERNAME_EMPTY_ERROR = 'ssh_username cannot be empty'
54
+ FORWARD_AGENT_BOOLEAN_ERROR = 'forward_agent must be a boolean (true or false)'
55
+
56
+ # Regular expression pattern for valid machine_name format.
57
+ #
58
+ # Valid machine names must:
59
+ # - Start with an alphanumeric character (a-z, A-Z, 0-9)
60
+ # - End with an alphanumeric character
61
+ # - May contain hyphens between alphanumeric segments
62
+ # - No consecutive hyphens allowed
63
+ MACHINE_NAME_PATTERN = /^[a-zA-Z0-9]+(-[a-zA-Z0-9]+)*$/
64
+
65
+ # Initialize configuration with unset values.
66
+ #
67
+ # @api public
68
+ def initialize
69
+ super
70
+ @distro = VagrantPlugins::OrbStack::UNSET_VALUE
71
+ @version = VagrantPlugins::OrbStack::UNSET_VALUE
72
+ @machine_name = VagrantPlugins::OrbStack::UNSET_VALUE
73
+ @ssh_username = VagrantPlugins::OrbStack::UNSET_VALUE
74
+ @forward_agent = VagrantPlugins::OrbStack::UNSET_VALUE
75
+ @logger = Log4r::Logger.new('vagrant_orbstack::config')
76
+ end
77
+
78
+ # Finalize configuration by setting defaults.
79
+ #
80
+ # Called by Vagrant after Vagrantfile is loaded to set default values
81
+ # for any unset configuration options.
82
+ #
83
+ # @api public
84
+ def finalize!
85
+ @distro = 'ubuntu' if @distro == VagrantPlugins::OrbStack::UNSET_VALUE
86
+ @version = nil if @version == VagrantPlugins::OrbStack::UNSET_VALUE
87
+ @machine_name = nil if @machine_name == VagrantPlugins::OrbStack::UNSET_VALUE
88
+ @ssh_username = nil if @ssh_username == VagrantPlugins::OrbStack::UNSET_VALUE
89
+ @forward_agent = false if @forward_agent == VagrantPlugins::OrbStack::UNSET_VALUE
90
+ end
91
+
92
+ # Validate configuration values.
93
+ #
94
+ # @param [Vagrant::Machine] _machine The machine to validate for
95
+ # @return [Hash<String, Array<String>>] Validation errors by namespace
96
+ # @api public
97
+ def validate(_machine)
98
+ errors = _detected_errors
99
+ validate_distro(errors)
100
+ validate_machine_name(errors)
101
+ validate_ssh_username(errors)
102
+ validate_forward_agent(errors)
103
+ { 'OrbStack Provider' => errors }
104
+ end
105
+
106
+ private
107
+
108
+ # Validation methods use defensive type coercion (.to_s) to prevent
109
+ # NoMethodError crashes if attributes are accidentally set to non-String types.
110
+ # This approach prioritizes robustness over strict type enforcement.
111
+ #
112
+ # Pattern: Always check nil first, then coerce to string for validation.
113
+ #
114
+ # Example:
115
+ # return if @attribute.nil?
116
+ # errors << ERROR_CONSTANT if @attribute.to_s.strip.empty?
117
+
118
+ # Validate that distro attribute is not empty.
119
+ #
120
+ # @param errors [Array<String>] Error accumulator array
121
+ # @return [void]
122
+ def validate_distro(errors)
123
+ errors << DISTRO_EMPTY_ERROR if @distro.nil? || @distro.to_s.strip.empty?
124
+ end
125
+
126
+ # Validate machine_name format if set.
127
+ #
128
+ # @param errors [Array<String>] Error accumulator array
129
+ # @return [void]
130
+ def validate_machine_name(errors)
131
+ return if @machine_name.nil?
132
+
133
+ # Convert to string for regex matching (defensive programming)
134
+ machine_name_str = @machine_name.to_s
135
+ errors << MACHINE_NAME_FORMAT_ERROR unless machine_name_str.match?(MACHINE_NAME_PATTERN)
136
+ end
137
+
138
+ # Validate that ssh_username attribute is not empty if set.
139
+ #
140
+ # @param errors [Array<String>] Error accumulator array
141
+ # @return [void]
142
+ def validate_ssh_username(errors)
143
+ return if @ssh_username.nil?
144
+ return unless @ssh_username.to_s.strip.empty?
145
+
146
+ errors << SSH_USERNAME_EMPTY_ERROR
147
+ end
148
+
149
+ # Validate that forward_agent is a boolean if set.
150
+ #
151
+ # @param errors [Array<String>] Error accumulator array
152
+ # @return [void]
153
+ def validate_forward_agent(errors)
154
+ return if @forward_agent.nil?
155
+ return if [true, false].include?(@forward_agent)
156
+
157
+ errors << FORWARD_AGENT_BOOLEAN_ERROR
158
+ end
159
+ end
160
+ end
161
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VagrantPlugins
4
+ module OrbStack
5
+ # Base error class for OrbStack provider errors.
6
+ # Inherits from Vagrant's VagrantError to integrate with Vagrant's error handling.
7
+ class Errors < Vagrant::Errors::VagrantError
8
+ error_namespace('vagrant_orbstack.errors')
9
+ end
10
+
11
+ # Error raised when OrbStack is not installed.
12
+ class OrbStackNotInstalled < Errors
13
+ error_key(:orbstack_not_installed)
14
+ end
15
+
16
+ # Error raised when OrbStack is not running.
17
+ class OrbStackNotRunning < Errors
18
+ error_key(:orbstack_not_running)
19
+ end
20
+
21
+ # Error raised when an OrbStack CLI command fails.
22
+ class CommandExecutionError < Errors
23
+ error_key(:command_execution_error)
24
+ end
25
+
26
+ # Error raised when an OrbStack CLI command times out.
27
+ class CommandTimeoutError < Errors
28
+ error_key(:command_timeout_error)
29
+ end
30
+
31
+ # Error raised when machine name collision cannot be resolved after retries.
32
+ class MachineNameCollisionError < Errors
33
+ error_key(:machine_name_collision)
34
+ end
35
+
36
+ # Error raised when SSH is not ready on a machine.
37
+ class SSHNotReady < Errors
38
+ error_key(:ssh_not_ready)
39
+ end
40
+
41
+ # Error raised when SSH connection fails.
42
+ class SSHConnectionFailed < Errors
43
+ error_key(:ssh_connection_failed)
44
+ end
45
+
46
+ # Reopen Errors class to add nested constants for namespaced access
47
+ # This allows both VagrantPlugins::OrbStack::CommandTimeoutError
48
+ # and VagrantPlugins::OrbStack::Errors::CommandTimeoutError to work
49
+ class Errors
50
+ # Alias error classes inside Errors namespace for tests that expect them there
51
+ OrbStackNotInstalled = ::VagrantPlugins::OrbStack::OrbStackNotInstalled
52
+ OrbStackNotInstalledError = OrbStackNotInstalled
53
+ OrbStackNotRunning = ::VagrantPlugins::OrbStack::OrbStackNotRunning
54
+ CommandExecutionError = ::VagrantPlugins::OrbStack::CommandExecutionError
55
+ CommandTimeoutError = ::VagrantPlugins::OrbStack::CommandTimeoutError
56
+ MachineNameCollisionError = ::VagrantPlugins::OrbStack::MachineNameCollisionError
57
+ SSHNotReady = ::VagrantPlugins::OrbStack::SSHNotReady
58
+ SSHConnectionFailed = ::VagrantPlugins::OrbStack::SSHConnectionFailed
59
+
60
+ # Alias for CLI errors
61
+ OrbStackCLIError = CommandExecutionError
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'vagrant-orbstack/version'
4
+
5
+ module VagrantPlugins
6
+ # OrbStack provider plugin namespace
7
+ module OrbStack
8
+ # OrbStack provider plugin for Vagrant.
9
+ #
10
+ # This plugin enables OrbStack as a provider backend for Vagrant,
11
+ # allowing users to create and manage Linux development environments
12
+ # on macOS using OrbStack's high-performance virtualization.
13
+ #
14
+ # @api public
15
+ class Plugin < Vagrant.plugin('2')
16
+ name 'vagrant-orbstack-provider'
17
+ description 'Enables OrbStack as a Vagrant provider for macOS development'
18
+
19
+ # Register OrbStack provider with Vagrant.
20
+ #
21
+ # @api private
22
+ provider(:orbstack, priority: 5) do
23
+ require_relative 'provider'
24
+ Provider
25
+ end
26
+
27
+ # Register configuration class for OrbStack provider.
28
+ #
29
+ # @api private
30
+ config(:orbstack, :provider) do
31
+ require_relative 'config'
32
+ Config
33
+ end
34
+
35
+ # Setup I18n locale files for error messages
36
+ def self.setup!
37
+ locale_path = Pathname.new(File.expand_path('../locales', __dir__))
38
+ I18n.load_path << locale_path.join('en.yml') if locale_path.exist?
39
+ end
40
+ end
41
+
42
+ # Initialize I18n on plugin load
43
+ Plugin.setup!
44
+ end
45
+ end