claide-plugins 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +41 -0
- data/.rubocop.yml +4 -0
- data/.rubocop_cocoapods.yml +116 -0
- data/.tm_properties +2 -0
- data/.travis.yml +24 -0
- data/CHANGELOG.md +113 -0
- data/Gemfile +18 -0
- data/Gemfile.lock +82 -0
- data/LICENSE +21 -0
- data/README.md +44 -0
- data/Rakefile +55 -0
- data/claide-plugins.gemspec +32 -0
- data/lib/claide/command/gem_helper.rb +120 -0
- data/lib/claide/command/gem_index_cache.rb +87 -0
- data/lib/claide/command/plugins.rb +47 -0
- data/lib/claide/command/plugins/create.rb +121 -0
- data/lib/claide/command/plugins/list.rb +34 -0
- data/lib/claide/command/plugins/search.rb +60 -0
- data/lib/claide/command/plugins_config.rb +35 -0
- data/lib/claide/command/plugins_helper.rb +134 -0
- data/lib/claide/executable.rb +116 -0
- data/lib/claide_plugin.rb +1 -0
- data/lib/claide_plugins.rb +3 -0
- data/spec/command/gem_helper_spec.rb +41 -0
- data/spec/command/gem_index_cache_spec.rb +38 -0
- data/spec/command/plugins/create_spec.rb +89 -0
- data/spec/command/plugins/list_spec.rb +29 -0
- data/spec/command/plugins/search_spec.rb +55 -0
- data/spec/command/plugins_helper_spec.rb +33 -0
- data/spec/command/plugins_spec.rb +45 -0
- data/spec/fixtures/claide-foo1.gemspec +10 -0
- data/spec/fixtures/claide-foo2.gemspec +9 -0
- data/spec/fixtures/plugins.json +22 -0
- data/spec/fixtures/unprefixed.gemspec +10 -0
- data/spec/spec_helper.rb +93 -0
- metadata +165 -0
data/Rakefile
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# Bootstrap
|
2
|
+
#-----------------------------------------------------------------------------#
|
3
|
+
|
4
|
+
task :bootstrap do
|
5
|
+
if system('which bundle')
|
6
|
+
sh 'bundle install'
|
7
|
+
else
|
8
|
+
$stderr.puts "\033[0;31m" \
|
9
|
+
"[!] Please install the bundler gem manually:\n" \
|
10
|
+
' $ [sudo] gem install bundler' \
|
11
|
+
"\e[0m"
|
12
|
+
exit 1
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
begin
|
17
|
+
|
18
|
+
require 'bundler/gem_tasks'
|
19
|
+
|
20
|
+
task :default => 'spec'
|
21
|
+
|
22
|
+
# Spec
|
23
|
+
#-----------------------------------------------------------------------------#
|
24
|
+
|
25
|
+
desc 'Runs all the specs'
|
26
|
+
task :spec do
|
27
|
+
start_time = Time.now
|
28
|
+
sh "bundle exec bacon #{specs('**')}"
|
29
|
+
duration = Time.now - start_time
|
30
|
+
puts "Tests completed in #{duration}s"
|
31
|
+
Rake::Task['rubocop'].invoke
|
32
|
+
end
|
33
|
+
|
34
|
+
def specs(dir)
|
35
|
+
FileList["spec/#{dir}/*_spec.rb"].shuffle.join(' ')
|
36
|
+
end
|
37
|
+
|
38
|
+
# Rubocop
|
39
|
+
#-----------------------------------------------------------------------------#
|
40
|
+
|
41
|
+
desc 'Checks code style'
|
42
|
+
task :rubocop do
|
43
|
+
require 'rubocop'
|
44
|
+
cli = RuboCop::CLI.new
|
45
|
+
result = cli.run(FileList['{spec,lib}/**/*.rb'])
|
46
|
+
abort('RuboCop failed!') unless result == 0
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
rescue LoadError
|
51
|
+
$stderr.puts "\033[0;31m" \
|
52
|
+
'[!] Some Rake tasks haven been disabled because the environment' \
|
53
|
+
' couldn’t be loaded. Be sure to run `rake bootstrap` first.' \
|
54
|
+
"\e[0m"
|
55
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'claide_plugins.rb'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'claide-plugins'
|
8
|
+
spec.version = CLAidePlugins::VERSION
|
9
|
+
spec.authors = ['David Grandinetti', 'Olivier Halligon']
|
10
|
+
spec.summary = %q{CLAide plugin which shows info about available CLAide plugins.}
|
11
|
+
spec.description = <<-DESC
|
12
|
+
This CLAide plugin shows information about all available CLAide plugins
|
13
|
+
(yes, this is very meta!).
|
14
|
+
This plugin adds the "plugins" subcommand to a binary so that you can list
|
15
|
+
all plugins (registered in the reference JSON hosted at CocoaPods/cocoapods-plugins)
|
16
|
+
DESC
|
17
|
+
spec.homepage = 'https://github.com/cocoapods/claide-plugins'
|
18
|
+
spec.license = 'MIT'
|
19
|
+
|
20
|
+
spec.files = `git ls-files`.split($/)
|
21
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
22
|
+
spec.require_paths = ['lib']
|
23
|
+
|
24
|
+
spec.add_runtime_dependency 'nap', '~> 1.0'
|
25
|
+
spec.add_runtime_dependency 'cork', '~> 0'
|
26
|
+
spec.add_runtime_dependency 'open4', '~> 1.3'
|
27
|
+
|
28
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
29
|
+
spec.add_development_dependency 'rake'
|
30
|
+
|
31
|
+
spec.required_ruby_version = '>= 2.0.0'
|
32
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'claide/command/gem_index_cache'
|
2
|
+
|
3
|
+
module CLAide
|
4
|
+
class Command
|
5
|
+
# This module is used by Command::PluginsHelper to download the Gem
|
6
|
+
# Specification data, check if a Gem is installed, and provide info
|
7
|
+
# on all versions of a Gem.
|
8
|
+
#
|
9
|
+
module GemHelper
|
10
|
+
# A GemIndexCache to manage downloading/caching the spec index.
|
11
|
+
#
|
12
|
+
@cache = nil
|
13
|
+
|
14
|
+
# Getter for GemIndexCache
|
15
|
+
#
|
16
|
+
# @return [GemIndexCache] a new or memoized GemIndexCache
|
17
|
+
#
|
18
|
+
def self.cache
|
19
|
+
@cache ||= GemIndexCache.new
|
20
|
+
end
|
21
|
+
|
22
|
+
# Instantiate a cache and download the spec index if it has
|
23
|
+
# not already been done.
|
24
|
+
#
|
25
|
+
def self.download_and_cache_specs
|
26
|
+
cache.download_and_cache_specs
|
27
|
+
end
|
28
|
+
|
29
|
+
# Tells if a gem is installed
|
30
|
+
#
|
31
|
+
# @param [String] gem_name
|
32
|
+
# The name of the plugin gem to test
|
33
|
+
#
|
34
|
+
# @param [String] version_string
|
35
|
+
# An optional version string, used to check if a specific
|
36
|
+
# version of a gem is installed
|
37
|
+
#
|
38
|
+
# @return [Bool] true if the gem is installed, false otherwise.
|
39
|
+
#
|
40
|
+
def self.gem_installed?(gem_name, version_string = nil)
|
41
|
+
version = Gem::Version.new(version_string) if version_string
|
42
|
+
|
43
|
+
if Gem::Specification.respond_to?(:find_all_by_name)
|
44
|
+
gems = Gem::Specification.find_all_by_name(gem_name)
|
45
|
+
return !gems.empty? unless version
|
46
|
+
gems.each { |gem| return true if gem.version == version }
|
47
|
+
false
|
48
|
+
else
|
49
|
+
dep = Gem::Dependency.new(gem_name, version_string)
|
50
|
+
!Gem.source_index.search(dep).empty?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Get the version of a gem that is installed locally. If more than
|
55
|
+
# one version is installed, this returns the first version found,
|
56
|
+
# which MAY not be the highest/newest version.
|
57
|
+
#
|
58
|
+
# @return [String] The version of the gem that is installed,
|
59
|
+
# or nil if it is not installed.
|
60
|
+
#
|
61
|
+
def self.installed_version(gem_name)
|
62
|
+
if Gem::Specification.respond_to?(:find_all_by_name)
|
63
|
+
gem = Gem::Specification.find_all_by_name(gem_name).first
|
64
|
+
else
|
65
|
+
dep = Gem::Dependency.new(gem_name)
|
66
|
+
gem = Gem.source_index.search(dep).first
|
67
|
+
end
|
68
|
+
gem ? gem.version.to_s : nil
|
69
|
+
end
|
70
|
+
|
71
|
+
# Create a string containing all versions of a plugin,
|
72
|
+
# colored to indicate if a specific version is installed
|
73
|
+
# locally.
|
74
|
+
#
|
75
|
+
# @param [String] plugin_name
|
76
|
+
# The name of the plugin gem
|
77
|
+
#
|
78
|
+
# @param [GemIndexCache] index_cache
|
79
|
+
# Optional index cache can be passed in, otherwise
|
80
|
+
# the module instance is used.
|
81
|
+
#
|
82
|
+
# @return [String] a string containing a comma separated
|
83
|
+
# concatenation of all versions of a plugin
|
84
|
+
# that were found on rubygems.org
|
85
|
+
#
|
86
|
+
def self.versions_string(plugin_name, index_cache = @cache)
|
87
|
+
name_tuples = index_cache.specs_with_name(plugin_name)
|
88
|
+
sorted_versions = name_tuples.sort_by(&:version)
|
89
|
+
version_strings = colorize_versions(sorted_versions)
|
90
|
+
version_strings.join ', '
|
91
|
+
end
|
92
|
+
|
93
|
+
#----------------#
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
# Colorize an Array of version strings so versions that are installed
|
98
|
+
# are green and uninstalled versions are yellow.
|
99
|
+
#
|
100
|
+
# @param [Array] versions
|
101
|
+
# sorted array of Gem::NameTuples representing all versions of
|
102
|
+
# a plugin gem.
|
103
|
+
#
|
104
|
+
# @return [Array] An array of strings, each one being the version
|
105
|
+
# string of the same plugin
|
106
|
+
#
|
107
|
+
def self.colorize_versions(versions)
|
108
|
+
colored_strings = []
|
109
|
+
versions.reverse_each do |name_tuple|
|
110
|
+
if gem_installed?(name_tuple.name, name_tuple.version.to_s)
|
111
|
+
colored_strings << name_tuple.version.to_s.green
|
112
|
+
else
|
113
|
+
colored_strings << name_tuple.version.to_s.yellow
|
114
|
+
end
|
115
|
+
end
|
116
|
+
colored_strings
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'claide/command/gem_helper'
|
2
|
+
|
3
|
+
module CLAide
|
4
|
+
class Command
|
5
|
+
# This class is used by Command::GemsHelper to download the Gem
|
6
|
+
# Specification index from rubygems.org and provide info about
|
7
|
+
# the index.
|
8
|
+
#
|
9
|
+
class GemIndexCache
|
10
|
+
# A memoized hash of all the rubygem specs. If it is nil, the specs will
|
11
|
+
# be downloaded, which will take a few seconds to download.
|
12
|
+
#
|
13
|
+
# @return [Hash] The hash of all rubygems
|
14
|
+
#
|
15
|
+
def specs
|
16
|
+
@specs ||= download_specs
|
17
|
+
end
|
18
|
+
|
19
|
+
# Alias to make the initial caching process more readable.
|
20
|
+
#
|
21
|
+
alias_method :download_and_cache_specs, :specs
|
22
|
+
|
23
|
+
# Get an Array of Gem::NameTuple objects that match a given
|
24
|
+
# spec name.
|
25
|
+
#
|
26
|
+
# @param [String] name
|
27
|
+
# The name of the gem to match on (e.g. 'cocoapods-try')
|
28
|
+
#
|
29
|
+
# @return [Array] Array of Gem::NameTuple that match the name
|
30
|
+
#
|
31
|
+
def specs_with_name(name)
|
32
|
+
matching_specs = @specs.select do |spec|
|
33
|
+
spec[0].name == name
|
34
|
+
end
|
35
|
+
|
36
|
+
name_tuples = []
|
37
|
+
matching_specs.each do |(name_tuple, _)|
|
38
|
+
name_tuples << name_tuple
|
39
|
+
end
|
40
|
+
|
41
|
+
name_tuples
|
42
|
+
end
|
43
|
+
|
44
|
+
#----------------#
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
# Force the rubygem spec index file
|
49
|
+
#
|
50
|
+
# @return [Hash] The hash of all rubygems
|
51
|
+
#
|
52
|
+
def download_specs
|
53
|
+
UI.puts 'Downloading Rubygem specification index...'
|
54
|
+
fetcher = Gem::SpecFetcher.fetcher
|
55
|
+
results, errors = fetcher.available_specs(:released)
|
56
|
+
|
57
|
+
unless errors.empty?
|
58
|
+
UI.puts 'Error downloading Rubygem specification index: ' +
|
59
|
+
errors.first.error.to_s
|
60
|
+
return []
|
61
|
+
end
|
62
|
+
|
63
|
+
flatten_fetcher_results(results)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Flatten the dictionary returned from Gem::SpecFetcher
|
67
|
+
# to a simple array.
|
68
|
+
#
|
69
|
+
# @param [Hash] results
|
70
|
+
# the hash returned from the call to
|
71
|
+
# Gem::SpecFetcher.available_specs()
|
72
|
+
#
|
73
|
+
# @return [Array] Array of all spec results
|
74
|
+
#
|
75
|
+
def flatten_fetcher_results(results)
|
76
|
+
specs = []
|
77
|
+
results.each do |source, source_specs|
|
78
|
+
source_specs.each do |tuple|
|
79
|
+
specs << [tuple, source]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
specs
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rest'
|
2
|
+
require 'json'
|
3
|
+
require 'cork'
|
4
|
+
require 'claide'
|
5
|
+
require 'claide/command/plugins_config'
|
6
|
+
|
7
|
+
UI = Cork::Board.new
|
8
|
+
|
9
|
+
module CLAide
|
10
|
+
module Plugins
|
11
|
+
class << self
|
12
|
+
attr_accessor :config
|
13
|
+
end
|
14
|
+
# set a default configuration that will work with claide-plugins
|
15
|
+
self.config = Configuration.new
|
16
|
+
end
|
17
|
+
|
18
|
+
# Indicates a runtime error **not** caused by a bug.
|
19
|
+
#
|
20
|
+
class PlainInformative < StandardError; end
|
21
|
+
|
22
|
+
# Indicates a user error.
|
23
|
+
#
|
24
|
+
class Informative < PlainInformative; end
|
25
|
+
|
26
|
+
class Command
|
27
|
+
# The claide plugins command.
|
28
|
+
#
|
29
|
+
class Plugins < Command
|
30
|
+
require 'claide/command/plugins/list'
|
31
|
+
require 'claide/command/plugins/search'
|
32
|
+
require 'claide/command/plugins/create'
|
33
|
+
|
34
|
+
self.abstract_command = true
|
35
|
+
self.default_subcommand = 'list'
|
36
|
+
|
37
|
+
self.summary = 'Show available plugins'
|
38
|
+
self.description = <<-DESC
|
39
|
+
Lists or searches the available plugins
|
40
|
+
and show if you have them installed or not.
|
41
|
+
|
42
|
+
Also allows you to quickly create a new
|
43
|
+
plugin using a provided template.
|
44
|
+
DESC
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'claide/command/plugins_helper'
|
2
|
+
require 'claide/executable'
|
3
|
+
|
4
|
+
module CLAide
|
5
|
+
class Command
|
6
|
+
class Plugins
|
7
|
+
# The create subcommand. Used to create a new plugin using either the
|
8
|
+
# default template (CocoaPods/cocoapods-plugin-template) or a custom
|
9
|
+
# template
|
10
|
+
#
|
11
|
+
class Create < Plugins
|
12
|
+
NAME_PREFIX = 'claide-'
|
13
|
+
|
14
|
+
self.summary = 'Creates a new plugin'
|
15
|
+
self.description = <<-DESC
|
16
|
+
Creates a scaffold for the development of a new plugin
|
17
|
+
named `NAME` according to the CocoaPods best practices.
|
18
|
+
|
19
|
+
If a `TEMPLATE_URL`, pointing to a git repo containing a
|
20
|
+
compatible template, is specified, it will be used
|
21
|
+
in place of the default one.
|
22
|
+
DESC
|
23
|
+
|
24
|
+
self.arguments = [
|
25
|
+
CLAide::Argument.new('NAME', true),
|
26
|
+
CLAide::Argument.new('TEMPLATE_URL', false),
|
27
|
+
]
|
28
|
+
|
29
|
+
def initialize(argv)
|
30
|
+
@name = argv.shift_argument
|
31
|
+
unless @name.nil? || @name.empty? || @name.start_with?(NAME_PREFIX)
|
32
|
+
@name = NAME_PREFIX + @name.dup
|
33
|
+
end
|
34
|
+
@template_url = argv.shift_argument
|
35
|
+
super
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate!
|
39
|
+
super
|
40
|
+
if @name.nil? || @name.empty?
|
41
|
+
help! 'A name for the plugin is required.'
|
42
|
+
end
|
43
|
+
|
44
|
+
help! 'The plugin name cannot contain spaces.' if @name.match(/\s/)
|
45
|
+
end
|
46
|
+
|
47
|
+
def run
|
48
|
+
clone_template
|
49
|
+
configure_template
|
50
|
+
show_reminder
|
51
|
+
end
|
52
|
+
|
53
|
+
#----------------------------------------#
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
# !@group Private helpers
|
58
|
+
|
59
|
+
extend CLAide::Executable
|
60
|
+
executable :git
|
61
|
+
|
62
|
+
TEMPLATE_BASE_URL = 'https://github.com/CocoaPods/'
|
63
|
+
TEMPLATE_REPO = TEMPLATE_BASE_URL + 'cocoapods-plugin-template.git'
|
64
|
+
TEMPLATE_INFO_URL = TEMPLATE_BASE_URL + 'cocoapods-plugin-template'
|
65
|
+
|
66
|
+
# Clones the template from the remote in the working directory using
|
67
|
+
# the name of the plugin.
|
68
|
+
#
|
69
|
+
# @return [void]
|
70
|
+
#
|
71
|
+
def clone_template
|
72
|
+
UI.section("-> Creating `#{@name}` plugin") do
|
73
|
+
UI.notice "using template '#{template_repo_url}'"
|
74
|
+
command = ['clone', template_repo_url, @name]
|
75
|
+
if method(:git!).arity == -1
|
76
|
+
git! command
|
77
|
+
else
|
78
|
+
# TODO: delete this conditional and use the other branch when
|
79
|
+
# 0.5.0 is released
|
80
|
+
require 'shellwords'
|
81
|
+
git! command.map(&:to_s).map(&:shellescape).join(' ')
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Runs the template configuration utilities.
|
87
|
+
#
|
88
|
+
# @return [void]
|
89
|
+
#
|
90
|
+
def configure_template
|
91
|
+
UI.section('-> Configuring template') do
|
92
|
+
Dir.chdir(@name) do
|
93
|
+
if File.file? 'configure'
|
94
|
+
system "./configure #{@name}"
|
95
|
+
else
|
96
|
+
UI.warn 'Template does not have a configure file.'
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Checks if a template URL is given else returns the TEMPLATE_REPO URL
|
103
|
+
#
|
104
|
+
# @return String
|
105
|
+
#
|
106
|
+
def template_repo_url
|
107
|
+
@template_url || TEMPLATE_REPO
|
108
|
+
end
|
109
|
+
|
110
|
+
# Shows a reminder to the plugin author to make a Pull Request
|
111
|
+
# in order to update plugins.json once the plugin is released
|
112
|
+
#
|
113
|
+
def show_reminder
|
114
|
+
repo = PluginsHelper.plugins_raw_url
|
115
|
+
UI.notice "Don't forget to create a Pull Request on #{repo}\n" \
|
116
|
+
' to add your plugin to the plugins.json file once it is released!'
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|