Class: Danger::DangerSwiftlint

Inherits:
Plugin
  • Object
show all
Defined in:
lib/danger_plugin.rb

Overview

Lint Swift files inside your projects. This is done using the [SwiftLint](github.com/realm/SwiftLint) tool. Results are passed out as a table in markdown.

Examples:

Specifying custom config file.


# Runs a linter with comma style disabled
swiftlint.config_file = '.swiftlint.yml'
swiftlint.lint_files

See Also:

  • artsy/eigen

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#binary_pathObject

The path to SwiftLint’s execution



23
24
25
# File 'lib/danger_plugin.rb', line 23

def binary_path
  @binary_path
end

#config_fileObject

The path to SwiftLint’s configuration file



26
27
28
# File 'lib/danger_plugin.rb', line 26

def config_file
  @config_file
end

#directoryObject

Allows you to specify a directory from where swiftlint will be run.



29
30
31
# File 'lib/danger_plugin.rb', line 29

def directory
  @directory
end

#verboseObject

Provides additional logging diagnostic information.



32
33
34
# File 'lib/danger_plugin.rb', line 32

def verbose
  @verbose
end

Instance Method Details

#excluded_files_from_config(filepath) ⇒ Array

Parses the configuration file and return the excluded files

Returns:

  • (Array)

    list of files excluded



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/danger_plugin.rb', line 146

def excluded_files_from_config(filepath)
  config = if filepath
    YAML.load_file(filepath)
  else
    {"excluded" => []}
  end

  excluded_paths = config['excluded'] || []

  # Extract excluded paths
  return excluded_paths.
    map { |path| File.join(File.dirname(filepath), path) }.
    map { |path| File.expand_path(path) }.
    select { |path| File.exists?(path) || Dir.exists?(path) }
end

#find_swift_files(files = nil, excluded_paths = [], dir_selected) ⇒ Array

Find swift files from the files glob If files are not provided it will use git modifield and added files

Returns:

  • (Array)

    swift files



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/danger_plugin.rb', line 118

def find_swift_files(files=nil, excluded_paths=[], dir_selected)
  # Assign files to lint
  files = files ? Dir.glob(files) : (git.modified_files - git.deleted_files) + git.added_files

  # Filter files to lint
  return files.
    # Ensure only swift files are selected
    select { |file| file.end_with?('.swift') }.
    # Make sure we don't fail when paths have spaces
    map { |file| Shellwords.escape(file) }.
    # Remove dups
    uniq.
    map { |file| File.expand_path(file) }.
    # Ensure only files in the selected directory
    select { |file| file.start_with?(dir_selected) }.
    # Reject files excluded on configuration
    reject { |file|
      excluded_paths.any? { |excluded_path|
        Find.find(excluded_path).
          map { |excluded_file| Shellwords.escape(excluded_file) }.
          include?(file)
      }
    }
end

#lint_files(files = nil, inline_mode: false, fail_on_error: false, additional_swiftlint_args: "") ⇒ void

This method returns an undefined value.

Lints Swift files. Will fail if ‘swiftlint` cannot be installed correctly. Generates a `markdown` list of warnings for the prose in a corpus of .markdown and .md files.

Parameters:

  • files (String) (defaults to: nil)

    A globbed string which should return the files that you want to lint, defaults to nil. if nil, modified and added files from the diff will be used.



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/danger_plugin.rb', line 42

def lint_files(files=nil, inline_mode: false, fail_on_error: false, additional_swiftlint_args: "")
  # Fails if swiftlint isn't installed
  raise "swiftlint is not installed" unless swiftlint.is_installed?

  config = if config_file
    File.expand_path(config_file)
  elsif File.file?('.swiftlint.yml')
    File.expand_path('.swiftlint.yml')
  else
    nil
  end
  log "Using config file: #{config}"
  
  dir_selected = directory ? File.expand_path(directory) : Dir.pwd
  log "Swiftlint will be run from #{dir_selected}"

  # Extract excluded paths
  excluded_paths = excluded_files_from_config(config)

  # Extract swift files (ignoring excluded ones)
  files = find_swift_files(files, excluded_paths, dir_selected)
  log "Swiftlint will lint the following files: #{files.join(', ')}"

  # Prepare swiftlint options
  options = {
    config: config,
    reporter: 'json',
    quiet: true,
    pwd: dir_selected
  }
  log "linting with options: #{options}"

  # Lint each file and collect the results
  issues = run_swiftlint(files, options, additional_swiftlint_args)
  log "Received from Swiftlint: #{issues}"

  # Filter warnings and errors
  warnings = issues.select { |issue| issue['severity'] == 'Warning' }
  errors = issues.select { |issue| issue['severity'] == 'Error' }

  if inline_mode
    # Reprt with inline comment
    send_inline_comment(warnings, "warn")
    send_inline_comment(errors, "fail")
  else
    # Report if any warning or error
    if warnings.count > 0 || errors.count > 0
      message = "### SwiftLint found issues\n\n"
      message << markdown_issues(warnings, 'Warnings') unless warnings.empty?
      message << markdown_issues(errors, 'Errors') unless errors.empty?
      markdown message

      # Fail Danger on errors
      if fail_on_error && errors.count > 0
        fail "Failed due to SwiftLint errors"
      end
    end
  end
end

#log(text) ⇒ Object



200
201
202
# File 'lib/danger_plugin.rb', line 200

def log(text)
  puts(text) if @verbose
end

#markdown_issues(results, heading) ⇒ String

Create a markdown table from swiftlint issues

Returns:

  • (String)


165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/danger_plugin.rb', line 165

def markdown_issues (results, heading)
  message = "#### #{heading}\n\n"

  message << "File | Line | Reason |\n"
  message << "| --- | ----- | ----- |\n"

  results.each do |r|
    filename = r['file'].split('/').last
    line = r['line']
    reason = r['reason']

    message << "#{filename} | #{line} | #{reason} \n"
  end

  message
end

#run_swiftlint(files, options, additional_swiftlint_args) ⇒ Array

Run swiftlint on each file and aggregate collect the issues

Returns:

  • (Array)

    swiftlint issues



105
106
107
108
109
110
111
112
# File 'lib/danger_plugin.rb', line 105

def run_swiftlint(files, options, additional_swiftlint_args)
  files
    .map { |file| options.merge({path: file})}
    .map { |full_options| swiftlint.lint(full_options, additional_swiftlint_args)}
    .reject { |s| s == '' }
    .map { |s| JSON.parse(s).flatten }
    .flatten
end

#send_inline_comment(results, method) ⇒ void

This method returns an undefined value.

Send inline comment with danger’s warn or fail method



185
186
187
188
189
190
191
# File 'lib/danger_plugin.rb', line 185

def send_inline_comment (results, method)
  dir = "#{Dir.pwd}/"
  results.each do |r|
  filename = r['file'].gsub(dir, "")
  send(method, r['reason'], file: filename, line: r['line'])
  end
end

#swiftlintSwiftLint

Make SwiftLint object for binary_path

Returns:

  • (SwiftLint)


196
197
198
# File 'lib/danger_plugin.rb', line 196

def swiftlint
  Swiftlint.new(binary_path)
end