diff --git a/Utilities/precommit.py b/Utilities/precommit.py new file mode 100755 index 000000000..5ee04b088 --- /dev/null +++ b/Utilities/precommit.py @@ -0,0 +1,120 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os +import sys +import subprocess +import datetime +import argparse +import commands + + +git_repo_path = os.path.abspath(subprocess.check_output(['git', 'rev-parse', '--show-toplevel']).strip()) + + +def splitall(path): + allparts = [] + while 1: + parts = os.path.split(path) + if parts[0] == path: # sentinel for absolute paths + allparts.insert(0, parts[0]) + break + elif parts[1] == path: # sentinel for relative paths + allparts.insert(0, parts[1]) + break + else: + path = parts[0] + allparts.insert(0, parts[1]) + return allparts + + +def process(filepath): + with open(filepath, 'rt') as f: + text = f.read() + original_text = text + + lines = text.split('\n') + while lines and lines[0].startswith('//'): + lines = lines[1:] + text = '\n'.join(lines) + text = text.strip() + + header = '''// +// Copyright (c) %s Open Whisper Systems. All rights reserved. +// + +''' % ( + datetime.datetime.now().year, + ) + text = header + text + '\n' + + if original_text == text: + return + + short_filepath = filepath[len(git_repo_path):] + if short_filepath.startswith(os.sep): + short_filepath = short_filepath[len(os.sep):] + print 'Updating:', short_filepath + + with open(filepath, 'wt') as f: + f.write(text) + + +def should_ignore_path(path): + ignore_paths = [ + os.path.join(git_repo_path, '.git') + ] + for ignore_path in ignore_paths: + if path.startswith(ignore_path): + return True + for component in splitall(path): + if component.startswith('.'): + return True + if component.endswith('.framework'): + return True + if component in ('Pods', 'ThirdParty', 'Carthage',): + return True + + return False + + +def process_if_appropriate(filepath): + filename = os.path.basename(filepath) + if filename.startswith('.'): + return + file_ext = os.path.splitext(filename)[1] + if file_ext not in ('.h', '.hpp', '.cpp', '.m', '.mm', '.pch', '.swift'): + return + if should_ignore_path(filepath): + return + process(filepath) + + +if __name__ == "__main__": + + parser = argparse.ArgumentParser(description='Precommit script.') + parser.add_argument('--all', action='store_true', help='process all files in or below current dir') + args = parser.parse_args() + + if args.all: + for rootdir, dirnames, filenames in os.walk(git_repo_path): + for filename in filenames: + file_path = os.path.abspath(os.path.join(rootdir, filename)) + process_if_appropriate(file_path) + else: + filepaths = [] + + # Staging + output = commands.getoutput('git diff --cached --name-only --diff-filter=ACMR') + filepaths.extend([line.strip() for line in output.split('\n')]) + + # Working + output = commands.getoutput('git diff --name-only --diff-filter=ACMR') + filepaths.extend([line.strip() for line in output.split('\n')]) + + # Only process each path once. + filepaths = sorted(set(filepaths)) + + for filepath in filepaths: + filepath = os.path.abspath(os.path.join(git_repo_path, filepath)) + process_if_appropriate(filepath) diff --git a/src/Util/OWSAnalytics.h b/src/Util/OWSAnalytics.h new file mode 100755 index 000000000..b43717430 --- /dev/null +++ b/src/Util/OWSAnalytics.h @@ -0,0 +1,72 @@ +// +// OWSAnalytics.h +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +typedef NS_ENUM(NSUInteger, OWSAnalyticsSeverity) { + OWSAnalyticsSeverityDebug = 0, + OWSAnalyticsSeverityInfo = 1, + OWSAnalyticsSeverityWarn = 2, + OWSAnalyticsSeverityError = 3, + // I suspect we'll stage the development of our analytics, + // initially building only a minimal solution: an endpoint which + // ignores most requests, and sends only the highest-severity + // events as email to developers. + // + // This "critical" level of severity is intended for that purpose (for now). + // + // We might want to have an additional level of severity for + // critical (crashing) bugs that occur during app startup. These + // events should be sent to the service immediately and the app + // should block until that request completes. + OWSAnalyticsSeverityCritical = 4, + OWSAnalyticsSeverityOff = 5 +}; + +// This is a placeholder. We don't yet serialize or transmit analytics events. +// +// If/when we take this on, we'll want to develop a solution that can be used +// report user activity - especially serious bugs - without compromising user +// privacy in any way. We must _never_ include any identifying information. +@interface OWSAnalytics : NSObject + +// description: A non-empty string without any leading whitespace. +// parameters: Optional. +// If non-nil, the keys should all be non-empty NSStrings. +// Values should be NSStrings or NSNumbers. ++ (void)logEvent:(NSString *)description + severity:(OWSAnalyticsSeverity)severity + parameters:(NSDictionary *)parameters + location:(const char *)location; + +@end + +#define OWSAnalyticsLogEvent(severityLevel, frmt, ...) \ + [OWSAnalytics logEvent:[NSString stringWithFormat:frmt, ##__VA_ARGS__] \ + severity:severityLevel \ + parameters:nil \ + location:__PRETTY_FUNCTION__]; + +#define OWSAnalyticsLogEventWithParameters(severityLevel, frmt, params) \ + [OWSAnalytics logEvent:frmt severity:severityLevel parameters:params location:__PRETTY_FUNCTION__]; + +#define OWSAnalyticsDebug(frmt, ...) OWSAnalyticsLogEvent(OWSAnalyticsSeverityDebug, frmt, ##__VA_ARGS__) +#define OWSAnalyticsDebugWithParameters(description, params) \ + OWSAnalyticsLogEventWithParameters(OWSAnalyticsSeverityDebug, description, params) + +#define OWSAnalyticsInfo(frmt, ...) OWSAnalyticsLogEvent(OWSAnalyticsSeverityInfo, frmt, ##__VA_ARGS__) +#define OWSAnalyticsInfoWithParameters(description, params) \ + OWSAnalyticsLogEventWithParameters(OWSAnalyticsSeverityInfo, description, params) + +#define OWSAnalyticsWarn(frmt, ...) OWSAnalyticsLogEvent(OWSAnalyticsSeverityWarn, frmt, ##__VA_ARGS__) +#define OWSAnalyticsWarnWithParameters(description, params) \ + OWSAnalyticsLogEventWithParameters(OWSAnalyticsSeverityWarn, description, params) + +#define OWSAnalyticsError(frmt, ...) OWSAnalyticsLogEvent(OWSAnalyticsSeverityError, frmt, ##__VA_ARGS__) +#define OWSAnalyticsErrorWithParameters(description, params) \ + OWSAnalyticsLogEventWithParameters(OWSAnalyticsSeverityError, description, params) + +#define OWSAnalyticsCritical(frmt, ...) OWSAnalyticsLogEvent(OWSAnalyticsSeverityCritical, frmt, ##__VA_ARGS__) +#define OWSAnalyticsCriticalWithParameters(description, params) \ + OWSAnalyticsLogEventWithParameters(OWSAnalyticsSeverityCritical, description, params) diff --git a/src/Util/OWSAnalytics.m b/src/Util/OWSAnalytics.m new file mode 100755 index 000000000..c48ac0fe3 --- /dev/null +++ b/src/Util/OWSAnalytics.m @@ -0,0 +1,76 @@ +// +// OWSAnalytics.m +// +// Copyright (c) 2017 Open Whisper Systems. All rights reserved. +// + +#import + +#import "OWSAnalytics.h" + +@implementation OWSAnalytics + ++ (instancetype)sharedInstance +{ + static OWSAnalytics *instance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + instance = [self new]; + // TODO: If we ever log these events to disk, + // we may want to protect these file(s) like TSStorageManager. + }); + return instance; +} + ++ (void)logEvent:(NSString *)description + severity:(OWSAnalyticsSeverity)severity + parameters:(NSDictionary *)parameters + location:(const char *)location +{ + + [[self sharedInstance] logEvent:description severity:severity parameters:parameters location:location]; +} + +- (void)logEvent:(NSString *)description + severity:(OWSAnalyticsSeverity)severity + parameters:(NSDictionary *)parameters + location:(const char *)location +{ + + DDLogFlag logFlag; + BOOL async = YES; + switch (severity) { + case OWSAnalyticsSeverityDebug: + logFlag = DDLogFlagDebug; + break; + case OWSAnalyticsSeverityInfo: + logFlag = DDLogFlagInfo; + break; + case OWSAnalyticsSeverityWarn: + logFlag = DDLogFlagWarning; + break; + case OWSAnalyticsSeverityError: + logFlag = DDLogFlagError; + async = NO; + break; + case OWSAnalyticsSeverityCritical: + logFlag = DDLogFlagError; + async = NO; + break; + default: + OWSAssert(0); + logFlag = DDLogFlagDebug; + break; + } + + // Log the event. + if (!parameters) { + LOG_MAYBE(async, LOG_LEVEL_DEF, logFlag, 0, nil, location, @"%@", description); + } else { + LOG_MAYBE(async, LOG_LEVEL_DEF, logFlag, 0, nil, location, @"%@ %@", description, parameters); + } + + // Do nothing. We don't yet serialize or transmit analytics events. +} + +@end