macOS: drop Mac service
Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
|
@ -14,8 +14,6 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Scalar\Scalar.csproj" />
|
||||
<ProjectReference Include="..\Scalar.Service\Scalar.Service.csproj" />
|
||||
<ProjectReference Include="..\Scalar.Notifications.Mac\Scalar.Notifications.Mac.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -38,8 +38,6 @@ fi
|
|||
|
||||
LOCALBIN_DIR="${OUT_DIR}/usr/local/bin"
|
||||
SCALAR_DESTINATION="${OUT_DIR}/usr/local/scalar"
|
||||
AGENTPLIST_DESTINATION="${OUT_DIR}/Library/LaunchAgents"
|
||||
LIBRARYAPPSUPPORT_DESTINATION="${OUT_DIR}/Library/Application Support/Scalar"
|
||||
|
||||
function CreateLayoutDirectories()
|
||||
{
|
||||
|
@ -47,7 +45,7 @@ function CreateLayoutDirectories()
|
|||
cleanLayout="rm -rf \"${OUT_DIR}\""
|
||||
eval $cleanLayout || exit 1
|
||||
|
||||
mkdirLayout="mkdir -p \"${LOCALBIN_DIR}\" \"${SCALAR_DESTINATION}\" \"${AGENTPLIST_DESTINATION}\" \"${LIBRARYAPPSUPPORT_DESTINATION}\""
|
||||
mkdirLayout="mkdir -p \"${LOCALBIN_DIR}\" \"${SCALAR_DESTINATION}\""
|
||||
eval $mkdirLayout || exit 1
|
||||
}
|
||||
|
||||
|
@ -83,48 +81,11 @@ function CopyUninstaller()
|
|||
eval $copyUninstaller || exit 1
|
||||
}
|
||||
|
||||
function CopyNativeApp()
|
||||
{
|
||||
SCALAR_NOTIFICATION_APP="${BIN_DIR}/Scalar.Notifications.Mac/bin/${CONFIGURATION}/native/${RUNTIMEIDENTIFIER}/Scalar.app"
|
||||
|
||||
if [ ! -d "$SCALAR_NOTIFICATION_APP" ] ; then
|
||||
echo "Error: Could not find native notification app at ${SCALAR_NOTIFICATION_APP}."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
copyNotificationApp="cp -Rf \"${SCALAR_NOTIFICATION_APP}\" \"${LIBRARYAPPSUPPORT_DESTINATION}\""
|
||||
eval $copyNotificationApp || exit 1
|
||||
}
|
||||
|
||||
function CopyAgentPlists()
|
||||
{
|
||||
NOTIFICATION_PLIST_PATH="${SRC_DIR}/Scalar.Notifications.Mac/org.scalar.usernotification.plist"
|
||||
SERVICE_PLIST_PATH="${SRC_DIR}/Scalar.Service/Mac/org.scalar.service.plist"
|
||||
|
||||
if [ ! -f "$NOTIFICATION_PLIST_PATH" ] ; then
|
||||
echo "Error: Could not find notification app agent plist at ${NOTIFICATION_PLIST_PATH}."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -f "$SERVICE_PLIST_PATH" ] ; then
|
||||
echo "Error: Could not find service agent plist at ${SERVICE_PLIST_PATH}."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
copyNotificationPlist="cp -Rf \"${NOTIFICATION_PLIST_PATH}\" \"${AGENTPLIST_DESTINATION}/.\""
|
||||
eval $copyNotificationPlist || exit 1
|
||||
|
||||
copyServicePlist="cp -Rf \"${SERVICE_PLIST_PATH}\" \"${AGENTPLIST_DESTINATION}/.\""
|
||||
eval $copyServicePlist || exit 1
|
||||
}
|
||||
|
||||
function Run()
|
||||
{
|
||||
CreateLayoutDirectories
|
||||
CopyScalar
|
||||
CopyUninstaller
|
||||
CopyNativeApp
|
||||
CopyAgentPlists
|
||||
}
|
||||
|
||||
Run
|
||||
|
|
|
@ -50,7 +50,7 @@ function CreateScalarInstaller()
|
|||
mkdirOutDir="mkdir -p \"${OUT_DIR}\" \"${COMPONENTS_DIR}\""
|
||||
eval $mkdirOutDir || exit 1
|
||||
|
||||
pkgBuildCommand="/usr/bin/pkgbuild --identifier $INSTALLER_PACKAGE_ID --component-plist \"${COMPONENTSPLIST_PATH}\" --scripts \"${SCRIPTS_DIR}\" --root \"${LAYOUT_DIR}\" \"${COMPONENTS_DIR}/${COMPONENT_PACKAGE_NAME}\""
|
||||
pkgBuildCommand="/usr/bin/pkgbuild --identifier $INSTALLER_PACKAGE_ID --scripts \"${SCRIPTS_DIR}\" --root \"${LAYOUT_DIR}\" \"${COMPONENTS_DIR}/${COMPONENT_PACKAGE_NAME}\""
|
||||
echo $pkgBuildCommand
|
||||
eval $pkgBuildCommand || exit 1
|
||||
}
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
<?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">
|
||||
<array>
|
||||
<dict>
|
||||
<key>BundleHasStrictIdentifier</key>
|
||||
<true/>
|
||||
<key>BundleIsRelocatable</key>
|
||||
<false/>
|
||||
<key>BundleIsVersionChecked</key>
|
||||
<true/>
|
||||
<key>BundleOverwriteAction</key>
|
||||
<string>upgrade</string>
|
||||
<key>RootRelativeBundlePath</key>
|
||||
<string>Library/Application Support/Scalar/Scalar.app</string>
|
||||
</dict>
|
||||
</array>
|
||||
</plist>
|
|
@ -1,37 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
function startOrRestartService()
|
||||
{
|
||||
domain=$1
|
||||
service=$2
|
||||
if [[ $domain == system* ]]; then
|
||||
plistPath="/Library/LaunchDaemons"
|
||||
elif [[ $domain == gui/* ]]; then
|
||||
plistPath="/Library/LaunchAgents"
|
||||
fi
|
||||
startCmd="/bin/launchctl bootstrap $domain $plistPath/$service.plist"
|
||||
restartCmd="/bin/launchctl kickstart -k $domain/$service"
|
||||
isLoaded=`/bin/launchctl print $domain/$service | wc -l`
|
||||
if [ $isLoaded -gt "0" ]; then
|
||||
echo "Restarting Service: '$restartCmd'"
|
||||
eval $restartCmd || exit 1
|
||||
else
|
||||
echo "Starting Service: '$startCmd'"
|
||||
eval $startCmd || exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Load Launch Agents in all active User sessions
|
||||
# There will be one loginwindow instance for each logged in user,
|
||||
# get its uid (this will correspond to the logged in user's id.)
|
||||
# Then use launchctl bootstrap gui/uid to auto load the Service
|
||||
# for each user.
|
||||
declare -a launchAgents=(
|
||||
"org.scalar.usernotification"
|
||||
"org.scalar.service"
|
||||
)
|
||||
for uid in $(ps -Ac -o uid,command | grep -iw "Finder" | awk '{print $1}'); do
|
||||
for nextLaunchAgent in "${launchAgents[@]}"; do
|
||||
startOrRestartService "gui/$uid" $nextLaunchAgent
|
||||
done
|
||||
done
|
|
@ -1,20 +0,0 @@
|
|||
<Project Sdk="Microsoft.Build.NoTargets">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<RuntimeIdentifier>osx-x64</RuntimeIdentifier>
|
||||
</PropertyGroup>
|
||||
|
||||
<Target Name="_Clean" AfterTargets="Clean">
|
||||
<RemoveDir Directories="$(ProjectOutPath)" />
|
||||
</Target>
|
||||
|
||||
<!--
|
||||
Only build and test the native bits when running on macOS.
|
||||
-->
|
||||
<Target Name="_XcodeBuild" AfterTargets="Publish" Condition="'$(OSPlatform)' == 'osx'">
|
||||
<Exec Command="$(MSBuildProjectDirectory)\build.sh $(Configuration) '$(ProjectOutPath)xcodebuild' '$(ProjectOutPath)bin\$(Configuration)\native\$(RuntimeIdentifier)' $(Version)" />
|
||||
<Exec Command="$(MSBuildProjectDirectory)\test.sh $(Configuration) '$(ProjectOutPath)xcodebuild.test'" />
|
||||
</Target>
|
||||
|
||||
</Project>
|
|
@ -1,22 +0,0 @@
|
|||
<?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>$(DEVELOPMENT_LANGUAGE)</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>BNDL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,8 +0,0 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import "ScalarAboutWindowController.h"
|
||||
|
||||
@interface ScalarMockAboutWindowController : ScalarAboutWindowController
|
||||
|
||||
@property (readonly) BOOL aboutBoxDisplayed;
|
||||
|
||||
@end
|
|
@ -1,23 +0,0 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import "ScalarMockAboutWindowController.h"
|
||||
|
||||
@interface ScalarMockAboutWindowController()
|
||||
|
||||
@property (readwrite) BOOL aboutBoxDisplayed;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ScalarMockAboutWindowController
|
||||
|
||||
- (instancetype) initWithProductInfo:(ScalarProductInfoFetcher *) productInfo
|
||||
{
|
||||
self = [super initWithProductInfoFetcher:productInfo];
|
||||
return self;
|
||||
}
|
||||
|
||||
- (IBAction)showWindow:(nullable id)sender
|
||||
{
|
||||
self.aboutBoxDisplayed = YES;
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,7 +0,0 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface ScalarMockNotificationCenter : NSUserNotificationCenter
|
||||
|
||||
@property (assign) BOOL notificationDelivered;
|
||||
|
||||
@end
|
|
@ -1,31 +0,0 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import "ScalarMockNotificationCenter.h"
|
||||
|
||||
@interface ScalarMockNotificationCenter ()
|
||||
|
||||
@property (strong) NSUserNotification *expectedNotification;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ScalarMockNotificationCenter
|
||||
|
||||
- (instancetype) initWithExpectedNotification:(NSUserNotification *) notification
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_expectedNotification = notification;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)deliverNotification:(NSUserNotification *) notification
|
||||
{
|
||||
if ([notification.title isEqualToString:self.expectedNotification.title] &&
|
||||
[notification.informativeText isEqualToString:self.expectedNotification.informativeText])
|
||||
{
|
||||
self.notificationDelivered = YES;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,13 +0,0 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import "ScalarProductInfoFetcher.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ScalarMockProductInfoFetcher : ScalarProductInfoFetcher
|
||||
|
||||
- (instancetype _Nullable) initWithGitVersion:(NSString *) gitVersion
|
||||
scalarVersion:(NSString *) scalarVersion;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,39 +0,0 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import "ScalarMockProductInfoFetcher.h"
|
||||
|
||||
@interface ScalarMockProductInfoFetcher()
|
||||
|
||||
@property (copy) NSString *gitVersion;
|
||||
@property (copy) NSString *scalarVersion;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ScalarMockProductInfoFetcher
|
||||
|
||||
- (instancetype) initWithGitVersion:(NSString *) gitVersion
|
||||
scalarVersion:(NSString *) scalarVersion
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_gitVersion = [gitVersion copy];
|
||||
_scalarVersion = [scalarVersion copy];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL) tryGetScalarVersion:(NSString *__autoreleasing *) version
|
||||
error:(NSError *__autoreleasing *) error
|
||||
{
|
||||
*version = self.scalarVersion;
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL) tryGetGitVersion:(NSString *__autoreleasing *) version
|
||||
error:(NSError *__autoreleasing *) error
|
||||
{
|
||||
*version = self.gitVersion;
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,47 +0,0 @@
|
|||
#import <XCTest/XCTest.h>
|
||||
#import "ScalarMockAboutWindowController.h"
|
||||
#import "ScalarMockProductInfoFetcher.h"
|
||||
|
||||
NSString * const ExpectedGitVersionString = @"2.20.1.vfs.1.1.104.g2ab7360";
|
||||
NSString * const ExpectedScalarVersionString = @"1.0.19116.1";
|
||||
|
||||
@interface ScalarAboutWindowControllerTests : XCTestCase
|
||||
|
||||
@property (strong) ScalarAboutWindowController *windowController;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ScalarAboutWindowControllerTests
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
[super setUp];
|
||||
|
||||
ScalarMockProductInfoFetcher *mockProductInfoFetcher =
|
||||
[[ScalarMockProductInfoFetcher alloc] initWithGitVersion:(NSString *) ExpectedGitVersionString
|
||||
scalarVersion:(NSString *) ExpectedScalarVersionString];
|
||||
|
||||
self.windowController = [[ScalarAboutWindowController alloc]
|
||||
initWithProductInfoFetcher:mockProductInfoFetcher];
|
||||
}
|
||||
|
||||
- (void)tearDown
|
||||
{
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testAboutWindowContainsScalarVersion
|
||||
{
|
||||
XCTAssertEqual(self.windowController.scalarVersion,
|
||||
ExpectedScalarVersionString,
|
||||
@"Incorrect Scalar version displayed in About box");
|
||||
}
|
||||
|
||||
- (void)testAboutWindowContainsGitVersion
|
||||
{
|
||||
XCTAssertEqual(self.windowController.gitVersion,
|
||||
ExpectedGitVersionString,
|
||||
@"Incorrect Git version displayed in About box");
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,115 +0,0 @@
|
|||
#import <XCTest/XCTest.h>
|
||||
#import "ScalarMessageParser.h"
|
||||
|
||||
@interface MessageParserTests : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation MessageParserTests
|
||||
|
||||
- (void)testParsingValidMessageSucceeds
|
||||
{
|
||||
NSDictionary *expectedDict = [self validMessage];
|
||||
NSData *messageData =
|
||||
[NSJSONSerialization dataWithJSONObject:expectedDict
|
||||
options:NSJSONWritingPrettyPrinted
|
||||
error:nil];
|
||||
|
||||
NSError *error;
|
||||
NSDictionary *parsedMessage;
|
||||
XCTAssertTrue([ScalarMessageParser tryParseData:messageData
|
||||
message:&parsedMessage
|
||||
error:&error]);
|
||||
XCTAssertNil(error);
|
||||
|
||||
[self validateParsedMessage:parsedMessage expectedMessage:expectedDict];
|
||||
}
|
||||
|
||||
- (void)testParsingMessageWithTrailingCtrlCharsSucceed
|
||||
{
|
||||
NSDictionary *expectedDict = [self validMessage];
|
||||
NSData *messageData =
|
||||
[NSJSONSerialization dataWithJSONObject:expectedDict
|
||||
options:NSJSONWritingPrettyPrinted
|
||||
error:nil];
|
||||
NSMutableData *dataWithCtrlChars = [NSMutableData dataWithData:messageData];
|
||||
NSString *stringWithCtrlChars = [NSString stringWithFormat:@"%c%c%c%c%c%c%c",
|
||||
0x07,
|
||||
0x08,
|
||||
0x1B,
|
||||
0x0C,
|
||||
0x0A,
|
||||
0x0D,
|
||||
0x09];
|
||||
[dataWithCtrlChars appendData:[stringWithCtrlChars
|
||||
dataUsingEncoding:NSUTF8StringEncoding]];
|
||||
|
||||
NSError *error;
|
||||
NSDictionary *parsedMessage;
|
||||
XCTAssertTrue([ScalarMessageParser tryParseData:dataWithCtrlChars
|
||||
message:&parsedMessage
|
||||
error:&error]);
|
||||
XCTAssertNil(error);
|
||||
|
||||
[self validateParsedMessage:parsedMessage expectedMessage:expectedDict];
|
||||
}
|
||||
|
||||
- (void)testParsingMalformedMessageFails
|
||||
{
|
||||
NSString *message = @"{ \"Id\", \"Message\", \"Foobar\"}";
|
||||
NSError *error;
|
||||
NSDictionary *parsedMessage;
|
||||
XCTAssertFalse([ScalarMessageParser tryParseData:[message dataUsingEncoding:NSUTF8StringEncoding]
|
||||
message:&parsedMessage
|
||||
error:&error]);
|
||||
XCTAssertNil(parsedMessage);
|
||||
XCTAssertNotNil(error);
|
||||
}
|
||||
|
||||
- (void)testParsingEmptyMessageFails
|
||||
{
|
||||
NSString *message = @"";
|
||||
NSError *error;
|
||||
NSDictionary *parsedMessage;
|
||||
|
||||
XCTAssertFalse([ScalarMessageParser tryParseData:[message dataUsingEncoding:NSUTF8StringEncoding]
|
||||
message:&parsedMessage
|
||||
error:&error]);
|
||||
XCTAssertNil(parsedMessage);
|
||||
XCTAssertNotNil(error);
|
||||
}
|
||||
|
||||
#pragma mark Utility Methods
|
||||
|
||||
- (NSDictionary *)validMessage
|
||||
{
|
||||
NSInteger messageId = 1;
|
||||
NSString *title = @"Scalar Mount";
|
||||
NSString *message = @"Successfully mount repo";
|
||||
NSString *enlistment = @"/Users/foo/bar";
|
||||
NSInteger enlistmentCount = 0;
|
||||
NSDictionary *validDict = @{
|
||||
@"Id" : [NSNumber numberWithLong:messageId],
|
||||
@"Title" : title,
|
||||
@"Message" : message,
|
||||
@"Enlistment" : enlistment,
|
||||
@"EnlistmentCount" : [NSNumber numberWithLong:enlistmentCount]
|
||||
};
|
||||
return validDict;
|
||||
}
|
||||
|
||||
- (BOOL)validateParsedMessage:(NSDictionary *)messageDict
|
||||
expectedMessage:(NSDictionary *)expectedDict
|
||||
{
|
||||
XCTAssertNotNil(messageDict, @"Parse error: failure parsing message");
|
||||
|
||||
[messageDict enumerateKeysAndObjectsUsingBlock:^(id _Nonnull key,
|
||||
id _Nonnull obj,
|
||||
BOOL * _Nonnull stop)
|
||||
{
|
||||
XCTAssertEqualObjects(obj,
|
||||
expectedDict[key],
|
||||
@"Parse error: mismatch in values of %@",
|
||||
key);
|
||||
}];
|
||||
}
|
||||
@end
|
|
@ -1,93 +0,0 @@
|
|||
#import <XCTest/XCTest.h>
|
||||
#import "ScalarNotification.h"
|
||||
|
||||
@interface ScalarNotificationTests : XCTestCase
|
||||
@end
|
||||
|
||||
@implementation ScalarNotificationTests
|
||||
|
||||
- (void)testCreateNotificationWithMissingIdFails
|
||||
{
|
||||
NSDictionary *message = @{
|
||||
@"Title" : @"foo",
|
||||
@"Message" : @"bar",
|
||||
@"Enlistment" : @"/foo/bar",
|
||||
@"EnlistmentCount" : [NSNumber numberWithLong:0]
|
||||
};
|
||||
|
||||
NSError *error;
|
||||
ScalarNotification *notification;
|
||||
|
||||
XCTAssertFalse([ScalarNotification tryValidateMessage:message
|
||||
buildNotification:¬ification
|
||||
error:&error]);
|
||||
XCTAssertNotNil(error);
|
||||
}
|
||||
|
||||
- (void)testCreateNotificationWithInvalidIdFails
|
||||
{
|
||||
NSDictionary *message = @{
|
||||
@"Id" : [NSNumber numberWithLong:32],
|
||||
@"Title" : @"foo",
|
||||
@"Message" : @"bar",
|
||||
@"EnlistmentCount" : [NSNumber numberWithLong:0]
|
||||
};
|
||||
|
||||
NSError *error;
|
||||
ScalarNotification *notification;
|
||||
XCTAssertFalse([ScalarNotification tryValidateMessage:message
|
||||
buildNotification:¬ification
|
||||
error:&error]);
|
||||
XCTAssertNotNil(error);
|
||||
}
|
||||
|
||||
- (void)testCreateAutomountNotificationWithValidMessageSucceeds
|
||||
{
|
||||
NSDictionary *message = @{
|
||||
@"Id" : [NSNumber numberWithLong:0],
|
||||
@"EnlistmentCount" : [NSNumber numberWithLong:5]
|
||||
};
|
||||
|
||||
NSError *error;
|
||||
ScalarNotification *notification;
|
||||
XCTAssertTrue([ScalarNotification tryValidateMessage:message
|
||||
buildNotification:¬ification
|
||||
error:&error]);
|
||||
XCTAssertTrue([notification.title isEqualToString:@"Scalar AutoMount"]);
|
||||
XCTAssertTrue([notification.message isEqualToString:@"Attempting to mount 5 Scalar repos(s)"]);
|
||||
XCTAssertNil(error);
|
||||
}
|
||||
|
||||
- (void)testCreateMountNotificationWithValidMessageSucceeds
|
||||
{
|
||||
NSString *enlistment = @"/Users/foo/bar/foo.bar";
|
||||
NSDictionary *message = @{
|
||||
@"Id" : [NSNumber numberWithLong:1],
|
||||
@"Enlistment" : enlistment
|
||||
};
|
||||
|
||||
NSError *error;
|
||||
ScalarNotification *notification;
|
||||
XCTAssertTrue([ScalarNotification tryValidateMessage:message
|
||||
buildNotification:¬ification
|
||||
error:&error]);
|
||||
XCTAssertTrue([notification.title isEqualToString:@"Scalar AutoMount"]);
|
||||
XCTAssertTrue([notification.message containsString:enlistment]);
|
||||
XCTAssertNil(error);
|
||||
}
|
||||
|
||||
- (void)testCreateMountNotificationWithMissingEnlistmentFails
|
||||
{
|
||||
NSDictionary *message = @{
|
||||
@"Id" : [NSNumber numberWithLong:1],
|
||||
};
|
||||
|
||||
NSError *error;
|
||||
ScalarNotification *notification;
|
||||
XCTAssertFalse([ScalarNotification tryValidateMessage:message
|
||||
buildNotification:¬ification
|
||||
error:&error]);
|
||||
XCTAssertNotNil(error);
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,56 +0,0 @@
|
|||
#import <XCTest/XCTest.h>
|
||||
#import "ScalarMockAboutWindowController.h"
|
||||
#import "ScalarMockProductInfoFetcher.h"
|
||||
#import "ScalarStatusBarItem.h"
|
||||
|
||||
NSString * const ExpectedAboutMenuTitle = @"About Scalar";
|
||||
|
||||
@interface ScalarStatusBarItemTests : XCTestCase
|
||||
|
||||
@property (strong) ScalarStatusBarItem *statusbarItem;
|
||||
@property (strong) ScalarMockAboutWindowController *aboutWindowController;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ScalarStatusBarItemTests
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
[super setUp];
|
||||
|
||||
ScalarMockProductInfoFetcher *mockProductInfoFetcher = [[ScalarMockProductInfoFetcher alloc]
|
||||
initWithGitVersion:@""
|
||||
scalarVersion:@""];
|
||||
|
||||
self.aboutWindowController = [[ScalarMockAboutWindowController alloc]
|
||||
initWithProductInfoFetcher:mockProductInfoFetcher];
|
||||
self.statusbarItem = [[ScalarStatusBarItem alloc]
|
||||
initWithAboutWindowController:self.aboutWindowController];
|
||||
|
||||
[self.statusbarItem load];
|
||||
}
|
||||
|
||||
- (void)tearDown
|
||||
{
|
||||
[super tearDown];
|
||||
}
|
||||
|
||||
- (void)testStatusItemContainsAboutMenu
|
||||
{
|
||||
NSMenu *statusMenu = [self.statusbarItem getStatusMenu];
|
||||
XCTAssertNotNil(statusMenu, @"Status bar does not contain Scalar menu");
|
||||
|
||||
NSMenuItem *menuItem = [statusMenu itemWithTitle:ExpectedAboutMenuTitle];
|
||||
XCTAssertNotNil(menuItem, @"Missing \"%@\" item in Scalar menu", ExpectedAboutMenuTitle);
|
||||
}
|
||||
|
||||
- (void)testAboutMenuClickDisplaysAboutBox
|
||||
{
|
||||
[self.statusbarItem handleMenuClick:nil];
|
||||
|
||||
XCTAssertTrue(self.aboutWindowController.aboutBoxDisplayed,
|
||||
@"Clicking on \"%@\" menu does not show About box",
|
||||
ExpectedAboutMenuTitle);
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,593 +0,0 @@
|
|||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 50;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
433FE819227B593500488730 /* ScalarAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 433FE818227B593500488730 /* ScalarAppDelegate.m */; };
|
||||
433FE81B227B594300488730 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 433FE81A227B594300488730 /* Assets.xcassets */; };
|
||||
433FE81E227B594300488730 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 433FE81C227B594300488730 /* MainMenu.xib */; };
|
||||
433FE821227B594300488730 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 433FE820227B594300488730 /* main.m */; };
|
||||
433FE8562280C21000488730 /* ScalarStatusBarItem.m in Sources */ = {isa = PBXBuildFile; fileRef = 433FE8542280C21000488730 /* ScalarStatusBarItem.m */; };
|
||||
433FE85A2280E10F00488730 /* ScalarProductInfoFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 433FE8592280E10F00488730 /* ScalarProductInfoFetcher.m */; };
|
||||
433FE8632281D06100488730 /* ScalarAboutWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 433FE8602281D06000488730 /* ScalarAboutWindowController.m */; };
|
||||
433FE8642281D06100488730 /* ScalarAboutWindowController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 433FE8612281D06100488730 /* ScalarAboutWindowController.xib */; };
|
||||
43583C362289BDAA003357D6 /* ScalarStatusBarItemTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 43583C352289BDAA003357D6 /* ScalarStatusBarItemTests.m */; };
|
||||
43583C382289BE94003357D6 /* ScalarAboutWindowControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 43583C372289BE94003357D6 /* ScalarAboutWindowControllerTests.m */; };
|
||||
435B1E0F228CC1E000E853F3 /* ScalarProcessRunner.m in Sources */ = {isa = PBXBuildFile; fileRef = 435B1E0E228CC1E000E853F3 /* ScalarProcessRunner.m */; };
|
||||
43AF0DF822B01C9800E54D48 /* ScalarNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = 43AF0DF422B01C9700E54D48 /* ScalarNotification.m */; };
|
||||
43AF0DFA22B01C9800E54D48 /* ScalarMessageParser.m in Sources */ = {isa = PBXBuildFile; fileRef = 43AF0DF522B01C9700E54D48 /* ScalarMessageParser.m */; };
|
||||
43AF0DFC22B01C9800E54D48 /* ScalarMessageListener.m in Sources */ = {isa = PBXBuildFile; fileRef = 43AF0DF622B01C9800E54D48 /* ScalarMessageListener.m */; };
|
||||
43AF0E0222B01E3200E54D48 /* ScalarNotificationDisplay.m in Sources */ = {isa = PBXBuildFile; fileRef = 43AF0E0122B01E3200E54D48 /* ScalarNotificationDisplay.m */; };
|
||||
43AF0E0322B023BE00E54D48 /* ScalarMockAboutWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = 435B1DFA228B4C8400E853F3 /* ScalarMockAboutWindowController.m */; };
|
||||
43AF0E0422B023BE00E54D48 /* ScalarMockProductInfoFetcher.m in Sources */ = {isa = PBXBuildFile; fileRef = 435B1DFE228B590100E853F3 /* ScalarMockProductInfoFetcher.m */; };
|
||||
43AF0E0522B023BE00E54D48 /* ScalarMockNotificationCenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 435B1E01228B6C3900E853F3 /* ScalarMockNotificationCenter.m */; };
|
||||
43AF0E0822B023F200E54D48 /* ScalarMessageParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 43AF0E0622B023F200E54D48 /* ScalarMessageParserTests.m */; };
|
||||
43AF0E0922B023F200E54D48 /* ScalarNotificationTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 43AF0E0722B023F200E54D48 /* ScalarNotificationTests.m */; };
|
||||
43C021F022B18BDD00F868F2 /* ScalarNotificationErrors.m in Sources */ = {isa = PBXBuildFile; fileRef = 43C021EF22B18BDD00F868F2 /* ScalarNotificationErrors.m */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
43583C302285E3E1003357D6 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 433FE80C227B593500488730 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 433FE813227B593500488730;
|
||||
remoteInfo = Scalar;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
433FE814227B593500488730 /* Scalar.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Scalar.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
433FE817227B593500488730 /* ScalarAppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScalarAppDelegate.h; sourceTree = "<group>"; };
|
||||
433FE818227B593500488730 /* ScalarAppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScalarAppDelegate.m; sourceTree = "<group>"; };
|
||||
433FE81A227B594300488730 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
433FE81D227B594300488730 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = "<group>"; };
|
||||
433FE81F227B594300488730 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
433FE820227B594300488730 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
433FE822227B594300488730 /* Scalar.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Scalar.entitlements; sourceTree = "<group>"; };
|
||||
433FE8532280C21000488730 /* ScalarStatusBarItem.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScalarStatusBarItem.h; sourceTree = "<group>"; };
|
||||
433FE8542280C21000488730 /* ScalarStatusBarItem.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScalarStatusBarItem.m; sourceTree = "<group>"; };
|
||||
433FE8582280E10F00488730 /* ScalarProductInfoFetcher.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScalarProductInfoFetcher.h; sourceTree = "<group>"; };
|
||||
433FE8592280E10F00488730 /* ScalarProductInfoFetcher.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScalarProductInfoFetcher.m; sourceTree = "<group>"; };
|
||||
433FE8602281D06000488730 /* ScalarAboutWindowController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScalarAboutWindowController.m; sourceTree = "<group>"; };
|
||||
433FE8612281D06100488730 /* ScalarAboutWindowController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ScalarAboutWindowController.xib; sourceTree = "<group>"; };
|
||||
433FE8622281D06100488730 /* ScalarAboutWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScalarAboutWindowController.h; sourceTree = "<group>"; };
|
||||
43583C2B2285E3E1003357D6 /* Scalar.UnitTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Scalar.UnitTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
43583C2F2285E3E1003357D6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
43583C352289BDAA003357D6 /* ScalarStatusBarItemTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScalarStatusBarItemTests.m; sourceTree = "<group>"; };
|
||||
43583C372289BE94003357D6 /* ScalarAboutWindowControllerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScalarAboutWindowControllerTests.m; sourceTree = "<group>"; };
|
||||
435B1DFA228B4C8400E853F3 /* ScalarMockAboutWindowController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScalarMockAboutWindowController.m; sourceTree = "<group>"; };
|
||||
435B1DFC228B4CB500E853F3 /* ScalarMockAboutWindowController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScalarMockAboutWindowController.h; sourceTree = "<group>"; };
|
||||
435B1DFD228B590100E853F3 /* ScalarMockProductInfoFetcher.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScalarMockProductInfoFetcher.h; sourceTree = "<group>"; };
|
||||
435B1DFE228B590100E853F3 /* ScalarMockProductInfoFetcher.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScalarMockProductInfoFetcher.m; sourceTree = "<group>"; };
|
||||
435B1E00228B6C3900E853F3 /* ScalarMockNotificationCenter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScalarMockNotificationCenter.h; sourceTree = "<group>"; };
|
||||
435B1E01228B6C3900E853F3 /* ScalarMockNotificationCenter.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScalarMockNotificationCenter.m; sourceTree = "<group>"; };
|
||||
435B1E0D228CC1E000E853F3 /* ScalarProcessRunner.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScalarProcessRunner.h; sourceTree = "<group>"; };
|
||||
435B1E0E228CC1E000E853F3 /* ScalarProcessRunner.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScalarProcessRunner.m; sourceTree = "<group>"; };
|
||||
43AF0DF222B01C9700E54D48 /* ScalarNotification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScalarNotification.h; sourceTree = "<group>"; };
|
||||
43AF0DF322B01C9700E54D48 /* ScalarMessageListener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScalarMessageListener.h; sourceTree = "<group>"; };
|
||||
43AF0DF422B01C9700E54D48 /* ScalarNotification.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScalarNotification.m; sourceTree = "<group>"; };
|
||||
43AF0DF522B01C9700E54D48 /* ScalarMessageParser.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScalarMessageParser.m; sourceTree = "<group>"; };
|
||||
43AF0DF622B01C9800E54D48 /* ScalarMessageListener.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScalarMessageListener.m; sourceTree = "<group>"; };
|
||||
43AF0DF722B01C9800E54D48 /* ScalarMessageParser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScalarMessageParser.h; sourceTree = "<group>"; };
|
||||
43AF0E0022B01E3200E54D48 /* ScalarNotificationDisplay.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScalarNotificationDisplay.h; sourceTree = "<group>"; };
|
||||
43AF0E0122B01E3200E54D48 /* ScalarNotificationDisplay.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScalarNotificationDisplay.m; sourceTree = "<group>"; };
|
||||
43AF0E0622B023F200E54D48 /* ScalarMessageParserTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScalarMessageParserTests.m; sourceTree = "<group>"; };
|
||||
43AF0E0722B023F200E54D48 /* ScalarNotificationTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ScalarNotificationTests.m; sourceTree = "<group>"; };
|
||||
43C021EE22B18BDD00F868F2 /* ScalarNotificationErrors.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScalarNotificationErrors.h; sourceTree = "<group>"; };
|
||||
43C021EF22B18BDD00F868F2 /* ScalarNotificationErrors.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScalarNotificationErrors.m; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
433FE811227B593500488730 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
43583C282285E3E1003357D6 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
430ED36D2281E6B200319408 /* UI */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
43AF0E0022B01E3200E54D48 /* ScalarNotificationDisplay.h */,
|
||||
43AF0E0122B01E3200E54D48 /* ScalarNotificationDisplay.m */,
|
||||
433FE8532280C21000488730 /* ScalarStatusBarItem.h */,
|
||||
433FE8542280C21000488730 /* ScalarStatusBarItem.m */,
|
||||
433FE8622281D06100488730 /* ScalarAboutWindowController.h */,
|
||||
433FE8602281D06000488730 /* ScalarAboutWindowController.m */,
|
||||
433FE8612281D06100488730 /* ScalarAboutWindowController.xib */,
|
||||
433FE817227B593500488730 /* ScalarAppDelegate.h */,
|
||||
433FE818227B593500488730 /* ScalarAppDelegate.m */,
|
||||
433FE81A227B594300488730 /* Assets.xcassets */,
|
||||
433FE81C227B594300488730 /* MainMenu.xib */,
|
||||
);
|
||||
path = UI;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
433FE80B227B593500488730 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
433FE816227B593500488730 /* StatusMenuItem */,
|
||||
43583C2C2285E3E1003357D6 /* Scalar.UnitTests */,
|
||||
433FE815227B593500488730 /* Products */,
|
||||
);
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
433FE815227B593500488730 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
433FE814227B593500488730 /* Scalar.app */,
|
||||
43583C2B2285E3E1003357D6 /* Scalar.UnitTests.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
433FE816227B593500488730 /* StatusMenuItem */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
43AF0DFF22B01CD400E54D48 /* IPC */,
|
||||
435B1E04228C88E300E853F3 /* Utilities */,
|
||||
430ED36D2281E6B200319408 /* UI */,
|
||||
433FE81F227B594300488730 /* Info.plist */,
|
||||
433FE820227B594300488730 /* main.m */,
|
||||
433FE822227B594300488730 /* Scalar.entitlements */,
|
||||
);
|
||||
path = StatusMenuItem;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
43583C2C2285E3E1003357D6 /* Scalar.UnitTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
43583C3B228A17D4003357D6 /* Mocks */,
|
||||
43583C2F2285E3E1003357D6 /* Info.plist */,
|
||||
43583C372289BE94003357D6 /* ScalarAboutWindowControllerTests.m */,
|
||||
43AF0E0722B023F200E54D48 /* ScalarNotificationTests.m */,
|
||||
43AF0E0622B023F200E54D48 /* ScalarMessageParserTests.m */,
|
||||
43583C352289BDAA003357D6 /* ScalarStatusBarItemTests.m */,
|
||||
);
|
||||
path = Scalar.UnitTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
43583C3B228A17D4003357D6 /* Mocks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
435B1DFC228B4CB500E853F3 /* ScalarMockAboutWindowController.h */,
|
||||
435B1DFA228B4C8400E853F3 /* ScalarMockAboutWindowController.m */,
|
||||
435B1DFD228B590100E853F3 /* ScalarMockProductInfoFetcher.h */,
|
||||
435B1DFE228B590100E853F3 /* ScalarMockProductInfoFetcher.m */,
|
||||
435B1E00228B6C3900E853F3 /* ScalarMockNotificationCenter.h */,
|
||||
435B1E01228B6C3900E853F3 /* ScalarMockNotificationCenter.m */,
|
||||
);
|
||||
path = Mocks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
435B1E04228C88E300E853F3 /* Utilities */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
433FE8582280E10F00488730 /* ScalarProductInfoFetcher.h */,
|
||||
433FE8592280E10F00488730 /* ScalarProductInfoFetcher.m */,
|
||||
435B1E0D228CC1E000E853F3 /* ScalarProcessRunner.h */,
|
||||
435B1E0E228CC1E000E853F3 /* ScalarProcessRunner.m */,
|
||||
43C021EE22B18BDD00F868F2 /* ScalarNotificationErrors.h */,
|
||||
43C021EF22B18BDD00F868F2 /* ScalarNotificationErrors.m */,
|
||||
);
|
||||
path = Utilities;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
43AF0DFF22B01CD400E54D48 /* IPC */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
43AF0DF222B01C9700E54D48 /* ScalarNotification.h */,
|
||||
43AF0DF422B01C9700E54D48 /* ScalarNotification.m */,
|
||||
43AF0DF322B01C9700E54D48 /* ScalarMessageListener.h */,
|
||||
43AF0DF622B01C9800E54D48 /* ScalarMessageListener.m */,
|
||||
43AF0DF722B01C9800E54D48 /* ScalarMessageParser.h */,
|
||||
43AF0DF522B01C9700E54D48 /* ScalarMessageParser.m */,
|
||||
);
|
||||
path = IPC;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
433FE813227B593500488730 /* Scalar */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 433FE830227B594300488730 /* Build configuration list for PBXNativeTarget "Scalar" */;
|
||||
buildPhases = (
|
||||
433FE810227B593500488730 /* Sources */,
|
||||
433FE811227B593500488730 /* Frameworks */,
|
||||
433FE812227B593500488730 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = Scalar;
|
||||
productName = Scalar;
|
||||
productReference = 433FE814227B593500488730 /* Scalar.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
43583C2A2285E3E1003357D6 /* Scalar.UnitTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 43583C322285E3E1003357D6 /* Build configuration list for PBXNativeTarget "Scalar.UnitTests" */;
|
||||
buildPhases = (
|
||||
43583C272285E3E1003357D6 /* Sources */,
|
||||
43583C282285E3E1003357D6 /* Frameworks */,
|
||||
43583C292285E3E1003357D6 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
43583C312285E3E1003357D6 /* PBXTargetDependency */,
|
||||
);
|
||||
name = Scalar.UnitTests;
|
||||
productName = Scalar.UnitTests;
|
||||
productReference = 43583C2B2285E3E1003357D6 /* Scalar.UnitTests.xctest */;
|
||||
productType = "com.apple.product-type.bundle.unit-test";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
433FE80C227B593500488730 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1100;
|
||||
ORGANIZATIONNAME = Microsoft;
|
||||
TargetAttributes = {
|
||||
433FE813227B593500488730 = {
|
||||
CreatedOnToolsVersion = 9.3;
|
||||
};
|
||||
43583C2A2285E3E1003357D6 = {
|
||||
CreatedOnToolsVersion = 9.3;
|
||||
TestTargetID = 433FE813227B593500488730;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 433FE80F227B593500488730 /* Build configuration list for PBXProject "Scalar" */;
|
||||
compatibilityVersion = "Xcode 9.3";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 433FE80B227B593500488730;
|
||||
productRefGroup = 433FE815227B593500488730 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
433FE813227B593500488730 /* Scalar */,
|
||||
43583C2A2285E3E1003357D6 /* Scalar.UnitTests */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
433FE812227B593500488730 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
433FE8642281D06100488730 /* ScalarAboutWindowController.xib in Resources */,
|
||||
433FE81B227B594300488730 /* Assets.xcassets in Resources */,
|
||||
433FE81E227B594300488730 /* MainMenu.xib in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
43583C292285E3E1003357D6 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
433FE810227B593500488730 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
433FE85A2280E10F00488730 /* ScalarProductInfoFetcher.m in Sources */,
|
||||
435B1E0F228CC1E000E853F3 /* ScalarProcessRunner.m in Sources */,
|
||||
433FE8632281D06100488730 /* ScalarAboutWindowController.m in Sources */,
|
||||
43AF0DF822B01C9800E54D48 /* ScalarNotification.m in Sources */,
|
||||
433FE821227B594300488730 /* main.m in Sources */,
|
||||
43C021F022B18BDD00F868F2 /* ScalarNotificationErrors.m in Sources */,
|
||||
43AF0DFA22B01C9800E54D48 /* ScalarMessageParser.m in Sources */,
|
||||
433FE819227B593500488730 /* ScalarAppDelegate.m in Sources */,
|
||||
43AF0E0222B01E3200E54D48 /* ScalarNotificationDisplay.m in Sources */,
|
||||
433FE8562280C21000488730 /* ScalarStatusBarItem.m in Sources */,
|
||||
43AF0DFC22B01C9800E54D48 /* ScalarMessageListener.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
43583C272285E3E1003357D6 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
43AF0E0322B023BE00E54D48 /* ScalarMockAboutWindowController.m in Sources */,
|
||||
43AF0E0422B023BE00E54D48 /* ScalarMockProductInfoFetcher.m in Sources */,
|
||||
43AF0E0822B023F200E54D48 /* ScalarMessageParserTests.m in Sources */,
|
||||
43AF0E0922B023F200E54D48 /* ScalarNotificationTests.m in Sources */,
|
||||
43AF0E0522B023BE00E54D48 /* ScalarMockNotificationCenter.m in Sources */,
|
||||
43583C362289BDAA003357D6 /* ScalarStatusBarItemTests.m in Sources */,
|
||||
43583C382289BE94003357D6 /* ScalarAboutWindowControllerTests.m in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
43583C312285E3E1003357D6 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 433FE813227B593500488730 /* Scalar */;
|
||||
targetProxy = 43583C302285E3E1003357D6 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
433FE81C227B594300488730 /* MainMenu.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
433FE81D227B594300488730 /* Base */,
|
||||
);
|
||||
name = MainMenu.xib;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
433FE82E227B594300488730 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = Scalar;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
433FE82F227B594300488730 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = Scalar;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = macosx;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
433FE831227B594300488730 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = Scalar;
|
||||
CODE_SIGN_ENTITLEMENTS = "";
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = UBF8T346G9;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
INFOPLIST_FILE = StatusMenuItem/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.microsoft.Scalar;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
433FE832227B594300488730 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = Scalar;
|
||||
CODE_SIGN_ENTITLEMENTS = "";
|
||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = UBF8T346G9;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
INFOPLIST_FILE = StatusMenuItem/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.13;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.microsoft.Scalar;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
43583C332285E3E1003357D6 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = UBF8T346G9;
|
||||
HEADER_SEARCH_PATHS = "$(SRCROOT)/Scalar.UnitTests/Mocks";
|
||||
INFOPLIST_FILE = Scalar.UnitTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
"@loader_path/../Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.microsoft.Scalar-UnitTests";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Scalar.app/Contents/MacOS/Scalar";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
43583C342285E3E1003357D6 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = UBF8T346G9;
|
||||
HEADER_SEARCH_PATHS = "$(SRCROOT)/Scalar.UnitTests/Mocks";
|
||||
INFOPLIST_FILE = Scalar.UnitTests/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
"@loader_path/../Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.microsoft.Scalar-UnitTests";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Scalar.app/Contents/MacOS/Scalar";
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
433FE80F227B593500488730 /* Build configuration list for PBXProject "Scalar" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
433FE82E227B594300488730 /* Debug */,
|
||||
433FE82F227B594300488730 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
433FE830227B594300488730 /* Build configuration list for PBXNativeTarget "Scalar" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
433FE831227B594300488730 /* Debug */,
|
||||
433FE832227B594300488730 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
43583C322285E3E1003357D6 /* Build configuration list for PBXNativeTarget "Scalar.UnitTests" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
43583C332285E3E1003357D6 /* Debug */,
|
||||
43583C342285E3E1003357D6 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 433FE80C227B593500488730 /* Project object */;
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:Scalar.xcodeproj">
|
||||
</FileRef>
|
||||
</Workspace>
|
|
@ -1,8 +0,0 @@
|
|||
<?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>IDEDidComputeMac32BitWarning</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,19 +0,0 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
typedef void (^NewMessageCallback) (NSDictionary *_Nonnull messageInfo);
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ScalarMessageListener : NSObject
|
||||
|
||||
@property (copy) NSString *socketPath;
|
||||
@property (copy) NewMessageCallback messageCallback;
|
||||
|
||||
- (instancetype _Nullable)initWithSocket:(NSString *)socketPath
|
||||
callback:(NewMessageCallback)callback;
|
||||
- (BOOL)startListening;
|
||||
- (void)stopListening;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,163 +0,0 @@
|
|||
#import <netinet/in.h>
|
||||
#import <sys/socket.h>
|
||||
#import <sys/un.h>
|
||||
#import "ScalarMessageListener.h"
|
||||
#import "ScalarMessageParser.h"
|
||||
|
||||
NSString * const NotificationServerPipeName = @"scalar.notification";
|
||||
|
||||
@interface ScalarMessageListener ()
|
||||
|
||||
@property (strong) NSFileHandle *connectionHandle;
|
||||
@property CFSocketRef socketRef;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ScalarMessageListener
|
||||
|
||||
- (instancetype)initWithSocket:(NSString *)socketPath
|
||||
callback:(nonnull NewMessageCallback)callback
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_socketPath =
|
||||
[[socketPath stringByAppendingPathComponent:NotificationServerPipeName]
|
||||
copy];
|
||||
_messageCallback = [callback copy];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
if (_socketRef != NULL)
|
||||
{
|
||||
CFSocketInvalidate(_socketRef);
|
||||
CFRelease(_socketRef);
|
||||
}
|
||||
}
|
||||
|
||||
- (BOOL)startListening
|
||||
{
|
||||
CFSocketRef cfSocket;
|
||||
if ((cfSocket = CFSocketCreate(kCFAllocatorDefault,
|
||||
PF_LOCAL,
|
||||
SOCK_STREAM,
|
||||
0,
|
||||
kCFSocketNoCallBack,
|
||||
NULL,
|
||||
NULL)) == NULL)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
CFAutorelease(cfSocket);
|
||||
|
||||
int reuse = TRUE;
|
||||
int socketDescriptor = CFSocketGetNative(cfSocket);
|
||||
if (setsockopt(socketDescriptor,
|
||||
SOL_SOCKET,
|
||||
SO_REUSEADDR,
|
||||
(void *) &reuse,
|
||||
sizeof(reuse)))
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
if ([[NSFileManager defaultManager] fileExistsAtPath:self.socketPath] &&
|
||||
![[NSFileManager defaultManager] removeItemAtPath:self.socketPath error:nil])
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
struct sockaddr_un sockAddress = {};
|
||||
memset(&sockAddress, 0, sizeof(sockAddress));
|
||||
sockAddress.sun_family = AF_UNIX;
|
||||
sockAddress.sun_len = sizeof(sockAddress);
|
||||
[self.socketPath getCString:sockAddress.sun_path
|
||||
maxLength:sizeof(sockAddress.sun_path)
|
||||
encoding:NSUTF8StringEncoding];
|
||||
|
||||
CFDataRef addressData = CFDataCreate(kCFAllocatorDefault,
|
||||
(const UInt8 *) &sockAddress,
|
||||
sizeof(sockAddress));
|
||||
CFAutorelease(addressData);
|
||||
if (CFSocketSetAddress(cfSocket, addressData) != kCFSocketSuccess)
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
self.socketRef = (CFSocketRef) CFRetain(cfSocket);
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(messageReadCompleteCallback:)
|
||||
name:NSFileHandleReadToEndOfFileCompletionNotification
|
||||
object:nil];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(newConnectionCallback:)
|
||||
name:NSFileHandleConnectionAcceptedNotification
|
||||
object:nil];
|
||||
|
||||
self.connectionHandle = [[NSFileHandle alloc] initWithFileDescriptor:socketDescriptor
|
||||
closeOnDealloc:YES];
|
||||
[self.connectionHandle acceptConnectionInBackgroundAndNotify];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)stopListening
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
|
||||
CFSocketInvalidate(self.socketRef);
|
||||
CFRelease(self.socketRef);
|
||||
|
||||
self.socketRef = NULL;
|
||||
self.connectionHandle = nil;
|
||||
}
|
||||
|
||||
- (void)newConnectionCallback:(NSNotification *)notification
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
NSFileHandle *readHandle = [[notification userInfo]
|
||||
objectForKey:NSFileHandleNotificationFileHandleItem];
|
||||
if (readHandle != nil)
|
||||
{
|
||||
[readHandle readToEndOfFileInBackgroundAndNotify];
|
||||
}
|
||||
|
||||
[self.connectionHandle acceptConnectionInBackgroundAndNotify];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)messageReadCompleteCallback:(NSNotification *)notification
|
||||
{
|
||||
@autoreleasepool
|
||||
{
|
||||
NSData *data = [[notification userInfo] objectForKey:NSFileHandleNotificationDataItem];
|
||||
if ((data != nil) && ([data length] > 0))
|
||||
{
|
||||
NSError *error;
|
||||
NSDictionary *message;
|
||||
if([ScalarMessageParser tryParseData:data message:&message error:&error])
|
||||
{
|
||||
self.messageCallback(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"ERROR: Could not parse notification payload: %@.", [error description]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
NSNumber *unixError = [[notification userInfo] objectForKey:@"NSFileHandleError"];
|
||||
NSLog(@"ERROR: Could not read data from socket %s.",
|
||||
unixError != nil ? strerror([unixError intValue]) : "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,13 +0,0 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ScalarMessageParser : NSObject
|
||||
|
||||
+ (BOOL)tryParseData:(NSData *)data
|
||||
message:(NSDictionary *_Nullable __autoreleasing *_Nonnull)parsedMessage
|
||||
error:(NSError *__autoreleasing *)error;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,63 +0,0 @@
|
|||
#import "ScalarMessageParser.h"
|
||||
#import "ScalarNotificationErrors.h"
|
||||
|
||||
NSString * const NotificationPrefix = @"Notification|";
|
||||
|
||||
@implementation ScalarMessageParser
|
||||
|
||||
+ (BOOL)tryParseData:(NSData *)data
|
||||
message:(NSDictionary *__autoreleasing *)parsedMessage
|
||||
error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
NSParameterAssert(parsedMessage);
|
||||
|
||||
NSString *messageStr;
|
||||
if (!(messageStr = [[NSString alloc] initWithData:data
|
||||
encoding:NSUTF8StringEncoding]))
|
||||
{
|
||||
if (error != nil)
|
||||
{
|
||||
NSString *info = [NSString stringWithFormat:@"%@: ERROR: error reading data.",
|
||||
NSStringFromSelector(_cmd)];
|
||||
*error = [NSError errorWithDomain:ScalarNotificationErrorDomain
|
||||
code:ScalarMessageReadError
|
||||
userInfo:@{ NSLocalizedDescriptionKey : info }];
|
||||
}
|
||||
*parsedMessage = nil;
|
||||
return NO;
|
||||
}
|
||||
|
||||
if ([messageStr hasPrefix:NotificationPrefix])
|
||||
{
|
||||
messageStr = [messageStr substringFromIndex:[NotificationPrefix length]];
|
||||
}
|
||||
|
||||
messageStr = [messageStr stringByTrimmingCharactersInSet:[NSCharacterSet controlCharacterSet]];
|
||||
|
||||
NSError *parseError;
|
||||
if (!(*parsedMessage = [NSJSONSerialization
|
||||
JSONObjectWithData:[messageStr dataUsingEncoding:NSUTF8StringEncoding]
|
||||
options:NSJSONReadingAllowFragments
|
||||
error:&parseError]))
|
||||
{
|
||||
if (error != nil)
|
||||
{
|
||||
if (parseError == nil)
|
||||
{
|
||||
NSString *info = [NSString stringWithFormat:@"%@: ERROR: Unknown parse error.",
|
||||
NSStringFromSelector(_cmd)];
|
||||
*error = [NSError errorWithDomain:ScalarNotificationErrorDomain
|
||||
code:ScalarMessageParseError
|
||||
userInfo:@{ NSLocalizedDescriptionKey : info }];
|
||||
}
|
||||
else
|
||||
{
|
||||
*error = parseError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return *parsedMessage != nil;
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,26 +0,0 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern NSString * const KnownMessagePrefix;
|
||||
|
||||
typedef NS_ENUM(NSInteger, Identifier)
|
||||
{
|
||||
AutomountStart,
|
||||
MountSuccess,
|
||||
MountFailure,
|
||||
UnknownMessage
|
||||
};
|
||||
|
||||
@interface ScalarNotification : NSObject
|
||||
|
||||
@property (assign, readonly) Identifier identifier;
|
||||
@property (copy, readonly) NSString *title;
|
||||
@property (copy, readonly) NSString *message;
|
||||
|
||||
+ (BOOL)tryValidateMessage:(NSDictionary *)jsonMessage
|
||||
buildNotification:(ScalarNotification *_Nullable *_Nonnull)notification
|
||||
error:(NSError *__autoreleasing *)error;
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,184 +0,0 @@
|
|||
#import "ScalarNotification.h"
|
||||
#import "ScalarNotificationErrors.h"
|
||||
|
||||
NSString * const IdentifierKey = @"Id";
|
||||
NSString * const EnlistmentKey = @"Enlistment";
|
||||
NSString * const EnlistmentCountKey = @"EnlistmentCount";
|
||||
NSString * const TitleKey = @"Title";
|
||||
NSString * const MessageKey = @"Message";
|
||||
|
||||
NSString * const AutomountTitle = @"Scalar AutoMount";
|
||||
NSString * const AutomountStartMessageFormat = @"Attempting to mount %lu Scalar repos(s)";
|
||||
NSString * const AutomountSuccessMessageFormat = @"The following Scalar repo is now mounted: \n%@";
|
||||
NSString * const AutomountFailureMessageFormat = @"The following Scalar repo failed to mount: \n%@";
|
||||
|
||||
@interface ScalarNotification()
|
||||
|
||||
@property (readwrite) NSString *title;
|
||||
@property (readwrite) NSString *message;
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
- (instancetype _Nullable)initAsMountSuccessWithMessage:(NSDictionary *)messageDict
|
||||
error:(NSError *__autoreleasing *)error;
|
||||
|
||||
- (instancetype _Nullable)initAsMountFailureWithMessage:(NSDictionary *)messageDict
|
||||
error:(NSError *__autoreleasing *)error;
|
||||
|
||||
- (instancetype _Nullable)initAsMountWithMessage:(NSDictionary *)messageDict
|
||||
title:(NSString *)title
|
||||
messageFormat:(NSString *)messageFormat
|
||||
error:(NSError *__autoreleasing *)error;
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
@end
|
||||
|
||||
@implementation ScalarNotification
|
||||
|
||||
+ (BOOL)tryValidateMessage:(NSDictionary *)jsonMessage
|
||||
buildNotification:(ScalarNotification **)notification
|
||||
error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
NSParameterAssert(notification);
|
||||
NSParameterAssert(jsonMessage);
|
||||
|
||||
id identifier = jsonMessage[IdentifierKey];
|
||||
if (![identifier isKindOfClass:[NSNumber class]])
|
||||
{
|
||||
if (error != nil)
|
||||
{
|
||||
*error = [NSError errorWithDomain:ScalarNotificationErrorDomain
|
||||
code:ScalarInvalidMessageIdFormatError
|
||||
userInfo:@{ NSLocalizedDescriptionKey : @"Unexpected message id/format)" }];
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
Identifier idValue = [identifier integerValue];
|
||||
NSError *initError = nil;
|
||||
switch (idValue)
|
||||
{
|
||||
case AutomountStart:
|
||||
{
|
||||
*notification = [[ScalarNotification alloc]
|
||||
initAsAutomountStartWithMessage:jsonMessage
|
||||
error:&initError];
|
||||
break;
|
||||
}
|
||||
|
||||
case MountSuccess:
|
||||
{
|
||||
*notification = [[ScalarNotification alloc]
|
||||
initAsMountSuccessWithMessage:jsonMessage
|
||||
error:&initError];
|
||||
break;
|
||||
}
|
||||
|
||||
case MountFailure:
|
||||
{
|
||||
*notification = [[ScalarNotification alloc]
|
||||
initAsMountFailureWithMessage:jsonMessage
|
||||
error:&initError];
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
*notification = nil;
|
||||
initError = [NSError errorWithDomain:ScalarNotificationErrorDomain
|
||||
code:ScalarUnsupportedMessageError
|
||||
userInfo:@{ NSLocalizedDescriptionKey : @"Unrecognised message id" }];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (error != nil)
|
||||
{
|
||||
*error = initError;
|
||||
}
|
||||
|
||||
return *notification != nil;
|
||||
}
|
||||
|
||||
#pragma mark Private initializers
|
||||
|
||||
- (instancetype)initAsAutomountStartWithMessage:(NSDictionary *)messageDict
|
||||
error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
id repoCount = messageDict[EnlistmentCountKey];
|
||||
if (repoCount && [repoCount isKindOfClass:[NSNumber class]])
|
||||
{
|
||||
_title = [AutomountTitle copy];
|
||||
_message = [[NSString stringWithFormat:AutomountStartMessageFormat, [repoCount unsignedIntegerValue]] copy];
|
||||
return self;
|
||||
}
|
||||
|
||||
if (error != nil)
|
||||
{
|
||||
*error = [NSError errorWithDomain:ScalarNotificationErrorDomain
|
||||
code:ScalarMissingRepoCountError
|
||||
userInfo:@{ NSLocalizedDescriptionKey : @"Missing repos count in AutomountStart message" }];
|
||||
}
|
||||
|
||||
self = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (instancetype)initAsMountSuccessWithMessage:(NSDictionary *)messageDict
|
||||
error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
return self = [self initAsMountWithMessage:messageDict
|
||||
title:(NSString *)AutomountTitle
|
||||
messageFormat:(NSString *)AutomountSuccessMessageFormat
|
||||
error:error];
|
||||
}
|
||||
|
||||
- (instancetype)initAsMountFailureWithMessage:(NSDictionary *)messageDict
|
||||
error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
return self = [self initAsMountWithMessage:messageDict
|
||||
title:(NSString *)AutomountTitle
|
||||
messageFormat:(NSString *)AutomountFailureMessageFormat
|
||||
error:error];
|
||||
}
|
||||
|
||||
- (instancetype)initAsMountWithMessage:(NSDictionary *)messageDict
|
||||
title:(NSString *)title
|
||||
messageFormat:(NSString *)messageFormat
|
||||
error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
NSParameterAssert(title);
|
||||
NSParameterAssert(messageFormat);
|
||||
|
||||
if (self = [super init])
|
||||
{
|
||||
id enlistment = messageDict[EnlistmentKey];
|
||||
if (enlistment && [enlistment isKindOfClass:[NSString class]])
|
||||
{
|
||||
_title = [title copy];
|
||||
_message = [[NSString stringWithFormat:
|
||||
(NSString *)messageFormat,
|
||||
enlistment] copy];
|
||||
return self;
|
||||
}
|
||||
|
||||
if (error != nil)
|
||||
{
|
||||
*error = [NSError errorWithDomain:ScalarNotificationErrorDomain
|
||||
code:ScalarMissingEntitlementInfoError
|
||||
userInfo:@{ NSLocalizedDescriptionKey : @"ERROR: missing enlistment info." }];
|
||||
}
|
||||
|
||||
self = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,36 +0,0 @@
|
|||
<?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>LSApplicationCategoryType</key>
|
||||
<string></string>
|
||||
<key>LSUIElement</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string></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>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>Copyright © 2019 Microsoft. All rights reserved.</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string>MainMenu</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
<string>NSApplication</string>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,10 +0,0 @@
|
|||
<?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>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-only</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,6 +0,0 @@
|
|||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"size" : "16x16",
|
||||
"idiom" : "mac",
|
||||
"filename" : "Scalar_16.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "16x16",
|
||||
"idiom" : "mac",
|
||||
"filename" : "Scalar_32-1.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "32x32",
|
||||
"idiom" : "mac",
|
||||
"filename" : "Scalar_32.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "32x32",
|
||||
"idiom" : "mac",
|
||||
"filename" : "Scalar_64.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "128x128",
|
||||
"idiom" : "mac",
|
||||
"filename" : "Scalar_128.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "128x128",
|
||||
"idiom" : "mac",
|
||||
"filename" : "Scalar_256-1.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "256x256",
|
||||
"idiom" : "mac",
|
||||
"filename" : "Scalar_256.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "256x256",
|
||||
"idiom" : "mac",
|
||||
"filename" : "Scalar_512-1.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"size" : "512x512",
|
||||
"idiom" : "mac",
|
||||
"filename" : "Scalar_512.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"size" : "512x512",
|
||||
"idiom" : "mac",
|
||||
"filename" : "Scalar_1024.png",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
До Ширина: | Высота: | Размер: 41 KiB |
До Ширина: | Высота: | Размер: 4.3 KiB |
До Ширина: | Высота: | Размер: 590 B |
До Ширина: | Высота: | Размер: 8.8 KiB |
До Ширина: | Высота: | Размер: 8.8 KiB |
До Ширина: | Высота: | Размер: 1.1 KiB |
До Ширина: | Высота: | Размер: 1.1 KiB |
До Ширина: | Высота: | Размер: 19 KiB |
До Ширина: | Высота: | Размер: 19 KiB |
До Ширина: | Высота: | Размер: 2.2 KiB |
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "Scalar_16.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "Scalar_32.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "Scalar_64.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
Двоичные данные
Scalar.Notifications.Mac/StatusMenuItem/UI/Assets.xcassets/StatusItem.imageset/Scalar_16.png
поставляемый
До Ширина: | Высота: | Размер: 590 B |
Двоичные данные
Scalar.Notifications.Mac/StatusMenuItem/UI/Assets.xcassets/StatusItem.imageset/Scalar_32.png
поставляемый
До Ширина: | Высота: | Размер: 1.1 KiB |
Двоичные данные
Scalar.Notifications.Mac/StatusMenuItem/UI/Assets.xcassets/StatusItem.imageset/Scalar_64.png
поставляемый
До Ширина: | Высота: | Размер: 2.2 KiB |
|
@ -1,59 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14109" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14109"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
|
||||
<connections>
|
||||
<outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customObject id="Voe-Tx-rLC" customClass="ScalarAppDelegate">
|
||||
<connections>
|
||||
<outlet property="Window" destination="QvC-M9-y7g" id="ykX-6N-Ma5"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
||||
<menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
|
||||
<items>
|
||||
<menuItem title="Scalar" id="1Xt-HY-uBw">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Scalar" systemMenu="apple" id="uQy-DD-JDr">
|
||||
<items>
|
||||
<menuItem title="About Scalar" id="5kV-Vb-QxS">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
|
||||
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
|
||||
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
|
||||
<menuItem title="Quit Scalar" keyEquivalent="q" id="4sb-4s-VLi">
|
||||
<connections>
|
||||
<action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
</items>
|
||||
<point key="canvasLocation" x="-66" y="25"/>
|
||||
</menu>
|
||||
<window title="Scalar" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="QvC-M9-y7g">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||
<rect key="contentRect" x="335" y="390" width="480" height="360"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1057"/>
|
||||
<view key="contentView" wantsLayer="YES" id="EiT-Mj-1SZ">
|
||||
<rect key="frame" x="0.0" y="0.0" width="480" height="360"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</view>
|
||||
<point key="canvasLocation" x="63" y="363"/>
|
||||
</window>
|
||||
</objects>
|
||||
</document>
|
|
@ -1,11 +0,0 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
#import "ScalarProductInfoFetcher.h"
|
||||
|
||||
@interface ScalarAboutWindowController : NSWindowController
|
||||
|
||||
@property (readonly, nullable) NSString *scalarVersion;
|
||||
@property (readonly, nullable) NSString *gitVersion;
|
||||
|
||||
- (instancetype _Nullable)initWithProductInfoFetcher:(ScalarProductInfoFetcher *_Nonnull)productInfoFetcher;
|
||||
|
||||
@end
|
|
@ -1,55 +0,0 @@
|
|||
#import "ScalarAboutWindowController.h"
|
||||
|
||||
@interface ScalarAboutWindowController ()
|
||||
|
||||
@property (strong) ScalarProductInfoFetcher *productInfoFetcher;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ScalarAboutWindowController
|
||||
|
||||
- (instancetype)initWithProductInfoFetcher:(ScalarProductInfoFetcher *)productInfoFetcher
|
||||
{
|
||||
if (productInfoFetcher == nil)
|
||||
{
|
||||
self = nil;
|
||||
}
|
||||
else if (self = [super initWithWindowNibName:@"ScalarAboutWindowController"])
|
||||
{
|
||||
_productInfoFetcher = productInfoFetcher;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)scalarVersion
|
||||
{
|
||||
NSString *version;
|
||||
NSError *error;
|
||||
if ([self.productInfoFetcher tryGetScalarVersion:&version error:&error])
|
||||
{
|
||||
return version;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"Error getting Scalar version: %@", [error description]);
|
||||
return @"Not available";
|
||||
}
|
||||
}
|
||||
|
||||
- (NSString *)gitVersion
|
||||
{
|
||||
NSString *version;
|
||||
NSError *error;
|
||||
if ([self.productInfoFetcher tryGetGitVersion:&version error:&error])
|
||||
{
|
||||
return version;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSLog(@"Error getting Git version: %@", [error description]);
|
||||
return @"Not available";
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,98 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14109" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14109"/>
|
||||
<capability name="box content view" minToolsVersion="7.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
<capability name="system font weights other than Regular or Bold" minToolsVersion="7.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<customObject id="-2" userLabel="File's Owner" customClass="ScalarAboutWindowController">
|
||||
<connections>
|
||||
<outlet property="window" destination="F0z-JX-Cv5" id="gIp-Ho-8D9"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<window title="About Scalar" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" oneShot="NO" showsToolbarButton="NO" visibleAtLaunch="NO" animationBehavior="utilityWindow" id="F0z-JX-Cv5">
|
||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
|
||||
<windowCollectionBehavior key="collectionBehavior" canJoinAllSpaces="YES" ignoresCycle="YES" fullScreenAuxiliary="YES"/>
|
||||
<rect key="contentRect" x="784" y="333" width="520" height="191"/>
|
||||
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1057"/>
|
||||
<value key="minSize" type="size" width="520" height="191"/>
|
||||
<value key="maxSize" type="size" width="520" height="191"/>
|
||||
<value key="minFullScreenContentSize" type="size" width="520" height="191"/>
|
||||
<value key="maxFullScreenContentSize" type="size" width="520" height="191"/>
|
||||
<view key="contentView" wantsLayer="YES" id="se5-gp-TjO">
|
||||
<rect key="frame" x="0.0" y="0.0" width="520" height="191"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<box autoresizesSubviews="NO" fixedFrame="YES" borderType="line" title="Version Info..." translatesAutoresizingMaskIntoConstraints="NO" id="bl4-5a-Z0p">
|
||||
<rect key="frame" x="176" y="16" width="327" height="155"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<view key="contentView" id="ZS0-xZ-RRj">
|
||||
<rect key="frame" x="1" y="1" width="325" height="139"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="rzD-cd-GNF">
|
||||
<rect key="frame" x="18" y="19" width="288" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" title="Scalar Version" id="hMd-va-lMt">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="gitVersion" id="cD3-Y6-vdy"/>
|
||||
</connections>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pGO-kl-sTg">
|
||||
<rect key="frame" x="18" y="101" width="75" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Scalar" allowsEditingTextAttributes="YES" id="zLl-4G-TZk">
|
||||
<font key="font" metaFont="systemSemibold" size="13"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kj1-ex-ImL">
|
||||
<rect key="frame" x="18" y="39" width="23" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Git" id="xeY-Ah-BYF">
|
||||
<font key="font" metaFont="systemSemibold" size="13"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" textCompletion="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2NU-O7-nN1">
|
||||
<rect key="frame" x="18" y="81" width="288" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" sendsActionOnEndEditing="YES" title="Scalar Version" id="sFb-nd-mdX">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
<connections>
|
||||
<binding destination="-2" name="value" keyPath="scalarVersion" id="6Wp-ud-281"/>
|
||||
</connections>
|
||||
</textField>
|
||||
</subviews>
|
||||
</view>
|
||||
</box>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="UtS-Mf-ucW">
|
||||
<rect key="frame" x="12" y="19" width="154" height="154"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxX="YES" flexibleMinY="YES" flexibleMaxY="YES"/>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="NSApplicationIcon" id="dl0-H5-uoz"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="-2" id="0bl-1N-AYu"/>
|
||||
</connections>
|
||||
<point key="canvasLocation" x="98" y="100.5"/>
|
||||
</window>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="NSApplicationIcon" width="128" height="128"/>
|
||||
</resources>
|
||||
</document>
|
|
@ -1,6 +0,0 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface ScalarAppDelegate : NSObject <NSApplicationDelegate>
|
||||
|
||||
@end
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
#import "ScalarAboutWindowController.h"
|
||||
#import "ScalarAppDelegate.h"
|
||||
#import "ScalarMessageListener.h"
|
||||
#import "ScalarNotificationDisplay.h"
|
||||
#import "ScalarNotification.h"
|
||||
#import "ScalarProductInfoFetcher.h"
|
||||
#import "ScalarStatusBarItem.h"
|
||||
#import "ScalarNotificationDisplay.h"
|
||||
|
||||
@interface ScalarAppDelegate ()
|
||||
|
||||
@property (weak) IBOutlet NSWindow *Window;
|
||||
@property (strong) ScalarStatusBarItem *StatusDisplay;
|
||||
@property (strong) ScalarMessageListener *messageListener;
|
||||
|
||||
- (void)displayNotification:(NSDictionary *_Nonnull)messageInfo;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ScalarAppDelegate
|
||||
|
||||
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
||||
{
|
||||
self.messageListener = [[ScalarMessageListener alloc]
|
||||
initWithSocket:NSTemporaryDirectory()
|
||||
callback:^(NSDictionary *messageInfo)
|
||||
{
|
||||
[self displayNotification:messageInfo];
|
||||
}];
|
||||
|
||||
[self.messageListener startListening];
|
||||
|
||||
ScalarProductInfoFetcher *productInfoFetcher =
|
||||
[[ScalarProductInfoFetcher alloc]
|
||||
initWithProcessRunner:[[ScalarProcessRunner alloc] initWithProcessFactory:^NSTask *
|
||||
{
|
||||
return [[NSTask alloc] init];
|
||||
}]];
|
||||
|
||||
self.StatusDisplay = [[ScalarStatusBarItem alloc] initWithAboutWindowController:
|
||||
[[ScalarAboutWindowController alloc]
|
||||
initWithProductInfoFetcher:productInfoFetcher]];
|
||||
|
||||
[self.StatusDisplay load];
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(NSNotification *)aNotification
|
||||
{
|
||||
[self.messageListener stopListening];
|
||||
}
|
||||
|
||||
- (void)displayNotification:(NSDictionary *_Nonnull)messageInfo
|
||||
{
|
||||
NSParameterAssert(messageInfo);
|
||||
|
||||
ScalarNotification *notification;
|
||||
NSError *error;
|
||||
if (![ScalarNotification tryValidateMessage:messageInfo
|
||||
buildNotification:¬ification
|
||||
error:&error])
|
||||
{
|
||||
NSLog(@"ERROR: Could not display notification. %@", [error description]);
|
||||
return;
|
||||
}
|
||||
|
||||
ScalarNotificationDisplay *notificationDisplay =
|
||||
[[ScalarNotificationDisplay alloc] initWithTitle:notification.title
|
||||
message:notification.message];
|
||||
|
||||
[notificationDisplay display];
|
||||
}
|
||||
@end
|
|
@ -1,10 +0,0 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface ScalarNotificationDisplay : NSObject
|
||||
|
||||
- (instancetype _Nullable)initWithTitle:(NSString *)title message:(NSString *)message;
|
||||
- (instancetype _Nullable)initWithUserNotification:(NSUserNotification *)userNotification
|
||||
notificationCenter:(NSUserNotificationCenter *)notificationCenter;
|
||||
- (void) display;
|
||||
|
||||
@end
|
|
@ -1,42 +0,0 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
#import "ScalarNotificationDisplay.h"
|
||||
|
||||
@interface ScalarNotificationDisplay ()
|
||||
|
||||
@property (strong) NSUserNotification *userNotification;
|
||||
@property (strong) NSUserNotificationCenter *userNotificationCenter;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ScalarNotificationDisplay
|
||||
|
||||
- (instancetype)initWithTitle:(NSString *)title message:(NSString *)message
|
||||
{
|
||||
NSUserNotification *userNotification = [[NSUserNotification alloc] init];
|
||||
userNotification.title = title;
|
||||
userNotification.informativeText = message;
|
||||
NSUserNotificationCenter *notificationCenter =
|
||||
[NSUserNotificationCenter defaultUserNotificationCenter];
|
||||
|
||||
return self = [self initWithUserNotification:userNotification
|
||||
notificationCenter:notificationCenter];
|
||||
}
|
||||
|
||||
- (instancetype)initWithUserNotification:(NSUserNotification *)userNotification
|
||||
notificationCenter:(NSUserNotificationCenter *)notificationCenter
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_userNotification = userNotification;
|
||||
_userNotificationCenter = notificationCenter;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)display
|
||||
{
|
||||
[self.userNotificationCenter deliverNotification:self.userNotification];
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,10 +0,0 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
@interface ScalarStatusBarItem : NSObject
|
||||
|
||||
- (instancetype _Nullable)initWithAboutWindowController:(ScalarAboutWindowController *_Nonnull)aboutWindowController;
|
||||
- (void)load;
|
||||
- (NSMenu *_Nullable)getStatusMenu;
|
||||
- (IBAction)handleMenuClick:(id)sender;
|
||||
|
||||
@end
|
|
@ -1,97 +0,0 @@
|
|||
#import "ScalarAboutWindowController.h"
|
||||
#import "ScalarStatusBarItem.h"
|
||||
#import "ScalarProductInfoFetcher.h"
|
||||
|
||||
@interface ScalarStatusBarItem ()
|
||||
|
||||
@property (strong, nonnull) NSStatusItem *statusItem;
|
||||
@property (strong, nonnull) ScalarAboutWindowController *aboutWindowController;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ScalarStatusBarItem
|
||||
|
||||
- (instancetype)initWithAboutWindowController:(ScalarAboutWindowController *)aboutWindowController
|
||||
{
|
||||
if (aboutWindowController == nil)
|
||||
{
|
||||
self = nil;
|
||||
}
|
||||
else if (self = [super init])
|
||||
{
|
||||
_aboutWindowController = aboutWindowController;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)load
|
||||
{
|
||||
self.statusItem = [[NSStatusBar systemStatusBar]
|
||||
statusItemWithLength:NSVariableStatusItemLength];
|
||||
|
||||
[self.statusItem setHighlightMode:YES];
|
||||
|
||||
[self addStatusButton];
|
||||
[self addMenuItems];
|
||||
}
|
||||
|
||||
- (IBAction)handleMenuClick:(id)sender
|
||||
{
|
||||
switch (((NSButton *) sender).tag)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
[self displayAboutBox];
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (IBAction)displayAboutBox
|
||||
{
|
||||
[[NSApplication sharedApplication] activateIgnoringOtherApps:YES];
|
||||
[self.aboutWindowController showWindow:self];
|
||||
[self.aboutWindowController.window makeKeyAndOrderFront:self];
|
||||
}
|
||||
|
||||
- (NSMenu *)getStatusMenu
|
||||
{
|
||||
return self.statusItem.menu;
|
||||
}
|
||||
|
||||
- (void)addStatusButton
|
||||
{
|
||||
NSImage *image = [NSImage imageNamed:@"StatusItem"];
|
||||
|
||||
[image setTemplate:YES];
|
||||
|
||||
[self.statusItem.button setImage:image];
|
||||
[self.statusItem.button setTarget:nil];
|
||||
[self.statusItem.button setAction:nil];
|
||||
}
|
||||
|
||||
- (void)addMenuItems
|
||||
{
|
||||
NSUInteger index = 0;
|
||||
NSMenu *menu = [[NSMenu alloc] init];
|
||||
NSMenuItem *aboutItem = [[NSMenuItem alloc]
|
||||
initWithTitle:@"About Scalar"
|
||||
action:@selector(handleMenuClick:)
|
||||
keyEquivalent:@""];
|
||||
|
||||
[aboutItem setTag:0];
|
||||
[aboutItem setTarget:self];
|
||||
[menu insertItem:[NSMenuItem separatorItem] atIndex:index++];
|
||||
[menu insertItem:aboutItem atIndex:index++];
|
||||
[menu setAutoenablesItems:NO];
|
||||
|
||||
[self.statusItem setMenu:menu];
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,18 +0,0 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
extern NSErrorDomain const ScalarNotificationErrorDomain;
|
||||
typedef NS_ERROR_ENUM(ScalarNotificationErrorDomain, ScalarNotificationErrorCode)
|
||||
{
|
||||
ScalarInitError = 200,
|
||||
ScalarAllocError,
|
||||
ScalarInvalidMessageIdFormatError,
|
||||
ScalarUnsupportedMessageError,
|
||||
ScalarMissingEntitlementInfoError,
|
||||
ScalarMissingRepoCountError,
|
||||
ScalarMessageParseError,
|
||||
ScalarMessageReadError,
|
||||
};
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,3 +0,0 @@
|
|||
#import "ScalarNotificationErrors.h"
|
||||
|
||||
NSErrorDomain const ScalarNotificationErrorDomain = @"ScalarNotificationErrorDomain";
|
|
@ -1,17 +0,0 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
typedef NSTask *_Nonnull (^ProcessFactory)(void);
|
||||
|
||||
@interface ScalarProcessRunner : NSObject
|
||||
|
||||
- (instancetype _Nullable)initWithProcessFactory:(ProcessFactory)processFactory;
|
||||
- (BOOL)tryRunExecutable:(NSURL *)path
|
||||
args:(NSArray<NSString *> *_Nullable)args
|
||||
output:(NSString *_Nullable __autoreleasing *_Nonnull)output
|
||||
error:(NSError * __autoreleasing *)error;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,82 +0,0 @@
|
|||
#import "ScalarProcessRunner.h"
|
||||
#import "ScalarNotificationErrors.h"
|
||||
|
||||
@interface ScalarProcessRunner()
|
||||
|
||||
@property (strong) ProcessFactory processFactory;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ScalarProcessRunner
|
||||
|
||||
- (instancetype)initWithProcessFactory:(ProcessFactory)processFactory
|
||||
{
|
||||
if (processFactory == nil)
|
||||
{
|
||||
self = nil;
|
||||
}
|
||||
else if (self = [super init])
|
||||
{
|
||||
_processFactory = processFactory;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
Runs an executable specified by path and args. If the executable could be run
|
||||
successfully - output will contain the executable's combined stderr/stdout
|
||||
output and the method returns YES. In case of failure, it returns NO and error
|
||||
will hold the executable's combined stderr/stdout output.
|
||||
|
||||
@param path - specify full path to the executable.
|
||||
@param args - specify any command line args to pass to the executable.
|
||||
@param output - contains executable's output, if it was successfully run.
|
||||
@param error - contains executable's output, if it exited with an error.
|
||||
@return YES if the executable was successfully run, NO otherwise.
|
||||
*/
|
||||
- (BOOL)tryRunExecutable:(NSURL *)path
|
||||
args:(NSArray<NSString *> *)args
|
||||
output:(NSString *__autoreleasing *)output
|
||||
error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
NSParameterAssert(path);
|
||||
NSParameterAssert(output);
|
||||
|
||||
NSTask *task = self.processFactory();
|
||||
NSPipe *taskOut = [NSPipe pipe];
|
||||
|
||||
task.executableURL = path;
|
||||
task.arguments = args;
|
||||
task.standardOutput = taskOut;
|
||||
task.standardError = taskOut;
|
||||
|
||||
int exitCode = -1;
|
||||
|
||||
if ([task launchAndReturnError:error])
|
||||
{
|
||||
[task waitUntilExit];
|
||||
|
||||
exitCode = [task terminationStatus];
|
||||
|
||||
*output = [[NSString alloc] initWithData:[taskOut.fileHandleForReading availableData]
|
||||
encoding:NSUTF8StringEncoding];
|
||||
|
||||
if (0 != exitCode)
|
||||
{
|
||||
if (error != nil)
|
||||
{
|
||||
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey : *output };
|
||||
*error = [NSError errorWithDomain:ScalarNotificationErrorDomain
|
||||
code:exitCode
|
||||
userInfo:userInfo];
|
||||
}
|
||||
|
||||
*output = nil;
|
||||
}
|
||||
}
|
||||
|
||||
return 0 == exitCode;
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,16 +0,0 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import "ScalarProcessRunner.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface ScalarProductInfoFetcher : NSObject
|
||||
|
||||
- (instancetype _Nullable)initWithProcessRunner:(ScalarProcessRunner *)processRunner;
|
||||
- (BOOL)tryGetGitVersion:(NSString *_Nullable __autoreleasing *_Nonnull)version
|
||||
error:(NSError **)error;
|
||||
- (BOOL)tryGetScalarVersion:(NSString *_Nullable __autoreleasing *_Nonnull)version
|
||||
error:(NSError **)error;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
|
@ -1,61 +0,0 @@
|
|||
#import <objc/runtime.h>
|
||||
#import "ScalarProductInfoFetcher.h"
|
||||
|
||||
NSString * const ScalarPath = @"/usr/local/bin/scalar";
|
||||
NSString * const GitPath = @"/usr/local/bin/git";
|
||||
|
||||
@interface ScalarProductInfoFetcher()
|
||||
|
||||
@property (strong, nonnull) ScalarProcessRunner *processRunner;
|
||||
|
||||
@end
|
||||
|
||||
@implementation ScalarProductInfoFetcher
|
||||
|
||||
- (instancetype)initWithProcessRunner:(ScalarProcessRunner *)processRunner
|
||||
{
|
||||
if (processRunner == nil)
|
||||
{
|
||||
self = nil;
|
||||
}
|
||||
else if (self = [super init])
|
||||
{
|
||||
_processRunner = processRunner;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (BOOL)tryGetScalarVersion:(NSString *__autoreleasing *)version
|
||||
error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
NSParameterAssert(version);
|
||||
|
||||
if (![self.processRunner tryRunExecutable:[NSURL fileURLWithPath:ScalarPath]
|
||||
args:@[ @"version" ]
|
||||
output:version
|
||||
error:error])
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)tryGetGitVersion:(NSString *__autoreleasing *)version
|
||||
error:(NSError *__autoreleasing *)error
|
||||
{
|
||||
NSParameterAssert(version);
|
||||
|
||||
if (![self.processRunner tryRunExecutable:[NSURL fileURLWithPath:GitPath]
|
||||
args:@[ @"version" ]
|
||||
output:version
|
||||
error:error])
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
|
@ -1,6 +0,0 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
int main(int argc, const char * argv[])
|
||||
{
|
||||
return NSApplicationMain(argc, argv);
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
CONFIGURATION=$1
|
||||
if [ -z $CONFIGURATION ]; then
|
||||
echo "Error: Build configuration not specified"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
INT_DIR=$2
|
||||
if [ -z "${INT_DIR}" ]; then
|
||||
echo "Error: Intermediate output directory not specified"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OUT_DIR=$3
|
||||
if [ -z "${OUT_DIR}" ]; then
|
||||
echo "Error: Output directory not specified"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
VERSION=$4
|
||||
if [ -z "${VERSION}" ]; then
|
||||
echo "Info: Version not set; not updating version number"
|
||||
fi
|
||||
|
||||
THIS_DIR="$( cd "$(dirname "$0")" ; pwd -P )"
|
||||
|
||||
# Set the version if not the default 'developer version'
|
||||
if [ -n "$VERSION" ] && [ "$VERSION" != "0.2.173.2" ]; then
|
||||
updateAppVersionCmd="(cd \"${THIS_DIR}\" && /usr/bin/xcrun agvtool new-marketing-version \"$VERSION\")"
|
||||
eval $updateAppVersionCmd || exit 1
|
||||
fi
|
||||
|
||||
# Build the product
|
||||
xcodebuild -configuration "$CONFIGURATION" -project "${THIS_DIR}/Scalar.xcodeproj" build -scheme "Scalar" -derivedDataPath "$INT_DIR" || exit 1
|
||||
|
||||
# Ensure the output directory exists
|
||||
mkdir -p "$OUT_DIR" || exit 1
|
||||
|
||||
# Copy output from intermediate output to final binary output directory
|
||||
cp -Rf "$INT_DIR/Build/Products/$CONFIGURATION/Scalar.app" "$OUT_DIR" || exit 1
|
|
@ -1,38 +0,0 @@
|
|||
<?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>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<!--
|
||||
Although Scalar.app gets launched in the GUI context of actively logged-in user, it
|
||||
does not seem to inherit any user-specific environment variables (like user customized
|
||||
$PATH specified in shell ~/.profile). Scalar needs /usr/local/bin to be in its
|
||||
search PATH environment, so specifying it here, along with rest of the default paths
|
||||
that are set by launchd.
|
||||
-->
|
||||
<key>PATH</key>
|
||||
<string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
||||
</dict>
|
||||
<key>Label</key>
|
||||
<string>org.scalar.usernotification</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/Library/Application Support/Scalar/Scalar.app/Contents/MacOS/Scalar</string>
|
||||
</array>
|
||||
<key>WorkingDirectory</key>
|
||||
<string>/usr/local/scalar</string>
|
||||
<key>ProcessType</key>
|
||||
<string>Interactive</string>
|
||||
<key>LimitLoadToSessionType</key>
|
||||
<string>Aqua</string>
|
||||
<key>Disabled</key>
|
||||
<false/>
|
||||
<key>OnDemand</key>
|
||||
<false/>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,16 +0,0 @@
|
|||
#!/bin/bash
|
||||
|
||||
CONFIGURATION=$1
|
||||
if [ -z $CONFIGURATION ]; then
|
||||
echo "Error: Build configuration not specified"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
INT_DIR=$2
|
||||
if [ -z $INT_DIR ]; then
|
||||
echo "Error: Intermediate output directory not specified"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Run tests
|
||||
xcodebuild -configuration "$CONFIGURATION" -project "Scalar.xcodeproj" test -scheme "Scalar" -derivedDataPath "$INT_DIR" || exit 1
|
|
@ -1,45 +0,0 @@
|
|||
using Scalar.Common.NamedPipes;
|
||||
using Scalar.Common.Tracing;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Scalar.Service.Handlers
|
||||
{
|
||||
public class MacNotificationHandler : INotificationHandler
|
||||
{
|
||||
private const string NotificationServerPipeName = "scalar.notification";
|
||||
private ITracer tracer;
|
||||
|
||||
public MacNotificationHandler(ITracer tracer)
|
||||
{
|
||||
this.tracer = tracer;
|
||||
}
|
||||
|
||||
public void SendNotification(NamedPipeMessages.Notification.Request request)
|
||||
{
|
||||
string pipeName = Path.Combine(Path.GetTempPath(), NotificationServerPipeName);
|
||||
using (NamedPipeClient client = new NamedPipeClient(pipeName))
|
||||
{
|
||||
if (client.Connect())
|
||||
{
|
||||
try
|
||||
{
|
||||
client.SendRequest(request.ToMessage());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("Area", "NotificationHandler");
|
||||
metadata.Add("Exception", ex.ToString());
|
||||
metadata.Add(TracingConstants.MessageKey.ErrorMessage, "MacOS notification display error");
|
||||
this.tracer.RelatedError(metadata, $"MacOS notification: {request.Title} - {request.Message}.");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.tracer.RelatedError($"ERROR: Communication failure with native notification display tool. Notification info: {request.Title} - {request.Message}.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
<?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>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<!--
|
||||
Although Scalar.Service gets launched in the GUI context of actively logged-in user, it
|
||||
does not seem to inherit any user-specific environment variables (like user customized
|
||||
$PATH specified in shell ~/.profile). Scalar needs /usr/local/bin to be in its
|
||||
search PATH environment, so specifying it here, along with rest of the default paths
|
||||
that are set by launchd.
|
||||
-->
|
||||
<key>PATH</key>
|
||||
<string>/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
|
||||
</dict>
|
||||
<key>Label</key>
|
||||
<string>org.scalar.service</string>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>/usr/local/scalar/Scalar.Service</string>
|
||||
</array>
|
||||
<key>WorkingDirectory</key>
|
||||
<string>/usr/local/scalar</string>
|
||||
<key>ProcessType</key>
|
||||
<string>Interactive</string>
|
||||
<key>LimitLoadToSessionType</key>
|
||||
<string>Aqua</string>
|
||||
<key>Disabled</key>
|
||||
<false/>
|
||||
<key>OnDemand</key>
|
||||
<false/>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
|
@ -1,142 +0,0 @@
|
|||
using Scalar.Common;
|
||||
using Scalar.Common.FileSystem;
|
||||
using Scalar.Common.NamedPipes;
|
||||
using Scalar.Common.RepoRegistry;
|
||||
using Scalar.Common.Tracing;
|
||||
using Scalar.Service.Handlers;
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace Scalar.Service
|
||||
{
|
||||
public class MacScalarService
|
||||
{
|
||||
public const string ServiceNameArgPrefix = "--servicename=";
|
||||
|
||||
private const string EtwArea = "ScalarService";
|
||||
|
||||
private ITracer tracer;
|
||||
private Thread serviceThread;
|
||||
private ManualResetEvent serviceStopped;
|
||||
private string serviceName;
|
||||
private IScalarRepoRegistry repoRegistry;
|
||||
private RequestHandler requestHandler;
|
||||
private MaintenanceTaskScheduler maintenanceTaskScheduler;
|
||||
|
||||
public MacScalarService(
|
||||
ITracer tracer,
|
||||
string serviceName,
|
||||
IScalarRepoRegistry repoRegistry)
|
||||
{
|
||||
this.tracer = tracer;
|
||||
this.repoRegistry = repoRegistry;
|
||||
this.serviceName = serviceName;
|
||||
|
||||
this.serviceStopped = new ManualResetEvent(false);
|
||||
this.serviceThread = new Thread(this.ServiceThreadMain);
|
||||
this.requestHandler = new RequestHandler(this.tracer, EtwArea);
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(this.serviceName))
|
||||
{
|
||||
string pipeName = ScalarPlatform.Instance.GetScalarServiceNamedPipeName(this.serviceName);
|
||||
this.tracer.RelatedInfo("Starting pipe server with name: " + pipeName);
|
||||
|
||||
using (NamedPipeServer pipeServer = NamedPipeServer.StartNewServer(
|
||||
pipeName,
|
||||
this.tracer,
|
||||
this.requestHandler.HandleRequest))
|
||||
{
|
||||
this.serviceThread.Start();
|
||||
this.serviceThread.Join();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.tracer.RelatedError("No name specified for Service Pipe.");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.LogExceptionAndExit(e, nameof(this.Run));
|
||||
}
|
||||
}
|
||||
|
||||
private static EventMetadata CreateEventMetadata(Exception e = null)
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("Area", EtwArea);
|
||||
if (e != null)
|
||||
{
|
||||
metadata.Add("Exception", e.ToString());
|
||||
}
|
||||
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private void ServiceThreadMain()
|
||||
{
|
||||
try
|
||||
{
|
||||
string currentUser = ScalarPlatform.Instance.GetCurrentUser();
|
||||
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("Version", ProcessHelper.GetCurrentProcessVersion());
|
||||
metadata.Add(nameof(currentUser), currentUser);
|
||||
this.tracer.RelatedEvent(EventLevel.Informational, $"ScalarService_{nameof(this.ServiceThreadMain)}", metadata);
|
||||
|
||||
if (int.TryParse(currentUser, out int sessionId))
|
||||
{
|
||||
try
|
||||
{
|
||||
this.maintenanceTaskScheduler = new MaintenanceTaskScheduler(
|
||||
this.tracer,
|
||||
new PhysicalFileSystem(),
|
||||
new MacScalarVerbRunner(this.tracer),
|
||||
this.repoRegistry);
|
||||
|
||||
// On Mac, there is no separate session Id. currentUser is used as sessionId
|
||||
this.maintenanceTaskScheduler.RegisterUser(new UserAndSession(currentUser, sessionId));
|
||||
this.maintenanceTaskScheduler.ScheduleRecurringTasks();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.tracer.RelatedError(CreateEventMetadata(e), "Failed to start maintenance scheduler");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EventMetadata errorMetadata = CreateEventMetadata();
|
||||
errorMetadata.Add(nameof(currentUser), currentUser);
|
||||
this.tracer.RelatedError(
|
||||
errorMetadata,
|
||||
$"{nameof(this.ServiceThreadMain)}: Failed to parse current user as int.");
|
||||
}
|
||||
|
||||
this.serviceStopped.WaitOne();
|
||||
this.serviceStopped.Dispose();
|
||||
this.serviceStopped = null;
|
||||
|
||||
if (this.maintenanceTaskScheduler != null)
|
||||
{
|
||||
this.maintenanceTaskScheduler.Dispose();
|
||||
this.maintenanceTaskScheduler = null;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
this.LogExceptionAndExit(e, nameof(this.ServiceThreadMain));
|
||||
}
|
||||
}
|
||||
|
||||
private void LogExceptionAndExit(Exception e, string method)
|
||||
{
|
||||
this.tracer.RelatedError(CreateEventMetadata(e), "Unhandled exception in " + method);
|
||||
Environment.Exit((int)ReturnCode.GenericError);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
using Scalar.Common;
|
||||
using Scalar.Common.Maintenance;
|
||||
using Scalar.Common.Tracing;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace Scalar.Service
|
||||
{
|
||||
public class MacScalarVerbRunner : IScalarVerbRunner
|
||||
{
|
||||
private readonly string scalarBinPath;
|
||||
private readonly string internalVerbJson;
|
||||
|
||||
private ScalarProcessLauncher processLauncher;
|
||||
private ITracer tracer;
|
||||
|
||||
public MacScalarVerbRunner(ITracer tracer, ScalarProcessLauncher processLauncher = null)
|
||||
{
|
||||
this.tracer = tracer;
|
||||
this.processLauncher = processLauncher ?? new ScalarProcessLauncher(tracer);
|
||||
|
||||
this.scalarBinPath = Path.Combine(
|
||||
ScalarPlatform.Instance.Constants.ScalarBinDirectoryPath,
|
||||
ScalarPlatform.Instance.Constants.ScalarExecutableName);
|
||||
|
||||
InternalVerbParameters internalParams = new InternalVerbParameters(startedByService: true);
|
||||
this.internalVerbJson = internalParams.ToJson();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calls the 'scalar maintenance' verb
|
||||
/// </summary>
|
||||
/// <param name="task">Maintenance task to run</param>
|
||||
/// <param name="repoRoot">Repo to maintain</param>
|
||||
/// <param name="sessionId">Ignored</param>
|
||||
/// <returns>
|
||||
/// true if the maintenance verb succeeded, and false otherwise
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// 'CallMaintenance' should only be called for repos that are owned by
|
||||
/// the owner of the current process.
|
||||
///
|
||||
/// 'launchctl asuser' *could* be used to launch has an arbitrary user,
|
||||
/// however, it is not used because it does not pass back the output/errors
|
||||
/// of the maintenance verb correctly.
|
||||
///
|
||||
/// On Mac this method:
|
||||
///
|
||||
/// - Is only called by Scalar.Service
|
||||
/// - Is only called for repos owned by the same user that's running Scalar.Service
|
||||
///
|
||||
/// And so there is no need to use 'launchctl'.
|
||||
/// </remarks>
|
||||
public bool CallMaintenance(MaintenanceTasks.Task task, string repoRoot, int sessionId)
|
||||
{
|
||||
string taskVerbName = MaintenanceTasks.GetVerbTaskName(task);
|
||||
string arguments =
|
||||
$"run {taskVerbName} \"{repoRoot}\" --{ScalarConstants.VerbParameters.InternalUseOnly} {this.internalVerbJson}";
|
||||
|
||||
ProcessResult result = this.processLauncher.LaunchProcess(this.scalarBinPath, arguments, repoRoot);
|
||||
if (result.ExitCode != 0)
|
||||
{
|
||||
EventMetadata metadata = new EventMetadata();
|
||||
metadata.Add("Area", "ScalarVerbRunner");
|
||||
metadata.Add(nameof(this.scalarBinPath), this.scalarBinPath);
|
||||
metadata.Add(nameof(arguments), arguments);
|
||||
metadata.Add(nameof(repoRoot), repoRoot);
|
||||
metadata.Add(nameof(result.ExitCode), result.ExitCode);
|
||||
metadata.Add(nameof(result.Output), result.Output);
|
||||
metadata.Add(nameof(result.Errors), result.Errors);
|
||||
|
||||
this.tracer.RelatedError(metadata, $"{nameof(this.CallMaintenance)}: Maintenance verb failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public class ScalarProcessLauncher
|
||||
{
|
||||
private ITracer tracer;
|
||||
|
||||
public ScalarProcessLauncher(ITracer tracer)
|
||||
{
|
||||
this.tracer = tracer;
|
||||
}
|
||||
|
||||
public virtual ProcessResult LaunchProcess(string executablePath, string arguments, string workingDirectory)
|
||||
{
|
||||
ProcessStartInfo processInfo = new ProcessStartInfo(executablePath);
|
||||
processInfo.Arguments = arguments;
|
||||
processInfo.WindowStyle = ProcessWindowStyle.Hidden;
|
||||
processInfo.WorkingDirectory = workingDirectory;
|
||||
processInfo.UseShellExecute = false;
|
||||
processInfo.RedirectStandardOutput = true;
|
||||
|
||||
return ProcessHelper.Run(processInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,12 +32,6 @@ namespace Scalar.Service
|
|||
ServiceBase.Run(service);
|
||||
}
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
AppDomain.CurrentDomain.UnhandledException += JsonUnhandledExceptionHandler;
|
||||
|
||||
CreateMacService(tracer, args).Run();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
|
@ -45,37 +39,6 @@ namespace Scalar.Service
|
|||
}
|
||||
}
|
||||
|
||||
private static MacScalarService CreateMacService(JsonTracer tracer, string[] args)
|
||||
{
|
||||
string serviceName = args.FirstOrDefault(arg => arg.StartsWith(MacScalarService.ServiceNameArgPrefix, StringComparison.OrdinalIgnoreCase));
|
||||
if (serviceName != null)
|
||||
{
|
||||
serviceName = serviceName.Substring(MacScalarService.ServiceNameArgPrefix.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
serviceName = ScalarConstants.Service.ServiceName;
|
||||
}
|
||||
|
||||
ScalarPlatform scalarPlatform = ScalarPlatform.Instance;
|
||||
|
||||
string logFilePath = ScalarPlatform.Instance.GetLogsDirectoryForGVFSComponent(serviceName);
|
||||
Directory.CreateDirectory(logFilePath);
|
||||
|
||||
tracer.AddLogFileEventListener(
|
||||
ScalarEnlistment.GetNewScalarLogFileName(logFilePath, ScalarConstants.LogFileTypes.Service),
|
||||
EventLevel.Informational,
|
||||
Keywords.Any);
|
||||
|
||||
string repoRegistryLocation = scalarPlatform.GetCommonAppDataRootForScalarComponent(ScalarConstants.RepoRegistry.RegistryDirectoryName);
|
||||
ScalarRepoRegistry repoRegistry = new ScalarRepoRegistry(
|
||||
tracer,
|
||||
new PhysicalFileSystem(),
|
||||
repoRegistryLocation);
|
||||
|
||||
return new MacScalarService(tracer, serviceName, repoRegistry);
|
||||
}
|
||||
|
||||
private static void JsonUnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs e)
|
||||
{
|
||||
using (JsonTracer tracer = new JsonTracer(ScalarConstants.Service.ServiceName, ScalarConstants.Service.ServiceName))
|
||||
|
|
|
@ -14,7 +14,4 @@ echo Copying signed files...
|
|||
|
||||
xcopy "%SIGNDIR%\pe\scalar.dll" "%LAYOUTDIR%\usr\local\scalar\" /k/h/y
|
||||
xcopy "%SIGNDIR%\pe\scalar.common.dll" "%LAYOUTDIR%\usr\local\scalar\" /k/h/y
|
||||
xcopy "%SIGNDIR%\pe\scalar.service.dll" "%LAYOUTDIR%\usr\local\scalar\" /k/h/y
|
||||
xcopy "%SIGNDIR%\macho\scalar" "%LAYOUTDIR%\usr\local\scalar\" /k/h/y
|
||||
xcopy "%SIGNDIR%\macho\scalar.service" "%LAYOUTDIR%\usr\local\scalar\" /k/h/y
|
||||
xcopy "%SIGNDIR%\macho\Scalar.app" "%LAYOUTDIR%\Library\Application Support\Scalar\Scalar.app\" /s/h/e/k/f/c/y
|
||||
|
|
|
@ -17,7 +17,4 @@ mkdir "%SIGNDIR%\pe"
|
|||
mkdir "%SIGNDIR%\macho"
|
||||
xcopy "%LAYOUTDIR%\usr\local\scalar\scalar.dll" "%SIGNDIR%\pe" /k/h/y
|
||||
xcopy "%LAYOUTDIR%\usr\local\scalar\scalar.common.dll" "%SIGNDIR%\pe" /k/h/y
|
||||
xcopy "%LAYOUTDIR%\usr\local\scalar\scalar.service.dll" "%SIGNDIR%\pe" /k/h/y
|
||||
xcopy "%LAYOUTDIR%\usr\local\scalar\scalar" "%SIGNDIR%\macho" /k/h/y
|
||||
xcopy "%LAYOUTDIR%\usr\local\scalar\scalar.service" "%SIGNDIR%\macho" /k/h/y
|
||||
xcopy "%LAYOUTDIR%\Library\Application Support\Scalar\Scalar.app" "%SIGNDIR%\macho\Scalar.app\" /s/h/e/k/f/c/y
|
||||
|
|
|
@ -11,12 +11,9 @@
|
|||
<ItemGroup>
|
||||
<FilesToSign Include="
|
||||
$(OutDir)\pe\scalar.dll;
|
||||
$(OutDir)\pe\scalar.common.dll;
|
||||
$(OutDir)\pe\scalar.service.dll;" />
|
||||
$(OutDir)\pe\scalar.common.dll;" />
|
||||
<MacFilesToSign Include="
|
||||
$(OutDir)\macho\scalar;
|
||||
$(OutDir)\macho\scalar.service;
|
||||
$(OutDir)\macho\Scalar.app;" />
|
||||
$(OutDir)\macho\scalar;" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using Scalar.Common;
|
||||
using Scalar.Common.Maintenance;
|
||||
using Scalar.Service;
|
||||
using Scalar.UnitTests.Mock.Common;
|
||||
using Scalar.UnitTests.Mock.FileSystem;
|
||||
using System.IO;
|
||||
|
||||
namespace Scalar.UnitTests.Service.Mac
|
||||
{
|
||||
[TestFixture]
|
||||
public class MacScalarVerbRunnerTests
|
||||
{
|
||||
private const int ExpectedActiveUserId = 502;
|
||||
private static readonly string ExpectedActiveRepoPath = Path.Combine(MockFileSystem.GetMockRoot(), "code", "repo2");
|
||||
|
||||
private MockTracer tracer;
|
||||
private MockPlatform scalarPlatform;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
this.tracer = new MockTracer();
|
||||
this.scalarPlatform = (MockPlatform)ScalarPlatform.Instance;
|
||||
this.scalarPlatform.MockCurrentUser = ExpectedActiveUserId.ToString();
|
||||
}
|
||||
|
||||
[TestCase]
|
||||
public void CallMaintenance_LaunchesVerbUsingCorrectArgs()
|
||||
{
|
||||
MaintenanceTasks.Task task = MaintenanceTasks.Task.FetchCommitsAndTrees;
|
||||
string taskVerbName = MaintenanceTasks.GetVerbTaskName(task);
|
||||
string scalarBinPath = Path.Combine(this.scalarPlatform.Constants.ScalarBinDirectoryPath, this.scalarPlatform.Constants.ScalarExecutableName);
|
||||
string expectedArgs =
|
||||
$"run {taskVerbName} \"{ExpectedActiveRepoPath}\" --{ScalarConstants.VerbParameters.InternalUseOnly} {new InternalVerbParameters(startedByService: true).ToJson()}";
|
||||
|
||||
Mock<MacScalarVerbRunner.ScalarProcessLauncher> procLauncherMock = new Mock<MacScalarVerbRunner.ScalarProcessLauncher>(MockBehavior.Strict, this.tracer);
|
||||
procLauncherMock.Setup(mp => mp.LaunchProcess(
|
||||
scalarBinPath,
|
||||
expectedArgs,
|
||||
ExpectedActiveRepoPath))
|
||||
.Returns(new ProcessResult(output: string.Empty, errors: string.Empty, exitCode: 0));
|
||||
|
||||
MacScalarVerbRunner verbProcess = new MacScalarVerbRunner(this.tracer, procLauncherMock.Object);
|
||||
verbProcess.CallMaintenance(task, ExpectedActiveRepoPath, ExpectedActiveUserId);
|
||||
|
||||
procLauncherMock.VerifyAll();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -32,8 +32,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Scalar.Installer.Windows",
|
|||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Scalar.Service.UI", "Scalar.Service.UI\Scalar.Service.UI.csproj", "{32D8335E-E9E8-4AD8-BAE5-162F53AA4D72}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Scalar.Notifications.Mac", "Scalar.Notifications.Mac\Scalar.Notifications.Mac.csproj", "{ED367118-BFB1-41CA-A010-E46C28796ED8}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Scalar.Packaging.Linux", "Scalar.Packaging.Linux\Scalar.Packaging.Linux.csproj", "{AAD540B9-E65F-4C0B-916C-4CB50DA7A7DB}"
|
||||
EndProject
|
||||
Global
|
||||
|
@ -86,10 +84,6 @@ Global
|
|||
{32D8335E-E9E8-4AD8-BAE5-162F53AA4D72}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{32D8335E-E9E8-4AD8-BAE5-162F53AA4D72}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{32D8335E-E9E8-4AD8-BAE5-162F53AA4D72}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{ED367118-BFB1-41CA-A010-E46C28796ED8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{ED367118-BFB1-41CA-A010-E46C28796ED8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{ED367118-BFB1-41CA-A010-E46C28796ED8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{ED367118-BFB1-41CA-A010-E46C28796ED8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{AAD540B9-E65F-4C0B-916C-4CB50DA7A7DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{AAD540B9-E65F-4C0B-916C-4CB50DA7A7DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{AAD540B9-E65F-4C0B-916C-4CB50DA7A7DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
|
|