diff --git a/.gitignore b/.gitignore index 8c3a21ade..ca52971f3 100644 --- a/.gitignore +++ b/.gitignore @@ -116,3 +116,4 @@ _tests/ wix/*.msi wix/*.wixobj wix/*.wixpdb +setup/Output/ diff --git a/setup/build.bat b/setup/build.bat new file mode 100644 index 000000000..6af0c1b7f --- /dev/null +++ b/setup/build.bat @@ -0,0 +1 @@ +inno\ISCC.exe nzbdrone.iss \ No newline at end of file diff --git a/setup/inno/Default.isl b/setup/inno/Default.isl new file mode 100644 index 000000000..b417cf916 --- /dev/null +++ b/setup/inno/Default.isl @@ -0,0 +1,336 @@ +; *** Inno Setup version 5.5.3+ English messages *** +; +; To download user-contributed translations of this file, go to: +; http://www.jrsoftware.org/files/istrans/ +; +; Note: When translating this text, do not add periods (.) to the end of +; messages that didn't have them already, because on those messages Inno +; Setup adds the periods automatically (appending a period would result in +; two periods being displayed). + +[LangOptions] +; The following three entries are very important. Be sure to read and +; understand the '[LangOptions] section' topic in the help file. +LanguageName=English +LanguageID=$0409 +LanguageCodePage=0 +; If the language you are translating to requires special font faces or +; sizes, uncomment any of the following entries and change them accordingly. +;DialogFontName= +;DialogFontSize=8 +;WelcomeFontName=Verdana +;WelcomeFontSize=12 +;TitleFontName=Arial +;TitleFontSize=29 +;CopyrightFontName=Arial +;CopyrightFontSize=8 + +[Messages] + +; *** Application titles +SetupAppTitle=Setup +SetupWindowTitle=Setup - %1 +UninstallAppTitle=Uninstall +UninstallAppFullTitle=%1 Uninstall + +; *** Misc. common +InformationTitle=Information +ConfirmTitle=Confirm +ErrorTitle=Error + +; *** SetupLdr messages +SetupLdrStartupMessage=This will install %1. Do you wish to continue? +LdrCannotCreateTemp=Unable to create a temporary file. Setup aborted +LdrCannotExecTemp=Unable to execute file in the temporary directory. Setup aborted + +; *** Startup error messages +LastErrorMessage=%1.%n%nError %2: %3 +SetupFileMissing=The file %1 is missing from the installation directory. Please correct the problem or obtain a new copy of the program. +SetupFileCorrupt=The setup files are corrupted. Please obtain a new copy of the program. +SetupFileCorruptOrWrongVer=The setup files are corrupted, or are incompatible with this version of Setup. Please correct the problem or obtain a new copy of the program. +InvalidParameter=An invalid parameter was passed on the command line:%n%n%1 +SetupAlreadyRunning=Setup is already running. +WindowsVersionNotSupported=This program does not support the version of Windows your computer is running. +WindowsServicePackRequired=This program requires %1 Service Pack %2 or later. +NotOnThisPlatform=This program will not run on %1. +OnlyOnThisPlatform=This program must be run on %1. +OnlyOnTheseArchitectures=This program can only be installed on versions of Windows designed for the following processor architectures:%n%n%1 +MissingWOW64APIs=The version of Windows you are running does not include functionality required by Setup to perform a 64-bit installation. To correct this problem, please install Service Pack %1. +WinVersionTooLowError=This program requires %1 version %2 or later. +WinVersionTooHighError=This program cannot be installed on %1 version %2 or later. +AdminPrivilegesRequired=You must be logged in as an administrator when installing this program. +PowerUserPrivilegesRequired=You must be logged in as an administrator or as a member of the Power Users group when installing this program. +SetupAppRunningError=Setup has detected that %1 is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit. +UninstallAppRunningError=Uninstall has detected that %1 is currently running.%n%nPlease close all instances of it now, then click OK to continue, or Cancel to exit. + +; *** Misc. errors +ErrorCreatingDir=Setup was unable to create the directory "%1" +ErrorTooManyFilesInDir=Unable to create a file in the directory "%1" because it contains too many files + +; *** Setup common messages +ExitSetupTitle=Exit Setup +ExitSetupMessage=Setup is not complete. If you exit now, the program will not be installed.%n%nYou may run Setup again at another time to complete the installation.%n%nExit Setup? +AboutSetupMenuItem=&About Setup... +AboutSetupTitle=About Setup +AboutSetupMessage=%1 version %2%n%3%n%n%1 home page:%n%4 +AboutSetupNote= +TranslatorNote= + +; *** Buttons +ButtonBack=< &Back +ButtonNext=&Next > +ButtonInstall=&Install +ButtonOK=OK +ButtonCancel=Cancel +ButtonYes=&Yes +ButtonYesToAll=Yes to &All +ButtonNo=&No +ButtonNoToAll=N&o to All +ButtonFinish=&Finish +ButtonBrowse=&Browse... +ButtonWizardBrowse=B&rowse... +ButtonNewFolder=&Make New Folder + +; *** "Select Language" dialog messages +SelectLanguageTitle=Select Setup Language +SelectLanguageLabel=Select the language to use during the installation: + +; *** Common wizard text +ClickNext=Click Next to continue, or Cancel to exit Setup. +BeveledLabel= +BrowseDialogTitle=Browse For Folder +BrowseDialogLabel=Select a folder in the list below, then click OK. +NewFolderName=New Folder + +; *** "Welcome" wizard page +WelcomeLabel1=Welcome to the [name] Setup Wizard +WelcomeLabel2=This will install [name/ver] on your computer.%n%nIt is recommended that you close all other applications before continuing. + +; *** "Password" wizard page +WizardPassword=Password +PasswordLabel1=This installation is password protected. +PasswordLabel3=Please provide the password, then click Next to continue. Passwords are case-sensitive. +PasswordEditLabel=&Password: +IncorrectPassword=The password you entered is not correct. Please try again. + +; *** "License Agreement" wizard page +WizardLicense=License Agreement +LicenseLabel=Please read the following important information before continuing. +LicenseLabel3=Please read the following License Agreement. You must accept the terms of this agreement before continuing with the installation. +LicenseAccepted=I &accept the agreement +LicenseNotAccepted=I &do not accept the agreement + +; *** "Information" wizard pages +WizardInfoBefore=Information +InfoBeforeLabel=Please read the following important information before continuing. +InfoBeforeClickLabel=When you are ready to continue with Setup, click Next. +WizardInfoAfter=Information +InfoAfterLabel=Please read the following important information before continuing. +InfoAfterClickLabel=When you are ready to continue with Setup, click Next. + +; *** "User Information" wizard page +WizardUserInfo=User Information +UserInfoDesc=Please enter your information. +UserInfoName=&User Name: +UserInfoOrg=&Organization: +UserInfoSerial=&Serial Number: +UserInfoNameRequired=You must enter a name. + +; *** "Select Destination Location" wizard page +WizardSelectDir=Select Destination Location +SelectDirDesc=Where should [name] be installed? +SelectDirLabel3=Setup will install [name] into the following folder. +SelectDirBrowseLabel=To continue, click Next. If you would like to select a different folder, click Browse. +DiskSpaceMBLabel=At least [mb] MB of free disk space is required. +CannotInstallToNetworkDrive=Setup cannot install to a network drive. +CannotInstallToUNCPath=Setup cannot install to a UNC path. +InvalidPath=You must enter a full path with drive letter; for example:%n%nC:\APP%n%nor a UNC path in the form:%n%n\\server\share +InvalidDrive=The drive or UNC share you selected does not exist or is not accessible. Please select another. +DiskSpaceWarningTitle=Not Enough Disk Space +DiskSpaceWarning=Setup requires at least %1 KB of free space to install, but the selected drive only has %2 KB available.%n%nDo you want to continue anyway? +DirNameTooLong=The folder name or path is too long. +InvalidDirName=The folder name is not valid. +BadDirName32=Folder names cannot include any of the following characters:%n%n%1 +DirExistsTitle=Folder Exists +DirExists=The folder:%n%n%1%n%nalready exists. Would you like to install to that folder anyway? +DirDoesntExistTitle=Folder Does Not Exist +DirDoesntExist=The folder:%n%n%1%n%ndoes not exist. Would you like the folder to be created? + +; *** "Select Components" wizard page +WizardSelectComponents=Select Components +SelectComponentsDesc=Which components should be installed? +SelectComponentsLabel2=Select the components you want to install; clear the components you do not want to install. Click Next when you are ready to continue. +FullInstallation=Full installation +; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language) +CompactInstallation=Compact installation +CustomInstallation=Custom installation +NoUninstallWarningTitle=Components Exist +NoUninstallWarning=Setup has detected that the following components are already installed on your computer:%n%n%1%n%nDeselecting these components will not uninstall them.%n%nWould you like to continue anyway? +ComponentSize1=%1 KB +ComponentSize2=%1 MB +ComponentsDiskSpaceMBLabel=Current selection requires at least [mb] MB of disk space. + +; *** "Select Additional Tasks" wizard page +WizardSelectTasks=Select Additional Tasks +SelectTasksDesc=Which additional tasks should be performed? +SelectTasksLabel2=Select the additional tasks you would like Setup to perform while installing [name], then click Next. + +; *** "Select Start Menu Folder" wizard page +WizardSelectProgramGroup=Select Start Menu Folder +SelectStartMenuFolderDesc=Where should Setup place the program's shortcuts? +SelectStartMenuFolderLabel3=Setup will create the program's shortcuts in the following Start Menu folder. +SelectStartMenuFolderBrowseLabel=To continue, click Next. If you would like to select a different folder, click Browse. +MustEnterGroupName=You must enter a folder name. +GroupNameTooLong=The folder name or path is too long. +InvalidGroupName=The folder name is not valid. +BadGroupName=The folder name cannot include any of the following characters:%n%n%1 +NoProgramGroupCheck2=&Don't create a Start Menu folder + +; *** "Ready to Install" wizard page +WizardReady=Ready to Install +ReadyLabel1=Setup is now ready to begin installing [name] on your computer. +ReadyLabel2a=Click Install to continue with the installation, or click Back if you want to review or change any settings. +ReadyLabel2b=Click Install to continue with the installation. +ReadyMemoUserInfo=User information: +ReadyMemoDir=Destination location: +ReadyMemoType=Setup type: +ReadyMemoComponents=Selected components: +ReadyMemoGroup=Start Menu folder: +ReadyMemoTasks=Additional tasks: + +; *** "Preparing to Install" wizard page +WizardPreparing=Preparing to Install +PreparingDesc=Setup is preparing to install [name] on your computer. +PreviousInstallNotCompleted=The installation/removal of a previous program was not completed. You will need to restart your computer to complete that installation.%n%nAfter restarting your computer, run Setup again to complete the installation of [name]. +CannotContinue=Setup cannot continue. Please click Cancel to exit. +ApplicationsFound=The following applications are using files that need to be updated by Setup. It is recommended that you allow Setup to automatically close these applications. +ApplicationsFound2=The following applications are using files that need to be updated by Setup. It is recommended that you allow Setup to automatically close these applications. After the installation has completed, Setup will attempt to restart the applications. +CloseApplications=&Automatically close the applications +DontCloseApplications=&Do not close the applications +ErrorCloseApplications=Setup was unable to automatically close all applications. It is recommended that you close all applications using files that need to be updated by Setup before continuing. + +; *** "Installing" wizard page +WizardInstalling=Installing +InstallingLabel=Please wait while Setup installs [name] on your computer. + +; *** "Setup Completed" wizard page +FinishedHeadingLabel=Completing the [name] Setup Wizard +FinishedLabelNoIcons=Setup has finished installing [name] on your computer. +FinishedLabel=Setup has finished installing [name] on your computer. The application may be launched by selecting the installed icons. +ClickFinish=Click Finish to exit Setup. +FinishedRestartLabel=To complete the installation of [name], Setup must restart your computer. Would you like to restart now? +FinishedRestartMessage=To complete the installation of [name], Setup must restart your computer.%n%nWould you like to restart now? +ShowReadmeCheck=Yes, I would like to view the README file +YesRadio=&Yes, restart the computer now +NoRadio=&No, I will restart the computer later +; used for example as 'Run MyProg.exe' +RunEntryExec=Run %1 +; used for example as 'View Readme.txt' +RunEntryShellExec=View %1 + +; *** "Setup Needs the Next Disk" stuff +ChangeDiskTitle=Setup Needs the Next Disk +SelectDiskLabel2=Please insert Disk %1 and click OK.%n%nIf the files on this disk can be found in a folder other than the one displayed below, enter the correct path or click Browse. +PathLabel=&Path: +FileNotInDir2=The file "%1" could not be located in "%2". Please insert the correct disk or select another folder. +SelectDirectoryLabel=Please specify the location of the next disk. + +; *** Installation phase messages +SetupAborted=Setup was not completed.%n%nPlease correct the problem and run Setup again. +EntryAbortRetryIgnore=Click Retry to try again, Ignore to proceed anyway, or Abort to cancel installation. + +; *** Installation status messages +StatusClosingApplications=Closing applications... +StatusCreateDirs=Creating directories... +StatusExtractFiles=Extracting files... +StatusCreateIcons=Creating shortcuts... +StatusCreateIniEntries=Creating INI entries... +StatusCreateRegistryEntries=Creating registry entries... +StatusRegisterFiles=Registering files... +StatusSavingUninstall=Saving uninstall information... +StatusRunProgram=Finishing installation... +StatusRestartingApplications=Restarting applications... +StatusRollback=Rolling back changes... + +; *** Misc. errors +ErrorInternal2=Internal error: %1 +ErrorFunctionFailedNoCode=%1 failed +ErrorFunctionFailed=%1 failed; code %2 +ErrorFunctionFailedWithMessage=%1 failed; code %2.%n%3 +ErrorExecutingProgram=Unable to execute file:%n%1 + +; *** Registry errors +ErrorRegOpenKey=Error opening registry key:%n%1\%2 +ErrorRegCreateKey=Error creating registry key:%n%1\%2 +ErrorRegWriteKey=Error writing to registry key:%n%1\%2 + +; *** INI errors +ErrorIniEntry=Error creating INI entry in file "%1". + +; *** File copying errors +FileAbortRetryIgnore=Click Retry to try again, Ignore to skip this file (not recommended), or Abort to cancel installation. +FileAbortRetryIgnore2=Click Retry to try again, Ignore to proceed anyway (not recommended), or Abort to cancel installation. +SourceIsCorrupted=The source file is corrupted +SourceDoesntExist=The source file "%1" does not exist +ExistingFileReadOnly=The existing file is marked as read-only.%n%nClick Retry to remove the read-only attribute and try again, Ignore to skip this file, or Abort to cancel installation. +ErrorReadingExistingDest=An error occurred while trying to read the existing file: +FileExists=The file already exists.%n%nWould you like Setup to overwrite it? +ExistingFileNewer=The existing file is newer than the one Setup is trying to install. It is recommended that you keep the existing file.%n%nDo you want to keep the existing file? +ErrorChangingAttr=An error occurred while trying to change the attributes of the existing file: +ErrorCreatingTemp=An error occurred while trying to create a file in the destination directory: +ErrorReadingSource=An error occurred while trying to read the source file: +ErrorCopying=An error occurred while trying to copy a file: +ErrorReplacingExistingFile=An error occurred while trying to replace the existing file: +ErrorRestartReplace=RestartReplace failed: +ErrorRenamingTemp=An error occurred while trying to rename a file in the destination directory: +ErrorRegisterServer=Unable to register the DLL/OCX: %1 +ErrorRegSvr32Failed=RegSvr32 failed with exit code %1 +ErrorRegisterTypeLib=Unable to register the type library: %1 + +; *** Post-installation errors +ErrorOpeningReadme=An error occurred while trying to open the README file. +ErrorRestartingComputer=Setup was unable to restart the computer. Please do this manually. + +; *** Uninstaller messages +UninstallNotFound=File "%1" does not exist. Cannot uninstall. +UninstallOpenError=File "%1" could not be opened. Cannot uninstall +UninstallUnsupportedVer=The uninstall log file "%1" is in a format not recognized by this version of the uninstaller. Cannot uninstall +UninstallUnknownEntry=An unknown entry (%1) was encountered in the uninstall log +ConfirmUninstall=Are you sure you want to completely remove %1 and all of its components? +UninstallOnlyOnWin64=This installation can only be uninstalled on 64-bit Windows. +OnlyAdminCanUninstall=This installation can only be uninstalled by a user with administrative privileges. +UninstallStatusLabel=Please wait while %1 is removed from your computer. +UninstalledAll=%1 was successfully removed from your computer. +UninstalledMost=%1 uninstall complete.%n%nSome elements could not be removed. These can be removed manually. +UninstalledAndNeedsRestart=To complete the uninstallation of %1, your computer must be restarted.%n%nWould you like to restart now? +UninstallDataCorrupted="%1" file is corrupted. Cannot uninstall + +; *** Uninstallation phase messages +ConfirmDeleteSharedFileTitle=Remove Shared File? +ConfirmDeleteSharedFile2=The system indicates that the following shared file is no longer in use by any programs. Would you like for Uninstall to remove this shared file?%n%nIf any programs are still using this file and it is removed, those programs may not function properly. If you are unsure, choose No. Leaving the file on your system will not cause any harm. +SharedFileNameLabel=File name: +SharedFileLocationLabel=Location: +WizardUninstalling=Uninstall Status +StatusUninstalling=Uninstalling %1... + +; *** Shutdown block reasons +ShutdownBlockReasonInstallingApp=Installing %1. +ShutdownBlockReasonUninstallingApp=Uninstalling %1. + +; The custom messages below aren't used by Setup itself, but if you make +; use of them in your scripts, you'll want to translate them. + +[CustomMessages] + +NameAndVersion=%1 version %2 +AdditionalIcons=Additional icons: +CreateDesktopIcon=Create a &desktop icon +CreateQuickLaunchIcon=Create a &Quick Launch icon +ProgramOnTheWeb=%1 on the Web +UninstallProgram=Uninstall %1 +LaunchProgram=Launch %1 +AssocFileExtension=&Associate %1 with the %2 file extension +AssocingFileExtension=Associating %1 with the %2 file extension... +AutoStartProgramGroupDescription=Startup: +AutoStartProgram=Automatically start %1 +AddonHostProgramNotFound=%1 could not be located in the folder you selected.%n%nDo you want to continue anyway? diff --git a/setup/inno/ISCC.exe b/setup/inno/ISCC.exe new file mode 100644 index 000000000..8e54535ce Binary files /dev/null and b/setup/inno/ISCC.exe differ diff --git a/setup/inno/ISCmplr.dll b/setup/inno/ISCmplr.dll new file mode 100644 index 000000000..39129a3d2 Binary files /dev/null and b/setup/inno/ISCmplr.dll differ diff --git a/setup/inno/ISPP.dll b/setup/inno/ISPP.dll new file mode 100644 index 000000000..87551fb89 Binary files /dev/null and b/setup/inno/ISPP.dll differ diff --git a/setup/inno/Setup.e32 b/setup/inno/Setup.e32 new file mode 100644 index 000000000..09cc13598 Binary files /dev/null and b/setup/inno/Setup.e32 differ diff --git a/setup/inno/SetupLdr.e32 b/setup/inno/SetupLdr.e32 new file mode 100644 index 000000000..956755065 Binary files /dev/null and b/setup/inno/SetupLdr.e32 differ diff --git a/setup/inno/WizModernImage.bmp b/setup/inno/WizModernImage.bmp new file mode 100644 index 000000000..cf844e093 Binary files /dev/null and b/setup/inno/WizModernImage.bmp differ diff --git a/setup/inno/WizModernSmallImage.bmp b/setup/inno/WizModernSmallImage.bmp new file mode 100644 index 000000000..1e8e49792 Binary files /dev/null and b/setup/inno/WizModernSmallImage.bmp differ diff --git a/setup/inno/islzma.dll b/setup/inno/islzma.dll new file mode 100644 index 000000000..49395ada5 Binary files /dev/null and b/setup/inno/islzma.dll differ diff --git a/setup/nzbdrone.iss b/setup/nzbdrone.iss new file mode 100644 index 000000000..422277df7 --- /dev/null +++ b/setup/nzbdrone.iss @@ -0,0 +1,52 @@ +; Script generated by the Inno Setup Script Wizard. +; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! + +#define AppName "NzbDrone" +#define AppPublisher "NzbDrone" +#define AppURL "http://www.nzbdrone.com/" +#define AppExeName "NzbDrone.exe" + +[Setup] +; NOTE: The value of AppId uniquely identifies this application. +; Do not use the same AppId value in installers for other applications. +; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) +AppId={{56C1065D-3523-4025-B76D-6F73F67F7F71} +AppName={#AppName} +AppVersion={%BUILD_NUMBER|1.0.0} +AppPublisher={#AppPublisher} +AppPublisherURL={#AppURL} +AppSupportURL={#AppURL} +AppUpdatesURL={#AppURL} +DefaultDirName={commonprograms}\{#AppName}\bin +DisableDirPage=yes +DefaultGroupName={#AppName} +DisableProgramGroupPage=yes +OutputBaseFilename=NzbDroneSetup +SolidCompression=yes +AppCopyright=Creative Commons 3.0 License +AllowUNCPath=False +UninstallDisplayIcon={app}\NzbDrone.exe +DisableReadyPage=True +CompressionThreads=2 +Compression=lzma2/normal + +[Languages] +Name: "english"; MessagesFile: "compiler:Default.isl" + +[Tasks] +Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked + +[Files] +Source: "C:\Dropbox\Git\NzbDrone\_output\NzbDrone.exe"; DestDir: "{app}"; Flags: ignoreversion +Source: "C:\Dropbox\Git\NzbDrone\_output\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs +; NOTE: Don't use "Flags: ignoreversion" on any shared system files + +[Icons] +Name: "{group}\{#AppName}"; Filename: "{app}\{#AppExeName}" +Name: "{commondesktop}\{#AppName}"; Filename: "{app}\{#AppExeName}"; Tasks: desktopicon + +[Run] +Filename: "{app}\nzbdrone.console.exe"; Parameters: "/i"; Flags: waituntilterminated + +[UninstallRun] +Filename: "{app}\nzbdrone.console.exe"; Parameters: "/u"; Flags: waituntilterminated skipifdoesntexist diff --git a/src/NzbDrone.App.Test/ContainerFixture.cs b/src/NzbDrone.App.Test/ContainerFixture.cs index a165f9348..d542b9dee 100644 --- a/src/NzbDrone.App.Test/ContainerFixture.cs +++ b/src/NzbDrone.App.Test/ContainerFixture.cs @@ -18,7 +18,7 @@ namespace NzbDrone.App.Test [TestFixture] public class ContainerFixture : TestBase { - StartupArguments args = new StartupArguments("first", "second"); + StartupContext args = new StartupContext("first", "second"); [Test] public void should_be_able_to_resolve_indexers() diff --git a/src/NzbDrone.App.Test/NzbDrone.Host.Test.csproj b/src/NzbDrone.App.Test/NzbDrone.Host.Test.csproj index 2b3db8178..274499d34 100644 --- a/src/NzbDrone.App.Test/NzbDrone.Host.Test.csproj +++ b/src/NzbDrone.App.Test/NzbDrone.Host.Test.csproj @@ -60,6 +60,7 @@ + diff --git a/src/NzbDrone.App.Test/NzbDroneProcessServiceFixture.cs b/src/NzbDrone.App.Test/NzbDroneProcessServiceFixture.cs new file mode 100644 index 000000000..0c792f0c3 --- /dev/null +++ b/src/NzbDrone.App.Test/NzbDroneProcessServiceFixture.cs @@ -0,0 +1,91 @@ +using System.Collections.Generic; +using Moq; +using NUnit.Framework; +using NzbDrone.Common.Model; +using NzbDrone.Common.Processes; +using NzbDrone.Host; +using NzbDrone.Test.Common; + +namespace NzbDrone.App.Test +{ + [TestFixture] + public class NzbDroneProcessServiceFixture : TestBase + { + private const int CURRENT_PROCESS_ID = 5; + + [SetUp] + public void Setup() + { + Mocker.GetMock().Setup(c => c.GetCurrentProcess()) + .Returns(new ProcessInfo() { Id = CURRENT_PROCESS_ID }); + } + + [Test] + public void should_continue_if_only_instance() + { + Mocker.GetMock() + .Setup(c => c.FindProcessByName(ProcessProvider.NZB_DRONE_CONSOLE_PROCESS_NAME)) + .Returns(new List()); + + Mocker.GetMock().Setup(c => c.FindProcessByName(ProcessProvider.NZB_DRONE_PROCESS_NAME)) + .Returns(new List + { + new ProcessInfo{Id = CURRENT_PROCESS_ID} + }); + + + Subject.PreventStartIfAlreadyRunning(); + + + Mocker.GetMock().Verify(c => c.LaunchWebUI(), Times.Never()); + + } + + [Test] + public void should_enforce_if_another_console_is_running() + { + Mocker.GetMock() + .Setup(c => c.FindProcessByName(ProcessProvider.NZB_DRONE_CONSOLE_PROCESS_NAME)) + .Returns(new List + { + new ProcessInfo{Id = 10} + }); + + Mocker.GetMock().Setup(c => c.FindProcessByName(ProcessProvider.NZB_DRONE_PROCESS_NAME)) + .Returns(new List + { + new ProcessInfo{Id = CURRENT_PROCESS_ID} + }); + + + + Assert.Throws(() => Subject.PreventStartIfAlreadyRunning()); + Mocker.GetMock().Verify(c => c.LaunchWebUI(), Times.Once()); + ExceptionVerification.ExpectedWarns(1); + } + + [Test] + public void should_return_false_if_another_gui_is_running() + { + Mocker.GetMock() + .Setup(c => c.FindProcessByName(ProcessProvider.NZB_DRONE_CONSOLE_PROCESS_NAME)) + .Returns(new List + { + new ProcessInfo{Id = CURRENT_PROCESS_ID} + + }); + + Mocker.GetMock().Setup(c => c.FindProcessByName(ProcessProvider.NZB_DRONE_PROCESS_NAME)) + .Returns(new List + { + new ProcessInfo{Id = 10} + }); + + + + Assert.Throws(() => Subject.PreventStartIfAlreadyRunning()); + Mocker.GetMock().Verify(c => c.LaunchWebUI(), Times.Once()); + ExceptionVerification.ExpectedWarns(1); + } + } +} diff --git a/src/NzbDrone.App.Test/RouterTest.cs b/src/NzbDrone.App.Test/RouterTest.cs index 569af6a9f..0cf7b6c3d 100644 --- a/src/NzbDrone.App.Test/RouterTest.cs +++ b/src/NzbDrone.App.Test/RouterTest.cs @@ -68,7 +68,7 @@ namespace NzbDrone.App.Test serviceProvider.Setup(c => c.ServiceExist(It.IsAny())).Returns(true); serviceProvider.Setup(c => c.GetStatus(It.IsAny())).Returns(ServiceControllerStatus.StartPending); - Subject.Route(); + Subject.Route(ApplicationModes.Service); serviceProvider.Verify(c => c.Run(It.IsAny()), Times.Once()); } diff --git a/src/NzbDrone.Automation.Test/AutomationTest.cs b/src/NzbDrone.Automation.Test/AutomationTest.cs index a7f3645cf..ed0b7dfa2 100644 --- a/src/NzbDrone.Automation.Test/AutomationTest.cs +++ b/src/NzbDrone.Automation.Test/AutomationTest.cs @@ -23,7 +23,7 @@ namespace NzbDrone.Automation.Test public AutomationTest() { - new StartupArguments(); + new StartupContext(); LogManager.Configuration = new LoggingConfiguration(); var consoleTarget = new ConsoleTarget { Layout = "${level}: ${message} ${exception}" }; diff --git a/src/NzbDrone.Common.Test/EnvironmentProviderTest.cs b/src/NzbDrone.Common.Test/EnvironmentProviderTest.cs index 588fb06ad..920064f70 100644 --- a/src/NzbDrone.Common.Test/EnvironmentProviderTest.cs +++ b/src/NzbDrone.Common.Test/EnvironmentProviderTest.cs @@ -35,9 +35,9 @@ namespace NzbDrone.Common.Test [Test] public void should_use_path_from_arg_if_provided() { - var args = new StartupArguments("-data=\"c:\\users\\test\\\""); + var args = new StartupContext("-data=\"c:\\users\\test\\\""); - Mocker.SetConstant(args); + Mocker.SetConstant(args); Subject.AppDataFolder.Should().Be("c:\\users\\test\\"); } } diff --git a/src/NzbDrone.Common.Test/EnvironmentTests/StartupArgumentsFixture.cs b/src/NzbDrone.Common.Test/EnvironmentTests/StartupArgumentsFixture.cs index d6bad1e86..c3b94154b 100644 --- a/src/NzbDrone.Common.Test/EnvironmentTests/StartupArgumentsFixture.cs +++ b/src/NzbDrone.Common.Test/EnvironmentTests/StartupArgumentsFixture.cs @@ -11,7 +11,7 @@ namespace NzbDrone.Common.Test.EnvironmentTests [Test] public void empty_array_should_return_empty_flags() { - var args = new StartupArguments(new string[0]); + var args = new StartupContext(new string[0]); args.Flags.Should().BeEmpty(); } @@ -21,7 +21,7 @@ namespace NzbDrone.Common.Test.EnvironmentTests [TestCase(" /t ")] public void should_parse_single_flag(string arg) { - var args = new StartupArguments(new[] { arg }); + var args = new StartupContext(new[] { arg }); args.Flags.Should().HaveCount(1); args.Flags.Contains("t").Should().BeTrue(); } @@ -32,7 +32,7 @@ namespace NzbDrone.Common.Test.EnvironmentTests [TestCase(" /key=\"value\"")] public void should_parse_args_with_alues(string arg) { - var args = new StartupArguments(new[] { arg }); + var args = new StartupContext(new[] { arg }); args.Args.Should().HaveCount(1); args.Args["key"].Should().Be("value"); } diff --git a/src/NzbDrone.Common.Test/ServiceFactoryFixture.cs b/src/NzbDrone.Common.Test/ServiceFactoryFixture.cs index 78dcbe442..157d0893d 100644 --- a/src/NzbDrone.Common.Test/ServiceFactoryFixture.cs +++ b/src/NzbDrone.Common.Test/ServiceFactoryFixture.cs @@ -15,7 +15,7 @@ namespace NzbDrone.Common.Test [SetUp] public void setup() { - Mocker.SetConstant(MainAppContainerBuilder.BuildContainer(new StartupArguments())); + Mocker.SetConstant(MainAppContainerBuilder.BuildContainer(new StartupContext())); } [Test] diff --git a/src/NzbDrone.Common/Composition/ContainerBuilderBase.cs b/src/NzbDrone.Common/Composition/ContainerBuilderBase.cs index 9ac2747f1..7a9154672 100644 --- a/src/NzbDrone.Common/Composition/ContainerBuilderBase.cs +++ b/src/NzbDrone.Common/Composition/ContainerBuilderBase.cs @@ -15,7 +15,7 @@ namespace NzbDrone.Common.Composition public IContainer Container { get; private set; } - protected ContainerBuilderBase(IStartupArguments args, params string[] assemblies) + protected ContainerBuilderBase(IStartupContext args, params string[] assemblies) { diff --git a/src/NzbDrone.Common/ConsoleService.cs b/src/NzbDrone.Common/ConsoleService.cs index cceeade42..9a4caad90 100644 --- a/src/NzbDrone.Common/ConsoleService.cs +++ b/src/NzbDrone.Common/ConsoleService.cs @@ -24,9 +24,9 @@ namespace NzbDrone.Common Console.WriteLine(); Console.WriteLine(" Usage: {0} ", Process.GetCurrentProcess().MainModule.ModuleName); Console.WriteLine(" Commands:"); - Console.WriteLine(" /{0} Install the application as a Windows Service ({1}).", StartupArguments.INSTALL_SERVICE, ServiceProvider.NZBDRONE_SERVICE_NAME); - Console.WriteLine(" /{0} Uninstall already installed Windows Service ({1}).", StartupArguments.UNINSTALL_SERVICE, ServiceProvider.NZBDRONE_SERVICE_NAME); - Console.WriteLine(" /{0} Don't open NzbDrone in a browser", StartupArguments.NO_BROWSER); + Console.WriteLine(" /{0} Install the application as a Windows Service ({1}).", StartupContext.INSTALL_SERVICE, ServiceProvider.NZBDRONE_SERVICE_NAME); + Console.WriteLine(" /{0} Uninstall already installed Windows Service ({1}).", StartupContext.UNINSTALL_SERVICE, ServiceProvider.NZBDRONE_SERVICE_NAME); + Console.WriteLine(" /{0} Don't open NzbDrone in a browser", StartupContext.NO_BROWSER); Console.WriteLine(" Run application in console mode."); } diff --git a/src/NzbDrone.Common/EnvironmentInfo/AppFolderInfo.cs b/src/NzbDrone.Common/EnvironmentInfo/AppFolderInfo.cs index 04f7b29ab..a7273a2af 100644 --- a/src/NzbDrone.Common/EnvironmentInfo/AppFolderInfo.cs +++ b/src/NzbDrone.Common/EnvironmentInfo/AppFolderInfo.cs @@ -22,7 +22,7 @@ namespace NzbDrone.Common.EnvironmentInfo private readonly Environment.SpecialFolder DATA_SPECIAL_FOLDER = Environment.SpecialFolder.CommonApplicationData; - public AppFolderInfo(IDiskProvider diskProvider, IStartupArguments startupArguments) + public AppFolderInfo(IDiskProvider diskProvider, IStartupContext startupContext) { _diskProvider = diskProvider; @@ -33,9 +33,9 @@ namespace NzbDrone.Common.EnvironmentInfo _logger = NzbDroneLogger.GetLogger(this); - if (startupArguments.Args.ContainsKey(StartupArguments.APPDATA)) + if (startupContext.Args.ContainsKey(StartupContext.APPDATA)) { - AppDataFolder = startupArguments.Args[StartupArguments.APPDATA]; + AppDataFolder = startupContext.Args[StartupContext.APPDATA]; } else { diff --git a/src/NzbDrone.Common/EnvironmentInfo/RuntimeInfo.cs b/src/NzbDrone.Common/EnvironmentInfo/RuntimeInfo.cs index 858db57b0..a4192a8a3 100644 --- a/src/NzbDrone.Common/EnvironmentInfo/RuntimeInfo.cs +++ b/src/NzbDrone.Common/EnvironmentInfo/RuntimeInfo.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.IO; using System.Security.Principal; +using System.ServiceProcess; using NLog; namespace NzbDrone.Common.EnvironmentInfo @@ -11,15 +12,21 @@ namespace NzbDrone.Common.EnvironmentInfo { bool IsUserInteractive { get; } bool IsAdmin { get; } + bool IsWindowsService { get; } } public class RuntimeInfo : IRuntimeInfo { private readonly Logger _logger; - public RuntimeInfo(Logger logger) + public RuntimeInfo(Logger logger, IServiceProvider serviceProvider) { _logger = logger; + + IsWindowsService = !IsUserInteractive && + OsInfo.IsWindows && + serviceProvider.ServiceExist(ServiceProvider.NZBDRONE_SERVICE_NAME) && + serviceProvider.GetStatus(ServiceProvider.NZBDRONE_SERVICE_NAME) == ServiceControllerStatus.StartPending; } public bool IsUserInteractive @@ -30,6 +37,8 @@ namespace NzbDrone.Common.EnvironmentInfo static RuntimeInfo() { IsProduction = InternalIsProduction(); + + } public bool IsAdmin @@ -49,6 +58,8 @@ namespace NzbDrone.Common.EnvironmentInfo } } + public bool IsWindowsService { get; private set; } + private static readonly string ProcessName = Process.GetCurrentProcess().ProcessName.ToLower(); public static bool IsProduction { get; private set; } diff --git a/src/NzbDrone.Common/EnvironmentInfo/StartupArguments.cs b/src/NzbDrone.Common/EnvironmentInfo/StartupContext.cs similarity index 61% rename from src/NzbDrone.Common/EnvironmentInfo/StartupArguments.cs rename to src/NzbDrone.Common/EnvironmentInfo/StartupContext.cs index 51c587bd0..0702fc861 100644 --- a/src/NzbDrone.Common/EnvironmentInfo/StartupArguments.cs +++ b/src/NzbDrone.Common/EnvironmentInfo/StartupContext.cs @@ -2,21 +2,23 @@ namespace NzbDrone.Common.EnvironmentInfo { - public interface IStartupArguments + public interface IStartupContext { HashSet Flags { get; } Dictionary Args { get; } + bool InstallService { get; } + bool UninstallService { get; } } - public class StartupArguments : IStartupArguments + public class StartupContext : IStartupContext { public const string APPDATA = "data"; public const string NO_BROWSER = "nobrowser"; - public const string INSTALL_SERVICE = "i"; - public const string UNINSTALL_SERVICE = "u"; + internal const string INSTALL_SERVICE = "i"; + internal const string UNINSTALL_SERVICE = "u"; public const string HELP = "?"; - public StartupArguments(params string[] args) + public StartupContext(params string[] args) { Flags = new HashSet(); Args = new Dictionary(); @@ -40,5 +42,22 @@ namespace NzbDrone.Common.EnvironmentInfo public HashSet Flags { get; private set; } public Dictionary Args { get; private set; } + + public bool InstallService + { + get + { + return Flags.Contains(INSTALL_SERVICE); + } + } + + public bool UninstallService + { + get + { + return Flags.Contains(UNINSTALL_SERVICE); + } + } + } } \ No newline at end of file diff --git a/src/NzbDrone.Common/Instrumentation/LogTargets.cs b/src/NzbDrone.Common/Instrumentation/LogTargets.cs index d63520e80..4f028aa60 100644 --- a/src/NzbDrone.Common/Instrumentation/LogTargets.cs +++ b/src/NzbDrone.Common/Instrumentation/LogTargets.cs @@ -9,9 +9,9 @@ namespace NzbDrone.Common.Instrumentation { public static class LogTargets { - public static void Register(IStartupArguments startupArguments, bool updateApp, bool inConsole) + public static void Register(IStartupContext startupContext, bool updateApp, bool inConsole) { - var appFolderInfo = new AppFolderInfo(new DiskProvider(), startupArguments); + var appFolderInfo = new AppFolderInfo(new DiskProvider(), startupContext); LogManager.Configuration = new LoggingConfiguration(); @@ -24,7 +24,7 @@ namespace NzbDrone.Common.Instrumentation } else { - if (inConsole && (OsInfo.IsLinux || new RuntimeInfo(null).IsUserInteractive)) + if (inConsole && (OsInfo.IsLinux || new RuntimeInfo(null, new ServiceProvider()).IsUserInteractive)) { RegisterConsole(); } diff --git a/src/NzbDrone.Common/NzbDrone.Common.csproj b/src/NzbDrone.Common/NzbDrone.Common.csproj index 6a6cc35f4..883ece0a3 100644 --- a/src/NzbDrone.Common/NzbDrone.Common.csproj +++ b/src/NzbDrone.Common/NzbDrone.Common.csproj @@ -86,7 +86,7 @@ - + diff --git a/src/NzbDrone.Common/Processes/ProcessProvider.cs b/src/NzbDrone.Common/Processes/ProcessProvider.cs index 75bde50f8..785b029e5 100644 --- a/src/NzbDrone.Common/Processes/ProcessProvider.cs +++ b/src/NzbDrone.Common/Processes/ProcessProvider.cs @@ -15,10 +15,12 @@ namespace NzbDrone.Common.Processes { ProcessInfo GetCurrentProcess(); ProcessInfo GetProcessById(int id); + List FindProcessByName(string name); void OpenDefaultBrowser(string url); void WaitForExit(Process process); void SetPriority(int processId, ProcessPriorityClass priority); void KillAll(string processName); + void Kill(int processId); bool Exists(string processName); ProcessPriorityClass GetCurrentProcessPriority(); Process Start(string path, string args = null, Action onOutputDataReceived = null, Action onErrorDataReceived = null); @@ -74,6 +76,11 @@ namespace NzbDrone.Common.Processes return processInfo; } + public List FindProcessByName(string name) + { + return Process.GetProcessesByName(name).Select(ConvertToProcessInfo).Where(c => c != null).ToList(); + } + public void OpenDefaultBrowser(string url) { Logger.Info("Opening URL [{0}]", url); @@ -213,23 +220,29 @@ namespace NzbDrone.Common.Processes process.Refresh(); + ProcessInfo processInfo = null; + try { - if (process.Id <= 0 || process.HasExited) return null; + if (process.Id <= 0) return null; + + processInfo = new ProcessInfo(); + processInfo.Id = process.Id; + processInfo.Name = process.ProcessName; + processInfo.StartPath = GetExeFileName(process); - return new ProcessInfo + if (process.HasExited) { - Id = process.Id, - StartPath = GetExeFileName(process), - Name = process.ProcessName - }; + processInfo = null; + } } - catch (Win32Exception) + catch (Win32Exception e) { - Logger.Warn("Coudn't get process info for " + process.ProcessName); + Logger.WarnException("Couldn't get process info for " + process.ProcessName, e); } - return null; + return processInfo; + } private static string GetExeFileName(Process process) @@ -242,7 +255,7 @@ namespace NzbDrone.Common.Processes return process.Modules.Cast().FirstOrDefault(module => module.ModuleName.ToLower().EndsWith(".exe")).FileName; } - private void Kill(int processId) + public void Kill(int processId) { var process = Process.GetProcesses().FirstOrDefault(p => p.Id == processId); diff --git a/src/NzbDrone.Console/ConsoleApp.cs b/src/NzbDrone.Console/ConsoleApp.cs index 68de86f25..1cf0aae11 100644 --- a/src/NzbDrone.Console/ConsoleApp.cs +++ b/src/NzbDrone.Console/ConsoleApp.cs @@ -1,5 +1,4 @@ using System; -using System.Threading; using NLog; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Instrumentation; @@ -15,23 +14,18 @@ namespace NzbDrone.Console { try { - var startupArgs = new StartupArguments(args); + var startupArgs = new StartupContext(args); LogTargets.Register(startupArgs, false, true); Bootstrap.Start(startupArgs, new ConsoleAlerts()); } - catch (TerminateApplicationException) - { - } catch (Exception e) { + System.Console.WriteLine(""); + System.Console.WriteLine(""); Logger.FatalException("EPIC FAIL!", e); + System.Console.WriteLine("Press any key to exit..."); System.Console.ReadLine(); } - - while (true) - { - Thread.Sleep(10 * 60); - } } } } diff --git a/src/NzbDrone.Core.Test/MediaCoverTests/MediaCoverServiceFixture.cs b/src/NzbDrone.Core.Test/MediaCoverTests/MediaCoverServiceFixture.cs index 3a4317c23..a157164b3 100644 --- a/src/NzbDrone.Core.Test/MediaCoverTests/MediaCoverServiceFixture.cs +++ b/src/NzbDrone.Core.Test/MediaCoverTests/MediaCoverServiceFixture.cs @@ -17,7 +17,7 @@ namespace NzbDrone.Core.Test.MediaCoverTests [SetUp] public void Setup() { - Mocker.SetConstant(new AppFolderInfo(new DiskProvider(), Mocker.Resolve())); + Mocker.SetConstant(new AppFolderInfo(new DiskProvider(), Mocker.Resolve())); } [Test] diff --git a/src/NzbDrone.Host/ApplicationServer.cs b/src/NzbDrone.Host/ApplicationServer.cs index ab15f2292..6ab53244f 100644 --- a/src/NzbDrone.Host/ApplicationServer.cs +++ b/src/NzbDrone.Host/ApplicationServer.cs @@ -1,8 +1,6 @@ -using System; -using System.ServiceProcess; +using System.ServiceProcess; using NLog; using NzbDrone.Common.EnvironmentInfo; -using NzbDrone.Common.Processes; using NzbDrone.Core.Configuration; using NzbDrone.Host.Owin; @@ -10,6 +8,7 @@ namespace NzbDrone.Host { public interface INzbDroneServiceFactory { + bool IsServiceStopped { get; } ServiceBase Build(); void Start(); } @@ -19,20 +18,20 @@ namespace NzbDrone.Host private readonly IConfigFileProvider _configFileProvider; private readonly IRuntimeInfo _runtimeInfo; private readonly IHostController _hostController; - private readonly IProcessProvider _processProvider; private readonly PriorityMonitor _priorityMonitor; - private readonly IStartupArguments _startupArguments; + private readonly IStartupContext _startupContext; + private readonly IBrowserService _browserService; private readonly Logger _logger; - public NzbDroneServiceFactory(IConfigFileProvider configFileProvider, IHostController hostController, IRuntimeInfo runtimeInfo, - IProcessProvider processProvider, PriorityMonitor priorityMonitor, IStartupArguments startupArguments, Logger logger) + public NzbDroneServiceFactory(IConfigFileProvider configFileProvider, IHostController hostController, + IRuntimeInfo runtimeInfo, PriorityMonitor priorityMonitor, IStartupContext startupContext, IBrowserService browserService, Logger logger) { _configFileProvider = configFileProvider; _hostController = hostController; _runtimeInfo = runtimeInfo; - _processProvider = processProvider; _priorityMonitor = priorityMonitor; - _startupArguments = startupArguments; + _startupContext = startupContext; + _browserService = browserService; _logger = logger; } @@ -45,19 +44,10 @@ namespace NzbDrone.Host { _hostController.StartServer(); - if (!_startupArguments.Flags.Contains(StartupArguments.NO_BROWSER) && - _runtimeInfo.IsUserInteractive && - _configFileProvider.LaunchBrowser) + if (!_startupContext.Flags.Contains(StartupContext.NO_BROWSER) + && _configFileProvider.LaunchBrowser) { - try - { - _logger.Info("Starting default browser. {0}", _hostController.AppUrl); - _processProvider.OpenDefaultBrowser(_hostController.AppUrl); - } - catch (Exception e) - { - _logger.ErrorException("Failed to open URL in default browser.", e); - } + _browserService.LaunchWebUI(); } _priorityMonitor.Start(); @@ -68,8 +58,11 @@ namespace NzbDrone.Host _logger.Info("Attempting to stop application."); _hostController.StopServer(); _logger.Info("Application has finished stop routine."); + IsServiceStopped = true; } + public bool IsServiceStopped { get; private set; } + public ServiceBase Build() { return this; diff --git a/src/NzbDrone.Host/Bootstrap.cs b/src/NzbDrone.Host/Bootstrap.cs index fb1d23eec..952e9c17e 100644 --- a/src/NzbDrone.Host/Bootstrap.cs +++ b/src/NzbDrone.Host/Bootstrap.cs @@ -1,4 +1,7 @@ -using System.Reflection; +using System; +using System.Reflection; +using System.Threading; +using NLog; using NzbDrone.Common.Composition; using NzbDrone.Common.EnvironmentInfo; using NzbDrone.Common.Instrumentation; @@ -9,27 +12,131 @@ namespace NzbDrone.Host { public static class Bootstrap { - public static IContainer Start(StartupArguments args, IUserAlert userAlert) + private static IContainer _container; + private static readonly Logger Logger = NzbDroneLogger.GetLogger(); + + + public static void Start(StartupContext startupContext, IUserAlert userAlert, Action startCallback = null) { - var logger = NzbDroneLogger.GetLogger(); + try + { + + GlobalExceptionHandlers.Register(); + IgnoreCertErrorPolicy.Register(); + + Logger.Info("Starting NzbDrone Console. Version {0}", Assembly.GetExecutingAssembly().GetName().Version); + + + if (!PlatformValidation.IsValidate(userAlert)) + { + throw new TerminateApplicationException("Missing system requirements"); + } - GlobalExceptionHandlers.Register(); - IgnoreCertErrorPolicy.Register(); + _container = MainAppContainerBuilder.BuildContainer(startupContext); - logger.Info("Starting NzbDrone Console. Version {0}", Assembly.GetExecutingAssembly().GetName().Version); + var appMode = GetApplicationMode(startupContext); + + Start(appMode); + + if (startCallback != null) + { + startCallback(_container); + } + + SpinToExit(appMode); + } + catch (TerminateApplicationException e) + { + Logger.Info(e.Message); + } + } - if (!PlatformValidation.IsValidate(userAlert)) + private static void Start(ApplicationModes applicationModes) + { + if (!IsInUtilityMode(applicationModes)) { - throw new TerminateApplicationException(); + EnsureSingleInstance(applicationModes == ApplicationModes.Service); } - var container = MainAppContainerBuilder.BuildContainer(args); + DbFactory.RegisterDatabase(_container); + _container.Resolve().Route(applicationModes); + } - DbFactory.RegisterDatabase(container); - container.Resolve().Route(); + private static void SpinToExit(ApplicationModes applicationModes) + { + if (IsInUtilityMode(applicationModes)) + { + return; + } + + var serviceFactory = _container.Resolve(); - return container; + while (!serviceFactory.IsServiceStopped) + { + Thread.Sleep(1000); + } } + + private static void EnsureSingleInstance(bool isService) + { + var instancePolicy = _container.Resolve(); + + if (isService) + { + instancePolicy.KillAllOtherInstance(); + } + else + { + instancePolicy.PreventStartIfAlreadyRunning(); + } + } + + + + private static ApplicationModes GetApplicationMode(StartupContext startupContext) + { + if (startupContext.Flags.Contains(StartupContext.HELP)) + { + return ApplicationModes.Help; + } + + if (!OsInfo.IsLinux && startupContext.InstallService) + { + return ApplicationModes.InstallService; + } + + if (!OsInfo.IsLinux && startupContext.UninstallService) + { + return ApplicationModes.UninstallService; + } + + if (_container.Resolve().IsWindowsService) + { + return ApplicationModes.Service; + } + + return ApplicationModes.Interactive; + } + + + + private static bool IsInUtilityMode(ApplicationModes applicationMode) + { + switch (applicationMode) + { + case ApplicationModes.InstallService: + case ApplicationModes.UninstallService: + case ApplicationModes.Help: + { + return true; + } + default: + { + return false; + } + } + } + } } \ No newline at end of file diff --git a/src/NzbDrone.Host/BrowserService.cs b/src/NzbDrone.Host/BrowserService.cs new file mode 100644 index 000000000..eebb361bc --- /dev/null +++ b/src/NzbDrone.Host/BrowserService.cs @@ -0,0 +1,50 @@ +using System; +using NLog; +using NzbDrone.Common.EnvironmentInfo; +using NzbDrone.Common.Processes; +using NzbDrone.Core.Configuration; + +namespace NzbDrone.Host +{ + public interface IBrowserService + { + void LaunchWebUI(); + } + + public class BrowserService : IBrowserService + { + private readonly IProcessProvider _processProvider; + private readonly IConfigFileProvider _configFileProvider; + private readonly IRuntimeInfo _runtimeInfo; + private readonly Logger _logger; + + public BrowserService(IProcessProvider processProvider, IConfigFileProvider configFileProvider, IRuntimeInfo runtimeInfo, Logger logger) + { + _processProvider = processProvider; + _configFileProvider = configFileProvider; + _runtimeInfo = runtimeInfo; + _logger = logger; + } + + public void LaunchWebUI() + { + var url = string.Format("http://localhost:{0}", _configFileProvider.Port); + try + { + if (_runtimeInfo.IsUserInteractive) + { + _logger.Info("Starting default browser. {0}", url); + _processProvider.OpenDefaultBrowser(url); + } + else + { + _logger.Debug("none-interactive runtime. Won't attempt to open browser."); + } + } + catch (Exception e) + { + _logger.ErrorException("Couldn't open default browser to " + url, e); + } + } + } +} \ No newline at end of file diff --git a/src/NzbDrone.Host/MainAppContainerBuilder.cs b/src/NzbDrone.Host/MainAppContainerBuilder.cs index 16a8f0fda..4f85b35ec 100644 --- a/src/NzbDrone.Host/MainAppContainerBuilder.cs +++ b/src/NzbDrone.Host/MainAppContainerBuilder.cs @@ -10,12 +10,12 @@ namespace NzbDrone.Host { public class MainAppContainerBuilder : ContainerBuilderBase { - public static IContainer BuildContainer(StartupArguments args) + public static IContainer BuildContainer(StartupContext args) { return new MainAppContainerBuilder(args).Container; } - private MainAppContainerBuilder(StartupArguments args) + private MainAppContainerBuilder(StartupContext args) : base(args, "NzbDrone.Host", "NzbDrone.Common", "NzbDrone.Core", "NzbDrone.Api", "NzbDrone.SignalR") { diff --git a/src/NzbDrone.Host/NzbDrone.Host.csproj b/src/NzbDrone.Host/NzbDrone.Host.csproj index 4cc61b5ce..7a0f05c83 100644 --- a/src/NzbDrone.Host/NzbDrone.Host.csproj +++ b/src/NzbDrone.Host/NzbDrone.Host.csproj @@ -116,6 +116,8 @@ + + diff --git a/src/NzbDrone.Host/NzbDroneProcessService.cs b/src/NzbDrone.Host/NzbDroneProcessService.cs new file mode 100644 index 000000000..c13a46f5b --- /dev/null +++ b/src/NzbDrone.Host/NzbDroneProcessService.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using NLog; +using NzbDrone.Common.Processes; + +namespace NzbDrone.Host +{ + public interface ISingleInstancePolicy + { + void PreventStartIfAlreadyRunning(); + void KillAllOtherInstance(); + } + + public class SingleInstancePolicy : ISingleInstancePolicy + { + private readonly IProcessProvider _processProvider; + private readonly IBrowserService _browserService; + private readonly Logger _logger; + + public SingleInstancePolicy(IProcessProvider processProvider, IBrowserService browserService, Logger logger) + { + _processProvider = processProvider; + _browserService = browserService; + _logger = logger; + } + + public void PreventStartIfAlreadyRunning() + { + if (IsAlreadyRunning()) + { + _logger.Warn("Another instance of NzbDrone is already running."); + _browserService.LaunchWebUI(); + throw new TerminateApplicationException("Another instance is already running"); + } + } + + public void KillAllOtherInstance() + { + foreach (var processId in GetOtherNzbDroneProcessIds()) + { + _processProvider.Kill(processId); + } + } + + private bool IsAlreadyRunning() + { + return GetOtherNzbDroneProcessIds().Any(); + } + + private List GetOtherNzbDroneProcessIds() + { + var currentId = _processProvider.GetCurrentProcess().Id; + var consoleIds = _processProvider.FindProcessByName(ProcessProvider.NZB_DRONE_CONSOLE_PROCESS_NAME) + .Select(c => c.Id); + var winformIds = _processProvider.FindProcessByName(ProcessProvider.NZB_DRONE_PROCESS_NAME).Select(c => c.Id); + + var otherProcesses = consoleIds.Union(winformIds).Except(new[] { currentId }).ToList(); + + if (otherProcesses.Any()) + { + _logger.Info("{0} instance(s) of NzbDrone are running", otherProcesses.Count); + } + + return otherProcesses; + } + } +} \ No newline at end of file diff --git a/src/NzbDrone.Host/Owin/IHostController.cs b/src/NzbDrone.Host/Owin/IHostController.cs index 026bff89e..130b48d4b 100644 --- a/src/NzbDrone.Host/Owin/IHostController.cs +++ b/src/NzbDrone.Host/Owin/IHostController.cs @@ -2,7 +2,6 @@ { public interface IHostController { - string AppUrl { get; } void StartServer(); void StopServer(); } diff --git a/src/NzbDrone.Host/Owin/OwinHostController.cs b/src/NzbDrone.Host/Owin/OwinHostController.cs index d750ee646..1a5d4ae9b 100644 --- a/src/NzbDrone.Host/Owin/OwinHostController.cs +++ b/src/NzbDrone.Host/Owin/OwinHostController.cs @@ -107,11 +107,6 @@ namespace NzbDrone.Host.Owin } } - public string AppUrl - { - get { return string.Format("http://localhost:{0}", _configFileProvider.Port); } - } - public void StopServer() { if (_host == null) return; diff --git a/src/NzbDrone.Host/Router.cs b/src/NzbDrone.Host/Router.cs index 76d9e1583..900a3a062 100644 --- a/src/NzbDrone.Host/Router.cs +++ b/src/NzbDrone.Host/Router.cs @@ -1,7 +1,5 @@ -using System.ServiceProcess; -using NLog; +using NLog; using NzbDrone.Common; -using NzbDrone.Common.EnvironmentInfo; namespace NzbDrone.Host { @@ -9,28 +7,18 @@ namespace NzbDrone.Host { private readonly INzbDroneServiceFactory _nzbDroneServiceFactory; private readonly IServiceProvider _serviceProvider; - private readonly IStartupArguments _startupArguments; private readonly IConsoleService _consoleService; - private readonly IRuntimeInfo _runtimeInfo; private readonly Logger _logger; - public Router(INzbDroneServiceFactory nzbDroneServiceFactory, IServiceProvider serviceProvider, IStartupArguments startupArguments, - IConsoleService consoleService, IRuntimeInfo runtimeInfo, Logger logger) + public Router(INzbDroneServiceFactory nzbDroneServiceFactory, IServiceProvider serviceProvider, + IConsoleService consoleService, Logger logger) { _nzbDroneServiceFactory = nzbDroneServiceFactory; _serviceProvider = serviceProvider; - _startupArguments = startupArguments; _consoleService = consoleService; - _runtimeInfo = runtimeInfo; _logger = logger; } - public void Route() - { - var appMode = GetApplicationMode(); - Route(appMode); - } - public void Route(ApplicationModes applicationModes) { _logger.Info("Application mode: {0}", applicationModes); @@ -86,32 +74,6 @@ namespace NzbDrone.Host } } - private ApplicationModes GetApplicationMode() - { - if (!_runtimeInfo.IsUserInteractive && - OsInfo.IsWindows && - _serviceProvider.ServiceExist(ServiceProvider.NZBDRONE_SERVICE_NAME) && - _serviceProvider.GetStatus(ServiceProvider.NZBDRONE_SERVICE_NAME) == ServiceControllerStatus.StartPending) - { - return ApplicationModes.Service; - } - - if (_startupArguments.Flags.Contains(StartupArguments.HELP)) - { - return ApplicationModes.Help; - } - - if (!OsInfo.IsLinux && _startupArguments.Flags.Contains(StartupArguments.INSTALL_SERVICE)) - { - return ApplicationModes.InstallService; - } - - if (!OsInfo.IsLinux && _startupArguments.Flags.Contains(StartupArguments.UNINSTALL_SERVICE)) - { - return ApplicationModes.UninstallService; - } - return ApplicationModes.Interactive; - } } } diff --git a/src/NzbDrone.Host/TerminateApplicationException.cs b/src/NzbDrone.Host/TerminateApplicationException.cs index 04464bea3..734fb65d2 100644 --- a/src/NzbDrone.Host/TerminateApplicationException.cs +++ b/src/NzbDrone.Host/TerminateApplicationException.cs @@ -4,5 +4,9 @@ namespace NzbDrone.Host { public class TerminateApplicationException : ApplicationException { + public TerminateApplicationException(string reason) + : base("Application is being terminated. Reason : " + reason) + { + } } } \ No newline at end of file diff --git a/src/NzbDrone.Integration.Test/IntegrationTest.cs b/src/NzbDrone.Integration.Test/IntegrationTest.cs index 90a5ad426..aac665ea1 100644 --- a/src/NzbDrone.Integration.Test/IntegrationTest.cs +++ b/src/NzbDrone.Integration.Test/IntegrationTest.cs @@ -50,7 +50,7 @@ namespace NzbDrone.Integration.Test public IntegrationTest() { - new StartupArguments(); + new StartupContext(); LogManager.Configuration = new LoggingConfiguration(); var consoleTarget = new ConsoleTarget { Layout = "${level}: ${message} ${exception}" }; diff --git a/src/NzbDrone.Test.Common/LoggingTest.cs b/src/NzbDrone.Test.Common/LoggingTest.cs index 992d1588c..4d11d696a 100644 --- a/src/NzbDrone.Test.Common/LoggingTest.cs +++ b/src/NzbDrone.Test.Common/LoggingTest.cs @@ -12,7 +12,7 @@ namespace NzbDrone.Test.Common protected static void InitLogging() { - new StartupArguments(); + new StartupContext(); TestLogger = LogManager.GetLogger("TestLogger"); diff --git a/src/NzbDrone.Test.Common/TestBase.cs b/src/NzbDrone.Test.Common/TestBase.cs index d6382b397..470cba8e1 100644 --- a/src/NzbDrone.Test.Common/TestBase.cs +++ b/src/NzbDrone.Test.Common/TestBase.cs @@ -93,7 +93,7 @@ namespace NzbDrone.Test.Common Mocker.SetConstant(LogManager.GetLogger("TestLogger")); - Mocker.SetConstant(new StartupArguments(new string[0])); + Mocker.SetConstant(new StartupContext(new string[0])); LogManager.ReconfigExistingLoggers(); diff --git a/src/NzbDrone.Update.Test/StartNzbDroneService.cs b/src/NzbDrone.Update.Test/StartNzbDroneService.cs index 3051e529f..75fc9d917 100644 --- a/src/NzbDrone.Update.Test/StartNzbDroneService.cs +++ b/src/NzbDrone.Update.Test/StartNzbDroneService.cs @@ -32,7 +32,7 @@ namespace NzbDrone.Update.Test Subject.Start(AppType.Service, targetFolder); - Mocker.GetMock().Verify(c => c.SpawnNewProcess("c:\\NzbDrone\\NzbDrone.Console.exe", StartupArguments.NO_BROWSER), Times.Once()); + Mocker.GetMock().Verify(c => c.SpawnNewProcess("c:\\NzbDrone\\NzbDrone.Console.exe", StartupContext.NO_BROWSER), Times.Once()); ExceptionVerification.ExpectedWarns(1); } diff --git a/src/NzbDrone.Update/UpdateApp.cs b/src/NzbDrone.Update/UpdateApp.cs index 0f42e25b1..83531b153 100644 --- a/src/NzbDrone.Update/UpdateApp.cs +++ b/src/NzbDrone.Update/UpdateApp.cs @@ -28,7 +28,7 @@ namespace NzbDrone.Update { try { - var startupArgument = new StartupArguments(args); + var startupArgument = new StartupContext(args); LogTargets.Register(startupArgument, true, true); Console.WriteLine("Starting NzbDrone Update Client"); diff --git a/src/NzbDrone.Update/UpdateContainerBuilder.cs b/src/NzbDrone.Update/UpdateContainerBuilder.cs index faac971eb..5f99a618c 100644 --- a/src/NzbDrone.Update/UpdateContainerBuilder.cs +++ b/src/NzbDrone.Update/UpdateContainerBuilder.cs @@ -5,15 +5,15 @@ namespace NzbDrone.Update { public class UpdateContainerBuilder : ContainerBuilderBase { - private UpdateContainerBuilder(IStartupArguments startupArguments) - : base(startupArguments, "NzbDrone.Update", "NzbDrone.Common") + private UpdateContainerBuilder(IStartupContext startupContext) + : base(startupContext, "NzbDrone.Update", "NzbDrone.Common") { } - public static IContainer Build(IStartupArguments startupArguments) + public static IContainer Build(IStartupContext startupContext) { - return new UpdateContainerBuilder(startupArguments).Container; + return new UpdateContainerBuilder(startupContext).Container; } } } \ No newline at end of file diff --git a/src/NzbDrone.Update/UpdateEngine/StartNzbDrone.cs b/src/NzbDrone.Update/UpdateEngine/StartNzbDrone.cs index 211961a83..63079033a 100644 --- a/src/NzbDrone.Update/UpdateEngine/StartNzbDrone.cs +++ b/src/NzbDrone.Update/UpdateEngine/StartNzbDrone.cs @@ -73,7 +73,7 @@ namespace NzbDrone.Update.UpdateEngine _logger.Info("Starting {0}", fileName); var path = Path.Combine(installationFolder, fileName); - _processProvider.SpawnNewProcess(path, StartupArguments.NO_BROWSER); + _processProvider.SpawnNewProcess(path, StartupContext.NO_BROWSER); } } } \ No newline at end of file diff --git a/src/NzbDrone.sln b/src/NzbDrone.sln index 3430ec2b4..8e2ad6194 100644 --- a/src/NzbDrone.sln +++ b/src/NzbDrone.sln @@ -68,6 +68,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.SignalR.Ow EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "External", "External", "{F6E3A728-AE77-4D02-BAC8-82FBC1402DDA}" EndProject +Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "NzbDrone.Setup", "NzbDrone.Setup\NzbDrone.Setup.wixproj", "{F5958838-EBE5-4A76-A27F-5AFA9A8D7818}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 @@ -170,6 +172,10 @@ Global {2B8C6DAD-4D85-41B1-83FD-248D9F347522}.Debug|x86.Build.0 = Debug|x86 {2B8C6DAD-4D85-41B1-83FD-248D9F347522}.Release|x86.ActiveCfg = Release|x86 {2B8C6DAD-4D85-41B1-83FD-248D9F347522}.Release|x86.Build.0 = Release|x86 + {F5958838-EBE5-4A76-A27F-5AFA9A8D7818}.Debug|x86.ActiveCfg = Debug|x86 + {F5958838-EBE5-4A76-A27F-5AFA9A8D7818}.Debug|x86.Build.0 = Debug|x86 + {F5958838-EBE5-4A76-A27F-5AFA9A8D7818}.Release|x86.ActiveCfg = Release|x86 + {F5958838-EBE5-4A76-A27F-5AFA9A8D7818}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -191,9 +197,9 @@ Global {3DCA7B58-B8B3-49AC-9D9E-56F4A0460976} = {486ADF86-DD89-4E19-B805-9D94F19800D9} {95C11A9E-56ED-456A-8447-2C89C1139266} = {486ADF86-DD89-4E19-B805-9D94F19800D9} {D12F7F2F-8A3C-415F-88FA-6DD061A84869} = {486ADF86-DD89-4E19-B805-9D94F19800D9} + {1B9A82C4-BCA1-4834-A33E-226F17BE070B} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA} {2B8C6DAD-4D85-41B1-83FD-248D9F347522} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA} {F6FC6BE7-0847-4817-A1ED-223DC647C3D7} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA} - {1B9A82C4-BCA1-4834-A33E-226F17BE070B} = {F6E3A728-AE77-4D02-BAC8-82FBC1402DDA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution EnterpriseLibraryConfigurationToolBinariesPath = packages\Unity.2.1.505.0\lib\NET35;packages\Unity.2.1.505.2\lib\NET35 diff --git a/src/NzbDrone/SysTray/SysTrayApp.cs b/src/NzbDrone/SysTray/SysTrayApp.cs index 3068bcd5c..62e73a650 100644 --- a/src/NzbDrone/SysTray/SysTrayApp.cs +++ b/src/NzbDrone/SysTray/SysTrayApp.cs @@ -2,8 +2,7 @@ using System.ComponentModel; using System.Windows.Forms; using NzbDrone.Common.EnvironmentInfo; -using NzbDrone.Common.Processes; -using NzbDrone.Host.Owin; +using NzbDrone.Host; namespace NzbDrone.SysTray { @@ -14,16 +13,14 @@ namespace NzbDrone.SysTray public class SystemTrayApp : Form, ISystemTrayApp { - private readonly IProcessProvider _processProvider; - private readonly IHostController _hostController; + private readonly IBrowserService _browserService; private readonly NotifyIcon _trayIcon = new NotifyIcon(); private readonly ContextMenu _trayMenu = new ContextMenu(); - public SystemTrayApp(IProcessProvider processProvider, IHostController hostController) + public SystemTrayApp(IBrowserService browserService) { - _processProvider = processProvider; - _hostController = hostController; + _browserService = browserService; } @@ -84,7 +81,7 @@ namespace NzbDrone.SysTray { try { - _processProvider.OpenDefaultBrowser(_hostController.AppUrl); + _browserService.LaunchWebUI(); } catch (Exception) { diff --git a/src/NzbDrone/WindowsApp.cs b/src/NzbDrone/WindowsApp.cs index 5b20de23b..d2b82a2a1 100644 --- a/src/NzbDrone/WindowsApp.cs +++ b/src/NzbDrone/WindowsApp.cs @@ -10,22 +10,22 @@ namespace NzbDrone { public static class WindowsApp { - private static readonly Logger Logger = NzbDroneLogger.GetLogger(); + private static readonly Logger Logger = NzbDroneLogger.GetLogger(); public static void Main(string[] args) { try { - var startupArgs = new StartupArguments(args); + var startupArgs = new StartupContext(args); LogTargets.Register(startupArgs, false, true); - var container = Bootstrap.Start(startupArgs, new MessageBoxUserAlert()); - container.Register(); - container.Resolve().Start(); - } - catch (TerminateApplicationException) - { + Bootstrap.Start(startupArgs, new MessageBoxUserAlert(), container => + { + container.Register(); + var trayApp = container.Resolve(); + trayApp.Start(); + }); } catch (Exception e) { @@ -34,5 +34,7 @@ namespace NzbDrone MessageBox.Show(text: message, buttons: MessageBoxButtons.OK, icon: MessageBoxIcon.Error, caption: "Epic Fail!"); } } + + } } diff --git a/wix/bin/WixIIsExtension.dll b/wix/bin/WixIIsExtension.dll deleted file mode 100644 index 652e61ea4..000000000 Binary files a/wix/bin/WixIIsExtension.dll and /dev/null differ diff --git a/wix/bin/WixNetFxExtension.dll b/wix/bin/WixNetFxExtension.dll deleted file mode 100644 index c82d7ad51..000000000 Binary files a/wix/bin/WixNetFxExtension.dll and /dev/null differ diff --git a/wix/bin/WixUIExtension.dll b/wix/bin/WixUIExtension.dll deleted file mode 100644 index 3ef5de577..000000000 Binary files a/wix/bin/WixUIExtension.dll and /dev/null differ diff --git a/wix/bin/WixUtilExtension.dll b/wix/bin/WixUtilExtension.dll deleted file mode 100644 index b729c196d..000000000 Binary files a/wix/bin/WixUtilExtension.dll and /dev/null differ diff --git a/wix/bin/WixVSExtension.dll b/wix/bin/WixVSExtension.dll deleted file mode 100644 index b93eb29e1..000000000 Binary files a/wix/bin/WixVSExtension.dll and /dev/null differ diff --git a/wix/bin/candle.exe b/wix/bin/candle.exe deleted file mode 100644 index 9eb01f8aa..000000000 Binary files a/wix/bin/candle.exe and /dev/null differ diff --git a/wix/bin/candle.exe.config b/wix/bin/candle.exe.config deleted file mode 100644 index b0602249b..000000000 --- a/wix/bin/candle.exe.config +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - diff --git a/wix/bin/darice.cub b/wix/bin/darice.cub deleted file mode 100644 index dab45677f..000000000 Binary files a/wix/bin/darice.cub and /dev/null differ diff --git a/wix/bin/heat.exe b/wix/bin/heat.exe deleted file mode 100644 index 5a62476eb..000000000 Binary files a/wix/bin/heat.exe and /dev/null differ diff --git a/wix/bin/heat.exe.config b/wix/bin/heat.exe.config deleted file mode 100644 index 78b87a22d..000000000 --- a/wix/bin/heat.exe.config +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - diff --git a/wix/bin/light.exe b/wix/bin/light.exe deleted file mode 100644 index 660b2cc91..000000000 Binary files a/wix/bin/light.exe and /dev/null differ diff --git a/wix/bin/light.exe.config b/wix/bin/light.exe.config deleted file mode 100644 index b0602249b..000000000 --- a/wix/bin/light.exe.config +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - diff --git a/wix/bin/wconsole.dll b/wix/bin/wconsole.dll deleted file mode 100644 index d16ee819c..000000000 Binary files a/wix/bin/wconsole.dll and /dev/null differ diff --git a/wix/bin/winterop.dll b/wix/bin/winterop.dll deleted file mode 100644 index 2975088f9..000000000 Binary files a/wix/bin/winterop.dll and /dev/null differ diff --git a/wix/bin/wix.dll b/wix/bin/wix.dll deleted file mode 100644 index b26665f1d..000000000 Binary files a/wix/bin/wix.dll and /dev/null differ diff --git a/wix/nzbdrone.wxs b/wix/nzbdrone.wxs deleted file mode 100644 index c21e8ab51..000000000 --- a/wix/nzbdrone.wxs +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - NETFRAMEWORK40FULL - - - - - - - - - \ No newline at end of file diff --git a/wix/package.bat b/wix/package.bat deleted file mode 100644 index 66dec2dd3..000000000 --- a/wix/package.bat +++ /dev/null @@ -1,5 +0,0 @@ -bin\heat.exe dir ..\_output\ -out _output.wxs -cg OUTPUT_GROUP -dr BIN_DIR -gg -scom -srd -sfrag -sreg -suid -svb6 - -"bin\candle.exe" nzbdrone.wxs _output.wxs -ext WixNetFxExtension -ext WixUIExtension -"bin\light.exe" nzbdrone.wixobj _output.wixobj -out "nzbdrone.msi" -ext WixNetFxExtension -ext WixUIExtension -b ..\_output -