diff --git a/Vagrant Manager.xcodeproj/project.pbxproj b/Vagrant Manager.xcodeproj/project.pbxproj index 4f0d992..ef828c5 100644 --- a/Vagrant Manager.xcodeproj/project.pbxproj +++ b/Vagrant Manager.xcodeproj/project.pbxproj @@ -7,6 +7,9 @@ objects = { /* Begin PBXBuildFile section */ + 2609ECF51BC89CEA00E4B44E /* NFSScanner.m in Sources */ = {isa = PBXBuildFile; fileRef = 2609ECF41BC89CEA00E4B44E /* NFSScanner.m */; settings = {ASSET_TAGS = (); }; }; + 2609ECF71BC8A04600E4B44E /* rdp@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 2609ECF61BC8A04600E4B44E /* rdp@2x.png */; settings = {ASSET_TAGS = (); }; }; + 2609ECF91BC8A05100E4B44E /* rdp.png in Resources */ = {isa = PBXBuildFile; fileRef = 2609ECF81BC8A05100E4B44E /* rdp.png */; settings = {ASSET_TAGS = (); }; }; 26130E9A1B1D565200872C4F /* bm_status_icon_off.png in Resources */ = {isa = PBXBuildFile; fileRef = 26130E981B1D565200872C4F /* bm_status_icon_off.png */; }; 26130E9B1B1D565200872C4F /* bm_status_icon_off@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 26130E991B1D565200872C4F /* bm_status_icon_off@2x.png */; }; 26130E9E1B1D596400872C4F /* bm_status_icon_suspended@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 26130E9C1B1D596400872C4F /* bm_status_icon_suspended@2x.png */; }; @@ -147,6 +150,10 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 2609ECF31BC89CEA00E4B44E /* NFSScanner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NFSScanner.h; sourceTree = ""; }; + 2609ECF41BC89CEA00E4B44E /* NFSScanner.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NFSScanner.m; sourceTree = ""; }; + 2609ECF61BC8A04600E4B44E /* rdp@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "rdp@2x.png"; sourceTree = ""; }; + 2609ECF81BC8A05100E4B44E /* rdp.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = rdp.png; sourceTree = ""; }; 26130E981B1D565200872C4F /* bm_status_icon_off.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = bm_status_icon_off.png; sourceTree = ""; }; 26130E991B1D565200872C4F /* bm_status_icon_off@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bm_status_icon_off@2x.png"; sourceTree = ""; }; 26130E9C1B1D596400872C4F /* bm_status_icon_suspended@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "bm_status_icon_suspended@2x.png"; sourceTree = ""; }; @@ -399,6 +406,8 @@ 83C4CDAA1A0605B700611DCC /* Vagrant Action Icons */ = { isa = PBXGroup; children = ( + 2609ECF81BC8A05100E4B44E /* rdp.png */, + 2609ECF61BC8A04600E4B44E /* rdp@2x.png */, 83C4CDF31A060DCB00611DCC /* ssh.png */, 83C4CDEF1A060D4600611DCC /* ssh@2x.png */, 83C4CDCF1A060B3600611DCC /* up@2x.png */, @@ -422,6 +431,8 @@ 83F5BBB31983B8F200ACE853 /* Core */ = { isa = PBXGroup; children = ( + 2609ECF31BC89CEA00E4B44E /* NFSScanner.h */, + 2609ECF41BC89CEA00E4B44E /* NFSScanner.m */, 837A308B1A5D170D00E12A40 /* Custom Commands */, 83F5BBB41983B8FB00ACE853 /* Vagrant Manager */, 83F5BBB51983B90800ACE853 /* Providers */, @@ -658,7 +669,7 @@ B1357A65187CEB3B00811CBC /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 0610; + LastUpgradeCheck = 0700; ORGANIZATIONNAME = Lanayo; }; buildConfigurationList = B1357A68187CEB3B00811CBC /* Build configuration list for PBXProject "Vagrant Manager" */; @@ -689,6 +700,7 @@ 83C4CE081A0628AC00611DCC /* vagrant_logo_refresh_3-clean@2x.png in Resources */, 83C4CDE51A060B3600611DCC /* provision@2x.png in Resources */, 838A3736191F5AAB006244FA /* Askpass in Resources */, + 2609ECF71BC8A04600E4B44E /* rdp@2x.png in Resources */, 83EAC43C18D067C900FF26A1 /* vagrant_logo_on-clean.png in Resources */, 83C4CDE91A060B3600611DCC /* destroy@2x.png in Resources */, 83C4CE161A062BCF00611DCC /* vagrant_logo_refresh_2-clean.png in Resources */, @@ -708,6 +720,7 @@ B150013419249FFD000A76E7 /* status_icon_on@2x.png in Resources */, 8339DCA2187E756B0036E162 /* TaskOutputWindow.xib in Resources */, 83C4CDE41A060B3600611DCC /* reload.png in Resources */, + 2609ECF91BC8A05100E4B44E /* rdp.png in Resources */, B17AA1901887CA4600B4C274 /* PreferencesWindow.xib in Resources */, B150012E19249CA2000A76E7 /* vagrant_logo_on-flat@2x.png in Resources */, 83C4CDDE1A060B3600611DCC /* up.png in Resources */, @@ -799,6 +812,7 @@ B17AA18F1887CA4600B4C274 /* PreferencesWindow.m in Sources */, 837A30891A5D0D5500E12A40 /* ManageCustomCommandsWindow.m in Sources */, 83FD353D19826945002EEA2D /* ManageBookmarksWindow.m in Sources */, + 2609ECF51BC89CEA00E4B44E /* NFSScanner.m in Sources */, 83F1696F1923789900C54F13 /* VersionComparison.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -877,6 +891,7 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; + ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_ENABLE_OBJC_EXCEPTIONS = YES; @@ -941,6 +956,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Vagrant Manager/Vagrant Manager-Prefix.pch"; INFOPLIST_FILE = "Vagrant Manager/Vagrant Manager-Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = "lanayo.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; @@ -958,6 +974,7 @@ GCC_PRECOMPILE_PREFIX_HEADER = YES; GCC_PREFIX_HEADER = "Vagrant Manager/Vagrant Manager-Prefix.pch"; INFOPLIST_FILE = "Vagrant Manager/Vagrant Manager-Info.plist"; + PRODUCT_BUNDLE_IDENTIFIER = "lanayo.${PRODUCT_NAME:rfc1034identifier}"; PRODUCT_NAME = "$(TARGET_NAME)"; WRAPPER_EXTENSION = app; }; diff --git a/Vagrant Manager/AboutWindow.h b/Vagrant Manager/AboutWindow.h index 52284a3..120d4b7 100644 --- a/Vagrant Manager/AboutWindow.h +++ b/Vagrant Manager/AboutWindow.h @@ -9,7 +9,11 @@ #import #import "BaseWindowController.h" -@interface AboutWindow : BaseWindowController +@interface AboutWindow : BaseWindowController = __MAC_10_11 +, WebPolicyDelegate, WebFrameLoadDelegate, WebUIDelegate +#endif +> @property (weak) IBOutlet WebView *webView; diff --git a/Vagrant Manager/AppDelegate.m b/Vagrant Manager/AppDelegate.m index 96cc957..c9ebe2f 100644 --- a/Vagrant Manager/AppDelegate.m +++ b/Vagrant Manager/AppDelegate.m @@ -232,6 +232,14 @@ - (void)openInstanceInTerminal:(VagrantInstance *)instance { } } +- (void)editVagrantfile:(VagrantInstance *)instance { + //open Vagrantfile in default text editor + NSTask *task = [[NSTask alloc] init]; + [task setLaunchPath:@"/bin/bash"]; + [task setArguments:@[@"-l", @"-c", [NSString stringWithFormat:@"open -t %@", [Util escapeShellArg:[instance getVagrantfilePath]]]]]; + [task launch]; +} + - (void)addBookmarkWithInstance:(VagrantInstance *)instance { [[BookmarkManager sharedManager] addBookmarkWithPath:instance.path displayName:instance.displayName providerIdentifier:instance.providerIdentifier]; [[BookmarkManager sharedManager] saveBookmarks]; @@ -307,7 +315,20 @@ - (void)checkForVagrantUpdates:(BOOL)showAlert { } }); }); +} + +- (void)editHostsFile { + NSString *terminalEditorName = [[NSUserDefaults standardUserDefaults] valueForKey:@"terminalEditorPreference"]; + NSString *terminalEditor; + if([terminalEditorName isEqualToString:@"vim"]) { + terminalEditor = @"vim"; + } else { + terminalEditor = @"nano"; + } + + NSString *taskCommand = [NSString stringWithFormat:@"sudo %@ /etc/hosts", [Util escapeShellArg:terminalEditor]]; + [self runTerminalCommand:taskCommand]; } #pragma mark - Vagrant Machine control @@ -340,6 +361,11 @@ - (void)runVagrantAction:(NSString*)action withMachine:(VagrantMachine*)machine if(machine.instance.providerIdentifier) { [commandParts addObject:[NSString stringWithFormat:@"--provider=%@", machine.instance.providerIdentifier]]; } + } else if([action isEqualToString:@"up-provision"]) { + [commandParts addObject:@"vagrant up --provision"]; + if(machine.instance.providerIdentifier) { + [commandParts addObject:[NSString stringWithFormat:@"--provider=%@", machine.instance.providerIdentifier]]; + } } else if([action isEqualToString:@"reload"]) { [commandParts addObject:@"vagrant reload"]; } else if([action isEqualToString:@"suspend"]) { @@ -350,6 +376,8 @@ - (void)runVagrantAction:(NSString*)action withMachine:(VagrantMachine*)machine [commandParts addObject:@"vagrant provision"]; } else if([action isEqualToString:@"destroy"]) { [commandParts addObject:@"vagrant destroy -f"]; + } else if([action isEqualToString:@"rdp"]) { + [commandParts addObject:@"vagrant rdp"]; } else { return; } @@ -385,6 +413,11 @@ - (void)runVagrantAction:(NSString*)action withInstance:(VagrantInstance*)instan if(instance.providerIdentifier) { [commandParts addObject:[NSString stringWithFormat:@"--provider=%@", instance.providerIdentifier]]; } + } else if([action isEqualToString:@"up-provision"]) { + [commandParts addObject:@"vagrant up --provision"]; + if(instance.providerIdentifier) { + [commandParts addObject:[NSString stringWithFormat:@"--provider=%@", instance.providerIdentifier]]; + } } else if([action isEqualToString:@"reload"]) { [commandParts addObject:@"vagrant reload"]; } else if([action isEqualToString:@"suspend"]) { @@ -395,6 +428,8 @@ - (void)runVagrantAction:(NSString*)action withInstance:(VagrantInstance*)instan [commandParts addObject:@"vagrant provision"]; } else if([action isEqualToString:@"destroy"]) { [commandParts addObject:@"vagrant destroy -f"]; + } else if([action isEqualToString:@"rdp"]) { + [commandParts addObject:@"vagrant rdp"]; } else { return; } diff --git a/Vagrant Manager/MenuDelegate.h b/Vagrant Manager/MenuDelegate.h index 0e166dc..543b854 100644 --- a/Vagrant Manager/MenuDelegate.h +++ b/Vagrant Manager/MenuDelegate.h @@ -17,8 +17,10 @@ - (void)performCustomCommand:(CustomCommand*)customCommand withMachine:(VagrantMachine*)machine; - (void)openInstanceInFinder:(VagrantInstance*)instance; - (void)openInstanceInTerminal:(VagrantInstance*)instance; +- (void)editVagrantfile:(VagrantInstance*)instance; - (void)addBookmarkWithInstance:(VagrantInstance*)instance; - (void)removeBookmarkWithInstance:(VagrantInstance*)instance; - (void)checkForVagrantUpdates:(BOOL)showAlert; +- (void)editHostsFile; @end \ No newline at end of file diff --git a/Vagrant Manager/NFSScanner.h b/Vagrant Manager/NFSScanner.h new file mode 100644 index 0000000..827e195 --- /dev/null +++ b/Vagrant Manager/NFSScanner.h @@ -0,0 +1,14 @@ +// +// NfsScanner.h +// Vagrant Manager +// +// Copyright (c) 2014 Lanayo. All rights reserved. +// + +#import + +@interface NFSScanner : NSObject + +- (NSArray*)getNFSInstancePaths; + +@end diff --git a/Vagrant Manager/NFSScanner.m b/Vagrant Manager/NFSScanner.m new file mode 100644 index 0000000..adda4d1 --- /dev/null +++ b/Vagrant Manager/NFSScanner.m @@ -0,0 +1,49 @@ +// +// NfsScanner.m +// Vagrant Manager +// +// Copyright (c) 2014 Lanayo. All rights reserved. +// + +#import "NFSScanner.h" + +@implementation NFSScanner + +//find vagrant instances in the NFS exports file +- (NSArray*)getNFSInstancePaths { + NSMutableArray *paths = [[NSMutableArray alloc] init]; + + //get contents of /etc/exports + NSError *err; + NSString *fileContents = [NSString stringWithContentsOfFile:@"/etc/exports" encoding:NSUTF8StringEncoding error:&err]; + + if(fileContents) { + //search for vagrant NFS paths + NSMutableArray *lines = [[fileContents componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]] mutableCopy]; + [lines removeObject:@""]; + + for(NSString *line in lines) { + + if([line rangeOfString:@"# VAGRANT-"].location != NSNotFound) { + continue; + } + + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(? 1) { + NSString *path = [line substringWithRange:[pathResult rangeAtIndex:1]]; + BOOL vagrantFileExists = [[NSFileManager defaultManager] fileExistsAtPath:[NSString pathWithComponents:@[path, @"Vagrantfile"]]]; + + if(vagrantFileExists) { + [paths addObject:path]; + } + } + } + } + } + + return [NSArray arrayWithArray:paths]; +} + +@end diff --git a/Vagrant Manager/NativeMenu.m b/Vagrant Manager/NativeMenu.m index 524fb26..59ef09d 100644 --- a/Vagrant Manager/NativeMenu.m +++ b/Vagrant Manager/NativeMenu.m @@ -113,6 +113,17 @@ - (id)init { [_menu addItem:[NSMenuItem separatorItem]]; + NSMenuItem *extrasMenuItem = [[NSMenuItem alloc] initWithTitle:@"Extras" action:nil keyEquivalent:@""]; + [_menu addItem:extrasMenuItem]; + + NSMenu *extrasMenu = [[NSMenu alloc] init]; + + NSMenuItem *editHostsMenuItem = [[NSMenuItem alloc] initWithTitle:@"Edit hosts file" action:@selector(editHostsMenuItemClicked:) keyEquivalent:@""]; + editHostsMenuItem.target = self; + [extrasMenu addItem:editHostsMenuItem]; + + [extrasMenuItem setSubmenu:extrasMenu]; + NSMenuItem *preferencesMenuItem = [[NSMenuItem alloc] initWithTitle:@"Preferences" action:@selector(preferencesMenuItemClicked:) keyEquivalent:@""]; preferencesMenuItem.target = self; [_menu addItem:preferencesMenuItem]; @@ -280,8 +291,12 @@ - (void)updateRefreshIcon:(id)sender { #pragma mark - Native menu item delegate -- (void)nativeMenuItemUpAllMachines:(NativeMenuItem *)menuItem { - [self performAction:@"up" withInstance:menuItem.instance]; +- (void)nativeMenuItemUpAllMachines:(NativeMenuItem *)menuItem withProvision:(BOOL)provision { + if(provision) { + [self performAction:@"up-provision" withInstance:menuItem.instance]; + } else { + [self performAction:@"up" withInstance:menuItem.instance]; + } } - (void)nativeMenuItemHaltAllMachines:(NativeMenuItem *)menuItem { @@ -292,6 +307,10 @@ - (void)nativeMenuItemSSHInstance:(NativeMenuItem*)menuItem { [self performAction:@"ssh" withInstance:menuItem.instance]; } +- (void)nativeMenuItemRDPInstance:(NativeMenuItem*)menuItem { + [self performAction:@"rdp" withInstance:menuItem.instance]; +} + - (void)nativeMenuItemReloadAllMachines:(NativeMenuItem*)menuItem { [self performAction:@"reload" withInstance:menuItem.instance]; } @@ -325,6 +344,10 @@ - (void)nativeMenuItemOpenTerminal:(NativeMenuItem*)menuItem { [self.delegate openInstanceInTerminal:menuItem.instance]; } +- (void)nativeMenuItemEditVagrantfile:(NativeMenuItem *)menuItem { + [self.delegate editVagrantfile:menuItem.instance]; +} + - (void)nativeMenuItemUpdateProviderIdentifier:(NativeMenuItem*)menuItem withProviderIdentifier:(NSString*)providerIdentifier { VagrantInstance *instance = menuItem.instance; @@ -347,8 +370,12 @@ - (void)nativeMenuItemAddBookmark:(NativeMenuItem*)menuItem { [self.delegate addBookmarkWithInstance:menuItem.instance]; } -- (void)nativeMenuItemUpMachine:(VagrantMachine *)machine { - [self performAction:@"up" withMachine:machine]; +- (void)nativeMenuItemUpMachine:(VagrantMachine *)machine withProvision:(BOOL)provision { + if(provision) { + [self performAction:@"up-provision" withMachine:machine]; + } else { + [self performAction:@"up" withMachine:machine]; + } } - (void)nativeMenuItemHaltMachine:(VagrantMachine *)machine { @@ -359,6 +386,10 @@ - (void)nativeMenuItemSSHMachine:(VagrantMachine*)machine { [self performAction:@"ssh" withMachine:machine]; } +- (void)nativeMenuItemRDPMachine:(VagrantMachine*)machine { + [self performAction:@"rdp" withMachine:machine]; +} + - (void)nativeMenuItemReloadMachine:(VagrantMachine *)machine { [self performAction:@"reload" withMachine:machine]; } @@ -414,6 +445,10 @@ - (void)manageCustomCommandsMenuItemClicked:(id)sender { } } +- (void)editHostsMenuItemClicked:(id)sender { + [self.delegate editHostsFile]; +} + - (void)preferencesMenuItemClicked:(id)sender { if(preferencesWindow && !preferencesWindow.isClosed) { [NSApp activateIgnoringOtherApps:YES]; diff --git a/Vagrant Manager/NativeMenuItem.h b/Vagrant Manager/NativeMenuItem.h index dfd5f0e..cf88e92 100644 --- a/Vagrant Manager/NativeMenuItem.h +++ b/Vagrant Manager/NativeMenuItem.h @@ -13,8 +13,9 @@ @protocol NativeMenuItemDelegate -- (void)nativeMenuItemUpAllMachines:(NativeMenuItem*)menuItem; +- (void)nativeMenuItemUpAllMachines:(NativeMenuItem*)menuItem withProvision:(BOOL)provision; - (void)nativeMenuItemSSHInstance:(NativeMenuItem*)menuItem; +- (void)nativeMenuItemRDPInstance:(NativeMenuItem*)menuItem; - (void)nativeMenuItemSuspendAllMachines:(NativeMenuItem*)menuItem; - (void)nativeMenuItemReloadAllMachines:(NativeMenuItem*)menuItem; - (void)nativeMenuItemHaltAllMachines:(NativeMenuItem*)menuItem; @@ -23,12 +24,14 @@ - (void)nativeMenuItemCustomCommandAllMachines:(NativeMenuItem*)menuItem withCommand:(CustomCommand*)customCommand; - (void)nativeMenuItemOpenFinder:(NativeMenuItem*)menuItem; - (void)nativeMenuItemOpenTerminal:(NativeMenuItem*)menuItem; +- (void)nativeMenuItemEditVagrantfile:(NativeMenuItem*)menuItem; - (void)nativeMenuItemUpdateProviderIdentifier:(NativeMenuItem*)menuItem withProviderIdentifier:(NSString*)providerIdentifier; - (void)nativeMenuItemRemoveBookmark:(NativeMenuItem*)menuItem; - (void)nativeMenuItemAddBookmark:(NativeMenuItem*)menuItem; -- (void)nativeMenuItemUpMachine:(VagrantMachine*)machine; +- (void)nativeMenuItemUpMachine:(VagrantMachine*)machine withProvision:(BOOL)provision; - (void)nativeMenuItemSSHMachine:(VagrantMachine*)machine; +- (void)nativeMenuItemRDPMachine:(VagrantMachine*)machine; - (void)nativeMenuItemSuspendMachine:(VagrantMachine*)machine; - (void)nativeMenuItemReloadMachine:(VagrantMachine*)machine; - (void)nativeMenuItemHaltMachine:(VagrantMachine*)machine; diff --git a/Vagrant Manager/NativeMenuItem.m b/Vagrant Manager/NativeMenuItem.m index 618b759..7beb329 100644 --- a/Vagrant Manager/NativeMenuItem.m +++ b/Vagrant Manager/NativeMenuItem.m @@ -11,14 +11,18 @@ @implementation NativeMenuItem { NSMenuItem *_instanceUpMenuItem; + NSMenuItem *_instanceUpProvisionMenuItem; NSMenuItem *_sshMenuItem; + NSMenuItem *_rdpMenuItem; NSMenuItem *_instanceReloadMenuItem; NSMenuItem *_instanceSuspendMenuItem; NSMenuItem *_instanceHaltMenuItem; + NSMenuItem *_instanceDestroyMenuItemPlaceholder; NSMenuItem *_instanceDestroyMenuItem; NSMenuItem *_instanceProvisionMenuItem; NSMenuItem *_instanceCustomCommandMenuItem; + NSMenuItem *_editVagrantfileMenuItem; NSMenuItem *_openInFinderMenuItem; NSMenuItem *_openInTerminalMenuItem; NSMenuItem *_addBookmarkMenuItem; @@ -64,6 +68,14 @@ - (void)refresh { [self.menuItem.submenu addItem:_instanceUpMenuItem]; } + if(!_instanceUpProvisionMenuItem) { + _instanceUpProvisionMenuItem = [[NSMenuItem alloc] initWithTitle:self.instance.machines.count > 1 ? @"Up All (with provision)" : @"Up (with provision)" action:@selector(upProvisionAllMachines:) keyEquivalent:@""]; + _instanceUpProvisionMenuItem.target = self; + _instanceUpProvisionMenuItem.image = [NSImage imageNamed:@"up"]; + [_instanceUpProvisionMenuItem.image setTemplate:YES]; + [self.menuItem.submenu addItem:_instanceUpProvisionMenuItem]; + } + if(!_sshMenuItem) { _sshMenuItem = [[NSMenuItem alloc] initWithTitle:@"SSH" action:@selector(sshInstance:) keyEquivalent:@""]; _sshMenuItem.target = self; @@ -71,7 +83,15 @@ - (void)refresh { [_sshMenuItem.image setTemplate:YES]; [self.menuItem.submenu addItem:_sshMenuItem]; } - + + if(!_rdpMenuItem) { + _rdpMenuItem = [[NSMenuItem alloc] initWithTitle:@"RDP" action:@selector(rdpInstance:) keyEquivalent:@""]; + _rdpMenuItem.target = self; + _rdpMenuItem.image = [NSImage imageNamed:@"rdp"]; + [_rdpMenuItem.image setTemplate:YES]; + [self.menuItem.submenu addItem:_rdpMenuItem]; + } + if(!_instanceReloadMenuItem) { _instanceReloadMenuItem = [[NSMenuItem alloc] initWithTitle:self.instance.machines.count > 1 ? @"Reload All" : @"Reload" action:@selector(reloadAllMachines:) keyEquivalent:@""]; _instanceReloadMenuItem.target = self; @@ -96,6 +116,20 @@ - (void)refresh { [self.menuItem.submenu addItem:_instanceHaltMenuItem]; } + BOOL optionKeyDestroy = [[NSUserDefaults standardUserDefaults] boolForKey:@"optionKeyDestroy"]; + + if(!_instanceDestroyMenuItemPlaceholder) { + _instanceDestroyMenuItemPlaceholder = [[NSMenuItem alloc] initWithTitle:self.instance.machines.count > 1 ? @"Destroy All" : @"Destroy" action:nil keyEquivalent:@""]; + _instanceDestroyMenuItemPlaceholder.image = [NSImage imageNamed:@"destroy"]; + [_instanceDestroyMenuItemPlaceholder.image setTemplate:YES]; + _instanceDestroyMenuItemPlaceholder.enabled = NO; + [self.menuItem.submenu addItem:_instanceDestroyMenuItemPlaceholder]; + } + + if(!optionKeyDestroy) { + _instanceDestroyMenuItemPlaceholder.hidden = YES; + } + if(!_instanceDestroyMenuItem) { _instanceDestroyMenuItem = [[NSMenuItem alloc] initWithTitle:self.instance.machines.count > 1 ? @"Destroy All" : @"Destroy" action:@selector(destroyAllMachines:) keyEquivalent:@""]; _instanceDestroyMenuItem.target = self; @@ -104,6 +138,13 @@ - (void)refresh { [self.menuItem.submenu addItem:_instanceDestroyMenuItem]; } + if(optionKeyDestroy) { + _instanceDestroyMenuItem.alternate = YES; + _instanceDestroyMenuItem.keyEquivalentModifierMask = NSAlternateKeyMask; + } else { + _instanceDestroyMenuItem.alternate = NO; + } + if(!_instanceProvisionMenuItem) { _instanceProvisionMenuItem = [[NSMenuItem alloc] initWithTitle:self.instance.machines.count > 1 ? @"Provision All" : @"Provision" action:@selector(provisionAllMachines:) keyEquivalent:@""]; _instanceProvisionMenuItem.target = self; @@ -142,6 +183,12 @@ - (void)refresh { [self.menuItem.submenu addItem:[NSMenuItem separatorItem]]; } + if (!_editVagrantfileMenuItem) { + _editVagrantfileMenuItem = [[NSMenuItem alloc] initWithTitle:@"Edit Vagrantfile" action:@selector(editVagrantfileMenuItemClicked:) keyEquivalent:@""]; + _editVagrantfileMenuItem.target = self; + [self.menuItem.submenu addItem:_editVagrantfileMenuItem]; + } + if (!_openInFinderMenuItem) { _openInFinderMenuItem = [[NSMenuItem alloc] initWithTitle:@"Open in Finder" action:@selector(finderMenuItemClicked:) keyEquivalent:@""]; _openInFinderMenuItem.target = self; @@ -198,7 +245,9 @@ - (void)refresh { if([self.instance getRunningMachineCount] < self.instance.machines.count) { [_instanceUpMenuItem setHidden:NO]; + [_instanceUpProvisionMenuItem setHidden:NO]; [_sshMenuItem setHidden:YES]; + [_rdpMenuItem setHidden:YES]; [_instanceReloadMenuItem setHidden:YES]; [_instanceSuspendMenuItem setHidden:YES]; [_instanceHaltMenuItem setHidden:YES]; @@ -208,7 +257,9 @@ - (void)refresh { if([self.instance getRunningMachineCount] > 0) { [_instanceUpMenuItem setHidden:YES]; + [_instanceUpProvisionMenuItem setHidden:YES]; [_sshMenuItem setHidden:NO]; + [_rdpMenuItem setHidden:NO]; [_instanceReloadMenuItem setHidden:NO]; [_instanceSuspendMenuItem setHidden:NO]; [_instanceHaltMenuItem setHidden:NO]; @@ -223,6 +274,7 @@ - (void)refresh { if (self.instance.machines.count > 1) { [_sshMenuItem setHidden:YES]; + [_rdpMenuItem setHidden:YES]; } if([[BookmarkManager sharedManager] getBookmarkWithPath:self.instance.path]) { @@ -284,6 +336,13 @@ - (void)refresh { [machineUpMenuItem.image setTemplate:YES]; [machineSubmenu addItem:machineUpMenuItem]; + NSMenuItem *machineUpProvisionMenuItem = [[NSMenuItem alloc] initWithTitle:@"Up (with provision)" action:@selector(upProvisionMachine:) keyEquivalent:@""]; + machineUpProvisionMenuItem.target = self; + machineUpProvisionMenuItem.representedObject = machine; + machineUpProvisionMenuItem.image = [NSImage imageNamed:@"up"]; + [machineUpProvisionMenuItem.image setTemplate:YES]; + [machineSubmenu addItem:machineUpProvisionMenuItem]; + NSMenuItem *machineSSHMenuItem = [[NSMenuItem alloc] initWithTitle:@"SSH" action:@selector(sshMachine:) keyEquivalent:@""]; machineSSHMenuItem.target = self; machineSSHMenuItem.representedObject = machine; @@ -291,6 +350,13 @@ - (void)refresh { [machineSSHMenuItem.image setTemplate:YES]; [machineSubmenu addItem:machineSSHMenuItem]; + NSMenuItem *machineRDPMenuItem = [[NSMenuItem alloc] initWithTitle:@"RDP" action:@selector(rdpMachine:) keyEquivalent:@""]; + machineRDPMenuItem.target = self; + machineRDPMenuItem.representedObject = machine; + machineRDPMenuItem.image = [NSImage imageNamed:@"rdp"]; + [machineRDPMenuItem.image setTemplate:YES]; + [machineSubmenu addItem:machineRDPMenuItem]; + NSMenuItem *machineReloadMenuItem = [[NSMenuItem alloc] initWithTitle:@"Reload" action:@selector(reloadMachine:) keyEquivalent:@""]; machineReloadMenuItem.target = self; machineReloadMenuItem.representedObject = machine; @@ -357,7 +423,9 @@ - (void)refresh { if(machine.state == RunningState) { [machineUpMenuItem setHidden:YES]; + [machineUpProvisionMenuItem setHidden:YES]; [machineSSHMenuItem setHidden:NO]; + [machineRDPMenuItem setHidden:NO]; [machineReloadMenuItem setHidden:NO]; [machineSuspendMenuItem setHidden:NO]; [machineHaltMenuItem setHidden:NO]; @@ -365,7 +433,9 @@ - (void)refresh { [machineCustomCommandMenuItem setHidden:NO]; } else { [machineUpMenuItem setHidden:NO]; + [machineUpProvisionMenuItem setHidden:NO]; [machineSSHMenuItem setHidden:YES]; + [machineRDPMenuItem setHidden:YES]; [machineReloadMenuItem setHidden:YES]; [machineSuspendMenuItem setHidden:YES]; [machineHaltMenuItem setHidden:YES]; @@ -383,13 +453,21 @@ - (void)refresh { } - (void)upAllMachines:(NSMenuItem*)sender { - [self.delegate nativeMenuItemUpAllMachines:self]; + [self.delegate nativeMenuItemUpAllMachines:self withProvision:NO]; +} + +- (void)upProvisionAllMachines:(NSMenuItem*)sender { + [self.delegate nativeMenuItemUpAllMachines:self withProvision:YES]; } - (void)sshInstance:(NSMenuItem*)sender { [self.delegate nativeMenuItemSSHInstance:self]; } +- (void)rdpInstance:(NSMenuItem*)sender { + [self.delegate nativeMenuItemRDPInstance:self]; +} + - (void)reloadAllMachines:(NSMenuItem*)sender { [self.delegate nativeMenuItemReloadAllMachines:self]; } @@ -418,6 +496,10 @@ - (void)finderMenuItemClicked:(NSMenuItem*)sender { [self.delegate nativeMenuItemOpenFinder:self]; } +- (void)editVagrantfileMenuItemClicked:(NSMenuItem*)sender { + [self.delegate nativeMenuItemEditVagrantfile:self]; +} + - (void)terminalMenuItemClicked:(NSMenuItem*)sender { [self.delegate nativeMenuItemOpenTerminal:self]; } @@ -435,13 +517,21 @@ - (void)addBookmarkMenuItemClicked:(NSMenuItem*)sender { } - (void)upMachine:(NSMenuItem*)sender { - [self.delegate nativeMenuItemUpMachine:sender.representedObject]; + [self.delegate nativeMenuItemUpMachine:sender.representedObject withProvision:NO]; +} + +- (void)upProvisionMachine:(NSMenuItem*)sender { + [self.delegate nativeMenuItemUpMachine:sender.representedObject withProvision:YES]; } - (void)sshMachine:(NSMenuItem*)sender { [self.delegate nativeMenuItemSSHMachine:sender.representedObject]; } +- (void)rdpMachine:(NSMenuItem*)sender { + [self.delegate nativeMenuItemRDPMachine:sender.representedObject]; +} + - (void)reloadMachine:(NSMenuItem*)sender { [self.delegate nativeMenuItemReloadMachine:sender.representedObject]; } @@ -466,4 +556,17 @@ - (void)customCommandMachine:(NSMenuItem*)sender { [self.delegate nativeMenuItemCustomCommandMachine:[sender.representedObject objectForKey:@"machine"] withCommand:[sender.representedObject objectForKey:@"command"]]; } +- (void)menuWillOpen:(NSMenu *)menu { + BOOL optionKeyDestroy = [[NSUserDefaults standardUserDefaults] boolForKey:@"optionKeyDestroy"]; + + if(optionKeyDestroy) { + _instanceDestroyMenuItemPlaceholder.hidden = NO; + _instanceDestroyMenuItem.alternate = YES; + _instanceDestroyMenuItem.keyEquivalentModifierMask = NSAlternateKeyMask; + } else { + _instanceDestroyMenuItem.alternate = NO; + _instanceDestroyMenuItemPlaceholder.hidden = YES; + } +} + @end diff --git a/Vagrant Manager/PreferencesWindow.h b/Vagrant Manager/PreferencesWindow.h index e30705c..673f373 100644 --- a/Vagrant Manager/PreferencesWindow.h +++ b/Vagrant Manager/PreferencesWindow.h @@ -14,10 +14,12 @@ @property (weak) IBOutlet NSButton *autoCloseCheckBox; @property (weak) IBOutlet NSButton *dontShowUpdateCheckBox; +@property (weak) IBOutlet NSButton *optionKeyDestroyCheckBox; @property (weak) IBOutlet NSButton *usePathAsInstanceDisplayNameCheckBox; @property (weak) IBOutlet NSButton *includeMachineNamesCheckBox; @property (weak) IBOutlet NSButton *dontShowRunningVmCountCheckBox; @property (weak) IBOutlet NSPopUpButton *terminalPreferencePopUpButton; +@property (weak) IBOutlet NSPopUpButton *terminalEditorPreferencePopUpButton; @property (weak) IBOutlet NSPopUpButton *statusBarIconThemePopUpButton; @property (weak) IBOutlet NSPopUpButton *updateStabilityPopUpButton; @property (weak) IBOutlet NSButton *sendProfileDataCheckBox; @@ -30,6 +32,7 @@ - (IBAction)autoCloseCheckBoxClicked:(id)sender; - (IBAction)dontShowUpdateCheckBoxClicked:(id)sender; +- (IBAction)optionKeyDestroyCheckBoxClicked:(id)sender; - (IBAction)usePathAsInstanceDisplayNameCheckBoxClicked:(id)sender; - (IBAction)includeMachineNamesCheckBoxClicked:(id)sender; - (IBAction)dontShowRunningVmCountCheckBoxClicked:(id)sender; diff --git a/Vagrant Manager/PreferencesWindow.m b/Vagrant Manager/PreferencesWindow.m index f1b4ee9..3c1126b 100644 --- a/Vagrant Manager/PreferencesWindow.m +++ b/Vagrant Manager/PreferencesWindow.m @@ -23,8 +23,10 @@ - (void)windowDidLoad { [super windowDidLoad]; NSString *terminalPreference = [[NSUserDefaults standardUserDefaults] stringForKey:@"terminalPreference"]; + NSString *terminalEditorPreference = [[NSUserDefaults standardUserDefaults] stringForKey:@"terminalEditorPreference"]; BOOL autoCloseTaskWindows = [[NSUserDefaults standardUserDefaults] boolForKey:@"autoCloseTaskWindows"]; BOOL dontShowUpdateNotification = [[NSUserDefaults standardUserDefaults] boolForKey:@"dontShowUpdateNotification"]; + BOOL optionKeyDestroy = [[NSUserDefaults standardUserDefaults] boolForKey:@"optionKeyDestroy"]; BOOL usePathAsInstanceDisplayName = [[NSUserDefaults standardUserDefaults] boolForKey:@"usePathAsInstanceDisplayName"]; BOOL includeMachineNames = [[NSUserDefaults standardUserDefaults] boolForKey:@"includeMachineNamesInMenu"]; BOOL dontShowRunningVmCount = [[NSUserDefaults standardUserDefaults] boolForKey:@"dontShowRunningVmCount"]; @@ -47,6 +49,12 @@ - (void)windowDidLoad { [self.terminalPreferencePopUpButton selectItemWithTag:100]; } + if ([terminalEditorPreference isEqualToString:@"vim"]) { + [self.terminalEditorPreferencePopUpButton selectItemWithTag:101]; + } else { + [self.terminalEditorPreferencePopUpButton selectItemWithTag:100]; + } + if([updateStability isEqualToString:@"rc"]) { [self.updateStabilityPopUpButton selectItemWithTag:101]; } else if([updateStability isEqualToString:@"beta"]) { @@ -61,6 +69,7 @@ - (void)windowDidLoad { [self.autoCloseCheckBox setState:autoCloseTaskWindows ? NSOnState : NSOffState]; [self.dontShowUpdateCheckBox setState:dontShowUpdateNotification ? NSOnState : NSOffState]; + [self.optionKeyDestroyCheckBox setState:optionKeyDestroy ? NSOnState : NSOffState]; [self.usePathAsInstanceDisplayNameCheckBox setState:usePathAsInstanceDisplayName ? NSOnState : NSOffState]; [self.includeMachineNamesCheckBox setState:includeMachineNames ? NSOnState : NSOffState]; [self.dontShowRunningVmCountCheckBox setState:dontShowRunningVmCount ? NSOnState : NSOffState]; @@ -85,6 +94,11 @@ - (IBAction)dontShowUpdateCheckBoxClicked:(id)sender { [[NSNotificationCenter defaultCenter] postNotificationName:@"vagrant-manager.show-update-notification-preference-changed" object:nil]; } +- (IBAction)optionKeyDestroyCheckBoxClicked:(id)sender { + [[NSUserDefaults standardUserDefaults] setBool:(self.optionKeyDestroyCheckBox.state == NSOnState) forKey:@"optionKeyDestroy"]; + [[NSUserDefaults standardUserDefaults] synchronize]; +} + - (IBAction)usePathAsInstanceDisplayNameCheckBoxClicked:(id)sender { [[NSUserDefaults standardUserDefaults] setBool:(self.usePathAsInstanceDisplayNameCheckBox.state == NSOnState) forKey:@"usePathAsInstanceDisplayName"]; [[NSUserDefaults standardUserDefaults] synchronize]; @@ -119,6 +133,19 @@ - (IBAction)terminalPreferencePopUpButtonClicked:(id)sender { [[NSUserDefaults standardUserDefaults] synchronize]; } +- (IBAction)terminalEditorPreferencePopUpButtonClicked:(id)sender { + NSString *terminalEditorPreference; + + if (self.terminalPreferencePopUpButton.selectedItem.tag == 101) { + terminalEditorPreference = @"vim"; + } else { + terminalEditorPreference = @"nano"; + } + + [[NSUserDefaults standardUserDefaults] setValue:terminalEditorPreference forKey:@"terminalEditorPreference"]; + [[NSUserDefaults standardUserDefaults] synchronize]; +} + - (IBAction)statusBarIconThemePopUpButtonClicked:(id)sender { NSString *statusBarIconTheme; diff --git a/Vagrant Manager/PreferencesWindow.xib b/Vagrant Manager/PreferencesWindow.xib index f4e2d41..8f24074 100644 --- a/Vagrant Manager/PreferencesWindow.xib +++ b/Vagrant Manager/PreferencesWindow.xib @@ -1,8 +1,9 @@ - + - + + @@ -14,10 +15,12 @@ + + @@ -28,22 +31,23 @@ - + - + - + - - + + - + + @@ -51,8 +55,9 @@ - + + @@ -68,8 +73,9 @@ - + + @@ -77,8 +83,9 @@ - + + @@ -98,8 +105,9 @@ - + + @@ -107,8 +115,9 @@ - + + @@ -133,8 +142,9 @@ - + + @@ -208,8 +224,9 @@ - + + @@ -217,8 +234,9 @@ - + + @@ -237,8 +256,9 @@ - + + @@ -257,8 +278,9 @@ - + + @@ -266,8 +288,9 @@ - + + @@ -299,8 +323,9 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + diff --git a/Vagrant Manager/Vagrant Manager-Info.plist b/Vagrant Manager/Vagrant Manager-Info.plist index 44c7e92..3275c0e 100644 --- a/Vagrant Manager/Vagrant Manager-Info.plist +++ b/Vagrant Manager/Vagrant Manager-Info.plist @@ -9,7 +9,7 @@ CFBundleIconFile CFBundleIdentifier - lanayo.${PRODUCT_NAME:rfc1034identifier} + $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2.4.4 + 2.5.0 CFBundleSignature ???? CFBundleVersion - 2.4.4 + 2.5.0 Configuration ${CONFIGURATION} LSApplicationCategoryType @@ -30,6 +30,11 @@ ${MACOSX_DEPLOYMENT_TARGET} LSUIElement + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + NSHumanReadableCopyright Copyright © 2014 Lanayo Technology NSMainNibFile diff --git a/Vagrant Manager/VagrantGlobalStatusScanner.h b/Vagrant Manager/VagrantGlobalStatusScanner.h index ae0f00a..749d01a 100644 --- a/Vagrant Manager/VagrantGlobalStatusScanner.h +++ b/Vagrant Manager/VagrantGlobalStatusScanner.h @@ -9,6 +9,6 @@ @interface VagrantGlobalStatusScanner : NSObject -- (NSMutableDictionary*)getInstances; +- (NSArray*)getInstancePaths; @end diff --git a/Vagrant Manager/VagrantGlobalStatusScanner.m b/Vagrant Manager/VagrantGlobalStatusScanner.m index d6571c7..feaac73 100644 --- a/Vagrant Manager/VagrantGlobalStatusScanner.m +++ b/Vagrant Manager/VagrantGlobalStatusScanner.m @@ -10,13 +10,13 @@ @implementation VagrantGlobalStatusScanner //find vagrant instances listed in the vagrant global-status command output -- (NSMutableDictionary*)getInstances { - NSMutableDictionary *instancePathDict = [NSMutableDictionary dictionary]; +- (NSArray*)getInstancePaths { + NSMutableArray *paths = [[NSMutableArray alloc] init]; //get output of vagrant global-status NSTask *task = [[NSTask alloc] init]; [task setLaunchPath:@"/bin/bash"]; - [task setArguments:@[@"-l", @"-c", @"vagrant global-status --prune 2> /dev/null"]]; + [task setArguments:@[@"-l", @"-c", @"vagrant global-status"]]; NSPipe *pipe = [NSPipe pipe]; [task setStandardInput:[NSPipe pipe]]; @@ -30,71 +30,27 @@ - (NSMutableDictionary*)getInstances { NSData *outputData = [[pipe fileHandleForReading] readDataToEndOfFile]; NSString *outputString = [[NSString alloc] initWithData:outputData encoding:NSUTF8StringEncoding]; - NSRange headerRange = [[NSRegularExpression regularExpressionWithPattern:@"^id\\s+name\\s+provider\\s+state\\s+directory" options:NSRegularExpressionAnchorsMatchLines error:nil] rangeOfFirstMatchInString:outputString options:0 range:NSMakeRange(0, outputString.length)]; - - if (headerRange.location == NSNotFound) { - dispatch_async(dispatch_get_main_queue(), ^{ - [[Util getApp] showNotificationWithTitle:@"Invalid Output" informativeText:@"`vagrant global-status --prune` output invalid, run command in terminal window to verify" taskWindowUUID:nil]; - }); - - return instancePathDict; - } - - NSString *header = [outputString substringWithRange:headerRange]; - - NSRange range = [header rangeOfString:@"name"]; - NSInteger nameIndex = (unsigned long)range.location; - - range = [header rangeOfString:@"provider"]; - NSInteger providerIndex = (unsigned long)range.location; - - range = [header rangeOfString:@"state"]; - NSInteger stateIndex = (unsigned long)range.location; - - range = [header rangeOfString:@"directory"]; - NSInteger directoryIndex = (unsigned long)range.location; - //search for machine state in output string - NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^([a-z0-9]{7}\\s.*)" options:NSRegularExpressionAnchorsMatchLines error:NULL]; + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\/.*)(\\n|$)" options:0 error:NULL]; NSArray *matches = [regex matchesInString:outputString options:0 range:NSMakeRange(0, [outputString length])]; - for(NSTextCheckingResult *match in matches) { - NSRange matchRange = [match rangeAtIndex:1]; - NSString *line = [[outputString substringWithRange:matchRange] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; - - NSString *machineName = [[line substringWithRange: NSMakeRange(nameIndex, providerIndex-nameIndex)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; - - NSString *machineProvider = [[line substringWithRange: NSMakeRange(providerIndex, stateIndex-providerIndex)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; - - NSString *machineState = [[line substringWithRange: NSMakeRange(stateIndex, directoryIndex-stateIndex)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + //NSRange idRange = [match rangeAtIndex:1]; + //NSRange nameRange = [match rangeAtIndex:2]; + //NSRange providerRange = [match rangeAtIndex:3]; + //NSRange stateRange = [match rangeAtIndex:4]; + NSRange pathRange = [match rangeAtIndex:5]; - NSString *machineDirectory = [[line substringWithRange: NSMakeRange(directoryIndex, line.length-directoryIndex)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + NSString *path = [[outputString substringWithRange:pathRange] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; - BOOL vagrantFileExists = [[NSFileManager defaultManager] fileExistsAtPath:[NSString pathWithComponents:@[machineDirectory, @"/Vagrantfile"]]]; + BOOL vagrantFileExists = [[NSFileManager defaultManager] fileExistsAtPath:[NSString pathWithComponents:@[path, @"/Vagrantfile"]]]; if(vagrantFileExists) { - VagrantInstance *instance = nil; - - if ([instancePathDict objectForKey:machineDirectory]) { - instance = [instancePathDict valueForKey:machineDirectory]; - } else { - instance = [[VagrantInstance alloc] initWithPath:machineDirectory providerIdentifier:machineProvider]; - } - - VagrantMachine *machine = [[VagrantMachine alloc] initWithInstance:instance name:machineName state:UnknownState]; - machine.stateString = machineState; - - [instance.machines addObject:machine]; - instancePathDict[machineDirectory] = instance; + [paths addObject:path]; } } - } else { - dispatch_async(dispatch_get_main_queue(), ^{ - [[Util getApp] showNotificationWithTitle:@"Refresh Error" informativeText:@"`vagrant global-status --prune` command encountered an error, run command in a terminal window to debug" taskWindowUUID:nil]; - }); } - return instancePathDict; + return [NSArray arrayWithArray:paths]; } @end diff --git a/Vagrant Manager/VagrantInstance.h b/Vagrant Manager/VagrantInstance.h index 6cabc7f..a5d9575 100644 --- a/Vagrant Manager/VagrantInstance.h +++ b/Vagrant Manager/VagrantInstance.h @@ -12,17 +12,19 @@ @interface VagrantInstance : NSObject @property (readonly) NSString *path; -@property (strong, nonatomic) NSString *displayName; -@property (strong, nonatomic) NSMutableArray *machines; +@property (readonly) NSString *displayName; +@property (readonly) NSArray *machines; @property (strong, nonatomic) NSString *providerIdentifier; - (id)initWithPath:(NSString*)path providerIdentifier:(NSString*)providerIdentifier; - (id)initWithPath:(NSString*)path displayName:(NSString*)displayName providerIdentifier:(NSString*)providerIdentifier; - (VagrantMachine*)getMachineWithName:(NSString*)name; +- (void)queryMachines; - (int)getRunningMachineCount; - (int)getMachineCountWithState:(VagrantMachineState)state; - (BOOL)hasVagrantfile; +- (NSString*)getVagrantfilePath; - (NSString*)getPath; diff --git a/Vagrant Manager/VagrantInstance.m b/Vagrant Manager/VagrantInstance.m index 3616334..e48f674 100644 --- a/Vagrant Manager/VagrantInstance.m +++ b/Vagrant Manager/VagrantInstance.m @@ -90,6 +90,47 @@ - (VagrantMachine*)getMachineWithName:(NSString*)name { return nil; } +//queries to state of all machines in this instance +- (void)queryMachines { + NSMutableArray *machines = [[NSMutableArray alloc] init]; + + if([self hasVagrantfile]) { + //get output of vagrant status + NSTask *task = [[NSTask alloc] init]; + [task setLaunchPath:@"/bin/bash"]; + [task setArguments:@[@"-l", @"-c", [NSString stringWithFormat:@"cd %@; vagrant status", [Util escapeShellArg:_path]]]]; + + NSPipe *pipe = [NSPipe pipe]; + [task setStandardInput:[NSPipe pipe]]; + [task setStandardOutput:pipe]; + + [task launch]; + [task waitUntilExit]; + + if(task.terminationStatus == 0) { + NSData *outputData = [[pipe fileHandleForReading] readDataToEndOfFile]; + NSString *outputString = [[NSString alloc] initWithData:outputData encoding:NSUTF8StringEncoding]; + + //search for machine state in output string + NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"([\\w-_\\.]+)\\s+([\\w\\s]+) \\(\\w+\\)" options:0 error:NULL]; + NSArray *matches = [regex matchesInString:outputString options:0 range:NSMakeRange(0, [outputString length])]; + for(NSTextCheckingResult *match in matches) { + NSRange nameRange = [match rangeAtIndex:1]; + NSRange stateRange = [match rangeAtIndex:2]; + + NSString *name = [[outputString substringWithRange:nameRange] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + NSString *stateString = [[outputString substringWithRange:stateRange] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; + + VagrantMachine *machine = [[VagrantMachine alloc] initWithInstance:self name:name state:UnknownState]; + machine.stateString = stateString; + [machines addObject:machine]; + } + } + } + + _machines = machines; +} + - (NSArray*)getMachines { return [NSArray arrayWithArray:_machines]; } diff --git a/Vagrant Manager/VagrantManager.m b/Vagrant Manager/VagrantManager.m index c0cd4ea..5a409ac 100644 --- a/Vagrant Manager/VagrantManager.m +++ b/Vagrant Manager/VagrantManager.m @@ -6,6 +6,7 @@ // #import "VagrantManager.h" +#import "NFSScanner.h" #import "VagrantGlobalStatusScanner.h" #import "BookmarkManager.h" @@ -95,33 +96,63 @@ - (VagrantInstance*)getInstanceForPath:(NSString*)path { //refresh list of instances by querying bookmarks, service providers, and NFS - (void)refreshInstances { + NSMutableArray *instances = [[NSMutableArray alloc] init]; + + BookmarkManager *bookmarkManager = [BookmarkManager sharedManager]; + + //create instance for each bookmark + NSMutableArray *bookmarks = [[BookmarkManager sharedManager] getBookmarks]; + for(Bookmark *bookmark in bookmarks) { + [instances addObject:[[VagrantInstance alloc] initWithPath:bookmark.path displayName:bookmark.displayName providerIdentifier:bookmark.providerIdentifier]]; + } + + NSMutableArray *allPaths = [[NSMutableArray alloc] init]; + + //scan for NFS exports + NFSScanner *nfsScanner = [[NFSScanner alloc] init]; + for(NSString *path in [nfsScanner getNFSInstancePaths]) { + //make sure it is not a bookmark and has not already been detected + if(![bookmarkManager getBookmarkWithPath:path] && ![allPaths containsObject:path]) { + [allPaths addObject:path]; + [instances addObject:[[VagrantInstance alloc] initWithPath:path providerIdentifier:nil]]; + } + } //scan vagrant global-status output VagrantGlobalStatusScanner *globalStatusScanner = [[VagrantGlobalStatusScanner alloc] init]; - NSMutableDictionary *instancesPathDict = [globalStatusScanner getInstances]; + for(NSString *path in [globalStatusScanner getInstancePaths]) { + //make sure it is not a bookmark and has not already been detected + if(![bookmarkManager getBookmarkWithPath:path] && ![allPaths containsObject:path]) { + [allPaths addObject:path]; + [instances addObject:[[VagrantInstance alloc] initWithPath:path providerIdentifier:nil]]; + } + } - BookmarkManager *bookmarkManager = [BookmarkManager sharedManager]; - NSArray *bookmarks = [bookmarkManager getBookmarks]; - - //add bookmark instances and go through all bookmarks to override display name and provider identifier - for (Bookmark *bookmark in bookmarks) { - if ([instancesPathDict objectForKey:bookmark.path]) { - VagrantInstance *instance = [instancesPathDict objectForKey:bookmark.path]; - instance.displayName = bookmark.displayName; - instance.providerIdentifier = bookmark.providerIdentifier; - } else { - instancesPathDict[bookmark.path] = [[VagrantInstance alloc] initWithPath:bookmark.path displayName:bookmark.displayName providerIdentifier:bookmark.providerIdentifier]; + //create instance for each detected path + NSDictionary *detectedPaths = [self detectInstancePaths]; + for(NSString *providerIdentifier in [detectedPaths allKeys]) { + NSArray *paths = [detectedPaths objectForKey:providerIdentifier]; + for(NSString *path in paths) { + //make sure it is not a bookmark and has not already been detected + if(![bookmarkManager getBookmarkWithPath:path] && ![allPaths containsObject:path]) { + [allPaths addObject:path]; + [instances addObject:[[VagrantInstance alloc] initWithPath:path providerIdentifier:providerIdentifier]]; + } } } //TODO: implement "last seen" functionality. Store paths of previously seen Vagrantfiles and check if they still exist + NSMutableArray *validPaths = [[NSMutableArray alloc] init]; //query all known instances for machines, process in parallel dispatch_group_t queryMachinesGroup = dispatch_group_create(); dispatch_queue_t queryMachinesQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); - for(VagrantInstance *instance in [instancesPathDict allValues]) { + for(VagrantInstance *instance in instances) { dispatch_group_async(queryMachinesGroup, queryMachinesQueue, ^{ + //query instance machines + [instance queryMachines]; + @synchronized(_instances) { VagrantInstance *existingInstance = [self getInstanceForPath:instance.path]; if(existingInstance) { diff --git a/Vagrant Manager/rdp.png b/Vagrant Manager/rdp.png new file mode 100644 index 0000000..b22a9a1 Binary files /dev/null and b/Vagrant Manager/rdp.png differ diff --git a/Vagrant Manager/rdp@2x.png b/Vagrant Manager/rdp@2x.png new file mode 100644 index 0000000..bc88b38 Binary files /dev/null and b/Vagrant Manager/rdp@2x.png differ