nanoc 3.4.3 → 3.5.0b1
Sign up to get free protection for your applications and to get access to all the features.
- data/CONTRIBUTING.md +25 -0
- data/Gemfile +3 -1
- data/Gemfile.lock +22 -13
- data/NEWS.md +27 -0
- data/README.md +3 -1
- data/lib/nanoc.rb +2 -2
- data/lib/nanoc/base/compilation/compiler_dsl.rb +19 -0
- data/lib/nanoc/base/core_ext/array.rb +18 -8
- data/lib/nanoc/base/core_ext/hash.rb +18 -8
- data/lib/nanoc/base/core_ext/pathname.rb +2 -8
- data/lib/nanoc/base/plugin_registry.rb +57 -19
- data/lib/nanoc/base/result_data/item_rep.rb +3 -15
- data/lib/nanoc/base/source_data/item.rb +1 -1
- data/lib/nanoc/base/source_data/layout.rb +1 -1
- data/lib/nanoc/base/source_data/site.rb +15 -19
- data/lib/nanoc/cli.rb +28 -23
- data/lib/nanoc/cli/command_runner.rb +4 -0
- data/lib/nanoc/cli/commands/autocompile.rb +15 -6
- data/lib/nanoc/cli/commands/check.rb +47 -0
- data/lib/nanoc/cli/commands/compile.rb +271 -195
- data/lib/nanoc/cli/commands/create-site.rb +5 -5
- data/lib/nanoc/cli/commands/deploy.rb +16 -4
- data/lib/nanoc/cli/commands/prune.rb +3 -3
- data/lib/nanoc/cli/commands/show-data.rb +73 -58
- data/lib/nanoc/cli/commands/show-rules.rb +1 -1
- data/lib/nanoc/cli/commands/validate-css.rb +2 -3
- data/lib/nanoc/cli/commands/validate-html.rb +2 -3
- data/lib/nanoc/cli/commands/validate-links.rb +5 -11
- data/lib/nanoc/cli/commands/view.rb +1 -1
- data/lib/nanoc/cli/commands/watch.rb +38 -20
- data/lib/nanoc/cli/error_handler.rb +122 -122
- data/lib/nanoc/data_sources.rb +2 -0
- data/lib/nanoc/data_sources/filesystem_unified.rb +1 -1
- data/lib/nanoc/data_sources/filesystem_verbose.rb +1 -1
- data/lib/nanoc/data_sources/static.rb +60 -0
- data/lib/nanoc/extra.rb +2 -0
- data/lib/nanoc/extra/checking.rb +16 -0
- data/lib/nanoc/extra/checking/check.rb +33 -0
- data/lib/nanoc/extra/checking/checks.rb +19 -0
- data/lib/nanoc/extra/checking/checks/css.rb +23 -0
- data/lib/nanoc/extra/checking/checks/external_links.rb +149 -0
- data/lib/nanoc/extra/checking/checks/html.rb +24 -0
- data/lib/nanoc/extra/checking/checks/internal_links.rb +57 -0
- data/lib/nanoc/extra/checking/checks/stale.rb +23 -0
- data/lib/nanoc/extra/checking/dsl.rb +31 -0
- data/lib/nanoc/extra/checking/issue.rb +19 -0
- data/lib/nanoc/extra/checking/runner.rb +130 -0
- data/lib/nanoc/extra/link_collector.rb +57 -0
- data/lib/nanoc/extra/pruner.rb +1 -1
- data/lib/nanoc/extra/validators/links.rb +5 -262
- data/lib/nanoc/extra/validators/w3c.rb +8 -76
- data/lib/nanoc/filters/colorize_syntax.rb +1 -1
- data/lib/nanoc/filters/handlebars.rb +2 -2
- data/lib/nanoc/filters/less.rb +1 -1
- data/lib/nanoc/filters/redcarpet.rb +1 -1
- data/lib/nanoc/filters/rubypants.rb +1 -1
- data/lib/nanoc/filters/slim.rb +1 -1
- data/lib/nanoc/helpers/blogging.rb +163 -104
- data/lib/nanoc/helpers/xml_sitemap.rb +9 -3
- data/tasks/doc.rake +2 -1
- data/test/base/core_ext/array_spec.rb +4 -4
- data/test/base/core_ext/hash_spec.rb +7 -7
- data/test/base/core_ext/pathname_spec.rb +12 -2
- data/test/base/test_compiler_dsl.rb +24 -0
- data/test/base/test_item_rep.rb +58 -26
- data/test/cli/commands/test_watch.rb +0 -8
- data/test/data_sources/test_static.rb +66 -0
- data/test/extra/checking/checks/test_css.rb +40 -0
- data/test/extra/checking/checks/test_external_links.rb +76 -0
- data/test/extra/checking/checks/test_html.rb +40 -0
- data/test/extra/checking/checks/test_internal_links.rb +46 -0
- data/test/extra/checking/checks/test_stale.rb +49 -0
- data/test/extra/checking/test_check.rb +16 -0
- data/test/extra/checking/test_dsl.rb +20 -0
- data/test/extra/checking/test_runner.rb +15 -0
- data/test/extra/test_link_collector.rb +93 -0
- data/test/extra/validators/test_links.rb +0 -64
- data/test/extra/validators/test_w3c.rb +20 -26
- data/test/filters/test_colorize_syntax.rb +15 -0
- data/test/filters/test_less.rb +14 -0
- data/test/filters/test_pandoc.rb +5 -1
- data/test/helpers/test_blogging.rb +52 -8
- data/test/helpers/test_xml_sitemap.rb +68 -0
- data/test/test_gem.rb +1 -1
- metadata +31 -6
@@ -4,7 +4,7 @@ usage 'create-site [options] path'
|
|
4
4
|
aliases :create_site, :cs
|
5
5
|
summary 'create a site'
|
6
6
|
description <<-EOS
|
7
|
-
Create a new site at the given path. The site will use the filesystem_unified data source by default, but this can be changed using the
|
7
|
+
Create a new site at the given path. The site will use the `filesystem_unified` data source by default, but this can be changed using the `--datasource` commandline option.
|
8
8
|
EOS
|
9
9
|
|
10
10
|
required :d, :datasource, 'specify the data source for the new site'
|
@@ -208,9 +208,9 @@ a:hover {
|
|
208
208
|
|
209
209
|
#main p {
|
210
210
|
margin: 20px 0;
|
211
|
-
|
211
|
+
|
212
212
|
font-size: 15px;
|
213
|
-
|
213
|
+
|
214
214
|
line-height: 20px;
|
215
215
|
}
|
216
216
|
|
@@ -220,7 +220,7 @@ a:hover {
|
|
220
220
|
|
221
221
|
#main li {
|
222
222
|
font-size: 15px;
|
223
|
-
|
223
|
+
|
224
224
|
line-height: 20px;
|
225
225
|
}
|
226
226
|
|
@@ -273,7 +273,7 @@ EOS
|
|
273
273
|
<head>
|
274
274
|
<meta charset="utf-8">
|
275
275
|
<title>A Brand New nanoc Site - <%= @item[:title] %></title>
|
276
|
-
<link rel="stylesheet"
|
276
|
+
<link rel="stylesheet" href="/style.css">
|
277
277
|
|
278
278
|
<!-- you don't need to keep this, but it's cool for stats! -->
|
279
279
|
<meta name="generator" content="nanoc <%= Nanoc::VERSION %>">
|
@@ -9,15 +9,16 @@ deployer_names = deployers.keys.sort_by { |k| k.to_s }
|
|
9
9
|
usage 'deploy [options]'
|
10
10
|
summary 'deploy the compiled site'
|
11
11
|
description <<-EOS
|
12
|
-
Deploys the compiled site. The compiled site contents in the output directory will be uploaded to the destination, which is specified using the
|
12
|
+
Deploys the compiled site. The compiled site contents in the output directory will be uploaded to the destination, which is specified using the `--target` option.
|
13
13
|
|
14
14
|
Available deployers: #{deployer_names.join(', ')}
|
15
15
|
|
16
16
|
EOS
|
17
17
|
|
18
|
-
option :t, :target,
|
19
|
-
flag :
|
20
|
-
|
18
|
+
option :t, :target, 'specify the location to deploy to (default: `default`)', :argument => :required
|
19
|
+
flag :C, :'no-check', 'do not run the issue checks marked for deployment'
|
20
|
+
flag :L, :list, 'list available locations to deploy to'
|
21
|
+
option :n, :'dry-run', 'show what would be deployed'
|
21
22
|
|
22
23
|
module Nanoc::CLI::Commands
|
23
24
|
|
@@ -57,6 +58,17 @@ module Nanoc::CLI::Commands
|
|
57
58
|
raise Nanoc::Errors::GenericTrivial, "The specified deploy target has an unrecognised kind “#{name}” (expected one of #{names.join(', ')})."
|
58
59
|
end
|
59
60
|
|
61
|
+
# Check
|
62
|
+
unless options[:'no-check']
|
63
|
+
puts "Running issue checks…"
|
64
|
+
ok = Nanoc::Extra::Checking::Runner.new(site).run_for_deploy
|
65
|
+
if !ok
|
66
|
+
puts "Issues found, deploy aborted."
|
67
|
+
return
|
68
|
+
end
|
69
|
+
puts "No issues found. Deploying!"
|
70
|
+
end
|
71
|
+
|
60
72
|
# Run
|
61
73
|
deployer = deployer_class.new(
|
62
74
|
site.config[:output_dir],
|
@@ -5,10 +5,10 @@ summary 'remove files not managed by nanoc from the output directory'
|
|
5
5
|
description <<-EOS
|
6
6
|
Find all files in the output directory that do not correspond to an item
|
7
7
|
managed by nanoc and remove them. Since this is a hazardous operation, an
|
8
|
-
additional
|
8
|
+
additional `--yes` flag is needed as confirmation.
|
9
9
|
|
10
|
-
Also see the auto_prune site configuration option in config.
|
11
|
-
automatically prune after compilation.
|
10
|
+
Also see the `auto_prune` site configuration option in `config.yamlz, which
|
11
|
+
will automatically prune after compilation.
|
12
12
|
EOS
|
13
13
|
|
14
14
|
flag :y, :yes, 'confirm deletion'
|
@@ -13,26 +13,59 @@ module Nanoc::CLI::Commands
|
|
13
13
|
class ShowData < ::Nanoc::CLI::CommandRunner
|
14
14
|
|
15
15
|
def run
|
16
|
-
# Make sure we are in a nanoc site directory
|
17
|
-
print "Loading site data... "
|
18
16
|
self.require_site
|
19
|
-
puts "done"
|
20
|
-
puts
|
21
17
|
|
22
18
|
# Get data
|
23
|
-
items
|
24
|
-
|
25
|
-
layouts
|
19
|
+
items = self.site.items
|
20
|
+
item_reps = items.map { |i| i.reps }.flatten
|
21
|
+
layouts = self.site.layouts
|
26
22
|
|
27
23
|
# Get dependency tracker
|
28
24
|
compiler = self.site.compiler
|
29
25
|
compiler.load
|
30
26
|
dependency_tracker = compiler.dependency_tracker
|
31
27
|
|
32
|
-
# Print
|
33
|
-
|
34
|
-
|
28
|
+
# Print data
|
29
|
+
self.print_item_dependencies(items, dependency_tracker)
|
30
|
+
self.print_item_rep_paths(items)
|
31
|
+
self.print_item_rep_outdatedness(items, compiler)
|
32
|
+
self.print_layouts(layouts, compiler)
|
33
|
+
end
|
34
|
+
|
35
|
+
protected
|
36
|
+
|
37
|
+
def sorted_with_prev(objects)
|
38
|
+
prev = nil
|
39
|
+
objects.sort_by { |o| o.identifier }.each do |object|
|
40
|
+
yield(object, prev)
|
41
|
+
prev = object
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def sorted_reps_with_prev(items)
|
46
|
+
prev = nil
|
35
47
|
items.sort_by { |i| i.identifier }.each do |item|
|
48
|
+
item.reps.sort_by { |r| r.name.to_s }.each do |rep|
|
49
|
+
yield(rep, prev)
|
50
|
+
prev = rep
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def print_header(title)
|
56
|
+
header = '=' * 78
|
57
|
+
header[3..(title.length+5)] = " #{title} "
|
58
|
+
|
59
|
+
puts
|
60
|
+
puts header
|
61
|
+
puts
|
62
|
+
end
|
63
|
+
|
64
|
+
def print_item_dependencies(items, dependency_tracker)
|
65
|
+
self.print_header('Item dependencies')
|
66
|
+
|
67
|
+
self.sorted_with_prev(items) do |item, prev|
|
68
|
+
puts if prev
|
36
69
|
puts "item #{item.identifier} depends on:"
|
37
70
|
predecessors = dependency_tracker.objects_causing_outdatedness_of(item).sort_by { |i| i ? i.identifier : '' }
|
38
71
|
predecessors.each do |pred|
|
@@ -43,63 +76,45 @@ module Nanoc::CLI::Commands
|
|
43
76
|
end
|
44
77
|
end
|
45
78
|
puts " (nothing)" if predecessors.empty?
|
46
|
-
puts
|
47
79
|
end
|
80
|
+
end
|
48
81
|
|
49
|
-
|
50
|
-
|
51
|
-
puts
|
52
|
-
items.sort_by { |i| i.identifier }.each do |item|
|
53
|
-
item.reps.sort_by { |r| r.name.to_s }.each do |rep|
|
54
|
-
puts "item #{item.identifier}, rep #{rep.name}:"
|
55
|
-
if rep.raw_paths.empty?
|
56
|
-
puts " (not written)"
|
57
|
-
end
|
58
|
-
length = rep.raw_paths.keys.map { |s| s.to_s.length }.max
|
59
|
-
rep.raw_paths.each do |snapshot_name, raw_path|
|
60
|
-
puts " [ %-#{length}s ] %s" % [ snapshot_name, raw_path ]
|
61
|
-
end
|
62
|
-
end
|
63
|
-
puts
|
64
|
-
end
|
82
|
+
def print_item_rep_paths(items)
|
83
|
+
self.print_header('Item representation paths')
|
65
84
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
else
|
76
|
-
puts " is not outdated"
|
77
|
-
end
|
85
|
+
self.sorted_reps_with_prev(items) do |rep, prev|
|
86
|
+
puts if prev
|
87
|
+
puts "item #{rep.item.identifier}, rep #{rep.name}:"
|
88
|
+
if rep.raw_paths.empty?
|
89
|
+
puts " (not written)"
|
90
|
+
end
|
91
|
+
length = rep.raw_paths.keys.map { |s| s.to_s.length }.max
|
92
|
+
rep.raw_paths.each do |snapshot_name, raw_path|
|
93
|
+
puts " [ %-#{length}s ] %s" % [ snapshot_name, raw_path ]
|
78
94
|
end
|
79
|
-
puts
|
80
95
|
end
|
96
|
+
end
|
81
97
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
puts
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
end
|
98
|
+
def print_item_rep_outdatedness(items, compiler)
|
99
|
+
self.print_header('Item representation outdatedness')
|
100
|
+
|
101
|
+
self.sorted_reps_with_prev(items) do |rep, prev|
|
102
|
+
puts if prev
|
103
|
+
puts "item #{rep.item.identifier}, rep #{rep.name}:"
|
104
|
+
outdatedness_reason = compiler.outdatedness_checker.outdatedness_reason_for(rep)
|
105
|
+
if outdatedness_reason
|
106
|
+
puts " is outdated: #{outdatedness_reason.message}"
|
107
|
+
else
|
108
|
+
puts " is not outdated"
|
94
109
|
end
|
95
|
-
puts " (nothing)" if predecessors.empty?
|
96
|
-
puts
|
97
110
|
end
|
111
|
+
end
|
98
112
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
layouts
|
113
|
+
def print_layouts(layouts, compiler)
|
114
|
+
self.print_header('Layouts')
|
115
|
+
|
116
|
+
self.sorted_with_prev(layouts) do |layout, prev|
|
117
|
+
puts if prev
|
103
118
|
puts "layout #{layout.identifier}:"
|
104
119
|
outdatedness_reason = compiler.outdatedness_checker.outdatedness_reason_for(layout)
|
105
120
|
if outdatedness_reason
|
@@ -4,7 +4,7 @@ usage 'show-rules [thing]'
|
|
4
4
|
aliases :explain
|
5
5
|
summary 'describe the rules for each item'
|
6
6
|
description <<-EOS
|
7
|
-
Prints the rules used for all items and layouts in the current site.
|
7
|
+
Prints the rules used for all items and layouts in the current site.
|
8
8
|
EOS
|
9
9
|
|
10
10
|
module Nanoc::CLI::Commands
|
@@ -3,6 +3,7 @@
|
|
3
3
|
usage 'validate-css [options]'
|
4
4
|
aliases :validate_css, :vcss
|
5
5
|
summary 'validate the site’s CSS'
|
6
|
+
be_hidden
|
6
7
|
description <<-EOS
|
7
8
|
Validates the site’s CSS files.
|
8
9
|
EOS
|
@@ -12,9 +13,7 @@ module Nanoc::CLI::Commands
|
|
12
13
|
class ValidateCSS < ::Nanoc::CLI::CommandRunner
|
13
14
|
|
14
15
|
def run
|
15
|
-
|
16
|
-
validator = ::Nanoc::Extra::Validators::W3C.new(site.config[:output_dir], [ :css ])
|
17
|
-
validator.run
|
16
|
+
Nanoc::CLI.run %w( check css )
|
18
17
|
end
|
19
18
|
|
20
19
|
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
usage 'validate-html [options]'
|
4
4
|
aliases :validate_html, :vhtml
|
5
5
|
summary 'validate the site’s HTML'
|
6
|
+
be_hidden
|
6
7
|
description <<-EOS
|
7
8
|
Validates the site’s HTML files.
|
8
9
|
EOS
|
@@ -12,9 +13,7 @@ module Nanoc::CLI::Commands
|
|
12
13
|
class ValidateHTML < ::Nanoc::CLI::CommandRunner
|
13
14
|
|
14
15
|
def run
|
15
|
-
|
16
|
-
validator = ::Nanoc::Extra::Validators::W3C.new(site.config[:output_dir], [ :html ])
|
17
|
-
validator.run
|
16
|
+
Nanoc::CLI.run %w( check html )
|
18
17
|
end
|
19
18
|
|
20
19
|
end
|
@@ -3,6 +3,7 @@
|
|
3
3
|
usage 'validate-links [options]'
|
4
4
|
aliases :validate_links, :vlink
|
5
5
|
summary 'validate links in site'
|
6
|
+
be_hidden
|
6
7
|
description <<-EOS
|
7
8
|
Validates the site’s links. By default, both internal and external links will be checked.
|
8
9
|
EOS
|
@@ -15,17 +16,10 @@ module Nanoc::CLI::Commands
|
|
15
16
|
class ValidateLinks < ::Nanoc::CLI::CommandRunner
|
16
17
|
|
17
18
|
def run
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
validator = ::Nanoc::Extra::Validators::Links.new(
|
24
|
-
dir,
|
25
|
-
index_filenames,
|
26
|
-
:internal => (options[:external] ? false : true),
|
27
|
-
:external => (options[:internal] ? false : true))
|
28
|
-
validator.run
|
19
|
+
checks = []
|
20
|
+
checks << 'ilinks' if options[:internal]
|
21
|
+
checks << 'elinks' if options[:external]
|
22
|
+
Nanoc::CLI.run [ 'check', checks ].flatten
|
29
23
|
end
|
30
24
|
|
31
25
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
usage 'view [options]'
|
4
4
|
summary 'start the web server that serves static files'
|
5
5
|
description <<-EOS
|
6
|
-
Start the static web server. Unless specified, the web server will run on port 3000 and listen on all IP addresses. Running this static web server requires
|
6
|
+
Start the static web server. Unless specified, the web server will run on port 3000 and listen on all IP addresses. Running this static web server requires `adsf` (not `asdf`!).
|
7
7
|
EOS
|
8
8
|
|
9
9
|
required :H, :handler, 'specify the handler to use (webrick/mongrel/...)'
|
@@ -11,7 +11,7 @@ module Nanoc::CLI::Commands
|
|
11
11
|
class Watch < ::Nanoc::CLI::CommandRunner
|
12
12
|
|
13
13
|
def run
|
14
|
-
require '
|
14
|
+
require 'listen'
|
15
15
|
require 'pathname'
|
16
16
|
|
17
17
|
require_site
|
@@ -20,12 +20,12 @@ module Nanoc::CLI::Commands
|
|
20
20
|
@notifier = Notifier.new
|
21
21
|
|
22
22
|
# Define rebuilder
|
23
|
-
rebuilder = lambda do |
|
23
|
+
rebuilder = lambda do |file_path|
|
24
24
|
# Determine filename
|
25
|
-
if
|
25
|
+
if file_path.nil?
|
26
26
|
filename = nil
|
27
27
|
else
|
28
|
-
filename = ::Pathname.new(
|
28
|
+
filename = ::Pathname.new(file_path).relative_path_from(::Pathname.new(Dir.getwd)).to_s
|
29
29
|
end
|
30
30
|
|
31
31
|
# Notify
|
@@ -63,24 +63,33 @@ module Nanoc::CLI::Commands
|
|
63
63
|
end
|
64
64
|
|
65
65
|
# Rebuild once
|
66
|
-
rebuilder.call(nil
|
66
|
+
rebuilder.call(nil)
|
67
67
|
|
68
68
|
# Get directories to watch
|
69
|
-
dirs_to_watch = watcher_config[:dirs_to_watch] ||
|
70
|
-
files_to_watch = watcher_config[:files_to_watch] ||
|
71
|
-
files_to_watch.
|
69
|
+
dirs_to_watch = watcher_config[:dirs_to_watch] || ['content', 'layouts', 'lib']
|
70
|
+
files_to_watch = watcher_config[:files_to_watch] || ['config.yaml', 'Rules', 'rules', 'Rules.rb', 'rules.rb']
|
71
|
+
files_to_watch = Regexp.new(files_to_watch.map { |name| "#{Regexp.quote(name)}$"}.join("|"))
|
72
|
+
ignore_dir = Regexp.new(Dir.glob("*").map{|dir| dir if File::ftype(dir) == "directory" }.compact.join("|"))
|
72
73
|
|
73
74
|
# Watch
|
74
75
|
puts "Watching for changes…"
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
76
|
+
|
77
|
+
callback = Proc.new do |modified, added, removed|
|
78
|
+
rebuilder.call(modified[0]) if modified[0]
|
79
|
+
rebuilder.call(added[0]) if added[0]
|
80
|
+
rebuilder.call(removed[0]) if removed[0]
|
81
|
+
end
|
82
|
+
|
83
|
+
listener = Listen::MultiListener.new(*dirs_to_watch).change(&callback)
|
84
|
+
listener_root = Listen::MultiListener.new('', :filter => files_to_watch, :ignore => ignore_dir).change(&callback)
|
85
|
+
|
86
|
+
begin
|
87
|
+
listener_root.start(false)
|
88
|
+
listener.start
|
89
|
+
rescue Interrupt
|
90
|
+
listener.stop
|
91
|
+
listener_root.stop
|
92
|
+
end
|
84
93
|
end
|
85
94
|
|
86
95
|
# Allows sending user notifications in a cross-platform way.
|
@@ -105,12 +114,21 @@ module Nanoc::CLI::Commands
|
|
105
114
|
|
106
115
|
def tool
|
107
116
|
@tool ||= begin
|
108
|
-
|
109
|
-
|
110
|
-
|
117
|
+
require 'terminal-notifier'
|
118
|
+
'terminal-notify'
|
119
|
+
rescue LoadError
|
120
|
+
begin
|
121
|
+
TOOLS.find { |t| !`#{FIND_BINARY_COMMAND} #{t}`.empty? }
|
122
|
+
rescue Errno::ENOENT
|
123
|
+
nil
|
124
|
+
end
|
111
125
|
end
|
112
126
|
end
|
113
127
|
|
128
|
+
def terminal_notify(message)
|
129
|
+
TerminalNotifier.notify(message, :title => "nanoc")
|
130
|
+
end
|
131
|
+
|
114
132
|
def growlnotify(message)
|
115
133
|
system('growlnotify', '-m', message)
|
116
134
|
end
|
@@ -58,6 +58,10 @@ module Nanoc::CLI
|
|
58
58
|
exit!(0)
|
59
59
|
end
|
60
60
|
end
|
61
|
+
Signal.trap('USR1') do
|
62
|
+
puts "Caught USR1; dumping a stack trace"
|
63
|
+
puts caller.map { |i| " #{i}" }.join("\n")
|
64
|
+
end
|
61
65
|
|
62
66
|
# Run
|
63
67
|
yield
|
@@ -89,7 +93,12 @@ module Nanoc::CLI
|
|
89
93
|
# @return [void]
|
90
94
|
def print_error(error)
|
91
95
|
write_compact_error(error, $stderr)
|
92
|
-
|
96
|
+
|
97
|
+
File.open('crash.log', 'w') do |io|
|
98
|
+
cio = Nanoc::CLI.wrap_in_cleaning_stream(io)
|
99
|
+
cio.add_stream_cleaner(::Nanoc::CLI::StreamCleaners::ANSIColors)
|
100
|
+
write_verbose_error(error, cio)
|
101
|
+
end
|
93
102
|
end
|
94
103
|
|
95
104
|
# Writes a compact representation of the error, suitable for a terminal, on
|
@@ -107,48 +116,13 @@ module Nanoc::CLI
|
|
107
116
|
stream.puts
|
108
117
|
stream.puts "Captain! We’ve been hit!"
|
109
118
|
|
110
|
-
#
|
111
|
-
stream
|
112
|
-
stream
|
113
|
-
stream
|
114
|
-
stream.puts "#{error.class}: #{error.message}"
|
115
|
-
resolution = self.resolution_for(error)
|
116
|
-
stream.puts "#{resolution}" if resolution
|
117
|
-
|
118
|
-
# Compilation stack
|
119
|
-
stream.puts
|
120
|
-
stream.puts format_title('Compilation stack:')
|
121
|
-
stream.puts
|
122
|
-
if self.stack.empty?
|
123
|
-
stream.puts " (empty)"
|
124
|
-
else
|
125
|
-
self.stack.reverse.each do |obj|
|
126
|
-
if obj.is_a?(Nanoc::ItemRep)
|
127
|
-
stream.puts " - [item] #{obj.item.identifier} (rep #{obj.name})"
|
128
|
-
else # layout
|
129
|
-
stream.puts " - [layout] #{obj.identifier}"
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
# Backtrace
|
135
|
-
stream.puts
|
136
|
-
stream.puts format_title('Stack trace:')
|
137
|
-
stream.puts
|
138
|
-
count = 10
|
139
|
-
error.backtrace[0...count].each_with_index do |item, index|
|
140
|
-
stream.puts " #{index}. #{item}"
|
141
|
-
end
|
142
|
-
if error.backtrace.size > count
|
143
|
-
puts " ... #{error.backtrace.size - count} more lines omitted. See full crash log for details."
|
144
|
-
end
|
119
|
+
# Sections
|
120
|
+
self.write_error_message( stream, error)
|
121
|
+
self.write_compilation_stack(stream, error)
|
122
|
+
self.write_stack_trace( stream, error)
|
145
123
|
|
146
124
|
# Issue link
|
147
|
-
stream
|
148
|
-
stream.puts "If you believe this is a bug in nanoc, please do report it at"
|
149
|
-
stream.puts "-> https://github.com/ddfreyne/nanoc/issues/new <-"
|
150
|
-
stream.puts
|
151
|
-
stream.puts "A detailed crash log has been written to ./crash.log."
|
125
|
+
self.write_issue_link(stream)
|
152
126
|
end
|
153
127
|
|
154
128
|
# Writes a verbose representation of the error on the given stream.
|
@@ -161,86 +135,19 @@ module Nanoc::CLI
|
|
161
135
|
#
|
162
136
|
# @return [void]
|
163
137
|
def write_verbose_error(error, stream)
|
164
|
-
#
|
138
|
+
# Header
|
165
139
|
stream.puts "Crashlog created at #{Time.now}"
|
166
|
-
stream.puts
|
167
|
-
|
168
|
-
# Exception and resolution (if any)
|
169
|
-
stream.puts '=== MESSAGE:'
|
170
|
-
stream.puts
|
171
|
-
stream.puts "#{error.class}: #{error.message}"
|
172
|
-
resolution = self.resolution_for(error)
|
173
|
-
stream.puts "#{resolution}" if resolution
|
174
|
-
stream.puts
|
175
|
-
|
176
|
-
# Compilation stack
|
177
|
-
stream.puts '=== COMPILATION STACK:'
|
178
|
-
stream.puts
|
179
|
-
if self.stack.empty?
|
180
|
-
stream.puts " (empty)"
|
181
|
-
else
|
182
|
-
self.stack.reverse.each do |obj|
|
183
|
-
if obj.is_a?(Nanoc::ItemRep)
|
184
|
-
stream.puts " - [item] #{obj.item.identifier} (rep #{obj.name})"
|
185
|
-
else # layout
|
186
|
-
stream.puts " - [layout] #{obj.identifier}"
|
187
|
-
end
|
188
|
-
end
|
189
|
-
end
|
190
|
-
stream.puts
|
191
|
-
|
192
|
-
# Backtrace
|
193
|
-
stream.puts '=== BACKTRACE:'
|
194
|
-
stream.puts
|
195
|
-
stream.puts error.backtrace.to_enum(:each_with_index).map { |item, index| " #{index}. #{item}" }.join("\n")
|
196
|
-
stream.puts
|
197
140
|
|
198
|
-
#
|
199
|
-
stream
|
200
|
-
stream
|
201
|
-
stream
|
202
|
-
stream
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
stream.puts
|
209
|
-
stream.puts uname
|
210
|
-
stream.puts
|
211
|
-
rescue Errno::ENOENT
|
212
|
-
end
|
213
|
-
|
214
|
-
# Installed gems
|
215
|
-
stream.puts '=== INSTALLED GEMS:'
|
216
|
-
stream.puts
|
217
|
-
self.gems_and_versions.each do |g|
|
218
|
-
stream.puts " #{g.first} #{g.last.join(', ')}"
|
219
|
-
end
|
220
|
-
stream.puts
|
221
|
-
|
222
|
-
# Environment
|
223
|
-
stream.puts '=== ENVIRONMENT:'
|
224
|
-
stream.puts
|
225
|
-
ENV.sort.each do |e|
|
226
|
-
stream.puts "#{e.first} => #{e.last.inspect}"
|
227
|
-
end
|
228
|
-
stream.puts
|
229
|
-
|
230
|
-
# Gemfile
|
231
|
-
if File.exist?('Gemfile.lock')
|
232
|
-
stream.puts '=== GEMFILE.LOCK:'
|
233
|
-
stream.puts
|
234
|
-
stream.puts File.read('Gemfile.lock')
|
235
|
-
stream.puts
|
236
|
-
end
|
237
|
-
|
238
|
-
# Load paths
|
239
|
-
stream.puts '=== $LOAD_PATH:'
|
240
|
-
stream.puts
|
241
|
-
$LOAD_PATH.each_with_index do |i, index|
|
242
|
-
stream.puts " #{index}. #{i}"
|
243
|
-
end
|
141
|
+
# Sections
|
142
|
+
self.write_error_message( stream, error, :verbose => true)
|
143
|
+
self.write_compilation_stack( stream, error, :verbose => true)
|
144
|
+
self.write_stack_trace( stream, error, :verbose => true)
|
145
|
+
self.write_version_information(stream, :verbose => true)
|
146
|
+
self.write_system_information( stream, :verbose => true)
|
147
|
+
self.write_installed_gems( stream, :verbose => true)
|
148
|
+
self.write_environment( stream, :verbose => true)
|
149
|
+
self.write_gemfile_lock( stream, :verbose => true)
|
150
|
+
self.write_load_paths( stream, :verbose => true)
|
244
151
|
end
|
245
152
|
|
246
153
|
protected
|
@@ -267,6 +174,7 @@ module Nanoc::CLI
|
|
267
174
|
(compiler && compiler.stack) || []
|
268
175
|
end
|
269
176
|
|
177
|
+
# @return [Hash<String, Array>] A hash containing the gem names as keys and gem versions as value
|
270
178
|
def gems_and_versions
|
271
179
|
gems = {}
|
272
180
|
Gem::Specification.find_all.sort_by { |s| [ s.name, s.version ] }.each do |spec|
|
@@ -287,12 +195,12 @@ module Nanoc::CLI
|
|
287
195
|
'erubis' => 'erubis',
|
288
196
|
'escape' => 'escape',
|
289
197
|
'fog' => 'fog',
|
290
|
-
'fssm' => 'fssm',
|
291
198
|
'haml' => 'haml',
|
292
199
|
'handlebars' => 'hbs',
|
293
200
|
'json' => 'json',
|
294
201
|
'kramdown' => 'kramdown',
|
295
202
|
'less' => 'less',
|
203
|
+
'listen' => 'listen',
|
296
204
|
'markaby' => 'markaby',
|
297
205
|
'maruku' => 'maruku',
|
298
206
|
'mime/types' => 'mime-types',
|
@@ -338,8 +246,100 @@ module Nanoc::CLI
|
|
338
246
|
end
|
339
247
|
end
|
340
248
|
|
341
|
-
def
|
342
|
-
|
249
|
+
def write_section_header(stream, title, params={})
|
250
|
+
stream.puts
|
251
|
+
if params[:verbose]
|
252
|
+
stream.puts '===== ' + title.upcase + ':'
|
253
|
+
else
|
254
|
+
stream.puts "\e[1m\e[31m" + title + ':' + "\e[0m"
|
255
|
+
end
|
256
|
+
stream.puts
|
257
|
+
end
|
258
|
+
|
259
|
+
def write_error_message(stream, error, params={})
|
260
|
+
self.write_section_header(stream, 'Message', params)
|
261
|
+
|
262
|
+
stream.puts "#{error.class}: #{error.message}"
|
263
|
+
resolution = self.resolution_for(error)
|
264
|
+
stream.puts "#{resolution}" if resolution
|
265
|
+
end
|
266
|
+
|
267
|
+
def write_compilation_stack(stream, error, params={})
|
268
|
+
self.write_section_header(stream, 'Compilation stack', params)
|
269
|
+
|
270
|
+
if self.stack.empty?
|
271
|
+
stream.puts " (empty)"
|
272
|
+
else
|
273
|
+
self.stack.reverse.each do |obj|
|
274
|
+
if obj.is_a?(Nanoc::ItemRep)
|
275
|
+
stream.puts " - [item] #{obj.item.identifier} (rep #{obj.name})"
|
276
|
+
else # layout
|
277
|
+
stream.puts " - [layout] #{obj.identifier}"
|
278
|
+
end
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
def write_stack_trace(stream, error, params={})
|
284
|
+
self.write_section_header(stream, 'Stack trace', params)
|
285
|
+
|
286
|
+
count = params[:verbose] ? -1 : 10
|
287
|
+
error.backtrace[0...count].each_with_index do |item, index|
|
288
|
+
stream.puts " #{index}. #{item}"
|
289
|
+
end
|
290
|
+
if error.backtrace.size > count
|
291
|
+
stream.puts " ... #{error.backtrace.size - count} more lines omitted. See full crash log for details."
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
def write_issue_link(stream, params={})
|
296
|
+
stream.puts
|
297
|
+
stream.puts "If you believe this is a bug in nanoc, please do report it at"
|
298
|
+
stream.puts "-> https://github.com/ddfreyne/nanoc/issues/new <-"
|
299
|
+
stream.puts
|
300
|
+
stream.puts "A detailed crash log has been written to ./crash.log."
|
301
|
+
end
|
302
|
+
|
303
|
+
def write_version_information(stream, params={})
|
304
|
+
self.write_section_header(stream, 'Version information', params)
|
305
|
+
stream.puts Nanoc.version_information
|
306
|
+
end
|
307
|
+
|
308
|
+
def write_system_information(stream, params={})
|
309
|
+
begin
|
310
|
+
uname = `uname -a`
|
311
|
+
self.write_section_header(stream, 'System information', params)
|
312
|
+
stream.puts uname
|
313
|
+
rescue Errno::ENOENT
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
def write_installed_gems(stream, params={})
|
318
|
+
self.write_section_header(stream, 'Installed gems', params)
|
319
|
+
self.gems_and_versions.each do |g|
|
320
|
+
stream.puts " #{g.first} #{g.last.join(', ')}"
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
def write_environment(stream, params={})
|
325
|
+
self.write_section_header(stream, 'Environment', params)
|
326
|
+
ENV.sort.each do |e|
|
327
|
+
stream.puts "#{e.first} => #{e.last.inspect}"
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
def write_gemfile_lock(stream, params={})
|
332
|
+
if File.exist?('Gemfile.lock')
|
333
|
+
self.write_section_header(stream, 'Gemfile.lock', params)
|
334
|
+
stream.puts File.read('Gemfile.lock')
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
def write_load_paths(stream, params={})
|
339
|
+
self.write_section_header(stream, 'Load paths', params)
|
340
|
+
$LOAD_PATH.each_with_index do |i, index|
|
341
|
+
stream.puts " #{index}. #{i}"
|
342
|
+
end
|
343
343
|
end
|
344
344
|
|
345
345
|
end
|