Swift-Lint Danger on Bitrise
กราบสวัสดีพ่อแม่พี่น้องที่หลงเข้ามาอ่านบทความแรกของผมด้วยนะครับ ในวันนี้เราจะมาพูดถึง lint ในภาษา Swift และ Danger กัน ว่าคืออะไร ใช้งานอย่างไร น่าสนใจแค่ไหน หลายๆคนคงเริ่มอยากจะรู้กันแล้ว ถ้างั้นเรามาเริ่มกันเลยย~
รู้จักกันก่อน Lint คืออะไร
ขอยกคำพูดของเจ้าของบทความมาเลยละกันนะครับ
Lint คือ Static code analysis เป็นเครื่องมือหนึ่งที่ช่วยการวิเคราะห์โครงสร้างของโค้ดและช่วยแนะนำวิธีการปรับปรุงที่มาพร้อมกับคำอธิบายมีให้ใช้ทุกภาษานะครับ
สามารถอ่านเพิ่มเติมได้ที่ มารู้จักกับ Android Lint — Jedsada Tiwongvorakul
Danger คืออะไร
นั่นสิ Danger คืออะไรกันนะ ไปอ่านเจอข้อความหนึ่งที่พูดถึง Danger ว่า
Danger runs after your CI, automating your team’s conventions surrounding code review.
This provides another logical step in your process, through this Danger can help lint your rote tasks in daily code review.
You can use Danger to codify your team’s norms, leaving humans to think about harder problems.
เอาเป็นว่าขอสรุปตามความเข้าใจของตัวเองก็แล้วกันนะครับ
Danger คือเครื่องมือหนึ่งที่ช่วยจัดการเรื่องการตรวจทานโค้ดให้อัตโนมัติตามมาตรฐานของทีมที่สร้างขึ้น
อีกทั้ง Danger ยังสามารถทำงานร่วมกับ lint ได้ด้วยนะ So cool ~
เริ่มต้นยังไง
สำหรับคนที่ใช้ fastlane (CI/CD tools) ในการทำงานอยู่แล้ว บอกเลยว่าง่ายมากครับ แต่ใครที่ยังไม่เคยใช้หรือกำลังจะเริ่มหัดใช้ แนะนำให้ใช้นะครับ เพราะจะทำให้ชีวิตง่ายขึ้นเยอะ
เตรียม Gemfile, Fastfile, Dangerfile และ .swiftlint.ymlให้พร้อม
ในส่วนของ Gemfile
เป็นไฟล์ที่ใช้สำหรับบอก Dependency version เช่น Library, Ruby Package ในภาษา Ruby ที่เราต้องการใช้ในโปรเจคของเรา ให้ทำการเพิ่ม gem ‘danger’
สำหรับการใช้งาน Danger tool และ gem ‘danger-checkstyle_format’, ‘~> 0.1.1’
เป็น plugin ของ Danger tool ที่ใช้สำหรับการอ่านไฟล์ Report จากคำสั่ง swiftlint ใน fastlane เพื่อเอาผลลัพธ์ที่ได้ไป Comment แบบ Inline Code บน GitHub นั่นเอง
สามารถ copy code ตัวอย่างด้านล่างไปใส่ใน TextEditor (เช่น Sublime) แล้ว Save เป็นชื่อ Gemfile
ได้เลยย~
ตัวอย่าง Gemfile
source “https://rubygems.org”
gem ‘danger’
gem ‘danger-checkstyle_format’, ‘~> 0.1.1’
ในส่วนของ Fastfile
เป็นไฟล์ที่ใช้สำหรับสร้างขั้นตอนการทำงานต่างๆให้กับ fastlane สามารถแบ่งออกเป็นหลายๆ lane ได้ ให้ทำการเพิ่ม lane สำหรับการใช้ lint ผ่านคำสั่ง swiftlint เข้าไป โดย Fastfile
นี้เขียนด้วยภาษา Ruby นะ
ตัวอย่าง Fastfile
fastlane_version "2.3.12"default_platform :iosEncoding.default_external = Encoding::UTF_8
Encoding.default_internal = Encoding::UTF_8platform :ios dobefore_all do
enddesc "Check and Generate swiftlint.result.xml report lint file"
lane :checking_by_lint doswiftlint(
mode: :lint, # SwiftLint mode: :lint (default) or :autocorrect
output_file: "./swiftlint.result.xml", # The path of the output file (optional)
reporter: "checkstyle", # The custom reporter to use (optional)
config_file: ".swiftlint.yml", # The path of the configuration file (optional)
ignore_exit_status: true, # Allow fastlane to continue even if SwiftLint returns a non-zero exit status (Default: false)
quiet: true, # Don't print status logs like 'Linting ' & 'Done linting' (Default: false)
strict: false # Fail on warnings? (Default: false)
)endafter_all do |lane|
# This block is called, only if the executed lane was successful
enderror do |lane, exception|
#slack(
# message: exception.message,
# success: false
#)
endend
สำหรับการตั้งค่าของคำสั่ง swiftlint สามารถดูได้จาก fastlane/swiftlint
ในส่วนของ Dangerfile
เป็นไฟล์ที่ใช้สำหรับตั้งค่าการทำงานของ Danger รวมถึงตั้งค่า/เรียกใช้ plugin ต่างๆของ Danger นั้น ยังสามารถกำหนด rule สำหรับการ PR (Pull Request) และแสดงผลลัพธ์ผ่าน Comment ใน GitHub ได้อีกด้วย
สามารถดูคำสั่งต่างๆของ Danger ได้ที่ danger.systems
ตัวอย่าง Dangerfile
# Ignore inline messages which lay outside a diff’s range of PR
github.dismiss_out_of_range_messages# Make it more obvious that a PR is a work in progress and shouldn’t be merged yet
warn(“PR is classed as Work in Progress”) if github.pr_title.include? “[WIP]” or github.pr_labels.include?(“WIP”)# Warn when there is a big PR
warn(“Big PR”) if git.lines_of_code > 300
warn(“Large PR”) if git.lines_of_code > 500
warn(“Huge PR”) if git.lines_of_code > 700
warn(“Freakin Huge PR”) if git.lines_of_code > 1000# lint
checkstyle_format.base_path = Dir.pwd
checkstyle_format.report ‘swiftlint.result.xml’
ในส่วนของ .swiftlint.yml
เป็นไฟล์ที่ใช้สำหรับการตั้งค่าและกำหนดเงื่อนไขสำหรับการทำงานของคำสั่ง swiftlint
สามารถศึกษารายละเอียดคำสั่งต่างๆของ swiftlint ได้ที่ realm/SwiftLint
ตัวอย่าง .swiftlint.yml
disabled_rules: # rule identifiers to exclude from running
# — colon
# — comma
# — control_statement
— cyclomatic_complexity
opt_in_rules: # some rules are only opt-in
# — empty_count
# Find all the available rules by running:
# swiftlint rules
included: # paths to include during linting. ` — path` is ignored if present.
— SwiftLintDanger
excluded: # paths to ignore during linting. Takes precedence over `included`.
— Carthage
— Pods
— Source/ExcludedFolder
— Source/ExcludedFile.swift
— Source/*/ExcludedFile.swift # Exclude files with a wildcard
analyzer_rules: # Rules run by `swiftlint analyze` (experimental)
— explicit_self# configurable rules can be customized from this configuration file
# binary rules can set their severity level
force_cast: warning # implicitly
force_try:
severity: warning # explicitly
# rules that have both warning and error levels, can set just the warning level
# implicitly
line_length: 350
# they can set both implicitly with an array
type_body_length:
— 300 # warning
— 400 # error
# or they can set both explicitly
file_length:
warning: 500
error: 1200
# naming rules can set warnings/errors for min_length and max_length
# additionally they can set excluded names
type_name:
min_length: 1 # only warning
max_length: # warning and error
warning: 40
error: 50
excluded: iPhone # excluded via string
identifier_name:
min_length: # only min_length
error: 3 # only error
excluded: # excluded via string array
— id
— URL
— GlobalAPIKey
reporter: “checkstyle” # reporter type (xcode, json, csv, checkstyle, junit, html, emoji, sonarqube, markdown)
หลังจากเตรียม Gemfile
, Fastfile
, Dangerfile
และ .swiftlint.yml
เรียบร้อยแล้ว ก็ทำ push code ขึ้น Repository ได้เลย โดยในบทความนี้ผมจะใช้ Repository ของ GitHub นะครับ
ในส่วนของ Podfile
นั้นจะคล้ายๆกับ Gemfile
แตกต่างกันตรงที่จะเรียกใช้ dependency จากคนละที่กัน ซึ่ง Podfile
จะเรียกผ่าน https://cocoapods.org ส่วน Gemfile
จะเรียกผ่าน https://rubygems.org
มาถึงตรงนี้ หาก Finder ท่านใดมองไม่เห็น hidden files ให้ทำตามนี้ได้เลยครับ Show/Hide Hidden Files on macOS
ตั้งค่า CI
ในบทความนี้ผมจะใช้ CI ของ bitrise เนื่องจากมีรูปแบบการใช้งานที่ง่าย และ UI ดูเป็นมิตรกับผู้ใช้ครับ และที่สำคัญคือ Free(mium) !! สำหรับคนที่ยังไม่เคยลองใช้นั้น ลองสักครั้งแล้วจะติดใจนะ อิอิ
สำหรับคนที่ใช้ GitHub อยู่แล้ว และ ยังไม่ได้สมัครกับ bitrise วิธีการง่ายนิดเดียวครับ แค่กดปุ่ม Sign Up ด้านขวาบน และ กดปุ่ม Sign up with GitHub หลังจากนั้นก็ตาม Step การสมัครทั่วไปครับ จะให้กรอก Username, Password และข้อมูลส่วนตัวอีกนิดๆหน่อยๆ ก็เป็นอันจบพิธี
หลังจากสมัครเสร็จเรียบร้อยแล้ว หน้า Dashboard ของเราก็จะเป็นประมาณนี้
ให้ทำการเพิ่ม Project เข้าไปใน bitrise โดยกดที่ Add your first app หรือสำหรับคนที่ใช้ bitrise อยู่แล้ว ก็จะมีปุ่ม Add New App ในหน้า Dashboard ครับ
เมื่อทำตาม step การ create new app เรียบร้อยแล้ว bitrise จะทำการ setup ทั้ง Deploy Keys, Webhooks ใน GitHub ให้เลย เป็นอะไรที่สะดวกมากครับ
หลังจากนั้นให้ทำการแก้ไข bitrise.yml โดยกดเข้าไปที่ Project > Workflow > bitrise.yml แล้วเพิ่ม code ดังต่อไปนี้ด้านล่าง workflows:
SwiftLintDanger:steps:- activate-ssh-key:run_if: ‘{{getenv “SSH_RSA_PRIVATE_KEY” | ne “”}}’- git-clone: {}- script:run_if: “.IsPR”inputs:- is_debug: ‘yes’- content: |-#!/bin/bashset -exbrew install swiftlintbundle install- fastlane:inputs:- lane: ios checking_by_lint- script:run_if: “.IsPR”inputs:- is_debug: ‘yes’- content: |-#!/bin/bashset -ex# Running Dangerbundle exec danger
หรือจะทำการเพิ่ม Workflow เองผ่านทาง Workflows ของ bitrise ก็ได้ครับ
ขั้นตอนการทำงานที่เราจะใช้คือ
- Activate SSH Key
- Git Clone Repository
- brew install swiftlint
- bundle install
- fastlane ios checking_by_lint
- bundle exec danger
ซึ่งก่อนที่ Danger จะทำงานได้นั้น ให้เราไปเพิ่ม Personal access tokens ใน GitHub โดยเข้าที่ Settings > Developer settings > Personal access tokens เพื่อจะนำ token ที่ได้ไปให้เจ้า Danger ใช้สำหรับการ Comment ใน GitHub นั่นเอง
นำ token ที่ได้มาใส่ใน Project > Workflow > Env Vars โดยใช้ key DANGER_GITHUB_API_TOKEN
และ ติ๊ก Replace variables in inputs? ให้เป็นสีเขียว
หลังจากนั้นให้ทำการตั้งค่า Triggers สำหรับการ Pull Request ใน GitHub เพื่อที่จะให้ CI ทำการรัน lint ทุกครั้งที่มีการขอ PR ใน GitHub และทำการ Review Code ให้โดยอัตโนมัติผ่าน Danger นั้นเอง โดยเข้าไปที่ Project > Workflow > Triggers > ADD TRIGGER โดยที่เราสามารถตั้งค่า source branch, target branch และ Workflow ที่จะทำให้ CI ทำงานได้ โดยในที่นี้จะตั้งค่าเป็น * เพื่อที่จะให้ CI ทำงานทุกครั้งที่มีการ PR นั่นเอง
มาถึงตรงนี้ CI ก็พร้อมสำหรับการทำงานแล้วครับ เย้ !~
TESTING…
โดยการใส่ code เพิ่มเข้าไปสำหรับการ test โดยใส่
var username : String?
ในหน้า ViewController หน้าตาก็จะประมาณนี้
โดยที่ถ้าผ่าน lint แล้ว lint จะทำการตรวจสอบ source code ตามเงื่อนไขที่ได้กำหนดไว้ในไฟล์ .swiftlint.yml
โดยในที่นี้จะแก้เป็นดังนี้
var username : String? > var username: String?
จากนั้นทำการ push code ขึ้นใน branch ใหม่ โดยที่นี้จะใช้ชื่อ test/lint
จากนั้นทำการขอ PR เข้าใน branch master แล้วเรามาดูการทำงานของ CI กันน~
CI ทำงานน !
RESULT…
เรามาดูผลการทำงานของ Swift-Lint และ Danger กันน !
จะเห็นได้ว่าคำสั่ง swiftlint ใน fastlane ทำการตรวจสอบ source code ที่มีการเปลี่ยนแปลงให้ ตามเงื่อนไขที่ได้กำหนดไว้ในไฟล์ .swiftlint.yml
จากนั้นจะสร้าง Report ขึ้นมาเป็นไฟล์ที่ชื่อ swiftlint.result.xml
และเจ้า Danger ก็จะนำ Report มาแสดงให้ใน Comment ของ GitHub นั่นเอง เอง เอง เอง ~
มาถึงตรงนี้ หากท่านใดทำการ PR (Pull Request) แล้ว bitrise ไม่ทำงานตาม Triggers ที่ได้กำหนดไว้ ให้ลองดูในส่วนของ Webhooks บน GitHub (Project > Settings > Webhooks) ว่าได้เปิดในส่วนของ PR (Pull Request) ไว้หรือไม่ ถ้าไม่ได้เปิดไว้ ก็สามารถกดเข้าไป Edit แล้วเลือกในส่วนของ Pull requests ได้เลยครับ
เป็นยังไงกันบ้างครับ น่าจะเป็นบทความที่ยาวมากๆ ไม่รู้ว่าอ่านแล้วจะเข้าใจกันไหมนะ ฮ่าๆ
สรุป
จากที่ได้ลองใช้มาสักระยะหนึ่งก็พบว่า lint ช่วยลดภาระให้คนที่เป็น Reviewer ในตอน PR (Pull Request) ได้เยอะมาก แทนที่ Reviewer จะมานั่งตรวจสอบ code เองทุกครั้ง ก็ยกให้เป็นหน้าที่ของ lint จัดการไปเลย อีกทั้งยังทำให้ code ของเรามีรูปแบบการเขียนที่เป็นมาตรฐาน ตรวจสอบได้ง่าย ทั้งนี้ทั้งนั้น เราก็ควรแก้ไข code ตามที่ lint ได้แนะนำให้ใน Comment ด้วยนะ เพราะ ถ้าเกิดใช้ lint แล้ว แต่ไม่ได้แก้ไขตามที่ lint ได้แนะนำไป ก็ไม่มีประโยชน์อะไรที่จะใช้ครับ
แถมๆ
หากใครลง swiftlint ไว้ในเครื่อง ลงผ่าน (Command Line ด้วยคำสั่ง brew install swiftlint
) สามารถใช้
swiftlint autocorrect [PATH]
ผ่าน Command Line ได้ด้วยนะ ซึ่งคำสั่งนี้จะทำหน้าที่ในการเปลี่ยน code ตามมาตรฐานของ lint ให้เองโดยอัตโนมัติ ! แต่ จะผ่านเงื่อนไขที่ได้กำหนดไว้ในไฟล์ .swiftlint.yml
ตอน PR (Pull Request) หรือไม่ก็อีกเรื่องหนึ่งนะจ๊ะ
สามารถตรวจสอบเงื่อนไขในไฟล์ .swiftlint.yml
ได้โดยใช้คำสั่ง
swiftlint lint
ผ่าน Command Line ใน Folder ที่ที่ .swiftlint.yml
อยู่ได้ ก็จะเหมือนเป็นการรัน lint บนเครื่องของเรานั่นเอง จะได้รู้ว่าเราควรปรับเงื่อนไขตรงส่วนไหนให้เหมาะสมกับงานที่เราทำอยู่ครับ
สำหรับใครที่ยังไม่เข้าใจสามารถเข้าไปดูเพิ่มเติมได้ที่ ตัวอย่าง Project SwiftLintDanger
สุดท้ายนี้ หวังว่าบทความนี้คงจะมีประโยชน์ไม่มากก็น้อยนะครับ และ หากมีข้อผิดพลาดประการใด ก็ขออภัยมา ณ ที่นี้ด้วยนะครับผม
สุดท้ายของสุดท้าย ยังไงม้าก็แพ้ลา เพราะ ลาไปก่อน สวัสดีครับ