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,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