pantograph 0.1.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.
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'