diff --git a/Common/Constants.cs b/Common/Constants.cs
index d39f363a2..bb6ae5e4e 100644
--- a/Common/Constants.cs
+++ b/Common/Constants.cs
@@ -6,5 +6,14 @@ public static class Constants
public const string MappedLiveSite = "/_app";
public const string MappedDevSite = "/_devapp";
public const string RepositoryPath = "repository";
+
+ public const string LockPath = "locks";
+ public const string DeploymentLockFile = "deployments.lock";
+ public const string InitLockFile = "init.lock";
+
+ public const string DeploymentCachePath = "deployments";
+ public const string TracePath = @"LogFiles\Git\trace";
+ public const string DeploySettingsPath = "settings.xml";
+ public const string TraceFile = "trace.xml";
}
}
\ No newline at end of file
diff --git a/Kudu.Console/Kudu.Console.csproj b/Kudu.Console/Kudu.Console.csproj
new file mode 100644
index 000000000..794666deb
--- /dev/null
+++ b/Kudu.Console/Kudu.Console.csproj
@@ -0,0 +1,84 @@
+
+
+
+ Debug
+ x86
+ 8.0.30703
+ 2.0
+ {E2B0EE28-8C96-497A-AB08-605B9FD841E9}
+ Exe
+ Properties
+ Kudu.Console
+ kudu
+ v4.0
+
+
+ 512
+ ..\
+ true
+
+
+ x86
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ x86
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+ ..\packages\System.IO.Abstractions.1.4.0.12\lib\net35\System.IO.Abstractions.dll
+
+
+
+
+
+
+
+
+
+ Constants.cs
+
+
+
+
+
+
+
+
+
+
+ {EC0ED988-2C60-4F31-A434-645E048BFD95}
+ Kudu.Contracts
+
+
+ {5320177C-725A-44BD-8FA6-F88D9725B46C}
+ Kudu.Core
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Kudu.Console/Program.cs b/Kudu.Console/Program.cs
new file mode 100644
index 000000000..5bda90729
--- /dev/null
+++ b/Kudu.Console/Program.cs
@@ -0,0 +1,132 @@
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Abstractions;
+using Kudu.Core;
+using Kudu.Core.Deployment;
+using Kudu.Core.Infrastructure;
+using Kudu.Core.SourceControl.Git;
+using Kudu.Core.Tracing;
+
+namespace Kudu.Console
+{
+ class Program
+ {
+ static int Main(string[] args)
+ {
+ if (args.Length < 2)
+ {
+ System.Console.WriteLine("Usage: kudu.exe {appRoot} {wapTargets}");
+ return 1;
+ }
+
+ while (!System.Diagnostics.Debugger.IsAttached)
+ {
+
+ }
+
+ System.Environment.SetEnvironmentVariable("GIT_DIR", null, System.EnvironmentVariableTarget.Process);
+
+ var appRoot = args[0];
+ var wapTargets = args[1];
+ string nugetCachePath = null;
+
+ IEnvironment env = GetEnvironment(appRoot, nugetCachePath);
+
+ // Setup the trace
+ string tracePath = Path.Combine(env.ApplicationRootPath, Constants.TracePath, Constants.TraceFile);
+ var traceFactory = new TracerFactory(() => new Tracer(tracePath));
+
+ // Calculate the lock path
+ string lockPath = Path.Combine(env.ApplicationRootPath, Constants.LockPath);
+ string deploymentLockPath = Path.Combine(lockPath, Constants.DeploymentLockFile);
+ var deploymentLock = new LockFile(traceFactory, deploymentLockPath);
+
+ var fs = new FileSystem();
+ var buildPropertyProvider = new BuildPropertyProvider(wapTargets);
+ var builderFactory = new SiteBuilderFactory(buildPropertyProvider, env);
+ var serverRepository = new GitDeploymentRepository(env.DeploymentRepositoryPath, traceFactory);
+
+ var logger = new ConsoleLogger();
+ var deploymentManager = new DeploymentManager(serverRepository,
+ builderFactory,
+ env,
+ fs,
+ traceFactory,
+ deploymentLock,
+ logger);
+
+ try
+ {
+ deploymentManager.Deploy();
+ }
+ catch(System.Exception ex)
+ {
+ System.Console.Error.WriteLine(ex.Message);
+
+ throw;
+ }
+
+ if (logger.HasErrors)
+ {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ private static IEnvironment GetEnvironment(string root, string nugetCachePath)
+ {
+ string deployPath = Path.Combine(root, Constants.WebRoot);
+ string deployCachePath = Path.Combine(root, Constants.DeploymentCachePath);
+ string deploymentRepositoryPath = Path.Combine(root, Constants.RepositoryPath);
+ string tempPath = Path.GetTempPath();
+ string deploymentTempPath = Path.Combine(tempPath, Constants.RepositoryPath);
+
+ return new Environment(root,
+ tempPath,
+ () => deploymentRepositoryPath,
+ () => null,
+ deployPath,
+ deployCachePath,
+ nugetCachePath);
+ }
+
+ private class BuildPropertyProvider : IBuildPropertyProvider
+ {
+ private readonly string _extensionsPath;
+
+ public BuildPropertyProvider(string extensionsPath)
+ {
+ _extensionsPath = extensionsPath;
+ }
+
+ public IDictionary GetProperties()
+ {
+ return new Dictionary {
+ { "MSBuildExtensionsPath32", _extensionsPath }
+ };
+ }
+ }
+
+ private class ConsoleLogger : ILogger
+ {
+ public ILogger Log(string value, LogEntryType type)
+ {
+ if (type == LogEntryType.Error)
+ {
+ HasErrors = true;
+
+ System.Console.Error.WriteLine(value);
+ }
+ else
+ {
+ System.Console.WriteLine(value);
+ }
+
+ return NullLogger.Instance;
+ }
+
+ public bool HasErrors { get; set; }
+ }
+ }
+}
diff --git a/Kudu.Console/Properties/AssemblyInfo.cs b/Kudu.Console/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..85a2d3f44
--- /dev/null
+++ b/Kudu.Console/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+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("Kudu.Console")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("Microsoft")]
+[assembly: AssemblyProduct("Kudu.Console")]
+[assembly: AssemblyCopyright("Copyright © Microsoft 2012")]
+[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("1d669d37-1397-4533-8bb7-561c46217f5d")]
+
+// 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("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/Kudu.Console/app.config b/Kudu.Console/app.config
new file mode 100644
index 000000000..e36560333
--- /dev/null
+++ b/Kudu.Console/app.config
@@ -0,0 +1,3 @@
+
+
+
diff --git a/Kudu.Console/packages.config b/Kudu.Console/packages.config
new file mode 100644
index 000000000..66ba7d7fa
--- /dev/null
+++ b/Kudu.Console/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/Kudu.Contracts/Deployment/IDeploymentCommandGenerator.cs b/Kudu.Contracts/Deployment/IDeploymentCommandGenerator.cs
new file mode 100644
index 000000000..6eb9a6a3a
--- /dev/null
+++ b/Kudu.Contracts/Deployment/IDeploymentCommandGenerator.cs
@@ -0,0 +1,7 @@
+namespace Kudu.Core.Deployment
+{
+ public interface IDeploymentCommandGenerator
+ {
+ string GetDeploymentCommand();
+ }
+}
diff --git a/Kudu.Contracts/IEnvironment.cs b/Kudu.Contracts/IEnvironment.cs
index 7312d1984..0906bce58 100644
--- a/Kudu.Contracts/IEnvironment.cs
+++ b/Kudu.Contracts/IEnvironment.cs
@@ -10,6 +10,5 @@ public interface IEnvironment
string ApplicationRootPath { get; }
string NuGetCachePath { get; }
string TempPath { get; }
- string AppName { get; }
}
}
diff --git a/Kudu.Contracts/Infrastructure/TaskExtensions.cs b/Kudu.Contracts/Infrastructure/TaskExtensions.cs
index 094220485..44691df09 100644
--- a/Kudu.Contracts/Infrastructure/TaskExtensions.cs
+++ b/Kudu.Contracts/Infrastructure/TaskExtensions.cs
@@ -60,7 +60,7 @@ public static void Catch(this Task task, Action handler)
Trace.TraceError(t.Exception.Message);
handler(t.Exception);
}
- }, TaskContinuationOptions.OnlyOnFaulted);
+ }, TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously);
}
public static Task Then(this Task task, Action continuation, CancellationToken cancellationToken = default(CancellationToken))
diff --git a/Kudu.Contracts/Kudu.Contracts.csproj b/Kudu.Contracts/Kudu.Contracts.csproj
index f92695311..07fa8c566 100644
--- a/Kudu.Contracts/Kudu.Contracts.csproj
+++ b/Kudu.Contracts/Kudu.Contracts.csproj
@@ -51,6 +51,7 @@
+
@@ -61,6 +62,7 @@
+
diff --git a/Kudu.Contracts/SourceControl/IDeploymentRepository.cs b/Kudu.Contracts/SourceControl/IDeploymentRepository.cs
new file mode 100644
index 000000000..d947a035a
--- /dev/null
+++ b/Kudu.Contracts/SourceControl/IDeploymentRepository.cs
@@ -0,0 +1,13 @@
+using Kudu.Core.SourceControl;
+
+namespace Kudu.Core.SourceControl
+{
+ public interface IDeploymentRepository
+ {
+ void Clean();
+ ChangeSet GetChangeSet(string id);
+ PushInfo GetPushInfo();
+ void Update();
+ void Update(string id);
+ }
+}
diff --git a/Kudu.Contracts/SourceControl/IServerRepository.cs b/Kudu.Contracts/SourceControl/IServerRepository.cs
index fdb571ffd..0aa7878c2 100644
--- a/Kudu.Contracts/SourceControl/IServerRepository.cs
+++ b/Kudu.Contracts/SourceControl/IServerRepository.cs
@@ -4,15 +4,10 @@ namespace Kudu.Core.SourceControl
{
public interface IServerRepository
{
- string CurrentId { get; }
bool Exists { get; }
- PushInfo GetPushInfo();
bool Initialize(RepositoryConfiguration configuration);
ChangeSet Initialize(RepositoryConfiguration configuration, string path);
- ChangeSet GetChangeSet(string id);
- void Update(string id);
- void Update();
- void Clean();
RepositoryType GetRepositoryType();
+ void Clean();
}
}
diff --git a/Kudu.Core/Deployment/CascadeLogger.cs b/Kudu.Core/Deployment/CascadeLogger.cs
new file mode 100644
index 000000000..34b6248fe
--- /dev/null
+++ b/Kudu.Core/Deployment/CascadeLogger.cs
@@ -0,0 +1,20 @@
+namespace Kudu.Core.Deployment
+{
+ internal class CascadeLogger : ILogger
+ {
+ private readonly ILogger _primary;
+ private readonly ILogger _secondary;
+
+ public CascadeLogger(ILogger primary, ILogger secondary)
+ {
+ _primary = primary;
+ _secondary = secondary;
+ }
+
+ public ILogger Log(string value, LogEntryType type)
+ {
+ _secondary.Log(value, type);
+ return _primary.Log(value, type);
+ }
+ }
+}
diff --git a/Kudu.Core/Deployment/DeploymentManager.cs b/Kudu.Core/Deployment/DeploymentManager.cs
index 48db97aba..3d3f669bb 100644
--- a/Kudu.Core/Deployment/DeploymentManager.cs
+++ b/Kudu.Core/Deployment/DeploymentManager.cs
@@ -4,23 +4,23 @@
using System.IO;
using System.IO.Abstractions;
using System.Linq;
-using Kudu.Contracts;
using Kudu.Contracts.Infrastructure;
+using Kudu.Contracts.Tracing;
using Kudu.Core.Infrastructure;
-using Kudu.Core.Tracing;
using Kudu.Core.SourceControl;
-using Kudu.Contracts.Tracing;
+using Kudu.Core.Tracing;
namespace Kudu.Core.Deployment
{
public class DeploymentManager : IDeploymentManager
{
- private readonly IServerRepository _serverRepository;
+ private readonly IDeploymentRepository _serverRepository;
private readonly ISiteBuilderFactory _builderFactory;
private readonly IEnvironment _environment;
private readonly IFileSystem _fileSystem;
private readonly ITraceFactory _traceFactory;
private readonly IOperationLock _deploymentLock;
+ private readonly ILogger _globalLogger;
private const string StatusFile = "status.xml";
private const string LogFile = "log.xml";
@@ -29,12 +29,13 @@ public class DeploymentManager : IDeploymentManager
public event Action StatusChanged;
- public DeploymentManager(IServerRepository serverRepository,
+ public DeploymentManager(IDeploymentRepository serverRepository,
ISiteBuilderFactory builderFactory,
IEnvironment environment,
IFileSystem fileSystem,
ITraceFactory traceFactory,
- IOperationLock deploymentLock)
+ IOperationLock deploymentLock,
+ ILogger globalLogger)
{
_serverRepository = serverRepository;
_builderFactory = builderFactory;
@@ -42,6 +43,7 @@ public DeploymentManager(IServerRepository serverRepository,
_fileSystem = fileSystem;
_traceFactory = traceFactory;
_deploymentLock = deploymentLock;
+ _globalLogger = globalLogger ?? NullLogger.Instance;
}
private string ActiveDeploymentId
@@ -222,6 +224,8 @@ public void Deploy()
else
{
tracer.Trace("Non-master branch deployed {0}", pushInfo.Branch.Name);
+
+ _globalLogger.Log("Non-master branch deployed {0}", pushInfo.Branch.Name);
}
ReportCompleted();
@@ -236,6 +240,8 @@ public void Deploy()
{
tracer.Trace("Deployment '{0}' already active", id);
+ _globalLogger.Log("Deployment '{0}' already active", id);
+
ReportCompleted();
deployStep.Dispose();
return;
@@ -255,6 +261,8 @@ public void Deploy()
}
catch (Exception ex)
{
+ _globalLogger.Log(ex);
+
tracer.TraceError(ex);
if (deployStep != null)
@@ -377,6 +385,8 @@ private void Build(string id, ITracer tracer, IDisposable deployStep)
}
catch (Exception ex)
{
+ _globalLogger.Log(ex);
+
tracer.TraceError(ex);
innerLogger.Log(ex);
@@ -412,6 +422,8 @@ private void Build(string id, ITracer tracer, IDisposable deployStep)
})
.Catch(ex =>
{
+ _globalLogger.Log(ex.GetBaseException());
+
// End the build step
buildStep.Dispose();
@@ -610,7 +622,9 @@ private DeploymentStatusFile CreateStatusFile(string id)
private ILogger GetLogger(string id)
{
- return new XmlLogger(_fileSystem, GetLogPath(id));
+ var path = GetLogPath(id);
+ var xmlLogger = new XmlLogger(_fileSystem, path);
+ return new CascadeLogger(xmlLogger, _globalLogger);
}
private string GetStatusFilePath(string id, bool ensureDirectory = true)
diff --git a/Kudu.Core/Deployment/NullLogger.cs b/Kudu.Core/Deployment/NullLogger.cs
new file mode 100644
index 000000000..cb9531886
--- /dev/null
+++ b/Kudu.Core/Deployment/NullLogger.cs
@@ -0,0 +1,12 @@
+namespace Kudu.Core.Deployment
+{
+ public class NullLogger : ILogger
+ {
+ public static NullLogger Instance = new NullLogger();
+
+ public ILogger Log(string value, LogEntryType type)
+ {
+ return Instance;
+ }
+ }
+}
diff --git a/Kudu.Core/Environment.cs b/Kudu.Core/Environment.cs
index 1dfa94cd6..4a1727ae1 100644
--- a/Kudu.Core/Environment.cs
+++ b/Kudu.Core/Environment.cs
@@ -1,6 +1,7 @@
using System;
using Kudu.Core.Infrastructure;
using Kudu.Core.SourceControl;
+using System.IO;
namespace Kudu.Core
{
@@ -12,16 +13,14 @@ public class Environment : IEnvironment
private readonly Func _deploymentRepositoryPathResolver;
private readonly Func _repositoryPathResolver;
- public Environment(string appName,
- string applicationRootPath,
- string tempPath,
+ public Environment(string applicationRootPath,
+ string tempPath,
Func deploymentRepositoryPathResolver,
Func repositoryPathResolver,
string deployPath,
- string deployCachePath,
+ string deployCachePath,
string nugetCachePath)
{
- AppName = appName;
ApplicationRootPath = applicationRootPath;
_tempPath = tempPath;
_deploymentRepositoryPathResolver = deploymentRepositoryPathResolver;
@@ -93,11 +92,5 @@ public string NuGetCachePath
get;
private set;
}
-
- public string AppName
- {
- get;
- private set;
- }
}
}
diff --git a/Kudu.Core/Kudu.Core.csproj b/Kudu.Core/Kudu.Core.csproj
index e1dabe07f..c2436fa18 100644
--- a/Kudu.Core/Kudu.Core.csproj
+++ b/Kudu.Core/Kudu.Core.csproj
@@ -69,6 +69,7 @@
Properties\CommonAssemblyInfo.cs
+
@@ -78,12 +79,14 @@
+
+
diff --git a/Kudu.Core/SourceControl/Git/GitDeploymentRepository.cs b/Kudu.Core/SourceControl/Git/GitDeploymentRepository.cs
new file mode 100644
index 000000000..baf3a4f88
--- /dev/null
+++ b/Kudu.Core/SourceControl/Git/GitDeploymentRepository.cs
@@ -0,0 +1,88 @@
+using System.IO;
+using System.Linq;
+using Kudu.Core.Tracing;
+
+namespace Kudu.Core.SourceControl.Git
+{
+ public class GitDeploymentRepository : IDeploymentRepository
+ {
+ private readonly GitExecutable _gitExe;
+ private readonly ITraceFactory _traceFactory;
+ private readonly GitExeRepository _repository;
+
+ public GitDeploymentRepository(string path, ITraceFactory traceFactory)
+ {
+ _gitExe = new GitExecutable(path);
+ _gitExe.SetTraceLevel(2);
+ _traceFactory = traceFactory;
+ _repository = new GitExeRepository(path, traceFactory);
+ _repository.SetTraceLevel(2);
+ }
+
+ private string PostReceiveHookPath
+ {
+ get
+ {
+ return Path.Combine(_gitExe.WorkingDirectory, ".git", "hooks", "post-receive");
+ }
+ }
+
+ private string PushInfoPath
+ {
+ get
+ {
+ return Path.Combine(_gitExe.WorkingDirectory, ".git", "pushinfo");
+ }
+ }
+
+ public void Clean()
+ {
+ _repository.Clean();
+ }
+
+ public ChangeSet GetChangeSet(string id)
+ {
+ return _repository.GetChangeSet(id);
+ }
+
+ public PushInfo GetPushInfo()
+ {
+ string path = PushInfoPath;
+
+ if (!File.Exists(path))
+ {
+ return null;
+ }
+
+ string[] pushDetails = File.ReadAllText(path).Split(' ');
+
+ if (pushDetails.Length == 3)
+ {
+ string oldId = pushDetails[0];
+ string newId = pushDetails[1];
+ string reference = pushDetails[2];
+ string branch = reference.Split('/').Last().Trim();
+ string fullNewId = _repository.Resolve(branch);
+
+ return new PushInfo
+ {
+ OldId = oldId,
+ NewId = newId,
+ Branch = new GitBranch(fullNewId, branch, false)
+ };
+ }
+
+ return null;
+ }
+
+ public void Update()
+ {
+ _repository.Update();
+ }
+
+ public void Update(string id)
+ {
+ _repository.Update(id);
+ }
+ }
+}
diff --git a/Kudu.Core/SourceControl/Git/GitExeServer.cs b/Kudu.Core/SourceControl/Git/GitExeServer.cs
index a61d90d08..94c307731 100644
--- a/Kudu.Core/SourceControl/Git/GitExeServer.cs
+++ b/Kudu.Core/SourceControl/Git/GitExeServer.cs
@@ -5,6 +5,7 @@
using Kudu.Contracts.Infrastructure;
using Kudu.Contracts.SourceControl;
using Kudu.Contracts.Tracing;
+using Kudu.Core.Deployment;
using Kudu.Core.Infrastructure;
using Kudu.Core.Tracing;
@@ -16,10 +17,11 @@ public class GitExeServer : IGitServer, IServerRepository
private readonly ITraceFactory _traceFactory;
private readonly GitExeRepository _repository;
private readonly IOperationLock _initLock;
+ private readonly IDeploymentCommandGenerator _deploymentCommandGenerator;
private static readonly TimeSpan _initTimeout = TimeSpan.FromMinutes(8);
- public GitExeServer(string path, IOperationLock initLock, ITraceFactory traceFactory)
+ public GitExeServer(string path, IOperationLock initLock, IDeploymentCommandGenerator deploymentCommandGenerator, ITraceFactory traceFactory)
{
_gitExe = new GitExecutable(path);
_gitExe.SetTraceLevel(2);
@@ -27,6 +29,7 @@ public GitExeServer(string path, IOperationLock initLock, ITraceFactory traceFac
_repository = new GitExeRepository(path, traceFactory);
_repository.SetTraceLevel(2);
_initLock = initLock;
+ _deploymentCommandGenerator = deploymentCommandGenerator;
}
private string PostReceiveHookPath
@@ -45,12 +48,9 @@ private string PushInfoPath
}
}
- public string CurrentId
+ public void Clean()
{
- get
- {
- return _repository.CurrentId;
- }
+ _repository.Clean();
}
public bool Exists
@@ -80,11 +80,6 @@ public void AdvertiseUploadPack(Stream output)
}
}
- public void Clean()
- {
- _repository.Clean();
- }
-
public bool Receive(Stream inputStream, Stream outputStream)
{
ITracer tracer = _traceFactory.GetTracer();
@@ -119,36 +114,6 @@ private void ServiceRpc(ITracer tracer, string serviceName, Stream input, Stream
_gitExe.Execute(tracer, input, output, @"{0} --stateless-rpc ""{1}""", serviceName, _gitExe.WorkingDirectory);
}
- public PushInfo GetPushInfo()
- {
- string path = PushInfoPath;
-
- if (!File.Exists(path))
- {
- return null;
- }
-
- string[] pushDetails = File.ReadAllText(path).Split(' ');
-
- if (pushDetails.Length == 3)
- {
- string oldId = pushDetails[0];
- string newId = pushDetails[1];
- string reference = pushDetails[2];
- string branch = reference.Split('/').Last().Trim();
- string fullNewId = _repository.Resolve(branch);
-
- return new PushInfo
- {
- OldId = oldId,
- NewId = newId,
- Branch = new GitBranch(fullNewId, branch, false)
- };
- }
-
- return null;
- }
-
public ChangeSet Initialize(RepositoryConfiguration configuration, string path)
{
if (Exists && !_initLock.IsHeld)
@@ -220,32 +185,20 @@ private void InitializeRepository(RepositoryConfiguration configuration)
{
FileSystemHelpers.EnsureDirectory(Path.GetDirectoryName(PostReceiveHookPath));
- File.WriteAllText(PostReceiveHookPath, @"#!/bin/sh
+ string content = @"#!/bin/sh
read i
echo $i > pushinfo
-");
+";
+ string command = "\n" + _deploymentCommandGenerator.GetDeploymentCommand();
+
+ File.WriteAllText(PostReceiveHookPath, content + command);
}
}
}
- public ChangeSet GetChangeSet(string id)
- {
- return _repository.GetChangeSet(id);
- }
-
public RepositoryType GetRepositoryType()
{
return RepositoryType.Git;
}
-
- public void Update(string id)
- {
- _repository.Update(id);
- }
-
- public void Update()
- {
- _repository.Update();
- }
}
}
diff --git a/Kudu.Core/SourceControl/Git/GitExecutable.cs b/Kudu.Core/SourceControl/Git/GitExecutable.cs
index 68b9349b5..1c0ca4025 100644
--- a/Kudu.Core/SourceControl/Git/GitExecutable.cs
+++ b/Kudu.Core/SourceControl/Git/GitExecutable.cs
@@ -7,6 +7,7 @@ internal class GitExecutable : Executable
public GitExecutable(string workingDirectory)
: base(PathUtility.ResolveGitPath(), workingDirectory)
{
+ //EnvironmentVariables[KnownVariables.GIT_DIR] = workingDirectory;
}
public void SetTraceLevel(int level)
@@ -37,6 +38,7 @@ private class KnownVariables
public const string GIT_CURL_VERBOSE = "GIT_CURL_VERBOSE";
public const string GIT_TRACE = "GIT_TRACE";
public const string GIT_SSL_NO_VERIFY = "GIT_SSL_NO_VERIFY";
+ public const string GIT_DIR = "GIT_DIR";
}
}
}
diff --git a/Kudu.Services.Web/App_Start/NinjectServices.cs b/Kudu.Services.Web/App_Start/NinjectServices.cs
index d821f1400..4426f1b1a 100644
--- a/Kudu.Services.Web/App_Start/NinjectServices.cs
+++ b/Kudu.Services.Web/App_Start/NinjectServices.cs
@@ -32,15 +32,6 @@ namespace Kudu.Services.Web.App_Start
{
public static class NinjectServices
{
- private const string LockPath = "locks";
- private const string DeploymentLockFile = "deployments.lock";
- private const string InitLockFile = "init.lock";
-
- private const string DeploymentCachePath = "deployments";
- private const string TracePath = @"LogFiles\Git\trace";
- private const string DeploySettingsPath = "settings.xml";
- private const string TraceFile = "trace.xml";
-
///
/// Starts the application
///
@@ -98,7 +89,7 @@ private static void RegisterServices(IKernel kernel)
if (AppSettings.TraceEnabled)
{
- string tracePath = Path.Combine(environment.ApplicationRootPath, TracePath, TraceFile);
+ string tracePath = Path.Combine(environment.ApplicationRootPath, Constants.TracePath, Constants.TraceFile);
System.Func createTracerThunk = () => new Tracer(tracePath);
// First try to use the current request profiler if any, otherwise create a new one
@@ -117,9 +108,9 @@ private static void RegisterServices(IKernel kernel)
// Setup the deployment lock
- string lockPath = Path.Combine(environment.ApplicationRootPath, LockPath);
- string deploymentLockPath = Path.Combine(lockPath, DeploymentLockFile);
- string initLockPath = Path.Combine(lockPath, InitLockFile);
+ string lockPath = Path.Combine(environment.ApplicationRootPath, Constants.LockPath);
+ string deploymentLockPath = Path.Combine(lockPath, Constants.DeploymentLockFile);
+ string initLockPath = Path.Combine(lockPath, Constants.InitLockFile);
var deploymentLock = new LockFile(kernel.Get(), deploymentLockPath);
var initLock = new LockFile(kernel.Get(), initLockPath);
@@ -132,7 +123,7 @@ private static void RegisterServices(IKernel kernel)
// 3. The profile dump
var paths = new[] {
environment.DeploymentCachePath,
- Path.Combine(environment.ApplicationRootPath, TracePath),
+ Path.Combine(environment.ApplicationRootPath, Constants.TracePath),
};
kernel.Bind().ToMethod(context => new DiagnosticsService(paths));
@@ -146,7 +137,10 @@ private static void RegisterServices(IKernel kernel)
kernel.Bind().To()
.InRequestScope();
- kernel.Bind().ToMethod(context => new GitExeServer(environment.DeploymentRepositoryPath, initLock, context.Kernel.Get()))
+ kernel.Bind().ToMethod(context => new GitExeServer(environment.DeploymentRepositoryPath,
+ initLock,
+ context.Kernel.Get(),
+ context.Kernel.Get()))
.InRequestScope();
kernel.Bind().To()
@@ -154,41 +148,19 @@ private static void RegisterServices(IKernel kernel)
.OnActivation(SubscribeForDeploymentEvents);
// Git server
- kernel.Bind().ToMethod(context => GetDeploymentManagerFactory(environment, initLock, deploymentLock, propertyProvider, context.Kernel.Get()));
+ kernel.Bind().To();
- kernel.Bind().ToMethod(context => new GitExeServer(environment.DeploymentRepositoryPath, initLock, context.Kernel.Get()))
+ kernel.Bind().ToMethod(context => new GitExeServer(environment.DeploymentRepositoryPath,
+ initLock,
+ context.Kernel.Get(),
+ context.Kernel.Get()))
.InRequestScope();
-
+
// Editor
kernel.Bind().ToMethod(context => GetEditorProjectSystem(environment, context))
.InRequestScope();
}
- private static IDeploymentManagerFactory GetDeploymentManagerFactory(IEnvironment environment,
- IOperationLock initLock,
- IOperationLock deploymentLock,
- IBuildPropertyProvider propertyProvider,
- ITraceFactory traceFactory)
- {
- return new DeploymentManagerFactory(() =>
- {
- var serverRepository = new GitExeServer(environment.DeploymentRepositoryPath, initLock, traceFactory);
- var fileSystem = new FileSystem();
- var siteBuilderFactory = new SiteBuilderFactory(propertyProvider, environment);
-
- var deploymentManager = new DeploymentManager(serverRepository,
- siteBuilderFactory,
- environment,
- fileSystem,
- traceFactory,
- deploymentLock);
-
- SubscribeForDeploymentEvents(deploymentManager);
-
- return deploymentManager;
- });
- }
-
private static IProjectSystem GetEditorProjectSystem(IEnvironment environment, IContext context)
{
return new ProjectSystem(environment.DeploymentTargetPath);
@@ -196,32 +168,28 @@ private static IProjectSystem GetEditorProjectSystem(IEnvironment environment, I
private static string GetSettingsPath(IEnvironment environment)
{
- return Path.Combine(environment.DeploymentCachePath, DeploySettingsPath);
+ return Path.Combine(environment.DeploymentCachePath, Constants.DeploySettingsPath);
}
private static IEnvironment GetEnvironment()
{
- string targetRoot = PathResolver.ResolveRootPath();
-
- string site = HttpRuntime.AppDomainAppVirtualPath.Trim('/');
- string root = Path.Combine(targetRoot, site);
+ string root = PathResolver.ResolveRootPath();
string deployPath = Path.Combine(root, Constants.WebRoot);
- string deployCachePath = Path.Combine(root, DeploymentCachePath);
+ string deployCachePath = Path.Combine(root, Constants.DeploymentCachePath);
string deploymentRepositoryPath = Path.Combine(root, Constants.RepositoryPath);
string tempPath = Path.GetTempPath();
string deploymentTempPath = Path.Combine(tempPath, Constants.RepositoryPath);
- return new Environment(site,
- root,
+ return new Environment(root,
tempPath,
() => deploymentRepositoryPath,
- () => ResolveRepositoryPath(site),
+ () => ResolveRepositoryPath(),
deployPath,
deployCachePath,
AppSettings.NuGetCachePath);
}
- private static string ResolveRepositoryPath(string site)
+ private static string ResolveRepositoryPath()
{
string path = PathResolver.ResolveDevelopmentPath();
@@ -230,7 +198,7 @@ private static string ResolveRepositoryPath(string site)
return null;
}
- return Path.Combine(path, site, Constants.WebRoot);
+ return Path.Combine(path, Constants.WebRoot);
}
private static void SubscribeForDeploymentEvents(IDeploymentManager deploymentManager)
diff --git a/Kudu.Services.Web/Kudu.Services.Web.csproj b/Kudu.Services.Web/Kudu.Services.Web.csproj
index 8e57cd2f7..aecaeab12 100644
--- a/Kudu.Services.Web/Kudu.Services.Web.csproj
+++ b/Kudu.Services.Web/Kudu.Services.Web.csproj
@@ -145,6 +145,7 @@
+
diff --git a/Kudu.Services.Web/Services/DeploymentCommandGenerator.cs b/Kudu.Services.Web/Services/DeploymentCommandGenerator.cs
new file mode 100644
index 000000000..99140b29b
--- /dev/null
+++ b/Kudu.Services.Web/Services/DeploymentCommandGenerator.cs
@@ -0,0 +1,25 @@
+using System;
+using System.IO;
+using System.Web;
+using Kudu.Core;
+using Kudu.Core.Deployment;
+
+namespace Kudu.Services.Web.Services
+{
+ public class DeploymentCommandGenerator : IDeploymentCommandGenerator
+ {
+ private readonly IEnvironment _environment;
+
+ public DeploymentCommandGenerator(IEnvironment environment)
+ {
+ _environment = environment;
+ }
+
+ public string GetDeploymentCommand()
+ {
+ return String.Format(@"""C:/dev/github/kudu/Kudu.Console/bin/Debug/kudu.exe"" ""{0}"" ""{1}""",
+ _environment.ApplicationRootPath,
+ Path.Combine(HttpRuntime.AppDomainAppPath, "msbuild"));
+ }
+ }
+}
\ No newline at end of file
diff --git a/Kudu.Services/GitServer/RpcService.cs b/Kudu.Services/GitServer/RpcService.cs
index 33f922969..0301b6800 100644
--- a/Kudu.Services/GitServer/RpcService.cs
+++ b/Kudu.Services/GitServer/RpcService.cs
@@ -45,18 +45,15 @@ namespace Kudu.Services.GitServer
[ServiceContract]
public class RpcService
{
- private readonly IDeploymentManagerFactory _deploymentManagerFactory;
private readonly IGitServer _gitServer;
private readonly ITracer _tracer;
private readonly IOperationLock _deploymentLock;
public RpcService(ITracer tracer,
IGitServer gitServer,
- IDeploymentManagerFactory deploymentManagerFactory,
IOperationLock deploymentLock)
{
_gitServer = gitServer;
- _deploymentManagerFactory = deploymentManagerFactory;
_tracer = tracer;
_deploymentLock = deploymentLock;
}
@@ -90,34 +87,14 @@ public HttpResponseMessage ReceivePack(HttpRequestMessage request)
var memoryStream = new MemoryStream();
- // Only if we've completed the receive pack should we start a deployment
- if (_gitServer.Receive(GetInputStream(request), memoryStream))
- {
- Deploy();
- }
- else
- {
- _deploymentLock.Release();
- }
+ _gitServer.Receive(GetInputStream(request), memoryStream);
+
+ _deploymentLock.Release();
return CreateResponse(memoryStream, "application/x-git-{0}-result".With("receive-pack"));
}
}
- private void Deploy()
- {
- ThreadPool.QueueUserWorkItem(_ =>
- {
- try { }
- finally
- {
- // Avoid thread aborts by putting this logic in the finally
- var deployer = new Deployer(_tracer, _deploymentManagerFactory, _deploymentLock);
- deployer.Deploy();
- }
- });
- }
-
private Stream GetInputStream(HttpRequestMessage request)
{
using (_tracer.Step("RpcService.GetInputStream"))
@@ -145,67 +122,5 @@ private HttpResponseMessage CreateResponse(MemoryStream stream, string mediaType
response.WriteNoCache();
return response;
}
-
- ///
- /// Let ASP.NET know about our background deployment thread.
- ///
- private class Deployer : IRegisteredObject
- {
- private readonly ITracer _tracer;
- private readonly IDeploymentManagerFactory _deploymentManagerFactory;
- private readonly IOperationLock _deploymentLock;
-
- public Deployer(ITracer tracer,
- IDeploymentManagerFactory deploymentManagerFactory,
- IOperationLock deploymentLock)
- {
- _tracer = tracer;
- _deploymentManagerFactory = deploymentManagerFactory;
- _deploymentLock = deploymentLock;
-
- // Let the hosting environment know about this object.
- HostingEnvironment.RegisterObject(this);
- }
-
- public void Stop(bool immediate)
- {
- if (!_deploymentLock.IsHeld)
- {
- return;
- }
-
- _tracer.TraceWarning("Initiating ASP.NET shutdown. Waiting on deployment to complete.");
-
- // Wait until ASP.NET or IIS kills us
- bool timeout = _deploymentLock.Wait(TimeSpan.MaxValue);
-
- if (timeout)
- {
- _tracer.TraceWarning("Deployment timed out.");
- }
- else
- {
- _tracer.Trace("Deployment completed.");
- }
- }
-
- public void Deploy()
- {
- try
- {
- IDeploymentManager deploymentManager = _deploymentManagerFactory.CreateDeploymentManager();
- deploymentManager.Deploy();
- }
- catch (Exception ex)
- {
- _tracer.TraceError(ex);
- }
- finally
- {
- _deploymentLock.Release();
- HostingEnvironment.UnregisterObject(this);
- }
- }
- }
}
}
diff --git a/Kudu.sln b/Kudu.sln
index 91e690395..6113da6f7 100644
--- a/Kudu.sln
+++ b/Kudu.sln
@@ -28,6 +28,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kudu.Performance", "Kudu.Pe
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kudu.TestHarness", "Kudu.TestHarness\Kudu.TestHarness.csproj", "{ACF3450A-8062-48D5-9C9D-8486261F290F}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kudu.Console", "Kudu.Console\Kudu.Console.csproj", "{E2B0EE28-8C96-497A-AB08-605B9FD841E9}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -148,6 +150,16 @@ Global
{ACF3450A-8062-48D5-9C9D-8486261F290F}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{ACF3450A-8062-48D5-9C9D-8486261F290F}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{ACF3450A-8062-48D5-9C9D-8486261F290F}.Release|x86.ActiveCfg = Release|Any CPU
+ {E2B0EE28-8C96-497A-AB08-605B9FD841E9}.Debug|Any CPU.ActiveCfg = Debug|x86
+ {E2B0EE28-8C96-497A-AB08-605B9FD841E9}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
+ {E2B0EE28-8C96-497A-AB08-605B9FD841E9}.Debug|Mixed Platforms.Build.0 = Debug|x86
+ {E2B0EE28-8C96-497A-AB08-605B9FD841E9}.Debug|x86.ActiveCfg = Debug|x86
+ {E2B0EE28-8C96-497A-AB08-605B9FD841E9}.Debug|x86.Build.0 = Debug|x86
+ {E2B0EE28-8C96-497A-AB08-605B9FD841E9}.Release|Any CPU.ActiveCfg = Release|x86
+ {E2B0EE28-8C96-497A-AB08-605B9FD841E9}.Release|Mixed Platforms.ActiveCfg = Release|x86
+ {E2B0EE28-8C96-497A-AB08-605B9FD841E9}.Release|Mixed Platforms.Build.0 = Release|x86
+ {E2B0EE28-8C96-497A-AB08-605B9FD841E9}.Release|x86.ActiveCfg = Release|x86
+ {E2B0EE28-8C96-497A-AB08-605B9FD841E9}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE