bitferry 0.0.5 → 0.0.7
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/CHANGES.md +10 -0
- data/bin/bitferryfx +3 -0
- data/lib/bitferry/cli.rb +13 -12
- data/lib/bitferry/fx.rb +131 -0
- data/lib/bitferry.rb +77 -14
- metadata +50 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f67881c016dae96456c1d0b1baee9b3949eb9bfd46aa5f96cf7232d219c1d7d8
|
4
|
+
data.tar.gz: a5613291cc0b050ce243c8cf4142d8020c363100b0655cf6fcb405c0b380daba
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 670ea9837098777a1dfa56df30283f2d6fac41df303003a33cd1e80711d5c4967f09ab72ba2247dbdb00c57830f0310825a064397f5db463e0ee927de72ce6dc
|
7
|
+
data.tar.gz: 433fb3934ae559cf42eb0a20d051923c0dc5e93eed32f5249dc95a1dde1930d3982fb925efd64f03d418fa9fefa54a74959ec87118c3c26d05ba0927667ee0cf
|
data/CHANGES.md
CHANGED
data/bin/bitferryfx
ADDED
data/lib/bitferry/cli.rb
CHANGED
@@ -110,6 +110,7 @@ def obtain_password
|
|
110
110
|
end
|
111
111
|
|
112
112
|
|
113
|
+
Bitferry.ui = :cli
|
113
114
|
Bitferry.log.level = Logger::DEBUG if $DEBUG
|
114
115
|
|
115
116
|
|
@@ -120,7 +121,7 @@ Clamp do
|
|
120
121
|
|
121
122
|
|
122
123
|
option '--version', :flag, 'Print version' do
|
123
|
-
puts Bitferry::VERSION
|
124
|
+
$stdout.puts Bitferry::VERSION
|
124
125
|
exit
|
125
126
|
end
|
126
127
|
|
@@ -144,26 +145,26 @@ Clamp do
|
|
144
145
|
def execute
|
145
146
|
Bitferry.restore
|
146
147
|
unless (xs = Bitferry::Volume.intact).empty?
|
147
|
-
puts '# Intact volumes'
|
148
|
-
puts
|
148
|
+
$stdout.puts '# Intact volumes'
|
149
|
+
$stdout.puts
|
149
150
|
xs.each do |volume|
|
150
|
-
puts " #{volume.tag} #{volume.root}"
|
151
|
+
$stdout.puts " #{volume.tag} #{volume.root}"
|
151
152
|
end
|
152
153
|
end
|
153
154
|
unless (xs = Bitferry::Task.intact).empty?
|
154
|
-
puts
|
155
|
-
puts '# Intact tasks'
|
156
|
-
puts
|
155
|
+
$stdout.puts
|
156
|
+
$stdout.puts '# Intact tasks'
|
157
|
+
$stdout.puts
|
157
158
|
xs.each do |task|
|
158
|
-
puts " #{task.tag} #{task.show_status}"
|
159
|
+
$stdout.puts " #{task.tag} #{task.show_status}"
|
159
160
|
end
|
160
161
|
end
|
161
162
|
if !(xs = Bitferry::Task.stale).empty? && Bitferry.verbosity == :verbose
|
162
|
-
puts
|
163
|
-
puts '# Stale tasks'
|
164
|
-
puts
|
163
|
+
$stdout.puts
|
164
|
+
$stdout.puts '# Stale tasks'
|
165
|
+
$stdout.puts
|
165
166
|
xs.each do |task|
|
166
|
-
puts " #{task.tag} #{task.show_status}"
|
167
|
+
$stdout.puts " #{task.tag} #{task.show_status}"
|
167
168
|
end
|
168
169
|
end
|
169
170
|
end
|
data/lib/bitferry/fx.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'fox16'
|
2
|
+
require 'stringio'
|
3
|
+
require 'bitferry'
|
4
|
+
|
5
|
+
|
6
|
+
include Fox
|
7
|
+
|
8
|
+
|
9
|
+
class Output < StringIO
|
10
|
+
|
11
|
+
def initialize(app, output)
|
12
|
+
super('rw+')
|
13
|
+
@app = app
|
14
|
+
@output = output
|
15
|
+
@output.text = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def write(*args) = @app.runOnUiThread { @output.appendText(args.join) }
|
19
|
+
|
20
|
+
def flush = nil
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
class UI < FXMainWindow
|
26
|
+
|
27
|
+
def initialize(app)
|
28
|
+
super(@app = app, 'BitferryFX', width: 400, height: 300)
|
29
|
+
top_frame = FXVerticalFrame.new(self, opts: LAYOUT_FILL)
|
30
|
+
tabs = FXTabBook.new(top_frame, opts: LAYOUT_FILL)
|
31
|
+
output_tab = FXTabItem.new(tabs, 'Output')
|
32
|
+
@output = FXText.new(tabs)
|
33
|
+
tasks_tab = FXTabItem.new(tabs, 'Tasks')
|
34
|
+
@tasks = FXTable.new(tabs)
|
35
|
+
@tasks.tableStyle |= TABLE_COL_SIZABLE | TABLE_NO_COLSELECT | TABLE_READONLY
|
36
|
+
@tasks.rowHeaderMode = LAYOUT_FIX_WIDTH
|
37
|
+
@tasks.rowHeaderWidth = 0
|
38
|
+
volumes_tab = FXTabItem.new(tabs, 'Volumes')
|
39
|
+
@volumes = FXTable.new(tabs)
|
40
|
+
@volumes.tableStyle |= TABLE_COL_SIZABLE | TABLE_NO_COLSELECT | TABLE_READONLY
|
41
|
+
@volumes.rowHeaderMode = LAYOUT_FIX_WIDTH
|
42
|
+
@volumes.rowHeaderWidth = 0
|
43
|
+
@progress = FXProgressBar.new(top_frame, height: 16, opts: LAYOUT_FILL_X | LAYOUT_FIX_HEIGHT)
|
44
|
+
controls = FXPacker.new(top_frame, opts: LAYOUT_FILL_X)
|
45
|
+
@simulate = FXCheckButton.new(controls, "&Simulation mode (dry run)\tPrevent operations from making any on-disk changes")
|
46
|
+
@simulate.checkState = Bitferry.simulate?
|
47
|
+
@verbose = FXCheckButton.new(controls, "&Verbose mode\tOutput internal logging information")
|
48
|
+
@verbose.checkState = false
|
49
|
+
buttons = FXPacker.new(top_frame, opts: LAYOUT_FILL_X | PACK_UNIFORM_WIDTH | FRAME_SUNKEN)
|
50
|
+
@process = FXButton.new(buttons, "&Process\tProcess all intact tasks", opts: BUTTON_NORMAL | BUTTON_INITIAL | BUTTON_DEFAULT | LAYOUT_SIDE_LEFT)
|
51
|
+
@process.connect(SEL_COMMAND) { process }
|
52
|
+
@process.setFocus
|
53
|
+
@quit = FXButton.new(buttons, "&Quit\tStop any pending operations and exit", opts: BUTTON_NORMAL | LAYOUT_SIDE_RIGHT)
|
54
|
+
@quit.connect(SEL_COMMAND) { exit }
|
55
|
+
@reload = FXButton.new(buttons, "&Reload\tReread volumes and tasks to capture volume changes", opts: BUTTON_NORMAL | LAYOUT_SIDE_RIGHT)
|
56
|
+
@reload.connect(SEL_COMMAND) { reset }
|
57
|
+
@sensible = [@process, @reload] # Controls which must be disabled during processing
|
58
|
+
reset
|
59
|
+
end
|
60
|
+
|
61
|
+
def process
|
62
|
+
Bitferry.simulate = @simulate.checked?
|
63
|
+
Bitferry.verbosity = @verbose.checked? ? :verbose : :default
|
64
|
+
@progress.setBarColor(:blue)
|
65
|
+
@progress.progress = 0
|
66
|
+
@output.text = nil
|
67
|
+
Thread.new {
|
68
|
+
@app.runOnUiThread { @sensible.each(&:disable) }
|
69
|
+
begin
|
70
|
+
Bitferry.process { |total, processed, failed|
|
71
|
+
@app.runOnUiThread {
|
72
|
+
@progress.setBarColor(:red) if failed > 0
|
73
|
+
@progress.progress = processed
|
74
|
+
@progress.total = total
|
75
|
+
}
|
76
|
+
}
|
77
|
+
ensure
|
78
|
+
@app.runOnUiThread { @sensible.each(&:enable) }
|
79
|
+
end
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
def reset
|
84
|
+
Bitferry.restore
|
85
|
+
@progress.progress = 0
|
86
|
+
$stdout = Output.new(@app, @output)
|
87
|
+
Bitferry::Logging.log = log = Logger.new($stdout)
|
88
|
+
log.progname = :bitferryfx
|
89
|
+
log.level = Logger::WARN
|
90
|
+
#
|
91
|
+
@volumes.setTableSize(Bitferry::Volume.intact.size, 2)
|
92
|
+
@volumes.setColumnText(0, 'Volume')
|
93
|
+
@volumes.setColumnText(1, 'Root')
|
94
|
+
i = 0
|
95
|
+
Bitferry::Volume.intact.each do |v|
|
96
|
+
@volumes.setItemText(i, 0, v.tag)
|
97
|
+
@volumes.setItemText(i, 1, v.root.to_s)
|
98
|
+
i += 1
|
99
|
+
end
|
100
|
+
#
|
101
|
+
@tasks.setTableSize(Bitferry::Task.intact.size, 4)
|
102
|
+
@tasks.setColumnText(0, 'Task')
|
103
|
+
@tasks.setColumnText(1, 'Operation')
|
104
|
+
@tasks.setColumnText(2, 'Source')
|
105
|
+
@tasks.setColumnText(3, 'Destination')
|
106
|
+
i = 0
|
107
|
+
Bitferry::Task.intact.each do |t|
|
108
|
+
@tasks.setItemText(i, 0, t.tag)
|
109
|
+
@tasks.setItemText(i, 1, t.show_operation)
|
110
|
+
@tasks.setItemText(i, 2, t.source.show_status)
|
111
|
+
@tasks.setItemText(i, 3, t.destination.show_status)
|
112
|
+
i += 1
|
113
|
+
end
|
114
|
+
#
|
115
|
+
end
|
116
|
+
|
117
|
+
def create
|
118
|
+
super
|
119
|
+
show(PLACEMENT_SCREEN)
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
FXApp.new do |app|
|
126
|
+
Bitferry.verbosity = :verbose
|
127
|
+
Bitferry.ui = :gui
|
128
|
+
UI.new(app)
|
129
|
+
app.create
|
130
|
+
app.run
|
131
|
+
end
|
data/lib/bitferry.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'date'
|
3
3
|
require 'open3'
|
4
|
+
require 'base64'
|
4
5
|
require 'logger'
|
6
|
+
require 'openssl'
|
5
7
|
require 'pathname'
|
6
|
-
require 'neatjson'
|
7
8
|
require 'rbconfig'
|
8
9
|
require 'fileutils'
|
9
10
|
require 'shellwords'
|
@@ -12,7 +13,7 @@ require 'shellwords'
|
|
12
13
|
module Bitferry
|
13
14
|
|
14
15
|
|
15
|
-
VERSION = '0.0.
|
16
|
+
VERSION = '0.0.7'
|
16
17
|
|
17
18
|
|
18
19
|
module Logging
|
@@ -24,6 +25,7 @@ module Bitferry
|
|
24
25
|
end
|
25
26
|
@log
|
26
27
|
end
|
28
|
+
def self.log=(log) @log = log end
|
27
29
|
def log = Logging.log
|
28
30
|
end
|
29
31
|
|
@@ -85,9 +87,11 @@ module Bitferry
|
|
85
87
|
end
|
86
88
|
|
87
89
|
|
88
|
-
def self.
|
90
|
+
def self.intact_tasks = Volume.intact.collect { |volume| volume.intact_tasks }.flatten.uniq
|
91
|
+
|
92
|
+
def self.process(*tags, &block)
|
89
93
|
log.info('processing tasks')
|
90
|
-
tasks =
|
94
|
+
tasks = intact_tasks
|
91
95
|
if tags.empty?
|
92
96
|
process = tasks
|
93
97
|
else
|
@@ -102,7 +106,17 @@ module Bitferry
|
|
102
106
|
end
|
103
107
|
end
|
104
108
|
end
|
105
|
-
|
109
|
+
tasks = process.uniq
|
110
|
+
total = tasks.size
|
111
|
+
processed = 0
|
112
|
+
failed = 0
|
113
|
+
result = tasks.all? do |task|
|
114
|
+
r = task.process
|
115
|
+
processed += 1
|
116
|
+
failed += 1 unless r
|
117
|
+
yield(total, processed, failed) if block_given?
|
118
|
+
r
|
119
|
+
end
|
106
120
|
result ? log.info('tasks processed') : log.warn('task process failure(s) reported')
|
107
121
|
result
|
108
122
|
end
|
@@ -137,6 +151,11 @@ module Bitferry
|
|
137
151
|
def self.verbosity=(mode) @verbosity = mode end
|
138
152
|
|
139
153
|
|
154
|
+
@ui = :cli
|
155
|
+
def self.ui = @ui
|
156
|
+
def self.ui=(ui) @ui = ui end
|
157
|
+
|
158
|
+
|
140
159
|
# Return true if run in the real Windows environment (e.g. not in real *NIX or various emulation layers such as MSYS, Cygwin etc.)
|
141
160
|
def self.windows?
|
142
161
|
@windows ||= /^(mingw)/.match?(RbConfig::CONFIG['target_os']) # RubyInstaller's MRI, other MinGW-build MRI
|
@@ -388,6 +407,7 @@ module Bitferry
|
|
388
407
|
|
389
408
|
|
390
409
|
def store
|
410
|
+
require 'neatjson'
|
391
411
|
tasks.each(&:commit)
|
392
412
|
hash = JSON.neat_generate(externalize, short: false, wrap: 200, afterColon: 1, afterComma: 1)
|
393
413
|
if Bitferry.simulate?
|
@@ -657,9 +677,9 @@ module Bitferry
|
|
657
677
|
def self.exec(*args)
|
658
678
|
cmd = [executable] + args
|
659
679
|
log.debug(cmd.collect(&:shellescape).join(' '))
|
660
|
-
stdout, status = Open3.
|
680
|
+
stdout, status = Open3.capture2e(*cmd)
|
661
681
|
unless status.success?
|
662
|
-
msg = "rclone exit code #{status.
|
682
|
+
msg = "rclone exit code #{status.exitstatus}"
|
663
683
|
log.error(msg)
|
664
684
|
raise RuntimeError, msg
|
665
685
|
end
|
@@ -667,10 +687,26 @@ module Bitferry
|
|
667
687
|
end
|
668
688
|
|
669
689
|
|
670
|
-
|
690
|
+
# https://github.com/rclone/rclone/blob/master/fs/config/obscure/obscure.go
|
691
|
+
SECRET = "\x9c\x93\x5b\x48\x73\x0a\x55\x4d\x6b\xfd\x7c\x63\xc8\x86\xa9\x2b\xd3\x90\x19\x8e\xb8\x12\x8a\xfb\xf4\xde\x16\x2b\x8b\x95\xf6\x38"
|
671
692
|
|
672
693
|
|
673
|
-
def self.
|
694
|
+
def self.obscure(plain)
|
695
|
+
cipher = OpenSSL::Cipher.new('AES-256-CTR')
|
696
|
+
cipher.encrypt
|
697
|
+
cipher.key = SECRET
|
698
|
+
Base64.urlsafe_encode64(cipher.random_iv + cipher.update(plain) + cipher.final, padding: false)
|
699
|
+
end
|
700
|
+
|
701
|
+
|
702
|
+
def self.reveal(token)
|
703
|
+
data = Base64.urlsafe_decode64(token)
|
704
|
+
cipher = OpenSSL::Cipher.new('AES-256-CTR')
|
705
|
+
cipher.decrypt
|
706
|
+
cipher.key = SECRET
|
707
|
+
cipher.iv = data[0...cipher.iv_len]
|
708
|
+
cipher.update(data[cipher.iv_len..-1]) + cipher.final
|
709
|
+
end
|
674
710
|
|
675
711
|
|
676
712
|
class Encryption
|
@@ -872,9 +908,18 @@ module Bitferry
|
|
872
908
|
def execute(*args)
|
873
909
|
cmd = [Rclone.executable] + args
|
874
910
|
cms = cmd.collect(&:shellescape).join(' ')
|
875
|
-
puts cms if Bitferry.verbosity == :verbose
|
911
|
+
$stdout.puts cms if Bitferry.verbosity == :verbose
|
876
912
|
log.info(cms)
|
877
|
-
|
913
|
+
if Bitferry.ui == :gui
|
914
|
+
t = nil
|
915
|
+
Open3.popen2e(*cmd) do |i, oe, thr|
|
916
|
+
while x = oe.gets; $stdout.puts(x) end
|
917
|
+
t = thr
|
918
|
+
end
|
919
|
+
status = t.value
|
920
|
+
else
|
921
|
+
status = Open3.pipeline(cmd).first
|
922
|
+
end
|
878
923
|
raise RuntimeError, "rclone exit code #{status.exitstatus}" unless status.success?
|
879
924
|
status.success?
|
880
925
|
end
|
@@ -1058,8 +1103,9 @@ module Bitferry
|
|
1058
1103
|
def execute(*args, simulate: false, chdir: nil)
|
1059
1104
|
cmd = [Restic.executable] + args
|
1060
1105
|
ENV['RESTIC_PASSWORD'] = password
|
1106
|
+
ENV['RESTIC_PROGRESS_FPS'] = 1.to_s if Bitferry.verbosity == :verbose && Bitferry.ui == :gui
|
1061
1107
|
cms = cmd.collect(&:shellescape).join(' ')
|
1062
|
-
puts cms if Bitferry.verbosity == :verbose
|
1108
|
+
$stdout.puts cms if Bitferry.verbosity == :verbose
|
1063
1109
|
log.info(cms)
|
1064
1110
|
if simulate
|
1065
1111
|
log.info('(simulated)')
|
@@ -1068,7 +1114,16 @@ module Bitferry
|
|
1068
1114
|
wd = Dir.getwd unless chdir.nil?
|
1069
1115
|
begin
|
1070
1116
|
Dir.chdir(chdir) unless chdir.nil?
|
1071
|
-
|
1117
|
+
if Bitferry.ui == :gui
|
1118
|
+
t = nil
|
1119
|
+
Open3.popen2e(*cmd) do |i, oe, thr|
|
1120
|
+
while x = oe.gets; $stdout.puts(x) end
|
1121
|
+
t = thr
|
1122
|
+
end
|
1123
|
+
status = t.value
|
1124
|
+
else
|
1125
|
+
status = Open3.pipeline(cmd).first
|
1126
|
+
end
|
1072
1127
|
raise RuntimeError, "restic exit code #{status.exitstatus}" unless status.success?
|
1073
1128
|
status.success?
|
1074
1129
|
ensure
|
@@ -1147,6 +1202,10 @@ module Bitferry
|
|
1147
1202
|
def show_direction = '-->'
|
1148
1203
|
|
1149
1204
|
|
1205
|
+
alias :source :directory
|
1206
|
+
alias :destination :repository
|
1207
|
+
|
1208
|
+
|
1150
1209
|
def process
|
1151
1210
|
begin
|
1152
1211
|
log.info("processing task #{tag}")
|
@@ -1235,7 +1294,7 @@ module Bitferry
|
|
1235
1294
|
end
|
1236
1295
|
|
1237
1296
|
|
1238
|
-
def exclude_filters = exclude.collect { |x| ['--exclude', x]}.flatten
|
1297
|
+
def exclude_filters = exclude.collect { |x| ['--exclude', x] }.flatten
|
1239
1298
|
|
1240
1299
|
|
1241
1300
|
def show_status = "#{show_operation} #{repository.show_status} #{show_direction} #{directory.show_status} #{show_filters}"
|
@@ -1247,6 +1306,10 @@ module Bitferry
|
|
1247
1306
|
def show_direction = '-->'
|
1248
1307
|
|
1249
1308
|
|
1309
|
+
alias :destination :directory
|
1310
|
+
alias :source :repository
|
1311
|
+
|
1312
|
+
|
1250
1313
|
def externalize
|
1251
1314
|
restic = {
|
1252
1315
|
process: process_options
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bitferry
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Oleg A. Khlybov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-06-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -39,19 +39,47 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '5.0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: seven-zip
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '1.
|
47
|
+
version: '1.4'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '1.
|
54
|
+
version: '1.4'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: archive-zip
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.12'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.12'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: redcarpet
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.6'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.6'
|
55
83
|
- !ruby/object:Gem::Dependency
|
56
84
|
name: neatjson
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,6 +94,20 @@ dependencies:
|
|
66
94
|
- - "~>"
|
67
95
|
- !ruby/object:Gem::Version
|
68
96
|
version: '0.10'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: fxruby
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.6'
|
104
|
+
type: :runtime
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '1.6'
|
69
111
|
- !ruby/object:Gem::Dependency
|
70
112
|
name: clamp
|
71
113
|
requirement: !ruby/object:Gem::Requirement
|
@@ -84,14 +126,17 @@ description:
|
|
84
126
|
email: fougas@mail.ru
|
85
127
|
executables:
|
86
128
|
- bitferry
|
129
|
+
- bitferryfx
|
87
130
|
extensions: []
|
88
131
|
extra_rdoc_files: []
|
89
132
|
files:
|
90
133
|
- CHANGES.md
|
91
134
|
- README.md
|
92
135
|
- bin/bitferry
|
136
|
+
- bin/bitferryfx
|
93
137
|
- lib/bitferry.rb
|
94
138
|
- lib/bitferry/cli.rb
|
139
|
+
- lib/bitferry/fx.rb
|
95
140
|
homepage: https://github.com/okhlybov/bitferry
|
96
141
|
licenses:
|
97
142
|
- BSD-3-Clause
|