Skip to content

Commit

Permalink
Initial build of NuGet Server hosting service and website
Browse files Browse the repository at this point in the history
  • Loading branch information
svenkle committed Apr 29, 2017
1 parent cd78a17 commit 5692a5d
Show file tree
Hide file tree
Showing 22 changed files with 999 additions and 1 deletion.
36 changes: 36 additions & 0 deletions NuGet Server.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Svenkle.NuGetServer.Service", "src\Svenkle.NuGetServer.Service\Svenkle.NuGetServer.Service.csproj", "{849C4739-2DC5-4112-97B0-23907F39A44E}"
ProjectSection(ProjectDependencies) = postProject
{4DBC0822-4CF2-41E3-A3E1-D87C64856985} = {4DBC0822-4CF2-41E3-A3E1-D87C64856985}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Svenkle.NuGetServer.Website", "src\Svenkle.NuGetServer.Website\Svenkle.NuGetServer.Website.csproj", "{4DBC0822-4CF2-41E3-A3E1-D87C64856985}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{50F4F821-6975-48B8-99D1-28536BE4047D}"
ProjectSection(SolutionItems) = preProject
build\Website.props = build\Website.props
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{849C4739-2DC5-4112-97B0-23907F39A44E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{849C4739-2DC5-4112-97B0-23907F39A44E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{849C4739-2DC5-4112-97B0-23907F39A44E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{849C4739-2DC5-4112-97B0-23907F39A44E}.Release|Any CPU.Build.0 = Release|Any CPU
{4DBC0822-4CF2-41E3-A3E1-D87C64856985}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4DBC0822-4CF2-41E3-A3E1-D87C64856985}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4DBC0822-4CF2-41E3-A3E1-D87C64856985}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4DBC0822-4CF2-41E3-A3E1-D87C64856985}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
# nuget-server
# NuGet Server
16 changes: 16 additions & 0 deletions build/Website.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TransformWebConfigEnabled>True</TransformWebConfigEnabled>
<AutoParameterizationWebConfigConnectionStrings>False</AutoParameterizationWebConfigConnectionStrings>
<OutputFolder>$(MSBuildProjectDirectory)\..\Svenkle.NuGetServer.Service\bin\$(Configuration)\Website</OutputFolder>
</PropertyGroup>
<Target Name="PublishWebsiteToService" AfterTargets="Build" DependsOnTargets="PipelinePreDeployCopyAllFilesToOneFolder">
<ItemGroup>
<Files Include="$(MSBuildProjectDirectory)\obj\$(Configuration)\Package\PackageTmp\**\*.*" />
</ItemGroup>
<Copy SourceFiles="@(Files)" DestinationFiles="@(Files->'$(OutputFolder)\%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="true" />
</Target>
<Target Name="CleanWebsiteInService" AfterTargets="Clean">
<RemoveDir Directories="$(OutputFolder)" Condition="Exists('$(OutputFolder)')"/>
</Target>
</Project>
Binary file added resources/iisexpress.msi
Binary file not shown.
9 changes: 9 additions & 0 deletions src/Svenkle.NuGetServer.Service/App.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
</startup>
<appSettings>
<add key="port" value="8080"/>
</appSettings>
</configuration>
12 changes: 12 additions & 0 deletions src/Svenkle.NuGetServer.Service/Exception.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;

namespace Svenkle.NuGetServer.Service
{
public class PrematureTerminationException : Exception
{
public PrematureTerminationException(string message) : base(message)
{

}
}
}
172 changes: 172 additions & 0 deletions src/Svenkle.NuGetServer.Service/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
using System;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.ServiceProcess;
using Microsoft.VisualBasic.FileIO;

