vagrant-ssh-config-manager 1.0.0.alpha.1 → 1.0.0.alpha.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d83235d8c0ae0433ed36f8ff12786052223feafc0e3ea37b58b3be62f02957fb
4
- data.tar.gz: 703739e7578fa03d3829b1d02b710becd7cce27a7448374e725a68e4a29be93c
3
+ metadata.gz: 1c65d67becc3cd83b365456e37514f3b2c1509e7d089a42419cc3202434c0b5e
4
+ data.tar.gz: fd9ddd98df6e0a3a260387250b5e843b33150705738debbfe5bc9ac7c528e0c0
5
5
  SHA512:
6
- metadata.gz: ca97b780d0f5a297698103b41314c0d50de8701754dbab7b250bb4a2b4ec4edb9d062f597f63adebc56559123e01b815591869e0428c31848db10df3750a4c41
7
- data.tar.gz: d4fadfa9354e313484ace06afd4335c7d0cfe104da5d9ea3c14e956895210427f66e73dfaf14b11390388101334e3d18dbc8815ebfdcc7bf277f67e9cd0074eb
6
+ metadata.gz: a56070239d397f265090a6d75e8ff00caa2438a819e4959f6954dd0118835b33c7c0bfc285d06e2ff3a3ea98fac6c95103c4e895c33b4fc8aede5a905a84fdbd
7
+ data.tar.gz: 67f5f03db6beaeafc6e8aa7bbe3d66f267d08ef062d74d0f50f77324d0acd786cdb17bc3a5c4212a732dc25c5f4a940e7a0e036e229e49354e142ab0b57f704a
data/.gitignore CHANGED
@@ -1,6 +1,8 @@
1
1
  vendor
2
- .github/instructions
2
+ .github/*instructions*
3
3
  test_file
4
4
  vagrant-ssh-config-manager-*.gem
5
5
  Gemfile.lock
6
- .vagrant
6
+ .vagrant
7
+ .pi/plans
8
+ .pi/discussions
data/.rubocop.yml CHANGED
@@ -2,25 +2,43 @@
2
2
 
3
3
  AllCops:
4
4
  NewCops: enable
5
+ SuggestExtensions: false
6
+
7
+ Metrics/ClassLength:
8
+ Max: 300
5
9
 
6
- # Disable metrics and complexity cops for large methods/classes
7
- Metrics/BlockLength:
8
- Enabled: false
9
10
  Metrics/MethodLength:
10
- Enabled: false
11
+ Max: 40
12
+ Exclude:
13
+ - 'lib/vagrant_ssh_config_manager/action/*.rb'
14
+ - 'lib/vagrant_ssh_config_manager/ssh_info_extractor.rb'
15
+
11
16
  Metrics/AbcSize:
12
- Enabled: false
17
+ Max: 35
18
+ Exclude:
19
+ - 'lib/vagrant_ssh_config_manager/action/*.rb'
20
+ - 'lib/vagrant_ssh_config_manager/config.rb'
21
+ - 'lib/vagrant_ssh_config_manager/ssh_info_extractor.rb'
22
+ - 'spec/**/*'
23
+
24
+ Layout/LineLength:
25
+ Max: 120
26
+ Exclude:
27
+ - 'spec/**/*'
28
+ - 'vagrant-ssh-config-manager.gemspec'
29
+
30
+ Metrics/BlockLength:
31
+ Exclude:
32
+ - 'spec/**/*'
33
+
34
+ # Keep these complexity cops out of the v1 cleanup gate for now. The enabled
35
+ # MethodLength/ClassLength/AbcSize gates cover the largest maintainability risks.
13
36
  Metrics/CyclomaticComplexity:
14
37
  Enabled: false
15
38
  Metrics/PerceivedComplexity:
16
39
  Enabled: false
17
- Metrics/ClassLength:
18
- Enabled: false
19
40
  Metrics/BlockNesting:
20
41
  Enabled: false
21
- Layout/LineLength:
22
- Enabled: false
23
- Max: 120
24
42
 
25
43
  # Disable accessor naming cop for spec files (get_ prefix methods allowed in tests)
26
44
  Naming/AccessorMethodName:
