skadategems-dev 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.travis.yml +12 -0
  4. data/Gemfile +13 -0
  5. data/Guardfile +8 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +74 -0
  8. data/Rakefile +6 -0
  9. data/bin/skadate +9 -0
  10. data/bin/skadategems-dev +18 -0
  11. data/integration/README.md +97 -0
  12. data/integration/cli/skadate_init_spec.rb +86 -0
  13. data/integration/cli/skadate_spec.rb +50 -0
  14. data/integration/cli/spec_helper.rb +23 -0
  15. data/integration/lib/remote/download_file_spec.rb +136 -0
  16. data/integration/spec_helper.rb +101 -0
  17. data/lib/skadategems/dev/bundle.rb +115 -0
  18. data/lib/skadategems/dev/cli/remote.rb +364 -0
  19. data/lib/skadategems/dev/cli/skadate.rb +184 -0
  20. data/lib/skadategems/dev/remote.rb +170 -0
  21. data/lib/skadategems/dev/remote/clone_logger.rb +118 -0
  22. data/lib/skadategems/dev/remote/configs.rb +97 -0
  23. data/lib/skadategems/dev/remote/directory.rb +88 -0
  24. data/lib/skadategems/dev/remote/file.rb +157 -0
  25. data/lib/skadategems/dev/remote/includes/mysql_connect.php +8 -0
  26. data/lib/skadategems/dev/skadate.rb +30 -0
  27. data/lib/skadategems/dev/software_version.rb +66 -0
  28. data/lib/skadategems/dev/source_controller.rb +101 -0
  29. data/lib/skadategems/dev/version.rb +5 -0
  30. data/skadategems-dev.gemspec +33 -0
  31. data/spec/skadategems/dev/bundle_spec.rb +268 -0
  32. data/spec/skadategems/dev/cli/remote_spec.rb +623 -0
  33. data/spec/skadategems/dev/cli/skadate_spec.rb +182 -0
  34. data/spec/skadategems/dev/remote/clone_logger_spec.rb +324 -0
  35. data/spec/skadategems/dev/remote/directory_spec.rb +117 -0
  36. data/spec/skadategems/dev/remote/file_spec.rb +74 -0
  37. data/spec/skadategems/dev/remote_spec.rb +95 -0
  38. data/spec/skadategems/dev/skadate_spec.rb +62 -0
  39. data/spec/skadategems/dev/software_version_spec.rb +92 -0
  40. data/spec/skadategems/dev/source_controller_spec.rb +243 -0
  41. data/spec/spec_helper.rb +32 -0
  42. 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