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,377 @@
1
+ require 'rubygems/requirement'
2
+
3
+ module Pantograph
4
+ class PantFile
5
+ # Stores all relevant information from the currently running process
6
+ attr_accessor :runner
7
+
8
+ # the platform in which we're currently in when parsing the Pantfile
9
+ # This is used to identify the platform in which the lane is in
10
+ attr_accessor :current_platform
11
+
12
+ SharedValues = Pantograph::Actions::SharedValues
13
+
14
+ # @return The runner which can be executed to trigger the given actions
15
+ def initialize(path = nil)
16
+ return unless (path || '').length > 0
17
+ UI.user_error!("Could not find Pantfile at path '#{path}'") unless File.exist?(path)
18
+ @path = File.expand_path(path)
19
+ content = File.read(path, encoding: "utf-8")
20
+
21
+ # From https://github.com/orta/danger/blob/master/lib/danger/Dangerfile.rb
22
+ if content.tr!('“”‘’‛', %(""'''))
23
+ UI.error("Your #{File.basename(path)} has had smart quotes sanitised. " \
24
+ 'To avoid issues in the future, you should not use ' \
25
+ 'TextEdit for editing it. If you are not using TextEdit, ' \
26
+ 'you should turn off smart quotes in your editor of choice.')
27
+ end
28
+
29
+ content.scan(/^\s*require ["'](.*?)["']/).each do |current|
30
+ gem_name = current.last
31
+ next if gem_name.include?(".") # these are local gems
32
+
33
+ begin
34
+ require(gem_name)
35
+ rescue LoadError
36
+ UI.important("You have required a gem, if this is a third party gem, please use `pantograph_require '#{gem_name}'` to ensure the gem is installed locally.")
37
+ end
38
+ end
39
+
40
+ parse(content, @path)
41
+ end
42
+
43
+ def parsing_binding
44
+ binding
45
+ end
46
+
47
+ def parse(data, path = nil)
48
+ @runner ||= Runner.new
49
+
50
+ Dir.chdir(PantographCore::PantographFolder.path || Dir.pwd) do # context: pantograph subfolder
51
+ # create nice path that we want to print in case of some problem
52
+ relative_path = path.nil? ? '(eval)' : Pathname.new(path).relative_path_from(Pathname.new(Dir.pwd)).to_s
53
+
54
+ begin
55
+ # We have to use #get_binding method, because some test files defines method called `path` (for example SwitcherPantfile)
56
+ # and local variable has higher priority, so it causes to remove content of original Pantfile for example. With #get_binding
57
+ # is this always clear and safe to declare any local variables we want, because the eval function uses the instance scope
58
+ # instead of local.
59
+
60
+ # rubocop:disable Security/Eval
61
+ eval(data, parsing_binding, relative_path) # using eval is ok for this case
62
+ # rubocop:enable Security/Eval
63
+ rescue SyntaxError => ex
64
+ match = ex.to_s.match(/#{Regexp.escape(relative_path)}:(\d+)/)
65
+ if match
66
+ line = match[1]
67
+ UI.content_error(data, line)
68
+ UI.user_error!("Syntax error in your Pantfile on line #{line}: #{ex}")
69
+ else
70
+ UI.user_error!("Syntax error in your Pantfile: #{ex}")
71
+ end
72
+ end
73
+ end
74
+
75
+ self
76
+ end
77
+
78
+ #####################################################
79
+ # @!group DSL
80
+ #####################################################
81
+
82
+ # User defines a new lane
83
+ def lane(lane_name, &block)
84
+ UI.user_error!("You have to pass a block using 'do' for lane '#{lane_name}'. Make sure you read the docs on GitHub.") unless block
85
+
86
+ self.runner.add_lane(Lane.new(platform: self.current_platform,
87
+ block: block,
88
+ description: desc_collection,
89
+ name: lane_name,
90
+ is_private: false))
91
+
92
+ @desc_collection = nil # reset the collected description again for the next lane
93
+ end
94
+
95
+ # User defines a new private lane, which can't be called from the CLI
96
+ def private_lane(lane_name, &block)
97
+ UI.user_error!("You have to pass a block using 'do' for lane '#{lane_name}'. Make sure you read the docs on GitHub.") unless block
98
+
99
+ self.runner.add_lane(Lane.new(platform: self.current_platform,
100
+ block: block,
101
+ description: desc_collection,
102
+ name: lane_name,
103
+ is_private: true))
104
+
105
+ @desc_collection = nil # reset the collected description again for the next lane
106
+ end
107
+
108
+ # User defines a lane that can overwrite existing lanes. Useful when importing a Pantfile
109
+ def override_lane(lane_name, &block)
110
+ UI.user_error!("You have to pass a block using 'do' for lane '#{lane_name}'. Make sure you read the docs on GitHub.") unless block
111
+
112
+ self.runner.add_lane(Lane.new(platform: self.current_platform,
113
+ block: block,
114
+ description: desc_collection,
115
+ name: lane_name,
116
+ is_private: false), true)
117
+
118
+ @desc_collection = nil # reset the collected description again for the next lane
119
+ end
120
+
121
+ # User defines a platform block
122
+ def platform(platform_name)
123
+ SupportedPlatforms.verify!(platform_name)
124
+
125
+ self.current_platform = platform_name
126
+
127
+ yield
128
+
129
+ self.current_platform = nil
130
+ end
131
+
132
+ # Is executed before each test run
133
+ def before_all(&block)
134
+ @runner.set_before_all(@current_platform, block)
135
+ end
136
+
137
+ # Is executed before each lane
138
+ def before_each(&block)
139
+ @runner.set_before_each(@current_platform, block)
140
+ end
141
+
142
+ # Is executed after each test run
143
+ def after_all(&block)
144
+ @runner.set_after_all(@current_platform, block)
145
+ end
146
+
147
+ # Is executed before each lane
148
+ def after_each(&block)
149
+ @runner.set_after_each(@current_platform, block)
150
+ end
151
+
152
+ # Is executed if an error occurred during pantograph execution
153
+ def error(&block)
154
+ @runner.set_error(@current_platform, block)
155
+ end
156
+
157
+ # Is used to look if the method is implemented as an action
158
+ def method_missing(method_sym, *arguments, &_block)
159
+ self.runner.trigger_action_by_name(method_sym, nil, false, *arguments)
160
+ end
161
+
162
+ #####################################################
163
+ # @!group Other things
164
+ #####################################################
165
+
166
+ # Is the given key a platform block or a lane?
167
+ def is_platform_block?(key)
168
+ UI.crash!('No key given') unless key
169
+
170
+ return false if self.runner.lanes.fetch(nil, {}).fetch(key.to_sym, nil)
171
+ return true if self.runner.lanes[key.to_sym].kind_of?(Hash)
172
+
173
+ if key.to_sym == :update
174
+ # The user ran `pantograph update`, instead of `pantograph update_pantograph`
175
+ # We're gonna be nice and understand what the user is trying to do
176
+ require 'pantograph/one_off'
177
+ Pantograph::OneOff.run(action: "update_pantograph", parameters: {})
178
+ else
179
+ UI.user_error!("Could not find '#{key}'. Available lanes: #{self.runner.available_lanes.join(', ')}")
180
+ end
181
+ end
182
+
183
+ def actions_path(path)
184
+ UI.crash!("Path '#{path}' not found!") unless File.directory?(path)
185
+
186
+ Actions.load_external_actions(path)
187
+ end
188
+
189
+ # Execute shell command
190
+ # Accepts arguments with with and without the command named keyword so that sh
191
+ # behaves like other actions with named keywords
192
+ # https://github.com/pantograph/pantograph/issues/14930
193
+ #
194
+ # Example:
195
+ # sh("ls")
196
+ # sh("ls", log: false)
197
+ # sh(command: "ls")
198
+ # sh(command: "ls", log: false)
199
+ def sh(*args, &b)
200
+ # First accepts hash (or named keywords) like other actions
201
+ # Otherwise uses sh method that doesn't have an interface like an action
202
+ if args.count == 1 && args.first.kind_of?(Hash)
203
+ hash = args.first
204
+ command = hash.delete(:command)
205
+
206
+ raise ArgumentError, "sh requires :command keyword in argument" if command.nil?
207
+
208
+ new_args = [*command, hash]
209
+ PantFile.sh(*new_args, &b)
210
+ else
211
+ PantFile.sh(*args, &b)
212
+ end
213
+ end
214
+
215
+ def self.sh(*command, log: true, error_callback: nil, &b)
216
+ command_header = log ? Actions.shell_command_from_args(*command) : "shell command"
217
+ Actions.execute_action(command_header) do
218
+ Actions.sh_no_action(*command, log: log, error_callback: error_callback, &b)
219
+ end
220
+ end
221
+
222
+ def desc(string)
223
+ desc_collection << string
224
+ end
225
+
226
+ def desc_collection
227
+ @desc_collection ||= []
228
+ end
229
+
230
+ def pantograph_require(gem_name)
231
+ PantographRequire.install_gem_if_needed(gem_name: gem_name, require_gem: true)
232
+ end
233
+
234
+ def generated_pantfile_id(id)
235
+ UI.important("The `generated_pantfile_id` action was deprecated, you can remove the line from your `Pantfile`")
236
+ end
237
+
238
+ def import(path = nil)
239
+ UI.user_error!("Please pass a path to the `import` action") unless path
240
+
241
+ path = path.dup.gsub("~", Dir.home)
242
+ unless Pathname.new(path).absolute? # unless an absolute path
243
+ path = File.join(File.expand_path('..', @path), path)
244
+ end
245
+
246
+ UI.user_error!("Could not find Pantfile at path '#{path}'") unless File.exist?(path)
247
+
248
+ # First check if there are local actions to import in the same directory as the Pantfile
249
+ actions_path = File.join(File.expand_path("..", path), 'actions')
250
+ Pantograph::Actions.load_external_actions(actions_path) if File.directory?(actions_path)
251
+
252
+ action_launched('import')
253
+
254
+ return_value = parse(File.read(path), path)
255
+
256
+ action_completed('import', status: PantographCore::ActionCompletionStatus::SUCCESS)
257
+
258
+ return return_value
259
+ end
260
+
261
+ # @param url [String] The git URL to clone the repository from
262
+ # @param branch [String] The branch to checkout in the repository
263
+ # @param path [String] The path to the Pantfile
264
+ # @param version [String, Array] Version requirement for repo tags
265
+ def import_from_git(url: nil, branch: 'HEAD', path: 'pantograph/Pantfile', version: nil)
266
+ UI.user_error!("Please pass a path to the `import_from_git` action") if url.to_s.length == 0
267
+
268
+ Actions.execute_action('import_from_git') do
269
+ require 'tmpdir'
270
+
271
+ action_launched('import_from_git')
272
+
273
+ # Checkout the repo
274
+ repo_name = url.split("/").last
275
+ checkout_param = branch
276
+
277
+ Dir.mktmpdir("fl_clone") do |tmp_path|
278
+ clone_folder = File.join(tmp_path, repo_name)
279
+
280
+ branch_option = "--branch #{branch}" if branch != 'HEAD'
281
+
282
+ UI.message("Cloning remote git repo...")
283
+ Helper.with_env_values('GIT_TERMINAL_PROMPT' => '0') do
284
+ Actions.sh("git clone #{url.shellescape} #{clone_folder.shellescape} --depth 1 -n #{branch_option}")
285
+ end
286
+
287
+ unless version.nil?
288
+ req = Gem::Requirement.new(version)
289
+ all_tags = fetch_remote_tags(folder: clone_folder)
290
+ checkout_param = all_tags.select { |t| req =~ PantographCore::TagVersion.new(t) }.last
291
+ UI.user_error!("No tag found matching #{version.inspect}") if checkout_param.nil?
292
+ end
293
+
294
+ Actions.sh("cd #{clone_folder.shellescape} && git checkout #{checkout_param.shellescape} #{path.shellescape}")
295
+
296
+ # We also want to check out all the local actions of this pantograph setup
297
+ containing = path.split(File::SEPARATOR)[0..-2]
298
+ containing = "." if containing.count == 0
299
+ actions_folder = File.join(containing, "actions")
300
+ begin
301
+ Actions.sh("cd #{clone_folder.shellescape} && git checkout #{checkout_param.shellescape} #{actions_folder.shellescape}")
302
+ rescue
303
+ # We don't care about a failure here, as local actions are optional
304
+ end
305
+
306
+ return_value = import(File.join(clone_folder, path))
307
+
308
+ action_completed('import_from_git', status: PantographCore::ActionCompletionStatus::SUCCESS)
309
+
310
+ return return_value
311
+ end
312
+ end
313
+ end
314
+
315
+ #####################################################
316
+ # @!group Versioning helpers
317
+ #####################################################
318
+
319
+ def fetch_remote_tags(folder: nil)
320
+ UI.message("Fetching remote git tags...")
321
+ Helper.with_env_values('GIT_TERMINAL_PROMPT' => '0') do
322
+ Actions.sh("cd #{folder.shellescape} && git fetch --all --tags -q")
323
+ end
324
+
325
+ # Fetch all possible tags
326
+ git_tags_string = Actions.sh("cd #{folder.shellescape} && git tag -l")
327
+ git_tags = git_tags_string.split("\n")
328
+
329
+ # Sort tags based on their version number
330
+ return git_tags
331
+ .select { |tag| PantographCore::TagVersion.correct?(tag) }
332
+ .sort_by { |tag| PantographCore::TagVersion.new(tag) }
333
+ end
334
+
335
+ #####################################################
336
+ # @!group Overwriting Ruby methods
337
+ #####################################################
338
+
339
+ # Speak out loud
340
+ def say(value)
341
+ # Overwrite this, since there is already a 'say' method defined in the Ruby standard library
342
+ value ||= yield
343
+
344
+ value = { text: value } if value.kind_of?(String) || value.kind_of?(Array)
345
+ self.runner.trigger_action_by_name(:say, nil, false, value)
346
+ end
347
+
348
+ def puts(value)
349
+ # Overwrite this, since there is already a 'puts' method defined in the Ruby standard library
350
+ value ||= yield if block_given?
351
+
352
+ action_launched('puts')
353
+ return_value = Pantograph::Actions::PutsAction.run([value])
354
+ action_completed('puts', status: PantographCore::ActionCompletionStatus::SUCCESS)
355
+ return return_value
356
+ end
357
+
358
+ def test(params = {})
359
+ # Overwrite this, since there is already a 'test' method defined in the Ruby standard library
360
+ self.runner.try_switch_to_lane(:test, [params])
361
+ end
362
+
363
+ def action_launched(action_name)
364
+ action_launch_context = PantographCore::ActionLaunchContext.context_for_action_name(action_name,
365
+ pantograph_client_language: :ruby,
366
+ args: ARGV)
367
+ PantographCore.session.action_launched(launch_context: action_launch_context)
368
+ end
369
+
370
+ def action_completed(action_name, status: nil)
371
+ completion_context = PantographCore::ActionCompletionContext.context_for_action_name(action_name,
372
+ args: ARGV,
373
+ status: status)
374
+ PantographCore.session.action_completed(completion_context: completion_context)
375
+ end
376
+ end
377
+ end
@@ -0,0 +1,75 @@
1
+ module Pantograph
2
+ class PantographRequire
3
+ class << self
4
+ def install_gem_if_needed(gem_name: nil, require_gem: true)
5
+ gem_require_name = format_gem_require_name(gem_name)
6
+
7
+ # check if it's installed
8
+ if gem_installed?(gem_name)
9
+ UI.success("gem '#{gem_name}' is already installed") if PantographCore::Globals.verbose?
10
+ require gem_require_name if require_gem
11
+ return true
12
+ end
13
+
14
+ if Helper.bundler?
15
+ # User uses bundler, we don't want to install gems on the fly here
16
+ # Instead tell the user how to add it to their Gemfile
17
+ UI.important("Missing gem '#{gem_name}', please add the following to your local Gemfile:")
18
+ UI.important("")
19
+ UI.command_output("gem \"#{gem_name}\"")
20
+ UI.important("")
21
+ UI.user_error!("Add 'gem \"#{gem_name}\"' to your Gemfile and restart pantograph") unless Helper.test?
22
+ end
23
+
24
+ require "rubygems/command_manager"
25
+ installer = Gem::CommandManager.instance[:install]
26
+
27
+ UI.important("Installing Ruby gem '#{gem_name}'...")
28
+
29
+ spec_name = self.find_gem_name(gem_name)
30
+ UI.important("Found gem \"#{spec_name}\" instead of the required name \"#{gem_name}\"") if spec_name != gem_name
31
+
32
+ return if Helper.test?
33
+
34
+ # We install the gem like this because we also want to gem to be available to be required
35
+ # at this point. If we were to shell out, this wouldn't be the case
36
+ installer.install_gem(spec_name, Gem::Requirement.default)
37
+ UI.success("Successfully installed '#{gem_name}'")
38
+ require gem_require_name if require_gem
39
+ end
40
+
41
+ def gem_installed?(name, req = Gem::Requirement.default)
42
+ installed = Gem::Specification.any? { |s| s.name == name and req =~ s.version }
43
+ return true if installed
44
+
45
+ # In special cases a gem is already preinstalled, e.g. YAML.
46
+ # To find out we try to load a gem with that name in a child process
47
+ # (so we don't actually load anything we don't want to load)
48
+ # See https://github.com/pantograph/pantograph/issues/6951
49
+ require_tester = <<-RB.gsub(/^ */, '')
50
+ begin
51
+ require ARGV.first
52
+ rescue LoadError
53
+ exit(1)
54
+ end
55
+ RB
56
+ system(RbConfig.ruby, "-e", require_tester.lines.map(&:chomp).join("; "), name)
57
+ return $?.success?
58
+ end
59
+
60
+ def find_gem_name(user_supplied_name)
61
+ fetcher = Gem::SpecFetcher.fetcher
62
+ gems = fetcher.suggest_gems_from_name(user_supplied_name)
63
+
64
+ return gems.first
65
+ end
66
+
67
+ def format_gem_require_name(gem_name)
68
+ # from "pantograph-plugin-xcversion" to "pantograph/plugin/xcversion"
69
+ gem_name = gem_name.tr("-", "/") if gem_name.start_with?("pantograph-plugin-")
70
+
71
+ return gem_name
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,55 @@
1
+ module Pantograph
2
+ # Use the RubyGems API to get all pantograph plugins
3
+ class PluginFetcher
4
+ require 'pantograph_core'
5
+ require 'pantograph/plugins/plugin_manager'
6
+
7
+ # Returns an array of PantographPlugin objects
8
+ def self.fetch_gems(search_query: nil)
9
+ require 'json'
10
+ require 'open-uri'
11
+
12
+ page = 1
13
+ plugins = []
14
+ loop do
15
+ url = "https://rubygems.org/api/v1/search.json?query=#{PluginManager.plugin_prefix}&page=#{page}"
16
+ PantographCore::UI.verbose("RubyGems API Request: #{url}")
17
+ results = JSON.parse(open(url).read)
18
+ break if results.count == 0
19
+
20
+ plugins += results.collect do |current|
21
+ PantographPlugin.new(current)
22
+ end
23
+ page += 1
24
+ end
25
+
26
+ return plugins if search_query.to_s.length == 0
27
+ plugins.keep_if do |current|
28
+ current.full_name.include?(search_query) or current.info.include?(search_query)
29
+ end
30
+
31
+ return plugins
32
+ end
33
+ end
34
+
35
+ class PantographPlugin
36
+ attr_accessor :full_name
37
+ attr_accessor :name
38
+ attr_accessor :downloads
39
+ attr_accessor :info
40
+ attr_accessor :homepage
41
+
42
+ def initialize(hash)
43
+ self.full_name = hash["name"]
44
+ self.name = self.full_name.gsub(PluginManager.plugin_prefix, '')
45
+ self.downloads = hash["downloads"]
46
+ self.info = hash["info"]
47
+ self.homepage = hash["homepage_uri"]
48
+ end
49
+
50
+ def linked_title
51
+ return "`#{name}`" if homepage.to_s.length == 0
52
+ return "[#{name}](#{homepage})"
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,86 @@
1
+ module Pantograph
2
+ # Generates a sample plugin by traversing a template directory structure
3
+ # and reproducing it in a destination location. At the same time, it runs
4
+ # variable replacements on directory names, file names, and runs ERB
5
+ # templating on file contents whose names end with '.erb'.
6
+ #
7
+ # Directory and file name variable replacements are defined like: %gem_name%
8
+ # The text between the percent signs will be used to invoke an accessor
9
+ # method on the PluginInfo object to get the replacement value.
10
+ class PluginGenerator
11
+ def initialize(ui: PluginGeneratorUI.new,
12
+ info_collector: PluginInfoCollector.new(ui),
13
+ template_root: File.join(File.dirname(__FILE__), 'template'),
14
+ dest_root: FileUtils.pwd)
15
+ @ui = ui
16
+ @info_collector = info_collector
17
+ @template_root = template_root
18
+ @dest_root = dest_root
19
+ end
20
+
21
+ # entry point
22
+ def generate(plugin_name = nil)
23
+ plugin_info = @info_collector.collect_info(plugin_name)
24
+
25
+ # Traverse all the files and directories in the template root,
26
+ # handling each in turn
27
+ Find.find(@template_root) do |template_path|
28
+ handle_template_path(template_path, plugin_info)
29
+ end
30
+
31
+ @ui.success("\nYour plugin was successfully generated at #{plugin_info.gem_name}/ 🚀")
32
+ @ui.success("\nTo get started with using this plugin, run")
33
+ @ui.message("\n pantograph add_plugin #{plugin_info.plugin_name}\n")
34
+ @ui.success("\nfrom a pantograph-enabled app project directory and provide the following as the path:")
35
+ @ui.message("\n #{File.expand_path(plugin_info.gem_name)}\n\n")
36
+ end
37
+
38
+ def handle_template_path(template_path, plugin_info)
39
+ dest_path = derive_dest_path(template_path, plugin_info)
40
+
41
+ if File.directory?(template_path)
42
+ FileUtils.mkdir_p(dest_path)
43
+ else
44
+ copy_file(template_path, dest_path, plugin_info)
45
+ end
46
+ end
47
+
48
+ def derive_dest_path(template_path, plugin_info)
49
+ relative_template_path = template_path.gsub(@template_root, '')
50
+ replaced_path = replace_path_variables(relative_template_path, plugin_info)
51
+
52
+ File.join(@dest_root, plugin_info.gem_name, replaced_path)
53
+ end
54
+
55
+ def copy_file(template_path, dest_path, plugin_info)
56
+ contents = File.read(template_path)
57
+
58
+ if dest_path.end_with?('.erb')
59
+ contents = ERB.new(contents).result(plugin_info.get_binding)
60
+ dest_path = dest_path[0...-4] # Remove the .erb suffix
61
+ end
62
+
63
+ File.write(dest_path, contents)
64
+ end
65
+
66
+ # Path variables can be defined like: %gem_name%
67
+ #
68
+ # The text between the percent signs will be used to invoke an accessor
69
+ # method on the PluginInfo object to be the replacement value.
70
+ def replace_path_variables(template_path, plugin_info)
71
+ path = template_path.dup
72
+
73
+ loop do
74
+ replacement_variable_regexp = /%([\w\-]*)%/
75
+ match = replacement_variable_regexp.match(path)
76
+
77
+ break unless match
78
+
79
+ replacement_value = plugin_info.send(match[1].to_sym)
80
+ path.gsub!(replacement_variable_regexp, replacement_value)
81
+ end
82
+
83
+ path
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,19 @@
1
+ module Pantograph
2
+ class PluginGeneratorUI
3
+ def success(text)
4
+ puts(text.green)
5
+ end
6
+
7
+ def message(text)
8
+ puts(text)
9
+ end
10
+
11
+ def input(text)
12
+ UI.input(text)
13
+ end
14
+
15
+ def confirm(text)
16
+ UI.confirm(text)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,49 @@
1
+ module Pantograph
2
+ class PluginInfo
3
+ attr_reader :plugin_name
4
+ attr_reader :author
5
+ attr_reader :gem_name
6
+ attr_reader :email
7
+ attr_reader :summary
8
+ attr_reader :details
9
+
10
+ def initialize(plugin_name, author, email, summary, details)
11
+ @plugin_name = plugin_name
12
+ @author = author
13
+ @email = email
14
+ @summary = summary
15
+ @details = details
16
+ end
17
+
18
+ def gem_name
19
+ "#{Pantograph::PluginManager::PANTOGRAPH_PLUGIN_PREFIX}#{plugin_name}"
20
+ end
21
+
22
+ def require_path
23
+ gem_name.tr('-', '/')
24
+ end
25
+
26
+ def actions_path
27
+ File.join(require_path, 'actions')
28
+ end
29
+
30
+ def helper_path
31
+ File.join(require_path, 'helper')
32
+ end
33
+
34
+ # Used to expose a local binding for use in ERB templating
35
+ #
36
+ # rubocop:disable Style/AccessorMethodName
37
+ def get_binding
38
+ binding
39
+ end
40
+ # rubocop:enable Style/AccessorMethodName
41
+
42
+ def ==(other)
43
+ @plugin_name == other.plugin_name &&
44
+ @author == other.author &&
45
+ @email == other.email &&
46
+ @summary == other.summary
47
+ end
48
+ end
49
+ end