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.
- checksums.yaml +4 -4
- data/NEWS +7 -0
- data/html/assets/compare/machinery-compare.js +43 -0
- data/html/assets/compare/machinery.js +17 -0
- data/html/assets/machinery.css +13 -0
- data/html/assets/modal.js +280 -0
- data/html/assets/show/machinery-show.js +16 -9
- data/html/assets/show/machinery.js +61 -25
- data/html/comparison.html.haml +194 -116
- data/html/index.html.haml +46 -12
- data/lib/array.rb +2 -1
- data/lib/cli.rb +44 -23
- data/lib/compare_task.rb +5 -29
- data/lib/comparison.rb +69 -0
- data/lib/config.rb +4 -0
- data/lib/export_task.rb +7 -0
- data/lib/file_scope.rb +2 -2
- data/lib/html.rb +165 -88
- data/lib/machinery.rb +6 -2
- data/lib/{generate_html_task.rb → man_task.rb} +8 -6
- data/lib/object.rb +1 -1
- data/lib/renderer.rb +70 -41
- data/lib/{scope_file_access.rb → scope_file_access_archive.rb} +23 -20
- data/lib/scope_file_access_flat.rb +36 -0
- data/lib/serve_html_task.rb +33 -0
- data/lib/show_task.rb +19 -5
- data/lib/system_file.rb +25 -0
- data/lib/version.rb +1 -1
- data/man/generated/machinery.1.gz +0 -0
- data/man/generated/machinery.1.html +60 -34
- data/plugins/changed_managed_files/changed_managed_files_renderer.rb +5 -5
- data/plugins/config_files/config_files_renderer.rb +7 -6
- data/plugins/groups/groups_renderer.rb +3 -3
- data/plugins/os/os_renderer.rb +5 -5
- data/plugins/packages/packages_model.rb +25 -0
- data/plugins/packages/packages_renderer.rb +36 -5
- data/plugins/patterns/patterns_renderer.rb +3 -3
- data/plugins/repositories/repositories_model.rb +1 -1
- data/plugins/repositories/repositories_renderer.rb +4 -4
- data/plugins/services/services_model.rb +2 -2
- data/plugins/services/services_renderer.rb +3 -3
- data/plugins/unmanaged_files/unmanaged_files_model.rb +2 -1
- data/plugins/unmanaged_files/unmanaged_files_renderer.rb +5 -5
- data/plugins/users/users_renderer.rb +3 -3
- 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 "
|
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
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
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
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
|
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
|
38
|
-
# return if
|
52
|
+
# def content(description)
|
53
|
+
# return if description.packages.empty?
|
39
54
|
#
|
40
55
|
# list "Packages" do
|
41
|
-
#
|
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 :
|
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
|
-
|
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
|
-
|
105
|
-
|
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
|
-
|
135
|
+
comparison.only_in1 || comparison.only_in2 || comparison.changed || comparison.common
|
132
136
|
else
|
133
|
-
|
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(
|
139
|
-
render_comparison_only_in(
|
140
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
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
Binary file
|