Skip to content

Commit

Permalink
Merge pull request #141 from czechboy0/hd/checkout-blueprint-files
Browse files Browse the repository at this point in the history
Refactored loading of checkout file, added support for xcscmblueprint file
  • Loading branch information
czechboy0 committed Sep 29, 2015
2 parents 87641d0 + 2d87606 commit d9165d7
Show file tree
Hide file tree
Showing 13 changed files with 253 additions and 168 deletions.
6 changes: 3 additions & 3 deletions BuildaKit/HDGitHubXCBotSyncer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -214,9 +214,9 @@ public class HDGitHubXCBotSyncer : Syncer {

public func syncPRsAndBranchesAndBots(repo repo: Repo, repoName: String, prs: [PullRequest], branches: [Branch], bots: [Bot], completion: () -> ()) {

let prsDescription = prs.map({ "\n\tPR \($0.number): \($0.title) [\($0.head.ref) -> \($0.base.ref)]" }) + ["\n"]
let branchesDescription = branches.map({ "\n\tBranch [\($0.name):\($0.commit.sha)]" }) + ["\n"]
let botsDescription = bots.map({ "\n\tBot \($0.name)" }) + ["\n"]
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")
Log.verbose("Resolving prs:\n\(prsDescription) \nand branches:\n\(branchesDescription)\nand bots:\n\(botsDescription)")

//create the changes necessary
Expand Down
134 changes: 11 additions & 123 deletions BuildaKit/Project.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,6 @@ import XcodeServerSDK

public class Project : JSONSerializable {

public enum AllowedCheckoutTypes: String {
case SSH = "SSH"
// case HTTPS - not yet supported, right now only SSH is supported
// (for bots reasons, will be built in when I have time)
// case SVN - not yet supported yet
}

public var url: NSURL {
didSet {
do { try self.refreshMetadata() } catch {}
Expand All @@ -35,68 +28,10 @@ public class Project : JSONSerializable {

public var availabilityState: AvailabilityCheckState

private(set) var workspaceMetadata: NSDictionary?
let forkOriginURL: String?

//convenience getters
public var projectName: String? { get { return self.pullValueForKey("IDESourceControlProjectName") }}
public var projectPath: String? { get { return self.pullValueForKey("IDESourceControlProjectPath") }}
public var projectWCCIdentifier: String? { get { return self.pullValueForKey("IDESourceControlProjectWCCIdentifier") }}
public var projectWCCName: String? {
get {
if let wccId = self.projectWCCIdentifier {
if let wcConfigs = self.workspaceMetadata?["IDESourceControlProjectWCConfigurations"] as? [NSDictionary] {
if let foundConfig = wcConfigs.filter({
if let loopWccId = $0.optionalStringForKey("IDESourceControlWCCIdentifierKey") {
return loopWccId == wccId
}
return false
}).first {
//so much effort for this little key...
return foundConfig.optionalStringForKey("IDESourceControlWCCName")
}
}
}
return nil
}
}
public var projectURL: NSURL? {
get {
if let urlString = self.pullValueForKey("IDESourceControlProjectURL") {

//if we have a fork, chose its URL, otherwise fallback to the loaded URL from the Checkout file
var finalUrlString = self.forkOriginURL ?? urlString
let type = self.checkoutType!
if type == .SSH {
if !finalUrlString.hasPrefix("git@") {
finalUrlString = "git@\(finalUrlString)"
}
}

return NSURL(string: finalUrlString)
}
return nil
}
}

public var checkoutType: AllowedCheckoutTypes? {
get {
if
let meta = self.workspaceMetadata,
let type = Project.parseCheckoutType(meta) {
return type
}
return nil
}
}

private func pullValueForKey(key: String) -> String? {
return self.workspaceMetadata?.optionalStringForKey(key)
}
private(set) public var workspaceMetadata: WorkspaceMetadata?

public init?(url: NSURL) {

self.forkOriginURL = nil
self.url = url
self.preferredTemplateId = nil
self.githubToken = nil
Expand All @@ -112,81 +47,33 @@ public class Project : JSONSerializable {
}
}

private init?(original: Project, forkOriginURL: String) {
private init(original: Project, forkOriginURL: String) throws {

self.forkOriginURL = forkOriginURL
self.url = original.url
self.preferredTemplateId = original.preferredTemplateId
self.githubToken = original.githubToken
self.availabilityState = original.availabilityState
self.publicSSHKeyUrl = original.publicSSHKeyUrl
self.privateSSHKeyUrl = original.privateSSHKeyUrl
self.sshPassphrase = original.sshPassphrase
do {
try self.refreshMetadata()
} catch {
Log.error(error)
return nil
}
self.workspaceMetadata = try original.workspaceMetadata?.duplicateWithForkURL(forkOriginURL)
}

public func duplicateForForkAtOriginURL(forkURL: String) -> Project? {

return Project(original: self, forkOriginURL: forkURL)
public func duplicateForForkAtOriginURL(forkURL: String) throws -> Project {
return try Project(original: self, forkOriginURL: forkURL)
}

public class func attemptToParseFromUrl(url: NSURL) throws -> NSDictionary {

let meta = try Project.loadWorkspaceMetadata(url)

//validate allowed remote url
if self.parseCheckoutType(meta) == nil {
//disallowed
let allowedString = [AllowedCheckoutTypes.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
}

return meta
}

private class func parseCheckoutType(metadata: NSDictionary) -> AllowedCheckoutTypes? {

if
let urlString = metadata.optionalStringForKey("IDESourceControlProjectURL"),
let url = NSURL(string: urlString)
{
let scheme = url.scheme
switch scheme {
case "github.com":
return AllowedCheckoutTypes.SSH
case "https":

if urlString.hasSuffix(".git") {
//HTTPS git
} else {
//SVN
}

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
}
} else {
return nil
}
public class func attemptToParseFromUrl(url: NSURL) throws -> WorkspaceMetadata {
return try Project.loadWorkspaceMetadata(url)
}

private func refreshMetadata() throws {

let meta = try Project.attemptToParseFromUrl(self.url)
self.workspaceMetadata = meta
}

public required init?(json: NSDictionary) {

self.forkOriginURL = nil
self.availabilityState = .Unchecked

if
Expand Down Expand Up @@ -224,19 +111,20 @@ public class Project : JSONSerializable {
self.publicSSHKeyUrl = nil
self.privateSSHKeyUrl = nil
self.sshPassphrase = nil
self.workspaceMetadata = nil
return nil
}
}

public init() {
self.forkOriginURL = nil
self.availabilityState = .Unchecked
self.url = NSURL()
self.preferredTemplateId = nil
self.githubToken = nil
self.publicSSHKeyUrl = nil
self.privateSSHKeyUrl = nil
self.sshPassphrase = nil
self.workspaceMetadata = nil
}

public func jsonify() -> NSDictionary {
Expand All @@ -260,14 +148,14 @@ public class Project : JSONSerializable {
return names
}

private class func loadWorkspaceMetadata(url: NSURL) throws -> NSDictionary {
private class func loadWorkspaceMetadata(url: NSURL) throws -> WorkspaceMetadata {

return try XcodeProjectParser.parseRepoMetadataFromProjectOrWorkspaceURL(url)
}

public func githubRepoName() -> String? {

if let projectUrl = self.projectURL {
if let projectUrl = self.workspaceMetadata?.projectURL {
let originalStringUrl = projectUrl.absoluteString
let stringUrl = originalStringUrl.lowercaseString

Expand Down
98 changes: 98 additions & 0 deletions BuildaKit/SourceControlFileParser.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
//
// SourceControlFileParser.swift
// Buildasaur
//
// Created by Honza Dvorsky on 29/09/2015.
// Copyright © 2015 Honza Dvorsky. All rights reserved.
//

import Foundation
import BuildaUtils

protocol SourceControlFileParser {

func supportedFileExtensions() -> [String]
func parseFileAtUrl(url: NSURL) throws -> WorkspaceMetadata
}

class CheckoutFileParser: SourceControlFileParser {

func supportedFileExtensions() -> [String] {
return ["xccheckout"]
}

func parseFileAtUrl(url: NSURL) throws -> WorkspaceMetadata {

//plist -> NSDictionary
guard let dictionary = NSDictionary(contentsOfURL: url) else { throw Error.withInfo("Failed to parse \(url)") }

//parse our required keys
let projectName = dictionary.optionalStringForKey("IDESourceControlProjectName")
let projectPath = dictionary.optionalStringForKey("IDESourceControlProjectPath")
let projectWCCIdentifier = dictionary.optionalStringForKey("IDESourceControlProjectWCCIdentifier")
let projectWCCName = { () -> String? in
if let wccId = projectWCCIdentifier {
if let wcConfigs = dictionary["IDESourceControlProjectWCConfigurations"] as? [NSDictionary] {
if let foundConfig = wcConfigs.filter({
if let loopWccId = $0.optionalStringForKey("IDESourceControlWCCIdentifierKey") {
return loopWccId == wccId
}
return false
}).first {
//so much effort for this little key...
return foundConfig.optionalStringForKey("IDESourceControlWCCName")
}
}
}
return nil
}()
let projectURLString = { dictionary.optionalStringForKey("IDESourceControlProjectURL") }()

return try WorkspaceMetadata(projectName: projectName, projectPath: projectPath, projectWCCIdentifier: projectWCCIdentifier, projectWCCName: projectWCCName, projectURLString: projectURLString)
}
}

class BlueprintFileParser: SourceControlFileParser {

func supportedFileExtensions() -> [String] {
return ["xcscmblueprint"]
}

func parseFileAtUrl(url: NSURL) throws -> WorkspaceMetadata {

//JSON -> NSDictionary
let data = try NSData(contentsOfURL: url, options: NSDataReadingOptions())
let jsonObject = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions())
guard let dictionary = jsonObject as? NSDictionary else { throw Error.withInfo("Failed to parse \(url)") }

//parse our required keys
let projectName = dictionary.optionalStringForKey("DVTSourceControlWorkspaceBlueprintNameKey")
let projectPath = dictionary.optionalStringForKey("DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey")
let projectWCCIdentifier = dictionary.optionalStringForKey("DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey")

var primaryRemoteRepositoryDictionary: NSDictionary?
if let wccId = projectWCCIdentifier {
if let wcConfigs = dictionary["DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey"] as? [NSDictionary] {
primaryRemoteRepositoryDictionary = wcConfigs.filter({
if let loopWccId = $0.optionalStringForKey("DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey") {
return loopWccId == wccId
}
return false
}).first
}
}

let projectURLString = primaryRemoteRepositoryDictionary?.optionalStringForKey("DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey")

var projectWCCName: String?
if
let copyPaths = dictionary["DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey"] as? [String: String],
let primaryRemoteRepoId = projectWCCIdentifier
{
projectWCCName = copyPaths[primaryRemoteRepoId]
}

return try WorkspaceMetadata(projectName: projectName, projectPath: projectPath, projectWCCIdentifier: projectWCCIdentifier, projectWCCName: projectWCCName, projectURLString: projectURLString)
}
}

9 changes: 5 additions & 4 deletions BuildaKit/SyncerBotManipulation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,17 @@ extension HDGitHubXCBotSyncer {

//to handle forks
let headOriginUrl = repo.repoUrlSSH
let localProjectOriginUrl = self.project.projectURL!.absoluteString
let localProjectOriginUrl = self.project.workspaceMetadata!.projectURL.absoluteString

let project: Project
if headOriginUrl != localProjectOriginUrl {

//we have a fork, duplicate the metadata with the fork's origin
if let source = self.project.duplicateForForkAtOriginURL(headOriginUrl) {
do {
let source = try self.project.duplicateForForkAtOriginURL(headOriginUrl)
project = source
} else {
self.notifyError(Error.withInfo("Couldn't create a Project for fork with origin at url \(headOriginUrl)"), context: "Creating a bot from a PR")
} catch {
self.notifyError(Error.withInfo("Couldn't create a Project for fork with origin at url \(headOriginUrl), error \(error)"), context: "Creating a bot from a PR")
completion()
return
}
Expand Down
Loading

0 comments on commit d9165d7

Please sign in to comment.