fastlane 1.92.0 → 1.93.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/lib/assets/AvailablePlugins.md.erb +24 -0
  3. data/lib/fastlane.rb +13 -6
  4. data/lib/fastlane/action_collector.rb +35 -2
  5. data/lib/fastlane/actions/actions_helper.rb +4 -0
  6. data/lib/fastlane/commands_generator.rb +61 -1
  7. data/lib/fastlane/lane.rb +1 -1
  8. data/lib/fastlane/lane_manager.rb +6 -2
  9. data/lib/fastlane/one_off.rb +7 -1
  10. data/lib/fastlane/plugins/plugin_fetcher.rb +59 -0
  11. data/lib/fastlane/plugins/plugin_generator.rb +86 -0
  12. data/lib/fastlane/plugins/plugin_generator_ui.rb +19 -0
  13. data/lib/fastlane/plugins/plugin_info.rb +47 -0
  14. data/lib/fastlane/plugins/plugin_info_collector.rb +150 -0
  15. data/lib/fastlane/plugins/plugin_manager.rb +358 -0
  16. data/lib/fastlane/plugins/plugin_search.rb +46 -0
  17. data/lib/fastlane/plugins/plugins.rb +11 -0
  18. data/lib/fastlane/plugins/template/%gem_name%.gemspec.erb +26 -0
  19. data/lib/fastlane/plugins/template/Gemfile +3 -0
  20. data/lib/fastlane/plugins/template/LICENSE.erb +21 -0
  21. data/lib/fastlane/plugins/template/README.md.erb +31 -0
  22. data/lib/fastlane/plugins/template/Rakefile +1 -0
  23. data/lib/fastlane/plugins/template/lib/fastlane/plugin/%plugin_name%.rb.erb +16 -0
  24. data/lib/fastlane/plugins/template/lib/fastlane/plugin/%plugin_name%/actions/%plugin_name%_action.rb.erb +35 -0
  25. data/lib/fastlane/plugins/template/lib/fastlane/plugin/%plugin_name%/helper/%plugin_name%_helper.rb.erb +12 -0
  26. data/lib/fastlane/plugins/template/lib/fastlane/plugin/%plugin_name%/version.rb.erb +5 -0
  27. data/lib/fastlane/plugins/template/spec/%plugin_name%_action_spec.rb.erb +9 -0
  28. data/lib/fastlane/plugins/template/spec/spec_helper.rb.erb +10 -0
  29. data/lib/fastlane/runner.rb +34 -12
  30. data/lib/fastlane/version.rb +1 -1
  31. metadata +60 -27
  32. data/lib/fastlane/actions/xcake.rb +0 -35
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f5a8f36b44fdba28a2384ebaaf3ed6c19640e4ed
4
- data.tar.gz: 97d37d0093e30964336a875e744ddc468b1ebdd9
3
+ metadata.gz: 3c67117c9fa728e8d4a628cd5ab40a74f615dfd1
4
+ data.tar.gz: 1397336e43b8257f610df1af93f04532a8480d18
5
5
  SHA512:
6
- metadata.gz: 9b767890903852e42d1a7a56b0ce9cb18649934ae69ed719534f6f3178f1595e3feeef445bd59f89c860d6a4e028878ce5359ec156627e9c12d58d01b5930270
7
- data.tar.gz: c55a8e615c2e69bb69388316c3618a58d8156d3ecd86b9da53ee9ea8d6f0816e16cf400b4ac9bf1929d3f1a89b68f7181c74221d06a3929e2e9a165d67758671
6
+ metadata.gz: c7400de730419a6c95102c79d32c623a267da6e540c2620241e12d645da46648d3433e36a8c344b5e0e49076607c75c0932e107ace0a4bb4dc0607e1d3666805
7
+ data.tar.gz: f91ff6eb451eff605418e3dc2416402bb7affc8448df7114ffcf13094e402772b74759d8f04b2e0de6f3e2abdaf21fa0e7e46774ce7bd78942d731df1c845b26
@@ -0,0 +1,24 @@
1
+ ### Available Plugins
2
+
3
+ To get an up to date list of all available plugins run
4
+
5
+ ```
6
+ fastlane search_plugins
7
+ ```
8
+
9
+ To search for a specific plugin
10
+
11
+ ```
12
+ fastlane search_plugins [search_query]
13
+ ```
14
+
15
+ You can find more information about how to start using plugins in [Plugins.md](https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Plugins.md).
16
+
17
+ #### List of plugins
18
+
19
+ | Plugin Name | Description | Downloads
20
+ --------------|-------------|----------|----------
21
+ <% @plugins.each do |current| %>
22
+ <%= current.linked_title %> | <%= current.info %> | <%= current.downloads %>
23
+
24
+ <% end %>
data/lib/fastlane.rb CHANGED
@@ -18,18 +18,25 @@ require 'fastlane/one_off'
18
18
  require 'fastlane/command_line_handler'
