Class: Fastlane::Actions::SwiftlintAction

Inherits:
Fastlane::Action show all
Defined in:
fastlane/lib/fastlane/actions/swiftlint.rb

Constant Summary

Constants inherited from Fastlane::Action

Fastlane::Action::AVAILABLE_CATEGORIES, Fastlane::Action::RETURN_TYPES

Documentation collapse

Class Method Summary collapse

Methods inherited from Fastlane::Action

action_name, author, deprecated_notes, details, lane_context, method_missing, other_action, return_type, sample_return_value, shell_out_should_use_bundle_exec?, step_text

Class Method Details

.authorsObject



211
212
213
# File 'fastlane/lib/fastlane/actions/swiftlint.rb', line 211

def self.authors
  ["KrauseFx"]
end

.available_optionsObject



110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'fastlane/lib/fastlane/actions/swiftlint.rb', line 110

def self.available_options
  [
    FastlaneCore::ConfigItem.new(key: :mode,
                                 env_name: "FL_SWIFTLINT_MODE",
                                 description: "SwiftLint mode: :lint, :fix, :autocorrect or :analyze",
                                 type: Symbol,
                                 default_value: :lint,
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :path,
                                 env_name: "FL_SWIFTLINT_PATH",
                                 description: "Specify path to lint",
                                 optional: true,
                                 verify_block: proc do |value|
                                   UI.user_error!("Couldn't find path '#{File.expand_path(value)}'") unless File.exist?(value)
                                 end),
    FastlaneCore::ConfigItem.new(key: :output_file,
                                 env_name: "FL_SWIFTLINT_OUTPUT_FILE",
                                 description: 'Path to output SwiftLint result',
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :config_file,
                                 env_name: "FL_SWIFTLINT_CONFIG_FILE",
                                 description: 'Custom configuration file of SwiftLint',
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :strict,
                                 env_name: "FL_SWIFTLINT_STRICT",
                                 description: 'Fail on warnings? (true/false)',
                                 default_value: false,
                                 type: Boolean,
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :files,
                                 env_name: "FL_SWIFTLINT_FILES",
                                 description: 'List of files to process',
                                 type: Array,
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :ignore_exit_status,
                                 env_name: "FL_SWIFTLINT_IGNORE_EXIT_STATUS",
                                 description: "Ignore the exit status of the SwiftLint command, so that serious violations \
                                              don't fail the build (true/false)",
                                 default_value: false,
                                 type: Boolean,
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :raise_if_swiftlint_error,
                                 env_name: "FL_SWIFTLINT_RAISE_IF_SWIFTLINT_ERROR",
                                 description: "Raises an error if swiftlint fails, so you can fail CI/CD jobs if necessary \
                                              (true/false)",
                                 default_value: false,
                                 type: Boolean,
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :reporter,
                                 env_name: "FL_SWIFTLINT_REPORTER",
                                 description: "Choose output reporter. Available: xcode, json, csv, checkstyle, codeclimate, \
                                               junit, html, emoji, sonarqube, markdown, github-actions-logging",
                                 optional: true,
                                 verify_block: proc do |value|
                                   available = ['xcode', 'json', 'csv', 'checkstyle', 'codeclimate', 'junit', 'html', 'emoji', 'sonarqube', 'markdown', 'github-actions-logging']
                                   UI.important("Known 'reporter' values are '#{available.join("', '")}'. If you're receiving errors from swiftlint related to the reporter, make sure the reporter identifier you're using is correct and it's supported by your version of swiftlint.") unless available.include?(value)
                                 end),
    FastlaneCore::ConfigItem.new(key: :quiet,
                                 env_name: "FL_SWIFTLINT_QUIET",
                                 description: "Don't print status logs like 'Linting <file>' & 'Done linting'",
                                 default_value: false,
                                 type: Boolean,
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :executable,
                                 env_name: "FL_SWIFTLINT_EXECUTABLE",
                                 description: "Path to the `swiftlint` executable on your machine",
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :format,
                                 env_name: "FL_SWIFTLINT_FORMAT",
                                 description: "Format code when mode is :autocorrect",
                                 default_value: false,
                                 type: Boolean,
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :no_cache,
                                 env_name: "FL_SWIFTLINT_NO_CACHE",
                                 description: "Ignore the cache when mode is :autocorrect or :lint",
                                 default_value: false,
                                 type: Boolean,
                                 optional: true),
    FastlaneCore::ConfigItem.new(key: :compiler_log_path,
                                 env_name: "FL_SWIFTLINT_COMPILER_LOG_PATH",
                                 description: "Compiler log path when mode is :analyze",
                                 optional: true,
                                 verify_block: proc do |value|
                                   UI.user_error!("Couldn't find compiler_log_path '#{File.expand_path(value)}'") unless File.exist?(value)
                                 end),
    FastlaneCore::ConfigItem.new(key: :progress,
                                 env_name: "FL_SWIFTLINT_PROGRESS",
                                 description: "Show a live-updating progress bar instead of each file being processed",
                                 default_value: false,
                                 type: Boolean,
                                 optional: true)
  ]
end

.categoryObject



237
238
239
# File 'fastlane/lib/fastlane/actions/swiftlint.rb', line 237

def self.category
  :testing
end

.descriptionObject



106
107
108
# File 'fastlane/lib/fastlane/actions/swiftlint.rb', line 106

def self.description
  "Run swift code validation using SwiftLint"
end

.example_codeObject



219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
# File 'fastlane/lib/fastlane/actions/swiftlint.rb', line 219

def self.example_code
  [
    'swiftlint(
      mode: :lint,                          # SwiftLint mode: :lint (default) or :autocorrect
      path: "/path/to/lint",                 # Specify path to lint (optional)
      output_file: "swiftlint.result.json", # The path of the output file (optional)
      config_file: ".swiftlint-ci.yml",     # The path of the configuration file (optional)
      files: [                              # List of files to process (optional)
        "AppDelegate.swift",
        "path/to/project/Model.swift"
      ],
      raise_if_swiftlint_error: true,      # Allow fastlane to raise an error if swiftlint fails
      ignore_exit_status: true              # Allow fastlane to continue even if SwiftLint returns a non-zero exit status

    )'
  ]
end

.handle_swiftlint_error(ignore_exit_status, exit_status) ⇒ Object



241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
# File 'fastlane/lib/fastlane/actions/swiftlint.rb', line 241

def self.handle_swiftlint_error(ignore_exit_status, exit_status)
  if ignore_exit_status
    failure_suffix = 'which would normally fail the build.'
    secondary_message = 'fastlane will continue because the `ignore_exit_status` option was used! 🙈'
  else
    failure_suffix = 'which represents a failure.'
    secondary_message = 'If you want fastlane to continue anyway, use the `ignore_exit_status` option. 🙈'
  end

  UI.important("")
  UI.important("SwiftLint finished with exit code #{exit_status}, #{failure_suffix}")
  UI.important(secondary_message)
  UI.important("")
  UI.user_error!("SwiftLint finished with errors (exit code: #{exit_status})") unless ignore_exit_status
end

.is_supported?(platform) ⇒ Boolean

Returns:



215
216
217
# File 'fastlane/lib/fastlane/actions/swiftlint.rb', line 215

def self.is_supported?(platform)
  [:ios, :mac].include?(platform)
end

.optional_flags(params) ⇒ Object



52
53
54
55
56
57
58
59
60
61
62
63
# File 'fastlane/lib/fastlane/actions/swiftlint.rb', line 52

def self.optional_flags(params)
  command = ""
  command << supported_option_switch(params, :strict, "0.9.2", true)
  command << " --config #{params[:config_file].shellescape}" if params[:config_file]
  command << " --reporter #{params[:reporter]}" if params[:reporter]
  command << supported_option_switch(params, :quiet, "0.9.0", true)
  command << supported_option_switch(params, :format, "0.11.0", true) if params[:mode] == :autocorrect
  command << supported_no_cache_option(params) if params[:no_cache]
  command << " --compiler-log-path #{params[:compiler_log_path].shellescape}" if params[:compiler_log_path]
  command << supported_option_switch(params, :progress, "0.49.1", true) if params[:progress]
  return command
end

.outputObject



205
206
# File 'fastlane/lib/fastlane/actions/swiftlint.rb', line 205

def self.output
end

.path_argument(params) ⇒ Object



65
66
67
68
69
70
71
# File 'fastlane/lib/fastlane/actions/swiftlint.rb', line 65

def self.path_argument(params)
  escaped_path = params[:path].shellescape
  version = swiftlint_version(executable: params[:executable])
  return " --path #{escaped_path}" if version < Gem::Version.new('0.48.0')

  " #{escaped_path}"
