machinery-tool 1.9.1 → 1.10.0

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 (45) hide show
  1. checksums.yaml +4 -4
  2. data/NEWS +7 -0
  3. data/html/assets/compare/machinery-compare.js +43 -0
  4. data/html/assets/compare/machinery.js +17 -0
  5. data/html/assets/machinery.css +13 -0
  6. data/html/assets/modal.js +280 -0
  7. data/html/assets/show/machinery-show.js +16 -9
  8. data/html/assets/show/machinery.js +61 -25
  9. data/html/comparison.html.haml +194 -116
  10. data/html/index.html.haml +46 -12
  11. data/lib/array.rb +2 -1
  12. data/lib/cli.rb +44 -23
  13. data/lib/compare_task.rb +5 -29
  14. data/lib/comparison.rb +69 -0
  15. data/lib/config.rb +4 -0
  16. data/lib/export_task.rb +7 -0
  17. data/lib/file_scope.rb +2 -2
  18. data/lib/html.rb +165 -88
  19. data/lib/machinery.rb +6 -2
  20. data/lib/{generate_html_task.rb → man_task.rb} +8 -6
  21. data/lib/object.rb +1 -1
  22. data/lib/renderer.rb +70 -41
  23. data/lib/{scope_file_access.rb → scope_file_access_archive.rb} +23 -20
  24. data/lib/scope_file_access_flat.rb +36 -0
  25. data/lib/serve_html_task.rb +33 -0
  26. data/lib/show_task.rb +19 -5
  27. data/lib/system_file.rb +25 -0
  28. data/lib/version.rb +1 -1
  29. data/man/generated/machinery.1.gz +0 -0
  30. data/man/generated/machinery.1.html +60 -34
  31. data/plugins/changed_managed_files/changed_managed_files_renderer.rb +5 -5
  32. data/plugins/config_files/config_files_renderer.rb +7 -6
  33. data/plugins/groups/groups_renderer.rb +3 -3
  34. data/plugins/os/os_renderer.rb +5 -5
  35. data/plugins/packages/packages_model.rb +25 -0
  36. data/plugins/packages/packages_renderer.rb +36 -5
  37. data/plugins/patterns/patterns_renderer.rb +3 -3
  38. data/plugins/repositories/repositories_model.rb +1 -1
  39. data/plugins/repositories/repositories_renderer.rb +4 -4
  40. data/plugins/services/services_model.rb +2 -2
  41. data/plugins/services/services_renderer.rb +3 -3
  42. data/plugins/unmanaged_files/unmanaged_files_model.rb +2 -1
  43. data/plugins/unmanaged_files/unmanaged_files_renderer.rb +5 -5
  44. data/plugins/users/users_renderer.rb +3 -3
  45. metadata +36 -4
data/lib/machinery.rb CHANGED
@@ -33,6 +33,7 @@ require "kramdown"
33
33
  require "find"
34
34
  require "pathname"
35
35
  require "nokogiri"
36
+ require "socket"
36
37
 
37
38
  require_relative "machinery_logger"
38
39
  require_relative "zypper"
@@ -75,7 +76,6 @@ require_relative "validate_task"
75
76
  require_relative "migration"
76
77
  require_relative "upgrade_format_task"
77
78
  require_relative "html"
78
- require_relative "generate_html_task"
79
79
  require_relative "hint"
80
80
  require_relative "mountpoints"
81
81
  require_relative "config_base"
@@ -92,7 +92,11 @@ require_relative "filter"
92
92
  require_relative "filter_option_parser"
93
93
  require_relative "file_scope"
94
94
  require_relative "system_file"
95
- require_relative "scope_file_access"
95
+ require_relative "scope_file_access_flat"
96
+ require_relative "scope_file_access_archive"
97
+ require_relative "man_task"
98
+ require_relative "comparison"
99
+ require_relative "serve_html_task"
96
100
 
97
101
  Dir[File.join(Machinery::ROOT, "plugins", "**", "*.rb")].each { |f| require(f) }
