Skip to content

Instantly share code, notes, and snippets.

@GeorgDangl
Last active August 26, 2021 11:49
Show Gist options
  • Save GeorgDangl/5521ef94bc6cae85793f8842f64fa3c5 to your computer and use it in GitHub Desktop.
Save GeorgDangl/5521ef94bc6cae85793f8842f64fa3c5 to your computer and use it in GitHub Desktop.
using Microsoft.Web.Deployment;
using System;
using System.IO;
namespace Nuke.Common.Tools.WebDeploy
{
public class AppOfflineWrapper : IDisposable
{
private readonly WebDeploySettings _settings;
private string _tempAppOfflinePath;
public AppOfflineWrapper(WebDeploySettings settings)
{
_settings = settings;
CreateAppOffline();
}
public void Dispose()
{
RemoveAppOffline();
}
private void CreateAppOffline()
{
if (!_settings.WrapAppOffline)
{
return;
}
GenerateTempAppOffline();
CreateOrDeleteAppOffline(true);
}
private void GenerateTempAppOffline()
{
_tempAppOfflinePath = Path.GetTempFileName();
File.WriteAllText(_tempAppOfflinePath, _settings.AppOfflineHtmlTemplate);
}
private void RemoveAppOffline()
{
if (!_settings.WrapAppOffline)
{
return;
}
CreateOrDeleteAppOffline(false);
File.Delete(_tempAppOfflinePath);
}
private void CreateOrDeleteAppOffline(bool create = true)
{
var sourceOptions = WebDeployOptionsFactory.GetSourceOptions(_settings);
var destinationOptions = WebDeployOptionsFactory.GetDestinationOptions(_settings);
destinationOptions.Trace += WebDeployLogger.DestinationOptions_Trace;
var syncOptions = WebDeployOptionsFactory.GetSyncOptions(_settings);
if (!create)
{
syncOptions.DoNotDelete = false;
}
var sourceProvider = DeploymentWellKnownProvider.ContentPath;
var destinationProvider = DeploymentWellKnownProvider.ContentPath;
using (var deploymentObject = DeploymentManager.CreateObject(sourceProvider, _tempAppOfflinePath, sourceOptions))
{
if (!create)
{
syncOptions.DeleteDestination = true;
}
deploymentObject.SyncTo(destinationProvider, _settings.SiteName + "/app_offline.htm", destinationOptions, syncOptions);
}
}
}
}
using System;
using System.Linq;
using Nuke.Common;
using Nuke.Common.Git;
using Nuke.Common.Tools.GitVersion;
using Nuke.Core;
using static Nuke.Common.Tools.DotNet.DotNetTasks;
using static Nuke.Common.Tools.Npm.NpmTasks;
using static Nuke.Core.IO.FileSystemTasks;
using static Nuke.Core.IO.PathConstruction;
using static Nuke.Core.EnvironmentInfo;
using Nuke.Core.Tooling;
using Nuke.Common.Tools.Npm;
using static Nuke.Common.Tools.WebConfigTransformRunner.WebConfigTransformRunnerTasks;
using Nuke.Common.Tools.WebConfigTransformRunner;
using Nuke.Common.Tools.DotNet;
using static Nuke.Common.Tools.WebDeploy.WebDeployTasks;
using Nuke.Common.Tools.WebDeploy;
class Build : NukeBuild
{
// Console application entry. Also defines the default target.
public static int Main() => Execute<Build>(x => x.Clean);
[Parameter] readonly string Environment;
[Parameter] readonly string WebDeployUsername;
[Parameter] readonly string WebDeployPassword;
[Parameter] readonly string WebDeployPublishUrl;
[Parameter] readonly string WebDeploySiteName;
Target Publish => _ => _
.Requires(() => Environment)
.DependsOn(FrontendRestore)
.DependsOn(BackendRestore)
.DependsOn(FrontendCompile)
.Executes(() =>
{
DotNetPublish(p =>
{
return p.SetWorkingDirectory(SourceDirectory / "project_name")
.SetConfiguration(Environment)
.SetOutput(OutputDirectory);
});
WebConfigTransformRunner(p =>
{
return p.SetWebConfigFilename(OutputDirectory / "web.config")
.SetTransformFilename(OutputDirectory / $"web.{Environment}.config")
.SetOutputFilename(OutputDirectory / "web.config");
});
});
Target Deploy => _ => _
.DependsOn(Publish)
.Requires(() => WebDeployUsername)
.Requires(() => WebDeployPassword)
.Requires(() => WebDeployPublishUrl)
.Requires(() => WebDeploySiteName)
.Executes(() =>
{
WebDeploy(s =>
{
return s.SetSourcePath(OutputDirectory)
.SetUsername(WebDeployUsername)
.SetPassword(WebDeployPassword)
.SetEnableAppOfflineRule(true)
.SetPublishUrl(WebDeployPublishUrl)
.SetSiteName(WebDeploySiteName)
.SetWrapAppOffline(true);
});
});
}
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net461</TargetFramework>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace></RootNamespace>
<IsPackable>False</IsPackable>
<NoWarn>CS0649;CS0169</NoWarn>
<!-- NUKE CONFIGURATION -->
<NukeVersion>0.1.454</NukeVersion>
<NukeCodeGeneration>False</NukeCodeGeneration>
<StartupObject>Build</StartupObject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Nuke.Common" Version="$(NukeVersion)" />
<PackageReference Include="Nuke.CodeGeneration" Version="$(NukeVersion)" Condition="'$(NukeCodeGeneration)' != 'False'" />
<PackageReference Include="Microsoft.Web.Deployment" Version="3.6.0" />
<PackageReference Include="GitVersion.CommandLine" Version="4.0.0-beta0011" />
<PackageReference Include="WebConfigTransformRunner" Version="1.0.0.1">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<NukeMetadata Include="**/*.json" Exclude="bin/**;obj/**" />
<None Remove="*.csproj.dotsettings;_schema.json;*.ref.*.txt" />
<None Remove="*.json;*.json.new" Condition="'$(NukeCodeGeneration)' == 'False'" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\*" Exclude="bin\**;obj\**;**\*.xproj;packages\**;@(EmbeddedResource)" />
</ItemGroup>
</Project>
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title>
App Offline - Maintenance
</title>
<style tyle="text/css">
body {
text-align: center;
padding: 150px;
font: 20px Helvetica, sans-serif;
color: #333;
}
h1 {
font-size: 50px;
}
div {
display: block;
text-align: left;
width: 650px;
margin: 0 auto;
}
</style>
</head>
<body>
<h1>App Temporary Offline</h1>
<div>
<p>This website is temporary offline for maintenance. It should be back in a few moments.</p>
<p>Sorry for the inconvenience!</p>
</div>
</body>
</html>
using Microsoft.Web.Deployment;
using Nuke.Core;
using System.Diagnostics;
namespace Nuke.Common.Tools.WebDeploy
{
public static class WebDeployLogger
{
public static void DestinationOptions_Trace(object sender, DeploymentTraceEventArgs e)
{
switch (e.EventLevel)
{
case TraceLevel.Error:
Logger.Error(e.Message);
break;
case TraceLevel.Warning:
Logger.Warn(e.Message);
break;
case TraceLevel.Info:
Logger.Info(e.Message);
break;
case TraceLevel.Verbose:
Logger.Trace(e.Message);
break;
}
}
}
}
using Microsoft.Web.Deployment;
using System.Linq;
namespace Nuke.Common.Tools.WebDeploy
{
public static class WebDeployOptionsFactory
{
public static DeploymentBaseOptions GetDestinationOptions(WebDeploySettings settings)
{
var options = new DeploymentBaseOptions();
options.AuthenticationType = "basic";
options.ComputerName = settings.PublishUrl;
options.UserName = settings.Username;
options.Password = settings.Password;
options.RetryAttempts = settings.RetryAttempts;
options.RetryInterval = settings.RetryInterval;
return options;
}
public static DeploymentBaseOptions GetSourceOptions(WebDeploySettings settings)
{
var options = new DeploymentBaseOptions();
return options;
}
public static DeploymentSyncOptions GetSyncOptions(WebDeploySettings settings)
{
var options = new DeploymentSyncOptions();
options.DoNotDelete = settings.EnableDoNotDeleteRule;
options.WhatIf = settings.ShowWhatIf;
if (settings.EnableAppOfflineRule)
{
var appOfflineRule = GetRuleByName("AppOffline");
options.Rules.Add(appOfflineRule);
}
// TODO MORE RULES
return options;
}
private static DeploymentRule GetRuleByName(string ruleName)
{
var availableRules = DeploymentSyncOptions.GetAvailableRules();
var rule = availableRules.Single(r => string.Equals(r.Name, ruleName, System.StringComparison.InvariantCultureIgnoreCase));
return rule;
}
}
}
using JetBrains.Annotations;
using Nuke.Core.Tooling;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
namespace Nuke.Common.Tools.WebDeploy
{
[PublicAPI]
[ExcludeFromCodeCoverage]
[Serializable]
public class WebDeploySettings : ToolSettings
{
[Obsolete("Not required for WebDeploySettings")]
public override string ToolPath => null;
public virtual bool UseDoNotDeleteRule { get; internal set; }
public virtual string PublishUrl { get; internal set; }
public virtual string Username { get; internal set; }
public virtual string Password { get; internal set; }
public virtual bool EnableDoNotDeleteRule { get; internal set; }
public virtual bool EnableAppOfflineRule { get; internal set; }
public virtual bool ShowWhatIf { get; internal set; }
public virtual string SiteName { get; internal set; }
public virtual string SourcePath { get; internal set; }
public virtual string DestinationPath { get; internal set; }
public virtual IReadOnlyDictionary<string, string> Parameters { get; internal set; }
public virtual int RetryAttempts { get; internal set; } = 5;
public virtual int RetryInterval { get; internal set; } = 5000;
/// <summary>
/// This is because Azure is still case sensitive when deploying App_Offline.htm via
/// the WebDeploy rule. An all-lowercase app_offline.htm will be created prior to deployment
/// and removed afterwards if this option is enabled.
/// </summary>
public virtual bool WrapAppOffline { get; internal set; }
public virtual string AppOfflineHtmlTemplate { get; internal set; } = GetDefaultAppOffline();
protected override void AssertValid()
{
base.AssertValid();
}
private static string GetDefaultAppOffline()
{
using (var templateStream = typeof(WebDeploySettings).Assembly.GetManifestResourceStream("Resources.DefaultAppOffline.html"))
{
if (templateStream == null)
{
return "App Temporary Offline for Maintenance";
}
using (var streamReader = new StreamReader(templateStream))
{
var template = streamReader.ReadToEnd();
return template;
}
}
}
}
}
using JetBrains.Annotations;
using Nuke.Core.Tooling;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
namespace Nuke.Common.Tools.WebDeploy
{
[PublicAPI]
[ExcludeFromCodeCoverage]
public static class WebDeploySettingsExtensions
{
[Pure]
public static WebDeploySettings SetUseDoNotDeleteRule(this WebDeploySettings toolSettings, bool useDoNotDeleteRule)
=> toolSettings.CloneAndModify(s => s.UseDoNotDeleteRule = useDoNotDeleteRule);
[Pure]
public static WebDeploySettings SetPublishUrl(this WebDeploySettings toolSettings, string publishUrl)
=> toolSettings.CloneAndModify(s => s.PublishUrl = publishUrl);
[Pure]
public static WebDeploySettings SetUsername(this WebDeploySettings toolSettings, string username)
=> toolSettings.CloneAndModify(s => s.Username = username);
[Pure]
public static WebDeploySettings SetPassword(this WebDeploySettings toolSettings, string password)
=> toolSettings.CloneAndModify(s => s.Password = password);
[Pure]
public static WebDeploySettings SetEnableDoNotDeleteRule(this WebDeploySettings toolSettings, bool enableDoNotDeleteRule)
=> toolSettings.CloneAndModify(s => s.EnableDoNotDeleteRule = enableDoNotDeleteRule);
[Pure]
public static WebDeploySettings SetEnableAppOfflineRule(this WebDeploySettings toolSettings, bool enableAppOfflineRule)
=> toolSettings.CloneAndModify(s => s.EnableAppOfflineRule = enableAppOfflineRule);
[Pure]
public static WebDeploySettings SetShowWhatIf(this WebDeploySettings toolSettings, bool showWhatIf)
=> toolSettings.CloneAndModify(s => s.ShowWhatIf = showWhatIf);
[Pure]
public static WebDeploySettings SetSiteName(this WebDeploySettings toolSettings, string siteName)
=> toolSettings.CloneAndModify(s => s.SiteName = siteName);
[Pure]
public static WebDeploySettings SetSourcePath(this WebDeploySettings toolSettings, string sourcePath)
=> toolSettings.CloneAndModify(s => s.SourcePath = sourcePath);
[Pure]
public static WebDeploySettings SetDestinationPath(this WebDeploySettings toolSettings, string destinationPath)
=> toolSettings.CloneAndModify(s => s.DestinationPath = destinationPath);
[Pure]
public static WebDeploySettings SetParameters(this WebDeploySettings toolSettings, IDictionary<string, string> parameters)
=> toolSettings.CloneAndModify(s => s.Parameters = new ReadOnlyDictionary<string, string>(parameters));
[Pure]
public static WebDeploySettings SetRetryAttempts(this WebDeploySettings toolSettings, int retryAttempts)
=> toolSettings.CloneAndModify(s => s.RetryAttempts = retryAttempts);
[Pure]
public static WebDeploySettings SetRetryInterval(this WebDeploySettings toolSettings, int retryInterval)
=> toolSettings.CloneAndModify(s => s.RetryInterval = retryInterval);
[Pure]
public static WebDeploySettings SetWrapAppOffline(this WebDeploySettings toolSettings, bool wrapAppOffline)
=> toolSettings.CloneAndModify(s => s.WrapAppOffline = wrapAppOffline);
[Pure]
public static WebDeploySettings SetAppOfflineHtmlTemplate(this WebDeploySettings toolSettings, string appOfflineHtmlTemplate)
=> toolSettings.CloneAndModify(s => s.AppOfflineHtmlTemplate = appOfflineHtmlTemplate);
private static WebDeploySettings CloneAndModify(this WebDeploySettings toolSettings, Action<WebDeploySettings> modifyAction)
{
toolSettings = toolSettings.NewInstance();
modifyAction(toolSettings);
return toolSettings;
}
}
}
using Microsoft.Web.Deployment;
using Nuke.Core.Tooling;
namespace Nuke.Common.Tools.WebDeploy
{
public class WebDeployTasks
{
public static void WebDeploy(Configure<WebDeploySettings> configurator)
{
var settings = configurator.InvokeSafe(new WebDeploySettings());
using (var appOfflineWrapper = new AppOfflineWrapper(settings))
{
var sourceOptions = WebDeployOptionsFactory.GetSourceOptions(settings);
var destinationOptions = WebDeployOptionsFactory.GetDestinationOptions(settings);
destinationOptions.Trace += WebDeployLogger.DestinationOptions_Trace;
var syncOptions = WebDeployOptionsFactory.GetSyncOptions(settings);
var sourceProvider = DeploymentWellKnownProvider.IisApp;
var destinationProvider = DeploymentWellKnownProvider.IisApp;
using (var deploymentObject = DeploymentManager.CreateObject(sourceProvider, settings.SourcePath, sourceOptions))
{
if (settings.Parameters != null)
{
foreach (var kv in settings.Parameters)
{
if (deploymentObject.SyncParameters.Contains(kv.Key))
{
deploymentObject.SyncParameters[kv.Key].Value = kv.Value;
}
else
{
deploymentObject.SyncParameters.Add(new DeploymentSyncParameter(kv.Key, kv.Key, "", "")
{
Value = kv.Value
});
}
}
}
deploymentObject.SyncTo(destinationProvider, settings.SiteName, destinationOptions, syncOptions);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment