private void ExecuteMigration(CommandOption token, CommandOption url, CommandOption configFile, bool forceFresh, CommandOption continueOnCritical) { ConfigJson config = null; var itemCount = 0; var revisionCount = 0; var importedItems = 0; var sw = new Stopwatch(); sw.Start(); try { string configFileName = configFile.Value(); ConfigReaderJson configReaderJson = new ConfigReaderJson(configFileName); config = configReaderJson.Deserialize(); var context = MigrationContext.Init("wi-import", config.Workspace, config.LogLevel, forceFresh, continueOnCritical.Value()); // connection settings for Azure DevOps/TFS: // full base url incl https, name of the project where the items will be migrated (if it doesn't exist on destination it will be created), personal access token var settings = new Settings(url.Value(), config.TargetProject, token.Value()) { BaseAreaPath = config.BaseAreaPath ?? string.Empty, // Root area path that will prefix area path of each migrated item BaseIterationPath = config.BaseIterationPath ?? string.Empty, // Root iteration path that will prefix each iteration IgnoreFailedLinks = config.IgnoreFailedLinks, ProcessTemplate = config.ProcessTemplate }; // initialize Azure DevOps/TFS connection. Creates/fetches project, fills area and iteration caches. var agent = Agent.Initialize(context, settings); if (agent == null) { Logger.Log(LogLevel.Critical, "Azure DevOps/TFS initialization error."); return; } var executionBuilder = new ExecutionPlanBuilder(context); var plan = executionBuilder.BuildExecutionPlan(); itemCount = plan.ReferenceQueue.AsEnumerable().Select(x => x.OriginId).Distinct().Count(); revisionCount = plan.ReferenceQueue.Count; BeginSession(configFileName, config, forceFresh, agent, itemCount, revisionCount); while (plan.TryPop(out ExecutionPlan.ExecutionItem executionItem)) { try { if (!forceFresh && context.Journal.IsItemMigrated(executionItem.OriginId, executionItem.Revision.Index)) { continue; } WorkItem wi = null; if (executionItem.WiId > 0) { wi = agent.GetWorkItem(executionItem.WiId); } else { wi = agent.CreateWI(executionItem.WiType); } Logger.Log(LogLevel.Info, $"Processing {importedItems + 1}/{revisionCount} - wi '{wi.Id}', jira '{executionItem.OriginId}, rev {executionItem.Revision.Index}'."); agent.ImportRevision(executionItem.Revision, wi); importedItems++; } catch (AbortMigrationException) { Logger.Log(LogLevel.Info, "Aborting migration..."); break; } catch (Exception ex) { try { Logger.Log(ex, $"Failed to import '{executionItem.ToString()}'."); } catch (AbortMigrationException) { break; } } } } catch (CommandParsingException e) { Logger.Log(LogLevel.Error, $"Invalid command line option(s): {e}"); } catch (Exception e) { Logger.Log(e, $"Unexpected migration error."); } finally { EndSession(itemCount, revisionCount, sw); } }
private static void BeginSession(string configFile, ConfigJson config, bool force, Agent agent, int itemsCount, int revisionCount) { var toolVersion = VersionInfo.GetVersionInfo(); var osVersion = System.Runtime.InteropServices.RuntimeInformation.OSDescription.Trim(); var machine = System.Environment.MachineName; var user = $"{System.Environment.UserDomainName}\\{System.Environment.UserName}"; var hostingType = GetHostingType(agent); Logger.Log(LogLevel.Info, $"Import started. Importing {itemsCount} items with {revisionCount} revisions."); Logger.StartSession("Azure DevOps Work Item Import", "wi-import-started", new Dictionary <string, string>() { { "Tool version :", toolVersion }, { "Start time :", DateTime.Now.ToString() }, { "Telemetry :", Logger.TelemetryStatus }, { "Session id :", Logger.SessionId }, { "Tool user :"******"Config :", configFile }, { "User :"******"Force :", force ? "yes" : "no" }, { "Log level :", config.LogLevel }, { "Machine :", machine }, { "System :", osVersion }, { "Azure DevOps url :", agent.Settings.Account }, { "Azure DevOps version :", "n/a" }, { "Azure DevOps type :", hostingType } }, new Dictionary <string, string>() { { "item-count", itemsCount.ToString() }, { "revision-count", revisionCount.ToString() }, { "system-version", "n/a" }, { "hosting-type", hostingType } }); }