98
102
 
@@ -15,11 +15,13 @@
15
15
  # To contact SUSE about this file by physical or electronic mail,
16
16
  # you may find current contact information at www.suse.com
17
17
 
18
- class GenerateHtmlTask
19
- def generate(description)
20
- output = Html.generate(description)
21
- Machinery::Ui.puts "The generated HTML file is stored in: \n" +
22
- "#{output}"
23
- Hint.print(:share_html_contents, directory: output)
18
+ class ManTask
19
+ def man
20
+ LocalSystem.validate_existence_of_package("man")
21
+ system("man", man_path)
22
+ end
23
+
24
+ def man_path
25
+ File.join(Machinery::ROOT, "man/generated/machinery.1.gz")
24
26
  end
25
27
  end
data/lib/object.rb CHANGED
@@ -152,7 +152,7 @@ module Machinery
152
152
  end
153
153
 
154
154
  def compare_with(other)
155
- self == other ? [nil, nil, self] : [self, other, nil]
155
+ self == other ? [nil, nil, nil, self] : [self, other, nil, nil]
156
156
  end
157
157
  end
158
158
  end
data/lib/renderer.rb CHANGED
@@ -18,7 +18,22 @@
18
18
  # Renderer is the base class for all text renderer plugins
19
19
  #
20
20
  # It defines methods for rendering data which will be called from the
21
- # specialized `do_render` methods in the subclasses.
21
+ # specialized content methods in the subclasses.
22
+ #
23
+ # Subclasses can define the output for the `show` and `compare` commands using
24
+ # the following `content` methods:
25
+ #
26
+ # For `machinery show`:
27
+ # [content] (required) Defines the output for a scope in `machinery show`
28
+ #
29
+ # For `machinery compare`:
30
+ # [compare_content_only_in] Defines the output of the "only in x" sections. The
31
+ # default behavior is to fall back to `content`.
32
+ # [compare_content_common] Defines the output of the "in both" section. The
33
+ # default behavior is to fall back to `content`.
34
+ # [compare_content_changed] Defines the output of the "in both with changed
35
+ # attributes" section for scopes where this is
36
+ # supported.
22
37
  #
23
38
  # The names of the subclasses are 1:1 mappings of the scope areas, e.g.
24
39
  # the PackagesRenderer class would be used for rendering when the user
@@ -34,11 +49,11 @@
34
49
  # Accordingly a simple renderer for the "packages" scope could look like this:
35
50
  #
36
51
  # class PackagesRenderer < Renderer
37
- # def do_render
38
- # return if @system_description.packages.empty?
52
+ # def content(description)
53
+ # return if description.packages.empty?
39
54
  #
40
55
  # list "Packages" do
41
- # @system_description.packages.each do |p|
56
+ # description.packages.each do |p|
42
57
  # item "#{p.name} (#{p.version})"
43
58
  # end
44
59
  # end
@@ -51,7 +66,8 @@ class Renderer
51
66
  attr_accessor :system_description
52
67
  attr_accessor :buffer
53
68
 
54
- abstract_method :do_render
69
+ abstract_method :content
70
+ abstract_method :compare_content_changed
55
71
 
56
72
  @@renderers = []
57
73
 
@@ -73,8 +89,15 @@ class Renderer
73
89
  end
74
90
  end
75
91
 
92
+ def scope
93
+ # Return the un-camelcased name of the inspector,
94
+ # e.g. "foo_bar" for "FooBarInspector"
95
+ scope = self.class.name.match(/^(.*)Renderer$/)[1]
96
+ scope.gsub(/([^A-Z])([A-Z])/, "\\1_\\2").downcase
97
+ end
98
+
99
+ # Renders one system description using the specialized `content` method
76
100
  def render(system_description, options = {})
77
- @system_description = system_description
78
101
  @options = options
79
102
  @buffer = ""
80
103
  @indent = 2
