makit 0.0.172 → 0.0.173

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: 3d33beba3997ecb2f3df901e594a88a7e297b0b9c668142e95f12e467695522f
4
- data.tar.gz: 8379dea16c7a518519e8d911d666cf9d79a1adb241a0e8f1693d3bed54969f8a
3
+ metadata.gz: '08a43e1b5e0220843cd50b9e0bc225a7f735a2aba7d7b4af14588515f78aea51'
4
+ data.tar.gz: 6e8cee30fb685c771dbb84bc28fe35eb02526d70f66386ecf368567d0dc55743
5
5
  SHA512:
6
- metadata.gz: f9050bff2fb06f6f5a265ea874354c13711de49b2a764e603d1fc8505248852c3839164b5e778d5f2cdf06852b14cead94e3e29dce1d0b0f49e75c0ac88a170f
7
- data.tar.gz: 0072df008c5b88eef16b6ebec531608a3efd9656028ace1caf7ef5c7566e4c4248165af9af2982346e045d55975888b3d1e7a5fa61b72ef5e8041234d7f8f862
6
+ metadata.gz: 84923a1df4d918af850b09c9ebc110ba6cea43b1e405c173bed5b1252e13ba17cad9c7c79939cc0bb900400a2c54b57d733cc5ba9df83aee6aa28b9dff450613
7
+ data.tar.gz: e9280746a278890230747f65cbc1266e24d1d3428a5ef5a9adef6891c20174da3a7c207831d1db3a10275d87cf2eddf024875f11d3b7c0e226bfe6376a1c92b5
data/lib/makit/port.rb CHANGED
@@ -1,10 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "open3"
4
+
3
5
  # This module provides classes for the Makit gem.
4
6
  module Makit
5
7
  # This class provide methods for working with ports
6
8
  #
7
9
  class Port
10
+ class Error < StandardError; end
11
+
8
12
  def self.is_port_available?(port)
9
13
  socket = Socket.new(:INET, :STREAM)
10
14
  socket.bind(Socket.sockaddr_in(port, ""))
@@ -28,5 +32,181 @@ module Makit
28
32
  def self.get_random_available_port
29
33
  get_available_port(get_random_port)
30
34
  end
