private async Task Build(ChangeSet changeSet, ITracer tracer, IDisposable deployStep, IFileFinder fileFinder, DeploymentAnalytics deploymentAnalytics)
{
if (changeSet == null || String.IsNullOrEmpty(changeSet.Id))
{
throw new ArgumentException("The changeSet.Id parameter is null or empty", "changeSet.Id");
}
ILogger logger = null;
IDeploymentStatusFile currentStatus = null;
string buildTempPath = null;
string id = changeSet.Id;
try
{
logger = GetLogger(id);
ILogger innerLogger = logger.Log(Resources.Log_PreparingDeployment, TrimId(id));
currentStatus = _status.Open(id);
currentStatus.Complete = false;
currentStatus.StartTime = DateTime.UtcNow;
currentStatus.Status = DeployStatus.Building;
currentStatus.StatusText = String.Format(CultureInfo.CurrentCulture, Resources.Status_BuildingAndDeploying, id);
currentStatus.Save();
ISiteBuilder builder = null;
string repositoryRoot = _environment.RepositoryPath;
var perDeploymentSettings = DeploymentSettingsManager.BuildPerDeploymentSettingsManager(repositoryRoot, _settings);
string delayMaxInStr = perDeploymentSettings.GetValue(SettingsKeys.MaxRandomDelayInSec);
if (!String.IsNullOrEmpty(delayMaxInStr))
{
int maxDelay;
if (!Int32.TryParse(delayMaxInStr, out maxDelay) || maxDelay < 0)
{
tracer.Trace("Invalid {0} value, expect a positive integer, received {1}", SettingsKeys.MaxRandomDelayInSec, delayMaxInStr);
}
else
{
tracer.Trace("{0} is set to {1}s", SettingsKeys.MaxRandomDelayInSec, maxDelay);
int gap = _random.Next(maxDelay);
using (tracer.Step("Randomization applied to {0}, Start sleeping for {1}s", maxDelay, gap))
{
logger.Log(Resources.Log_DelayingBeforeDeployment, gap);
await Task.Delay(TimeSpan.FromSeconds(gap));
}
}
}
try
{
using (tracer.Step("Determining deployment builder"))
{
builder = _builderFactory.CreateBuilder(tracer, innerLogger, perDeploymentSettings, fileFinder);
deploymentAnalytics.ProjectType = builder.ProjectType;
tracer.Trace("Builder is {0}", builder.GetType().Name);
}
}
catch (Exception ex)
{
// If we get a TargetInvocationException, use the inner exception instead to avoid
// useless 'Exception has been thrown by the target of an invocation' messages
var targetInvocationException = ex as System.Reflection.TargetInvocationException;
if (targetInvocationException != null)
{
ex = targetInvocationException.InnerException;
}
_globalLogger.Log(ex);
innerLogger.Log(ex);
MarkStatusComplete(currentStatus, success: false);
FailDeployment(tracer, deployStep, deploymentAnalytics, ex);
return;
}
// Create a directory for the script output temporary artifacts
// Use tick count (in hex) instead of guid to keep the path for getting to long
buildTempPath = Path.Combine(_environment.TempPath, DateTime.UtcNow.Ticks.ToString("x"));
FileSystemHelpers.EnsureDirectory(buildTempPath);
var context = new DeploymentContext
{
NextManifestFilePath = GetDeploymentManifestPath(id),
PreviousManifestFilePath = GetActiveDeploymentManifestPath(),
Tracer = tracer,
Logger = logger,
GlobalLogger = _globalLogger,
OutputPath = GetOutputPath(_environment, perDeploymentSettings),
BuildTempPath = buildTempPath,
CommitId = id,
Message = changeSet.Message
};
if (context.PreviousManifestFilePath == null)
{
// this file (/site/firstDeploymentManifest) capture the last active deployment when disconnecting SCM
context.PreviousManifestFilePath = Path.Combine(_environment.SiteRootPath, Constants.FirstDeploymentManifestFileName);
if (!FileSystemHelpers.FileExists(context.PreviousManifestFilePath))
{
// In the first deployment we want the wwwroot directory to be cleaned, we do that using a manifest file
// That has the expected content of a clean deployment (only one file: hostingstart.html)
// This will result in KuduSync cleaning this file.
context.PreviousManifestFilePath = Path.Combine(_environment.ScriptPath, Constants.FirstDeploymentManifestFileName);
}
}
PreDeployment(tracer);
using (tracer.Step("Building"))
{
try
{
await builder.Build(context);
builder.PostBuild(context);
await _functionManager.SyncTriggersAsync();
if (_settings.TouchWebConfigAfterDeployment())
{
TryTouchWebConfig(context);
}
FinishDeployment(id, deployStep);
deploymentAnalytics.VsProjectId = TryGetVsProjectId(context);
deploymentAnalytics.Result = DeployStatus.Success.ToString();
}
catch (Exception ex)
{
MarkStatusComplete(currentStatus, success: false);
FailDeployment(tracer, deployStep, deploymentAnalytics, ex);
return;
}
}
}
catch (Exception ex)
{
FailDeployment(tracer, deployStep, deploymentAnalytics, ex);
}
finally
{
// Clean the temp folder up
CleanBuild(tracer, buildTempPath);
}
}