@@ -94,54 +117,65 @@ class Renderer
94
117
  heading(header)
95
118
  end
96
119
 
97
- do_render
120
+ content(system_description)
98
121
 
99
122
  @buffer += "\n" unless @buffer.empty? || @buffer.end_with?("\n\n")
100
123
 
101
124
  @buffer
102
125
  end
103
126
 
104
- def render_comparison_section(description)
105
- @system_description = description
106
- indent { do_render }
107
- @buffer += "\n" unless @buffer.empty? || @buffer.end_with?("\n\n")
108
- end
109
-
110
- def render_comparison_only_in(description, scope)
111
- if description[scope]
112
- puts "Only in '#{description.name}':"
113
- render_comparison_section(description)
114
- end
115
- end
116
-
117
- def render_comparison_common(description, scope)
118
- if description[scope]
119
- puts "Common to both systems:"
120
- render_comparison_section(description)
121
- end
122
- end
123
-
124
- def render_comparison(description1, description2, description_common, options = {})
127
+ # Renders the result of a comparison of two system descriptions.
128
+ def render_comparison(comparison, options = {})
125
129
  @options = options
126
130
  @buffer = ""
127
131
  @indent = 0
128
132
  @stack = []
129
133
 
130
134
  show_heading = if options[:show_all]
131
- description1[scope] || description2[scope] || description_common[scope]
135
+ comparison.only_in1 || comparison.only_in2 || comparison.changed || comparison.common
132
136
  else
133
- description1[scope] || description2[scope]
137
+ comparison.only_in1 || comparison.only_in2 || comparison.changed
134
138
  end
135
139
 
136
140
  heading(display_name) if show_heading
137
141
 
138
- render_comparison_only_in(description1, scope)
139
- render_comparison_only_in(description2, scope)
140
- render_comparison_common(description_common, scope) if options[:show_all]
141
-
142
+ render_comparison_only_in(comparison.as_description(:one))
143
+ render_comparison_only_in(comparison.as_description(:two))
144
+ render_comparison_changed(comparison) if comparison.changed
145
+ render_comparison_common(comparison.as_description(:common)) if @options[:show_all]
142
146
  @buffer
143
147
  end
144
148
 
149
+ def render_comparison_only_in(description)
150
+ return if !description[scope]
151
+
152
+ puts "Only in '#{description.name}':"
153
+ indent { compare_content_only_in(description) }
154
+ @buffer += "\n" unless @buffer.empty? || @buffer.end_with?("\n\n")
155
+ end
156
+
157
+ def render_comparison_changed(comparison)
158
+ puts "In both with different attributes ('#{comparison.name1}' <> '#{comparison.name2}'):"
159
+ indent { compare_content_changed(comparison.changed) }
160
+ @buffer += "\n" unless @buffer.empty? || @buffer.end_with?("\n\n")
161
+ end
162
+
163
+ def render_comparison_common(description)
164
+ return if !description[scope]
165
+
166
+ puts "Common to both systems:"
167
+ indent { compare_content_common(description) }
168
+ @buffer += "\n" unless @buffer.empty? || @buffer.end_with?("\n\n")
169
+ end
170
+
171
+ def compare_content_only_in(description)
172
+ content(description)
173
+ end
174
+
175
+ def compare_content_common(description)
176
+ content(description)
177
+ end
178
+
145
179
  def render_comparison_missing_scope(description1, description2)
146
180
  @buffer = ""
147
181
  @indent = 0
@@ -164,6 +198,8 @@ class Renderer
164
198
  @buffer
165
199
  end
166
200
 
201
+ protected
202
+
167
203
  def heading(s)
168
204
  @buffer += "# #{s}\n\n"
169
205
  end
@@ -214,13 +250,6 @@ class Renderer
214
250
  end
215
251
  end
216
252
 
