Edited at

継続的にSwiftLintを実行する

More than 3 years have passed since last update.


目的

Swiftも2.0になり、使う機会が増えてきました。

複数人で書くことも増えてくると、コードフォーマットの統一を図りたくなってきます。

realm/SwiftLintを利用したら、github/swift-style-guideベースのチェックが出来そうです。

ローカルでやる場合は、Xcodeに設定しておけば保存などのタイミングでチェックしてくれるのですが、担当者によっては無視してしまう可能性があります。

また、SwiftLintも日々チェック項目が追加されているので、開発者のローカルのSwiftLintが古く、チェックしきれてない可能性もあります。

Androidのコードを自動で解析し、GitHubのpull requestにコメントするみたいにしておけば、CircleCI→GitHubのコメントをしてくれ、Slackと連携しておけば違反が可視化しやすそうです。


実現すること

CircleCI上で、SwiftLintを実行し、違反があればGitHubのコミットコメントorプルリクコメントに追加します。

実現にあたっては、

などを利用します。


手順


前提

CircleCIの場合、iOS planの課金を行っている必要があります。

TravisCIの方も、流れは共通だと思うので、circle.ymlなどを読み替えて頂ければと思います。

受託開発でもfastlaneとCircleCIでiOSアプリを継続的デリバリーと重複している部分もあり、端折ってる部分もあります。合わせて参照ください。


circle.ymlの設定


circle.yml

machine:

timezone: Asia/Tokyo
xcode:
version: "7.2"
dependencies:
pre:
- brew update
- brew install swiftlint
test:
override:
# workaround https://github.com/realm/SwiftLint/issues/13
- ln -s /Applications/Xcode-7.2.app /Applications/Xcode.app
- bundle exec fastlane analyze

dependenciesのフェーズで、swiftlintのインストールを行っています。

常に最新版を利用して欲しいので、先にbrew updateを行っています。(2016/01現在、どんどんアップデートされています)

testフェーズでは、fastlaneのアクション実行前に、symlinkの作成を行っています。

コメントに記載した通り、ライブラリのロードエラー(?)が発生することへの回避策です。

Xcode-7.2の部分は、machine.xcode.versionの部分と一致させておく必要があるのかな、と思っています。

'7.2'になっている部分は、CircleCI上のXcodeの最新バージョンに逐次アップデートした方が良さそうです。


Gemfileの設定


Gemfile

source "https://rubygems.org"

gem 'cocoapods'
gem 'fastlane'
gem 'checkstyle_filter-git'
gem 'saddler'
gem 'saddler-reporter-github'
gem 'swiftlint_translate_checkstyle_format'


ここでは、cocoapodsと同時に、fastlaneやチェック結果をGitHubに送るためのsaddlerなどを指定しています。

ローカルで実行する場合は、bundle install --path vendor/bundleなどを実行し、bundle exec cocoapodsbundle exec fastlane analyzeと実行する必要があります。


fastlane/Appfileの設定

プロジェクトの設定ファイルをそのまま利用するので、特に指定しません。

ファイルだけ作っておきました。


fastlane/Fastfileの設定


Fastfile

fastlane_version '1.51.0'

default_platform :ios

# And you need to set environment variables.
# GITHUB_ACCESS_TOKEN : GitHub access token, using when comment to Pull Request and create release.

platform :ios do
desc 'Analyze codes'
desc 'This may comment to pull request'
lane :analyze do
create_lint_config

swiftlint(
output_file: 'swiftlint.result.json',
config_file: '.swiftlint-ci.yml'
)

sh 'cp ../swiftlint.result.json $CIRCLE_ARTIFACTS/'

sh <<-EOS
cd ../;
cat swiftlint.result.json \
| swiftlint_translate_checkstyle_format translate \
| checkstyle_filter-git diff origin/master \
| saddler report --require saddler/reporter/github --reporter
#{reporter}
EOS

delete_lint_config
end

def create_lint_config
File.delete('../.swiftlint-ci.yml') if File.exist?('../.swiftlint-ci.yml')
require 'yaml'
config = YAML.load_file('../.swiftlint.yml')
config['reporter'] = 'json'
open('../.swiftlint-ci.yml', 'w') do |file|
YAML.dump(config, file)
end
end

def delete_lint_config
File.delete('../.swiftlint-ci.yml')
end

def reporter
if ENV['CI_PULL_REQUEST'].nil? || ENV['CI_PULL_REQUEST'].empty?
'Saddler::Reporter::Github::CommitReviewComment'
else
'Saddler::Reporter::Github::PullRequestReviewComment'
end
end
end


analyzeというlaneを定義しています。

swiftlintにconfig_fileというオプションを付けてもらったので、それを利用し、CI用の設定ファイル(.swiftlint-ci.yml)を使います。

リポジトリ上には.swiftlint.ymlを置いておき、create_lint_configでそこからreporterのみを変更したものを.swiftlint-ci.ymlとして利用します。

delete_lint_configで削除しておきます。

GitHubのコメントに追加する部分が、Rubyのコード上でコマンドを実行しているのが微妙なので、なんとかしたいところ。


CircleCIの環境変数設定

Fastfileに記載したとおり、GITHUB_ACCESS_TOKENを設定する必要があります。

BOTアカウントを作成し、そのユーザのaccess tokenを取得したほうが、見栄えが良いかと。(コメントが並んだ時とか)


完成

あとは、違反を含んだコードをコミット、GitHubにpushすると、CircleCIが動き、コミットコメントが作成されると思います。

人間のレビューの前に、それらの自動レビューの結果をクリーンにしておくことで、人間のレビューが楽になるはずです。