aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Tango.UnitTesting/AdvancedInstaller
ModeNameSize
-rw-r--r--AdvancedInstaller_TST.cs2178logstatsplain
d---------DemoProject157logstatsplain
using CommandLine;
using Ionic.Zip;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Tango.AdvancedInstaller;
using Tango.Core;
using Tango.Core.Cryptography;
using Tango.Core.Helpers;
using Tango.MachineStudio.Common.Web;
using Tango.Transport.Web;
using Tango.Web;

namespace Tango.MachineStudio.Common.Publish
{
    public class MachineStudioPublisher : ExtendedObject
    {
        private MachineStudioWebClient _client;

        /// <summary>
        /// Occurs on publish progress.
        /// </summary>
        public event EventHandler<PublishProgressEventArgs> PublishProgress;

        private PublishOptions _options;
        /// <summary>
        /// Gets or sets the publish options.
        /// </summary>
        public PublishOptions Options
        {
            get { return _options; }
            set { _options = value; RaisePropertyChangedAuto(); }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="MachineStudioPublisher"/> class.
        /// </summary>
        public MachineStudioPublisher()
        {
            _client = new MachineStudioWebClient();
            Options = new PublishOptions();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="MachineStudioPublisher"/> class.
        /// </summary>
        /// <param name="publishOptions">The publish options.</param>
        public MachineStudioPublisher(PublishOptions publishOptions) : this()
        {
            Options = publishOptions;
        }

        /// <summary>
        /// Gets the latest version.
        /// </summary>
        /// <returns></returns>
        public async Task<String> GetRemoteVersion()
        {
            _client.Environment = Options.Environment;
            var response = await _client.GetLatestVersion(new LatestVersionRequest());
            return response.Version;
        }

        /// <summary>
        /// Gets the latest version.
        /// </summary>
        /// <returns></returns>
        public async Task<String> GetRemoteVersion(DeploymentSlot environment)
        {
            _client.Environment = environment;
            var response = await _client.GetLatestVersion(new LatestVersionRequest());
            return response.Version;
        }

        /// <summary>
        /// Gets the latest version.
        /// </summary>
        /// <returns></returns>
        public String GetLocalVersion()
        {
            return FileVersionInfo.GetVersionInfo(GetMachineStudioExecutablePath()).ProductVersion;
        }

        /// <summary>
        /// Gets the machine studio executable path.
        /// </summary>
        /// <returns></returns>
        public String GetMachineStudioExecutablePath()
        {
            String appPath = Path.Combine(Options.GetApplicationPath(), "Tango.MachineStudio.UI.exe");
            return appPath;
        }

        /// <summary>
        /// Login to machine service and returns an access token.
        /// </summary>
        /// <returns></returns>
        private Task Login()
        {
            return Task.Factory.StartNew(() =>
            {
                return _client.Login(new LoginRequest()
                {
                    Email = Options.Email,
                    Password = Options.Password,
                    Version = GetLocalVersion(),
                }).Result;
            });
        }

        /// <summary>
        /// Publish a machine studio version using the specified <see cref="Options"/>.
        /// </summary>
        /// <returns></returns>
        public Task Publish()
        {
            _client.Environment = Options.Environment;

            String appPath = GetMachineStudioExecutablePath();
            String folder = Options.GetApplicationPath();

            if (!File.Exists(appPath))
            {
                throw new FileNotFoundException($"Could not locate the machine studio executable at {appPath}.");
            }

            if (!String.IsNullOrWhiteSpace(Options.InstallerProject))
            {
                if (!File.Exists(Options.InstallerProject))
                {
                    throw new FileNotFoundException($"Installer project not found at '{Options.InstallerProject}'.");
                }

                if (!Directory.Exists(Options.InstallerOutputFolder))
                {
                    throw new DirectoryNotFoundException($"Installer output folder does not exists '{Options.InstallerOutputFolder}'.");
                }
            }

            String tempFile = TemporaryManager.CreateFile();

            return Task.Factory.StartNew(() =>
            {
                try
                {
                    OnPublishProgress(0, 100, $"Logging in to machine service at {Options.Environment.ToAddress()}...");
                    Login().Wait();

                    OnPublishProgress(0, 100, $"Fetching remote version from {Options.Environment.ToAddress()}...");

                    String remote_version = GetRemoteVersion().Result;
                    String local_version = GetLocalVersion();

                    OnPublishProgress(0, 100, $"Remote version: {remote_version}");
                    OnPublishProgress(0, 100, $"Local version: {local_version}");

                    if (Version.Parse(local_version) <= Version.Parse(remote_version))
                    {
                        throw new InvalidOperationException($"The local version '{local_version}' is not greater than the remote version '{remote_version}'.");
                    }

                    OnPublishProgress(0, 100, $"Requesting version upload...");

                    var response = _client.UploadVersion(new UploadVersionRequest()
                    {
                        Version = local_version,
                        Comments = Options.Comments,
                        WithInstaller = !String.IsNullOrWhiteSpace(Options.InstallerProject),
                    }).Result;

                    if (!String.IsNullOrWhiteSpace(Options.InstallerProject))
                    {
                        String output_folder = Options.InstallerOutputFolder;
                        OnPublishProgress(0, 100, $"Building installer project...");
                        InstallerBuilder installerBuilder = new InstallerBuilder(Options.InstallerProject);
                        String installer_name = $"Machine Studio Installer_v{Version.Parse(local_version).ToString(3)}.exe";
                        String output_file = Path.Combine(output_folder, installer_name);
                        installerBuilder.Build(local_version, output_file).Wait();

                        Thread.Sleep(5000);

                        OnPublishProgress(0, 100, $"Uploading installer '{installer_name}'...");

                        using (StorageBlobUploader uploader = new StorageBlobUploader(response.InstallerBlobAddress, output_file))
                        {
                            uploader.Progress += (x, e) =>
                            {
                                OnPublishProgress(e.Current, e.Total, $"Uploading installer {(((double)e.Current / (double)e.Total) * 100d).ToString("0.0")}%...", true);
                            };

                            uploader.Upload().Wait();
                        }
                    }

                    OnPublishProgress(0, 100, $"Starting version packaging...");

                    using (ZipFile zip = new ZipFile())
                    {
                        zip.AddDirectory(Path.Combine(folder, "x86"), "/x86");
                        zip.AddDirectory(Path.Combine(folder, "x64"), "/x64");

                        var files = Directory.GetFiles(folder, "*.*", SearchOption.TopDirectoryOnly).ToList();

                        foreach (var file in files)
                        {
                            zip.AddFile(file, "/");
                        }

                        zip.SaveProgress += (x, e) =>
                        {
                            if (e.EventType == ZipProgressEventType.Saving_BeforeWriteEntry)
                            {
                                OnPublishProgress(e.EntriesSaved + 1, e.EntriesTotal, $"Compressing files {(((double)(e.EntriesSaved + 1) / (double)e.EntriesTotal) * 100d).ToString("0.0")}%...", true);
                            }
                        };

                        zip.Save(tempFile);
                    }

                    OnPublishProgress(0, 100, $"Starting version upload...");

                    using (StorageBlobUploader uploader = new StorageBlobUploader(response.BlobAddress, tempFile))
                    {
                        uploader.Progress += (x, e) =>
                        {
                            OnPublishProgress(e.Current, e.Total, $"Uploading version {(((double)e.Current / (double)e.Total) * 100d).ToString("0.0")}%...", true);
                        };

                        uploader.Upload().Wait();
                    }

                    OnPublishProgress(100, 100, $"Finalizing version upload...");

                    _client.NotifyVersionUploadCompleted(new UploadCompletedRequest()
                    {
                        Token = response.Token,
                    }).Wait();

                    remote_version = GetRemoteVersion().Result;
                    local_version = GetLocalVersion();

                    OnPublishProgress(0, 0, $"Remote version: {remote_version}");
                    OnPublishProgress(0, 0, $"Local version: {local_version}");

                    if (remote_version != local_version)
                    {
                        throw new InvalidOperationException("The remote version does not seems to have been updated.");
                    }

                    OnPublishProgress(0, 0, "Version published successfully.");
                }
                catch (Exception ex)
                {
                    OnPublishProgress(0, 100, $"Failed: {ex.Message}");
                    throw ex;
                }
                finally
                {
                    PathHelper.TryDeleteFile(tempFile);
                }
            });
        }

        /// <summary>
        /// Raises the publish progress event.
        /// </summary>
        /// <param name="progress">The progress.</param>
        /// <param name="total">The total.</param>
        /// <param name="message">The message.</param>
        protected virtual void OnPublishProgress(double progress, double total, String message, bool singleLine = false)
        {
            PublishProgress?.Invoke(this, new PublishProgressEventArgs()
            {
                Progress = progress,
                Total = total,
                Message = message,
                SingleLineRecommended = singleLine,
            });
        }
    }
}