data/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ [![Gem Version](https://badge.fury.io/rb/vagrant-ssh-config-manager.svg)](https://badge.fury.io/rb/vagrant-ssh-config-manager) [![Run Tests](https://github.com/marekruzicka/vagrant-ssh-config-manager/actions/workflows/test.yml/badge.svg)](https://github.com/marekruzicka/vagrant-ssh-config-manager/actions/workflows/test.yml)
2
+
1
3
  # Vagrant SSH Config Manager
2
4
 
3
5
  A Vagrant plugin that automatically manages SSH configuration entries for Vagrant-managed VMs, providing seamless SSH access without the need to remember ports, IPs, or key locations.
@@ -6,8 +8,8 @@ A Vagrant plugin that automatically manages SSH configuration entries for Vagran
6
8
 
7
9
  - **Automatic SSH Config Management**: Creates and maintains SSH config files when VMs are started
8
10
  - **Project Isolation**: Each Vagrant project gets its own SSH config namespace to avoid conflicts
9
- - **Separate File Management**: Creates individual SSH config files for each VM in a dedicated directory
10
- - **Include Directive Management**: Automatically manages Include directives in your main SSH config
11
+ - **Per-VM File Management**: Creates one SSH config file per VM under `~/.ssh/config.d/vagrant/`
12
+ - **Include Directive Management**: Optionally manages the `Include ~/.ssh/config.d/vagrant/*.conf` directive in your main SSH config
11
13
  - **Lifecycle Integration**: Hooks into all Vagrant VM lifecycle events (up, destroy, reload, halt, provision)
12
14
  - **Configurable Behavior**: Extensive configuration options for different workflows
13
15
  - **Robust Error Handling**: Graceful handling of file permission issues and concurrent access
@@ -97,9 +99,9 @@ end
97
99
 
98
100
  ## How It Works
99
101
 
100
- ### Separate File Architecture (Default)
102
+ ### Per-VM File Architecture
101
103
 
102
- The plugin creates individual SSH config files for each VM in a dedicated directory (default: `~/.ssh/config.d/vagrant/`). This approach:
104
+ v1 uses a per-VM SSH config architecture. The plugin creates one SSH config file for each VM in a dedicated directory (default: `~/.ssh/config.d/vagrant/`) and SSH reads those files through an `Include` directive. This approach:
103
105
 
104
106
  - **Prevents conflicts**: Each VM has its own config file
105
107
  - **Enables easier cleanup**: Destroying a VM removes only its config file
@@ -110,12 +112,21 @@ The plugin creates individual SSH config files for each VM in a dedicated direct
110
112
 
111
113
  ```
112
114
  ~/.ssh/config # Your main SSH config
115
+ └── Include ~/.ssh/config.d/vagrant/*.conf
113
116
  ~/.ssh/config.d/vagrant/ # Plugin-managed directory
114
117
  ├── a1b2c3d4-web.conf # Individual VM config files
115
118
  ├── a1b2c3d4-db.conf
116
119
  └── f5e6d7c8-api.conf # From different projects
117
120
  ```
118
121
 
122
+ Per-VM filenames use the first eight characters of the MD5 hash of the project path for storage uniqueness:
123
+
124
+ ```
125
+ {md5-project-hash}-{vm}.conf
126
+ ```
127
+
128
+ The MD5 hash is only used in filenames. SSH `Host` aliases never include the MD5 hash.
129
+
119
130
  ### Include Directive Management
120
131
 
121
132
  When `manage_includes` is enabled (default: false), the plugin automatically adds:
@@ -131,10 +142,17 @@ Include ~/.ssh/config.d/vagrant/*.conf
131
142
 
132
143
  ### Project Isolation
133
144
 
134
- Each Vagrant project gets a unique identifier based on the project path:
145
+ `project_isolation` controls SSH alias naming:
146
+
147
+ - `project_isolation: true` (default): `Host {project}-{vm}` such as `my-app-web`
148
+ - `project_isolation: false`: `Host {vm}` such as `web`
149
+
150
+ Set `project_isolation` to `false` when host names are managed elsewhere, such as by `vagrant-hostmanager`. Be mindful of hostname collisions when using non-isolated aliases.
151
+
152
+ Storage filenames remain hash-prefixed regardless of `project_isolation`:
153
+
135
154
  - Project path: `/home/user/my-app`
136
155
  - Project hash: `a1b2c3d4` (first 8 chars of MD5)
137
- - SSH host names: `my-app-web`, `my-app-db`
138
156
  - Config files: `a1b2c3d4-web.conf`, `a1b2c3d4-db.conf`
139
157
 
140
158
  ### Individual Config File Example
data/Rakefile CHANGED
@@ -3,7 +3,22 @@
3
3
  require 'bundler/gem_tasks'
4
4
  require 'rspec/core/rake_task'
5
5
 
6
- RSpec::Core::RakeTask.new(:spec)
6
+ # Unit and integration specs must run in separate Ruby processes.
7
+ # Integration specs load the real Vagrant gem which defines
8
+ # Vagrant::Plugin::V2::Config as a class; unit specs mock it as a
9
+ # module, causing TypeError when loaded in the same process.
10
+ namespace :spec do
11
+ RSpec::Core::RakeTask.new(:unit) do |t|
12
+ t.pattern = 'spec/unit/**/*_spec.rb'
13
+ end
14
+
15
+ RSpec::Core::RakeTask.new(:integration) do |t|
16
+ t.pattern = 'spec/integration/**/*_spec.rb'
17
+ end
18
+ end
19
+
20
+ desc 'Run all specs (unit + integration in separate processes)'
21
+ task spec: ['spec:unit', 'spec:integration']
7
22
 
8
23
  task default: :spec
9
24
 
data/TESTING.md CHANGED
@@ -5,14 +5,12 @@ This project uses a **hybrid testing approach** that combines fast unit tests wi
5
5
  ## 🚀 Quick Start
6
6
 
7
7
  ```bash
8
- # Run unit tests (fast, recommended for development)
9
- bundle exec rspec spec/unit/
8
+ # Canonical full-suite command
9
+ bundle exec rake
10
10
 
11
- # Run integration tests (real APIs)
12
- bundle exec rspec spec/integration/
13
-
14
- # Run both test suites
15
- ./test-all.sh
11
+ # Individual suites
12
+ bundle exec rake spec:unit
13
+ bundle exec rake spec:integration
16
14
 
17
15
  # Helper scripts for detailed output:
18
16
  ./test-unit.sh # Unit tests with documentation format
@@ -20,6 +18,8 @@ bundle exec rspec spec/integration/
20
18
  ./test-all.sh # Both suites with progress format
21
19
  ```
22
20
 
21
+ `bundle exec rake` is the supported full-suite command. It runs unit and integration specs in separate Ruby processes because integration specs load real Vagrant while unit specs mock some Vagrant constants.
22
+
23
23
  ## 📁 Test Structure
24
24
 
25
25
  ```
@@ -29,26 +29,23 @@ spec/
29
29
  ├── unit/ # Fast, isolated unit tests
30
30
  │ ├── config_spec.rb # ✅ Config class (32 examples)
31
31
  │ ├── file_locker_spec.rb # ✅ FileLocker class (27 examples)
32
- ├── file_manager_spec.rb # ✅ FileManager class (39 examples)
33
- │ └── ssh_config_manager_spec.rb # ✅ SshConfigManager class (43 examples)
32
+ └── file_manager_spec.rb # ✅ FileManager class (39 examples)
34
33
  └── integration/ # Real API integration tests
35
34
  └── include_manager_spec.rb # ✅ IncludeManager (14 examples)
36
35
  ```
37
36
 
38
37
  ## ✅ Current Test Status
39
38
 
40
- - **Unit Tests**: 141 examples, 0 failures (~0.23s)
41
- - **Integration Tests**: 14 examples, 0 failures (~0.02s)
42
- - **Total**: **155 examples, 0 failures** (100% pass rate)
39
+ - **Unit Tests**: run with `bundle exec rake spec:unit`
40
+ - **Integration Tests**: run with `bundle exec rake spec:integration`
41
+ - **Full Suite**: run with `bundle exec rake`
43
42
  - **Combined Runtime**: ~0.25 seconds (extremely fast!)
44
43
 
45
44
  ## 🚀 Performance Metrics
46
45
 
47
46
  ```bash
48
- $ ./test-all.sh
49
- 📦 Unit Tests: 141 examples, 0 failures (0.23s)
50
- 🔗 Integration Tests: 14 examples, 0 failures (0.02s)
51
- ✅ Total: 155 examples, 0 failures
47
+ $ bundle exec rake
48
+ # Runs unit specs, then integration specs, in separate Ruby processes
52
49
  ```
53
50
 
54
51
  ## 🔧 Test Types
@@ -56,17 +53,16 @@ $ ./test-all.sh
56
53
  ### Unit Tests (spec/unit/)
57
54
  - **Purpose**: Fast feedback, isolated component testing
58
55
  - **Setup**: Mocked dependencies, no real Vagrant loading
59
- - **Speed**: ~0.23 seconds for 141 tests
56
+ - **Command**: `bundle exec rake spec:unit`
60
57
  - **Coverage**:
61
- - Config validation and setup (32 examples)
62
- - FileLocker concurrency and thread safety (27 examples)
63
- - FileManager SSH file operations (39 examples)
64
- - SshConfigManager entry management (43 examples)
58
+ - Config validation and setup
59
+ - FileLocker concurrency and thread safety
60
+ - FileManager SSH file operations
65
61
 
66
62
  ### Integration Tests (spec/integration/)
67
63
  - **Purpose**: Real API verification, end-to-end testing
68
64
  - **Setup**: Real Vagrant APIs, isolated file system environments
69
- - **Speed**: ~0.02 seconds for 14 tests
65
+ - **Command**: `bundle exec rake spec:integration`
70
66
  - **Coverage**: IncludeManager SSH config manipulation, plugin markers
71
67
 
72
68
  ## 🛠 Helper Scripts
@@ -84,7 +84,8 @@ module VagrantPlugins
84
84
  errors << "sshconfigmanager.ssh_config_dir cannot be created: #{e.message}"
85
85
  end
86
86
  else
87
- errors << "sshconfigmanager.ssh_config_dir does not exist and auto_create_dir is disabled: #{@ssh_config_dir}"
87
+ errors << 'sshconfigmanager.ssh_config_dir does not exist and auto_create_dir is disabled: '
88
+ errors[-1] += @ssh_config_dir
88
89
  end
89
90
  else
90
91
  errors << 'sshconfigmanager.ssh_config_dir must be a string path'
@@ -152,20 +153,30 @@ module VagrantPlugins
152
153
  result = self.class.new
153
154
 
154
155
  # Merge each attribute, preferring the other config's values if set
155
- result.enabled = other.enabled == UNSET_VALUE ? @enabled : other.enabled
156
- result.ssh_config_dir = other.ssh_config_dir == UNSET_VALUE ? @ssh_config_dir : other.ssh_config_dir
157
- result.manage_includes = other.manage_includes == UNSET_VALUE ? @manage_includes : other.manage_includes
158
- result.auto_create_dir = other.auto_create_dir == UNSET_VALUE ? @auto_create_dir : other.auto_create_dir
159
- result.cleanup_empty_dir = other.cleanup_empty_dir == UNSET_VALUE ? @cleanup_empty_dir : other.cleanup_empty_dir
160
- result.auto_remove_on_destroy = other.auto_remove_on_destroy == UNSET_VALUE ? @auto_remove_on_destroy : other.auto_remove_on_destroy
161
- result.update_on_reload = other.update_on_reload == UNSET_VALUE ? @update_on_reload : other.update_on_reload
162
- result.refresh_on_provision = other.refresh_on_provision == UNSET_VALUE ? @refresh_on_provision : other.refresh_on_provision
163
- result.keep_config_on_halt = other.keep_config_on_halt == UNSET_VALUE ? @keep_config_on_halt : other.keep_config_on_halt
164
- result.project_isolation = other.project_isolation == UNSET_VALUE ? @project_isolation : other.project_isolation
156
+ config_attributes.each do |attribute|
157
+ result.public_send("#{attribute}=", merged_value(other, attribute))
158
+ end
165
159
 
166
160
  result
167
161
  end
168
162
 
163
+ # Config attributes that can be inherited/merged.
164
+ def config_attributes
165
+ %i[
166
+ enabled ssh_config_dir manage_includes auto_create_dir cleanup_empty_dir
167
+ auto_remove_on_destroy update_on_reload refresh_on_provision
168
+ keep_config_on_halt project_isolation
169
+ ]
170
+ end
171
+
172
+ # Prefer explicitly set child values when merging config objects.
173
+ def merged_value(other, attribute)
174
+ other_value = other.public_send(attribute)
175
+ return public_send(attribute) if other_value == UNSET_VALUE
176
+
177
+ other_value
178
+ end
179
+
169
180
  # Create SSH config directory with proper permissions
170
181
  def ensure_ssh_config_directory
171
182
  return false unless @auto_create_dir
@@ -178,13 +189,6 @@ module VagrantPlugins
178
189
  false
179
190
  end
180
191
  end
181
-
182
- # Retrieve the SSH config manager for a given machine
183
- def ssh_manager_instance(_machine)
184
- require_relative 'file_manager'
185
- FileManager.new(self)
186
- end
187
- alias get_ssh_manager_instance ssh_manager_instance
188
192
  end
189
193
  end
190
194
  end
@@ -204,12 +204,35 @@ module VagrantPlugins
204
204
 
205
205
  # Generate SSH host name for machine
206
206
  def generate_host_name(machine)
207
- if @config.project_isolation
208
- project_name = File.basename(machine.env.root_path)
209
- "#{project_name}-#{machine.name}"
210
- else
211
- machine.name.to_s
212
- end
207
+ host_name = if @config.project_isolation
208
+ project_name = sanitize_name(File.basename(machine.env.root_path))
209
+ vm_name = sanitize_name(machine.name.to_s)
210
+ "#{project_name}-#{vm_name}"
211
+ else
212
+ sanitize_name(machine.name.to_s)
213
+ end
214
+
215
+ truncate_host_name(host_name)
216
+ end
217
+
218
+ # Sanitize project and VM names for use as SSH host aliases.
219
+ def sanitize_name(name)
220
+ return 'unknown' if name.nil? || name.to_s.strip.empty?
221
+
222
+ name.to_s
223
+ .gsub(/[^a-zA-Z0-9\-_.]/, '-')
224
+ .gsub(/\.+/, '.')
225
+ .gsub(/-+/, '-')
226
+ .gsub(/^[-._]+|[-._]+$/, '')
227
+ .downcase
228
+ .then { |value| value.empty? ? 'unknown' : value }
229
+ end
230
+
231
+ # Keep generated aliases short enough for common hostname consumers.
232
+ def truncate_host_name(host_name, max_length = 64)
233
+ return host_name if host_name.length <= max_length
234
+
235
+ host_name[0...max_length].gsub(/[-._]+$/, '')
213
236
  end
214
237
 
215
238
  # Write file atomically using temporary file and rename
@@ -39,14 +39,12 @@ module VagrantPlugins
39
39
  hook.before(Vagrant::Action::Builtin::GracefulHalt, Action::Halt)
40
40
  end
41
41
 
42
- action_hook(:ssh_config_manager, :machine_action_suspend) do |hook|
43
- require 'vagrant_ssh_config_manager/action/halt'
44
- hook.before(Vagrant::Action::Builtin::Suspend, Action::Halt)
45
- end
42
+ # Suspend hook removed: Vagrant 2.4.x has no built-in suspend action
43
+ # middleware constant. Halt behavior (keep SSH config) covers suspend.
46
44
 
47
45
  action_hook(:ssh_config_manager, :machine_action_resume) do |hook|
48
46
  require 'vagrant_ssh_config_manager/action/up'
49
- hook.after(Vagrant::Action::Builtin::Resume, Action::Up)
47
+ hook.after(Vagrant::Action::Builtin::WaitForCommunicator, Action::Up)
50
48
  end
51
49
 
52
50
  action_hook(:ssh_config_manager, :machine_action_provision) do |hook|
@@ -2,6 +2,6 @@
2
2
 
3
3
  module VagrantPlugins
4
4
  module SshConfigManager
5
- VERSION = '1.0.0.alpha.1'
5
+ VERSION = '1.0.0.alpha.2'
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vagrant-ssh-config-manager
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.alpha.1
4
+ version: 1.0.0.alpha.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marek Ruzicka
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-07-14 00:00:00.000000000 Z
11
+ date: 2026-06-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -109,7 +109,6 @@ files:
109
109
  - lib/vagrant_ssh_config_manager/file_manager.rb
110
110
  - lib/vagrant_ssh_config_manager/include_manager.rb
111
111
  - lib/vagrant_ssh_config_manager/plugin.rb
112
- - lib/vagrant_ssh_config_manager/ssh_config_manager.rb
113
112
  - lib/vagrant_ssh_config_manager/ssh_info_extractor.rb
114
113
  - lib/vagrant_ssh_config_manager/version.rb
115
114
  - test-all.sh