35
+
36
+ # Ensures a port is available by killing any process using it, or finding the next available port.
37
+ #
38
+ # @param port [Integer] The port to ensure is available
39
+ # @param force [Boolean] If true (default), retries up to 3 times to kill processes on the port.
40
+ # If false, does not kill; finds and returns the next available port starting from +port+.
41
+ # @return [Integer] The port number (guaranteed to be available)
42
+ # @raise [Makit::Port::Error] If force is true and the port is still in use after 3 attempts
43
+ #
44
+ # @example Force free the port (default)
45
+ # Makit::Port.ensure_available(5261) # => 5261
46
+ # Makit::Port.ensure_available(5261, force: true)
47
+ #
48
+ # @example Find next available port without killing
49
+ # Makit::Port.ensure_available(5261, force: false) # => 5261 or next free port
50
+ #
51
+ def self.ensure_available(port, force: true)
52
+ raise ArgumentError, "port must be a positive integer" unless port.is_a?(Integer) && port > 0
53
+
54
+ return port unless port_in_use?(port)
55
+
56
+ if force
57
+ max_retries = 3
58
+ retries = 0
59
+
60
+ while port_in_use?(port) && retries < max_retries
61
+ kill_process_on_port(port)
62
+ sleep(0.5)
63
+ retries += 1
64
+ end
65
+
66
+ if port_in_use?(port)
67
+ raise Error, "Port #{port} is still in use after #{max_retries} attempts to free it"
68
+ end
69
+
70
+ return port
71
+ end
72
+
73
+ find_available_port(port)
74
+ end
75
+
76
+ # Ensures a port is free by killing any process using it.
77
+ #
78
+ # Examples:
79
+ # Makit::Port.ensure_port_free(5261)
80
+ # Makit::Port.ensure_port_free(5261, verbose: true)
81
+ #
82
+ # Returns true if the port is now free, false if it couldn't be freed.
83
+ #
84
+ def self.ensure_port_free(port, verbose: false)
85
+ raise ArgumentError, "port must be a positive integer" unless port.is_a?(Integer) && port > 0
86
+
87
+ return true unless port_in_use?(port)
88
+
89
+ puts "Port #{port} is in use. Attempting to free it..." if verbose
90
+
91
+ killed = kill_process_on_port(port, verbose: verbose)
92
+
93
+ if killed
94
+ # Wait a moment for the port to be released
95
+ sleep(1)
96
+ # Verify port is now free
97
+ unless port_in_use?(port)
98
+ puts "Port #{port} is now free." if verbose
99
+ return true
100
+ else
101
+ puts "Warning: Port #{port} is still in use after killing process." if verbose
102
+ return false
103
+ end
104
+ else
105
+ puts "Warning: Could not kill process on port #{port}." if verbose
106
+ return false
107
+ end
108
+ rescue Error, ArgumentError => e
109
+ puts "Error freeing port #{port}: #{e.message}" if verbose
110
+ false
111
+ end
112
+
113
+ # -----------------------
114
+ # Internals
115
+ # -----------------------
116
+
117
+ def self.port_in_use?(port)
118
+ if RUBY_PLATFORM =~ /mswin|mingw|cygwin/
119
+ # Windows: Use netstat
120
+ stdout, _stderr, status = Open3.capture3("netstat", "-ano")
121
+ return false unless status.success?
122
+ stdout.lines.any? { |line| line.include?(":#{port}") && line.include?("LISTENING") }
123
+ else
124
+ # Unix/macOS: Use lsof
125
+ stdout, _stderr, status = Open3.capture3("lsof", "-ti:#{port}")
126
+ status.success? && !stdout.strip.empty?
127
+ end
128
+ rescue Errno::ENOENT
129
+ # Command not found - assume port is not in use
130
+ false
131
+ end
132
+ private_class_method :port_in_use?
133
+
134
+ def self.kill_process_on_port(port, verbose: false)
135
+ if RUBY_PLATFORM =~ /mswin|mingw|cygwin/
136
+ # Windows: Use netstat to find PID, then taskkill
137
+ stdout, _stderr, status = Open3.capture3("netstat", "-ano")
138
+ return false unless status.success?
139
+
140
+ pids = []
141
+ stdout.lines.each do |line|
142
+ if line.include?(":#{port}") && line.include?("LISTENING")
143
+ parts = line.split
144
+ pid = parts.last
145
+ pids << pid if pid && pid.match?(/^\d+$/)
146
+ end
147
+ end
148
+
149
+ killed_any = false
150
+ pids.uniq.each do |pid|
151
+ begin
152
+ stdout, _stderr, status = Open3.capture3("taskkill", "/F", "/PID", pid)
153
+ if status.success?
154
+ puts "Killed process #{pid} on port #{port}" if verbose
155
+ killed_any = true
156
+ end
157
+ rescue Errno::ENOENT
158
+ # taskkill not found
159
+ return false
160
+ end
161
+ end
162
+
163
+ killed_any
164
+ else
165
+ # Unix/macOS: Use lsof to find PID, then kill
166
+ stdout, _stderr, status = Open3.capture3("lsof", "-ti:#{port}")
167
+ return false unless status.success?
168
+ return true if stdout.strip.empty? # Port is already free
169
+
170
+ pids = stdout.strip.split("\n").map(&:strip).reject(&:empty?)
171
+ return false if pids.empty?
172
+
173
+ killed_any = false
174
+ pids.each do |pid|
175
+ begin
176
+ stdout, _stderr, status = Open3.capture3("kill", "-9", pid)
177
+ if status.success?
178
+ puts "Killed process #{pid} on port #{port}" if verbose
179
+ killed_any = true
180
+ end
181
+ rescue Errno::ENOENT
182
+ # kill not found
183
+ return false
184
+ end
185
+ end
186
+
187
+ killed_any
188
+ end
189
+ rescue Errno::ENOENT
190
+ # Command not found
191
+ false
192
+ end
193
+ private_class_method :kill_process_on_port
194
+
195
+ def self.find_available_port(start_port, max_attempts = 100)
196
+ port = start_port
197
+ attempts = 0
198
+
199
+ while port_in_use?(port) && attempts < max_attempts
200
+ port += 1
201
+ attempts += 1
202
+ end
203
+
204
+ if attempts >= max_attempts
205
+ raise Error, "Could not find available port starting from #{start_port}"
206
+ end
207
+
208
+ port
209
+ end
210
+ private_class_method :find_available_port
31
211
  end
32
212
  end
@@ -1,128 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "open3"
3
+ require_relative "port"
4
4
 
5
5
  module Makit
6
+ # @deprecated Use {Makit::Port} instead. This module delegates to Makit::Port for backward compatibility.
6
7
  module PortUtility
7
- class Error < StandardError; end
8
+ Error = Makit::Port::Error
8
9
 
9
- # Ensures a port is free by killing any process using it.
10
- #
11
- # Examples:
12
- # Makit::PortUtility.ensure_port_free(5261)
13
- # Makit::PortUtility.ensure_port_free(5261, verbose: true)
14
- #
15
- # Returns true if the port is now free, false if it couldn't be freed.
16
- #
10
+ # @see Makit::Port.ensure_port_free
17
11
  def self.ensure_port_free(port, verbose: false)
