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,184 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'uri'
|
3
|
+
require 'thor'
|
4
|
+
require 'execphp'
|
5
|
+
require 'skadategems/dev/skadate'
|
6
|
+
require 'skadategems/dev/bundle'
|
7
|
+
require 'skadategems/dev/cli/remote'
|
8
|
+
|
9
|
+
module SkadateGems::Dev
|
10
|
+
module CLI
|
11
|
+
|
12
|
+
class Skadate < Thor
|
13
|
+
namespace 'skadate'
|
14
|
+
|
15
|
+
desc 'version [ list | [ about ] | number | build ]',
|
16
|
+
'Display Skadate software version information'
|
17
|
+
long_desc <<-LONGDESC
|
18
|
+
To detect the version of the software that is located under the current working directory:
|
19
|
+
\x5
|
20
|
+
\x5 $> skadate version
|
21
|
+
\x5
|
22
|
+
|
23
|
+
To list all versions supported by the tool:
|
24
|
+
\x5
|
25
|
+
\x5 $> skadate version list
|
26
|
+
\x5
|
27
|
+
\x5
|
28
|
+
LONGDESC
|
29
|
+
def version(section = 'about')
|
30
|
+
section =
|
31
|
+
case section[0]
|
32
|
+
when 'l'
|
33
|
+
:list
|
34
|
+
when 'a'
|
35
|
+
:about
|
36
|
+
when 'n'
|
37
|
+
:number
|
38
|
+
when 'b'
|
39
|
+
:build
|
40
|
+
else
|
41
|
+
say "Error: `skadate version` unrecognized info section `#{section}`", :red
|
42
|
+
return help('version')
|
43
|
+
end
|
44
|
+
|
45
|
+
if section == :list
|
46
|
+
current_version = current.source.version
|
47
|
+
|
48
|
+
SoftwareVersion::LIST.each do |version|
|
49
|
+
if version == current_version
|
50
|
+
say('* ', version_status(current_version)[0], false)
|
51
|
+
else
|
52
|
+
say ' ', nil, false
|
53
|
+
end
|
54
|
+
|
55
|
+
say "skadate-#{version} [#{version.release_date.strftime('%Y-%m-%d')}]"
|
56
|
+
end
|
57
|
+
|
58
|
+
return
|
59
|
+
end
|
60
|
+
|
61
|
+
version = current.source.version!
|
62
|
+
color, status = version_status(version)
|
63
|
+
|
64
|
+
case section
|
65
|
+
when :about
|
66
|
+
say 'Skadate Software '
|
67
|
+
say("#{version} ", :bold)
|
68
|
+
|
69
|
+
if version.codename
|
70
|
+
say('(', :white, false)
|
71
|
+
say("#{version.codename}", :cyan, false)
|
72
|
+
say ') '
|
73
|
+
end
|
74
|
+
|
75
|
+
say('[', :white, false)
|
76
|
+
say(status, color, false)
|
77
|
+
say ']'
|
78
|
+
|
79
|
+
if version.release_date
|
80
|
+
say "Release date: #{version.release_date.strftime('%B %e, %Y')}"
|
81
|
+
end
|
82
|
+
|
83
|
+
say 'Copyright (c) 2004-2014 Skalfa LLC'
|
84
|
+
|
85
|
+
when :number
|
86
|
+
say(version, color)
|
87
|
+
|
88
|
+
when :build
|
89
|
+
say(version.build, color)
|
90
|
+
|
91
|
+
else
|
92
|
+
raise "unexpected case :#{section}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
desc 'init', 'Initialize Skadate development project'
|
97
|
+
|
98
|
+
option :remote_url, type: :string,
|
99
|
+
banner: 'URL',
|
100
|
+
aliases: :r,
|
101
|
+
desc: 'Remote application server url'
|
102
|
+
|
103
|
+
def init
|
104
|
+
source = current.source
|
105
|
+
|
106
|
+
dev_dir = source.filename('.dev')
|
107
|
+
FileUtils.mkdir(dev_dir) unless File.exists?(dev_dir)
|
108
|
+
|
109
|
+
htaccess_file_path = source.filename('.dev/.htaccess')
|
110
|
+
unless File.exists?(htaccess_file_path)
|
111
|
+
File.write(htaccess_file_path, <<-HTACCESS)
|
112
|
+
Order Deny,Allow
|
113
|
+
Deny from all
|
114
|
+
HTACCESS
|
115
|
+
end
|
116
|
+
|
117
|
+
if options[:remote_url]
|
118
|
+
execphp = ExecPHP::ServerSideAccessor.new
|
119
|
+
|
120
|
+
rs_config = ExecPHP::RemoteServer.new(
|
121
|
+
URI.join(options[:remote_url], 'exec.php').to_s,
|
122
|
+
execphp.access_token
|
123
|
+
)
|
124
|
+
|
125
|
+
rs_config.save_as source.filename('.dev/remote-server.json')
|
126
|
+
execphp.generate source.filename('exec.php')
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
desc 'export [FILENAME]',
|
131
|
+
'Export source files for deployment'
|
132
|
+
|
133
|
+
option :layout_theme, type: :string,
|
134
|
+
desc: 'Exclude all themes except a specified one from being bundled',
|
135
|
+
banner: 'NAME',
|
136
|
+
aliases: '-l'
|
137
|
+
|
138
|
+
long_desc <<-LONGDESC
|
139
|
+
Export current project source files as an archive for deployment.
|
140
|
+
|
141
|
+
To save the largest part of disk space,
|
142
|
+
it's recommended to specify layout theme name using the `--layout-theme` switch,
|
143
|
+
that will exclude all unnecessary `layout/themes` from being bundled.
|
144
|
+
|
145
|
+
For example:
|
146
|
+
\x5
|
147
|
+
\x5 $> skadate export --layout-theme stencil
|
148
|
+
\x5
|
149
|
+
\x5
|
150
|
+
LONGDESC
|
151
|
+
def export(filename = nil)
|
152
|
+
version = current.source.version!
|
153
|
+
|
154
|
+
filename ||= "./skadate-#{version}-#{Time.now.to_i}.tar.gz"
|
155
|
+
|
156
|
+
bundle = Bundle.new(current)
|
157
|
+
bundle.layout_theme = options[:layout_theme] if options[:layout_theme]
|
158
|
+
bundle.gzip(filename)
|
159
|
+
end
|
160
|
+
|
161
|
+
register Remote, :remote,
|
162
|
+
'remote < ping | exec | clone | config | ... > [OPTIONS]',
|
163
|
+
'Remote application control'
|
164
|
+
|
165
|
+
protected
|
166
|
+
def current
|
167
|
+
@current ||= SkadateGems::Dev::Skadate.new(Dir.pwd)
|
168
|
+
end
|
169
|
+
|
170
|
+
def version_status(version)
|
171
|
+
return :red, 'unknown' unless version.supported?
|
172
|
+
|
173
|
+
list = SoftwareVersion::LIST
|
174
|
+
|
175
|
+
case version
|
176
|
+
when list[-1] then [:green, 'latest']
|
177
|
+
when list[-2] then [:yellow, 'previous']
|
178
|
+
else [:red, 'outdated']
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
require 'execphp'
|
2
|
+
require 'skadategems/dev/remote/directory'
|
3
|
+
require 'skadategems/dev/remote/configs'
|
4
|
+
|
5
|
+
module SkadateGems
|
6
|
+
module Dev
|
7
|
+
|
8
|
+
# Represents remote Skadate-driven application.
|
9
|
+
class Remote
|
10
|
+
|
11
|
+
attr_reader :accessor
|
12
|
+
|
13
|
+
def initialize(config_filename)
|
14
|
+
@accessor = ExecPHP::RemoteServer.from_file(config_filename)
|
15
|
+
end
|
16
|
+
|
17
|
+
def exec(options = {}, &block)
|
18
|
+
batch = ScriptBatch.new
|
19
|
+
|
20
|
+
if options[:include]
|
21
|
+
[*options[:include]].each do |include_name|
|
22
|
+
batch.include_file ::File.join(__dir__, "remote/includes/#{include_name}.php")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
block.call(batch)
|
27
|
+
|
28
|
+
@accessor.exec(batch)
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [Remote::Directory] remote source directory accessor
|
32
|
+
def directory(path)
|
33
|
+
Directory.new(self, path, 0)
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [Remote::Directory] remote source root directory
|
37
|
+
def root
|
38
|
+
@root ||= directory '/'
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Remote::Configs] remote application configs accessor
|
42
|
+
def configs
|
43
|
+
@config ||= Configs.new(self)
|
44
|
+
end
|
45
|
+
|
46
|
+
def create_mysql_dump(&block)
|
47
|
+
response = exec do |script|
|
48
|
+
script << <<-PHPSCRIPT
|
49
|
+
require_once DIR_INTERNALS . 'system.const.php';
|
50
|
+
|
51
|
+
if (strpos(DIR_INTERNAL_C, DIR_SITE_ROOT) !== 0) {
|
52
|
+
header('HTTP/1.1 500 Internal Server Error');
|
53
|
+
exit('Error: unable to detect path to `internal_c/`.');
|
54
|
+
}
|
55
|
+
|
56
|
+
function program_available($bin) {
|
57
|
+
$output = null;
|
58
|
+
$result = -1;
|
59
|
+
exec("type $bin>/dev/null 2>&1", $output, $result);
|
60
|
+
return $result === 0;
|
61
|
+
}
|
62
|
+
|
63
|
+
if (!program_available('mysqldump')) {
|
64
|
+
header('HTTP/1.1 500 Internal Server Error');
|
65
|
+
exit('Error: `mysqldump` is not available');
|
66
|
+
}
|
67
|
+
|
68
|
+
$tmp_dir = DIR_INTERNAL_C;
|
69
|
+
$tmp_dir_path = substr($tmp_dir, strlen(DIR_SITE_ROOT));
|
70
|
+
|
71
|
+
$uniqid = uniqid();
|
72
|
+
$sql_filename = "$uniqid.sql";
|
73
|
+
|
74
|
+
$db_host = DB_HOST;
|
75
|
+
$db_user = DB_USER;
|
76
|
+
$db_pass = DB_PASS;
|
77
|
+
$db_name = DB_NAME;
|
78
|
+
|
79
|
+
$output = null;
|
80
|
+
$result = -1;
|
81
|
+
exec("cd '$tmp_dir' && mysqldump --host='$db_host' --user='$db_user' --password='$db_pass' '$db_name' > '$sql_filename'", $output, $result);
|
82
|
+
if (!file_exists($tmp_dir . $sql_filename)) {
|
83
|
+
header('HTTP/1.1 500 Internal Server Error');
|
84
|
+
exit('`mysqldump` error: ' . join($output, "\n"));
|
85
|
+
}
|
86
|
+
|
87
|
+
if (program_available('tar')) {
|
88
|
+
$tar_filename = "$uniqid.tar.gz";
|
89
|
+
|
90
|
+
$output = null;
|
91
|
+
$result = -1;
|
92
|
+
exec("cd '$tmp_dir' && tar -czf '$tar_filename' '$sql_filename'", $output, $result);
|
93
|
+
if (file_exists($tmp_dir . $tar_filename)) {
|
94
|
+
unlink($tmp_dir . $sql_filename);
|
95
|
+
exit('["' . $tmp_dir_path . $tar_filename . '", ' . filesize($tmp_dir . $tar_filename) . ']');
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
exit('["' . $tmp_dir_path . $sql_filename . '", ' . filesize($tmp_dir . $sql_filename) . ']');
|
100
|
+
PHPSCRIPT
|
101
|
+
end
|
102
|
+
|
103
|
+
raise "#{response.code} [#{response.message}]:\n >> #{response.body}" if response.code != '200'
|
104
|
+
|
105
|
+
path, size = JSON.parse(response.body)
|
106
|
+
block.call File.new(self, path, size)
|
107
|
+
|
108
|
+
response = exec do |script|
|
109
|
+
script << <<-PHPSCRIPT
|
110
|
+
echo unlink(DIR_SITE_ROOT . '#{path}') ? '1' : '0';
|
111
|
+
PHPSCRIPT
|
112
|
+
end
|
113
|
+
|
114
|
+
raise "#{response.code} [#{response.message}]:\n >> #{response.body}" if response.code != '200'
|
115
|
+
|
116
|
+
response.body == '1'
|
117
|
+
end
|
118
|
+
|
119
|
+
# Detect the name of the `userfiles` directory requesting a remote application.
|
120
|
+
# @return [Remote::Directory] remote `userfiles` directory
|
121
|
+
def userfiles
|
122
|
+
response = exec do |script|
|
123
|
+
script << <<-PHPSCRIPT
|
124
|
+
require_once DIR_INTERNALS . 'system.const.php';
|
125
|
+
|
126
|
+
if (strpos(DIR_USERFILES, DIR_SITE_ROOT) !== 0) {
|
127
|
+
header('HTTP/1.1 500 Internal Server Error');
|
128
|
+
exit('Error: unable to detect path to `/userfiles`.');
|
129
|
+
}
|
130
|
+
|
131
|
+
echo substr(DIR_USERFILES, strlen(DIR_SITE_ROOT) - 1, -1);
|
132
|
+
PHPSCRIPT
|
133
|
+
end
|
134
|
+
|
135
|
+
raise "#{response.code} [#{response.message}]:\n >> #{response.body}" if response.code != '200'
|
136
|
+
|
137
|
+
directory(response.body)
|
138
|
+
end
|
139
|
+
|
140
|
+
# PHP script batch.
|
141
|
+
class ScriptBatch < ExecPHP::ScriptBatch
|
142
|
+
|
143
|
+
# Initializes a remote script batch which automatically
|
144
|
+
# finds and requires Skadate's `config.php` file.
|
145
|
+
def initialize
|
146
|
+
super do |script|
|
147
|
+
script << <<-PHPSCRIPT
|
148
|
+
$try_dir = dirname(__FILE__);
|
149
|
+
while (!(file_exists($config_file = $try_dir . '/internals/config.php')
|
150
|
+
|| file_exists($config_file = $try_dir . '/internals/$config.php')))
|
151
|
+
{
|
152
|
+
$prev_dir = $try_dir;
|
153
|
+
if ($prev_dir == ($try_dir = dirname($try_dir))) {
|
154
|
+
throw new Exception('Error: unable to find a config file');
|
155
|
+
}
|
156
|
+
}
|
157
|
+
|
158
|
+
require_once $config_file;
|
159
|
+
|
160
|
+
define('DIR_INTERNALS', dirname($config_file) . DIRECTORY_SEPARATOR);
|
161
|
+
PHPSCRIPT
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
end
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'skadategems/dev/remote/directory'
|
2
|
+
require 'skadategems/dev/remote/file'
|
3
|
+
require 'thor/shell'
|
4
|
+
|
5
|
+
module SkadateGems::Dev
|
6
|
+
class Remote
|
7
|
+
|
8
|
+
class CloneLogger < Thor::Base.shell
|
9
|
+
|
10
|
+
def self.start(log_file_path, &block)
|
11
|
+
::File.open(log_file_path, 'w') do |file_io|
|
12
|
+
block.call new(file_io)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(file_io)
|
17
|
+
@file_io = file_io
|
18
|
+
super()
|
19
|
+
end
|
20
|
+
|
21
|
+
# @param entry [Remote::Entry] remote fs entry
|
22
|
+
# @param status [Symbol] log entry status
|
23
|
+
# @param label [String] log entry badge label
|
24
|
+
# @yield if block is given expecting that it returns an entry processing status
|
25
|
+
def entry(entry, status = nil, label = nil)
|
26
|
+
icon = if status == :skipped
|
27
|
+
'*'
|
28
|
+
elsif entry.is_a?(Directory)
|
29
|
+
'='
|
30
|
+
elsif entry.is_a?(File)
|
31
|
+
'~'
|
32
|
+
end
|
33
|
+
|
34
|
+
prefix = "#{' ' * padding}#{icon}>"
|
35
|
+
|
36
|
+
print(if entry.is_a?(Directory)
|
37
|
+
"#{prefix} #{set_color(entry.path, :blue)}"
|
38
|
+
elsif entry.is_a?(File)
|
39
|
+
"#{prefix} #{set_color(entry.basename, :white)} (#{set_color(entry.friendly_size, :cyan)})"
|
40
|
+
else
|
41
|
+
"#{prefix} #{set_color(entry.to_s, :on_black)}"
|
42
|
+
end)
|
43
|
+
|
44
|
+
write(if entry.is_a?(Directory)
|
45
|
+
"#{prefix} #{entry.path}"
|
46
|
+
elsif entry.is_a?(File)
|
47
|
+
"#{prefix} #{entry.basename} (#{entry.friendly_size})"
|
48
|
+
else
|
49
|
+
"#{prefix} #{entry}"
|
50
|
+
end)
|
51
|
+
|
52
|
+
if status == :skipped
|
53
|
+
label ||= 'skipped'
|
54
|
+
print(" [#{set_color(label, :magenta)}]")
|
55
|
+
write(" [#{label}]")
|
56
|
+
end
|
57
|
+
|
58
|
+
if block_given?
|
59
|
+
print ' ...'
|
60
|
+
stdout.flush
|
61
|
+
|
62
|
+
status = yield
|
63
|
+
|
64
|
+
label, color =
|
65
|
+
case status
|
66
|
+
when :downloaded
|
67
|
+
['downloaded', :green]
|
68
|
+
when :unchanged
|
69
|
+
['unchanged', :cyan]
|
70
|
+
else
|
71
|
+
# no status badge
|
72
|
+
end
|
73
|
+
|
74
|
+
if label
|
75
|
+
print "\b" * 4 if can_display_colors?
|
76
|
+
print(" [#{set_color(label, color)}]")
|
77
|
+
write(" [#{label}]")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
rescue
|
82
|
+
print "\b" * 4 if can_display_colors?
|
83
|
+
print " [#{set_color('error', :red)}]"
|
84
|
+
write " [error]\n#{'>' * 30}\n[#{$!.class.name}]: #{$!.message}\n#{'-' * 30}"
|
85
|
+
raise $!
|
86
|
+
ensure
|
87
|
+
print "\n"
|
88
|
+
write "\n"
|
89
|
+
stdout.flush
|
90
|
+
end
|
91
|
+
|
92
|
+
# @param line [String] string line to log
|
93
|
+
# @param color [Symbol] string color to colorize if supported
|
94
|
+
def puts(line, color = nil)
|
95
|
+
unless mute?
|
96
|
+
super set_color(line, color)
|
97
|
+
stdout.flush
|
98
|
+
end
|
99
|
+
|
100
|
+
write "#{line}\n"
|
101
|
+
end
|
102
|
+
|
103
|
+
def set_color(string, *colors)
|
104
|
+
can_display_colors? ? super(string, *colors) : string
|
105
|
+
end
|
106
|
+
|
107
|
+
def print(obj, *rest)
|
108
|
+
super unless mute?
|
109
|
+
end
|
110
|
+
|
111
|
+
def write(string)
|
112
|
+
@file_io << string if @file_io
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|