zold 0.13.34 → 0.13.35
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +1 -1
- data/README.md +6 -5
- data/bin/zold +6 -1
- data/fixtures/scripts/_head.sh +4 -2
- data/fixtures/scripts/push-and-pull.sh +1 -1
- data/lib/zold/amount.rb +5 -0
- data/lib/zold/atomic_file.rb +3 -3
- data/lib/zold/backtrace.rb +7 -1
- data/lib/zold/commands/clean.rb +1 -1
- data/lib/zold/commands/create.rb +2 -2
- data/lib/zold/commands/diff.rb +2 -2
- data/lib/zold/commands/fetch.rb +8 -4
- data/lib/zold/commands/merge.rb +3 -3
- data/lib/zold/commands/node.rb +32 -11
- data/lib/zold/commands/propagate.rb +1 -1
- data/lib/zold/commands/push.rb +14 -9
- data/lib/zold/commands/remote.rb +4 -2
- data/lib/zold/commands/routines/bonuses.rb +9 -5
- data/lib/zold/commands/routines/spread.rb +4 -1
- data/lib/zold/commands/show.rb +2 -2
- data/lib/zold/commands/taxes.rb +2 -1
- data/lib/zold/http.rb +9 -1
- data/lib/zold/id.rb +4 -0
- data/lib/zold/json_page.rb +43 -0
- data/lib/zold/metronome.rb +15 -15
- data/lib/zold/node/async_entrance.rb +81 -0
- data/lib/zold/node/entrance.rb +26 -91
- data/lib/zold/node/farm.rb +21 -15
- data/lib/zold/node/front.rb +30 -17
- data/lib/zold/node/safe_entrance.rb +78 -0
- data/lib/zold/node/spread_entrance.rb +105 -0
- data/lib/zold/patch.rb +7 -5
- data/lib/zold/remotes.rb +19 -7
- data/lib/zold/txn.rb +3 -1
- data/lib/zold/version.rb +1 -1
- data/lib/zold/wallet.rb +3 -3
- data/lib/zold/wallets.rb +2 -0
- data/test/commands/routines/test_bonuses.rb +2 -2
- data/test/commands/test_fetch.rb +9 -35
- data/test/commands/test_merge.rb +1 -1
- data/test/commands/test_node.rb +4 -2
- data/test/commands/test_push.rb +4 -2
- data/test/node/fake_entrance.rb +41 -0
- data/test/node/fake_node.rb +37 -29
- data/test/node/test_async_entrance.rb +88 -0
- data/test/node/test_entrance.rb +1 -19
- data/test/node/test_front.rb +3 -2
- data/test/node/test_safe_entrance.rb +56 -0
- data/test/node/test_spread_entrance.rb +60 -0
- data/test/test__helper.rb +0 -1
- data/test/test_atomic_file.rb +39 -0
- data/test/test_backtrace.rb +40 -0
- data/test/test_metronome.rb +0 -1
- data/test/test_patch.rb +17 -0
- data/test/test_remotes.rb +21 -0
- data/test/test_wallet.rb +14 -1
- data/test/test_zold.rb +1 -0
- metadata +16 -2
@@ -51,10 +51,13 @@ module Zold
|
|
51
51
|
(@opts['ignore-score-weakness'] ? ['--ignore-score-weakness'] : [])
|
52
52
|
)
|
53
53
|
return if winners.empty?
|
54
|
-
|
54
|
+
unless @wallets.find(Id.new(@opts['bonus-wallet'])).exists?
|
55
55
|
Pull.new(wallets: @wallets, remotes: @remotes, copies: @copies, log: @log).run(
|
56
|
-
['pull', @opts['bonus-wallet']]
|
56
|
+
['pull', @opts['bonus-wallet']] +
|
57
|
+
(@opts['ignore-score-weakness'] ? ['--ignore-score-weakness'] : [])
|
57
58
|
)
|
59
|
+
end
|
60
|
+
winners.each do |score|
|
58
61
|
Pay.new(wallets: @wallets, remotes: @remotes, log: @log).run(
|
59
62
|
[
|
60
63
|
'pay', @opts['bonus-wallet'], score.invoice, @opts['bonus-amount'].to_s,
|
@@ -62,10 +65,11 @@ module Zold
|
|
62
65
|
'--private-key', @opts['private-key']
|
63
66
|
]
|
64
67
|
)
|
65
|
-
Push.new(wallets: @wallets, remotes: @remotes, log: @log).run(
|
66
|
-
['push', @opts['bonus-wallet']]
|
67
|
-
)
|
68
68
|
end
|
69
|
+
Push.new(wallets: @wallets, remotes: @remotes, log: @log).run(
|
70
|
+
['push', @opts['bonus-wallet']] +
|
71
|
+
(@opts['ignore-score-weakness'] ? ['--ignore-score-weakness'] : [])
|
72
|
+
)
|
69
73
|
end
|
70
74
|
end
|
71
75
|
end
|
@@ -41,7 +41,10 @@ module Zold
|
|
41
41
|
|
42
42
|
def exec(_ = 0)
|
43
43
|
sleep(60) unless @opts['routine-immediately']
|
44
|
-
@
|
44
|
+
@wallets.all.sample(10).map do |w|
|
45
|
+
id = Id.new(w)
|
46
|
+
@entrance.push(id, File.read(@wallets.find(id).path))
|
47
|
+
end
|
45
48
|
end
|
46
49
|
end
|
47
50
|
end
|
data/lib/zold/commands/show.rb
CHANGED
@@ -50,8 +50,8 @@ Available options:"
|
|
50
50
|
List.new(wallets: @wallets, log: @log).run(args)
|
51
51
|
else
|
52
52
|
total = Amount::ZERO
|
53
|
-
mine.each do |id|
|
54
|
-
total += show(@wallets.find(
|
53
|
+
mine.map { |i| Id.new(i) }.each do |id|
|
54
|
+
total += show(@wallets.find(id), opts)
|
55
55
|
end
|
56
56
|
total
|
57
57
|
end
|
data/lib/zold/commands/taxes.rb
CHANGED
@@ -24,6 +24,7 @@ require 'rainbow'
|
|
24
24
|
require_relative 'args'
|
25
25
|
require_relative 'pay'
|
26
26
|
require_relative '../log'
|
27
|
+
require_relative '../json_page'
|
27
28
|
require_relative '../score'
|
28
29
|
require_relative '../id'
|
29
30
|
require_relative '../tax'
|
@@ -120,7 +121,7 @@ Available options:"
|
|
120
121
|
@remotes.iterate(@log) do |r|
|
121
122
|
res = r.http.get
|
122
123
|
r.assert_code(200, res)
|
123
|
-
json =
|
124
|
+
json = JsonPage.new(res.body).to_hash
|
124
125
|
score = Score.parse_json(json['score'])
|
125
126
|
r.assert_valid_score(score)
|
126
127
|
r.assert_score_strength(score)
|
data/lib/zold/http.rb
CHANGED
@@ -42,11 +42,18 @@ module Zold
|
|
42
42
|
# reboot itself, if the version is higher.
|
43
43
|
VERSION_HEADER = 'X-Zold-Version'.freeze
|
44
44
|
|
45
|
-
|
45
|
+
# HTTP header we add, in order to inform the node about our
|
46
|
+
# network. This is done in order to isolate test networks from
|
47
|
+
# production one.
|
48
|
+
NETWORK_HEADER = 'X-Zold-Network'.freeze
|
49
|
+
|
50
|
+
def initialize(uri, score = Score::ZERO, network: 'test')
|
46
51
|
raise 'URI can\'t be nil' if uri.nil?
|
47
52
|
@uri = uri.is_a?(String) ? URI(uri) : uri
|
48
53
|
raise 'Score can\'t be nil' if score.nil?
|
49
54
|
@score = score
|
55
|
+
raise 'Network can\'t be nil' if network.nil?
|
56
|
+
@network = network
|
50
57
|
end
|
51
58
|
|
52
59
|
def get
|
@@ -105,6 +112,7 @@ module Zold
|
|
105
112
|
'Accept-Encoding': 'gzip'
|
106
113
|
}
|
107
114
|
headers[Http::VERSION_HEADER] = VERSION
|
115
|
+
headers[Http::NETWORK_HEADER] = @network
|
108
116
|
headers[Http::SCORE_HEADER] = @score.reduced(4).to_text if @score.valid? && !@score.expired? && @score.value > 3
|
109
117
|
headers
|
110
118
|
end
|
data/lib/zold/id.rb
CHANGED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Copyright (c) 2018 Yegor Bugayenko
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the 'Software'), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in all
|
11
|
+
# copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
# SOFTWARE.
|
20
|
+
|
21
|
+
require 'json'
|
22
|
+
|
23
|
+
# JSON page.
|
24
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
25
|
+
# Copyright:: Copyright (c) 2018 Yegor Bugayenko
|
26
|
+
# License:: MIT
|
27
|
+
module Zold
|
28
|
+
# JSON page
|
29
|
+
class JsonPage
|
30
|
+
def initialize(text)
|
31
|
+
raise 'JSON text can\'t be nil' if text.nil?
|
32
|
+
raise 'JSON must be of type String' unless text.is_a?(String)
|
33
|
+
@text = text
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_hash
|
37
|
+
raise 'JSON is empty, can\'t parse' if @text.empty?
|
38
|
+
JSON.parse(@text)
|
39
|
+
rescue JSON::ParserError => e
|
40
|
+
raise "Failed to parse JSON (#{e.message}): #{@text}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/zold/metronome.rb
CHANGED
@@ -48,11 +48,12 @@ module Zold
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def start
|
51
|
+
alive = true
|
51
52
|
@routines.each do |r|
|
52
53
|
@threads << Thread.start do
|
53
54
|
Thread.current.name = r.class.name
|
54
55
|
step = 0
|
55
|
-
|
56
|
+
while alive
|
56
57
|
start = Time.now
|
57
58
|
begin
|
58
59
|
r.exec(step)
|
@@ -69,21 +70,20 @@ module Zold
|
|
69
70
|
begin
|
70
71
|
yield(self)
|
71
72
|
ensure
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
@log.info("
|
73
|
+
alive = false
|
74
|
+
@log.info("Stopping the metronome with #{@threads.count} threads: #{@threads.map(&:name).join(', ')}")
|
75
|
+
start = Time.now
|
76
|
+
@threads.each do |t|
|
77
|
+
tstart = Time.now
|
78
|
+
if t.join(60)
|
79
|
+
@log.info("Thread #{t.name} finished in #{(Time.now - tstart).round(2)}s")
|
80
|
+
else
|
81
|
+
t.exit
|
82
|
+
@log.info("Thread #{t.name} killed in #{(Time.now - tstart).round(2)}s")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
@log.info("Metronome stopped in #{(Time.now - start).round(2)}s, #{@failures.count} failures")
|
85
86
|
end
|
86
|
-
@log.info("Metronome stopped in #{(Time.now - start).round(2)}s, #{@failures.count} failures")
|
87
87
|
end
|
88
88
|
end
|
89
89
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# Copyright (c) 2018 Yegor Bugayenko
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the 'Software'), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in all
|
11
|
+
# copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
# SOFTWARE.
|
20
|
+
|
21
|
+
require 'concurrent'
|
22
|
+
require_relative '../log'
|
23
|
+
require_relative '../verbose_thread'
|
24
|
+
|
25
|
+
# The async entrance of the web front.
|
26
|
+
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
27
|
+
# Copyright:: Copyright (c) 2018 Yegor Bugayenko
|
28
|
+
# License:: MIT
|
29
|
+
module Zold
|
30
|
+
# The entrance
|
31
|
+
class AsyncEntrance
|
32
|
+
def initialize(entrance, log: Log::Quiet.new)
|
33
|
+
raise 'Entrance can\'t be nil' if entrance.nil?
|
34
|
+
@entrance = entrance
|
35
|
+
raise 'Log can\'t be nil' if log.nil?
|
36
|
+
@log = log
|
37
|
+
end
|
38
|
+
|
39
|
+
def start
|
40
|
+
@entrance.start do
|
41
|
+
@pool = Concurrent::FixedThreadPool.new(
|
42
|
+
Concurrent.processor_count,
|
43
|
+
max_queue: Concurrent.processor_count * 10,
|
44
|
+
fallback_policy: :abort
|
45
|
+
)
|
46
|
+
begin
|
47
|
+
yield(self)
|
48
|
+
ensure
|
49
|
+
@log.info("Stopping async entrance, pool length is #{@pool.length}, queue length is #{@pool.queue_length}")
|
50
|
+
@pool.shutdown
|
51
|
+
if @pool.wait_for_termination(10)
|
52
|
+
@log.info('Async entrance terminated peacefully')
|
53
|
+
else
|
54
|
+
@pool.kill
|
55
|
+
@log.info('Async entrance was killed')
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_json
|
62
|
+
@entrance.to_json.merge(
|
63
|
+
'pool.completed_task_count': @pool.completed_task_count,
|
64
|
+
'pool.largest_length': @pool.largest_length,
|
65
|
+
'pool.length': @pool.length,
|
66
|
+
'pool.queue_length': @pool.queue_length,
|
67
|
+
'pool.running': @pool.running?
|
68
|
+
)
|
69
|
+
end
|
70
|
+
|
71
|
+
def push(id, body)
|
72
|
+
@pool.post do
|
73
|
+
VerboseThread.new(@log).run(true) do
|
74
|
+
@entrance.push(id, body)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
@log.debug("Pushed #{id}/#{body.length}b to #{@entrance.class.name}, \
|
78
|
+
pool: #{@pool.length}/#{@pool.queue_length}")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/lib/zold/node/entrance.rb
CHANGED
@@ -18,17 +18,15 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
19
|
# SOFTWARE.
|
20
20
|
|
21
|
-
require 'concurrent'
|
22
21
|
require 'tempfile'
|
23
|
-
require_relative 'emission'
|
24
22
|
require_relative '../log'
|
25
23
|
require_relative '../remotes'
|
26
24
|
require_relative '../copies'
|
27
25
|
require_relative '../tax'
|
26
|
+
require_relative '../commands/clean'
|
28
27
|
require_relative '../commands/merge'
|
29
28
|
require_relative '../commands/fetch'
|
30
29
|
require_relative '../commands/push'
|
31
|
-
require_relative '../commands/clean'
|
32
30
|
|
33
31
|
# The entrance of the web front.
|
34
32
|
# Author:: Yegor Bugayenko (yegor256@gmail.com)
|
@@ -38,115 +36,52 @@ module Zold
|
|
38
36
|
# The entrance
|
39
37
|
class Entrance
|
40
38
|
def initialize(wallets, remotes, copies, address, log: Log::Quiet.new)
|
39
|
+
raise 'Wallets can\'t be nil' if wallets.nil?
|
40
|
+
raise 'Wallets must be of type Wallets' unless wallets.is_a?(Wallets)
|
41
41
|
@wallets = wallets
|
42
|
+
raise 'Remotes can\'t be nil' if remotes.nil?
|
43
|
+
raise "Remotes must be of type Remotes: #{remotes.class.name}" unless remotes.is_a?(Remotes)
|
42
44
|
@remotes = remotes
|
45
|
+
raise 'Copies can\'t be nil' if copies.nil?
|
43
46
|
@copies = copies
|
47
|
+
raise 'Address can\'t be nil' if address.nil?
|
44
48
|
@address = address
|
49
|
+
raise 'Log can\'t be nil' if log.nil?
|
45
50
|
@log = log
|
46
|
-
@semaphores = Concurrent::Map.new
|
47
|
-
@push_mutex = Mutex.new
|
48
|
-
@modified = Set.new
|
49
|
-
@pool = Concurrent::FixedThreadPool.new(16, max_queue: 64, fallback_policy: :abort)
|
50
|
-
@pushes = Concurrent::FixedThreadPool.new(1, max_queue: 64, fallback_policy: :abort)
|
51
|
-
end
|
52
|
-
|
53
|
-
def to_json
|
54
|
-
{
|
55
|
-
'semaphores': @semaphores.size,
|
56
|
-
'modified': @modified.length,
|
57
|
-
'pool': {
|
58
|
-
'completed_task_count': @pool.completed_task_count,
|
59
|
-
'largest_length': @pool.largest_length,
|
60
|
-
'length': @pool.length,
|
61
|
-
'queue_length': @pool.queue_length
|
62
|
-
},
|
63
|
-
'pushes': {
|
64
|
-
'completed_task_count': @pushes.completed_task_count,
|
65
|
-
'largest_length': @pushes.largest_length,
|
66
|
-
'length': @pushes.length,
|
67
|
-
'queue_length': @pushes.queue_length
|
68
|
-
}
|
69
|
-
}
|
70
|
-
end
|
71
|
-
|
72
|
-
def push(id, body, sync: true)
|
73
|
-
check(body)
|
74
|
-
if sync
|
75
|
-
push_sync(id, body)
|
76
|
-
else
|
77
|
-
@pool.post do
|
78
|
-
push_sync(id, body)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
def check(body)
|
84
|
-
Tempfile.open do |f|
|
85
|
-
File.write(f.path, body)
|
86
|
-
wallet = Wallet.new(f)
|
87
|
-
break unless wallet.network == Wallet::MAIN_NETWORK
|
88
|
-
balance = wallet.balance
|
89
|
-
if balance.negative? && !wallet.root?
|
90
|
-
raise "The balance #{balance} of #{wallet.id} is negative and it's not a root wallet"
|
91
|
-
end
|
92
|
-
Emission.new(wallet).check
|
93
|
-
tax = Tax.new(wallet)
|
94
|
-
if tax.in_debt?
|
95
|
-
raise "Taxes are not paid, can't accept the wallet; the debt is #{tax.debt} (#{tax.debt.to_i} zents)"
|
96
|
-
end
|
97
|
-
end
|
98
51
|
end
|
99
52
|
|
100
|
-
def
|
101
|
-
|
102
|
-
@push_mutex.synchronize { @modified += ids }
|
103
|
-
@pushes.post { push_one } if @pushes.length < 2
|
53
|
+
def start
|
54
|
+
yield(self)
|
104
55
|
end
|
105
56
|
|
106
|
-
|
107
|
-
|
108
|
-
# Returns a list of modifed wallets (as Zold::Id)
|
109
|
-
def push_sync(id, body)
|
110
|
-
@semaphores.put_if_absent(id.to_s, Mutex.new)
|
111
|
-
@semaphores.get(id.to_s).synchronize do
|
112
|
-
start = Time.now
|
113
|
-
modified = push_unsafe(id, body)
|
114
|
-
if modified.empty?
|
115
|
-
@log.info("Accepted #{id} in #{(Time.now - start).round(2)}s \
|
116
|
-
and modified nothing (this is most likely a bug!)")
|
117
|
-
else
|
118
|
-
@log.info("Accepted #{id} in #{(Time.now - start).round(2)}s and modified #{modified.join(', ')}")
|
119
|
-
end
|
120
|
-
modified
|
121
|
-
end
|
57
|
+
def to_json
|
58
|
+
{}
|
122
59
|
end
|
123
60
|
|
124
61
|
# Returns a list of modifed wallets (as Zold::Id)
|
125
|
-
def
|
62
|
+
def push(id, body)
|
63
|
+
raise 'Id can\'t be nil' if id.nil?
|
64
|
+
raise 'Id must be of type Id' unless id.is_a?(Id)
|
65
|
+
raise 'Body can\'t be nil' if body.nil?
|
66
|
+
start = Time.now
|
126
67
|
copies = Copies.new(File.join(@copies, id.to_s))
|
127
68
|
localhost = '0.0.0.0'
|
128
69
|
copies.add(body, localhost, Remotes::PORT, 0)
|
129
|
-
|
130
|
-
|
131
|
-
|
70
|
+
unless @remotes.all.empty?
|
71
|
+
Fetch.new(
|
72
|
+
wallets: @wallets, remotes: @remotes, copies: copies.root, log: @log
|
73
|
+
).run(['fetch', id.to_s, "--ignore-node=#{@address}"])
|
74
|
+
end
|
132
75
|
modified = Merge.new(
|
133
76
|
wallets: @wallets, copies: copies.root, log: @log
|
134
77
|
).run(['merge', id.to_s, '--no-baseline'])
|
135
78
|
Clean.new(wallets: @wallets, copies: copies.root, log: @log).run(['clean', id.to_s])
|
136
79
|
copies.remove(localhost, Remotes::PORT)
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
def push_one
|
142
|
-
@push_mutex.synchronize do
|
143
|
-
id = @modified.to_a[0]
|
144
|
-
@modified.delete(id)
|
145
|
-
return if id.nil?
|
146
|
-
Push.new(
|
147
|
-
wallets: @wallets, remotes: @remotes, log: @log
|
148
|
-
).run(['push', "--ignore-node=#{@address}"] + [id.to_s])
|
80
|
+
unless modified.empty?
|
81
|
+
@log.info("Accepted #{id} in #{(Time.now - start).round(2)}s \
|
82
|
+
and modified #{modified.join(', ')}")
|
149
83
|
end
|
84
|
+
modified
|
150
85
|
end
|
151
86
|
end
|
152
87
|
end
|