217
- def scope
218
- # Return the un-camelcased name of the inspector,
219
- # e.g. "foo_bar" for "FooBarInspector"
220
- scope = self.class.name.match(/^(.*)Renderer$/)[1]
221
- scope.gsub(/([^A-Z])([A-Z])/, "\\1_\\2").downcase
222
- end
223
-
224
253
  private
225
254
 
226
255
  def print_indented(s)
@@ -1,23 +1,3 @@
1
- module ScopeFileAccessFlat
2
- def retrieve_files_from_system(system, paths)
3
- system.retrieve_files(paths, scope_file_store.path)
4
- end
5
-
6
- def write_file(system_file, target)
7
- raise Machinery::Errors::FileUtilsError, "Not a file" if !system_file.file?
8
-
9
- target_path = File.join(target, system_file.name)
10
- FileUtils.mkdir_p(File.dirname(target_path))
11
- FileUtils.cp(file_path(system_file), target_path)
12
- end
13
-
14
- def file_path(system_file)
15
- raise Machinery::Errors::FileUtilsError, "Not a file" if !system_file.file?
16
-
17
- File.join(scope_file_store.path, system_file.name)
18
- end
19
- end
20
-
21
1
  module ScopeFileAccessArchive
22
2
  def retrieve_files_from_system_as_archive(system, files, excluded_files)
23
3
  archive_path = File.join(scope_file_store.path, "files.tgz")
@@ -64,4 +44,27 @@ module ScopeFileAccessArchive
64
44
  File.join(system_file.scope.scope_file_store.path, "files.tgz")
65
45
  end
66
46
  end
