using System; using System.Threading; using UnityEditor; using Codice.Client.Common.EventTracking; using Codice.CM.Common; using Codice.LogWrapper; using PlasticGui; using PlasticGui.Help.Conditions; using PlasticGui.WebApi.Responses; using Unity.PlasticSCM.Editor.AssetUtils; using Unity.PlasticSCM.Editor.Configuration; using Unity.PlasticSCM.Editor.UI; using Unity.PlasticSCM.Editor.Views.CreateWorkspace; using Unity.PlasticSCM.Editor.WebApi; namespace Unity.PlasticSCM.Editor.Hub.Operations { internal class CreateWorkspace { internal static bool IsOperationInProgress { get { return mIsOperationInProgress; } } internal static void LaunchOperation(OperationParams parameters) { mIsOperationInProgress = true; mLog.Debug("Waiting for editor to be idle"); Execute.AfterEditorIsIdleForSeconds(IDLE_DELAY_SECONDS, () => { mLog.Debug("Editor is idle, proceeding with workspace creation"); CreateWorkspace createWorkspaceOperation = new CreateWorkspace(); createWorkspaceOperation.CreateWorkspaceOperation(parameters); }); } internal static WorkspaceInfo CreateWorkspaceForRepSpec( RepositorySpec repositorySpec, string wkPath, ILog log) { CreateWorkspaceDialogUserAssistant assistant = CreateWorkspaceDialogUserAssistant.ForWkPathAndName( PlasticGuiConfig.Get().Configuration.DefaultWorkspaceRoot, PlasticGui.Plastic.API.GetAllWorkspacesArray()); assistant.RepositoryChanged( repositorySpec.ToString(), string.Empty, string.Empty); WorkspaceInfo wkInfo = PlasticGui.Plastic.API.CreateWorkspace( wkPath, assistant.GetProposedWorkspaceName(), repositorySpec.ToString()); log.DebugFormat("Created workspace '{0}' on '{1}'", wkInfo.Name, wkInfo.ClientPath); return wkInfo; } void CreateWorkspaceOperation(OperationParams parameters) { RefreshAsset.BeforeLongAssetOperation(); try { ThreadPool.QueueUserWorkItem( CreateWorkspaceIfNeeded, parameters); while (mStatus != Status.Finished) { if (mDisplayProgress) { DisplayProgress(mStatus, parameters.Repository); } Thread.Sleep(150); } } finally { mIsOperationInProgress = false; EditorUtility.ClearProgressBar(); RefreshAsset.AfterLongAssetOperation(null); if (!mOperationFailed) { UVCSPlugin.Instance.Enable(); UVCSWindow window = ShowWindow.UVCS(); window.ExecuteFullReload(); if (mCreatedWkInfo != null) GetWindowIfOpened.BranchExplorer()?.OnWorkspaceCreated( mCreatedWkInfo); } } } void CreateWorkspaceIfNeeded(object state) { OperationParams parameters = (OperationParams)state; try { mDisplayProgress = true; mStatus = Status.ConfiguringCredentials; TokenExchangeResponse tokenExchangeResponse = AutoConfig.PlasticCredentials( parameters.AccessToken, parameters.RepositorySpec.Server); if (tokenExchangeResponse.Error != null) { mOperationFailed = true; LogTokenExchangeErrorInConsole(tokenExchangeResponse.Error); return; } mStatus = Status.CreatingWorkspace; TrackFeatureUseEvent.For( parameters.RepositorySpec, TrackFeatureUseEvent. Features.UnityPackage.CreateWorkspaceFromHub); mCreatedWkInfo = CreateWorkspaceForRepSpec( parameters.RepositorySpec, parameters.WorkspaceFullPath, mLog); mStatus = Status.PerformingInitialCheckin; PerformInitialCheckinIfRepositoryIsEmpty( mCreatedWkInfo, parameters.RepositorySpec, PlasticGui.Plastic.API, mLog); } catch (Exception ex) { LogException(ex); LogExceptionErrorInConsole(ex); mOperationFailed = true; } finally { mStatus = Status.Finished; } } static void PerformInitialCheckinIfRepositoryIsEmpty( WorkspaceInfo wkInfo, RepositorySpec repositorySpec, IPlasticAPI plasticApi, ILog log) { try { bool isEmptyRepository = IsEmptyRepositoryCondition. Evaluate(wkInfo, repositorySpec, plasticApi); if (!isEmptyRepository) return; PerformInitialCheckin.ForWorkspace(wkInfo, false, plasticApi); log.DebugFormat("Created initial checkin on repository '{0}'", repositorySpec.ToString()); } catch (Exception ex) { // create the initial checkin if it's possible, otherwise // just log the exception (no error shown for the user) LogException(ex); } } static void DisplayProgress( Status status, string repository) { string progressMessage = GetProgressString(status); float progressPercent = (int)status / (float)Status.Finished; EditorUtility.DisplayProgressBar( string.Format("{0} {1}", PlasticLocalization.Name.CreatingWorkspaceProgress.GetString(), repository), progressMessage, progressPercent); } static void LogTokenExchangeErrorInConsole(ErrorResponse.ErrorFields error) { UnityEngine.Debug.LogErrorFormat( PlasticLocalization.Name.ErrorCreatingWorkspaceForProject.GetString(), string.Format("Unable to get TokenExchangeResponse: {0} [code {1}]", error.Message, error.ErrorCode)); } static void LogExceptionErrorInConsole(Exception ex) { UnityEngine.Debug.LogErrorFormat( PlasticLocalization.Name.ErrorCreatingWorkspaceForProject.GetString(), ex.Message); } static void LogException(Exception ex) { mLog.WarnFormat("Message: {0}", ex.Message); mLog.DebugFormat("StackTrace:{0}{1}", Environment.NewLine, ex.StackTrace); } static string GetProgressString(Status status) { switch (status) { case Status.Starting: return PlasticLocalization.GetString( PlasticLocalization.Name.CreateWorkspaceProgressStarting); case Status.ConfiguringCredentials: return PlasticLocalization.GetString( PlasticLocalization.Name.CreateWorkspaceProgressConfiguringCredentials); case Status.CreatingWorkspace: return PlasticLocalization.GetString( PlasticLocalization.Name.CreateWorkspaceProgressCreatingWorkspace); case Status.PerformingInitialCheckin: return PlasticLocalization.GetString( PlasticLocalization.Name.CreateWorkspaceProgressPerformingInitialCheckin); case Status.Finished: return PlasticLocalization.GetString( PlasticLocalization.Name.CreateWorkspaceProgressFinished); } return string.Empty; } CreateWorkspace() { } enum Status : int { Starting = 1, ConfiguringCredentials = 2, CreatingWorkspace = 3, PerformingInitialCheckin = 4, Finished = 5 }; volatile Status mStatus = Status.Starting; volatile bool mOperationFailed = false; volatile bool mDisplayProgress; volatile WorkspaceInfo mCreatedWkInfo; const double IDLE_DELAY_SECONDS = 1.0; static bool mIsOperationInProgress; static readonly ILog mLog = PlasticApp.GetLogger("CreateWorkspace"); } }