19
19
  require 'fastlane/documentation/docs_generator'
20
20
  require 'fastlane/other_action'
21
+ require 'fastlane/plugins/plugins'
21
22
 
22
23
  module Fastlane
23
24
  Helper = FastlaneCore::Helper # you gotta love Ruby: Helper.* should use the Helper class contained in FastlaneCore
24
25
  UI = FastlaneCore::UI
25
26
 
26
- def self.load_actions
27
- Fastlane::Actions.load_default_actions
28
- Fastlane::Actions.load_helpers
27
+ class << self
28
+ def load_actions
29
+ Fastlane::Actions.load_default_actions
30
+ Fastlane::Actions.load_helpers
29
31
 
30
- if Fastlane::FastlaneFolder.path
31
- actions_path = File.join(Fastlane::FastlaneFolder.path, 'actions')
32
- Fastlane::Actions.load_external_actions(actions_path) if File.directory?(actions_path)
32
+ if Fastlane::FastlaneFolder.path
33
+ actions_path = File.join(Fastlane::FastlaneFolder.path, 'actions')
34
+ Fastlane::Actions.load_external_actions(actions_path) if File.directory?(actions_path)
35
+ end
36
+ end
37
+
38
+ def plugin_manager
39
+ @plugin_manager ||= Fastlane::PluginManager.new
33
40
  end
34
41
  end
35
42
  end
@@ -1,8 +1,19 @@
1
1
  module Fastlane
2
2
  class ActionCollector < FastlaneCore::ToolCollector
3
+ # Is this an official fastlane action, that is bundled with fastlane?
3
4
  def is_official?(name)
4
5
  return true if name == :lane_switch
5
- Actions.get_all_official_actions.include? name
6
+ Actions.get_all_official_actions.include?(name)
7
+ end
8
+
9
+ def name_to_track(name)
10
+ return name if is_official?(name)
11
+
12
+ Fastlane.plugin_manager.plugin_references.each do |plugin_name, value|
13
+ return "#{plugin_name}/#{name}" if value[:actions].include?(name)
14
+ end
15
+
16
+ return nil
6
17
  end
7
18
 
8
19
  def show_message
@@ -15,7 +26,29 @@ module Fastlane
15
26
  end
16
27
 
17
28
  def determine_version(name)
18
- super(name) || Fastlane::VERSION
29
+ self.class.determine_version(name)
30
+ end
31
+
32
+ # e.g.
33
+ # :gym
34
+ # :xcversion
35
+ # "fastlane-plugin-my_plugin/xcversion"
36
+ def self.determine_version(name)
37
+ result = super(name)
38
+ return result if result
39
+
40
+ if name.to_s.include?(PluginManager.plugin_prefix)
41
+ # That's an action from a plugin, we need to fetch its version number
42
+ begin
43
+ plugin_name = name.split("/").first.gsub(PluginManager.plugin_prefix, '')
44
+ return Fastlane.const_get(plugin_name.fastlane_class)::VERSION
45
+ rescue => ex
46
+ UI.verbose(ex)
47
+ return "undefined"
48
+ end
49
+ end
50
+
51
+ return Fastlane::VERSION # that's the case for all built-in actions
19
52
  end
20
53
  end
21
54
  end
@@ -101,5 +101,9 @@ module Fastlane
101
101
  end
102
102
  end
103
103
  end
104
+
105
+ def self.formerly_bundled_actions
106
+ ["xcake"]
107
+ end
104
108
  end
105
109
  end
@@ -8,8 +8,13 @@ module Fastlane
8
8
  include Commander::Methods
9
9
 
10
10
  def self.start
