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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +175 -0
- data/LICENSE +21 -0
- data/README.md +628 -0
- data/lib/vagrant-orbstack/action/create.rb +177 -0
- data/lib/vagrant-orbstack/action/destroy.rb +90 -0
- data/lib/vagrant-orbstack/action/halt.rb +64 -0
- data/lib/vagrant-orbstack/action/machine_validation.rb +60 -0
- data/lib/vagrant-orbstack/action/reload.rb +56 -0
- data/lib/vagrant-orbstack/action/ssh_run.rb +70 -0
- data/lib/vagrant-orbstack/action/start.rb +68 -0
- data/lib/vagrant-orbstack/action.rb +21 -0
- data/lib/vagrant-orbstack/config.rb +161 -0
- data/lib/vagrant-orbstack/errors.rb +64 -0
- data/lib/vagrant-orbstack/plugin.rb +45 -0
- data/lib/vagrant-orbstack/provider.rb +460 -0
- data/lib/vagrant-orbstack/util/machine_namer.rb +130 -0
- data/lib/vagrant-orbstack/util/orbstack_cli.rb +283 -0
- data/lib/vagrant-orbstack/util/ssh_readiness_checker.rb +115 -0
- data/lib/vagrant-orbstack/util/state_cache.rb +106 -0
- data/lib/vagrant-orbstack/version.rb +7 -0
- data/lib/vagrant-orbstack-provider.rb +6 -0
- data/lib/vagrant-orbstack.rb +36 -0
- data/locales/en.yml +49 -0
- metadata +71 -0
|
@@ -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
|