diff --git a/Readme.md b/Readme.md index b7736ab..d195773 100644 --- a/Readme.md +++ b/Readme.md @@ -28,29 +28,48 @@ You can see which version of Bob is currently running by typing `version`
The script to run for each target is specified when the command is instantiated.
For example:
```swift +let config = try Config() let buildTargets = [ TravisTarget(name: "staging", script: Script("fastlane ios distribute_staging")), TravisTarget(name: "testflight", script: Script("fastlane ios distribute_testflight")), ] -let buildCommand = TravisScriptCommand(name: "build", config: travisConfig, targets: buildTargets, defaultBranch: "Develop") +let travisCI = try TravisCI(config: config) +let buildCommand = TravisScriptCommand(name: "build", travis: travisCI, targets: buildTargets, defaultBranch: BranchName("develop")) try bob.register(buildCommand) ``` -would register a command with the name `build`. Typing `build staging` would start the lane `distribute_staging` on Travis. - +would register a command with the name `build`. Typing `build staging` would start the lane `distribute_staging` on Travis. +Your `bob.json` file should look like this (replace your token & urls) +```json +{ + "travis-repo-url": "<# Url of the repo. Along the lines of https://api.travis-ci.com/repo/{owner%2Frepo} #>", + "travis-token": <# Travis CI access token #> + <# other configurations #> +} +``` ### Align Version iOS specific command used to change the `CFBundleShortVersionString` and `CFBundleVersion` values in specified `.plist` files.
For example:
```swift +let config = try Config() let plistPaths: [String] = [ "App/Info.plist", "siriKit/Info.plist", "siriKitUI/Info.plist" ] -let alignCommand = AlignVersionCommand(config: gitHubConfig, defaultBranch: "Develop", plistPaths: plistPaths, author: author) +let gitHub = try GitHub(config: config) +let alignCommand = AlignVersionCommand(gitHub: gitHub, defaultBranch: BranchName("develop"), plistPaths: plistPaths, author: Author(name: "bob", email: "bob@example.com")) try bob.register(alignCommand) ``` would register a command that can be invoked by typing `align 3.0 4`. Bob would then create a commit on GitHub by changing the 3 specified files. - +Your `bob.json` file should look like this (replace your token & urls) +```json +{ + "github-username": <# Username of authenticated Github user #>, + "github-access-token": <# Persoanl access token for the authenticated user #>, + "github-repo-url": <# Url of the repo. Alogn the lines of https://api.github.com/repos/{owner}/{repo} #>, + <# other configurations #> +} +``` ## Getting started ### Creating a bot on Slack @@ -85,6 +104,15 @@ You can delete the unused template files by running: rm -rf Sources/App/Controllers rm -rf Sources/App/Models ``` + +All of your configured tokens will reside in `bob.json` file in the `Config` folder. It should look like this (replace your token), and you can add this file to your `.gitignore`. +```json +{ + "slack-token": <# Slack bot token #>, + <# other configurations #> +} +``` + All of your custom code will reside in the `Sources/App` folder.
Create an Xcode project by running ```bash @@ -94,9 +122,9 @@ Change the `Sources/App/main.swift` file to: ```swift import Bob -let config = Bob.Configuration(slackToken: "your-slack-token") +let config = try Config() let bob = Bob(config: config) - +<# register commands #> try bob.start() ``` and you're good to go. Select the `App` scheme and run it. You can now send messages to `Bob` via Slack, and it will respond. diff --git a/Sources/Bob/APIs/GitHub/GitHub.swift b/Sources/Bob/APIs/GitHub/GitHub.swift index cce50b3..b58fc99 100644 --- a/Sources/Bob/APIs/GitHub/GitHub.swift +++ b/Sources/Bob/APIs/GitHub/GitHub.swift @@ -82,7 +82,7 @@ public struct Commit { } /// Used for communicating with the GitHub api -public class GitHub { +public final class GitHub { /// Configuration needed for authentication with the api public struct Configuration { @@ -104,12 +104,12 @@ public class GitHub { private let base64LoginData: String private let repoUrl: String - private let drop: Droplet - public init(config: Configuration, droplet: Droplet) { + private let client: ClientFactoryProtocol + public init(config: Configuration, client: ClientFactoryProtocol) { let authString = config.username + ":" + config.personalAccessToken self.base64LoginData = authString.data(using: .utf8)!.base64EncodedString() self.repoUrl = config.repoUrl - self.drop = droplet + self.client = client } private func uri(at path: String) -> String { @@ -118,7 +118,7 @@ public class GitHub { private func perform(_ request: Request) throws -> JSON { request.headers[HeaderKey("Authorization")] = "Basic " + self.base64LoginData - let response = try self.drop.client.respond(to: request) + let response = try self.client.respond(to: request) if response.status.isSuccessfulRequest { if let json = response.json { return json @@ -288,3 +288,27 @@ public class GitHub { } } + +extension Config { + /// Resolves configured GitHub configuration + func resolveGitHubConfiguration() throws -> GitHub.Configuration { + guard let user = self[Bob.configFile, "github-username"]?.string else { + throw "Unable to find GitHub username. It should be found in \" Configs/bob.json\" under the key \"github-username\"." + } + guard let token = self[Bob.configFile, "github-access-token"]?.string else { + throw "Unable to find GitHub personal access token. It should be found in \" Configs/bob.json\" under the key \"github-access-token\"." + } + guard let repoURL = self[Bob.configFile, "github-repo-url"]?.string else { + throw "Unable to find GitHub repository URL. It should be found in \" Configs/bob.json\" under the key \"github-repo-url\"." + } + return GitHub.Configuration(username: user, personalAccessToken: token, repoUrl: repoURL) + } +} + +extension GitHub: ConfigInitializable { + public convenience init(config: Config) throws { + let configuration = try config.resolveGitHubConfiguration() + let client = try config.resolveClient() + self.init(config: configuration, client: client) + } +} diff --git a/Sources/Bob/APIs/TravisCI/TravisCI.swift b/Sources/Bob/APIs/TravisCI/TravisCI.swift index 203e392..afc977c 100644 --- a/Sources/Bob/APIs/TravisCI/TravisCI.swift +++ b/Sources/Bob/APIs/TravisCI/TravisCI.swift @@ -54,7 +54,7 @@ extension Dictionary where Key: ExpressibleByStringLiteral, Value: Any { } /// Used for communication with TravisCI api -public class TravisCI { +public final class TravisCI { /// Configuration needed for authentication with the api @@ -70,13 +70,14 @@ public class TravisCI { } private let config: Configuration - private let drop: Droplet + private let client: ClientFactoryProtocol /// Initializes the object with provided configuration /// /// - Parameter config: Configuration to use - public init(config: Configuration, droplet: Droplet) { + /// - Parameter client: HTTP Client factory to use + public init(config: Configuration, client: ClientFactoryProtocol) { self.config = config - self.drop = droplet + self.client = client } @@ -102,10 +103,32 @@ public class TravisCI { request.headers[HeaderKey("Travis-API-Version")] = "3" request.headers[HeaderKey("Content-Type")] = "application/json" - let response = try self.drop.client.respond(to: request) + let response = try self.client.respond(to: request) if !response.status.isSuccessfulRequest { throw "Error: `" + request.uri.description + "` - " + response.status.reasonPhrase } } - +} + + +extension Config { + /// Resolves configured Travis CI configuration + func resolveTravisConfiguration() throws -> TravisCI.Configuration { + guard let url = self[Bob.configFile, "travis-repo-url"]?.string else { + throw "Unable to find Travis CI repo URL. It should be found in \" Configs/bob.json\" under the key \"travis-repo-url\"." + } + + guard let token = self[Bob.configFile, "travis-token"]?.string else { + throw "Unable to find Travis CI access token. It should be found in \" Configs/bob.json\" under the key \"travis-token\"." + } + return TravisCI.Configuration(repoUrl: url, token: token) + } +} + +extension TravisCI: ConfigInitializable { + public convenience init(config: Config) throws { + let configuration = try config.resolveTravisConfiguration() + let client = try config.resolveClient() + self.init(config: configuration, client: client) + } } diff --git a/Sources/Bob/Core/Bob.swift b/Sources/Bob/Core/Bob.swift index c875fcc..90c44c9 100644 --- a/Sources/Bob/Core/Bob.swift +++ b/Sources/Bob/Core/Bob.swift @@ -20,10 +20,11 @@ import Foundation import Vapor -public class Bob { +public final class Bob { static let version: String = "1.0.2" - + static let configFile: String = "bob" + /// Struct containing all properties needed for Bob to function public struct Configuration { public let slackToken: String @@ -44,9 +45,9 @@ public class Bob { /// Initializer /// /// - Parameter configuration: Configuration for setup - /// - Parameter droplet: Droplet - public init(config: Configuration, droplet: Droplet) { - self.slackClient = SlackClient(token: config.slackToken, droplet: droplet) + /// - Parameter client: HTTP Client to use + public init(config: Configuration, client: ClientFactoryProtocol) { + self.slackClient = SlackClient(token: config.slackToken, client: client) self.factory = CommandFactory(commands: [HelloCommand(), VersionCommand()]) self.processor = CommandProcessor(factory: self.factory) self.executor = CommandExecutor() @@ -102,3 +103,21 @@ fileprivate extension CommandFactory { } } + +extension Config { + /// Resolves configured Bob configuration + func resolveBobConfiguration() throws -> Bob.Configuration { + guard let token = self[Bob.configFile, "slack-token"]?.string else { + throw "Unable to find Slack access token. It should be found in \" Configs/bob.json\" under the key \"slack-token\"." + } + return Bob.Configuration(slackToken: token) + } +} + +extension Bob: ConfigInitializable { + public convenience init(config: Config) throws { + let configuration = try config.resolveBobConfiguration() + let client = try config.resolveClient() + self.init(config: configuration, client: client) + } +} diff --git a/Sources/Bob/Core/Slack/SlackClient.swift b/Sources/Bob/Core/Slack/SlackClient.swift index a5d92fc..f03239e 100644 --- a/Sources/Bob/Core/Slack/SlackClient.swift +++ b/Sources/Bob/Core/Slack/SlackClient.swift @@ -41,15 +41,15 @@ extension ClientFactoryProtocol { class SlackClient { private let token: String - private let droplet: Droplet - init(token: String, droplet: Droplet) { + private let client: ClientFactoryProtocol + init(token: String, client: ClientFactoryProtocol) { self.token = token - self.droplet = droplet + self.client = client } func connect(onMessage: @escaping (_ message: String, _ sender: MessageSender) -> Void) throws { - let rtmResponse = try self.droplet.client.loadRealtimeApi(token: self.token) + let rtmResponse = try self.client.loadRealtimeApi(token: self.token) guard let webSocketURL = rtmResponse.json?["url"]?.string else { throw "Unable to retrieve `url` from slack. Reason \(rtmResponse.status.reasonPhrase). Raw response \(rtmResponse.data)" } try WebSocketFactory.shared.connect(to: webSocketURL) { (socket) in