grably 0.0.1
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 +7 -0
- data/LICENSE.txt +202 -0
- data/README.md +2 -0
- data/exe/grably +2 -0
- data/lib/ext/class.rb +28 -0
- data/lib/grably.rb +83 -0
- data/lib/grably/cli.rb +46 -0
- data/lib/grably/core.rb +15 -0
- data/lib/grably/core/app/enchancer.rb +36 -0
- data/lib/grably/core/application.rb +8 -0
- data/lib/grably/core/colors.rb +86 -0
- data/lib/grably/core/commands.rb +23 -0
- data/lib/grably/core/commands/cp.rb +103 -0
- data/lib/grably/core/commands/digest.rb +12 -0
- data/lib/grably/core/commands/log.rb +19 -0
- data/lib/grably/core/commands/run.rb +85 -0
- data/lib/grably/core/commands/serialize.rb +16 -0
- data/lib/grably/core/configuration.rb +39 -0
- data/lib/grably/core/configuration/pretty_print.rb +22 -0
- data/lib/grably/core/digest.rb +93 -0
- data/lib/grably/core/dsl.rb +15 -0
- data/lib/grably/core/essentials.rb +49 -0
- data/lib/grably/core/module.rb +64 -0
- data/lib/grably/core/product.rb +301 -0
- data/lib/grably/core/task.rb +30 -0
- data/lib/grably/core/task/bucket.rb +29 -0
- data/lib/grably/core/task/enchancer.rb +50 -0
- data/lib/grably/core/task/expand.rb +15 -0
- data/lib/grably/core/task/jobs.rb +58 -0
- data/lib/grably/job.rb +28 -0
- data/lib/grably/job/class.rb +93 -0
- data/lib/grably/job/exceptions.rb +0 -0
- data/lib/grably/job/instance.rb +159 -0
- data/lib/grably/job/manifest.rb +67 -0
- data/lib/grably/jobs.rb +4 -0
- data/lib/grably/jobs/sync.rb +91 -0
- data/lib/grably/jobs/text.rb +4 -0
- data/lib/grably/jobs/text/erb.rb +40 -0
- data/lib/grably/jobs/text/json.rb +12 -0
- data/lib/grably/jobs/text/text.rb +21 -0
- data/lib/grably/jobs/text/yaml.rb +12 -0
- data/lib/grably/jobs/unzip.rb +1 -0
- data/lib/grably/jobs/upload.rb +1 -0
- data/lib/grably/jobs/zip.rb +2 -0
- data/lib/grably/jobs/zip/unzip.rb +24 -0
- data/lib/grably/jobs/zip/zip.rb +46 -0
- data/lib/grably/runner.rb +31 -0
- data/lib/grably/server.rb +83 -0
- data/lib/grably/utils/pretty_printer.rb +63 -0
- data/lib/grably/version.rb +12 -0
- metadata +164 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
module Grably
|
2
|
+
module Core
|
3
|
+
module TaskExtensions
|
4
|
+
# Add expand method to Task
|
5
|
+
module Expand
|
6
|
+
# Expands expression in task context
|
7
|
+
# @param expr [Object] expand expression
|
8
|
+
# @return Array<Product> result of expand in task context
|
9
|
+
def expand(expr)
|
10
|
+
Product.expand(expr, self)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Grably
|
2
|
+
module Core
|
3
|
+
module TaskExtensions
|
4
|
+
# # Jobs
|
5
|
+
# @see Grably::Core::Job
|
6
|
+
module Jobs
|
7
|
+
def method_missing(meth, *args, &block)
|
8
|
+
job_class = find_job_class(meth.to_s)
|
9
|
+
if job_class
|
10
|
+
execute_job_with_args(args, job_class, meth)
|
11
|
+
else
|
12
|
+
super
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute_job_with_args(args, job_class, meth)
|
17
|
+
working_dir = job_dir(task_dir, meth.to_s)
|
18
|
+
FileUtils.mkdir_p(working_dir)
|
19
|
+
job_class.new.run(self, working_dir, *args)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Create working directory for instantiated job inside task directory
|
23
|
+
# @param [String] base_dir task working directory
|
24
|
+
# @param [String] job_name Grably::Job call name
|
25
|
+
# @return [String] job working directory
|
26
|
+
def job_dir(base_dir, job_name)
|
27
|
+
# All this flow is working under assumption that all task jobs called
|
28
|
+
# in same order. We store counter for each job in Task instance and
|
29
|
+
# it updated throug all task live time. Each time task instance is
|
30
|
+
# recreated we use frech (zero) counter
|
31
|
+
counter = (jobs[job_name] || -1) + 1
|
32
|
+
jobs[job_name] = counter
|
33
|
+
name = [job_name, counter.to_s.rjust(3, '0')].join('-')
|
34
|
+
File.join(base_dir, name)
|
35
|
+
end
|
36
|
+
|
37
|
+
def jobs
|
38
|
+
@jobs ||= {}
|
39
|
+
end
|
40
|
+
|
41
|
+
def respond_to_missing?(meth, include_private = false)
|
42
|
+
find_job_class(meth) || super
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def find_job_class(name)
|
48
|
+
n = name.to_sym
|
49
|
+
all_classes = Grably::Job.jobs.flat_map do |c|
|
50
|
+
ObjectSpace.each_object(Class).select { |klass| klass <= c }
|
51
|
+
end
|
52
|
+
|
53
|
+
all_classes.find { |c| c.job_call_name == n }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
data/lib/grably/job.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require_relative 'core/product'
|
2
|
+
require_relative 'core/digest'
|
3
|
+
|
4
|
+
require_relative 'job/exceptions'
|
5
|
+
require_relative 'job/manifest'
|
6
|
+
require_relative 'job/class'
|
7
|
+
require_relative 'job/instance'
|
8
|
+
|
9
|
+
module Grably
|
10
|
+
# TBD
|
11
|
+
module Job
|
12
|
+
# includes "Job::InstanceMethods" and extends "Job::ClassMethods"
|
13
|
+
# using the Job.included callback.
|
14
|
+
# @!parse include Job::InstanceMethods
|
15
|
+
# @!parse extend Job::ClassMethods
|
16
|
+
class << self
|
17
|
+
def included(receiver)
|
18
|
+
receiver.extend ClassMethods
|
19
|
+
receiver.send :include, InstanceMethods
|
20
|
+
jobs << receiver
|
21
|
+
end
|
22
|
+
|
23
|
+
def jobs
|
24
|
+
@jobs ||= []
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Grably
|
2
|
+
module Job
|
3
|
+
module ClassMethods # :nodoc:
|
4
|
+
# Mark instance variable as source product container. When field contains
|
5
|
+
# source product job will track its state between launches. If product
|
6
|
+
# was changed since last run job is considered as changed and will be
|
7
|
+
# rebuild. This method also generates attribute_reader for given variable.
|
8
|
+
# @param name [Symbol] attribute name
|
9
|
+
# @param extras [Hash] extra arguments. Now unsued.
|
10
|
+
def src(name, extras = {})
|
11
|
+
class_exec do
|
12
|
+
attr_reader name
|
13
|
+
register_job_argument(name, :src, extras)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Mark instance variable as multiple source product container. When field
|
18
|
+
# contains multiple src products job will track each product state
|
19
|
+
# between launches. If any of source products was changed since last run
|
20
|
+
# job is considered as changed and will be rebuild. This method also
|
21
|
+
# generates attribute_reader for given variable.
|
22
|
+
# @param name [Symbol] attribute name
|
23
|
+
# @param extras [Hash] extra arguments. Now unsued.
|
24
|
+
def srcs(name, extras = {})
|
25
|
+
class_exec do
|
26
|
+
attr_reader name
|
27
|
+
register_job_argument(name, :srcs, extras)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Mark instance variable as incremental sources container. When field
|
32
|
+
# contains incremental sources it does not impact on job state. But job
|
33
|
+
# setup will generate state delta between two launches. This allows to
|
34
|
+
# decide if job should be rebuild completely or only changed files should
|
35
|
+
# be rebuild. This method generates two syntatic instance methods:
|
36
|
+
# * standart attribute accessor which contains current products
|
37
|
+
# * ! attribute_accessor (i.e. foo! ) which will return delta array in
|
38
|
+
# following format: [ modifications, additions, deletions ]
|
39
|
+
# @param name [Symbol] attribute name
|
40
|
+
# @param extras [Hash] extra arguments. Now unsued.
|
41
|
+
def srcs!(name, extras = {})
|
42
|
+
class_exec do
|
43
|
+
attr_reader name
|
44
|
+
register_job_argument(name, :isrcs, extras)
|
45
|
+
eval("def #{name}!; @deltas[:#{name}]; end") # rubocop:disable Security/Eval
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Mark instance variable as option container. When field contains option
|
50
|
+
# job will track its value between launches. If value was changed since
|
51
|
+
# last run job is considered as changed and will be rebuild. This method
|
52
|
+
# also generates attribute_reader for given variable.
|
53
|
+
# @param name [Symbol] attribute name
|
54
|
+
# @param extras [Hash] extra arguments. Now unsued.
|
55
|
+
def opt(name, extras = {})
|
56
|
+
class_exec do
|
57
|
+
attr_reader name
|
58
|
+
register_job_argument(name, :opt, extras)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def register_job_argument(name, type, extras)
|
63
|
+
job_args[name] = [type, extras]
|
64
|
+
end
|
65
|
+
|
66
|
+
def job_args
|
67
|
+
@job_args ||= {}
|
68
|
+
end
|
69
|
+
|
70
|
+
def job_call_name
|
71
|
+
return unless name # TODO: in runtime some anonymous sublcass instances
|
72
|
+
# can be created find out when and why
|
73
|
+
# job_call_name by user with call_as method. Yet it is not required
|
74
|
+
# if job_call_name empty we'll try to sytetize job name
|
75
|
+
@job_call_name ||= synthesize_job_name
|
76
|
+
end
|
77
|
+
|
78
|
+
def call_as(name)
|
79
|
+
@job_call_name = name
|
80
|
+
end
|
81
|
+
|
82
|
+
# Try to syntetize job name from class name
|
83
|
+
# * Strip all parent modules names
|
84
|
+
# * Cut off Job postfix from class name if any
|
85
|
+
# * convert class name to snake case
|
86
|
+
def synthesize_job_name
|
87
|
+
klass_name = name.scan(/[^:]+/).last
|
88
|
+
klass_name = klass_name[/(.+)Job/, 1] || klass_name
|
89
|
+
klass_name.scan(/[A-Z][a-z]+/).map(&:downcase).join('_').to_sym
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
File without changes
|
@@ -0,0 +1,159 @@
|
|
1
|
+
module Grably
|
2
|
+
module Job
|
3
|
+
module InstanceMethods # rubocop:disable Metrics/ModuleLength, Style/Documentation
|
4
|
+
def run(task, working_dir, *args) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
5
|
+
prepare(task, working_dir)
|
6
|
+
initialize_state(*args) if stateful?
|
7
|
+
|
8
|
+
if changed?
|
9
|
+
clean unless incremental?
|
10
|
+
log " * [#{self.class.job_call_name}] building"
|
11
|
+
result = @manifest.result = Grably::Core::Product.expand(build)
|
12
|
+
@manifest.dump
|
13
|
+
else
|
14
|
+
log " * [#{self.class.job_call_name}] uptodate"
|
15
|
+
result = @manifest.result
|
16
|
+
end
|
17
|
+
|
18
|
+
result
|
19
|
+
rescue StandardError => err
|
20
|
+
# If any error occured this will force rebuild on next run
|
21
|
+
@manifest.remove if @manifest
|
22
|
+
raise(err)
|
23
|
+
end
|
24
|
+
|
25
|
+
# When job is changed it need to be reevaluated so build method will be
|
26
|
+
# launched. If job remains unchanged previous result will be returned
|
27
|
+
def changed?
|
28
|
+
@changed
|
29
|
+
end
|
30
|
+
|
31
|
+
# Means that job has state which can be tracked between launches. If
|
32
|
+
# job is stateless it always changed.
|
33
|
+
def stateful?
|
34
|
+
@stateful
|
35
|
+
end
|
36
|
+
|
37
|
+
# If job contains incremental srcs it is incremental. Incremental job
|
38
|
+
# does not clean self state between launches. It should be managed by
|
39
|
+
# user.
|
40
|
+
def incremental?
|
41
|
+
@job_args.any? { |_name, desc| desc.first == :isrc }
|
42
|
+
end
|
43
|
+
|
44
|
+
def job_dir(path = nil)
|
45
|
+
path.nil? ? @job_dir : File.join(@job_dir, path)
|
46
|
+
end
|
47
|
+
|
48
|
+
def meta
|
49
|
+
@manifest.meta
|
50
|
+
end
|
51
|
+
|
52
|
+
# Celans job state by removing all files from working directory
|
53
|
+
def clean
|
54
|
+
FileUtils.rm_rf(Dir[File.join(@job_dir, '*')])
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def prepare(task, working_dir)
|
60
|
+
@job_dir = working_dir # initialize job dir
|
61
|
+
@t = task
|
62
|
+
@job_args = job_args_lookup
|
63
|
+
# We need to track state only if job has arguments.
|
64
|
+
@stateful = !@job_args.empty?
|
65
|
+
# If job is stateful it is not changed by default. Only after stat will
|
66
|
+
# be initialized we can say if it changed. If job has no state we should
|
67
|
+
# always rebuild it. So changed != stateful
|
68
|
+
@changed = !@stateful
|
69
|
+
# In case if job has incremental sources we should keep deltas
|
70
|
+
# somewhere. Deltas are kept in hash where they stored by field name.
|
71
|
+
@deltas = {}
|
72
|
+
end
|
73
|
+
|
74
|
+
# Executes job state initialization by assigning values to instance
|
75
|
+
# variables
|
76
|
+
def initialize_state(*args)
|
77
|
+
# Load previous state
|
78
|
+
@manifest = Manifest.new(job_dir)
|
79
|
+
_loaded = @manifest.load # try to load manifest
|
80
|
+
|
81
|
+
if self.class.method_defined?(:setup)
|
82
|
+
# This means that class has user defined method for initialization so
|
83
|
+
# we should call this method for setup
|
84
|
+
setup(*args)
|
85
|
+
else
|
86
|
+
# If no setup method defined we will use synthetic setup which assumes
|
87
|
+
# that arguments is a Hash where keys is job argument names
|
88
|
+
synthetic_setup(*args)
|
89
|
+
end
|
90
|
+
expand_job_arguments
|
91
|
+
end
|
92
|
+
|
93
|
+
def synthetic_setup(args)
|
94
|
+
check_synthetic_arguments(args)
|
95
|
+
args.each { |k, v| instance_variable_set("@#{k}", v) }
|
96
|
+
end
|
97
|
+
|
98
|
+
def check_synthetic_arguments(args)
|
99
|
+
raise "Expected Hash got #{args.inspect}" unless args.is_a?(Hash)
|
100
|
+
job_args = @job_args.keys
|
101
|
+
extra = args.keys - job_args
|
102
|
+
raise "Unknown arguments: #{extra.join(', ')}" unless extra.empty?
|
103
|
+
missing = job_args - args.keys
|
104
|
+
raise "Missing arguments #{missing.join(', ')}" unless missing.empty?
|
105
|
+
end
|
106
|
+
|
107
|
+
# Walks through all job arguments and does proper expand operation for its
|
108
|
+
# value
|
109
|
+
def expand_job_arguments
|
110
|
+
@job_args.each do |name, desc|
|
111
|
+
type, _extras = desc
|
112
|
+
value = instance_variable_get("@#{name}")
|
113
|
+
update_argument(name, type, value)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def update_argument(name, type, value) # rubocop:disable Metrics/MethodLength
|
118
|
+
case type
|
119
|
+
when :src
|
120
|
+
# src expects single product
|
121
|
+
value = Product.expand(value, @t)
|
122
|
+
raise(ArgumentError, "Expected only one product for #{name}") unless value.length == 1
|
123
|
+
value = value.first
|
124
|
+
when :srcs, :isrcs
|
125
|
+
# src and isrcs expects multiple products
|
126
|
+
value = Product.expand(value, @t)
|
127
|
+
when :opt # rubocop:disable Lint/EmptyWhen
|
128
|
+
# do nothing, we do not expand opt values. keeping them as is
|
129
|
+
when nil
|
130
|
+
raise(ArgumentError, "#{name} not defined")
|
131
|
+
else
|
132
|
+
raise(ArgumentError, "Unknown type #{type} for #{name}")
|
133
|
+
end
|
134
|
+
|
135
|
+
@manifest.update(name, type, value, ->(*a) { on_job_arg_set(*a) })
|
136
|
+
instance_variable_set("@#{name}", value)
|
137
|
+
end
|
138
|
+
|
139
|
+
# rubocop:disable Metrics/ParameterLists
|
140
|
+
def on_job_arg_set(name, type, old_digest, old_val, new_digest, new_val)
|
141
|
+
if type == :isrcs
|
142
|
+
# isrcs never impact changed state. We just need to gather infomration
|
143
|
+
# about what actualy was changed
|
144
|
+
@deltas[name] = Core::Digest.diff_digests(old_digest, new_digest)
|
145
|
+
elsif old_val != new_val || old_digest != new_digest
|
146
|
+
@changed = true
|
147
|
+
end
|
148
|
+
end
|
149
|
+
# rubocop:enable Metrics/ParameterLists
|
150
|
+
|
151
|
+
def job_args_lookup(klass = self.class, args = {})
|
152
|
+
job_args_lookup(klass.superclass, args) if klass.superclass
|
153
|
+
args.update(klass.job_args) if klass.included_modules.include?(Grably::Job)
|
154
|
+
|
155
|
+
args
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module Grably
|
5
|
+
module Job
|
6
|
+
# Manifest file with job data. Manifest keeps information about previous run
|
7
|
+
# including passed arguments and output file digests
|
8
|
+
class Manifest
|
9
|
+
# Manifest file location relative to jobdir
|
10
|
+
MANIFEST = '.manifest'.freeze
|
11
|
+
|
12
|
+
attr_reader :manifest_file
|
13
|
+
|
14
|
+
def initialize(job_dir)
|
15
|
+
@job_dir = job_dir
|
16
|
+
@data = OpenStruct.new(src: {}, srcs: {}, opt: {}, result: nil, meta: {})
|
17
|
+
@manifest_file = File.join(job_dir, MANIFEST)
|
18
|
+
end
|
19
|
+
|
20
|
+
def update(name, type, value, update_hook) # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
21
|
+
case type
|
22
|
+
when :src
|
23
|
+
old_value, old_digest = @data.src[name]
|
24
|
+
new_digest = Grably::Digest.digest(value).first
|
25
|
+
when :srcs
|
26
|
+
old_value, old_digest = @data.srcs[name]
|
27
|
+
new_digest = Grably::Digest.digest(*value)
|
28
|
+
when :opt
|
29
|
+
old_value, _old_digest = @data.opt[name]
|
30
|
+
old_digest = nil
|
31
|
+
new_digest = nil
|
32
|
+
else
|
33
|
+
raise ArgumentError, 'Invalid type: ' + type
|
34
|
+
end
|
35
|
+
|
36
|
+
@data.send(type)[name] = [value, new_digest]
|
37
|
+
update_hook.call(name, type, old_value, old_digest, value, new_digest)
|
38
|
+
[old_value, old_digest]
|
39
|
+
end
|
40
|
+
|
41
|
+
def result=(products)
|
42
|
+
digests = Grably::Digest.digest(*products)
|
43
|
+
@data.result = [products, digests]
|
44
|
+
end
|
45
|
+
|
46
|
+
def meta
|
47
|
+
@data.meta
|
48
|
+
end
|
49
|
+
|
50
|
+
def result
|
51
|
+
@data.result.first
|
52
|
+
end
|
53
|
+
|
54
|
+
def load
|
55
|
+
@data = load_obj(manifest_file) if File.exist?(manifest_file)
|
56
|
+
end
|
57
|
+
|
58
|
+
def dump
|
59
|
+
save_obj(manifest_file, @data)
|
60
|
+
end
|
61
|
+
|
62
|
+
def remove
|
63
|
+
FileUtils.rm_f(manifest_file)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/grably/jobs.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
module Grably # :nodoc:
|
2
|
+
# TBD
|
3
|
+
class SyncJob
|
4
|
+
include Grably::Job
|
5
|
+
|
6
|
+
PROTO_SSH = %r{^ssh://(.*)$}
|
7
|
+
SSH_HOST = /(.+?):(.+?)@(.+)/
|
8
|
+
DEFAULT_RSYNC_PARAMS = %w(-avz --progress --delete).freeze
|
9
|
+
|
10
|
+
srcs :files
|
11
|
+
opt :dst
|
12
|
+
|
13
|
+
opt :host
|
14
|
+
opt :proto
|
15
|
+
opt :no_partial
|
16
|
+
opt :ssh_key
|
17
|
+
opt :ssh_pass
|
18
|
+
opt :ssh_port
|
19
|
+
|
20
|
+
def setup(srcs, dst = nil, _p = {})
|
21
|
+
@files = srcs
|
22
|
+
@dst = dst || job_dir
|
23
|
+
|
24
|
+
@proto = :file
|
25
|
+
|
26
|
+
unpack_dst_hash(@dst) unless @dst.is_a?(String)
|
27
|
+
@dst.match(PROTO_SSH) do |m|
|
28
|
+
@proto = :ssh
|
29
|
+
@dst = m[1]
|
30
|
+
end
|
31
|
+
setup_ssh_opts if @proto == :ssh
|
32
|
+
end
|
33
|
+
|
34
|
+
def changed?
|
35
|
+
true
|
36
|
+
end
|
37
|
+
|
38
|
+
def build
|
39
|
+
trace "Syncing products to #{dst}"
|
40
|
+
cp_smart(files, dst, log: proto == :file)
|
41
|
+
ssh_sync if proto == :ssh
|
42
|
+
proto == :ssh ? [] : dst
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def ssh_cmd
|
48
|
+
cmd = %w(ssh)
|
49
|
+
cmd += ['-i', ssh_key] if ssh_key
|
50
|
+
cmd += ['-p', ssh_port] if ssh_port
|
51
|
+
cmd = ['sshpass', '-p', ssh_pass] + cmd if ssh_pass
|
52
|
+
"'#{cmd.join(' ')}'"
|
53
|
+
end
|
54
|
+
|
55
|
+
def unpack_dst_hash(dst)
|
56
|
+
@proto = :ssh
|
57
|
+
@no_partial = dst[:no_partial]
|
58
|
+
@ssh_key = dst[:ssh_key]
|
59
|
+
@ssh_pass = dst[:ssh_pass]
|
60
|
+
@ssh_port = dst[:ssh_port]
|
61
|
+
@dst = dst[:host]
|
62
|
+
end
|
63
|
+
|
64
|
+
def ssh_sync
|
65
|
+
if files.empty?
|
66
|
+
warn 'Nothing to sync' if files.empty?
|
67
|
+
end
|
68
|
+
|
69
|
+
log "Syncing #{files.size} files to #{host}"
|
70
|
+
['rsync', *rsync_params, @dst, '-e', ssh_cmd, host].run_log
|
71
|
+
end
|
72
|
+
|
73
|
+
def rsync_params
|
74
|
+
params = DEFAULT_RSYNC_PARAMS.dup
|
75
|
+
params << '--partial' unless no_partial
|
76
|
+
|
77
|
+
params
|
78
|
+
end
|
79
|
+
|
80
|
+
def setup_ssh_opts
|
81
|
+
@host = @dst
|
82
|
+
# '/' at the end will cause to sync internal file structure
|
83
|
+
@dst = job_dir('files') + '/'
|
84
|
+
|
85
|
+
@host.match(SSH_HOST) do |m|
|
86
|
+
@ssh_pass = m[2]
|
87
|
+
@host = [m[1], m[2]].join('@')
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|