11
+ # since at this point we haven't yet loaded commander
12
+ # however we do want to log verbose information in the PluginManager
13
+ $verbose = true if ARGV.include?("--verbose")
14
+
11
15
  FastlaneCore::UpdateChecker.start_looking_for_update('fastlane')
12
16
  Fastlane.load_actions
17
+ Fastlane.plugin_manager.load_plugins
13
18
  self.new.run
14
19
  ensure
15
20
  FastlaneCore::UpdateChecker.show_update_status('fastlane', Fastlane::VERSION)
@@ -170,8 +175,63 @@ module Fastlane
170
175
  end
171
176
  end
172
177
 
173
- default_command :trigger
178
+ #####################################################
179
+ # @!group Plugins
180
+ #####################################################
181
+
182
+ command :new_plugin do |c|
183
+ c.syntax = 'fastlane new_plugin [plugin_name]'
184
+ c.description = 'Create a new plugin that can be used with fastlane'
185
+
186
+ c.action do |args, options|
187
+ PluginGenerator.new.generate(args.shift)
188
+ end
189
+ end
190
+
191
+ command :add_plugin do |c|
192
+ c.syntax = 'fastlane add_plugin [plugin_name]'
193
+ c.description = 'Add a new plugin to your fastlane setup'
194
+
195
+ c.action do |args, options|
196
+ args << UI.input("Enter the name of the plugin to install: ") if args.empty?
197
+ args.each do |plugin_name|
198
+ Fastlane.plugin_manager.add_dependency(plugin_name)
199
+ end
200
+
201
+ UI.important("Make sure to commit your Gemfile, Gemfile.lock and #{PluginManager::PLUGINFILE_NAME} to version control")
202
+ Fastlane.plugin_manager.install_dependencies!
203
+ end
204
+ end
205
+
206
+ command :install_plugins do |c|
207
+ c.syntax = 'fastlane install_plugins'
208
+ c.description = 'Install all plugins for this project'
174
209
 
210
+ c.action do |args, options|
211
+ Fastlane.plugin_manager.install_dependencies!
212
+ end
213
+ end
214
+
215
+ command :update_plugins do |c|
216
+ c.syntax = 'fastlane update_plugins'
217
+ c.description = 'Update all plugin dependencies'
218
+
219
+ c.action do |args, options|
220
+ Fastlane.plugin_manager.update_dependencies!
221
+ end
222
+ end
223
+
224
+ command :search_plugins do |c|
225
+ c.syntax = 'fastlane search_plugins [search_query]'
226
+ c.description = 'Search for plugins, search query is optional'
227
+
228
+ c.action do |args, options|
229
+ search_query = args.last
230
+ PluginSearch.print_plugins(search_query: search_query)
231
+ end
232
+ end
233
+
234
+ default_command :trigger
175
235
  run!
176
236
  end
177
237
 
data/lib/fastlane/lane.rb CHANGED
@@ -55,7 +55,7 @@ module Fastlane
55
55
  end
56
56
 
57
57
  def black_list
58
- %w(run init new_action lanes list docs action actions help)
58
+ %w(run init new_action lanes list docs action actions enable_auto_complete new_plugin add_plugin install_plugins update_plugins search_plugins help)
59
59
  end
60
60
 
61
61
  def gray_list
@@ -44,10 +44,14 @@ module Fastlane
44
44
  e = nil
45
45
  begin
46
46
  ff.runner.execute(lane, platform, parameters)
47
- rescue => ex
47
+ rescue Exception => ex # rubocop:disable Lint/RescueException
48
+ # We also catch Exception, since the implemented action might send a SystemExit signal
49
+ # (or similar). We still want to catch that, since we want properly finish running fastlane
50
+ # Tested with `xcake`, which throws a `Xcake::Informative` object
51
+
48
52
  UI.important 'Variable Dump:'.yellow
49
53
  UI.message Actions.lane_context
50
- UI.error ex.to_s
54
+ UI.error ex.to_s if ex.kind_of?(StandardError) # we don't want to print things like 'system exit'
51
55
  e = ex
52
56
  end
53
57
 
@@ -29,7 +29,13 @@ module Fastlane
29
29
  begin
30
30
  class_ref = Fastlane::Actions.const_get(class_name)
31
31
  rescue NameError
32
- UI.user_error!("Action '#{action}' not available, run `fastlane actions` to get a full list")
32
+ if Fastlane::Actions.formerly_bundled_actions.include?(action)
33
+ # This was a formerly bundled action which is now a plugin.
34
+ UI.verbose(caller.join("\n"))
35
+ UI.user_error!("The action '#{action}' is no longer bundled with fastlane. You can install it using `fastlane add_plugin #{action}`")
36
+ else
37
+ UI.user_error!("Action '#{action}' not available, run `fastlane actions` to get a full list")
38
+ end
33
39
  end
34
40
 
35
41
  r = Runner.new
@@ -0,0 +1,59 @@
1
+ module Fastlane
2
+ # Use the RubyGems API to get all fastlane plugins
3
+ class PluginFetcher
4
+ require 'fastlane_core'
5
+ require 'fastlane/plugins/plugin_manager'
6
+
7
+ # Returns an array of FastlanePlugin objects
8
+ def self.fetch_gems(search_query: nil)
9
+ require 'json'
10
+ require 'open-uri'
11
+ url = "https://rubygems.org/api/v1/search.json?query=#{PluginManager.plugin_prefix}"
12
+ results = JSON.parse(open(url).read)
13
+
14
+ plugins = results.collect do |current|
15
+ FastlanePlugin.new(current)
16
+ end
17
+
18
+ return plugins if search_query.to_s.length == 0
19
+
20
+ plugins.keep_if do |current|
21
+ current.full_name.include?(search_query)
22
+ end
23
+ end
24
+
25
+ def self.update_md_file!
26
+ @plugins = fetch_gems
27
+
28
+ lib_path = FastlaneCore::Helper.gem_path('fastlane')
29
+ template_path = File.join(lib_path, "lib/assets/AvailablePlugins.md.erb")
30
+ md = ERB.new(File.read(template_path), nil, '<>').result(binding) # http://www.rrn.dk/rubys-erb-templating-system
31
+
32
+ puts md
33
+ output_path = "docs/AvailablePlugins.md"
34
+ File.write(output_path, md)
35
+ FastlaneCore::UI.success("Successfully written plugin file to '#{output_path}'")
36
+ end
37
+ end
38
+
39
+ class FastlanePlugin
40
+ attr_accessor :full_name
41
+ attr_accessor :name
42
+ attr_accessor :downloads
43
+ attr_accessor :info
44
+ attr_accessor :homepage
45
+
46
+ def initialize(hash)
47
+ self.full_name = hash["name"]
48
+ self.name = self.full_name.gsub(PluginManager.plugin_prefix, '')
49
+ self.downloads = hash["downloads"]
50
+ self.info = hash["info"]
51
+ self.homepage = hash["homepage_uri"]
52
+ end
53
+
54
+ def linked_title
55
+ return "`#{name}`" if homepage.to_s.length == 0
56
+ return "[#{name}](#{homepage})"
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,86 @@
1
+ module Fastlane
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 fastlane add_plugin #{plugin_info.plugin_name}\n"
34
+ @ui.success "\nfrom a fastlane-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 Fastlane
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,47 @@
1
+ module Fastlane
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
+
9
+ def initialize(plugin_name, author, email, summary)
10
+ @plugin_name = plugin_name
11
+ @author = author
12
+ @email = email
13
+ @summary = summary
14
+ end
15
+
16
+ def gem_name
17
+ "#{Fastlane::PluginManager::FASTLANE_PLUGIN_PREFIX}#{plugin_name}"
18
+ end
19
+
20
+ def require_path
21
+ gem_name.tr('-', '/')
22
+ end
23
+
24
+ def actions_path
25
+ File.join(require_path, 'actions')
26
+ end
27
+
28
+ def helper_path
29
+ File.join(require_path, 'helper')
30
+ end
31
+
32
+ # Used to expose a local binding for use in ERB templating
33
+ #
34
+ # rubocop:disable Style/AccessorMethodName
35
+ def get_binding
36
+ binding
37
+ end
38
+ # rubocop:enable Style/AccessorMethodName
39
+
40
+ def ==(other)
41
+ @plugin_name == other.plugin_name &&
42
+ @author == other.author &&
43
+ @email == other.email &&
44
+ @summary == other.summary
45
+ end
46
+ end
47
+ end