fastlane 0.10.0 → 0.11.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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/bin/fastlane +10 -3
  3. data/lib/assets/FastfileTemplate +52 -46
  4. data/lib/fastlane.rb +1 -0
  5. data/lib/fastlane/action.rb +14 -0
  6. data/lib/fastlane/actions/actions_helper.rb +1 -0
  7. data/lib/fastlane/actions/add_git_tag.rb +4 -0
  8. data/lib/fastlane/actions/cert.rb +4 -0
  9. data/lib/fastlane/actions/clean_build_artifacts.rb +4 -0
  10. data/lib/fastlane/actions/commit_version_bump.rb +4 -0
  11. data/lib/fastlane/actions/crashlytics.rb +5 -0
  12. data/lib/fastlane/actions/default_platform.rb +31 -0
  13. data/lib/fastlane/actions/deliver.rb +4 -0
  14. data/lib/fastlane/actions/deploygate.rb +4 -0
  15. data/lib/fastlane/actions/ensure_git_status_clean.rb +4 -0
  16. data/lib/fastlane/actions/fastlane_version.rb +4 -0
  17. data/lib/fastlane/actions/frameit.rb +4 -0
  18. data/lib/fastlane/actions/gcovr.rb +4 -0
  19. data/lib/fastlane/actions/hipchat.rb +4 -0
  20. data/lib/fastlane/actions/hockey.rb +4 -0
  21. data/lib/fastlane/actions/increment_build_number.rb +4 -0
  22. data/lib/fastlane/actions/increment_version_number.rb +4 -0
  23. data/lib/fastlane/actions/install_carthage.rb +4 -0
  24. data/lib/fastlane/actions/install_cocapods.rb +4 -0
  25. data/lib/fastlane/actions/ipa.rb +10 -0
  26. data/lib/fastlane/actions/notify.rb +4 -0
  27. data/lib/fastlane/actions/opt_out_usage.rb +4 -0
  28. data/lib/fastlane/actions/pem.rb +4 -2
  29. data/lib/fastlane/actions/produce.rb +4 -0
  30. data/lib/fastlane/actions/push_to_git_remote.rb +4 -0
  31. data/lib/fastlane/actions/register_devices.rb +4 -0
  32. data/lib/fastlane/actions/reset_git_repo.rb +4 -0
  33. data/lib/fastlane/actions/resign.rb +4 -0
  34. data/lib/fastlane/actions/s3.rb +4 -0
  35. data/lib/fastlane/actions/say.rb +4 -0
  36. data/lib/fastlane/actions/sigh.rb +4 -0
  37. data/lib/fastlane/actions/slack.rb +79 -66
  38. data/lib/fastlane/actions/snapshot.rb +4 -0
  39. data/lib/fastlane/actions/team_id.rb +4 -0
  40. data/lib/fastlane/actions/team_name.rb +4 -0
  41. data/lib/fastlane/actions/testmunk.rb +4 -0
  42. data/lib/fastlane/actions/typetalk.rb +4 -0
  43. data/lib/fastlane/actions/update_project_code_signing.rb +4 -0
  44. data/lib/fastlane/actions/xcode_select.rb +4 -0
  45. data/lib/fastlane/actions/xcodebuild.rb +24 -0
  46. data/lib/fastlane/actions/xctool.rb +4 -0
  47. data/lib/fastlane/docs_generator.rb +37 -17
  48. data/lib/fastlane/fast_file.rb +100 -42
  49. data/lib/fastlane/lane_manager.rb +80 -40
  50. data/lib/fastlane/runner.rb +71 -27
  51. data/lib/fastlane/supported_platforms.rb +18 -0
  52. data/lib/fastlane/version.rb +1 -1
  53. metadata +6 -4
@@ -23,6 +23,10 @@ module Fastlane
23
23
  def self.author
24
24
  "KrauseFx"
25
25
  end
26
+
27
+ def self.is_supported?(platform)
28
+ platform == :ios
29
+ end
26
30
  end
27
31
  end
28
32
  end
@@ -1,30 +1,50 @@
1
1
  module Fastlane
2
2
  class DocsGenerator
3
3
  def self.run(output_path, ff)
