using System; using System.Collections.Generic; using System.Configuration; using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Http; using System.Text; using System.Threading.Tasks; using System.Windows; using Tango.AdvancedInstaller; using Tango.Core; using Tango.Core.Commands; using Tango.FSE.BL.Web; using Tango.FSE.Web.Messages; using Tango.Git; using Tango.MachineService.Gateway; using Tango.Settings; using Tango.SharedUI; using Tango.Transport.Web; namespace Tango.FSE.Publisher.UI { public class MainWindowVM : ViewModel { private FSEWebClient _client; private String _buildFolder; #if TS private const String APP_EXE_NAME = "Tango.TwineRSM.UI.exe"; #else private const String APP_EXE_NAME = "Tango.FSE.UI.exe"; #endif private List _environments; public List Environments { get { return _environments; } set { _environments = value; RaisePropertyChangedAuto(); } } private EnvironmentConfiguration _selectedEnvironment; public EnvironmentConfiguration SelectedEnvironment { get { return _selectedEnvironment; } set { _selectedEnvironment = value; RaisePropertyChangedAuto(); OnSelectedEnvironmentChanged(); InvalidateRelayCommands(); } } private String _localVersion; public String LocalVersion { get { return _localVersion; } set { _localVersion = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } } private String _remoteVersion; public String RemoteVersion { get { return _remoteVersion; } set { _remoteVersion = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } } private String _buidConfig; public String BuildConfig { get { return _buidConfig; } set { _buidConfig = value; RaisePropertyChangedAuto(); OnBuildConfigChanged(); InvalidateRelayCommands(); } } private String _email; public String Email { get { return _email; } set { _email = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } } private String _password; public String Password { get { return _password; } set { _password = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } } private String _comments; public String Comments { get { return _comments; } set { _comments = value; RaisePropertyChangedAuto(); InvalidateRelayCommands(); } } private bool _createTag; public bool CreateTag { get { return _createTag; } set { _createTag = value; RaisePropertyChangedAuto(); } } private bool _autoCommitAndPush; public bool AutoCommitAndPush { get { return _autoCommitAndPush; } set { _autoCommitAndPush = value; RaisePropertyChangedAuto(); } } private TangoProgress _progress; public TangoProgress Progress { get { return _progress; } set { _progress = value; RaisePropertyChangedAuto(); } } public BuildVariants BuildVariant { get { #if TS return BuildVariants.TwineRSM; #else return BuildVariants.FSE; #endif } } public RelayCommand PublishCommand { get; set; } public MainWindowVM() { PublishCommand = new RelayCommand(Publish, CanPublish); var settings = SettingsManager.Default.GetOrCreate(); Email = settings.Email; Password = settings.Password; CreateTag = true; AutoCommitAndPush = true; } private bool CanPublish() { try { return SelectedEnvironment != null && Email.IsNotNullOrEmpty() && Password.IsNotNullOrEmpty() && Comments.IsNotNullOrEmpty() && System.Version.Parse(LocalVersion) > System.Version.Parse(RemoteVersion) && IsFree; } catch (Exception ex) { Debug.WriteLine(ex); return false; } } public async void Init() { try { IsFree = false; UpdateProgress("Retrieving environments..."); using (HttpClient http = new HttpClient()) { http.DefaultRequestHeaders.TryAddWithoutValidation("Cookie", "x-ms-routing-name=self"); GatewayClient client = new GatewayClient(ConfigurationManager.AppSettings.Get("GatewayUrl"), http); Environments = (await client.GetEnvironmentsAsync(new EnvironmentsRequest())).Environments.OrderBy(x => x.DisplayIndex).ToList(); } SelectedEnvironment = Environments.SingleOrDefault(x => x.Name == "DEV"); BuildConfig = "Release"; ProgressReady(); } catch (Exception ex) { ShowError($"Error retrieving environments from gateway service.\n{ex.FlattenMessage()}"); } finally { IsFree = true; } } private void OnBuildConfigChanged() { UpdateProgress("Searching build path..."); String path = Path.GetFullPath($@"../../../../Build\{(BuildVariant == BuildVariants.FSE ? "FSE" : "Twine RSM")}\{BuildConfig}"); if (!Directory.Exists(path)) { ShowError($"Could not locate {BuildVariant} build folder at '{path}'."); } _buildFolder = path; try { String appExe = Path.Combine(path, APP_EXE_NAME); LocalVersion = FileVersionInfo.GetVersionInfo(appExe).ProductVersion; ProgressReady(); } catch (Exception ex) { ShowError($"Could not determine the local application version.\n{ex.FlattenMessage()}"); } } private async Task OnSelectedEnvironmentChanged() { if (SelectedEnvironment != null) { try { IsFree = false; UpdateProgress("Getting remote version..."); _client = new FSEWebClient(SelectedEnvironment.MachineServiceAddress); RemoteVersion = (await _client.GetLatestVersion(new Web.Messages.LatestVersionRequest() { Build = BuildVariant })).Version; ProgressReady(); } catch (Exception ex) { ShowError($"Error retrieving the remote version.\n{ex.FlattenMessage()}"); } finally { IsFree = true; } } } private async void Publish() { try { IsFree = false; UpdateProgress($"Publishing {BuildVariant.ToDescription()}..."); var settings = SettingsManager.Default.GetOrCreate(); settings.Email = Email; settings.Password = Password; settings.Save(); UpdateProgress($"Validating {BuildVariant.ToDescription()} installer...."); String projectPath = Path.GetFullPath($@"../../../../Advanced Installer Projects\{BuildVariant} Installer.aip"); if (!File.Exists(projectPath)) { throw new FileNotFoundException($"Could not locate advanced installer project at '{projectPath}'."); } String installerName = $"{BuildVariant.ToDescription()} v{System.Version.Parse(LocalVersion).ToString(3)}.exe"; String installerPath = Path.GetFullPath($@"../../../../Build\Installers\{BuildVariant}\Release\{installerName}"); if (!Directory.Exists(Path.GetDirectoryName(installerPath))) { Directory.CreateDirectory(Path.GetDirectoryName(installerPath)); } UpdateProgress("Authenticating with machine service..."); await _client.Login(new LoginRequest() { Email = Email, Password = Password, Version = LocalVersion, Build = BuildVariant }); UpdateProgress("Requesting version upload..."); var uploadResponse = await _client.UploadVersion(new UploadVersionRequest() { Comments = Comments, InstallerBlobName = installerName, Version = LocalVersion, Build = BuildVariant }); UpdateProgress($"Building {BuildVariant.ToDescription()} installer..."); InstallerBuilder builder = new InstallerBuilder(projectPath); await builder.Build(LocalVersion, installerPath); if (!File.Exists(installerPath)) { throw new FileNotFoundException($"Installer build was successful but the output installer could not be located at the specified path '{installerPath}'."); } UpdateProgress($"Uploading {BuildVariant.ToDescription()} installer..."); using (StorageBlobUploader uploader = new StorageBlobUploader(uploadResponse.InstallerBlobAddress, installerPath)) { uploader.Progress += (x, e) => { UpdateProgress($"Uploading {BuildVariant.ToDescription()} installer...", false, e.Current, e.Total); }; await uploader.Upload(); } UpdateProgress("Finalizing version publishing..."); await _client.NotifyVersionUploadCompleted(new UploadCompletedRequest() { Token = uploadResponse.Token, Build = BuildVariant }); UpdateProgress("Validating..."); await OnSelectedEnvironmentChanged(); if (LocalVersion != RemoteVersion) { throw new InvalidDataException("Publishing completed but the local and remote versions are different?!"); } if (CreateTag) { await Task.Factory.StartNew(() => { String repoPath = Path.GetFullPath("../../../../../../"); String tagVersion = System.Version.Parse(LocalVersion).ToString(3); String tagName = $"{BuildVariant}_v{tagVersion}"; using (GitRepositoryManager git = new GitRepositoryManager(repoPath, Email, ConfigurationManager.AppSettings.Get("PAT"))) { UpdateProgress("Checking repository changes..."); int changes = git.GetChanges().Count; if (changes > 0) { if (AutoCommitAndPush) { UpdateProgress("Committing repository changes..."); git.Commit(tagName); } else { throw new InvalidOperationException($"There are {changes} uncommitted changes on the repository. Please commit and push all changes before creating the Tag"); } } UpdateProgress("Checking outgoing commits..."); int commits = git.GetOutgoingCommits().Count; //if (commits > 0) //{ if (AutoCommitAndPush) { UpdateProgress("Pushing repository changes..."); git.Sync(); } else { throw new InvalidOperationException($"There are {commits} outgoing commits on the repository. Please push all commits before creating the Tag"); } //} git.Progress += (x, e) => { UpdateProgress($"Pushing Tag '{tagName}'...", false, e.Progress.Value, e.Progress.Maximum); }; UpdateProgress($"Creating Tag '{tagName}'..."); git.CreatePushTag(tagName, Comments, "Roy Ben Shabat"); } }); } Process.Start(SelectedEnvironment.MachineServiceAddress + $"/fse?buildVariant={(int)BuildVariant}"); UpdateProgress("Version published successfully!", false, 100, 100); ShowInfo($"{BuildVariant.ToDescription()} published successfully!"); IsFree = true; } catch (Exception ex) { ShowError($"Error while trying to publish {BuildVariant.ToDescription()}\n{ex.FlattenMessage()}"); IsFree = true; } } private void ShowError(String error) { UpdateProgress("Error!", false, 0, 100); MessageBox.Show(error, $"{BuildVariant} Publisher", MessageBoxButton.OK, MessageBoxImage.Error); } private void ShowInfo(String message) { MessageBox.Show(message, $"{BuildVariant} Publisher", MessageBoxButton.OK, MessageBoxImage.Information); } private void UpdateProgress(String message, bool isIndeterminate = true, double value = 0, double max = 100) { Progress = new TangoProgress(message, isIndeterminate, value, max); } private void ProgressReady() { UpdateProgress("Ready", false, 0, 100); } } }