serverengine 2.2.3 → 2.3.0
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 +4 -4
- data/.github/workflows/linux.yml +31 -0
- data/.github/workflows/windows.yml +42 -0
- data/Changelog +20 -0
- data/NOTICE +1 -1
- data/README.md +3 -2
- data/Rakefile +0 -16
- data/lib/serverengine/daemon_logger.rb +37 -0
- data/lib/serverengine/multi_process_server.rb +7 -1
- data/lib/serverengine/multi_thread_server.rb +3 -0
- data/lib/serverengine/multi_worker_server.rb +35 -12
- data/lib/serverengine/process_manager.rb +9 -1
- data/lib/serverengine/server.rb +1 -1
- data/lib/serverengine/socket_manager.rb +2 -0
- data/lib/serverengine/version.rb +1 -1
- data/lib/serverengine/winsock.rb +16 -10
- data/lib/serverengine.rb +20 -5
- data/serverengine.gemspec +1 -7
- data/spec/blocking_flag_spec.rb +16 -4
- data/spec/daemon_logger_spec.rb +25 -0
- data/spec/multi_spawn_server_spec.rb +196 -13
- data/spec/server_worker_context.rb +16 -5
- data/spec/spec_helper.rb +5 -0
- data/spec/supervisor_spec.rb +8 -2
- data/spec/winsock_spec.rb +17 -0
- metadata +21 -5
- data/.travis.yml +0 -21
- data/appveyor.yml +0 -24
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b1c09d476bdc08a078882ba14ee74f487ab988a397d2228c3e83f611fff94df1
|
|
4
|
+
data.tar.gz: 33732af3d1c591ec28c67249b1b67b16b142f80048f70c3cf5c769b3e267b76a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 383aaf8822f01aec3ce2e4a25243da8ca741da9d11f97c601c31cc6985e285743a088cafdd4fd918b58a966960376f65b28645b1fca74d0741e64052bd329ac8
|
|
7
|
+
data.tar.gz: ca10bd75948760060c8ae433cb2021a7024d6b076d84157012db5ae6809d01c9a8bb2eb2bb1f9f8d84f0881e4cbaf3bebfb25ef959130574e0cf717550ee1867
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
name: Testing on Ubuntu
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [master]
|
|
5
|
+
pull_request:
|
|
6
|
+
branches: [master]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ${{ matrix.os }}
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
ruby: [ '3.1', '3.0', '2.7' ]
|
|
15
|
+
os:
|
|
16
|
+
- ubuntu-latest
|
|
17
|
+
name: Unit testing with Ruby ${{ matrix.ruby }} on ${{ matrix.os }}
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v2
|
|
20
|
+
- name: Set up Ruby
|
|
21
|
+
uses: ruby/setup-ruby@v1
|
|
22
|
+
with:
|
|
23
|
+
ruby-version: ${{ matrix.ruby }}
|
|
24
|
+
- name: Install dependencies
|
|
25
|
+
run: |
|
|
26
|
+
gem install bundler rake
|
|
27
|
+
bundle install --jobs 4 --retry 3
|
|
28
|
+
- name: Run tests
|
|
29
|
+
env:
|
|
30
|
+
CI: true
|
|
31
|
+
run: bundle exec rake spec
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Testing on Windows
|
|
2
|
+
on:
|
|
3
|
+
push:
|
|
4
|
+
branches: [master]
|
|
5
|
+
pull_request:
|
|
6
|
+
branches: [master]
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ${{ matrix.os }}
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
ruby: [ '3.1', '2.7' ]
|
|
15
|
+
os:
|
|
16
|
+
- windows-latest
|
|
17
|
+
include:
|
|
18
|
+
- ruby: '3.0.3'
|
|
19
|
+
os: windows-latest
|
|
20
|
+
# On Ruby 3.0, we need to use fiddle 1.0.8 or later to retrieve correct
|
|
21
|
+
# error code. In addition, we have to specify the path of fiddle by RUBYLIB
|
|
22
|
+
# because RubyInstaller loads Ruby's bundled fiddle before initializing gem.
|
|
23
|
+
# See also:
|
|
24
|
+
# * https://github.com/ruby/fiddle/issues/72
|
|
25
|
+
# * https://bugs.ruby-lang.org/issues/17813
|
|
26
|
+
# * https://github.com/oneclick/rubyinstaller2/blob/8225034c22152d8195bc0aabc42a956c79d6c712/lib/ruby_installer/build/dll_directory.rb
|
|
27
|
+
ruby-lib-opt: RUBYLIB=%RUNNER_TOOL_CACHE%/Ruby/3.0.3/x64/lib/ruby/gems/3.0.0/gems/fiddle-1.1.0/lib
|
|
28
|
+
|
|
29
|
+
name: Unit testing with Ruby ${{ matrix.ruby }} on ${{ matrix.os }}
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@v2
|
|
32
|
+
- name: Set up Ruby
|
|
33
|
+
uses: ruby/setup-ruby@v1
|
|
34
|
+
with:
|
|
35
|
+
ruby-version: ${{ matrix.ruby }}
|
|
36
|
+
- name: Add Fiddle 1.1.0
|
|
37
|
+
if: ${{ matrix.ruby == '3.0.3' }}
|
|
38
|
+
run: gem install fiddle --version 1.1.0
|
|
39
|
+
- name: Install dependencies
|
|
40
|
+
run: ridk exec bundle install --jobs 4 --retry 3
|
|
41
|
+
- name: Run tests
|
|
42
|
+
run: bundle exec rake spec ${{ matrix.ruby-lib-opt }}
|
data/Changelog
CHANGED
|
@@ -1,3 +1,23 @@
|
|
|
1
|
+
2022-06-13 version 2.3.0
|
|
2
|
+
|
|
3
|
+
* Add restart_worker_interval option to prevent workers restart immediately
|
|
4
|
+
after kill
|
|
5
|
+
* Reopen log file when rotation done by external tool is detected
|
|
6
|
+
* Fix unexpected behavior of start_worker_delay option
|
|
7
|
+
* Remove windows-pr dependency
|
|
8
|
+
* Fix a potential crash that command_sender_pipe of ProcessManager::Monitor
|
|
9
|
+
raises error on shutdown
|
|
10
|
+
* Allow to load serverengine/socket_manager without servernegine/utils
|
|
11
|
+
* Fix unstable tests
|
|
12
|
+
|
|
13
|
+
2022-01-13 version 2.2.5:
|
|
14
|
+
|
|
15
|
+
* Fix DLL load error on Ruby 3.1 on Windows
|
|
16
|
+
|
|
17
|
+
2021-05-24 version 2.2.4:
|
|
18
|
+
|
|
19
|
+
* Ensure to get correct Win32 socket error on Ruby 3.0
|
|
20
|
+
|
|
1
21
|
2021-02-17 version 2.2.3:
|
|
2
22
|
|
|
3
23
|
* Change SocketManager's port assignment strategy on Windows
|
data/NOTICE
CHANGED
data/README.md
CHANGED
|
@@ -478,10 +478,11 @@ Available methods are different depending on `worker_type`. ServerEngine support
|
|
|
478
478
|
- **disable_reload** disables USR2 signal (default: false)
|
|
479
479
|
- **server_restart_wait** sets wait time before restarting server after last restarting (default: 1.0) [dynamic reloadable]
|
|
480
480
|
- **server_detach_wait** sets wait time before starting live restart (default: 10.0) [dynamic reloadable]
|
|
481
|
-
- Multithread server and multiprocess server: available only when `worker_type` is thread or process
|
|
481
|
+
- Multithread server and multiprocess server: available only when `worker_type` is "thread" or "process" or "spawn"
|
|
482
482
|
- **workers** sets number of workers (default: 1) [dynamic reloadable]
|
|
483
|
-
- **start_worker_delay** sets
|
|
483
|
+
- **start_worker_delay** sets the delay between each worker-start when starting/restarting multiple workers at once (default: 0) [dynamic reloadable]
|
|
484
484
|
- **start_worker_delay_rand** randomizes start_worker_delay at this ratio (default: 0.2) [dynamic reloadable]
|
|
485
|
+
- **restart_worker_interval** sets wait time before restarting a stopped worker (default: 0) [dynamic reloadable]
|
|
485
486
|
- Multiprocess server: available only when `worker_type` is "process"
|
|
486
487
|
- **worker_process_name** changes process name ($0) of workers [dynamic reloadable]
|
|
487
488
|
- **worker_heartbeat_interval** sets interval of heartbeats in seconds (default: 1.0) [dynamic reloadable]
|
data/Rakefile
CHANGED
|
@@ -8,19 +8,3 @@ require 'rspec/core/rake_task'
|
|
|
8
8
|
|
|
9
9
|
RSpec::Core::RakeTask.new(:spec)
|
|
10
10
|
task :default => [:spec, :build]
|
|
11
|
-
|
|
12
|
-
# 1. update Changelog and lib/serverengine/version.rb
|
|
13
|
-
# 2. bundle && bundle exec rake build:all
|
|
14
|
-
# 3. release 3 packages built on pkg/ directory
|
|
15
|
-
namespace :build do
|
|
16
|
-
desc 'Build gems for all platforms'
|
|
17
|
-
task :all do
|
|
18
|
-
Bundler.with_clean_env do
|
|
19
|
-
%w[ruby x86-mingw32 x64-mingw32].each do |name|
|
|
20
|
-
ENV['GEM_BUILD_FAKE_PLATFORM'] = name
|
|
21
|
-
Rake::Task["build"].execute
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
|
|
@@ -55,6 +55,9 @@ module ServerEngine
|
|
|
55
55
|
# update path string
|
|
56
56
|
old_file_dev = @file_dev
|
|
57
57
|
@file_dev = LogDevice.new(logdev, shift_age: @rotate_age, shift_size: @rotate_size)
|
|
58
|
+
# Enable to detect rotation done by external tools.
|
|
59
|
+
# Otherwise it continues writing logs to old file unexpectedly.
|
|
60
|
+
@file_dev.extend(RotationAware)
|
|
58
61
|
old_file_dev.close if old_file_dev
|
|
59
62
|
@logdev = @file_dev
|
|
60
63
|
end
|
|
@@ -130,6 +133,40 @@ module ServerEngine
|
|
|
130
133
|
nil
|
|
131
134
|
end
|
|
132
135
|
|
|
136
|
+
module RotationAware
|
|
137
|
+
def self.extended(obj)
|
|
138
|
+
obj.update_ino
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def update_ino
|
|
142
|
+
(@ino_mutex ||= Mutex.new).synchronize do
|
|
143
|
+
@ino = File.stat(filename).ino rescue nil
|
|
144
|
+
@last_ino_time = Time.now
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def reopen(log = nil)
|
|
149
|
+
super(log)
|
|
150
|
+
update_ino
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def reopen!
|
|
154
|
+
super
|
|
155
|
+
update_ino
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def write(message)
|
|
159
|
+
reopen_needed = false
|
|
160
|
+
@ino_mutex.synchronize do
|
|
161
|
+
if (Time.now - @last_ino_time).abs > 1
|
|
162
|
+
ino = File.stat(filename).ino rescue nil
|
|
163
|
+
reopen_needed = true if ino && ino != @ino
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
reopen! if reopen_needed
|
|
167
|
+
super(message)
|
|
168
|
+
end
|
|
169
|
+
end
|
|
133
170
|
end
|
|
134
171
|
|
|
135
172
|
end
|
|
@@ -105,9 +105,11 @@ module ServerEngine
|
|
|
105
105
|
@unrecoverable_exit_codes = unrecoverable_exit_codes
|
|
106
106
|
@unrecoverable_exit = false
|
|
107
107
|
@exitstatus = nil
|
|
108
|
+
@restart_at = nil
|
|
108
109
|
end
|
|
109
110
|
|
|
110
111
|
attr_reader :exitstatus
|
|
112
|
+
attr_accessor :restart_at
|
|
111
113
|
|
|
112
114
|
def send_stop(stop_graceful)
|
|
113
115
|
@stop = true
|
|
@@ -142,7 +144,11 @@ module ServerEngine
|
|
|
142
144
|
return false unless @pmon
|
|
143
145
|
|
|
144
146
|
if stat = @pmon.try_join
|
|
145
|
-
|
|
147
|
+
if @stop
|
|
148
|
+
@worker.logger.info "Worker #{@wid} finished with #{ServerEngine.format_join_status(stat)}"
|
|
149
|
+
else
|
|
150
|
+
@worker.logger.error "Worker #{@wid} finished unexpectedly with #{ServerEngine.format_join_status(stat)}"
|
|
151
|
+
end
|
|
146
152
|
if stat.is_a?(Process::Status) && stat.exited? && @unrecoverable_exit_codes.include?(stat.exitstatus)
|
|
147
153
|
@unrecoverable_exit = true
|
|
148
154
|
@exitstatus = stat.exitstatus
|
|
@@ -55,8 +55,8 @@ module ServerEngine
|
|
|
55
55
|
|
|
56
56
|
def run
|
|
57
57
|
while true
|
|
58
|
-
|
|
59
|
-
break if
|
|
58
|
+
num_alive_or_restarting = keepalive_workers
|
|
59
|
+
break if num_alive_or_restarting == 0
|
|
60
60
|
wait_tick
|
|
61
61
|
end
|
|
62
62
|
end
|
|
@@ -85,6 +85,7 @@ module ServerEngine
|
|
|
85
85
|
|
|
86
86
|
@start_worker_delay = @config[:start_worker_delay] || 0
|
|
87
87
|
@start_worker_delay_rand = @config[:start_worker_delay_rand] || 0.2
|
|
88
|
+
@restart_worker_interval = @config[:restart_worker_interval] || 0
|
|
88
89
|
|
|
89
90
|
scale_workers(@config[:workers] || 1)
|
|
90
91
|
|
|
@@ -96,12 +97,12 @@ module ServerEngine
|
|
|
96
97
|
end
|
|
97
98
|
|
|
98
99
|
def keepalive_workers
|
|
99
|
-
|
|
100
|
+
num_alive_or_restarting = 0
|
|
100
101
|
|
|
101
102
|
@monitors.each_with_index do |m,wid|
|
|
102
103
|
if m && m.alive?
|
|
103
104
|
# alive
|
|
104
|
-
|
|
105
|
+
num_alive_or_restarting += 1
|
|
105
106
|
|
|
106
107
|
elsif m && m.respond_to?(:recoverable?) && !m.recoverable?
|
|
107
108
|
# exited, with unrecoverable exit code
|
|
@@ -116,8 +117,12 @@ module ServerEngine
|
|
|
116
117
|
elsif wid < @num_workers
|
|
117
118
|
# scale up or reboot
|
|
118
119
|
unless @stop
|
|
119
|
-
|
|
120
|
-
|
|
120
|
+
if m
|
|
121
|
+
restart_worker(wid)
|
|
122
|
+
else
|
|
123
|
+
start_new_worker(wid)
|
|
124
|
+
end
|
|
125
|
+
num_alive_or_restarting += 1
|
|
121
126
|
end
|
|
122
127
|
|
|
123
128
|
elsif m
|
|
@@ -126,7 +131,27 @@ module ServerEngine
|
|
|
126
131
|
end
|
|
127
132
|
end
|
|
128
133
|
|
|
129
|
-
return
|
|
134
|
+
return num_alive_or_restarting
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def start_new_worker(wid)
|
|
138
|
+
delayed_start_worker(wid)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def restart_worker(wid)
|
|
142
|
+
m = @monitors[wid]
|
|
143
|
+
|
|
144
|
+
is_already_restarting = !m.restart_at.nil?
|
|
145
|
+
if is_already_restarting
|
|
146
|
+
delayed_start_worker(wid) if m.restart_at <= Time.now()
|
|
147
|
+
return
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
if @restart_worker_interval > 0
|
|
151
|
+
m.restart_at = Time.now() + @restart_worker_interval
|
|
152
|
+
else
|
|
153
|
+
delayed_start_worker(wid)
|
|
154
|
+
end
|
|
130
155
|
end
|
|
131
156
|
|
|
132
157
|
def delayed_start_worker(wid)
|
|
@@ -135,15 +160,13 @@ module ServerEngine
|
|
|
135
160
|
Kernel.rand * @start_worker_delay * @start_worker_delay_rand -
|
|
136
161
|
@start_worker_delay * @start_worker_delay_rand / 2
|
|
137
162
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
wait = delay - (now - @last_start_worker_time)
|
|
163
|
+
wait = delay - (Time.now.to_f - @last_start_worker_time)
|
|
141
164
|
sleep wait if wait > 0
|
|
142
165
|
|
|
143
|
-
@last_start_worker_time = now
|
|
166
|
+
@last_start_worker_time = Time.now.to_f
|
|
144
167
|
end
|
|
145
168
|
|
|
146
|
-
start_worker(wid)
|
|
169
|
+
@monitors[wid] = start_worker(wid)
|
|
147
170
|
end
|
|
148
171
|
end
|
|
149
172
|
|
|
@@ -333,7 +333,15 @@ module ServerEngine
|
|
|
333
333
|
end
|
|
334
334
|
|
|
335
335
|
def send_command(command)
|
|
336
|
-
|
|
336
|
+
pid = @pid
|
|
337
|
+
return false unless pid
|
|
338
|
+
|
|
339
|
+
begin
|
|
340
|
+
@command_sender_pipe.write(command)
|
|
341
|
+
return true
|
|
342
|
+
rescue #Errno::EPIPE
|
|
343
|
+
return false
|
|
344
|
+
end
|
|
337
345
|
end
|
|
338
346
|
|
|
339
347
|
def try_join
|
data/lib/serverengine/server.rb
CHANGED
|
@@ -22,6 +22,8 @@ require 'securerandom'
|
|
|
22
22
|
require 'json'
|
|
23
23
|
require 'base64'
|
|
24
24
|
|
|
25
|
+
require_relative 'utils' # for ServerEngine.windows?
|
|
26
|
+
|
|
25
27
|
module ServerEngine
|
|
26
28
|
module SocketManager
|
|
27
29
|
# This token is used for communication between peers. If token is mismatched, messages will be discarded
|
data/lib/serverengine/version.rb
CHANGED
data/lib/serverengine/winsock.rb
CHANGED
|
@@ -21,6 +21,7 @@ module ServerEngine
|
|
|
21
21
|
require 'fiddle/import'
|
|
22
22
|
require 'fiddle/types'
|
|
23
23
|
require 'socket'
|
|
24
|
+
require 'rbconfig'
|
|
24
25
|
|
|
25
26
|
extend Fiddle::Importer
|
|
26
27
|
|
|
@@ -78,6 +79,19 @@ module ServerEngine
|
|
|
78
79
|
end
|
|
79
80
|
end
|
|
80
81
|
|
|
82
|
+
def self.last_error
|
|
83
|
+
# On Ruby 3.0 calling WSAGetLastError here can't retrieve correct error
|
|
84
|
+
# code because Ruby's internal code resets it.
|
|
85
|
+
# See also:
|
|
86
|
+
# * https://github.com/ruby/fiddle/issues/72
|
|
87
|
+
# * https://bugs.ruby-lang.org/issues/17813
|
|
88
|
+
if Fiddle.respond_to?(:win32_last_socket_error)
|
|
89
|
+
Fiddle.win32_last_socket_error || 0
|
|
90
|
+
else
|
|
91
|
+
self.WSAGetLastError
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
81
95
|
INVALID_SOCKET = -1
|
|
82
96
|
end
|
|
83
97
|
|
|
@@ -85,21 +99,13 @@ module ServerEngine
|
|
|
85
99
|
extend Fiddle::Importer
|
|
86
100
|
|
|
87
101
|
dlload "kernel32"
|
|
88
|
-
extern "int GetModuleFileNameA(int, char *, int)"
|
|
89
102
|
extern "int CloseHandle(int)"
|
|
90
103
|
|
|
91
|
-
|
|
92
|
-
GetModuleFileNameA(0, ruby_bin_path_buf, ruby_bin_path_buf.size)
|
|
93
|
-
|
|
94
|
-
ruby_bin_path = ruby_bin_path_buf.to_s.gsub(/\\/, '/')
|
|
95
|
-
ruby_dll_paths = File.dirname(ruby_bin_path) + '/*msvcr*ruby*.dll'
|
|
96
|
-
ruby_dll_path = Dir.glob(ruby_dll_paths).first
|
|
97
|
-
dlload ruby_dll_path
|
|
98
|
-
|
|
104
|
+
dlload RbConfig::CONFIG['LIBRUBY_SO']
|
|
99
105
|
extern "int rb_w32_map_errno(int)"
|
|
100
106
|
|
|
101
107
|
def self.raise_last_error(name)
|
|
102
|
-
errno = rb_w32_map_errno(WinSock.
|
|
108
|
+
errno = rb_w32_map_errno(WinSock.last_error)
|
|
103
109
|
raise SystemCallError.new(name, errno)
|
|
104
110
|
end
|
|
105
111
|
|
data/lib/serverengine.rb
CHANGED
|
@@ -35,12 +35,27 @@ module ServerEngine
|
|
|
35
35
|
|
|
36
36
|
def self.ruby_bin_path
|
|
37
37
|
if ServerEngine.windows?
|
|
38
|
-
|
|
39
|
-
ruby_path = "\0" * 256
|
|
40
|
-
Windows::Library::GetModuleFileName.call(0, ruby_path, 256)
|
|
41
|
-
return ruby_path.rstrip.gsub(/\\/, '/')
|
|
38
|
+
ServerEngine::Win32.ruby_bin_path
|
|
42
39
|
else
|
|
43
|
-
|
|
40
|
+
File.join(RbConfig::CONFIG["bindir"], RbConfig::CONFIG["RUBY_INSTALL_NAME"]) + RbConfig::CONFIG["EXEEXT"]
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
if ServerEngine.windows?
|
|
45
|
+
module Win32
|
|
46
|
+
require 'fiddle/import'
|
|
47
|
+
|
|
48
|
+
extend Fiddle::Importer
|
|
49
|
+
|
|
50
|
+
dlload "kernel32"
|
|
51
|
+
extern "int GetModuleFileNameW(int, void *, int)"
|
|
52
|
+
|
|
53
|
+
def self.ruby_bin_path
|
|
54
|
+
ruby_bin_path_buf = Fiddle::Pointer.malloc(1024)
|
|
55
|
+
len = GetModuleFileNameW(0, ruby_bin_path_buf, ruby_bin_path_buf.size / 2)
|
|
56
|
+
path_bytes = ruby_bin_path_buf[0, len * 2]
|
|
57
|
+
path_bytes.encode('UTF-8', 'UTF-16LE').gsub(/\\/, '/')
|
|
58
|
+
end
|
|
44
59
|
end
|
|
45
60
|
end
|
|
46
61
|
end
|
data/serverengine.gemspec
CHANGED
|
@@ -27,11 +27,5 @@ Gem::Specification.new do |gem|
|
|
|
27
27
|
gem.add_development_dependency 'rake-compiler-dock', ['~> 0.5.0']
|
|
28
28
|
gem.add_development_dependency 'rake-compiler', ['~> 0.9.4']
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
fake_platform = ENV['GEM_BUILD_FAKE_PLATFORM'].to_s
|
|
32
|
-
gem.platform = fake_platform unless fake_platform.empty?
|
|
33
|
-
if /mswin|mingw/ =~ fake_platform || (/mswin|mingw/ =~ RUBY_PLATFORM && fake_platform.empty?)
|
|
34
|
-
# windows dependencies
|
|
35
|
-
gem.add_runtime_dependency("windows-pr", ["~> 1.2.5"])
|
|
36
|
-
end
|
|
30
|
+
gem.add_development_dependency "timecop", ["~> 0.9.5"]
|
|
37
31
|
end
|
data/spec/blocking_flag_spec.rb
CHANGED
|
@@ -20,10 +20,16 @@ describe ServerEngine::BlockingFlag do
|
|
|
20
20
|
it 'wait_for_set timeout' do
|
|
21
21
|
start = Time.now
|
|
22
22
|
|
|
23
|
-
subject.wait_for_set(0.
|
|
23
|
+
subject.wait_for_set(0.1)
|
|
24
24
|
elapsed = Time.now - start
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
if ServerEngine.windows? && ENV['CI'] == 'True'
|
|
27
|
+
# timer seems low accuracy on Windows CI container, often a bit shorter
|
|
28
|
+
# than expected
|
|
29
|
+
elapsed.should >= 0.1 * 0.95
|
|
30
|
+
else
|
|
31
|
+
elapsed.should >= 0.1
|
|
32
|
+
end
|
|
27
33
|
end
|
|
28
34
|
|
|
29
35
|
it 'wait_for_reset timeout' do
|
|
@@ -31,10 +37,16 @@ describe ServerEngine::BlockingFlag do
|
|
|
31
37
|
|
|
32
38
|
start = Time.now
|
|
33
39
|
|
|
34
|
-
subject.wait_for_reset(0.
|
|
40
|
+
subject.wait_for_reset(0.1)
|
|
35
41
|
elapsed = Time.now - start
|
|
36
42
|
|
|
37
|
-
|
|
43
|
+
if ServerEngine.windows? && ENV['CI'] == 'True'
|
|
44
|
+
# timer seems low accuracy on Windows CI container, often a bit shorter
|
|
45
|
+
# than expected
|
|
46
|
+
elapsed.should >= 0.1 * 0.95
|
|
47
|
+
else
|
|
48
|
+
elapsed.should >= 0.1
|
|
49
|
+
end
|
|
38
50
|
end
|
|
39
51
|
|
|
40
52
|
it 'wait' do
|
data/spec/daemon_logger_spec.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'stringio'
|
|
2
|
+
require 'timecop'
|
|
2
3
|
|
|
3
4
|
describe ServerEngine::DaemonLogger do
|
|
4
5
|
before { FileUtils.rm_rf("tmp") }
|
|
@@ -172,4 +173,28 @@ describe ServerEngine::DaemonLogger do
|
|
|
172
173
|
$stderr = STDERR
|
|
173
174
|
stderr.should_not =~ /(log shifting failed|log writing failed|log rotation inter-process lock failed)/
|
|
174
175
|
end
|
|
176
|
+
|
|
177
|
+
it 'reopen log when path is renamed' do
|
|
178
|
+
pending "rename isn't supported on windows" if ServerEngine.windows?
|
|
179
|
+
|
|
180
|
+
log = DaemonLogger.new("tmp/rotate.log", { level: 'info', log_rotate_age: 0 })
|
|
181
|
+
|
|
182
|
+
log.info '11111'
|
|
183
|
+
File.read("tmp/rotate.log").should include('11111')
|
|
184
|
+
File.rename("tmp/rotate.log", "tmp/rotate.log.1")
|
|
185
|
+
|
|
186
|
+
Timecop.travel(Time.now + 1)
|
|
187
|
+
|
|
188
|
+
log.info '22222'
|
|
189
|
+
contents = File.read("tmp/rotate.log.1")
|
|
190
|
+
contents.should include('11111')
|
|
191
|
+
contents.should include('22222')
|
|
192
|
+
|
|
193
|
+
FileUtils.touch("tmp/rotate.log")
|
|
194
|
+
Timecop.travel(Time.now + 1)
|
|
195
|
+
|
|
196
|
+
log.info '33333'
|
|
197
|
+
File.read("tmp/rotate.log").should include('33333')
|
|
198
|
+
File.read("tmp/rotate.log.1").should_not include('33333')
|
|
199
|
+
end
|
|
175
200
|
end
|
|
@@ -1,25 +1,208 @@
|
|
|
1
1
|
require 'timeout'
|
|
2
|
+
require 'timecop'
|
|
2
3
|
|
|
3
4
|
describe ServerEngine::MultiSpawnServer do
|
|
4
5
|
include_context 'test server and worker'
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
describe 'starts worker processes' do
|
|
8
|
+
context 'with command_sender=pipe' do
|
|
9
|
+
it do
|
|
10
|
+
config = {workers: 2, command_sender: 'pipe', log_stdout: false, log_stderr: false}
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
s = ServerEngine::MultiSpawnServer.new(TestWorker) { config.dup }
|
|
13
|
+
t = Thread.new { s.main }
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
begin
|
|
16
|
+
wait_for_fork
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
Timeout.timeout(5) do
|
|
19
|
+
sleep(0.5) until test_state(:worker_run) == 2
|
|
20
|
+
end
|
|
21
|
+
test_state(:worker_run).should == 2
|
|
22
|
+
ensure
|
|
23
|
+
s.stop(true)
|
|
24
|
+
t.join
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
describe 'keepalive_workers' do
|
|
31
|
+
let(:config) {
|
|
32
|
+
{
|
|
33
|
+
workers: workers,
|
|
34
|
+
command_sender: 'pipe',
|
|
35
|
+
log_stdout: false,
|
|
36
|
+
log_stderr: false,
|
|
37
|
+
start_worker_delay: start_worker_delay,
|
|
38
|
+
start_worker_delay_rand: 0,
|
|
39
|
+
restart_worker_interval: restart_worker_interval,
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
let(:workers) { 3 }
|
|
43
|
+
let(:server) { ServerEngine::MultiSpawnServer.new(TestWorker) { config.dup } }
|
|
44
|
+
let(:monitors) { server.instance_variable_get(:@monitors) }
|
|
45
|
+
|
|
46
|
+
context 'default' do
|
|
47
|
+
let(:start_worker_delay) { 0 }
|
|
48
|
+
let(:restart_worker_interval) { 0 }
|
|
49
|
+
|
|
50
|
+
it do
|
|
51
|
+
t = Thread.new { server.main }
|
|
52
|
+
|
|
53
|
+
begin
|
|
54
|
+
wait_for_fork
|
|
55
|
+
|
|
56
|
+
Timeout.timeout(5) do
|
|
57
|
+
sleep(0.5) until monitors.count { |m| m && m.alive? } == workers
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
monitors.each do |m|
|
|
61
|
+
m.send_stop(true)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# To prevent the judge before stopping once
|
|
65
|
+
wait_for_stop
|
|
66
|
+
|
|
67
|
+
-> {
|
|
68
|
+
Timeout.timeout(5) do
|
|
69
|
+
sleep(0.5) until monitors.count { |m| m.alive? } == workers
|
|
70
|
+
end
|
|
71
|
+
}.should_not raise_error, "Not all workers restarted correctly."
|
|
72
|
+
ensure
|
|
73
|
+
server.stop(true)
|
|
74
|
+
t.join
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
context 'with only restart_worker_interval' do
|
|
80
|
+
let(:start_worker_delay) { 0 }
|
|
81
|
+
let(:restart_worker_interval) { 10 }
|
|
82
|
+
|
|
83
|
+
it do
|
|
84
|
+
t = Thread.new { server.main }
|
|
85
|
+
|
|
86
|
+
begin
|
|
87
|
+
wait_for_fork
|
|
88
|
+
|
|
89
|
+
# Wait for initial starting
|
|
90
|
+
Timeout.timeout(5) do
|
|
91
|
+
sleep(0.5) until monitors.count { |m| m && m.alive? } == workers
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
monitors.each do |m|
|
|
95
|
+
m.send_stop(true)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Wait for all workers to stop and to be set restarting time
|
|
99
|
+
Timeout.timeout(5) do
|
|
100
|
+
sleep(0.5) until monitors.count { |m| m.alive? || m.restart_at.nil? } == 0
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
Timecop.freeze
|
|
104
|
+
|
|
105
|
+
mergin_time = 3
|
|
106
|
+
|
|
107
|
+
Timecop.freeze(Time.now + restart_worker_interval - mergin_time)
|
|
108
|
+
sleep(1.5)
|
|
109
|
+
monitors.count { |m| m.alive? }.should == 0
|
|
110
|
+
|
|
111
|
+
Timecop.freeze(Time.now + 2 * mergin_time)
|
|
112
|
+
-> {
|
|
113
|
+
Timeout.timeout(5) do
|
|
114
|
+
sleep(0.5) until monitors.count { |m| m.alive? } == workers
|
|
115
|
+
end
|
|
116
|
+
}.should_not raise_error, "Not all workers restarted correctly."
|
|
117
|
+
ensure
|
|
118
|
+
server.stop(true)
|
|
119
|
+
t.join
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
context 'with only start_worker_delay' do
|
|
125
|
+
let(:start_worker_delay) { 3 }
|
|
126
|
+
let(:restart_worker_interval) { 0 }
|
|
127
|
+
|
|
128
|
+
it do
|
|
129
|
+
t = Thread.new { server.main }
|
|
130
|
+
|
|
131
|
+
begin
|
|
132
|
+
wait_for_fork
|
|
133
|
+
|
|
134
|
+
# Initial starts are delayed too, so set longer timeout.
|
|
135
|
+
# (`start_worker_delay` uses `sleep` inside, so Timecop can't skip this wait.)
|
|
136
|
+
Timeout.timeout(start_worker_delay * workers) do
|
|
137
|
+
sleep(0.5) until monitors.count { |m| m && m.alive? } == workers
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# Skip time to avoid getting a delay for the initial starts.
|
|
141
|
+
Timecop.travel(Time.now + start_worker_delay)
|
|
142
|
+
|
|
143
|
+
monitors.each do |m|
|
|
144
|
+
m.send_stop(true)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
sleep(3)
|
|
148
|
+
|
|
149
|
+
# The first worker should restart immediately.
|
|
150
|
+
monitors.count { |m| m.alive? }.should satisfy { |c| 0 < c && c < workers }
|
|
151
|
+
|
|
152
|
+
# `start_worker_delay` uses `sleep` inside, so Timecop can't skip this wait.
|
|
153
|
+
sleep(start_worker_delay * workers)
|
|
154
|
+
monitors.count { |m| m.alive? }.should == workers
|
|
155
|
+
ensure
|
|
156
|
+
server.stop(true)
|
|
157
|
+
t.join
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
context 'with both options' do
|
|
163
|
+
let(:start_worker_delay) { 3 }
|
|
164
|
+
let(:restart_worker_interval) { 10 }
|
|
165
|
+
|
|
166
|
+
it do
|
|
167
|
+
t = Thread.new { server.main }
|
|
168
|
+
|
|
169
|
+
begin
|
|
170
|
+
wait_for_fork
|
|
171
|
+
|
|
172
|
+
# Initial starts are delayed too, so set longer timeout.
|
|
173
|
+
# (`start_worker_delay` uses `sleep` inside, so Timecop can't skip this wait.)
|
|
174
|
+
Timeout.timeout(start_worker_delay * workers) do
|
|
175
|
+
sleep(0.5) until monitors.count { |m| m && m.alive? } == workers
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
monitors.each do |m|
|
|
179
|
+
m.send_stop(true)
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
# Wait for all workers to stop and to be set restarting time
|
|
183
|
+
Timeout.timeout(5) do
|
|
184
|
+
sleep(0.5) until monitors.count { |m| m.alive? || m.restart_at.nil? } == 0
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
Timecop.freeze
|
|
188
|
+
|
|
189
|
+
mergin_time = 3
|
|
190
|
+
|
|
191
|
+
Timecop.freeze(Time.now + restart_worker_interval - mergin_time)
|
|
192
|
+
sleep(1.5)
|
|
193
|
+
monitors.count { |m| m.alive? }.should == 0
|
|
194
|
+
|
|
195
|
+
Timecop.travel(Time.now + 2 * mergin_time)
|
|
196
|
+
sleep(1.5)
|
|
197
|
+
monitors.count { |m| m.alive? }.should satisfy { |c| 0 < c && c < workers }
|
|
198
|
+
|
|
199
|
+
# `start_worker_delay` uses `sleep` inside, so Timecop can't skip this wait.
|
|
200
|
+
sleep(start_worker_delay * workers)
|
|
201
|
+
monitors.count { |m| m.alive? }.should == workers
|
|
202
|
+
ensure
|
|
203
|
+
server.stop(true)
|
|
204
|
+
t.join
|
|
18
205
|
end
|
|
19
|
-
test_state(:worker_run).should == 2
|
|
20
|
-
ensure
|
|
21
|
-
s.stop(true)
|
|
22
|
-
t.join
|
|
23
206
|
end
|
|
24
207
|
end
|
|
25
208
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
require 'thread'
|
|
3
3
|
require 'yaml'
|
|
4
|
+
require 'timecop'
|
|
4
5
|
|
|
5
6
|
def reset_test_state
|
|
6
7
|
FileUtils.mkdir_p 'tmp'
|
|
@@ -165,8 +166,9 @@ module TestWorker
|
|
|
165
166
|
|
|
166
167
|
def run
|
|
167
168
|
incr_test_state :worker_run
|
|
168
|
-
|
|
169
|
-
|
|
169
|
+
# This means this worker will automatically finish after 50 seconds.
|
|
170
|
+
10.times do
|
|
171
|
+
# repeats multiple times because signal handlers
|
|
170
172
|
# interrupts wait
|
|
171
173
|
@stop_flag.wait(5.0)
|
|
172
174
|
end
|
|
@@ -252,16 +254,25 @@ end
|
|
|
252
254
|
|
|
253
255
|
shared_context 'test server and worker' do
|
|
254
256
|
before { reset_test_state }
|
|
257
|
+
after { Timecop.return }
|
|
258
|
+
|
|
259
|
+
unless self.const_defined?(:WAIT_RATIO)
|
|
260
|
+
if ServerEngine.windows?
|
|
261
|
+
WAIT_RATIO = 2
|
|
262
|
+
else
|
|
263
|
+
WAIT_RATIO = 1
|
|
264
|
+
end
|
|
265
|
+
end
|
|
255
266
|
|
|
256
267
|
def wait_for_fork
|
|
257
|
-
sleep
|
|
268
|
+
sleep 1.5 * WAIT_RATIO
|
|
258
269
|
end
|
|
259
270
|
|
|
260
271
|
def wait_for_stop
|
|
261
|
-
sleep 0.8
|
|
272
|
+
sleep 0.8 * WAIT_RATIO
|
|
262
273
|
end
|
|
263
274
|
|
|
264
275
|
def wait_for_restart
|
|
265
|
-
sleep 1.5
|
|
276
|
+
sleep 1.5 * WAIT_RATIO
|
|
266
277
|
end
|
|
267
278
|
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/supervisor_spec.rb
CHANGED
|
@@ -191,13 +191,19 @@ describe ServerEngine::Supervisor do
|
|
|
191
191
|
sv, t = start_supervisor(RunErrorWorker, server_restart_wait: 1, command_sender: sender)
|
|
192
192
|
|
|
193
193
|
begin
|
|
194
|
-
sleep 2.
|
|
194
|
+
sleep 2.5
|
|
195
195
|
ensure
|
|
196
196
|
sv.stop(true)
|
|
197
197
|
t.join
|
|
198
198
|
end
|
|
199
199
|
|
|
200
|
-
|
|
200
|
+
if ServerEngine.windows?
|
|
201
|
+
# Because launching a process on Windows is high cost,
|
|
202
|
+
# it doesn't often reach to 3.
|
|
203
|
+
test_state(:worker_run).should <= 3
|
|
204
|
+
else
|
|
205
|
+
test_state(:worker_run).should == 3
|
|
206
|
+
end
|
|
201
207
|
end
|
|
202
208
|
end
|
|
203
209
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
describe ServerEngine::WinSock do
|
|
2
|
+
# On Ruby 3.0, you need to use fiddle 1.0.8 or later to retrieve a correct
|
|
3
|
+
# error code. In addition, you need to specify the path of fiddle by RUBYLIB
|
|
4
|
+
# or `ruby -I` when you use RubyInstaller because it loads Ruby's bundled
|
|
5
|
+
# fiddle before initializing gem.
|
|
6
|
+
# See also:
|
|
7
|
+
# * https://github.com/ruby/fiddle/issues/72
|
|
8
|
+
# * https://bugs.ruby-lang.org/issues/17813
|
|
9
|
+
# * https://github.com/oneclick/rubyinstaller2/blob/8225034c22152d8195bc0aabc42a956c79d6c712/lib/ruby_installer/build/dll_directory.rb
|
|
10
|
+
context 'last_error' do
|
|
11
|
+
it 'bind error' do
|
|
12
|
+
expect(WinSock.bind(0, nil, 0)).to be -1
|
|
13
|
+
WSAENOTSOCK = 10038
|
|
14
|
+
expect(WinSock.last_error).to be WSAENOTSOCK
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end if ServerEngine.windows?
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: serverengine
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sadayuki Furuhashi
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2022-06-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: sigdump
|
|
@@ -80,6 +80,20 @@ dependencies:
|
|
|
80
80
|
- - "~>"
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: 0.9.4
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: timecop
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - "~>"
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: 0.9.5
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - "~>"
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: 0.9.5
|
|
83
97
|
description: A framework to implement robust multiprocess servers like Unicorn
|
|
84
98
|
email:
|
|
85
99
|
- frsyuki@gmail.com
|
|
@@ -87,16 +101,16 @@ executables: []
|
|
|
87
101
|
extensions: []
|
|
88
102
|
extra_rdoc_files: []
|
|
89
103
|
files:
|
|
104
|
+
- ".github/workflows/linux.yml"
|
|
105
|
+
- ".github/workflows/windows.yml"
|
|
90
106
|
- ".gitignore"
|
|
91
107
|
- ".rspec"
|
|
92
|
-
- ".travis.yml"
|
|
93
108
|
- Changelog
|
|
94
109
|
- Gemfile
|
|
95
110
|
- LICENSE
|
|
96
111
|
- NOTICE
|
|
97
112
|
- README.md
|
|
98
113
|
- Rakefile
|
|
99
|
-
- appveyor.yml
|
|
100
114
|
- examples/server.rb
|
|
101
115
|
- examples/spawn_worker_script.rb
|
|
102
116
|
- lib/serverengine.rb
|
|
@@ -134,6 +148,7 @@ files:
|
|
|
134
148
|
- spec/socket_manager_spec.rb
|
|
135
149
|
- spec/spec_helper.rb
|
|
136
150
|
- spec/supervisor_spec.rb
|
|
151
|
+
- spec/winsock_spec.rb
|
|
137
152
|
homepage: https://github.com/fluent/serverengine
|
|
138
153
|
licenses:
|
|
139
154
|
- Apache 2.0
|
|
@@ -153,7 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
153
168
|
- !ruby/object:Gem::Version
|
|
154
169
|
version: '0'
|
|
155
170
|
requirements: []
|
|
156
|
-
rubygems_version: 3.
|
|
171
|
+
rubygems_version: 3.3.7
|
|
157
172
|
signing_key:
|
|
158
173
|
specification_version: 4
|
|
159
174
|
summary: ServerEngine - multiprocess server framework
|
|
@@ -168,3 +183,4 @@ test_files:
|
|
|
168
183
|
- spec/socket_manager_spec.rb
|
|
169
184
|
- spec/spec_helper.rb
|
|
170
185
|
- spec/supervisor_spec.rb
|
|
186
|
+
- spec/winsock_spec.rb
|
data/.travis.yml
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
language: ruby
|
|
2
|
-
|
|
3
|
-
rvm:
|
|
4
|
-
- 2.4.9
|
|
5
|
-
- 2.5.7
|
|
6
|
-
- 2.6.5
|
|
7
|
-
- 2.7
|
|
8
|
-
- ruby-head
|
|
9
|
-
|
|
10
|
-
branches:
|
|
11
|
-
only:
|
|
12
|
-
- master
|
|
13
|
-
|
|
14
|
-
script: bundle exec rake spec
|
|
15
|
-
|
|
16
|
-
before_install:
|
|
17
|
-
- gem update bundler
|
|
18
|
-
|
|
19
|
-
matrix:
|
|
20
|
-
allow_failures:
|
|
21
|
-
- rvm: ruby-head
|
data/appveyor.yml
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
install:
|
|
3
|
-
- SET PATH=C:\Ruby%ruby_version%\bin;%PATH%
|
|
4
|
-
- ruby --version
|
|
5
|
-
- gem --version
|
|
6
|
-
- bundle install
|
|
7
|
-
build: off
|
|
8
|
-
test_script:
|
|
9
|
-
- bundle exec rake -rdevkit
|
|
10
|
-
|
|
11
|
-
environment:
|
|
12
|
-
matrix:
|
|
13
|
-
- ruby_version: "23-x64"
|
|
14
|
-
devkit: C:\Ruby23-x64\DevKit
|
|
15
|
-
- ruby_version: "23"
|
|
16
|
-
devkit: C:\Ruby23\DevKit
|
|
17
|
-
- ruby_version: "22-x64"
|
|
18
|
-
devkit: C:\Ruby23-x64\DevKit
|
|
19
|
-
- ruby_version: "22"
|
|
20
|
-
devkit: C:\Ruby23\DevKit
|
|
21
|
-
- ruby_version: "21-x64"
|
|
22
|
-
devkit: C:\Ruby23-x64\DevKit
|
|
23
|
-
- ruby_version: "21"
|
|
24
|
-
devkit: C:\Ruby23\DevKit
|