-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add doctor:variables, :environment, and :gems
This adds various "doctor" tasks that can be used for troubleshooting. To see all the doctor output, run e.g. `cap production doctor`. This will print a report like this: ``` Environment Ruby ruby 2.3.0p0 (2015-12-25 revision 53290) [x86_64-darwin14] Rubygems 2.6.2 Bundler 1.11.2 Command cap production doctor Gems capistrano 3.4.0 airbrussh 1.0.1 rake 10.5.0 (update available) sshkit 1.9.0 capistrano-bundler 1.1.4 capistrano-rails 1.1.6 Variables :application "myapp" :assets_prefix "assets" :assets_roles [:web] :branch "master" ... etc. ``` To obtain the variables information in particular, code has been added to audit the setting and fetching of variables. Variables set by Capistrano itself and its plugins are whitelisted, but others are "untrusted". If a variable is untrusted and it seems like it is never used, then `doctor:variables` will print a warning (include source location) for that variable name, like this: ``` :copy_strategy is not a recognized Capistrano setting (config/deploy.rb:14) ``` Finally, the RubyGems API is used to check the remote gem repository to see if any newer versions of Capistrano gems are available (this is gracefully skipped if there is no network connection). Any outdated gems will be indicated in the `doctor:gems` output.
- Loading branch information
1 parent
66f7bae
commit b5e4aa6
Showing
17 changed files
with
640 additions
and
77 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
Feature: Doctor | ||
|
||
Background: | ||
Given a test app with the default configuration | ||
|
||
Scenario: Running the doctor task | ||
When I run cap "doctor" | ||
Then the task is successful | ||
And contains "Environment" in the output | ||
And contains "Gems" in the output | ||
And contains "Variables" in the output |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
module Capistrano | ||
class Configuration | ||
# Holds the variables assigned at Capistrano runtime via `set` and retrieved | ||
# with `fetch`. Does internal bookkeeping to help identify user mistakes | ||
# like spelling errors or unused variables that may lead to unexpected | ||
# behavior. Also allows validation rules to be registered with `validate`. | ||
class Variables | ||
CAPISTRANO_LOCATION = File.expand_path("../..", __FILE__).freeze | ||
IGNORED_LOCATIONS = [ | ||
"#{CAPISTRANO_LOCATION}/configuration/variables.rb:", | ||
"#{CAPISTRANO_LOCATION}/configuration.rb:", | ||
"#{CAPISTRANO_LOCATION}/dsl/env.rb:", | ||
"/dsl.rb:", | ||
"/forwardable.rb:" | ||
].freeze | ||
private_constant :CAPISTRANO_LOCATION, :IGNORED_LOCATIONS | ||
|
||
def initialize(values={}) | ||
@trusted_keys = [] | ||
@fetched_keys = [] | ||
@locations = {} | ||
@values = values | ||
@trusted = true | ||
end | ||
|
||
def untrusted! | ||
@trusted = false | ||
yield | ||
ensure | ||
@trusted = true | ||
end | ||
|
||
def set(key, value=nil, &block) | ||
invoke_validations(key, value, &block) | ||
@trusted_keys << key if trusted? | ||
remember_location(key) | ||
values[key] = block || value | ||
trace_set(key) | ||
values[key] | ||
end | ||
|
||
def fetch(key, default=nil, &block) | ||
fetched_keys << key | ||
peek(key, default, &block) | ||
end | ||
|
||
# Internal use only. | ||
def peek(key, default=nil, &block) | ||
value = fetch_for(key, default, &block) | ||
while callable_without_parameters?(value) | ||
value = (values[key] = value.call) | ||
end | ||
value | ||
end | ||
|
||
def fetch_for(key, default, &block) | ||
block ? values.fetch(key, &block) : values.fetch(key, default) | ||
end | ||
|
||
def delete(key) | ||
values.delete(key) | ||
end | ||
|
||
def validate(key, &validator) | ||
vs = (validators[key] || []) | ||
vs << validator | ||
validators[key] = vs | ||
end | ||
|
||
def trusted_keys | ||
@trusted_keys.dup | ||
end | ||
|
||
def untrusted_keys | ||
keys - @trusted_keys | ||
end | ||
|
||
def keys | ||
values.keys | ||
end | ||
|
||
# Keys that have been set, but which have never been fetched. | ||
def unused_keys | ||
keys - fetched_keys | ||
end | ||
|
||
# Returns an array of source file location(s) where the given key was | ||
# assigned (i.e. where `set` was called). If the key was never assigned, | ||
# returns `nil`. | ||
def source_locations(key) | ||
locations[key] | ||
end | ||
|
||
private | ||
|
||
attr_reader :locations, :values, :fetched_keys | ||
|
||
def trusted? | ||
@trusted | ||
end | ||
|
||
def remember_location(key) | ||
location = caller.find do |line| | ||
IGNORED_LOCATIONS.none? { |i| line.include?(i) } | ||
end | ||
(locations[key] ||= []) << location | ||
end | ||
|
||
def callable_without_parameters?(x) | ||
x.respond_to?(:call) && (!x.respond_to?(:arity) || x.arity == 0) | ||
end | ||
|
||
def validators | ||
@validators ||= {} | ||
end | ||
|
||
def invoke_validations(key, value, &block) | ||
unless value.nil? || block.nil? | ||
raise Capistrano::ValidationError, | ||
"Value and block both passed to Configuration#set" | ||
end | ||
|
||
return unless validators.key? key | ||
|
||
validators[key].each do |validator| | ||
validator.call(key, block || value) | ||
end | ||
end | ||
|
||
def trace_set(key) | ||
return unless fetch(:print_config_variables, false) | ||
puts "Config variable set: #{key.inspect} => #{values[key].inspect}" | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
require "capistrano/doctor/environment_doctor" | ||
require "capistrano/doctor/gems_doctor" | ||
require "capistrano/doctor/variables_doctor" | ||
|
||
load File.expand_path("../tasks/doctor.rake", __FILE__) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
require "capistrano/doctor/output_helpers" | ||
|
||
module Capistrano | ||
module Doctor | ||
class EnvironmentDoctor | ||
include Capistrano::Doctor::OutputHelpers | ||
|
||
def call | ||
title("Environment") | ||
puts <<-OUT.gsub(/^\s+/, "") | ||
Ruby #{RUBY_DESCRIPTION} | ||
Rubygems #{Gem::VERSION} | ||
Bundler #{defined?(Bundler::VERSION) ? Bundler::VERSION : 'N/A'} | ||
Command #{$PROGRAM_NAME} #{ARGV.join(' ')} | ||
OUT | ||
end | ||
end | ||
end | ||
end |
Oops, something went wrong.