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.
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