pantograph 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (202) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +1 -0
  3. data/LICENSE +21 -0
  4. data/README.md +197 -0
  5. data/bin/bin-proxy +19 -0
  6. data/bin/pantograph +23 -0
  7. data/pantograph/README.md +11 -0
  8. data/pantograph/lib/assets/ActionDetails.md.erb +106 -0
  9. data/pantograph/lib/assets/Actions.md.erb +43 -0
  10. data/pantograph/lib/assets/DefaultPantfileTemplate +20 -0
  11. data/pantograph/lib/assets/completions/completion.bash +23 -0
  12. data/pantograph/lib/assets/completions/completion.fish +39 -0
  13. data/pantograph/lib/assets/completions/completion.sh +12 -0
  14. data/pantograph/lib/assets/completions/completion.zsh +23 -0
  15. data/pantograph/lib/assets/custom_action_template.rb +80 -0
  16. data/pantograph/lib/assets/report_template.xml.erb +15 -0
  17. data/pantograph/lib/pantograph/action.rb +194 -0
  18. data/pantograph/lib/pantograph/action_collector.rb +35 -0
  19. data/pantograph/lib/pantograph/actions/README.md +3 -0
  20. data/pantograph/lib/pantograph/actions/actions_helper.rb +166 -0
  21. data/pantograph/lib/pantograph/actions/add_extra_platforms.rb +45 -0
  22. data/pantograph/lib/pantograph/actions/artifactory.rb +157 -0
  23. data/pantograph/lib/pantograph/actions/bundle_install.rb +156 -0
  24. data/pantograph/lib/pantograph/actions/changelog_from_git_commits.rb +197 -0
  25. data/pantograph/lib/pantograph/actions/clipboard.rb +52 -0
  26. data/pantograph/lib/pantograph/actions/cloc.rb +89 -0
  27. data/pantograph/lib/pantograph/actions/create_pull_request.rb +190 -0
  28. data/pantograph/lib/pantograph/actions/danger.rb +131 -0
  29. data/pantograph/lib/pantograph/actions/debug.rb +32 -0
  30. data/pantograph/lib/pantograph/actions/default_platform.rb +47 -0
  31. data/pantograph/lib/pantograph/actions/download.rb +76 -0
  32. data/pantograph/lib/pantograph/actions/echo.rb +14 -0
  33. data/pantograph/lib/pantograph/actions/ensure_bundle_exec.rb +59 -0
  34. data/pantograph/lib/pantograph/actions/ensure_env_vars.rb +58 -0
  35. data/pantograph/lib/pantograph/actions/ensure_git_branch.rb +69 -0
  36. data/pantograph/lib/pantograph/actions/ensure_git_status_clean.rb +81 -0
  37. data/pantograph/lib/pantograph/actions/erb.rb +88 -0
  38. data/pantograph/lib/pantograph/actions/get_build_number_repository.rb +120 -0
  39. data/pantograph/lib/pantograph/actions/get_github_release.rb +163 -0
  40. data/pantograph/lib/pantograph/actions/git_add.rb +93 -0
  41. data/pantograph/lib/pantograph/actions/git_branch.rb +58 -0
  42. data/pantograph/lib/pantograph/actions/git_commit.rb +80 -0
  43. data/pantograph/lib/pantograph/actions/git_pull.rb +53 -0
  44. data/pantograph/lib/pantograph/actions/git_submodule_update.rb +52 -0
  45. data/pantograph/lib/pantograph/actions/git_tag_exists.rb +74 -0
  46. data/pantograph/lib/pantograph/actions/github_api.rb +262 -0
  47. data/pantograph/lib/pantograph/actions/gradle.rb +278 -0
  48. data/pantograph/lib/pantograph/actions/import.rb +49 -0
  49. data/pantograph/lib/pantograph/actions/import_from_git.rb +71 -0
  50. data/pantograph/lib/pantograph/actions/is_ci.rb +51 -0
  51. data/pantograph/lib/pantograph/actions/jira.rb +115 -0
  52. data/pantograph/lib/pantograph/actions/lane_context.rb +60 -0
  53. data/pantograph/lib/pantograph/actions/last_git_commit.rb +58 -0
  54. data/pantograph/lib/pantograph/actions/last_git_tag.rb +51 -0
  55. data/pantograph/lib/pantograph/actions/make_changelog_from_jenkins.rb +81 -0
  56. data/pantograph/lib/pantograph/actions/min_pantograph_version.rb +57 -0
  57. data/pantograph/lib/pantograph/actions/nexus_upload.rb +230 -0
  58. data/pantograph/lib/pantograph/actions/notification.rb +75 -0
  59. data/pantograph/lib/pantograph/actions/number_of_commits.rb +75 -0
  60. data/pantograph/lib/pantograph/actions/opt_out_usage.rb +40 -0
  61. data/pantograph/lib/pantograph/actions/pantograph_version.rb +15 -0
  62. data/pantograph/lib/pantograph/actions/println.rb +14 -0
  63. data/pantograph/lib/pantograph/actions/prompt.rb +119 -0
  64. data/pantograph/lib/pantograph/actions/push_git_tags.rb +76 -0
  65. data/pantograph/lib/pantograph/actions/push_to_git_remote.rb +127 -0
  66. data/pantograph/lib/pantograph/actions/puts.rb +68 -0
  67. data/pantograph/lib/pantograph/actions/reset_git_repo.rb +121 -0
  68. data/pantograph/lib/pantograph/actions/rocket.rb +83 -0
  69. data/pantograph/lib/pantograph/actions/rsync.rb +74 -0
  70. data/pantograph/lib/pantograph/actions/ruby_version.rb +56 -0
  71. data/pantograph/lib/pantograph/actions/say.rb +56 -0
  72. data/pantograph/lib/pantograph/actions/scp.rb +114 -0
  73. data/pantograph/lib/pantograph/actions/set_github_release.rb +274 -0
  74. data/pantograph/lib/pantograph/actions/sh.rb +71 -0
  75. data/pantograph/lib/pantograph/actions/skip_docs.rb +52 -0
  76. data/pantograph/lib/pantograph/actions/slack.rb +288 -0
  77. data/pantograph/lib/pantograph/actions/sonar.rb +156 -0
  78. data/pantograph/lib/pantograph/actions/ssh.rb +162 -0
  79. data/pantograph/lib/pantograph/actions/twitter.rb +89 -0
  80. data/pantograph/lib/pantograph/actions/update_pantograph.rb +177 -0
  81. data/pantograph/lib/pantograph/actions/zip.rb +120 -0
  82. data/pantograph/lib/pantograph/auto_complete.rb +82 -0
  83. data/pantograph/lib/pantograph/boolean.rb +5 -0
  84. data/pantograph/lib/pantograph/cli_tools_distributor.rb +183 -0
  85. data/pantograph/lib/pantograph/command_line_handler.rb +43 -0
  86. data/pantograph/lib/pantograph/commands_generator.rb +344 -0
  87. data/pantograph/lib/pantograph/configuration_helper.rb +26 -0
  88. data/pantograph/lib/pantograph/core_ext/bundler_monkey_patch.rb +14 -0
  89. data/pantograph/lib/pantograph/documentation/actions_list.rb +214 -0
  90. data/pantograph/lib/pantograph/documentation/docs_generator.rb +95 -0
  91. data/pantograph/lib/pantograph/documentation/markdown_docs_generator.rb +221 -0
  92. data/pantograph/lib/pantograph/environment_printer.rb +282 -0
  93. data/pantograph/lib/pantograph/erb_template_helper.rb +30 -0
  94. data/pantograph/lib/pantograph/features.rb +4 -0
  95. data/pantograph/lib/pantograph/helper/README.md +29 -0
  96. data/pantograph/lib/pantograph/helper/dotenv_helper.rb +50 -0
  97. data/pantograph/lib/pantograph/helper/gem_helper.rb +26 -0
  98. data/pantograph/lib/pantograph/helper/git_helper.rb +135 -0
  99. data/pantograph/lib/pantograph/helper/gradle_helper.rb +62 -0
  100. data/pantograph/lib/pantograph/helper/sh_helper.rb +134 -0
  101. data/pantograph/lib/pantograph/junit_generator.rb +27 -0
  102. data/pantograph/lib/pantograph/lane.rb +97 -0
  103. data/pantograph/lib/pantograph/lane_list.rb +77 -0
  104. data/pantograph/lib/pantograph/lane_manager.rb +140 -0
  105. data/pantograph/lib/pantograph/lane_manager_base.rb +92 -0
  106. data/pantograph/lib/pantograph/markdown_table_formatter.rb +62 -0
  107. data/pantograph/lib/pantograph/new_action.rb +47 -0
  108. data/pantograph/lib/pantograph/one_off.rb +45 -0
  109. data/pantograph/lib/pantograph/other_action.rb +29 -0
  110. data/pantograph/lib/pantograph/pant_file.rb +377 -0
  111. data/pantograph/lib/pantograph/pantograph_require.rb +75 -0
  112. data/pantograph/lib/pantograph/plugins/plugin_fetcher.rb +55 -0
  113. data/pantograph/lib/pantograph/plugins/plugin_generator.rb +86 -0
  114. data/pantograph/lib/pantograph/plugins/plugin_generator_ui.rb +19 -0
  115. data/pantograph/lib/pantograph/plugins/plugin_info.rb +49 -0
  116. data/pantograph/lib/pantograph/plugins/plugin_info_collector.rb +159 -0
  117. data/pantograph/lib/pantograph/plugins/plugin_manager.rb +387 -0
  118. data/pantograph/lib/pantograph/plugins/plugin_search.rb +46 -0
  119. data/pantograph/lib/pantograph/plugins/plugin_update_manager.rb +70 -0
  120. data/pantograph/lib/pantograph/plugins/plugins.rb +12 -0
  121. data/pantograph/lib/pantograph/plugins/template/%gem_name%.gemspec.erb +35 -0
  122. data/pantograph/lib/pantograph/plugins/template/.circleci/config.yml +43 -0
  123. data/pantograph/lib/pantograph/plugins/template/.gitignore +12 -0
  124. data/pantograph/lib/pantograph/plugins/template/.rspec +5 -0
  125. data/pantograph/lib/pantograph/plugins/template/.rubocop.yml +179 -0
  126. data/pantograph/lib/pantograph/plugins/template/.travis.yml +4 -0
  127. data/pantograph/lib/pantograph/plugins/template/Gemfile +6 -0
  128. data/pantograph/lib/pantograph/plugins/template/LICENSE.erb +21 -0
  129. data/pantograph/lib/pantograph/plugins/template/README.md.erb +52 -0
  130. data/pantograph/lib/pantograph/plugins/template/Rakefile +9 -0
  131. data/pantograph/lib/pantograph/plugins/template/lib/pantograph/plugin/%plugin_name%/actions/%plugin_name%_action.rb.erb +47 -0
  132. data/pantograph/lib/pantograph/plugins/template/lib/pantograph/plugin/%plugin_name%/helper/%plugin_name%_helper.rb.erb +16 -0
  133. data/pantograph/lib/pantograph/plugins/template/lib/pantograph/plugin/%plugin_name%/version.rb.erb +5 -0
  134. data/pantograph/lib/pantograph/plugins/template/lib/pantograph/plugin/%plugin_name%.rb.erb +16 -0
  135. data/pantograph/lib/pantograph/plugins/template/pantograph/Pantfile.erb +3 -0
  136. data/pantograph/lib/pantograph/plugins/template/pantograph/Pluginfile.erb +1 -0
  137. data/pantograph/lib/pantograph/plugins/template/spec/%plugin_name%_action_spec.rb.erb +9 -0
  138. data/pantograph/lib/pantograph/plugins/template/spec/spec_helper.rb.erb +15 -0
  139. data/pantograph/lib/pantograph/runner.rb +371 -0
  140. data/pantograph/lib/pantograph/server/action_command.rb +61 -0
  141. data/pantograph/lib/pantograph/server/action_command_return.rb +14 -0
  142. data/pantograph/lib/pantograph/server/command_executor.rb +7 -0
  143. data/pantograph/lib/pantograph/server/command_parser.rb +36 -0
  144. data/pantograph/lib/pantograph/server/control_command.rb +23 -0
  145. data/pantograph/lib/pantograph/server/json_return_value_processor.rb +72 -0
  146. data/pantograph/lib/pantograph/server/socket_server.rb +232 -0
  147. data/pantograph/lib/pantograph/server/socket_server_action_command_executor.rb +101 -0
  148. data/pantograph/lib/pantograph/setup/setup.rb +290 -0
  149. data/pantograph/lib/pantograph/setup/setup_android.rb +64 -0
  150. data/pantograph/lib/pantograph/setup/setup_ios.rb +412 -0
  151. data/pantograph/lib/pantograph/shells.rb +6 -0
  152. data/pantograph/lib/pantograph/supported_platforms.rb +28 -0
  153. data/pantograph/lib/pantograph/tools.rb +10 -0
  154. data/pantograph/lib/pantograph/version.rb +5 -0
  155. data/pantograph/lib/pantograph.rb +51 -0
  156. data/pantograph_core/README.md +79 -0
  157. data/pantograph_core/lib/assets/XMLTemplate.xml.erb +12 -0
  158. data/pantograph_core/lib/pantograph_core/analytics/action_completion_context.rb +34 -0
  159. data/pantograph_core/lib/pantograph_core/analytics/action_launch_context.rb +38 -0
  160. data/pantograph_core/lib/pantograph_core/analytics/analytics_event_builder.rb +23 -0
  161. data/pantograph_core/lib/pantograph_core/analytics/analytics_ingester_client.rb +54 -0
  162. data/pantograph_core/lib/pantograph_core/analytics/analytics_session.rb +71 -0
  163. data/pantograph_core/lib/pantograph_core/cert_checker.rb +116 -0
  164. data/pantograph_core/lib/pantograph_core/command_executor.rb +99 -0
  165. data/pantograph_core/lib/pantograph_core/configuration/commander_generator.rb +103 -0
  166. data/pantograph_core/lib/pantograph_core/configuration/config_item.rb +314 -0
  167. data/pantograph_core/lib/pantograph_core/configuration/configuration.rb +332 -0
  168. data/pantograph_core/lib/pantograph_core/configuration/configuration_file.rb +182 -0
  169. data/pantograph_core/lib/pantograph_core/core_ext/shellwords.rb +63 -0
  170. data/pantograph_core/lib/pantograph_core/core_ext/string.rb +17 -0
  171. data/pantograph_core/lib/pantograph_core/env.rb +9 -0
  172. data/pantograph_core/lib/pantograph_core/feature/feature.rb +51 -0
  173. data/pantograph_core/lib/pantograph_core/features.rb +4 -0
  174. data/pantograph_core/lib/pantograph_core/globals.rb +27 -0
  175. data/pantograph_core/lib/pantograph_core/helper.rb +409 -0
  176. data/pantograph_core/lib/pantograph_core/keychain_importer.rb +74 -0
  177. data/pantograph_core/lib/pantograph_core/languages.rb +14 -0
  178. data/pantograph_core/lib/pantograph_core/module.rb +29 -0
  179. data/pantograph_core/lib/pantograph_core/pantograph_folder.rb +39 -0
  180. data/pantograph_core/lib/pantograph_core/pantograph_pty.rb +57 -0
  181. data/pantograph_core/lib/pantograph_core/pkg_file_analyser.rb +44 -0
  182. data/pantograph_core/lib/pantograph_core/print_table.rb +131 -0
  183. data/pantograph_core/lib/pantograph_core/string_filters.rb +51 -0
  184. data/pantograph_core/lib/pantograph_core/swag.rb +85 -0
  185. data/pantograph_core/lib/pantograph_core/tag_version.rb +31 -0
  186. data/pantograph_core/lib/pantograph_core/test_parser.rb +107 -0
  187. data/pantograph_core/lib/pantograph_core/ui/disable_colors.rb +17 -0
  188. data/pantograph_core/lib/pantograph_core/ui/errors/pantograph_common_error.rb +19 -0
  189. data/pantograph_core/lib/pantograph_core/ui/errors/pantograph_crash.rb +11 -0
  190. data/pantograph_core/lib/pantograph_core/ui/errors/pantograph_error.rb +25 -0
  191. data/pantograph_core/lib/pantograph_core/ui/errors/pantograph_exception.rb +19 -0
  192. data/pantograph_core/lib/pantograph_core/ui/errors/pantograph_shell_error.rb +11 -0
  193. data/pantograph_core/lib/pantograph_core/ui/errors.rb +1 -0
  194. data/pantograph_core/lib/pantograph_core/ui/github_issue_inspector_reporter.rb +62 -0
  195. data/pantograph_core/lib/pantograph_core/ui/implementations/shell.rb +159 -0
  196. data/pantograph_core/lib/pantograph_core/ui/interface.rb +205 -0
  197. data/pantograph_core/lib/pantograph_core/ui/pantograph_runner.rb +276 -0
  198. data/pantograph_core/lib/pantograph_core/ui/ui.rb +26 -0
  199. data/pantograph_core/lib/pantograph_core/update_checker/changelog.rb +37 -0
  200. data/pantograph_core/lib/pantograph_core/update_checker/update_checker.rb +107 -0
  201. data/pantograph_core/lib/pantograph_core.rb +45 -0
  202. metadata +987 -0
