grably 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|