4
- output = "fastlane documentation\n"
5
- output += "================\n"
4
+ output = ["fastlane documentation"]
5
+ output << "================"
6
6
 
7
- output += "# Installation\n"
8
- output += "```\n"
9
- output += "sudo gem install fastlane\n"
10
- output += "```\n\n"
7
+ output << "# Installation"
8
+ output << "```"
9
+ output << "sudo gem install fastlane"
10
+ output << "```"
11
11
 
12
- output += "# Available Actions\n"
12
+ output << "# Available Actions"
13
13
 
14
- ff.runner.description_blocks.each do |lane, description|
15
- output += "## #{lane}\n\n"
16
- output += "```\n"
17
- output += "fastlane #{lane}\n"
18
- output += "```\n\n"
19
- output += description + "\n"
14
+ all_keys = ff.runner.description_blocks.keys.reject(&:nil?)
15
+ all_keys.unshift(nil) # because we want root elements on top.. always! They have key nil
16
+
17
+ all_keys.each do |platform|
18
+ output << "## #{platform}" if platform
19
+
20
+ value = ff.runner.description_blocks[platform]
21
+
22
+ value.each do |lane, description|
23
+ output << render(platform, lane, description)
24
+ end
25
+
26
+ output << "----"
20
27
  end
21
28
 
22
- output += "\n\n----\n"
23
- output += "More information about fastlane can be found on [https://fastlane.tools](https://fastlane.tools).\n\n"
24
- output += "The documentation of fastlane can be found on [GitHub](https://github.com/KrauseFx/fastlane)"
29
+ output << "More information about fastlane can be found on [https://fastlane.tools](https://fastlane.tools)."
30
+ output << "The documentation of fastlane can be found on [GitHub](https://github.com/KrauseFx/fastlane)"
25
31
 
26
- File.write(output_path, output)
32
+ File.write(output_path, output.join("\n"))
27
33
  Helper.log.info "Successfully generated documentation to path '#{File.expand_path(output_path)}'".green
28
34
  end
35
+
36
+ private
37
+
38
+ def self.render(platform, lane, description)
39
+ full_name = [platform, lane].reject(&:nil?).join(' ')
40
+
41
+ output = []
42
+ output << "### #{full_name}"
43
+ output << "```"
44
+ output << "fastlane #{full_name}"
45
+ output << "```"
46
+ output << description
47
+ output
48
+ end
29
49
  end
30
50
  end
@@ -12,14 +12,6 @@ module Fastlane
12
12
  parse(content)
13
13
  end
14
14
 
15
- def collector
16
- @collector ||= ActionCollector.new
17
- end
18
-
19
- def desc_collection
20
- @desc_collection ||= []
21
- end
22
-
23
15
  def parse(data)
24
16
  @runner = Runner.new
25
17
 
@@ -30,55 +22,51 @@ module Fastlane
30
22
  self
31
23
  end
32
24
 
33
- def lane(key, &block)
34
- desc = desc_collection.join("\n\n")
35
- @runner.set_block(key, block, desc)
36
- @desc_collection = nil # reset again
37
- end
38
25
 
39
- def before_all(&block)
40
- @runner.set_before_all(block)
41
- end
26
+ #####################################################
27
+ # @!group DSL
28
+ #####################################################
42
29
 
43
- def after_all(&block)
44
- @runner.set_after_all(block)
45
- end
46
30
 
47
- def error(&block)
48
- @runner.set_error(block)
49
- end
31
+ # User defines a new lane
32
+ def lane(lane_name, &block)
33
+ raise "You have to pass a block using 'do' for lane '#{lane_name}'. Make sure you read the docs on GitHub.".red unless block
34
+
35
+ desc = desc_collection.join("\n\n")
36
+ platform = @current_platform
50
37
 
51
- # Speak out loud
52
- def say(value)
53
- # Overwrite this, since there is already a 'say' method defined in the Ruby standard library
54
- value ||= yield
55
- Actions.execute_action('say') do
56
- Fastlane::Actions::SayAction.run([value])
57
- end
38
+ @runner.set_block(lane_name, platform, block, desc)
39
+
40
+ @desc_collection = nil # reset the collected description again for the next lane
58
41
  end
