nanoc-cli 4.11.13
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 +7 -0
- data/NEWS.md +3 -0
- data/README.md +3 -0
- data/lib/nanoc-cli.rb +3 -0
- data/lib/nanoc/cli.rb +237 -0
- data/lib/nanoc/cli/ansi_string_colorizer.rb +30 -0
- data/lib/nanoc/cli/cleaning_stream.rb +160 -0
- data/lib/nanoc/cli/command_runner.rb +74 -0
- data/lib/nanoc/cli/commands/compile.rb +57 -0
- data/lib/nanoc/cli/commands/create-site.rb +257 -0
- data/lib/nanoc/cli/commands/nanoc.rb +42 -0
- data/lib/nanoc/cli/commands/prune.rb +49 -0
- data/lib/nanoc/cli/commands/shell.rb +57 -0
- data/lib/nanoc/cli/commands/show-data.rb +185 -0
- data/lib/nanoc/cli/commands/show-plugins.rb +97 -0
- data/lib/nanoc/cli/commands/view.rb +68 -0
- data/lib/nanoc/cli/compile_listeners/abstract.rb +58 -0
- data/lib/nanoc/cli/compile_listeners/aggregate.rb +50 -0
- data/lib/nanoc/cli/compile_listeners/debug_printer.rb +100 -0
- data/lib/nanoc/cli/compile_listeners/diff_generator.rb +101 -0
- data/lib/nanoc/cli/compile_listeners/file_action_printer.rb +80 -0
- data/lib/nanoc/cli/compile_listeners/timing_recorder.rb +170 -0
- data/lib/nanoc/cli/error_handler.rb +365 -0
- data/lib/nanoc/cli/logger.rb +77 -0
- data/lib/nanoc/cli/stack_trace_writer.rb +51 -0
- data/lib/nanoc/cli/stream_cleaners/abstract.rb +23 -0
- data/lib/nanoc/cli/stream_cleaners/ansi_colors.rb +15 -0
- data/lib/nanoc/cli/stream_cleaners/utf8.rb +20 -0
- data/lib/nanoc/cli/transform.rb +18 -0
- data/lib/nanoc/cli/version.rb +7 -0
- metadata +127 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 6614fdc62f969231b662ed63c693f5c2e28185ab2e139cab3fb1e88b46f4707c
|
4
|
+
data.tar.gz: 3e55bbea5197f43f0e09afcb908fc438f283a2cd5fd4a23a52ef8020a31f1d37
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0f57b9258cc0639792f33863e56785778bc6c1750b86aeab3d1131547e912799304ce8875a1bb0fa6069eab8a1bb80a2ff2377220c780d4f95a7b44f103cd71b
|
7
|
+
data.tar.gz: 6778aefc198872092095f5b9da16454a333f1f516919eb53349671339d84ad280a95e9ee460ed2505f55c6a6d84b6297d6b3fe6d5ffb903de8ae680c8d3c9083
|
data/NEWS.md
ADDED
data/README.md
ADDED
data/lib/nanoc-cli.rb
ADDED
data/lib/nanoc/cli.rb
ADDED
@@ -0,0 +1,237 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'nanoc/core'
|
4
|
+
require 'diff/lcs'
|
5
|
+
require 'diff/lcs/hunk'
|
6
|
+
require 'logger'
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'cri'
|
10
|
+
rescue LoadError => e
|
11
|
+
$stderr.puts e
|
12
|
+
$stderr.puts "If you are using a Gemfile, make sure that the Gemfile contains Nanoc ('gem \"nanoc\"')."
|
13
|
+
exit 1
|
14
|
+
end
|
15
|
+
|
16
|
+
module Nanoc
|
17
|
+
# @api private
|
18
|
+
module CLI
|
19
|
+
# @return [Boolean] true if debug output is enabled, false if not
|
20
|
+
def self.debug?
|
21
|
+
@debug || false
|
22
|
+
end
|
23
|
+
|
24
|
+
# @param [Boolean] boolean true if debug output should be enabled,
|
25
|
+
# false if it should not
|
26
|
+
#
|
27
|
+
# @return [void]
|
28
|
+
def self.debug=(boolean)
|
29
|
+
@debug = boolean
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.verbosity
|
33
|
+
@verbosity || 0
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.verbosity=(val)
|
37
|
+
@verbosity = val
|
38
|
+
end
|
39
|
+
|
40
|
+
# Wraps `$stdout` and `$stderr` in appropriate cleaning streams.
|
41
|
+
#
|
42
|
+
# @return [void]
|
43
|
+
def self.setup_cleaning_streams
|
44
|
+
$stdout = wrap_in_cleaning_stream($stdout)
|
45
|
+
$stderr = wrap_in_cleaning_stream($stderr)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Wraps the given stream in a cleaning stream. The cleaning streams will
|
49
|
+
# have the proper stream cleaners configured.
|
50
|
+
#
|
51
|
+
# @param [IO] io The stream to wrap
|
52
|
+
#
|
53
|
+
# @return [::Nanoc::CLI::CleaningStream]
|
54
|
+
def self.wrap_in_cleaning_stream(io)
|
55
|
+
cio = ::Nanoc::CLI::CleaningStream.new(io)
|
56
|
+
|
57
|
+
unless enable_utf8?(io)
|
58
|
+
cio.add_stream_cleaner(Nanoc::CLI::StreamCleaners::UTF8)
|
59
|
+
end
|
60
|
+
|
61
|
+
unless enable_ansi_colors?(io)
|
62
|
+
cio.add_stream_cleaner(Nanoc::CLI::StreamCleaners::ANSIColors)
|
63
|
+
end
|
64
|
+
|
65
|
+
cio
|
66
|
+
end
|
67
|
+
|
68
|
+
# @return [Boolean] true if UTF-8 support is present, false if not
|
69
|
+
def self.enable_utf8?(io)
|
70
|
+
return true unless io.tty?
|
71
|
+
|
72
|
+
%w[LC_ALL LC_CTYPE LANG].any? { |e| ENV[e] =~ /UTF/i }
|
73
|
+
end
|
74
|
+
|
75
|
+
# @return [Boolean] true if color support is present, false if not
|
76
|
+
def self.enable_ansi_colors?(io)
|
77
|
+
io.tty? && !ENV.key?('NO_COLOR')
|
78
|
+
end
|
79
|
+
|
80
|
+
# Invokes the Nanoc command-line tool with the given arguments.
|
81
|
+
#
|
82
|
+
# @param [Array<String>] args An array of command-line arguments
|
83
|
+
#
|
84
|
+
# @return [void]
|
85
|
+
def self.run(args)
|
86
|
+
Nanoc::CLI::ErrorHandler.handle_while do
|
87
|
+
setup
|
88
|
+
root_command.run(args)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# @return [Cri::Command] The root command, i.e. the command-line tool itself
|
93
|
+
def self.root_command
|
94
|
+
@root_command
|
95
|
+
end
|
96
|
+
|
97
|
+
# Adds the given command to the collection of available commands.
|
98
|
+
#
|
99
|
+
# @param [Cri::Command] cmd The command to add
|
100
|
+
#
|
101
|
+
# @return [void]
|
102
|
+
def self.add_command(cmd)
|
103
|
+
root_command.add_command(cmd)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Schedules the given block to be executed after the CLI has been set up.
|
107
|
+
#
|
108
|
+
# @return [void]
|
109
|
+
def self.after_setup(&block)
|
110
|
+
# TODO: decide what should happen if the CLI is already set up
|
111
|
+
add_after_setup_proc(block)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Makes the command-line interface ready for use.
|
115
|
+
#
|
116
|
+
# @return [void]
|
117
|
+
def self.setup
|
118
|
+
Nanoc::CLI.setup_cleaning_streams
|
119
|
+
setup_commands
|
120
|
+
load_custom_commands
|
121
|
+
after_setup_procs.each(&:call)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Sets up the root command and base subcommands.
|
125
|
+
#
|
126
|
+
# @return [void]
|
127
|
+
def self.setup_commands
|
128
|
+
# Reinit
|
129
|
+
@root_command = nil
|
130
|
+
|
131
|
+
# Add root command
|
132
|
+
filename = __dir__ + '/cli/commands/nanoc.rb'
|
133
|
+
@root_command = Cri::Command.load_file(filename, infer_name: true)
|
134
|
+
|
135
|
+
# Add help command
|
136
|
+
help_cmd = Cri::Command.new_basic_help
|
137
|
+
add_command(help_cmd)
|
138
|
+
|
139
|
+
# Add other commands
|
140
|
+
cmd_filenames = Dir[__dir__ + '/cli/commands/*.rb']
|
141
|
+
cmd_filenames.each do |cmd_filename|
|
142
|
+
basename = File.basename(cmd_filename, '.rb')
|
143
|
+
|
144
|
+
next if basename == 'nanoc'
|
145
|
+
|
146
|
+
cmd = Cri::Command.load_file(cmd_filename, infer_name: true)
|
147
|
+
add_command(cmd)
|
148
|
+
end
|
149
|
+
|
150
|
+
if defined?(Bundler)
|
151
|
+
# Discover external commands through Bundler
|
152
|
+
begin
|
153
|
+
Bundler.require(:nanoc)
|
154
|
+
rescue Bundler::GemfileNotFound
|
155
|
+
# When running Nanoc with Bundler being defined but
|
156
|
+
# no gemfile being present (rubygems automatically loads
|
157
|
+
# Bundler when executing from command line), don't crash.
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Loads site-specific commands.
|
163
|
+
#
|
164
|
+
# @return [void]
|
165
|
+
def self.load_custom_commands
|
166
|
+
if Nanoc::Core::SiteLoader.cwd_is_nanoc_site?
|
167
|
+
config = Nanoc::Core::ConfigLoader.new.new_from_cwd
|
168
|
+
config[:commands_dirs].each do |path|
|
169
|
+
load_commands_at(path)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def self.load_commands_at(path)
|
175
|
+
recursive_contents_of(path).each do |filename|
|
176
|
+
# Create command
|
177
|
+
command = Cri::Command.load_file(filename, infer_name: true)
|
178
|
+
|
179
|
+
# Get supercommand
|
180
|
+
pieces = filename.gsub(/^#{path}\/|\.rb$/, '').split('/')
|
181
|
+
pieces = pieces[0, pieces.size - 1] || []
|
182
|
+
root = Nanoc::CLI.root_command
|
183
|
+
supercommand = pieces.reduce(root) do |cmd, piece|
|
184
|
+
cmd.nil? ? nil : cmd.command_named(piece)
|
185
|
+
end
|
186
|
+
|
187
|
+
# Add to supercommand
|
188
|
+
if supercommand.nil?
|
189
|
+
raise "Cannot load command at #{filename} because its supercommand cannot be found"
|
190
|
+
end
|
191
|
+
|
192
|
+
supercommand.add_command(command)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
# @return [Array] The directory contents
|
197
|
+
def self.recursive_contents_of(path)
|
198
|
+
return [] unless File.directory?(path)
|
199
|
+
|
200
|
+
files, dirs = *Dir[path + '/*'].sort.partition { |e| File.file?(e) }
|
201
|
+
dirs.each { |d| files.concat recursive_contents_of(d) }
|
202
|
+
files
|
203
|
+
end
|
204
|
+
|
205
|
+
def self.after_setup_procs
|
206
|
+
@after_setup_procs || []
|
207
|
+
end
|
208
|
+
|
209
|
+
def self.add_after_setup_proc(proc)
|
210
|
+
@after_setup_procs ||= []
|
211
|
+
@after_setup_procs << proc
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
inflector_class = Class.new(Zeitwerk::Inflector) do
|
217
|
+
def camelize(basename, abspath)
|
218
|
+
case basename
|
219
|
+
when 'version', 'cli', 'utf8'
|
220
|
+
basename.upcase
|
221
|
+
when 'ansi_colors'
|
222
|
+
'ANSIColors'
|
223
|
+
when 'ansi_string_colorizer'
|
224
|
+
'ANSIStringColorizer'
|
225
|
+
else
|
226
|
+
super
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
loader = Zeitwerk::Loader.new
|
232
|
+
loader.inflector = inflector_class.new
|
233
|
+
loader.push_dir(__dir__ + '/..')
|
234
|
+
loader.ignore(__dir__ + '/../nanoc-cli.rb')
|
235
|
+
loader.ignore(__dir__ + '/cli/commands')
|
236
|
+
loader.setup
|
237
|
+
loader.eager_load
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nanoc
|
4
|
+
module CLI
|
5
|
+
# A simple ANSI colorizer for strings. When given a string and a list of
|
6
|
+
# attributes, it returns a colorized string.
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
module ANSIStringColorizer
|
10
|
+
# TODO: complete mapping
|
11
|
+
MAPPING = {
|
12
|
+
bold: "\e[1m",
|
13
|
+
red: "\e[31m",
|
14
|
+
green: "\e[32m",
|
15
|
+
yellow: "\e[33m",
|
16
|
+
blue: "\e[34m",
|
17
|
+
}.freeze
|
18
|
+
|
19
|
+
# @param [String] str The string to colorize
|
20
|
+
#
|
21
|
+
# @param [Array] attrs An array of attributes from `MAPPING` to colorize the
|
22
|
+
# string with
|
23
|
+
#
|
24
|
+
# @return [String] A string colorized using the given attributes
|
25
|
+
def self.c(str, *attrs)
|
26
|
+
attrs.map { |a| MAPPING[a] }.join('') + str + "\e[0m"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nanoc
|
4
|
+
module CLI
|
5
|
+
# An output stream that passes output through stream cleaners. This can be
|
6
|
+
# used to strip ANSI color sequences, for instance.
|
7
|
+
class CleaningStream
|
8
|
+
# @param [IO, StringIO] stream The stream to wrap
|
9
|
+
def initialize(stream)
|
10
|
+
@stream = stream
|
11
|
+
@stream_cleaners = []
|
12
|
+
end
|
13
|
+
|
14
|
+
# Adds a stream cleaner for the given class to this cleaning stream. If the
|
15
|
+
# cleaning stream already has the given stream cleaner, nothing happens.
|
16
|
+
#
|
17
|
+
# @param [Nanoc::CLI::StreamCleaners::Abstract] klass The class of the
|
18
|
+
# stream cleaner to add
|
19
|
+
#
|
20
|
+
# @return [void]
|
21
|
+
def add_stream_cleaner(klass)
|
22
|
+
unless @stream_cleaners.map(&:class).include?(klass)
|
23
|
+
@stream_cleaners << klass.new
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Removes the stream cleaner for the given class from this cleaning stream.
|
28
|
+
# If the cleaning stream does not have the given stream cleaner, nothing
|
29
|
+
# happens.
|
30
|
+
#
|
31
|
+
# @param [Nanoc::CLI::StreamCleaners::Abstract] klass The class of the
|
32
|
+
# stream cleaner to add
|
33
|
+
#
|
34
|
+
# @return [void]
|
35
|
+
def remove_stream_cleaner(klass)
|
36
|
+
@stream_cleaners.delete_if { |c| c.class == klass }
|
37
|
+
end
|
38
|
+
|
39
|
+
# @group IO proxy methods
|
40
|
+
|
41
|
+
# @see IO#write
|
42
|
+
def write(str)
|
43
|
+
_nanoc_swallow_broken_pipe_errors_while do
|
44
|
+
@stream.write(_nanoc_clean(str))
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# @see IO#<<
|
49
|
+
def <<(str)
|
50
|
+
_nanoc_swallow_broken_pipe_errors_while do
|
51
|
+
@stream.<<(_nanoc_clean(str))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# @see IO#tty?
|
56
|
+
def tty?
|
57
|
+
@cached_is_tty ||= @stream.tty?
|
58
|
+
end
|
59
|
+
|
60
|
+
# @see IO#isatty
|
61
|
+
def isatty
|
62
|
+
tty?
|
63
|
+
end
|
64
|
+
|
65
|
+
# @see IO#flush
|
66
|
+
def flush
|
67
|
+
_nanoc_swallow_broken_pipe_errors_while do
|
68
|
+
@stream.flush
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# @see IO#tell
|
73
|
+
def tell
|
74
|
+
@stream.tell
|
75
|
+
end
|
76
|
+
|
77
|
+
# @see IO#print
|
78
|
+
def print(str)
|
79
|
+
_nanoc_swallow_broken_pipe_errors_while do
|
80
|
+
@stream.print(_nanoc_clean(str))
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# @see IO#puts
|
85
|
+
def puts(*str)
|
86
|
+
_nanoc_swallow_broken_pipe_errors_while do
|
87
|
+
@stream.puts(*str.map { |ss| _nanoc_clean(ss) })
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# @see StringIO#string
|
92
|
+
def string
|
93
|
+
@stream.string
|
94
|
+
end
|
95
|
+
|
96
|
+
# @see IO#reopen
|
97
|
+
def reopen(*args)
|
98
|
+
@stream.reopen(*args)
|
99
|
+
end
|
100
|
+
|
101
|
+
# @see IO#close
|
102
|
+
def close
|
103
|
+
@stream.close
|
104
|
+
end
|
105
|
+
|
106
|
+
# @see File#exist?
|
107
|
+
def exist?
|
108
|
+
@stream.exist?
|
109
|
+
end
|
110
|
+
|
111
|
+
# @see File.exists?
|
112
|
+
def exists?
|
113
|
+
@stream.exists?
|
114
|
+
end
|
115
|
+
|
116
|
+
# @see IO.winsize
|
117
|
+
def winsize
|
118
|
+
@stream.winsize
|
119
|
+
end
|
120
|
+
|
121
|
+
# @see IO.winsize=
|
122
|
+
def winsize=(arg)
|
123
|
+
@stream.winsize = arg
|
124
|
+
end
|
125
|
+
|
126
|
+
# @see IO.sync
|
127
|
+
def sync
|
128
|
+
@stream.sync
|
129
|
+
end
|
130
|
+
|
131
|
+
# @see IO.sync=
|
132
|
+
def sync=(arg)
|
133
|
+
@stream.sync = arg
|
134
|
+
end
|
135
|
+
|
136
|
+
# @see IO.sync=
|
137
|
+
def external_encoding
|
138
|
+
@stream.external_encoding
|
139
|
+
end
|
140
|
+
|
141
|
+
# @see ARGF.set_encoding
|
142
|
+
# rubocop:disable Naming/AccessorMethodName
|
143
|
+
def set_encoding(*args)
|
144
|
+
@stream.set_encoding(*args)
|
145
|
+
end
|
146
|
+
# rubocop:enable Naming/AccessorMethodName
|
147
|
+
|
148
|
+
protected
|
149
|
+
|
150
|
+
def _nanoc_clean(str)
|
151
|
+
@stream_cleaners.reduce(str.to_s.scrub) { |acc, elem| elem.clean(acc) }
|
152
|
+
end
|
153
|
+
|
154
|
+
def _nanoc_swallow_broken_pipe_errors_while
|
155
|
+
yield
|
156
|
+
rescue Errno::EPIPE
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|