File: Tasks\Kudu\KuduDeploy.cs
Web Access
Project: ..\..\..\src\WebSdk\Publish\Tasks\Microsoft.NET.Sdk.Publish.Tasks.csproj (Microsoft.NET.Sdk.Publish.Tasks)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
///--------------------------------------------------------------------------------------------
/// KuduDeploy.cs
///
/// Support for WAWS deployment using Kudu API.
///
/// Copyright(c) 2006 Microsoft Corporation
///--------------------------------------------------------------------------------------------
 
using Microsoft.NET.Sdk.Publish.Tasks.Properties;
using Framework = Microsoft.Build.Framework;
 
namespace Microsoft.NET.Sdk.Publish.Tasks.Kudu
{
    public sealed class KuduDeploy : Task
    {
        public static readonly int TimeoutMilliseconds = 180000;
        [Framework.Required]
        public string? PublishIntermediateOutputPath
        {
            get;
            set;
        }
 
        [Framework.Required]
        public string? PublishUrl
        {
            get;
            set;
        }
 
        [Framework.Required]
        public string? UserName
        {
            get;
            set;
        }
 
        [Framework.Required]
        public string? Password
        {
            get;
            set;
        }
 
        [Framework.Required]
        public string? PublishSiteName
        {
            get;
            set;
        }
 
        public bool DeployIndividualFiles
        {
            get;
            set;
        }
 
        internal KuduConnectionInfo GetConnectionInfo()
        {
            KuduConnectionInfo connectionInfo = new()
            {
                DestinationUrl = PublishUrl,
                UserName = UserName,
                Password = Password,
                SiteName = PublishSiteName
            };
 
            return connectionInfo;
        }
 
        public override bool Execute()
        {
            if (string.IsNullOrEmpty(PublishIntermediateOutputPath))
            {
                Log.LogError(Resources.KUDUDEPLOY_DeployOutputPathEmpty);
                return false;
            }
 
            KuduConnectionInfo connectionInfo = GetConnectionInfo();
            if (string.IsNullOrEmpty(connectionInfo.UserName) || string.IsNullOrEmpty(connectionInfo.Password) || string.IsNullOrEmpty(connectionInfo.DestinationUrl))
            {
                Log.LogError(Resources.KUDUDEPLOY_ConnectionInfoMissing);
                return false;
            }
 
            bool publishStatus = DeployFiles(connectionInfo);
 
            if (publishStatus)
            {
                Log.LogMessage(Framework.MessageImportance.High, Resources.KUDUDEPLOY_PublishSucceeded);
            }
            else
            {
                Log.LogError(Resources.KUDUDEPLOY_PublishFailed);
            }
 
            return publishStatus;
        }
 
        internal bool DeployFiles(KuduConnectionInfo connectionInfo)
        {
            KuduVfsDeploy fileDeploy = new(connectionInfo, Log);
 
            bool success;
            if (!DeployIndividualFiles)
            {
                success = DeployZipFile(connectionInfo);
                return success;
            }
 
            // Deploy the files.
            System.Threading.Tasks.Task? deployTask = fileDeploy.DeployAsync(PublishIntermediateOutputPath);
            try
            {
                success = deployTask?.Wait(TimeoutMilliseconds) ?? false;
                if (!success)
                {
                    Log.LogError(string.Format(Resources.KUDUDEPLOY_AzurePublishErrorReason, Resources.KUDUDEPLOY_OperationTimeout));
                }
            }
            catch (AggregateException ae)
            {
                Log.LogError(string.Format(Resources.KUDUDEPLOY_AzurePublishErrorReason, ae.Flatten().Message));
                success = false;
            }
 
            return success;
        }
 
        internal bool DeployZipFile(KuduConnectionInfo connectionInfo)
        {
            bool success;
            KuduZipDeploy zipDeploy = new(connectionInfo, Log);
 
            string? zipFileFullPath = CreateZipFile(PublishIntermediateOutputPath);
            Task<bool> zipTask = zipDeploy.DeployAsync(zipFileFullPath);
            try
            {
                success = zipTask.Wait(TimeoutMilliseconds);
                if (!success)
                {
                    Log.LogError(string.Format(Resources.KUDUDEPLOY_AzurePublishErrorReason, Resources.KUDUDEPLOY_OperationTimeout));
                }
            }
            catch (AggregateException ae)
            {
                Log.LogError(string.Format(Resources.KUDUDEPLOY_AzurePublishErrorReason, ae.Flatten().Message));
                success = false;
            }
 
            // Clean up the resources.
            DeleteTempZipFile(zipFileFullPath);
 
            return success && zipTask.Result;
        }
 
        internal string? CreateZipFile(string? sourcePath)
        {
            if (sourcePath is null)
            {
                return null;
            }
 
            // Zip the files from PublishOutput path.
            string zipFileFullPath = Path.Combine(Path.GetTempPath(), string.Format("Publish{0}.zip", new Random().Next(int.MaxValue)));
            Log.LogMessage(Framework.MessageImportance.High, string.Format(Resources.KUDUDEPLOY_CopyingToTempLocation, zipFileFullPath));
 
            try
            {
                System.IO.Compression.ZipFile.CreateFromDirectory(sourcePath, zipFileFullPath);
            }
            catch (Exception e)
            {
                Log.LogError(string.Format(Resources.KUDUDEPLOY_AzurePublishErrorReason, e.Message));
                // If we are unable to zip the file, then we fail.
                return null;
            }
 
            Log.LogMessage(Framework.MessageImportance.High, Resources.KUDUDEPLOY_CopyingToTempLocationCompleted);
 
            return zipFileFullPath;
        }
 
        internal System.Threading.Tasks.Task DeleteTempZipFile(string? tempFilePath)
        {
            return System.Threading.Tasks.Task.Factory.StartNew(
                () =>
                {
                    if (File.Exists(tempFilePath))
                    {
                        try
                        {
                            File.Delete(tempFilePath);
                        }
                        catch
                        {
                            // We don't need to do any thing if we are unable to delete the temp file.
                        }
                    }
                }
            );
        }
    }
}