42
+
43
+ # User defines a platform block
44
+ def platform(platform_name, &block)
45
+ SupportedPlatforms.verify!platform_name
59
46
 
60
- def actions_path(path)
61
- raise "Path '#{path}' not found!".red unless File.directory?(path)
47
+ @current_platform = platform_name
62
48
 
63
- Actions.load_external_actions(path)
49
+ block.call
50
+
51
+ @current_platform = nil
64
52
  end
65
53
 
66
- # Execute shell command
67
- def sh(command)
68
- Actions.execute_action(command) do
69
- Actions.sh_no_action(command)
70
- end
54
+ # Is executed before each test run
55
+ def before_all(&block)
56
+ @runner.set_before_all(@current_platform, block)
71
57
  end
72
58
 
73
- # Fastfile was finished executing
74
- def did_finish
75
- collector.did_finish
59
+ # Is executed after each test run
60
+ def after_all(&block)
61
+ @runner.set_after_all(@current_platform, block)
76
62
  end
77
63
 
78
- def desc(string)
79
- desc_collection << string
64
+ # Is executed if an error occured during fastlane execution
65
+ def error(&block)
66
+ @runner.set_error(@current_platform, block)
80
67
  end
81
68
 
69
+ # Is used to look if the method is implemented as an action
82
70
  def method_missing(method_sym, *arguments, &_block)
83
71
  # First, check if there is a predefined method in the actions folder
84
72
 
@@ -96,6 +84,9 @@ module Fastlane
96
84
 
97
85
  step_name = class_ref.step_text rescue nil
98
86
  step_name = method_sym.to_s unless step_name
87
+
88
+ verify_supported_os(method_sym, class_ref)
89
+
99
90
  Helper.log_alert("Step: " + step_name)
100
91
 
101
92
  begin
@@ -112,5 +103,72 @@ module Fastlane
112
103
  raise "Action '#{method_sym}' of class '#{class_name}' was found, but has no `run` method.".red
113
104
  end
114
105
  end
106
+
107
+
108
+ #####################################################
109
+ # @!group Other things
110
+ #####################################################
111
+
112
+ # Speak out loud
113
+ def say(value)
114
+ # Overwrite this, since there is already a 'say' method defined in the Ruby standard library
115
+ value ||= yield
116
+ Actions.execute_action('say') do
117
+ Fastlane::Actions::SayAction.run([value])
118
+ end
119
+ end
120
+
121
+ # Is the given key a platform block or a lane?
122
+ def is_platform_block?(key)
123
+ raise 'No key given'.red unless key
124
+
125
+ return false if self.runner.blocks[nil][key.to_sym]
126
+ return true if self.runner.blocks[key.to_sym].kind_of?Hash
127
+
128
+ raise "Could not find '#{key}'. Available lanes: #{self.runner.available_lanes.join(', ')}".red
129
+ end
130
+
131
+ def actions_path(path)
132
+ raise "Path '#{path}' not found!".red unless File.directory?(path)
133
+
134
+ Actions.load_external_actions(path)
135
+ end
136
+
137
+ # Execute shell command
138
+ def sh(command)
139
+ Actions.execute_action(command) do
140
+ Actions.sh_no_action(command)
141
+ end
142
+ end
143
+
144
+ def verify_supported_os(name, class_ref)
145
+ if class_ref.respond_to?(:is_supported?)
146
+ if Actions.lane_context[Actions::SharedValues::PLATFORM_NAME]
147
+ # This value is filled in based on the executed platform block. Might be nil when lane is in root of Fastfile
148
+ platform = Actions.lane_context[Actions::SharedValues::PLATFORM_NAME]
149
+
150
+ unless class_ref.is_supported?(platform)
151
+ raise "Action '#{name}' doesn't support required operating system '#{platform}'.".red
152
+ end
153
+ end
154
+ end
155
+ end
156
+
157
+ # Fastfile was finished executing
158
+ def did_finish
159
+ collector.did_finish
160
+ end
161
+
162
+ def desc(string)
163
+ desc_collection << string
164
+ end
165
+
166
+ def collector
167
+ @collector ||= ActionCollector.new
168
+ end
169
+
170
+ def desc_collection
171
+ @desc_collection ||= []
172
+ end
115
173
  end
