skadategems-dev 0.0.9
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/.gitignore +16 -0
- data/.travis.yml +12 -0
- data/Gemfile +13 -0
- data/Guardfile +8 -0
- data/LICENSE.txt +22 -0
- data/README.md +74 -0
- data/Rakefile +6 -0
- data/bin/skadate +9 -0
- data/bin/skadategems-dev +18 -0
- data/integration/README.md +97 -0
- data/integration/cli/skadate_init_spec.rb +86 -0
- data/integration/cli/skadate_spec.rb +50 -0
- data/integration/cli/spec_helper.rb +23 -0
- data/integration/lib/remote/download_file_spec.rb +136 -0
- data/integration/spec_helper.rb +101 -0
- data/lib/skadategems/dev/bundle.rb +115 -0
- data/lib/skadategems/dev/cli/remote.rb +364 -0
- data/lib/skadategems/dev/cli/skadate.rb +184 -0
- data/lib/skadategems/dev/remote.rb +170 -0
- data/lib/skadategems/dev/remote/clone_logger.rb +118 -0
- data/lib/skadategems/dev/remote/configs.rb +97 -0
- data/lib/skadategems/dev/remote/directory.rb +88 -0
- data/lib/skadategems/dev/remote/file.rb +157 -0
- data/lib/skadategems/dev/remote/includes/mysql_connect.php +8 -0
- data/lib/skadategems/dev/skadate.rb +30 -0
- data/lib/skadategems/dev/software_version.rb +66 -0
- data/lib/skadategems/dev/source_controller.rb +101 -0
- data/lib/skadategems/dev/version.rb +5 -0
- data/skadategems-dev.gemspec +33 -0
- data/spec/skadategems/dev/bundle_spec.rb +268 -0
- data/spec/skadategems/dev/cli/remote_spec.rb +623 -0
- data/spec/skadategems/dev/cli/skadate_spec.rb +182 -0
- data/spec/skadategems/dev/remote/clone_logger_spec.rb +324 -0
- data/spec/skadategems/dev/remote/directory_spec.rb +117 -0
- data/spec/skadategems/dev/remote/file_spec.rb +74 -0
- data/spec/skadategems/dev/remote_spec.rb +95 -0
- data/spec/skadategems/dev/skadate_spec.rb +62 -0
- data/spec/skadategems/dev/software_version_spec.rb +92 -0
- data/spec/skadategems/dev/source_controller_spec.rb +243 -0
- data/spec/spec_helper.rb +32 -0
- metadata +153 -0
@@ -0,0 +1,136 @@
|
|
1
|
+
require File.expand_path('../../spec_helper', __dir__)
|
2
|
+
require 'skadategems/dev/skadate'
|
3
|
+
require 'fileutils'
|
4
|
+
require 'digest/md5'
|
5
|
+
|
6
|
+
module SkadateGems::Dev
|
7
|
+
describe Remote::File do
|
8
|
+
let(:skadate) { Skadate.new(skadate_local_dir) }
|
9
|
+
|
10
|
+
let(:big_file_path) do
|
11
|
+
ENV['SKADATE_BIG_FILE_PATH'] || File.join(skadate_source_dir, 'install/skadate9.sql')
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:big_file_name) { File.basename(big_file_path) }
|
15
|
+
|
16
|
+
let(:remote_file) do
|
17
|
+
Remote::File.new(skadate.remote, big_file_name, File.size(big_file_path))
|
18
|
+
end
|
19
|
+
|
20
|
+
before(:each) do
|
21
|
+
remote_install
|
22
|
+
|
23
|
+
symlink = File.join(skadate_remote_dir, big_file_name)
|
24
|
+
FileUtils.ln_s(big_file_path, symlink, verbose: verbose?)
|
25
|
+
|
26
|
+
FileUtils.cd(skadate_local_dir, verbose: verbose?)
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#request' do
|
30
|
+
|
31
|
+
it 'yields `http response` that allows us to #read_body as a stream' do
|
32
|
+
segments_count = 0
|
33
|
+
|
34
|
+
remote_file.request do |response|
|
35
|
+
response.read_body { |_| segments_count += 1 }
|
36
|
+
end
|
37
|
+
|
38
|
+
puts "[segments count = #{segments_count}]" if verbose?
|
39
|
+
expect(segments_count).to be > 100
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when :md5_checksum is given' do
|
43
|
+
|
44
|
+
context "and remote file's checksum is the same" do
|
45
|
+
let(:checksum) { Digest::MD5.file(big_file_path).hexdigest }
|
46
|
+
|
47
|
+
it 'responds with [304 "Not Modified"] and empty body' do
|
48
|
+
remote_file.request(md5_checksum: checksum) do |response|
|
49
|
+
expect(response).to be_a Net::HTTPNotModified
|
50
|
+
response.read_body { |_| raise 'should not have been called!' }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
context "but remote file's checksum is different" do
|
56
|
+
let(:checksum) { 'a' * 32 }
|
57
|
+
|
58
|
+
it 'responds with [200 "OK"] and file content as a stream' do
|
59
|
+
segments_count = 0
|
60
|
+
|
61
|
+
remote_file.request(md5_checksum: checksum) do |response|
|
62
|
+
expect(response).to be_a Net::HTTPOK
|
63
|
+
response.read_body { |_| segments_count += 1 }
|
64
|
+
end
|
65
|
+
|
66
|
+
puts "[segments count = #{segments_count}]" if verbose?
|
67
|
+
expect(segments_count).to be > 100
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#save_as(filename)' do
|
76
|
+
let(:local_file_path) { File.join(skadate_local_dir, big_file_name) }
|
77
|
+
|
78
|
+
context 'when there is no local file with a given filename' do
|
79
|
+
it 'downloads and saves a remote file' do
|
80
|
+
return_value = nil
|
81
|
+
|
82
|
+
expect {
|
83
|
+
return_value = remote_file.save_as(local_file_path)
|
84
|
+
}.to change { File.exists?(local_file_path) }.to true
|
85
|
+
|
86
|
+
expect(return_value).to be_true
|
87
|
+
|
88
|
+
expect(FileUtils.identical?(big_file_path, local_file_path)).to be_true
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'when a given file is already exists' do
|
93
|
+
|
94
|
+
context 'and files are the same' do
|
95
|
+
before(:each) do
|
96
|
+
FileUtils.cp(big_file_path, local_file_path, verbose: verbose?)
|
97
|
+
expect(FileUtils.identical?(big_file_path, local_file_path)).to be_true
|
98
|
+
end
|
99
|
+
|
100
|
+
it "doesn't download a remote file" do
|
101
|
+
FileUtils.chmod('u-w', local_file_path, verbose: verbose?)
|
102
|
+
expect(File.writable?(local_file_path)).to be_false
|
103
|
+
|
104
|
+
return_value = nil
|
105
|
+
|
106
|
+
expect {
|
107
|
+
return_value = remote_file.save_as(local_file_path)
|
108
|
+
}.to_not change { File.writable?(local_file_path) }
|
109
|
+
|
110
|
+
expect(return_value).to eq false
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context 'but the files are different' do
|
115
|
+
before(:each) do
|
116
|
+
File.write(local_file_path, 'Oh Noo!')
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'downloads and rewrites a local file' do
|
120
|
+
return_value = nil
|
121
|
+
|
122
|
+
expect {
|
123
|
+
return_value = remote_file.save_as(local_file_path)
|
124
|
+
}.to change {
|
125
|
+
FileUtils.identical?(local_file_path, big_file_path)
|
126
|
+
}.to true
|
127
|
+
|
128
|
+
expect(return_value).to be_true
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# integration/spec_helper.rb
|
2
|
+
|
3
|
+
require File.expand_path('../spec/spec_helper', __dir__)
|
4
|
+
require 'skadategems/dev/cli/skadate'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
|
9
|
+
config.before(:each) do
|
10
|
+
# cleanup sandbox directories
|
11
|
+
[skadate_local_dir, skadate_remote_dir].each do |entry|
|
12
|
+
if entry.is_a?(String) && (entry['tmp'] || entry['temp']) && entry['skadate']
|
13
|
+
FileUtils.rm_r Dir.glob("#{entry}/{*,.dev,.ht*}"), :verbose => verbose?
|
14
|
+
else
|
15
|
+
raise <<-ERRORMSG
|
16
|
+
Error: bad tmp dir entry "#{path}".
|
17
|
+
Explanation: by security reason tmp directories must contain 'tmp' and 'skadate' keywords in their pathname.
|
18
|
+
ERRORMSG
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def env_var!(name)
|
24
|
+
ENV[name] or raise "`#{name}` is not defined"
|
25
|
+
end
|
26
|
+
|
27
|
+
def verbose?
|
28
|
+
(verbose = ENV['VERBOSE']) && verbose != '' && verbose.upcase != 'FALSE'
|
29
|
+
end
|
30
|
+
|
31
|
+
def skadate_tmp_dir
|
32
|
+
@skadate_tmp_dir ||= File.join(File.symlink?('/tmp') ? "/#{File.readlink('/tmp')}" : '/tmp', 'skadate')
|
33
|
+
end
|
34
|
+
|
35
|
+
def skadate_source_dir
|
36
|
+
@skadate_source_dir ||= env_var! 'SKADATE_SOURCE_DIR'
|
37
|
+
end
|
38
|
+
|
39
|
+
def skadate_local_dir
|
40
|
+
@skadate_local_dir ||= ENV['SKADATE_LOCAL_DIR'] || File.join(skadate_tmp_dir, 'local')
|
41
|
+
end
|
42
|
+
|
43
|
+
def skadate_remote_dir
|
44
|
+
@skadate_remote_dir ||= ENV['SKADATE_REMOTE_DIR'] || File.join(skadate_tmp_dir, 'remote')
|
45
|
+
end
|
46
|
+
|
47
|
+
def skadate_local_host
|
48
|
+
ENV['SKADATE_LOCAL_HOST'] || 'skadate-local-test'
|
49
|
+
end
|
50
|
+
|
51
|
+
def skadate_remote_host
|
52
|
+
ENV['SKADATE_REMOTE_HOST'] || 'skadate-remote-test'
|
53
|
+
end
|
54
|
+
|
55
|
+
def remote_install(options = {})
|
56
|
+
FileUtils.cd(skadate_remote_dir, :verbose => verbose?) do
|
57
|
+
FileUtils.mkdir('internals', :verbose => verbose?)
|
58
|
+
|
59
|
+
puts '[...] > internals/config.php' if verbose?
|
60
|
+
IO.write('internals/config.php', <<-CONFIGPHP)
|
61
|
+
<?php
|
62
|
+
define('DIR_SITE_ROOT', '#{skadate_remote_dir}/');
|
63
|
+
define('SITE_URL', 'http://#{skadate_remote_host}/');
|
64
|
+
CONFIGPHP
|
65
|
+
end
|
66
|
+
|
67
|
+
FileUtils.cd(skadate_local_dir, :verbose => verbose?) do
|
68
|
+
puts "skadate init -r http://#{skadate_remote_host}/" if verbose?
|
69
|
+
SkadateGems::Dev::CLI::Skadate.start ['init', '-r', "http://#{skadate_remote_host}/"]
|
70
|
+
FileUtils.cp('exec.php', skadate_remote_dir, :verbose => verbose?)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def local_install(options = {})
|
75
|
+
end
|
76
|
+
|
77
|
+
def source_copy(source_file_paths, destination_root = :local, options = {})
|
78
|
+
destination_root = skadate_local_dir if destination_root == :local
|
79
|
+
destination_root = skadate_remote_dir if destination_root == :remote
|
80
|
+
|
81
|
+
[*source_file_paths].each do |entry|
|
82
|
+
src = File.join(skadate_source_dir, entry)
|
83
|
+
dest = File.join(destination_root, entry)
|
84
|
+
FileUtils.mkdir_p File.dirname(dest), :verbose => verbose?
|
85
|
+
FileUtils.cp(src, dest, options.merge(:verbose => verbose?))
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# @param bytes [Fixnum] value in bytes
|
90
|
+
# @return [String] human readable format
|
91
|
+
def human_readable_bytes(bytes)
|
92
|
+
units = %W(B KiB MiB GiB TiB)
|
93
|
+
|
94
|
+
size, unit = units.reduce(bytes.to_f) do |(fsize, _), type|
|
95
|
+
fsize > 512 ? [fsize / 1024, type] : (break [fsize, type])
|
96
|
+
end
|
97
|
+
|
98
|
+
"#{size > 9 || size.modulo(1) < 0.1 ? '%d' : '%.1f'} %s" % [size, unit]
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rubygems/package'
|
3
|
+
require 'zlib'
|
4
|
+
require 'fileutils'
|
5
|
+
|
6
|
+
module SkadateGems
|
7
|
+
module Dev
|
8
|
+
|
9
|
+
class Bundle
|
10
|
+
|
11
|
+
attr_accessor :layout_theme
|
12
|
+
|
13
|
+
# Bundler instance initializer.
|
14
|
+
#
|
15
|
+
# @param skadate [Skadate]
|
16
|
+
def initialize(skadate)
|
17
|
+
@source = skadate.source
|
18
|
+
@internal_c = @source.internal_c
|
19
|
+
@external_c = @source.external_c
|
20
|
+
@userfiles = @source.userfiles
|
21
|
+
@themes_dir = @source.filename('layout/themes/')
|
22
|
+
end
|
23
|
+
|
24
|
+
# Filter files by their filename.
|
25
|
+
#
|
26
|
+
# @param filename [String] absolute path to a file
|
27
|
+
# @return [boolean] true if given filename should be bundled
|
28
|
+
def should_include?(filename)
|
29
|
+
return false if filename.start_with?(@internal_c, @external_c, @userfiles) ||
|
30
|
+
filename.end_with?('.tar', '.gz', '.zip', '.swp', '~', 'Thumbs.db', 'desktop.ini')
|
31
|
+
|
32
|
+
if filename.start_with?(@themes_dir) && layout_theme &&
|
33
|
+
!filename.start_with?(File.join(@themes_dir, layout_theme))
|
34
|
+
return false
|
35
|
+
end
|
36
|
+
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
40
|
+
# Create a tar file in memory.
|
41
|
+
#
|
42
|
+
# @return [StringIO] whose underlying String is the contents of the tar file
|
43
|
+
def tar
|
44
|
+
return @tar if @tar
|
45
|
+
|
46
|
+
string_io = StringIO.new('')
|
47
|
+
Gem::Package::TarWriter.new(string_io) do |tar|
|
48
|
+
|
49
|
+
htaccess = File.join(@source.root, '.htaccess')
|
50
|
+
if File.exists?(htaccess)
|
51
|
+
tar.add_file '.htaccess', 0644 do |tar_file|
|
52
|
+
File.open(htaccess, 'rb') { |file| tar_file.write(file.read) }
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
Dir[File.join(@source.root, '**/*')].each do |filename|
|
57
|
+
next unless should_include?(filename)
|
58
|
+
|
59
|
+
if File.directory?(filename)
|
60
|
+
tar.mkdir (relative_path filename), 0755
|
61
|
+
|
62
|
+
htaccess = File.join(filename, '.htaccess')
|
63
|
+
if File.exists?(htaccess)
|
64
|
+
filename = htaccess # iteration continues...
|
65
|
+
else
|
66
|
+
next
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
tar.add_file (relative_path filename), 0644 do |tar_file|
|
71
|
+
File.open(filename, 'rb') { |file| tar_file.write(file.read) }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
[ (int_c = relative_path @internal_c),
|
76
|
+
"#{int_c}/cache",
|
77
|
+
"#{int_c}/components",
|
78
|
+
"#{int_c}/forms",
|
79
|
+
"#{int_c}/lang",
|
80
|
+
relative_path(@external_c),
|
81
|
+
relative_path(@userfiles),
|
82
|
+
].each { |dir| tar.mkdir dir, 0777 }
|
83
|
+
end
|
84
|
+
|
85
|
+
string_io.rewind
|
86
|
+
@tar = string_io
|
87
|
+
end
|
88
|
+
|
89
|
+
# gzips the underlying string in the given `StringIO`,
|
90
|
+
# returning a new `StringIO` representing the compressed file.
|
91
|
+
#
|
92
|
+
# @param filename [String] optional output filename
|
93
|
+
# @return [StringIO|Fixnum]
|
94
|
+
def gzip(filename = nil)
|
95
|
+
gz = StringIO.new('')
|
96
|
+
z = Zlib::GzipWriter.new(gz)
|
97
|
+
z.write(tar.string)
|
98
|
+
z.close # this is necessary!
|
99
|
+
|
100
|
+
if filename
|
101
|
+
File.binwrite(filename, gz.string)
|
102
|
+
else
|
103
|
+
StringIO.new(gz.string)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
def relative_path(filename)
|
109
|
+
filename.sub(/^#{Regexp.escape(@source.root)}\/?/, '')
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,364 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'thor'
|
3
|
+
require 'json'
|
4
|
+
require 'skadategems/dev/skadate'
|
5
|
+
require 'skadategems/dev/remote/clone_logger'
|
6
|
+
require 'ruby-progressbar'
|
7
|
+
|
8
|
+
module SkadateGems::Dev
|
9
|
+
module CLI
|
10
|
+
|
11
|
+
class Remote < Thor
|
12
|
+
namespace 'remote'
|
13
|
+
|
14
|
+
desc 'ping', 'Send ping request to a remote application'
|
15
|
+
long_desc <<-LONGDESC
|
16
|
+
Send ping request to a remote server's `exec.php` script. \x5
|
17
|
+
\x5
|
18
|
+
\x5 Example usage: \x5
|
19
|
+
\x5
|
20
|
+
\x5 $> skadate remote ping
|
21
|
+
\x5
|
22
|
+
\x5
|
23
|
+
LONGDESC
|
24
|
+
|
25
|
+
def ping
|
26
|
+
accessor = current.remote.accessor
|
27
|
+
script = ExecPHP::ScriptBatch.new { |s| s << 'echo "pong!";' }
|
28
|
+
|
29
|
+
say '=> '
|
30
|
+
say accessor.execphp_uri, :cyan
|
31
|
+
|
32
|
+
accessor.exec(script) do |res|
|
33
|
+
say '#> '
|
34
|
+
say "#{res.code} [#{res.message}]", (res.code == '200' ? :green : :red)
|
35
|
+
|
36
|
+
if res.body.empty?
|
37
|
+
say 'Error: empty response body', :red
|
38
|
+
elsif res.body != 'pong!'
|
39
|
+
say 'Error: wrong response body:', :red
|
40
|
+
if res.body.length > 1000
|
41
|
+
say res.body[0..984], nil, false
|
42
|
+
say ' ... (continued)', :yellow
|
43
|
+
else
|
44
|
+
say '"' + res.body + '"'
|
45
|
+
end
|
46
|
+
else
|
47
|
+
say '>> '
|
48
|
+
say res.body, :white
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
desc 'exec FILENAME', 'Execute php scripts remotely'
|
54
|
+
long_desc <<-LONGDESC
|
55
|
+
Send a given php file to a remote application server for execution.
|
56
|
+
|
57
|
+
Example usage:
|
58
|
+
\x5
|
59
|
+
\x5 $> skadate remote exec path/to/my_script.php
|
60
|
+
\x5
|
61
|
+
\x5
|
62
|
+
LONGDESC
|
63
|
+
|
64
|
+
def exec(filename)
|
65
|
+
puts current.remote.exec { |batch| batch.include_file(filename) }.body
|
66
|
+
end
|
67
|
+
|
68
|
+
desc 'config ID [VALUE]', 'Manage remote application configuration'
|
69
|
+
|
70
|
+
option :type, :type => :string,
|
71
|
+
:desc => 'Explicitly specify config value type'
|
72
|
+
|
73
|
+
def config(key_or_id, value = nil)
|
74
|
+
key_or_id = key_or_id.to_i if numeric? key_or_id
|
75
|
+
config = current.remote.configs[key_or_id]
|
76
|
+
|
77
|
+
say '>> ', (:yellow unless value.nil?), false
|
78
|
+
say "##{config.id} ", :white, false
|
79
|
+
say '| '
|
80
|
+
say config.name, :cyan, false
|
81
|
+
say ': '
|
82
|
+
say config.value.to_json, (color_for config.value), value.nil?
|
83
|
+
|
84
|
+
unless value.nil?
|
85
|
+
value =
|
86
|
+
case options[:type]
|
87
|
+
when nil
|
88
|
+
if value == 'true'
|
89
|
+
true
|
90
|
+
elsif value == 'false'
|
91
|
+
false
|
92
|
+
elsif numeric? value
|
93
|
+
value.to_i
|
94
|
+
else
|
95
|
+
value
|
96
|
+
end
|
97
|
+
when 'str'
|
98
|
+
when 'string'
|
99
|
+
value
|
100
|
+
when 'int'
|
101
|
+
when 'integer'
|
102
|
+
when 'number'
|
103
|
+
value.to_i
|
104
|
+
when 'bool'
|
105
|
+
when 'boolean'
|
106
|
+
value != 'false' && value != '0'
|
107
|
+
when 'json'
|
108
|
+
JSON.parse(value)
|
109
|
+
else
|
110
|
+
raise "unrecognized config value type '#{options[:type]}'"
|
111
|
+
end
|
112
|
+
|
113
|
+
say ' => ', :white, false
|
114
|
+
say value.to_json, (color_for value)
|
115
|
+
|
116
|
+
say '>> '
|
117
|
+
if yes?('perform this update? [yn]')
|
118
|
+
config.value = value
|
119
|
+
|
120
|
+
case config.update_status
|
121
|
+
when :updated
|
122
|
+
say '[updated]', :green
|
123
|
+
when :not_modified
|
124
|
+
say '[not modified]', :yellow
|
125
|
+
else
|
126
|
+
say '[error]', :red
|
127
|
+
end
|
128
|
+
else
|
129
|
+
say '[cancelled]', :red
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
desc 'clone', 'Clone production-running application'
|
135
|
+
long_desc <<-LONGDESC
|
136
|
+
Clone production-running application locally for development purposes.
|
137
|
+
|
138
|
+
To download remote application source files:
|
139
|
+
\x5
|
140
|
+
\x5 $> skadate remote clone --sources \x5
|
141
|
+
\x5
|
142
|
+
|
143
|
+
To get a remote application database dump:
|
144
|
+
\x5
|
145
|
+
\x5 $> skadate remote clone --database \x5
|
146
|
+
\x5
|
147
|
+
|
148
|
+
Or if you want to make a full clone of a running application including all the data and user uploaded files:
|
149
|
+
\x5
|
150
|
+
\x5 $> skadate remote clone --sources --database --userfiles \x5
|
151
|
+
\x5
|
152
|
+
|
153
|
+
Make a note that by default `remote clone --sources` copies only one `layout/theme` that is currently active on a remote server.
|
154
|
+
If you want to get another theme specify it using the `--layout-theme` switch as following:
|
155
|
+
\x5
|
156
|
+
\x5 $> skadate remote clone --layout-theme stencil \x5
|
157
|
+
\x5
|
158
|
+
\x5
|
159
|
+
LONGDESC
|
160
|
+
|
161
|
+
option :sources, :type => :boolean,
|
162
|
+
:desc => 'Copy application source files'
|
163
|
+
|
164
|
+
option :schema, :type => :boolean,
|
165
|
+
:desc => 'Copy database schema'
|
166
|
+
|
167
|
+
option :configs, :type => :boolean,
|
168
|
+
:desc => 'Copy application-related configs data'
|
169
|
+
|
170
|
+
option :database, :type => :boolean,
|
171
|
+
:desc => 'Copy the whole application database'
|
172
|
+
|
173
|
+
option :userfiles, :type => :boolean,
|
174
|
+
:desc => 'Copy `userfiles` directory contents'
|
175
|
+
|
176
|
+
option :layout_theme, :type => :string,
|
177
|
+
:banner => 'NAME',
|
178
|
+
:desc => 'Force layout theme'
|
179
|
+
|
180
|
+
IGNORED_DIR_PATHS = %w[
|
181
|
+
/$external_c /$internal_c /$userfiles /$userfiles/tmp
|
182
|
+
/external_c /internal_c /userfiles /userfiles/tmp
|
183
|
+
/cgi-bin
|
184
|
+
/layout/themes
|
185
|
+
/m/application/cache/smarty_compile
|
186
|
+
]
|
187
|
+
|
188
|
+
IGNORED_FILE_EXTENSIONS = %w[.tar .bz2 .gz .zip .rar .iso]
|
189
|
+
|
190
|
+
# default values, rewritable by ENV['SKADATE_MAX_*_FILE_SIZE']
|
191
|
+
MAX_SOURCE_FILE_SIZE = 1024 * 1024 * 2 # ~2 megabytes
|
192
|
+
MAX_USER_FILE_SIZE = 1024 * 1024 * 10 # ~10 megabytes
|
193
|
+
|
194
|
+
# show progressbar when downloading file size exceeds this value
|
195
|
+
SHOW_PROGRESS_FILE_SIZE = 1024 * 512 # ~512 kilobytes
|
196
|
+
|
197
|
+
def clone
|
198
|
+
if options[:schema] || options[:configs]
|
199
|
+
say <<-STDOUT, :red
|
200
|
+
Remote clone options `--schema`, `--configs` are currently not implemented, please use `--database` for now.
|
201
|
+
STDOUT
|
202
|
+
return
|
203
|
+
end
|
204
|
+
|
205
|
+
if options.size == 0
|
206
|
+
say 'Error: `remote clone` options are required', :red
|
207
|
+
return help('clone')
|
208
|
+
end
|
209
|
+
|
210
|
+
remote = current.remote
|
211
|
+
layout_theme = options[:layout_theme]
|
212
|
+
@max_file_size = MAX_SOURCE_FILE_SIZE
|
213
|
+
|
214
|
+
dev_logs_dir = ::File.join(current.root_dir, '.dev', 'log')
|
215
|
+
FileUtils.mkdir_p(dev_logs_dir) unless ::File.exists?(dev_logs_dir)
|
216
|
+
log_filename = ::File.join(dev_logs_dir, "remote-clone[#{Time.now.to_i}].log")
|
217
|
+
|
218
|
+
logger.start(log_filename) do |log|
|
219
|
+
if options[:sources]
|
220
|
+
log.puts '>> Downloading application sources...', :yellow
|
221
|
+
download_source_dir(remote.root, log)
|
222
|
+
|
223
|
+
if layout_theme.nil?
|
224
|
+
log.puts('>> Getting active theme name...', :yellow)
|
225
|
+
config = remote.configs[1]
|
226
|
+
raise <<-ERRMSG unless config.name == 'theme'
|
227
|
+
unexpected remote application config entry `#{config.name}`, (expected `theme` [config_id: 1])
|
228
|
+
ERRMSG
|
229
|
+
layout_theme = config.value
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
if layout_theme
|
234
|
+
log.puts ">> Downloading [/layout/themes/#{layout_theme}/*]...", :yellow
|
235
|
+
download_source_dir remote.directory("/layout/themes/#{layout_theme}"), log
|
236
|
+
end
|
237
|
+
|
238
|
+
if options[:database]
|
239
|
+
log.puts '>> Creating remote database dump...', :yellow
|
240
|
+
remote.create_mysql_dump do |remote_file|
|
241
|
+
local_filename = File.join(current.root_dir,
|
242
|
+
"remote-db-clone.#{remote_file.basename =~ /\.tar\.gz$/ ? 'tar.gz' : 'sql'}")
|
243
|
+
|
244
|
+
if log.mute?
|
245
|
+
remote_file.save_as(local_filename)
|
246
|
+
else
|
247
|
+
progressbar = nil
|
248
|
+
remote_file.save_as(local_filename) do |chunk_size|
|
249
|
+
if progressbar
|
250
|
+
progressbar.progress += chunk_size
|
251
|
+
else
|
252
|
+
progressbar = create_progressbar(remote_file, log.padding, chunk_size)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
log.puts '>> Remote cache cleanup...', :yellow
|
258
|
+
end ? log.puts('>> Done!', :green) : log.puts('>> Warning: remote cache cleanup failed', :red)
|
259
|
+
end
|
260
|
+
|
261
|
+
if options[:userfiles]
|
262
|
+
log.puts '>> Detecting `$userfiles` directory...', :yellow
|
263
|
+
@max_file_size = MAX_USER_FILE_SIZE
|
264
|
+
download_source_dir(remote.userfiles, log)
|
265
|
+
end
|
266
|
+
|
267
|
+
log.puts '>> Completed!', :green
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
protected
|
272
|
+
def current
|
273
|
+
@current ||= SkadateGems::Dev::Skadate.new(Dir.pwd)
|
274
|
+
end
|
275
|
+
|
276
|
+
def numeric?(string)
|
277
|
+
string =~ /\A\d+\z/
|
278
|
+
end
|
279
|
+
|
280
|
+
def color_for(value)
|
281
|
+
if value.is_a? String
|
282
|
+
:green
|
283
|
+
elsif value.is_a? Fixnum
|
284
|
+
:blue
|
285
|
+
elsif value.is_a?(TrueClass) || value.is_a?(FalseClass)
|
286
|
+
:magenta
|
287
|
+
elsif value.is_a?(Array) || value.is_a?(Hash)
|
288
|
+
:yellow
|
289
|
+
else
|
290
|
+
:red
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
def logger
|
295
|
+
SkadateGems::Dev::Remote::CloneLogger
|
296
|
+
end
|
297
|
+
|
298
|
+
# @param directory [Remote::Directory] remote directory entry
|
299
|
+
# @param log [Remote::CloneLogger] logger instance
|
300
|
+
def download_source_dir(directory, log)
|
301
|
+
list = nil
|
302
|
+
log.entry(directory) { list = directory.list }
|
303
|
+
return unless list
|
304
|
+
|
305
|
+
log.padding += 1
|
306
|
+
|
307
|
+
begin
|
308
|
+
list.each do |entry|
|
309
|
+
if entry.directory?
|
310
|
+
if IGNORED_DIR_PATHS.include?(entry.path)
|
311
|
+
log.entry(entry, :skipped, 'skipped by pathname') { }
|
312
|
+
else
|
313
|
+
download_source_dir(entry, log)
|
314
|
+
end
|
315
|
+
else
|
316
|
+
if IGNORED_FILE_EXTENSIONS.include?(entry.extname)
|
317
|
+
log.entry(entry, :skipped, "skipped by (*#{entry.extname})") { }
|
318
|
+
elsif entry.size > @max_file_size
|
319
|
+
log.entry(entry, :skipped, 'skipped by filesize') { }
|
320
|
+
else
|
321
|
+
log.entry(entry) do
|
322
|
+
local_filename = File.join(current.root_dir, entry.path)
|
323
|
+
|
324
|
+
downloaded =
|
325
|
+
if !log.mute? && entry.size > SHOW_PROGRESS_FILE_SIZE
|
326
|
+
progressbar = nil
|
327
|
+
entry.save_as(local_filename) do |chunk_size|
|
328
|
+
if progressbar
|
329
|
+
progressbar.progress += chunk_size
|
330
|
+
else
|
331
|
+
progressbar = create_progressbar(entry, log.padding, chunk_size)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
else
|
335
|
+
entry.save_as(local_filename)
|
336
|
+
end
|
337
|
+
|
338
|
+
downloaded ? :downloaded : :unchanged
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
ensure
|
344
|
+
log.padding -= 1
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
def create_progressbar(entry, padding, starting_at = 0)
|
349
|
+
format =
|
350
|
+
"#{' ' * padding}~> %t (#{set_color(entry.friendly_size, :cyan)}) [#{set_color('%p%%', :green)}] [#{set_color('%B', :green)}] %e "
|
351
|
+
|
352
|
+
ProgressBar.create title: entry.basename,
|
353
|
+
total: entry.size,
|
354
|
+
smoothing: 0.5,
|
355
|
+
throttle_rate: 0.3,
|
356
|
+
format: format,
|
357
|
+
progress_mark: '·',
|
358
|
+
starting_at: starting_at
|
359
|
+
end
|
360
|
+
|
361
|
+
end
|
362
|
+
|
363
|
+
end
|
364
|
+
end
|