Skip to content

Commit

Permalink
Merge pull request #125 from czechboy0/hd/heartbeat
Browse files Browse the repository at this point in the history
Anonymous Heartbeat Sending
  • Loading branch information
czechboy0 committed Sep 17, 2015
2 parents a7595dd + 6b71e6a commit 074255e
Show file tree
Hide file tree
Showing 10 changed files with 446 additions and 5 deletions.
19 changes: 19 additions & 0 deletions BuildaHeartbeatKit/BuildaHeartbeatKit.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// BuildaHeartbeatKit.h
// BuildaHeartbeatKit
//
// Created by Honza Dvorsky on 17/09/2015.
// Copyright © 2015 Honza Dvorsky. All rights reserved.
//

#import <Cocoa/Cocoa.h>

//! Project version number for BuildaHeartbeatKit.
FOUNDATION_EXPORT double BuildaHeartbeatKitVersionNumber;

//! Project version string for BuildaHeartbeatKit.
FOUNDATION_EXPORT const unsigned char BuildaHeartbeatKitVersionString[];

// In this header, you should import all the public headers of your framework using statements like #import <BuildaHeartbeatKit/PublicHeader.h>


107 changes: 107 additions & 0 deletions BuildaHeartbeatKit/Heartbeat.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
//
// Heartbeat.swift
// Buildasaur
//
// Created by Honza Dvorsky on 17/09/2015.
// Copyright © 2015 Honza Dvorsky. All rights reserved.
//

import Foundation
import ekgclient
import BuildaUtils

public protocol HeartbeatManagerDelegate {
func numberOfRunningSyncers() -> Int
}

//READ: https://github.com/czechboy0/Buildasaur/tree/master#heartpulse-heartbeat
@objc public class HeartbeatManager: NSObject {

public var delegate: HeartbeatManagerDelegate?

private let client: EkgClient
private let creationTime: Double
private var timer: NSTimer?
private let interval: Double = 24 * 60 * 60 //send heartbeat once in 24 hours

public init(server: String) {
let bundle = NSBundle.mainBundle()
let appIdentifier = EkgClientHelper.pullAppIdentifierFromBundle(bundle) ?? "Unknown app"
let version = EkgClientHelper.pullVersionFromBundle(bundle) ?? "?"
let buildNumber = EkgClientHelper.pullBuildNumberFromBundle(bundle) ?? "?"
let appInfo = AppInfo(appIdentifier: appIdentifier, version: version, build: buildNumber)
let host = NSURL(string: server)!
let serverInfo = ServerInfo(host: host)
let userDefaults = NSUserDefaults.standardUserDefaults()

self.creationTime = NSDate().timeIntervalSince1970
let client = EkgClient(userDefaults: userDefaults, appInfo: appInfo, serverInfo: serverInfo)
self.client = client
}

deinit {
self.stop()
}

public func start() {
self.sendLaunchedEvent()
self.startSendingHeartbeat()
}

public func stop() {
self.stopSendingHeartbeat()
}

private func sendEvent(event: Event) {

var sendReal = true
#if DEBUG
sendReal = false
#endif

guard sendReal else {
Log.info("Not sending events in debug environment")
return
}

Log.info("Sending heartbeat event \(event.jsonify())")

self.client.sendEvent(event) {
if let error = $0 {
Log.error("Failed to send a heartbeat event. Error \(error)")
}
}
}

private func sendLaunchedEvent() {
self.sendEvent(LaunchEvent())
}

private func sendHeartbeatEvent() {
let uptime = NSDate().timeIntervalSince1970 - self.creationTime
let numberOfRunningSyncers = self.delegate?.numberOfRunningSyncers() ?? 0
self.sendEvent(HeartbeatEvent(uptime: uptime, numberOfRunningSyncers: numberOfRunningSyncers))
}

func _timerFired(timer: NSTimer?=nil) {
self.sendHeartbeatEvent()
}

private func startSendingHeartbeat() {

//send once now
self._timerFired()

self.timer?.invalidate()
self.timer = NSTimer.scheduledTimerWithTimeInterval(
self.interval,
target: self,
selector: "_timerFired:",
userInfo: nil,
repeats: true)
}

private func stopSendingHeartbeat() {
timer?.invalidate()
}
}
28 changes: 28 additions & 0 deletions BuildaHeartbeatKit/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(CURRENT_PROJECT_VERSION)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2015 Honza Dvorsky. All rights reserved.</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
48 changes: 47 additions & 1 deletion BuildaKit/StorageManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Foundation
import BuildaGitServer
import BuildaUtils
import XcodeServerSDK
import BuildaHeartbeatKit

public class StorageManager {

Expand All @@ -19,11 +20,22 @@ public class StorageManager {
private(set) public var servers: [XcodeServerConfig] = []
private(set) public var projects: [Project] = []
private(set) public var buildTemplates: [BuildTemplate] = []
private(set) public var config: [String: AnyObject] = [:]

private var heartbeatManager: HeartbeatManager!

init() {

//initialize all stored Syncers
self.loadAllFromPersistence()
if let heartbeatOptOut = self.config["heartbeat_opt_out"] as? Bool where heartbeatOptOut {
Log.info("User opted out of anonymous heartbeat")
} else {
Log.info("Will send anonymous heartbeat. To opt out add `\"heartbeat_opt_out\" = true` to ~/Library/Application Support/Buildasaur/Config.json")
self.heartbeatManager = HeartbeatManager(server: "https://builda-ekg.herokuapp.com")
self.heartbeatManager.delegate = self
self.heartbeatManager.start()
}
}

deinit {
Expand Down Expand Up @@ -135,12 +147,30 @@ public class StorageManager {

public func loadAllFromPersistence() {

self.loadConfig()
self.loadProjects()
self.loadServers()
self.loadSyncers()
self.loadBuildTemplates()
}

func loadConfig() {
let configUrl = Persistence.getFileInAppSupportWithName("Config.json", isDirectory: false)
do {
let json = try Persistence.loadJSONFromUrl(configUrl)

if let json = json as? [String: AnyObject] {
self.config = json
return
}
} catch {
//file not found
if (error as NSError).code != 260 {
Log.error("Failed to read Config, error \(error). Will be ignored. Please don't play with the persistence :(")
}
}
}

func loadServers() {

self.servers.removeAll(keepCapacity: true)
Expand Down Expand Up @@ -243,6 +273,16 @@ public class StorageManager {
})
}

public func saveConfig() {
let configUrl = Persistence.getFileInAppSupportWithName("Config.json", isDirectory: false)
let json = self.config
do {
try Persistence.saveJSONToUrl(json, url: configUrl)
} catch {
assert(false, "Failed to save Config, \(error)")
}
}

public func saveProjects() {

let projectsUrl = Persistence.getFileInAppSupportWithName("Projects.json", isDirectory: false)
Expand Down Expand Up @@ -301,6 +341,7 @@ public class StorageManager {
public func saveAll() {
//save to persistence

self.saveConfig()
self.saveProjects()
self.saveServers()
self.saveBuildTemplates()
Expand All @@ -321,5 +362,10 @@ public class StorageManager {
syncer.active = true
}
}

}

extension StorageManager: HeartbeatManagerDelegate {
public func numberOfRunningSyncers() -> Int {
return self.syncers.filter { $0.active }.count
}
}
Loading

0 comments on commit 074255e

Please sign in to comment.