116
174
  end
@@ -1,66 +1,59 @@
1
1
  module Fastlane
2
2
  class LaneManager
3
- def self.cruise_lanes(lanes, env=nil)
4
- Actions.lane_context[Actions::SharedValues::ENVIRONMENT] = env
5
- raise 'lanes must be an array' unless lanes.is_a?(Array)
3
+ # @param platform The name of the platform to execute
4
+ # @param lane_name The name of the lane to execute
5
+ def self.cruise_lane(platform, lane, env = nil)
6
+ raise 'lane must be a string' unless (lane.is_a?(String) or lane.nil?)
7
+ raise 'platform must be a string' unless (platform.is_a?(String) or platform.nil?)
6
8
 
7
9
  ff = Fastlane::FastFile.new(File.join(Fastlane::FastlaneFolder.path, 'Fastfile'))
8
10
 
9
- if lanes.count == 0
10
- loop do
11
- Helper.log.error "You must provide a lane to drive. Available lanes:"
12
- available = ff.runner.available_lanes
13
-
14
- available.each_with_index do |lane, index|
15
- puts "#{index + 1}) #{lane}"
16
- end
17
-
18
- i = gets.strip.to_i - 1
19
- if i >= 0 and (available[i] rescue nil)
20
- lanes = [available[i]]
21
- Helper.log.info "Driving the lane #{lanes.first}. Next time launch fastlane using `fastlane #{lanes.first}`".green
22
- break # yeah
23
- end
11
+ unless (ff.is_platform_block?lane rescue false) # rescue, because this raises an exception if it can't be found at all
12
+ # maybe the user specified a default platform
13
+ # We'll only do this, if the lane specified isn't a platform, as we want to list all platforms then
14
+ platform ||= Actions.lane_context[Actions::SharedValues::DEFAULT_PLATFORM]
15
+ end
24
16
 
25
- Helper.log.error "Invalid input. Please enter the number of the lane you want to use".red
17
+ if not platform and lane
18
+ # Either, the user runs a specific lane in root or want to auto complete the available lanes for a platform
19
+ # e.g. `fastlane ios` should list all available iOS actions
20
+ if ff.is_platform_block?lane
21
+ platform = lane
22
+ lane = nil
26
23
  end
27
24
  end
28
25
 
29
- # Making sure the default '.env' and '.env.default' get loaded
30
- env_file = File.join(Fastlane::FastlaneFolder.path || "", '.env')
31
- env_default_file = File.join(Fastlane::FastlaneFolder.path || "", '.env.default')
32
- Dotenv.load(env_file, env_default_file)
26
+ platform, lane = choose_lane(ff, platform) unless lane
33
27
 
34
- # Loads .env file for the environment passed in through options
35
- if env
36
- env_file = File.join(Fastlane::FastlaneFolder.path || "", ".env.#{env}")
37
- Helper.log.info "Loading from '#{env_file}'".green
38
- Dotenv.overload(env_file)
39
- end
28
+ load_dot_env(env)
40
29
 
41
- start = Time.now
30
+ started = Time.now
42
31
  e = nil
43
32
  begin
44
- lanes.each do |key|
45
- ff.runner.execute(key)
46
- end
33
+ ff.runner.execute(lane, platform)
47
34
  rescue => ex
48
- if Actions.lane_context.count > 0
49
- Helper.log.info 'Variable Dump:'.yellow
50
- Helper.log.info Actions.lane_context
51
- end
35
+ Helper.log.info 'Variable Dump:'.yellow
36
+ Helper.log.info Actions.lane_context
52
37
  Helper.log.fatal ex
53
38
  e = ex
54
39
  end
55
40
 
41
+ duration = ((Time.now - started) / 60.0).round
42
+
43
+ finish_fastlane(ff, duration, e)
44
+
45
+ return ff
46
+ end
47
+
48
+ # All the finishing up that needs to be done
49
+ def self.finish_fastlane(ff, duration, error)
56
50
  thread = ff.did_finish
57
51
 
58
52
  # Finished with all the lanes
59
53
  Fastlane::JUnitGenerator.generate(Fastlane::Actions.executed_actions)
60
54
 
61
- duration = ((Time.now - start) / 60.0).round
62
55
 
