From ae175e92dd9a98207490e7d3294da6fb6c2f375c Mon Sep 17 00:00:00 2001 From: Rachel Caileff Date: Wed, 24 Feb 2016 17:40:45 -0600 Subject: [PATCH 01/14] Fix this: Use of unresolved identifier 'BuildasaurKeys' Update GitServerPublic.swift to call BuildasaurxcodeprojKeys instead. --- BuildaGitServer/GitServerPublic.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/BuildaGitServer/GitServerPublic.swift b/BuildaGitServer/GitServerPublic.swift index bf34d1e..7b449d4 100644 --- a/BuildaGitServer/GitServerPublic.swift +++ b/BuildaGitServer/GitServerPublic.swift @@ -54,15 +54,15 @@ public enum GitService: String { public func serviceKey() -> String { switch self { - case .GitHub: return BuildasaurKeys().gitHubAPIClientId() - case .BitBucket: return BuildasaurKeys().bitBucketAPIClientId() + case .GitHub: return BuildasaurxcodeprojKeys().gitHubAPIClientId() + case .BitBucket: return BuildasaurxcodeprojKeys().bitBucketAPIClientId() } } public func serviceSecret() -> String { switch self { - case .GitHub: return BuildasaurKeys().gitHubAPIClientSecret() - case .BitBucket: return BuildasaurKeys().bitBucketAPIClientSecret() + case .GitHub: return BuildasaurxcodeprojKeys().gitHubAPIClientSecret() + case .BitBucket: return BuildasaurxcodeprojKeys().bitBucketAPIClientSecret() } } } From bef29bbd02d50e636ad4bf1c3704040de5f9564f Mon Sep 17 00:00:00 2001 From: Rachel Caileff Date: Wed, 9 Mar 2016 10:41:19 -0600 Subject: [PATCH 02/14] Implement support for Enterprise GitHub using Personal Tokens. Add .EnterpriseGitHub to GitService enum. Change GitService to use an associated value for .EnterpriseGitHub's host. Get the enterprise server's host from the git repo in the keychain. Add and use GitService.type() because the new version of GitService breaks things like "==" and using the rawValue as a dictionary key. Assert that OAuth client/secret will not be use for Enterprise GitHub. Fail horribly if someone tries. Add a couple of EnterpriseGitHubTests that are commented out because they fail without a repo to contact. Replaced hardcoded strings in WorkspaceMetadata with calls to GitService.foo.hostname(). Move some ProjectViewController.ConfigEditViewController logic into the switch statement because the code had assumed anything other than .BitBucket was .GitHub. Also switch BuildasaurxcodeprojKeys back to BuildasaurKeys to be consistent with master. --- BuildaGitServer/Base/Authentication.swift | 14 +++- BuildaGitServer/Base/BaseTypes.swift | 2 +- BuildaGitServer/Base/GitServerFactory.swift | 5 ++ BuildaGitServer/GitServerPublic.swift | 32 +++++-- .../EnterpriseGitHubSourceTests.swift | 83 +++++++++++++++++++ BuildaKit/SyncerManager.swift | 2 +- BuildaKit/WorkspaceMetadata.swift | 7 +- Buildasaur.xcodeproj/project.pbxproj | 4 + Buildasaur/Base.lproj/Main.storyboard | 4 +- Buildasaur/ProjectViewController.swift | 29 +++++-- Buildasaur/ServiceAuthentication.swift | 2 + 11 files changed, 157 insertions(+), 27 deletions(-) create mode 100644 BuildaGitServerTests/EnterpriseGitHubSourceTests.swift diff --git a/BuildaGitServer/Base/Authentication.swift b/BuildaGitServer/Base/Authentication.swift index 9a465a3..f9020fb 100644 --- a/BuildaGitServer/Base/Authentication.swift +++ b/BuildaGitServer/Base/Authentication.swift @@ -40,9 +40,17 @@ extension ProjectAuthenticator: KeychainStringSerializable { 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])") + + var service: GitService + switch comps[0] { + case GitService.GitHub.hostname(): + service = GitService.GitHub + case GitService.BitBucket.hostname(): + service = GitService.BitBucket + default: + service = GitService.EnterpriseGitHub(host: comps[0]) } + guard let type = ProjectAuthenticator.AuthType(rawValue: comps[2]) else { throw Error.withInfo("Unsupported auth type: \(comps[2])") } @@ -55,7 +63,7 @@ extension ProjectAuthenticator: KeychainStringSerializable { public func toString() -> String { return [ - self.service.rawValue, + self.service.hostname(), self.username, self.type.rawValue, self.secret diff --git a/BuildaGitServer/Base/BaseTypes.swift b/BuildaGitServer/Base/BaseTypes.swift index b05f988..786977f 100644 --- a/BuildaGitServer/Base/BaseTypes.swift +++ b/BuildaGitServer/Base/BaseTypes.swift @@ -35,7 +35,7 @@ public class SourceServerFactory { public func createServer(service: GitService, auth: ProjectAuthenticator?) -> SourceServerType { if let auth = auth { - precondition(service == auth.service) + precondition(service.type() == auth.service.type()) } return GitServerFactory.server(service, auth: auth) diff --git a/BuildaGitServer/Base/GitServerFactory.swift b/BuildaGitServer/Base/GitServerFactory.swift index a2f1186..8ccc698 100644 --- a/BuildaGitServer/Base/GitServerFactory.swift +++ b/BuildaGitServer/Base/GitServerFactory.swift @@ -20,6 +20,11 @@ class GitServerFactory { let baseURL = "https://api.github.com" let endpoints = GitHubEndpoints(baseURL: baseURL, auth: auth) server = GitHubServer(endpoints: endpoints, http: http) + case .GitHub, .EnterpriseGitHub: + //let baseURL = "https://api.\(service.hostname())" + let baseURL = "https://\(service.hostname())" + let endpoints = GitHubEndpoints(baseURL: baseURL, auth: auth) + server = GitHubServer(endpoints: endpoints, http: http) case .BitBucket: let baseURL = "https://api.bitbucket.org" let endpoints = BitBucketEndpoints(baseURL: baseURL, auth: auth) diff --git a/BuildaGitServer/GitServerPublic.swift b/BuildaGitServer/GitServerPublic.swift index 7b449d4..82fc5f4 100644 --- a/BuildaGitServer/GitServerPublic.swift +++ b/BuildaGitServer/GitServerPublic.swift @@ -12,14 +12,24 @@ import Keys import ReactiveCocoa import Result -public enum GitService: String { - case GitHub = "github" - case BitBucket = "bitbucket" +public enum GitService { + case GitHub + case EnterpriseGitHub(host: String) + case BitBucket // case GitLab = "gitlab" - + + public func type() -> String { + switch self { + case .GitHub: return "github" + case .EnterpriseGitHub: return "enterprisegithub" + case .BitBucket: return "bitbucket" + } + } + public func prettyName() -> String { switch self { case .GitHub: return "GitHub" + case .EnterpriseGitHub: return "EnterpriseGitHub" case .BitBucket: return "BitBucket" } } @@ -27,6 +37,7 @@ public enum GitService: String { public func logoName() -> String { switch self { case .GitHub: return "github" + case .EnterpriseGitHub: return "enterprisegithub" case .BitBucket: return "bitbucket" } } @@ -34,6 +45,7 @@ public enum GitService: String { public func hostname() -> String { switch self { case .GitHub: return "github.com" + case .EnterpriseGitHub(let host): return host case .BitBucket: return "bitbucket.org" } } @@ -41,6 +53,7 @@ public enum GitService: String { public func authorizeUrl() -> String { switch self { case .GitHub: return "https://github.com/login/oauth/authorize" + case .EnterpriseGitHub: return "https://\(hostname())/login/oauth/authorize" case .BitBucket: return "https://bitbucket.org/site/oauth2/authorize" } } @@ -48,21 +61,24 @@ public enum GitService: String { public func accessTokenUrl() -> String { switch self { case .GitHub: return "https://github.com/login/oauth/access_token" + case .EnterpriseGitHub: assert(false) case .BitBucket: return "https://bitbucket.org/site/oauth2/access_token" } } public func serviceKey() -> String { switch self { - case .GitHub: return BuildasaurxcodeprojKeys().gitHubAPIClientId() - case .BitBucket: return BuildasaurxcodeprojKeys().bitBucketAPIClientId() + case .GitHub: return BuildasaurKeys().gitHubAPIClientId() + case .EnterpriseGitHub: assert(false) + case .BitBucket: return BuildasaurKeys().bitBucketAPIClientId() } } public func serviceSecret() -> String { switch self { - case .GitHub: return BuildasaurxcodeprojKeys().gitHubAPIClientSecret() - case .BitBucket: return BuildasaurxcodeprojKeys().bitBucketAPIClientSecret() + case .GitHub: return BuildasaurKeys().gitHubAPIClientSecret() + case .EnterpriseGitHub: assert(false) + case .BitBucket: return BuildasaurKeys().bitBucketAPIClientSecret() } } } diff --git a/BuildaGitServerTests/EnterpriseGitHubSourceTests.swift b/BuildaGitServerTests/EnterpriseGitHubSourceTests.swift new file mode 100644 index 0000000..aa8d26e --- /dev/null +++ b/BuildaGitServerTests/EnterpriseGitHubSourceTests.swift @@ -0,0 +1,83 @@ +// +// EnterpriseGitHubSourceTests.swift +// Buildasaur +// +// Created by Rachel Caileff on 3/8/16. +// Copyright © 2016 Honza Dvorsky. All rights reserved. +// + +import Cocoa +import XCTest +@testable import BuildaGitServer +import BuildaUtils + +class EnterpriseGitHubSourceTests: XCTestCase { + + var github: GitHubServer! + + override func setUp() { + super.setUp() + + self.github = GitServerFactory.server(.EnterpriseGitHub(host: "git.mycompany.com"), auth: nil) as! GitHubServer // TODO: fill in accessible enterprise github host + } + + 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": "my/repo" // TODO: fill in accessible enterprise github repo +// ] +// +// 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: [GitHubPullRequest] = 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": "my/repo" // TODO: fill in accessible enterprise github repo +// ] +// +// 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: [GitHubBranch] = GitHubArray(body) +// XCTAssertGreaterThan(branches.count, 0, "We need > 0 items to test parsing") +// Log.verbose("Parsed branches: \(branches)") +// } else { +// XCTFail("Body nil") +// } +// } +// } +} diff --git a/BuildaKit/SyncerManager.swift b/BuildaKit/SyncerManager.swift index 7d72c5c..0295c66 100644 --- a/BuildaKit/SyncerManager.swift +++ b/BuildaKit/SyncerManager.swift @@ -116,7 +116,7 @@ extension SyncerManager: HeartbeatManagerDelegate { public func typesOfRunningSyncers() -> [String : Int] { return self.syncers.filter { $0.active }.reduce([:]) { (all, syncer) -> [String: Int] in var stats = all - let syncerType = syncer._project.workspaceMetadata!.service.rawValue + let syncerType = syncer._project.workspaceMetadata!.service.type() stats[syncerType] = (stats[syncerType] ?? 0) + 1 return stats } diff --git a/BuildaKit/WorkspaceMetadata.swift b/BuildaKit/WorkspaceMetadata.swift index 3fb4fdd..664ee6f 100644 --- a/BuildaKit/WorkspaceMetadata.swift +++ b/BuildaKit/WorkspaceMetadata.swift @@ -78,9 +78,9 @@ extension WorkspaceMetadata { let scheme = NSURL(string: urlString)!.scheme switch scheme { - case "github.com": + case GitService.GitHub.hostname(): return (CheckoutType.SSH, .GitHub) - case "bitbucket.org": + case GitService.BitBucket.hostname(): return (CheckoutType.SSH, .BitBucket) case "https": @@ -93,7 +93,8 @@ extension WorkspaceMetadata { Log.error("HTTPS or SVN not yet supported, please create an issue on GitHub if you want it added (czechboy0/Buildasaur)") return nil default: - return nil + var urlPieces = urlString.split(":") + return (CheckoutType.SSH, .EnterpriseGitHub(host: urlPieces[0])) } } } diff --git a/Buildasaur.xcodeproj/project.pbxproj b/Buildasaur.xcodeproj/project.pbxproj index bcfb714..7ab259a 100644 --- a/Buildasaur.xcodeproj/project.pbxproj +++ b/Buildasaur.xcodeproj/project.pbxproj @@ -157,6 +157,7 @@ 50ADEFE7D2EF4699DED04224 /* Pods_BuildaGitServer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36699A7FE0D3AFD88AD7B045 /* Pods_BuildaGitServer.framework */; }; 56C0A05D3B3E3581DEEF4FB0 /* Pods_BuildaKitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BF6053CD3D62FC0E0D73A25 /* Pods_BuildaKitTests.framework */; }; 78D6132977F05F7A73A1B808 /* Pods_Buildasaur.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 93327F5F8E340696C059C413 /* Pods_Buildasaur.framework */; }; + 8476D4061C8F4CD000463074 /* EnterpriseGitHubSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8476D4051C8F4CD000463074 /* EnterpriseGitHubSourceTests.swift */; }; C36E3439B05CEFA228E45AE3 /* Pods_BuildaGitServerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 84C28C2D0A7294FD001BDF5B /* Pods_BuildaGitServerTests.framework */; }; CF8249D720271FDB033C7CF8 /* Pods_BuildaHeartbeatKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72A661282067D9AFD99D4095 /* Pods_BuildaHeartbeatKit.framework */; }; /* End PBXBuildFile section */ @@ -399,6 +400,7 @@ 5F760D488BD75B28665F9576 /* Pods-BuildaKit.testing.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaKit.testing.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaKit/Pods-BuildaKit.testing.xcconfig"; sourceTree = ""; }; 6358BD9D54AF08FBB17D6657 /* 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 = ""; }; 72A661282067D9AFD99D4095 /* Pods_BuildaHeartbeatKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BuildaHeartbeatKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 8476D4051C8F4CD000463074 /* EnterpriseGitHubSourceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnterpriseGitHubSourceTests.swift; sourceTree = ""; }; 84C28C2D0A7294FD001BDF5B /* Pods_BuildaGitServerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BuildaGitServerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 8D9CBDB5469FB644E5584126 /* Pods-Buildasaur.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Buildasaur.release.xcconfig"; path = "Pods/Target Support Files/Pods-Buildasaur/Pods-Buildasaur.release.xcconfig"; sourceTree = ""; }; 93327F5F8E340696C059C413 /* Pods_Buildasaur.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Buildasaur.framework; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -729,6 +731,7 @@ 3A7577A91C593A6A0023F08C /* Data */, 3A7AF60F1C591FAD000FD726 /* BitBucketServerTests.swift */, 3A58B1581A3B96C9003E0266 /* GitHubServerTests.swift */, + 8476D4051C8F4CD000463074 /* EnterpriseGitHubSourceTests.swift */, 3AAF6EF51A3CE5BA00C657FB /* Supporting Files */, ); path = BuildaGitServerTests; @@ -1622,6 +1625,7 @@ buildActionMask = 2147483647; files = ( 3A7AF6101C591FAD000FD726 /* BitBucketServerTests.swift in Sources */, + 8476D4061C8F4CD000463074 /* EnterpriseGitHubSourceTests.swift in Sources */, 3AAF6F0B1A3CE82B00C657FB /* GitHubServerTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Buildasaur/Base.lproj/Main.storyboard b/Buildasaur/Base.lproj/Main.storyboard index fd90fcf..ab2c74d 100644 --- a/Buildasaur/Base.lproj/Main.storyboard +++ b/Buildasaur/Base.lproj/Main.storyboard @@ -1,8 +1,8 @@ - + - + diff --git a/Buildasaur/ProjectViewController.swift b/Buildasaur/ProjectViewController.swift index 22f069a..5b4406a 100644 --- a/Buildasaur/ProjectViewController.swift +++ b/Buildasaur/ProjectViewController.swift @@ -115,13 +115,13 @@ class ProjectViewController: ConfigEditViewController { .startWithNext { [weak self] (proj, auth, forceUseToken) in self?.updateServiceMeta(proj, auth: auth, userWantsTokenAuth: forceUseToken) } - combineLatest(self.tokenTextField.rac_text, self.userWantsTokenAuth.producer) - .startWithNext { [weak self] token, forceToken in + combineLatest(self.tokenTextField.rac_text, self.userWantsTokenAuth.producer, meta) + .startWithNext { [weak self] token, forceToken, meta in if forceToken { if token.isEmpty { self?.authenticator.value = nil } else { - self?.authenticator.value = ProjectAuthenticator(service: .GitHub, username: "GIT", type: .PersonalToken, secret: token) + self?.authenticator.value = ProjectAuthenticator(service: meta.service, username: "GIT", type: .PersonalToken, secret: token) } } } @@ -166,22 +166,33 @@ class ProjectViewController: ConfigEditViewController { let alreadyHasAuth = auth != nil + var showTokenField = false + switch service { - case .GitHub: + case GitService.GitHub: if let auth = auth where auth.type == .PersonalToken && !auth.secret.isEmpty { self.tokenTextField.stringValue = auth.secret } else { self.tokenTextField.stringValue = "" } self.useTokenButton.hidden = alreadyHasAuth - case .BitBucket: + self.loginButton.hidden = alreadyHasAuth + self.logoutButton.hidden = !alreadyHasAuth + showTokenField = userWantsTokenAuth && (auth?.type == .PersonalToken || auth == nil) + case GitService.EnterpriseGitHub: + if !alreadyHasAuth { + self.tokenTextField.stringValue = "" + } + self.useTokenButton.hidden = alreadyHasAuth + self.loginButton.hidden = true + self.logoutButton.hidden = true + showTokenField = true + case GitService.BitBucket: self.useTokenButton.hidden = true + self.loginButton.hidden = alreadyHasAuth + self.logoutButton.hidden = !alreadyHasAuth } - self.loginButton.hidden = alreadyHasAuth - self.logoutButton.hidden = !alreadyHasAuth - - let showTokenField = userWantsTokenAuth && service == .GitHub && (auth?.type == .PersonalToken || auth == nil) self.tokenStackView.hidden = !showTokenField } diff --git a/Buildasaur/ServiceAuthentication.swift b/Buildasaur/ServiceAuthentication.swift index 08b2ab1..93cbb04 100644 --- a/Buildasaur/ServiceAuthentication.swift +++ b/Buildasaur/ServiceAuthentication.swift @@ -66,6 +66,8 @@ class ServiceAuthenticator { switch service { case .GitHub: return self.getGitHubParameters() + case .EnterpriseGitHub: + assert(false) case .BitBucket: return self.getBitBucketParameters() // default: From fa8bc25d9dce5e741a676b36e723b7f2e1f77887 Mon Sep 17 00:00:00 2001 From: Rachel Caileff Date: Wed, 9 Mar 2016 11:09:15 -0600 Subject: [PATCH 03/14] Fix a couple of errors introduced when I removed a local hack --- BuildaGitServer/Base/GitServerFactory.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/BuildaGitServer/Base/GitServerFactory.swift b/BuildaGitServer/Base/GitServerFactory.swift index 8ccc698..eb91108 100644 --- a/BuildaGitServer/Base/GitServerFactory.swift +++ b/BuildaGitServer/Base/GitServerFactory.swift @@ -20,9 +20,8 @@ class GitServerFactory { let baseURL = "https://api.github.com" let endpoints = GitHubEndpoints(baseURL: baseURL, auth: auth) server = GitHubServer(endpoints: endpoints, http: http) - case .GitHub, .EnterpriseGitHub: - //let baseURL = "https://api.\(service.hostname())" - let baseURL = "https://\(service.hostname())" + case .EnterpriseGitHub: + let baseURL = "https://api.\(service.hostname())" let endpoints = GitHubEndpoints(baseURL: baseURL, auth: auth) server = GitHubServer(endpoints: endpoints, http: http) case .BitBucket: From ab06ee3ff05c99269d3ccaafd153e9c1eddf787d Mon Sep 17 00:00:00 2001 From: Rachel Caileff Date: Wed, 9 Mar 2016 13:52:50 -0600 Subject: [PATCH 04/14] Another assert that Enterprise GitHub doesn't do OAuth --- BuildaGitServer/GitServerPublic.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BuildaGitServer/GitServerPublic.swift b/BuildaGitServer/GitServerPublic.swift index 82fc5f4..7a4cd0a 100644 --- a/BuildaGitServer/GitServerPublic.swift +++ b/BuildaGitServer/GitServerPublic.swift @@ -53,7 +53,7 @@ public enum GitService { public func authorizeUrl() -> String { switch self { case .GitHub: return "https://github.com/login/oauth/authorize" - case .EnterpriseGitHub: return "https://\(hostname())/login/oauth/authorize" + case .EnterpriseGitHub: assert(false) case .BitBucket: return "https://bitbucket.org/site/oauth2/authorize" } } From d3e308a2b4598809573dd5aa6f4f39296d4d83e0 Mon Sep 17 00:00:00 2001 From: Rachel Caileff Date: Wed, 24 Feb 2016 17:40:45 -0600 Subject: [PATCH 05/14] Fix this: Use of unresolved identifier 'BuildasaurKeys' Update GitServerPublic.swift to call BuildasaurxcodeprojKeys instead. --- BuildaGitServer/GitServerPublic.swift | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/BuildaGitServer/GitServerPublic.swift b/BuildaGitServer/GitServerPublic.swift index bf34d1e..7b449d4 100644 --- a/BuildaGitServer/GitServerPublic.swift +++ b/BuildaGitServer/GitServerPublic.swift @@ -54,15 +54,15 @@ public enum GitService: String { public func serviceKey() -> String { switch self { - case .GitHub: return BuildasaurKeys().gitHubAPIClientId() - case .BitBucket: return BuildasaurKeys().bitBucketAPIClientId() + case .GitHub: return BuildasaurxcodeprojKeys().gitHubAPIClientId() + case .BitBucket: return BuildasaurxcodeprojKeys().bitBucketAPIClientId() } } public func serviceSecret() -> String { switch self { - case .GitHub: return BuildasaurKeys().gitHubAPIClientSecret() - case .BitBucket: return BuildasaurKeys().bitBucketAPIClientSecret() + case .GitHub: return BuildasaurxcodeprojKeys().gitHubAPIClientSecret() + case .BitBucket: return BuildasaurxcodeprojKeys().bitBucketAPIClientSecret() } } } From 066ecb7f3e8c8e1ced2cd6d35549f6835112010a Mon Sep 17 00:00:00 2001 From: Rachel Caileff Date: Wed, 9 Mar 2016 10:41:19 -0600 Subject: [PATCH 06/14] Implement support for Enterprise GitHub using Personal Tokens. Add .EnterpriseGitHub to GitService enum. Change GitService to use an associated value for .EnterpriseGitHub's host. Get the enterprise server's host from the git repo in the keychain. Add and use GitService.type() because the new version of GitService breaks things like "==" and using the rawValue as a dictionary key. Assert that OAuth client/secret will not be use for Enterprise GitHub. Fail horribly if someone tries. Add a couple of EnterpriseGitHubTests that are commented out because they fail without a repo to contact. Replaced hardcoded strings in WorkspaceMetadata with calls to GitService.foo.hostname(). Move some ProjectViewController.ConfigEditViewController logic into the switch statement because the code had assumed anything other than .BitBucket was .GitHub. Also switch BuildasaurxcodeprojKeys back to BuildasaurKeys to be consistent with master. --- BuildaGitServer/Base/Authentication.swift | 14 ++- BuildaGitServer/Base/BaseTypes.swift | 2 +- BuildaGitServer/Base/GitServerFactory.swift | 5 + BuildaGitServer/GitServerPublic.swift | 32 +++-- .../EnterpriseGitHubSourceTests.swift | 83 +++++++++++++ BuildaKit/SyncerManager.swift | 2 +- BuildaKit/WorkspaceMetadata.swift | 7 +- Buildasaur.xcodeproj/project.pbxproj | 110 +++++++++--------- Buildasaur/Base.lproj/Main.storyboard | 4 +- Buildasaur/ProjectViewController.swift | 29 +++-- Buildasaur/ServiceAuthentication.swift | 2 + 11 files changed, 211 insertions(+), 79 deletions(-) create mode 100644 BuildaGitServerTests/EnterpriseGitHubSourceTests.swift diff --git a/BuildaGitServer/Base/Authentication.swift b/BuildaGitServer/Base/Authentication.swift index 9a465a3..f9020fb 100644 --- a/BuildaGitServer/Base/Authentication.swift +++ b/BuildaGitServer/Base/Authentication.swift @@ -40,9 +40,17 @@ extension ProjectAuthenticator: KeychainStringSerializable { 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])") + + var service: GitService + switch comps[0] { + case GitService.GitHub.hostname(): + service = GitService.GitHub + case GitService.BitBucket.hostname(): + service = GitService.BitBucket + default: + service = GitService.EnterpriseGitHub(host: comps[0]) } + guard let type = ProjectAuthenticator.AuthType(rawValue: comps[2]) else { throw Error.withInfo("Unsupported auth type: \(comps[2])") } @@ -55,7 +63,7 @@ extension ProjectAuthenticator: KeychainStringSerializable { public func toString() -> String { return [ - self.service.rawValue, + self.service.hostname(), self.username, self.type.rawValue, self.secret diff --git a/BuildaGitServer/Base/BaseTypes.swift b/BuildaGitServer/Base/BaseTypes.swift index b05f988..786977f 100644 --- a/BuildaGitServer/Base/BaseTypes.swift +++ b/BuildaGitServer/Base/BaseTypes.swift @@ -35,7 +35,7 @@ public class SourceServerFactory { public func createServer(service: GitService, auth: ProjectAuthenticator?) -> SourceServerType { if let auth = auth { - precondition(service == auth.service) + precondition(service.type() == auth.service.type()) } return GitServerFactory.server(service, auth: auth) diff --git a/BuildaGitServer/Base/GitServerFactory.swift b/BuildaGitServer/Base/GitServerFactory.swift index a2f1186..8ccc698 100644 --- a/BuildaGitServer/Base/GitServerFactory.swift +++ b/BuildaGitServer/Base/GitServerFactory.swift @@ -20,6 +20,11 @@ class GitServerFactory { let baseURL = "https://api.github.com" let endpoints = GitHubEndpoints(baseURL: baseURL, auth: auth) server = GitHubServer(endpoints: endpoints, http: http) + case .GitHub, .EnterpriseGitHub: + //let baseURL = "https://api.\(service.hostname())" + let baseURL = "https://\(service.hostname())" + let endpoints = GitHubEndpoints(baseURL: baseURL, auth: auth) + server = GitHubServer(endpoints: endpoints, http: http) case .BitBucket: let baseURL = "https://api.bitbucket.org" let endpoints = BitBucketEndpoints(baseURL: baseURL, auth: auth) diff --git a/BuildaGitServer/GitServerPublic.swift b/BuildaGitServer/GitServerPublic.swift index 7b449d4..82fc5f4 100644 --- a/BuildaGitServer/GitServerPublic.swift +++ b/BuildaGitServer/GitServerPublic.swift @@ -12,14 +12,24 @@ import Keys import ReactiveCocoa import Result -public enum GitService: String { - case GitHub = "github" - case BitBucket = "bitbucket" +public enum GitService { + case GitHub + case EnterpriseGitHub(host: String) + case BitBucket // case GitLab = "gitlab" - + + public func type() -> String { + switch self { + case .GitHub: return "github" + case .EnterpriseGitHub: return "enterprisegithub" + case .BitBucket: return "bitbucket" + } + } + public func prettyName() -> String { switch self { case .GitHub: return "GitHub" + case .EnterpriseGitHub: return "EnterpriseGitHub" case .BitBucket: return "BitBucket" } } @@ -27,6 +37,7 @@ public enum GitService: String { public func logoName() -> String { switch self { case .GitHub: return "github" + case .EnterpriseGitHub: return "enterprisegithub" case .BitBucket: return "bitbucket" } } @@ -34,6 +45,7 @@ public enum GitService: String { public func hostname() -> String { switch self { case .GitHub: return "github.com" + case .EnterpriseGitHub(let host): return host case .BitBucket: return "bitbucket.org" } } @@ -41,6 +53,7 @@ public enum GitService: String { public func authorizeUrl() -> String { switch self { case .GitHub: return "https://github.com/login/oauth/authorize" + case .EnterpriseGitHub: return "https://\(hostname())/login/oauth/authorize" case .BitBucket: return "https://bitbucket.org/site/oauth2/authorize" } } @@ -48,21 +61,24 @@ public enum GitService: String { public func accessTokenUrl() -> String { switch self { case .GitHub: return "https://github.com/login/oauth/access_token" + case .EnterpriseGitHub: assert(false) case .BitBucket: return "https://bitbucket.org/site/oauth2/access_token" } } public func serviceKey() -> String { switch self { - case .GitHub: return BuildasaurxcodeprojKeys().gitHubAPIClientId() - case .BitBucket: return BuildasaurxcodeprojKeys().bitBucketAPIClientId() + case .GitHub: return BuildasaurKeys().gitHubAPIClientId() + case .EnterpriseGitHub: assert(false) + case .BitBucket: return BuildasaurKeys().bitBucketAPIClientId() } } public func serviceSecret() -> String { switch self { - case .GitHub: return BuildasaurxcodeprojKeys().gitHubAPIClientSecret() - case .BitBucket: return BuildasaurxcodeprojKeys().bitBucketAPIClientSecret() + case .GitHub: return BuildasaurKeys().gitHubAPIClientSecret() + case .EnterpriseGitHub: assert(false) + case .BitBucket: return BuildasaurKeys().bitBucketAPIClientSecret() } } } diff --git a/BuildaGitServerTests/EnterpriseGitHubSourceTests.swift b/BuildaGitServerTests/EnterpriseGitHubSourceTests.swift new file mode 100644 index 0000000..aa8d26e --- /dev/null +++ b/BuildaGitServerTests/EnterpriseGitHubSourceTests.swift @@ -0,0 +1,83 @@ +// +// EnterpriseGitHubSourceTests.swift +// Buildasaur +// +// Created by Rachel Caileff on 3/8/16. +// Copyright © 2016 Honza Dvorsky. All rights reserved. +// + +import Cocoa +import XCTest +@testable import BuildaGitServer +import BuildaUtils + +class EnterpriseGitHubSourceTests: XCTestCase { + + var github: GitHubServer! + + override func setUp() { + super.setUp() + + self.github = GitServerFactory.server(.EnterpriseGitHub(host: "git.mycompany.com"), auth: nil) as! GitHubServer // TODO: fill in accessible enterprise github host + } + + 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": "my/repo" // TODO: fill in accessible enterprise github repo +// ] +// +// 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: [GitHubPullRequest] = 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": "my/repo" // TODO: fill in accessible enterprise github repo +// ] +// +// 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: [GitHubBranch] = GitHubArray(body) +// XCTAssertGreaterThan(branches.count, 0, "We need > 0 items to test parsing") +// Log.verbose("Parsed branches: \(branches)") +// } else { +// XCTFail("Body nil") +// } +// } +// } +} diff --git a/BuildaKit/SyncerManager.swift b/BuildaKit/SyncerManager.swift index 7d72c5c..0295c66 100644 --- a/BuildaKit/SyncerManager.swift +++ b/BuildaKit/SyncerManager.swift @@ -116,7 +116,7 @@ extension SyncerManager: HeartbeatManagerDelegate { public func typesOfRunningSyncers() -> [String : Int] { return self.syncers.filter { $0.active }.reduce([:]) { (all, syncer) -> [String: Int] in var stats = all - let syncerType = syncer._project.workspaceMetadata!.service.rawValue + let syncerType = syncer._project.workspaceMetadata!.service.type() stats[syncerType] = (stats[syncerType] ?? 0) + 1 return stats } diff --git a/BuildaKit/WorkspaceMetadata.swift b/BuildaKit/WorkspaceMetadata.swift index 3fb4fdd..664ee6f 100644 --- a/BuildaKit/WorkspaceMetadata.swift +++ b/BuildaKit/WorkspaceMetadata.swift @@ -78,9 +78,9 @@ extension WorkspaceMetadata { let scheme = NSURL(string: urlString)!.scheme switch scheme { - case "github.com": + case GitService.GitHub.hostname(): return (CheckoutType.SSH, .GitHub) - case "bitbucket.org": + case GitService.BitBucket.hostname(): return (CheckoutType.SSH, .BitBucket) case "https": @@ -93,7 +93,8 @@ extension WorkspaceMetadata { Log.error("HTTPS or SVN not yet supported, please create an issue on GitHub if you want it added (czechboy0/Buildasaur)") return nil default: - return nil + var urlPieces = urlString.split(":") + return (CheckoutType.SSH, .EnterpriseGitHub(host: urlPieces[0])) } } } diff --git a/Buildasaur.xcodeproj/project.pbxproj b/Buildasaur.xcodeproj/project.pbxproj index bd43354..ce55b5b 100644 --- a/Buildasaur.xcodeproj/project.pbxproj +++ b/Buildasaur.xcodeproj/project.pbxproj @@ -7,9 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - 0928E6AF8EED8E829B0F4286 /* Pods_Buildasaur.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8FA09FE729BC440366D74E2E /* Pods_Buildasaur.framework */; }; - 130C2BB548050F030930B54F /* Pods_BuildaHeartbeatKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 41155620B4193BDB27CD7E83 /* Pods_BuildaHeartbeatKit.framework */; }; - 158554341171FBBD63B039E2 /* Pods_BuildaGitServerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F29FCDBD0B3B9740956E5206 /* Pods_BuildaGitServerTests.framework */; }; 3A0034F01C5975C000A4ECB5 /* bitbucket_post_status.json in Resources */ = {isa = PBXBuildFile; fileRef = 3A0034EF1C5975C000A4ECB5 /* bitbucket_post_status.json */; }; 3A0034F21C59775100A4ECB5 /* bitbucket_get_status.json in Resources */ = {isa = PBXBuildFile; fileRef = 3A0034F11C59775100A4ECB5 /* bitbucket_get_status.json */; }; 3A014BA51C57A94B00B82B4A /* Authentication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A014BA41C57A94B00B82B4A /* Authentication.swift */; }; @@ -156,9 +153,14 @@ 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 */; }; + 6427B0D35F605AB6A280DD94 /* Pods_BuildaHeartbeatKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D127C1A6AC9BC2A276D87C9B /* Pods_BuildaHeartbeatKit.framework */; }; 775501D89C0E4D209E6EE5D0 /* Pods_BuildaGitServer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B9139F7877761955F17C535E /* Pods_BuildaGitServer.framework */; }; + 81EFD68FDD3B64B18D787351 /* Pods_BuildaKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5BFDB59D042B1F4EBA021132 /* Pods_BuildaKit.framework */; }; + 8476D4061C8F4CD000463074 /* EnterpriseGitHubSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8476D4051C8F4CD000463074 /* EnterpriseGitHubSourceTests.swift */; }; + 8B22517BB7E47C997C8D7EDE /* Pods_Buildasaur.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B105F15DC0D8FB6C61EB1C33 /* Pods_Buildasaur.framework */; }; + 90E0B3BC47E1D9EEC556FA03 /* Pods_BuildaGitServerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A8D94F1D70D30788206EC2D /* Pods_BuildaGitServerTests.framework */; }; D59A4C6EEE903468E69AEA81 /* Pods_BuildaKitTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4EC43508E8A4F9D6E5ED0126 /* Pods_BuildaKitTests.framework */; }; - E9F513792E8C95C43A68D1CD /* Pods_BuildaKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7827BDC6B1752C193A0EAA05 /* Pods_BuildaKit.framework */; }; + E9F513792E8C95C43A68D1CD /* (null) in Frameworks */ = {isa = PBXBuildFile; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -224,6 +226,7 @@ /* Begin PBXFileReference section */ 105246397B31495A090CD1D1 /* Pods-BuildaKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaKit.release.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaKit/Pods-BuildaKit.release.xcconfig"; sourceTree = ""; }; + 17265C17307D20F4C67DBAD5 /* Pods-BuildaKitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaKitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaKitTests/Pods-BuildaKitTests.debug.xcconfig"; sourceTree = ""; }; 2D7FAD09E7CF5F0D5E6CC526 /* 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 = ""; }; 38739D0E658C834537584B63 /* Pods-BuildaGitServerTests.testing.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaGitServerTests.testing.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaGitServerTests/Pods-BuildaGitServerTests.testing.xcconfig"; sourceTree = ""; }; 3A0034EF1C5975C000A4ECB5 /* bitbucket_post_status.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = bitbucket_post_status.json; path = Data/bitbucket_post_status.json; sourceTree = ""; }; @@ -375,27 +378,27 @@ 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 = ""; }; - 3D172B5BBA1416D1EE92A8A5 /* Pods-BuildaKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaKitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaKitTests/Pods-BuildaKitTests.release.xcconfig"; sourceTree = ""; }; - 41155620B4193BDB27CD7E83 /* Pods_BuildaHeartbeatKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BuildaHeartbeatKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 3C9B7B3EE7C9258EA7A0C574 /* Pods-BuildaGitServerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaGitServerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaGitServerTests/Pods-BuildaGitServerTests.release.xcconfig"; sourceTree = ""; }; 44E8AF72E61B7A15D6E70352 /* Pods-BuildaGitServer.testing.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaGitServer.testing.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaGitServer/Pods-BuildaGitServer.testing.xcconfig"; sourceTree = ""; }; - 4A4FCE02D4B5A180AAF85C12 /* 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 = ""; }; + 4715CCD872F3565B0D290B3B /* Pods-BuildaHeartbeatKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaHeartbeatKit.release.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaHeartbeatKit/Pods-BuildaHeartbeatKit.release.xcconfig"; sourceTree = ""; }; 4EC43508E8A4F9D6E5ED0126 /* Pods_BuildaKitTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BuildaKitTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 6A0C42D6343BC2A4C4C899AB /* Pods-BuildaHeartbeatKit.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaHeartbeatKit.release.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaHeartbeatKit/Pods-BuildaHeartbeatKit.release.xcconfig"; sourceTree = ""; }; - 7827BDC6B1752C193A0EAA05 /* Pods_BuildaKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BuildaKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 54CC296C4C15EE5B4CB1A940 /* Pods-Buildasaur.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Buildasaur.release.xcconfig"; path = "Pods/Target Support Files/Pods-Buildasaur/Pods-Buildasaur.release.xcconfig"; sourceTree = ""; }; + 5BFDB59D042B1F4EBA021132 /* Pods_BuildaKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BuildaKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 626C1B28139A9F7ED2D2EB72 /* 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 = ""; }; 8176576B54DB918274B10F51 /* Pods-Buildasaur.testing.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Buildasaur.testing.xcconfig"; path = "Pods/Target Support Files/Pods-Buildasaur/Pods-Buildasaur.testing.xcconfig"; sourceTree = ""; }; - 8FA09FE729BC440366D74E2E /* Pods_Buildasaur.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Buildasaur.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 906BE3BD7A02D5A19E6E5269 /* 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 = ""; }; - A1308D46FA858C964191A82B /* Pods-BuildaGitServer.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaGitServer.release.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaGitServer/Pods-BuildaGitServer.release.xcconfig"; sourceTree = ""; }; - A4E55389A75FA900E8317C54 /* Pods-BuildaGitServerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaGitServerTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaGitServerTests/Pods-BuildaGitServerTests.release.xcconfig"; sourceTree = ""; }; - A588086CCA23190FC2B378E5 /* Pods-Buildasaur.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Buildasaur.release.xcconfig"; path = "Pods/Target Support Files/Pods-Buildasaur/Pods-Buildasaur.release.xcconfig"; sourceTree = ""; }; - B21869E8D6C35C4DE0A4936A /* Pods-BuildaKit.testing.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaKit.testing.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaKit/Pods-BuildaKit.testing.xcconfig"; sourceTree = ""; }; + 8476D4051C8F4CD000463074 /* EnterpriseGitHubSourceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnterpriseGitHubSourceTests.swift; sourceTree = ""; }; + 8B29124289142782829E7634 /* 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 = ""; }; + 9A8D94F1D70D30788206EC2D /* Pods_BuildaGitServerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BuildaGitServerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B105F15DC0D8FB6C61EB1C33 /* Pods_Buildasaur.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Buildasaur.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B434ACFEEBD14A1E176F9889 /* Pods-BuildaKitTests.testing.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaKitTests.testing.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaKitTests/Pods-BuildaKitTests.testing.xcconfig"; sourceTree = ""; }; B9139F7877761955F17C535E /* Pods_BuildaGitServer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BuildaGitServer.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - D0F6A06E560D8D41488E34CB /* Pods-BuildaKitTests.testing.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaKitTests.testing.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaKitTests/Pods-BuildaKitTests.testing.xcconfig"; sourceTree = ""; }; - DBBDB133AF2AD2E2CA9AEB13 /* Pods-BuildaKitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaKitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaKitTests/Pods-BuildaKitTests.debug.xcconfig"; sourceTree = ""; }; - DC5ECE2BBCC7B037A35A3BE9 /* Pods-BuildaHeartbeatKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaHeartbeatKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaHeartbeatKit/Pods-BuildaHeartbeatKit.debug.xcconfig"; sourceTree = ""; }; - E44B7C2CE3D5BFAC8FF7EBDD /* 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 = ""; }; + D127C1A6AC9BC2A276D87C9B /* Pods_BuildaHeartbeatKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BuildaHeartbeatKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + DE66755DF1B1957837DE13B4 /* Pods-BuildaKit.testing.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaKit.testing.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaKit/Pods-BuildaKit.testing.xcconfig"; sourceTree = ""; }; + EA372FE934D1F73BADA8E5FC /* Pods-BuildaGitServer.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaGitServer.release.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaGitServer/Pods-BuildaGitServer.release.xcconfig"; sourceTree = ""; }; F105E28B52A2616166887266 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - F29FCDBD0B3B9740956E5206 /* Pods_BuildaGitServerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_BuildaGitServerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F36E0D1DA1D28291A8A94FEB /* Pods-BuildaKitTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaKitTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaKitTests/Pods-BuildaKitTests.release.xcconfig"; sourceTree = ""; }; + F5D5709F9FC4F63C8170A0EE /* Pods-BuildaHeartbeatKit.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaHeartbeatKit.debug.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaHeartbeatKit/Pods-BuildaHeartbeatKit.debug.xcconfig"; sourceTree = ""; }; + F722889B83953693C2CCC2EA /* 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 = ""; }; FBFCE164C6B59B0C7DC8FBEE /* Pods-BuildaGitServerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-BuildaGitServerTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-BuildaGitServerTests/Pods-BuildaGitServerTests.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -407,7 +410,7 @@ 3A81BB2B1B5A77E9004732CD /* BuildaKit.framework in Frameworks */, 3A756DC41BAB425E00508B69 /* BuildaHeartbeatKit.framework in Frameworks */, 3AAF6EFB1A3CE5BA00C657FB /* BuildaGitServer.framework in Frameworks */, - 0928E6AF8EED8E829B0F4286 /* Pods_Buildasaur.framework in Frameworks */, + 8B22517BB7E47C997C8D7EDE /* Pods_Buildasaur.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -415,7 +418,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 130C2BB548050F030930B54F /* Pods_BuildaHeartbeatKit.framework in Frameworks */, + 6427B0D35F605AB6A280DD94 /* Pods_BuildaHeartbeatKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -425,7 +428,8 @@ files = ( 3A756DCC1BAB44A200508B69 /* BuildaHeartbeatKit.framework in Frameworks */, 3A81BB351B5A7898004732CD /* BuildaGitServer.framework in Frameworks */, - E9F513792E8C95C43A68D1CD /* Pods_BuildaKit.framework in Frameworks */, + E9F513792E8C95C43A68D1CD /* (null) in Frameworks */, + 81EFD68FDD3B64B18D787351 /* Pods_BuildaKit.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -451,7 +455,7 @@ buildActionMask = 2147483647; files = ( 3AAF6EEF1A3CE5BA00C657FB /* BuildaGitServer.framework in Frameworks */, - 158554341171FBBD63B039E2 /* Pods_BuildaGitServerTests.framework in Frameworks */, + 90E0B3BC47E1D9EEC556FA03 /* Pods_BuildaGitServerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -716,6 +720,7 @@ 3A7577A91C593A6A0023F08C /* Data */, 3A7AF60F1C591FAD000FD726 /* BitBucketServerTests.swift */, 3A58B1581A3B96C9003E0266 /* GitHubServerTests.swift */, + 8476D4051C8F4CD000463074 /* EnterpriseGitHubSourceTests.swift */, 3AAF6EF51A3CE5BA00C657FB /* Supporting Files */, ); path = BuildaGitServerTests; @@ -892,24 +897,24 @@ 4014768B3F6B8B7FEEFE1654 /* Pods */ = { isa = PBXGroup; children = ( - 4A4FCE02D4B5A180AAF85C12 /* Pods-BuildaGitServer.debug.xcconfig */, 44E8AF72E61B7A15D6E70352 /* Pods-BuildaGitServer.testing.xcconfig */, - A1308D46FA858C964191A82B /* Pods-BuildaGitServer.release.xcconfig */, FBFCE164C6B59B0C7DC8FBEE /* Pods-BuildaGitServerTests.debug.xcconfig */, 38739D0E658C834537584B63 /* Pods-BuildaGitServerTests.testing.xcconfig */, - A4E55389A75FA900E8317C54 /* Pods-BuildaGitServerTests.release.xcconfig */, - DC5ECE2BBCC7B037A35A3BE9 /* Pods-BuildaHeartbeatKit.debug.xcconfig */, 2D7FAD09E7CF5F0D5E6CC526 /* Pods-BuildaHeartbeatKit.testing.xcconfig */, - 6A0C42D6343BC2A4C4C899AB /* Pods-BuildaHeartbeatKit.release.xcconfig */, - E44B7C2CE3D5BFAC8FF7EBDD /* Pods-BuildaKit.debug.xcconfig */, - B21869E8D6C35C4DE0A4936A /* Pods-BuildaKit.testing.xcconfig */, 105246397B31495A090CD1D1 /* Pods-BuildaKit.release.xcconfig */, - DBBDB133AF2AD2E2CA9AEB13 /* Pods-BuildaKitTests.debug.xcconfig */, - D0F6A06E560D8D41488E34CB /* Pods-BuildaKitTests.testing.xcconfig */, - 3D172B5BBA1416D1EE92A8A5 /* Pods-BuildaKitTests.release.xcconfig */, - 906BE3BD7A02D5A19E6E5269 /* Pods-Buildasaur.debug.xcconfig */, 8176576B54DB918274B10F51 /* Pods-Buildasaur.testing.xcconfig */, - A588086CCA23190FC2B378E5 /* Pods-Buildasaur.release.xcconfig */, + F722889B83953693C2CCC2EA /* Pods-BuildaGitServer.debug.xcconfig */, + EA372FE934D1F73BADA8E5FC /* Pods-BuildaGitServer.release.xcconfig */, + 3C9B7B3EE7C9258EA7A0C574 /* Pods-BuildaGitServerTests.release.xcconfig */, + F5D5709F9FC4F63C8170A0EE /* Pods-BuildaHeartbeatKit.debug.xcconfig */, + 4715CCD872F3565B0D290B3B /* Pods-BuildaHeartbeatKit.release.xcconfig */, + 8B29124289142782829E7634 /* Pods-BuildaKit.debug.xcconfig */, + DE66755DF1B1957837DE13B4 /* Pods-BuildaKit.testing.xcconfig */, + 17265C17307D20F4C67DBAD5 /* Pods-BuildaKitTests.debug.xcconfig */, + B434ACFEEBD14A1E176F9889 /* Pods-BuildaKitTests.testing.xcconfig */, + F36E0D1DA1D28291A8A94FEB /* Pods-BuildaKitTests.release.xcconfig */, + 626C1B28139A9F7ED2D2EB72 /* Pods-Buildasaur.debug.xcconfig */, + 54CC296C4C15EE5B4CB1A940 /* Pods-Buildasaur.release.xcconfig */, ); name = Pods; sourceTree = ""; @@ -919,11 +924,11 @@ children = ( F105E28B52A2616166887266 /* Pods.framework */, B9139F7877761955F17C535E /* Pods_BuildaGitServer.framework */, - F29FCDBD0B3B9740956E5206 /* Pods_BuildaGitServerTests.framework */, - 41155620B4193BDB27CD7E83 /* Pods_BuildaHeartbeatKit.framework */, - 7827BDC6B1752C193A0EAA05 /* Pods_BuildaKit.framework */, 4EC43508E8A4F9D6E5ED0126 /* Pods_BuildaKitTests.framework */, - 8FA09FE729BC440366D74E2E /* Pods_Buildasaur.framework */, + 9A8D94F1D70D30788206EC2D /* Pods_BuildaGitServerTests.framework */, + D127C1A6AC9BC2A276D87C9B /* Pods_BuildaHeartbeatKit.framework */, + 5BFDB59D042B1F4EBA021132 /* Pods_BuildaKit.framework */, + B105F15DC0D8FB6C61EB1C33 /* Pods_Buildasaur.framework */, ); name = Frameworks; sourceTree = ""; @@ -1606,6 +1611,7 @@ buildActionMask = 2147483647; files = ( 3A7AF6101C591FAD000FD726 /* BitBucketServerTests.swift in Sources */, + 8476D4061C8F4CD000463074 /* EnterpriseGitHubSourceTests.swift in Sources */, 3AAF6F0B1A3CE82B00C657FB /* GitHubServerTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1743,7 +1749,7 @@ }; 3A56878D1A3B93BD0066DB2B /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 906BE3BD7A02D5A19E6E5269 /* Pods-Buildasaur.debug.xcconfig */; + baseConfigurationReference = 626C1B28139A9F7ED2D2EB72 /* Pods-Buildasaur.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; COMBINE_HIDPI_IMAGES = YES; @@ -1758,7 +1764,7 @@ }; 3A56878E1A3B93BD0066DB2B /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A588086CCA23190FC2B378E5 /* Pods-Buildasaur.release.xcconfig */; + baseConfigurationReference = 54CC296C4C15EE5B4CB1A940 /* Pods-Buildasaur.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CODE_SIGN_IDENTITY = "Developer ID Application: Jan Dvorsky (7BJ2984YDK)"; @@ -1775,7 +1781,7 @@ }; 3A756DC61BAB425E00508B69 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = DC5ECE2BBCC7B037A35A3BE9 /* Pods-BuildaHeartbeatKit.debug.xcconfig */; + baseConfigurationReference = F5D5709F9FC4F63C8170A0EE /* Pods-BuildaHeartbeatKit.debug.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; @@ -1830,7 +1836,7 @@ }; 3A756DC81BAB425E00508B69 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 6A0C42D6343BC2A4C4C899AB /* Pods-BuildaHeartbeatKit.release.xcconfig */; + baseConfigurationReference = 4715CCD872F3565B0D290B3B /* Pods-BuildaHeartbeatKit.release.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; @@ -1972,7 +1978,7 @@ }; 3A81BB2D1B5A77E9004732CD /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = E44B7C2CE3D5BFAC8FF7EBDD /* Pods-BuildaKit.debug.xcconfig */; + baseConfigurationReference = 8B29124289142782829E7634 /* Pods-BuildaKit.debug.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; @@ -1998,7 +2004,7 @@ }; 3A81BB2E1B5A77E9004732CD /* Testing */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B21869E8D6C35C4DE0A4936A /* Pods-BuildaKit.testing.xcconfig */; + baseConfigurationReference = DE66755DF1B1957837DE13B4 /* Pods-BuildaKit.testing.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; @@ -2055,7 +2061,7 @@ }; 3A81BB301B5A77E9004732CD /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = DBBDB133AF2AD2E2CA9AEB13 /* Pods-BuildaKitTests.debug.xcconfig */; + baseConfigurationReference = 17265C17307D20F4C67DBAD5 /* Pods-BuildaKitTests.debug.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; @@ -2071,7 +2077,7 @@ }; 3A81BB311B5A77E9004732CD /* Testing */ = { isa = XCBuildConfiguration; - baseConfigurationReference = D0F6A06E560D8D41488E34CB /* Pods-BuildaKitTests.testing.xcconfig */; + baseConfigurationReference = B434ACFEEBD14A1E176F9889 /* Pods-BuildaKitTests.testing.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; @@ -2090,7 +2096,7 @@ }; 3A81BB321B5A77E9004732CD /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 3D172B5BBA1416D1EE92A8A5 /* Pods-BuildaKitTests.release.xcconfig */; + baseConfigurationReference = F36E0D1DA1D28291A8A94FEB /* Pods-BuildaKitTests.release.xcconfig */; buildSettings = { CLANG_ENABLE_MODULES = YES; COMBINE_HIDPI_IMAGES = YES; @@ -2108,7 +2114,7 @@ }; 3AAF6EFE1A3CE5BA00C657FB /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 4A4FCE02D4B5A180AAF85C12 /* Pods-BuildaGitServer.debug.xcconfig */; + baseConfigurationReference = F722889B83953693C2CCC2EA /* Pods-BuildaGitServer.debug.xcconfig */; buildSettings = { COMBINE_HIDPI_IMAGES = YES; CURRENT_PROJECT_VERSION = 1; @@ -2136,7 +2142,7 @@ }; 3AAF6EFF1A3CE5BA00C657FB /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A1308D46FA858C964191A82B /* Pods-BuildaGitServer.release.xcconfig */; + baseConfigurationReference = EA372FE934D1F73BADA8E5FC /* Pods-BuildaGitServer.release.xcconfig */; buildSettings = { COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; @@ -2182,7 +2188,7 @@ }; 3AAF6F021A3CE5BA00C657FB /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = A4E55389A75FA900E8317C54 /* Pods-BuildaGitServerTests.release.xcconfig */; + baseConfigurationReference = 3C9B7B3EE7C9258EA7A0C574 /* Pods-BuildaGitServerTests.release.xcconfig */; buildSettings = { COMBINE_HIDPI_IMAGES = YES; ENABLE_NS_ASSERTIONS = YES; diff --git a/Buildasaur/Base.lproj/Main.storyboard b/Buildasaur/Base.lproj/Main.storyboard index fd90fcf..ab2c74d 100644 --- a/Buildasaur/Base.lproj/Main.storyboard +++ b/Buildasaur/Base.lproj/Main.storyboard @@ -1,8 +1,8 @@ - + - + diff --git a/Buildasaur/ProjectViewController.swift b/Buildasaur/ProjectViewController.swift index 22f069a..5b4406a 100644 --- a/Buildasaur/ProjectViewController.swift +++ b/Buildasaur/ProjectViewController.swift @@ -115,13 +115,13 @@ class ProjectViewController: ConfigEditViewController { .startWithNext { [weak self] (proj, auth, forceUseToken) in self?.updateServiceMeta(proj, auth: auth, userWantsTokenAuth: forceUseToken) } - combineLatest(self.tokenTextField.rac_text, self.userWantsTokenAuth.producer) - .startWithNext { [weak self] token, forceToken in + combineLatest(self.tokenTextField.rac_text, self.userWantsTokenAuth.producer, meta) + .startWithNext { [weak self] token, forceToken, meta in if forceToken { if token.isEmpty { self?.authenticator.value = nil } else { - self?.authenticator.value = ProjectAuthenticator(service: .GitHub, username: "GIT", type: .PersonalToken, secret: token) + self?.authenticator.value = ProjectAuthenticator(service: meta.service, username: "GIT", type: .PersonalToken, secret: token) } } } @@ -166,22 +166,33 @@ class ProjectViewController: ConfigEditViewController { let alreadyHasAuth = auth != nil + var showTokenField = false + switch service { - case .GitHub: + case GitService.GitHub: if let auth = auth where auth.type == .PersonalToken && !auth.secret.isEmpty { self.tokenTextField.stringValue = auth.secret } else { self.tokenTextField.stringValue = "" } self.useTokenButton.hidden = alreadyHasAuth - case .BitBucket: + self.loginButton.hidden = alreadyHasAuth + self.logoutButton.hidden = !alreadyHasAuth + showTokenField = userWantsTokenAuth && (auth?.type == .PersonalToken || auth == nil) + case GitService.EnterpriseGitHub: + if !alreadyHasAuth { + self.tokenTextField.stringValue = "" + } + self.useTokenButton.hidden = alreadyHasAuth + self.loginButton.hidden = true + self.logoutButton.hidden = true + showTokenField = true + case GitService.BitBucket: self.useTokenButton.hidden = true + self.loginButton.hidden = alreadyHasAuth + self.logoutButton.hidden = !alreadyHasAuth } - self.loginButton.hidden = alreadyHasAuth - self.logoutButton.hidden = !alreadyHasAuth - - let showTokenField = userWantsTokenAuth && service == .GitHub && (auth?.type == .PersonalToken || auth == nil) self.tokenStackView.hidden = !showTokenField } diff --git a/Buildasaur/ServiceAuthentication.swift b/Buildasaur/ServiceAuthentication.swift index 08b2ab1..93cbb04 100644 --- a/Buildasaur/ServiceAuthentication.swift +++ b/Buildasaur/ServiceAuthentication.swift @@ -66,6 +66,8 @@ class ServiceAuthenticator { switch service { case .GitHub: return self.getGitHubParameters() + case .EnterpriseGitHub: + assert(false) case .BitBucket: return self.getBitBucketParameters() // default: From 935956b6a7ee74f71d3d1fef7776e562e520684a Mon Sep 17 00:00:00 2001 From: Rachel Caileff Date: Wed, 9 Mar 2016 11:09:15 -0600 Subject: [PATCH 07/14] Fix a couple of errors introduced when I removed a local hack --- BuildaGitServer/Base/GitServerFactory.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/BuildaGitServer/Base/GitServerFactory.swift b/BuildaGitServer/Base/GitServerFactory.swift index 8ccc698..eb91108 100644 --- a/BuildaGitServer/Base/GitServerFactory.swift +++ b/BuildaGitServer/Base/GitServerFactory.swift @@ -20,9 +20,8 @@ class GitServerFactory { let baseURL = "https://api.github.com" let endpoints = GitHubEndpoints(baseURL: baseURL, auth: auth) server = GitHubServer(endpoints: endpoints, http: http) - case .GitHub, .EnterpriseGitHub: - //let baseURL = "https://api.\(service.hostname())" - let baseURL = "https://\(service.hostname())" + case .EnterpriseGitHub: + let baseURL = "https://api.\(service.hostname())" let endpoints = GitHubEndpoints(baseURL: baseURL, auth: auth) server = GitHubServer(endpoints: endpoints, http: http) case .BitBucket: From bd5fcbde21d3e2bff8262ca0cbfd6ad0380738ea Mon Sep 17 00:00:00 2001 From: Rachel Caileff Date: Wed, 9 Mar 2016 13:52:50 -0600 Subject: [PATCH 08/14] Another assert that Enterprise GitHub doesn't do OAuth --- BuildaGitServer/GitServerPublic.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BuildaGitServer/GitServerPublic.swift b/BuildaGitServer/GitServerPublic.swift index 82fc5f4..7a4cd0a 100644 --- a/BuildaGitServer/GitServerPublic.swift +++ b/BuildaGitServer/GitServerPublic.swift @@ -53,7 +53,7 @@ public enum GitService { public func authorizeUrl() -> String { switch self { case .GitHub: return "https://github.com/login/oauth/authorize" - case .EnterpriseGitHub: return "https://\(hostname())/login/oauth/authorize" + case .EnterpriseGitHub: assert(false) case .BitBucket: return "https://bitbucket.org/site/oauth2/authorize" } } From 39f9b63e0e887ef1550adec2f7c15355c2714a35 Mon Sep 17 00:00:00 2001 From: Rachel Caileff Date: Wed, 9 Mar 2016 15:14:45 -0600 Subject: [PATCH 09/14] Merge xcode project.pbxproj --- Buildasaur.xcodeproj/project.pbxproj | 90 ++++++++++++++-------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/Buildasaur.xcodeproj/project.pbxproj b/Buildasaur.xcodeproj/project.pbxproj index ce55b5b..59fc00a 100644 --- a/Buildasaur.xcodeproj/project.pbxproj +++ b/Buildasaur.xcodeproj/project.pbxproj @@ -967,14 +967,14 @@ isa = PBXNativeTarget; buildConfigurationList = 3A56878C1A3B93BD0066DB2B /* Build configuration list for PBXNativeTarget "Buildasaur" */; buildPhases = ( - 7F7B5A38B90C0DC548380B41 /* Check Pods Manifest.lock */, + 7F7B5A38B90C0DC548380B41 /* 📦 Check Pods Manifest.lock */, 3A56876C1A3B93BD0066DB2B /* Sources */, 3A56876D1A3B93BD0066DB2B /* Frameworks */, 3A56876E1A3B93BD0066DB2B /* Resources */, 3A58B14E1A3B9604003E0266 /* Embed Frameworks */, 3ABEF9741C4B1DD800ED983F /* Crashlytics */, - 95810B17A48B7A0701C0D988 /* Embed Pods Frameworks */, - 2C56A540DDB9310EB6066F2E /* Copy Pods Resources */, + 95810B17A48B7A0701C0D988 /* 📦 Embed Pods Frameworks */, + 2C56A540DDB9310EB6066F2E /* 📦 Copy Pods Resources */, ); buildRules = ( ); @@ -992,12 +992,12 @@ isa = PBXNativeTarget; buildConfigurationList = 3A756DC91BAB425E00508B69 /* Build configuration list for PBXNativeTarget "BuildaHeartbeatKit" */; buildPhases = ( - A357470D0074E00EB65486C4 /* Check Pods Manifest.lock */, + A357470D0074E00EB65486C4 /* 📦 Check Pods Manifest.lock */, 3A756DB81BAB425E00508B69 /* Sources */, 3A756DB91BAB425E00508B69 /* Frameworks */, 3A756DBA1BAB425E00508B69 /* Headers */, 3A756DBB1BAB425E00508B69 /* Resources */, - FCA2AC3A5FAE55B062298D99 /* Copy Pods Resources */, + FCA2AC3A5FAE55B062298D99 /* 📦 Copy Pods Resources */, ); buildRules = ( ); @@ -1012,12 +1012,12 @@ isa = PBXNativeTarget; buildConfigurationList = 3A81BB331B5A77E9004732CD /* Build configuration list for PBXNativeTarget "BuildaKit" */; buildPhases = ( - B1C478B6293E2189D88A1ED0 /* Check Pods Manifest.lock */, + B1C478B6293E2189D88A1ED0 /* 📦 Check Pods Manifest.lock */, 3A81BB111B5A77E9004732CD /* Sources */, 3A81BB121B5A77E9004732CD /* Frameworks */, 3A81BB131B5A77E9004732CD /* Headers */, 3A81BB141B5A77E9004732CD /* Resources */, - B7714B96633F79A27D1C8661 /* Copy Pods Resources */, + B7714B96633F79A27D1C8661 /* 📦 Copy Pods Resources */, ); buildRules = ( ); @@ -1033,12 +1033,12 @@ isa = PBXNativeTarget; buildConfigurationList = 3A81BB341B5A77E9004732CD /* Build configuration list for PBXNativeTarget "BuildaKitTests" */; buildPhases = ( - 350D4892586930B7AED3C4FA /* Check Pods Manifest.lock */, + 350D4892586930B7AED3C4FA /* 📦 Check Pods Manifest.lock */, 3A81BB1B1B5A77E9004732CD /* Sources */, 3A81BB1C1B5A77E9004732CD /* Frameworks */, 3A81BB1D1B5A77E9004732CD /* Resources */, - 64FEAE1B37282010DE92435F /* Embed Pods Frameworks */, - 4D620C21D25FEA24CC14A453 /* Copy Pods Resources */, + 64FEAE1B37282010DE92435F /* 📦 Embed Pods Frameworks */, + 4D620C21D25FEA24CC14A453 /* 📦 Copy Pods Resources */, ); buildRules = ( ); @@ -1054,12 +1054,12 @@ isa = PBXNativeTarget; buildConfigurationList = 3AAF6EFD1A3CE5BA00C657FB /* Build configuration list for PBXNativeTarget "BuildaGitServer" */; buildPhases = ( - CA239972B01BEAB058E5288C /* Check Pods Manifest.lock */, + CA239972B01BEAB058E5288C /* 📦 Check Pods Manifest.lock */, 3AAF6EDF1A3CE5BA00C657FB /* Sources */, 3AAF6EE01A3CE5BA00C657FB /* Frameworks */, 3AAF6EE11A3CE5BA00C657FB /* Headers */, 3AAF6EE21A3CE5BA00C657FB /* Resources */, - 567E9244948E61DA4DDCAC68 /* Copy Pods Resources */, + 567E9244948E61DA4DDCAC68 /* 📦 Copy Pods Resources */, ); buildRules = ( ); @@ -1074,12 +1074,12 @@ isa = PBXNativeTarget; buildConfigurationList = 3AAF6F001A3CE5BA00C657FB /* Build configuration list for PBXNativeTarget "BuildaGitServerTests" */; buildPhases = ( - 3FADD00C3BA8BD1EE7164D23 /* Check Pods Manifest.lock */, + 3FADD00C3BA8BD1EE7164D23 /* 📦 Check Pods Manifest.lock */, 3AAF6EEA1A3CE5BA00C657FB /* Sources */, 3AAF6EEB1A3CE5BA00C657FB /* Frameworks */, 3AAF6EEC1A3CE5BA00C657FB /* Resources */, - 27B14128279E4AB1336EECDF /* Embed Pods Frameworks */, - 6E96A2CDDE06356C87CFC70A /* Copy Pods Resources */, + 27B14128279E4AB1336EECDF /* 📦 Embed Pods Frameworks */, + 6E96A2CDDE06356C87CFC70A /* 📦 Copy Pods Resources */, ); buildRules = ( ); @@ -1215,14 +1215,14 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ - 27B14128279E4AB1336EECDF /* Embed Pods Frameworks */ = { + 27B14128279E4AB1336EECDF /* 📦 Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Embed Pods Frameworks"; + name = "📦 Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -1230,14 +1230,14 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-BuildaGitServerTests/Pods-BuildaGitServerTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 2C56A540DDB9310EB6066F2E /* Copy Pods Resources */ = { + 2C56A540DDB9310EB6066F2E /* 📦 Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Copy Pods Resources"; + name = "📦 Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -1245,14 +1245,14 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Buildasaur/Pods-Buildasaur-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 350D4892586930B7AED3C4FA /* Check Pods Manifest.lock */ = { + 350D4892586930B7AED3C4FA /* 📦 Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Check Pods Manifest.lock"; + name = "📦 Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -1274,14 +1274,14 @@ shellPath = /bin/bash; shellScript = "#if [ \"Release\" = \"${CONFIGURATION}\" ]; then\n if [[ $(whoami) == \"honzadvorsky\" ]] ; then\n source ~/.profile\n echo \"Running Crashlytics\"\n \"${PODS_ROOT}/Fabric/run\" $KEY_FABRIC_BUILDASAUR_PUBLIC $KEY_FABRIC_BUILDASAUR_PRIVATE\n fi\n#fi"; }; - 3FADD00C3BA8BD1EE7164D23 /* Check Pods Manifest.lock */ = { + 3FADD00C3BA8BD1EE7164D23 /* 📦 Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Check Pods Manifest.lock"; + name = "📦 Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -1289,14 +1289,14 @@ shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; - 4D620C21D25FEA24CC14A453 /* Copy Pods Resources */ = { + 4D620C21D25FEA24CC14A453 /* 📦 Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Copy Pods Resources"; + name = "📦 Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -1304,14 +1304,14 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-BuildaKitTests/Pods-BuildaKitTests-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 567E9244948E61DA4DDCAC68 /* Copy Pods Resources */ = { + 567E9244948E61DA4DDCAC68 /* 📦 Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Copy Pods Resources"; + name = "📦 Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -1319,14 +1319,14 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-BuildaGitServer/Pods-BuildaGitServer-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 64FEAE1B37282010DE92435F /* Embed Pods Frameworks */ = { + 64FEAE1B37282010DE92435F /* 📦 Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Embed Pods Frameworks"; + name = "📦 Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -1334,14 +1334,14 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-BuildaKitTests/Pods-BuildaKitTests-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - 6E96A2CDDE06356C87CFC70A /* Copy Pods Resources */ = { + 6E96A2CDDE06356C87CFC70A /* 📦 Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Copy Pods Resources"; + name = "📦 Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -1349,14 +1349,14 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-BuildaGitServerTests/Pods-BuildaGitServerTests-resources.sh\"\n"; showEnvVarsInLog = 0; }; - 7F7B5A38B90C0DC548380B41 /* Check Pods Manifest.lock */ = { + 7F7B5A38B90C0DC548380B41 /* 📦 Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Check Pods Manifest.lock"; + name = "📦 Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -1364,14 +1364,14 @@ shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; - 95810B17A48B7A0701C0D988 /* Embed Pods Frameworks */ = { + 95810B17A48B7A0701C0D988 /* 📦 Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Embed Pods Frameworks"; + name = "📦 Embed Pods Frameworks"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -1379,14 +1379,14 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Buildasaur/Pods-Buildasaur-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - A357470D0074E00EB65486C4 /* Check Pods Manifest.lock */ = { + A357470D0074E00EB65486C4 /* 📦 Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Check Pods Manifest.lock"; + name = "📦 Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -1394,14 +1394,14 @@ shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; - B1C478B6293E2189D88A1ED0 /* Check Pods Manifest.lock */ = { + B1C478B6293E2189D88A1ED0 /* 📦 Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Check Pods Manifest.lock"; + name = "📦 Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -1409,14 +1409,14 @@ shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; - B7714B96633F79A27D1C8661 /* Copy Pods Resources */ = { + B7714B96633F79A27D1C8661 /* 📦 Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Copy Pods Resources"; + name = "📦 Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -1424,14 +1424,14 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-BuildaKit/Pods-BuildaKit-resources.sh\"\n"; showEnvVarsInLog = 0; }; - CA239972B01BEAB058E5288C /* Check Pods Manifest.lock */ = { + CA239972B01BEAB058E5288C /* 📦 Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Check Pods Manifest.lock"; + name = "📦 Check Pods Manifest.lock"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; @@ -1439,14 +1439,14 @@ shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; - FCA2AC3A5FAE55B062298D99 /* Copy Pods Resources */ = { + FCA2AC3A5FAE55B062298D99 /* 📦 Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); - name = "Copy Pods Resources"; + name = "📦 Copy Pods Resources"; outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; From 847074718e75a774e5269a04eeefd40f736c636e Mon Sep 17 00:00:00 2001 From: Rachel Caileff Date: Mon, 14 Mar 2016 12:04:38 -0500 Subject: [PATCH 10/14] Revert CocoaPlugin version change in Main.storyboard --- Buildasaur/Base.lproj/Main.storyboard | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Buildasaur/Base.lproj/Main.storyboard b/Buildasaur/Base.lproj/Main.storyboard index ab2c74d..81777fb 100644 --- a/Buildasaur/Base.lproj/Main.storyboard +++ b/Buildasaur/Base.lproj/Main.storyboard @@ -2,7 +2,7 @@ - + From 70ace29fac1be8dd578e3adba12e19595862d6f8 Mon Sep 17 00:00:00 2001 From: Rachel Caileff Date: Mon, 14 Mar 2016 17:38:41 -0500 Subject: [PATCH 11/14] Distinguish between an unsupported server and an Enterprise GitHub server in ProjectAuthenticator.fromString. Add tests for ProjectAuthenticator.fromString. --- BuildaGitServer/Base/Authentication.swift | 18 ++++- .../AuthenticationTests.swift | 71 +++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 BuildaGitServerTests/AuthenticationTests.swift diff --git a/BuildaGitServer/Base/Authentication.swift b/BuildaGitServer/Base/Authentication.swift index f9020fb..d61927c 100644 --- a/BuildaGitServer/Base/Authentication.swift +++ b/BuildaGitServer/Base/Authentication.swift @@ -48,7 +48,11 @@ extension ProjectAuthenticator: KeychainStringSerializable { case GitService.BitBucket.hostname(): service = GitService.BitBucket default: - service = GitService.EnterpriseGitHub(host: comps[0]) + let host = comps[0] + guard let maybeService = createEnterpriseService(host) else { + throw Error.withInfo("Unsupported service: \(host)") + } + service = maybeService } guard let type = ProjectAuthenticator.AuthType(rawValue: comps[2]) else { @@ -69,4 +73,16 @@ extension ProjectAuthenticator: KeychainStringSerializable { self.secret ].joinWithSeparator(":") } + + internal static func createEnterpriseService(host: String) -> GitService? { + guard let url = NSURL.init(string: "http://\(host)") else { return nil } + do { + let response = try NSString.init(contentsOfURL: url, encoding: NSASCIIStringEncoding) + if response.lowercaseString.containsString("github") { + return GitService.EnterpriseGitHub(host: host) + } + } catch { + } + return nil + } } diff --git a/BuildaGitServerTests/AuthenticationTests.swift b/BuildaGitServerTests/AuthenticationTests.swift new file mode 100644 index 0000000..0b8bd96 --- /dev/null +++ b/BuildaGitServerTests/AuthenticationTests.swift @@ -0,0 +1,71 @@ +// +// AuthenticationTests.swift +// Buildasaur +// +// Created by Rachel Caileff on 3/14/16. +// Copyright © 2016 Honza Dvorsky. All rights reserved. +// + +import XCTest +@testable import BuildaGitServer + +class AuthenticationTests: XCTestCase { + + let gitService = "GIT" + let tokenType = "PersonalToken" + let tokenValue = "1234567890" + + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testEmptyStringShouldThrow() { + do { + try ProjectAuthenticator.fromString("") + XCTFail() + } catch { + // Expected behavior + } + } + + func testInvalidHostShouldThrow() { + do { + try ProjectAuthenticator.fromString("a:\(gitService):\(tokenType):\(tokenValue)") + XCTFail() + } catch { + // Expected behavior + } + } + + func testNonexistantHostShouldThrow() { + do { + try ProjectAuthenticator.fromString("some.fakehostname.com:\(gitService):\(tokenType):\(tokenValue)") + XCTFail() + } catch { + // Expected behavior + } + } + + func testInvalidAuthTypeShouldThrow() { + do { + try ProjectAuthenticator.fromString("\(GitService.GitHub.hostname()):\(gitService):junkstring:\(tokenValue)") + XCTFail() + } catch { + // Expected behavior + } + } + + func testValidStringShouldNotThrow() { + do { + try ProjectAuthenticator.fromString("\(GitService.GitHub.hostname()):\(gitService):\(tokenType):\(tokenValue)") + } catch { + XCTFail() + } + } +} From 6db5f129b1dd76e882d80fd878fae23c156361fc Mon Sep 17 00:00:00 2001 From: Rachel Caileff Date: Fri, 18 Mar 2016 09:45:15 -0500 Subject: [PATCH 12/14] Shorten NSURL.init(...) to NSURL(...) --- BuildaGitServer/Base/Authentication.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BuildaGitServer/Base/Authentication.swift b/BuildaGitServer/Base/Authentication.swift index d61927c..4caf5f4 100644 --- a/BuildaGitServer/Base/Authentication.swift +++ b/BuildaGitServer/Base/Authentication.swift @@ -75,7 +75,7 @@ extension ProjectAuthenticator: KeychainStringSerializable { } internal static func createEnterpriseService(host: String) -> GitService? { - guard let url = NSURL.init(string: "http://\(host)") else { return nil } + guard let url = NSURL(string: "http://\(host)") else { return nil } do { let response = try NSString.init(contentsOfURL: url, encoding: NSASCIIStringEncoding) if response.lowercaseString.containsString("github") { From b29ea8f2a9f20fa71fe62257f6e6a2cc445efc89 Mon Sep 17 00:00:00 2001 From: Rachel Caileff Date: Sat, 14 May 2016 16:00:41 -0500 Subject: [PATCH 13/14] Fix xcscmblueprint so BuildaKitTests pass --- .../xcshareddata/Buildasaur-TestProject-iOS.xcscmblueprint | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BuildaKitTests/TestProjects/Buildasaur-TestProject-iOS/Buildasaur-TestProject-iOS.xcworkspace/xcshareddata/Buildasaur-TestProject-iOS.xcscmblueprint b/BuildaKitTests/TestProjects/Buildasaur-TestProject-iOS/Buildasaur-TestProject-iOS.xcworkspace/xcshareddata/Buildasaur-TestProject-iOS.xcscmblueprint index 14e688d..3740dc9 100644 --- a/BuildaKitTests/TestProjects/Buildasaur-TestProject-iOS/Buildasaur-TestProject-iOS.xcworkspace/xcshareddata/Buildasaur-TestProject-iOS.xcscmblueprint +++ b/BuildaKitTests/TestProjects/Buildasaur-TestProject-iOS/Buildasaur-TestProject-iOS.xcworkspace/xcshareddata/Buildasaur-TestProject-iOS.xcscmblueprint @@ -15,7 +15,7 @@ "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "BuildaKitTests/TestProjects/Buildasaur-TestProject-iOS/Buildasaur-TestProject-iOS.xcworkspace", "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ { - "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:czechboy0\/Buildasaur.git", + "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "git@github.com:czechboy0\/Buildasaur.git", "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "1C5C2A17EEADA6DBF6678501245487A71FBE28BB" } From 0ca86a0c415da06b94b6ef55950401238132358a Mon Sep 17 00:00:00 2001 From: Rachel Caileff Date: Tue, 17 May 2016 12:55:46 -0500 Subject: [PATCH 14/14] Refactoring, adding logging, and updating tests. Refactor createEnterpriseService into GitService and use it in WorkspaceMetadata.swift so parse has a basic check for whether the potential enterprise git server exists. Log the error when createEnterpriseService catches. Dry up TODO comments in enterprise github tests. Add tests for createEnterpriseService and make tryEndpoint more robust. Fix EnterpriseGitHubSourceTests.swift tests to match GitHubServerTests.swift changes. Fix a now-broken GitService comparison in a test helper. --- BuildaGitServer/Base/Authentication.swift | 14 +------ BuildaGitServer/GitServerPublic.swift | 13 +++++++ .../EnterpriseGitHubSourceTests.swift | 39 +++++++++++++------ BuildaKit/WorkspaceMetadata.swift | 9 +++-- BuildaKitTests/WorkspaceMetadataTests.swift | 2 +- Buildasaur/ProjectViewController.swift | 6 +-- 6 files changed, 50 insertions(+), 33 deletions(-) diff --git a/BuildaGitServer/Base/Authentication.swift b/BuildaGitServer/Base/Authentication.swift index 4caf5f4..6f5765e 100644 --- a/BuildaGitServer/Base/Authentication.swift +++ b/BuildaGitServer/Base/Authentication.swift @@ -49,7 +49,7 @@ extension ProjectAuthenticator: KeychainStringSerializable { service = GitService.BitBucket default: let host = comps[0] - guard let maybeService = createEnterpriseService(host) else { + guard let maybeService = GitService.createEnterpriseService(host) else { throw Error.withInfo("Unsupported service: \(host)") } service = maybeService @@ -73,16 +73,4 @@ extension ProjectAuthenticator: KeychainStringSerializable { self.secret ].joinWithSeparator(":") } - - internal static func createEnterpriseService(host: String) -> GitService? { - guard let url = NSURL(string: "http://\(host)") else { return nil } - do { - let response = try NSString.init(contentsOfURL: url, encoding: NSASCIIStringEncoding) - if response.lowercaseString.containsString("github") { - return GitService.EnterpriseGitHub(host: host) - } - } catch { - } - return nil - } } diff --git a/BuildaGitServer/GitServerPublic.swift b/BuildaGitServer/GitServerPublic.swift index 7a4cd0a..eab7fa3 100644 --- a/BuildaGitServer/GitServerPublic.swift +++ b/BuildaGitServer/GitServerPublic.swift @@ -81,6 +81,19 @@ public enum GitService { case .BitBucket: return BuildasaurKeys().bitBucketAPIClientSecret() } } + + public static func createEnterpriseService(host: String) -> GitService? { + guard let url = NSURL(string: "http://\(host)") else { return nil } + do { + let response = try NSString.init(contentsOfURL: url, encoding: NSASCIIStringEncoding) + if response.lowercaseString.containsString("github") { + return GitService.EnterpriseGitHub(host: host) + } + } catch { + Log.error("\(error)") + } + return nil + } } public class GitServer : HTTPServer { diff --git a/BuildaGitServerTests/EnterpriseGitHubSourceTests.swift b/BuildaGitServerTests/EnterpriseGitHubSourceTests.swift index aa8d26e..b8f6663 100644 --- a/BuildaGitServerTests/EnterpriseGitHubSourceTests.swift +++ b/BuildaGitServerTests/EnterpriseGitHubSourceTests.swift @@ -14,6 +14,7 @@ import BuildaUtils class EnterpriseGitHubSourceTests: XCTestCase { var github: GitHubServer! + var repo: String = "my/repo" // TODO: fill in accessible enterprise github repo override func setUp() { super.setUp() @@ -32,29 +33,43 @@ class EnterpriseGitHubSourceTests: XCTestCase { let expect = expectationWithDescription("Waiting for url request") - let request = try! self.github.endpoints.createRequest(method, endpoint: endpoint, params: params) + do { + let request = try self.github.endpoints.createRequest(method, endpoint: endpoint, params: params) - self.github.http.sendRequest(request, completion: { (response, body, error) -> () in + self.github.http.sendRequest(request, completion: { (response, body, error) -> () in - completion(body: body, error: error) - expect.fulfill() - }) + completion(body: body, error: error) + expect.fulfill() + }) - waitForExpectationsWithTimeout(10, handler: nil) + waitForExpectationsWithTimeout(10, handler: nil) + } catch { + XCTFail() + } } +// func testCreateEnterpriseServiceShouldReturnNilWhenPassedAnInvalidHostname() { +// let gitService = GitService.createEnterpriseService("some.fakehostname.com") +// XCTAssertNil(gitService) +// } +// +// func testCreateEnterpriseServiceShouldReturnAGitServiceWhenPassedAValidHostname() { +// let gitService = GitService.createEnterpriseService("github.com") +// XCTAssertNotNil(gitService) +// } +// // func testGetPullRequests() { // // let params = [ -// "repo": "my/repo" // TODO: fill in accessible enterprise github repo +// "repo": self.repo // ] // // 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: [GitHubPullRequest] = GitHubArray(body) -// XCTAssertGreaterThan(prs.count, 0, "We need > 0 items to test parsing") +// let prs: [GitHubPullRequest]? = try? GitHubArray(body) +// XCTAssertGreaterThan(prs?.count ?? -1, 0, "We need > 0 items to test parsing") // Log.verbose("Parsed PRs: \(prs)") // } else { // XCTFail("Body nil") @@ -65,15 +80,15 @@ class EnterpriseGitHubSourceTests: XCTestCase { // func testGetBranches() { // // let params = [ -// "repo": "my/repo" // TODO: fill in accessible enterprise github repo +// "repo": self.repo // ] // // 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: [GitHubBranch] = GitHubArray(body) -// XCTAssertGreaterThan(branches.count, 0, "We need > 0 items to test parsing") +// let branches: [GitHubBranch]? = try? GitHubArray(body) +// XCTAssertGreaterThan(branches?.count ?? -1, 0, "We need > 0 items to test parsing") // Log.verbose("Parsed branches: \(branches)") // } else { // XCTFail("Body nil") diff --git a/BuildaKit/WorkspaceMetadata.swift b/BuildaKit/WorkspaceMetadata.swift index 5fe574b..1c11d3b 100644 --- a/BuildaKit/WorkspaceMetadata.swift +++ b/BuildaKit/WorkspaceMetadata.swift @@ -78,11 +78,12 @@ extension WorkspaceMetadata { gitService = .GitHub } else if url.resourceSpecifier.containsString(GitService.BitBucket.hostname()) { gitService = .BitBucket - } else if url.resourceSpecivier.containsString(GitService.EnterpriseGitHub.hostname() { - var urlPieces = urlString.split(":") - gitService = .EnterpriseGitHub(host: urlPieces[0])) } else { - Log.error("This git service is not yet supported.") + var urlPieces = projectURLString.split(":") + gitService = GitService.createEnterpriseService(urlPieces[0]) + if gitService == nil { + Log.error("This git service is not yet supported.") + } } switch url.scheme { diff --git a/BuildaKitTests/WorkspaceMetadataTests.swift b/BuildaKitTests/WorkspaceMetadataTests.swift index d5baef8..1683a07 100644 --- a/BuildaKitTests/WorkspaceMetadataTests.swift +++ b/BuildaKitTests/WorkspaceMetadataTests.swift @@ -20,7 +20,7 @@ class WorkspaceMetadataTests: XCTestCase { } expect(checkoutType) == expectedCheckoutType - expect(service) == expectedGitService + expect(service.type()) == expectedGitService.type() } // MARK: GitHub diff --git a/Buildasaur/ProjectViewController.swift b/Buildasaur/ProjectViewController.swift index 5b4406a..b4bb241 100644 --- a/Buildasaur/ProjectViewController.swift +++ b/Buildasaur/ProjectViewController.swift @@ -169,7 +169,7 @@ class ProjectViewController: ConfigEditViewController { var showTokenField = false switch service { - case GitService.GitHub: + case .GitHub: if let auth = auth where auth.type == .PersonalToken && !auth.secret.isEmpty { self.tokenTextField.stringValue = auth.secret } else { @@ -179,7 +179,7 @@ class ProjectViewController: ConfigEditViewController { self.loginButton.hidden = alreadyHasAuth self.logoutButton.hidden = !alreadyHasAuth showTokenField = userWantsTokenAuth && (auth?.type == .PersonalToken || auth == nil) - case GitService.EnterpriseGitHub: + case .EnterpriseGitHub: if !alreadyHasAuth { self.tokenTextField.stringValue = "" } @@ -187,7 +187,7 @@ class ProjectViewController: ConfigEditViewController { self.loginButton.hidden = true self.logoutButton.hidden = true showTokenField = true - case GitService.BitBucket: + case .BitBucket: self.useTokenButton.hidden = true self.loginButton.hidden = alreadyHasAuth self.logoutButton.hidden = !alreadyHasAuth