Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deep links into Xcode from GitHub comments #176

Merged
merged 3 commits into from
Oct 14, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 40 additions & 15 deletions BuildaKit/SyncPairResolver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ public class SyncPairResolver {
commit: String,
issue: Issue?,
bot: Bot,
hostname: String,
integrations: [Integration]) -> SyncPair.Actions {

var integrationsToCancel: [Integration] = []
Expand Down Expand Up @@ -114,12 +115,17 @@ public class SyncPairResolver {
$0.currentStep == .Completed
}

let link = { (integration: Integration) -> String in
SyncPairResolver.linkToServer(hostname, bot: bot, integration: integration)
}

//resolve to a status
let actions = self.resolveCommitStatusFromLatestIntegrations(
commit,
issue: issue,
pending: latestPendingIntegration,
running: runningIntegration,
link: link,
completed: completedIntegrations)

//merge in nested actions
Expand Down Expand Up @@ -186,22 +192,35 @@ public class SyncPairResolver {
return sortedHeadCommitIntegrations
}

class func linkToServer(hostname: String, bot: Bot, integration: Integration) -> String {

//unfortunately, since github doesn't allow non-https links anywhere, we
//must proxy through Satellite (https://github.com/czechboy0/satellite)
//all it does is it redirects to the desired xcbot://... url, which in
//turn opens Xcode on the integration page. all good!

// let link = "xcbot://\(hostname)/botID/\(bot.id)/integrationID/\(integration.id)"
let link = "https://stlt.herokuapp.com/v1/xcs_deeplink/\(hostname)/\(bot.id)/\(integration.id)"
return link
}

func resolveCommitStatusFromLatestIntegrations(
commit: String,
issue: Issue?,
pending: Integration?,
running: Integration?,
link: (Integration) -> String,
completed: Set<Integration>) -> SyncPair.Actions {

let statusWithComment: HDGitHubXCBotSyncer.GitHubStatusAndComment
var integrationsToCancel: [Integration] = []

//if there's any pending integration, we're ["Pending" - Waiting in the queue]
if let _ = pending {
if let pending = pending {

//TODO: show how many builds are ahead in the queue and estimate when it will be
//started and when finished? (there is an average running time on each bot, it should be easy)
let status = HDGitHubXCBotSyncer.createStatusFromState(.Pending, description: "Build waiting in the queue...")
let status = HDGitHubXCBotSyncer.createStatusFromState(.Pending, description: "Build waiting in the queue...", targetUrl: link(pending))
statusWithComment = (status: status, comment: nil)

//also, cancel the running integration, if it's there any
Expand All @@ -216,7 +235,7 @@ public class SyncPairResolver {
//there is a running integration.
//TODO: estimate, based on the average running time of this bot and on the started timestamp, when it will finish. add that to the description.
let currentStepString = running.currentStep.rawValue
let status = HDGitHubXCBotSyncer.createStatusFromState(.Pending, description: "Integration step: \(currentStepString)...")
let status = HDGitHubXCBotSyncer.createStatusFromState(.Pending, description: "Integration step: \(currentStepString)...", targetUrl: link(running))
statusWithComment = (status: status, comment: nil)

} else {
Expand All @@ -225,12 +244,12 @@ public class SyncPairResolver {
if completed.count > 0 {

//we have some completed integrations
statusWithComment = self.resolveStatusFromCompletedIntegrations(completed)
statusWithComment = self.resolveStatusFromCompletedIntegrations(completed, link: link)

} else {
//this shouldn't happen.
Log.error("LOGIC ERROR! This shouldn't happen, there are no completed integrations!")
let status = HDGitHubXCBotSyncer.createStatusFromState(.Error, description: "* UNKNOWN STATE, Builda ERROR *")
let status = HDGitHubXCBotSyncer.createStatusFromState(.Error, description: "* UNKNOWN STATE, Builda ERROR *", targetUrl: nil)
statusWithComment = (status: status, "Builda error, unknown state!")
}
}
Expand All @@ -255,7 +274,9 @@ public class SyncPairResolver {
}

func resolveStatusFromCompletedIntegrations(
integrations: Set<Integration>) -> HDGitHubXCBotSyncer.GitHubStatusAndComment {
integrations: Set<Integration>,
link: (Integration) -> String
) -> HDGitHubXCBotSyncer.GitHubStatusAndComment {

//get integrations sorted by number
let sortedDesc = Array(integrations).sort { $0.number > $1.number }
Expand All @@ -272,9 +293,10 @@ public class SyncPairResolver {
}
}).first {

var lines = HDGitHubXCBotSyncer.baseCommentLinesFromIntegration(passingIntegration)
let linkToIntegration = link(passingIntegration)
var lines = HDGitHubXCBotSyncer.baseCommentLinesFromIntegration(passingIntegration, link: linkToIntegration)

let status = HDGitHubXCBotSyncer.createStatusFromState(.Success, description: "Build passed!")
let status = HDGitHubXCBotSyncer.createStatusFromState(.Success, description: "Build passed!", targetUrl: linkToIntegration)

let summary = passingIntegration.buildResultSummary!
if passingIntegration.result == .Succeeded {
Expand Down Expand Up @@ -302,8 +324,9 @@ public class SyncPairResolver {
$0.result! == Integration.Result.TestFailures
}).first {

var lines = HDGitHubXCBotSyncer.baseCommentLinesFromIntegration(testFailingIntegration)
let status = HDGitHubXCBotSyncer.createStatusFromState(.Failure, description: "Build failed tests!")
let linkToIntegration = link(testFailingIntegration)
var lines = HDGitHubXCBotSyncer.baseCommentLinesFromIntegration(testFailingIntegration, link: linkToIntegration)
let status = HDGitHubXCBotSyncer.createStatusFromState(.Failure, description: "Build failed tests!", targetUrl: linkToIntegration)
let summary = testFailingIntegration.buildResultSummary!
let testFailureCount = summary.testFailureCount
lines.append(resultString + "**Build failed \(testFailureCount) " + "test".pluralizeStringIfNecessary(testFailureCount) + "** out of \(summary.testsCount)")
Expand All @@ -315,9 +338,10 @@ public class SyncPairResolver {
$0.result! != Integration.Result.Canceled
}).first {

var lines = HDGitHubXCBotSyncer.baseCommentLinesFromIntegration(erroredIntegration)
let linkToIntegration = link(erroredIntegration)
var lines = HDGitHubXCBotSyncer.baseCommentLinesFromIntegration(erroredIntegration, link: linkToIntegration)
let errorCount: Int = erroredIntegration.buildResultSummary?.errorCount ?? -1
let status = HDGitHubXCBotSyncer.createStatusFromState(.Error, description: "Build error!")
let status = HDGitHubXCBotSyncer.createStatusFromState(.Error, description: "Build error!", targetUrl: linkToIntegration)
lines.append(resultString + "**\(errorCount) " + "error".pluralizeStringIfNecessary(errorCount) + ", failing state: \(erroredIntegration.result!.rawValue)**")
return self.statusAndCommentFromLines(lines, status: status)
}
Expand All @@ -327,16 +351,17 @@ public class SyncPairResolver {
$0.result! == Integration.Result.Canceled
}).first {

var lines = HDGitHubXCBotSyncer.baseCommentLinesFromIntegration(canceledIntegration)
let status = HDGitHubXCBotSyncer.createStatusFromState(.Error, description: "Build canceled!")
let linkToIntegration = link(canceledIntegration)
var lines = HDGitHubXCBotSyncer.baseCommentLinesFromIntegration(canceledIntegration, link: linkToIntegration)
let status = HDGitHubXCBotSyncer.createStatusFromState(.Error, description: "Build canceled!", targetUrl: linkToIntegration)

//TODO: find out who canceled it and add it to the comment?
lines.append("Build was **manually canceled**.")
return self.statusAndCommentFromLines(lines, status: status)
}

//hmm no idea, if we got all the way here. just leave it with no state.
let status = HDGitHubXCBotSyncer.createStatusFromState(.NoState, description: nil)
let status = HDGitHubXCBotSyncer.createStatusFromState(.NoState, description: nil, targetUrl: nil)
return (status: status, comment: nil)
}
}
35 changes: 22 additions & 13 deletions BuildaKit/SyncPair_Branch_Bot.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,24 +42,33 @@ public class SyncPair_Branch_Bot: SyncPair {
let headCommit = self.branch.commit.sha
let issue: Issue? = nil //TODO: only pull/create if we're failing

self.getIntegrations(bot, completion: { (integrations, error) -> () in
self.syncer.xcodeServer.getHostname { (hostname, error) -> () in

if let error = error {
completion(error: error)
return
}

let actions = self.resolver.resolveActionsForCommitAndIssueWithBotIntegrations(
headCommit,
issue: issue,
bot: bot,
integrations: integrations)

//in case of branches, we also (optionally) want to add functionality for creating an issue if the branch starts failing and updating with comments the same way we do with PRs.
//also, when the build is finally successful on the branch, the issue will be automatically closed.
//TODO: add this functionality here and add it as another action available from a sync pair

self.performActions(actions, completion: completion)
})
self.getIntegrations(bot, completion: { (integrations, error) -> () in

if let error = error {
completion(error: error)
return
}

let actions = self.resolver.resolveActionsForCommitAndIssueWithBotIntegrations(
headCommit,
issue: issue,
bot: bot,
hostname: hostname!,
integrations: integrations)

//in case of branches, we also (optionally) want to add functionality for creating an issue if the branch starts failing and updating with comments the same way we do with PRs.
//also, when the build is finally successful on the branch, the issue will be automatically closed.
//TODO: add this functionality here and add it as another action available from a sync pair

self.performActions(actions, completion: completion)
})
}
}
}
25 changes: 17 additions & 8 deletions BuildaKit/SyncPair_PR_Bot.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public class SyncPair_PR_Bot: SyncPair {
let pr = self.pr
let headCommit = pr.head.sha
let issue = pr

self.getIntegrations(bot, completion: { (integrations, error) -> () in

if let error = error {
Expand All @@ -61,19 +61,28 @@ public class SyncPair_PR_Bot: SyncPair {

if isEnabled {

let actions = self.resolver.resolveActionsForCommitAndIssueWithBotIntegrations(
headCommit,
issue: issue,
bot: bot,
integrations: integrations)
self.performActions(actions, completion: completion)
self.syncer.xcodeServer.getHostname { (hostname, error) -> () in

if let error = error {
completion(error: error)
return
}

let actions = self.resolver.resolveActionsForCommitAndIssueWithBotIntegrations(
headCommit,
issue: issue,
bot: bot,
hostname: hostname!,
integrations: integrations)
self.performActions(actions, completion: completion)
}

} else {

//not enabled, make sure the PR reflects that and the instructions are clear
Log.verbose("Bot \(bot.name) is not yet enabled, ignoring...")

let status = HDGitHubXCBotSyncer.createStatusFromState(.Pending, description: "Waiting for \"lttm\" to start testing")
let status = HDGitHubXCBotSyncer.createStatusFromState(.Pending, description: "Waiting for \"lttm\" to start testing", targetUrl: nil)
let notYetEnabled: HDGitHubXCBotSyncer.GitHubStatusAndComment = (status: status, comment: nil)
syncer.updateCommitStatusIfNecessary(notYetEnabled, commit: headCommit, issue: pr, completion: completion)
}
Expand Down
10 changes: 8 additions & 2 deletions BuildaKit/SyncerBotUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,17 @@ extension HDGitHubXCBotSyncer {
}
}

class func baseCommentLinesFromIntegration(integration: Integration) -> [String] {
class func baseCommentLinesFromIntegration(integration: Integration, link: String?) -> [String] {

var lines = [String]()

lines.append("Result of Integration **\(integration.number)**")
var integrationText = "Integration \(integration.number)"
if let link = link {
//linkify
integrationText = "[\(integrationText)](\(link))"
}

lines.append("Result of \(integrationText)")
lines.append("---")

if let duration = self.formattedDurationOfIntegration(integration) {
Expand Down
6 changes: 3 additions & 3 deletions BuildaKit/SyncerGitHubUtils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import BuildaUtils

extension HDGitHubXCBotSyncer {

class func createStatusFromState(state: Status.State, description: String?) -> Status {
class func createStatusFromState(state: Status.State, description: String?, targetUrl: String?) -> Status {

//TODO: add useful targetUrl and potentially have multiple contexts to show multiple stats on the PR
//TODO: potentially have multiple contexts to show multiple stats on the PR
let context = "Buildasaur"
return Status(state: state, description: description, targetUrl: nil, context: context)
return Status(state: state, description: description, targetUrl: targetUrl, context: context)
}

func updateCommitStatusIfNecessary(
Expand Down
Loading