public Task Build(DeploymentContext context) { var tcs = new TaskCompletionSource<object>(); ILogger innerLogger = context.Logger.Log(Resources.Log_PreparingFiles); try { using (context.Tracer.Step("Copying files to output directory")) { // Copy to the output path and use the previous manifest if there DeploymentHelper.CopyWithManifest(_sourcePath, context.OutputPath, context.PreviousMainfest); } using (context.Tracer.Step("Building manifest")) { // Generate a manifest from those build artifacts context.ManifestWriter.AddFiles(_sourcePath); } // Log the copied files from the manifest innerLogger.LogFileList(context.ManifestWriter.GetPaths()); } catch (Exception ex) { context.Tracer.TraceError(ex); context.GlobalLogger.Log(ex); innerLogger.Log(ex); tcs.SetException(ex); // Bail out early return tcs.Task; } try { // Download node packages DownloadNodePackages(context); AddIISNodeConfig(context); tcs.SetResult(null); } catch (Exception ex) { context.Tracer.TraceError(ex); // HACK: Log an empty error to the global logger (post receive hook console output). // The reason we don't log the real exception is because the 'live output' when downloding // npm packages has already been captured. context.GlobalLogger.LogError(); tcs.SetException(ex); } return tcs.Task; }
public override Task Build(DeploymentContext context) { ILogger buildLogger = context.Logger.Log(Resources.Log_BuildingSolution, Path.GetFileName(SolutionPath)); try { string propertyString = GetPropertyString(); if (!String.IsNullOrEmpty(propertyString)) { propertyString = " /p:" + propertyString; } using (context.Tracer.Step("Running msbuild on solution")) { // Build the solution first string log = ExecuteMSBuild(context.Tracer, @"""{0}"" /verbosity:m /nologo{1}", SolutionPath, propertyString); buildLogger.Log(log); } return BuildProject(context); } catch (Exception ex) { context.Tracer.TraceError(ex); buildLogger.Log(ex); var tcs = new TaskCompletionSource<object>(); tcs.SetException(ex); return tcs.Task; } }
protected override Task BuildProject(DeploymentContext context) { var tcs = new TaskCompletionSource<object>(); ILogger copyLogger = context.Logger.Log(Resources.Log_PreparingFiles); try { using (context.Tracer.Step("Copying files to output directory")) { // Copy to the output path DeploymentHelper.CopyWithManifest(_projectPath, context.OutputPath, context.PreviousMainfest); } using (context.Tracer.Step("Building manifest")) { // Generate the manifest from the project path context.ManifestWriter.AddFiles(_projectPath); } // Log the copied files from the manifest copyLogger.LogFileList(context.ManifestWriter.GetPaths()); tcs.SetResult(null); } catch (Exception ex) { context.Tracer.TraceError(ex); copyLogger.Log(ex); tcs.SetException(ex); } return tcs.Task; }
protected override Task BuildProject(DeploymentContext context) { var tcs = new TaskCompletionSource<object>(); var innerLogger = context.Logger.Log("Using website project {0}.", _projectPath); try { using (context.Profiler.Step("Copying files to output directory")) { // Copy to the output path DeploymentHelper.CopyWithManifest(_projectPath, context.OutputPath, context.PreviousMainfest); } using (context.Profiler.Step("Building manifest")) { // Generate the manifest from the project path context.ManifestWriter.AddFiles(_projectPath); } innerLogger.Log("Done."); tcs.SetResult(null); } catch (Exception e) { innerLogger.Log("Copying website failed.", LogEntryType.Error); innerLogger.Log(e); tcs.SetException(e); } return tcs.Task; }
/// <summary> /// Download node packages as part of the deployment /// </summary> private void DownloadNodePackages(ILogger logger, DeploymentContext context) { // Check to see if there's a package.json file string packagePath = Path.Combine(context.OutputPath, PackageJsonFile); if (!File.Exists(packagePath)) { // If the package.json file doesn't exist then don't bother to run npm install return; } using (context.Profiler.Step("Downloading node packages")) { var npm = new NpmExecutable(context.OutputPath); if (!npm.IsAvailable) { logger.Log("NPM not installed or couldn't be located. Skipping package installation."); return; } // Set the npm proxy settings based on the default settings var proxy = WebRequest.DefaultWebProxy; var httpProxyUrl = proxy.GetProxy(new Uri("http://registry.npmjs.org/")); var httpsProxyUrl = proxy.GetProxy(new Uri("https://registry.npmjs.org/")); if (httpProxyUrl != null) { npm.EnvironmentVariables["HTTP_PROXY"] = httpProxyUrl.ToString(); } if (httpsProxyUrl != null) { npm.EnvironmentVariables["HTTPS_PROXY"] = httpsProxyUrl.ToString(); } // Use the temp path as the user profile path in case we don't have the right // permission set. This normally happens under IIS as a restricted user (ApplicationPoolIdentity). string npmUserProfile = Path.Combine(_tempPath, "npm"); npm.EnvironmentVariables["USERPROFILE"] = npmUserProfile; npm.EnvironmentVariables["LocalAppData"] = npmUserProfile; npm.EnvironmentVariables["AppData"] = npmUserProfile; try { // Use the http proxy since https is failing for some reason npm.Execute("config set registry \"http://registry.npmjs.org/\""); } catch(Exception ex) { // This fails if it's already set Debug.WriteLine(ex.Message); } // Run install on the output directory string log = npm.Execute(context.Profiler, "install").Item1; logger.Log(log); } }
/// <summary> /// Download node packages as part of the deployment /// </summary> private void DownloadNodePackages(ILogger logger, DeploymentContext context) { // Check to see if there's a package.json file string packagePath = Path.Combine(context.OutputPath, PackageJsonFile); if (!File.Exists(packagePath)) { // If the package.json file doesn't exist then don't bother to run npm install return; } using (context.Profiler.Step("Downloading node packages")) { var npm = new NpmExecutable(context.OutputPath); if (!npm.IsAvailable) { logger.Log(Resources.Log_NpmNotInstalled); return; } // Set the npm proxy settings based on the default settings var proxy = WebRequest.DefaultWebProxy; var httpUrl = new Uri("http://registry.npmjs.org/"); var httpsUrl = new Uri("https://registry.npmjs.org/"); var proxyHttpProxyUrl = proxy.GetProxy(httpUrl); var proxyHttpsProxyUrl = proxy.GetProxy(httpsUrl); if (proxyHttpProxyUrl != httpUrl) { npm.EnvironmentVariables["HTTP_PROXY"] = proxyHttpProxyUrl.ToString(); } if (proxyHttpsProxyUrl != httpsUrl) { npm.EnvironmentVariables["HTTPS_PROXY"] = proxyHttpsProxyUrl.ToString(); } try { // Use the http proxy since https is failing for some reason npm.Execute("config set registry \"http://registry.npmjs.org/\""); } catch (Exception ex) { // This fails if it's already set Debug.WriteLine(ex.Message); } // Run install on the output directory string log = npm.Execute(context.Profiler, "install").Item1; logger.Log(log); } }
public Task Build(DeploymentContext context) { var tcs = new TaskCompletionSource<object>(); ILogger innerLogger = context.Logger.Log(Resources.Log_CopyingFiles); try { using (context.Tracer.Step("Copying files to output directory")) { // Copy to the output path and use the previous manifest if there DeploymentHelper.CopyWithManifest(_sourcePath, context.OutputPath, context.PreviousMainfest); } using (context.Tracer.Step("Building manifest")) { // Generate a manifest from those build artifacts context.ManifestWriter.AddFiles(_sourcePath); } // Log the copied files from the manifest innerLogger.LogFileList(context.ManifestWriter.GetPaths()); } catch (Exception ex) { context.Tracer.TraceError(ex); innerLogger.Log(ex); tcs.SetException(ex); // Bail out early return tcs.Task; } try { // Download node packages DownloadNodePackages(context); tcs.SetResult(null); } catch (Exception ex) { context.Tracer.TraceError(ex); tcs.SetException(ex); } return tcs.Task; }
public Task Build(DeploymentContext context) { var tcs = new TaskCompletionSource<object>(); ILogger customLogger = context.Logger.Log("Running custom deployment..."); Executable exe = GetExecutable(); exe.EnvironmentVariables[SourcePath] = _repositoryPath; exe.EnvironmentVariables[TargetPath] = context.OutputPath; // Populate the enviornment with the build propeties foreach (var property in _propertyProvider.GetProperties()) { exe.EnvironmentVariables[property.Key] = property.Value; } // Add the msbuild path and git path to the %PATH% so more tools are available var toolsPaths = new[] { Path.GetDirectoryName(PathUtility.ResolveMSBuildPath()), Path.GetDirectoryName(PathUtility.ResolveGitPath()) }; exe.AddToPath(toolsPaths); try { string output = exe.ExecuteWithConsoleOutput(context.Tracer, String.Empty).Item1; customLogger.Log(output); tcs.SetResult(null); } catch (Exception ex) { context.Tracer.TraceError(ex); // HACK: Log an empty error to the global logger (post receive hook console output). // The reason we don't log the real exception is because the 'live output' running // msbuild has already been captured. context.GlobalLogger.LogError(); customLogger.Log(ex); tcs.SetException(ex); } return tcs.Task; }
public async Task HandleAutoSwap(string currentDeploymetId, DeploymentContext context) { ITracer tracer = context.Tracer; if (!IsAutoSwapEnabled()) { tracer.Trace("AutoSwap is not enabled"); return; } string jwtToken = System.Environment.GetEnvironmentVariable(Constants.SiteRestrictedJWT); if (string.IsNullOrWhiteSpace(jwtToken)) { tracer.Trace("Jwt token is null"); return; } // active deployment is always a success deployment string lastDeploymentId = _deploymentStatusManager.ActiveDeploymentId; if (string.Equals(currentDeploymetId, lastDeploymentId, StringComparison.OrdinalIgnoreCase)) { tracer.Trace("Deployment haven't changed, no need for auto swap: {0}", lastDeploymentId); return; } try { FileSystemHelpers.WriteAllTextToFile(_autoSwapLockFilePath, String.Empty); } catch (Exception ex) { tracer.TraceError(ex); } string operationId = "AUTOSWAP" + Guid.NewGuid(); var queryStrings = HttpUtility.ParseQueryString(string.Empty); queryStrings["slot"] = _autoSwapSlotName; queryStrings["operationId"] = operationId; var client = new OperationClient(context.Tracer); await client.PostAsync<string>("/operations/autoswap?" + queryStrings.ToString()); context.Logger.Log("Requesting auto swap to slot - '{0}' operation id - '{1}' deployment id - '{2}'".FormatInvariant(_autoSwapSlotName, operationId, currentDeploymetId)); }
public override Task Build(DeploymentContext context) { ILogger buildLogger = context.Logger.Log(Resources.Log_BuildingSolution, Path.GetFileName(SolutionPath)); try { string propertyString = GetPropertyString(); if (!String.IsNullOrEmpty(propertyString)) { propertyString = " /p:" + propertyString; } string extraArguments = GetMSBuildExtraArguments(); using (context.Tracer.Step("Running msbuild on solution")) { // Build the solution first string log = ExecuteMSBuild(context.Tracer, @"""{0}"" /verbosity:m /nologo{1} {2}", SolutionPath, propertyString, extraArguments); buildLogger.Log(log); } return BuildProject(context); } catch (Exception ex) { context.Tracer.TraceError(ex); // HACK: Log an empty error to the global logger (post receive hook console output). // The reason we don't log the real exception is because the 'live output' running // msbuild has already been captured. context.GlobalLogger.Log(String.Empty, LogEntryType.Error); buildLogger.Log(ex); var tcs = new TaskCompletionSource<object>(); tcs.SetException(ex); return tcs.Task; } }
public override Task Build(DeploymentContext context) { var tcs = new TaskCompletionSource<object>(); var innerLogger = context.Logger.Log("Building web project {0}.", Path.GetFileName(_projectPath)); try { string buildTempPath = Path.Combine(_tempPath, "builds", Guid.NewGuid().ToString()); string log = null; using (context.Profiler.Step("Running msbuild on project file")) { log = BuildProject(context.Profiler, buildTempPath); } using (context.Profiler.Step("Copying files to output directory")) { // Copy to the output path and use the previous manifest if there DeploymentHelper.CopyWithManifest(buildTempPath, context.OutputPath, context.PreviousMainfest); } using (context.Profiler.Step("Building manifest")) { // Generate a manifest from those build artifacts context.ManifestWriter.AddFiles(buildTempPath); } innerLogger.Log(log); tcs.SetResult(null); } catch (Exception ex) { innerLogger.Log("Building web project failed.", LogEntryType.Error); innerLogger.Log(ex); tcs.SetException(ex); } return tcs.Task; }
public Task Build(DeploymentContext context) { var tcs = new TaskCompletionSource<object>(); var innerLogger = context.Logger.Log("Copying files."); innerLogger.Log("Copying files to {0}.", context.OutputPath); try { using (context.Profiler.Step("Copying files to output directory")) { // Copy to the output path and use the previous manifest if there DeploymentHelper.CopyWithManifest(_sourcePath, context.OutputPath, context.PreviousMainfest); } // Download node packages DownloadNodePackages(innerLogger, context); using (context.Profiler.Step("Building manifest")) { // Generate a manifest from those build artifacts context.ManifestWriter.AddFiles(_sourcePath); } innerLogger.Log("Done."); tcs.SetResult(null); } catch (Exception ex) { innerLogger.Log("Copying files failed."); innerLogger.Log(ex); tcs.SetException(ex); } return tcs.Task; }
protected abstract Task BuildProject(DeploymentContext context);
public abstract Task Build(DeploymentContext context);
public override Task Build(DeploymentContext context) { var tcs = new TaskCompletionSource <object>(); string buildTempPath = Path.Combine(_tempPath, Guid.NewGuid().ToString()); ILogger buildLogger = context.Logger.Log(Resources.Log_BuildingWebProject, Path.GetFileName(_projectPath)); try { using (context.Tracer.Step("Running msbuild on project file")) { string log = BuildProject(context.Tracer, buildTempPath); // Log the details of the build buildLogger.Log(log); } } catch (Exception ex) { context.Tracer.TraceError(ex); // HACK: Log an empty error to the global logger (post receive hook console output). // The reason we don't log the real exception is because the 'live output' running // msbuild has already been captured. context.GlobalLogger.LogError(); buildLogger.Log(ex); tcs.SetException(ex); return(tcs.Task); } ILogger copyLogger = context.Logger.Log(Resources.Log_PreparingFiles); try { using (context.Tracer.Step("Copying files to output directory")) { // Copy to the output path and use the previous manifest if there DeploymentHelper.CopyWithManifest(buildTempPath, context.OutputPath, context.PreviousManifest); } using (context.Tracer.Step("Building manifest")) { // Generate a manifest from those build artifacts context.ManifestWriter.AddFiles(buildTempPath); } // Log the copied files from the manifest copyLogger.LogFileList(context.ManifestWriter.GetPaths()); tcs.SetResult(null); } catch (Exception ex) { context.Tracer.TraceError(ex); context.GlobalLogger.Log(ex); copyLogger.Log(ex); tcs.SetException(ex); } finally { // Clean up the build artifacts after copying them CleanBuild(context.Tracer, buildTempPath); } return(tcs.Task); }
/// <summary> /// Builds and deploys a particular changeset. Puts all build artifacts in a deployments/{id} /// </summary> private void Build(string id, ITracer tracer, IDisposable deployStep) { if (String.IsNullOrEmpty(id)) { throw new ArgumentException("The id parameter is null or empty", "id"); } ILogger logger = null; IDeploymentStatusFile currentStatus = null; IDisposable buildStep = null; try { logger = GetLogger(id); ILogger innerLogger = logger.Log(Resources.Log_PreparingDeployment, TrimId(id)); currentStatus = _status.Open(id); currentStatus.Complete = false; currentStatus.StartTime = DateTime.Now; currentStatus.Status = DeployStatus.Building; currentStatus.StatusText = String.Format(CultureInfo.CurrentCulture, Resources.Status_BuildingAndDeploying, id); currentStatus.Save(); ISiteBuilder builder = null; try { builder = _builderFactory.CreateBuilder(tracer, innerLogger); } 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); tracer.TraceError(ex); innerLogger.Log(ex); currentStatus.MarkFailed(); deployStep.Dispose(); return; } buildStep = tracer.Step("Building"); var context = new DeploymentContext { ManifestWriter = GetDeploymentManifestWriter(id), PreviousManifest = GetActiveDeploymentManifestReader(), Tracer = tracer, Logger = logger, GlobalLogger = _globalLogger, OutputPath = _environment.WebRootPath, }; context.NextManifestFilePath = context.ManifestWriter.ManifestFilePath; if (context.PreviousManifest == null) { // 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.PreviousManifest = new DeploymentManifest(Path.Combine(_environment.ScriptPath, Constants.FirstDeploymentManifestFileName)); } context.PreviousManifestFilePath = context.PreviousManifest.ManifestFilePath; builder.Build(context) .Then(() => { // End the build step buildStep.Dispose(); // Run post deployment steps FinishDeployment(id, deployStep); }) .Catch(ex => { // End the build step buildStep.Dispose(); currentStatus.MarkFailed(); // End the deploy step deployStep.Dispose(); return ex.Handled(); }); } catch (Exception ex) { tracer.TraceError(ex); if (buildStep != null) { buildStep.Dispose(); } deployStep.Dispose(); } }
/// <summary> /// Selects a node.js version to run the application with and augments iisnode.yml accordingly /// </summary> private void SelectNodeVersion(DeploymentContext context) { var fileSystem = new FileSystem(); var nodeSiteEnabler = new NodeSiteEnabler( fileSystem, repoFolder: _sourcePath, siteFolder: context.OutputPath, scriptPath: _scriptPath); ILogger innerLogger = null; try { if (nodeSiteEnabler.LooksLikeNode()) { innerLogger = context.Logger.Log(Resources.Log_SelectNodeJsVersion); string log = nodeSiteEnabler.SelectNodeVersion(context.Tracer); if (!String.IsNullOrEmpty(log)) { innerLogger.Log(log); } } } catch (Exception ex) { if (innerLogger != null) { innerLogger.Log(ex); } throw; } }
/// <summary> /// Add a web.config file if we detect a Node site /// </summary> private void AddIISNodeConfig(DeploymentContext context) { var nodeSiteEnabler = new NodeSiteEnabler( new FileSystem(), repoFolder: _sourcePath, siteFolder: context.OutputPath, scriptPath: _scriptPath); // Check if need to do anythng related to Node if (nodeSiteEnabler.NeedNodeHandling()) { // If we can figure out the start file, create the config file. // Otherwise give a warning string nodeStartFile = nodeSiteEnabler.GetNodeStartFile(); if (nodeStartFile != null) { context.Logger.Log(Resources.Log_CreatingNodeConfig); nodeSiteEnabler.CreateConfigFile(nodeStartFile); } else { context.Logger.Log(Resources.Log_NodeWithMissingServerJs); } } }
public async Task HandleAutoSwapTests() { string deploymentId = Guid.Empty.ToString(); var deploymentSettingsMock = new Mock<IDeploymentSettingsManager>(); var enviromentMock = new Mock<IEnvironment>(); var deploymentStatusManagerMock = new Mock<IDeploymentStatusManager>(); var tracerMock = new Mock<ITracer>(); var deploymentContextMock = new DeploymentContext() { Logger = Mock.Of<ILogger>(), Tracer = tracerMock.Object }; enviromentMock.Setup(e => e.LocksPath).Returns(@"x:\foo"); deploymentStatusManagerMock.Setup(d => d.ActiveDeploymentId).Returns(deploymentId); var handler = new AutoSwapHandler( enviromentMock.Object, deploymentSettingsMock.Object, Mock.Of<ITraceFactory>()); TestTracer.Trace("Autoswap will not happen, since it is not enabled."); await handler.HandleAutoSwap(deploymentId, deploymentContextMock.Logger, deploymentContextMock.Tracer); TestTracer.Trace("Autoswap will not happen, since there is no JWT token."); System.Environment.SetEnvironmentVariable(Constants.SiteRestrictedJWT, null); deploymentSettingsMock.Setup( s => s.GetValue(It.Is<string>(v => "WEBSITE_SWAP_SLOTNAME".StartsWith(v)), It.IsAny<bool>()) ).Returns("someslot"); handler = new AutoSwapHandler( enviromentMock.Object, deploymentSettingsMock.Object, Mock.Of<ITraceFactory>()); var fileSystemMock = new Mock<IFileSystem>(); var fileInfoMock = new Mock<IFileInfoFactory>(); var fileInfoBaseMock = new Mock<FileInfoBase>(); FileSystemHelpers.Instance = fileSystemMock.Object; fileSystemMock.Setup(f => f.FileInfo).Returns(fileInfoMock.Object); fileInfoMock.Setup(f => f.FromFileName(It.IsAny<string>())).Returns(fileInfoBaseMock.Object); fileInfoBaseMock.Setup(f => f.Exists).Returns(true); fileInfoBaseMock.Setup(f => f.LastWriteTimeUtc).Returns(DateTime.UtcNow); await handler.HandleAutoSwap(deploymentId, deploymentContextMock.Logger, deploymentContextMock.Tracer); try { string jwtToken = Guid.NewGuid().ToString(); string hostName = "foo.scm.bar"; System.Environment.SetEnvironmentVariable(Constants.SiteRestrictedJWT, jwtToken); System.Environment.SetEnvironmentVariable(Constants.HttpHost, hostName); tracerMock.Verify(l => l.Trace("AutoSwap is not enabled", It.IsAny<IDictionary<string, string>>()), Times.Once); tracerMock.Verify(l => l.Trace("AutoSwap is not enabled", It.IsAny<IDictionary<string, string>>()), Times.Once); TestTracer.Trace("Autoswap will be triggered"); string newDeploymentId = Guid.NewGuid().ToString(); string autoSwapRequestUrl = null; string bearerToken = null; OperationClient.ClientHandler = new TestMessageHandler((HttpRequestMessage requestMessage) => { autoSwapRequestUrl = requestMessage.RequestUri.AbsoluteUri; bearerToken = requestMessage.Headers.GetValues("Authorization").First(); return new HttpResponseMessage(HttpStatusCode.OK); }); await handler.HandleAutoSwap(newDeploymentId, deploymentContextMock.Logger, deploymentContextMock.Tracer); Assert.NotNull(autoSwapRequestUrl); Assert.True(autoSwapRequestUrl.StartsWith("https://foo.scm.bar/operations/autoswap?slot=someslot&operationId=AUTOSWAP")); Assert.NotNull(bearerToken); Assert.Equal("Bearer " + jwtToken, bearerToken); } finally { System.Environment.SetEnvironmentVariable(Constants.SiteRestrictedJWT, null); System.Environment.SetEnvironmentVariable(Constants.HttpHost, null); OperationClient.ClientHandler = null; } }
/// <summary> /// Download node packages as part of the deployment. /// </summary> private void DownloadNodePackages(DeploymentContext context) { // Check to see if there's a package.json file string packagePath = Path.Combine(context.OutputPath, PackageJsonFile); if (!File.Exists(packagePath)) { // If the package.json file doesn't exist then don't bother to run npm install return; } ILogger innerLogger = context.Logger.Log(Resources.Log_RunningNPM); using (context.Tracer.Step("Downloading node packages")) { var npm = new NpmExecutable(context.OutputPath); if (!npm.IsAvailable) { context.Tracer.TraceError(Resources.Log_NpmNotInstalled); innerLogger.Log(Resources.Log_NpmNotInstalled, LogEntryType.Error); return; } // Set the npm proxy settings based on the default settings var proxy = WebRequest.DefaultWebProxy; var httpUrl = new Uri("http://registry.npmjs.org/"); var httpsUrl = new Uri("https://registry.npmjs.org/"); var proxyHttpProxyUrl = proxy.GetProxy(httpUrl); var proxyHttpsProxyUrl = proxy.GetProxy(httpsUrl); if (proxyHttpProxyUrl != httpUrl) { npm.EnvironmentVariables["HTTP_PROXY"] = proxyHttpProxyUrl.ToString(); } if (proxyHttpsProxyUrl != httpsUrl) { npm.EnvironmentVariables["HTTPS_PROXY"] = proxyHttpsProxyUrl.ToString(); } // REVIEW: Do we still need this? try { // Use the http proxy since https is failing for some reason npm.Execute("config set registry \"http://registry.npmjs.org/\""); } catch (Exception ex) { // This fails if it's already set context.Tracer.TraceError(ex); } try { string log = null; using (var writer = new ProgressWriter()) { writer.Start(); // Run install on the output directory log = npm.Install(context.Tracer, writer); // Run rebuild in case any binary packages are included in the git repository log = npm.Rebuild(context.Tracer, writer); } if (String.IsNullOrWhiteSpace(log)) { innerLogger.Log(Resources.Log_PackagesAlreadyInstalled); } else { innerLogger.Log(log); } } catch (Exception ex) { // Log the exception innerLogger.Log(ex); // re-throw throw; } } }
public string GenerateOryxBuildCommand(DeploymentContext context) { StringBuilder args = new StringBuilder(); // Input/Output args.AppendFormat("oryx build {0} -o {1}", context.OutputPath, context.OutputPath); // Language switch (Language) { case Framework.None: break; case Framework.NodeJs: args.AppendFormat(" --platform nodejs"); break; case Framework.Python: args.AppendFormat(" --platform python"); break; case Framework.DotNETCore: args.AppendFormat(" --platform dotnet"); break; case Framework.PHP: args.AppendFormat(" --platform php"); break; } // Version args.AppendFormat(" --platform-version {0}", Version); // Build Flags switch (Flags) { case BuildOptimizationsFlags.Off: case BuildOptimizationsFlags.None: break; case BuildOptimizationsFlags.CompressModules: AddTempDirectoryOption(args, context.BuildTempPath); if (Language == Framework.NodeJs) { AddNodeCompressOption(args, "tar-gz"); } else if (Language == Framework.Python) { AddPythonCompressOption(args); } break; case BuildOptimizationsFlags.UseExpressBuild: AddTempDirectoryOption(args, context.BuildTempPath); if (Language == Framework.NodeJs) { AddNodeCompressOption(args, "zip"); } break; } // Virtual Env? if (!String.IsNullOrEmpty(VirtualEnv)) { args.AppendFormat(" -p virtualenv_name={0}", VirtualEnv); } // Publish Output? if (!String.IsNullOrEmpty(PublishFolder)) { args.AppendFormat(" -publishedOutputPath {0}", PublishFolder); } return(args.ToString()); }
public string GenerateOryxBuildCommand(DeploymentContext context) { StringBuilder args = new StringBuilder(); // Language switch (Language) { case Framework.None: // Input/Output if (Flags == BuildOptimizationsFlags.DeploymentV2) { OryxArgumentsHelper.AddOryxBuildCommand(args, source: context.RepositoryPath, destination: context.BuildTempPath); } else { OryxArgumentsHelper.AddOryxBuildCommand(args, source: context.RepositoryPath, destination: context.OutputPath); } break; case Framework.NodeJs: // Input/Output if (Flags == BuildOptimizationsFlags.DeploymentV2) { OryxArgumentsHelper.AddOryxBuildCommand(args, source: context.RepositoryPath, destination: context.BuildTempPath); } else { OryxArgumentsHelper.AddOryxBuildCommand(args, source: context.RepositoryPath, destination: context.OutputPath); } OryxArgumentsHelper.AddLanguage(args, "nodejs"); break; case Framework.Python: // Input/Output if (Flags == BuildOptimizationsFlags.DeploymentV2) { OryxArgumentsHelper.AddOryxBuildCommand(args, source: context.RepositoryPath, destination: context.BuildTempPath); } else { OryxArgumentsHelper.AddOryxBuildCommand(args, source: context.RepositoryPath, destination: context.OutputPath); } OryxArgumentsHelper.AddLanguage(args, "python"); break; case Framework.DotNETCore: if (Flags == BuildOptimizationsFlags.UseExpressBuild || Flags == BuildOptimizationsFlags.DeploymentV2) { // We don't want to copy the built artifacts to wwwroot for ExpressBuild scenario OryxArgumentsHelper.AddOryxBuildCommand(args, source: context.RepositoryPath, destination: context.BuildTempPath); } else { // Input/Output [For .NET core, the source path is the RepositoryPath] OryxArgumentsHelper.AddOryxBuildCommand(args, source: context.RepositoryPath, destination: context.OutputPath); } OryxArgumentsHelper.AddLanguage(args, "dotnet"); break; case Framework.PHP: // Input/Output if (Flags == BuildOptimizationsFlags.DeploymentV2) { OryxArgumentsHelper.AddOryxBuildCommand(args, source: context.RepositoryPath, destination: context.BuildTempPath); } else { OryxArgumentsHelper.AddOryxBuildCommand(args, source: context.RepositoryPath, destination: context.OutputPath); } OryxArgumentsHelper.AddLanguage(args, "php"); break; } // Version switch (Language) { case Framework.None: break; case Framework.PHP: OryxArgumentsHelper.AddLanguageVersion(args, Version); break; case Framework.NodeJs: if (Version.Contains("lts", StringComparison.OrdinalIgnoreCase)) { // 10-LTS, 12-LTS should use versions 10, 12 etc // Oryx Builder uses lts for major versions Version = Version.Replace("lts", "").Replace("-", ""); } if (string.IsNullOrEmpty(Version) || Version.Contains("10.16", StringComparison.OrdinalIgnoreCase)) { // Active LTS Version = "10"; } else if (Version.Contains("12.9", StringComparison.OrdinalIgnoreCase)) { Version = "12"; } OryxArgumentsHelper.AddLanguageVersion(args, Version); break; case Framework.Python: OryxArgumentsHelper.AddLanguageVersion(args, Version); break; // work around issue regarding sdk version vs runtime version case Framework.DotNETCore: if (Version == "1.0") { Version = "1.1"; } else if (Version == "2.0") { Version = "2.1"; } OryxArgumentsHelper.AddLanguageVersion(args, Version); break; default: break; } // Build Flags switch (Flags) { case BuildOptimizationsFlags.Off: case BuildOptimizationsFlags.None: break; case BuildOptimizationsFlags.CompressModules: OryxArgumentsHelper.AddTempDirectoryOption(args, context.BuildTempPath); if (Language == Framework.NodeJs) { OryxArgumentsHelper.AddNodeCompressOption(args, "tar-gz"); } else if (Language == Framework.Python) { OryxArgumentsHelper.AddPythonCompressOption(args, "tar-gz"); } break; case BuildOptimizationsFlags.UseExpressBuild: OryxArgumentsHelper.AddTempDirectoryOption(args, context.BuildTempPath); if (Language == Framework.NodeJs) { OryxArgumentsHelper.AddNodeCompressOption(args, "zip"); } else if (Language == Framework.Python) { OryxArgumentsHelper.AddPythonCompressOption(args, "zip"); } break; case BuildOptimizationsFlags.UseTempDirectory: OryxArgumentsHelper.AddTempDirectoryOption(args, context.BuildTempPath); break; case BuildOptimizationsFlags.DeploymentV2: OryxArgumentsHelper.AddTempDirectoryOption(args, context.BuildTempPath); break; } // Virtual Env? if (!String.IsNullOrEmpty(VirtualEnv)) { OryxArgumentsHelper.AddPythonVirtualEnv(args, VirtualEnv); } OryxArgumentsHelper.AddDebugLog(args); return(args.ToString()); }
/// <summary> /// Builds and deploys a particular changeset. Puts all build artifacts in a deployments/{id} /// </summary> private async Task Build( ChangeSet changeSet, ITracer tracer, IDisposable deployStep, IRepository repository, DeploymentInfoBase deploymentInfo, DeploymentAnalytics deploymentAnalytics, bool fullBuildByDefault) { 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; // Add in per-deploy default settings values based on the details of this deployment var perDeploymentDefaults = new Dictionary <string, string> { { SettingsKeys.DoBuildDuringDeployment, fullBuildByDefault.ToString() } }; var settingsProviders = _settings.SettingsProviders.Concat( new[] { new BasicSettingsProvider(perDeploymentDefaults, SettingsProvidersPriority.PerDeploymentDefault) }); var perDeploymentSettings = DeploymentSettingsManager.BuildPerDeploymentSettingsManager(repository.RepositoryPath, settingsProviders); 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, repository); 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(), IgnoreManifest = deploymentInfo != null && deploymentInfo.CleanupTargetDirectory, // Ignoring the manifest will cause kudusync to delete sub-directories / files // in the destination directory that are not present in the source directory, // without checking the manifest to see if the file was copied over to the destination // during a previous kudusync operation. This effectively performs a clean deployment // from the source to the destination directory. Tracer = tracer, Logger = logger, GlobalLogger = _globalLogger, OutputPath = GetOutputPath(deploymentInfo, _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 PostDeploymentHelper.SyncFunctionsTriggers(_environment.RequestId, new PostDeploymentTraceListener(tracer, logger), deploymentInfo?.SyncFunctionsTriggersPath); if (_settings.TouchWatchedFileAfterDeployment()) { TryTouchWatchedFile(context, deploymentInfo); } if (_settings.RunFromLocalZip() && deploymentInfo is ZipDeploymentInfo) { await PostDeploymentHelper.UpdateSiteVersion(deploymentInfo as ZipDeploymentInfo, _environment, logger); } 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); } }
/// <summary> /// Builds and deploys a particular changeset. Puts all build artifacts in a deployments/{id} /// </summary> 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); } }
/// <summary> /// Builds and deploys a particular changeset. Puts all build artifacts in a deployments/{id} /// </summary> private async Task Build(string id, ITracer tracer, IDisposable deployStep, IFileFinder fileFinder) { if (String.IsNullOrEmpty(id)) { throw new ArgumentException("The id parameter is null or empty", "id"); } ILogger logger = null; IDeploymentStatusFile currentStatus = null; 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); try { using (tracer.Step("Determining deployment builder")) { builder = _builderFactory.CreateBuilder(tracer, innerLogger, perDeploymentSettings, fileFinder); 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); tracer.TraceError(ex); innerLogger.Log(ex); currentStatus.MarkFailed(); deployStep.Dispose(); return; } var context = new DeploymentContext { NextManifestFilePath = GetDeploymentManifestPath(id), PreviousManifestFilePath = GetActiveDeploymentManifestPath(), Tracer = tracer, Logger = logger, GlobalLogger = _globalLogger, OutputPath = GetOutputPath(_environment, perDeploymentSettings), }; if (context.PreviousManifestFilePath == null) { // 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); } using (tracer.Step("Building")) { try { await builder.Build(context); TryTouchWebConfig(context); // Run post deployment steps FinishDeployment(id, deployStep); } catch (Exception ex) { tracer.TraceError(ex); currentStatus.MarkFailed(); // End the deploy step deployStep.Dispose(); return; } } } catch (Exception ex) { tracer.TraceError(ex); deployStep.Dispose(); } }
/// <summary> /// Builds and deploys a particular changeset. Puts all build artifacts in a deployments/{id} /// </summary> private void Build(string id, ITracer tracer, IDisposable deployStep) { if (String.IsNullOrEmpty(id)) { throw new ArgumentException(); } ILogger logger = null; DeploymentStatusFile currentStatus = null; IDisposable buildStep = null; try { logger = GetLogger(tracer, id); ILogger innerLogger = logger.Log(Resources.Log_PreparingDeployment, TrimId(id)); currentStatus = OpenStatusFile(id); currentStatus.Complete = false; currentStatus.StartTime = DateTime.Now; currentStatus.Status = DeployStatus.Building; currentStatus.StatusText = String.Format(CultureInfo.CurrentCulture, Resources.Status_BuildingAndDeploying, id); currentStatus.Save(_fileSystem); ReportStatus(id); ISiteBuilder builder = null; try { builder = _builderFactory.CreateBuilder(tracer, innerLogger); } catch (Exception ex) { _globalLogger.Log(ex); tracer.TraceError(ex); innerLogger.Log(ex); MarkFailed(currentStatus); ReportStatus(id); deployStep.Dispose(); return; } buildStep = tracer.Step("Building"); var context = new DeploymentContext { ManifestWriter = GetDeploymentManifestWriter(id), PreviousMainfest = GetActiveDeploymentManifestReader(), Tracer = tracer, Logger = logger, GlobalLogger = _globalLogger, OutputPath = _environment.DeploymentTargetPath, }; builder.Build(context) .Then(() => { // End the build step buildStep.Dispose(); // Run post deployment steps FinishDeployment(id, tracer, deployStep); }) .Catch(ex => { // End the build step buildStep.Dispose(); MarkFailed(currentStatus); ReportStatus(id); // End the deploy step deployStep.Dispose(); }); } catch (Exception ex) { tracer.TraceError(ex); logger.LogUnexpetedError(); if (buildStep != null) { buildStep.Dispose(); } deployStep.Dispose(); } }
private static string TryGetVsProjectId(DeploymentContext context) { try { // Read web.config string webConfigPath = Path.Combine(context.OutputPath, "web.config"); if (File.Exists(webConfigPath)) { using (var stream = File.OpenRead(webConfigPath)) { Guid? projectId = ProjectGuidParser.GetProjectGuidFromWebConfig(stream); return projectId.HasValue ? projectId.Value.ToString() : null; } } } catch (Exception ex) { context.Tracer.TraceError(ex); } return null; }
/// <summary> /// Builds and deploys a particular changeset. Puts all build artifacts in a deployments/{id} /// </summary> private void Build(string id, ITracer tracer, IDisposable deployStep) { if (String.IsNullOrEmpty(id)) { throw new ArgumentException(); } ILogger logger = null; DeploymentStatusFile currentStatus = null; IDisposable buildStep = null; try { logger = GetLogger(id); ILogger innerLogger = logger.Log(Resources.Log_PreparingDeployment, TrimId(id)); currentStatus = OpenStatusFile(id); currentStatus.Complete = false; currentStatus.StartTime = DateTime.Now; currentStatus.Status = DeployStatus.Building; currentStatus.StatusText = String.Format(CultureInfo.CurrentCulture, Resources.Status_BuildingAndDeploying, id); currentStatus.Save(_fileSystem); ReportStatus(id); ISiteBuilder builder = null; try { builder = _builderFactory.CreateBuilder(tracer, innerLogger); } 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); tracer.TraceError(ex); innerLogger.Log(ex); MarkFailed(currentStatus); ReportStatus(id); deployStep.Dispose(); return; } buildStep = tracer.Step("Building"); var context = new DeploymentContext { ManifestWriter = GetDeploymentManifestWriter(id), PreviousManifest = GetActiveDeploymentManifestReader(), Tracer = tracer, Logger = logger, GlobalLogger = _globalLogger, OutputPath = _environment.WebRootPath, }; context.NextManifestFilePath = context.ManifestWriter.ManifestFilePath; if (context.PreviousManifest != null) { context.PreviousManifestFilePath = context.PreviousManifest.ManifestFilePath; } else { // In this case it is the first deployment (no active deployment) // So we remove all files from wwwroot FileSystemHelpers.DeleteDirectoryContentsSafe(context.OutputPath); } builder.Build(context) .Then(() => { // End the build step buildStep.Dispose(); // Run post deployment steps FinishDeployment(id, tracer, deployStep); }) .Catch(ex => { // End the build step buildStep.Dispose(); MarkFailed(currentStatus); ReportStatus(id); // End the deploy step deployStep.Dispose(); return ex.Handled(); }); } catch (Exception ex) { tracer.TraceError(ex); logger.LogUnexpetedError(); if (buildStep != null) { buildStep.Dispose(); } deployStep.Dispose(); } }
public Task Build(DeploymentContext context) { var tcs = new TaskCompletionSource<object>(); ILogger customLogger = context.Logger.Log("Running custom deployment command..."); // Creates an executable pointing to cmd and the working directory being // the repository root var exe = new Executable("cmd", _repositoryPath); exe.EnvironmentVariables[SourcePath] = _repositoryPath; exe.EnvironmentVariables[TargetPath] = context.OutputPath; // Create a directory for the script output temporary artifacts string buildTempPath = Path.Combine(_tempPath, Guid.NewGuid().ToString()); FileSystemHelpers.EnsureDirectory(buildTempPath); exe.EnvironmentVariables[BuildTempPath] = buildTempPath; // Populate the enviornment with the build propeties foreach (var property in _propertyProvider.GetProperties()) { exe.EnvironmentVariables[property.Key] = property.Value; } // Set the path so we can add more variables exe.EnvironmentVariables["PATH"] = System.Environment.GetEnvironmentVariable("PATH"); // Add the msbuild path and git path to the %PATH% so more tools are available var toolsPaths = new[] { Path.GetDirectoryName(PathUtility.ResolveMSBuildPath()), Path.GetDirectoryName(PathUtility.ResolveGitPath()) }; exe.AddToPath(toolsPaths); try { string output = exe.ExecuteWithConsoleOutput(context.Tracer, "/c " + _command, String.Empty).Item1; customLogger.Log(output); tcs.SetResult(null); } catch (Exception ex) { context.Tracer.TraceError(ex); // HACK: Log an empty error to the global logger (post receive hook console output). // The reason we don't log the real exception is because the 'live output' running // msbuild has already been captured. context.GlobalLogger.LogError(); customLogger.Log(ex); tcs.SetException(ex); } finally { // Clean the temp folder up FileSystemHelpers.DeleteDirectorySafe(buildTempPath); } return tcs.Task; }
/// <summary> /// Download node packages as part of the deployment. /// </summary> private void DownloadNodePackages(DeploymentContext context) { // Check to see if there's a package.json file string packagePath = Path.Combine(context.OutputPath, PackageJsonFile); if (!File.Exists(packagePath)) { // If the package.json file doesn't exist then don't bother to run npm install return; } ILogger innerLogger = context.Logger.Log(Resources.Log_RunningNPM); using (context.Tracer.Step("Downloading node packages")) { var npm = new NpmExecutable(context.OutputPath); if (!npm.IsAvailable) { context.Tracer.TraceError(Resources.Log_NpmNotInstalled); innerLogger.Log(Resources.Log_NpmNotInstalled, LogEntryType.Error); return; } // Set the npm proxy settings based on the default settings var proxy = WebRequest.DefaultWebProxy; var httpUrl = new Uri("http://registry.npmjs.org/"); var httpsUrl = new Uri("https://registry.npmjs.org/"); var proxyHttpProxyUrl = proxy.GetProxy(httpUrl); var proxyHttpsProxyUrl = proxy.GetProxy(httpsUrl); if (proxyHttpProxyUrl != httpUrl) { npm.EnvironmentVariables["HTTP_PROXY"] = proxyHttpProxyUrl.ToString(); } if (proxyHttpsProxyUrl != httpsUrl) { npm.EnvironmentVariables["HTTPS_PROXY"] = proxyHttpsProxyUrl.ToString(); } npm.SetHomePath(_homePath); // REVIEW: Do we still need this? try { // Use the http proxy since https is failing for some reason npm.Execute("config set registry \"http://registry.npmjs.org/\""); } catch (Exception ex) { // This fails if it's already set context.Tracer.TraceError(ex); } try { string log = null; using (var writer = new ProgressWriter()) { writer.Start(); // Run install on the output directory log = npm.Install(context.Tracer, writer); } if (String.IsNullOrWhiteSpace(log)) { innerLogger.Log(Resources.Log_PackagesAlreadyInstalled); } else { innerLogger.Log(log); } } catch (Exception ex) { // Log the exception innerLogger.Log(ex); // re-throw throw; } } }
private static void TouchWatchedFileIfNeeded(IDeploymentSettingsManager settings, DeploymentInfoBase deploymentInfo, DeploymentContext context) { if (deploymentInfo != null && !deploymentInfo.WatchedFileEnabled) { return; } if (!settings.RunFromZip() && settings.TouchWatchedFileAfterDeployment()) { TryTouchWatchedFile(context, deploymentInfo); } }
public override Task Build(DeploymentContext context) { var tcs = new TaskCompletionSource<object>(); string buildTempPath = Path.Combine(_tempPath, Guid.NewGuid().ToString()); ILogger buildLogger = context.Logger.Log(Resources.Log_BuildingWebProject, Path.GetFileName(_projectPath)); try { using (context.Tracer.Step("Running msbuild on project file")) { string log = BuildProject(context.Tracer, buildTempPath); // Log the details of the build buildLogger.Log(log); } } catch (Exception ex) { context.Tracer.TraceError(ex); // HACK: Log an empty error to the global logger (post receive hook console output). // The reason we don't log the real exception is because the 'live output' running // msbuild has already been captured. context.GlobalLogger.LogError(); buildLogger.Log(ex); tcs.SetException(ex); return tcs.Task; } ILogger copyLogger = context.Logger.Log(Resources.Log_PreparingFiles); try { using (context.Tracer.Step("Copying files to output directory")) { // Copy to the output path and use the previous manifest if there DeploymentHelper.CopyWithManifest(buildTempPath, context.OutputPath, context.PreviousMainfest); } using (context.Tracer.Step("Building manifest")) { // Generate a manifest from those build artifacts context.ManifestWriter.AddFiles(buildTempPath); } // Log the copied files from the manifest copyLogger.LogFileList(context.ManifestWriter.GetPaths()); tcs.SetResult(null); } catch (Exception ex) { context.Tracer.TraceError(ex); context.GlobalLogger.Log(ex); copyLogger.Log(ex); tcs.SetException(ex); } finally { // Clean up the build artifacts after copying them CleanBuild(context.Tracer, buildTempPath); } return tcs.Task; }
public override Task Build(DeploymentContext context) { throw new NotImplementedException(); }
/// <summary> /// Builds and deploys a particular changeset. Puts all build artifacts in a deployments/{id} /// </summary> private void Build(string id, ITracer tracer, IDisposable deployStep) { if (String.IsNullOrEmpty(id)) { throw new ArgumentException(); } ILogger logger = null; DeploymentStatusFile currentStatus = null; IDisposable buildStep = null; try { logger = GetLogger(id); ILogger innerLogger = logger.Log(Resources.Log_PreparingDeployment, TrimId(id)); currentStatus = OpenStatusFile(id); currentStatus.Complete = false; currentStatus.StartTime = DateTime.Now; currentStatus.Status = DeployStatus.Building; currentStatus.StatusText = String.Format(CultureInfo.CurrentCulture, Resources.Status_BuildingAndDeploying, id); currentStatus.Save(_fileSystem); ReportStatus(id); ISiteBuilder builder = null; try { builder = _builderFactory.CreateBuilder(tracer, innerLogger); } catch (Exception ex) { _globalLogger.Log(ex); tracer.TraceError(ex); innerLogger.Log(ex); MarkFailed(currentStatus); ReportStatus(id); deployStep.Dispose(); return; } buildStep = tracer.Step("Building"); var context = new DeploymentContext { ManifestWriter = GetDeploymentManifestWriter(id), PreviousMainfest = GetActiveDeploymentManifestReader(), Tracer = tracer, Logger = logger, GlobalLogger = _globalLogger, OutputPath = _environment.DeploymentTargetPath, }; builder.Build(context) .Then(() => { // End the build step buildStep.Dispose(); // Run post deployment steps FinishDeployment(id, tracer, deployStep); }) .Catch(ex => { // End the build step buildStep.Dispose(); MarkFailed(currentStatus); ReportStatus(id); // End the deploy step deployStep.Dispose(); }); } catch (Exception ex) { tracer.TraceError(ex); logger.LogUnexpetedError(); if (buildStep != null) { buildStep.Dispose(); } deployStep.Dispose(); } }
private static void TryTouchWebConfig(DeploymentContext context) { try { // Touch web.config string webConfigPath = Path.Combine(context.OutputPath, "web.config"); if (File.Exists(webConfigPath)) { File.SetLastWriteTimeUtc(webConfigPath, DateTime.UtcNow); } } catch (Exception ex) { context.Tracer.TraceError(ex); } }
/// <summary> /// Builds and deploys a particular changeset. Puts all build artifacts in a deployments/{id} /// </summary> 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); 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.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); } }
public Task Build(DeploymentContext context) { var tcs = new TaskCompletionSource<object>(); ILogger customLogger = context.Logger.Log("Running custom deployment command..."); // Creates an executable pointing to cmd and the working directory being // the repository root var exe = new Executable(StarterScriptPath, _repositoryPath, _settings.GetCommandIdleTimeout()); exe.AddDeploymentSettingsAsEnvironmentVariables(_settings); exe.EnvironmentVariables[ExternalCommandFactory.SourcePath] = _repositoryPath; exe.EnvironmentVariables[ExternalCommandFactory.TargetPath] = context.OutputPath; exe.EnvironmentVariables[ExternalCommandBuilder.PreviousManifestPath] = (context.PreviousManifest != null) ? context.PreviousManifest.ManifestFilePath : String.Empty; exe.EnvironmentVariables[ExternalCommandBuilder.NextManifestPath] = context.ManifestWriter.ManifestFilePath; exe.EnvironmentVariables[ExternalCommandFactory.MSBuildPath] = PathUtility.ResolveMSBuildPath(); exe.EnvironmentVariables[ExternalCommandFactory.KuduSyncCommandKey] = KuduSyncCommand; exe.EnvironmentVariables[ExternalCommandFactory.SelectNodeVersionCommandKey] = SelectNodeVersionCommand; exe.EnvironmentVariables[ExternalCommandFactory.NpmJsPathKey] = PathUtility.ResolveNpmJsPath(); exe.EnvironmentVariables[WellKnownEnvironmentVariables.NuGetPackageRestoreKey] = "true"; exe.SetHomePath(_homePath); // Create a directory for the script output temporary artifacts string buildTempPath = Path.Combine(_tempPath, Guid.NewGuid().ToString()); FileSystemHelpers.EnsureDirectory(buildTempPath); exe.EnvironmentVariables[ExternalCommandBuilder.BuildTempPath] = buildTempPath; // Populate the enviornment with the build propeties foreach (var property in _propertyProvider.GetProperties()) { exe.EnvironmentVariables[property.Key] = property.Value; } // Set the path so we can add more variables exe.EnvironmentVariables["PATH"] = System.Environment.GetEnvironmentVariable("PATH"); // Add the msbuild path and git path to the %PATH% so more tools are available var toolsPaths = new[] { Path.GetDirectoryName(PathUtility.ResolveMSBuildPath()), Path.GetDirectoryName(PathUtility.ResolveGitPath()) }; exe.AddToPath(toolsPaths); try { exe.ExecuteWithProgressWriter(customLogger, context.Tracer, _command, String.Empty); tcs.SetResult(null); } catch (CommandLineException ex) { context.Tracer.TraceError(ex); // HACK: Log an empty error to the global logger (post receive hook console output). // The reason we don't log the real exception is because the 'live output' running // msbuild has already been captured. context.GlobalLogger.LogError(); // Add the output stream and the error stream to the log for better // debugging customLogger.Log(ex.Output, LogEntryType.Error); customLogger.Log(ex.Error, LogEntryType.Error); tcs.SetException(ex); } finally { // Clean the temp folder up FileSystemHelpers.DeleteDirectorySafe(buildTempPath); } return tcs.Task; }
/// <summary> /// Selects a node.js version to run the application with and augments iisnode.yml accordingly /// </summary> private void SelectNodeVersion(DeploymentContext context) { var fileSystem = new FileSystem(); var nodeSiteEnabler = new NodeSiteEnabler( fileSystem, repoFolder: _sourcePath, siteFolder: context.OutputPath, scriptPath: _scriptPath); if (nodeSiteEnabler.LooksLikeNode()) { nodeSiteEnabler.SelectNodeVersion(context.Tracer); } }
/// <summary> /// Builds and deploys a particular changeset. Puts all build artifacts in a deployments/{id} /// </summary> private async Task Build(string id, ITracer tracer, IDisposable deployStep, IFileFinder fileFinder, DeploymentAnalytics deploymentAnalytics) { if (String.IsNullOrEmpty(id)) { throw new ArgumentException("The id parameter is null or empty", "id"); } ILogger logger = null; IDeploymentStatusFile currentStatus = null; string buildTempPath = null; 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); 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 buildTempPath = Path.Combine(_environment.TempPath, Guid.NewGuid().ToString()); 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 }; 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); TryTouchWebConfig(context); // Run post deployment steps FinishDeployment(id, deployStep); 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); } }
public Task Build(DeploymentContext context) { var tcs = new TaskCompletionSource <object>(); ILogger customLogger = context.Logger.Log("Running custom deployment command..."); // Creates an executable pointing to cmd and the working directory being // the repository root var exe = new Executable(StarterScriptPath, _repositoryPath, _settings.GetCommandIdleTimeout()); exe.EnvironmentVariables[ExternalCommandFactory.SourcePath] = _repositoryPath; exe.EnvironmentVariables[ExternalCommandFactory.TargetPath] = context.OutputPath; exe.EnvironmentVariables[ExternalCommandBuilder.PreviousManifestPath] = (context.PreviousManifest != null) ? context.PreviousManifest.ManifestFilePath : String.Empty; exe.EnvironmentVariables[ExternalCommandBuilder.NextManifestPath] = context.ManifestWriter.ManifestFilePath; exe.EnvironmentVariables[ExternalCommandFactory.MSBuildPath] = PathUtility.ResolveMSBuildPath(); exe.EnvironmentVariables[ExternalCommandFactory.KuduSyncCommandKey] = KuduSyncCommand; exe.EnvironmentVariables[ExternalCommandFactory.SelectNodeVersionCommandKey] = SelectNodeVersionCommand; exe.EnvironmentVariables[ExternalCommandFactory.NpmJsPathKey] = PathUtility.ResolveNpmJsPath(); exe.EnvironmentVariables[WellKnownEnvironmentVariables.NuGetPackageRestoreKey] = "true"; exe.AddDeploymentSettingsAsEnvironmentVariables(_settings); exe.SetHomePath(_homePath); // Create a directory for the script output temporary artifacts string buildTempPath = Path.Combine(_tempPath, Guid.NewGuid().ToString()); FileSystemHelpers.EnsureDirectory(buildTempPath); exe.EnvironmentVariables[ExternalCommandBuilder.BuildTempPath] = buildTempPath; // Populate the enviornment with the build propeties foreach (var property in _propertyProvider.GetProperties()) { exe.EnvironmentVariables[property.Key] = property.Value; } // Set the path so we can add more variables exe.EnvironmentVariables["PATH"] = System.Environment.GetEnvironmentVariable("PATH"); // Add the msbuild path and git path to the %PATH% so more tools are available var toolsPaths = new[] { Path.GetDirectoryName(PathUtility.ResolveMSBuildPath()), Path.GetDirectoryName(PathUtility.ResolveGitPath()), Path.GetDirectoryName(PathUtility.ResolveVsTestPath()) }; exe.AddToPath(toolsPaths); try { exe.ExecuteWithProgressWriter(customLogger, context.Tracer, _command, String.Empty); tcs.SetResult(null); } catch (CommandLineException ex) { context.Tracer.TraceError(ex); // HACK: Log an empty error to the global logger (post receive hook console output). // The reason we don't log the real exception is because the 'live output' running // msbuild has already been captured. context.GlobalLogger.LogError(); // Add the output stream and the error stream to the log for better // debugging customLogger.Log(ex.Output, LogEntryType.Error); customLogger.Log(ex.Error, LogEntryType.Error); tcs.SetException(ex); } finally { // Clean the temp folder up FileSystemHelpers.DeleteDirectorySafe(buildTempPath); } return(tcs.Task); }