@@ -0,0 +1,159 @@
1
+ module Pantograph
2
+ class PluginInfoCollector
3
+ def initialize(ui = PluginGeneratorUI.new)
4
+ @ui = ui
5
+ end
6
+
7
+ def collect_info(initial_name = nil)
8
+ plugin_name = collect_plugin_name(initial_name)
9
+ author = collect_author(detect_author)
10
+ email = collect_email(detect_email)
11
+ summary = collect_summary
12
+ details = collect_details
13
+
14
+ PluginInfo.new(plugin_name, author, email, summary, details)
15
+ end
16
+
17
+ #
18
+ # Plugin name
19
+ #
20
+
21
+ def collect_plugin_name(initial_name = nil)
22
+ plugin_name = initial_name
23
+ first_try = true
24
+
25
+ loop do
26
+ if !first_try || plugin_name.to_s.empty?
27
+ plugin_name = @ui.input("What would you like to be the name of your plugin?")
28
+ end
29
+ first_try = false
30
+
31
+ unless plugin_name_valid?(plugin_name)
32
+ fixed_name = fix_plugin_name(plugin_name)
33
+
34
+ if plugin_name_valid?(fixed_name)
35
+ plugin_name = fixed_name if @ui.confirm("\nWould '#{fixed_name}' be okay to use for your plugin name?")
36
+ end
37
+ end
38
+
39
+ break if plugin_name_valid?(plugin_name)
40
+
41
+ gem_name = PluginManager.to_gem_name(plugin_name)
42
+
43
+ if gem_name_taken?(gem_name)
44
+ # Gem name is already taken on RubyGems
45
+ @ui.message("\nThe gem name '#{gem_name}' is already taken on RubyGems, please choose a different plugin name.")
46
+ else
47
+ # That's a naming error
48
+ @ui.message("\nPlugin names can only contain lower case letters, numbers, and underscores")
49
+ @ui.message("and should not contain 'pantograph' or 'plugin'.")
50
+ end
51
+ end
52
+
53
+ plugin_name
54
+ end
55
+
56
+ def plugin_name_valid?(name)
57
+ # Only lower case letters, numbers and underscores allowed
58
+ /^[a-z0-9_]+$/ =~ name &&
59
+ # Does not contain the words 'pantograph' or 'plugin' since those will become
60
+ # part of the gem name
61
+ [/pantograph/, /plugin/].none? { |regex| regex =~ name } &&
62
+ # Gem name isn't taken on RubyGems yet
63
+ !gem_name_taken?(PluginManager.to_gem_name(name))
64
+ end
65
+
66
+ # Checks if the gem name is still free on RubyGems
67
+ def gem_name_taken?(name)
68
+ require 'open-uri'
69
+ require 'json'
70
+ url = "https://rubygems.org/api/v1/gems/#{name}.json"
71
+ response = JSON.parse(open(url).read)
72
+ return !!response['version']
73
+ rescue
74
+ false
75
+ end
76
+
77
+ # Applies a series of replacement rules to turn the requested plugin name into one
78
+ # that is acceptable, returning that suggestion
79
+ def fix_plugin_name(name)
80
+ name = name.to_s.downcase
81
+ fixes = {
82
+ /[\- ]/ => '_', # dashes and spaces become underscores
83
+ /[^a-z0-9_]/ => '', # anything other than lower case letters, numbers and underscores is removed
84
+ /pantograph[_]?/ => '', # 'pantograph' or 'pantograph_' is removed
85
+ /plugin[_]?/ => '' # 'plugin' or 'plugin_' is removed
86
+ }
87
+ fixes.each do |regex, replacement|
88
+ name = name.gsub(regex, replacement)
89
+ end
90
+ name
91
+ end
92
+
93
+ #
94
+ # Author
95
+ #
96
+
97
+ def detect_author
98
+ git_name = Helper.backticks('git config --get user.name', print: PantographCore::Globals.verbose?).strip
99
+ return git_name.empty? ? nil : git_name
100
+ end
101
+
102
+ def collect_author(initial_author = nil)
103
+ return initial_author if author_valid?(initial_author)
104
+ author = nil
105
+ loop do
106
+ author = @ui.input("What is the plugin author's name?")
107
+ break if author_valid?(author)
108
+
109
+ @ui.message('An author name is required.')
110
+ end
111
+
112
+ author
113
+ end
114
+
115
+ def author_valid?(author)
116
+ !author.to_s.strip.empty?
117
+ end
118
+
119
+ #
120
+ # Email
121
+ #
122
+
123
+ def detect_email
124
+ git_email = Helper.backticks('git config --get user.email', print: PantographCore::Globals.verbose?).strip
125
+ return git_email.empty? ? nil : git_email
126
+ end
127
+
128
+ def collect_email(initial_email = nil)
129
+ return initial_email || @ui.input("What is the plugin author's email address?")
130
+ end
131
+
132
+ #
133
+ # Summary
134
+ #
135
+
136
+ def collect_summary
137
+ summary = nil
138
+ loop do
139
+ summary = @ui.input("Please enter a short summary of this pantograph plugin:")
140
+ break if summary_valid?(summary)
141
+
142
+ @ui.message('A summary is required.')
143
+ end
144
+
145
+ summary
146
+ end
147
+
148
+ def summary_valid?(summary)
149
+ !summary.to_s.strip.empty?
150
+ end
151
+ #
152
+ # Summary
153
+ #
154
+
155
+ def collect_details
156
+ return @ui.input("Please enter a detailed description of this pantograph plugin:").to_s
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,387 @@
1
+ require "pantograph/cli_tools_distributor"
2
+
3
+ module Pantograph
4
+ class PluginManager
5
+ require "bundler"
6
+
7
+ PLUGINFILE_NAME = "Pluginfile".freeze
8
+ DEFAULT_GEMFILE_PATH = "Gemfile".freeze
9
+ AUTOGENERATED_LINE = "# Autogenerated by pantograph\n#\n# Ensure this file is checked in to source control!\n\n"
10
+ GEMFILE_SOURCE_LINE = "source \"https://rubygems.org\"\n"
11
+ PANTOGRAPH_PLUGIN_PREFIX = "pantograph-plugin-"
12
+ TROUBLESHOOTING_URL = "https://docs.pantograph.tools/plugins/plugins-troubleshooting/"
13
+
14
+ #####################################################
15
+ # @!group Reading the files and their paths
16
+ #####################################################
17
+
18
+ def gemfile_path
19
+ # This is pretty important, since we don't know what kind of
20
+ # Gemfile the user has (e.g. Gemfile, gems.rb, or custom env variable)
21
+ Bundler::SharedHelpers.default_gemfile.to_s
22
+ rescue Bundler::GemfileNotFound
23
+ nil
24
+ end
25
+
26
+ def pluginfile_path
27
+ if PantographCore::PantographFolder.path
28
+ return File.join(PantographCore::PantographFolder.path, PLUGINFILE_NAME)
29
+ else
30
+ return nil
31
+ end
32
+ end
33
+
34
+ def gemfile_content
35
+ File.read(gemfile_path) if gemfile_path && File.exist?(gemfile_path)
36
+ end
37
+
38
+ def pluginfile_content
39
+ File.read(pluginfile_path) if pluginfile_path && File.exist?(pluginfile_path)
40
+ end
41
+
42
+ #####################################################
43
+ # @!group Helpers
44
+ #####################################################
45
+
46
+ def self.plugin_prefix
47
+ PANTOGRAPH_PLUGIN_PREFIX
48
+ end
49
+
50
+ def self.to_gem_name(plugin_name)
51
+ plugin_name.start_with?(plugin_prefix) ? plugin_name : (plugin_prefix + plugin_name)
52
+ end
53
+
54
+ # Returns an array of gems that are added to the Gemfile or Pluginfile
55
+ def available_gems
56
+ return [] unless gemfile_path
57
+ dsl = Bundler::Dsl.evaluate(gemfile_path, nil, true)
58
+ return dsl.dependencies.map(&:name)
59
+ end
60
+
61
+ # Returns an array of pantograph plugins that are added to the Gemfile or Pluginfile
62
+ # The returned array contains the string with their prefixes (e.g. pantograph-plugin-xcversion)
63
+ def available_plugins
64
+ available_gems.keep_if do |current|
65
+ current.start_with?(self.class.plugin_prefix)
66
+ end
67
+ end
68
+
69
+ # Check if a plugin is added as dependency to either the
70
+ # Gemfile or the Pluginfile
71
+ def plugin_is_added_as_dependency?(plugin_name)
72
+ UI.user_error!("pantograph plugins must start with '#{self.class.plugin_prefix}' string") unless plugin_name.start_with?(self.class.plugin_prefix)
73
+ return available_plugins.include?(plugin_name)
74
+ end
75
+
76
+ #####################################################
77
+ # @!group Modifying dependencies
78
+ #####################################################
79
+
80
+ def add_dependency(plugin_name)
81
+ UI.user_error!("pantograph is not setup for this project, make sure you have a pantograph folder") unless pluginfile_path
82
+ plugin_name = self.class.plugin_prefix + plugin_name unless plugin_name.start_with?(self.class.plugin_prefix)
83
+
84
+ if plugin_name.gsub(self.class.plugin_prefix, '').include?("-")
85
+ # e.g. "pantograph-plugin-ya_tu-sabes" (which is invalid)
86
+ UI.user_error!("Plugin name must not contain a '-', did you mean '_'?")
87
+ end
88
+
89
+ unless plugin_is_added_as_dependency?(plugin_name)
90
+ content = pluginfile_content || AUTOGENERATED_LINE
91
+
92
+ unless content.end_with?("\n")
93
+ content += "\n"
94
+ end
95
+
96
+ line_to_add = "gem '#{plugin_name}'"
97
+ line_to_add += gem_dependency_suffix(plugin_name)
98
+ UI.verbose("Adding line: #{line_to_add}")
99
+
100
+ content += "#{line_to_add}\n"
101
+ File.write(pluginfile_path, content)
102
+ UI.success("Plugin '#{plugin_name}' was added to '#{pluginfile_path}'")
103
+ end
104
+
105
+ # We do this *after* creating the Plugin file
106
+ # Since `bundle exec` would be broken if something fails on the way
107
+ ensure_plugins_attached!
108
+
109
+ true
110
+ end
111
+
112
+ # Get a suffix (e.g. `path` or `git` for the gem dependency)
113
+ def gem_dependency_suffix(plugin_name)
114
+ return "" unless self.class.fetch_gem_info_from_rubygems(plugin_name).nil?
115
+
116
+ selection_git_url = "Git URL"
117
+ selection_path = "Local Path"
118
+ selection_rubygems = "RubyGems.org ('#{plugin_name}' seems to not be available there)"
119
+ selection_gem_server = "Other Gem Server"
120
+ selection = UI.select(
121
+ "Seems like the plugin is not available on RubyGems, what do you want to do?",
122
+ [selection_git_url, selection_path, selection_rubygems, selection_gem_server]
123
+ )
124
+
125
+ if selection == selection_git_url
126
+ git_url = UI.input('Please enter the URL to the plugin, including the protocol (e.g. https:// or git://)')
127
+ return ", git: '#{git_url}'"
128
+ elsif selection == selection_path
129
+ path = UI.input('Please enter the relative path to the plugin you want to use. It has to point to the directory containing the .gemspec file')
130
+ return ", path: '#{path}'"
131
+ elsif selection == selection_rubygems
132
+ return ""
133
+ elsif selection == selection_gem_server
134
+ source_url = UI.input('Please enter the gem source URL which hosts the plugin you want to use, including the protocol (e.g. https:// or git://)')
135
+ return ", source: '#{source_url}'"
136
+ else
137
+ UI.user_error!("Unknown input #{selection}")
138
+ end
139
+ end
140
+
141
+ # Modify the user's Gemfile to load the plugins
142
+ def attach_plugins_to_gemfile!(path_to_gemfile)
143
+ content = gemfile_content || (AUTOGENERATED_LINE + GEMFILE_SOURCE_LINE)
144
+
145
+ # We have to make sure pantograph is also added to the Gemfile, since we now use
146
+ # bundler to run pantograph
147
+ content += "\ngem 'pantograph'\n" unless available_gems.include?('pantograph')
148
+ content += "\n#{self.class.code_to_attach}\n"
149
+
150
+ File.write(path_to_gemfile, content)
151
+ end
152
+
153
+ #####################################################
154
+ # @!group Accessing RubyGems
155
+ #####################################################
156
+
157
+ def self.fetch_gem_info_from_rubygems(gem_name)
158
+ require 'open-uri'
159
+ require 'json'
160
+ url = "https://rubygems.org/api/v1/gems/#{gem_name}.json"
161
+ begin
162
+ JSON.parse(open(url).read)
163
+ rescue
164
+ nil
165
+ end
166
+ end
167
+
168
+ #####################################################
169
+ # @!group Installing and updating dependencies
170
+ #####################################################
171
+
172
+ # Warning: This will exec out
173
+ # This is necessary since the user might be prompted for their password
174
+ def install_dependencies!
175
+ # Using puts instead of `UI` to have the same style as the `echo`
176
+ puts("Installing plugin dependencies...")
177
+ ensure_plugins_attached!
178
+ with_clean_bundler_env do
179
+ cmd = "bundle install"
180
+ cmd << " --quiet" unless PantographCore::Globals.verbose?
181
+ cmd << " && echo 'Successfully installed plugins'"
182
+ UI.command(cmd) if PantographCore::Globals.verbose?
183
+ exec(cmd)
184
+ end
185
+ end
186
+
187
+ # Warning: This will exec out
188
+ # This is necessary since the user might be prompted for their password
189
+ def update_dependencies!
190
+ puts("Updating plugin dependencies...")
191
+ ensure_plugins_attached!
192
+ plugins = available_plugins
193
+ if plugins.empty?
194
+ UI.user_error!("No plugins are installed")
195
+ end
196
+ with_clean_bundler_env do
197
+ cmd = "bundle update"
198
+ cmd << " #{plugins.join(' ')}"
199
+ cmd << " --quiet" unless PantographCore::Globals.verbose?
200
+ cmd << " && echo 'Successfully updated plugins'"
201
+ UI.command(cmd) if PantographCore::Globals.verbose?
202
+ exec(cmd)
203
+ end
204
+ end
205
+
206
+ def with_clean_bundler_env
207
+ # There is an interesting problem with using exec to call back into Bundler
208
+ # The `bundle ________` command that we exec, inherits all of the Bundler
209
+ # state we'd already built up during this run. That was causing the command
210
+ # to fail, telling us to install the Gem we'd just introduced, even though
211
+ # that is exactly what we are trying to do!
212
+ #
213
+ # Bundler.with_clean_env solves this problem by resetting Bundler state before the
214
+ # exec'd call gets merged into this process.
215
+
216
+ Bundler.with_clean_env do
217
+ yield if block_given?
218
+ end
219
+ end
220
+
221
+ #####################################################
222
+ # @!group Initial setup
223
+ #####################################################
224
+
225
+ def setup
226
+ UI.important("It looks like pantograph plugins are not yet set up for this project.")
227
+
228
+ path_to_gemfile = gemfile_path || DEFAULT_GEMFILE_PATH
229
+
230
+ if gemfile_content.to_s.length > 0
231
+ UI.important("pantograph will modify your existing Gemfile at path '#{path_to_gemfile}'")
232
+ else
233
+ UI.important("pantograph will create a new Gemfile at path '#{path_to_gemfile}'")
234
+ end
235
+
236
+ UI.important("This change is necessary for pantograph plugins to work")
237
+
238
+ unless UI.confirm("Should pantograph modify the Gemfile at path '#{path_to_gemfile}' for you?")
239
+ UI.important("Please add the following code to '#{path_to_gemfile}':")
240
+ puts("")
241
+ puts(self.class.code_to_attach.magenta) # we use `puts` instead of `UI` to make it easier to copy and paste
242
+ UI.user_error!("Please update '#{path_to_gemfile} and run pantograph again")
243
+ end
244
+
245
+ attach_plugins_to_gemfile!(path_to_gemfile)
246
+ UI.success("Successfully modified '#{path_to_gemfile}'")
247
+ end
248
+
249
+ # The code required to load the Plugins file
250
+ def self.code_to_attach
251
+ if PantographCore::PantographFolder.path
252
+ pantograph_folder_name = File.basename(PantographCore::PantographFolder.path)
253
+ else
254
+ pantograph_folder_name = "pantograph"
255
+ end
256
+ "plugins_path = File.join(File.dirname(__FILE__), '#{pantograph_folder_name}', '#{PluginManager::PLUGINFILE_NAME}')\n" \
257
+ "eval_gemfile(plugins_path) if File.exist?(plugins_path)"
258
+ end
259
+
260
+ # Makes sure, the user's Gemfile actually loads the Plugins file
261
+ def plugins_attached?
262
+ gemfile_path && gemfile_content.include?(PluginManager::PLUGINFILE_NAME)
263
+ end
264
+
265
+ def ensure_plugins_attached!
266
+ return if plugins_attached?
267
+ self.setup
268
+ end
269
+
270
+ #####################################################
271
+ # @!group Requiring the plugins
272
+ #####################################################
273
+
274
+ # Iterate over all available plugins
275
+ # which follow the naming convention
276
+ # pantograph-plugin-[plugin_name]
277
+ # This will make sure to load the action
278
+ # and all its helpers
279
+ def load_plugins
280
+ UI.verbose("Checking if there are any plugins that should be loaded...")
281
+
282
+ loaded_plugins = false
283
+ available_plugins.each do |gem_name|
284
+ UI.verbose("Loading '#{gem_name}' plugin")
285
+ begin
286
+ # BEFORE requiring the gem, we get a list of loaded actions
287
+ # This way we can check inside `store_plugin_reference` if
288
+ # any actions were overwritten
289
+ self.loaded_pantograph_actions.concat(Pantograph::Actions.constants)
290
+
291
+ PantographRequire.install_gem_if_needed(gem_name: gem_name, require_gem: true)
292
+
293
+ store_plugin_reference(gem_name)
294
+ loaded_plugins = true
295
+ rescue StandardError, ScriptError => ex # some errors, like ScriptError are not caught unless explicitly
296
+ UI.error("Error loading plugin '#{gem_name}': #{ex}")
297
+
298
+ # We'll still add it to the table, to make the error
299
+ # much more visible and obvious
300
+ self.plugin_references[gem_name] = {
301
+ version_number: Pantograph::ActionCollector.determine_version(gem_name),
302
+ actions: []
303
+ }
304
+ end
305
+ end
306
+
307
+ if !loaded_plugins && self.pluginfile_content.to_s.include?(PluginManager.plugin_prefix)
308
+ UI.error("It seems like you wanted to load some plugins, however they couldn't be loaded")
309
+ UI.error("Please follow the troubleshooting guide: #{TROUBLESHOOTING_URL}")
310
+ end
311
+
312
+ skip_print_plugin_info = self.plugin_references.empty? || CLIToolsDistributor.running_version_command? || PantographCore::Env.truthy?("PANTOGRAPH_ENV_PRINTER")
313
+
314
+ # We want to avoid printing output other than the version number if we are running `pantograph -v`
315
+ print_plugin_information(self.plugin_references) unless skip_print_plugin_info
316
+ end
317
+
318
+ # Prints a table all the plugins that were loaded
319
+ def print_plugin_information(references)
320
+ rows = references.collect do |current|
321
+ if current[1][:actions].empty?
322
+ # Something is wrong with this plugin, no available actions
323
+ [current[0].red, current[1][:version_number], "No actions found".red]
324
+ else
325
+ [current[0], current[1][:version_number], current[1][:actions].join("\n")]
326
+ end
327
+ end
328
+
329
+ require 'terminal-table'
330
+ puts(Terminal::Table.new({
331
+ rows: PantographCore::PrintTable.transform_output(rows),
332
+ title: "Used plugins".green,
333
+ headings: ["Plugin", "Version", "Action"]
334
+ }))
335
+ puts("")
336
+ end
337
+
338
+ #####################################################
339
+ # @!group Reference between plugins to actions
340
+ #####################################################
341
+
342
+ # Connection between plugins and their actions
343
+ # Example value of plugin_references
344
+ # => {"pantograph-plugin-ruby" => {
345
+ # version_number: "0.1.0",
346
+ # actions: [:rspec, :rubocop]
347
+ # }}
348
+ def plugin_references
349
+ @plugin_references ||= {}
350
+ end
351
+
352
+ # Contains an array of symbols for the action classes
353
+ def loaded_pantograph_actions
354
+ @pantograph_actions ||= []
355
+ end
356
+
357
+ def store_plugin_reference(gem_name)
358
+ module_name = gem_name.gsub(PluginManager.plugin_prefix, '').pantograph_class
359
+ # We store a collection of the imported plugins
360
+ # This way we can tell which action came from what plugin
361
+ # (a plugin may contain any number of actions)
362
+ version_number = Pantograph::ActionCollector.determine_version(gem_name)
363
+ references = Pantograph.const_get(module_name).all_classes.collect do |path|
364
+ next unless File.dirname(path).end_with?("/actions") # we only want to match actions
365
+
366
+ File.basename(path).gsub("_action", "").gsub(".rb", "").to_sym # the _action is optional
367
+ end
368
+ references.compact!
369
+
370
+ # Check if this overwrites a built-in action and
371
+ # show a warning if that's the case
372
+ references.each do |current_ref|
373
+ # current_ref is a symbol, e.g. :emoji_fetcher
374
+ class_name = (current_ref.to_s.pantograph_class + 'Action').to_sym
375
+
376
+ if self.loaded_pantograph_actions.include?(class_name)
377
+ UI.important("Plugin '#{module_name}' overwrites already loaded action '#{current_ref}'")
378
+ end
379
+ end
380
+
381
+ self.plugin_references[gem_name] = {
382
+ version_number: version_number,
383
+ actions: references
384
+ }
385
+ end
386
+ end
387
+ end
@@ -0,0 +1,46 @@
1
+ module Pantograph
2
+ class PluginSearch
3
+ require 'terminal-table'
4
+ require 'word_wrap'
5
+
6
+ def self.print_plugins(search_query: nil)
7
+ if search_query
8
+ UI.message("Looking for pantograph plugins containing '#{search_query}'...")
9
+ else
10
+ UI.message("Listing all available pantograph plugins")
11
+ end
12
+
13
+ plugins = Pantograph::PluginFetcher.fetch_gems(search_query: search_query)
14
+
15
+ if plugins.empty?
16
+ UI.user_error!("Couldn't find any available pantograph plugins containing '#{search_query}'")
17
+ end
18
+
19
+ rows = plugins.collect do |current|
20
+ [
21
+ current.name.green,
22
+ WordWrap.ww(current.info, 50),
23
+ current.downloads
24
+ ]
25
+ end
26
+
27
+ params = {
28
+ rows: PantographCore::PrintTable.transform_output(rows),
29
+ title: (search_query ? "pantograph plugins '#{search_query}'" : "Available pantograph plugins").green,
30
+ headings: ["Name", "Description", "Downloads"]
31
+ }
32
+
33
+ puts("")
34
+ puts(Terminal::Table.new(params))
35
+ puts("")
36
+
37
+ if plugins.count == 1
38
+ print_plugin_details(plugins.last)
39
+ end
40
+ end
41
+
42
+ def self.print_plugin_details(plugin)
43
+ UI.message("You can find more information for #{plugin.name} on #{plugin.homepage.green}")
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,70 @@
1
+ module Pantograph
2
+ # Alert the user when updates for plugins are available
3
+ class PluginUpdateManager
4
+ def self.start_looking_for_updates
5
+ return if PantographCore::Env.truthy?("PANTOGRAPH_SKIP_UPDATE_CHECK")
6
+
7
+ Thread.new do
8
+ self.plugin_references.each do |plugin_name, current_plugin|
9
+ begin
10
+ self.server_results[plugin_name] = fetch_latest_version(plugin_name)
11
+ rescue
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ def self.show_update_status
18
+ return if PantographCore::Env.truthy?("PANTOGRAPH_SKIP_UPDATE_CHECK")
19
+
20
+ # We set self.server_results to be nil
21
+ # this way the table is not printed twice
22
+ # (next to the summary table or when an exception happens)
23
+ return unless self.server_results.count > 0
24
+
25
+ rows = []
26
+ self.plugin_references.each do |plugin_name, current_plugin|
27
+ latest_version = self.server_results[plugin_name]
28
+ next if latest_version.nil?
29
+ current_version = Gem::Version.new(current_plugin[:version_number])
30
+ next if current_version >= latest_version
31
+
32
+ rows << [
33
+ plugin_name.gsub(PluginManager.plugin_prefix, ''),
34
+ current_version.to_s.red,
35
+ latest_version.to_s.green
36
+ ]
37
+ end
38
+
39
+ if rows.empty?
40
+ UI.verbose("All plugins are up to date")
41
+ return
42
+ end
43
+
44
+ require 'terminal-table'
45
+ puts(Terminal::Table.new({
46
+ rows: PantographCore::PrintTable.transform_output(rows),
47
+ title: "Plugin updates available".yellow,
48
+ headings: ["Plugin", "Your Version", "Latest Version"]
49
+ }))
50
+ UI.message("To update all plugins, just run")
51
+ UI.command "bundle exec pantograph update_plugins"
52
+ puts('')
53
+ @server_results = nil
54
+ end
55
+
56
+ def self.plugin_references
57
+ Pantograph.plugin_manager.plugin_references
58
+ end
59
+
60
+ def self.fetch_latest_version(gem_name)
61
+ Gem::Version.new(PluginManager.fetch_gem_info_from_rubygems(gem_name)["version"])
62
+ rescue
63
+ nil
64
+ end
65
+
66
+ def self.server_results
67
+ @server_results ||= {}
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,12 @@
1
+ require 'fileutils'
2
+ require 'erb'
3
+ require 'find'
4
+
5
+ require 'pantograph/plugins/plugin_info'
6
+ require 'pantograph/plugins/plugin_generator'
7
+ require 'pantograph/plugins/plugin_generator_ui'
8
+ require 'pantograph/plugins/plugin_info_collector'
9
+ require 'pantograph/plugins/plugin_manager'
10
+ require 'pantograph/plugins/plugin_search'
11
+ require 'pantograph/plugins/plugin_fetcher'
12
+ require 'pantograph/plugins/plugin_update_manager'