namespace Svenkle.NuGetServer.Service
{
public class Service : ServiceBase
{
private Process _hostRunnerProcess;
private ProcessStartInfo _hostRunnerProcessStartInfo;
private const string TempFolderName = "NuGetServer";
private const string WebsiteFolderName = "Website";
private const string HostRunnerFolderName = "IIS Express";
private const string HostRunnerExecutableFilename = "iisexpress.exe";
private const string HostRunnerInstallationFilename = "iisexpress.msi";
private const string HostRunnerConfigurationFolderName = "user";
private string _temporaryPath;
private string _rootFolderPath;
private string _hostFolderPath;
private string _websiteFolderPath;
private string _hostInstanceConfigurationPath;
private string _hostInstanceFilePath;
private string _hostRunnerArguments;
private int _port;

private static void Main(string[] args)
{

var service = new Service();
if (Environment.UserInteractive)
{
Console.WriteLine($"{typeof(Service).Namespace}");
service.OnStart(args);
Console.ReadLine();
service.Stop();
}
else
{
Run(service);
}
}

protected override void OnStart(string[] args)
{
AppDomain.CurrentDomain.UnhandledException += ServiceOnUnhandledException;
AppDomain.CurrentDomain.ProcessExit += ServiceOnProcessExit;

_temporaryPath = Path.Combine(Path.GetTempPath(), TempFolderName);
_rootFolderPath = Path.GetDirectoryName(new Uri(typeof(Service).Assembly.CodeBase).LocalPath);
_hostFolderPath = Path.Combine(_rootFolderPath, HostRunnerFolderName);
_websiteFolderPath = Path.Combine(_rootFolderPath, WebsiteFolderName);
_hostInstanceFilePath = Path.Combine(_hostFolderPath, HostRunnerExecutableFilename);
_hostInstanceConfigurationPath = Path.Combine(_hostFolderPath, HostRunnerConfigurationFolderName);

ExtractHostRunner();
ConfigureHostRunner();
StartHostRunner();
}

protected override void OnStop()
{
StopHostRunner();
}

private void StartHostRunner()
{
_hostRunnerProcess.Start();
_hostRunnerProcess.BeginErrorReadLine();
}

private void StopHostRunner()
{
if (_hostRunnerProcess != null && !_hostRunnerProcess.HasExited)
{
_hostRunnerProcess.CancelErrorRead();
_hostRunnerProcess.Kill();
_hostRunnerProcess = null;
}
}

private void ExtractHostRunner()
{
var hostRunnerResourceFilePath = ExtractResource(Resources.iisexpress, HostRunnerInstallationFilename);
var extractedHostRunnerTemporaryFolder = ExtractWindowsInstallPackage(hostRunnerResourceFilePath);
var extractedHostRunnerFolder = Path.Combine(extractedHostRunnerTemporaryFolder, "WowOnly");

if (!Directory.Exists(_hostFolderPath))
Directory.CreateDirectory(_hostFolderPath);

if (!Directory.Exists(_hostInstanceConfigurationPath))
Directory.CreateDirectory(_hostInstanceConfigurationPath);

// Use a VB Move command as C# doesn't allow moving between volumes
FileSystem.MoveDirectory(extractedHostRunnerFolder, _hostFolderPath, true);
}

private void ConfigureHostRunner()
{
if (!int.TryParse(ConfigurationManager.AppSettings["port"], out _port))
_port = 8080;

_hostRunnerArguments = $"/path:\"{_websiteFolderPath}\" /port:{_port} /systray:{true} /userhome:\"{_hostInstanceConfigurationPath}\" /trace:error";
_hostRunnerProcessStartInfo = new ProcessStartInfo(_hostInstanceFilePath, _hostRunnerArguments)
{
WindowStyle = ProcessWindowStyle.Hidden,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
RedirectStandardInput = false
};

_hostRunnerProcess = new Process
{
StartInfo = _hostRunnerProcessStartInfo,
EnableRaisingEvents = true
};

_hostRunnerProcess.Exited += HostRunnerOnExited;
_hostRunnerProcess.ErrorDataReceived += HostRunnerOnErrorDataReceived;
}

private void HostRunnerOnExited(object sender, EventArgs eventArgs)
{
Console.WriteLine(nameof(HostRunnerOnExited));
Stop();
}

private void ServiceOnProcessExit(object sender, EventArgs eventArgs)
{
Console.WriteLine(nameof(ServiceOnProcessExit));
StopHostRunner();
}

private string ExtractWindowsInstallPackage(string packageFilePath)
{
var extractedFolderPath = Path.Combine(_temporaryPath, Path.GetFileNameWithoutExtension(packageFilePath));
var process = Process.Start("msiexec.exe", $"/a \"{packageFilePath}\" /qb TARGETDIR=\"{extractedFolderPath}\" /quiet");
process.WaitForExit();
return extractedFolderPath;
}

private string ExtractResource(byte[] resourceData, string resourceName)
{
if (!Directory.Exists(_temporaryPath))
Directory.CreateDirectory(_temporaryPath);

var extractedFilePath = Path.Combine(_temporaryPath, resourceName);

using (var stream = new MemoryStream(resourceData))
using (var file = File.Create(extractedFilePath))
{
stream.CopyTo(file);
return extractedFilePath;
}
}

private void HostRunnerOnErrorDataReceived(object sender, DataReceivedEventArgs dataReceivedEventArgs)
{
Console.WriteLine(dataReceivedEventArgs.Data);
StopHostRunner();
}

private void ServiceOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
Console.WriteLine((Exception)e.ExceptionObject);
StopHostRunner();
}
}
}
34 changes: 34 additions & 0 deletions src/Svenkle.NuGetServer.Service/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using System.Reflection;
using System.Runtime.InteropServices;

// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Svenkle.NuGetServer.Service")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("NuGet Server")]
[assembly: AssemblyCopyright("Copyright © Svenkle 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]

// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]

// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("849c4739-2dc5-4112-97b0-23907f39a44e")]

// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.0.*")]
73 changes: 73 additions & 0 deletions src/Svenkle.NuGetServer.Service/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 5692a5d

Please sign in to comment.