63
- unless e
56
+ unless error
64
57
  thread.join # to wait for the request to be finished
65
58
  if duration > 5
66
59
  Helper.log.info "fastlane.tools just saved you #{duration} minutes! 🎉".green
@@ -70,9 +63,56 @@ module Fastlane
70
63
  else
71
64
  thread.join # to wait for the request to be finished
72
65
  Helper.log.fatal 'fastlane finished with errors'.red
73
- raise e
66
+ raise error
67
+ end
68
+ end
69
+
70
+ # Lane chooser if user didn't provide a lane
71
+ # @param platform: is probably nil, but user might have called `fastlane android`, and only wants to list those actions
72
+ def self.choose_lane(ff, platform)
73
+ loop do
74
+ Helper.log.error "You must provide a lane to drive. Available lanes:"
75
+ available = ff.runner.available_lanes(platform)
76
+
77
+ available.each_with_index do |lane, index|
78
+ puts "#{index + 1}) #{lane}"
79
+ end
80
+
81
+ i = $stdin.gets.strip.to_i - 1
82
+ if i >= 0 and (available[i] rescue nil)
83
+ selection = available[i]
84
+ Helper.log.info "Driving the lane #{selection}. Next time launch fastlane using `fastlane #{selection}`".green
85
+ platform = selection.split(' ')[0]
86
+ lane_name = selection.split(' ')[1]
87
+
88
+ unless lane_name # no specific platform, just a root lane
89
+ lane_name = platform
90
+ platform = nil
91
+ end
92
+
93
+ return platform, lane_name # yeah
94
+ end
95
+
96
+ Helper.log.error "Invalid input. Please enter the number of the lane you want to use".red
74
97
  end
75
98
  end
76
99
 
100
+ def self.load_dot_env(env)
101
+ require 'dotenv'
102
+
103
+ Actions.lane_context[Actions::SharedValues::ENVIRONMENT] = env
104
+
105
+ # Making sure the default '.env' and '.env.default' get loaded
106
+ env_file = File.join(Fastlane::FastlaneFolder.path || "", '.env')
107
+ env_default_file = File.join(Fastlane::FastlaneFolder.path || "", '.env.default')
108
+ Dotenv.load(env_file, env_default_file)
109
+
110
+ # Loads .env file for the environment passed in through options
111
+ if env
112
+ env_file = File.join(Fastlane::FastlaneFolder.path || "", ".env.#{env}")
113
+ Helper.log.info "Loading from '#{env_file}'".green
114
+ Dotenv.overload(env_file)
115
+ end
116
+ end
77
117
  end
78
118
  end
@@ -1,27 +1,43 @@
1
1
  module Fastlane
2
2
  class Runner
3
3
 
4
- def execute(key)
5
- key = key.to_sym
6
- Helper.log.info "Driving the lane '#{key}'".green
7
- Actions.lane_context[Actions::SharedValues::LANE_NAME] = key
8
- ENV["FASTLANE_LANE_NAME"] = key.to_s
4
+ # This will take care of executing **one** lane.
5
+ # @param lane_name The name of the lane to execute
6
+ # @param platform The name of the platform to execute
7
+ def execute(lane, platform = nil)
8
+ raise "No lane given" unless lane
9
+
10
+ lane = lane.to_sym
11
+
12
+ platform = platform.to_sym if platform # might be nil, which is okay => root element
13
+
14
+ Actions.lane_context[Actions::SharedValues::PLATFORM_NAME] = platform # set this in any case: important
15
+
16
+ full_lane_name = [platform, lane].reject(&:nil?).join(' ')
17
+ Helper.log.info "Driving the lane '#{full_lane_name}'".green
18
+ Actions.lane_context[Actions::SharedValues::LANE_NAME] = full_lane_name
19
+ ENV["FASTLANE_LANE_NAME"] = full_lane_name
9
20
 
10
21
  return_val = nil
11
22
 
12
23
  path_to_use = Fastlane::FastlaneFolder.path || Dir.pwd
13
24
  Dir.chdir(path_to_use) do # the file is located in the fastlane folder
14
- @before_all.call(key) if @before_all
15
-
16
- return_val = nil
17
25
 
