guard 1.4.0 → 2.18.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 +7 -0
- data/CHANGELOG.md +1 -677
- data/LICENSE +4 -2
- data/README.md +91 -753
- data/bin/_guard-core +11 -0
- data/bin/guard +108 -3
- data/lib/guard/aruba_adapter.rb +59 -0
- data/lib/guard/cli/environments/bundler.rb +22 -0
- data/lib/guard/cli/environments/evaluate_only.rb +35 -0
- data/lib/guard/cli/environments/valid.rb +69 -0
- data/lib/guard/cli.rb +129 -128
- data/lib/guard/commander.rb +104 -0
- data/lib/guard/commands/all.rb +37 -0
- data/lib/guard/commands/change.rb +31 -0
- data/lib/guard/commands/notification.rb +26 -0
- data/lib/guard/commands/pause.rb +29 -0
- data/lib/guard/commands/reload.rb +36 -0
- data/lib/guard/commands/scope.rb +38 -0
- data/lib/guard/commands/show.rb +24 -0
- data/lib/guard/config.rb +18 -0
- data/lib/guard/deprecated/dsl.rb +45 -0
- data/lib/guard/deprecated/evaluator.rb +39 -0
- data/lib/guard/deprecated/guard.rb +328 -0
- data/lib/guard/deprecated/guardfile.rb +84 -0
- data/lib/guard/deprecated/watcher.rb +27 -0
- data/lib/guard/dsl.rb +332 -363
- data/lib/guard/dsl_describer.rb +132 -122
- data/lib/guard/dsl_reader.rb +51 -0
- data/lib/guard/group.rb +34 -14
- data/lib/guard/guardfile/evaluator.rb +232 -0
- data/lib/guard/guardfile/generator.rb +128 -0
- data/lib/guard/guardfile.rb +24 -60
- data/lib/guard/interactor.rb +31 -255
- data/lib/guard/internals/debugging.rb +68 -0
- data/lib/guard/internals/groups.rb +40 -0
- data/lib/guard/internals/helpers.rb +13 -0
- data/lib/guard/internals/plugins.rb +53 -0
- data/lib/guard/internals/queue.rb +51 -0
- data/lib/guard/internals/scope.rb +121 -0
- data/lib/guard/internals/session.rb +180 -0
- data/lib/guard/internals/state.rb +25 -0
- data/lib/guard/internals/tracing.rb +33 -0
- data/lib/guard/internals/traps.rb +10 -0
- data/lib/guard/jobs/base.rb +21 -0
- data/lib/guard/jobs/pry_wrapper.rb +336 -0
- data/lib/guard/jobs/sleep.rb +26 -0
- data/lib/guard/notifier.rb +46 -212
- data/lib/guard/options.rb +22 -0
- data/lib/guard/plugin.rb +303 -0
- data/lib/guard/plugin_util.rb +191 -0
- data/lib/guard/rake_task.rb +42 -0
- data/lib/guard/runner.rb +80 -140
- data/lib/guard/templates/Guardfile +14 -0
- data/lib/guard/terminal.rb +13 -0
- data/lib/guard/ui/colors.rb +56 -0
- data/lib/guard/ui/config.rb +70 -0
- data/lib/guard/ui/logger.rb +30 -0
- data/lib/guard/ui.rb +163 -128
- data/lib/guard/version.rb +1 -2
- data/lib/guard/watcher/pattern/deprecated_regexp.rb +45 -0
- data/lib/guard/watcher/pattern/match_result.rb +18 -0
- data/lib/guard/watcher/pattern/matcher.rb +33 -0
- data/lib/guard/watcher/pattern/pathname_path.rb +15 -0
- data/lib/guard/watcher/pattern/simple_path.rb +23 -0
- data/lib/guard/watcher/pattern.rb +24 -0
- data/lib/guard/watcher.rb +52 -95
- data/lib/guard.rb +108 -376
- data/lib/tasks/releaser.rb +116 -0
- data/man/guard.1 +12 -9
- data/man/guard.1.html +18 -12
- metadata +148 -77
- data/images/guard.png +0 -0
- data/lib/guard/guard.rb +0 -156
- data/lib/guard/hook.rb +0 -120
- data/lib/guard/interactors/coolline.rb +0 -64
- data/lib/guard/interactors/helpers/completion.rb +0 -32
- data/lib/guard/interactors/helpers/terminal.rb +0 -46
- data/lib/guard/interactors/readline.rb +0 -94
- data/lib/guard/interactors/simple.rb +0 -19
- data/lib/guard/notifiers/emacs.rb +0 -69
- data/lib/guard/notifiers/gntp.rb +0 -118
- data/lib/guard/notifiers/growl.rb +0 -99
- data/lib/guard/notifiers/growl_notify.rb +0 -92
- data/lib/guard/notifiers/libnotify.rb +0 -96
- data/lib/guard/notifiers/notifysend.rb +0 -84
- data/lib/guard/notifiers/rb_notifu.rb +0 -102
- data/lib/guard/notifiers/terminal_notifier.rb +0 -66
- data/lib/guard/notifiers/tmux.rb +0 -69
- data/lib/guard/version.rbc +0 -130
data/lib/guard.rb
CHANGED
@@ -1,437 +1,169 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "thread"
|
2
|
+
require "listen"
|
3
|
+
|
4
|
+
require "guard/config"
|
5
|
+
require "guard/deprecated/guard" unless Guard::Config.new.strict?
|
6
|
+
require "guard/internals/helpers"
|
7
|
+
|
8
|
+
require "guard/internals/debugging"
|
9
|
+
require "guard/internals/traps"
|
10
|
+
require "guard/internals/queue"
|
11
|
+
|
12
|
+
# TODO: remove this class altogether
|
13
|
+
require "guard/interactor"
|
3
14
|
|
4
15
|
# Guard is the main module for all Guard related modules and classes.
|
5
16
|
# Also Guard plugins should use this namespace.
|
6
|
-
#
|
7
17
|
module Guard
|
18
|
+
Deprecated::Guard.add_deprecated(self) unless Config.new.strict?
|
8
19
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
require 'guard/runner'
|
15
|
-
require 'guard/ui'
|
16
|
-
require 'guard/watcher'
|
17
|
-
|
18
|
-
# The Guardfile template for `guard init`
|
19
|
-
GUARDFILE_TEMPLATE = File.expand_path('../guard/templates/Guardfile', __FILE__)
|
20
|
+
class << self
|
21
|
+
attr_reader :state
|
22
|
+
attr_reader :queue
|
23
|
+
attr_reader :listener
|
24
|
+
attr_reader :interactor
|
20
25
|
|
21
|
-
|
22
|
-
HOME_TEMPLATES = File.expand_path('~/.guard/templates')
|
26
|
+
# @private api
|
23
27
|
|
24
|
-
|
25
|
-
attr_accessor :options, :interactor, :runner, :listener, :lock
|
28
|
+
include Internals::Helpers
|
26
29
|
|
27
|
-
#
|
30
|
+
# Initializes the Guard singleton:
|
28
31
|
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
+
# * Initialize the internal Guard state;
|
33
|
+
# * Create the interactor
|
34
|
+
# * Select and initialize the file change listener.
|
32
35
|
#
|
33
36
|
# @option options [Boolean] clear if auto clear the UI should be done
|
34
37
|
# @option options [Boolean] notify if system notifications should be shown
|
35
38
|
# @option options [Boolean] debug if debug output should be shown
|
36
39
|
# @option options [Array<String>] group the list of groups to start
|
37
|
-
# @option options [String] watchdir the
|
40
|
+
# @option options [Array<String>] watchdir the directories to watch
|
38
41
|
# @option options [String] guardfile the path to the Guardfile
|
39
|
-
# @deprecated @option options [Boolean] watch_all_modifications watches all file modifications if true
|
40
|
-
# @deprecated @option options [Boolean] no_vendor ignore vendored dependencies
|
41
42
|
#
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
@watchdir = (options[:watchdir] && File.expand_path(options[:watchdir])) || Dir.pwd
|
46
|
-
@runner = ::Guard::Runner.new
|
47
|
-
@allow_stop = Listen::Turnstile.new
|
43
|
+
# @return [Guard] the Guard singleton
|
44
|
+
def setup(cmdline_options = {})
|
45
|
+
init(cmdline_options)
|
48
46
|
|
49
|
-
::
|
50
|
-
deprecated_options_warning
|
47
|
+
@queue = Internals::Queue.new(Guard)
|
51
48
|
|
52
|
-
|
53
|
-
setup_guards
|
54
|
-
setup_listener
|
55
|
-
setup_signal_traps
|
56
|
-
|
57
|
-
debug_command_execution if @options[:debug]
|
58
|
-
|
59
|
-
::Guard::Dsl.evaluate_guardfile(options)
|
60
|
-
::Guard::UI.error 'No guards found in Guardfile, please add at least one.' if @guards.empty?
|
61
|
-
|
62
|
-
runner.deprecation_warning if @options[:show_deprecations]
|
63
|
-
|
64
|
-
setup_notifier
|
65
|
-
setup_interactor
|
66
|
-
|
67
|
-
self
|
68
|
-
end
|
49
|
+
_evaluate(state.session.evaluator_options)
|
69
50
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
#
|
74
|
-
def setup_groups
|
75
|
-
@groups = [Group.new(:default)]
|
76
|
-
end
|
51
|
+
# NOTE: this should be *after* evaluate so :directories can work
|
52
|
+
# TODO: move listener setup to session?
|
53
|
+
@listener = Listen.send(*state.session.listener_args, &_listener_callback)
|
77
54
|
|
78
|
-
|
79
|
-
|
80
|
-
# @see Guard.guards
|
81
|
-
#
|
82
|
-
def setup_guards
|
83
|
-
@guards = []
|
84
|
-
end
|
55
|
+
ignores = state.session.guardfile_ignore
|
56
|
+
@listener.ignore(ignores) unless ignores.empty?
|
85
57
|
|
86
|
-
|
87
|
-
|
88
|
-
# Currently two signals are caught:
|
89
|
-
# - `USR1` which pauses listening to changes.
|
90
|
-
# - `USR2` which resumes listening to changes.
|
91
|
-
#
|
92
|
-
def setup_signal_traps
|
93
|
-
unless defined?(JRUBY_VERSION)
|
94
|
-
if Signal.list.keys.include?('USR1')
|
95
|
-
Signal.trap('USR1') { ::Guard.pause unless @listener.paused? }
|
96
|
-
end
|
97
|
-
|
98
|
-
if Signal.list.keys.include?('USR2')
|
99
|
-
Signal.trap('USR2') { ::Guard.pause if @listener.paused? }
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
58
|
+
ignores = state.session.guardfile_ignore_bang
|
59
|
+
@listener.ignore!(ignores) unless ignores.empty?
|
103
60
|
|
104
|
-
|
105
|
-
#
|
106
|
-
def setup_listener
|
107
|
-
listener_callback = lambda do |modified, added, removed|
|
108
|
-
::Guard::Dsl.reevaluate_guardfile if ::Guard::Watcher.match_guardfile?(modified)
|
61
|
+
Notifier.connect(state.session.notify_options)
|
109
62
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
end
|
63
|
+
traps = Internals::Traps
|
64
|
+
traps.handle("USR1") { async_queue_add([:guard_pause, :paused]) }
|
65
|
+
traps.handle("USR2") { async_queue_add([:guard_pause, :unpaused]) }
|
114
66
|
|
115
|
-
|
116
|
-
|
117
|
-
listener_options[option.to_sym] = options[option] if options.key?(option)
|
118
|
-
end
|
67
|
+
@interactor = Interactor.new(state.session.interactor_name == :sleep)
|
68
|
+
traps.handle("INT") { @interactor.handle_interrupt }
|
119
69
|
|
120
|
-
|
70
|
+
self
|
121
71
|
end
|
122
72
|
|
123
|
-
|
124
|
-
|
125
|
-
def setup_notifier
|
126
|
-
options[:notify] && ENV['GUARD_NOTIFY'] != 'false' ? ::Guard::Notifier.turn_on : ::Guard::Notifier.turn_off
|
73
|
+
def init(cmdline_options)
|
74
|
+
@state = Internals::State.new(cmdline_options)
|
127
75
|
end
|
128
76
|
|
129
|
-
#
|
77
|
+
# Asynchronously trigger changes
|
130
78
|
#
|
131
|
-
|
132
|
-
unless options[:no_interactions]
|
133
|
-
@interactor = ::Guard::Interactor.fabricate
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
# Start Guard by evaluating the `Guardfile`, initializing declared Guard plugins
|
138
|
-
# and starting the available file change listener.
|
139
|
-
# Main method for Guard that is called from the CLI when Guard starts.
|
79
|
+
# Currently supported args:
|
140
80
|
#
|
141
|
-
#
|
142
|
-
#
|
143
|
-
# - Configure Notifiers
|
144
|
-
# - Initialize the declared Guard plugins
|
145
|
-
# - Start the available file change listener
|
81
|
+
# @example Old style hash:
|
82
|
+
# async_queue_add(modified: ['foo'], added: ['bar'], removed: [])
|
146
83
|
#
|
147
|
-
#
|
148
|
-
#
|
149
|
-
# @option options [Boolean] debug if debug output should be shown
|
150
|
-
# @option options [Array<String>] group the list of groups to start
|
151
|
-
# @option options [String] watchdir the director to watch
|
152
|
-
# @option options [String] guardfile the path to the Guardfile
|
84
|
+
# @example New style signals with args:
|
85
|
+
# async_queue_add([:guard_pause, :unpaused ])
|
153
86
|
#
|
154
|
-
def
|
155
|
-
|
156
|
-
::Guard::UI.info "Guard is now watching at '#{ @watchdir }'"
|
87
|
+
def async_queue_add(changes)
|
88
|
+
@queue << changes
|
157
89
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
listener.start(false)
|
163
|
-
|
164
|
-
@allow_stop.wait if @allow_stop
|
90
|
+
# Putting interactor in background puts guard into foreground
|
91
|
+
# so it can handle change notifications
|
92
|
+
Thread.new { interactor.background }
|
165
93
|
end
|
166
94
|
|
167
|
-
|
168
|
-
#
|
169
|
-
def stop
|
170
|
-
listener.stop
|
171
|
-
interactor.stop if interactor
|
172
|
-
runner.run(:stop)
|
173
|
-
::Guard::UI.info 'Bye bye...', :reset => true
|
95
|
+
private
|
174
96
|
|
175
|
-
|
97
|
+
# Check if any of the changes are actually watched for
|
98
|
+
# TODO: why iterate twice? reuse this info when running tasks
|
99
|
+
def _relevant_changes?(changes)
|
100
|
+
# TODO: no coverage!
|
101
|
+
files = changes.values.flatten(1)
|
102
|
+
scope = Guard.state.scope
|
103
|
+
watchers = scope.grouped_plugins.map do |_group, plugins|
|
104
|
+
plugins.map(&:watchers).flatten
|
105
|
+
end.flatten
|
106
|
+
watchers.any? { |watcher| files.any? { |file| watcher.match(file) } }
|
176
107
|
end
|
177
108
|
|
178
|
-
|
179
|
-
|
180
|
-
# @param [Hash] scopes hash with a Guard plugin or a group scope
|
181
|
-
#
|
182
|
-
def reload(scopes = {})
|
183
|
-
within_preserved_state do
|
184
|
-
::Guard::UI.clear(:force => true)
|
185
|
-
::Guard::UI.action_with_scopes('Reload', scopes)
|
186
|
-
::Guard::Dsl.reevaluate_guardfile if scopes.empty?
|
187
|
-
runner.run(:reload, scopes)
|
188
|
-
end
|
109
|
+
def _relative_pathnames(paths)
|
110
|
+
paths.map { |path| _relative_pathname(path) }
|
189
111
|
end
|
190
112
|
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
::Guard::UI.action_with_scopes('Run', scopes)
|
199
|
-
runner.run(:run_all, scopes)
|
200
|
-
end
|
201
|
-
end
|
113
|
+
def _listener_callback
|
114
|
+
lambda do |modified, added, removed|
|
115
|
+
relative_paths = {
|
116
|
+
modified: _relative_pathnames(modified),
|
117
|
+
added: _relative_pathnames(added),
|
118
|
+
removed: _relative_pathnames(removed)
|
119
|
+
}
|
202
120
|
|
203
|
-
|
204
|
-
#
|
205
|
-
def pause
|
206
|
-
if listener.paused?
|
207
|
-
::Guard::UI.info 'Un-paused files modification listening', :reset => true
|
208
|
-
listener.unpause
|
209
|
-
else
|
210
|
-
::Guard::UI.info 'Paused files modification listening', :reset => true
|
211
|
-
listener.pause
|
212
|
-
end
|
213
|
-
end
|
121
|
+
_guardfile_deprecated_check(relative_paths[:modified])
|
214
122
|
|
215
|
-
|
216
|
-
#
|
217
|
-
# @see Guard.groups
|
218
|
-
#
|
219
|
-
# @example Filter Guard plugins by String or Symbol
|
220
|
-
# Guard.guards('rspec')
|
221
|
-
# Guard.guards(:rspec)
|
222
|
-
#
|
223
|
-
# @example Filter Guard plugins by Regexp
|
224
|
-
# Guard.guards(/rsp.+/)
|
225
|
-
#
|
226
|
-
# @example Filter Guard plugins by Hash
|
227
|
-
# Guard.guards({ :name => 'rspec', :group => 'backend' })
|
228
|
-
#
|
229
|
-
# @param [String, Symbol, Regexp, Hash] filter the filter to apply to the Guard plugins
|
230
|
-
# @return [Array<Guard>] the filtered Guard plugins
|
231
|
-
#
|
232
|
-
def guards(filter = nil)
|
233
|
-
@guards ||= []
|
234
|
-
|
235
|
-
case filter
|
236
|
-
when String, Symbol
|
237
|
-
@guards.find { |guard| guard.class.to_s.downcase.sub('guard::', '') == filter.to_s.downcase.gsub('-', '') }
|
238
|
-
when Regexp
|
239
|
-
@guards.find_all { |guard| guard.class.to_s.downcase.sub('guard::', '') =~ filter }
|
240
|
-
when Hash
|
241
|
-
filter.inject(@guards) do |matches, (k, v)|
|
242
|
-
if k.to_sym == :name
|
243
|
-
matches.find_all { |guard| guard.class.to_s.downcase.sub('guard::', '') == v.to_s.downcase.gsub('-', '') }
|
244
|
-
else
|
245
|
-
matches.find_all { |guard| guard.send(k).to_sym == v.to_sym }
|
246
|
-
end
|
247
|
-
end
|
248
|
-
else
|
249
|
-
@guards
|
123
|
+
async_queue_add(relative_paths) if _relevant_changes?(relative_paths)
|
250
124
|
end
|
251
125
|
end
|
252
126
|
|
253
|
-
#
|
254
|
-
|
255
|
-
|
256
|
-
#
|
257
|
-
# @example Filter groups by String or Symbol
|
258
|
-
# Guard.groups('backend')
|
259
|
-
# Guard.groups(:backend)
|
260
|
-
#
|
261
|
-
# @example Filter groups by Regexp
|
262
|
-
# Guard.groups(/(back|front)end/)
|
263
|
-
#
|
264
|
-
# @param [String, Symbol, Regexp] filter the filter to apply to the Groups
|
265
|
-
# @return [Array<Group>] the filtered groups
|
266
|
-
#
|
267
|
-
def groups(filter = nil)
|
268
|
-
case filter
|
269
|
-
when String, Symbol
|
270
|
-
@groups.find { |group| group.name == filter.to_sym }
|
271
|
-
when Regexp
|
272
|
-
@groups.find_all { |group| group.name.to_s =~ filter }
|
273
|
-
else
|
274
|
-
@groups
|
275
|
-
end
|
127
|
+
# TODO: obsoleted? (move to Dsl?)
|
128
|
+
def _pluginless_guardfile?
|
129
|
+
state.session.plugins.all.empty?
|
276
130
|
end
|
277
131
|
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
# @param [Array<Watcher>] watchers the list of declared watchers
|
282
|
-
# @param [Array<Hash>] callbacks the list of callbacks
|
283
|
-
# @param [Hash] options the plugin options (see the given Guard documentation)
|
284
|
-
# @return [Guard::Guard] the added Guard plugin
|
285
|
-
#
|
286
|
-
def add_guard(name, watchers = [], callbacks = [], options = {})
|
287
|
-
if name.to_sym == :ego
|
288
|
-
::Guard::UI.deprecation('Guard::Ego is now part of Guard. You can remove it from your Guardfile.')
|
289
|
-
else
|
290
|
-
guard_class = get_guard_class(name)
|
291
|
-
callbacks.each { |callback| Hook.add_callback(callback[:listener], guard_class, callback[:events]) }
|
292
|
-
guard = guard_class.new(watchers, options)
|
293
|
-
@guards << guard
|
294
|
-
guard
|
295
|
-
end
|
296
|
-
end
|
132
|
+
def _evaluate(options)
|
133
|
+
evaluator = Guardfile::Evaluator.new(options)
|
134
|
+
evaluator.evaluate
|
297
135
|
|
298
|
-
|
299
|
-
#
|
300
|
-
# @param [String] name the group name
|
301
|
-
# @option options [Boolean] halt_on_fail if a task execution
|
302
|
-
# should be halted for all Guard plugins in this group if one Guard throws `:task_has_failed`
|
303
|
-
# @return [Guard::Group] the group added (or retrieved from the `@groups` variable if already present)
|
304
|
-
#
|
305
|
-
def add_group(name, options = {})
|
306
|
-
group = groups(name)
|
307
|
-
if group.nil?
|
308
|
-
group = ::Guard::Group.new(name, options)
|
309
|
-
@groups << group
|
310
|
-
end
|
311
|
-
group
|
312
|
-
end
|
136
|
+
UI.reset_and_clear
|
313
137
|
|
314
|
-
|
315
|
-
|
316
|
-
# to avoid state inconsistency.
|
317
|
-
#
|
318
|
-
# @yield the block to run
|
319
|
-
#
|
320
|
-
def within_preserved_state
|
321
|
-
lock.synchronize do
|
322
|
-
begin
|
323
|
-
interactor.stop if interactor
|
324
|
-
@result = yield
|
325
|
-
rescue Interrupt
|
326
|
-
end
|
327
|
-
|
328
|
-
interactor.start if interactor
|
329
|
-
end
|
330
|
-
@result
|
331
|
-
end
|
138
|
+
msg = "No plugins found in Guardfile, please add at least one."
|
139
|
+
UI.error msg if _pluginless_guardfile?
|
332
140
|
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
# * `dashed-guard-name` will become `Guard::DashedGuardName`
|
338
|
-
# * `underscore_guard_name` will become `Guard::UnderscoreGuardName`
|
339
|
-
#
|
340
|
-
# When no class is found with the strict case sensitive rules, another
|
341
|
-
# try is made to locate the class without matching case:
|
342
|
-
#
|
343
|
-
# * `rspec` will find a class `Guard::RSpec`
|
344
|
-
#
|
345
|
-
# @param [String] name the name of the Guard
|
346
|
-
# @param [Boolean] fail_gracefully whether error messages should not be printed
|
347
|
-
# @return [Class, nil] the loaded class
|
348
|
-
#
|
349
|
-
def get_guard_class(name, fail_gracefully=false)
|
350
|
-
name = name.to_s
|
351
|
-
try_require = false
|
352
|
-
const_name = name.gsub(/\/(.?)/) { "::#{ $1.upcase }" }.gsub(/(?:^|[_-])(.)/) { $1.upcase }
|
353
|
-
begin
|
354
|
-
require "guard/#{ name.downcase }" if try_require
|
355
|
-
self.const_get(self.constants.find { |c| c.to_s == const_name } || self.constants.find { |c| c.to_s.downcase == const_name.downcase })
|
356
|
-
rescue TypeError
|
357
|
-
unless try_require
|
358
|
-
try_require = true
|
359
|
-
retry
|
360
|
-
else
|
361
|
-
::Guard::UI.error "Could not find class Guard::#{ const_name.capitalize }"
|
362
|
-
end
|
363
|
-
rescue LoadError => loadError
|
364
|
-
unless fail_gracefully
|
365
|
-
::Guard::UI.error "Could not load 'guard/#{ name.downcase }' or find class Guard::#{ const_name.capitalize }"
|
366
|
-
::Guard::UI.error loadError.to_s
|
367
|
-
end
|
141
|
+
if evaluator.inline?
|
142
|
+
UI.info("Using inline Guardfile.")
|
143
|
+
elsif evaluator.custom?
|
144
|
+
UI.info("Using Guardfile at #{ evaluator.path }.")
|
368
145
|
end
|
146
|
+
rescue Guardfile::Evaluator::NoPluginsError => e
|
147
|
+
UI.error(e.message)
|
369
148
|
end
|
370
149
|
|
371
|
-
#
|
372
|
-
#
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
if
|
378
|
-
Gem::Specification.find_by_name("guard-#{ name }").full_gem_path
|
379
|
-
else
|
380
|
-
Gem.source_index.find_name("guard-#{ name }").last.full_gem_path
|
381
|
-
end
|
382
|
-
rescue
|
383
|
-
::Guard::UI.error "Could not find 'guard-#{ name }' gem path."
|
384
|
-
end
|
150
|
+
# TODO: remove at some point
|
151
|
+
# TODO: not tested because collides with ongoing refactoring
|
152
|
+
def _guardfile_deprecated_check(modified)
|
153
|
+
modified.map!(&:to_s)
|
154
|
+
regexp = %r{^(?:.+/)?Guardfile$}
|
155
|
+
guardfiles = modified.select { |path| regexp.match(path) }
|
156
|
+
return if guardfiles.empty?
|
385
157
|
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
#
|
390
|
-
def guard_gem_names
|
391
|
-
if Gem::Version.create(Gem::VERSION) >= Gem::Version.create('1.8.0')
|
392
|
-
Gem::Specification.find_all.select { |x| x.name =~ /^guard-/ }
|
393
|
-
else
|
394
|
-
Gem.source_index.find_name(/^guard-/)
|
395
|
-
end.map { |x| x.name.sub(/^guard-/, '') }
|
396
|
-
end
|
397
|
-
|
398
|
-
# Adds a command logger in debug mode. This wraps common command
|
399
|
-
# execution functions and logs the executed command before execution.
|
400
|
-
#
|
401
|
-
def debug_command_execution
|
402
|
-
Kernel.send(:alias_method, :original_system, :system)
|
403
|
-
Kernel.send(:define_method, :system) do |command, *args|
|
404
|
-
::Guard::UI.debug "Command execution: #{ command } #{ args.join(' ') }"
|
405
|
-
original_system command, *args
|
158
|
+
guardfile = Pathname("Guardfile").realpath
|
159
|
+
real_guardfiles = guardfiles.detect do |path|
|
160
|
+
/^Guardfile$/.match(path) || Pathname(path).expand_path == guardfile
|
406
161
|
end
|
407
162
|
|
408
|
-
|
409
|
-
Kernel.send(:define_method, :'`') do |command|
|
410
|
-
::Guard::UI.debug "Command execution: #{ command }"
|
411
|
-
original_backtick command
|
412
|
-
end
|
413
|
-
end
|
163
|
+
return unless real_guardfiles
|
414
164
|
|
415
|
-
|
416
|
-
|
417
|
-
Starting with Guard v1.1 the 'watch_all_modifications' option is removed and is now always on.
|
418
|
-
EOS
|
419
|
-
|
420
|
-
# Deprecation message for the `no_vendor` start option
|
421
|
-
NO_VENDOR_DEPRECATION = <<-EOS.gsub(/^\s*/, '')
|
422
|
-
Starting with Guard v1.1 the 'no_vendor' option is removed because the monitoring
|
423
|
-
gems are now part of a new gem called Listen. (https://github.com/guard/listen)
|
424
|
-
|
425
|
-
You can specify a custom version of any monitoring gem directly in your Gemfile
|
426
|
-
if you want to overwrite Listen's default monitoring gems.
|
427
|
-
EOS
|
428
|
-
|
429
|
-
# Displays a warning for each deprecated options used.
|
430
|
-
#
|
431
|
-
def deprecated_options_warning
|
432
|
-
::Guard::UI.deprecation(WATCH_ALL_MODIFICATIONS_DEPRECATION) if options[:watch_all_modifications]
|
433
|
-
::Guard::UI.deprecation(NO_VENDOR_DEPRECATION) if options[:no_vendor]
|
165
|
+
UI.warning "Guardfile changed -- _guard-core will exit.\n"
|
166
|
+
exit 2 # nonzero to break any while loop
|
434
167
|
end
|
435
|
-
|
436
168
|
end
|
437
169
|
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# TODO: extract to gem?
|
2
|
+
|
3
|
+
class Releaser
|
4
|
+
def initialize(options = {})
|
5
|
+
@project_name = options.delete(:project_name) do
|
6
|
+
fail "project_name is needed!"
|
7
|
+
end
|
8
|
+
|
9
|
+
@gem_name = options.delete(:gem_name) do
|
10
|
+
fail "gem_name is needed!"
|
11
|
+
end
|
12
|
+
|
13
|
+
@github_repo = options.delete(:github_repo) do
|
14
|
+
fail "github_repo is needed!"
|
15
|
+
end
|
16
|
+
|
17
|
+
@version = options.delete(:version) do
|
18
|
+
fail "version is needed!"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def full
|
23
|
+
rubygems
|
24
|
+
github
|
25
|
+
end
|
26
|
+
|
27
|
+
def rubygems
|
28
|
+
input = nil
|
29
|
+
loop do
|
30
|
+
STDOUT.puts "Release #{@project_name} #{@version} to RubyGems? (y/n)"
|
31
|
+
input = STDIN.gets.chomp.downcase
|
32
|
+
break if %w(y n).include?(input)
|
33
|
+
end
|
34
|
+
|
35
|
+
exit if input == "n"
|
36
|
+
|
37
|
+
Rake::Task["release"].invoke
|
38
|
+
end
|
39
|
+
|
40
|
+
def github
|
41
|
+
tag_name = "v#{@version}"
|
42
|
+
|
43
|
+
require "gems"
|
44
|
+
|
45
|
+
_verify_released
|
46
|
+
_verify_tag_pushed
|
47
|
+
|
48
|
+
require "octokit"
|
49
|
+
gh_client = Octokit::Client.new(netrc: true)
|
50
|
+
|
51
|
+
gh_release = _detect_gh_release(gh_client, tag_name, true)
|
52
|
+
return unless gh_release
|
53
|
+
|
54
|
+
STDOUT.puts "Draft release for #{tag_name}:\n"
|
55
|
+
STDOUT.puts gh_release.body
|
56
|
+
STDOUT.puts "\n-------------------------\n\n"
|
57
|
+
|
58
|
+
_confirm_publish
|
59
|
+
|
60
|
+
return unless _update_release(gh_client, gh_release, tag_name)
|
61
|
+
|
62
|
+
gh_release = _detect_gh_release(gh_client, tag_name, false)
|
63
|
+
|
64
|
+
_success_summary(gh_release, tag_name)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def _verify_released
|
70
|
+
if @version != Gems.info(@gem_name)["version"]
|
71
|
+
STDOUT.puts "#{@project_name} #{@version} is not yet released."
|
72
|
+
STDOUT.puts "Please release it first with: rake release:gem"
|
73
|
+
exit
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def _verify_tag_pushed
|
78
|
+
tags = `git ls-remote --tags origin`.split("\n")
|
79
|
+
return if tags.detect { |tag| tag =~ /v#{@version}$/ }
|
80
|
+
|
81
|
+
STDOUT.puts "The tag v#{@version} has not yet been pushed."
|
82
|
+
STDOUT.puts "Please push it first with: rake release:gem"
|
83
|
+
exit
|
84
|
+
end
|
85
|
+
|
86
|
+
def _success_summary(gh_release, tag_name)
|
87
|
+
href = gh_release.rels[:html].href
|
88
|
+
STDOUT.puts "GitHub release #{tag_name} has been published!"
|
89
|
+
STDOUT.puts "\nPlease enjoy and spread the word!"
|
90
|
+
STDOUT.puts "Lack of inspiration? Here's a tweet you could improve:\n\n"
|
91
|
+
STDOUT.puts "Just released #{@project_name} #{@version}! #{href}"
|
92
|
+
end
|
93
|
+
|
94
|
+
def _detect_gh_release(gh_client, tag_name, draft)
|
95
|
+
gh_releases = gh_client.releases(@github_repo)
|
96
|
+
gh_releases.detect { |r| r.tag_name == tag_name && r.draft == draft }
|
97
|
+
end
|
98
|
+
|
99
|
+
def _confirm_publish
|
100
|
+
input = nil
|
101
|
+
loop do
|
102
|
+
STDOUT.puts "Would you like to publish this GitHub release now? (y/n)"
|
103
|
+
input = STDIN.gets.chomp.downcase
|
104
|
+
break if %w(y n).include?(input)
|
105
|
+
end
|
106
|
+
|
107
|
+
exit if input == "n"
|
108
|
+
end
|
109
|
+
|
110
|
+
def _update_release(gh_client, gh_release, tag_name)
|
111
|
+
result = gh_client.update_release(gh_release.rels[:self].href, draft: false)
|
112
|
+
return true if result
|
113
|
+
STDOUT.puts "GitHub release #{tag_name} couldn't be published!"
|
114
|
+
false
|
115
|
+
end
|
116
|
+
end
|