Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Making Slackbots deploy iOS apps for you

Making Slackbots deploy iOS apps for you

In this presentation I talked about the issues a big dev team like ours had when deploying iOS apps to TestFlight and Crashlytics, as well as the solutions that Fastlane, Slack, and Vapor framework has provided us.

Presented originally at the Tokyo iOS Meetup.(https://www.meetup.com/TokyoiOSMeetup/events/237038601/)

John Estropia

May 20, 2017
Tweet

More Decks by John Estropia

Other Decks in Programming

Transcript

  1. @JOHNESTROPIA ➤ Been writing iOS apps since 2011 ➤ Author

    of Swift Core Data framework, CoreStore ➤ Creator of Pokemon GO utility app, PokeRater
  2. @JOHNESTROPIA ➤ Currently Senior Engineer at (member of IAC /

    Match Group) ➤ ➤ iOS Daily active users: 120k+ ! ➤ ➤ iOS Daily active users: 118k+ !, 10k+ " ➤ Deployment is taken very seriously
  3. TYPICAL IOS DEPLOYMENT PROCESS 1. Compile 2. Code-Sign 3. Upload

    to iTunesConnect or other distribution services (e.g. Crashlytics) 4. Submit for Apple Review
  4. 1. COMPILING ➤ In our case: • 350+ *.swift files

    • 220+ *.h files • 230+ *.m files • (3rd party libraries not included) ➤ Problem: Compile time • Debug: 5~mins • Release: 15~mins • Dev can’t do any coding during this time (Xcode is busy)
  5. 2. CODE-SIGNING ➤ In our case: • Multiple schemes each

    with their own certificates/mobileprovisioning • Mix-and-match of: ✦ Country: ! / " ✦ Deployment: Debugger / Crashlytics / TestFlight / AppStore ✦ Server: Staging / Production ➤ Problem: Error prone across multiple developers • Missing certificates / private keys • Sharing vs. Security • Confusing (which scheme uploads to where?)
  6. 3. UPLOADING TO ITUNESCONNECT ETC. ➤ In our case: •

    Many team members ✦ Pairs ! → 6 devs ✦ Pairs " → 3 devs ➤ Problem: Sensitive to Mac’s installed tools and configurations • Xcode version (sometimes Mac version) • Apple accounts • Code-Signing again
  7. WHAT’S ➤ Set of tools (Ruby scripts) for common actions

    such as: ➤ git branch management ➤ ensuring Xcode version ➤ managing certificates and keys ➤ building / archiving the app ➤ uploading to TestFlight, Crashlytics, HockeyApp, Appetize, etc., etc. ➤ notifications through Slack, Chatwork, Twitter, etc. ➤ A whole lot more: https://docs.fastlane.tools/actions/
  8. WHAT’S ➤ Just declare script invocations in a FastFile (ruby)

    file: lane :deploy do |options| country = options[:country] deployment = options[:deployment] server = options[:server] # ... sigh(...) # manages certificates (auto-downloads certs) gym(...) # archives the app pilot(...) # uploads to iTunesConnect end $ fastlane deploy country:jp deployment:testflight server:production
  9. THANKS TO FASTLANE 1. Compile 2. Code-Sign 3. Upload to

    iTunesConnect 4. Submit for Apple Review ← Problems solved! ← Problems solved! ← Still long and disruptive
  10. SO MUCH ROOM FOR IMPROVEMENT… ➤ Compilations are still long

    and waste time. ➤ We still have security concerns sharing our certificates and keys. ➤ We found more problems: Fastlane’s tools are convenient, but everyone is doing things independently. ➤ Incrementing version numbers (e.g. Which branch’s version should be incremented?) ➤ Hard to track who uploaded what. ➤ Build notifications became noise.
  11. THE IDEA ➤ With a centralized build server, devs can

    freely use their Xcode even during deployment. ➤ Certificates and keys are in one place and do not need to be shared. ➤ Prioritize tasks appropriately (e.g. version bumps) ➤ Granular control of notifications ➤ Basically, if we wrote our own server anything is possible.
  12. DECISIONS, DECISIONS… ➤ We thought of two scenarios to make

    this work: ➤ Cloud service for the build server + integrated Slack app to send commands. ➤ Dedicated local Mac as the build server + write a Mac app to run a Slack bot to parse chat commands.
  13. ➤ We thought of two scenarios to make this work:

    ➤ Cloud service for the build server + integrated Slack app to send commands. ➤ Dedicated local Mac as the build server + write a Mac app to run a Slack bot to parse chat commands. DECISIONS, DECISIONS… ➤ Most services cost $$$ (# of concurrent tasks, builds per day, users) ➤ Would be a pain to configure the level of control we want ➤ Local Mac = Secure ➤ Runs on company network but still have access to Slack ➤ Certifications/authentications don’t need to leave the machine. ➤ Coding a bot sounded very cool!
  14. SLACKBOT ➤ WebSocket-based API (Real Time Messaging API) ➤ but

    Web API also available ➤ https://api.slack.com/rtm ➤ The bot is just like a normal user, on steroids: ➤ Can passively listen to ALL chat messages in ALL channels it is invited to ➤ the bot app can then parse those messages and handle appropriately ➤ and also send Slack responses
  15. VAPOR ➤ "Server-side" Swift web framework ➤ By default has

    no dependence on Cocoa frameworks (can run on Linux) ➤ They have a Slackbot template! ➤ https://github.com/vapor-community/slack-bot ➤ Just provide Slack auth tokens in the bot-config.json file and the bot is ready
  16. BASIC SWIFT CODE LOGIC ➤ Parse messages of the form

    @<botname> <command> <parameters> ➤ Interpret the command, transform parameters if needed ➤ Validate sender permissions, enqueue tasks as appropriate ➤ Execute Fastlane from Swift using Process (NSTask in Obj-C) ➤ Process Fastlane's console output using Pipe (NSPipe in Obj-C) ➤ Interpret the output, report back to Slack chat ➤ Abuse Slack's custom emoji support! :partyparrot: :sadparrot: :loading: :done:
  17. PROCESS + PIPE (NSTASK + NSPIPE) let task = Process()

    task.launchPath = "/bin/bash" task.arguments = ["fastlane deploy country:jp deployment:appstore server:staging"] // ... let pipe = Pipe() task.standardOutput = pipe task.standardError = pipe pipe.fileHandleForReading.waitForDataInBackgroundAndNotify() let logObserver = NotificationCenter.default.addObserver( forName: NSNotification.Name.NSFileHandleDataAvailable, object: pipe.fileHandleForReading, queue: nil, using: { (notification) -> Void in // ... read fileHandle for console output strings } ) task.launch() task.waitUntilExit()