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,97 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module SkadateGems
|
4
|
+
module Dev
|
5
|
+
|
6
|
+
class Remote
|
7
|
+
|
8
|
+
# Represents remote application configurations accessor.
|
9
|
+
class Configs
|
10
|
+
|
11
|
+
# @param remote [Remote] skadate remote app
|
12
|
+
def initialize(remote)
|
13
|
+
@remote = remote
|
14
|
+
end
|
15
|
+
|
16
|
+
# Configuration value accessor method.
|
17
|
+
# @param section_path_or_id [Fixnum|String] config section path or `config_id`
|
18
|
+
def [](section_path_or_id)
|
19
|
+
case section_path_or_id.class.to_s
|
20
|
+
when 'String'
|
21
|
+
raise 'Getting remote configs by path is currently not implemented'
|
22
|
+
when 'Fixnum'
|
23
|
+
get_by_id(section_path_or_id)
|
24
|
+
else
|
25
|
+
raise ArgumentError, "unexpected `section_path_or_id` type #{section_path_or_id.class}"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# @param config_id [Fixnum] configuration id
|
30
|
+
def get_by_id(config_id)
|
31
|
+
response = @remote.exec include: 'mysql_connect' do |script|
|
32
|
+
script << <<-PHPSCRIPT
|
33
|
+
$result = $mysql->query("SELECT `name`, `value` FROM `".TBL_CONFIG."` WHERE `config_id`=#{config_id}");
|
34
|
+
$config = $result->fetch_object();
|
35
|
+
echo '{ ';
|
36
|
+
echo '"name": "'.$config->name.'", ';
|
37
|
+
echo '"value": '.$config->value;
|
38
|
+
echo ' }';
|
39
|
+
PHPSCRIPT
|
40
|
+
end
|
41
|
+
|
42
|
+
result = JSON.parse(response.body)
|
43
|
+
|
44
|
+
Config.new(@remote, config_id, result['name'], result['value'])
|
45
|
+
end
|
46
|
+
|
47
|
+
# Represents remote application config entry.
|
48
|
+
class Config
|
49
|
+
|
50
|
+
# @return [Fixnum] config entry id
|
51
|
+
attr_reader :id
|
52
|
+
|
53
|
+
# @return [String] config entry name
|
54
|
+
attr_reader :name
|
55
|
+
|
56
|
+
# @return [mixed] config entry value
|
57
|
+
attr_reader :value
|
58
|
+
|
59
|
+
# @return [nil | :updated | :not_modified] config value update status
|
60
|
+
attr_reader :update_status
|
61
|
+
|
62
|
+
# @param remote [Remote] remote skadate app
|
63
|
+
# @param id [Fixnum] config id
|
64
|
+
# @param name [String] config name
|
65
|
+
# @param value [mixed] config value
|
66
|
+
def initialize(remote, id, name, value)
|
67
|
+
@remote, @id, @name, @value = remote, id, name, value
|
68
|
+
end
|
69
|
+
|
70
|
+
# Perform remote application config value update.
|
71
|
+
# Changes the #update_status value.
|
72
|
+
# @param value [#to_json] json serializable value
|
73
|
+
def value=(value)
|
74
|
+
if value == @value
|
75
|
+
@update_status = :not_modified
|
76
|
+
return
|
77
|
+
end
|
78
|
+
|
79
|
+
response = @remote.exec(include: 'mysql_connect') do |script|
|
80
|
+
script << <<-PHPSCRIPT
|
81
|
+
$value = $mysql->real_escape_string('#{value.to_json.gsub(/['\\]/, '\\\\\0')}');
|
82
|
+
$mysql->query("UPDATE `".TBL_CONFIG."` SET `value`='$value' WHERE `config_id`=#{@id}");
|
83
|
+
echo $mysql->affected_rows;
|
84
|
+
PHPSCRIPT
|
85
|
+
end
|
86
|
+
|
87
|
+
@update_status = (response.body == '1') ? :updated : :not_modified
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'skadategems/dev/remote/file'
|
3
|
+
|
4
|
+
module SkadateGems
|
5
|
+
module Dev
|
6
|
+
|
7
|
+
class Remote
|
8
|
+
|
9
|
+
# Represents a remote directory.
|
10
|
+
class Directory < File
|
11
|
+
|
12
|
+
# @attr list_hidden [boolean]
|
13
|
+
attr_accessor :list_hidden
|
14
|
+
|
15
|
+
# @return [boolean] always true
|
16
|
+
def directory?
|
17
|
+
true
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [String] always '/'
|
21
|
+
def extname
|
22
|
+
'/'
|
23
|
+
end
|
24
|
+
|
25
|
+
# List remote directory files.
|
26
|
+
# @return [Array < Directory | File >] where each entry
|
27
|
+
# may be either a `Remote::Directory` or a `Remote::File` accessor object.
|
28
|
+
def list
|
29
|
+
path = self.path.dup
|
30
|
+
path[0] = '' if path[0] == '/'
|
31
|
+
path[-1] = '' if path[-1] == '/'
|
32
|
+
|
33
|
+
response = @remote.exec do |batch|
|
34
|
+
batch << <<-PHPSCRIPT
|
35
|
+
$dir = dir(DIR_SITE_ROOT . '#{path.gsub(/['\\]/, '\\\\\0')}');
|
36
|
+
|
37
|
+
$list = array();
|
38
|
+
while (false !== ($file = $dir->read())) {
|
39
|
+
if (#{if list_hidden
|
40
|
+
'$file === \'.\' || $file === \'..\''
|
41
|
+
else
|
42
|
+
'$file{0} === \'.\' && $file !== \'.htaccess\''
|
43
|
+
end}) {
|
44
|
+
continue;
|
45
|
+
}
|
46
|
+
|
47
|
+
$path = "$dir->path/$file";
|
48
|
+
|
49
|
+
if ($path === $_SERVER['SCRIPT_FILENAME']) {
|
50
|
+
continue;
|
51
|
+
}
|
52
|
+
|
53
|
+
if (is_file($path)) {
|
54
|
+
$type = 'f';
|
55
|
+
} elseif (is_dir($path)) {
|
56
|
+
$type = 'd';
|
57
|
+
} else {
|
58
|
+
continue;
|
59
|
+
}
|
60
|
+
|
61
|
+
$list[] = array($file, $type, filesize($path));
|
62
|
+
}
|
63
|
+
|
64
|
+
echo json_encode($list);
|
65
|
+
PHPSCRIPT
|
66
|
+
end
|
67
|
+
|
68
|
+
result = []
|
69
|
+
|
70
|
+
JSON.parse(response.body).each do |entry|
|
71
|
+
name, type, size = entry
|
72
|
+
_path = ::File.join(self.path, name)
|
73
|
+
|
74
|
+
result << if type == 'd'
|
75
|
+
Directory.new(@remote, _path, size)
|
76
|
+
else
|
77
|
+
File.new(@remote, _path, size)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
result.sort! { |a, b| a.basename <=> b.basename }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'digest/md5'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
module SkadateGems
|
6
|
+
module Dev
|
7
|
+
|
8
|
+
class Remote
|
9
|
+
|
10
|
+
# Remote file accessor.
|
11
|
+
class File
|
12
|
+
|
13
|
+
# @return [String] relative path to a file
|
14
|
+
attr_reader :path
|
15
|
+
|
16
|
+
# @return [Fixnum] remote file size in bytes
|
17
|
+
attr_reader :size
|
18
|
+
|
19
|
+
def initialize(remote, path, size = nil)
|
20
|
+
@remote, @path, @size = remote, path, size
|
21
|
+
end
|
22
|
+
|
23
|
+
def directory?
|
24
|
+
false
|
25
|
+
end
|
26
|
+
|
27
|
+
# @return [String] remote file basename
|
28
|
+
def basename
|
29
|
+
::File.basename(@path)
|
30
|
+
end
|
31
|
+
|
32
|
+
# @return [String] remote file extension
|
33
|
+
def extname
|
34
|
+
::File.extname(@path)
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [String] human readable file size
|
38
|
+
def friendly_size
|
39
|
+
units = %W(B KiB MiB GiB TiB)
|
40
|
+
|
41
|
+
size, unit = units.reduce(@size.to_f) do |(fsize, _), type|
|
42
|
+
fsize > 512 ? [fsize / 1024, type] : (break [fsize, type])
|
43
|
+
end
|
44
|
+
|
45
|
+
"#{size > 9 || size.modulo(1) < 0.1 ? '%d' : '%.1f'} %s" % [size, unit]
|
46
|
+
end
|
47
|
+
|
48
|
+
# Request remote file to read it's contents.
|
49
|
+
# @yield [response] passes a http response to the block
|
50
|
+
def request(md5_checksum: nil, &block)
|
51
|
+
path = self.path.dup
|
52
|
+
path[0] = '' if path[0] == '/'
|
53
|
+
|
54
|
+
batch = ScriptBatch.new
|
55
|
+
|
56
|
+
batch << <<-PHPSCRIPT
|
57
|
+
$filename = DIR_SITE_ROOT . '#{path.gsub(/['\\]/, '\\\\\0')}';
|
58
|
+
|
59
|
+
if (!file_exists($filename)) {
|
60
|
+
header('HTTP/1.1 404 Not Found');
|
61
|
+
exit;
|
62
|
+
}
|
63
|
+
|
64
|
+
$md5_checksum = md5_file($filename);
|
65
|
+
PHPSCRIPT
|
66
|
+
|
67
|
+
if md5_checksum
|
68
|
+
raise ':md5_checksum must be a String' unless md5_checksum.is_a? String
|
69
|
+
|
70
|
+
batch << <<-PHPSCRIPT
|
71
|
+
if ($md5_checksum === '#{md5_checksum.gsub(/['\\]/, '\\\\\0')}') {
|
72
|
+
header('HTTP/1.1 304 Not Modified');
|
73
|
+
exit;
|
74
|
+
}
|
75
|
+
PHPSCRIPT
|
76
|
+
end
|
77
|
+
|
78
|
+
batch << <<-PHPSCRIPT
|
79
|
+
header('Content-Description: File Transfer');
|
80
|
+
header('Content-Type: application/octet-stream');
|
81
|
+
header('Content-Disposition: attachment; filename=' . basename($filename));
|
82
|
+
header('Content-Length: ' . filesize($filename));
|
83
|
+
header('X-Content-MD5-Checksum: ' . $md5_checksum);
|
84
|
+
ob_clean();
|
85
|
+
flush();
|
86
|
+
readfile($filename);
|
87
|
+
exit;
|
88
|
+
PHPSCRIPT
|
89
|
+
|
90
|
+
@remote.accessor.exec(batch) do |response|
|
91
|
+
if response.is_a?(Net::HTTPNotModified)
|
92
|
+
block.call(response)
|
93
|
+
elsif response.is_a?(Net::HTTPOK)
|
94
|
+
if response['content-disposition'] == "attachment; filename=#{basename}" &&
|
95
|
+
response['x-content-md5-checksum']
|
96
|
+
block.call(response)
|
97
|
+
else
|
98
|
+
raise 'unexpected response headers'
|
99
|
+
end
|
100
|
+
else
|
101
|
+
raise "[#{response.code} \"#{response.message}\"]"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Download and save a remote file as a given filename.
|
107
|
+
# @param filename [String] filename to save file as
|
108
|
+
# @yield [chunk_size] on each segment receive
|
109
|
+
# @yieldparam chunk_size [Fixnum] received segment size
|
110
|
+
def save_as(filename)
|
111
|
+
current_checksum = if ::File.exists?(filename)
|
112
|
+
Digest::MD5.file(filename).hexdigest
|
113
|
+
end
|
114
|
+
|
115
|
+
request(md5_checksum: current_checksum) do |response|
|
116
|
+
if response.is_a?(Net::HTTPNotModified)
|
117
|
+
return false
|
118
|
+
elsif response.is_a?(Net::HTTPOK)
|
119
|
+
@size = response['content-length'].to_i if @size.nil?
|
120
|
+
new_checksum = response['x-content-md5-checksum']
|
121
|
+
md5 = Digest::MD5.new
|
122
|
+
|
123
|
+
begin
|
124
|
+
tempfile = Tempfile.create([basename, extname])
|
125
|
+
response.read_body do |chunk|
|
126
|
+
tempfile << chunk
|
127
|
+
md5 << chunk
|
128
|
+
yield chunk.size if block_given?
|
129
|
+
end
|
130
|
+
tempfile.close
|
131
|
+
|
132
|
+
if md5.hexdigest != new_checksum
|
133
|
+
raise "md5 checksum mismatch:\n [#{md5.hexdigest}]\n [#{new_checksum}]"
|
134
|
+
end
|
135
|
+
|
136
|
+
parent_dir = ::File.dirname(filename)
|
137
|
+
FileUtils.mkdir_p(parent_dir) unless ::File.exists?(parent_dir)
|
138
|
+
|
139
|
+
FileUtils.mv(tempfile.path, filename)
|
140
|
+
FileUtils.chmod(0644, filename)
|
141
|
+
|
142
|
+
return true
|
143
|
+
ensure
|
144
|
+
tempfile.close unless tempfile.closed?
|
145
|
+
end
|
146
|
+
else
|
147
|
+
raise "unexpected response #{response}"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<?php
|
2
|
+
|
3
|
+
$mysql = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME) or die("Error " . mysqli_error($link));
|
4
|
+
if ($mysql->connect_error) {
|
5
|
+
die('Connect Error (' . $mysql->connect_errno . ') ' . $mysql->connect_error);
|
6
|
+
}
|
7
|
+
|
8
|
+
require_once DIR_INTERNALS . 'db_tbl.const.php';
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'skadategems/dev/source_controller'
|
2
|
+
require 'skadategems/dev/remote'
|
3
|
+
|
4
|
+
module SkadateGems
|
5
|
+
module Dev
|
6
|
+
|
7
|
+
# Represents Skadate development project that is located in a specified `:root_dir`.
|
8
|
+
class Skadate
|
9
|
+
|
10
|
+
attr_reader :root_dir
|
11
|
+
|
12
|
+
# @param root_dir [String] path to skadate source root directory
|
13
|
+
def initialize(root_dir)
|
14
|
+
@root_dir = root_dir
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [SourceController] skadate source controller object
|
18
|
+
def source
|
19
|
+
@source ||= SourceController.new(@root_dir)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [Remote] remote application accessor
|
23
|
+
def remote
|
24
|
+
@remote ||= Remote.new(source.filename '.dev/remote-server.json')
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module SkadateGems
|
4
|
+
module Dev
|
5
|
+
|
6
|
+
# Represents Skadate software version
|
7
|
+
class SoftwareVersion
|
8
|
+
LIST, MAP = [], {}
|
9
|
+
|
10
|
+
attr_reader :major
|
11
|
+
attr_reader :minor
|
12
|
+
attr_reader :build
|
13
|
+
attr_reader :release_date
|
14
|
+
attr_reader :codename
|
15
|
+
|
16
|
+
# @param version [String] string version representation
|
17
|
+
# @param release_date [Date] version release date
|
18
|
+
# @param codename [String] software version codename
|
19
|
+
def initialize(version, release_date = nil, codename = nil)
|
20
|
+
@major, @minor, @build = version.split('.', 3).map { |n| n.to_i }
|
21
|
+
@release_date = release_date
|
22
|
+
@codename = codename
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [String] version string representation
|
26
|
+
def to_s
|
27
|
+
"#{@major}.#{@minor}.#{@build}"
|
28
|
+
end
|
29
|
+
|
30
|
+
# Versions equality operator
|
31
|
+
def == (other)
|
32
|
+
return other == to_s if other.instance_of?(String)
|
33
|
+
|
34
|
+
return false unless other.instance_of?(SoftwareVersion)
|
35
|
+
|
36
|
+
@major == other.major &&
|
37
|
+
@minor == other.minor &&
|
38
|
+
@build == other.build
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [boolean] true if current version is supported by `skadategems-dev`
|
42
|
+
def supported?
|
43
|
+
MAP.key?(to_s)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Raised in case of software version compatibility error.
|
47
|
+
# @see Dev::SourceController#version!
|
48
|
+
class CompatibilityError < StandardError; end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Skadate software supported versions.
|
52
|
+
[ ['9.0.2500', Date.new(2012, 1, 30)],
|
53
|
+
['9.0.2555', Date.new(2012, 5, 23)],
|
54
|
+
['9.2.2657', Date.new(2012, 10, 12)],
|
55
|
+
['9.2.2960', Date.new(2013, 4, 16), 'Master'],
|
56
|
+
['9.3.3144', Date.new(2013, 10, 8), 'Master II']
|
57
|
+
].each do |params|
|
58
|
+
version = SoftwareVersion.new(*params)
|
59
|
+
SoftwareVersion::LIST << version
|
60
|
+
SoftwareVersion::MAP[params[0]] = version
|
61
|
+
end
|
62
|
+
|
63
|
+
SoftwareVersion::LIST.freeze
|
64
|
+
SoftwareVersion::MAP.freeze
|
65
|
+
end
|
66
|
+
end
|