18
- if blocks[key]
19
- return_val = blocks[key].call
20
- else
21
- raise "Could not find lane for type '#{key}'. Available lanes: #{available_lanes.join(', ')}".red
26
+ unless (blocks[platform][lane] rescue nil)
27
+ raise "Could not find lane '#{full_lane_name}'. Available lanes: #{available_lanes.join(', ')}".red
22
28
  end
23
29
 
24
- @after_all.call(key) if @after_all # this is only called if no exception was raised before
30
+ # Call the platform specific before_all block and then the general one
31
+ before_all_blocks[platform].call(lane) if (before_all_blocks[platform] and platform != nil)
32
+ before_all_blocks[nil].call(lane) if before_all_blocks[nil]
33
+
34
+ return_val = blocks[platform][lane].call
35
+
36
+
37
+ # `after_all` is only called if no exception was raised before
38
+ # Call the platform specific before_all block and then the general one
39
+ after_all_blocks[platform].call(lane) if (after_all_blocks[platform] and platform != nil)
40
+ after_all_blocks[nil].call(lane) if (after_all_blocks[nil])
25
41
  end
26
42
 
27
43
  return return_val
@@ -29,41 +45,69 @@ module Fastlane
29
45
  Dir.chdir(path_to_use) do
30
46
  # Provide error block exception without colour code
31
47
  error_ex = ex.exception(ex.message.gsub(/\033\[\d+m/, ''))
32
- @error.call(key, error_ex) if @error # notify the block
48
+
49
+ error_blocks[platform].call(lane, error_ex) if (error_blocks[platform] and platform != nil)
50
+ error_blocks[nil].call(lane, error_ex) if error_blocks[nil]
33
51
  end
34
52
  raise ex
35
53
  end
36
54
 
37
- def available_lanes
38
- blocks.keys
55
+ # @param filter_platform: Filter, to only show the lanes of a given platform
56
+ def available_lanes(filter_platform = nil)
57
+ all = []
58
+ blocks.each do |platform, lane|
59
+ next if (filter_platform and filter_platform.to_s != platform.to_s) # skip actions that don't match
60
+
61
+ lane.each do |lane_name, block|
62
+ all << [platform, lane_name].reject(&:nil?).join(' ')
63
+ end
64
+ end
65
+ all
39
66
  end
40
67
 
41
68
  # Called internally
42
- def set_before_all(block)
43
- @before_all = block
69
+ def set_before_all(platform, block)
70
+ before_all_blocks[platform] = block
44
71
  end
45
72
 
46
- def set_after_all(block)
47
- @after_all = block
73
+ def set_after_all(platform, block)
74
+ after_all_blocks[platform] = block
48
75
  end
49
76
 
50
- def set_error(block)
51
- @error = block
77
+ def set_error(platform, block)
78
+ error_blocks[platform] = block
52
79
  end
53
80
 
54
- # @param key: The name of the lane
81
+ # @param lane: The name of the lane
82
+ # @param platform: The platform for the given block - might be nil - nil is actually the root of Fastfile with no specific platform
55
83
  # @param block: The block of the lane
56
84
  # @param desc: Description of this action
57
- def set_block(key, block, desc = nil)
58
- raise "Lane '#{key}' was defined multiple times!".red if blocks[key]
59
- blocks[key] = block
60
- description_blocks[key] = desc
85
+ def set_block(lane, platform, block, desc = nil)
86
+ blocks[platform] ||= {}
87
+ description_blocks[platform] ||= {}
88
+
89
+ raise "Lane '#{lane}' was defined multiple times!".red if blocks[platform][lane]
90
+
91
+ blocks[platform][lane] = block
92
+ description_blocks[platform][lane] = desc
61
93
  end
62
94
 
63
95
  def blocks
64
96
  @blocks ||= {}
65
97
  end
66
98
 
99
+ def before_all_blocks
100
+ @before_all ||= {}
101
+ end
102
+
103
+ def after_all_blocks
104
+ @after_all ||= {}
105
+ end
106
+
107
+ def error_blocks
108
+ @error_blocks ||= {}
109
+ end
110
+
67
111
  def description_blocks
68
112
  @description_blocks ||= {}
69
113
  end