From af1e9f90e83ef0fd8deae4fdef874ae69d3e7208 Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Fri, 16 Oct 2015 15:18:22 +0100 Subject: [PATCH 01/63] started abstracting away the source server types so that we can hide more than just GitHub behind them. protocols ftw! --- BuildaGitServer/BaseTypes.swift | 45 ++++++++++++++++++++++++++++ BuildaKit/StorageManager.swift | 1 - BuildaKit/Syncer.swift | 1 - Buildasaur.xcodeproj/project.pbxproj | 12 ++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 BuildaGitServer/BaseTypes.swift diff --git a/BuildaGitServer/BaseTypes.swift b/BuildaGitServer/BaseTypes.swift new file mode 100644 index 0000000..7e56d2d --- /dev/null +++ b/BuildaGitServer/BaseTypes.swift @@ -0,0 +1,45 @@ +// +// BaseTypes.swift +// Buildasaur +// +// Created by Honza Dvorsky on 10/16/15. +// Copyright © 2015 Honza Dvorsky. All rights reserved. +// + +import Foundation + +//TODO: migrate all of buildasaur to handle the protocol types below so +//that we can start hiding away the Git server details and support not just +//GitHub but more, like Bitbucket. + +public protocol SourceServer { + + func getBranchesOfRepo(repo: String, completion: (branches: [BranchType]?, error: ErrorType?)) + func getOpenPullRequests(repo: String, completion: (prs: [PullRequestType]?, error: ErrorType?)) + func getRepo(repo: String, completion: (repo: RepoType?, error: ErrorType?)) + func getStatusOfCommit(commit: String, repo: String, completion: (status: StatusType?, error: ErrorType?)) + func postStatusOfCommit(commit: String, status: StatusType, repo: String, completion: (status: StatusType?, error: ErrorType?)) + func postCommentOnIssue(comment: String, issueNumber: Int, repo: String, completion: (comment: CommentType?, error: ErrorType?)) + func findMatchingCommentInIssue(keyword: String, issueNumber: Int, repo: String, completion: (foundComments: [CommentType]?, error: ErrorType?)) +} + +public protocol RepoType { + //TODO: add required properties +} + +public protocol BranchType { + //TODO: add required properties +} + +public protocol PullRequestType { + //TODO: add required properties +} + +public protocol StatusType { + //TODO: add required properties +} + +public protocol CommentType { + //TODO: add required properties +} + diff --git a/BuildaKit/StorageManager.swift b/BuildaKit/StorageManager.swift index b3c6dc6..0c1a6c6 100644 --- a/BuildaKit/StorageManager.swift +++ b/BuildaKit/StorageManager.swift @@ -7,7 +7,6 @@ // import Foundation -import BuildaGitServer import BuildaUtils import XcodeServerSDK import ReactiveCocoa diff --git a/BuildaKit/Syncer.swift b/BuildaKit/Syncer.swift index 512202b..a9e5e10 100644 --- a/BuildaKit/Syncer.swift +++ b/BuildaKit/Syncer.swift @@ -7,7 +7,6 @@ // import Foundation -import BuildaGitServer import BuildaUtils import XcodeServerSDK import ReactiveCocoa diff --git a/Buildasaur.xcodeproj/project.pbxproj b/Buildasaur.xcodeproj/project.pbxproj index 4e17ef7..6c36e8c 100644 --- a/Buildasaur.xcodeproj/project.pbxproj +++ b/Buildasaur.xcodeproj/project.pbxproj @@ -14,6 +14,7 @@ 3A0FF5A21BBFE8BA00FB8051 /* SyncerConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A0FF5A11BBFE8BA00FB8051 /* SyncerConfig.swift */; settings = {ASSET_TAGS = (); }; }; 3A0FF5A41BBFF9FA00FB8051 /* ProjectConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A0FF5A31BBFF9FA00FB8051 /* ProjectConfig.swift */; settings = {ASSET_TAGS = (); }; }; 3A0FF5AE1BC0067700FB8051 /* SyncerManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A0FF5AD1BC0067700FB8051 /* SyncerManager.swift */; settings = {ASSET_TAGS = (); }; }; + 3A1023741BD13DE80068FAB9 /* BaseTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A1023731BD13DE80068FAB9 /* BaseTypes.swift */; settings = {ASSET_TAGS = (); }; }; 3A28AF531BC984850011756E /* ConfigTriplet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A28AF521BC984850011756E /* ConfigTriplet.swift */; settings = {ASSET_TAGS = (); }; }; 3A3231B11B5AEF7900B53E3F /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A3231B01B5AEF7900B53E3F /* Logging.swift */; }; 3A32CD141A3D00F800861A34 /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A32CD131A3D00F800861A34 /* Comment.swift */; }; @@ -224,6 +225,7 @@ 3A0FF5A11BBFE8BA00FB8051 /* SyncerConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncerConfig.swift; sourceTree = ""; }; 3A0FF5A31BBFF9FA00FB8051 /* ProjectConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProjectConfig.swift; sourceTree = ""; }; 3A0FF5AD1BC0067700FB8051 /* SyncerManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncerManager.swift; sourceTree = ""; }; + 3A1023731BD13DE80068FAB9 /* BaseTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseTypes.swift; sourceTree = ""; }; 3A28AF521BC984850011756E /* ConfigTriplet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigTriplet.swift; sourceTree = ""; }; 3A3231B01B5AEF7900B53E3F /* Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = ""; }; 3A32CD131A3D00F800861A34 /* Comment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Comment.swift; sourceTree = ""; }; @@ -454,6 +456,14 @@ name = Utils; sourceTree = ""; }; + 3A1023721BD12FBD0068FAB9 /* Base */ = { + isa = PBXGroup; + children = ( + 3A1023731BD13DE80068FAB9 /* BaseTypes.swift */, + ); + name = Base; + sourceTree = ""; + }; 3A5687671A3B93BD0066DB2B = { isa = PBXGroup; children = ( @@ -639,6 +649,7 @@ children = ( 3AAF6F031A3CE66A00C657FB /* GitServerPublic.swift */, 3AAF6EE81A3CE5BA00C657FB /* BuildaGitServer.h */, + 3A1023721BD12FBD0068FAB9 /* Base */, 3A6355DB1A3BBDB000545BF9 /* GitHub */, 3AAF6EE61A3CE5BA00C657FB /* Supporting Files */, ); @@ -1480,6 +1491,7 @@ buildActionMask = 2147483647; files = ( 3AB3F0E21A3CEBAC005F717F /* GitHubEntity.swift in Sources */, + 3A1023741BD13DE80068FAB9 /* BaseTypes.swift in Sources */, 3A32CD181A3D01E300861A34 /* Issue.swift in Sources */, 3AB3F0E11A3CEBAC005F717F /* Commit.swift in Sources */, 3AB3F0E31A3CEBAC005F717F /* PullRequest.swift in Sources */, From e0bb07fc681397c318e8df8e88faec9a5a095518 Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Fri, 16 Oct 2015 15:38:27 +0100 Subject: [PATCH 02/63] started defining methods required by all server protocols --- BuildaGitServer/BaseTypes.swift | 7 +++- BuildaGitServer/Branch.swift | 8 ++--- BuildaGitServer/Comment.swift | 8 ++--- BuildaGitServer/Commit.swift | 6 ++-- BuildaGitServer/GitHubEndpoints.swift | 10 +++--- BuildaGitServer/GitHubEntity.swift | 20 +++++------ BuildaGitServer/GitHubFactory.swift | 4 +-- BuildaGitServer/GitHubRateLimit.swift | 12 +++---- BuildaGitServer/GitHubServer.swift | 36 ++++++++++---------- BuildaGitServer/GitHubServerExtensions.swift | 6 ++-- BuildaGitServer/Issue.swift | 10 +++--- BuildaGitServer/PullRequest.swift | 8 ++--- BuildaGitServer/PullRequestBranch.swift | 10 +++--- BuildaGitServer/Repo.swift | 14 ++++---- BuildaGitServer/Status.swift | 26 +++++++------- BuildaGitServer/User.swift | 10 +++--- 16 files changed, 100 insertions(+), 95 deletions(-) diff --git a/BuildaGitServer/BaseTypes.swift b/BuildaGitServer/BaseTypes.swift index 7e56d2d..5f04922 100644 --- a/BuildaGitServer/BaseTypes.swift +++ b/BuildaGitServer/BaseTypes.swift @@ -12,7 +12,7 @@ import Foundation //that we can start hiding away the Git server details and support not just //GitHub but more, like Bitbucket. -public protocol SourceServer { +public protocol SourceServerType { func getBranchesOfRepo(repo: String, completion: (branches: [BranchType]?, error: ErrorType?)) func getOpenPullRequests(repo: String, completion: (prs: [PullRequestType]?, error: ErrorType?)) @@ -23,6 +23,11 @@ public protocol SourceServer { func findMatchingCommentInIssue(keyword: String, issueNumber: Int, repo: String, completion: (foundComments: [CommentType]?, error: ErrorType?)) } +public protocol SourceServerFactory { + + func createServer(config: NSDictionary) -> SourceServerType +} + public protocol RepoType { //TODO: add required properties } diff --git a/BuildaGitServer/Branch.swift b/BuildaGitServer/Branch.swift index 5ac8bac..89aa347 100644 --- a/BuildaGitServer/Branch.swift +++ b/BuildaGitServer/Branch.swift @@ -8,12 +8,12 @@ import Foundation -public class Branch : GitHubEntity { +class Branch : GitHubEntity { - public let name: String - public let commit: Commit + let name: String + let commit: Commit - public required init(json: NSDictionary) { + required init(json: NSDictionary) { self.name = json.stringForKey("name") self.commit = Commit(json: json.dictionaryForKey("commit")) diff --git a/BuildaGitServer/Comment.swift b/BuildaGitServer/Comment.swift index 89014ae..4717214 100644 --- a/BuildaGitServer/Comment.swift +++ b/BuildaGitServer/Comment.swift @@ -8,12 +8,12 @@ import Foundation -public class Comment : GitHubEntity { +class Comment : GitHubEntity { - public let body: String - public let author: User + let body: String + let author: User - public required init(json: NSDictionary) { + required init(json: NSDictionary) { self.body = json.stringForKey("body") self.author = User(json: json.dictionaryForKey("user")) diff --git a/BuildaGitServer/Commit.swift b/BuildaGitServer/Commit.swift index c884cd7..32f01e1 100644 --- a/BuildaGitServer/Commit.swift +++ b/BuildaGitServer/Commit.swift @@ -9,11 +9,11 @@ import Foundation //GitHub commit in all its glory, with git commit metadata, plus comments, committer, author and parents info -public class Commit : GitHubEntity { +class Commit : GitHubEntity { - public let sha: String + let sha: String - public required init(json: NSDictionary) { + required init(json: NSDictionary) { self.sha = json.stringForKey("sha") diff --git a/BuildaGitServer/GitHubEndpoints.swift b/BuildaGitServer/GitHubEndpoints.swift index 5d12e27..6992391 100644 --- a/BuildaGitServer/GitHubEndpoints.swift +++ b/BuildaGitServer/GitHubEndpoints.swift @@ -9,9 +9,9 @@ import Foundation import BuildaUtils -public class GitHubEndpoints { +class GitHubEndpoints { - public enum Endpoint { + enum Endpoint { case Users case Repos case PullRequests @@ -23,7 +23,7 @@ public class GitHubEndpoints { case Merges } - public enum MergeResult { + enum MergeResult { case Success(NSDictionary) case NothingToMerge case Conflict @@ -33,7 +33,7 @@ public class GitHubEndpoints { private let baseURL: String private let token: String? - public init(baseURL: String, token: String?) { + init(baseURL: String, token: String?) { self.baseURL = baseURL self.token = token } @@ -141,7 +141,7 @@ public class GitHubEndpoints { } - public func createRequest(method:HTTP.Method, endpoint:Endpoint, params: [String : String]? = nil, query: [String : String]? = nil, body:NSDictionary? = nil) throws -> NSMutableURLRequest { + func createRequest(method:HTTP.Method, endpoint:Endpoint, params: [String : String]? = nil, query: [String : String]? = nil, body:NSDictionary? = nil) throws -> NSMutableURLRequest { let endpointURL = self.endpointURL(endpoint, params: params) let queryString = HTTP.stringForQuery(query) diff --git a/BuildaGitServer/GitHubEntity.swift b/BuildaGitServer/GitHubEntity.swift index d2ac153..0459252 100644 --- a/BuildaGitServer/GitHubEntity.swift +++ b/BuildaGitServer/GitHubEntity.swift @@ -8,36 +8,36 @@ import Foundation -public protocol GitHub { +protocol GitHub { init(json: NSDictionary) } -public class GitHubEntity : GitHub { +class GitHubEntity : GitHub { - public let htmlUrl: String? - public let url: String? - public let id: Int? + let htmlUrl: String? + let url: String? + let id: Int? //initializer which takes a dictionary and fills in values for recognized keys - public required init(json: NSDictionary) { + required init(json: NSDictionary) { self.htmlUrl = json.optionalStringForKey("html_url") self.url = json.optionalStringForKey("url") self.id = json.optionalIntForKey("id") } - public init() { + init() { self.htmlUrl = nil self.url = nil self.id = nil } - public func dictionarify() -> NSDictionary { + func dictionarify() -> NSDictionary { assertionFailure("Must be overriden by subclasses that wish to dictionarify their data") return NSDictionary() } - public class func optional(json: NSDictionary?) -> T? { + class func optional(json: NSDictionary?) -> T? { if let json = json { return T(json: json) } @@ -47,7 +47,7 @@ public class GitHubEntity : GitHub { } //parse an array of dictionaries into an array of parsed entities -public func GitHubArray(jsonArray: NSArray!) -> [T] { +func GitHubArray(jsonArray: NSArray!) -> [T] { let array = jsonArray as! [NSDictionary]! let parsed = array.map { diff --git a/BuildaGitServer/GitHubFactory.swift b/BuildaGitServer/GitHubFactory.swift index f13cf46..7b47ca5 100644 --- a/BuildaGitServer/GitHubFactory.swift +++ b/BuildaGitServer/GitHubFactory.swift @@ -8,9 +8,9 @@ import Foundation -public class GitHubFactory { +class GitHubFactory { - public class func server(token: String?) -> GitHubServer { + class func server(token: String?) -> GitHubServer { let baseURL = "https://api.github.com" let endpoints = GitHubEndpoints(baseURL: baseURL, token: token) diff --git a/BuildaGitServer/GitHubRateLimit.swift b/BuildaGitServer/GitHubRateLimit.swift index d11f9d8..e81848b 100644 --- a/BuildaGitServer/GitHubRateLimit.swift +++ b/BuildaGitServer/GitHubRateLimit.swift @@ -8,14 +8,14 @@ import Foundation -public struct GitHubRateLimit { +struct GitHubRateLimit { - public let resetTime: Double - public let limit: Int - public let remaining: Int - public let now: Double = NSDate().timeIntervalSince1970 + let resetTime: Double + let limit: Int + let remaining: Int + let now: Double = NSDate().timeIntervalSince1970 - public func getReport() -> String { + func getReport() -> String { let resetInterval = 3600.0 //reset interval is 1 hour let startTime = self.resetTime - resetInterval diff --git a/BuildaGitServer/GitHubServer.swift b/BuildaGitServer/GitHubServer.swift index 1b714d7..0045e63 100644 --- a/BuildaGitServer/GitHubServer.swift +++ b/BuildaGitServer/GitHubServer.swift @@ -9,12 +9,12 @@ import Foundation import BuildaUtils -public class GitHubServer : GitServer { +class GitHubServer : GitServer { - public let endpoints: GitHubEndpoints - public var latestRateLimitInfo: GitHubRateLimit? + let endpoints: GitHubEndpoints + var latestRateLimitInfo: GitHubRateLimit? - public init(endpoints: GitHubEndpoints, http: HTTP? = nil) { + init(endpoints: GitHubEndpoints, http: HTTP? = nil) { self.endpoints = endpoints super.init(http: http) @@ -196,7 +196,7 @@ extension GitHubServer { /** * GET all open pull requests of a repo (full name). */ - public func getOpenPullRequests(repo: String, completion: (prs: [PullRequest]?, error: NSError?) -> ()) { + func getOpenPullRequests(repo: String, completion: (prs: [PullRequest]?, error: NSError?) -> ()) { let params = [ "repo": repo @@ -220,7 +220,7 @@ extension GitHubServer { /** * GET a pull requests of a repo (full name) by its number. */ - public func getPullRequest(pullRequestNumber: Int, repo: String, completion: (pr: PullRequest?, error: NSError?) -> ()) { + func getPullRequest(pullRequestNumber: Int, repo: String, completion: (pr: PullRequest?, error: NSError?) -> ()) { let params = [ "repo": repo, @@ -246,7 +246,7 @@ extension GitHubServer { /** * GET all open issues of a repo (full name). */ - public func getOpenIssues(repo: String, completion: (issues: [Issue]?, error: NSError?) -> ()) { + func getOpenIssues(repo: String, completion: (issues: [Issue]?, error: NSError?) -> ()) { let params = [ "repo": repo @@ -270,7 +270,7 @@ extension GitHubServer { /** * GET an issue of a repo (full name) by its number. */ - public func getIssue(issueNumber: Int, repo: String, completion: (issue: Issue?, error: NSError?) -> ()) { + func getIssue(issueNumber: Int, repo: String, completion: (issue: Issue?, error: NSError?) -> ()) { let params = [ "repo": repo, @@ -296,7 +296,7 @@ extension GitHubServer { /** * POST a new Issue */ - public func postNewIssue(issueTitle: String, issueBody: String?, repo: String, completion: (issue: Issue?, error: NSError?) -> ()) { + func postNewIssue(issueTitle: String, issueBody: String?, repo: String, completion: (issue: Issue?, error: NSError?) -> ()) { let params = [ "repo": repo, @@ -326,7 +326,7 @@ extension GitHubServer { /** * Close an Issue by its number and repo (full name). */ - public func closeIssue(issueNumber: Int, repo: String, completion: (issue: Issue?, error: NSError?) -> ()) { + func closeIssue(issueNumber: Int, repo: String, completion: (issue: Issue?, error: NSError?) -> ()) { let params = [ "repo": repo, @@ -356,7 +356,7 @@ extension GitHubServer { /** * GET the status of a commit (sha) from a repo. */ - public func getStatusOfCommit(sha: String, repo: String, completion: (status: Status?, error: NSError?) -> ()) { + func getStatusOfCommit(sha: String, repo: String, completion: (status: Status?, error: NSError?) -> ()) { let params = [ "repo": repo, @@ -384,7 +384,7 @@ extension GitHubServer { /** * POST a new status on a commit. */ - public func postStatusOfCommit(status: Status, sha: String, repo: String, completion: (status: Status?, error: NSError?) -> ()) { + func postStatusOfCommit(status: Status, sha: String, repo: String, completion: (status: Status?, error: NSError?) -> ()) { let params = [ "repo": repo, @@ -413,7 +413,7 @@ extension GitHubServer { * and general issue comments - which appear in both Issues and Pull Requests (bc a PR is an Issue + code) * This API only fetches the general issue comments, NOT comments on code. */ - public func getCommentsOfIssue(issueNumber: Int, repo: String, completion: (comments: [Comment]?, error: NSError?) -> ()) { + func getCommentsOfIssue(issueNumber: Int, repo: String, completion: (comments: [Comment]?, error: NSError?) -> ()) { let params = [ "repo": repo, @@ -439,7 +439,7 @@ extension GitHubServer { /** * POST a comment on an issue. */ - public func postCommentOnIssue(commentBody: String, issueNumber: Int, repo: String, completion: (comment: Comment?, error: NSError?) -> ()) { + func postCommentOnIssue(commentBody: String, issueNumber: Int, repo: String, completion: (comment: Comment?, error: NSError?) -> ()) { let params = [ "repo": repo, @@ -469,7 +469,7 @@ extension GitHubServer { /** * PATCH edit a comment with id */ - public func editCommentOnIssue(commentId: Int, newCommentBody: String, repo: String, completion: (comment: Comment?, error: NSError?) -> ()) { + func editCommentOnIssue(commentId: Int, newCommentBody: String, repo: String, completion: (comment: Comment?, error: NSError?) -> ()) { let params = [ "repo": repo, @@ -500,7 +500,7 @@ extension GitHubServer { * POST merge a head branch/commit into a base branch. * has a couple of different responses, a bit tricky */ - public func mergeHeadIntoBase(head head: String, base: String, commitMessage: String, repo: String, completion: (result: GitHubEndpoints.MergeResult?, error: NSError?) -> ()) { + func mergeHeadIntoBase(head head: String, base: String, commitMessage: String, repo: String, completion: (result: GitHubEndpoints.MergeResult?, error: NSError?) -> ()) { let params = [ "repo": repo @@ -551,7 +551,7 @@ extension GitHubServer { /** * GET branches of a repo */ - public func getBranchesOfRepo(repo: String, completion: (branches: [Branch]?, error: NSError?) -> ()) { + func getBranchesOfRepo(repo: String, completion: (branches: [Branch]?, error: NSError?) -> ()) { let params = [ "repo": repo @@ -577,7 +577,7 @@ extension GitHubServer { /** * GET repo metadata */ - public func getRepo(repo: String, completion: (repo: Repo?, error: NSError?) -> ()) { + func getRepo(repo: String, completion: (repo: Repo?, error: NSError?) -> ()) { let params = [ "repo": repo diff --git a/BuildaGitServer/GitHubServerExtensions.swift b/BuildaGitServer/GitHubServerExtensions.swift index 466654f..c321e26 100644 --- a/BuildaGitServer/GitHubServerExtensions.swift +++ b/BuildaGitServer/GitHubServerExtensions.swift @@ -10,12 +10,12 @@ import Foundation import BuildaUtils //functions to make working with github easier - utility functions -public extension GitHubServer { +extension GitHubServer { /** * Get the latest status of a pull request. */ - public func getStatusOfPullRequest(pullRequestNumber: Int, repo: String, completion: (status: Status?, error: NSError?) -> ()) { + func getStatusOfPullRequest(pullRequestNumber: Int, repo: String, completion: (status: Status?, error: NSError?) -> ()) { self.getPullRequest(pullRequestNumber, repo: repo) { (pr, error) -> () in @@ -35,7 +35,7 @@ public extension GitHubServer { } //TODO: support paging through all the comments. currently we only fetch the last ~30 comments. - public func findMatchingCommentInIssue(commentsToMatch: [String], issue: Int, repo: String, completion: (foundComments: [Comment]?, error: NSError?) -> ()) { + func findMatchingCommentInIssue(commentsToMatch: [String], issue: Int, repo: String, completion: (foundComments: [Comment]?, error: NSError?) -> ()) { self.getCommentsOfIssue(issue, repo: repo) { (comments, error) -> () in diff --git a/BuildaGitServer/Issue.swift b/BuildaGitServer/Issue.swift index 8d08821..2713f0a 100644 --- a/BuildaGitServer/Issue.swift +++ b/BuildaGitServer/Issue.swift @@ -8,13 +8,13 @@ import Foundation -public class Issue : GitHubEntity { +class Issue : GitHubEntity { - public let number: Int - public let body: String - public let title: String + let number: Int + let body: String + let title: String - public required init(json: NSDictionary) { + required init(json: NSDictionary) { self.number = json.intForKey("number") self.body = json.stringForKey("body") diff --git a/BuildaGitServer/PullRequest.swift b/BuildaGitServer/PullRequest.swift index 2cea966..7652b65 100644 --- a/BuildaGitServer/PullRequest.swift +++ b/BuildaGitServer/PullRequest.swift @@ -8,12 +8,12 @@ import Foundation -public class PullRequest : Issue { +class PullRequest : Issue { - public let head: PullRequestBranch - public let base: PullRequestBranch + let head: PullRequestBranch + let base: PullRequestBranch - public required init(json: NSDictionary) { + required init(json: NSDictionary) { self.head = PullRequestBranch(json: json.dictionaryForKey("head")) self.base = PullRequestBranch(json: json.dictionaryForKey("base")) diff --git a/BuildaGitServer/PullRequestBranch.swift b/BuildaGitServer/PullRequestBranch.swift index 9a2a360..f223a76 100644 --- a/BuildaGitServer/PullRequestBranch.swift +++ b/BuildaGitServer/PullRequestBranch.swift @@ -10,13 +10,13 @@ import Foundation //PullRequestBranch is a special type of a branch - it also includes repo info (bc PRs can be cross repos) //normal branches include less information -public class PullRequestBranch : GitHubEntity { +class PullRequestBranch : GitHubEntity { - public let ref: String - public let sha: String - public let repo: Repo + let ref: String + let sha: String + let repo: Repo - public required init(json: NSDictionary) { + required init(json: NSDictionary) { self.ref = json.stringForKey("ref") self.sha = json.stringForKey("sha") diff --git a/BuildaGitServer/Repo.swift b/BuildaGitServer/Repo.swift index 0c39036..0f3be66 100644 --- a/BuildaGitServer/Repo.swift +++ b/BuildaGitServer/Repo.swift @@ -8,15 +8,15 @@ import Foundation -public class Repo : GitHubEntity { +class Repo : GitHubEntity { - public let name: String - public let fullName: String - public let repoUrlHTTPS: String - public let repoUrlSSH: String - public let permissions: NSDictionary + let name: String + let fullName: String + let repoUrlHTTPS: String + let repoUrlSSH: String + let permissions: NSDictionary - public required init(json: NSDictionary) { + required init(json: NSDictionary) { self.name = json.stringForKey("name") self.fullName = json.stringForKey("full_name") diff --git a/BuildaGitServer/Status.swift b/BuildaGitServer/Status.swift index 142459e..f969ca0 100644 --- a/BuildaGitServer/Status.swift +++ b/BuildaGitServer/Status.swift @@ -9,9 +9,9 @@ import Foundation import BuildaUtils -public class Status : GitHubEntity, Equatable { +class Status : GitHubEntity, Equatable { - public enum State : String { + enum State : String { case NoState = "" case Pending = "pending" case Success = "success" @@ -19,14 +19,14 @@ public class Status : GitHubEntity, Equatable { case Failure = "failure" } - public let state: State - public let description: String? - public let targetUrl: String? - public let context: String? - public let created: String? - public let creator: User? + let state: State + let description: String? + let targetUrl: String? + let context: String? + let created: String? + let creator: User? - public required init(json: NSDictionary) { + required init(json: NSDictionary) { self.state = State(rawValue: json.stringForKey("state"))! self.description = json.optionalStringForKey("description") @@ -42,7 +42,7 @@ public class Status : GitHubEntity, Equatable { super.init(json: json) } - public init(state: State, description: String?, targetUrl: String?, context: String?) { + init(state: State, description: String?, targetUrl: String?, context: String?) { self.state = state self.description = description @@ -54,7 +54,7 @@ public class Status : GitHubEntity, Equatable { super.init() } - public override func dictionarify() -> NSDictionary { + override func dictionarify() -> NSDictionary { let dictionary = NSMutableDictionary() @@ -67,14 +67,14 @@ public class Status : GitHubEntity, Equatable { } } -public func ==(lhs: Status, rhs: Status) -> Bool { +func ==(lhs: Status, rhs: Status) -> Bool { return lhs.state == rhs.state && lhs.description == rhs.description } //for sending statuses upstream extension Status { - public class func toDict(state: State, description: String? = nil, targetUrl: String? = nil, context: String? = nil) -> [String: String] { + class func toDict(state: State, description: String? = nil, targetUrl: String? = nil, context: String? = nil) -> [String: String] { return [ "state" : state.rawValue, "target_url" : targetUrl ?? "", diff --git a/BuildaGitServer/User.swift b/BuildaGitServer/User.swift index 6704520..65d7d22 100644 --- a/BuildaGitServer/User.swift +++ b/BuildaGitServer/User.swift @@ -8,13 +8,13 @@ import Foundation -public class User : GitHubEntity { +class User : GitHubEntity { - public let userName: String - public let realName: String? - public let avatarUrl: String? + let userName: String + let realName: String? + let avatarUrl: String? - public required init(json: NSDictionary) { + required init(json: NSDictionary) { self.userName = json.stringForKey("login") self.realName = json.optionalStringForKey("name") From 65e42e3d1a20a85428a589c65d50c954a44aeef6 Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Wed, 21 Oct 2015 22:35:38 +0100 Subject: [PATCH 03/63] move --- ...{GitHubServerExtensions.swift => SourceServerExtensions.swift} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename BuildaGitServer/{GitHubServerExtensions.swift => SourceServerExtensions.swift} (100%) diff --git a/BuildaGitServer/GitHubServerExtensions.swift b/BuildaGitServer/SourceServerExtensions.swift similarity index 100% rename from BuildaGitServer/GitHubServerExtensions.swift rename to BuildaGitServer/SourceServerExtensions.swift From 5117309b658ebb5bf7e803d9aed894fdea63c634 Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Wed, 21 Oct 2015 22:35:49 +0100 Subject: [PATCH 04/63] mega refactor in progress --- BuildaGitServer/BaseTypes.swift | 94 +++++++++++-- BuildaGitServer/Branch.swift | 10 ++ BuildaGitServer/Comment.swift | 3 + BuildaGitServer/GitHubServer.swift | 137 ++++++++++++++----- BuildaGitServer/PullRequest.swift | 7 +- BuildaGitServer/Repo.swift | 16 ++- BuildaGitServer/SourceServerExtensions.swift | 12 +- BuildaGitServer/Status.swift | 33 +++++ BuildaKit/HDGitHubXCBotSyncer.swift | 6 +- BuildaKit/NetworkUtils.swift | 18 ++- BuildaKit/SummaryBuilder.swift | 14 +- BuildaKit/SyncPairExtensions.swift | 4 +- BuildaKit/SyncPairResolver.swift | 14 +- BuildaKit/SyncPair_Branch_Bot.swift | 8 +- BuildaKit/SyncPair_PR_NoBot.swift | 8 +- BuildaKit/SyncerBotManipulation.swift | 8 +- BuildaKit/SyncerBotNaming.swift | 4 +- BuildaKit/SyncerFactory.swift | 14 +- BuildaKit/SyncerGitHubUtils.swift | 6 +- BuildaKit/SyncerLogic.swift | 32 +++-- Buildasaur.xcodeproj/project.pbxproj | 8 +- 21 files changed, 336 insertions(+), 120 deletions(-) diff --git a/BuildaGitServer/BaseTypes.swift b/BuildaGitServer/BaseTypes.swift index 5f04922..10d2dd2 100644 --- a/BuildaGitServer/BaseTypes.swift +++ b/BuildaGitServer/BaseTypes.swift @@ -14,37 +14,111 @@ import Foundation public protocol SourceServerType { - func getBranchesOfRepo(repo: String, completion: (branches: [BranchType]?, error: ErrorType?)) - func getOpenPullRequests(repo: String, completion: (prs: [PullRequestType]?, error: ErrorType?)) - func getRepo(repo: String, completion: (repo: RepoType?, error: ErrorType?)) - func getStatusOfCommit(commit: String, repo: String, completion: (status: StatusType?, error: ErrorType?)) - func postStatusOfCommit(commit: String, status: StatusType, repo: String, completion: (status: StatusType?, error: ErrorType?)) - func postCommentOnIssue(comment: String, issueNumber: Int, repo: String, completion: (comment: CommentType?, error: ErrorType?)) - func findMatchingCommentInIssue(keyword: String, issueNumber: Int, repo: String, completion: (foundComments: [CommentType]?, error: ErrorType?)) + func getBranchesOfRepo(repo: String, completion: (branches: [BranchType]?, error: ErrorType?) -> ()) + func getOpenPullRequests(repo: String, completion: (prs: [PullRequestType]?, error: ErrorType?) -> ()) + func getPullRequest(pullRequestNumber: Int, repo: String, completion: (pr: PullRequestType?, error: ErrorType?) -> ()) + func getRepo(repo: String, completion: (repo: RepoType?, error: ErrorType?) -> ()) + func getStatusOfCommit(commit: String, repo: String, completion: (status: StatusType?, error: ErrorType?) -> ()) + func postStatusOfCommit(commit: String, status: StatusType, repo: String, completion: (status: StatusType?, error: ErrorType?) -> ()) + func postCommentOnIssue(comment: String, issueNumber: Int, repo: String, completion: (comment: CommentType?, error: ErrorType?) -> ()) + func getCommentsOfIssue(issueNumber: Int, repo: String, completion: (comments: [CommentType]?, error: ErrorType?) -> ()) + + func createStatusFromState(state: BuildState, description: String?, targetUrl: String?, context: String?) -> StatusType +} + +public enum SourceServerOption { + case Token(String) +} + +extension SourceServerOption: Hashable { + public var hashValue: Int { + switch self { + case .Token(_): + return 1 + } + } +} + +public func ==(lhs: SourceServerOption, rhs: SourceServerOption) -> Bool { + + if case .Token(let lhsToken) = lhs, case .Token(let rhsToken) = rhs { + return lhsToken == rhsToken + } + + return false } -public protocol SourceServerFactory { +public class SourceServerFactory { - func createServer(config: NSDictionary) -> SourceServerType + public init() { } + + public func createServer(config: Set) -> SourceServerType { + + //TODO: generalize + if let tokenOption = config.first, + case .Token(let token) = tokenOption { + + return GitHubFactory.server(token) + } + preconditionFailure("Insufficient data provided to create a source server") + } +} + +public struct RepoPermissions { + public let read: Bool + public let write: Bool + public init(read: Bool, write: Bool) { + self.read = read + self.write = write + } } public protocol RepoType { + + var permissions: RepoPermissions { get } + //TODO: add required properties } public protocol BranchType { + + var name: String { get } + var commitSHA: String { get } + //TODO: add required properties } -public protocol PullRequestType { +public protocol IssueType { + + var number: Int { get } +} + +public protocol PullRequestType: IssueType { + + var headName: String { get } + //TODO: add required properties } +public enum BuildState { + case NoState + case Pending + case Success + case Error + case Failure +} + public protocol StatusType { + + var state: BuildState { get } + //TODO: add required properties } public protocol CommentType { + + var body: String { get } + //TODO: add required properties } diff --git a/BuildaGitServer/Branch.swift b/BuildaGitServer/Branch.swift index 89aa347..0ae8a17 100644 --- a/BuildaGitServer/Branch.swift +++ b/BuildaGitServer/Branch.swift @@ -20,3 +20,13 @@ class Branch : GitHubEntity { super.init(json: json) } } + +extension Branch: BranchType { + + //name (see above) + + var commitSHA: String { + return self.commit.sha + } + +} diff --git a/BuildaGitServer/Comment.swift b/BuildaGitServer/Comment.swift index 4717214..79c4ed0 100644 --- a/BuildaGitServer/Comment.swift +++ b/BuildaGitServer/Comment.swift @@ -22,3 +22,6 @@ class Comment : GitHubEntity { } } +extension Comment: CommentType { + +} diff --git a/BuildaGitServer/GitHubServer.swift b/BuildaGitServer/GitHubServer.swift index 0045e63..735a46a 100644 --- a/BuildaGitServer/GitHubServer.swift +++ b/BuildaGitServer/GitHubServer.swift @@ -23,13 +23,78 @@ class GitHubServer : GitServer { //TODO: from each of these calls, return a "cancellable" object which can be used for cancelling +extension GitHubServer: SourceServerType { + + func getBranchesOfRepo(repo: String, completion: (branches: [BranchType]?, error: ErrorType?) -> ()) { + + self._getBranchesOfRepo(repo) { (branches, error) -> () in + completion(branches: branches, error: error) + } + } + + func getOpenPullRequests(repo: String, completion: (prs: [PullRequestType]?, error: ErrorType?) -> ()) { + + self._getOpenPullRequests(repo) { (prs, error) -> () in + completion(prs: prs, error: error) + } + } + + func getPullRequest(pullRequestNumber: Int, repo: String, completion: (pr: PullRequestType?, error: ErrorType?) -> ()) { + + self._getPullRequest(pullRequestNumber, repo: repo) { (pr, error) -> () in + completion(pr: pr, error: error) + } + } + + func getRepo(repo: String, completion: (repo: RepoType?, error: ErrorType?) -> ()) { + + self._getRepo(repo) { (repo, error) -> () in + completion(repo: repo, error: error) + } + } + + func getStatusOfCommit(commit: String, repo: String, completion: (status: StatusType?, error: ErrorType?) -> ()) { + + self._getStatusOfCommit(commit, repo: repo) { (status, error) -> () in + completion(status: status, error: error) + } + } + + func postStatusOfCommit(commit: String, status: StatusType, repo: String, completion: (status: StatusType?, error: ErrorType?) -> ()) { + + self._postStatusOfCommit(status as! Status, sha: commit, repo: repo) { (status, error) -> () in + completion(status: status, error: error) + } + } + + func postCommentOnIssue(comment: String, issueNumber: Int, repo: String, completion: (comment: CommentType?, error: ErrorType?) -> ()) { + + self._postCommentOnIssue(comment, issueNumber: issueNumber, repo: repo) { (comment, error) -> () in + completion(comment: comment, error: error) + } + } + + func getCommentsOfIssue(issueNumber: Int, repo: String, completion: (comments: [CommentType]?, error: ErrorType?) -> ()) { + + self._getCommentsOfIssue(issueNumber, repo: repo) { (comments, error) -> () in + completion(comments: comments, error: error) + } + } + + func createStatusFromState(buildState: BuildState, description: String?, targetUrl: String?, context: String?) -> StatusType { + + let state = Status.State.fromBuildState(buildState) + return Status(state: state, description: description, targetUrl: targetUrl, context: context) + } +} + //FYI - GitHub API has a rate limit of 5,000 requests per hour. should be more than enough, but keep it in mind //when calling the API frequently. extension GitHubServer { - private func sendRequestWithPossiblePagination(request: NSURLRequest, accumulatedResponseBody: NSArray, completion: HTTP.Completion) { + private func _sendRequestWithPossiblePagination(request: NSURLRequest, accumulatedResponseBody: NSArray, completion: HTTP.Completion) { - self.sendRequest(request) { + self._sendRequest(request) { (response, body, error) -> () in if error != nil { @@ -44,7 +109,7 @@ extension GitHubServer { if let links = response?.allHeaderFields["Link"] as? String { //now parse page links - if let pageInfo = self.parsePageLinks(links) { + if let pageInfo = self._parsePageLinks(links) { //here look at the links and go to the next page, accumulate the body from this response //and pass it through @@ -53,7 +118,7 @@ extension GitHubServer { let newRequest = request.mutableCopy() as! NSMutableURLRequest newRequest.URL = nextUrl - self.sendRequestWithPossiblePagination(newRequest, accumulatedResponseBody: newBody, completion: completion) + self._sendRequestWithPossiblePagination(newRequest, accumulatedResponseBody: newBody, completion: completion) return } } @@ -73,7 +138,7 @@ extension GitHubServer { case Last = "last" } - private func parsePageLinks(links: String) -> [RelPage: NSURL]? { + private func _parsePageLinks(links: String) -> [RelPage: NSURL]? { var linkDict = [RelPage: NSURL]() @@ -115,7 +180,7 @@ extension GitHubServer { return linkDict } - private func sendRequest(request: NSURLRequest, completion: HTTP.Completion) { + private func _sendRequest(request: NSURLRequest, completion: HTTP.Completion) { self.http.sendRequest(request, completion: { (response, body, error) -> () in @@ -172,7 +237,7 @@ extension GitHubServer { }) } - private func sendRequestWithMethod(method: HTTP.Method, endpoint: GitHubEndpoints.Endpoint, params: [String: String]?, query: [String: String]?, body: NSDictionary?, completion: HTTP.Completion) { + private func _sendRequestWithMethod(method: HTTP.Method, endpoint: GitHubEndpoints.Endpoint, params: [String: String]?, query: [String: String]?, body: NSDictionary?, completion: HTTP.Completion) { var allParams = [ "method": method.rawValue @@ -187,7 +252,7 @@ extension GitHubServer { do { let request = try self.endpoints.createRequest(method, endpoint: endpoint, params: allParams, query: query, body: body) - self.sendRequestWithPossiblePagination(request, accumulatedResponseBody: NSArray(), completion: completion) + self._sendRequestWithPossiblePagination(request, accumulatedResponseBody: NSArray(), completion: completion) } catch { completion(response: nil, body: nil, error: Error.withInfo("Couldn't create Request, error \(error)")) } @@ -196,12 +261,12 @@ extension GitHubServer { /** * GET all open pull requests of a repo (full name). */ - func getOpenPullRequests(repo: String, completion: (prs: [PullRequest]?, error: NSError?) -> ()) { + private func _getOpenPullRequests(repo: String, completion: (prs: [PullRequest]?, error: NSError?) -> ()) { let params = [ "repo": repo ] - self.sendRequestWithMethod(.GET, endpoint: .PullRequests, params: params, query: nil, body: nil) { (response, body, error) -> () in + self._sendRequestWithMethod(.GET, endpoint: .PullRequests, params: params, query: nil, body: nil) { (response, body, error) -> () in if error != nil { completion(prs: nil, error: error) @@ -220,14 +285,14 @@ extension GitHubServer { /** * GET a pull requests of a repo (full name) by its number. */ - func getPullRequest(pullRequestNumber: Int, repo: String, completion: (pr: PullRequest?, error: NSError?) -> ()) { + private func _getPullRequest(pullRequestNumber: Int, repo: String, completion: (pr: PullRequest?, error: NSError?) -> ()) { let params = [ "repo": repo, "pr": pullRequestNumber.description ] - self.sendRequestWithMethod(.GET, endpoint: .PullRequests, params: params, query: nil, body: nil) { (response, body, error) -> () in + self._sendRequestWithMethod(.GET, endpoint: .PullRequests, params: params, query: nil, body: nil) { (response, body, error) -> () in if error != nil { completion(pr: nil, error: error) @@ -246,12 +311,12 @@ extension GitHubServer { /** * GET all open issues of a repo (full name). */ - func getOpenIssues(repo: String, completion: (issues: [Issue]?, error: NSError?) -> ()) { + private func _getOpenIssues(repo: String, completion: (issues: [Issue]?, error: NSError?) -> ()) { let params = [ "repo": repo ] - self.sendRequestWithMethod(.GET, endpoint: .Issues, params: params, query: nil, body: nil) { (response, body, error) -> () in + self._sendRequestWithMethod(.GET, endpoint: .Issues, params: params, query: nil, body: nil) { (response, body, error) -> () in if error != nil { completion(issues: nil, error: error) @@ -270,14 +335,14 @@ extension GitHubServer { /** * GET an issue of a repo (full name) by its number. */ - func getIssue(issueNumber: Int, repo: String, completion: (issue: Issue?, error: NSError?) -> ()) { + private func _getIssue(issueNumber: Int, repo: String, completion: (issue: Issue?, error: NSError?) -> ()) { let params = [ "repo": repo, "issue": issueNumber.description ] - self.sendRequestWithMethod(.GET, endpoint: .Issues, params: params, query: nil, body: nil) { (response, body, error) -> () in + self._sendRequestWithMethod(.GET, endpoint: .Issues, params: params, query: nil, body: nil) { (response, body, error) -> () in if error != nil { completion(issue: nil, error: error) @@ -296,7 +361,7 @@ extension GitHubServer { /** * POST a new Issue */ - func postNewIssue(issueTitle: String, issueBody: String?, repo: String, completion: (issue: Issue?, error: NSError?) -> ()) { + private func _postNewIssue(issueTitle: String, issueBody: String?, repo: String, completion: (issue: Issue?, error: NSError?) -> ()) { let params = [ "repo": repo, @@ -307,7 +372,7 @@ extension GitHubServer { "body": issueBody ?? "" ] - self.sendRequestWithMethod(.POST, endpoint: .Issues, params: params, query: nil, body: body) { (response, body, error) -> () in + self._sendRequestWithMethod(.POST, endpoint: .Issues, params: params, query: nil, body: body) { (response, body, error) -> () in if error != nil { completion(issue: nil, error: error) @@ -326,7 +391,7 @@ extension GitHubServer { /** * Close an Issue by its number and repo (full name). */ - func closeIssue(issueNumber: Int, repo: String, completion: (issue: Issue?, error: NSError?) -> ()) { + private func _closeIssue(issueNumber: Int, repo: String, completion: (issue: Issue?, error: NSError?) -> ()) { let params = [ "repo": repo, @@ -337,7 +402,7 @@ extension GitHubServer { "state": "closed" ] - self.sendRequestWithMethod(.PATCH, endpoint: .Issues, params: params, query: nil, body: body) { (response, body, error) -> () in + self._sendRequestWithMethod(.PATCH, endpoint: .Issues, params: params, query: nil, body: body) { (response, body, error) -> () in if error != nil { completion(issue: nil, error: error) @@ -356,14 +421,14 @@ extension GitHubServer { /** * GET the status of a commit (sha) from a repo. */ - func getStatusOfCommit(sha: String, repo: String, completion: (status: Status?, error: NSError?) -> ()) { + private func _getStatusOfCommit(sha: String, repo: String, completion: (status: Status?, error: NSError?) -> ()) { let params = [ "repo": repo, "sha": sha ] - self.sendRequestWithMethod(.GET, endpoint: .Statuses, params: params, query: nil, body: nil) { (response, body, error) -> () in + self._sendRequestWithMethod(.GET, endpoint: .Statuses, params: params, query: nil, body: nil) { (response, body, error) -> () in if error != nil { completion(status: nil, error: error) @@ -384,7 +449,7 @@ extension GitHubServer { /** * POST a new status on a commit. */ - func postStatusOfCommit(status: Status, sha: String, repo: String, completion: (status: Status?, error: NSError?) -> ()) { + private func _postStatusOfCommit(status: Status, sha: String, repo: String, completion: (status: Status?, error: NSError?) -> ()) { let params = [ "repo": repo, @@ -392,7 +457,7 @@ extension GitHubServer { ] let body = status.dictionarify() - self.sendRequestWithMethod(.POST, endpoint: .Statuses, params: params, query: nil, body: body) { (response, body, error) -> () in + self._sendRequestWithMethod(.POST, endpoint: .Statuses, params: params, query: nil, body: body) { (response, body, error) -> () in if error != nil { completion(status: nil, error: error) @@ -413,14 +478,14 @@ extension GitHubServer { * and general issue comments - which appear in both Issues and Pull Requests (bc a PR is an Issue + code) * This API only fetches the general issue comments, NOT comments on code. */ - func getCommentsOfIssue(issueNumber: Int, repo: String, completion: (comments: [Comment]?, error: NSError?) -> ()) { + private func _getCommentsOfIssue(issueNumber: Int, repo: String, completion: (comments: [Comment]?, error: NSError?) -> ()) { let params = [ "repo": repo, "issue": issueNumber.description ] - self.sendRequestWithMethod(.GET, endpoint: .IssueComments, params: params, query: nil, body: nil) { (response, body, error) -> () in + self._sendRequestWithMethod(.GET, endpoint: .IssueComments, params: params, query: nil, body: nil) { (response, body, error) -> () in if error != nil { completion(comments: nil, error: error) @@ -439,7 +504,7 @@ extension GitHubServer { /** * POST a comment on an issue. */ - func postCommentOnIssue(commentBody: String, issueNumber: Int, repo: String, completion: (comment: Comment?, error: NSError?) -> ()) { + private func _postCommentOnIssue(commentBody: String, issueNumber: Int, repo: String, completion: (comment: Comment?, error: NSError?) -> ()) { let params = [ "repo": repo, @@ -450,7 +515,7 @@ extension GitHubServer { "body": commentBody ] - self.sendRequestWithMethod(.POST, endpoint: .IssueComments, params: params, query: nil, body: body) { (response, body, error) -> () in + self._sendRequestWithMethod(.POST, endpoint: .IssueComments, params: params, query: nil, body: body) { (response, body, error) -> () in if error != nil { completion(comment: nil, error: error) @@ -469,7 +534,7 @@ extension GitHubServer { /** * PATCH edit a comment with id */ - func editCommentOnIssue(commentId: Int, newCommentBody: String, repo: String, completion: (comment: Comment?, error: NSError?) -> ()) { + private func _editCommentOnIssue(commentId: Int, newCommentBody: String, repo: String, completion: (comment: Comment?, error: NSError?) -> ()) { let params = [ "repo": repo, @@ -480,7 +545,7 @@ extension GitHubServer { "body": newCommentBody ] - self.sendRequestWithMethod(.PATCH, endpoint: .IssueComments, params: params, query: nil, body: body) { (response, body, error) -> () in + self._sendRequestWithMethod(.PATCH, endpoint: .IssueComments, params: params, query: nil, body: body) { (response, body, error) -> () in if error != nil { completion(comment: nil, error: error) @@ -500,7 +565,7 @@ extension GitHubServer { * POST merge a head branch/commit into a base branch. * has a couple of different responses, a bit tricky */ - func mergeHeadIntoBase(head head: String, base: String, commitMessage: String, repo: String, completion: (result: GitHubEndpoints.MergeResult?, error: NSError?) -> ()) { + private func _mergeHeadIntoBase(head head: String, base: String, commitMessage: String, repo: String, completion: (result: GitHubEndpoints.MergeResult?, error: NSError?) -> ()) { let params = [ "repo": repo @@ -512,7 +577,7 @@ extension GitHubServer { "commit_message": commitMessage ] - self.sendRequestWithMethod(.POST, endpoint: .Merges, params: params, query: nil, body: body) { (response, body, error) -> () in + self._sendRequestWithMethod(.POST, endpoint: .Merges, params: params, query: nil, body: body) { (response, body, error) -> () in if error != nil { completion(result: nil, error: error) @@ -551,13 +616,13 @@ extension GitHubServer { /** * GET branches of a repo */ - func getBranchesOfRepo(repo: String, completion: (branches: [Branch]?, error: NSError?) -> ()) { + private func _getBranchesOfRepo(repo: String, completion: (branches: [Branch]?, error: NSError?) -> ()) { let params = [ "repo": repo ] - self.sendRequestWithMethod(.GET, endpoint: .Branches, params: params, query: nil, body: nil) { + self._sendRequestWithMethod(.GET, endpoint: .Branches, params: params, query: nil, body: nil) { (response, body, error) -> () in if error != nil { @@ -577,13 +642,13 @@ extension GitHubServer { /** * GET repo metadata */ - func getRepo(repo: String, completion: (repo: Repo?, error: NSError?) -> ()) { + private func _getRepo(repo: String, completion: (repo: Repo?, error: NSError?) -> ()) { let params = [ "repo": repo ] - self.sendRequestWithMethod(.GET, endpoint: .Repos, params: params, query: nil, body: nil) { + self._sendRequestWithMethod(.GET, endpoint: .Repos, params: params, query: nil, body: nil) { (response, body, error) -> () in if error != nil { diff --git a/BuildaGitServer/PullRequest.swift b/BuildaGitServer/PullRequest.swift index 7652b65..0b9822f 100644 --- a/BuildaGitServer/PullRequest.swift +++ b/BuildaGitServer/PullRequest.swift @@ -22,4 +22,9 @@ class PullRequest : Issue { } } - +extension PullRequest: PullRequestType { + + var headName: String { + return self.head.ref + } +} diff --git a/BuildaGitServer/Repo.swift b/BuildaGitServer/Repo.swift index 0f3be66..d042fbe 100644 --- a/BuildaGitServer/Repo.swift +++ b/BuildaGitServer/Repo.swift @@ -14,7 +14,7 @@ class Repo : GitHubEntity { let fullName: String let repoUrlHTTPS: String let repoUrlSSH: String - let permissions: NSDictionary + let permissionsDict: NSDictionary required init(json: NSDictionary) { @@ -24,11 +24,21 @@ class Repo : GitHubEntity { self.repoUrlSSH = json.stringForKey("ssh_url") if let permissions = json.optionalDictionaryForKey("permissions") { - self.permissions = permissions + self.permissionsDict = permissions } else { - self.permissions = NSDictionary() + self.permissionsDict = NSDictionary() } super.init(json: json) } } + +extension Repo: RepoType { + + var permissions: RepoPermissions { + + let read = self.permissionsDict["pull"] as? Bool ?? false + let write = self.permissionsDict["push"] as? Bool ?? false + return RepoPermissions(read: read, write: write) + } +} diff --git a/BuildaGitServer/SourceServerExtensions.swift b/BuildaGitServer/SourceServerExtensions.swift index c321e26..297ccf2 100644 --- a/BuildaGitServer/SourceServerExtensions.swift +++ b/BuildaGitServer/SourceServerExtensions.swift @@ -1,5 +1,5 @@ // -// GitHubServerExtensions.swift +// SourceServerExtensions.swift // Buildasaur // // Created by Honza Dvorsky on 14/12/2014. @@ -10,12 +10,12 @@ import Foundation import BuildaUtils //functions to make working with github easier - utility functions -extension GitHubServer { +extension SourceServerType { /** * Get the latest status of a pull request. */ - func getStatusOfPullRequest(pullRequestNumber: Int, repo: String, completion: (status: Status?, error: NSError?) -> ()) { + func getStatusOfPullRequest(pullRequestNumber: Int, repo: String, completion: (status: StatusType?, error: ErrorType?) -> ()) { self.getPullRequest(pullRequestNumber, repo: repo) { (pr, error) -> () in @@ -26,7 +26,7 @@ extension GitHubServer { if let pr = pr { //fetched PR, now take its head's sha - that's the commit we care about. - let sha = pr.head.sha + let sha = pr.headName self.getStatusOfCommit(sha, repo: repo, completion: completion) } else { completion(status: nil, error: Error.withInfo("PR is nil and error is nil")) @@ -35,7 +35,7 @@ extension GitHubServer { } //TODO: support paging through all the comments. currently we only fetch the last ~30 comments. - func findMatchingCommentInIssue(commentsToMatch: [String], issue: Int, repo: String, completion: (foundComments: [Comment]?, error: NSError?) -> ()) { + func findMatchingCommentInIssue(commentsToMatch: [String], issue: Int, repo: String, completion: (foundComments: [CommentType]?, error: ErrorType?) -> ()) { self.getCommentsOfIssue(issue, repo: repo) { (comments, error) -> () in @@ -45,7 +45,7 @@ extension GitHubServer { } if let comments = comments { - let filtered = comments.filter { (comment: Comment) -> Bool in + let filtered = comments.filter { (comment: CommentType) -> Bool in let filteredSearch = commentsToMatch.filter { (searchString: String) -> Bool in diff --git a/BuildaGitServer/Status.swift b/BuildaGitServer/Status.swift index f969ca0..c028fdf 100644 --- a/BuildaGitServer/Status.swift +++ b/BuildaGitServer/Status.swift @@ -17,6 +17,36 @@ class Status : GitHubEntity, Equatable { case Success = "success" case Error = "error" case Failure = "failure" + + static func fromBuildState(buildState: BuildState) -> State { + switch buildState { + case .NoState: + return .NoState + case .Pending: + return .Pending + case .Success: + return .Success + case .Error: + return .Error + case .Failure: + return .Failure + } + } + + func toBuildState() -> BuildState { + switch self { + case .NoState: + return .NoState + case .Pending: + return .Pending + case .Success: + return .Success + case .Error: + return .Error + case .Failure: + return .Failure + } + } } let state: State @@ -82,5 +112,8 @@ extension Status { "context" : context ?? "" ] } +} + +extension Status: StatusType { } diff --git a/BuildaKit/HDGitHubXCBotSyncer.swift b/BuildaKit/HDGitHubXCBotSyncer.swift index b1bf7a9..0fd62ab 100644 --- a/BuildaKit/HDGitHubXCBotSyncer.swift +++ b/BuildaKit/HDGitHubXCBotSyncer.swift @@ -13,7 +13,7 @@ import ReactiveCocoa public class HDGitHubXCBotSyncer : Syncer { - public var github: GitHubServer + public var sourceServer: SourceServerType public var xcodeServer: XcodeServer public var project: Project public var buildTemplate: BuildTemplate @@ -25,11 +25,11 @@ public class HDGitHubXCBotSyncer : Syncer { return ConfigTriplet(syncer: self.config.value, server: self.xcodeServer.config, project: self.project.config.value, buildTemplate: self.buildTemplate, triggers: self.triggers.map { $0.config }) } - public init(integrationServer: XcodeServer, sourceServer: GitHubServer, project: Project, buildTemplate: BuildTemplate, triggers: [Trigger], config: SyncerConfig) { + public init(integrationServer: XcodeServer, sourceServer: SourceServerType, project: Project, buildTemplate: BuildTemplate, triggers: [Trigger], config: SyncerConfig) { self.config = MutableProperty(config) - self.github = sourceServer + self.sourceServer = sourceServer self.xcodeServer = integrationServer self.project = project self.buildTemplate = buildTemplate diff --git a/BuildaKit/NetworkUtils.swift b/BuildaKit/NetworkUtils.swift index 198b8b2..35d0afd 100644 --- a/BuildaKit/NetworkUtils.swift +++ b/BuildaKit/NetworkUtils.swift @@ -13,10 +13,14 @@ import XcodeServerSDK public class NetworkUtils { - public class func checkAvailabilityOfGitHubWithCurrentSettingsOfProject(project: Project, completion: (success: Bool, error: NSError?) -> ()) { + public class func checkAvailabilityOfGitHubWithCurrentSettingsOfProject(project: Project, completion: (success: Bool, error: ErrorType?) -> ()) { let token = project.config.value.githubToken - let server = GitHubFactory.server(token) + //TODO: have project spit out Set + + let options: Set = [.Token(token)] + let server: SourceServerType = SourceServerFactory().createServer(options) + let credentialValidationBlueprint = project.createSourceControlBlueprintForCredentialVerification() //check if we can get PRs, that should be representative enough @@ -30,12 +34,12 @@ public class NetworkUtils { return } - if - let repo = repo, - let readPermission = repo.permissions["pull"] as? Bool, - let writePermission = repo.permissions["push"] as? Bool - { + if let repo = repo { + let permissions = repo.permissions + let readPermission = permissions.read + let writePermission = permissions.write + //look at the permissions in the PR metadata if !readPermission { completion(success: false, error: Error.withInfo("Missing read permission for repo")) diff --git a/BuildaKit/SummaryBuilder.swift b/BuildaKit/SummaryBuilder.swift index 1e7b9d0..b53337b 100644 --- a/BuildaKit/SummaryBuilder.swift +++ b/BuildaKit/SummaryBuilder.swift @@ -23,7 +23,7 @@ class SummaryBuilder { //MARK: high level - func buildPassing(integration: Integration) -> HDGitHubXCBotSyncer.GitHubStatusAndComment { + func buildPassing(integration: Integration) -> StatusAndComment { let linkToIntegration = self.linkBuilder(integration) self.addBaseCommentFromIntegration(integration) @@ -45,7 +45,7 @@ class SummaryBuilder { return self.buildWithStatus(status) } - func buildFailingTests(integration: Integration) -> HDGitHubXCBotSyncer.GitHubStatusAndComment { + func buildFailingTests(integration: Integration) -> StatusAndComment { let linkToIntegration = self.linkBuilder(integration) @@ -57,7 +57,7 @@ class SummaryBuilder { return self.buildWithStatus(status) } - func buildErrorredIntegration(integration: Integration) -> HDGitHubXCBotSyncer.GitHubStatusAndComment { + func buildErrorredIntegration(integration: Integration) -> StatusAndComment { let linkToIntegration = self.linkBuilder(integration) self.addBaseCommentFromIntegration(integration) @@ -68,7 +68,7 @@ class SummaryBuilder { return self.buildWithStatus(status) } - func buildCanceledIntegration(integration: Integration) -> HDGitHubXCBotSyncer.GitHubStatusAndComment { + func buildCanceledIntegration(integration: Integration) -> StatusAndComment { let linkToIntegration = self.linkBuilder(integration) @@ -80,7 +80,7 @@ class SummaryBuilder { return self.buildWithStatus(status) } - func buildEmptyIntegration() -> HDGitHubXCBotSyncer.GitHubStatusAndComment { + func buildEmptyIntegration() -> StatusAndComment { let status = HDGitHubXCBotSyncer.createStatusFromState(.NoState, description: nil, targetUrl: nil) return (status: status, comment: nil) @@ -152,7 +152,7 @@ class SummaryBuilder { self.lines.append("Build was **manually canceled**.") } - func buildWithStatus(status: Status) -> HDGitHubXCBotSyncer.GitHubStatusAndComment { + func buildWithStatus(status: StatusType) -> StatusAndComment { let comment: String? if lines.count == 0 { @@ -160,7 +160,7 @@ class SummaryBuilder { } else { comment = lines.joinWithSeparator("\n") } - return (status: status, comment: comment) + return StatusAndComment(status: status, comment: comment) } } diff --git a/BuildaKit/SyncPairExtensions.swift b/BuildaKit/SyncPairExtensions.swift index e1b6f4a..41c4fe7 100644 --- a/BuildaKit/SyncPairExtensions.swift +++ b/BuildaKit/SyncPairExtensions.swift @@ -15,7 +15,7 @@ extension SyncPair { public struct Actions { public let integrationsToCancel: [Integration]? - public let githubStatusToSet: (status: HDGitHubXCBotSyncer.GitHubStatusAndComment, commit: String, issue: Issue?)? + public let statusToSet: (status: StatusAndComment, commit: String, issue: IssueType?)? public let startNewIntegrationBot: Bot? //if non-nil, starts a new integration on this bot } @@ -32,7 +32,7 @@ extension SyncPair { }) } - if let newStatus = actions.githubStatusToSet { + if let newStatus = actions.statusToSet { let status = newStatus.status let commit = newStatus.commit diff --git a/BuildaKit/SyncPairResolver.swift b/BuildaKit/SyncPairResolver.swift index 4cb481d..fa0eb7f 100644 --- a/BuildaKit/SyncPairResolver.swift +++ b/BuildaKit/SyncPairResolver.swift @@ -19,7 +19,7 @@ public class SyncPairResolver { public func resolveActionsForCommitAndIssueWithBotIntegrations( commit: String, - issue: Issue?, + issue: IssueType?, bot: Bot, hostname: String, integrations: [Integration]) -> SyncPair.Actions { @@ -76,7 +76,7 @@ public class SyncPairResolver { //A1. - it's empty, kick off an integration for the latest commit return SyncPair.Actions( integrationsToCancel: integrationsToCancel, - githubStatusToSet: nil, + statusToSet: nil, startNewIntegrationBot: bot ) } @@ -131,7 +131,7 @@ public class SyncPairResolver { //merge in nested actions return SyncPair.Actions( integrationsToCancel: integrationsToCancel + (actions.integrationsToCancel ?? []), - githubStatusToSet: actions.githubStatusToSet, + statusToSet: actions.statusToSet, startNewIntegrationBot: actions.startNewIntegrationBot ?? (startNewIntegration ? bot : nil) ) } @@ -206,13 +206,13 @@ public class SyncPairResolver { func resolveCommitStatusFromLatestIntegrations( commit: String, - issue: Issue?, + issue: IssueType?, pending: Integration?, running: Integration?, link: (Integration) -> String?, completed: Set) -> SyncPair.Actions { - let statusWithComment: HDGitHubXCBotSyncer.GitHubStatusAndComment + let statusWithComment: StatusAndComment var integrationsToCancel: [Integration] = [] //if there's any pending integration, we're ["Pending" - Waiting in the queue] @@ -257,7 +257,7 @@ public class SyncPairResolver { return SyncPair.Actions( integrationsToCancel: integrationsToCancel, - githubStatusToSet: (status: statusWithComment, commit: commit, issue: issue), + statusToSet: (status: statusWithComment, commit: commit, issue: issue), startNewIntegrationBot: nil ) } @@ -265,7 +265,7 @@ public class SyncPairResolver { func resolveStatusFromCompletedIntegrations( integrations: Set, link: (Integration) -> String? - ) -> HDGitHubXCBotSyncer.GitHubStatusAndComment { + ) -> StatusAndComment { //get integrations sorted by number let sortedDesc = Array(integrations).sort { $0.number > $1.number } diff --git a/BuildaKit/SyncPair_Branch_Bot.swift b/BuildaKit/SyncPair_Branch_Bot.swift index 299f01b..5fab016 100644 --- a/BuildaKit/SyncPair_Branch_Bot.swift +++ b/BuildaKit/SyncPair_Branch_Bot.swift @@ -13,11 +13,11 @@ import BuildaUtils public class SyncPair_Branch_Bot: SyncPair { - let branch: Branch + let branch: BranchType let bot: Bot let resolver: SyncPairBranchResolver - public init(branch: Branch, bot: Bot, resolver: SyncPairBranchResolver) { + public init(branch: BranchType, bot: Bot, resolver: SyncPairBranchResolver) { self.branch = branch self.bot = bot self.resolver = resolver @@ -39,8 +39,8 @@ public class SyncPair_Branch_Bot: SyncPair { private func syncBranchWithBot(completion: Completion) { let bot = self.bot - let headCommit = self.branch.commit.sha - let issue: Issue? = nil //TODO: only pull/create if we're failing + let headCommit = self.branch.commitSHA + let issue: IssueType? = nil //TODO: only pull/create if we're failing self.syncer.xcodeServer.getHostname { (hostname, error) -> () in diff --git a/BuildaKit/SyncPair_PR_NoBot.swift b/BuildaKit/SyncPair_PR_NoBot.swift index a619ee0..28daca1 100644 --- a/BuildaKit/SyncPair_PR_NoBot.swift +++ b/BuildaKit/SyncPair_PR_NoBot.swift @@ -11,9 +11,9 @@ import BuildaGitServer class SyncPair_PR_NoBot: SyncPair { - let pr: PullRequest + let pr: PullRequestType - init(pr: PullRequest) { + init(pr: PullRequestType) { self.pr = pr super.init() } @@ -28,12 +28,12 @@ class SyncPair_PR_NoBot: SyncPair { } override func syncPairName() -> String { - return "PR (\(self.pr.head.ref)) + No Bot" + return "PR (\(self.pr.headName)) + No Bot" } //MARK: Internal - private class func createBotForPR(syncer syncer: HDGitHubXCBotSyncer, pr: PullRequest, completion: Completion) { + private class func createBotForPR(syncer syncer: HDGitHubXCBotSyncer, pr: PullRequestType, completion: Completion) { syncer.createBotFromPR(pr, completion: { () -> () in completion(error: nil) diff --git a/BuildaKit/SyncerBotManipulation.swift b/BuildaKit/SyncerBotManipulation.swift index 23a8330..4e94dfc 100644 --- a/BuildaKit/SyncerBotManipulation.swift +++ b/BuildaKit/SyncerBotManipulation.swift @@ -44,7 +44,7 @@ extension HDGitHubXCBotSyncer { }) } - private func createBotFromName(botName: String, branch: String, repo: Repo, completion: () -> ()) { + private func createBotFromName(botName: String, branch: String, repo: RepoType, completion: () -> ()) { /* synced bots must have a manual schedule, Builda tells the bot to reintegrate in case of a new commit. @@ -90,15 +90,15 @@ extension HDGitHubXCBotSyncer { } } - func createBotFromPR(pr: PullRequest, completion: () -> ()) { + func createBotFromPR(pr: PullRequestType, completion: () -> ()) { - let branchName = pr.head.ref + let branchName = pr.headName let botName = BotNaming.nameForBotWithPR(pr, repoName: self.repoName()!) self.createBotFromName(botName, branch: branchName, repo: pr.head.repo, completion: completion) } - func createBotFromBranch(branch: Branch, repo: Repo, completion: () -> ()) { + func createBotFromBranch(branch: BranchType, repo: RepoType, completion: () -> ()) { let branchName = branch.name let botName = BotNaming.nameForBotWithBranch(branch, repoName: self.repoName()!) diff --git a/BuildaKit/SyncerBotNaming.swift b/BuildaKit/SyncerBotNaming.swift index 68139ef..21dafd9 100644 --- a/BuildaKit/SyncerBotNaming.swift +++ b/BuildaKit/SyncerBotNaming.swift @@ -20,11 +20,11 @@ class BotNaming { return bot.name.hasPrefix(self.prefixForBuildaBotInRepoWithName(repoName)) } - class func nameForBotWithBranch(branch: Branch, repoName: String) -> String { + class func nameForBotWithBranch(branch: BranchType, repoName: String) -> String { return "\(self.prefixForBuildaBotInRepoWithName(repoName)) |-> \(branch.name)" } - class func nameForBotWithPR(pr: PullRequest, repoName: String) -> String { + class func nameForBotWithPR(pr: PullRequestType, repoName: String) -> String { return "\(self.prefixForBuildaBotInRepoWithName(repoName)) PR #\(pr.number)" } diff --git a/BuildaKit/SyncerFactory.swift b/BuildaKit/SyncerFactory.swift index cbc8038..22454b4 100644 --- a/BuildaKit/SyncerFactory.swift +++ b/BuildaKit/SyncerFactory.swift @@ -16,7 +16,7 @@ public protocol SyncerFactoryType { func newEditableTriplet() -> EditableConfigTriplet func createXcodeServer(config: XcodeServerConfig) -> XcodeServer func createProject(config: ProjectConfig) -> Project? - func createSourceServer(token: String) -> GitHubServer + func createSourceServer(token: String) -> SourceServerType func createTrigger(config: TriggerConfig) -> Trigger } @@ -31,7 +31,7 @@ public class SyncerFactory: SyncerFactoryType { private func createSyncer(triplet: ConfigTriplet) -> HDGitHubXCBotSyncer? { let xcodeServer = self.createXcodeServer(triplet.server) - let githubServer = self.createSourceServer(triplet.project.githubToken) + let sourceServer = self.createSourceServer(triplet.project.githubToken) //TODO: pull out as SourceServerOptions let maybeProject = self.createProject(triplet.project) let triggers = triplet.triggers.map { self.createTrigger($0) } @@ -41,7 +41,7 @@ public class SyncerFactory: SyncerFactoryType { { poolAttempt.config.value = triplet.syncer poolAttempt.xcodeServer = xcodeServer - poolAttempt.github = githubServer + poolAttempt.sourceServer = sourceServer poolAttempt.project = project poolAttempt.buildTemplate = triplet.buildTemplate poolAttempt.triggers = triggers @@ -50,7 +50,7 @@ public class SyncerFactory: SyncerFactoryType { let syncer = HDGitHubXCBotSyncer( integrationServer: xcodeServer, - sourceServer: githubServer, + sourceServer: sourceServer, project: project, buildTemplate: triplet.buildTemplate, triggers: triggers, @@ -117,8 +117,10 @@ public class SyncerFactory: SyncerFactoryType { return project } - public func createSourceServer(token: String) -> GitHubServer { - let server = GitHubFactory.server(token) + public func createSourceServer(token: String) -> SourceServerType { + + let options: Set = [.Token(token)] + let server: SourceServerType = SourceServerFactory().createServer(options) return server } diff --git a/BuildaKit/SyncerGitHubUtils.swift b/BuildaKit/SyncerGitHubUtils.swift index b36035a..7e2af4b 100644 --- a/BuildaKit/SyncerGitHubUtils.swift +++ b/BuildaKit/SyncerGitHubUtils.swift @@ -12,11 +12,13 @@ import BuildaUtils extension HDGitHubXCBotSyncer { - class func createStatusFromState(state: Status.State, description: String?, targetUrl: String?) -> Status { + class func createStatusFromState(state: BuildState, description: String?, targetUrl: String?) -> StatusType { //TODO: potentially have multiple contexts to show multiple stats on the PR let context = "Buildasaur" - return Status(state: state, description: description, targetUrl: targetUrl, context: context) + + self.sourceServer + } func updateCommitStatusIfNecessary( diff --git a/BuildaKit/SyncerLogic.swift b/BuildaKit/SyncerLogic.swift index 70a03c6..bf7632d 100644 --- a/BuildaKit/SyncerLogic.swift +++ b/BuildaKit/SyncerLogic.swift @@ -11,32 +11,40 @@ import BuildaGitServer import XcodeServerSDK import BuildaUtils +public struct StatusAndComment { + public let status: StatusType + public let comment: String? + + public init(status: StatusType, comment: String? = nil) { + self.status = status + self.comment = comment + } +} + extension HDGitHubXCBotSyncer { var _project: Project { return self.project } var _xcodeServer: XcodeServer { return self.xcodeServer } - var _github: GitHubServer { return self.github } + var _sourceServer: SourceServerType { return self.github } var _buildTemplate: BuildTemplate { return self.buildTemplate } var _waitForLttm: Bool { return self.config.value.waitForLttm } var _postStatusComments: Bool { return self.config.value.postStatusComments } var _watchedBranchNames: [String] { return self.config.value.watchedBranchNames } public typealias BotActions = ( - prsToSync: [(pr: PullRequest, bot: Bot)], - prBotsToCreate: [PullRequest], - branchesToSync: [(branch: Branch, bot: Bot)], - branchBotsToCreate: [Branch], + prsToSync: [(pr: PullRequestType, bot: Bot)], + prBotsToCreate: [PullRequestType], + branchesToSync: [(branch: BranchType, bot: Bot)], + branchBotsToCreate: [BranchType], botsToDelete: [Bot]) - - public typealias GitHubStatusAndComment = (status: Status, comment: String?) - + public func repoName() -> String? { return self._project.githubRepoName() } internal func syncRepoWithName(repoName: String, completion: () -> ()) { - self._github.getRepo(repoName, completion: { (repo, error) -> () in + self._sourceServer.getRepo(repoName, completion: { (repo, error) -> () in if error != nil { //whoops, no more syncing for now @@ -55,10 +63,10 @@ extension HDGitHubXCBotSyncer { }) } - private func syncRepoWithNameAndMetadata(repoName: String, repo: Repo, completion: () -> ()) { + private func syncRepoWithNameAndMetadata(repoName: String, repo: RepoType, completion: () -> ()) { - //pull PRs from github - self._github.getOpenPullRequests(repoName, completion: { (prs, error) -> () in + //pull PRs from source server + self._sourceServer.getOpenPullRequests(repoName, completion: { (prs, error) -> () in if error != nil { //whoops, no more syncing for now diff --git a/Buildasaur.xcodeproj/project.pbxproj b/Buildasaur.xcodeproj/project.pbxproj index 1bf9d5e..8e17895 100644 --- a/Buildasaur.xcodeproj/project.pbxproj +++ b/Buildasaur.xcodeproj/project.pbxproj @@ -19,7 +19,7 @@ 3A3231B11B5AEF7900B53E3F /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A3231B01B5AEF7900B53E3F /* Logging.swift */; }; 3A32CD141A3D00F800861A34 /* Comment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A32CD131A3D00F800861A34 /* Comment.swift */; }; 3A32CD181A3D01E300861A34 /* Issue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A32CD171A3D01E300861A34 /* Issue.swift */; }; - 3A32CD1E1A3D2ADD00861A34 /* GitHubServerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A32CD1D1A3D2ADD00861A34 /* GitHubServerExtensions.swift */; }; + 3A32CD1E1A3D2ADD00861A34 /* SourceServerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A32CD1D1A3D2ADD00861A34 /* SourceServerExtensions.swift */; }; 3A395B551BCF007000BB6947 /* LoginItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A395B541BCF007000BB6947 /* LoginItem.swift */; settings = {ASSET_TAGS = (); }; }; 3A3BDC1F1AF6D34900D2CD99 /* GitHubRateLimit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A3BDC1E1AF6D34900D2CD99 /* GitHubRateLimit.swift */; }; 3A3BF8C71BD7C1680050A0B7 /* CheckoutFileParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A3BF8C61BD7C1680050A0B7 /* CheckoutFileParser.swift */; settings = {ASSET_TAGS = (); }; }; @@ -233,7 +233,7 @@ 3A3231B01B5AEF7900B53E3F /* Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = ""; }; 3A32CD131A3D00F800861A34 /* Comment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Comment.swift; sourceTree = ""; }; 3A32CD171A3D01E300861A34 /* Issue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Issue.swift; sourceTree = ""; }; - 3A32CD1D1A3D2ADD00861A34 /* GitHubServerExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubServerExtensions.swift; sourceTree = ""; }; + 3A32CD1D1A3D2ADD00861A34 /* SourceServerExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SourceServerExtensions.swift; sourceTree = ""; }; 3A38CF951A3CE94C00F41AFA /* Status.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Status.swift; path = BuildaGitServer/Status.swift; sourceTree = SOURCE_ROOT; }; 3A395B541BCF007000BB6947 /* LoginItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginItem.swift; sourceTree = ""; }; 3A3BDC1E1AF6D34900D2CD99 /* GitHubRateLimit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubRateLimit.swift; sourceTree = ""; }; @@ -466,6 +466,7 @@ isa = PBXGroup; children = ( 3A1023731BD13DE80068FAB9 /* BaseTypes.swift */, + 3A32CD1D1A3D2ADD00861A34 /* SourceServerExtensions.swift */, ); name = Base; sourceTree = ""; @@ -532,7 +533,6 @@ 3A6355D71A3BBCD200545BF9 /* GitHubEndpoints.swift */, 3A6355D91A3BBDA500545BF9 /* GitHubFactory.swift */, 3A58B1521A3B967C003E0266 /* GitHubServer.swift */, - 3A32CD1D1A3D2ADD00861A34 /* GitHubServerExtensions.swift */, 3A3BDC1E1AF6D34900D2CD99 /* GitHubRateLimit.swift */, ); name = GitHub; @@ -1518,7 +1518,7 @@ 3AB3F0E61A3CEBAC005F717F /* User.swift in Sources */, 3AB3F0E91A3CEBAC005F717F /* GitHubFactory.swift in Sources */, 3A3BDC1F1AF6D34900D2CD99 /* GitHubRateLimit.swift in Sources */, - 3A32CD1E1A3D2ADD00861A34 /* GitHubServerExtensions.swift in Sources */, + 3A32CD1E1A3D2ADD00861A34 /* SourceServerExtensions.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; From da07eed6de8b2c0b9434a381eae9e4c814307e88 Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Wed, 21 Oct 2015 23:16:38 +0100 Subject: [PATCH 05/63] refactoring in progress, abstracting away github types, very much not compiling right now, but we'll get there... worry not ;) --- BuildaGitServer/BaseTypes.swift | 18 ++++++++++++ BuildaGitServer/GitHubRateLimit.swift | 6 ++++ BuildaGitServer/GitHubServer.swift | 14 ++++----- BuildaGitServer/PullRequest.swift | 8 +++++ BuildaGitServer/Repo.swift | 10 +++++++ BuildaGitServer/SourceServerExtensions.swift | 2 +- BuildaGitServer/Status.swift | 31 ++++++++++---------- BuildaKit/SyncPairResolver.swift | 15 ++++++---- BuildaKit/SyncPair_Branch_Bot.swift | 1 + BuildaKit/SyncPair_Branch_NoBot.swift | 8 ++--- BuildaKit/SyncPair_PR_Bot.swift | 15 +++++----- BuildaKit/Syncer.swift | 4 +++ BuildaKit/SyncerBotManipulation.swift | 4 +-- BuildaKit/SyncerGitHubUtils.swift | 19 ++++++------ BuildaKit/SyncerLogic.swift | 26 ++++++++-------- 15 files changed, 115 insertions(+), 66 deletions(-) diff --git a/BuildaGitServer/BaseTypes.swift b/BuildaGitServer/BaseTypes.swift index 10d2dd2..e96fcb5 100644 --- a/BuildaGitServer/BaseTypes.swift +++ b/BuildaGitServer/BaseTypes.swift @@ -73,9 +73,16 @@ public struct RepoPermissions { } } +public protocol RateLimitType { + + var report: String { get } +} + public protocol RepoType { var permissions: RepoPermissions { get } + var originUrlSSH: String { get } + var latestRateLimitInfo: RateLimitType? { get } //TODO: add required properties } @@ -96,6 +103,8 @@ public protocol IssueType { public protocol PullRequestType: IssueType { var headName: String { get } + var headCommitSHA: String { get } + var headRepo: RepoType { get } //TODO: add required properties } @@ -111,10 +120,19 @@ public enum BuildState { public protocol StatusType { var state: BuildState { get } + var description: String? { get } //TODO: add required properties } +extension StatusType { + + func isEqual(rhs: StatusType) -> Bool { + let lhs = self + return lhs.state == rhs.state && lhs.description == rhs.description + } +} + public protocol CommentType { var body: String { get } diff --git a/BuildaGitServer/GitHubRateLimit.swift b/BuildaGitServer/GitHubRateLimit.swift index e81848b..77f9c9f 100644 --- a/BuildaGitServer/GitHubRateLimit.swift +++ b/BuildaGitServer/GitHubRateLimit.swift @@ -33,5 +33,11 @@ struct GitHubRateLimit { let report = "count: \(consumed)/\(self.limit), renews in \(Int(remainingTime)) seconds, rate: \(rateOfConsumptionPretty)/\(maxRateOfConsumptionPretty), using \(usedRatePercent)% of the allowed request rate." return report } +} + +extension GitHubRateLimit: RateLimitType { + var report: String { + return self.getReport() + } } diff --git a/BuildaGitServer/GitHubServer.swift b/BuildaGitServer/GitHubServer.swift index 735a46a..956d374 100644 --- a/BuildaGitServer/GitHubServer.swift +++ b/BuildaGitServer/GitHubServer.swift @@ -62,7 +62,7 @@ extension GitHubServer: SourceServerType { func postStatusOfCommit(commit: String, status: StatusType, repo: String, completion: (status: StatusType?, error: ErrorType?) -> ()) { - self._postStatusOfCommit(status as! Status, sha: commit, repo: repo) { (status, error) -> () in + self._postStatusOfCommit(status as! GitHubStatus, sha: commit, repo: repo) { (status, error) -> () in completion(status: status, error: error) } } @@ -83,8 +83,8 @@ extension GitHubServer: SourceServerType { func createStatusFromState(buildState: BuildState, description: String?, targetUrl: String?, context: String?) -> StatusType { - let state = Status.State.fromBuildState(buildState) - return Status(state: state, description: description, targetUrl: targetUrl, context: context) + let state = GitHubStatus.GitHubState.fromBuildState(buildState) + return GitHubStatus(state: state, description: description, targetUrl: targetUrl, context: context) } } @@ -421,7 +421,7 @@ extension GitHubServer { /** * GET the status of a commit (sha) from a repo. */ - private func _getStatusOfCommit(sha: String, repo: String, completion: (status: Status?, error: NSError?) -> ()) { + private func _getStatusOfCommit(sha: String, repo: String, completion: (status: GitHubStatus?, error: NSError?) -> ()) { let params = [ "repo": repo, @@ -436,7 +436,7 @@ extension GitHubServer { } if let body = body as? NSArray { - let statuses: [Status] = GitHubArray(body) + let statuses: [GitHubStatus] = GitHubArray(body) //sort them by creation date let mostRecentStatus = statuses.sort({ return $0.created! > $1.created! }).first completion(status: mostRecentStatus, error: nil) @@ -449,7 +449,7 @@ extension GitHubServer { /** * POST a new status on a commit. */ - private func _postStatusOfCommit(status: Status, sha: String, repo: String, completion: (status: Status?, error: NSError?) -> ()) { + private func _postStatusOfCommit(status: GitHubStatus, sha: String, repo: String, completion: (status: GitHubStatus?, error: NSError?) -> ()) { let params = [ "repo": repo, @@ -465,7 +465,7 @@ extension GitHubServer { } if let body = body as? NSDictionary { - let status = Status(json: body) + let status = GitHubStatus(json: body) completion(status: status, error: nil) } else { completion(status: nil, error: Error.withInfo("Wrong body \(body)")) diff --git a/BuildaGitServer/PullRequest.swift b/BuildaGitServer/PullRequest.swift index 0b9822f..8558f37 100644 --- a/BuildaGitServer/PullRequest.swift +++ b/BuildaGitServer/PullRequest.swift @@ -27,4 +27,12 @@ extension PullRequest: PullRequestType { var headName: String { return self.head.ref } + + var headCommitSHA: String { + return self.head.sha + } + + var headRepo: RepoType { + return self.head.repo + } } diff --git a/BuildaGitServer/Repo.swift b/BuildaGitServer/Repo.swift index d042fbe..291d37c 100644 --- a/BuildaGitServer/Repo.swift +++ b/BuildaGitServer/Repo.swift @@ -41,4 +41,14 @@ extension Repo: RepoType { let write = self.permissionsDict["push"] as? Bool ?? false return RepoPermissions(read: read, write: write) } + + var originUrlSSH: String { + + return self.repoUrlSSH + } + + var latestRateLimitInfo: RateLimitType? { + + return self.latestRateLimitInfo + } } diff --git a/BuildaGitServer/SourceServerExtensions.swift b/BuildaGitServer/SourceServerExtensions.swift index 297ccf2..5593c35 100644 --- a/BuildaGitServer/SourceServerExtensions.swift +++ b/BuildaGitServer/SourceServerExtensions.swift @@ -35,7 +35,7 @@ extension SourceServerType { } //TODO: support paging through all the comments. currently we only fetch the last ~30 comments. - func findMatchingCommentInIssue(commentsToMatch: [String], issue: Int, repo: String, completion: (foundComments: [CommentType]?, error: ErrorType?) -> ()) { + public func findMatchingCommentInIssue(commentsToMatch: [String], issue: Int, repo: String, completion: (foundComments: [CommentType]?, error: ErrorType?) -> ()) { self.getCommentsOfIssue(issue, repo: repo) { (comments, error) -> () in diff --git a/BuildaGitServer/Status.swift b/BuildaGitServer/Status.swift index c028fdf..bb781f1 100644 --- a/BuildaGitServer/Status.swift +++ b/BuildaGitServer/Status.swift @@ -9,16 +9,16 @@ import Foundation import BuildaUtils -class Status : GitHubEntity, Equatable { +class GitHubStatus : GitHubEntity { - enum State : String { + enum GitHubState : String { case NoState = "" case Pending = "pending" case Success = "success" case Error = "error" case Failure = "failure" - static func fromBuildState(buildState: BuildState) -> State { + static func fromBuildState(buildState: BuildState) -> GitHubState { switch buildState { case .NoState: return .NoState @@ -49,7 +49,7 @@ class Status : GitHubEntity, Equatable { } } - let state: State + let githubState: GitHubState let description: String? let targetUrl: String? let context: String? @@ -58,7 +58,7 @@ class Status : GitHubEntity, Equatable { required init(json: NSDictionary) { - self.state = State(rawValue: json.stringForKey("state"))! + self.githubState = GitHubState(rawValue: json.stringForKey("state"))! self.description = json.optionalStringForKey("description") self.targetUrl = json.optionalStringForKey("target_url") self.context = json.optionalStringForKey("context") @@ -72,9 +72,9 @@ class Status : GitHubEntity, Equatable { super.init(json: json) } - init(state: State, description: String?, targetUrl: String?, context: String?) { + init(state: GitHubState, description: String?, targetUrl: String?, context: String?) { - self.state = state + self.githubState = state self.description = description self.targetUrl = targetUrl self.context = context @@ -88,7 +88,7 @@ class Status : GitHubEntity, Equatable { let dictionary = NSMutableDictionary() - dictionary["state"] = self.state.rawValue + dictionary["state"] = self.githubState.rawValue dictionary.optionallyAddValueForKey(self.description, key: "description") dictionary.optionallyAddValueForKey(self.targetUrl, key: "target_url") dictionary.optionallyAddValueForKey(self.context, key: "context") @@ -97,14 +97,10 @@ class Status : GitHubEntity, Equatable { } } -func ==(lhs: Status, rhs: Status) -> Bool { - return lhs.state == rhs.state && lhs.description == rhs.description -} - //for sending statuses upstream -extension Status { +extension GitHubStatus { - class func toDict(state: State, description: String? = nil, targetUrl: String? = nil, context: String? = nil) -> [String: String] { + class func toDict(state: GitHubState, description: String? = nil, targetUrl: String? = nil, context: String? = nil) -> [String: String] { return [ "state" : state.rawValue, "target_url" : targetUrl ?? "", @@ -114,6 +110,9 @@ extension Status { } } -extension Status: StatusType { +extension GitHubStatus: StatusType { -} + var state: BuildState { + return self.githubState.toBuildState() + } +} \ No newline at end of file diff --git a/BuildaKit/SyncPairResolver.swift b/BuildaKit/SyncPairResolver.swift index fa0eb7f..a9e4e5e 100644 --- a/BuildaKit/SyncPairResolver.swift +++ b/BuildaKit/SyncPairResolver.swift @@ -22,6 +22,7 @@ public class SyncPairResolver { issue: IssueType?, bot: Bot, hostname: String, + syncer: HDGitHubXCBotSyncer, integrations: [Integration]) -> SyncPair.Actions { var integrationsToCancel: [Integration] = [] @@ -126,6 +127,7 @@ public class SyncPairResolver { pending: latestPendingIntegration, running: runningIntegration, link: link, + syncer: syncer, completed: completedIntegrations) //merge in nested actions @@ -210,6 +212,7 @@ public class SyncPairResolver { pending: Integration?, running: Integration?, link: (Integration) -> String?, + syncer: HDGitHubXCBotSyncer, completed: Set) -> SyncPair.Actions { let statusWithComment: StatusAndComment @@ -220,8 +223,8 @@ public class SyncPairResolver { //TODO: show how many builds are ahead in the queue and estimate when it will be //started and when finished? (there is an average running time on each bot, it should be easy) - let status = HDGitHubXCBotSyncer.createStatusFromState(.Pending, description: "Build waiting in the queue...", targetUrl: link(pending)) - statusWithComment = (status: status, comment: nil) + let status = syncer.createStatusFromState(.Pending, description: "Build waiting in the queue...", targetUrl: link(pending)) + statusWithComment = StatusAndComment(status: status, comment: nil) //also, cancel the running integration, if it's there any if let running = running { @@ -235,8 +238,8 @@ public class SyncPairResolver { //there is a running integration. //TODO: estimate, based on the average running time of this bot and on the started timestamp, when it will finish. add that to the description. let currentStepString = running.currentStep.rawValue - let status = HDGitHubXCBotSyncer.createStatusFromState(.Pending, description: "Integration step: \(currentStepString)...", targetUrl: link(running)) - statusWithComment = (status: status, comment: nil) + let status = syncer.createStatusFromState(.Pending, description: "Integration step: \(currentStepString)...", targetUrl: link(running)) + statusWithComment = StatusAndComment(status: status, comment: nil) } else { @@ -249,8 +252,8 @@ public class SyncPairResolver { } else { //this shouldn't happen. Log.error("LOGIC ERROR! This shouldn't happen, there are no completed integrations!") - let status = HDGitHubXCBotSyncer.createStatusFromState(.Error, description: "* UNKNOWN STATE, Builda ERROR *", targetUrl: nil) - statusWithComment = (status: status, "Builda error, unknown state!") + let status = syncer.createStatusFromState(.Error, description: "* UNKNOWN STATE, Builda ERROR *", targetUrl: nil) + statusWithComment = StatusAndComment(status: status, comment: "Builda error, unknown state!") } } } diff --git a/BuildaKit/SyncPair_Branch_Bot.swift b/BuildaKit/SyncPair_Branch_Bot.swift index 5fab016..0fdc1f5 100644 --- a/BuildaKit/SyncPair_Branch_Bot.swift +++ b/BuildaKit/SyncPair_Branch_Bot.swift @@ -61,6 +61,7 @@ public class SyncPair_Branch_Bot: SyncPair { issue: issue, bot: bot, hostname: hostname!, + syncer: self.syncer, integrations: integrations) //in case of branches, we also (optionally) want to add functionality for creating an issue if the branch starts failing and updating with comments the same way we do with PRs. diff --git a/BuildaKit/SyncPair_Branch_NoBot.swift b/BuildaKit/SyncPair_Branch_NoBot.swift index 0546aad..a093354 100644 --- a/BuildaKit/SyncPair_Branch_NoBot.swift +++ b/BuildaKit/SyncPair_Branch_NoBot.swift @@ -12,10 +12,10 @@ import BuildaGitServer class SyncPair_Branch_NoBot: SyncPair { - let branch: Branch - let repo: Repo + let branch: BranchType + let repo: RepoType - init(branch: Branch, repo: Repo) { + init(branch: BranchType, repo: RepoType) { self.branch = branch self.repo = repo super.init() @@ -37,7 +37,7 @@ class SyncPair_Branch_NoBot: SyncPair { //MARK: Internal - private class func createBotForBranch(syncer syncer: HDGitHubXCBotSyncer, branch: Branch, repo: Repo, completion: Completion) { + private class func createBotForBranch(syncer syncer: HDGitHubXCBotSyncer, branch: BranchType, repo: RepoType, completion: Completion) { syncer.createBotFromBranch(branch, repo: repo, completion: { () -> () in completion(error: nil) diff --git a/BuildaKit/SyncPair_PR_Bot.swift b/BuildaKit/SyncPair_PR_Bot.swift index 7e17968..8f8c7af 100644 --- a/BuildaKit/SyncPair_PR_Bot.swift +++ b/BuildaKit/SyncPair_PR_Bot.swift @@ -13,11 +13,11 @@ import BuildaUtils public class SyncPair_PR_Bot: SyncPair { - let pr: PullRequest + let pr: PullRequestType let bot: Bot public let resolver: SyncPairPRResolver - public init(pr: PullRequest, bot: Bot, resolver: SyncPairPRResolver) { + public init(pr: PullRequestType, bot: Bot, resolver: SyncPairPRResolver) { self.pr = pr self.bot = bot self.resolver = resolver @@ -31,7 +31,7 @@ public class SyncPair_PR_Bot: SyncPair { } override func syncPairName() -> String { - return "PR (\(self.pr.number):\(self.pr.head.ref)) + Bot (\(self.bot.name))" + return "PR (\(self.pr.number):\(self.pr.headName)) + Bot (\(self.bot.name))" } //MARK: Internal @@ -41,7 +41,7 @@ public class SyncPair_PR_Bot: SyncPair { let syncer = self.syncer let bot = self.bot let pr = self.pr - let headCommit = pr.head.sha + let headCommit = pr.headCommitSHA let issue = pr self.getIntegrations(bot, completion: { (integrations, error) -> () in @@ -73,6 +73,7 @@ public class SyncPair_PR_Bot: SyncPair { issue: issue, bot: bot, hostname: hostname!, + syncer: self.syncer, integrations: integrations) self.performActions(actions, completion: completion) } @@ -82,8 +83,8 @@ public class SyncPair_PR_Bot: SyncPair { //not enabled, make sure the PR reflects that and the instructions are clear Log.verbose("Bot \(bot.name) is not yet enabled, ignoring...") - let status = HDGitHubXCBotSyncer.createStatusFromState(.Pending, description: "Waiting for \"lttm\" to start testing", targetUrl: nil) - let notYetEnabled: HDGitHubXCBotSyncer.GitHubStatusAndComment = (status: status, comment: nil) + let status = self.syncer.createStatusFromState(BuildState.Pending, description: "Waiting for \"lttm\" to start testing", targetUrl: nil) + let notYetEnabled = StatusAndComment(status: status, comment: nil) syncer.updateCommitStatusIfNecessary(notYetEnabled, commit: headCommit, issue: pr, completion: completion) } }) @@ -106,7 +107,7 @@ public class SyncPair_PR_Bot: SyncPair { if let repoName = syncer.repoName() { - self.syncer._github.findMatchingCommentInIssue(keyword, issue: self.pr.number, repo: repoName) { + self.syncer.sourceServer.findMatchingCommentInIssue(keyword, issue: self.pr.number, repo: repoName) { (foundComments, error) -> () in if error != nil { diff --git a/BuildaKit/Syncer.swift b/BuildaKit/Syncer.swift index a9e5e10..106a050 100644 --- a/BuildaKit/Syncer.swift +++ b/BuildaKit/Syncer.swift @@ -143,6 +143,10 @@ class Trampoline: NSObject { self.notifyError(Error.withInfo(errorString), context: context) } + func notifyError(error: ErrorType?, context: String?) { + self.notifyError(error as? NSError, context: context) + } + func notifyError(error: NSError?, context: String?) { var message = "Syncing encountered a problem. " diff --git a/BuildaKit/SyncerBotManipulation.swift b/BuildaKit/SyncerBotManipulation.swift index 4e94dfc..28d42e4 100644 --- a/BuildaKit/SyncerBotManipulation.swift +++ b/BuildaKit/SyncerBotManipulation.swift @@ -59,7 +59,7 @@ extension HDGitHubXCBotSyncer { let template = self.buildTemplate //to handle forks - let headOriginUrl = repo.repoUrlSSH + let headOriginUrl = repo.originUrlSSH let localProjectOriginUrl = self._project.workspaceMetadata!.projectURL.absoluteString let project: Project @@ -95,7 +95,7 @@ extension HDGitHubXCBotSyncer { let branchName = pr.headName let botName = BotNaming.nameForBotWithPR(pr, repoName: self.repoName()!) - self.createBotFromName(botName, branch: branchName, repo: pr.head.repo, completion: completion) + self.createBotFromName(botName, branch: branchName, repo: pr.headRepo, completion: completion) } func createBotFromBranch(branch: BranchType, repo: RepoType, completion: () -> ()) { diff --git a/BuildaKit/SyncerGitHubUtils.swift b/BuildaKit/SyncerGitHubUtils.swift index 7e2af4b..98c3e30 100644 --- a/BuildaKit/SyncerGitHubUtils.swift +++ b/BuildaKit/SyncerGitHubUtils.swift @@ -12,23 +12,22 @@ import BuildaUtils extension HDGitHubXCBotSyncer { - class func createStatusFromState(state: BuildState, description: String?, targetUrl: String?) -> StatusType { + func createStatusFromState(state: BuildState, description: String?, targetUrl: String?) -> StatusType { //TODO: potentially have multiple contexts to show multiple stats on the PR let context = "Buildasaur" - self.sourceServer - + return self._sourceServer.createStatusFromState(state, description: description, targetUrl: targetUrl, context: context) } func updateCommitStatusIfNecessary( - newStatus: GitHubStatusAndComment, + newStatus: StatusAndComment, commit: String, - issue: Issue?, + issue: IssueType?, completion: SyncPair.Completion) { let repoName = self.repoName()! - self._github.getStatusOfCommit(commit, repo: repoName, completion: { (status, error) -> () in + self._sourceServer.getStatusOfCommit(commit, repo: repoName, completion: { (status, error) -> () in if error != nil { let e = Error.withInfo("Commit \(commit) failed to return status", internalError: error) @@ -36,7 +35,7 @@ extension HDGitHubXCBotSyncer { return } - if status == nil || newStatus.status != status! { + if status == nil || !newStatus.status.isEqual(status!) { //TODO: add logic for handling the creation of a new Issue for branch tracking //and the deletion of it when build succeeds etc. @@ -49,9 +48,9 @@ extension HDGitHubXCBotSyncer { }) } - func postStatusWithComment(statusWithComment: GitHubStatusAndComment, commit: String, repo: String, issue: Issue?, completion: SyncPair.Completion) { + func postStatusWithComment(statusWithComment: StatusAndComment, commit: String, repo: String, issue: IssueType?, completion: SyncPair.Completion) { - self._github.postStatusOfCommit(statusWithComment.status, sha: commit, repo: repo) { (status, error) -> () in + self._sourceServer.postStatusOfCommit(commit, status: statusWithComment.status, repo: repo) { (status, error) -> () in if error != nil { let e = Error.withInfo("Failed to post a status on commit \(commit) of repo \(repo)", internalError: error) @@ -68,7 +67,7 @@ extension HDGitHubXCBotSyncer { let comment = statusWithComment.comment where postStatusComments { //we have a comment, post it - self._github.postCommentOnIssue(comment, issueNumber: issue.number, repo: repo, completion: { (comment, error) -> () in + self._sourceServer.postCommentOnIssue(comment, issueNumber: issue.number, repo: repo, completion: { (comment, error) -> () in if error != nil { let e = Error.withInfo("Failed to post a comment \"\(comment)\" on Issue \(issue.number) of repo \(repo)", internalError: error) diff --git a/BuildaKit/SyncerLogic.swift b/BuildaKit/SyncerLogic.swift index bf7632d..b74f7a3 100644 --- a/BuildaKit/SyncerLogic.swift +++ b/BuildaKit/SyncerLogic.swift @@ -25,7 +25,7 @@ extension HDGitHubXCBotSyncer { var _project: Project { return self.project } var _xcodeServer: XcodeServer { return self.xcodeServer } - var _sourceServer: SourceServerType { return self.github } + var _sourceServer: SourceServerType { return self.sourceServer } var _buildTemplate: BuildTemplate { return self.buildTemplate } var _waitForLttm: Bool { return self.config.value.waitForLttm } var _postStatusComments: Bool { return self.config.value.postStatusComments } @@ -87,14 +87,14 @@ extension HDGitHubXCBotSyncer { }) } - private func syncRepoWithPRs(repoName: String, repo: Repo, prs: [PullRequest], completion: () -> ()) { + private func syncRepoWithPRs(repoName: String, repo: RepoType, prs: [PullRequestType], completion: () -> ()) { //only fetch branches if there are any watched ones. there might be tens or hundreds of branches //so we don't want to fetch them unless user actually is watching any non-PR branches. if self._watchedBranchNames.count > 0 { //we have PRs, now fetch branches - self._github.getBranchesOfRepo(repoName, completion: { (branches, error) -> () in + self._sourceServer.getBranchesOfRepo(repoName, completion: { (branches, error) -> () in if error != nil { //whoops, no more syncing for now @@ -118,7 +118,7 @@ extension HDGitHubXCBotSyncer { } } - private func syncRepoWithPRsAndBranches(repoName: String, repo: Repo, prs: [PullRequest], branches: [Branch], completion: () -> ()) { + private func syncRepoWithPRsAndBranches(repoName: String, repo: RepoType, prs: [PullRequestType], branches: [BranchType], completion: () -> ()) { //we have branches, now fetch bots self._xcodeServer.getBots({ (bots, error) -> () in @@ -138,7 +138,7 @@ extension HDGitHubXCBotSyncer { self.syncPRsAndBranchesAndBots(repo: repo, repoName: repoName, prs: prs, branches: branches, bots: bots, completion: { //everything is done, report the damage of GitHub rate limit - if let rateLimitInfo = self._github.latestRateLimitInfo { + if let rateLimitInfo = self._sourceServer.latestRateLimitInfo { let report = rateLimitInfo.getReport() self.reports["GitHub Rate Limit"] = report @@ -154,7 +154,7 @@ extension HDGitHubXCBotSyncer { }) } - public func syncPRsAndBranchesAndBots(repo repo: Repo, repoName: String, prs: [PullRequest], branches: [Branch], bots: [Bot], completion: () -> ()) { + public func syncPRsAndBranchesAndBots(repo repo: RepoType, repoName: String, prs: [PullRequestType], branches: [BranchType], bots: [Bot], completion: () -> ()) { let prsDescription = prs.map({ " PR \($0.number): \($0.title) [\($0.head.ref) -> \($0.base.ref)]" }).joinWithSeparator("\n") let branchesDescription = branches.map({ " Branch [\($0.name):\($0.commit.sha)]" }).joinWithSeparator("\n") @@ -173,8 +173,8 @@ extension HDGitHubXCBotSyncer { public func resolvePRsAndBranchesAndBots( repoName repoName: String, - prs: [PullRequest], - branches: [Branch], + prs: [PullRequestType], + branches: [BranchType], bots: [Bot]) -> BotActions { @@ -186,16 +186,16 @@ extension HDGitHubXCBotSyncer { var mappedBots = buildaBots.toDictionary({ $0.name }) //PRs that also have a bot, prsToSync - var prsToSync: [(pr: PullRequest, bot: Bot)] = [] + var prsToSync: [(pr: PullRequestType, bot: Bot)] = [] //branches that also have a bot, branchesToSync - var branchesToSync: [(branch: Branch, bot: Bot)] = [] + var branchesToSync: [(branch: BranchType, bot: Bot)] = [] //PRs that don't have a bot yet, to create - var prBotsToCreate: [PullRequest] = [] + var prBotsToCreate: [PullRequestType] = [] //branches that don't have a bot yet, to create - var branchBotsToCreate: [Branch] = [] + var branchBotsToCreate: [BranchType] = [] //make sure every PR has a bot for pr in prs { @@ -254,7 +254,7 @@ extension HDGitHubXCBotSyncer { return (prsToSync, prBotsToCreate, branchesToSync, branchBotsToCreate, botsToDelete) } - public func createSyncPairsFrom(repo repo: Repo, botActions: BotActions) -> [SyncPair] { + public func createSyncPairsFrom(repo repo: RepoType, botActions: BotActions) -> [SyncPair] { //create sync pairs for each action needed let syncPRBotSyncPairs = botActions.prsToSync.map({ From 8d984ff277e36b874d66211764d5de8c41fcf629 Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Thu, 21 Jan 2016 17:50:57 +0000 Subject: [PATCH 06/63] compiles, but is still very broken (work in progress) --- BuildaGitServer/BaseTypes.swift | 6 +- BuildaGitServer/GitHubServer.swift | 6 +- BuildaGitServer/PullRequest.swift | 4 + BuildaGitServerTests/GitHubServerTests.swift | 341 +++++++++--------- BuildaKit/SummaryBuilder.swift | 17 +- BuildaKit/SyncPair_PR_Bot.swift | 2 +- BuildaKit/SyncerGitHubUtils.swift | 6 +- BuildaKit/SyncerLogic.swift | 20 +- Buildasaur.xcodeproj/project.pbxproj | 1 + .../xcschemes/Buildasaur.xcscheme | 21 +- Buildasaur/BranchWatchingViewController.swift | 14 +- .../ManualBotManagementViewController.swift | 4 +- 12 files changed, 233 insertions(+), 209 deletions(-) diff --git a/BuildaGitServer/BaseTypes.swift b/BuildaGitServer/BaseTypes.swift index e96fcb5..7c3fafa 100644 --- a/BuildaGitServer/BaseTypes.swift +++ b/BuildaGitServer/BaseTypes.swift @@ -106,6 +106,10 @@ public protocol PullRequestType: IssueType { var headCommitSHA: String { get } var headRepo: RepoType { get } + var baseName: String { get } + + var title: String { get } + //TODO: add required properties } @@ -127,7 +131,7 @@ public protocol StatusType { extension StatusType { - func isEqual(rhs: StatusType) -> Bool { + public func isEqual(rhs: StatusType) -> Bool { let lhs = self return lhs.state == rhs.state && lhs.description == rhs.description } diff --git a/BuildaGitServer/GitHubServer.swift b/BuildaGitServer/GitHubServer.swift index b1e626a..67d8fa0 100644 --- a/BuildaGitServer/GitHubServer.swift +++ b/BuildaGitServer/GitHubServer.swift @@ -90,11 +90,9 @@ extension GitHubServer: SourceServerType { } } -//FYI - GitHub API has a rate limit of 5,000 requests per hour. should be more than enough, but keep it in mind -//when calling the API frequently. extension GitHubServer { - private func _sendRequestWithPossiblePagination(request: NSURLRequest, accumulatedResponseBody: NSArray, completion: HTTP.Completion) { + private func _sendRequestWithPossiblePagination(request: NSMutableURLRequest, accumulatedResponseBody: NSArray, completion: HTTP.Completion) { self._sendRequest(request) { (response, body, error) -> () in @@ -182,7 +180,7 @@ extension GitHubServer { return linkDict } - private func _sendRequest(request: NSURLRequest, completion: HTTP.Completion) { + private func _sendRequest(request: NSMutableURLRequest, completion: HTTP.Completion) { let cachedInfo = self.cache.getCachedInfoForRequest(request) if let etag = cachedInfo.etag { diff --git a/BuildaGitServer/PullRequest.swift b/BuildaGitServer/PullRequest.swift index 8558f37..f71501c 100644 --- a/BuildaGitServer/PullRequest.swift +++ b/BuildaGitServer/PullRequest.swift @@ -35,4 +35,8 @@ extension PullRequest: PullRequestType { var headRepo: RepoType { return self.head.repo } + + var baseName: String { + return self.base.ref + } } diff --git a/BuildaGitServerTests/GitHubServerTests.swift b/BuildaGitServerTests/GitHubServerTests.swift index 1799dd9..4e25796 100644 --- a/BuildaGitServerTests/GitHubServerTests.swift +++ b/BuildaGitServerTests/GitHubServerTests.swift @@ -13,176 +13,177 @@ import BuildaUtils class GitHubSourceTests: XCTestCase { - var github: GitHubServer! - - override func setUp() { - super.setUp() - - self.github = GitHubFactory.server(nil) - } - - override func tearDown() { - - self.github = nil - - super.tearDown() - } - - func tryEndpoint(method: HTTP.Method, endpoint: GitHubEndpoints.Endpoint, params: [String: String]?, completion: (body: AnyObject!, error: NSError!) -> ()) { - - let expect = expectationWithDescription("Waiting for url request") - - let request = try! self.github.endpoints.createRequest(method, endpoint: endpoint, params: params) - - self.github.http.sendRequest(request, completion: { (response, body, error) -> () in - - completion(body: body, error: error) - expect.fulfill() - }) - - waitForExpectationsWithTimeout(10, handler: nil) - } - - func testGetPullRequests() { - - let params = [ - "repo": "czechboy0/Buildasaur-Tester" - ] - - self.tryEndpoint(.GET, endpoint: .PullRequests, params: params) { (body, error) -> () in - - XCTAssertNotNil(body, "Body must be non-nil") - if let body = body as? NSArray { - let prs: [PullRequest] = GitHubArray(body) - XCTAssertGreaterThan(prs.count, 0, "We need > 0 items to test parsing") - Log.verbose("Parsed PRs: \(prs)") - } else { - XCTFail("Body nil") - } - } - } - - func testGetBranches() { - - let params = [ - "repo": "czechboy0/Buildasaur-Tester" - ] - - self.tryEndpoint(.GET, endpoint: .Branches, params: params) { (body, error) -> () in - - XCTAssertNotNil(body, "Body must be non-nil") - if let body = body as? NSArray { - let branches: [Branch] = GitHubArray(body) - XCTAssertGreaterThan(branches.count, 0, "We need > 0 items to test parsing") - Log.verbose("Parsed branches: \(branches)") - } else { - XCTFail("Body nil") - } - } - } - - //manual parsing tested here, sort of a documentation as well - - func testUserParsing() { - - let dictionary = [ - "login": "czechboy0", - "name": "Honza Dvorsky", - "avatar_url": "https://avatars.githubusercontent.com/u/2182121?v=3", - "html_url": "https://github.com/czechboy0" - ] - - let user = User(json: dictionary) - XCTAssertEqual(user.userName, "czechboy0") - XCTAssertEqual(user.realName!, "Honza Dvorsky") - XCTAssertEqual(user.avatarUrl!, "https://avatars.githubusercontent.com/u/2182121?v=3") - XCTAssertEqual(user.htmlUrl!, "https://github.com/czechboy0") - } - - func testRepoParsing() { - - let dictionary = [ - "name": "Buildasaur", - "full_name": "czechboy0/Buildasaur", - "clone_url": "https://github.com/czechboy0/Buildasaur.git", - "ssh_url": "git@github.com:czechboy0/Buildasaur.git", - "html_url": "https://github.com/czechboy0/Buildasaur" - ] - - let repo = Repo(json: dictionary) - XCTAssertEqual(repo.name, "Buildasaur") - XCTAssertEqual(repo.fullName, "czechboy0/Buildasaur") - XCTAssertEqual(repo.repoUrlHTTPS, "https://github.com/czechboy0/Buildasaur.git") - XCTAssertEqual(repo.repoUrlSSH, "git@github.com:czechboy0/Buildasaur.git") - XCTAssertEqual(repo.htmlUrl!, "https://github.com/czechboy0/Buildasaur") - } - - func testCommitParsing() { - - let dictionary: NSDictionary = [ - "sha": "08182438ed2ef3b34bd97db85f39deb60e2dcd7d", - "url": "https://api.github.com/repos/czechboy0/Buildasaur/commits/08182438ed2ef3b34bd97db85f39deb60e2dcd7d" - ] - - let commit = Commit(json: dictionary) - XCTAssertEqual(commit.sha, "08182438ed2ef3b34bd97db85f39deb60e2dcd7d") - XCTAssertEqual(commit.url!, "https://api.github.com/repos/czechboy0/Buildasaur/commits/08182438ed2ef3b34bd97db85f39deb60e2dcd7d") - } - - func testBranchParsing() { - - let commitDictionary = [ - "sha": "08182438ed2ef3b34bd97db85f39deb60e2dcd7d", - "url": "https://api.github.com/repos/czechboy0/Buildasaur/commits/08182438ed2ef3b34bd97db85f39deb60e2dcd7d" - ] - let dictionary = [ - "name": "master", - "commit": commitDictionary - ] - - let branch = Branch(json: dictionary) - XCTAssertEqual(branch.name, "master") - XCTAssertEqual(branch.commit.sha, "08182438ed2ef3b34bd97db85f39deb60e2dcd7d") - XCTAssertEqual(branch.commit.url!, "https://api.github.com/repos/czechboy0/Buildasaur/commits/08182438ed2ef3b34bd97db85f39deb60e2dcd7d") - } - - func testPullRequestBranchParsing() { - - let dictionary = [ - "ref": "fb-loadNode", - "sha": "7e45fa772565969ee801b0bdce0f560122e34610", - "user": [ - "login": "aleclarson", - "avatar_url": "https://avatars.githubusercontent.com/u/1925840?v=3", - "url": "https://api.github.com/users/aleclarson", - "html_url": "https://github.com/aleclarson", - ], - "repo": [ - "name": "AsyncDisplayKit", - "full_name": "aleclarson/AsyncDisplayKit", - "owner": [ - "login": "aleclarson", - "avatar_url": "https://avatars.githubusercontent.com/u/1925840?v=3", - "url": "https://api.github.com/users/aleclarson", - "html_url": "https://github.com/aleclarson", - ], - "html_url": "https://github.com/aleclarson/AsyncDisplayKit", - "description": "Smooth asynchronous user interfaces for iOS apps.", - "url": "https://api.github.com/repos/aleclarson/AsyncDisplayKit", - "ssh_url": "git@github.com:aleclarson/AsyncDisplayKit.git", - "clone_url": "https://github.com/aleclarson/AsyncDisplayKit.git", - ] - ] - - let prbranch = PullRequestBranch(json: dictionary) - XCTAssertEqual(prbranch.ref, "fb-loadNode") - XCTAssertEqual(prbranch.sha, "7e45fa772565969ee801b0bdce0f560122e34610") - XCTAssertEqual(prbranch.repo.name, "AsyncDisplayKit") - } - - func testResponseCaching() { - //TODO - } + //TODO: fix tests +// var github: GitHubServer! +// +// override func setUp() { +// super.setUp() +// +// self.github = GitHubFactory.server(nil) +// } +// +// override func tearDown() { +// +// self.github = nil +// +// super.tearDown() +// } +// +// func tryEndpoint(method: HTTP.Method, endpoint: GitHubEndpoints.Endpoint, params: [String: String]?, completion: (body: AnyObject!, error: NSError!) -> ()) { +// +// let expect = expectationWithDescription("Waiting for url request") +// +// let request = try! self.github.endpoints.createRequest(method, endpoint: endpoint, params: params) +// +// self.github.http.sendRequest(request, completion: { (response, body, error) -> () in +// +// completion(body: body, error: error) +// expect.fulfill() +// }) +// +// waitForExpectationsWithTimeout(10, handler: nil) +// } +// +// func testGetPullRequests() { +// +// let params = [ +// "repo": "czechboy0/Buildasaur-Tester" +// ] +// +// self.tryEndpoint(.GET, endpoint: .PullRequests, params: params) { (body, error) -> () in +// +// XCTAssertNotNil(body, "Body must be non-nil") +// if let body = body as? NSArray { +// let prs: [PullRequest] = GitHubArray(body) +// XCTAssertGreaterThan(prs.count, 0, "We need > 0 items to test parsing") +// Log.verbose("Parsed PRs: \(prs)") +// } else { +// XCTFail("Body nil") +// } +// } +// } +// +// func testGetBranches() { +// +// let params = [ +// "repo": "czechboy0/Buildasaur-Tester" +// ] +// +// self.tryEndpoint(.GET, endpoint: .Branches, params: params) { (body, error) -> () in +// +// XCTAssertNotNil(body, "Body must be non-nil") +// if let body = body as? NSArray { +// let branches: [Branch] = GitHubArray(body) +// XCTAssertGreaterThan(branches.count, 0, "We need > 0 items to test parsing") +// Log.verbose("Parsed branches: \(branches)") +// } else { +// XCTFail("Body nil") +// } +// } +// } +// +// //manual parsing tested here, sort of a documentation as well +// +// func testUserParsing() { +// +// let dictionary = [ +// "login": "czechboy0", +// "name": "Honza Dvorsky", +// "avatar_url": "https://avatars.githubusercontent.com/u/2182121?v=3", +// "html_url": "https://github.com/czechboy0" +// ] +// +// let user = User(json: dictionary) +// XCTAssertEqual(user.userName, "czechboy0") +// XCTAssertEqual(user.realName!, "Honza Dvorsky") +// XCTAssertEqual(user.avatarUrl!, "https://avatars.githubusercontent.com/u/2182121?v=3") +// XCTAssertEqual(user.htmlUrl!, "https://github.com/czechboy0") +// } +// +// func testRepoParsing() { +// +// let dictionary = [ +// "name": "Buildasaur", +// "full_name": "czechboy0/Buildasaur", +// "clone_url": "https://github.com/czechboy0/Buildasaur.git", +// "ssh_url": "git@github.com:czechboy0/Buildasaur.git", +// "html_url": "https://github.com/czechboy0/Buildasaur" +// ] +// +// let repo = Repo(json: dictionary) +// XCTAssertEqual(repo.name, "Buildasaur") +// XCTAssertEqual(repo.fullName, "czechboy0/Buildasaur") +// XCTAssertEqual(repo.repoUrlHTTPS, "https://github.com/czechboy0/Buildasaur.git") +// XCTAssertEqual(repo.repoUrlSSH, "git@github.com:czechboy0/Buildasaur.git") +// XCTAssertEqual(repo.htmlUrl!, "https://github.com/czechboy0/Buildasaur") +// } +// +// func testCommitParsing() { +// +// let dictionary: NSDictionary = [ +// "sha": "08182438ed2ef3b34bd97db85f39deb60e2dcd7d", +// "url": "https://api.github.com/repos/czechboy0/Buildasaur/commits/08182438ed2ef3b34bd97db85f39deb60e2dcd7d" +// ] +// +// let commit = Commit(json: dictionary) +// XCTAssertEqual(commit.sha, "08182438ed2ef3b34bd97db85f39deb60e2dcd7d") +// XCTAssertEqual(commit.url!, "https://api.github.com/repos/czechboy0/Buildasaur/commits/08182438ed2ef3b34bd97db85f39deb60e2dcd7d") +// } +// +// func testBranchParsing() { +// +// let commitDictionary = [ +// "sha": "08182438ed2ef3b34bd97db85f39deb60e2dcd7d", +// "url": "https://api.github.com/repos/czechboy0/Buildasaur/commits/08182438ed2ef3b34bd97db85f39deb60e2dcd7d" +// ] +// let dictionary = [ +// "name": "master", +// "commit": commitDictionary +// ] +// +// let branch = Branch(json: dictionary) +// XCTAssertEqual(branch.name, "master") +// XCTAssertEqual(branch.commit.sha, "08182438ed2ef3b34bd97db85f39deb60e2dcd7d") +// XCTAssertEqual(branch.commit.url!, "https://api.github.com/repos/czechboy0/Buildasaur/commits/08182438ed2ef3b34bd97db85f39deb60e2dcd7d") +// } +// +// func testPullRequestBranchParsing() { +// +// let dictionary = [ +// "ref": "fb-loadNode", +// "sha": "7e45fa772565969ee801b0bdce0f560122e34610", +// "user": [ +// "login": "aleclarson", +// "avatar_url": "https://avatars.githubusercontent.com/u/1925840?v=3", +// "url": "https://api.github.com/users/aleclarson", +// "html_url": "https://github.com/aleclarson", +// ], +// "repo": [ +// "name": "AsyncDisplayKit", +// "full_name": "aleclarson/AsyncDisplayKit", +// "owner": [ +// "login": "aleclarson", +// "avatar_url": "https://avatars.githubusercontent.com/u/1925840?v=3", +// "url": "https://api.github.com/users/aleclarson", +// "html_url": "https://github.com/aleclarson", +// ], +// "html_url": "https://github.com/aleclarson/AsyncDisplayKit", +// "description": "Smooth asynchronous user interfaces for iOS apps.", +// "url": "https://api.github.com/repos/aleclarson/AsyncDisplayKit", +// "ssh_url": "git@github.com:aleclarson/AsyncDisplayKit.git", +// "clone_url": "https://github.com/aleclarson/AsyncDisplayKit.git", +// ] +// ] +// +// let prbranch = PullRequestBranch(json: dictionary) +// XCTAssertEqual(prbranch.ref, "fb-loadNode") +// XCTAssertEqual(prbranch.sha, "7e45fa772565969ee801b0bdce0f560122e34610") +// XCTAssertEqual(prbranch.repo.name, "AsyncDisplayKit") +// } +// +// func testResponseCaching() { +// //TODO +// } } diff --git a/BuildaKit/SummaryBuilder.swift b/BuildaKit/SummaryBuilder.swift index b53337b..3d9c52f 100644 --- a/BuildaKit/SummaryBuilder.swift +++ b/BuildaKit/SummaryBuilder.swift @@ -28,7 +28,7 @@ class SummaryBuilder { let linkToIntegration = self.linkBuilder(integration) self.addBaseCommentFromIntegration(integration) - let status = HDGitHubXCBotSyncer.createStatusFromState(.Success, description: "Build passed!", targetUrl: linkToIntegration) + let status = self.createStatus(.Success, description: "Build passed!", targetUrl: linkToIntegration) let buildResultSummary = integration.buildResultSummary! if integration.result == .Succeeded { @@ -51,7 +51,7 @@ class SummaryBuilder { self.addBaseCommentFromIntegration(integration) - let status = HDGitHubXCBotSyncer.createStatusFromState(.Failure, description: "Build failed tests!", targetUrl: linkToIntegration) + let status = self.createStatus(.Failure, description: "Build failed tests!", targetUrl: linkToIntegration) let buildResultSummary = integration.buildResultSummary! self.appendTestFailure(buildResultSummary) return self.buildWithStatus(status) @@ -62,7 +62,7 @@ class SummaryBuilder { let linkToIntegration = self.linkBuilder(integration) self.addBaseCommentFromIntegration(integration) - let status = HDGitHubXCBotSyncer.createStatusFromState(.Error, description: "Build error!", targetUrl: linkToIntegration) + let status = self.createStatus(.Error, description: "Build error!", targetUrl: linkToIntegration) self.appendErrors(integration) return self.buildWithStatus(status) @@ -74,7 +74,7 @@ class SummaryBuilder { self.addBaseCommentFromIntegration(integration) - let status = HDGitHubXCBotSyncer.createStatusFromState(.Error, description: "Build canceled!", targetUrl: linkToIntegration) + let status = self.createStatus(.Error, description: "Build canceled!", targetUrl: linkToIntegration) self.appendCancel() return self.buildWithStatus(status) @@ -82,12 +82,17 @@ class SummaryBuilder { func buildEmptyIntegration() -> StatusAndComment { - let status = HDGitHubXCBotSyncer.createStatusFromState(.NoState, description: nil, targetUrl: nil) - return (status: status, comment: nil) + let status = self.createStatus(.NoState, description: nil, targetUrl: nil) + return self.buildWithStatus(status) } //MARK: utils + private func createStatus(state: BuildState, description: String?, targetUrl: String?) -> StatusType { + fatalError() +// let status = self.syncer.createStatusFromState(state, description: description, targetUrl: targetUrl) + } + func addBaseCommentFromIntegration(integration: Integration) { var integrationText = "Integration \(integration.number)" diff --git a/BuildaKit/SyncPair_PR_Bot.swift b/BuildaKit/SyncPair_PR_Bot.swift index 8f8c7af..d2cbed9 100644 --- a/BuildaKit/SyncPair_PR_Bot.swift +++ b/BuildaKit/SyncPair_PR_Bot.swift @@ -111,7 +111,7 @@ public class SyncPair_PR_Bot: SyncPair { (foundComments, error) -> () in if error != nil { - let e = Error.withInfo("Fetching comments", internalError: error) + let e = Error.withInfo("Fetching comments", internalError: error as? NSError) completion(isEnabled: false, error: e) return } diff --git a/BuildaKit/SyncerGitHubUtils.swift b/BuildaKit/SyncerGitHubUtils.swift index 98c3e30..85970dc 100644 --- a/BuildaKit/SyncerGitHubUtils.swift +++ b/BuildaKit/SyncerGitHubUtils.swift @@ -30,7 +30,7 @@ extension HDGitHubXCBotSyncer { self._sourceServer.getStatusOfCommit(commit, repo: repoName, completion: { (status, error) -> () in if error != nil { - let e = Error.withInfo("Commit \(commit) failed to return status", internalError: error) + let e = Error.withInfo("Commit \(commit) failed to return status", internalError: error as? NSError) completion(error: e) return } @@ -53,7 +53,7 @@ extension HDGitHubXCBotSyncer { self._sourceServer.postStatusOfCommit(commit, status: statusWithComment.status, repo: repo) { (status, error) -> () in if error != nil { - let e = Error.withInfo("Failed to post a status on commit \(commit) of repo \(repo)", internalError: error) + let e = Error.withInfo("Failed to post a status on commit \(commit) of repo \(repo)", internalError: error as? NSError) completion(error: e) return } @@ -70,7 +70,7 @@ extension HDGitHubXCBotSyncer { self._sourceServer.postCommentOnIssue(comment, issueNumber: issue.number, repo: repo, completion: { (comment, error) -> () in if error != nil { - let e = Error.withInfo("Failed to post a comment \"\(comment)\" on Issue \(issue.number) of repo \(repo)", internalError: error) + let e = Error.withInfo("Failed to post a comment \"\(comment)\" on Issue \(issue.number) of repo \(repo)", internalError: error as? NSError) completion(error: e) } else { completion(error: nil) diff --git a/BuildaKit/SyncerLogic.swift b/BuildaKit/SyncerLogic.swift index b74f7a3..dcfd496 100644 --- a/BuildaKit/SyncerLogic.swift +++ b/BuildaKit/SyncerLogic.swift @@ -137,12 +137,12 @@ extension HDGitHubXCBotSyncer { //we have both PRs and Bots, resolve self.syncPRsAndBranchesAndBots(repo: repo, repoName: repoName, prs: prs, branches: branches, bots: bots, completion: { - //everything is done, report the damage of GitHub rate limit - if let rateLimitInfo = self._sourceServer.latestRateLimitInfo { + //everything is done, report the damage of the server's rate limit + if let rateLimitInfo = repo.latestRateLimitInfo { - let report = rateLimitInfo.getReport() - self.reports["GitHub Rate Limit"] = report - Log.info("GitHub Rate Limit: \(report)") + let report = rateLimitInfo.report + self.reports["Rate Limit"] = report + Log.info("Rate Limit: \(report)") } completion() @@ -156,9 +156,13 @@ extension HDGitHubXCBotSyncer { public func syncPRsAndBranchesAndBots(repo repo: RepoType, repoName: String, prs: [PullRequestType], branches: [BranchType], bots: [Bot], completion: () -> ()) { - let prsDescription = prs.map({ " PR \($0.number): \($0.title) [\($0.head.ref) -> \($0.base.ref)]" }).joinWithSeparator("\n") - let branchesDescription = branches.map({ " Branch [\($0.name):\($0.commit.sha)]" }).joinWithSeparator("\n") - let botsDescription = bots.map({ " Bot \($0.name)" }).joinWithSeparator("\n") + let prsDescription = prs.map { (pr: PullRequestType) -> String in + " PR \(pr.number): \(pr.title) [\(pr.headName) -> \(pr.baseName)]" + }.joinWithSeparator("\n") + let branchesDescription = branches.map { (branch: BranchType) -> String in + " Branch [\(branch.name):\(branch.commitSHA)]" } + .joinWithSeparator("\n") + let botsDescription = bots.map { " Bot \($0.name)" }.joinWithSeparator("\n") Log.verbose("Resolving prs:\n\(prsDescription) \nand branches:\n\(branchesDescription)\nand bots:\n\(botsDescription)") //create the changes necessary diff --git a/Buildasaur.xcodeproj/project.pbxproj b/Buildasaur.xcodeproj/project.pbxproj index 70599e9..4c66268 100644 --- a/Buildasaur.xcodeproj/project.pbxproj +++ b/Buildasaur.xcodeproj/project.pbxproj @@ -2122,6 +2122,7 @@ baseConfigurationReference = AFE44BFFCEFA7E33318AFA65 /* Pods-BuildaGitServer.release.xcconfig */; buildSettings = { COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DYLIB_COMPATIBILITY_VERSION = 1; diff --git a/Buildasaur.xcodeproj/xcshareddata/xcschemes/Buildasaur.xcscheme b/Buildasaur.xcodeproj/xcshareddata/xcschemes/Buildasaur.xcscheme index 5b4b3b3..1741dde 100644 --- a/Buildasaur.xcodeproj/xcshareddata/xcschemes/Buildasaur.xcscheme +++ b/Buildasaur.xcodeproj/xcshareddata/xcschemes/Buildasaur.xcscheme @@ -11,7 +11,8 @@ buildForRunning = "YES" buildForProfiling = "YES" buildForArchiving = "YES" - buildForAnalyzing = "YES"> + buildForAnalyzing = "YES" + hideIssues = "NO"> + buildForAnalyzing = "YES" + hideIssues = "NO"> + buildForAnalyzing = "YES" + hideIssues = "NO"> + buildForAnalyzing = "YES" + hideIssues = "NO"> + buildForAnalyzing = "YES" + hideIssues = "NO"> + buildForAnalyzing = "YES" + hideIssues = "NO"> + buildForAnalyzing = "YES" + hideIssues = "NO"> [ShowableBranch] in //map branches to PR numbers - let mappedPRs = prs.dictionarifyWithKey { $0.head.ref } + let mappedPRs = prs.dictionarifyWithKey { $0.headName } return branches.map { let pr = mappedPRs[$0.name]?.number @@ -77,16 +77,16 @@ class BranchWatchingViewController: NSViewController, NSTableViewDelegate, NSTab })) } - func fetchBranchesProducer() -> SignalProducer<[Branch], NSError> { + func fetchBranchesProducer() -> SignalProducer<[BranchType], NSError> { let repoName = self.syncer.project.githubRepoName()! return SignalProducer { [weak self] sink, _ in guard let sself = self else { return } - sself.syncer.github.getBranchesOfRepo(repoName) { (branches, error) -> () in + sself.syncer.sourceServer.getBranchesOfRepo(repoName) { (branches, error) -> () in if let error = error { - sink.sendFailed(error) + sink.sendFailed(error as NSError) } else { sink.sendNext(branches!) sink.sendCompleted() @@ -95,16 +95,16 @@ class BranchWatchingViewController: NSViewController, NSTableViewDelegate, NSTab }.observeOn(UIScheduler()) } - func fetchPRsProducer() -> SignalProducer<[PullRequest], NSError> { + func fetchPRsProducer() -> SignalProducer<[PullRequestType], NSError> { let repoName = self.syncer.project.githubRepoName()! return SignalProducer { [weak self] sink, _ in guard let sself = self else { return } - sself.syncer.github.getOpenPullRequests(repoName) { (prs, error) -> () in + sself.syncer.sourceServer.getOpenPullRequests(repoName) { (prs, error) -> () in if let error = error { - sink.sendFailed(error) + sink.sendFailed(error as NSError) } else { sink.sendNext(prs!) sink.sendCompleted() diff --git a/Buildasaur/ManualBotManagementViewController.swift b/Buildasaur/ManualBotManagementViewController.swift index a0f4e98..c996c1b 100644 --- a/Buildasaur/ManualBotManagementViewController.swift +++ b/Buildasaur/ManualBotManagementViewController.swift @@ -50,11 +50,11 @@ class ManualBotManagementViewController: NSViewController { } } - func fetchBranches(completion: ([Branch]?, NSError?) -> ()) { + func fetchBranches(completion: ([BranchType]?, ErrorType?) -> ()) { self.branchActivityIndicator.startAnimation(nil) let repoName = self.syncer.project.githubRepoName()! - self.syncer.github.getBranchesOfRepo(repoName, completion: { (branches, error) -> () in + self.syncer.sourceServer.getBranchesOfRepo(repoName, completion: { (branches, error) -> () in NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in From a23752167ff5c1b5df171b8e821fc364c4e1b7c5 Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Thu, 21 Jan 2016 18:13:17 +0000 Subject: [PATCH 07/63] starting to work issues out, buildasaur is sort of running, but many corners were cut, so due diligence is in order now --- BuildaGitServer/GitHubServer.swift | 6 +++--- BuildaGitServer/Issue.swift | 2 +- BuildaGitServer/PullRequest.swift | 5 +---- Buildasaur/Info.plist | 4 ++-- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/BuildaGitServer/GitHubServer.swift b/BuildaGitServer/GitHubServer.swift index 67d8fa0..e7d14b0 100644 --- a/BuildaGitServer/GitHubServer.swift +++ b/BuildaGitServer/GitHubServer.swift @@ -30,14 +30,14 @@ extension GitHubServer: SourceServerType { func getBranchesOfRepo(repo: String, completion: (branches: [BranchType]?, error: ErrorType?) -> ()) { self._getBranchesOfRepo(repo) { (branches, error) -> () in - completion(branches: branches, error: error) + completion(branches: branches?.map { $0 as BranchType }, error: error) } } func getOpenPullRequests(repo: String, completion: (prs: [PullRequestType]?, error: ErrorType?) -> ()) { self._getOpenPullRequests(repo) { (prs, error) -> () in - completion(prs: prs, error: error) + completion(prs: prs?.map { $0 as PullRequestType }, error: error) } } @@ -79,7 +79,7 @@ extension GitHubServer: SourceServerType { func getCommentsOfIssue(issueNumber: Int, repo: String, completion: (comments: [CommentType]?, error: ErrorType?) -> ()) { self._getCommentsOfIssue(issueNumber, repo: repo) { (comments, error) -> () in - completion(comments: comments, error: error) + completion(comments: comments?.map { $0 as CommentType }, error: error) } } diff --git a/BuildaGitServer/Issue.swift b/BuildaGitServer/Issue.swift index 2713f0a..d4fdce0 100644 --- a/BuildaGitServer/Issue.swift +++ b/BuildaGitServer/Issue.swift @@ -12,7 +12,7 @@ class Issue : GitHubEntity { let number: Int let body: String - let title: String + var title: String required init(json: NSDictionary) { diff --git a/BuildaGitServer/PullRequest.swift b/BuildaGitServer/PullRequest.swift index f71501c..b1224c4 100644 --- a/BuildaGitServer/PullRequest.swift +++ b/BuildaGitServer/PullRequest.swift @@ -8,7 +8,7 @@ import Foundation -class PullRequest : Issue { +class PullRequest : Issue, PullRequestType { let head: PullRequestBranch let base: PullRequestBranch @@ -20,9 +20,6 @@ class PullRequest : Issue { super.init(json: json) } -} - -extension PullRequest: PullRequestType { var headName: String { return self.head.ref diff --git a/Buildasaur/Info.plist b/Buildasaur/Info.plist index 37a5b7e..c7ff5c6 100644 --- a/Buildasaur/Info.plist +++ b/Buildasaur/Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.6.5 + 0.8.0 CFBundleSignature ???? CFBundleVersion - 30 + 33 LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) LSUIElement From 22d3713ccc5fc411421536f3dd2ec4eeec0df62a Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Thu, 21 Jan 2016 18:38:19 +0000 Subject: [PATCH 08/63] protocols ftw! --- BuildaGitServer/BaseTypes.swift | 10 ++++------ BuildaGitServer/GitHubServer.swift | 3 ++- BuildaKit/SummaryBuilder.swift | 6 ++++-- BuildaKit/SyncPairResolver.swift | 16 +++++++++------- BuildaKit/SyncPair_Branch_Bot.swift | 2 +- BuildaKit/SyncPair_PR_Bot.swift | 2 +- BuildaKit/SyncerGitHubUtils.swift | 12 ++++++------ 7 files changed, 27 insertions(+), 24 deletions(-) diff --git a/BuildaGitServer/BaseTypes.swift b/BuildaGitServer/BaseTypes.swift index 7c3fafa..8f85248 100644 --- a/BuildaGitServer/BaseTypes.swift +++ b/BuildaGitServer/BaseTypes.swift @@ -8,11 +8,11 @@ import Foundation -//TODO: migrate all of buildasaur to handle the protocol types below so -//that we can start hiding away the Git server details and support not just -//GitHub but more, like Bitbucket. +public protocol BuildStatusCreator { + func createStatusFromState(state: BuildState, description: String?, targetUrl: String?) -> StatusType +} -public protocol SourceServerType { +public protocol SourceServerType: BuildStatusCreator { func getBranchesOfRepo(repo: String, completion: (branches: [BranchType]?, error: ErrorType?) -> ()) func getOpenPullRequests(repo: String, completion: (prs: [PullRequestType]?, error: ErrorType?) -> ()) @@ -22,8 +22,6 @@ public protocol SourceServerType { func postStatusOfCommit(commit: String, status: StatusType, repo: String, completion: (status: StatusType?, error: ErrorType?) -> ()) func postCommentOnIssue(comment: String, issueNumber: Int, repo: String, completion: (comment: CommentType?, error: ErrorType?) -> ()) func getCommentsOfIssue(issueNumber: Int, repo: String, completion: (comments: [CommentType]?, error: ErrorType?) -> ()) - - func createStatusFromState(state: BuildState, description: String?, targetUrl: String?, context: String?) -> StatusType } public enum SourceServerOption { diff --git a/BuildaGitServer/GitHubServer.swift b/BuildaGitServer/GitHubServer.swift index e7d14b0..9dd2ffc 100644 --- a/BuildaGitServer/GitHubServer.swift +++ b/BuildaGitServer/GitHubServer.swift @@ -83,9 +83,10 @@ extension GitHubServer: SourceServerType { } } - func createStatusFromState(buildState: BuildState, description: String?, targetUrl: String?, context: String?) -> StatusType { + func createStatusFromState(buildState: BuildState, description: String?, targetUrl: String?) -> StatusType { let state = GitHubStatus.GitHubState.fromBuildState(buildState) + let context = "Buildasaur" return GitHubStatus(state: state, description: description, targetUrl: targetUrl, context: context) } } diff --git a/BuildaKit/SummaryBuilder.swift b/BuildaKit/SummaryBuilder.swift index 3d9c52f..a65051f 100644 --- a/BuildaKit/SummaryBuilder.swift +++ b/BuildaKit/SummaryBuilder.swift @@ -13,6 +13,7 @@ import BuildaGitServer class SummaryBuilder { + var statusCreator: BuildStatusCreator! var lines: [String] = [] let resultString: String var linkBuilder: (Integration) -> String? = { _ in nil } @@ -89,8 +90,9 @@ class SummaryBuilder { //MARK: utils private func createStatus(state: BuildState, description: String?, targetUrl: String?) -> StatusType { - fatalError() -// let status = self.syncer.createStatusFromState(state, description: description, targetUrl: targetUrl) + + let status = self.statusCreator.createStatusFromState(state, description: description, targetUrl: targetUrl) + return status } func addBaseCommentFromIntegration(integration: Integration) { diff --git a/BuildaKit/SyncPairResolver.swift b/BuildaKit/SyncPairResolver.swift index a9e4e5e..eda1bb6 100644 --- a/BuildaKit/SyncPairResolver.swift +++ b/BuildaKit/SyncPairResolver.swift @@ -22,7 +22,7 @@ public class SyncPairResolver { issue: IssueType?, bot: Bot, hostname: String, - syncer: HDGitHubXCBotSyncer, + buildCreator: BuildStatusCreator, integrations: [Integration]) -> SyncPair.Actions { var integrationsToCancel: [Integration] = [] @@ -127,7 +127,7 @@ public class SyncPairResolver { pending: latestPendingIntegration, running: runningIntegration, link: link, - syncer: syncer, + statusCreator: buildCreator, completed: completedIntegrations) //merge in nested actions @@ -212,7 +212,7 @@ public class SyncPairResolver { pending: Integration?, running: Integration?, link: (Integration) -> String?, - syncer: HDGitHubXCBotSyncer, + statusCreator: BuildStatusCreator, completed: Set) -> SyncPair.Actions { let statusWithComment: StatusAndComment @@ -223,7 +223,7 @@ public class SyncPairResolver { //TODO: show how many builds are ahead in the queue and estimate when it will be //started and when finished? (there is an average running time on each bot, it should be easy) - let status = syncer.createStatusFromState(.Pending, description: "Build waiting in the queue...", targetUrl: link(pending)) + let status = statusCreator.createStatusFromState(.Pending, description: "Build waiting in the queue...", targetUrl: link(pending)) statusWithComment = StatusAndComment(status: status, comment: nil) //also, cancel the running integration, if it's there any @@ -238,7 +238,7 @@ public class SyncPairResolver { //there is a running integration. //TODO: estimate, based on the average running time of this bot and on the started timestamp, when it will finish. add that to the description. let currentStepString = running.currentStep.rawValue - let status = syncer.createStatusFromState(.Pending, description: "Integration step: \(currentStepString)...", targetUrl: link(running)) + let status = statusCreator.createStatusFromState(.Pending, description: "Integration step: \(currentStepString)...", targetUrl: link(running)) statusWithComment = StatusAndComment(status: status, comment: nil) } else { @@ -247,12 +247,12 @@ public class SyncPairResolver { if completed.count > 0 { //we have some completed integrations - statusWithComment = self.resolveStatusFromCompletedIntegrations(completed, link: link) + statusWithComment = self.resolveStatusFromCompletedIntegrations(completed, statusCreator: statusCreator, link: link) } else { //this shouldn't happen. Log.error("LOGIC ERROR! This shouldn't happen, there are no completed integrations!") - let status = syncer.createStatusFromState(.Error, description: "* UNKNOWN STATE, Builda ERROR *", targetUrl: nil) + let status = statusCreator.createStatusFromState(.Error, description: "* UNKNOWN STATE, Builda ERROR *", targetUrl: nil) statusWithComment = StatusAndComment(status: status, comment: "Builda error, unknown state!") } } @@ -267,6 +267,7 @@ public class SyncPairResolver { func resolveStatusFromCompletedIntegrations( integrations: Set, + statusCreator: BuildStatusCreator, link: (Integration) -> String? ) -> StatusAndComment { @@ -274,6 +275,7 @@ public class SyncPairResolver { let sortedDesc = Array(integrations).sort { $0.number > $1.number } let summary = SummaryBuilder() summary.linkBuilder = link + summary.statusCreator = statusCreator //if there are any succeeded, it wins - iterating from the end if let passingIntegration = sortedDesc.filter({ diff --git a/BuildaKit/SyncPair_Branch_Bot.swift b/BuildaKit/SyncPair_Branch_Bot.swift index 0fdc1f5..c29551e 100644 --- a/BuildaKit/SyncPair_Branch_Bot.swift +++ b/BuildaKit/SyncPair_Branch_Bot.swift @@ -61,7 +61,7 @@ public class SyncPair_Branch_Bot: SyncPair { issue: issue, bot: bot, hostname: hostname!, - syncer: self.syncer, + buildCreator: self.syncer, integrations: integrations) //in case of branches, we also (optionally) want to add functionality for creating an issue if the branch starts failing and updating with comments the same way we do with PRs. diff --git a/BuildaKit/SyncPair_PR_Bot.swift b/BuildaKit/SyncPair_PR_Bot.swift index d2cbed9..635ff9b 100644 --- a/BuildaKit/SyncPair_PR_Bot.swift +++ b/BuildaKit/SyncPair_PR_Bot.swift @@ -73,7 +73,7 @@ public class SyncPair_PR_Bot: SyncPair { issue: issue, bot: bot, hostname: hostname!, - syncer: self.syncer, + buildCreator: self.syncer, integrations: integrations) self.performActions(actions, completion: completion) } diff --git a/BuildaKit/SyncerGitHubUtils.swift b/BuildaKit/SyncerGitHubUtils.swift index 85970dc..aaeacd7 100644 --- a/BuildaKit/SyncerGitHubUtils.swift +++ b/BuildaKit/SyncerGitHubUtils.swift @@ -10,15 +10,15 @@ import Foundation import BuildaGitServer import BuildaUtils -extension HDGitHubXCBotSyncer { +extension HDGitHubXCBotSyncer: BuildStatusCreator { - func createStatusFromState(state: BuildState, description: String?, targetUrl: String?) -> StatusType { - - //TODO: potentially have multiple contexts to show multiple stats on the PR - let context = "Buildasaur" + public func createStatusFromState(state: BuildState, description: String?, targetUrl: String?) -> StatusType { - return self._sourceServer.createStatusFromState(state, description: description, targetUrl: targetUrl, context: context) + return self._sourceServer.createStatusFromState(state, description: description, targetUrl: targetUrl) } +} + +extension HDGitHubXCBotSyncer { func updateCommitStatusIfNecessary( newStatus: StatusAndComment, From 070e35d77341445e285477dee8bb507fcc4a565c Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Thu, 21 Jan 2016 18:55:26 +0000 Subject: [PATCH 09/63] fixed a beautiful infinite recursion. anyway, buildasaur passed the first test, seems to be pretty much working again. now i need to fix tests. --- BuildaGitServer/Repo.swift | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/BuildaGitServer/Repo.swift b/BuildaGitServer/Repo.swift index 291d37c..4215d09 100644 --- a/BuildaGitServer/Repo.swift +++ b/BuildaGitServer/Repo.swift @@ -16,6 +16,8 @@ class Repo : GitHubEntity { let repoUrlSSH: String let permissionsDict: NSDictionary + var latestRateLimitInfo: RateLimitType? + required init(json: NSDictionary) { self.name = json.stringForKey("name") @@ -46,9 +48,4 @@ extension Repo: RepoType { return self.repoUrlSSH } - - var latestRateLimitInfo: RateLimitType? { - - return self.latestRateLimitInfo - } } From 717e7ed39631790bac3427dcad5b2d4713a2198b Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Thu, 21 Jan 2016 19:22:26 +0000 Subject: [PATCH 10/63] renaming summary builder tests to what they really are, github specific summaries. most likely we'll use them everywhere, but it's better to be explicit about this dependency early. --- ....swift => GitHubSummaryBuilderTests.swift} | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) rename BuildaKitTests/{SummaryBuilderTests.swift => GitHubSummaryBuilderTests.swift} (93%) diff --git a/BuildaKitTests/SummaryBuilderTests.swift b/BuildaKitTests/GitHubSummaryBuilderTests.swift similarity index 93% rename from BuildaKitTests/SummaryBuilderTests.swift rename to BuildaKitTests/GitHubSummaryBuilderTests.swift index 12e6336..96eff62 100644 --- a/BuildaKitTests/SummaryBuilderTests.swift +++ b/BuildaKitTests/GitHubSummaryBuilderTests.swift @@ -12,7 +12,7 @@ import BuildaGitServer @testable import BuildaKit import Nimble -class SummaryBuilderTests: XCTestCase { +class GitHubSummaryBuilderTests: XCTestCase { //MARK: utils @@ -38,7 +38,7 @@ class SummaryBuilderTests: XCTestCase { let exp_comment = "Result of Integration 15\n---\n*Duration*: 28 seconds\n*Result*: **Perfect build!** :+1:" let exp_status = "Build passed!" - let exp_state = Status.State.Success + let exp_state = BuildState.Success expect(result.comment) == exp_comment expect(result.status.description) == exp_status expect(result.status.state) == exp_state @@ -55,7 +55,7 @@ class SummaryBuilderTests: XCTestCase { let exp_comment = "Result of [Integration 15](https://link/to/d3884f0ab7df9c699bc81405f4045ec6)\n---\n*Duration*: 28 seconds\n*Result*: **Perfect build!** :+1:" let exp_status = "Build passed!" - let exp_state = Status.State.Success + let exp_state = BuildState.Success let exp_link = "https://link/to/d3884f0ab7df9c699bc81405f4045ec6" expect(result.comment) == exp_comment expect(result.status.description) == exp_status @@ -72,7 +72,7 @@ class SummaryBuilderTests: XCTestCase { let exp_comment = "Result of Integration 15\n---\n*Duration*: 28 seconds\n*Result*: **Perfect build!** :+1:\n*Test Coverage*: 12%" let exp_status = "Build passed!" - let exp_state = Status.State.Success + let exp_state = BuildState.Success expect(result.comment) == exp_comment expect(result.status.description) == exp_status expect(result.status.state) == exp_state @@ -88,7 +88,7 @@ class SummaryBuilderTests: XCTestCase { let exp_comment = "Result of Integration 15\n---\n*Duration*: 28 seconds\n*Result*: **Perfect build!** All 99 tests passed. :+1:\n*Test Coverage*: 12%" let exp_status = "Build passed!" - let exp_state = Status.State.Success + let exp_state = BuildState.Success expect(result.comment) == exp_comment expect(result.status.description) == exp_status expect(result.status.state) == exp_state @@ -103,7 +103,7 @@ class SummaryBuilderTests: XCTestCase { let exp_comment = "Result of Integration 15\n---\n*Duration*: 28 seconds\n*Result*: All 99 tests passed, but please **fix 2 warnings**.\n*Test Coverage*: 12%" let exp_status = "Build passed!" - let exp_state = Status.State.Success + let exp_state = BuildState.Success expect(result.comment) == exp_comment expect(result.status.description) == exp_status expect(result.status.state) == exp_state @@ -118,7 +118,7 @@ class SummaryBuilderTests: XCTestCase { let exp_comment = "Result of Integration 15\n---\n*Duration*: 28 seconds\n*Result*: All 99 tests passed, but please **fix 3 analyzer warnings**.\n*Test Coverage*: 12%" let exp_status = "Build passed!" - let exp_state = Status.State.Success + let exp_state = BuildState.Success expect(result.comment) == exp_comment expect(result.status.description) == exp_status expect(result.status.state) == exp_state @@ -134,7 +134,7 @@ class SummaryBuilderTests: XCTestCase { let exp_comment = "Result of Integration 15\n---\n*Duration*: 28 seconds\n*Result*: **Build failed 1 test** out of 99" let exp_status = "Build failed tests!" - let exp_state = Status.State.Failure + let exp_state = BuildState.Failure expect(result.comment) == exp_comment expect(result.status.description) == exp_status expect(result.status.state) == exp_state @@ -149,7 +149,7 @@ class SummaryBuilderTests: XCTestCase { let exp_comment = "Result of Integration 15\n---\n*Duration*: 28 seconds\n*Result*: **4 errors, failing state: build-errors**" let exp_status = "Build error!" - let exp_state = Status.State.Error + let exp_state = BuildState.Error expect(result.comment) == exp_comment expect(result.status.description) == exp_status expect(result.status.state) == exp_state @@ -160,11 +160,12 @@ class SummaryBuilderTests: XCTestCase { let buildResultSummary = MockBuildResultSummary() let integration = self.integration(.Canceled, buildResultSummary: buildResultSummary) let summary = SummaryBuilder() + summary.statusCreator = MockBuildStatusCreator() let result = summary.buildCanceledIntegration(integration) let exp_comment = "Result of Integration 15\n---\n*Duration*: 28 seconds\nBuild was **manually canceled**." let exp_status = "Build canceled!" - let exp_state = Status.State.Error + let exp_state = BuildState.Error expect(result.comment) == exp_comment expect(result.status.description) == exp_status expect(result.status.state) == exp_state @@ -175,7 +176,7 @@ class SummaryBuilderTests: XCTestCase { let summary = SummaryBuilder() let result = summary.buildEmptyIntegration() - let exp_state = Status.State.NoState + let exp_state = BuildState.NoState expect(result.comment).to(beNil()) expect(result.status.description).to(beNil()) expect(result.status.state) == exp_state From cc747dc7c2cc855cb79b0f4635ebdc2384b0fbf3 Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Thu, 21 Jan 2016 19:22:49 +0000 Subject: [PATCH 11/63] tests passing again! --- BuildaGitServer/BaseTypes.swift | 1 + BuildaGitServerTests/GitHubServerTests.swift | 343 +++++++++--------- BuildaKit/SyncPairResolver.swift | 4 +- BuildaKit/SyncPair_Branch_Bot.swift | 2 +- BuildaKit/SyncPair_PR_Bot.swift | 2 +- .../GitHubSummaryBuilderTests.swift | 11 +- BuildaKitTests/Mocks.swift | 10 +- BuildaKitTests/SyncPair_PR_Bot_Tests.swift | 69 ++-- BuildaKitTests/SyncerTests.swift | 6 +- Buildasaur.xcodeproj/project.pbxproj | 8 +- 10 files changed, 237 insertions(+), 219 deletions(-) diff --git a/BuildaGitServer/BaseTypes.swift b/BuildaGitServer/BaseTypes.swift index 8f85248..437b872 100644 --- a/BuildaGitServer/BaseTypes.swift +++ b/BuildaGitServer/BaseTypes.swift @@ -123,6 +123,7 @@ public protocol StatusType { var state: BuildState { get } var description: String? { get } + var targetUrl: String? { get } //TODO: add required properties } diff --git a/BuildaGitServerTests/GitHubServerTests.swift b/BuildaGitServerTests/GitHubServerTests.swift index 4e25796..a95c654 100644 --- a/BuildaGitServerTests/GitHubServerTests.swift +++ b/BuildaGitServerTests/GitHubServerTests.swift @@ -8,182 +8,181 @@ import Cocoa import XCTest -import BuildaGitServer +@testable import BuildaGitServer import BuildaUtils class GitHubSourceTests: XCTestCase { - //TODO: fix tests -// var github: GitHubServer! -// -// override func setUp() { -// super.setUp() -// -// self.github = GitHubFactory.server(nil) -// } -// -// override func tearDown() { -// -// self.github = nil -// -// super.tearDown() -// } -// -// func tryEndpoint(method: HTTP.Method, endpoint: GitHubEndpoints.Endpoint, params: [String: String]?, completion: (body: AnyObject!, error: NSError!) -> ()) { -// -// let expect = expectationWithDescription("Waiting for url request") -// -// let request = try! self.github.endpoints.createRequest(method, endpoint: endpoint, params: params) -// -// self.github.http.sendRequest(request, completion: { (response, body, error) -> () in -// -// completion(body: body, error: error) -// expect.fulfill() -// }) -// -// waitForExpectationsWithTimeout(10, handler: nil) -// } -// -// func testGetPullRequests() { -// -// let params = [ -// "repo": "czechboy0/Buildasaur-Tester" -// ] -// -// self.tryEndpoint(.GET, endpoint: .PullRequests, params: params) { (body, error) -> () in -// -// XCTAssertNotNil(body, "Body must be non-nil") -// if let body = body as? NSArray { -// let prs: [PullRequest] = GitHubArray(body) -// XCTAssertGreaterThan(prs.count, 0, "We need > 0 items to test parsing") -// Log.verbose("Parsed PRs: \(prs)") -// } else { -// XCTFail("Body nil") -// } -// } -// } -// -// func testGetBranches() { -// -// let params = [ -// "repo": "czechboy0/Buildasaur-Tester" -// ] -// -// self.tryEndpoint(.GET, endpoint: .Branches, params: params) { (body, error) -> () in -// -// XCTAssertNotNil(body, "Body must be non-nil") -// if let body = body as? NSArray { -// let branches: [Branch] = GitHubArray(body) -// XCTAssertGreaterThan(branches.count, 0, "We need > 0 items to test parsing") -// Log.verbose("Parsed branches: \(branches)") -// } else { -// XCTFail("Body nil") -// } -// } -// } -// -// //manual parsing tested here, sort of a documentation as well -// -// func testUserParsing() { -// -// let dictionary = [ -// "login": "czechboy0", -// "name": "Honza Dvorsky", -// "avatar_url": "https://avatars.githubusercontent.com/u/2182121?v=3", -// "html_url": "https://github.com/czechboy0" -// ] -// -// let user = User(json: dictionary) -// XCTAssertEqual(user.userName, "czechboy0") -// XCTAssertEqual(user.realName!, "Honza Dvorsky") -// XCTAssertEqual(user.avatarUrl!, "https://avatars.githubusercontent.com/u/2182121?v=3") -// XCTAssertEqual(user.htmlUrl!, "https://github.com/czechboy0") -// } -// -// func testRepoParsing() { -// -// let dictionary = [ -// "name": "Buildasaur", -// "full_name": "czechboy0/Buildasaur", -// "clone_url": "https://github.com/czechboy0/Buildasaur.git", -// "ssh_url": "git@github.com:czechboy0/Buildasaur.git", -// "html_url": "https://github.com/czechboy0/Buildasaur" -// ] -// -// let repo = Repo(json: dictionary) -// XCTAssertEqual(repo.name, "Buildasaur") -// XCTAssertEqual(repo.fullName, "czechboy0/Buildasaur") -// XCTAssertEqual(repo.repoUrlHTTPS, "https://github.com/czechboy0/Buildasaur.git") -// XCTAssertEqual(repo.repoUrlSSH, "git@github.com:czechboy0/Buildasaur.git") -// XCTAssertEqual(repo.htmlUrl!, "https://github.com/czechboy0/Buildasaur") -// } -// -// func testCommitParsing() { -// -// let dictionary: NSDictionary = [ -// "sha": "08182438ed2ef3b34bd97db85f39deb60e2dcd7d", -// "url": "https://api.github.com/repos/czechboy0/Buildasaur/commits/08182438ed2ef3b34bd97db85f39deb60e2dcd7d" -// ] -// -// let commit = Commit(json: dictionary) -// XCTAssertEqual(commit.sha, "08182438ed2ef3b34bd97db85f39deb60e2dcd7d") -// XCTAssertEqual(commit.url!, "https://api.github.com/repos/czechboy0/Buildasaur/commits/08182438ed2ef3b34bd97db85f39deb60e2dcd7d") -// } -// -// func testBranchParsing() { -// -// let commitDictionary = [ -// "sha": "08182438ed2ef3b34bd97db85f39deb60e2dcd7d", -// "url": "https://api.github.com/repos/czechboy0/Buildasaur/commits/08182438ed2ef3b34bd97db85f39deb60e2dcd7d" -// ] -// let dictionary = [ -// "name": "master", -// "commit": commitDictionary -// ] -// -// let branch = Branch(json: dictionary) -// XCTAssertEqual(branch.name, "master") -// XCTAssertEqual(branch.commit.sha, "08182438ed2ef3b34bd97db85f39deb60e2dcd7d") -// XCTAssertEqual(branch.commit.url!, "https://api.github.com/repos/czechboy0/Buildasaur/commits/08182438ed2ef3b34bd97db85f39deb60e2dcd7d") -// } -// -// func testPullRequestBranchParsing() { -// -// let dictionary = [ -// "ref": "fb-loadNode", -// "sha": "7e45fa772565969ee801b0bdce0f560122e34610", -// "user": [ -// "login": "aleclarson", -// "avatar_url": "https://avatars.githubusercontent.com/u/1925840?v=3", -// "url": "https://api.github.com/users/aleclarson", -// "html_url": "https://github.com/aleclarson", -// ], -// "repo": [ -// "name": "AsyncDisplayKit", -// "full_name": "aleclarson/AsyncDisplayKit", -// "owner": [ -// "login": "aleclarson", -// "avatar_url": "https://avatars.githubusercontent.com/u/1925840?v=3", -// "url": "https://api.github.com/users/aleclarson", -// "html_url": "https://github.com/aleclarson", -// ], -// "html_url": "https://github.com/aleclarson/AsyncDisplayKit", -// "description": "Smooth asynchronous user interfaces for iOS apps.", -// "url": "https://api.github.com/repos/aleclarson/AsyncDisplayKit", -// "ssh_url": "git@github.com:aleclarson/AsyncDisplayKit.git", -// "clone_url": "https://github.com/aleclarson/AsyncDisplayKit.git", -// ] -// ] -// -// let prbranch = PullRequestBranch(json: dictionary) -// XCTAssertEqual(prbranch.ref, "fb-loadNode") -// XCTAssertEqual(prbranch.sha, "7e45fa772565969ee801b0bdce0f560122e34610") -// XCTAssertEqual(prbranch.repo.name, "AsyncDisplayKit") -// } -// -// func testResponseCaching() { -// //TODO -// } + var github: GitHubServer! + + override func setUp() { + super.setUp() + + self.github = GitHubFactory.server(nil) + } + + override func tearDown() { + + self.github = nil + + super.tearDown() + } + + func tryEndpoint(method: HTTP.Method, endpoint: GitHubEndpoints.Endpoint, params: [String: String]?, completion: (body: AnyObject!, error: NSError!) -> ()) { + + let expect = expectationWithDescription("Waiting for url request") + + let request = try! self.github.endpoints.createRequest(method, endpoint: endpoint, params: params) + + self.github.http.sendRequest(request, completion: { (response, body, error) -> () in + + completion(body: body, error: error) + expect.fulfill() + }) + + waitForExpectationsWithTimeout(10, handler: nil) + } + + func testGetPullRequests() { + + let params = [ + "repo": "czechboy0/Buildasaur-Tester" + ] + + self.tryEndpoint(.GET, endpoint: .PullRequests, params: params) { (body, error) -> () in + + XCTAssertNotNil(body, "Body must be non-nil") + if let body = body as? NSArray { + let prs: [PullRequest] = GitHubArray(body) + XCTAssertGreaterThan(prs.count, 0, "We need > 0 items to test parsing") + Log.verbose("Parsed PRs: \(prs)") + } else { + XCTFail("Body nil") + } + } + } + + func testGetBranches() { + + let params = [ + "repo": "czechboy0/Buildasaur-Tester" + ] + + self.tryEndpoint(.GET, endpoint: .Branches, params: params) { (body, error) -> () in + + XCTAssertNotNil(body, "Body must be non-nil") + if let body = body as? NSArray { + let branches: [Branch] = GitHubArray(body) + XCTAssertGreaterThan(branches.count, 0, "We need > 0 items to test parsing") + Log.verbose("Parsed branches: \(branches)") + } else { + XCTFail("Body nil") + } + } + } + + //manual parsing tested here, sort of a documentation as well + + func testUserParsing() { + + let dictionary = [ + "login": "czechboy0", + "name": "Honza Dvorsky", + "avatar_url": "https://avatars.githubusercontent.com/u/2182121?v=3", + "html_url": "https://github.com/czechboy0" + ] + + let user = User(json: dictionary) + XCTAssertEqual(user.userName, "czechboy0") + XCTAssertEqual(user.realName!, "Honza Dvorsky") + XCTAssertEqual(user.avatarUrl!, "https://avatars.githubusercontent.com/u/2182121?v=3") + XCTAssertEqual(user.htmlUrl!, "https://github.com/czechboy0") + } + + func testRepoParsing() { + + let dictionary = [ + "name": "Buildasaur", + "full_name": "czechboy0/Buildasaur", + "clone_url": "https://github.com/czechboy0/Buildasaur.git", + "ssh_url": "git@github.com:czechboy0/Buildasaur.git", + "html_url": "https://github.com/czechboy0/Buildasaur" + ] + + let repo = Repo(json: dictionary) + XCTAssertEqual(repo.name, "Buildasaur") + XCTAssertEqual(repo.fullName, "czechboy0/Buildasaur") + XCTAssertEqual(repo.repoUrlHTTPS, "https://github.com/czechboy0/Buildasaur.git") + XCTAssertEqual(repo.repoUrlSSH, "git@github.com:czechboy0/Buildasaur.git") + XCTAssertEqual(repo.htmlUrl!, "https://github.com/czechboy0/Buildasaur") + } + + func testCommitParsing() { + + let dictionary: NSDictionary = [ + "sha": "08182438ed2ef3b34bd97db85f39deb60e2dcd7d", + "url": "https://api.github.com/repos/czechboy0/Buildasaur/commits/08182438ed2ef3b34bd97db85f39deb60e2dcd7d" + ] + + let commit = Commit(json: dictionary) + XCTAssertEqual(commit.sha, "08182438ed2ef3b34bd97db85f39deb60e2dcd7d") + XCTAssertEqual(commit.url!, "https://api.github.com/repos/czechboy0/Buildasaur/commits/08182438ed2ef3b34bd97db85f39deb60e2dcd7d") + } + + func testBranchParsing() { + + let commitDictionary = [ + "sha": "08182438ed2ef3b34bd97db85f39deb60e2dcd7d", + "url": "https://api.github.com/repos/czechboy0/Buildasaur/commits/08182438ed2ef3b34bd97db85f39deb60e2dcd7d" + ] + let dictionary = [ + "name": "master", + "commit": commitDictionary + ] + + let branch = Branch(json: dictionary) + XCTAssertEqual(branch.name, "master") + XCTAssertEqual(branch.commit.sha, "08182438ed2ef3b34bd97db85f39deb60e2dcd7d") + XCTAssertEqual(branch.commit.url!, "https://api.github.com/repos/czechboy0/Buildasaur/commits/08182438ed2ef3b34bd97db85f39deb60e2dcd7d") + } + + func testPullRequestBranchParsing() { + + let dictionary = [ + "ref": "fb-loadNode", + "sha": "7e45fa772565969ee801b0bdce0f560122e34610", + "user": [ + "login": "aleclarson", + "avatar_url": "https://avatars.githubusercontent.com/u/1925840?v=3", + "url": "https://api.github.com/users/aleclarson", + "html_url": "https://github.com/aleclarson", + ], + "repo": [ + "name": "AsyncDisplayKit", + "full_name": "aleclarson/AsyncDisplayKit", + "owner": [ + "login": "aleclarson", + "avatar_url": "https://avatars.githubusercontent.com/u/1925840?v=3", + "url": "https://api.github.com/users/aleclarson", + "html_url": "https://github.com/aleclarson", + ], + "html_url": "https://github.com/aleclarson/AsyncDisplayKit", + "description": "Smooth asynchronous user interfaces for iOS apps.", + "url": "https://api.github.com/repos/aleclarson/AsyncDisplayKit", + "ssh_url": "git@github.com:aleclarson/AsyncDisplayKit.git", + "clone_url": "https://github.com/aleclarson/AsyncDisplayKit.git", + ] + ] + + let prbranch = PullRequestBranch(json: dictionary) + XCTAssertEqual(prbranch.ref, "fb-loadNode") + XCTAssertEqual(prbranch.sha, "7e45fa772565969ee801b0bdce0f560122e34610") + XCTAssertEqual(prbranch.repo.name, "AsyncDisplayKit") + } + + func testResponseCaching() { + //TODO + } } diff --git a/BuildaKit/SyncPairResolver.swift b/BuildaKit/SyncPairResolver.swift index eda1bb6..8769b88 100644 --- a/BuildaKit/SyncPairResolver.swift +++ b/BuildaKit/SyncPairResolver.swift @@ -22,7 +22,7 @@ public class SyncPairResolver { issue: IssueType?, bot: Bot, hostname: String, - buildCreator: BuildStatusCreator, + buildStatusCreator: BuildStatusCreator, integrations: [Integration]) -> SyncPair.Actions { var integrationsToCancel: [Integration] = [] @@ -127,7 +127,7 @@ public class SyncPairResolver { pending: latestPendingIntegration, running: runningIntegration, link: link, - statusCreator: buildCreator, + statusCreator: buildStatusCreator, completed: completedIntegrations) //merge in nested actions diff --git a/BuildaKit/SyncPair_Branch_Bot.swift b/BuildaKit/SyncPair_Branch_Bot.swift index c29551e..c2f26f9 100644 --- a/BuildaKit/SyncPair_Branch_Bot.swift +++ b/BuildaKit/SyncPair_Branch_Bot.swift @@ -61,7 +61,7 @@ public class SyncPair_Branch_Bot: SyncPair { issue: issue, bot: bot, hostname: hostname!, - buildCreator: self.syncer, + buildStatusCreator: self.syncer, integrations: integrations) //in case of branches, we also (optionally) want to add functionality for creating an issue if the branch starts failing and updating with comments the same way we do with PRs. diff --git a/BuildaKit/SyncPair_PR_Bot.swift b/BuildaKit/SyncPair_PR_Bot.swift index 635ff9b..aa2dcdb 100644 --- a/BuildaKit/SyncPair_PR_Bot.swift +++ b/BuildaKit/SyncPair_PR_Bot.swift @@ -73,7 +73,7 @@ public class SyncPair_PR_Bot: SyncPair { issue: issue, bot: bot, hostname: hostname!, - buildCreator: self.syncer, + buildStatusCreator: self.syncer, integrations: integrations) self.performActions(actions, completion: completion) } diff --git a/BuildaKitTests/GitHubSummaryBuilderTests.swift b/BuildaKitTests/GitHubSummaryBuilderTests.swift index 96eff62..1c7d96f 100644 --- a/BuildaKitTests/GitHubSummaryBuilderTests.swift +++ b/BuildaKitTests/GitHubSummaryBuilderTests.swift @@ -34,6 +34,7 @@ class GitHubSummaryBuilderTests: XCTestCase { let buildResultSummary = MockBuildResultSummary() let integration = self.integration(.Succeeded, buildResultSummary: buildResultSummary) let summary = SummaryBuilder() + summary.statusCreator = MockGitHubServer() let result = summary.buildPassing(integration) let exp_comment = "Result of Integration 15\n---\n*Duration*: 28 seconds\n*Result*: **Perfect build!** :+1:" @@ -50,6 +51,7 @@ class GitHubSummaryBuilderTests: XCTestCase { let buildResultSummary = MockBuildResultSummary() let integration = self.integration(.Succeeded, buildResultSummary: buildResultSummary) let summary = SummaryBuilder() + summary.statusCreator = MockGitHubServer() summary.linkBuilder = self.linkBuilder() let result = summary.buildPassing(integration) @@ -68,6 +70,7 @@ class GitHubSummaryBuilderTests: XCTestCase { let buildResultSummary = MockBuildResultSummary(codeCoveragePercentage: 12) let integration = self.integration(.Succeeded, buildResultSummary: buildResultSummary) let summary = SummaryBuilder() + summary.statusCreator = MockGitHubServer() let result = summary.buildPassing(integration) let exp_comment = "Result of Integration 15\n---\n*Duration*: 28 seconds\n*Result*: **Perfect build!** :+1:\n*Test Coverage*: 12%" @@ -84,6 +87,7 @@ class GitHubSummaryBuilderTests: XCTestCase { let buildResultSummary = MockBuildResultSummary(testsCount: 99, codeCoveragePercentage: 12) let integration = self.integration(.Succeeded, buildResultSummary: buildResultSummary) let summary = SummaryBuilder() + summary.statusCreator = MockGitHubServer() let result = summary.buildPassing(integration) let exp_comment = "Result of Integration 15\n---\n*Duration*: 28 seconds\n*Result*: **Perfect build!** All 99 tests passed. :+1:\n*Test Coverage*: 12%" @@ -99,6 +103,7 @@ class GitHubSummaryBuilderTests: XCTestCase { let buildResultSummary = MockBuildResultSummary(testsCount: 99, warningCount: 2, codeCoveragePercentage: 12) let integration = self.integration(.Warnings, buildResultSummary: buildResultSummary) let summary = SummaryBuilder() + summary.statusCreator = MockGitHubServer() let result = summary.buildPassing(integration) let exp_comment = "Result of Integration 15\n---\n*Duration*: 28 seconds\n*Result*: All 99 tests passed, but please **fix 2 warnings**.\n*Test Coverage*: 12%" @@ -114,6 +119,7 @@ class GitHubSummaryBuilderTests: XCTestCase { let buildResultSummary = MockBuildResultSummary(analyzerWarningCount: 3, testsCount: 99, codeCoveragePercentage: 12) let integration = self.integration(.AnalyzerWarnings, buildResultSummary: buildResultSummary) let summary = SummaryBuilder() + summary.statusCreator = MockGitHubServer() let result = summary.buildPassing(integration) let exp_comment = "Result of Integration 15\n---\n*Duration*: 28 seconds\n*Result*: All 99 tests passed, but please **fix 3 analyzer warnings**.\n*Test Coverage*: 12%" @@ -130,6 +136,7 @@ class GitHubSummaryBuilderTests: XCTestCase { let buildResultSummary = MockBuildResultSummary(testFailureCount: 1, testsCount: 99) let integration = self.integration(.TestFailures, buildResultSummary: buildResultSummary) let summary = SummaryBuilder() + summary.statusCreator = MockGitHubServer() let result = summary.buildFailingTests(integration) let exp_comment = "Result of Integration 15\n---\n*Duration*: 28 seconds\n*Result*: **Build failed 1 test** out of 99" @@ -145,6 +152,7 @@ class GitHubSummaryBuilderTests: XCTestCase { let buildResultSummary = MockBuildResultSummary(errorCount: 4) let integration = self.integration(.BuildErrors, buildResultSummary: buildResultSummary) let summary = SummaryBuilder() + summary.statusCreator = MockGitHubServer() let result = summary.buildErrorredIntegration(integration) let exp_comment = "Result of Integration 15\n---\n*Duration*: 28 seconds\n*Result*: **4 errors, failing state: build-errors**" @@ -160,7 +168,7 @@ class GitHubSummaryBuilderTests: XCTestCase { let buildResultSummary = MockBuildResultSummary() let integration = self.integration(.Canceled, buildResultSummary: buildResultSummary) let summary = SummaryBuilder() - summary.statusCreator = MockBuildStatusCreator() + summary.statusCreator = MockGitHubServer() let result = summary.buildCanceledIntegration(integration) let exp_comment = "Result of Integration 15\n---\n*Duration*: 28 seconds\nBuild was **manually canceled**." @@ -174,6 +182,7 @@ class GitHubSummaryBuilderTests: XCTestCase { func testEmpty() { let summary = SummaryBuilder() + summary.statusCreator = MockGitHubServer() let result = summary.buildEmptyIntegration() let exp_state = BuildState.NoState diff --git a/BuildaKitTests/Mocks.swift b/BuildaKitTests/Mocks.swift index 84b3752..249a82f 100644 --- a/BuildaKitTests/Mocks.swift +++ b/BuildaKitTests/Mocks.swift @@ -8,7 +8,7 @@ import Foundation import BuildaUtils -import BuildaGitServer +@testable import BuildaGitServer import Buildasaur import XcodeServerSDK import BuildaKit @@ -123,6 +123,14 @@ class MockIssue: Issue { } } +class MockBuildStatusCreator: BuildStatusCreator { + func createStatusFromState(state: BuildState, description: String?, targetUrl: String?) -> StatusType { + return GitHubStatus(state: GitHubStatus.GitHubState.fromBuildState(state), description: "Things happened", targetUrl: "http://hello.world", context: "Buildasaur") + } + + init() { } +} + class MockPullRequest: PullRequest { class func mockDictionary(number: Int, title: String, head: NSDictionary, base: NSDictionary) -> NSDictionary { diff --git a/BuildaKitTests/SyncPair_PR_Bot_Tests.swift b/BuildaKitTests/SyncPair_PR_Bot_Tests.swift index 2ece2db..4a76e03 100644 --- a/BuildaKitTests/SyncPair_PR_Bot_Tests.swift +++ b/BuildaKitTests/SyncPair_PR_Bot_Tests.swift @@ -27,50 +27,51 @@ func XCTBAssertNotNil(@autoclosure expression: () -> T?, message: String = " class SyncPair_PR_Bot_Tests: XCTestCase { - func mockedPRAndBotAndCommit() -> (PullRequest, Bot, String) { + func mockedPRAndBotAndCommit() -> (PullRequestType, Bot, String, BuildStatusCreator) { - let pr = MockPullRequest(number: 1, title: "Awesomified The Engine") + let pr: PullRequestType = MockPullRequest(number: 1, title: "Awesomified The Engine") let bot = MockBot(name: "BuildaBot [me/Repo] PR #1") - let commit = pr.head.sha + let commit = pr.headCommitSHA + let buildStatusCreator = MockBuildStatusCreator() - return (pr, bot, commit) + return (pr, bot, commit, buildStatusCreator) } func testNoIntegrationsYet() { - let (pr, bot, commit) = self.mockedPRAndBotAndCommit() + let (pr, bot, commit, statusCreator) = self.mockedPRAndBotAndCommit() let integrations = [Integration]() - let actions = SyncPairPRResolver().resolveActionsForCommitAndIssueWithBotIntegrations(commit, issue: pr, bot: bot, hostname: "localhost", integrations: integrations) + let actions = SyncPairPRResolver().resolveActionsForCommitAndIssueWithBotIntegrations(commit, issue: pr, bot: bot, hostname: "localhost", buildStatusCreator: statusCreator, integrations: integrations) XCTAssertEqual(actions.integrationsToCancel?.count ?? 0, 0) - XCTBAssertNil(actions.githubStatusToSet) + XCTBAssertNil(actions.statusToSet) XCTAssertNotNil(actions.startNewIntegrationBot) } func testFirstIntegrationPending() { - let (pr, bot, commit) = self.mockedPRAndBotAndCommit() + let (pr, bot, commit, statusCreator) = self.mockedPRAndBotAndCommit() let integrations = [ MockIntegration(number: 1, step: Integration.Step.Pending) ] - let actions = SyncPairPRResolver().resolveActionsForCommitAndIssueWithBotIntegrations(commit, issue: pr, bot: bot, hostname: "localhost", integrations: integrations) + let actions = SyncPairPRResolver().resolveActionsForCommitAndIssueWithBotIntegrations(commit, issue: pr, bot: bot, hostname: "localhost", buildStatusCreator: statusCreator, integrations: integrations) XCTAssertEqual(actions.integrationsToCancel?.count ?? 0, 0) XCTAssertNil(actions.startNewIntegrationBot) - XCTBAssertNotNil(actions.githubStatusToSet) - XCTAssertEqual(actions.githubStatusToSet!.status.status.state, Status.State.Pending) + XCTBAssertNotNil(actions.statusToSet) + XCTAssertEqual(actions.statusToSet!.status.status.state, BuildState.Pending) } func testMultipleIntegrationsPending() { - let (pr, bot, commit) = self.mockedPRAndBotAndCommit() + let (pr, bot, commit, statusCreator) = self.mockedPRAndBotAndCommit() let integrations = [ MockIntegration(number: 1, step: Integration.Step.Pending), MockIntegration(number: 2, step: Integration.Step.Pending), MockIntegration(number: 3, step: Integration.Step.Pending) ] - let actions = SyncPairPRResolver().resolveActionsForCommitAndIssueWithBotIntegrations(commit, issue: pr, bot: bot, hostname: "localhost", integrations: integrations) + let actions = SyncPairPRResolver().resolveActionsForCommitAndIssueWithBotIntegrations(commit, issue: pr, bot: bot, hostname: "localhost", buildStatusCreator: statusCreator, integrations: integrations) //should cancel all except for the last one let toCancel = Set(actions.integrationsToCancel!) @@ -78,83 +79,83 @@ class SyncPair_PR_Bot_Tests: XCTestCase { XCTAssertTrue(toCancel.contains(integrations[0])) XCTAssertTrue(toCancel.contains(integrations[1])) XCTAssertNil(actions.startNewIntegrationBot) - XCTBAssertNotNil(actions.githubStatusToSet) - XCTAssertEqual(actions.githubStatusToSet!.status.status.state, Status.State.Pending) + XCTBAssertNotNil(actions.statusToSet) + XCTAssertEqual(actions.statusToSet!.status.status.state, BuildState.Pending) } func testOneIntegrationRunning() { - let (pr, bot, commit) = self.mockedPRAndBotAndCommit() + let (pr, bot, commit, statusCreator) = self.mockedPRAndBotAndCommit() let integrations = [ MockIntegration(number: 1, step: Integration.Step.Building), ] - let actions = SyncPairPRResolver().resolveActionsForCommitAndIssueWithBotIntegrations(commit, issue: pr, bot: bot, hostname: "localhost", integrations: integrations) + let actions = SyncPairPRResolver().resolveActionsForCommitAndIssueWithBotIntegrations(commit, issue: pr, bot: bot, hostname: "localhost", buildStatusCreator: statusCreator, integrations: integrations) XCTAssertEqual(actions.integrationsToCancel!.count, 0) XCTAssertNil(actions.startNewIntegrationBot) - XCTBAssertNotNil(actions.githubStatusToSet) - XCTAssertEqual(actions.githubStatusToSet!.status.status.state, Status.State.Pending) + XCTBAssertNotNil(actions.statusToSet) + XCTAssertEqual(actions.statusToSet!.status.status.state, BuildState.Pending) } func testOneIntegrationTestsFailed() { - let (pr, bot, commit) = self.mockedPRAndBotAndCommit() + let (pr, bot, commit, statusCreator) = self.mockedPRAndBotAndCommit() let integrations = [ MockIntegration(number: 1, step: Integration.Step.Completed, sha: "head_sha", result: Integration.Result.TestFailures) ] - let actions = SyncPairPRResolver().resolveActionsForCommitAndIssueWithBotIntegrations(commit, issue: pr, bot: bot, hostname: "localhost", integrations: integrations) + let actions = SyncPairPRResolver().resolveActionsForCommitAndIssueWithBotIntegrations(commit, issue: pr, bot: bot, hostname: "localhost", buildStatusCreator: statusCreator, integrations: integrations) XCTAssertEqual(actions.integrationsToCancel!.count, 0) XCTAssertNil(actions.startNewIntegrationBot) - XCTBAssertNotNil(actions.githubStatusToSet) - XCTAssertEqual(actions.githubStatusToSet!.status.status.state, Status.State.Failure) + XCTBAssertNotNil(actions.statusToSet) + XCTAssertEqual(actions.statusToSet!.status.status.state, BuildState.Failure) } func testOneIntegrationSuccess() { - let (pr, bot, commit) = self.mockedPRAndBotAndCommit() + let (pr, bot, commit, statusCreator) = self.mockedPRAndBotAndCommit() let integrations = [ MockIntegration(number: 1, step: Integration.Step.Completed, sha: "head_sha", result: Integration.Result.Succeeded) ] - let actions = SyncPairPRResolver().resolveActionsForCommitAndIssueWithBotIntegrations(commit, issue: pr, bot: bot, hostname: "localhost", integrations: integrations) + let actions = SyncPairPRResolver().resolveActionsForCommitAndIssueWithBotIntegrations(commit, issue: pr, bot: bot, hostname: "localhost", buildStatusCreator: statusCreator, integrations: integrations) XCTAssertEqual(actions.integrationsToCancel!.count, 0) XCTAssertNil(actions.startNewIntegrationBot) - XCTBAssertNotNil(actions.githubStatusToSet) - XCTAssertEqual(actions.githubStatusToSet!.status.status.state, Status.State.Success) + XCTBAssertNotNil(actions.statusToSet) + XCTAssertEqual(actions.statusToSet!.status.status.state, BuildState.Success) } func testTwoIntegrationOneRunningOnePending() { - let (pr, bot, commit) = self.mockedPRAndBotAndCommit() + let (pr, bot, commit, statusCreator) = self.mockedPRAndBotAndCommit() let integrations = [ MockIntegration(number: 1, step: Integration.Step.Building, sha: "head_sha"), MockIntegration(number: 2, step: Integration.Step.Pending, sha: "head_sha") ] - let actions = SyncPairPRResolver().resolveActionsForCommitAndIssueWithBotIntegrations(commit, issue: pr, bot: bot, hostname: "localhost", integrations: integrations) + let actions = SyncPairPRResolver().resolveActionsForCommitAndIssueWithBotIntegrations(commit, issue: pr, bot: bot, hostname: "localhost", buildStatusCreator: statusCreator, integrations: integrations) XCTAssertEqual(actions.integrationsToCancel!.count, 1) XCTAssertNil(actions.startNewIntegrationBot) - XCTBAssertNotNil(actions.githubStatusToSet) - XCTAssertEqual(actions.githubStatusToSet!.status.status.state, Status.State.Pending) + XCTBAssertNotNil(actions.statusToSet) + XCTAssertEqual(actions.statusToSet!.status.status.state, BuildState.Pending) } func testTwoIntegrationsDifferentCommits() { - let (pr, bot, commit) = self.mockedPRAndBotAndCommit() + let (pr, bot, commit, statusCreator) = self.mockedPRAndBotAndCommit() let integrations = [ MockIntegration(number: 1, step: Integration.Step.Building, sha: "head_sha_old"), ] - let actions = SyncPairPRResolver().resolveActionsForCommitAndIssueWithBotIntegrations(commit, issue: pr, bot: bot, hostname: "localhost", integrations: integrations) + let actions = SyncPairPRResolver().resolveActionsForCommitAndIssueWithBotIntegrations(commit, issue: pr, bot: bot, hostname: "localhost", buildStatusCreator: statusCreator, integrations: integrations) XCTAssertEqual(actions.integrationsToCancel!.count, 1) XCTAssertNotNil(actions.startNewIntegrationBot) - XCTBAssertNil(actions.githubStatusToSet) //no change + XCTBAssertNil(actions.statusToSet) //no change } //TODO: add more complicated cases diff --git a/BuildaKitTests/SyncerTests.swift b/BuildaKitTests/SyncerTests.swift index 84f680e..b38f745 100644 --- a/BuildaKitTests/SyncerTests.swift +++ b/BuildaKitTests/SyncerTests.swift @@ -60,7 +60,7 @@ class SyncerTests: XCTestCase { func testCreatingChangeActions_MultiplePR_NoBots() { - let prs = [ + let prs: [PullRequestType] = [ MockPullRequest(number: 4, title: ""), MockPullRequest(number: 7, title: "") ] @@ -100,12 +100,12 @@ class SyncerTests: XCTestCase { MockBot(name: "BuildaBot [me/Repo] |-> gh/bot_to_delete"), ] - let prs = [ + let prs: [PullRequestType] = [ MockPullRequest(number: 4, title: ""), MockPullRequest(number: 7, title: "") ] - let branches = [ + let branches: [BranchType] = [ MockBranch(name: "cd/broke_something"), MockBranch(name: "ab/fixed_errthing"), MockBranch(name: "ef/migrating_from_php_to_mongo_db") diff --git a/Buildasaur.xcodeproj/project.pbxproj b/Buildasaur.xcodeproj/project.pbxproj index 4c66268..9c209ce 100644 --- a/Buildasaur.xcodeproj/project.pbxproj +++ b/Buildasaur.xcodeproj/project.pbxproj @@ -126,7 +126,7 @@ 3ACF93AA1BBEC7AB00CD888C /* XcodeScheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3ACF93A81BBEC7AB00CD888C /* XcodeScheme.swift */; }; 3AD338B01AAE31D500ECD0F2 /* BuildTemplateViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AD338AF1AAE31D500ECD0F2 /* BuildTemplateViewController.swift */; }; 3AD5D2E81BD01EAF008DBB45 /* SummaryBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AD5D2E71BD01EAF008DBB45 /* SummaryBuilder.swift */; }; - 3AD5D2EA1BD02144008DBB45 /* SummaryBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AD5D2E91BD02144008DBB45 /* SummaryBuilderTests.swift */; }; + 3AD5D2EA1BD02144008DBB45 /* GitHubSummaryBuilderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AD5D2E91BD02144008DBB45 /* GitHubSummaryBuilderTests.swift */; }; 3AE0AB2B1BB991AB00A52D20 /* SyncerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE0AB2A1BB991AB00A52D20 /* SyncerViewModel.swift */; }; 3AE4F6CE1BBC88450006CA1C /* RACUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE4F6CD1BBC88450006CA1C /* RACUtils.swift */; }; 3AE4F6D01BBC8D250006CA1C /* EmptyProjectViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE4F6CF1BBC8D250006CA1C /* EmptyProjectViewController.swift */; }; @@ -350,7 +350,7 @@ 3ACF93A81BBEC7AB00CD888C /* XcodeScheme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XcodeScheme.swift; sourceTree = ""; }; 3AD338AF1AAE31D500ECD0F2 /* BuildTemplateViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BuildTemplateViewController.swift; sourceTree = ""; }; 3AD5D2E71BD01EAF008DBB45 /* SummaryBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SummaryBuilder.swift; sourceTree = ""; }; - 3AD5D2E91BD02144008DBB45 /* SummaryBuilderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SummaryBuilderTests.swift; sourceTree = ""; }; + 3AD5D2E91BD02144008DBB45 /* GitHubSummaryBuilderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubSummaryBuilderTests.swift; sourceTree = ""; }; 3AE0AB2A1BB991AB00A52D20 /* SyncerViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncerViewModel.swift; sourceTree = ""; }; 3AE4F6CD1BBC88450006CA1C /* RACUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RACUtils.swift; sourceTree = ""; }; 3AE4F6CF1BBC8D250006CA1C /* EmptyProjectViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyProjectViewController.swift; sourceTree = ""; }; @@ -609,7 +609,7 @@ 3A7F0CC71BC1508C0079A511 /* GeneralTests.swift */, 3ACBADD61B5ADE0E00204457 /* MockHelpers.swift */, 3ACBADD81B5ADE1400204457 /* Mocks.swift */, - 3AD5D2E91BD02144008DBB45 /* SummaryBuilderTests.swift */, + 3AD5D2E91BD02144008DBB45 /* GitHubSummaryBuilderTests.swift */, 3ACBADDA1B5ADE1400204457 /* SyncerTests.swift */, 3ACBADDB1B5ADE1400204457 /* SyncPair_PR_Bot_Tests.swift */, 3A9D741E1BCBF86900DCA23C /* Migration */, @@ -1489,7 +1489,7 @@ buildActionMask = 2147483647; files = ( 3ACBADD71B5ADE0E00204457 /* MockHelpers.swift in Sources */, - 3AD5D2EA1BD02144008DBB45 /* SummaryBuilderTests.swift in Sources */, + 3AD5D2EA1BD02144008DBB45 /* GitHubSummaryBuilderTests.swift in Sources */, 3ACBADDE1B5ADE1400204457 /* SyncerTests.swift in Sources */, 3ACBADDF1B5ADE1400204457 /* SyncPair_PR_Bot_Tests.swift in Sources */, 3ACBADDC1B5ADE1400204457 /* Mocks.swift in Sources */, From 237956962303ffdcfd6a387168f2e4c9ef3a519c Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Thu, 21 Jan 2016 23:28:00 +0000 Subject: [PATCH 12/63] no need for those todo's --- BuildaGitServer/BaseTypes.swift | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/BuildaGitServer/BaseTypes.swift b/BuildaGitServer/BaseTypes.swift index 437b872..a28b6a1 100644 --- a/BuildaGitServer/BaseTypes.swift +++ b/BuildaGitServer/BaseTypes.swift @@ -81,16 +81,12 @@ public protocol RepoType { var permissions: RepoPermissions { get } var originUrlSSH: String { get } var latestRateLimitInfo: RateLimitType? { get } - - //TODO: add required properties } public protocol BranchType { var name: String { get } var commitSHA: String { get } - - //TODO: add required properties } public protocol IssueType { @@ -107,8 +103,6 @@ public protocol PullRequestType: IssueType { var baseName: String { get } var title: String { get } - - //TODO: add required properties } public enum BuildState { @@ -124,8 +118,6 @@ public protocol StatusType { var state: BuildState { get } var description: String? { get } var targetUrl: String? { get } - - //TODO: add required properties } extension StatusType { @@ -139,7 +131,5 @@ extension StatusType { public protocol CommentType { var body: String { get } - - //TODO: add required properties } From 4e1bcac4cb50f9ced21013421489ee5de286d640 Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Thu, 21 Jan 2016 23:44:58 +0000 Subject: [PATCH 13/63] adding folders --- Buildasaur.xcodeproj/project.pbxproj | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Buildasaur.xcodeproj/project.pbxproj b/Buildasaur.xcodeproj/project.pbxproj index 9c209ce..e6429e0 100644 --- a/Buildasaur.xcodeproj/project.pbxproj +++ b/Buildasaur.xcodeproj/project.pbxproj @@ -668,6 +668,7 @@ 3AAF6EE81A3CE5BA00C657FB /* BuildaGitServer.h */, 3AAF6F031A3CE66A00C657FB /* GitServerPublic.swift */, 3A35CD6B1BE96E880088D57A /* Base */, + 3ABF0ADC1C51A47A00882A40 /* BitBucket */, 3A6355DB1A3BBDB000545BF9 /* GitHub */, 3AAF6EE61A3CE5BA00C657FB /* Supporting Files */, ); @@ -699,6 +700,21 @@ name = "Supporting Files"; sourceTree = ""; }; + 3ABF0ADC1C51A47A00882A40 /* BitBucket */ = { + isa = PBXGroup; + children = ( + 3ABF0ADD1C51A48C00882A40 /* Entities */, + ); + name = BitBucket; + sourceTree = ""; + }; + 3ABF0ADD1C51A48C00882A40 /* Entities */ = { + isa = PBXGroup; + children = ( + ); + name = Entities; + sourceTree = ""; + }; 3AC156251BB95C130068A1A3 /* Dashboard */ = { isa = PBXGroup; children = ( From 56452ec47db56c22b770e7204bf9e834a142c6e3 Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Thu, 21 Jan 2016 23:47:40 +0000 Subject: [PATCH 14/63] moving things around --- BuildaGitServer/{ => Base}/BaseTypes.swift | 0 .../{ => Base}/SourceServerExtensions.swift | 0 BuildaGitServer/{ => GitHub}/Branch.swift | 0 BuildaGitServer/{ => GitHub}/Comment.swift | 0 BuildaGitServer/{ => GitHub}/Commit.swift | 0 .../{ => GitHub}/GitHubEndpoints.swift | 0 .../{ => GitHub}/GitHubEntity.swift | 0 .../{ => GitHub}/GitHubFactory.swift | 0 .../{ => GitHub}/GitHubRateLimit.swift | 0 .../{ => GitHub}/GitHubServer.swift | 0 BuildaGitServer/{ => GitHub}/Issue.swift | 0 .../{ => GitHub}/PullRequest.swift | 0 .../{ => GitHub}/PullRequestBranch.swift | 0 BuildaGitServer/{ => GitHub}/Repo.swift | 0 BuildaGitServer/{ => GitHub}/Status.swift | 0 BuildaGitServer/{ => GitHub}/User.swift | 0 Buildasaur.xcodeproj/project.pbxproj | 30 +++++++++---------- 17 files changed, 15 insertions(+), 15 deletions(-) rename BuildaGitServer/{ => Base}/BaseTypes.swift (100%) rename BuildaGitServer/{ => Base}/SourceServerExtensions.swift (100%) rename BuildaGitServer/{ => GitHub}/Branch.swift (100%) rename BuildaGitServer/{ => GitHub}/Comment.swift (100%) rename BuildaGitServer/{ => GitHub}/Commit.swift (100%) rename BuildaGitServer/{ => GitHub}/GitHubEndpoints.swift (100%) rename BuildaGitServer/{ => GitHub}/GitHubEntity.swift (100%) rename BuildaGitServer/{ => GitHub}/GitHubFactory.swift (100%) rename BuildaGitServer/{ => GitHub}/GitHubRateLimit.swift (100%) rename BuildaGitServer/{ => GitHub}/GitHubServer.swift (100%) rename BuildaGitServer/{ => GitHub}/Issue.swift (100%) rename BuildaGitServer/{ => GitHub}/PullRequest.swift (100%) rename BuildaGitServer/{ => GitHub}/PullRequestBranch.swift (100%) rename BuildaGitServer/{ => GitHub}/Repo.swift (100%) rename BuildaGitServer/{ => GitHub}/Status.swift (100%) rename BuildaGitServer/{ => GitHub}/User.swift (100%) diff --git a/BuildaGitServer/BaseTypes.swift b/BuildaGitServer/Base/BaseTypes.swift similarity index 100% rename from BuildaGitServer/BaseTypes.swift rename to BuildaGitServer/Base/BaseTypes.swift diff --git a/BuildaGitServer/SourceServerExtensions.swift b/BuildaGitServer/Base/SourceServerExtensions.swift similarity index 100% rename from BuildaGitServer/SourceServerExtensions.swift rename to BuildaGitServer/Base/SourceServerExtensions.swift diff --git a/BuildaGitServer/Branch.swift b/BuildaGitServer/GitHub/Branch.swift similarity index 100% rename from BuildaGitServer/Branch.swift rename to BuildaGitServer/GitHub/Branch.swift diff --git a/BuildaGitServer/Comment.swift b/BuildaGitServer/GitHub/Comment.swift similarity index 100% rename from BuildaGitServer/Comment.swift rename to BuildaGitServer/GitHub/Comment.swift diff --git a/BuildaGitServer/Commit.swift b/BuildaGitServer/GitHub/Commit.swift similarity index 100% rename from BuildaGitServer/Commit.swift rename to BuildaGitServer/GitHub/Commit.swift diff --git a/BuildaGitServer/GitHubEndpoints.swift b/BuildaGitServer/GitHub/GitHubEndpoints.swift similarity index 100% rename from BuildaGitServer/GitHubEndpoints.swift rename to BuildaGitServer/GitHub/GitHubEndpoints.swift diff --git a/BuildaGitServer/GitHubEntity.swift b/BuildaGitServer/GitHub/GitHubEntity.swift similarity index 100% rename from BuildaGitServer/GitHubEntity.swift rename to BuildaGitServer/GitHub/GitHubEntity.swift diff --git a/BuildaGitServer/GitHubFactory.swift b/BuildaGitServer/GitHub/GitHubFactory.swift similarity index 100% rename from BuildaGitServer/GitHubFactory.swift rename to BuildaGitServer/GitHub/GitHubFactory.swift diff --git a/BuildaGitServer/GitHubRateLimit.swift b/BuildaGitServer/GitHub/GitHubRateLimit.swift similarity index 100% rename from BuildaGitServer/GitHubRateLimit.swift rename to BuildaGitServer/GitHub/GitHubRateLimit.swift diff --git a/BuildaGitServer/GitHubServer.swift b/BuildaGitServer/GitHub/GitHubServer.swift similarity index 100% rename from BuildaGitServer/GitHubServer.swift rename to BuildaGitServer/GitHub/GitHubServer.swift diff --git a/BuildaGitServer/Issue.swift b/BuildaGitServer/GitHub/Issue.swift similarity index 100% rename from BuildaGitServer/Issue.swift rename to BuildaGitServer/GitHub/Issue.swift diff --git a/BuildaGitServer/PullRequest.swift b/BuildaGitServer/GitHub/PullRequest.swift similarity index 100% rename from BuildaGitServer/PullRequest.swift rename to BuildaGitServer/GitHub/PullRequest.swift diff --git a/BuildaGitServer/PullRequestBranch.swift b/BuildaGitServer/GitHub/PullRequestBranch.swift similarity index 100% rename from BuildaGitServer/PullRequestBranch.swift rename to BuildaGitServer/GitHub/PullRequestBranch.swift diff --git a/BuildaGitServer/Repo.swift b/BuildaGitServer/GitHub/Repo.swift similarity index 100% rename from BuildaGitServer/Repo.swift rename to BuildaGitServer/GitHub/Repo.swift diff --git a/BuildaGitServer/Status.swift b/BuildaGitServer/GitHub/Status.swift similarity index 100% rename from BuildaGitServer/Status.swift rename to BuildaGitServer/GitHub/Status.swift diff --git a/BuildaGitServer/User.swift b/BuildaGitServer/GitHub/User.swift similarity index 100% rename from BuildaGitServer/User.swift rename to BuildaGitServer/GitHub/User.swift diff --git a/Buildasaur.xcodeproj/project.pbxproj b/Buildasaur.xcodeproj/project.pbxproj index e6429e0..1f3087a 100644 --- a/Buildasaur.xcodeproj/project.pbxproj +++ b/Buildasaur.xcodeproj/project.pbxproj @@ -235,11 +235,11 @@ 3A0FF5AD1BC0067700FB8051 /* SyncerManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncerManager.swift; sourceTree = ""; }; 3A28AF521BC984850011756E /* ConfigTriplet.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigTriplet.swift; sourceTree = ""; }; 3A3231B01B5AEF7900B53E3F /* Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = ""; }; - 3A32CD131A3D00F800861A34 /* Comment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Comment.swift; sourceTree = ""; }; - 3A32CD171A3D01E300861A34 /* Issue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Issue.swift; sourceTree = ""; }; + 3A32CD131A3D00F800861A34 /* Comment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Comment.swift; path = GitHub/Comment.swift; sourceTree = ""; }; + 3A32CD171A3D01E300861A34 /* Issue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Issue.swift; path = GitHub/Issue.swift; sourceTree = ""; }; 3A35CD6C1BE96E9B0088D57A /* BaseTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseTypes.swift; sourceTree = ""; }; 3A35CD6D1BE96E9B0088D57A /* SourceServerExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SourceServerExtensions.swift; sourceTree = ""; }; - 3A38CF951A3CE94C00F41AFA /* Status.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Status.swift; path = BuildaGitServer/Status.swift; sourceTree = SOURCE_ROOT; }; + 3A38CF951A3CE94C00F41AFA /* Status.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Status.swift; path = GitHub/Status.swift; sourceTree = ""; }; 3A395B541BCF007000BB6947 /* LoginItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginItem.swift; sourceTree = ""; }; 3A3BDC1E1AF6D34900D2CD99 /* GitHubRateLimit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubRateLimit.swift; sourceTree = ""; }; 3A3BF8C61BD7C1680050A0B7 /* CheckoutFileParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckoutFileParser.swift; sourceTree = ""; }; @@ -256,15 +256,15 @@ 3A5687751A3B93BD0066DB2B /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 3A5687791A3B93BD0066DB2B /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 3A56877C1A3B93BD0066DB2B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - 3A58B1521A3B967C003E0266 /* GitHubServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GitHubServer.swift; path = BuildaGitServer/GitHubServer.swift; sourceTree = SOURCE_ROOT; }; + 3A58B1521A3B967C003E0266 /* GitHubServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubServer.swift; sourceTree = ""; }; 3A58B1581A3B96C9003E0266 /* GitHubServerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubServerTests.swift; sourceTree = ""; }; 3A5B04A91AB4ABEB00F60536 /* TriggerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TriggerViewController.swift; sourceTree = ""; }; 3A5B04AF1AB5144700F60536 /* ManualBotManagementViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManualBotManagementViewController.swift; sourceTree = ""; }; - 3A6355D71A3BBCD200545BF9 /* GitHubEndpoints.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GitHubEndpoints.swift; path = BuildaGitServer/GitHubEndpoints.swift; sourceTree = SOURCE_ROOT; }; - 3A6355D91A3BBDA500545BF9 /* GitHubFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GitHubFactory.swift; path = BuildaGitServer/GitHubFactory.swift; sourceTree = SOURCE_ROOT; }; - 3A6355DF1A3BC77F00545BF9 /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = User.swift; path = BuildaGitServer/User.swift; sourceTree = SOURCE_ROOT; }; - 3A6355E11A3BCD2800545BF9 /* Repo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Repo.swift; path = BuildaGitServer/Repo.swift; sourceTree = SOURCE_ROOT; }; - 3A6355E31A3BD57D00545BF9 /* PullRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PullRequest.swift; path = BuildaGitServer/PullRequest.swift; sourceTree = SOURCE_ROOT; }; + 3A6355D71A3BBCD200545BF9 /* GitHubEndpoints.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubEndpoints.swift; sourceTree = ""; }; + 3A6355D91A3BBDA500545BF9 /* GitHubFactory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GitHubFactory.swift; sourceTree = ""; }; + 3A6355DF1A3BC77F00545BF9 /* User.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = User.swift; path = GitHub/User.swift; sourceTree = ""; }; + 3A6355E11A3BCD2800545BF9 /* Repo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Repo.swift; path = GitHub/Repo.swift; sourceTree = ""; }; + 3A6355E31A3BD57D00545BF9 /* PullRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PullRequest.swift; path = GitHub/PullRequest.swift; sourceTree = ""; }; 3A756DBD1BAB425E00508B69 /* BuildaHeartbeatKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BuildaHeartbeatKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3A756DBF1BAB425E00508B69 /* BuildaHeartbeatKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BuildaHeartbeatKit.h; sourceTree = ""; }; 3A756DC11BAB425E00508B69 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -293,15 +293,15 @@ 3A9D74211BCC041600DCA23C /* Buildasaur-format-0-example1 */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "Buildasaur-format-0-example1"; path = "Migration/Buildasaur-format-0-example1"; sourceTree = ""; }; 3A9D74231BCC103E00DCA23C /* Buildasaur-format-1-example1 */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "Buildasaur-format-1-example1"; path = "Migration/Buildasaur-format-1-example1"; sourceTree = ""; }; 3A9D74251BCC19BB00DCA23C /* Buildasaur-format-2-example1 */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "Buildasaur-format-2-example1"; path = "Migration/Buildasaur-format-2-example1"; sourceTree = ""; }; - 3A9DEC7A1A3BDA6C008C8270 /* PullRequestBranch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PullRequestBranch.swift; path = BuildaGitServer/PullRequestBranch.swift; sourceTree = SOURCE_ROOT; }; + 3A9DEC7A1A3BDA6C008C8270 /* PullRequestBranch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PullRequestBranch.swift; path = GitHub/PullRequestBranch.swift; sourceTree = ""; }; 3AA573051BCD70FC00172D3E /* URLUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLUtils.swift; sourceTree = ""; }; 3AAA1B651AAB6E9800FA1598 /* SeparatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SeparatorView.swift; sourceTree = ""; }; 3AAA1B671AAB722600FA1598 /* ProjectViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProjectViewController.swift; sourceTree = ""; }; 3AAA1B751AAC504700FA1598 /* XcodeServerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XcodeServerViewController.swift; sourceTree = ""; }; 3AAA1B771AAC508A00FA1598 /* ConfigEditViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConfigEditViewController.swift; sourceTree = ""; }; - 3AAF6E671A3CC1F400C657FB /* GitHubEntity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GitHubEntity.swift; path = BuildaGitServer/GitHubEntity.swift; sourceTree = SOURCE_ROOT; }; - 3AAF6E691A3CCA0900C657FB /* Branch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Branch.swift; path = BuildaGitServer/Branch.swift; sourceTree = SOURCE_ROOT; }; - 3AAF6E6B1A3CCA3B00C657FB /* Commit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Commit.swift; path = BuildaGitServer/Commit.swift; sourceTree = SOURCE_ROOT; }; + 3AAF6E671A3CC1F400C657FB /* GitHubEntity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = GitHubEntity.swift; path = GitHub/GitHubEntity.swift; sourceTree = ""; }; + 3AAF6E691A3CCA0900C657FB /* Branch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Branch.swift; path = GitHub/Branch.swift; sourceTree = ""; }; + 3AAF6E6B1A3CCA3B00C657FB /* Commit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Commit.swift; path = GitHub/Commit.swift; sourceTree = ""; }; 3AAF6EE41A3CE5BA00C657FB /* BuildaGitServer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BuildaGitServer.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3AAF6EE71A3CE5BA00C657FB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3AAF6EE81A3CE5BA00C657FB /* BuildaGitServer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BuildaGitServer.h; sourceTree = ""; }; @@ -480,7 +480,7 @@ 3A35CD6C1BE96E9B0088D57A /* BaseTypes.swift */, 3A35CD6D1BE96E9B0088D57A /* SourceServerExtensions.swift */, ); - name = Base; + path = Base; sourceTree = ""; }; 3A5687671A3B93BD0066DB2B = { @@ -548,7 +548,7 @@ 3A3BDC1E1AF6D34900D2CD99 /* GitHubRateLimit.swift */, ); name = GitHub; - path = BuildaGitServer; + path = BuildaGitServer/GitHub; sourceTree = SOURCE_ROOT; }; 3A6355DE1A3BC77500545BF9 /* Entities */ = { From 2f60167da6f0612998f9a71dfcf1cb0a1074fedd Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Fri, 22 Jan 2016 22:02:51 +0000 Subject: [PATCH 15/63] added keychain for ssh passphrase, github token and server passwords --- BuildaKit/Logging.swift | 4 +- BuildaKit/NetworkUtils.swift | 2 +- BuildaKit/Persistence.swift | 12 +- BuildaKit/PersistenceMigrator.swift | 114 +++++++++++++++++- BuildaKit/ProjectConfig.swift | 13 +- BuildaKit/SecurePersistence.swift | 57 +++++++++ BuildaKit/StorageManager.swift | 29 ++++- BuildaKit/SyncerFactory.swift | 3 +- .../9B53CC35-57DA-4DA0-9B85-05FCF109512A.json | 26 ++++ .../B31C6530-BB84-4A93-B08C-54074AAB5F37.json | 26 ++++ .../Buildasaur-format-2-example2/Builda.log | 89 ++++++++++++++ .../Buildasaur-format-2-example2/Config.json | 3 + .../Projects.json | 18 +++ .../ServerConfigs.json | 14 +++ .../Buildasaur-format-2-example2/Syncers.json | 26 ++++ .../4E66D0D5-D5CC-417E-A40E-73B513CE4E10.json | 25 ++++ .../E8F5285A-A262-4630-AF7B-236772B75760.json | 7 ++ .../9B53CC35-57DA-4DA0-9B85-05FCF109512A.json | 26 ++++ .../B31C6530-BB84-4A93-B08C-54074AAB5F37.json | 26 ++++ .../Buildasaur-format-3-example1/Config.json | 3 + .../Logs/Builda.log | 89 ++++++++++++++ .../Projects.json | 14 +++ .../ServerConfigs.json | 12 ++ .../Buildasaur-format-3-example1/Syncers.json | 26 ++++ .../4E66D0D5-D5CC-417E-A40E-73B513CE4E10.json | 25 ++++ .../E8F5285A-A262-4630-AF7B-236772B75760.json | 7 ++ BuildaKitTests/MigrationTests.swift | 19 +++ Buildasaur.xcodeproj/project.pbxproj | 16 ++- Buildasaur/AppDelegate.swift | 14 +-- Buildasaur/ProjectViewController.swift | 6 +- Podfile | 3 +- Podfile.lock | 9 +- 32 files changed, 729 insertions(+), 34 deletions(-) create mode 100644 BuildaKit/SecurePersistence.swift create mode 100644 BuildaKitTests/Migration/Buildasaur-format-2-example2/BuildTemplates/9B53CC35-57DA-4DA0-9B85-05FCF109512A.json create mode 100644 BuildaKitTests/Migration/Buildasaur-format-2-example2/BuildTemplates/B31C6530-BB84-4A93-B08C-54074AAB5F37.json create mode 100644 BuildaKitTests/Migration/Buildasaur-format-2-example2/Builda.log create mode 100644 BuildaKitTests/Migration/Buildasaur-format-2-example2/Config.json create mode 100644 BuildaKitTests/Migration/Buildasaur-format-2-example2/Projects.json create mode 100644 BuildaKitTests/Migration/Buildasaur-format-2-example2/ServerConfigs.json create mode 100644 BuildaKitTests/Migration/Buildasaur-format-2-example2/Syncers.json create mode 100644 BuildaKitTests/Migration/Buildasaur-format-2-example2/Triggers/4E66D0D5-D5CC-417E-A40E-73B513CE4E10.json create mode 100644 BuildaKitTests/Migration/Buildasaur-format-2-example2/Triggers/E8F5285A-A262-4630-AF7B-236772B75760.json create mode 100644 BuildaKitTests/Migration/Buildasaur-format-3-example1/BuildTemplates/9B53CC35-57DA-4DA0-9B85-05FCF109512A.json create mode 100644 BuildaKitTests/Migration/Buildasaur-format-3-example1/BuildTemplates/B31C6530-BB84-4A93-B08C-54074AAB5F37.json create mode 100644 BuildaKitTests/Migration/Buildasaur-format-3-example1/Config.json create mode 100644 BuildaKitTests/Migration/Buildasaur-format-3-example1/Logs/Builda.log create mode 100644 BuildaKitTests/Migration/Buildasaur-format-3-example1/Projects.json create mode 100644 BuildaKitTests/Migration/Buildasaur-format-3-example1/ServerConfigs.json create mode 100644 BuildaKitTests/Migration/Buildasaur-format-3-example1/Syncers.json create mode 100644 BuildaKitTests/Migration/Buildasaur-format-3-example1/Triggers/4E66D0D5-D5CC-417E-A40E-73B513CE4E10.json create mode 100644 BuildaKitTests/Migration/Buildasaur-format-3-example1/Triggers/E8F5285A-A262-4630-AF7B-236772B75760.json diff --git a/BuildaKit/Logging.swift b/BuildaKit/Logging.swift index 4ec0801..c179aff 100644 --- a/BuildaKit/Logging.swift +++ b/BuildaKit/Logging.swift @@ -13,7 +13,9 @@ public class Logging { public class func setup(persistence: Persistence, alsoIntoFile: Bool) { - let path = persistence.fileURLWithName("Builda.log", intention: .Writing, isDirectory: false) + let path = persistence + .fileURLWithName("Logs", intention: .Writing, isDirectory: true) + .URLByAppendingPathComponent("Builda.log", isDirectory: false) var loggers = [Logger]() diff --git a/BuildaKit/NetworkUtils.swift b/BuildaKit/NetworkUtils.swift index 35d0afd..51faf70 100644 --- a/BuildaKit/NetworkUtils.swift +++ b/BuildaKit/NetworkUtils.swift @@ -15,7 +15,7 @@ public class NetworkUtils { public class func checkAvailabilityOfGitHubWithCurrentSettingsOfProject(project: Project, completion: (success: Bool, error: ErrorType?) -> ()) { - let token = project.config.value.githubToken + let token = project.config.value.serverAuthentication! //TODO: have project spit out Set let options: Set = [.Token(token)] diff --git a/BuildaKit/Persistence.swift b/BuildaKit/Persistence.swift index 50dfbf6..87222d6 100644 --- a/BuildaKit/Persistence.swift +++ b/BuildaKit/Persistence.swift @@ -228,6 +228,16 @@ public class Persistence { _ = try? self.fileManager.copyItemAtURL(url, toURL: writeUrl) } + public func copyFileToFolder(fileName: String, folder: String) { + + let url = self.fileURLWithName(fileName, intention: .Reading, isDirectory: false) + let writeUrl = self + .fileURLWithName(folder, intention: .Writing, isDirectory: true) + .URLByAppendingPathComponent(fileName, isDirectory: false) + + _ = try? self.fileManager.copyItemAtURL(url, toURL: writeUrl) + } + public func createFolderIfNotExists(url: NSURL) { let fm = self.fileManager @@ -244,7 +254,7 @@ public class Persistence { case WritingNoCreateFolder } - private func folderForIntention(intention: PersistenceIntention) -> NSURL { + func folderForIntention(intention: PersistenceIntention) -> NSURL { switch intention { case .Reading: return self.readingFolder diff --git a/BuildaKit/PersistenceMigrator.swift b/BuildaKit/PersistenceMigrator.swift index 7d8906e..ad01806 100644 --- a/BuildaKit/PersistenceMigrator.swift +++ b/BuildaKit/PersistenceMigrator.swift @@ -8,6 +8,7 @@ import Foundation import BuildaUtils +import XcodeServerSDK public protocol MigratorType { init(persistence: Persistence) @@ -47,7 +48,8 @@ public class CompositeMigrator: MigratorType { public required init(persistence: Persistence) { self.childMigrators = [ Migrator_v0_v1(persistence: persistence), - Migrator_v1_v2(persistence: persistence) + Migrator_v1_v2(persistence: persistence), + Migrator_v2_v3(persistence: persistence) ] } @@ -107,8 +109,6 @@ class Migrator_v0_v1: MigratorType { /* - ServerConfigs.json: each server now has an id - - - Config.json: persistence_version: 1 -> 2 */ class Migrator_v1_v2: MigratorType { @@ -271,3 +271,111 @@ class Migrator_v1_v2: MigratorType { self.persistence.saveDictionary("Config.json", item: mutableConfig) } } + +/* +- ServerConfigs.json: password moved to the keychain +- Projects.json: github_token -> server_authentication, ssh_passphrase moved to keychain +- move any .log files to a separate folder called 'Logs' +*/ +class Migrator_v2_v3: MigratorType { + + internal var persistence: Persistence + required init(persistence: Persistence) { + self.persistence = persistence + } + + func isMigrationRequired() -> Bool { + + return self.persistenceVersion() == 2 + } + + func attemptMigration() throws { + + let pers = self.persistence + + //migrate + self.migrateProjectAuthentication() + self.migrateServerAuthentication() + self.migrateLogs() + + //copy the rest + pers.copyFileToWriteLocation("Syncers.json", isDirectory: false) + pers.copyFileToWriteLocation("BuildTemplates", isDirectory: true) + pers.copyFileToWriteLocation("Triggers", isDirectory: true) + + let config = self.config() + let mutableConfig = config.mutableCopy() as! NSMutableDictionary + mutableConfig[kPersistenceVersion] = 3 + + //save the updated config + pers.saveDictionary("Config.json", item: mutableConfig) + } + + func migrateProjectAuthentication() { + + let pers = self.persistence + let projects = pers.loadArrayOfDictionariesFromFile("Projects.json") ?? [] + let mutableProjects = projects.map { $0.mutableCopy() as! NSMutableDictionary } + + let renamedAuth = mutableProjects.map { + (d: NSMutableDictionary) -> NSDictionary in + + let id = d.stringForKey("id") + let token = d.stringForKey("github_token") + let passphrase = d.optionalStringForKey("ssh_passphrase") + d.removeObjectForKey("github_token") + d.removeObjectForKey("ssh_passphrase") + + let tokenKeychain = SecurePersistence.sourceServerTokenKeychain() + tokenKeychain[id] = token + + let passphraseKeychain = SecurePersistence.sourceServerPassphraseKeychain() + passphraseKeychain[id] = passphrase + + precondition(tokenKeychain[id] == token, "Saved token must match") + precondition(passphraseKeychain[id] == passphrase, "Saved passphrase must match") + + return d + } + + pers.saveArray("Projects.json", items: renamedAuth) + } + + func migrateServerAuthentication() { + + let pers = self.persistence + let servers = pers.loadArrayOfDictionariesFromFile("ServerConfigs.json") ?? [] + let mutableServers = servers.map { $0.mutableCopy() as! NSMutableDictionary } + + let withoutPasswords = mutableServers.map { + (d: NSMutableDictionary) -> NSDictionary in + + let password = d.stringForKey("password") + let key = (try! XcodeServerConfig(json: d)).keychainKey() + + let keychain = SecurePersistence.xcodeServerPasswordKeychain() + keychain[key] = password + + d.removeObjectForKey("password") + + precondition(keychain[key] == password, "Saved password must match") + + return d + } + + pers.saveArray("ServerConfigs.json", items: withoutPasswords) + } + + func migrateLogs() { + + let pers = self.persistence + (pers.filesInFolder(pers.folderForIntention(.Reading)) ?? []) + .map { $0.lastPathComponent ?? "" } + .filter { $0.hasSuffix("log") } + .forEach { + pers.copyFileToFolder($0, folder: "Logs") + pers.deleteFile($0) + } + } +} + diff --git a/BuildaKit/ProjectConfig.swift b/BuildaKit/ProjectConfig.swift index 7727928..15eee32 100644 --- a/BuildaKit/ProjectConfig.swift +++ b/BuildaKit/ProjectConfig.swift @@ -13,16 +13,17 @@ public struct ProjectConfig { public let id: RefType public var url: String - public var githubToken: String public var privateSSHKeyPath: String public var publicSSHKeyPath: String - public var sshPassphrase: String? + + public var sshPassphrase: String? //loaded from the keychain + public var serverAuthentication: String? //loaded from the keychain //creates a new default ProjectConfig public init() { self.id = Ref.new() self.url = "" - self.githubToken = "" + self.serverAuthentication = "" self.privateSSHKeyPath = "" self.publicSSHKeyPath = "" self.sshPassphrase = nil @@ -36,10 +37,8 @@ public struct ProjectConfig { private struct Keys { static let URL = "url" - static let GitHubToken = "github_token" static let PrivateSSHKeyPath = "ssh_private_key_url" static let PublicSSHKeyPath = "ssh_public_key_url" - static let SSHPassphrase = "ssh_passphrase" static let Id = "id" } @@ -50,22 +49,18 @@ extension ProjectConfig: JSONSerializable { let json = NSMutableDictionary() json[Keys.URL] = self.url - json[Keys.GitHubToken] = self.githubToken json[Keys.PrivateSSHKeyPath] = self.privateSSHKeyPath json[Keys.PublicSSHKeyPath] = self.publicSSHKeyPath json[Keys.Id] = self.id - json.optionallyAddValueForKey(self.sshPassphrase, key: "ssh_passphrase") return json } public init(json: NSDictionary) throws { self.url = try json.get(Keys.URL) - self.githubToken = try json.get(Keys.GitHubToken) self.privateSSHKeyPath = try json.get(Keys.PrivateSSHKeyPath) self.publicSSHKeyPath = try json.get(Keys.PublicSSHKeyPath) self.id = try json.get(Keys.Id) - self.sshPassphrase = try json.getOptionally(Keys.SSHPassphrase) } } diff --git a/BuildaKit/SecurePersistence.swift b/BuildaKit/SecurePersistence.swift new file mode 100644 index 0000000..b55e744 --- /dev/null +++ b/BuildaKit/SecurePersistence.swift @@ -0,0 +1,57 @@ +// +// SecurePersistence.swift +// Buildasaur +// +// Created by Honza Dvorsky on 1/22/16. +// Copyright © 2016 Honza Dvorsky. All rights reserved. +// + +import Foundation +import KeychainAccess +import XcodeServerSDK + +final class SecurePersistence { + + static let Prefix = "com.honzadvorsky.buildasaur" + + static func xcodeServerPasswordKeychain() -> Keychain { + let keychain = Keychain(service: "\(Prefix).xcs.password") + return keychain + } + + static func sourceServerTokenKeychain() -> Keychain { + let keychain = Keychain(service: "\(Prefix).source_server.oauth_tokens") + return keychain + } + + static func sourceServerPassphraseKeychain() -> Keychain { + let keychain = Keychain(service: "\(Prefix).source_server.passphrase") + return keychain + } +} + +public protocol KeychainSaveable { + func keychainKey() -> String +} + +extension XcodeServerConfig: KeychainSaveable { + public func keychainKey() -> String { + return "\(self.host):\(self.user ?? "")" + } +} + +extension ProjectConfig: KeychainSaveable { + public func keychainKey() -> String { + return self.id + } +} + +extension Keychain { + + func updateIfNeeded(key: String, value: String?) { + if self[key] != value { + self[key] = value + } + } +} + diff --git a/BuildaKit/StorageManager.swift b/BuildaKit/StorageManager.swift index 0c1a6c6..790f033 100644 --- a/BuildaKit/StorageManager.swift +++ b/BuildaKit/StorageManager.swift @@ -166,10 +166,27 @@ public class StorageManager { private func loadAllFromPersistence() { self.config.value = self.persistence.loadDictionaryFromFile("Config.json") ?? [:] + let allProjects: [ProjectConfig] = self.persistence.loadArrayFromFile("Projects.json") ?? [] - self.projectConfigs.value = allProjects.dictionarifyWithKey { $0.id } + //load server tokens from keychain + let projectConfigKeychain = SecurePersistence.sourceServerTokenKeychain() + self.projectConfigs.value = allProjects + .map { + (var p: ProjectConfig) -> ProjectConfig in + p.serverAuthentication = projectConfigKeychain[p.keychainKey()] + return p + }.dictionarifyWithKey { $0.id } + let allServerConfigs: [XcodeServerConfig] = self.persistence.loadArrayFromFile("ServerConfigs.json") ?? [] - self.serverConfigs.value = allServerConfigs.dictionarifyWithKey { $0.id } + //load xcs passwords from keychain + let xcsConfigKeychain = SecurePersistence.xcodeServerPasswordKeychain() + self.serverConfigs.value = allServerConfigs + .map { + (var x: XcodeServerConfig) -> XcodeServerConfig in + x.password = xcsConfigKeychain[x.keychainKey()] + return x + }.dictionarifyWithKey { $0.id } + let allTemplates: [BuildTemplate] = self.persistence.loadArrayFromFolder("BuildTemplates") ?? [] self.buildTemplates.value = allTemplates.dictionarifyWithKey { $0.id } let allTriggers: [TriggerConfig] = self.persistence.loadArrayFromFolder("Triggers") ?? [] @@ -210,11 +227,19 @@ public class StorageManager { private func saveProjectConfigs(configs: [String: ProjectConfig]) { let projectConfigs: NSArray = Array(configs.values).map { $0.jsonify() } + let projectConfigKeychain = SecurePersistence.sourceServerTokenKeychain() + configs.values.forEach { + projectConfigKeychain.updateIfNeeded($0.keychainKey(), value: $0.serverAuthentication) + } self.persistence.saveArray("Projects.json", items: projectConfigs) } private func saveServerConfigs(configs: [String: XcodeServerConfig]) { let serverConfigs = Array(configs.values).map { $0.jsonify() } + let serverConfigKeychain = SecurePersistence.sourceServerTokenKeychain() + configs.values.forEach { + serverConfigKeychain.updateIfNeeded($0.keychainKey(), value: $0.password) + } self.persistence.saveArray("ServerConfigs.json", items: serverConfigs) } diff --git a/BuildaKit/SyncerFactory.swift b/BuildaKit/SyncerFactory.swift index 22454b4..7b1e862 100644 --- a/BuildaKit/SyncerFactory.swift +++ b/BuildaKit/SyncerFactory.swift @@ -31,7 +31,8 @@ public class SyncerFactory: SyncerFactoryType { private func createSyncer(triplet: ConfigTriplet) -> HDGitHubXCBotSyncer? { let xcodeServer = self.createXcodeServer(triplet.server) - let sourceServer = self.createSourceServer(triplet.project.githubToken) //TODO: pull out as SourceServerOptions + //TODO: pull out authentication as SourceServerOptions + let sourceServer = self.createSourceServer(triplet.project.serverAuthentication ?? "") let maybeProject = self.createProject(triplet.project) let triggers = triplet.triggers.map { self.createTrigger($0) } diff --git a/BuildaKitTests/Migration/Buildasaur-format-2-example2/BuildTemplates/9B53CC35-57DA-4DA0-9B85-05FCF109512A.json b/BuildaKitTests/Migration/Buildasaur-format-2-example2/BuildTemplates/9B53CC35-57DA-4DA0-9B85-05FCF109512A.json new file mode 100644 index 0000000..c0064b6 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-2-example2/BuildTemplates/9B53CC35-57DA-4DA0-9B85-05FCF109512A.json @@ -0,0 +1,26 @@ +{ + "id" : "9B53CC35-57DA-4DA0-9B85-05FCF109512A", + "project_name" : "Buildasaur", + "schedule" : { + "weeklyScheduleDay" : 0, + "periodicScheduleInterval" : 0, + "hourOfIntegration" : 0, + "minutesAfterHourToIntegrate" : 0, + "scheduleType" : 3 + }, + "triggers" : [ + "E8F5285A-A262-4630-AF7B-236772B75760", + "4E66D0D5-D5CC-417E-A40E-73B513CE4E10" + ], + "should_analyze" : true, + "platform_type" : "com.apple.platform.macosx", + "scheme" : "Buildasaur", + "device_filter" : 0, + "cleaning_policy" : 0, + "testing_devices" : [ + + ], + "should_archive" : false, + "name" : "Buildasaur PR", + "should_test" : true +} \ No newline at end of file diff --git a/BuildaKitTests/Migration/Buildasaur-format-2-example2/BuildTemplates/B31C6530-BB84-4A93-B08C-54074AAB5F37.json b/BuildaKitTests/Migration/Buildasaur-format-2-example2/BuildTemplates/B31C6530-BB84-4A93-B08C-54074AAB5F37.json new file mode 100644 index 0000000..8d6a805 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-2-example2/BuildTemplates/B31C6530-BB84-4A93-B08C-54074AAB5F37.json @@ -0,0 +1,26 @@ +{ + "id" : "B31C6530-BB84-4A93-B08C-54074AAB5F37", + "project_name" : "Buildasaur-Tester", + "schedule" : { + "weeklyScheduleDay" : 0, + "periodicScheduleInterval" : 0, + "hourOfIntegration" : 0, + "minutesAfterHourToIntegrate" : 0, + "scheduleType" : 3 + }, + "triggers" : [ + "E8F5285A-A262-4630-AF7B-236772B75760", + "4E66D0D5-D5CC-417E-A40E-73B513CE4E10" + ], + "should_analyze" : true, + "platform_type" : "com.apple.platform.iphoneos", + "scheme" : "Buildasaur-Tester", + "device_filter" : 0, + "cleaning_policy" : 0, + "testing_devices" : [ + + ], + "should_archive" : false, + "name" : "BuildaTest PR", + "should_test" : true +} \ No newline at end of file diff --git a/BuildaKitTests/Migration/Buildasaur-format-2-example2/Builda.log b/BuildaKitTests/Migration/Buildasaur-format-2-example2/Builda.log new file mode 100644 index 0000000..253ec35 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-2-example2/Builda.log @@ -0,0 +1,89 @@ +* +* +* + ____ _ _ _ +| _ \ (_) | | | +| |_) |_ _ _| | __| | __ _ ___ __ _ _ _ _ __ +| _ <| | | | | |/ _` |/ _` / __|/ _` | | | | '__| +| |_) | |_| | | | (_| | (_| \__ \ (_| | |_| | | +|____/ \__,_|_|_|\__,_|\__,_|___/\__,_|\__,_|_| + +Buildasaur 0.5.1 launched at 2015-10-12 13:57:46 +0000. +* +* +* + +[INFO]: Will send anonymous heartbeat. To opt out add `"heartbeat_opt_out" = true` to ~/Library/Application Support/Buildasaur/Config.json +[INFO]: Sending heartbeat event ["event_type": launch] +[INFO]: Sending heartbeat event ["event_type": heartbeat, "uptime": 0.03319501876831055, "running_syncers": 0] +[INFO]: Project: file:///Users/honzadvorsky/Documents/Buildasaur-Tester/Buildasaur-Tester.xcodeproj +[VERBOSE]: Finished fetching devices +[INFO]: Key: Optional(file:///Users/honzadvorsky/.ssh/id_rsa.pub) +[INFO]: Key: Optional(file:///Users/honzadvorsky/.ssh/id_rsa) + +------------------------------------ + +[INFO]: Sync starting at 2015-10-12 14:00:27 +0000 +[VERBOSE]: Resolving prs: + PR 9: test change [czechboy0-patch-6 -> master] + PR 8: Update README.md PR2 [czechboy0-patch-3 -> czechboy0-patch-2] + PR 6: Update ViewController.swift [czechboy0-patch-4 -> master] + PR 4: Update README.md [czechboy0-patch-2 -> master] +and branches: + +and bots: + Bot Buildasaur Bot + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #6 + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #9 + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #4 + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #8 + Bot BuildaBot [czechboy0/XcodeServerSDK] PR #117 + Bot BuildaBot [czechboy0/XcodeServerSDK] PR #106 + Bot BuildaBot [czechboy0/Buildasaur] PR #138 + Bot BuildaBot [czechboy0/XcodeServerSDK] |-> master + Bot BuildaBot [czechboy0/Buildasaur] |-> master + Bot Buildasaur-TestProject-iOS Bot +[VERBOSE]: SyncPair PR (9:czechboy0-patch-6) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #9) finished sync after 0.388 seconds. +[VERBOSE]: SyncPair PR (6:czechboy0-patch-4) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #6) finished sync after 0.702 seconds. +[VERBOSE]: SyncPair PR (4:czechboy0-patch-2) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #4) finished sync after 0.718 seconds. +[VERBOSE]: SyncPair PR (8:czechboy0-patch-3) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #8) finished sync after 1.156 seconds. +[INFO]: GitHub Rate Limit: count: 0/5000, renews in 3598 seconds, rate: 0.0/1.38, using 0.0% of the allowed request rate. +[INFO]: Sync finished successfully at 2015-10-12 14:00:29 +0000, took 1.59 seconds. + +------------------------------------ + +[INFO]: Sync starting at 2015-10-12 14:00:34 +0000 +[VERBOSE]: Resolving prs: + PR 9: test change [czechboy0-patch-6 -> master] + PR 8: Update README.md PR2 [czechboy0-patch-3 -> czechboy0-patch-2] + PR 6: Update ViewController.swift [czechboy0-patch-4 -> master] + PR 4: Update README.md [czechboy0-patch-2 -> master] +and branches: + Branch [almost-master:5b1d734f0170583b54fa749ff9bc47670abd66ad] + Branch [czechboy0-patch-1:ddb1e4840e35a611c2627168d8330ddf80c569d9] + Branch [czechboy0-patch-2:26cf7e9a027579f057aceaa9b09625317d31efde] + Branch [czechboy0-patch-3:ef2d5e2e0755bdff3945134e9a160d86a0698fe2] + Branch [czechboy0-patch-4:603e18851f66ee66a1223ea65e5f1e18e4353664] + Branch [czechboy0-patch-5:c1da8cd5cc61c62f8700766314ba26c7f03ea9ab] + Branch [czechboy0-patch-6:2ab73752fe7ed2d8f67187d7ba0552126a158de1] + Branch [master:510352edd9cdabd45f608e4327a8dd48ac517230] +and bots: + Bot Buildasaur Bot + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #6 + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #9 + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #4 + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #8 + Bot BuildaBot [czechboy0/XcodeServerSDK] PR #117 + Bot BuildaBot [czechboy0/XcodeServerSDK] PR #106 + Bot BuildaBot [czechboy0/Buildasaur] PR #138 + Bot BuildaBot [czechboy0/XcodeServerSDK] |-> master + Bot BuildaBot [czechboy0/Buildasaur] |-> master + Bot Buildasaur-TestProject-iOS Bot +[VERBOSE]: SyncPair PR (4:czechboy0-patch-2) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #4) finished sync after 0.068 seconds. +[VERBOSE]: SyncPair PR (6:czechboy0-patch-4) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #6) finished sync after 0.165 seconds. +[VERBOSE]: SyncPair PR (8:czechboy0-patch-3) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #8) finished sync after 0.186 seconds. +[VERBOSE]: SyncPair PR (9:czechboy0-patch-6) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #9) finished sync after 0.191 seconds. +[INFO]: Successfully created bot BuildaBot [czechboy0/Buildasaur-Tester] |-> master +[VERBOSE]: SyncPair Branch (master) + No Bot finished sync after 3.97 seconds. +[INFO]: GitHub Rate Limit: count: 0/5000, renews in 3593 seconds, rate: 0.0/1.38, using 0.0% of the allowed request rate. +[INFO]: Sync finished successfully at 2015-10-12 14:00:38 +0000, took 4.027 seconds. diff --git a/BuildaKitTests/Migration/Buildasaur-format-2-example2/Config.json b/BuildaKitTests/Migration/Buildasaur-format-2-example2/Config.json new file mode 100644 index 0000000..2a71818 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-2-example2/Config.json @@ -0,0 +1,3 @@ +{ + "persistence_version" : 2 +} \ No newline at end of file diff --git a/BuildaKitTests/Migration/Buildasaur-format-2-example2/Projects.json b/BuildaKitTests/Migration/Buildasaur-format-2-example2/Projects.json new file mode 100644 index 0000000..ad48e69 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-2-example2/Projects.json @@ -0,0 +1,18 @@ +[ + { + "ssh_public_key_url" : "\/Users\/honzadvorsky\/.ssh\/id_rsa.pub", + "id" : "4E8E7708-01FB-448A-B929-A54887CC5857", + "url" : "\/Users\/honzadvorsky\/Documents\/Buildasaur-Tester\/Buildasaur-Tester.xcodeproj", + "github_token" : "example_token-123456786543456765432", + "ssh_private_key_url" : "\/Users\/honzadvorsky\/.ssh\/id_rsa", + "ssh_passphrase": "my_passphrase_much_secret" + }, + { + "ssh_public_key_url" : "\/Users\/honzadvorsky\/.ssh\/id_rsa.pub", + "id" : "D316F062-7FEC-497A-B20E-6776AA413009", + "url" : "\/Users\/honzadvorsky\/Documents\/Buildasaur\/Buildasaur.xcodeproj", + "github_token" : "example_token-44446786543456765432", + "ssh_private_key_url" : "\/Users\/honzadvorsky\/.ssh\/id_rsa", + "ssh_passphrase": "my_passphrase_much_secret_2" + } +] \ No newline at end of file diff --git a/BuildaKitTests/Migration/Buildasaur-format-2-example2/ServerConfigs.json b/BuildaKitTests/Migration/Buildasaur-format-2-example2/ServerConfigs.json new file mode 100644 index 0000000..7f621f0 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-2-example2/ServerConfigs.json @@ -0,0 +1,14 @@ +[ + { + "password" : "SuperSecretPa55word", + "id" : "D143B09C-CB1B-4831-8BA1-E2F8AB039B56", + "host" : "https:\/\/127.0.0.1", + "user" : "testadmin" + }, + { + "password" : "SuperSecretPa55word77878787", + "id" : "76826238-64AE-4F48-B4B8-52A6B7A65EE4", + "host" : "https:\/\/localhost", + "user" : "testadmin8" + } +] \ No newline at end of file diff --git a/BuildaKitTests/Migration/Buildasaur-format-2-example2/Syncers.json b/BuildaKitTests/Migration/Buildasaur-format-2-example2/Syncers.json new file mode 100644 index 0000000..c75f4f4 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-2-example2/Syncers.json @@ -0,0 +1,26 @@ +[ + { + "preferred_template_ref" : "B31C6530-BB84-4A93-B08C-54074AAB5F37", + "id" : "564C267D-FF06-4008-9EF6-66B3AC1A3BDE", + "sync_interval" : 19, + "server_ref" : "D143B09C-CB1B-4831-8BA1-E2F8AB039B56", + "wait_for_lttm" : false, + "watched_branches" : [ + "master" + ], + "project_ref" : "4E8E7708-01FB-448A-B929-A54887CC5857", + "post_status_comments" : false + }, + { + "preferred_template_ref" : "9B53CC35-57DA-4DA0-9B85-05FCF109512A", + "id" : "B3A35C28-2176-4D88-8F60-5C769AEDBB2E", + "sync_interval" : 15, + "server_ref" : "76826238-64AE-4F48-B4B8-52A6B7A65EE4", + "wait_for_lttm" : false, + "watched_branches" : [ + "master" + ], + "project_ref" : "D316F062-7FEC-497A-B20E-6776AA413009", + "post_status_comments" : false + } +] \ No newline at end of file diff --git a/BuildaKitTests/Migration/Buildasaur-format-2-example2/Triggers/4E66D0D5-D5CC-417E-A40E-73B513CE4E10.json b/BuildaKitTests/Migration/Buildasaur-format-2-example2/Triggers/4E66D0D5-D5CC-417E-A40E-73B513CE4E10.json new file mode 100644 index 0000000..b480ff1 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-2-example2/Triggers/4E66D0D5-D5CC-417E-A40E-73B513CE4E10.json @@ -0,0 +1,25 @@ +{ + "phase" : 2, + "id" : "4E66D0D5-D5CC-417E-A40E-73B513CE4E10", + "scriptBody" : "", + "conditions" : { + "status" : 2, + "onWarnings" : true, + "onBuildErrors" : true, + "onInternalErrors" : true, + "onAnalyzerWarnings" : true, + "onFailingTests" : true, + "onSuccess" : true + }, + "type" : 2, + "name" : "Postbuild Email", + "emailConfiguration" : { + "includeCommitMessages" : true, + "additionalRecipients" : [ + "h@d.com", + "yo@ma.lo" + ], + "emailCommitters" : false, + "includeIssueDetails" : true + } +} \ No newline at end of file diff --git a/BuildaKitTests/Migration/Buildasaur-format-2-example2/Triggers/E8F5285A-A262-4630-AF7B-236772B75760.json b/BuildaKitTests/Migration/Buildasaur-format-2-example2/Triggers/E8F5285A-A262-4630-AF7B-236772B75760.json new file mode 100644 index 0000000..5c263b7 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-2-example2/Triggers/E8F5285A-A262-4630-AF7B-236772B75760.json @@ -0,0 +1,7 @@ +{ + "phase" : 1, + "scriptBody" : "echo \"hello\"\n", + "id" : "E8F5285A-A262-4630-AF7B-236772B75760", + "type" : 1, + "name" : "Prebuild Script" +} \ No newline at end of file diff --git a/BuildaKitTests/Migration/Buildasaur-format-3-example1/BuildTemplates/9B53CC35-57DA-4DA0-9B85-05FCF109512A.json b/BuildaKitTests/Migration/Buildasaur-format-3-example1/BuildTemplates/9B53CC35-57DA-4DA0-9B85-05FCF109512A.json new file mode 100644 index 0000000..c0064b6 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-3-example1/BuildTemplates/9B53CC35-57DA-4DA0-9B85-05FCF109512A.json @@ -0,0 +1,26 @@ +{ + "id" : "9B53CC35-57DA-4DA0-9B85-05FCF109512A", + "project_name" : "Buildasaur", + "schedule" : { + "weeklyScheduleDay" : 0, + "periodicScheduleInterval" : 0, + "hourOfIntegration" : 0, + "minutesAfterHourToIntegrate" : 0, + "scheduleType" : 3 + }, + "triggers" : [ + "E8F5285A-A262-4630-AF7B-236772B75760", + "4E66D0D5-D5CC-417E-A40E-73B513CE4E10" + ], + "should_analyze" : true, + "platform_type" : "com.apple.platform.macosx", + "scheme" : "Buildasaur", + "device_filter" : 0, + "cleaning_policy" : 0, + "testing_devices" : [ + + ], + "should_archive" : false, + "name" : "Buildasaur PR", + "should_test" : true +} \ No newline at end of file diff --git a/BuildaKitTests/Migration/Buildasaur-format-3-example1/BuildTemplates/B31C6530-BB84-4A93-B08C-54074AAB5F37.json b/BuildaKitTests/Migration/Buildasaur-format-3-example1/BuildTemplates/B31C6530-BB84-4A93-B08C-54074AAB5F37.json new file mode 100644 index 0000000..8d6a805 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-3-example1/BuildTemplates/B31C6530-BB84-4A93-B08C-54074AAB5F37.json @@ -0,0 +1,26 @@ +{ + "id" : "B31C6530-BB84-4A93-B08C-54074AAB5F37", + "project_name" : "Buildasaur-Tester", + "schedule" : { + "weeklyScheduleDay" : 0, + "periodicScheduleInterval" : 0, + "hourOfIntegration" : 0, + "minutesAfterHourToIntegrate" : 0, + "scheduleType" : 3 + }, + "triggers" : [ + "E8F5285A-A262-4630-AF7B-236772B75760", + "4E66D0D5-D5CC-417E-A40E-73B513CE4E10" + ], + "should_analyze" : true, + "platform_type" : "com.apple.platform.iphoneos", + "scheme" : "Buildasaur-Tester", + "device_filter" : 0, + "cleaning_policy" : 0, + "testing_devices" : [ + + ], + "should_archive" : false, + "name" : "BuildaTest PR", + "should_test" : true +} \ No newline at end of file diff --git a/BuildaKitTests/Migration/Buildasaur-format-3-example1/Config.json b/BuildaKitTests/Migration/Buildasaur-format-3-example1/Config.json new file mode 100644 index 0000000..906c841 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-3-example1/Config.json @@ -0,0 +1,3 @@ +{ + "persistence_version" : 3 +} \ No newline at end of file diff --git a/BuildaKitTests/Migration/Buildasaur-format-3-example1/Logs/Builda.log b/BuildaKitTests/Migration/Buildasaur-format-3-example1/Logs/Builda.log new file mode 100644 index 0000000..253ec35 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-3-example1/Logs/Builda.log @@ -0,0 +1,89 @@ +* +* +* + ____ _ _ _ +| _ \ (_) | | | +| |_) |_ _ _| | __| | __ _ ___ __ _ _ _ _ __ +| _ <| | | | | |/ _` |/ _` / __|/ _` | | | | '__| +| |_) | |_| | | | (_| | (_| \__ \ (_| | |_| | | +|____/ \__,_|_|_|\__,_|\__,_|___/\__,_|\__,_|_| + +Buildasaur 0.5.1 launched at 2015-10-12 13:57:46 +0000. +* +* +* + +[INFO]: Will send anonymous heartbeat. To opt out add `"heartbeat_opt_out" = true` to ~/Library/Application Support/Buildasaur/Config.json +[INFO]: Sending heartbeat event ["event_type": launch] +[INFO]: Sending heartbeat event ["event_type": heartbeat, "uptime": 0.03319501876831055, "running_syncers": 0] +[INFO]: Project: file:///Users/honzadvorsky/Documents/Buildasaur-Tester/Buildasaur-Tester.xcodeproj +[VERBOSE]: Finished fetching devices +[INFO]: Key: Optional(file:///Users/honzadvorsky/.ssh/id_rsa.pub) +[INFO]: Key: Optional(file:///Users/honzadvorsky/.ssh/id_rsa) + +------------------------------------ + +[INFO]: Sync starting at 2015-10-12 14:00:27 +0000 +[VERBOSE]: Resolving prs: + PR 9: test change [czechboy0-patch-6 -> master] + PR 8: Update README.md PR2 [czechboy0-patch-3 -> czechboy0-patch-2] + PR 6: Update ViewController.swift [czechboy0-patch-4 -> master] + PR 4: Update README.md [czechboy0-patch-2 -> master] +and branches: + +and bots: + Bot Buildasaur Bot + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #6 + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #9 + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #4 + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #8 + Bot BuildaBot [czechboy0/XcodeServerSDK] PR #117 + Bot BuildaBot [czechboy0/XcodeServerSDK] PR #106 + Bot BuildaBot [czechboy0/Buildasaur] PR #138 + Bot BuildaBot [czechboy0/XcodeServerSDK] |-> master + Bot BuildaBot [czechboy0/Buildasaur] |-> master + Bot Buildasaur-TestProject-iOS Bot +[VERBOSE]: SyncPair PR (9:czechboy0-patch-6) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #9) finished sync after 0.388 seconds. +[VERBOSE]: SyncPair PR (6:czechboy0-patch-4) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #6) finished sync after 0.702 seconds. +[VERBOSE]: SyncPair PR (4:czechboy0-patch-2) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #4) finished sync after 0.718 seconds. +[VERBOSE]: SyncPair PR (8:czechboy0-patch-3) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #8) finished sync after 1.156 seconds. +[INFO]: GitHub Rate Limit: count: 0/5000, renews in 3598 seconds, rate: 0.0/1.38, using 0.0% of the allowed request rate. +[INFO]: Sync finished successfully at 2015-10-12 14:00:29 +0000, took 1.59 seconds. + +------------------------------------ + +[INFO]: Sync starting at 2015-10-12 14:00:34 +0000 +[VERBOSE]: Resolving prs: + PR 9: test change [czechboy0-patch-6 -> master] + PR 8: Update README.md PR2 [czechboy0-patch-3 -> czechboy0-patch-2] + PR 6: Update ViewController.swift [czechboy0-patch-4 -> master] + PR 4: Update README.md [czechboy0-patch-2 -> master] +and branches: + Branch [almost-master:5b1d734f0170583b54fa749ff9bc47670abd66ad] + Branch [czechboy0-patch-1:ddb1e4840e35a611c2627168d8330ddf80c569d9] + Branch [czechboy0-patch-2:26cf7e9a027579f057aceaa9b09625317d31efde] + Branch [czechboy0-patch-3:ef2d5e2e0755bdff3945134e9a160d86a0698fe2] + Branch [czechboy0-patch-4:603e18851f66ee66a1223ea65e5f1e18e4353664] + Branch [czechboy0-patch-5:c1da8cd5cc61c62f8700766314ba26c7f03ea9ab] + Branch [czechboy0-patch-6:2ab73752fe7ed2d8f67187d7ba0552126a158de1] + Branch [master:510352edd9cdabd45f608e4327a8dd48ac517230] +and bots: + Bot Buildasaur Bot + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #6 + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #9 + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #4 + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #8 + Bot BuildaBot [czechboy0/XcodeServerSDK] PR #117 + Bot BuildaBot [czechboy0/XcodeServerSDK] PR #106 + Bot BuildaBot [czechboy0/Buildasaur] PR #138 + Bot BuildaBot [czechboy0/XcodeServerSDK] |-> master + Bot BuildaBot [czechboy0/Buildasaur] |-> master + Bot Buildasaur-TestProject-iOS Bot +[VERBOSE]: SyncPair PR (4:czechboy0-patch-2) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #4) finished sync after 0.068 seconds. +[VERBOSE]: SyncPair PR (6:czechboy0-patch-4) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #6) finished sync after 0.165 seconds. +[VERBOSE]: SyncPair PR (8:czechboy0-patch-3) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #8) finished sync after 0.186 seconds. +[VERBOSE]: SyncPair PR (9:czechboy0-patch-6) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #9) finished sync after 0.191 seconds. +[INFO]: Successfully created bot BuildaBot [czechboy0/Buildasaur-Tester] |-> master +[VERBOSE]: SyncPair Branch (master) + No Bot finished sync after 3.97 seconds. +[INFO]: GitHub Rate Limit: count: 0/5000, renews in 3593 seconds, rate: 0.0/1.38, using 0.0% of the allowed request rate. +[INFO]: Sync finished successfully at 2015-10-12 14:00:38 +0000, took 4.027 seconds. diff --git a/BuildaKitTests/Migration/Buildasaur-format-3-example1/Projects.json b/BuildaKitTests/Migration/Buildasaur-format-3-example1/Projects.json new file mode 100644 index 0000000..a82d7e1 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-3-example1/Projects.json @@ -0,0 +1,14 @@ +[ + { + "ssh_public_key_url" : "\/Users\/honzadvorsky\/.ssh\/id_rsa.pub", + "id" : "4E8E7708-01FB-448A-B929-A54887CC5857", + "url" : "\/Users\/honzadvorsky\/Documents\/Buildasaur-Tester\/Buildasaur-Tester.xcodeproj", + "ssh_private_key_url" : "\/Users\/honzadvorsky\/.ssh\/id_rsa" + }, + { + "ssh_public_key_url" : "\/Users\/honzadvorsky\/.ssh\/id_rsa.pub", + "id" : "D316F062-7FEC-497A-B20E-6776AA413009", + "url" : "\/Users\/honzadvorsky\/Documents\/Buildasaur\/Buildasaur.xcodeproj", + "ssh_private_key_url" : "\/Users\/honzadvorsky\/.ssh\/id_rsa" + } +] \ No newline at end of file diff --git a/BuildaKitTests/Migration/Buildasaur-format-3-example1/ServerConfigs.json b/BuildaKitTests/Migration/Buildasaur-format-3-example1/ServerConfigs.json new file mode 100644 index 0000000..9750701 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-3-example1/ServerConfigs.json @@ -0,0 +1,12 @@ +[ + { + "id" : "D143B09C-CB1B-4831-8BA1-E2F8AB039B56", + "host" : "https:\/\/127.0.0.1", + "user" : "testadmin" + }, + { + "id" : "76826238-64AE-4F48-B4B8-52A6B7A65EE4", + "host" : "https:\/\/localhost", + "user" : "testadmin8" + } +] \ No newline at end of file diff --git a/BuildaKitTests/Migration/Buildasaur-format-3-example1/Syncers.json b/BuildaKitTests/Migration/Buildasaur-format-3-example1/Syncers.json new file mode 100644 index 0000000..c75f4f4 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-3-example1/Syncers.json @@ -0,0 +1,26 @@ +[ + { + "preferred_template_ref" : "B31C6530-BB84-4A93-B08C-54074AAB5F37", + "id" : "564C267D-FF06-4008-9EF6-66B3AC1A3BDE", + "sync_interval" : 19, + "server_ref" : "D143B09C-CB1B-4831-8BA1-E2F8AB039B56", + "wait_for_lttm" : false, + "watched_branches" : [ + "master" + ], + "project_ref" : "4E8E7708-01FB-448A-B929-A54887CC5857", + "post_status_comments" : false + }, + { + "preferred_template_ref" : "9B53CC35-57DA-4DA0-9B85-05FCF109512A", + "id" : "B3A35C28-2176-4D88-8F60-5C769AEDBB2E", + "sync_interval" : 15, + "server_ref" : "76826238-64AE-4F48-B4B8-52A6B7A65EE4", + "wait_for_lttm" : false, + "watched_branches" : [ + "master" + ], + "project_ref" : "D316F062-7FEC-497A-B20E-6776AA413009", + "post_status_comments" : false + } +] \ No newline at end of file diff --git a/BuildaKitTests/Migration/Buildasaur-format-3-example1/Triggers/4E66D0D5-D5CC-417E-A40E-73B513CE4E10.json b/BuildaKitTests/Migration/Buildasaur-format-3-example1/Triggers/4E66D0D5-D5CC-417E-A40E-73B513CE4E10.json new file mode 100644 index 0000000..b480ff1 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-3-example1/Triggers/4E66D0D5-D5CC-417E-A40E-73B513CE4E10.json @@ -0,0 +1,25 @@ +{ + "phase" : 2, + "id" : "4E66D0D5-D5CC-417E-A40E-73B513CE4E10", + "scriptBody" : "", + "conditions" : { + "status" : 2, + "onWarnings" : true, + "onBuildErrors" : true, + "onInternalErrors" : true, + "onAnalyzerWarnings" : true, + "onFailingTests" : true, + "onSuccess" : true + }, + "type" : 2, + "name" : "Postbuild Email", + "emailConfiguration" : { + "includeCommitMessages" : true, + "additionalRecipients" : [ + "h@d.com", + "yo@ma.lo" + ], + "emailCommitters" : false, + "includeIssueDetails" : true + } +} \ No newline at end of file diff --git a/BuildaKitTests/Migration/Buildasaur-format-3-example1/Triggers/E8F5285A-A262-4630-AF7B-236772B75760.json b/BuildaKitTests/Migration/Buildasaur-format-3-example1/Triggers/E8F5285A-A262-4630-AF7B-236772B75760.json new file mode 100644 index 0000000..5c263b7 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-3-example1/Triggers/E8F5285A-A262-4630-AF7B-236772B75760.json @@ -0,0 +1,7 @@ +{ + "phase" : 1, + "scriptBody" : "echo \"hello\"\n", + "id" : "E8F5285A-A262-4630-AF7B-236772B75760", + "type" : 1, + "name" : "Prebuild Script" +} \ No newline at end of file diff --git a/BuildaKitTests/MigrationTests.swift b/BuildaKitTests/MigrationTests.swift index 8ffbd13..e856678 100644 --- a/BuildaKitTests/MigrationTests.swift +++ b/BuildaKitTests/MigrationTests.swift @@ -119,6 +119,25 @@ class MigrationTests: XCTestCase { } } + func testMigration_v2_v3() { + + let readingURL = self.resourceURLFromTestBundle("Buildasaur-format-2-example2") + let writingURL = self.writingURL("v2-v3") + let expectedURL = self.resourceURLFromTestBundle("Buildasaur-format-3-example1") + + let fileManager = NSFileManager.defaultManager() + + let persistence = Persistence(readingFolder: readingURL, writingFolder: writingURL, fileManager: fileManager) + let migrator = Migrator_v2_v3(persistence: persistence) + + do { + try migrator.attemptMigration() + try self.ensureEqualHierarchies(persistence, urlExpected: expectedURL, urlReal: writingURL) + } catch { + fail("\(error)") + } + } + func testPersistenceSetter() { let tmp = NSURL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) diff --git a/Buildasaur.xcodeproj/project.pbxproj b/Buildasaur.xcodeproj/project.pbxproj index 1f3087a..b22b5bf 100644 --- a/Buildasaur.xcodeproj/project.pbxproj +++ b/Buildasaur.xcodeproj/project.pbxproj @@ -130,6 +130,9 @@ 3AE0AB2B1BB991AB00A52D20 /* SyncerViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE0AB2A1BB991AB00A52D20 /* SyncerViewModel.swift */; }; 3AE4F6CE1BBC88450006CA1C /* RACUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE4F6CD1BBC88450006CA1C /* RACUtils.swift */; }; 3AE4F6D01BBC8D250006CA1C /* EmptyProjectViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AE4F6CF1BBC8D250006CA1C /* EmptyProjectViewController.swift */; }; + 3AE8B79B1C52DAD20033F6EF /* Buildasaur-format-2-example2 in Resources */ = {isa = PBXBuildFile; fileRef = 3AE8B79A1C52DAD20033F6EF /* Buildasaur-format-2-example2 */; }; + 3AED13131C529BB600E3B7FF /* Buildasaur-format-3-example1 in Resources */ = {isa = PBXBuildFile; fileRef = 3AED13121C529BB600E3B7FF /* Buildasaur-format-3-example1 */; }; + 3AED13151C52A1A300E3B7FF /* SecurePersistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AED13141C52A1A300E3B7FF /* SecurePersistence.swift */; }; 3AF090B81B1134AA0058567F /* BranchWatchingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF090B71B1134AA0058567F /* BranchWatchingViewController.swift */; }; 3AF1B1241AAC7CA500917EF3 /* SyncerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF1B1231AAC7CA500917EF3 /* SyncerViewController.swift */; }; 723E971AA98B199C70B7C3AA /* Pods_BuildaHeartbeatKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 406E01BAFEA2D2588BFD2D5A /* Pods_BuildaHeartbeatKit.framework */; }; @@ -354,6 +357,9 @@ 3AE0AB2A1BB991AB00A52D20 /* SyncerViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncerViewModel.swift; sourceTree = ""; }; 3AE4F6CD1BBC88450006CA1C /* RACUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RACUtils.swift; sourceTree = ""; }; 3AE4F6CF1BBC8D250006CA1C /* EmptyProjectViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EmptyProjectViewController.swift; sourceTree = ""; }; + 3AE8B79A1C52DAD20033F6EF /* Buildasaur-format-2-example2 */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "Buildasaur-format-2-example2"; path = "Migration/Buildasaur-format-2-example2"; sourceTree = ""; }; + 3AED13121C529BB600E3B7FF /* Buildasaur-format-3-example1 */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "Buildasaur-format-3-example1"; path = "Migration/Buildasaur-format-3-example1"; sourceTree = ""; }; + 3AED13141C52A1A300E3B7FF /* SecurePersistence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecurePersistence.swift; sourceTree = ""; }; 3AF090B71B1134AA0058567F /* BranchWatchingViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BranchWatchingViewController.swift; sourceTree = ""; }; 3AF1B1231AAC7CA500917EF3 /* SyncerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncerViewController.swift; sourceTree = ""; }; 406E01BAFEA2D2588BFD2D5A /* Pods_BuildaHeartbeatKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BuildaHeartbeatKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -634,9 +640,11 @@ 3A9D741E1BCBF86900DCA23C /* Migration */ = { isa = PBXGroup; children = ( - 3A9D74251BCC19BB00DCA23C /* Buildasaur-format-2-example1 */, - 3A9D74231BCC103E00DCA23C /* Buildasaur-format-1-example1 */, 3A9D74211BCC041600DCA23C /* Buildasaur-format-0-example1 */, + 3A9D74231BCC103E00DCA23C /* Buildasaur-format-1-example1 */, + 3A9D74251BCC19BB00DCA23C /* Buildasaur-format-2-example1 */, + 3AE8B79A1C52DAD20033F6EF /* Buildasaur-format-2-example2 */, + 3AED13121C529BB600E3B7FF /* Buildasaur-format-3-example1 */, 3A9D741F1BCBF87900DCA23C /* MigrationTests.swift */, ); name = Migration; @@ -748,6 +756,7 @@ 3ACBADE71B5ADE2A00204457 /* StorageManager.swift */, 3A9D741C1BCBDDA200DCA23C /* PersistenceMigrator.swift */, 3A395B541BCF007000BB6947 /* LoginItem.swift */, + 3AED13141C52A1A300E3B7FF /* SecurePersistence.swift */, ); name = Persistence; sourceTree = ""; @@ -1144,8 +1153,10 @@ buildActionMask = 2147483647; files = ( 3A9D74221BCC041600DCA23C /* Buildasaur-format-0-example1 in Resources */, + 3AED13131C529BB600E3B7FF /* Buildasaur-format-3-example1 in Resources */, 3ACBADDD1B5ADE1400204457 /* sampleFinishedIntegration.json in Resources */, 3A9D74241BCC103E00DCA23C /* Buildasaur-format-1-example1 in Resources */, + 3AE8B79B1C52DAD20033F6EF /* Buildasaur-format-2-example2 in Resources */, 3A9D74261BCC19BB00DCA23C /* Buildasaur-format-2-example1 in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1455,6 +1466,7 @@ 3A0FF5A41BBFF9FA00FB8051 /* ProjectConfig.swift in Sources */, 3ACBAE0E1B5ADE2A00204457 /* SyncPair_PR_NoBot.swift in Sources */, 3ACF93AA1BBEC7AB00CD888C /* XcodeScheme.swift in Sources */, + 3AED13151C52A1A300E3B7FF /* SecurePersistence.swift in Sources */, 3A28AF531BC984850011756E /* ConfigTriplet.swift in Sources */, 3ACBADFD1B5ADE2A00204457 /* CommonExtensions.swift in Sources */, 3ACBAE131B5ADE2A00204457 /* SyncPairResolver.swift in Sources */, diff --git a/Buildasaur/AppDelegate.swift b/Buildasaur/AppDelegate.swift index e519f7f..541bfd8 100644 --- a/Buildasaur/AppDelegate.swift +++ b/Buildasaur/AppDelegate.swift @@ -68,19 +68,19 @@ class AppDelegate: NSObject, NSApplicationDelegate { let migrator = CompositeMigrator(persistence: persistence) if migrator.isMigrationRequired() { - Log.info("Migration required, launching migrator") + print("Migration required, launching migrator") do { try migrator.attemptMigration() } catch { - Log.error("Migration failed with error \(error), wiping folder...") + print("Migration failed with error \(error), wiping folder...") //wipe the persistence. start over if we failed to migrate _ = try? fileManager.removeItemAtURL(persistence.readingFolder) } - Log.info("Migration finished") + print("Migration finished") } else { - Log.verbose("No migration necessary, skipping...") + print("No migration necessary, skipping...") } } @@ -88,12 +88,12 @@ class AppDelegate: NSObject, NSApplicationDelegate { let persistence = PersistenceFactory.createStandardPersistence() - //setup logging - Logging.setup(persistence, alsoIntoFile: true) - //migration self.migratePersistence(persistence) + //setup logging + Logging.setup(persistence, alsoIntoFile: true) + //create storage manager let storageManager = StorageManager(persistence: persistence) let factory = SyncerFactory() diff --git a/Buildasaur/ProjectViewController.swift b/Buildasaur/ProjectViewController.swift index 10eb200..cd3fee9 100644 --- a/Buildasaur/ProjectViewController.swift +++ b/Buildasaur/ProjectViewController.swift @@ -80,7 +80,7 @@ class ProjectViewController: ConfigEditViewController { //dump whenever config changes prod.startWithNext { [weak self] in - self?.tokenTextField.stringValue = $0.githubToken + self?.tokenTextField.stringValue = $0.serverAuthentication ?? "" let priv = $0.privateSSHKeyPath self?.privateKeyUrl.value = priv.isEmpty ? nil : NSURL(fileURLWithPath: priv) let pub = $0.publicSSHKeyPath @@ -178,12 +178,12 @@ class ProjectViewController: ConfigEditViewController { guard let privateKeyPath = self.privateKeyUrl.value?.path, let publicKeyPath = self.publicKeyUrl.value?.path, - let githubToken = self.tokenTextField.stringValue.nonEmpty() else { + let token = self.tokenTextField.stringValue.nonEmpty() else { return nil } var config = self.projectConfig.value - config.githubToken = githubToken + config.serverAuthentication = token config.sshPassphrase = sshPassphrase config.privateSSHKeyPath = privateKeyPath config.publicSSHKeyPath = publicKeyPath diff --git a/Podfile b/Podfile index 5379996..af6eb48 100644 --- a/Podfile +++ b/Podfile @@ -12,7 +12,7 @@ end def also_xcode_pods pods_for_errbody - pod 'XcodeServerSDK', '~> 0.5.5' + pod 'XcodeServerSDK', '~> 0.5.6' pod 'ekgclient', '~> 0.3.0' end @@ -33,6 +33,7 @@ end target 'BuildaKit' do buildasaur_app_pods + pod 'KeychainAccess' end target 'BuildaKitTests' do diff --git a/Podfile.lock b/Podfile.lock index 60b2b64..ae4bc9a 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -10,6 +10,7 @@ PODS: - Ji/Ji (1.2.0): - Ji/Ji-libxml - Ji/Ji-libxml (1.2.0) + - KeychainAccess (2.3.3) - Nimble (3.0.0) - ReactiveCocoa (4.0.0-RC.1): - ReactiveCocoa/UI (= 4.0.0-RC.1) @@ -24,7 +25,7 @@ PODS: - Result (~> 1.0) - Result (1.0.1) - SwiftSafe (0.1) - - XcodeServerSDK (0.5.5): + - XcodeServerSDK (0.5.6): - BuildaUtils (~> 0.2.3) DEPENDENCIES: @@ -32,9 +33,10 @@ DEPENDENCIES: - CryptoSwift - ekgclient (~> 0.3.0) - Ji (~> 1.2.0) + - KeychainAccess - Nimble (~> 3.0.0) - ReactiveCocoa (= 4.0.0-RC.1) - - XcodeServerSDK (~> 0.5.5) + - XcodeServerSDK (~> 0.5.6) SPEC CHECKSUMS: Alamofire: 39dddb7d3725d1771b1d2f7099c8bd45bd83ffbb @@ -42,10 +44,11 @@ SPEC CHECKSUMS: CryptoSwift: d382228d6301c09474132417878a741c2a2e68cd ekgclient: 40f5d347e2ede450b3e50ac7c6bd84d96e7b84ad Ji: ddebb22f9ac445db6e884b66f78ea74fb135fdb7 + KeychainAccess: 9b7e49e48277e329b1b3195e54697ed313d30acf Nimble: 4c353d43735b38b545cbb4cb91504588eb5de926 ReactiveCocoa: 2b0f654beb7642b82cfd3fdb845205ae9889422c Result: caef80340451e1f07492fa1e89117f83613bce24 SwiftSafe: 77ffd12b02678790bec1ef56a2d14ec5036f1fd6 - XcodeServerSDK: f56b521e016a1efd593dda649535eea254d41ec3 + XcodeServerSDK: 6300badc4a799da9ad6e970b366eda18d4a47bda COCOAPODS: 0.39.0 From 13aca9147eab68b8b12c87264b2a57a5c16c4d66 Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Fri, 22 Jan 2016 22:30:14 +0000 Subject: [PATCH 16/63] made keychain thread safe --- BuildaKit/PersistenceMigrator.swift | 12 ++-- BuildaKit/SecurePersistence.swift | 64 ++++++++++++++----- BuildaKit/StorageManager.swift | 8 +-- .../ServerConfigs.json | 2 +- .../ServerConfigs.json | 2 +- BuildaKitTests/MigrationTests.swift | 7 ++ 6 files changed, 67 insertions(+), 28 deletions(-) diff --git a/BuildaKit/PersistenceMigrator.swift b/BuildaKit/PersistenceMigrator.swift index ad01806..88d7fe4 100644 --- a/BuildaKit/PersistenceMigrator.swift +++ b/BuildaKit/PersistenceMigrator.swift @@ -327,13 +327,13 @@ class Migrator_v2_v3: MigratorType { d.removeObjectForKey("ssh_passphrase") let tokenKeychain = SecurePersistence.sourceServerTokenKeychain() - tokenKeychain[id] = token + tokenKeychain.writeIfNeeded(id, value: token) let passphraseKeychain = SecurePersistence.sourceServerPassphraseKeychain() - passphraseKeychain[id] = passphrase + passphraseKeychain.writeIfNeeded(id, value: passphrase) - precondition(tokenKeychain[id] == token, "Saved token must match") - precondition(passphraseKeychain[id] == passphrase, "Saved passphrase must match") + precondition(tokenKeychain.read(id) == token, "Saved token must match") + precondition(passphraseKeychain.read(id) == passphrase, "Saved passphrase must match") return d } @@ -354,11 +354,11 @@ class Migrator_v2_v3: MigratorType { let key = (try! XcodeServerConfig(json: d)).keychainKey() let keychain = SecurePersistence.xcodeServerPasswordKeychain() - keychain[key] = password + keychain.writeIfNeeded(key, value: password) d.removeObjectForKey("password") - precondition(keychain[key] == password, "Saved password must match") + precondition(keychain.read(key) == password, "Saved password must match") return d } diff --git a/BuildaKit/SecurePersistence.swift b/BuildaKit/SecurePersistence.swift index b55e744..406c261 100644 --- a/BuildaKit/SecurePersistence.swift +++ b/BuildaKit/SecurePersistence.swift @@ -9,25 +9,66 @@ import Foundation import KeychainAccess import XcodeServerSDK +import SwiftSafe final class SecurePersistence { - static let Prefix = "com.honzadvorsky.buildasaur" + #if TESTING + static let Prefix = "com.honzadvorsky.buildasaur.testing" + #else + static let Prefix = "com.honzadvorsky.buildasaur" + #endif - static func xcodeServerPasswordKeychain() -> Keychain { + static func xcodeServerPasswordKeychain() -> SecurePersistence { let keychain = Keychain(service: "\(Prefix).xcs.password") - return keychain + return self.init(keychain: keychain) } - static func sourceServerTokenKeychain() -> Keychain { + static func sourceServerTokenKeychain() -> SecurePersistence { let keychain = Keychain(service: "\(Prefix).source_server.oauth_tokens") - return keychain + return self.init(keychain: keychain) } - static func sourceServerPassphraseKeychain() -> Keychain { + static func sourceServerPassphraseKeychain() -> SecurePersistence { let keychain = Keychain(service: "\(Prefix).source_server.passphrase") - return keychain + return self.init(keychain: keychain) } + + private let keychain: Keychain + private let safe: Safe + + private init(keychain: Keychain, safe: Safe = CREW()) { + self.keychain = keychain + self.safe = safe + } + + func read(key: String) -> String? { + var val: String? + self.safe.read { + val = self.keychain[key] + } + return val + } + + func writeIfNeeded(key: String, value: String?) { + self.safe.write { + self.updateIfNeeded(key, value: value) + } + } + + private func updateIfNeeded(key: String, value: String?) { + if self.keychain[key] != value { + self.keychain[key] = value + } + } + + #if TESTING + func wipe() { + self.safe.write { + _ = try? self.keychain.removeAll() + } + } + #endif } public protocol KeychainSaveable { @@ -46,12 +87,3 @@ extension ProjectConfig: KeychainSaveable { } } -extension Keychain { - - func updateIfNeeded(key: String, value: String?) { - if self[key] != value { - self[key] = value - } - } -} - diff --git a/BuildaKit/StorageManager.swift b/BuildaKit/StorageManager.swift index 790f033..2707512 100644 --- a/BuildaKit/StorageManager.swift +++ b/BuildaKit/StorageManager.swift @@ -173,7 +173,7 @@ public class StorageManager { self.projectConfigs.value = allProjects .map { (var p: ProjectConfig) -> ProjectConfig in - p.serverAuthentication = projectConfigKeychain[p.keychainKey()] + p.serverAuthentication = projectConfigKeychain.read(p.keychainKey()) return p }.dictionarifyWithKey { $0.id } @@ -183,7 +183,7 @@ public class StorageManager { self.serverConfigs.value = allServerConfigs .map { (var x: XcodeServerConfig) -> XcodeServerConfig in - x.password = xcsConfigKeychain[x.keychainKey()] + x.password = xcsConfigKeychain.read(x.keychainKey()) return x }.dictionarifyWithKey { $0.id } @@ -229,7 +229,7 @@ public class StorageManager { let projectConfigs: NSArray = Array(configs.values).map { $0.jsonify() } let projectConfigKeychain = SecurePersistence.sourceServerTokenKeychain() configs.values.forEach { - projectConfigKeychain.updateIfNeeded($0.keychainKey(), value: $0.serverAuthentication) + projectConfigKeychain.writeIfNeeded($0.keychainKey(), value: $0.serverAuthentication) } self.persistence.saveArray("Projects.json", items: projectConfigs) } @@ -238,7 +238,7 @@ public class StorageManager { let serverConfigs = Array(configs.values).map { $0.jsonify() } let serverConfigKeychain = SecurePersistence.sourceServerTokenKeychain() configs.values.forEach { - serverConfigKeychain.updateIfNeeded($0.keychainKey(), value: $0.password) + serverConfigKeychain.writeIfNeeded($0.keychainKey(), value: $0.password) } self.persistence.saveArray("ServerConfigs.json", items: serverConfigs) } diff --git a/BuildaKitTests/Migration/Buildasaur-format-2-example2/ServerConfigs.json b/BuildaKitTests/Migration/Buildasaur-format-2-example2/ServerConfigs.json index 7f621f0..f222a99 100644 --- a/BuildaKitTests/Migration/Buildasaur-format-2-example2/ServerConfigs.json +++ b/BuildaKitTests/Migration/Buildasaur-format-2-example2/ServerConfigs.json @@ -3,7 +3,7 @@ "password" : "SuperSecretPa55word", "id" : "D143B09C-CB1B-4831-8BA1-E2F8AB039B56", "host" : "https:\/\/127.0.0.1", - "user" : "testadmin" + "user" : "testadmin1" }, { "password" : "SuperSecretPa55word77878787", diff --git a/BuildaKitTests/Migration/Buildasaur-format-3-example1/ServerConfigs.json b/BuildaKitTests/Migration/Buildasaur-format-3-example1/ServerConfigs.json index 9750701..caa2de8 100644 --- a/BuildaKitTests/Migration/Buildasaur-format-3-example1/ServerConfigs.json +++ b/BuildaKitTests/Migration/Buildasaur-format-3-example1/ServerConfigs.json @@ -2,7 +2,7 @@ { "id" : "D143B09C-CB1B-4831-8BA1-E2F8AB039B56", "host" : "https:\/\/127.0.0.1", - "user" : "testadmin" + "user" : "testadmin1" }, { "id" : "76826238-64AE-4F48-B4B8-52A6B7A65EE4", diff --git a/BuildaKitTests/MigrationTests.swift b/BuildaKitTests/MigrationTests.swift index e856678..92a6691 100644 --- a/BuildaKitTests/MigrationTests.swift +++ b/BuildaKitTests/MigrationTests.swift @@ -133,6 +133,13 @@ class MigrationTests: XCTestCase { do { try migrator.attemptMigration() try self.ensureEqualHierarchies(persistence, urlExpected: expectedURL, urlReal: writingURL) + + //wipe the test keychains + SecurePersistence.sourceServerPassphraseKeychain().wipe() + SecurePersistence.sourceServerTokenKeychain().wipe() + SecurePersistence.xcodeServerPasswordKeychain().wipe() + NSThread.sleepForTimeInterval(0.5) //let the queues finish + } catch { fail("\(error)") } From 3d84eb89d8eae4ea45ee5419a1cb6494bada47d0 Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Fri, 22 Jan 2016 23:10:51 +0000 Subject: [PATCH 17/63] fixed the migrator and a missed copy-paste bug, now everything seems to be working well with the keychain --- BuildaKit/PersistenceMigrator.swift | 4 +- BuildaKit/StorageManager.swift | 2 +- BuildaKitTests/MigrationTests.swift | 2 +- Buildasaur.xcodeproj/project.pbxproj | 132 ---------------------- BuildasaurUITests/BuildasaurUITests.swift | 36 ------ BuildasaurUITests/Info.plist | 24 ---- 6 files changed, 5 insertions(+), 195 deletions(-) delete mode 100644 BuildasaurUITests/BuildasaurUITests.swift delete mode 100644 BuildasaurUITests/Info.plist diff --git a/BuildaKit/PersistenceMigrator.swift b/BuildaKit/PersistenceMigrator.swift index 88d7fe4..597a7cf 100644 --- a/BuildaKit/PersistenceMigrator.swift +++ b/BuildaKit/PersistenceMigrator.swift @@ -58,7 +58,9 @@ public class CompositeMigrator: MigratorType { } public func attemptMigration() throws { - try self.childMigrators.forEach { try $0.attemptMigration() } + try self.childMigrators + .filter { $0.isMigrationRequired() } + .forEach { try $0.attemptMigration() } } } diff --git a/BuildaKit/StorageManager.swift b/BuildaKit/StorageManager.swift index 2707512..6fa9698 100644 --- a/BuildaKit/StorageManager.swift +++ b/BuildaKit/StorageManager.swift @@ -236,7 +236,7 @@ public class StorageManager { private func saveServerConfigs(configs: [String: XcodeServerConfig]) { let serverConfigs = Array(configs.values).map { $0.jsonify() } - let serverConfigKeychain = SecurePersistence.sourceServerTokenKeychain() + let serverConfigKeychain = SecurePersistence.xcodeServerPasswordKeychain() configs.values.forEach { serverConfigKeychain.writeIfNeeded($0.keychainKey(), value: $0.password) } diff --git a/BuildaKitTests/MigrationTests.swift b/BuildaKitTests/MigrationTests.swift index 92a6691..7c7f560 100644 --- a/BuildaKitTests/MigrationTests.swift +++ b/BuildaKitTests/MigrationTests.swift @@ -144,7 +144,7 @@ class MigrationTests: XCTestCase { fail("\(error)") } } - + func testPersistenceSetter() { let tmp = NSURL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) diff --git a/Buildasaur.xcodeproj/project.pbxproj b/Buildasaur.xcodeproj/project.pbxproj index b22b5bf..83933f3 100644 --- a/Buildasaur.xcodeproj/project.pbxproj +++ b/Buildasaur.xcodeproj/project.pbxproj @@ -40,7 +40,6 @@ 3A756DC51BAB425E00508B69 /* BuildaHeartbeatKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3A756DBD1BAB425E00508B69 /* BuildaHeartbeatKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 3A756DCB1BAB42C200508B69 /* Heartbeat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A756DCA1BAB42C200508B69 /* Heartbeat.swift */; }; 3A756DCC1BAB44A200508B69 /* BuildaHeartbeatKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A756DBD1BAB425E00508B69 /* BuildaHeartbeatKit.framework */; }; - 3A7F0CBF1BC14E640079A511 /* BuildasaurUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7F0CBE1BC14E640079A511 /* BuildasaurUITests.swift */; }; 3A7F0CC81BC1508C0079A511 /* GeneralTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A7F0CC71BC1508C0079A511 /* GeneralTests.swift */; }; 3A81BB191B5A77E9004732CD /* BuildaKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 3A81BB181B5A77E9004732CD /* BuildaKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 3A81BB201B5A77E9004732CD /* BuildaKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3A81BB161B5A77E9004732CD /* BuildaKit.framework */; }; @@ -148,13 +147,6 @@ remoteGlobalIDString = 3A756DBC1BAB425E00508B69; remoteInfo = BuildaHeartbeatKit; }; - 3A7F0CC11BC14E640079A511 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 3A5687681A3B93BD0066DB2B /* Project object */; - proxyType = 1; - remoteGlobalIDString = 3A56876F1A3B93BD0066DB2B; - remoteInfo = Buildasaur; - }; 3A81BB211B5A77E9004732CD /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 3A5687681A3B93BD0066DB2B /* Project object */; @@ -272,9 +264,6 @@ 3A756DBF1BAB425E00508B69 /* BuildaHeartbeatKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BuildaHeartbeatKit.h; sourceTree = ""; }; 3A756DC11BAB425E00508B69 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3A756DCA1BAB42C200508B69 /* Heartbeat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Heartbeat.swift; sourceTree = ""; }; - 3A7F0CBC1BC14E640079A511 /* BuildasaurUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BuildasaurUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 3A7F0CBE1BC14E640079A511 /* BuildasaurUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildasaurUITests.swift; sourceTree = ""; }; - 3A7F0CC01BC14E640079A511 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 3A7F0CC71BC1508C0079A511 /* GeneralTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneralTests.swift; sourceTree = ""; }; 3A81BB161B5A77E9004732CD /* BuildaKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BuildaKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3A81BB181B5A77E9004732CD /* BuildaKit.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BuildaKit.h; sourceTree = ""; }; @@ -399,13 +388,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 3A7F0CB91BC14E640079A511 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 3A81BB121B5A77E9004732CD /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -493,7 +475,6 @@ isa = PBXGroup; children = ( 3A5687721A3B93BD0066DB2B /* Buildasaur */, - 3A7F0CBD1BC14E640079A511 /* BuildasaurUITests */, 3A81BB171B5A77E9004732CD /* BuildaKit */, 3A81BB251B5A77E9004732CD /* BuildaKitTests */, 3AAF6EE51A3CE5BA00C657FB /* BuildaGitServer */, @@ -514,7 +495,6 @@ 3A81BB161B5A77E9004732CD /* BuildaKit.framework */, 3A81BB1F1B5A77E9004732CD /* BuildaKitTests.xctest */, 3A756DBD1BAB425E00508B69 /* BuildaHeartbeatKit.framework */, - 3A7F0CBC1BC14E640079A511 /* BuildasaurUITests.xctest */, ); name = Products; sourceTree = ""; @@ -585,15 +565,6 @@ path = BuildaHeartbeatKit; sourceTree = ""; }; - 3A7F0CBD1BC14E640079A511 /* BuildasaurUITests */ = { - isa = PBXGroup; - children = ( - 3A7F0CBE1BC14E640079A511 /* BuildasaurUITests.swift */, - 3A7F0CC01BC14E640079A511 /* Info.plist */, - ); - path = BuildasaurUITests; - sourceTree = ""; - }; 3A81BB171B5A77E9004732CD /* BuildaKit */ = { isa = PBXGroup; children = ( @@ -945,24 +916,6 @@ productReference = 3A756DBD1BAB425E00508B69 /* BuildaHeartbeatKit.framework */; productType = "com.apple.product-type.framework"; }; - 3A7F0CBB1BC14E640079A511 /* BuildasaurUITests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 3A7F0CC61BC14E640079A511 /* Build configuration list for PBXNativeTarget "BuildasaurUITests" */; - buildPhases = ( - 3A7F0CB81BC14E640079A511 /* Sources */, - 3A7F0CB91BC14E640079A511 /* Frameworks */, - 3A7F0CBA1BC14E640079A511 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 3A7F0CC21BC14E640079A511 /* PBXTargetDependency */, - ); - name = BuildasaurUITests; - productName = BuildasaurUITests; - productReference = 3A7F0CBC1BC14E640079A511 /* BuildasaurUITests.xctest */; - productType = "com.apple.product-type.bundle.ui-testing"; - }; 3A81BB151B5A77E9004732CD /* BuildaKit */ = { isa = PBXNativeTarget; buildConfigurationList = 3A81BB331B5A77E9004732CD /* Build configuration list for PBXNativeTarget "BuildaKit" */; @@ -1071,10 +1024,6 @@ CreatedOnToolsVersion = 7.0; DevelopmentTeam = 7BJ2984YDK; }; - 3A7F0CBB1BC14E640079A511 = { - CreatedOnToolsVersion = 7.0.1; - TestTargetID = 3A56876F1A3B93BD0066DB2B; - }; 3A81BB151B5A77E9004732CD = { CreatedOnToolsVersion = 7.0; DevelopmentTeam = 7BJ2984YDK; @@ -1106,7 +1055,6 @@ projectRoot = ""; targets = ( 3A56876F1A3B93BD0066DB2B /* Buildasaur */, - 3A7F0CBB1BC14E640079A511 /* BuildasaurUITests */, 3A81BB151B5A77E9004732CD /* BuildaKit */, 3A81BB1E1B5A77E9004732CD /* BuildaKitTests */, 3AAF6EE31A3CE5BA00C657FB /* BuildaGitServer */, @@ -1134,13 +1082,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 3A7F0CBA1BC14E640079A511 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 3A81BB141B5A77E9004732CD /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1449,14 +1390,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 3A7F0CB81BC14E640079A511 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 3A7F0CBF1BC14E640079A511 /* BuildasaurUITests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 3A81BB111B5A77E9004732CD /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1566,11 +1499,6 @@ target = 3A756DBC1BAB425E00508B69 /* BuildaHeartbeatKit */; targetProxy = 3A756DC21BAB425E00508B69 /* PBXContainerItemProxy */; }; - 3A7F0CC21BC14E640079A511 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 3A56876F1A3B93BD0066DB2B /* Buildasaur */; - targetProxy = 3A7F0CC11BC14E640079A511 /* PBXContainerItemProxy */; - }; 3A81BB221B5A77E9004732CD /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 3A81BB151B5A77E9004732CD /* BuildaKit */; @@ -1928,56 +1856,6 @@ }; name = Testing; }; - 3A7F0CC31BC14E640079A511 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - COMBINE_HIDPI_IMAGES = YES; - GCC_NO_COMMON_BLOCKS = YES; - INFOPLIST_FILE = BuildasaurUITests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.11; - PRODUCT_BUNDLE_IDENTIFIER = com.honzadvorsky.BuildasaurUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_TARGET_NAME = Buildasaur; - USES_XCTRUNNER = YES; - }; - name = Debug; - }; - 3A7F0CC41BC14E640079A511 /* Testing */ = { - isa = XCBuildConfiguration; - buildSettings = { - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = NO; - ENABLE_NS_ASSERTIONS = YES; - ENABLE_TESTABILITY = YES; - GCC_NO_COMMON_BLOCKS = YES; - INFOPLIST_FILE = BuildasaurUITests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.11; - PRODUCT_BUNDLE_IDENTIFIER = com.honzadvorsky.BuildasaurUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_TARGET_NAME = Buildasaur; - USES_XCTRUNNER = YES; - }; - name = Testing; - }; - 3A7F0CC51BC14E640079A511 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - COMBINE_HIDPI_IMAGES = YES; - COPY_PHASE_STRIP = NO; - ENABLE_NS_ASSERTIONS = YES; - GCC_NO_COMMON_BLOCKS = YES; - INFOPLIST_FILE = BuildasaurUITests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.11; - PRODUCT_BUNDLE_IDENTIFIER = com.honzadvorsky.BuildasaurUITests; - PRODUCT_NAME = "$(TARGET_NAME)"; - TEST_TARGET_NAME = Buildasaur; - USES_XCTRUNNER = YES; - }; - name = Release; - }; 3A81BB2D1B5A77E9004732CD /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 17FE44590CED1491A8F03F35 /* Pods-BuildaKit.debug.xcconfig */; @@ -2244,16 +2122,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 3A7F0CC61BC14E640079A511 /* Build configuration list for PBXNativeTarget "BuildasaurUITests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 3A7F0CC31BC14E640079A511 /* Debug */, - 3A7F0CC41BC14E640079A511 /* Testing */, - 3A7F0CC51BC14E640079A511 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 3A81BB331B5A77E9004732CD /* Build configuration list for PBXNativeTarget "BuildaKit" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/BuildasaurUITests/BuildasaurUITests.swift b/BuildasaurUITests/BuildasaurUITests.swift deleted file mode 100644 index ed6c383..0000000 --- a/BuildasaurUITests/BuildasaurUITests.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// BuildasaurUITests.swift -// BuildasaurUITests -// -// Created by Honza Dvorsky on 10/4/15. -// Copyright © 2015 Honza Dvorsky. All rights reserved. -// - -import XCTest - -class BuildasaurUITests: XCTestCase { - - override func setUp() { - super.setUp() - - // Put setup code here. This method is called before the invocation of each test method in the class. - - // In UI tests it is usually best to stop immediately when a failure occurs. - continueAfterFailure = false - // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. - XCUIApplication().launch() - - // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } - -// func testCreateNewServer() { - //TODO: add a couple UI tests (how do we solve the issue of - //inputting passwords and tokens though?) - //probs: mock both github and xcode server -// } -} diff --git a/BuildasaurUITests/Info.plist b/BuildasaurUITests/Info.plist deleted file mode 100644 index ba72822..0000000 --- a/BuildasaurUITests/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - BNDL - CFBundleShortVersionString - 1.0 - CFBundleSignature - ???? - CFBundleVersion - 1 - - From 4aa7a13f9249377ab80ee7dea99eb55d01d35249 Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Fri, 22 Jan 2016 23:19:48 +0000 Subject: [PATCH 18/63] don't forget the passphrasers! --- BuildaKit/StorageManager.swift | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/BuildaKit/StorageManager.swift b/BuildaKit/StorageManager.swift index 6fa9698..8e8dc28 100644 --- a/BuildaKit/StorageManager.swift +++ b/BuildaKit/StorageManager.swift @@ -168,12 +168,14 @@ public class StorageManager { self.config.value = self.persistence.loadDictionaryFromFile("Config.json") ?? [:] let allProjects: [ProjectConfig] = self.persistence.loadArrayFromFile("Projects.json") ?? [] - //load server tokens from keychain - let projectConfigKeychain = SecurePersistence.sourceServerTokenKeychain() + //load server token & ssh passphrase from keychain + let tokenKeychain = SecurePersistence.sourceServerTokenKeychain() + let passphraseKeychain = SecurePersistence.sourceServerPassphraseKeychain() self.projectConfigs.value = allProjects .map { (var p: ProjectConfig) -> ProjectConfig in - p.serverAuthentication = projectConfigKeychain.read(p.keychainKey()) + p.serverAuthentication = tokenKeychain.read(p.keychainKey()) + p.sshPassphrase = passphraseKeychain.read(p.keychainKey()) return p }.dictionarifyWithKey { $0.id } @@ -227,9 +229,11 @@ public class StorageManager { private func saveProjectConfigs(configs: [String: ProjectConfig]) { let projectConfigs: NSArray = Array(configs.values).map { $0.jsonify() } - let projectConfigKeychain = SecurePersistence.sourceServerTokenKeychain() + let tokenKeychain = SecurePersistence.sourceServerTokenKeychain() + let passphraseKeychain = SecurePersistence.sourceServerPassphraseKeychain() configs.values.forEach { - projectConfigKeychain.writeIfNeeded($0.keychainKey(), value: $0.serverAuthentication) + tokenKeychain.writeIfNeeded($0.keychainKey(), value: $0.serverAuthentication) + passphraseKeychain.writeIfNeeded($0.keychainKey(), value: $0.sshPassphrase) } self.persistence.saveArray("Projects.json", items: projectConfigs) } From 1def56163bce0d4d23fa1284edd658847c43a0ee Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Sat, 23 Jan 2016 12:50:25 +0000 Subject: [PATCH 19/63] switched to using a in-memory dictionary instead of the real keychain in tests - was hanging when ran on CI --- BuildaKit/SecurePersistence.swift | 57 ++++++++++++++++------------- BuildaKitTests/MigrationTests.swift | 7 ---- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/BuildaKit/SecurePersistence.swift b/BuildaKit/SecurePersistence.swift index 406c261..aad9c72 100644 --- a/BuildaKit/SecurePersistence.swift +++ b/BuildaKit/SecurePersistence.swift @@ -14,38 +14,48 @@ import SwiftSafe final class SecurePersistence { #if TESTING - static let Prefix = "com.honzadvorsky.buildasaur.testing" - #else - static let Prefix = "com.honzadvorsky.buildasaur" + typealias Keychain = NSMutableDictionary #endif + static let Prefix = "com.honzadvorsky.buildasaur" + + private let keychain: Keychain + private let safe: Safe + + private init(keychain: Keychain, safe: Safe = EREW()) { + self.keychain = keychain + self.safe = safe + } + static func xcodeServerPasswordKeychain() -> SecurePersistence { - let keychain = Keychain(service: "\(Prefix).xcs.password") - return self.init(keychain: keychain) + return self.keychain("\(Prefix).xcs.password") } static func sourceServerTokenKeychain() -> SecurePersistence { - let keychain = Keychain(service: "\(Prefix).source_server.oauth_tokens") - return self.init(keychain: keychain) + return self.keychain("\(Prefix).source_server.oauth_tokens") } static func sourceServerPassphraseKeychain() -> SecurePersistence { - let keychain = Keychain(service: "\(Prefix).source_server.passphrase") - return self.init(keychain: keychain) + return self.keychain("\(Prefix).source_server.passphrase") } - private let keychain: Keychain - private let safe: Safe - - private init(keychain: Keychain, safe: Safe = CREW()) { - self.keychain = keychain - self.safe = safe + static private func keychain(service: String) -> SecurePersistence { + #if TESTING + let keychain = NSMutableDictionary() + #else + let keychain = Keychain(service: service) + #endif + return self.init(keychain: keychain) } func read(key: String) -> String? { var val: String? self.safe.read { - val = self.keychain[key] + #if TESTING + val = self.keychain[key] as? String + #else + val = self.keychain[key] + #endif } return val } @@ -57,18 +67,15 @@ final class SecurePersistence { } private func updateIfNeeded(key: String, value: String?) { - if self.keychain[key] != value { + #if TESTING + let existing = self.keychain[key] as? String + #else + let existing = self.keychain[key] + #endif + if existing != value { self.keychain[key] = value } } - - #if TESTING - func wipe() { - self.safe.write { - _ = try? self.keychain.removeAll() - } - } - #endif } public protocol KeychainSaveable { diff --git a/BuildaKitTests/MigrationTests.swift b/BuildaKitTests/MigrationTests.swift index 7c7f560..0b4c196 100644 --- a/BuildaKitTests/MigrationTests.swift +++ b/BuildaKitTests/MigrationTests.swift @@ -133,13 +133,6 @@ class MigrationTests: XCTestCase { do { try migrator.attemptMigration() try self.ensureEqualHierarchies(persistence, urlExpected: expectedURL, urlReal: writingURL) - - //wipe the test keychains - SecurePersistence.sourceServerPassphraseKeychain().wipe() - SecurePersistence.sourceServerTokenKeychain().wipe() - SecurePersistence.xcodeServerPasswordKeychain().wipe() - NSThread.sleepForTimeInterval(0.5) //let the queues finish - } catch { fail("\(error)") } From ed414f9c9dd6cb7c6dc5b28b51ca4a3fc92903e6 Mon Sep 17 00:00:00 2001 From: Honza Dvorsky Date: Wed, 27 Jan 2016 01:32:15 +0000 Subject: [PATCH 20/63] added OAuth support, already in the UI for GitHub, needs a bit more work on the UI side, but works. this opens up the door to bitbucket. --- BuildaGitServer/Base/Authentication.swift | 64 +++++ BuildaGitServer/Base/BaseTypes.swift | 39 +--- BuildaGitServer/GitHub/GitHubEndpoints.swift | 14 +- BuildaGitServer/GitHub/GitHubFactory.swift | 6 +- BuildaGitServer/GitHub/GitHubServer.swift | 2 +- BuildaGitServer/GitServerPublic.swift | 25 ++ BuildaKit/NetworkUtils.swift | 7 +- BuildaKit/PersistenceMigrator.swift | 62 ++++- BuildaKit/ProjectConfig.swift | 5 +- BuildaKit/SecurePersistence.swift | 14 ++ BuildaKit/StorageManager.swift | 12 +- BuildaKit/SyncerFactory.swift | 13 +- BuildaKit/WorkspaceMetadata.swift | 11 +- .../9B53CC35-57DA-4DA0-9B85-05FCF109512A.json | 26 +++ .../B31C6530-BB84-4A93-B08C-54074AAB5F37.json | 26 +++ .../Buildasaur-format-4-example1/Config.json | 3 + .../Logs/Builda.log | 89 +++++++ .../Projects.json | 14 ++ .../ServerConfigs.json | 12 + .../Buildasaur-format-4-example1/Syncers.json | 26 +++ .../4E66D0D5-D5CC-417E-A40E-73B513CE4E10.json | 25 ++ .../E8F5285A-A262-4630-AF7B-236772B75760.json | 7 + BuildaKitTests/MigrationTests.swift | 19 ++ Buildasaur.xcodeproj/project.pbxproj | 32 +++ Buildasaur/AppDelegate.swift | 32 +++ Buildasaur/Base.lproj/Main.storyboard | 220 ++++++++++++------ Buildasaur/DashboardViewController.swift | 3 +- Buildasaur/EditorViewControllerFactory.swift | 5 +- Buildasaur/Info.plist | 13 +- Buildasaur/ProjectViewController.swift | 105 ++++++++- Buildasaur/ServiceAuthentication.swift | 81 +++++++ Meta/github.png | Bin 0 -> 2131769 bytes Podfile | 17 +- Podfile.lock | 10 + 34 files changed, 901 insertions(+), 138 deletions(-) create mode 100644 BuildaGitServer/Base/Authentication.swift create mode 100644 BuildaKitTests/Migration/Buildasaur-format-4-example1/BuildTemplates/9B53CC35-57DA-4DA0-9B85-05FCF109512A.json create mode 100644 BuildaKitTests/Migration/Buildasaur-format-4-example1/BuildTemplates/B31C6530-BB84-4A93-B08C-54074AAB5F37.json create mode 100644 BuildaKitTests/Migration/Buildasaur-format-4-example1/Config.json create mode 100644 BuildaKitTests/Migration/Buildasaur-format-4-example1/Logs/Builda.log create mode 100644 BuildaKitTests/Migration/Buildasaur-format-4-example1/Projects.json create mode 100644 BuildaKitTests/Migration/Buildasaur-format-4-example1/ServerConfigs.json create mode 100644 BuildaKitTests/Migration/Buildasaur-format-4-example1/Syncers.json create mode 100644 BuildaKitTests/Migration/Buildasaur-format-4-example1/Triggers/4E66D0D5-D5CC-417E-A40E-73B513CE4E10.json create mode 100644 BuildaKitTests/Migration/Buildasaur-format-4-example1/Triggers/E8F5285A-A262-4630-AF7B-236772B75760.json create mode 100644 Buildasaur/ServiceAuthentication.swift create mode 100644 Meta/github.png diff --git a/BuildaGitServer/Base/Authentication.swift b/BuildaGitServer/Base/Authentication.swift new file mode 100644 index 0000000..9a465a3 --- /dev/null +++ b/BuildaGitServer/Base/Authentication.swift @@ -0,0 +1,64 @@ +// +// Authentication.swift +// Buildasaur +// +// Created by Honza Dvorsky on 1/26/16. +// Copyright © 2016 Honza Dvorsky. All rights reserved. +// + +import Foundation +import BuildaUtils + +public struct ProjectAuthenticator { + + public enum AuthType: String { + case PersonalToken + case OAuthToken + } + + public let service: GitService + public let username: String + public let type: AuthType + public let secret: String + + public init(service: GitService, username: String, type: AuthType, secret: String) { + self.service = service + self.username = username + self.type = type + self.secret = secret + } +} + +public protocol KeychainStringSerializable { + static func fromString(value: String) throws -> Self + func toString() -> String +} + +extension ProjectAuthenticator: KeychainStringSerializable { + + public static func fromString(value: String) throws -> ProjectAuthenticator { + + let comps = value.componentsSeparatedByString(":") + guard comps.count >= 4 else { throw Error.withInfo("Corrupted keychain string") } + guard let service = GitService(rawValue: comps[0]) else { + throw Error.withInfo("Unsupported service: \(comps[0])") + } + guard let type = ProjectAuthenticator.AuthType(rawValue: comps[2]) else { + throw Error.withInfo("Unsupported auth type: \(comps[2])") + } + //join the rest back in case we have ":" in the token + let remaining = comps.dropFirst(3).joinWithSeparator(":") + let auth = ProjectAuthenticator(service: service, username: comps[1], type: type, secret: remaining) + return auth + } + + public func toString() -> String { + + return [ + self.service.rawValue, + self.username, + self.type.rawValue, + self.secret + ].joinWithSeparator(":") + } +} diff --git a/BuildaGitServer/Base/BaseTypes.swift b/BuildaGitServer/Base/BaseTypes.swift index a28b6a1..186bd84 100644 --- a/BuildaGitServer/Base/BaseTypes.swift +++ b/BuildaGitServer/Base/BaseTypes.swift @@ -24,41 +24,22 @@ public protocol SourceServerType: BuildStatusCreator { func getCommentsOfIssue(issueNumber: Int, repo: String, completion: (comments: [CommentType]?, error: ErrorType?) -> ()) } -public enum SourceServerOption { - case Token(String) -} - -extension SourceServerOption: Hashable { - public var hashValue: Int { - switch self { - case .Token(_): - return 1 - } - } -} - -public func ==(lhs: SourceServerOption, rhs: SourceServerOption) -> Bool { - - if case .Token(let lhsToken) = lhs, case .Token(let rhsToken) = rhs { - return lhsToken == rhsToken - } - - return false -} - public class SourceServerFactory { public init() { } - public func createServer(config: Set) -> SourceServerType { + public func createServer(service: GitService, auth: ProjectAuthenticator?) -> SourceServerType { + + if let auth = auth { + precondition(service == auth.service) + } - //TODO: generalize - if let tokenOption = config.first, - case .Token(let token) = tokenOption { - - return GitHubFactory.server(token) + switch service { + case .GitHub: + return GitHubFactory.server(auth) + case .BitBucket: + fatalError("Not implemented yet") } - preconditionFailure("Insufficient data provided to create a source server") } } diff --git a/BuildaGitServer/GitHub/GitHubEndpoints.swift b/BuildaGitServer/GitHub/GitHubEndpoints.swift index 6992391..5887e0e 100644 --- a/BuildaGitServer/GitHub/GitHubEndpoints.swift +++ b/BuildaGitServer/GitHub/GitHubEndpoints.swift @@ -31,11 +31,11 @@ class GitHubEndpoints { } private let baseURL: String - private let token: String? + private let auth: ProjectAuthenticator? - init(baseURL: String, token: String?) { + init(baseURL: String, auth: ProjectAuthenticator?) { self.baseURL = baseURL - self.token = token + self.auth = auth } private func endpointURL(endpoint: Endpoint, params: [String: String]? = nil) -> String { @@ -152,8 +152,12 @@ class GitHubEndpoints { let request = NSMutableURLRequest(URL: url) request.HTTPMethod = method.rawValue - if let token = self.token { - request.setValue("token \(token)", forHTTPHeaderField:"Authorization") + if let auth = self.auth { + + switch auth.type { + case .PersonalToken, .OAuthToken: + request.setValue("token \(auth.secret)", forHTTPHeaderField:"Authorization") + } } if let body = body { diff --git a/BuildaGitServer/GitHub/GitHubFactory.swift b/BuildaGitServer/GitHub/GitHubFactory.swift index 7b47ca5..3866b2e 100644 --- a/BuildaGitServer/GitHub/GitHubFactory.swift +++ b/BuildaGitServer/GitHub/GitHubFactory.swift @@ -10,10 +10,10 @@ import Foundation class GitHubFactory { - class func server(token: String?) -> GitHubServer { - + class func server(auth: ProjectAuthenticator?) -> GitHubServer { + let baseURL = "https://api.github.com" - let endpoints = GitHubEndpoints(baseURL: baseURL, token: token) + let endpoints = GitHubEndpoints(baseURL: baseURL, auth: auth) let server = GitHubServer(endpoints: endpoints) return server diff --git a/BuildaGitServer/GitHub/GitHubServer.swift b/BuildaGitServer/GitHub/GitHubServer.swift index 9dd2ffc..68e1a46 100644 --- a/BuildaGitServer/GitHub/GitHubServer.swift +++ b/BuildaGitServer/GitHub/GitHubServer.swift @@ -19,7 +19,7 @@ class GitHubServer : GitServer { init(endpoints: GitHubEndpoints, http: HTTP? = nil) { self.endpoints = endpoints - super.init(http: http) + super.init(service: .GitHub, http: http) } } diff --git a/BuildaGitServer/GitServerPublic.swift b/BuildaGitServer/GitServerPublic.swift index 1c2b897..5f266e0 100644 --- a/BuildaGitServer/GitServerPublic.swift +++ b/BuildaGitServer/GitServerPublic.swift @@ -9,7 +9,32 @@ import Foundation import BuildaUtils +public enum GitService: String { + case GitHub = "github" + case BitBucket = "bitbucket" +// case GitLab = "gitlab" + + public func prettyName() -> String { + switch self { + case .GitHub: return "GitHub" + case .BitBucket: return "BitBucket" + } + } + + public func logoName() -> String { + switch self { + case .GitHub: return "github" + case .BitBucket: return "bitbucket" + } + } +} + public class GitServer : HTTPServer { + let service: GitService + init(service: GitService, http: HTTP? = nil) { + self.service = service + super.init(http: http) + } } diff --git a/BuildaKit/NetworkUtils.swift b/BuildaKit/NetworkUtils.swift index 51faf70..bde944f 100644 --- a/BuildaKit/NetworkUtils.swift +++ b/BuildaKit/NetworkUtils.swift @@ -15,11 +15,8 @@ public class NetworkUtils { public class func checkAvailabilityOfGitHubWithCurrentSettingsOfProject(project: Project, completion: (success: Bool, error: ErrorType?) -> ()) { - let token = project.config.value.serverAuthentication! - //TODO: have project spit out Set - - let options: Set = [.Token(token)] - let server: SourceServerType = SourceServerFactory().createServer(options) + let auth = project.config.value.serverAuthentication + let server = SourceServerFactory().createServer(.GitHub, auth: auth) let credentialValidationBlueprint = project.createSourceControlBlueprintForCredentialVerification() diff --git a/BuildaKit/PersistenceMigrator.swift b/BuildaKit/PersistenceMigrator.swift index 597a7cf..29e292d 100644 --- a/BuildaKit/PersistenceMigrator.swift +++ b/BuildaKit/PersistenceMigrator.swift @@ -9,6 +9,7 @@ import Foundation import BuildaUtils import XcodeServerSDK +@testable import BuildaGitServer public protocol MigratorType { init(persistence: Persistence) @@ -49,7 +50,8 @@ public class CompositeMigrator: MigratorType { self.childMigrators = [ Migrator_v0_v1(persistence: persistence), Migrator_v1_v2(persistence: persistence), - Migrator_v2_v3(persistence: persistence) + Migrator_v2_v3(persistence: persistence), + Migrator_v3_v4(persistence: persistence) ] } @@ -276,7 +278,7 @@ class Migrator_v1_v2: MigratorType { /* - ServerConfigs.json: password moved to the keychain -- Projects.json: github_token -> server_authentication, ssh_passphrase moved to keychain +- Projects.json: github_token -> oauth_tokens keychain, ssh_passphrase moved to keychain - move any .log files to a separate folder called 'Logs' */ class Migrator_v2_v3: MigratorType { @@ -381,3 +383,59 @@ class Migrator_v2_v3: MigratorType { } } +/* +- keychain oauth_tokens need to be prepended with the service, username etc. +- "token1234" -> "github:username:personaltoken:token1234" +- unfortunately we haven't kept the username anywhere, so we'll just put +- "GIT" there instead. +*/ +class Migrator_v3_v4: MigratorType { + + internal var persistence: Persistence + required init(persistence: Persistence) { + self.persistence = persistence + } + + func isMigrationRequired() -> Bool { + + return self.persistenceVersion() == 3 + } + + func attemptMigration() throws { + + let pers = self.persistence + + //migrate + self.migrateKeychainTokens() + + //copy the rest + pers.copyFileToWriteLocation("Projects.json", isDirectory: false) + pers.copyFileToWriteLocation("ServerConfigs.json", isDirectory: false) + pers.copyFileToWriteLocation("Syncers.json", isDirectory: false) + pers.copyFileToWriteLocation("BuildTemplates", isDirectory: true) + pers.copyFileToWriteLocation("Triggers", isDirectory: true) + pers.copyFileToWriteLocation("Logs", isDirectory: true) + + let config = self.config() + let mutableConfig = config.mutableCopy() as! NSMutableDictionary + mutableConfig[kPersistenceVersion] = 4 + + //save the updated config + pers.saveDictionary("Config.json", item: mutableConfig) + } + + func migrateKeychainTokens() { + + let tokenKeychain = SecurePersistence.sourceServerTokenKeychain() + + tokenKeychain.readAll().forEach { (id, key) in + //all keys migrated are github personal tokens + let auth = ProjectAuthenticator(service: .GitHub, username: "GIT", type: .PersonalToken, secret: key) + let formatted = auth.toString() + tokenKeychain.writeIfNeeded(id, value: formatted) + } + tokenKeychain.readAll() //wait till all writes have completed + } +} + + diff --git a/BuildaKit/ProjectConfig.swift b/BuildaKit/ProjectConfig.swift index 15eee32..292d437 100644 --- a/BuildaKit/ProjectConfig.swift +++ b/BuildaKit/ProjectConfig.swift @@ -8,6 +8,7 @@ import Foundation import BuildaUtils +import BuildaGitServer public struct ProjectConfig { @@ -17,13 +18,13 @@ public struct ProjectConfig { public var publicSSHKeyPath: String public var sshPassphrase: String? //loaded from the keychain - public var serverAuthentication: String? //loaded from the keychain + public var serverAuthentication: ProjectAuthenticator? //loaded from the keychain //creates a new default ProjectConfig public init() { self.id = Ref.new() self.url = "" - self.serverAuthentication = "" + self.serverAuthentication = nil self.privateSSHKeyPath = "" self.publicSSHKeyPath = "" self.sshPassphrase = nil diff --git a/BuildaKit/SecurePersistence.swift b/BuildaKit/SecurePersistence.swift index aad9c72..4418b33 100644 --- a/BuildaKit/SecurePersistence.swift +++ b/BuildaKit/SecurePersistence.swift @@ -60,6 +60,20 @@ final class SecurePersistence { return val } + func readAll() -> [(String, String)] { + var all: [(String, String)] = [] + self.safe.read { + #if TESTING + let keychain = self.keychain + all = keychain.allKeys.map { ($0, keychain[$0] as! String) } + #else + let keychain = self.keychain + all = keychain.allKeys().map { ($0, keychain[$0]!) } + #endif + } + return all + } + func writeIfNeeded(key: String, value: String?) { self.safe.write { self.updateIfNeeded(key, value: value) diff --git a/BuildaKit/StorageManager.swift b/BuildaKit/StorageManager.swift index 8e8dc28..695ed18 100644 --- a/BuildaKit/StorageManager.swift +++ b/BuildaKit/StorageManager.swift @@ -10,6 +10,7 @@ import Foundation import BuildaUtils import XcodeServerSDK import ReactiveCocoa +import BuildaGitServer public enum StorageManagerError: ErrorType { case DuplicateServerConfig(XcodeServerConfig) @@ -28,6 +29,7 @@ public class StorageManager { private let persistence: Persistence public init(persistence: Persistence) { + self.persistence = persistence self.loadAllFromPersistence() self.setupSaving() @@ -174,7 +176,11 @@ public class StorageManager { self.projectConfigs.value = allProjects .map { (var p: ProjectConfig) -> ProjectConfig in - p.serverAuthentication = tokenKeychain.read(p.keychainKey()) + var auth: ProjectAuthenticator? + if let val = tokenKeychain.read(p.keychainKey()) { + auth = try? ProjectAuthenticator.fromString(val) + } + p.serverAuthentication = auth p.sshPassphrase = passphraseKeychain.read(p.keychainKey()) return p }.dictionarifyWithKey { $0.id } @@ -232,7 +238,9 @@ public class StorageManager { let tokenKeychain = SecurePersistence.sourceServerTokenKeychain() let passphraseKeychain = SecurePersistence.sourceServerPassphraseKeychain() configs.values.forEach { - tokenKeychain.writeIfNeeded($0.keychainKey(), value: $0.serverAuthentication) + if let auth = $0.serverAuthentication { + tokenKeychain.writeIfNeeded($0.keychainKey(), value: auth.toString()) + } passphraseKeychain.writeIfNeeded($0.keychainKey(), value: $0.sshPassphrase) } self.persistence.saveArray("Projects.json", items: projectConfigs) diff --git a/BuildaKit/SyncerFactory.swift b/BuildaKit/SyncerFactory.swift index 7b1e862..a87d288 100644 --- a/BuildaKit/SyncerFactory.swift +++ b/BuildaKit/SyncerFactory.swift @@ -16,7 +16,7 @@ public protocol SyncerFactoryType { func newEditableTriplet() -> EditableConfigTriplet func createXcodeServer(config: XcodeServerConfig) -> XcodeServer func createProject(config: ProjectConfig) -> Project? - func createSourceServer(token: String) -> SourceServerType + func createSourceServer(service: GitService, auth: ProjectAuthenticator?) -> SourceServerType func createTrigger(config: TriggerConfig) -> Trigger } @@ -31,13 +31,15 @@ public class SyncerFactory: SyncerFactoryType { private func createSyncer(triplet: ConfigTriplet) -> HDGitHubXCBotSyncer? { let xcodeServer = self.createXcodeServer(triplet.server) - //TODO: pull out authentication as SourceServerOptions - let sourceServer = self.createSourceServer(triplet.project.serverAuthentication ?? "") let maybeProject = self.createProject(triplet.project) let triggers = triplet.triggers.map { self.createTrigger($0) } guard let project = maybeProject else { return nil } + guard let service = project.workspaceMetadata?.service else { return nil } + + let sourceServer = self.createSourceServer(service, auth: triplet.project.serverAuthentication) + if let poolAttempt = self.syncerPool[triplet.syncer.id] { poolAttempt.config.value = triplet.syncer @@ -118,10 +120,9 @@ public class SyncerFactory: SyncerFactoryType { return project } - public func createSourceServer(token: String) -> SourceServerType { + public func createSourceServer(service: GitService, auth: ProjectAuthenticator?) -> SourceServerType { - let options: Set = [.Token(token)] - let server: SourceServerType = SourceServerFactory().createServer(options) + let server = SourceServerFactory().createServer(service, auth: auth) return server } diff --git a/BuildaKit/WorkspaceMetadata.swift b/BuildaKit/WorkspaceMetadata.swift index 9f1fe08..90e2ecb 100644 --- a/BuildaKit/WorkspaceMetadata.swift +++ b/BuildaKit/WorkspaceMetadata.swift @@ -8,6 +8,7 @@ import Foundation import BuildaUtils +import BuildaGitServer public enum CheckoutType: String { case SSH = "SSH" @@ -23,6 +24,7 @@ public struct WorkspaceMetadata { public let projectWCCIdentifier: String public let projectWCCName: String public let projectURL: NSURL + public let service: GitService public let checkoutType: CheckoutType init(projectName: String?, projectPath: String?, projectWCCIdentifier: String?, projectWCCName: String?, projectURLString: String?) throws { @@ -33,7 +35,7 @@ public struct WorkspaceMetadata { guard let projectWCCIdentifier = projectWCCIdentifier else { throw errorForMissingKey("Project WCC Identifier") } guard let projectWCCName = projectWCCName else { throw errorForMissingKey("Project WCC Name") } guard let projectURLString = projectURLString else { throw errorForMissingKey("Project URL") } - guard let checkoutType = WorkspaceMetadata.parseCheckoutType(projectURLString) else { + guard let (checkoutType, service) = WorkspaceMetadata.parse(projectURLString) else { let allowedString = [CheckoutType.SSH].map({ $0.rawValue }).joinWithSeparator(", ") let error = Error.withInfo("Disallowed checkout type, the project must be checked out over one of the supported schemes: \(allowedString)") throw error @@ -53,6 +55,7 @@ public struct WorkspaceMetadata { self.projectWCCName = projectWCCName self.projectURL = projectURL self.checkoutType = checkoutType + self.service = service } func duplicateWithForkURL(forkUrlString: String?) throws -> WorkspaceMetadata { @@ -62,7 +65,7 @@ public struct WorkspaceMetadata { extension WorkspaceMetadata { - internal static func parseCheckoutType(projectURLString: String) -> CheckoutType? { + internal static func parse(projectURLString: String) -> (CheckoutType, GitService)? { var urlString = projectURLString @@ -76,7 +79,9 @@ extension WorkspaceMetadata { let scheme = NSURL(string: urlString)!.scheme switch scheme { case "github.com": - return CheckoutType.SSH + return (CheckoutType.SSH, .GitHub) + case "bitbucket.org": + return (CheckoutType.SSH, .BitBucket) case "https": if urlString.hasSuffix(".git") { diff --git a/BuildaKitTests/Migration/Buildasaur-format-4-example1/BuildTemplates/9B53CC35-57DA-4DA0-9B85-05FCF109512A.json b/BuildaKitTests/Migration/Buildasaur-format-4-example1/BuildTemplates/9B53CC35-57DA-4DA0-9B85-05FCF109512A.json new file mode 100644 index 0000000..c0064b6 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-4-example1/BuildTemplates/9B53CC35-57DA-4DA0-9B85-05FCF109512A.json @@ -0,0 +1,26 @@ +{ + "id" : "9B53CC35-57DA-4DA0-9B85-05FCF109512A", + "project_name" : "Buildasaur", + "schedule" : { + "weeklyScheduleDay" : 0, + "periodicScheduleInterval" : 0, + "hourOfIntegration" : 0, + "minutesAfterHourToIntegrate" : 0, + "scheduleType" : 3 + }, + "triggers" : [ + "E8F5285A-A262-4630-AF7B-236772B75760", + "4E66D0D5-D5CC-417E-A40E-73B513CE4E10" + ], + "should_analyze" : true, + "platform_type" : "com.apple.platform.macosx", + "scheme" : "Buildasaur", + "device_filter" : 0, + "cleaning_policy" : 0, + "testing_devices" : [ + + ], + "should_archive" : false, + "name" : "Buildasaur PR", + "should_test" : true +} \ No newline at end of file diff --git a/BuildaKitTests/Migration/Buildasaur-format-4-example1/BuildTemplates/B31C6530-BB84-4A93-B08C-54074AAB5F37.json b/BuildaKitTests/Migration/Buildasaur-format-4-example1/BuildTemplates/B31C6530-BB84-4A93-B08C-54074AAB5F37.json new file mode 100644 index 0000000..8d6a805 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-4-example1/BuildTemplates/B31C6530-BB84-4A93-B08C-54074AAB5F37.json @@ -0,0 +1,26 @@ +{ + "id" : "B31C6530-BB84-4A93-B08C-54074AAB5F37", + "project_name" : "Buildasaur-Tester", + "schedule" : { + "weeklyScheduleDay" : 0, + "periodicScheduleInterval" : 0, + "hourOfIntegration" : 0, + "minutesAfterHourToIntegrate" : 0, + "scheduleType" : 3 + }, + "triggers" : [ + "E8F5285A-A262-4630-AF7B-236772B75760", + "4E66D0D5-D5CC-417E-A40E-73B513CE4E10" + ], + "should_analyze" : true, + "platform_type" : "com.apple.platform.iphoneos", + "scheme" : "Buildasaur-Tester", + "device_filter" : 0, + "cleaning_policy" : 0, + "testing_devices" : [ + + ], + "should_archive" : false, + "name" : "BuildaTest PR", + "should_test" : true +} \ No newline at end of file diff --git a/BuildaKitTests/Migration/Buildasaur-format-4-example1/Config.json b/BuildaKitTests/Migration/Buildasaur-format-4-example1/Config.json new file mode 100644 index 0000000..906c841 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-4-example1/Config.json @@ -0,0 +1,3 @@ +{ + "persistence_version" : 3 +} \ No newline at end of file diff --git a/BuildaKitTests/Migration/Buildasaur-format-4-example1/Logs/Builda.log b/BuildaKitTests/Migration/Buildasaur-format-4-example1/Logs/Builda.log new file mode 100644 index 0000000..253ec35 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-4-example1/Logs/Builda.log @@ -0,0 +1,89 @@ +* +* +* + ____ _ _ _ +| _ \ (_) | | | +| |_) |_ _ _| | __| | __ _ ___ __ _ _ _ _ __ +| _ <| | | | | |/ _` |/ _` / __|/ _` | | | | '__| +| |_) | |_| | | | (_| | (_| \__ \ (_| | |_| | | +|____/ \__,_|_|_|\__,_|\__,_|___/\__,_|\__,_|_| + +Buildasaur 0.5.1 launched at 2015-10-12 13:57:46 +0000. +* +* +* + +[INFO]: Will send anonymous heartbeat. To opt out add `"heartbeat_opt_out" = true` to ~/Library/Application Support/Buildasaur/Config.json +[INFO]: Sending heartbeat event ["event_type": launch] +[INFO]: Sending heartbeat event ["event_type": heartbeat, "uptime": 0.03319501876831055, "running_syncers": 0] +[INFO]: Project: file:///Users/honzadvorsky/Documents/Buildasaur-Tester/Buildasaur-Tester.xcodeproj +[VERBOSE]: Finished fetching devices +[INFO]: Key: Optional(file:///Users/honzadvorsky/.ssh/id_rsa.pub) +[INFO]: Key: Optional(file:///Users/honzadvorsky/.ssh/id_rsa) + +------------------------------------ + +[INFO]: Sync starting at 2015-10-12 14:00:27 +0000 +[VERBOSE]: Resolving prs: + PR 9: test change [czechboy0-patch-6 -> master] + PR 8: Update README.md PR2 [czechboy0-patch-3 -> czechboy0-patch-2] + PR 6: Update ViewController.swift [czechboy0-patch-4 -> master] + PR 4: Update README.md [czechboy0-patch-2 -> master] +and branches: + +and bots: + Bot Buildasaur Bot + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #6 + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #9 + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #4 + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #8 + Bot BuildaBot [czechboy0/XcodeServerSDK] PR #117 + Bot BuildaBot [czechboy0/XcodeServerSDK] PR #106 + Bot BuildaBot [czechboy0/Buildasaur] PR #138 + Bot BuildaBot [czechboy0/XcodeServerSDK] |-> master + Bot BuildaBot [czechboy0/Buildasaur] |-> master + Bot Buildasaur-TestProject-iOS Bot +[VERBOSE]: SyncPair PR (9:czechboy0-patch-6) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #9) finished sync after 0.388 seconds. +[VERBOSE]: SyncPair PR (6:czechboy0-patch-4) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #6) finished sync after 0.702 seconds. +[VERBOSE]: SyncPair PR (4:czechboy0-patch-2) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #4) finished sync after 0.718 seconds. +[VERBOSE]: SyncPair PR (8:czechboy0-patch-3) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #8) finished sync after 1.156 seconds. +[INFO]: GitHub Rate Limit: count: 0/5000, renews in 3598 seconds, rate: 0.0/1.38, using 0.0% of the allowed request rate. +[INFO]: Sync finished successfully at 2015-10-12 14:00:29 +0000, took 1.59 seconds. + +------------------------------------ + +[INFO]: Sync starting at 2015-10-12 14:00:34 +0000 +[VERBOSE]: Resolving prs: + PR 9: test change [czechboy0-patch-6 -> master] + PR 8: Update README.md PR2 [czechboy0-patch-3 -> czechboy0-patch-2] + PR 6: Update ViewController.swift [czechboy0-patch-4 -> master] + PR 4: Update README.md [czechboy0-patch-2 -> master] +and branches: + Branch [almost-master:5b1d734f0170583b54fa749ff9bc47670abd66ad] + Branch [czechboy0-patch-1:ddb1e4840e35a611c2627168d8330ddf80c569d9] + Branch [czechboy0-patch-2:26cf7e9a027579f057aceaa9b09625317d31efde] + Branch [czechboy0-patch-3:ef2d5e2e0755bdff3945134e9a160d86a0698fe2] + Branch [czechboy0-patch-4:603e18851f66ee66a1223ea65e5f1e18e4353664] + Branch [czechboy0-patch-5:c1da8cd5cc61c62f8700766314ba26c7f03ea9ab] + Branch [czechboy0-patch-6:2ab73752fe7ed2d8f67187d7ba0552126a158de1] + Branch [master:510352edd9cdabd45f608e4327a8dd48ac517230] +and bots: + Bot Buildasaur Bot + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #6 + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #9 + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #4 + Bot BuildaBot [czechboy0/Buildasaur-Tester] PR #8 + Bot BuildaBot [czechboy0/XcodeServerSDK] PR #117 + Bot BuildaBot [czechboy0/XcodeServerSDK] PR #106 + Bot BuildaBot [czechboy0/Buildasaur] PR #138 + Bot BuildaBot [czechboy0/XcodeServerSDK] |-> master + Bot BuildaBot [czechboy0/Buildasaur] |-> master + Bot Buildasaur-TestProject-iOS Bot +[VERBOSE]: SyncPair PR (4:czechboy0-patch-2) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #4) finished sync after 0.068 seconds. +[VERBOSE]: SyncPair PR (6:czechboy0-patch-4) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #6) finished sync after 0.165 seconds. +[VERBOSE]: SyncPair PR (8:czechboy0-patch-3) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #8) finished sync after 0.186 seconds. +[VERBOSE]: SyncPair PR (9:czechboy0-patch-6) + Bot (BuildaBot [czechboy0/Buildasaur-Tester] PR #9) finished sync after 0.191 seconds. +[INFO]: Successfully created bot BuildaBot [czechboy0/Buildasaur-Tester] |-> master +[VERBOSE]: SyncPair Branch (master) + No Bot finished sync after 3.97 seconds. +[INFO]: GitHub Rate Limit: count: 0/5000, renews in 3593 seconds, rate: 0.0/1.38, using 0.0% of the allowed request rate. +[INFO]: Sync finished successfully at 2015-10-12 14:00:38 +0000, took 4.027 seconds. diff --git a/BuildaKitTests/Migration/Buildasaur-format-4-example1/Projects.json b/BuildaKitTests/Migration/Buildasaur-format-4-example1/Projects.json new file mode 100644 index 0000000..a82d7e1 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-4-example1/Projects.json @@ -0,0 +1,14 @@ +[ + { + "ssh_public_key_url" : "\/Users\/honzadvorsky\/.ssh\/id_rsa.pub", + "id" : "4E8E7708-01FB-448A-B929-A54887CC5857", + "url" : "\/Users\/honzadvorsky\/Documents\/Buildasaur-Tester\/Buildasaur-Tester.xcodeproj", + "ssh_private_key_url" : "\/Users\/honzadvorsky\/.ssh\/id_rsa" + }, + { + "ssh_public_key_url" : "\/Users\/honzadvorsky\/.ssh\/id_rsa.pub", + "id" : "D316F062-7FEC-497A-B20E-6776AA413009", + "url" : "\/Users\/honzadvorsky\/Documents\/Buildasaur\/Buildasaur.xcodeproj", + "ssh_private_key_url" : "\/Users\/honzadvorsky\/.ssh\/id_rsa" + } +] \ No newline at end of file diff --git a/BuildaKitTests/Migration/Buildasaur-format-4-example1/ServerConfigs.json b/BuildaKitTests/Migration/Buildasaur-format-4-example1/ServerConfigs.json new file mode 100644 index 0000000..caa2de8 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-4-example1/ServerConfigs.json @@ -0,0 +1,12 @@ +[ + { + "id" : "D143B09C-CB1B-4831-8BA1-E2F8AB039B56", + "host" : "https:\/\/127.0.0.1", + "user" : "testadmin1" + }, + { + "id" : "76826238-64AE-4F48-B4B8-52A6B7A65EE4", + "host" : "https:\/\/localhost", + "user" : "testadmin8" + } +] \ No newline at end of file diff --git a/BuildaKitTests/Migration/Buildasaur-format-4-example1/Syncers.json b/BuildaKitTests/Migration/Buildasaur-format-4-example1/Syncers.json new file mode 100644 index 0000000..c75f4f4 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-4-example1/Syncers.json @@ -0,0 +1,26 @@ +[ + { + "preferred_template_ref" : "B31C6530-BB84-4A93-B08C-54074AAB5F37", + "id" : "564C267D-FF06-4008-9EF6-66B3AC1A3BDE", + "sync_interval" : 19, + "server_ref" : "D143B09C-CB1B-4831-8BA1-E2F8AB039B56", + "wait_for_lttm" : false, + "watched_branches" : [ + "master" + ], + "project_ref" : "4E8E7708-01FB-448A-B929-A54887CC5857", + "post_status_comments" : false + }, + { + "preferred_template_ref" : "9B53CC35-57DA-4DA0-9B85-05FCF109512A", + "id" : "B3A35C28-2176-4D88-8F60-5C769AEDBB2E", + "sync_interval" : 15, + "server_ref" : "76826238-64AE-4F48-B4B8-52A6B7A65EE4", + "wait_for_lttm" : false, + "watched_branches" : [ + "master" + ], + "project_ref" : "D316F062-7FEC-497A-B20E-6776AA413009", + "post_status_comments" : false + } +] \ No newline at end of file diff --git a/BuildaKitTests/Migration/Buildasaur-format-4-example1/Triggers/4E66D0D5-D5CC-417E-A40E-73B513CE4E10.json b/BuildaKitTests/Migration/Buildasaur-format-4-example1/Triggers/4E66D0D5-D5CC-417E-A40E-73B513CE4E10.json new file mode 100644 index 0000000..b480ff1 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-4-example1/Triggers/4E66D0D5-D5CC-417E-A40E-73B513CE4E10.json @@ -0,0 +1,25 @@ +{ + "phase" : 2, + "id" : "4E66D0D5-D5CC-417E-A40E-73B513CE4E10", + "scriptBody" : "", + "conditions" : { + "status" : 2, + "onWarnings" : true, + "onBuildErrors" : true, + "onInternalErrors" : true, + "onAnalyzerWarnings" : true, + "onFailingTests" : true, + "onSuccess" : true + }, + "type" : 2, + "name" : "Postbuild Email", + "emailConfiguration" : { + "includeCommitMessages" : true, + "additionalRecipients" : [ + "h@d.com", + "yo@ma.lo" + ], + "emailCommitters" : false, + "includeIssueDetails" : true + } +} \ No newline at end of file diff --git a/BuildaKitTests/Migration/Buildasaur-format-4-example1/Triggers/E8F5285A-A262-4630-AF7B-236772B75760.json b/BuildaKitTests/Migration/Buildasaur-format-4-example1/Triggers/E8F5285A-A262-4630-AF7B-236772B75760.json new file mode 100644 index 0000000..5c263b7 --- /dev/null +++ b/BuildaKitTests/Migration/Buildasaur-format-4-example1/Triggers/E8F5285A-A262-4630-AF7B-236772B75760.json @@ -0,0 +1,7 @@ +{ + "phase" : 1, + "scriptBody" : "echo \"hello\"\n", + "id" : "E8F5285A-A262-4630-AF7B-236772B75760", + "type" : 1, + "name" : "Prebuild Script" +} \ No newline at end of file diff --git a/BuildaKitTests/MigrationTests.swift b/BuildaKitTests/MigrationTests.swift index 0b4c196..1bcb5fe 100644 --- a/BuildaKitTests/MigrationTests.swift +++ b/BuildaKitTests/MigrationTests.swift @@ -137,7 +137,26 @@ class MigrationTests: XCTestCase { fail("\(error)") } } + + func testMigration_v3_v4() { + + let readingURL = self.resourceURLFromTestBundle("Buildasaur-format-3-example2") + let writingURL = self.writingURL("v3-v4") + let expectedURL = self.resourceURLFromTestBundle("Buildasaur-format-4-example1") + + let fileManager = NSFileManager.defaultManager() + let persistence = Persistence(readingFolder: readingURL, writingFolder: writingURL, fileManager: fileManager) + let migrator = Migrator_v3_v4(persistence: persistence) + + do { + try migrator.attemptMigration() + try self.ensureEqualHierarchies(persistence, urlExpected: expectedURL, urlReal: writingURL) + } catch { + fail("\(error)") + } + } + func testPersistenceSetter() { let tmp = NSURL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) diff --git a/Buildasaur.xcodeproj/project.pbxproj b/Buildasaur.xcodeproj/project.pbxproj index 83933f3..0832e4a 100644 --- a/Buildasaur.xcodeproj/project.pbxproj +++ b/Buildasaur.xcodeproj/project.pbxproj @@ -10,6 +10,9 @@ 079FD08F18890B9ED0EEC9A5 /* Pods_BuildaKitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BB2056DC704255FB4A676BA5 /* Pods_BuildaKitTests.framework */; }; 0E33784B1AC8F17E34DEEB74 /* Pods_Buildasaur.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0E259E9106C895130138567A /* Pods_Buildasaur.framework */; }; 15A92AB3A2DE8DA7A566FB3F /* Pods_BuildaGitServerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 16B84F653EF9BEA46717FE32 /* Pods_BuildaGitServerTests.framework */; }; + 3A014BA51C57A94B00B82B4A /* Authentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A014BA41C57A94B00B82B4A /* Authentication.swift */; }; + 3A08B3611C58301D00ED7A1F /* github.png in Resources */ = {isa = PBXBuildFile; fileRef = 3A08B3601C58301D00ED7A1F /* github.png */; }; + 3A08B3641C5837EA00ED7A1F /* ServiceAuthentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A08B3631C5837EA00ED7A1F /* ServiceAuthentication.swift */; }; 3A0FF5A01BBFDA7E00FB8051 /* RACUIExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A0FF59F1BBFDA7E00FB8051 /* RACUIExtensions.swift */; }; 3A0FF5A21BBFE8BA00FB8051 /* SyncerConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A0FF5A11BBFE8BA00FB8051 /* SyncerConfig.swift */; }; 3A0FF5A41BBFF9FA00FB8051 /* ProjectConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A0FF5A31BBFF9FA00FB8051 /* ProjectConfig.swift */; }; @@ -134,6 +137,7 @@ 3AED13151C52A1A300E3B7FF /* SecurePersistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AED13141C52A1A300E3B7FF /* SecurePersistence.swift */; }; 3AF090B81B1134AA0058567F /* BranchWatchingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF090B71B1134AA0058567F /* BranchWatchingViewController.swift */; }; 3AF1B1241AAC7CA500917EF3 /* SyncerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF1B1231AAC7CA500917EF3 /* SyncerViewController.swift */; }; + 3AFA31E21C57FA0B00F40175 /* Buildasaur-format-4-example1 in Resources */ = {isa = PBXBuildFile; fileRef = 3AFA31E11C57FA0B00F40175 /* Buildasaur-format-4-example1 */; }; 723E971AA98B199C70B7C3AA /* Pods_BuildaHeartbeatKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 406E01BAFEA2D2588BFD2D5A /* Pods_BuildaHeartbeatKit.framework */; }; BB73AD3BDA259AB9818479B0 /* Pods_BuildaKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2264BCD5DCD655137828C758 /* Pods_BuildaKit.framework */; }; E27B6BBE05DDA405B130E5AA /* Pods_BuildaGitServer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 01DD14FE852793A94935C322 /* Pods_BuildaGitServer.framework */; }; @@ -224,6 +228,9 @@ 17FE44590CED1491A8F03F35 /* Pods-BuildaKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaKit/Pods-BuildaKit.debug.xcconfig"; sourceTree = ""; }; 2140AD08ECF0B73145689AAD /* Pods-BuildaGitServer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaGitServer.debug.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaGitServer/Pods-BuildaGitServer.debug.xcconfig"; sourceTree = ""; }; 2264BCD5DCD655137828C758 /* Pods_BuildaKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BuildaKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 3A014BA41C57A94B00B82B4A /* Authentication.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Authentication.swift; sourceTree = ""; }; + 3A08B3601C58301D00ED7A1F /* github.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = github.png; path = Meta/github.png; sourceTree = SOURCE_ROOT; }; + 3A08B3631C5837EA00ED7A1F /* ServiceAuthentication.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ServiceAuthentication.swift; sourceTree = ""; }; 3A0FF59F1BBFDA7E00FB8051 /* RACUIExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RACUIExtensions.swift; sourceTree = ""; }; 3A0FF5A11BBFE8BA00FB8051 /* SyncerConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncerConfig.swift; sourceTree = ""; }; 3A0FF5A31BBFF9FA00FB8051 /* ProjectConfig.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProjectConfig.swift; sourceTree = ""; }; @@ -351,6 +358,7 @@ 3AED13141C52A1A300E3B7FF /* SecurePersistence.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SecurePersistence.swift; sourceTree = ""; }; 3AF090B71B1134AA0058567F /* BranchWatchingViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BranchWatchingViewController.swift; sourceTree = ""; }; 3AF1B1231AAC7CA500917EF3 /* SyncerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SyncerViewController.swift; sourceTree = ""; }; + 3AFA31E11C57FA0B00F40175 /* Buildasaur-format-4-example1 */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "Buildasaur-format-4-example1"; path = "Migration/Buildasaur-format-4-example1"; sourceTree = ""; }; 406E01BAFEA2D2588BFD2D5A /* Pods_BuildaHeartbeatKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BuildaHeartbeatKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 45EC0AC30FBA8A17E280ABB8 /* Pods-BuildaHeartbeatKit.testing.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaHeartbeatKit.testing.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaHeartbeatKit/Pods-BuildaHeartbeatKit.testing.xcconfig"; sourceTree = ""; }; 4E3042CD161DCBFC84D300DD /* Pods-Buildasaur.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Buildasaur.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Buildasaur/Pods-Buildasaur.debug.xcconfig"; sourceTree = ""; }; @@ -452,6 +460,22 @@ name = Pods; sourceTree = ""; }; + 3A08B35D1C582FF700ED7A1F /* Service Logos */ = { + isa = PBXGroup; + children = ( + 3A08B3601C58301D00ED7A1F /* github.png */, + ); + name = "Service Logos"; + sourceTree = ""; + }; + 3A08B3621C5837D400ED7A1F /* Authentication */ = { + isa = PBXGroup; + children = ( + 3A08B3631C5837EA00ED7A1F /* ServiceAuthentication.swift */, + ); + name = Authentication; + sourceTree = ""; + }; 3A0FF59E1BBFDA5C00FB8051 /* Utils */ = { isa = PBXGroup; children = ( @@ -467,6 +491,7 @@ children = ( 3A35CD6C1BE96E9B0088D57A /* BaseTypes.swift */, 3A35CD6D1BE96E9B0088D57A /* SourceServerExtensions.swift */, + 3A014BA41C57A94B00B82B4A /* Authentication.swift */, ); path = Base; sourceTree = ""; @@ -510,6 +535,7 @@ 3AAA1B631AAB6E6800FA1598 /* ViewController */, 3AE0AB291BB9919700A52D20 /* ViewModels */, 3AAA1B641AAB6E8500FA1598 /* Views */, + 3A08B3621C5837D400ED7A1F /* Authentication */, 3A0FF59E1BBFDA5C00FB8051 /* Utils */, ); path = Buildasaur; @@ -518,6 +544,7 @@ 3A5687731A3B93BD0066DB2B /* Supporting Files */ = { isa = PBXGroup; children = ( + 3A08B35D1C582FF700ED7A1F /* Service Logos */, 3A5687741A3B93BD0066DB2B /* Info.plist */, 3A9BB91C1BCEFE8F0049C0C0 /* launch_item.plist */, ); @@ -616,6 +643,7 @@ 3A9D74251BCC19BB00DCA23C /* Buildasaur-format-2-example1 */, 3AE8B79A1C52DAD20033F6EF /* Buildasaur-format-2-example2 */, 3AED13121C529BB600E3B7FF /* Buildasaur-format-3-example1 */, + 3AFA31E11C57FA0B00F40175 /* Buildasaur-format-4-example1 */, 3A9D741F1BCBF87900DCA23C /* MigrationTests.swift */, ); name = Migration; @@ -1069,6 +1097,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 3A08B3611C58301D00ED7A1F /* github.png in Resources */, 3A56877A1A3B93BD0066DB2B /* Images.xcassets in Resources */, 3A56877D1A3B93BD0066DB2B /* Main.storyboard in Resources */, 3A9BB91D1BCEFE8F0049C0C0 /* launch_item.plist in Resources */, @@ -1096,6 +1125,7 @@ 3A9D74221BCC041600DCA23C /* Buildasaur-format-0-example1 in Resources */, 3AED13131C529BB600E3B7FF /* Buildasaur-format-3-example1 in Resources */, 3ACBADDD1B5ADE1400204457 /* sampleFinishedIntegration.json in Resources */, + 3AFA31E21C57FA0B00F40175 /* Buildasaur-format-4-example1 in Resources */, 3A9D74241BCC103E00DCA23C /* Buildasaur-format-1-example1 in Resources */, 3AE8B79B1C52DAD20033F6EF /* Buildasaur-format-2-example2 in Resources */, 3A9D74261BCC19BB00DCA23C /* Buildasaur-format-2-example1 in Resources */, @@ -1378,6 +1408,7 @@ 3A9109C31BC2B05C00C2AECA /* MainEditor_EditeeDelegate.swift in Sources */, 3A9109BD1BC27C5500C2AECA /* EditorState.swift in Sources */, 3AAA1B681AAB722600FA1598 /* ProjectViewController.swift in Sources */, + 3A08B3641C5837EA00ED7A1F /* ServiceAuthentication.swift in Sources */, 3AB3FDCB1B05644300021197 /* MenuItemManager.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1480,6 +1511,7 @@ 3AB3F0E91A3CEBAC005F717F /* GitHubFactory.swift in Sources */, 3A35CD6F1BE96E9B0088D57A /* SourceServerExtensions.swift in Sources */, 3A3BDC1F1AF6D34900D2CD99 /* GitHubRateLimit.swift in Sources */, + 3A014BA51C57A94B00B82B4A /* Authentication.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Buildasaur/AppDelegate.swift b/Buildasaur/AppDelegate.swift index 541bfd8..24306e9 100644 --- a/Buildasaur/AppDelegate.swift +++ b/Buildasaur/AppDelegate.swift @@ -24,6 +24,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { var syncerManager: SyncerManager! let menuItemManager = MenuItemManager() + let serviceAuthenticator = ServiceAuthenticator() var storyboardLoader: StoryboardLoader! @@ -38,6 +39,8 @@ class AppDelegate: NSObject, NSApplicationDelegate { #else self.setup() #endif + + } func setup() { @@ -47,6 +50,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { // defs.setBool(true, forKey: "NSConstraintBasedLayoutVisualizeMutuallyExclusiveConstraints") // defs.synchronize() + self.setupURLCallback() self.setupPersistence() self.storyboardLoader = StoryboardLoader(storyboard: NSStoryboard.mainStoryboard) @@ -61,6 +65,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { self.dashboardWindow = self.windowForPresentableViewControllerWithIdentifier("dashboard")!.0 } + func migratePersistence(persistence: Persistence) { let fileManager = NSFileManager.defaultManager() @@ -107,9 +112,19 @@ class AppDelegate: NSObject, NSApplicationDelegate { let dashboard: DashboardViewController = self.storyboardLoader .presentableViewControllerWithStoryboardIdentifier("dashboardViewController", uniqueIdentifier: "dashboard", delegate: self) dashboard.syncerManager = self.syncerManager + dashboard.serviceAuthenticator = self.serviceAuthenticator return dashboard } + func handleUrl(url: NSURL) { + + print("Handling incoming url") + + if url.host == "oauth-callback" { + self.serviceAuthenticator.handleUrl(url) + } + } + func applicationShouldHandleReopen(sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool { self.showMainWindow() @@ -157,6 +172,23 @@ class AppDelegate: NSObject, NSApplicationDelegate { } } +extension AppDelegate { + + func setupURLCallback() { + + // listen to scheme url + NSAppleEventManager.sharedAppleEventManager().setEventHandler(self, andSelector:"handleGetURLEvent:withReplyEvent:", forEventClass: AEEventClass(kInternetEventClass), andEventID: AEEventID(kAEGetURL)) + } + + func handleGetURLEvent(event: NSAppleEventDescriptor!, withReplyEvent: NSAppleEventDescriptor!) { + if let urlString = event.paramDescriptorForKeyword(AEKeyword(keyDirectObject))?.stringValue, url = NSURL(string: urlString) { + + //handle url + self.handleUrl(url) + } + } +} + extension AppDelegate: PresentableViewControllerDelegate { func configureViewController(viewController: PresentableViewController) { diff --git a/Buildasaur/Base.lproj/Main.storyboard b/Buildasaur/Base.lproj/Main.storyboard index 8083c15..08ae70f 100644 --- a/Buildasaur/Base.lproj/Main.storyboard +++ b/Buildasaur/Base.lproj/Main.storyboard @@ -877,7 +877,7 @@