47
+
48
+ def file_content(system_file)
49
+ if !extracted
50
+ raise Machinery::Errors::FileUtilsError, "The requested file '#{system_file.name}' is not" \
51
+ " available because files for scope '#{scope_name}' were not extracted."
52
+ end
53
+
54
+ tarball_path = File.join(scope_file_store.path, "files.tgz")
55
+ begin
56
+ Cheetah.run("tar", "xfO", tarball_path, system_file.name.gsub(/^\//, ""), stdout: :capture)
57
+ rescue
58
+ raise Machinery::Errors::FileUtilsError,
59
+ "The requested file '#{system_file.name}' was not found."
60
+ end
61
+ end
62
+
63
+ def binary?(system_file)
64
+ content = file_content(system_file)
65
+ return false if content.empty?
66
+
67
+ output = Cheetah.run("file", "-", stdin: content, stdout: :capture)
68
+ !output.include?("ASCII")
69
+ end
67
70
  end
@@ -0,0 +1,36 @@
1
+ module ScopeFileAccessFlat
2
+ def retrieve_files_from_system(system, paths)
3
+ system.retrieve_files(paths, scope_file_store.path)
4
+ end
5
+
6
+ def write_file(system_file, target)
7
+ raise Machinery::Errors::FileUtilsError, "Not a file" if !system_file.file?
8
+
9
+ target_path = File.join(target, system_file.name)
10
+ FileUtils.mkdir_p(File.dirname(target_path))
11
+ FileUtils.cp(file_path(system_file), target_path)
12
+ end
13
+
14
+ def file_path(system_file)
15
+ raise Machinery::Errors::FileUtilsError, "Not a file" if !system_file.file?
16
+
17
+ File.join(scope_file_store.path, system_file.name)
18
+ end
19
+
20
+ def file_content(system_file)
21
+ if !extracted
22
+ raise Machinery::Errors::FileUtilsError, "The requested file '#{system_file.name}' is" \
23
+ " not available because files for scope '#{scope_name}' were not extracted."
24
+ end
25
+
26
+ File.read(file_path(system_file))
27
+ end
28
+
29
+ def binary?(system_file)
30
+ path = system_file.scope.file_path(system_file)
31
+ return false if File.zero?(path)
32
+
33
+ output = Cheetah.run("file", path, stdout: :capture)
34
+ !output.include?("ASCII")
35
+ end
36
+ end
@@ -0,0 +1,33 @@
1
+ # Copyright (c) 2013-2015 SUSE LLC
2
+ #
3
+ # This program is free software; you can redistribute it and/or
4
+ # modify it under the terms of version 3 of the GNU General Public License as
5
+ # published by the Free Software Foundation.
6
+ #
7
+ # This program is distributed in the hope that it will be useful,
8
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
9
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
+ # GNU General Public License for more details.
11
+ #
12
+ # You should have received a copy of the GNU General Public License
13
+ # along with this program; if not, contact SUSE LLC.
14
+ #
15
+ # To contact SUSE about this file by physical or electronic mail,
16
+ # you may find current contact information at www.suse.com
17
+
18
+ class ServeHtmlTask
19
+ def serve(description, ip, port)
20
+ url = "http://#{ip}:#{port}/#{CGI.escape(description.name)}"
21
+
22
+ Machinery::Ui.use_pager = false
23
+ Machinery::Ui.puts <<EOF
24
+ The description is now available at #{url}
25
+
26
+ The web server can be closed with Ctrl+C.
27
+ EOF
28
+
29
+ server = Html.run_server(description.store, port: port, ip: ip)
30
+
31
+ server.join
32
+ end
33
+ end
data/lib/show_task.rb CHANGED
@@ -19,7 +19,7 @@ class ShowTask
19
19
  def show(description, scopes, filter, options = {})
20
20
  scopes = Inspector.sort_scopes(scopes)
21
21
  if options[:show_html]
22
- show_html(description)
22
+ show_html(description, options)
23
23
  else
24
24
  filter.apply!(description)
25
25
  show_console(description, scopes, options )
@@ -28,12 +28,26 @@ class ShowTask
28
28
 
29
29
  private
30
30
 
31
- def show_html(description)
31
+ def show_html(description, options)
32
32
  begin
33
33
  LocalSystem.validate_existence_of_package("xdg-utils")
34
- Html.generate(description)
35
- html_path = SystemDescriptionStore.new.html_path(description.name)
36
- LoggedCheetah.run("xdg-open", html_path)
34
+
35
+ url = "http://#{options[:ip]}:#{options[:port]}/#{CGI.escape(description.name)}"
36
+
37
+ Machinery::Ui.use_pager = false
38
+ Machinery::Ui.puts <<EOF
39
+ There is a web server running, serving the description on #{url}.
40
+
41
+ The server can be closed with Ctrl+C.
42
+ EOF
43
+
44
+ server = Html.run_server(description.store, port: options[:port], ip: options[:ip])
45
+
46
+ Html.when_server_ready(options[:ip], options[:port]) do
47
+ LoggedCheetah.run("xdg-open", url)
48
+ end
49
+
50
+ server.join # Wait until the user cancelled the blocking webserver
37
51
  rescue Cheetah::ExecutionFailed => e
38
52
  raise Machinery::Errors::OpenInBrowserFailed.new(
39
53
  "Could not open system description '#{description.name}' in the web browser.\n" \
data/lib/system_file.rb CHANGED
@@ -40,5 +40,30 @@ module Machinery
40
40
  false
41
41
  end
42
42
  end
43
+
44
+ def on_disk?
45
+ assert_scope
46
+
47
+ scope.extracted && file? && !deleted?
48
+ end
49
+
50
+ def binary?
51
+ assert_scope
52
+ scope.binary?(self)
53
+ end
54
+
55
+ def content
56
+ assert_scope
57
+ scope.file_content(self)
58
+ end
59
+
60
+ private
61
+
62
+ def assert_scope
63
+ return if scope
64
+
65
+ raise Machinery::Errors::MachineryError,
66
+ "File store related method unavailable, the SystemFile does not have a Scope associated."
67
+ end
43
68
  end
44
69
  end
data/lib/version.rb CHANGED
@@ -17,6 +17,6 @@
17
17
 
18
18
  module Machinery
19
19
 
20
- VERSION = "1.9.1"
20
+ VERSION = "1.10.0"
21
21
 
22
22
  end
Binary file