fastlane 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
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