18
- raise ArgumentError, "port must be a positive integer" unless port.is_a?(Integer) && port > 0
19
-
20
- return true unless port_in_use?(port)
21
-
22
- puts "Port #{port} is in use. Attempting to free it..." if verbose
23
-
24
- killed = kill_process_on_port(port, verbose: verbose)
25
-
26
- if killed
27
- # Wait a moment for the port to be released
28
- sleep(1)
29
- # Verify port is now free
30
- unless port_in_use?(port)
31
- puts "Port #{port} is now free." if verbose
32
- return true
33
- else
34
- puts "Warning: Port #{port} is still in use after killing process." if verbose
35
- return false
36
- end
37
- else
38
- puts "Warning: Could not kill process on port #{port}." if verbose
39
- return false
40
- end
41
- rescue Error, ArgumentError => e
42
- puts "Error freeing port #{port}: #{e.message}" if verbose
43
- false
44
- end
45
-
46
- # -----------------------
47
- # Internals
48
- # -----------------------
49
-
50
- def self.port_in_use?(port)
51
- if RUBY_PLATFORM =~ /mswin|mingw|cygwin/
52
- # Windows: Use netstat
53
- stdout, _stderr, status = Open3.capture3("netstat", "-ano")
54
- return false unless status.success?
55
- stdout.lines.any? { |line| line.include?(":#{port}") && line.include?("LISTENING") }
56
- else
57
- # Unix/macOS: Use lsof
58
- stdout, _stderr, status = Open3.capture3("lsof", "-ti:#{port}")
59
- status.success? && !stdout.strip.empty?
60
- end
61
- rescue Errno::ENOENT
62
- # Command not found - assume port is not in use
63
- false
64
- end
65
- private_class_method :port_in_use?
66
-
67
- def self.kill_process_on_port(port, verbose: false)
68
- if RUBY_PLATFORM =~ /mswin|mingw|cygwin/
69
- # Windows: Use netstat to find PID, then taskkill
70
- stdout, _stderr, status = Open3.capture3("netstat", "-ano")
71
- return false unless status.success?
72
-
73
- pids = []
74
- stdout.lines.each do |line|
75
- if line.include?(":#{port}") && line.include?("LISTENING")
76
- parts = line.split
77
- pid = parts.last
78
- pids << pid if pid && pid.match?(/^\d+$/)
79
- end
80
- end
81
-
82
- killed_any = false
83
- pids.uniq.each do |pid|
84
- begin
85
- stdout, _stderr, status = Open3.capture3("taskkill", "/F", "/PID", pid)
86
- if status.success?
87
- puts "Killed process #{pid} on port #{port}" if verbose
88
- killed_any = true
89
- end
90
- rescue Errno::ENOENT
91
- # taskkill not found
92
- return false
93
- end
94
- end
95
-
96
- killed_any
97
- else
98
- # Unix/macOS: Use lsof to find PID, then kill
99
- stdout, _stderr, status = Open3.capture3("lsof", "-ti:#{port}")
100
- return false unless status.success?
101
- return true if stdout.strip.empty? # Port is already free
102
-
103
- pids = stdout.strip.split("\n").map(&:strip).reject(&:empty?)
104
- return false if pids.empty?
105
-
106
- killed_any = false
107
- pids.each do |pid|
108
- begin
109
- stdout, _stderr, status = Open3.capture3("kill", "-9", pid)
110
- if status.success?
111
- puts "Killed process #{pid} on port #{port}" if verbose
112
- killed_any = true
113
- end
114
- rescue Errno::ENOENT
115
- # kill not found
116
- return false
117
- end
118
- end
119
-
120
- killed_any
121
- end
122
- rescue Errno::ENOENT
123
- # Command not found
124
- false
12
+ warn "[Makit::PortUtility] DEPRECATED: Makit::PortUtility.ensure_port_free is deprecated. " \
13
+ "Use Makit::Port.ensure_port_free(port, verbose: false) instead."
14
+ Makit::Port.ensure_port_free(port, verbose: verbose)
125
15
  end
126
- private_class_method :kill_process_on_port
127
16
  end
128
17
  end
data/lib/makit/version.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Makit
4
4
  # Static version for now to avoid circular dependency issues
5
- #VERSION = "0.0.172"
5
+ #VERSION = "0.0.173"
6
6
 
7
7
  # Version management utilities for various file formats
8
8
  #
data/lib/makit.rb CHANGED
@@ -92,6 +92,7 @@ require_relative "makit/protoc"
92
92
  require_relative "makit/zip"
93
93
  require_relative "makit/gems"
94
94
  require_relative "makit/nuget_cache"
95
+ require_relative "makit/port"
95
96
  require_relative "makit/port_utility"
96
97
  require_relative "makit/zip_utility"
97
98
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: makit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.172
4
+ version: 0.0.173
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lou Parslow