end

.return_valueObject



208
209
# File 'fastlane/lib/fastlane/actions/swiftlint.rb', line 208

def self.return_value
end

.run(params) ⇒ Object



4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'fastlane/lib/fastlane/actions/swiftlint.rb', line 4

def self.run(params)
  unless Helper.which('swiftlint') || params[:executable] || Helper.test?
    UI.user_error!("You have to install swiftlint using `brew install swiftlint` or specify the executable path with the `:executable` option.")
  end

  version = swiftlint_version(executable: params[:executable])
  if params[:mode] == :autocorrect && version < Gem::Version.new('0.5.0') && !Helper.test?
    UI.user_error!("Your version of swiftlint (#{version}) does not support autocorrect mode.\nUpdate swiftlint using `brew update && brew upgrade swiftlint`")
  end

  # See 'Breaking' section release notes here: https://github.com/realm/SwiftLint/releases/tag/0.43.0
  if params[:mode] == :autocorrect && version >= Gem::Version.new('0.43.0')
    UI.deprecated("Your version of swiftlint (#{version}) has deprecated autocorrect mode, please start using fix mode in input param")
    UI.important("For now, switching swiftlint mode `from :autocorrect to :fix` for you 😇")
    params[:mode] = :fix
  elsif params[:mode] == :fix && version < Gem::Version.new('0.43.0')
    UI.important("Your version of swiftlint (#{version}) does not support fix mode.\nUpdate swiftlint using `brew update && brew upgrade swiftlint`")
    UI.important("For now, switching swiftlint mode `from :fix to :autocorrect` for you 😇")
    params[:mode] = :autocorrect
  end

  mode_format = params[:mode] == :fix ? "--" : ""
  command = (params[:executable] || "swiftlint").dup
  command << " #{mode_format}#{params[:mode]}"
  command << optional_flags(params)

  if params[:files]
    if version < Gem::Version.new('0.5.1')
      UI.user_error!("Your version of swiftlint (#{version}) does not support list of files as input.\nUpdate swiftlint using `brew update && brew upgrade swiftlint`")
    end

    files = params[:files].map.with_index(0) { |f, i| "SCRIPT_INPUT_FILE_#{i}=#{f.shellescape}" }.join(" ")
    command = command.prepend("SCRIPT_INPUT_FILE_COUNT=#{params[:files].count} #{files} ")
    command << " --use-script-input-files"
  end

  command << path_argument(params) if params[:path]

  command << " > #{params[:output_file].shellescape}" if params[:output_file]

  begin
    Actions.sh(command)
  rescue
    handle_swiftlint_error(params[:ignore_exit_status], $?.exitstatus)
    raise if params[:raise_if_swiftlint_error]
  end
end

.supported_no_cache_option(params) ⇒ Object



79
80
81
82
83
84
85
# File 'fastlane/lib/fastlane/actions/swiftlint.rb', line 79

def self.supported_no_cache_option(params)
  if [:autocorrect, :fix, :lint].include?(params[:mode])
    return " --no-cache"
  else
    return ""
  end
end

.supported_option_switch(params, option, min_version, can_ignore = false) ⇒ Object

Return "--option" switch if option is on and current SwiftLint version is greater or equal than min version. Return "" otherwise.



89
90
91
92
93
94
95
96
97
98
99
100
# File 'fastlane/lib/fastlane/actions/swiftlint.rb', line 89

def self.supported_option_switch(params, option, min_version, can_ignore = false)
  return "" unless params[option]
  version = swiftlint_version(executable: params[:executable])
  if version < Gem::Version.new(min_version)
    message = "Your version of swiftlint (#{version}) does not support '--#{option}' option.\nUpdate swiftlint to #{min_version} or above using `brew update && brew upgrade swiftlint`"
    message += "\nThe option will be ignored." if can_ignore
    can_ignore ? UI.important(message) : UI.user_error!(message)
    ""
  else
    " --#{option}"
  end
end

.swiftlint_version(executable: nil) ⇒ Object

Get current SwiftLint version



74
75
76
77
# File 'fastlane/lib/fastlane/actions/swiftlint.rb', line 74

def self.swiftlint_version(executable: nil)
  binary = executable || 'swiftlint'
  Gem::Version.new(`#{binary} version`.chomp)
end