From bf85eab5ca3d44609e65ea783429d179c05033d2 Mon Sep 17 00:00:00 2001 From: Willy-Peter Schaub Date: Tue, 17 Nov 2015 11:07:25 -0800 Subject: [PATCH] Create src folder and drop the early preview codebase. --- src/.nuget/NuGet.config | 6 + src/ALMRangersCodeAnalysisDictionary.xml | 25 + src/ALMRangersCommonAssemblyInfo.cs | 21 + src/ALMRangersRuleSet.ruleset | 58 + src/RMWorkflowMigrator.CmdLine/App.config | 6 + src/RMWorkflowMigrator.CmdLine/Options.cs | 82 + src/RMWorkflowMigrator.CmdLine/Program.cs | 251 ++ .../Properties/AssemblyInfo.cs | 29 + .../RMWorkflowMigrator.CmdLine.csproj | 96 + ...portedReleaseManagementVersionException.cs | 21 + .../packages.config | 4 + .../Interfaces/IRMComponentRepository.cs | 24 + .../Interfaces/IRMDeployerToolRepository.cs | 18 + .../IRMReleaseTemplateRepository.cs | 20 + .../Interfaces/IRMUserRepository.cs | 22 + .../Interfaces/IRMVersionRepository.cs | 18 + .../Model/ConfigurationVariable.cs | 36 + .../ConfigurationVariableEqualityComparer.cs | 26 + .../Model/DeployerTool.cs | 22 + .../Model/DeployerToolResource.cs | 18 + .../Model/RMComponent.cs | 33 + .../Model/RMDeploymentSequence.cs | 18 + .../Model/RMGroup.cs | 22 + .../Model/RMUser.cs | 18 + .../Model/RMVersion.cs | 24 + .../Model/VariableReplacementMethod.cs | 34 + .../Properties/AssemblyInfo.cs | 28 + .../RMWorkflowMigrator.DataAccess.csproj | 86 + .../Repository/RMComponentRepository.cs | 141 ++ .../Repository/RMDeployerToolRepository.cs | 87 + .../Repository/RMReleaseTemplateRepository.cs | 77 + .../Repository/RMUserRepository.cs | 80 + .../Repository/RMVersionRepository.cs | 56 + .../FileSystem.cs | 51 + .../IFileSystem.cs | 20 + .../Model/GenerationEventArgs.cs | 30 + .../Model/GenerationEventType.cs | 44 + .../Model/ScriptAction.cs | 36 + .../Model/ScriptGeneratorException.cs | 26 + .../Model/ScriptManualIntervention.cs | 26 + .../Properties/AssemblyInfo.cs | 29 + ...rkflowMigrator.Generator.PowerShell.csproj | 139 + .../ScriptGenerator.cs | 471 ++++ .../Templates/IndividualActionTemplate.cs | 710 ++++++ .../Templates/IndividualActionTemplate.tt | 70 + .../Templates/InitializationScript.tt | 17 + .../Templates/InitializationScript1.cs | 442 ++++ .../Templates/ManualInterventionTemplate.cs | 407 +++ .../Templates/ManualInterventionTemplate.tt | 16 + .../Templates/ReleaseScriptTemplate.cs | 537 ++++ .../Templates/ReleaseScriptTemplate.tt | 30 + .../Templates/TokenizationScript.cs | 337 +++ .../Templates/TokenizationScript.tt | 30 + .../UniquePropertyResolver.cs | 98 + .../packages.config | 4 + .../ActionParsedEventArgs.cs | 15 + .../BlockType.cs | 64 + .../ContainerParsedEventArgs.cs | 20 + .../DeploymentSequence.cs | 24 + .../IReleaseActionContainer.cs | 20 + .../IReleaseWorkflowBlock.cs | 22 + .../ManualIntervention.cs | 20 + .../Properties/AssemblyInfo.cs | 28 + .../RMWorkflowMigrator.Parser.Model.csproj | 76 + .../ReleaseAction.cs | 28 + .../ReleaseActionContainer.cs | 33 + .../RollbackAlwaysBlock.cs | 18 + .../RollbackBlock.cs | 27 + .../WorkflowBlockBase.cs | 26 + src/RMWorkflowMigrator.Parser/ActionParser.cs | 124 + .../ContainerParser.cs | 121 + .../Properties/AssemblyInfo.cs | 29 + .../RMWorkflowMigrator.Parser.csproj | 82 + .../ReleaseTemplateParser.cs | 35 + .../Properties/AssemblyInfo.cs | 25 + .../RMWorkflowMigrator.Tests.Unit.csproj | 129 + .../TestHelpers/FakeComponentRepo.cs | 40 + .../TestHelpers/FakeDeployerToolRepo.cs | 22 + .../TestHelpers/FakeFileSystem.cs | 42 + .../TestHelpers/FakeUserRepo.cs | 34 + ...ting_A_Folder_Structure_From_A_Workflow.cs | 295 +++ .../When_Generating_Scripts.cs | 2252 +++++++++++++++++ ...rsing_Release_Template_Workflow_Actions.cs | 236 ++ ..._Template_Workflow_Deployment_Sequences.cs | 236 ++ ...When_Resolving_Unique_Script_Parameters.cs | 355 +++ .../packages.config | 4 + src/RMWorkflowMigrator.sln | 66 + src/RMWorkflowMigrator.sln.DotSettings | 6 + src/Settings.StyleCop | 81 + 89 files changed, 9712 insertions(+) create mode 100644 src/.nuget/NuGet.config create mode 100644 src/ALMRangersCodeAnalysisDictionary.xml create mode 100644 src/ALMRangersCommonAssemblyInfo.cs create mode 100644 src/ALMRangersRuleSet.ruleset create mode 100644 src/RMWorkflowMigrator.CmdLine/App.config create mode 100644 src/RMWorkflowMigrator.CmdLine/Options.cs create mode 100644 src/RMWorkflowMigrator.CmdLine/Program.cs create mode 100644 src/RMWorkflowMigrator.CmdLine/Properties/AssemblyInfo.cs create mode 100644 src/RMWorkflowMigrator.CmdLine/RMWorkflowMigrator.CmdLine.csproj create mode 100644 src/RMWorkflowMigrator.CmdLine/UnsupportedReleaseManagementVersionException.cs create mode 100644 src/RMWorkflowMigrator.CmdLine/packages.config create mode 100644 src/RMWorkflowMigrator.DataAccess/Interfaces/IRMComponentRepository.cs create mode 100644 src/RMWorkflowMigrator.DataAccess/Interfaces/IRMDeployerToolRepository.cs create mode 100644 src/RMWorkflowMigrator.DataAccess/Interfaces/IRMReleaseTemplateRepository.cs create mode 100644 src/RMWorkflowMigrator.DataAccess/Interfaces/IRMUserRepository.cs create mode 100644 src/RMWorkflowMigrator.DataAccess/Interfaces/IRMVersionRepository.cs create mode 100644 src/RMWorkflowMigrator.DataAccess/Model/ConfigurationVariable.cs create mode 100644 src/RMWorkflowMigrator.DataAccess/Model/ConfigurationVariableEqualityComparer.cs create mode 100644 src/RMWorkflowMigrator.DataAccess/Model/DeployerTool.cs create mode 100644 src/RMWorkflowMigrator.DataAccess/Model/DeployerToolResource.cs create mode 100644 src/RMWorkflowMigrator.DataAccess/Model/RMComponent.cs create mode 100644 src/RMWorkflowMigrator.DataAccess/Model/RMDeploymentSequence.cs create mode 100644 src/RMWorkflowMigrator.DataAccess/Model/RMGroup.cs create mode 100644 src/RMWorkflowMigrator.DataAccess/Model/RMUser.cs create mode 100644 src/RMWorkflowMigrator.DataAccess/Model/RMVersion.cs create mode 100644 src/RMWorkflowMigrator.DataAccess/Model/VariableReplacementMethod.cs create mode 100644 src/RMWorkflowMigrator.DataAccess/Properties/AssemblyInfo.cs create mode 100644 src/RMWorkflowMigrator.DataAccess/RMWorkflowMigrator.DataAccess.csproj create mode 100644 src/RMWorkflowMigrator.DataAccess/Repository/RMComponentRepository.cs create mode 100644 src/RMWorkflowMigrator.DataAccess/Repository/RMDeployerToolRepository.cs create mode 100644 src/RMWorkflowMigrator.DataAccess/Repository/RMReleaseTemplateRepository.cs create mode 100644 src/RMWorkflowMigrator.DataAccess/Repository/RMUserRepository.cs create mode 100644 src/RMWorkflowMigrator.DataAccess/Repository/RMVersionRepository.cs create mode 100644 src/RMWorkflowMigrator.Generator.PowerShell/FileSystem.cs create mode 100644 src/RMWorkflowMigrator.Generator.PowerShell/IFileSystem.cs create mode 100644 src/RMWorkflowMigrator.Generator.PowerShell/Model/GenerationEventArgs.cs create mode 100644 src/RMWorkflowMigrator.Generator.PowerShell/Model/GenerationEventType.cs create mode 100644 src/RMWorkflowMigrator.Generator.PowerShell/Model/ScriptAction.cs create mode 100644 src/RMWorkflowMigrator.Generator.PowerShell/Model/ScriptGeneratorException.cs create mode 100644 src/RMWorkflowMigrator.Generator.PowerShell/Model/ScriptManualIntervention.cs create mode 100644 src/RMWorkflowMigrator.Generator.PowerShell/Properties/AssemblyInfo.cs create mode 100644 src/RMWorkflowMigrator.Generator.PowerShell/RMWorkflowMigrator.Generator.PowerShell.csproj create mode 100644 src/RMWorkflowMigrator.Generator.PowerShell/ScriptGenerator.cs create mode 100644 src/RMWorkflowMigrator.Generator.PowerShell/Templates/IndividualActionTemplate.cs create mode 100644 src/RMWorkflowMigrator.Generator.PowerShell/Templates/IndividualActionTemplate.tt create mode 100644 src/RMWorkflowMigrator.Generator.PowerShell/Templates/InitializationScript.tt create mode 100644 src/RMWorkflowMigrator.Generator.PowerShell/Templates/InitializationScript1.cs create mode 100644 src/RMWorkflowMigrator.Generator.PowerShell/Templates/ManualInterventionTemplate.cs create mode 100644 src/RMWorkflowMigrator.Generator.PowerShell/Templates/ManualInterventionTemplate.tt create mode 100644 src/RMWorkflowMigrator.Generator.PowerShell/Templates/ReleaseScriptTemplate.cs create mode 100644 src/RMWorkflowMigrator.Generator.PowerShell/Templates/ReleaseScriptTemplate.tt create mode 100644 src/RMWorkflowMigrator.Generator.PowerShell/Templates/TokenizationScript.cs create mode 100644 src/RMWorkflowMigrator.Generator.PowerShell/Templates/TokenizationScript.tt create mode 100644 src/RMWorkflowMigrator.Generator.PowerShell/UniquePropertyResolver.cs create mode 100644 src/RMWorkflowMigrator.Generator.PowerShell/packages.config create mode 100644 src/RMWorkflowMigrator.Parser.Model/ActionParsedEventArgs.cs create mode 100644 src/RMWorkflowMigrator.Parser.Model/BlockType.cs create mode 100644 src/RMWorkflowMigrator.Parser.Model/ContainerParsedEventArgs.cs create mode 100644 src/RMWorkflowMigrator.Parser.Model/DeploymentSequence.cs create mode 100644 src/RMWorkflowMigrator.Parser.Model/IReleaseActionContainer.cs create mode 100644 src/RMWorkflowMigrator.Parser.Model/IReleaseWorkflowBlock.cs create mode 100644 src/RMWorkflowMigrator.Parser.Model/ManualIntervention.cs create mode 100644 src/RMWorkflowMigrator.Parser.Model/Properties/AssemblyInfo.cs create mode 100644 src/RMWorkflowMigrator.Parser.Model/RMWorkflowMigrator.Parser.Model.csproj create mode 100644 src/RMWorkflowMigrator.Parser.Model/ReleaseAction.cs create mode 100644 src/RMWorkflowMigrator.Parser.Model/ReleaseActionContainer.cs create mode 100644 src/RMWorkflowMigrator.Parser.Model/RollbackAlwaysBlock.cs create mode 100644 src/RMWorkflowMigrator.Parser.Model/RollbackBlock.cs create mode 100644 src/RMWorkflowMigrator.Parser.Model/WorkflowBlockBase.cs create mode 100644 src/RMWorkflowMigrator.Parser/ActionParser.cs create mode 100644 src/RMWorkflowMigrator.Parser/ContainerParser.cs create mode 100644 src/RMWorkflowMigrator.Parser/Properties/AssemblyInfo.cs create mode 100644 src/RMWorkflowMigrator.Parser/RMWorkflowMigrator.Parser.csproj create mode 100644 src/RMWorkflowMigrator.Parser/ReleaseTemplateParser.cs create mode 100644 src/RMWorkflowMigrator.Tests.Unit/Properties/AssemblyInfo.cs create mode 100644 src/RMWorkflowMigrator.Tests.Unit/RMWorkflowMigrator.Tests.Unit.csproj create mode 100644 src/RMWorkflowMigrator.Tests.Unit/TestHelpers/FakeComponentRepo.cs create mode 100644 src/RMWorkflowMigrator.Tests.Unit/TestHelpers/FakeDeployerToolRepo.cs create mode 100644 src/RMWorkflowMigrator.Tests.Unit/TestHelpers/FakeFileSystem.cs create mode 100644 src/RMWorkflowMigrator.Tests.Unit/TestHelpers/FakeUserRepo.cs create mode 100644 src/RMWorkflowMigrator.Tests.Unit/When_Generating_A_Folder_Structure_From_A_Workflow.cs create mode 100644 src/RMWorkflowMigrator.Tests.Unit/When_Generating_Scripts.cs create mode 100644 src/RMWorkflowMigrator.Tests.Unit/When_Parsing_Release_Template_Workflow_Actions.cs create mode 100644 src/RMWorkflowMigrator.Tests.Unit/When_Parsing_Release_Template_Workflow_Deployment_Sequences.cs create mode 100644 src/RMWorkflowMigrator.Tests.Unit/When_Resolving_Unique_Script_Parameters.cs create mode 100644 src/RMWorkflowMigrator.Tests.Unit/packages.config create mode 100644 src/RMWorkflowMigrator.sln create mode 100644 src/RMWorkflowMigrator.sln.DotSettings create mode 100644 src/Settings.StyleCop diff --git a/src/.nuget/NuGet.config b/src/.nuget/NuGet.config new file mode 100644 index 0000000..38448ac --- /dev/null +++ b/src/.nuget/NuGet.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/ALMRangersCodeAnalysisDictionary.xml b/src/ALMRangersCodeAnalysisDictionary.xml new file mode 100644 index 0000000..3762055 --- /dev/null +++ b/src/ALMRangersCodeAnalysisDictionary.xml @@ -0,0 +1,25 @@ + + + + + Changeset + Changesets + Tfs + TFS + + + StyleCop + SourceFiles + + + Stylecop + Sourcefiles + + + + + AWS + SSH + + + \ No newline at end of file diff --git a/src/ALMRangersCommonAssemblyInfo.cs b/src/ALMRangersCommonAssemblyInfo.cs new file mode 100644 index 0000000..1c8fa5f --- /dev/null +++ b/src/ALMRangersCommonAssemblyInfo.cs @@ -0,0 +1,21 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// -------------------------------------------------------------------------------------------------------------------- + +using System.Reflection; + +[assembly: AssemblyCompany("Microsoft Corporation")] +[assembly: AssemblyCopyright("Copyright ©2015 Microsoft Corporation")] +[assembly: AssemblyTrademark("Microsoft Visual Studio ALM Rangers")] + +// Version information for an assembly consists of the following four values: +// Major Version +// Minor Version +// Build Number +// Revision +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.*")] \ No newline at end of file diff --git a/src/ALMRangersRuleSet.ruleset b/src/ALMRangersRuleSet.ruleset new file mode 100644 index 0000000..b07bfe8 --- /dev/null +++ b/src/ALMRangersRuleSet.ruleset @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RMWorkflowMigrator.CmdLine/App.config b/src/RMWorkflowMigrator.CmdLine/App.config new file mode 100644 index 0000000..2d2a12d --- /dev/null +++ b/src/RMWorkflowMigrator.CmdLine/App.config @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/RMWorkflowMigrator.CmdLine/Options.cs b/src/RMWorkflowMigrator.CmdLine/Options.cs new file mode 100644 index 0000000..8d5ae45 --- /dev/null +++ b/src/RMWorkflowMigrator.CmdLine/Options.cs @@ -0,0 +1,82 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the Options type. +// +// -------------------------------------------------------------------------------------------------------------------- + +// ReSharper disable UnusedAutoPropertyAccessor.Global +namespace Microsoft.ALMRangers.RMWorkflowMigrator.CmdLine +{ + using System; + using System.Data.SqlClient; + + using CommandLine; + using CommandLine.Text; + + internal class Options + { + [Option('n', "SqlServerName", Required = true, HelpText = "The name of the SqlServer server hosting the Release Management database.")] + public string SqlServerName { get; set; } + + [Option('d', "DatabaseName", Required = true, HelpText = "The name of the Release Management database.")] + public string DatabaseName { get; set; } + + [Option("ConnectTimeout", Required = false, DefaultValue = 15, HelpText = "The length of time (in seconds) to wait for a connection to the SQL server before terminating the attempt and generating an error. By default timeout is 15 seconds.")] + public int ConnectTimeout { get; set; } + + [Option('l', "NetworkLibrary", Required = false, HelpText = "The name of the network library used to establish a connection to the SQL Server. Supported values include dbnmpntw (Named Pipes), dbmsrpcn (Multiprotocol), dbmsadsn (AppleTalk), dbmsgnet (VIA), dbmslpcn (Shared Memory) and dbmsspxn (IPX/SPX), and dbmssocn (TCP/IP). The corresponding network DLL must be installed on the system to which you connect. If you do not specify a network and you use a local server (for example, \".\" or \"(local)\"), Shared Memory is used.")] + public string NetworkLibrary { get; set; } + + [Option('t', "TemplateName", Required = true, HelpText = "Name of the template to export. If the name contains spaces use \"the name\"")] + public string TemplateName { get; set; } + + [Option('s', "TemplateStage", Required = true, HelpText = "Stage of the template to export. If the name contains spaces use \"the name\"")] + public string TemplateStage { get; set; } + + [Option('o', "OutputFolder", DefaultValue = "Output", HelpText = @"The folder to output the migration PowerShell scripts to. Can be relative or absolute. If this parameter is being enclosed in quotation marks, do not include a trailing backslash. Ex: ""C:\Output"", not ""C:\Output\""")] + public string OutputFolder { get; set; } + + [Option('c', "CreateParameterizedScripts", DefaultValue = false, HelpText = "Create scripts with parameter blocks instead of separate initialization scripts")] + public bool CreateParameterizedScripts { get; set; } + + [Option('v', "Verbose", DefaultValue = false, HelpText = "Prints the detailed messages to standard output.")] + public bool Verbose { get; set; } + + public string ConnectionString + { + get + { + var builder = new SqlConnectionStringBuilder { IntegratedSecurity = true, ConnectTimeout = this.ConnectTimeout }; + + if (!string.IsNullOrEmpty(this.SqlServerName)) + { + builder.DataSource = this.SqlServerName; + } + + if (!string.IsNullOrEmpty(this.DatabaseName)) + { + builder.InitialCatalog = this.DatabaseName; + } + + if (!string.IsNullOrEmpty(this.NetworkLibrary)) + { + builder.NetworkLibrary = this.NetworkLibrary; + } + + return builder.ConnectionString; + } + } + + [ParserState] + public IParserState LastParserState { get; set; } + + [HelpOption] + public string GetUsage() + { + return HelpText.AutoBuild(this, current => HelpText.DefaultParsingErrorsHandler(this, current)); + } + } +} diff --git a/src/RMWorkflowMigrator.CmdLine/Program.cs b/src/RMWorkflowMigrator.CmdLine/Program.cs new file mode 100644 index 0000000..a4e4ebb --- /dev/null +++ b/src/RMWorkflowMigrator.CmdLine/Program.cs @@ -0,0 +1,251 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the Program type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.CmdLine +{ + using System; + using System.Collections.Generic; + using System.Data.SqlClient; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + + using CommandLine; + + using DataAccess.Model; + using DataAccess.Repository; + using Generator.PowerShell; + using Generator.PowerShell.Model; + using Parser; + using Parser.Model; + + public static class Program + { + private static readonly Dictionary SupportedVersions = new Dictionary + { + { "12.0.21031.1", false }, + { "12.0.30110.0", false }, + { "12.0.30501.0", false }, + { "12.0.30723.0", false }, + + // Everything above this point is untested. U3 may work. Earlier is unlikely. + { "12.0.31101.0", true }, + { "14.0.23102.0", true } + }; + + private static readonly Dictionary VersionMapping = new Dictionary + { + { "12.0.21031.1", "2013 RTM" }, + { "12.0.30110.0", "2013 Update 1" }, + { "12.0.30501.0", "2013 Update 2" }, + { "12.0.30723.0", "2013 Update 3" }, + { "12.0.31101.0", "2013 Update 4" }, + { "14.0.23102.0", "2015 RTM" } + }; + + private static Options options; + + public static void Main(string[] args) + { + options = GetOptions(args); + + if (!string.IsNullOrEmpty(options.OutputFolder) && options.OutputFolder.Contains("\"")) + { + Console.WriteLine("ERROR: The OutputPath (-o) parameter was provided with a trailing backslash. Please remove the trailing backslash and try again."); + DisplayParameters(); + return; + } + + if (options.LastParserState != null && options.LastParserState.Errors.Any()) + { + DisplayParameters(); + return; + } + + // Check we have at least been passed a server name + if (!string.IsNullOrEmpty(options.SqlServerName)) + { + var generatorTask = RunGeneratorAsync(); + Task.WaitAll(generatorTask); + } + } + + private static Options GetOptions(string[] args) + { + var parsedOptions = new Options(); + try + { + if (!Parser.Default.ParseArguments(args, parsedOptions)) + { + return parsedOptions; + } + } + catch (ParserException ex) + { + Console.WriteLine($"{Environment.NewLine}Microsoft.ALMRangers.RMWorkflowMigrator: Parameter error"); + Console.WriteLine(ex.Message); + if (ex.InnerException != null) + { + Console.WriteLine(ex.InnerException.Message); + } + } + + return parsedOptions; + } + + private static async Task RetrieveRmVersion() + { + var versionRepo = new RMVersionRepository(options.ConnectionString); + var version = await versionRepo.GetRMVersion(); + PrintOnlyIfVerbose($"Release Management version detected: {version} ({VersionMapping[version]})"); + + if (SupportedVersions[version]) + { + return version.StartsWith("14.") ? RMVersion.Rm2015 : RMVersion.Rm2013; + } + + var exceptionMessage = new StringBuilder(); + exceptionMessage.AppendLine($"Version {version} ({VersionMapping[version]}) is unsupported."); + var supportedVersions = SupportedVersions.Where(srv => srv.Value).Select(srv => VersionMapping[srv.Key]); + exceptionMessage.AppendLine("Supported versions:"); + exceptionMessage.AppendLine(string.Join($",{Environment.NewLine}", supportedVersions)); + + throw new UnsupportedReleaseManagementVersionException(exceptionMessage.ToString()); + } + + private static async Task RunGeneratorAsync() + { + try + { + var version = await RetrieveRmVersion(); + + var workflow = await RetrieveWorkflow(version); + + if (workflow != null) + { + var scriptGenerator = ConfigureScriptGenerator(version); + + PrintOnlyIfVerbose("Parsing release template"); + var sequence = workflow.ToReleaseTemplate(); + PrintOnlyIfVerbose("Done parsing release template"); + + PrintOnlyIfVerbose("Generating PowerShell"); + await scriptGenerator.GenerateScriptAsync(sequence, options.OutputFolder); + PrintOnlyIfVerbose("Done generating PowerShell"); + + Console.WriteLine($"{Environment.NewLine}Release workflow generated"); + } + else + { + Console.WriteLine($"{Environment.NewLine}No Results returned for TemplateName: '{0}' and StageName: '{1}'\n", options.TemplateName, options.TemplateStage); + } + } + catch (AggregateException ae) + { + ae.Handle(x => + { + if (x is SqlException) // This we know how to handle. + { + Console.WriteLine($"{Environment.NewLine}Cannot connect to the DB '{0}' on the SQL server '{1}'{Environment.NewLine}", options.DatabaseName, options.SqlServerName); return true; + } + + Console.WriteLine($"{Environment.NewLine}Unexpected error in processing '{0}'{Environment.NewLine}", x.Message); + return false; + }); + } + // ReSharper disable once CatchAllClause + catch (Exception ex) + { + // final global catch + Console.WriteLine("A critical error occurred:"); + Console.WriteLine(ex.Message); + Console.WriteLine(ex.StackTrace); + } + } + + private static ScriptGenerator ConfigureScriptGenerator(RMVersion version) + { + PrintOnlyIfVerbose($"Generating the scripts for the workflow '{options.TemplateName}' stage '{options.TemplateStage}' into folder '{options.OutputFolder}'{Environment.NewLine}"); + + // make sure we have the output folder + var fs = new FileSystem(); + fs.CreateDirectory(options.OutputFolder); + + var scriptGenerator = new ScriptGenerator( + fs, + new RMComponentRepository(options.ConnectionString, version), + new RMUserRepository(options.ConnectionString, version), + new RMDeployerToolRepository(options.ConnectionString, version), + !options.CreateParameterizedScripts); + + // enable full logging + scriptGenerator.ScriptGenerationNotification += PrintGeneratorEvents; + ActionParser.ActionParsed += PrintActionEvents; + ContainerParser.ContainerParsed += PrintContainerEvents; + return scriptGenerator; + } + + private static async Task RetrieveWorkflow(RMVersion version) + { + var repository = new RMReleaseTemplateRepository( + options.ConnectionString, + options.TemplateName, + options.TemplateStage, + version); + + PrintOnlyIfVerbose($"{Environment.NewLine}Connecting to the DB '{options.DatabaseName}' on the SQL server '{options.SqlServerName}'"); + + // get the workflow + var workflow = await repository.GetDeploymentSequence(); + return workflow; + } + + private static void PrintContainerEvents(object sender, ContainerParsedEventArgs args) + { + PrintOnlyIfVerbose($"{args.ItemType}: {args.DisplayName}"); + } + + private static void PrintGeneratorEvents(object sender, GenerationEventArgs args) + { + PrintOnlyIfVerbose($"{args.GenerationEventType}: {args.Sequence} - {args.BlockType} {args.DisplayName} IsContainer: {args.IsContainer} IsEnabled: {args.IsEnabled}"); + } + + private static void PrintActionEvents(object sender, ActionParsedEventArgs args) + { + PrintOnlyIfVerbose($"{args.ItemType}: {args.DisplayName}"); + } + + private static void PrintOnlyIfVerbose(string eventText) + { + if (!options.Verbose) + { + return; + } + + Console.WriteLine(eventText); + Console.Out.Flush(); + } + + /// + /// List the provided parameters + /// + private static void DisplayParameters() + { + Console.WriteLine($"{Environment.NewLine}Microsoft.ALMRangers.RMWorkflowMigrator Tool"); + Console.WriteLine("============================================"); + Console.WriteLine($"OutputFolder:\t\t\t{options.OutputFolder}"); + Console.WriteLine($"ConnectionString:\t\t{options.ConnectionString}"); + Console.WriteLine($"TemplateName:\t\t\t{options.TemplateName}"); + Console.WriteLine($"TemplateStage:\t\t\t{options.TemplateStage}"); + Console.WriteLine($"CreateParameterizedScripts:\t{options.CreateParameterizedScripts}{Environment.NewLine}"); + Console.WriteLine($"Verbose:\t\t\t{options.Verbose}{Environment.NewLine}"); + Console.WriteLine(); + } + } +} diff --git a/src/RMWorkflowMigrator.CmdLine/Properties/AssemblyInfo.cs b/src/RMWorkflowMigrator.CmdLine/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f5510fd --- /dev/null +++ b/src/RMWorkflowMigrator.CmdLine/Properties/AssemblyInfo.cs @@ -0,0 +1,29 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Assembly-level attributes +// +// -------------------------------------------------------------------------------------------------------------------- + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Microsoft.ALMRangers.RMWorkflowMigrator.CommandLine")] +[assembly: AssemblyDescription("Workflow Migrator command line utility")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("Microsoft.ALMRangers.RMWorkflowMigrator.CommandLine")] + +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("e5ec1c9d-b621-47c5-8d91-0680ebb91fe0")] \ No newline at end of file diff --git a/src/RMWorkflowMigrator.CmdLine/RMWorkflowMigrator.CmdLine.csproj b/src/RMWorkflowMigrator.CmdLine/RMWorkflowMigrator.CmdLine.csproj new file mode 100644 index 0000000..696b07c --- /dev/null +++ b/src/RMWorkflowMigrator.CmdLine/RMWorkflowMigrator.CmdLine.csproj @@ -0,0 +1,96 @@ + + + + + Debug + AnyCPU + {E5EC1C9D-B621-47C5-8D91-0680EBB91FE0} + Exe + Properties + Microsoft.ALMRangers.RMWorkflowMigrator.CmdLine + RMWorkflowMigrator + v4.6 + 512 + SAK + SAK + SAK + SAK + + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + ..\ALMRangersRuleSet.ruleset + true + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + ..\ALMRangersRuleSet.ruleset + true + + + + ..\packages\CommandLineParser.1.9.71\lib\net45\CommandLine.dll + True + + + + + + + + + + + + + Properties\ALMRangersCommonAssemblyInfo.cs + + + + + + + + + + + + + {3cb3250e-e8f8-4164-bd1d-b8e910916fab} + RMWorkflowMigrator.DataAccess + + + {b5c99627-e15f-4d33-adf9-22012953a47b} + RMWorkflowMigrator.Generator.PowerShell + + + {dab5cc84-4e29-43e0-9ba9-81d377ea08ee} + RMWorkflowMigrator.Parser.Model + + + {7e2dc042-dfb7-40db-af1d-d28378702b30} + RMWorkflowMigrator.Parser + + + + + \ No newline at end of file diff --git a/src/RMWorkflowMigrator.CmdLine/UnsupportedReleaseManagementVersionException.cs b/src/RMWorkflowMigrator.CmdLine/UnsupportedReleaseManagementVersionException.cs new file mode 100644 index 0000000..714864f --- /dev/null +++ b/src/RMWorkflowMigrator.CmdLine/UnsupportedReleaseManagementVersionException.cs @@ -0,0 +1,21 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the UnsupportedReleaseManagementVersionException type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.CmdLine +{ + using System; + + [Serializable] + public class UnsupportedReleaseManagementVersionException : Exception + { + public UnsupportedReleaseManagementVersionException(string message) : base(message) + { + } + } +} diff --git a/src/RMWorkflowMigrator.CmdLine/packages.config b/src/RMWorkflowMigrator.CmdLine/packages.config new file mode 100644 index 0000000..0d9f591 --- /dev/null +++ b/src/RMWorkflowMigrator.CmdLine/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/RMWorkflowMigrator.DataAccess/Interfaces/IRMComponentRepository.cs b/src/RMWorkflowMigrator.DataAccess/Interfaces/IRMComponentRepository.cs new file mode 100644 index 0000000..901e01b --- /dev/null +++ b/src/RMWorkflowMigrator.DataAccess/Interfaces/IRMComponentRepository.cs @@ -0,0 +1,24 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the IRMComponentRepository interface. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Interfaces +{ + using System; + using System.Collections.Generic; + using System.Threading.Tasks; + + using Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model; + + public interface IRMComponentRepository + { + Task GetComponentByIdAsync(Guid workflowGuid, int stageId); + + Task> GetComponentConfigurationVariablesAsync(Guid workflowGuid, int stageId); + } +} diff --git a/src/RMWorkflowMigrator.DataAccess/Interfaces/IRMDeployerToolRepository.cs b/src/RMWorkflowMigrator.DataAccess/Interfaces/IRMDeployerToolRepository.cs new file mode 100644 index 0000000..261f024 --- /dev/null +++ b/src/RMWorkflowMigrator.DataAccess/Interfaces/IRMDeployerToolRepository.cs @@ -0,0 +1,18 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the IRMDeployerToolRepository interface. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Interfaces +{ + using System.Threading.Tasks; + + public interface IRMDeployerToolRepository + { + Task WriteToolToDiskAsync(int deployerToolId, string diskPath); + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.DataAccess/Interfaces/IRMReleaseTemplateRepository.cs b/src/RMWorkflowMigrator.DataAccess/Interfaces/IRMReleaseTemplateRepository.cs new file mode 100644 index 0000000..186a7c9 --- /dev/null +++ b/src/RMWorkflowMigrator.DataAccess/Interfaces/IRMReleaseTemplateRepository.cs @@ -0,0 +1,20 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the IRMReleaseTemplateRepository interface. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Interfaces +{ + using System.Threading.Tasks; + + using Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model; + + public interface IRMReleaseTemplateRepository + { + Task GetDeploymentSequence(); + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.DataAccess/Interfaces/IRMUserRepository.cs b/src/RMWorkflowMigrator.DataAccess/Interfaces/IRMUserRepository.cs new file mode 100644 index 0000000..9cc0dc7 --- /dev/null +++ b/src/RMWorkflowMigrator.DataAccess/Interfaces/IRMUserRepository.cs @@ -0,0 +1,22 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the IRMUserRepository interface. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Interfaces +{ + using System.Threading.Tasks; + + using Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model; + + public interface IRMUserRepository + { + Task GetGroupAsync(int groupId); + + Task GetUserAsync(int userId); + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.DataAccess/Interfaces/IRMVersionRepository.cs b/src/RMWorkflowMigrator.DataAccess/Interfaces/IRMVersionRepository.cs new file mode 100644 index 0000000..10c3c5b --- /dev/null +++ b/src/RMWorkflowMigrator.DataAccess/Interfaces/IRMVersionRepository.cs @@ -0,0 +1,18 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the IRMVersionRepository interface. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Interfaces +{ + using System.Threading.Tasks; + + public interface IRMVersionRepository + { + Task GetRMVersion(); + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.DataAccess/Model/ConfigurationVariable.cs b/src/RMWorkflowMigrator.DataAccess/Model/ConfigurationVariable.cs new file mode 100644 index 0000000..f4f3c46 --- /dev/null +++ b/src/RMWorkflowMigrator.DataAccess/Model/ConfigurationVariable.cs @@ -0,0 +1,36 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the ConfigurationVariable type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model +{ + public class ConfigurationVariable + { + /// + /// Gets or sets the original name of the RM configuration variable. + /// + /// + /// The name of the configuration variable as it originated from the database. + /// + public string OriginalName { get; set; } = string.Empty; + + /// + /// Gets or sets the remapped name of the RM configuration variable. + /// + /// + /// The remapped name of the configuration variable, after it has been disambiguated and made unique + /// + public string RemappedName { get; set; } + + public string Value { get; set; } = string.Empty; + + public bool IsParameter { get; set; } + + public bool Encrypted { get; set; } + } +} diff --git a/src/RMWorkflowMigrator.DataAccess/Model/ConfigurationVariableEqualityComparer.cs b/src/RMWorkflowMigrator.DataAccess/Model/ConfigurationVariableEqualityComparer.cs new file mode 100644 index 0000000..dfac5df --- /dev/null +++ b/src/RMWorkflowMigrator.DataAccess/Model/ConfigurationVariableEqualityComparer.cs @@ -0,0 +1,26 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the ConfigurationVariableEqualityComparer type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model +{ + using System.Collections.Generic; + + public class ConfigurationVariableEqualityComparer : IEqualityComparer + { + public bool Equals(ConfigurationVariable x, ConfigurationVariable y) + { + return x.RemappedName == y.RemappedName; + } + + public int GetHashCode(ConfigurationVariable obj) + { + return obj.RemappedName.GetHashCode(); + } + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.DataAccess/Model/DeployerTool.cs b/src/RMWorkflowMigrator.DataAccess/Model/DeployerTool.cs new file mode 100644 index 0000000..98b84ae --- /dev/null +++ b/src/RMWorkflowMigrator.DataAccess/Model/DeployerTool.cs @@ -0,0 +1,22 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the DeployerTool type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model +{ + using System.Collections.Generic; + + public class DeployerTool + { + public int Id { get; set; } + + public string Name { get; set; } = string.Empty; + + public IEnumerable Resources { get; set; } + } +} diff --git a/src/RMWorkflowMigrator.DataAccess/Model/DeployerToolResource.cs b/src/RMWorkflowMigrator.DataAccess/Model/DeployerToolResource.cs new file mode 100644 index 0000000..8078ef1 --- /dev/null +++ b/src/RMWorkflowMigrator.DataAccess/Model/DeployerToolResource.cs @@ -0,0 +1,18 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the DeployerToolResource type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model +{ + public class DeployerToolResource + { + public string FileName { get; set; } = string.Empty; + + public byte[] BinaryData { get; set; } + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.DataAccess/Model/RMComponent.cs b/src/RMWorkflowMigrator.DataAccess/Model/RMComponent.cs new file mode 100644 index 0000000..13c3692 --- /dev/null +++ b/src/RMWorkflowMigrator.DataAccess/Model/RMComponent.cs @@ -0,0 +1,33 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the RMDeploymentSequence type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model +{ + using System; + using System.Collections.Generic; + + public class RMComponent + { + public int Id { get; set; } + + public int DeployerToolId { get; set; } + + public Guid WorkflowActivityId { get; set; } + + public string FileExtensionFilter { get; set; } = string.Empty; + + public string Command { get; set; } = string.Empty; + + public string Arguments { get; set; } = string.Empty; + + public VariableReplacementMethod VariableReplacementMethod { get; set; } + + public IEnumerable ConfigurationVariables { get; set; } = new List(); + } +} diff --git a/src/RMWorkflowMigrator.DataAccess/Model/RMDeploymentSequence.cs b/src/RMWorkflowMigrator.DataAccess/Model/RMDeploymentSequence.cs new file mode 100644 index 0000000..4237ee4 --- /dev/null +++ b/src/RMWorkflowMigrator.DataAccess/Model/RMDeploymentSequence.cs @@ -0,0 +1,18 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the RMDeploymentSequence type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model +{ + public class RMDeploymentSequence + { + public int StageId { get; set; } + + public string WorkflowXaml { get; set; } + } +} diff --git a/src/RMWorkflowMigrator.DataAccess/Model/RMGroup.cs b/src/RMWorkflowMigrator.DataAccess/Model/RMGroup.cs new file mode 100644 index 0000000..d196ac3 --- /dev/null +++ b/src/RMWorkflowMigrator.DataAccess/Model/RMGroup.cs @@ -0,0 +1,22 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the RMGroup type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model +{ + using System.Collections.Generic; + + public class RMGroup + { + public int Id { get; set; } + + public string Name { get; set; } + + public IEnumerable GroupMembers { get; set; } + } +} diff --git a/src/RMWorkflowMigrator.DataAccess/Model/RMUser.cs b/src/RMWorkflowMigrator.DataAccess/Model/RMUser.cs new file mode 100644 index 0000000..7048961 --- /dev/null +++ b/src/RMWorkflowMigrator.DataAccess/Model/RMUser.cs @@ -0,0 +1,18 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the RMUser type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model +{ + public class RMUser + { + public int Id { get; set; } + + public string Name { get; set; } + } +} diff --git a/src/RMWorkflowMigrator.DataAccess/Model/RMVersion.cs b/src/RMWorkflowMigrator.DataAccess/Model/RMVersion.cs new file mode 100644 index 0000000..3d79e91 --- /dev/null +++ b/src/RMWorkflowMigrator.DataAccess/Model/RMVersion.cs @@ -0,0 +1,24 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the RMVersion type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model +{ + public enum RMVersion + { + /// + /// Release Management 2013 + /// + Rm2013, + + /// + /// Release Management 2015 + /// + Rm2015 + } +} diff --git a/src/RMWorkflowMigrator.DataAccess/Model/VariableReplacementMethod.cs b/src/RMWorkflowMigrator.DataAccess/Model/VariableReplacementMethod.cs new file mode 100644 index 0000000..11f17fc --- /dev/null +++ b/src/RMWorkflowMigrator.DataAccess/Model/VariableReplacementMethod.cs @@ -0,0 +1,34 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the VariableReplacementMethod type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model +{ + public enum VariableReplacementMethod + { + /// + /// Replace Variable only in command + /// + OnlyInCommand = 1, + + /// + /// Replace Variable before installation + /// + BeforeInstallation = 2, + + /// + /// Replace Variable after installation + /// + AfterInstallation = 3, + + /// + /// Replace Variable before and after installation + /// + BeforeAndAfterInstallation = 4 + } +} diff --git a/src/RMWorkflowMigrator.DataAccess/Properties/AssemblyInfo.cs b/src/RMWorkflowMigrator.DataAccess/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d075056 --- /dev/null +++ b/src/RMWorkflowMigrator.DataAccess/Properties/AssemblyInfo.cs @@ -0,0 +1,28 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Assembly-level attributes +// +// -------------------------------------------------------------------------------------------------------------------- + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess")] +[assembly: AssemblyDescription("Workflow Migrator data access layer")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("8a99a33f-2142-41ca-97b6-40f11cad0bd9")] \ No newline at end of file diff --git a/src/RMWorkflowMigrator.DataAccess/RMWorkflowMigrator.DataAccess.csproj b/src/RMWorkflowMigrator.DataAccess/RMWorkflowMigrator.DataAccess.csproj new file mode 100644 index 0000000..b024594 --- /dev/null +++ b/src/RMWorkflowMigrator.DataAccess/RMWorkflowMigrator.DataAccess.csproj @@ -0,0 +1,86 @@ + + + + + Debug + AnyCPU + {3CB3250E-E8F8-4164-BD1D-B8E910916FAB} + Library + Properties + Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess + RMWorkflowMigrator.DataAccess + v4.6 + 512 + + SAK + SAK + SAK + SAK + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + ..\ALMRangersRuleSet.ruleset + true + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + ..\ALMRangersRuleSet.ruleset + true + + + + + + + + + + + + + Properties\ALMRangersCommonAssemblyInfo.cs + + + False + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RMWorkflowMigrator.DataAccess/Repository/RMComponentRepository.cs b/src/RMWorkflowMigrator.DataAccess/Repository/RMComponentRepository.cs new file mode 100644 index 0000000..4add7bd --- /dev/null +++ b/src/RMWorkflowMigrator.DataAccess/Repository/RMComponentRepository.cs @@ -0,0 +1,141 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the RMComponentRepository type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Repository +{ + using System; + using System.Collections.Generic; + using System.Data.Common; + using System.Data.SqlClient; + using System.Data.SqlTypes; + using System.Diagnostics.CodeAnalysis; + using System.IO; + using System.Threading.Tasks; + + using Interfaces; + using Model; + + public class RMComponentRepository : IRMComponentRepository + { + private readonly string connectionString; + private readonly string tableNamePrefix; + + public RMComponentRepository(string connectionString, RMVersion version) + { + this.connectionString = connectionString; + this.tableNamePrefix = version == RMVersion.Rm2015 ? "RM.tbl_" : "dbo."; + } + + /// + /// Get workflow Component by ID + /// + /// Workflow GUID + /// Stage ID + /// + /// An error occurred in a , or object during a streaming operation. For more information about streaming, see SqlClient Streaming Support. + /// An error occurred in a , or object during a streaming operation. For more information about streaming, see SqlClient Streaming Support. + /// The , or object was closed during a streaming operation. For more information about streaming, see SqlClient Streaming Support. + /// SQL Server returned an error while executing the command text. A timeout occurred during a streaming operation. For more information about streaming, see SqlClient Streaming Support. + /// A other than Binary or VarBinary was used when was set to . For more information about streaming, see SqlClient Streaming Support.A other than Char, NChar, NVarChar, VarChar, or Xml was used when was set to .A other than Xml was used when was set to . + /// An error occurred while executing the command text. + /// Component with specified ID can't be found. + /// Trying to read a column that does not exist. + /// The connection drops or is closed during the data retrieval.The is closed during the data retrieval.There is no data ready to be read (for example, the first hasn't been called, or returned false).Tried to read a previously-read column in sequential mode.There was an asynchronous operation in progress. This applies to all Get* methods when running in sequential mode, as they could be called while reading a stream. + /// The value of the column was null ( == true), retrieving a non-SQL type. + [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed.")] + public async Task GetComponentByIdAsync(Guid workflowGuid, int stageId) + { + var sqlQuery = $@"SELECT c.Id, c.DeployerToolId, c.FileExtensionFilter, c.Command, c.Arguments, c.VariableReplacementModeId + FROM {this.tableNamePrefix}Component c + inner join {this.tableNamePrefix}ApplicationVersionStageActivity avsa + ON c.Id = avsa.ComponentId + WHERE avsa.ApplicationVersionStageId = @StageId + AND avsa.WorkflowActivityId = @WorkflowActivityId"; + + using (var sqlConnection = new SqlConnection { ConnectionString = this.connectionString }) + using (var componentCommand = new SqlCommand(sqlQuery, sqlConnection)) + { + componentCommand.Parameters.AddWithValue("@stageId", stageId); + componentCommand.Parameters.AddWithValue("@WorkflowActivityId", workflowGuid); + await sqlConnection.OpenAsync(); + + using (var sqlReader = await componentCommand.ExecuteReaderAsync()) + { + if (!await sqlReader.ReadAsync()) + { + throw new ArgumentException($"Could not retrieve a component with the ID {workflowGuid}."); + } + + return new RMComponent + { + Id = sqlReader.GetFieldValue(0), + DeployerToolId = sqlReader.GetFieldValue(1), + WorkflowActivityId = workflowGuid, + FileExtensionFilter = sqlReader.GetFieldValue(2), + Command = sqlReader.GetFieldValue(3), + Arguments = sqlReader.GetFieldValue(4), + VariableReplacementMethod = (VariableReplacementMethod)sqlReader.GetFieldValue(5), + ConfigurationVariables = await this.GetComponentConfigurationVariablesAsync(workflowGuid, stageId) + }; + } + } + } + + /// + /// Gets configuration variables for component + /// + /// Workflow GUID + /// Component ID + /// Collection of configuration variables + /// A other than Binary or VarBinary was used when was set to . For more information about streaming, see SqlClient Streaming Support.A other than Char, NChar, NVarChar, VarChar, or Xml was used when was set to .A other than Xml was used when was set to . + /// SQL Server returned an error while executing the command text.A timeout occurred during a streaming operation. For more information about streaming, see SqlClient Streaming Support. + /// An error occurred in a , or object during a streaming operation. For more information about streaming, see SqlClient Streaming Support. + /// An error occurred while executing the command text. + /// The value of the column was null ( == true), retrieving a non-SQL type. + [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed.")] + public async Task> GetComponentConfigurationVariablesAsync(Guid workflowGuid, int stageId) + { + var sqlQuery = + $@"SELECT cv.Name, cvv.Value, cv.IsParameter, cv.TypeId + FROM {this.tableNamePrefix}ConfigurationVariable cv + inner join {this.tableNamePrefix}ConfigurationVariableValue cvv + ON cv.Id = cvv.ConfigurationVariableId + inner join {this.tableNamePrefix}ApplicationVersionStageActivity avsa + ON cvv.ApplicationVersionStageActivityId = avsa.Id + WHERE avsa.ApplicationVersionStageId = @StageId + AND avsa.WorkflowActivityId = @WorkflowActivityId"; + + using (var sqlConnection = new SqlConnection { ConnectionString = this.connectionString }) + using (var command = new SqlCommand(sqlQuery, sqlConnection)) + { + command.Parameters.AddWithValue("@stageId", stageId); + command.Parameters.AddWithValue("@WorkflowActivityId", workflowGuid); + await sqlConnection.OpenAsync(); + + var configVars = new List(); + using (var sqlReader = await command.ExecuteReaderAsync()) + { + while (await sqlReader.ReadAsync()) + { + var cv = new ConfigurationVariable + { + OriginalName = sqlReader.GetFieldValue(0), + Value = sqlReader.GetFieldValue(1), + IsParameter = sqlReader.GetFieldValue(2), + Encrypted = sqlReader.GetFieldValue(3) == 2 + }; + configVars.Add(cv); + } + + return configVars; + } + } + } + } +} diff --git a/src/RMWorkflowMigrator.DataAccess/Repository/RMDeployerToolRepository.cs b/src/RMWorkflowMigrator.DataAccess/Repository/RMDeployerToolRepository.cs new file mode 100644 index 0000000..9e3da04 --- /dev/null +++ b/src/RMWorkflowMigrator.DataAccess/Repository/RMDeployerToolRepository.cs @@ -0,0 +1,87 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the RMDeployerToolRepository type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Repository +{ + using System; + using System.Data; + using System.Data.Common; + using System.Data.SqlClient; + using System.Diagnostics.CodeAnalysis; + using System.IO; + using System.Security; + using System.Threading.Tasks; + + using Interfaces; + using Model; + + public class RMDeployerToolRepository : IRMDeployerToolRepository + { + private readonly string connectionString; + private readonly string tableNamePrefix; + + public RMDeployerToolRepository(string connectionString, RMVersion version) + { + this.connectionString = connectionString; + this.tableNamePrefix = version == RMVersion.Rm2015 ? "RM.tbl_" : "dbo."; + } + + /// + /// Export deployer tool to disk + /// + /// Tool ID + /// Disk path where to export + /// + /// A other than Binary or VarBinary was used when was set to . For more information about streaming, see SqlClient Streaming Support.A other than Char, NChar, NVarChar, VarChar, or Xml was used when was set to .A other than Xml was used when was set to . + /// SQL Server returned an error while executing the command text.A timeout occurred during a streaming operation. For more information about streaming, see SqlClient Streaming Support. + /// An error occurred in a , or object during a streaming operation. For more information about streaming, see SqlClient Streaming Support. + /// An error occurred while executing the command text. + /// The caller does not have the required permission. + /// The file cannot be found, such as when mode is FileMode. Truncate or FileMode.Open, and the file specified by path does not exist. The file must already exist in these modes. + /// The specified path is invalid, such as being on an unmapped drive. + [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")] + public async Task WriteToolToDiskAsync(int deployerToolId, string diskPath) + { + if (File.Exists(diskPath)) + { + return; + } + + var toolQuery = + $@"SELECT dt.Name [ToolName], r.Name [FileName], r.BinaryData [Data] FROM {this.tableNamePrefix}DeployerTool dt + INNER JOIN {this.tableNamePrefix}deployertoolresource dtr + ON dtr.DeployerToolId = dt.id + INNER JOIN {this.tableNamePrefix}Resource r + ON r.id = dtr.resourceid + WHERE dt.id = @DeployerToolId"; + + using (var sqlConnection = new SqlConnection { ConnectionString = this.connectionString }) + using (var command = new SqlCommand(toolQuery, sqlConnection)) + { + command.Parameters.AddWithValue("@DeployerToolId", deployerToolId); + await sqlConnection.OpenAsync(); + using (var reader = await command.ExecuteReaderAsync(CommandBehavior.SequentialAccess)) + { + while (await reader.ReadAsync()) + { + var fileName = await reader.GetFieldValueAsync(1); + var outputPath = Path.Combine(diskPath, fileName); + + using (var fs = new FileStream(outputPath, FileMode.Create)) + using (var bw = new BinaryWriter(fs)) + { + bw.Write(await reader.GetFieldValueAsync(2)); + bw.Flush(); + } + } + } + } + } + } +} diff --git a/src/RMWorkflowMigrator.DataAccess/Repository/RMReleaseTemplateRepository.cs b/src/RMWorkflowMigrator.DataAccess/Repository/RMReleaseTemplateRepository.cs new file mode 100644 index 0000000..137c4f7 --- /dev/null +++ b/src/RMWorkflowMigrator.DataAccess/Repository/RMReleaseTemplateRepository.cs @@ -0,0 +1,77 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the RMReleaseTemplateRepository type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Repository +{ + using System; + using System.Data; + using System.Data.SqlClient; + using System.Threading.Tasks; + + using Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Interfaces; + using Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model; + + public class RMReleaseTemplateRepository : IRMReleaseTemplateRepository + { + private readonly string connectionString; + private readonly string templateName; + private readonly string templateStage; + private readonly RMVersion version; + + public RMReleaseTemplateRepository(string connectionString, string templateName, string templateStage, RMVersion version) + { + this.connectionString = connectionString; + this.templateName = templateName; + this.templateStage = templateStage; + this.version = version; + } + + public async Task GetDeploymentSequence() + { + var tableNamePrefix = this.version == RMVersion.Rm2015 ? "RM.tbl_" : "dbo."; + + var sqlQuery = $@"SELECT avs.Workflow, avs.id [StageId] FROM + {tableNamePrefix}ApplicationVersion av + INNER JOIN {tableNamePrefix}ApplicationVersionStage avs + ON avs.ApplicationVersionId = av.id + INNER JOIN {tableNamePrefix}Stage s + ON s.Id = avs.stageid + INNER JOIN {tableNamePrefix}stagetype st + ON st.id = s.stagetypeid + WHERE st.name = @stageName + AND av.name = @templateName"; + + using (var sqlConnection = new SqlConnection { ConnectionString = this.connectionString }) + using (var workflowCommand = new SqlCommand(sqlQuery, sqlConnection)) + using (var da = new SqlDataAdapter(workflowCommand)) + using (var results = new DataTable()) + { + workflowCommand.Parameters.AddWithValue("@stageName", this.templateStage); + workflowCommand.Parameters.AddWithValue("@templateName", this.templateName); + + await sqlConnection.OpenAsync(); + + da.Fill(results); + + if (results.Rows.Count == 0) + { + throw new ArgumentException($@"Unable to locate a release template named ""{this.templateName}"" with a stage named ""{this.templateStage}""."); + } + + var sequence = new RMDeploymentSequence + { + WorkflowXaml = (string)results.Rows[0]["Workflow"], + StageId = (int)results.Rows[0]["StageID"] + }; + + return sequence; + } + } + } +} diff --git a/src/RMWorkflowMigrator.DataAccess/Repository/RMUserRepository.cs b/src/RMWorkflowMigrator.DataAccess/Repository/RMUserRepository.cs new file mode 100644 index 0000000..b880de5 --- /dev/null +++ b/src/RMWorkflowMigrator.DataAccess/Repository/RMUserRepository.cs @@ -0,0 +1,80 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the RMUserRepository type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Repository +{ + using System.Data.Common; + using System.Data.SqlClient; + using System.Threading.Tasks; + + using Interfaces; + using Model; + + public class RMUserRepository : IRMUserRepository + { + private readonly string connectionString; + private readonly string tableNamePrefix; + + public RMUserRepository(string connectionString, RMVersion version) + { + this.connectionString = connectionString; + this.tableNamePrefix = version == RMVersion.Rm2015 ? "RM.tbl_" : "dbo."; + } + + /// + /// Gets Group from repository by ID + /// + /// Group ID + /// + /// An error occurred while executing the command text. + public async Task GetGroupAsync(int groupId) + { + string groupSql = $@"SELECT sg.Name FROM {this.tableNamePrefix}SecurityGroup sg + LEFT OUTER JOIN {this.tableNamePrefix}SecurityGroup_TfsGroup sgtg + ON sgtg.SecurityGroupId = sg.id + LEFT JOIN {this.tableNamePrefix}tfsgroup tg + ON tg.id = sgtg.TfsGroupId + LEFT OUTER JOIN {this.tableNamePrefix}SecurityGroup_AdGroup sgag + ON sgag.SecurityGroupId = sg.id + LEFT OUTER JOIN {this.tableNamePrefix}adgroup ag + ON ag.id = sgag.AdGroupId + INNER JOIN {this.tableNamePrefix}SecurityGroup_User sgu + ON sgu.SecurityGroupId = sg.id + WHERE isdeleted = 0 + AND sg.ID = @GroupId"; + + using (var sqlConnection = new SqlConnection { ConnectionString = this.connectionString }) + using (var groupCommand = new SqlCommand(groupSql, sqlConnection)) + { + groupCommand.Parameters.AddWithValue("@GroupID", groupId); + await sqlConnection.OpenAsync(); + return new RMGroup { Id = groupId, Name = await groupCommand.ExecuteScalarAsync() as string }; + } + } + + /// + /// Gets User by ID + /// + /// User ID + /// + /// An error occurred while executing the command text. + public async Task GetUserAsync(int userId) + { + string userSql = $@"SELECT DisplayName, UserName, Email FROM {this.tableNamePrefix}User WHERE isdeleted = 0 AND id = @UserId"; + + using (var sqlConnection = new SqlConnection { ConnectionString = this.connectionString }) + using (var userCommand = new SqlCommand(userSql, sqlConnection)) + { + userCommand.Parameters.AddWithValue("@UserID", userId); + await sqlConnection.OpenAsync(); + return new RMUser() { Id = userId, Name = await userCommand.ExecuteScalarAsync() as string }; + } + } + } +} diff --git a/src/RMWorkflowMigrator.DataAccess/Repository/RMVersionRepository.cs b/src/RMWorkflowMigrator.DataAccess/Repository/RMVersionRepository.cs new file mode 100644 index 0000000..a63c4e5 --- /dev/null +++ b/src/RMWorkflowMigrator.DataAccess/Repository/RMVersionRepository.cs @@ -0,0 +1,56 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the RMVersionRepository type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Repository +{ + using System.Data.SqlClient; + using System.Threading.Tasks; + + using Interfaces; + + public class RMVersionRepository : IRMVersionRepository + { + private readonly string connectionString; + + public RMVersionRepository(string connectionString) + { + this.connectionString = connectionString; + } + + public async Task GetRMVersion() + { + string version; + const string VersionQuery2013 = @"Select v.Number From dbo.Version AS v"; + const string VersionQuery2015 = @"Select v.Number From RM.tbl_Version AS v"; + + try + { + // This might fail, we don't want to require access to the master database to query table schema + version = await this.CheckVersion(VersionQuery2015); + } + catch (SqlException e) when (e.Number == 208) + { + // if this call fails, we want the exception to bubble up normally since it's a legitimate error + version = await this.CheckVersion(VersionQuery2013); + } + + return version; + } + + private async Task CheckVersion(string versionSql) + { + using (var sqlConnection = new SqlConnection { ConnectionString = this.connectionString }) + using (var sqlCommand = new SqlCommand(versionSql, sqlConnection)) + { + sqlConnection.Open(); + return await sqlCommand.ExecuteScalarAsync() as string; + } + } + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Generator.PowerShell/FileSystem.cs b/src/RMWorkflowMigrator.Generator.PowerShell/FileSystem.cs new file mode 100644 index 0000000..c4b1889 --- /dev/null +++ b/src/RMWorkflowMigrator.Generator.PowerShell/FileSystem.cs @@ -0,0 +1,51 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the FileSystem type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell +{ + using System.Collections.ObjectModel; + using System.IO; + using System.Linq; + using System.Management.Automation; + + using Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model; + + public class FileSystem : IFileSystem + { + public void CreateDirectory(string path) + { + Directory.CreateDirectory(path); + } + + public void WriteAllText(string path, string contents) + { + File.WriteAllText(path, contents); + + if (new FileInfo(path).Extension != ".ps1") + { + return; + } + + Collection errors; + PSParser.Tokenize(contents, out errors); + + if (errors.Any()) + { + throw new ScriptGeneratorException( + "The script that was generated at path {path} contains syntax errors. This is likely an application bug.", + errors.ToDictionary(error => error.Token)); + } + } + + public bool Exists(string path) + { + return File.Exists(path); + } + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Generator.PowerShell/IFileSystem.cs b/src/RMWorkflowMigrator.Generator.PowerShell/IFileSystem.cs new file mode 100644 index 0000000..f9ade46 --- /dev/null +++ b/src/RMWorkflowMigrator.Generator.PowerShell/IFileSystem.cs @@ -0,0 +1,20 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the IFileSystem type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell +{ + public interface IFileSystem + { + void CreateDirectory(string path); + + void WriteAllText(string path, string contents); + + bool Exists(string path); + } +} diff --git a/src/RMWorkflowMigrator.Generator.PowerShell/Model/GenerationEventArgs.cs b/src/RMWorkflowMigrator.Generator.PowerShell/Model/GenerationEventArgs.cs new file mode 100644 index 0000000..a01039b --- /dev/null +++ b/src/RMWorkflowMigrator.Generator.PowerShell/Model/GenerationEventArgs.cs @@ -0,0 +1,30 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the GenerationEventArgs type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model +{ + using System; + + using Microsoft.ALMRangers.RMWorkflowMigrator.Parser.Model; + + public class GenerationEventArgs : EventArgs + { + public BlockType BlockType { get; set; } + + public string DisplayName { get; set; } + + public int Sequence { get; set; } + + public bool IsEnabled { get; set; } + + public bool IsContainer { get; set; } + + public GenerationEventType GenerationEventType { get; set; } + } +} diff --git a/src/RMWorkflowMigrator.Generator.PowerShell/Model/GenerationEventType.cs b/src/RMWorkflowMigrator.Generator.PowerShell/Model/GenerationEventType.cs new file mode 100644 index 0000000..853bf4a --- /dev/null +++ b/src/RMWorkflowMigrator.Generator.PowerShell/Model/GenerationEventType.cs @@ -0,0 +1,44 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the GenerationEventType type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model +{ + public enum GenerationEventType + { + /// + /// Container start event type + /// + ContainerStart, + + /// + /// Container end event type + /// + ContainerEnd, + + /// + /// Action start event type + /// + ActionStart, + + /// + /// Action end event type + /// + ActionEnd, + + /// + /// Warning event + /// + Warning, + + /// + /// Error event + /// + Error + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Generator.PowerShell/Model/ScriptAction.cs b/src/RMWorkflowMigrator.Generator.PowerShell/Model/ScriptAction.cs new file mode 100644 index 0000000..9b630a1 --- /dev/null +++ b/src/RMWorkflowMigrator.Generator.PowerShell/Model/ScriptAction.cs @@ -0,0 +1,36 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model +{ + using System.Collections.Generic; + using Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model; + + public class ScriptAction + { + public string DisplayName { get; set; } + + public bool Enabled { get; set; } + + public bool IsComponent { get; set; } + + public int DeployerToolId { get; set; } + + public string FileExtensionFilter { get; set; } + + public string Command { get; set; } + + public string Arguments { get; set; } + + public VariableReplacementMethod VariableReplacementMethod { get; set; } + + public IEnumerable ConfigurationVariables { get; set; } = new List(); + + public int Sequence { get; set; } + + public Dictionary> RollbackScripts { get; set; } = new Dictionary>(); + } +} diff --git a/src/RMWorkflowMigrator.Generator.PowerShell/Model/ScriptGeneratorException.cs b/src/RMWorkflowMigrator.Generator.PowerShell/Model/ScriptGeneratorException.cs new file mode 100644 index 0000000..4d016bd --- /dev/null +++ b/src/RMWorkflowMigrator.Generator.PowerShell/Model/ScriptGeneratorException.cs @@ -0,0 +1,26 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the ScriptGeneratorException type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model +{ + using System; + using System.Collections.Generic; + using System.Management.Automation; + + [Serializable] + public class ScriptGeneratorException : Exception + { + public ScriptGeneratorException(string message, IDictionary scriptErrors) : base(message) + { + this.ScriptErrors = scriptErrors; + } + + private IDictionary ScriptErrors { get; set; } + } +} diff --git a/src/RMWorkflowMigrator.Generator.PowerShell/Model/ScriptManualIntervention.cs b/src/RMWorkflowMigrator.Generator.PowerShell/Model/ScriptManualIntervention.cs new file mode 100644 index 0000000..f40511a --- /dev/null +++ b/src/RMWorkflowMigrator.Generator.PowerShell/Model/ScriptManualIntervention.cs @@ -0,0 +1,26 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the ScriptManualIntervention type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model +{ + public class ScriptManualIntervention + { + public string DisplayName { get; set; } + + public bool Enabled { get; set; } + + public int Sequence { get; set; } + + public string InterventionText { get; set; } + + public string Target { get; set; } + + public bool IsTargetGroup { get; set; } + } +} diff --git a/src/RMWorkflowMigrator.Generator.PowerShell/Properties/AssemblyInfo.cs b/src/RMWorkflowMigrator.Generator.PowerShell/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..364a79d --- /dev/null +++ b/src/RMWorkflowMigrator.Generator.PowerShell/Properties/AssemblyInfo.cs @@ -0,0 +1,29 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Assembly-level attributes +// +// -------------------------------------------------------------------------------------------------------------------- + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell")] +[assembly: AssemblyDescription("Workflow Migrator PowerShell script generator")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("b5c99627-e15f-4d33-adf9-22012953a47b")] \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Generator.PowerShell/RMWorkflowMigrator.Generator.PowerShell.csproj b/src/RMWorkflowMigrator.Generator.PowerShell/RMWorkflowMigrator.Generator.PowerShell.csproj new file mode 100644 index 0000000..6f10186 --- /dev/null +++ b/src/RMWorkflowMigrator.Generator.PowerShell/RMWorkflowMigrator.Generator.PowerShell.csproj @@ -0,0 +1,139 @@ + + + + + Debug + AnyCPU + {B5C99627-E15F-4D33-ADF9-22012953A47B} + Library + Properties + Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell + RMWorkflowMigrator.Generator.PowerShell + v4.6 + 512 + SAK + SAK + SAK + SAK + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + ..\ALMRangersRuleSet.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + ..\ALMRangersRuleSet.ruleset + + + + + + + ..\packages\System.Management.Automation_PowerShell_3.0.6.3.9600.17400\lib\net40\System.Management.Automation.dll + True + + + + + + + + + + + Properties\ALMRangersCommonAssemblyInfo.cs + + + + + ManualInterventionTemplate.tt + True + True + + + True + True + InitializationScript.tt + + + + + + + + + IndividualActionTemplate.tt + True + True + + + ReleaseScriptTemplate.tt + True + True + + + + True + True + TokenizationScript.tt + + + + + + TextTemplatingFilePreprocessor + ManualInterventionTemplate.cs + + + TextTemplatingFilePreprocessor + InitializationScript1.cs + + + TextTemplatingFilePreprocessor + IndividualActionTemplate.cs + + + TextTemplatingFilePreprocessor + ReleaseScriptTemplate.cs + + + TextTemplatingFilePreprocessor + TokenizationScript.cs + + + + + {3cb3250e-e8f8-4164-bd1d-b8e910916fab} + RMWorkflowMigrator.DataAccess + + + {dab5cc84-4e29-43e0-9ba9-81d377ea08ee} + RMWorkflowMigrator.Parser.Model + + + + + + + + + + + \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Generator.PowerShell/ScriptGenerator.cs b/src/RMWorkflowMigrator.Generator.PowerShell/ScriptGenerator.cs new file mode 100644 index 0000000..90f5b0a --- /dev/null +++ b/src/RMWorkflowMigrator.Generator.PowerShell/ScriptGenerator.cs @@ -0,0 +1,471 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Linq; + using System.Threading.Tasks; + + using Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Interfaces; + using Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model; + using Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model; + using static Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model.GenerationEventType; + using Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Templates; + using Microsoft.ALMRangers.RMWorkflowMigrator.Parser.Model; + + /// + /// Generates a series of PowerShell scripts for a Release Management release template given a object. + /// + public class ScriptGenerator + { + private readonly IFileSystem fs; + + private readonly bool generateInitializationScript; + + private readonly string initScriptName = "InitializationScript.ps1"; + + private readonly string meaningfulDisplayNameFolderFormat = "{0}_{1}_{2}"; + + private readonly string meaninglessDisplayNameFolderFormat = "{0}_{1}"; + + private readonly string releaseScriptName = "ReleaseScript.ps1"; + + private readonly IRMComponentRepository componentRepo; + + private readonly IRMDeployerToolRepository deployerToolRepo; + + private readonly IRMUserRepository userRepo; + + private string deployerToolsPath = "DeployerTools"; + + public ScriptGenerator( + IFileSystem fs, + IRMComponentRepository componentRepo, + IRMUserRepository userRepo, + IRMDeployerToolRepository deployerToolRepo, + bool generateInitializationScript = true) + { + this.fs = fs; + this.componentRepo = componentRepo; + this.userRepo = userRepo; + this.deployerToolRepo = deployerToolRepo; + this.generateInitializationScript = generateInitializationScript; + } + + public event EventHandler ScriptGenerationNotification; + + /// + /// Generates the script. + /// + /// + /// The sequence. + /// + /// + /// The target path. + /// + /// + /// The . + /// + public async Task GenerateScriptAsync(DeploymentSequence sequence, string targetPath) + { + this.deployerToolsPath = Path.Combine(targetPath, "DeployerTools"); + var tokenScriptPath = Path.Combine(this.deployerToolsPath, "TokenizationScript.ps1"); + + this.fs.CreateDirectory(this.deployerToolsPath); + + if (!this.fs.Exists(tokenScriptPath)) + { + var tokenScript = new TokenizationScript(); + this.fs.WriteAllText(tokenScriptPath, tokenScript.TransformText()); + } + + foreach (var container in sequence.Containers) + { + await this.ProcessContainerContainerAsync(sequence.ReleaseTemplateStageId, container, targetPath); + } + } + + private static ScriptAction CreateScriptAction(RMComponent component, ReleaseAction action) + { + return new ScriptAction + { + Enabled = action.Enabled, + DisplayName = action.DisplayName, + Sequence = action.Sequence, + IsComponent = action.IsComponent, + DeployerToolId = component.DeployerToolId, + FileExtensionFilter = component.FileExtensionFilter, + Command = component.Command, + Arguments = component.Arguments, + VariableReplacementMethod = component.VariableReplacementMethod, + ConfigurationVariables = component.ConfigurationVariables + }; + } + + private static string CreateScriptFromTemplate( + IEnumerable scriptElements, + IEnumerable manualInterventionElements, + IEnumerable scriptParams, + bool generateInitScript) + { + var individualActions = new Dictionary(); + var rollbackComponents = + scriptElements.SelectMany(se => se.RollbackScripts.Values) + .SelectMany(se => se) + .Where(se => se.IsComponent && se.DeployerToolId != 0); + + var components = + scriptElements.Where(se => se.IsComponent && se.DeployerToolId != 0).Union(rollbackComponents).ToList(); + foreach (var individualAction in scriptElements.ToList()) + { + var actionTemplate = new IndividualActionTemplate + { + Session = + new Dictionary + { + { + "action", + individualAction + }, + { + "components", + components + } + } + }; + actionTemplate.Initialize(); + individualActions.Add(individualAction.Sequence, actionTemplate.TransformText()); + } + + foreach (var manualIntervention in manualInterventionElements.ToList()) + { + var manualInterventionTemplate = new ManualInterventionTemplate + { + Session = + new Dictionary + { + { + "manualIntervention", + manualIntervention + } + } + }; + manualInterventionTemplate.Initialize(); + individualActions.Add(manualIntervention.Sequence, manualInterventionTemplate.TransformText()); + } + + var releaseScript = new ReleaseScriptTemplate + { + Session = + new Dictionary + { + { + "releaseActions", + individualActions + .OrderBy( + ia => + ia.Key) + .Select( + ia => + ia.Value) + }, + { + "components", + components + }, + { + "scriptParams", + scriptParams + .ToList() + }, + { + "generateInitializationScript", + generateInitScript + } + } + }; + + releaseScript.Initialize(); + return releaseScript.TransformText(); + } + + private static GenerationEventArgs GetActionGenerationArgs(ReleaseAction action, GenerationEventType eventType) + { + return new GenerationEventArgs + { + BlockType = action.ItemType, + DisplayName = action.DisplayName, + Sequence = action.Sequence, + IsEnabled = action.Enabled, + IsContainer = false, + GenerationEventType = eventType + }; + } + + private static GenerationEventArgs GetContainerGenerationArgs( + IReleaseActionContainer container, + GenerationEventType eventType) + { + return new GenerationEventArgs + { + BlockType = container.ItemType, + DisplayName = container.DisplayName, + Sequence = container.Sequence, + IsEnabled = true, + IsContainer = true, + GenerationEventType = eventType + }; + } + + private async Task CreateScriptManualIntervention( + ManualIntervention manualIntervention) + { + string target; + if (manualIntervention.IsGroup) + { + var groupData = await this.userRepo.GetGroupAsync(manualIntervention.UserId); + target = groupData.Name; + } + else + { + var userData = await this.userRepo.GetUserAsync(manualIntervention.UserId); + target = userData.Name; + } + + return new ScriptManualIntervention + { + DisplayName = manualIntervention.DisplayName, + Sequence = manualIntervention.Sequence, + Enabled = manualIntervention.Enabled, + InterventionText = manualIntervention.Text, + Target = target, + IsTargetGroup = manualIntervention.IsGroup + }; + } + + private async Task ProcessContainerContainerAsync( + int stageId, + IReleaseActionContainer container, + string targetPath) + { + this.ScriptGenerationNotification?.Invoke(this, GetContainerGenerationArgs(container, ContainerStart)); + + var folderFormat = container.DisplayNameIsMeaningful + ? this.meaningfulDisplayNameFolderFormat + : this.meaninglessDisplayNameFolderFormat; + var newPath = Path.Combine( + targetPath, + string.Format(folderFormat, container.Sequence, container.ItemType, container.ValidFileName)); + this.fs.CreateDirectory(newPath); + + foreach (var subcontainer in + container.SubItems.OfType>() + .Where( + subcontainer => + subcontainer.ItemType != BlockType.Rollback && subcontainer.ItemType != BlockType.RollbackAlways)) + { + // ScriptGenerationNotification?.Invoke(this, GetContainerGenerationArgs(subcontainer, ContainerStart)); + await this.ProcessContainerContainerAsync(stageId, subcontainer, newPath); + + // ScriptGenerationNotification?.Invoke(this, GetContainerGenerationArgs(subcontainer, ContainerEnd)); + } + + await this.ProcessReleaseActionContainerAsync(stageId, container.SubItems?.OfType(), newPath); + this.ScriptGenerationNotification?.Invoke(this, GetContainerGenerationArgs(container, ContainerEnd)); + } + + private async Task ProcessReleaseActionContainerAsync( + int stageId, + IEnumerable actions, + string targetPath) + { + if (!actions.Any()) + { + return; + } + + var scriptActionElements = new List(); + var scriptManualInterventionElements = new List(); + + var enabledActions = actions.Where(a => a.Enabled); + + /* Find all of the actions that are not rollback blocks and create a ScriptAction that encompasses both the + stuff extracted from the RM XAML workflow and stuff we pull out of the RM database. + */ + foreach (var action in actions) + { + if (action is RollbackBlock) + { + continue; + } + + if (!action.Enabled) + { + this.ScriptGenerationNotification?.Invoke(this, GetActionGenerationArgs(action, Warning)); + continue; + } + + this.ScriptGenerationNotification?.Invoke(this, GetActionGenerationArgs(action, ActionStart)); + + if (action.ItemType == BlockType.ManualIntervention) + { + scriptManualInterventionElements.Add( + await this.CreateScriptManualIntervention((ManualIntervention)action)); + } + else + { + var component = await this.componentRepo.GetComponentByIdAsync(action.WorkflowActivityId, stageId); + await this.deployerToolRepo.WriteToolToDiskAsync(component.DeployerToolId, this.deployerToolsPath); + scriptActionElements.Add(CreateScriptAction(component, action)); + } + + this.ScriptGenerationNotification?.Invoke(this, GetActionGenerationArgs(action, ActionEnd)); + } + + // Resolve the rollback actions within this container -- a rollback is a subcontainer full of actions, tied to a specific step (or all steps, in the case of rollback always) + var rollbackActions = await this.ResolveRollbackActions(stageId, enabledActions, scriptActionElements); + + /* We'll need to resolve unique script parameters, since RM can have non-unique parameters. For example, + Action 1: __Hello __ = "placeholder" + Action 2: __Hello__ = "Bar" + We can't pass in a parameter called $Hello to a script, since it requires different values. + Thus, we need to flatten out the list of parameters across the entire server/tag container and "uniqueify" them. + */ + var elementsToResolve = + rollbackActions.SelectMany(ra => ra.Value).Union(scriptActionElements).OrderBy(s => s.Sequence); + + UniquePropertyResolver.ResolveProperties(elementsToResolve); + + // Invoke the T4 template for the container and for any rollback/rollback always blocks and write them out to disk + var rollbackParameters = + rollbackActions.SelectMany(s => s.Value).SelectMany(s => s.ConfigurationVariables).ToList(); + var scriptParameters = scriptActionElements.SelectMany(s => s.ConfigurationVariables).ToList(); + + var releaseScriptText = CreateScriptFromTemplate( + scriptActionElements, + scriptManualInterventionElements, + scriptParameters.Union(rollbackParameters).Distinct(new ConfigurationVariableEqualityComparer()), + this.generateInitializationScript); + this.fs.WriteAllText(Path.Combine(targetPath, this.releaseScriptName), releaseScriptText); + + foreach (var rollbackGroup in rollbackActions) + { + string rollbackScriptName = $"{rollbackGroup.Key}.ps1"; + + var rollbackScript = CreateScriptFromTemplate( + rollbackGroup.Value, + scriptManualInterventionElements, + rollbackGroup.Value.SelectMany(s => s.ConfigurationVariables).Distinct(), + false); + this.fs.WriteAllText(Path.Combine(targetPath, rollbackScriptName), rollbackScript); + } + + if (this.generateInitializationScript) + { + var initScript = new InitializationScript + { + Session = + new Dictionary + { + { + "releaseActions", + scriptActionElements + }, + { + "scriptParams", + scriptParameters + .Union( + rollbackParameters) + .Distinct(new ConfigurationVariableEqualityComparer()) + } + } + }; + initScript.Initialize(); + this.fs.WriteAllText(Path.Combine(targetPath, this.initScriptName), initScript.TransformText()); + } + } + + private async Task>> ResolveRollbackActions( + int stageId, + IEnumerable actions, + IReadOnlyCollection scriptElements) + { + var rollbackActions = new Dictionary>(); + + // Group the rollback action parameters + var groupedRollbackActions = + actions.OfType() + .GroupBy( + g => + string.Format( + g.DisplayNameIsMeaningful + ? this.meaningfulDisplayNameFolderFormat + : this.meaninglessDisplayNameFolderFormat, + g.Sequence, + g.ItemType, + g.DisplayName)) + .Select(s => new { s.Key, Item = s.First() }) + .ToList(); + + foreach (var rollbackGroup in groupedRollbackActions) + { + var rollbackScriptActions = new List(); + if (rollbackGroup.Item == null) + { + continue; + } + + /* Figure out which actions need rollback blocks + Normally, a rollback is attached to just the preceding action. + Rollback always blocks are attached to all of the actions in the script. Basically, if the script fails, it should always run that block. + */ + IEnumerable scriptElementsToAttachRollbacks; + if (rollbackGroup.Item.ItemType == BlockType.RollbackAlways) + { + scriptElementsToAttachRollbacks = scriptElements.ToList(); + } + else + { + // Attach the rollback script to the action directly preceding it, and to all actions that happen after. + scriptElementsToAttachRollbacks = + scriptElements.Where(se => se.Sequence >= rollbackGroup.Item.Sequence - 1).ToList(); + } + + this.ScriptGenerationNotification?.Invoke( + this, + GetContainerGenerationArgs(rollbackGroup.Item, ContainerStart)); + foreach (var rollbackAction in rollbackGroup.Item.SubItems.OfType()) + { + this.ScriptGenerationNotification?.Invoke( + this, + GetActionGenerationArgs(rollbackAction, ActionStart)); + var component = + await this.componentRepo.GetComponentByIdAsync(rollbackAction.WorkflowActivityId, stageId); + await this.deployerToolRepo.WriteToolToDiskAsync(component.DeployerToolId, this.deployerToolsPath); + var action = CreateScriptAction(component, rollbackAction); + action.Sequence += rollbackGroup.Item.Sequence; + rollbackScriptActions.Add(action); + this.ScriptGenerationNotification?.Invoke(this, GetActionGenerationArgs(rollbackAction, ActionEnd)); + } + + foreach (var scriptElementToAttachRollback in scriptElementsToAttachRollbacks) + { + scriptElementToAttachRollback.RollbackScripts.Add(rollbackGroup.Key, rollbackScriptActions); + } + + rollbackActions.Add(rollbackGroup.Key, rollbackScriptActions); + this.ScriptGenerationNotification?.Invoke( + this, + GetContainerGenerationArgs(rollbackGroup.Item, ContainerEnd)); + } + + return rollbackActions; + } + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Generator.PowerShell/Templates/IndividualActionTemplate.cs b/src/RMWorkflowMigrator.Generator.PowerShell/Templates/IndividualActionTemplate.cs new file mode 100644 index 0000000..d432a74 --- /dev/null +++ b/src/RMWorkflowMigrator.Generator.PowerShell/Templates/IndividualActionTemplate.cs @@ -0,0 +1,710 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 14.0.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Templates +{ + using System.Linq; + using DataAccess.Model; + using Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model; + using System; + + /// + /// Class to produce the template output + /// + + #line 1 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "14.0.0.0")] + public partial class IndividualActionTemplate : IndividualActionTemplateBase + { +#line hidden + /// + /// Create the template output + /// + public virtual string TransformText() + { + this.Write("\r\n# "); + + #line 9 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(action.DisplayName)); + + #line default + #line hidden + this.Write("\r\n# Parameters: \r\n"); + + #line 11 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +foreach (var configVar in action.ConfigurationVariables.Where(cv => cv.IsParameter)) { + + #line default + #line hidden + this.Write(" # "); + + #line 12 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(configVar.RemappedName)); + + #line default + #line hidden + this.Write(": "); + + #line 12 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(configVar.Value)); + + #line default + #line hidden + this.Write("\r\n"); + + #line 13 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +} + + #line default + #line hidden + + #line 14 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +if (action.IsComponent && action.ConfigurationVariables.Any(cv => !cv.IsParameter)) { + + #line default + #line hidden + this.Write("# Configuration Variables:\r\n"); + + #line 16 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +foreach (var configVar in action.ConfigurationVariables.Where(cv => !cv.IsParameter)) { + + #line default + #line hidden + this.Write(" # "); + + #line 17 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(configVar.RemappedName)); + + #line default + #line hidden + this.Write(": "); + + #line 17 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(configVar.Value)); + + #line default + #line hidden + this.Write("\r\n"); + + #line 18 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +} + + #line default + #line hidden + + #line 19 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +} + + #line default + #line hidden + this.Write("try {\r\n Write-Output \"Executing "); + + #line 21 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(action.DisplayName)); + + #line default + #line hidden + this.Write("\"\r\n"); + + #line 22 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +if (action.IsComponent) { +var hasConfigVariables = action.ConfigurationVariables.Any(cv => !cv.IsParameter); +var configVariables = action.ConfigurationVariables.Where(cv => !cv.IsParameter); +if (hasConfigVariables) { + + #line default + #line hidden + this.Write(" $configVariables = @{\r\n"); + + #line 27 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +foreach (var configVariable in configVariables) { + + #line default + #line hidden + this.Write(" \"__"); + + #line 28 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(configVariable.OriginalName)); + + #line default + #line hidden + this.Write("__\" = $"); + + #line 28 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(configVariable.RemappedName)); + + #line default + #line hidden + this.Write("\r\n"); + + #line 29 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +} + + #line default + #line hidden + this.Write(" }\r\n"); + + #line 31 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +} +if ((action.VariableReplacementMethod == VariableReplacementMethod.BeforeInstallation || action.VariableReplacementMethod == VariableReplacementMethod.BeforeAndAfterInstallation) && hasConfigVariables) { + + #line default + #line hidden + this.Write(" # Perform token replacement before installation\r\n\t&\"$(join-path $DeployerTool" + + "sPath \"TokenizationScript.ps1\")\" -FilePath $ComponentPath"); + + #line 34 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(action.Sequence)); + + #line default + #line hidden + this.Write(" -FileSpec \""); + + #line 34 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(action.FileExtensionFilter)); + + #line default + #line hidden + this.Write("\" -Values $configVariables\r\n\r\n"); + + #line 36 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +} +if (action.DeployerToolId != 0) { + + #line default + #line hidden + this.Write(" cd $ComponentPath"); + + #line 38 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(action.Sequence)); + + #line default + #line hidden + this.Write("\r\n &\"$(join-path $DeployerToolsPath \""); + + #line 39 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(action.Command)); + + #line default + #line hidden + this.Write("\")\" "); + + #line 39 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(action.Arguments)); + + #line default + #line hidden + this.Write("\r\n cd $basePath \r\n"); + + #line 41 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +} +else { + + #line default + #line hidden + this.Write(" &\"$(join-path $DeployerToolsPath \""); + + #line 43 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(action.Command)); + + #line default + #line hidden + this.Write("\")\" "); + + #line 43 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(action.Arguments)); + + #line default + #line hidden + this.Write("\r\n"); + + #line 44 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +} + + #line default + #line hidden + + #line 45 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +if ((action.VariableReplacementMethod == VariableReplacementMethod.AfterInstallation || action.VariableReplacementMethod == VariableReplacementMethod.BeforeAndAfterInstallation) && hasConfigVariables) { +var targetPath = action.ConfigurationVariables.First(cv => cv.OriginalName == "Installation Path").RemappedName; + + + #line default + #line hidden + this.Write("\r\n # Perform token replacement after installation\r\n &\"$(join-path $Deployer" + + "ToolsPath \"TokenizationScript.ps1\")\" -FilePath $"); + + #line 50 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(targetPath)); + + #line default + #line hidden + this.Write(" -FileSpec \""); + + #line 50 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(action.FileExtensionFilter)); + + #line default + #line hidden + this.Write("\" -Values $configVariables\r\n"); + + #line 51 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +} + + #line default + #line hidden + + #line 52 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +} else { + + #line default + #line hidden + this.Write(" &\"$(join-path $DeployerToolsPath \""); + + #line 53 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(action.Command)); + + #line default + #line hidden + this.Write("\")\" "); + + #line 53 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(action.Arguments)); + + #line default + #line hidden + this.Write("\r\n"); + + #line 54 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +} + + #line default + #line hidden + this.Write("}\r\ncatch {\r\n Write-output \""); + + #line 57 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(action.DisplayName)); + + #line default + #line hidden + this.Write(" failed. Error:\"\r\n write-output $_\r\n"); + + #line 59 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +if (action.RollbackScripts.Any()) { + + #line default + #line hidden + this.Write("Write-output \"Executing rollback script(s)\"\r\n"); + + #line 61 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +if (action.IsComponent) { + + #line default + #line hidden + this.Write(" cd $basePath\r\n"); + + #line 63 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +} + + #line default + #line hidden + + #line 64 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +foreach (var script in action.RollbackScripts) { + + #line default + #line hidden + this.Write(" .\\"); + + #line 65 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(script.Key)); + + #line default + #line hidden + this.Write(".ps1 "); + + #line 65 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(string.Join(" ", script.Value.OrderBy(rsa => rsa.Sequence).SelectMany(rsa => rsa.ConfigurationVariables).Where(s => !string.IsNullOrWhiteSpace(s.Value) && s.IsParameter).Select(s => "-" + s.RemappedName + " \"$" + s.RemappedName + "\"")))); + + #line default + #line hidden + this.Write(" "); + + #line 65 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(string.Join(" ", script.Value.Where(s => s.IsComponent).Select(c => $"-ComponentPath{c.Sequence} $ComponentPath{c.Sequence}")))); + + #line default + #line hidden + this.Write(" -DeployerToolsPath $DeployerToolsPath\r\n"); + + #line 66 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +} + + #line default + #line hidden + + #line 67 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" +} + + #line default + #line hidden + this.Write(" exit 1\r\n}\r\nWrite-Output \"\"\r\n"); + return this.GenerationEnvironment.ToString(); + } + + #line 1 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\IndividualActionTemplate.tt" + +private global::Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model.ScriptAction _actionField; + +/// +/// Access the action parameter of the template. +/// +private global::Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model.ScriptAction action +{ + get + { + return this._actionField; + } +} + +private global::System.Collections.Generic.IEnumerable _componentsField; + +/// +/// Access the components parameter of the template. +/// +private global::System.Collections.Generic.IEnumerable components +{ + get + { + return this._componentsField; + } +} + + +/// +/// Initialize the template +/// +public virtual void Initialize() +{ + if ((this.Errors.HasErrors == false)) + { +bool actionValueAcquired = false; +if (this.Session.ContainsKey("action")) +{ + this._actionField = ((global::Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model.ScriptAction)(this.Session["action"])); + actionValueAcquired = true; +} +if ((actionValueAcquired == false)) +{ + object data = global::System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("action"); + if ((data != null)) + { + this._actionField = ((global::Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model.ScriptAction)(data)); + } +} +bool componentsValueAcquired = false; +if (this.Session.ContainsKey("components")) +{ + this._componentsField = ((global::System.Collections.Generic.IEnumerable)(this.Session["components"])); + componentsValueAcquired = true; +} +if ((componentsValueAcquired == false)) +{ + object data = global::System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("components"); + if ((data != null)) + { + this._componentsField = ((global::System.Collections.Generic.IEnumerable)(data)); + } +} + + + } +} + + + + #line default + #line hidden + } + + #line default + #line hidden + #region Base class + /// + /// Base class for this transformation + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "14.0.0.0")] + public class IndividualActionTemplateBase + { + #region Fields + private global::System.Text.StringBuilder generationEnvironmentField; + private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField; + private global::System.Collections.Generic.List indentLengthsField; + private string currentIndentField = ""; + private bool endsWithNewline; + private global::System.Collections.Generic.IDictionary sessionField; + #endregion + #region Properties + /// + /// The string builder that generation-time code is using to assemble generated output + /// + protected System.Text.StringBuilder GenerationEnvironment + { + get + { + if ((this.generationEnvironmentField == null)) + { + this.generationEnvironmentField = new global::System.Text.StringBuilder(); + } + return this.generationEnvironmentField; + } + set + { + this.generationEnvironmentField = value; + } + } + /// + /// The error collection for the generation process + /// + public System.CodeDom.Compiler.CompilerErrorCollection Errors + { + get + { + if ((this.errorsField == null)) + { + this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection(); + } + return this.errorsField; + } + } + /// + /// A list of the lengths of each indent that was added with PushIndent + /// + private System.Collections.Generic.List indentLengths + { + get + { + if ((this.indentLengthsField == null)) + { + this.indentLengthsField = new global::System.Collections.Generic.List(); + } + return this.indentLengthsField; + } + } + /// + /// Gets the current indent we use when adding lines to the output + /// + public string CurrentIndent + { + get + { + return this.currentIndentField; + } + } + /// + /// Current transformation session + /// + public virtual global::System.Collections.Generic.IDictionary Session + { + get + { + return this.sessionField; + } + set + { + this.sessionField = value; + } + } + #endregion + #region Transform-time helpers + /// + /// Write text directly into the generated output + /// + public void Write(string textToAppend) + { + if (string.IsNullOrEmpty(textToAppend)) + { + return; + } + // If we're starting off, or if the previous text ended with a newline, + // we have to append the current indent first. + if (((this.GenerationEnvironment.Length == 0) + || this.endsWithNewline)) + { + this.GenerationEnvironment.Append(this.currentIndentField); + this.endsWithNewline = false; + } + // Check if the current text ends with a newline + if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture)) + { + this.endsWithNewline = true; + } + // This is an optimization. If the current indent is "", then we don't have to do any + // of the more complex stuff further down. + if ((this.currentIndentField.Length == 0)) + { + this.GenerationEnvironment.Append(textToAppend); + return; + } + // Everywhere there is a newline in the text, add an indent after it + textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField)); + // If the text ends with a newline, then we should strip off the indent added at the very end + // because the appropriate indent will be added when the next time Write() is called + if (this.endsWithNewline) + { + this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length)); + } + else + { + this.GenerationEnvironment.Append(textToAppend); + } + } + /// + /// Write text directly into the generated output + /// + public void WriteLine(string textToAppend) + { + this.Write(textToAppend); + this.GenerationEnvironment.AppendLine(); + this.endsWithNewline = true; + } + /// + /// Write formatted text directly into the generated output + /// + public void Write(string format, params object[] args) + { + this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// + /// Write formatted text directly into the generated output + /// + public void WriteLine(string format, params object[] args) + { + this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// + /// Raise an error + /// + public void Error(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + this.Errors.Add(error); + } + /// + /// Raise a warning + /// + public void Warning(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + error.IsWarning = true; + this.Errors.Add(error); + } + /// + /// Increase the indent + /// + public void PushIndent(string indent) + { + if ((indent == null)) + { + throw new global::System.ArgumentNullException("indent"); + } + this.currentIndentField = (this.currentIndentField + indent); + this.indentLengths.Add(indent.Length); + } + /// + /// Remove the last indent that was added with PushIndent + /// + public string PopIndent() + { + string returnValue = ""; + if ((this.indentLengths.Count > 0)) + { + int indentLength = this.indentLengths[(this.indentLengths.Count - 1)]; + this.indentLengths.RemoveAt((this.indentLengths.Count - 1)); + if ((indentLength > 0)) + { + returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength)); + this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength)); + } + } + return returnValue; + } + /// + /// Remove any indentation + /// + public void ClearIndent() + { + this.indentLengths.Clear(); + this.currentIndentField = ""; + } + #endregion + #region ToString Helpers + /// + /// Utility class to produce culture-oriented representation of an object as a string. + /// + public class ToStringInstanceHelper + { + private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture; + /// + /// Gets or sets format provider to be used by ToStringWithCulture method. + /// + public System.IFormatProvider FormatProvider + { + get + { + return this.formatProviderField ; + } + set + { + if ((value != null)) + { + this.formatProviderField = value; + } + } + } + /// + /// This is called from the compile/run appdomain to convert objects within an expression block to a string + /// + public string ToStringWithCulture(object objectToConvert) + { + if ((objectToConvert == null)) + { + throw new global::System.ArgumentNullException("objectToConvert"); + } + System.Type t = objectToConvert.GetType(); + System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] { + typeof(System.IFormatProvider)}); + if ((method == null)) + { + return objectToConvert.ToString(); + } + else + { + return ((string)(method.Invoke(objectToConvert, new object[] { + this.formatProviderField }))); + } + } + } + private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper(); + /// + /// Helper to produce culture-oriented representation of an object as a string + /// + public ToStringInstanceHelper ToStringHelper + { + get + { + return this.toStringHelperField; + } + } + #endregion + } + #endregion +} diff --git a/src/RMWorkflowMigrator.Generator.PowerShell/Templates/IndividualActionTemplate.tt b/src/RMWorkflowMigrator.Generator.PowerShell/Templates/IndividualActionTemplate.tt new file mode 100644 index 0000000..abc67c9 --- /dev/null +++ b/src/RMWorkflowMigrator.Generator.PowerShell/Templates/IndividualActionTemplate.tt @@ -0,0 +1,70 @@ +<#@ parameter type="Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model.ScriptAction" name= "action" #> +<#@ parameter type="System.Collections.Generic.IEnumerable" name= "components" #> +<#@ template language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="DataAccess.Model" #> +<#@ import namespace="Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model" #> + +# <#=action.DisplayName#> +# Parameters: +<#foreach (var configVar in action.ConfigurationVariables.Where(cv => cv.IsParameter)) {#> + # <#=configVar.RemappedName#>: <#=configVar.Value#> +<#}#> +<#if (action.IsComponent && action.ConfigurationVariables.Any(cv => !cv.IsParameter)) {#> +# Configuration Variables: +<#foreach (var configVar in action.ConfigurationVariables.Where(cv => !cv.IsParameter)) {#> + # <#=configVar.RemappedName#>: <#=configVar.Value#> +<#}#> +<#}#> +try { + Write-Output "Executing <#=action.DisplayName#>" +<#if (action.IsComponent) { +var hasConfigVariables = action.ConfigurationVariables.Any(cv => !cv.IsParameter); +var configVariables = action.ConfigurationVariables.Where(cv => !cv.IsParameter); +if (hasConfigVariables) {#> + $configVariables = @{ +<#foreach (var configVariable in configVariables) {#> + "__<#=configVariable.OriginalName#>__" = $<#=configVariable.RemappedName#> +<#}#> + } +<#} +if ((action.VariableReplacementMethod == VariableReplacementMethod.BeforeInstallation || action.VariableReplacementMethod == VariableReplacementMethod.BeforeAndAfterInstallation) && hasConfigVariables) {#> + # Perform token replacement before installation + &"$(join-path $DeployerToolsPath "TokenizationScript.ps1")" -FilePath $ComponentPath<#=action.Sequence#> -FileSpec "<#=action.FileExtensionFilter#>" -Values $configVariables + +<#} +if (action.DeployerToolId != 0) {#> + cd $ComponentPath<#=action.Sequence#> + &"$(join-path $DeployerToolsPath "<#=action.Command#>")" <#=action.Arguments#> + cd $basePath +<#} +else {#> + &"$(join-path $DeployerToolsPath "<#=action.Command#>")" <#=action.Arguments#> +<#}#> +<#if ((action.VariableReplacementMethod == VariableReplacementMethod.AfterInstallation || action.VariableReplacementMethod == VariableReplacementMethod.BeforeAndAfterInstallation) && hasConfigVariables) { +var targetPath = action.ConfigurationVariables.First(cv => cv.OriginalName == "Installation Path").RemappedName; +#> + + # Perform token replacement after installation + &"$(join-path $DeployerToolsPath "TokenizationScript.ps1")" -FilePath $<#=targetPath#> -FileSpec "<#=action.FileExtensionFilter#>" -Values $configVariables +<#}#> +<#} else {#> + &"$(join-path $DeployerToolsPath "<#=action.Command#>")" <#=action.Arguments#> +<#}#> +} +catch { + Write-output "<#=action.DisplayName#> failed. Error:" + write-output $_ +<#if (action.RollbackScripts.Any()) {#> +Write-output "Executing rollback script(s)" +<#if (action.IsComponent) { #> + cd $basePath +<#}#> +<#foreach (var script in action.RollbackScripts) {#> + .\\<#=script.Key#>.ps1 <#=string.Join(" ", script.Value.OrderBy(rsa => rsa.Sequence).SelectMany(rsa => rsa.ConfigurationVariables).Where(s => !string.IsNullOrWhiteSpace(s.Value) && s.IsParameter).Select(s => "-" + s.RemappedName + " \"$" + s.RemappedName + "\""))#> <#=string.Join(" ", script.Value.Where(s => s.IsComponent).Select(c => $"-ComponentPath{c.Sequence} $ComponentPath{c.Sequence}"))#> -DeployerToolsPath $DeployerToolsPath +<#}#> +<#}#> + exit 1 +} +Write-Output "" diff --git a/src/RMWorkflowMigrator.Generator.PowerShell/Templates/InitializationScript.tt b/src/RMWorkflowMigrator.Generator.PowerShell/Templates/InitializationScript.tt new file mode 100644 index 0000000..0614dc6 --- /dev/null +++ b/src/RMWorkflowMigrator.Generator.PowerShell/Templates/InitializationScript.tt @@ -0,0 +1,17 @@ +<#@ parameter type="System.Collections.Generic.IEnumerable" name= "releaseActions" #> +<#@ parameter type="System.Collections.Generic.IEnumerable" name= "scriptParams" #> +<#@ template language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="DataAccess.Model" #> +<#@ import namespace="Model" #> +<#foreach (var scriptParam in scriptParams) {#> +$<#=scriptParam.RemappedName#> = "<#=scriptParam.Value.Replace(@"\""", @"`""")#>" +<#}#> +<#var components = releaseActions.Where(ra => ra.IsComponent && ra.DeployerToolId != 0)?.Union(releaseActions.SelectMany(ra => ra.RollbackScripts).SelectMany(rs => rs.Value).Where(rs => rs.IsComponent && rs.DeployerToolId != 0)); +foreach (var component in components) {#> +$ComponentPath<#=component.Sequence#> = "" # Path to <#=component.DisplayName#> +<#}#> +$DeployerToolsPath = "" # The path to the folder that contains the deployment tools diff --git a/src/RMWorkflowMigrator.Generator.PowerShell/Templates/InitializationScript1.cs b/src/RMWorkflowMigrator.Generator.PowerShell/Templates/InitializationScript1.cs new file mode 100644 index 0000000..7a6cee3 --- /dev/null +++ b/src/RMWorkflowMigrator.Generator.PowerShell/Templates/InitializationScript1.cs @@ -0,0 +1,442 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 14.0.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Templates +{ + using System.Linq; + using System.Text; + using System.Collections.Generic; + using DataAccess.Model; + using Model; + using System; + + /// + /// Class to produce the template output + /// + + #line 1 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\InitializationScript.tt" + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "14.0.0.0")] + public partial class InitializationScript : InitializationScriptBase + { +#line hidden + /// + /// Create the template output + /// + public virtual string TransformText() + { + + #line 10 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\InitializationScript.tt" +foreach (var scriptParam in scriptParams) { + + #line default + #line hidden + this.Write("$"); + + #line 11 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\InitializationScript.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(scriptParam.RemappedName)); + + #line default + #line hidden + this.Write(" = \""); + + #line 11 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\InitializationScript.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(scriptParam.Value.Replace(@"\""", @"`"""))); + + #line default + #line hidden + this.Write("\"\r\n"); + + #line 12 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\InitializationScript.tt" +} + + #line default + #line hidden + + #line 13 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\InitializationScript.tt" +var components = releaseActions.Where(ra => ra.IsComponent && ra.DeployerToolId != 0)?.Union(releaseActions.SelectMany(ra => ra.RollbackScripts).SelectMany(rs => rs.Value).Where(rs => rs.IsComponent && rs.DeployerToolId != 0)); +foreach (var component in components) { + + #line default + #line hidden + this.Write("$ComponentPath"); + + #line 15 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\InitializationScript.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(component.Sequence)); + + #line default + #line hidden + this.Write(" = \"\" # Path to "); + + #line 15 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\InitializationScript.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(component.DisplayName)); + + #line default + #line hidden + this.Write("\r\n"); + + #line 16 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\InitializationScript.tt" +} + + #line default + #line hidden + this.Write("$DeployerToolsPath = \"\" # The path to the folder that contains the deployment too" + + "ls\r\n"); + return this.GenerationEnvironment.ToString(); + } + + #line 1 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\InitializationScript.tt" + +private global::System.Collections.Generic.IEnumerable _releaseActionsField; + +/// +/// Access the releaseActions parameter of the template. +/// +private global::System.Collections.Generic.IEnumerable releaseActions +{ + get + { + return this._releaseActionsField; + } +} + +private global::System.Collections.Generic.IEnumerable _scriptParamsField; + +/// +/// Access the scriptParams parameter of the template. +/// +private global::System.Collections.Generic.IEnumerable scriptParams +{ + get + { + return this._scriptParamsField; + } +} + + +/// +/// Initialize the template +/// +public virtual void Initialize() +{ + if ((this.Errors.HasErrors == false)) + { +bool releaseActionsValueAcquired = false; +if (this.Session.ContainsKey("releaseActions")) +{ + this._releaseActionsField = ((global::System.Collections.Generic.IEnumerable)(this.Session["releaseActions"])); + releaseActionsValueAcquired = true; +} +if ((releaseActionsValueAcquired == false)) +{ + object data = global::System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("releaseActions"); + if ((data != null)) + { + this._releaseActionsField = ((global::System.Collections.Generic.IEnumerable)(data)); + } +} +bool scriptParamsValueAcquired = false; +if (this.Session.ContainsKey("scriptParams")) +{ + this._scriptParamsField = ((global::System.Collections.Generic.IEnumerable)(this.Session["scriptParams"])); + scriptParamsValueAcquired = true; +} +if ((scriptParamsValueAcquired == false)) +{ + object data = global::System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("scriptParams"); + if ((data != null)) + { + this._scriptParamsField = ((global::System.Collections.Generic.IEnumerable)(data)); + } +} + + + } +} + + + + #line default + #line hidden + } + + #line default + #line hidden + #region Base class + /// + /// Base class for this transformation + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "14.0.0.0")] + public class InitializationScriptBase + { + #region Fields + private global::System.Text.StringBuilder generationEnvironmentField; + private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField; + private global::System.Collections.Generic.List indentLengthsField; + private string currentIndentField = ""; + private bool endsWithNewline; + private global::System.Collections.Generic.IDictionary sessionField; + #endregion + #region Properties + /// + /// The string builder that generation-time code is using to assemble generated output + /// + protected System.Text.StringBuilder GenerationEnvironment + { + get + { + if ((this.generationEnvironmentField == null)) + { + this.generationEnvironmentField = new global::System.Text.StringBuilder(); + } + return this.generationEnvironmentField; + } + set + { + this.generationEnvironmentField = value; + } + } + /// + /// The error collection for the generation process + /// + public System.CodeDom.Compiler.CompilerErrorCollection Errors + { + get + { + if ((this.errorsField == null)) + { + this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection(); + } + return this.errorsField; + } + } + /// + /// A list of the lengths of each indent that was added with PushIndent + /// + private System.Collections.Generic.List indentLengths + { + get + { + if ((this.indentLengthsField == null)) + { + this.indentLengthsField = new global::System.Collections.Generic.List(); + } + return this.indentLengthsField; + } + } + /// + /// Gets the current indent we use when adding lines to the output + /// + public string CurrentIndent + { + get + { + return this.currentIndentField; + } + } + /// + /// Current transformation session + /// + public virtual global::System.Collections.Generic.IDictionary Session + { + get + { + return this.sessionField; + } + set + { + this.sessionField = value; + } + } + #endregion + #region Transform-time helpers + /// + /// Write text directly into the generated output + /// + public void Write(string textToAppend) + { + if (string.IsNullOrEmpty(textToAppend)) + { + return; + } + // If we're starting off, or if the previous text ended with a newline, + // we have to append the current indent first. + if (((this.GenerationEnvironment.Length == 0) + || this.endsWithNewline)) + { + this.GenerationEnvironment.Append(this.currentIndentField); + this.endsWithNewline = false; + } + // Check if the current text ends with a newline + if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture)) + { + this.endsWithNewline = true; + } + // This is an optimization. If the current indent is "", then we don't have to do any + // of the more complex stuff further down. + if ((this.currentIndentField.Length == 0)) + { + this.GenerationEnvironment.Append(textToAppend); + return; + } + // Everywhere there is a newline in the text, add an indent after it + textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField)); + // If the text ends with a newline, then we should strip off the indent added at the very end + // because the appropriate indent will be added when the next time Write() is called + if (this.endsWithNewline) + { + this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length)); + } + else + { + this.GenerationEnvironment.Append(textToAppend); + } + } + /// + /// Write text directly into the generated output + /// + public void WriteLine(string textToAppend) + { + this.Write(textToAppend); + this.GenerationEnvironment.AppendLine(); + this.endsWithNewline = true; + } + /// + /// Write formatted text directly into the generated output + /// + public void Write(string format, params object[] args) + { + this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// + /// Write formatted text directly into the generated output + /// + public void WriteLine(string format, params object[] args) + { + this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// + /// Raise an error + /// + public void Error(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + this.Errors.Add(error); + } + /// + /// Raise a warning + /// + public void Warning(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + error.IsWarning = true; + this.Errors.Add(error); + } + /// + /// Increase the indent + /// + public void PushIndent(string indent) + { + if ((indent == null)) + { + throw new global::System.ArgumentNullException("indent"); + } + this.currentIndentField = (this.currentIndentField + indent); + this.indentLengths.Add(indent.Length); + } + /// + /// Remove the last indent that was added with PushIndent + /// + public string PopIndent() + { + string returnValue = ""; + if ((this.indentLengths.Count > 0)) + { + int indentLength = this.indentLengths[(this.indentLengths.Count - 1)]; + this.indentLengths.RemoveAt((this.indentLengths.Count - 1)); + if ((indentLength > 0)) + { + returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength)); + this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength)); + } + } + return returnValue; + } + /// + /// Remove any indentation + /// + public void ClearIndent() + { + this.indentLengths.Clear(); + this.currentIndentField = ""; + } + #endregion + #region ToString Helpers + /// + /// Utility class to produce culture-oriented representation of an object as a string. + /// + public class ToStringInstanceHelper + { + private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture; + /// + /// Gets or sets format provider to be used by ToStringWithCulture method. + /// + public System.IFormatProvider FormatProvider + { + get + { + return this.formatProviderField ; + } + set + { + if ((value != null)) + { + this.formatProviderField = value; + } + } + } + /// + /// This is called from the compile/run appdomain to convert objects within an expression block to a string + /// + public string ToStringWithCulture(object objectToConvert) + { + if ((objectToConvert == null)) + { + throw new global::System.ArgumentNullException("objectToConvert"); + } + System.Type t = objectToConvert.GetType(); + System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] { + typeof(System.IFormatProvider)}); + if ((method == null)) + { + return objectToConvert.ToString(); + } + else + { + return ((string)(method.Invoke(objectToConvert, new object[] { + this.formatProviderField }))); + } + } + } + private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper(); + /// + /// Helper to produce culture-oriented representation of an object as a string + /// + public ToStringInstanceHelper ToStringHelper + { + get + { + return this.toStringHelperField; + } + } + #endregion + } + #endregion +} diff --git a/src/RMWorkflowMigrator.Generator.PowerShell/Templates/ManualInterventionTemplate.cs b/src/RMWorkflowMigrator.Generator.PowerShell/Templates/ManualInterventionTemplate.cs new file mode 100644 index 0000000..7f04113 --- /dev/null +++ b/src/RMWorkflowMigrator.Generator.PowerShell/Templates/ManualInterventionTemplate.cs @@ -0,0 +1,407 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 14.0.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Templates +{ + using System.Linq; + using DataAccess.Model; + using System; + + /// + /// Class to produce the template output + /// + + #line 1 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ManualInterventionTemplate.tt" + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "14.0.0.0")] + public partial class ManualInterventionTemplate : ManualInterventionTemplateBase + { +#line hidden + /// + /// Create the template output + /// + public virtual string TransformText() + { + this.Write("# Manual Intervention: "); + + #line 6 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ManualInterventionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(manualIntervention.DisplayName)); + + #line default + #line hidden + this.Write("\r\n"); + + #line 7 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ManualInterventionTemplate.tt" +if (manualIntervention.IsTargetGroup) { + + #line default + #line hidden + this.Write("# Group: "); + + #line 8 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ManualInterventionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(manualIntervention.Target)); + + #line default + #line hidden + this.Write("\r\n"); + + #line 9 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ManualInterventionTemplate.tt" +} +else { + + #line default + #line hidden + this.Write("# User: "); + + #line 11 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ManualInterventionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(manualIntervention.Target)); + + #line default + #line hidden + this.Write("\r\n"); + + #line 12 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ManualInterventionTemplate.tt" +} + + #line default + #line hidden + this.Write("<#\r\n"); + + #line 14 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ManualInterventionTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(manualIntervention.InterventionText)); + + #line default + #line hidden + this.Write("\r\n#>\r\n# This manual intervention cannot be converted. Please see the guidance doc" + + "umentation for how to adapt your deployment process."); + return this.GenerationEnvironment.ToString(); + } + + #line 1 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ManualInterventionTemplate.tt" + +private global::Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model.ScriptManualIntervention _manualInterventionField; + +/// +/// Access the manualIntervention parameter of the template. +/// +private global::Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model.ScriptManualIntervention manualIntervention +{ + get + { + return this._manualInterventionField; + } +} + + +/// +/// Initialize the template +/// +public virtual void Initialize() +{ + if ((this.Errors.HasErrors == false)) + { +bool manualInterventionValueAcquired = false; +if (this.Session.ContainsKey("manualIntervention")) +{ + this._manualInterventionField = ((global::Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model.ScriptManualIntervention)(this.Session["manualIntervention"])); + manualInterventionValueAcquired = true; +} +if ((manualInterventionValueAcquired == false)) +{ + object data = global::System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("manualIntervention"); + if ((data != null)) + { + this._manualInterventionField = ((global::Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model.ScriptManualIntervention)(data)); + } +} + + + } +} + + + + #line default + #line hidden + } + + #line default + #line hidden + #region Base class + /// + /// Base class for this transformation + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "14.0.0.0")] + public class ManualInterventionTemplateBase + { + #region Fields + private global::System.Text.StringBuilder generationEnvironmentField; + private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField; + private global::System.Collections.Generic.List indentLengthsField; + private string currentIndentField = ""; + private bool endsWithNewline; + private global::System.Collections.Generic.IDictionary sessionField; + #endregion + #region Properties + /// + /// The string builder that generation-time code is using to assemble generated output + /// + protected System.Text.StringBuilder GenerationEnvironment + { + get + { + if ((this.generationEnvironmentField == null)) + { + this.generationEnvironmentField = new global::System.Text.StringBuilder(); + } + return this.generationEnvironmentField; + } + set + { + this.generationEnvironmentField = value; + } + } + /// + /// The error collection for the generation process + /// + public System.CodeDom.Compiler.CompilerErrorCollection Errors + { + get + { + if ((this.errorsField == null)) + { + this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection(); + } + return this.errorsField; + } + } + /// + /// A list of the lengths of each indent that was added with PushIndent + /// + private System.Collections.Generic.List indentLengths + { + get + { + if ((this.indentLengthsField == null)) + { + this.indentLengthsField = new global::System.Collections.Generic.List(); + } + return this.indentLengthsField; + } + } + /// + /// Gets the current indent we use when adding lines to the output + /// + public string CurrentIndent + { + get + { + return this.currentIndentField; + } + } + /// + /// Current transformation session + /// + public virtual global::System.Collections.Generic.IDictionary Session + { + get + { + return this.sessionField; + } + set + { + this.sessionField = value; + } + } + #endregion + #region Transform-time helpers + /// + /// Write text directly into the generated output + /// + public void Write(string textToAppend) + { + if (string.IsNullOrEmpty(textToAppend)) + { + return; + } + // If we're starting off, or if the previous text ended with a newline, + // we have to append the current indent first. + if (((this.GenerationEnvironment.Length == 0) + || this.endsWithNewline)) + { + this.GenerationEnvironment.Append(this.currentIndentField); + this.endsWithNewline = false; + } + // Check if the current text ends with a newline + if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture)) + { + this.endsWithNewline = true; + } + // This is an optimization. If the current indent is "", then we don't have to do any + // of the more complex stuff further down. + if ((this.currentIndentField.Length == 0)) + { + this.GenerationEnvironment.Append(textToAppend); + return; + } + // Everywhere there is a newline in the text, add an indent after it + textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField)); + // If the text ends with a newline, then we should strip off the indent added at the very end + // because the appropriate indent will be added when the next time Write() is called + if (this.endsWithNewline) + { + this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length)); + } + else + { + this.GenerationEnvironment.Append(textToAppend); + } + } + /// + /// Write text directly into the generated output + /// + public void WriteLine(string textToAppend) + { + this.Write(textToAppend); + this.GenerationEnvironment.AppendLine(); + this.endsWithNewline = true; + } + /// + /// Write formatted text directly into the generated output + /// + public void Write(string format, params object[] args) + { + this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// + /// Write formatted text directly into the generated output + /// + public void WriteLine(string format, params object[] args) + { + this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// + /// Raise an error + /// + public void Error(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + this.Errors.Add(error); + } + /// + /// Raise a warning + /// + public void Warning(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + error.IsWarning = true; + this.Errors.Add(error); + } + /// + /// Increase the indent + /// + public void PushIndent(string indent) + { + if ((indent == null)) + { + throw new global::System.ArgumentNullException("indent"); + } + this.currentIndentField = (this.currentIndentField + indent); + this.indentLengths.Add(indent.Length); + } + /// + /// Remove the last indent that was added with PushIndent + /// + public string PopIndent() + { + string returnValue = ""; + if ((this.indentLengths.Count > 0)) + { + int indentLength = this.indentLengths[(this.indentLengths.Count - 1)]; + this.indentLengths.RemoveAt((this.indentLengths.Count - 1)); + if ((indentLength > 0)) + { + returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength)); + this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength)); + } + } + return returnValue; + } + /// + /// Remove any indentation + /// + public void ClearIndent() + { + this.indentLengths.Clear(); + this.currentIndentField = ""; + } + #endregion + #region ToString Helpers + /// + /// Utility class to produce culture-oriented representation of an object as a string. + /// + public class ToStringInstanceHelper + { + private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture; + /// + /// Gets or sets format provider to be used by ToStringWithCulture method. + /// + public System.IFormatProvider FormatProvider + { + get + { + return this.formatProviderField ; + } + set + { + if ((value != null)) + { + this.formatProviderField = value; + } + } + } + /// + /// This is called from the compile/run appdomain to convert objects within an expression block to a string + /// + public string ToStringWithCulture(object objectToConvert) + { + if ((objectToConvert == null)) + { + throw new global::System.ArgumentNullException("objectToConvert"); + } + System.Type t = objectToConvert.GetType(); + System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] { + typeof(System.IFormatProvider)}); + if ((method == null)) + { + return objectToConvert.ToString(); + } + else + { + return ((string)(method.Invoke(objectToConvert, new object[] { + this.formatProviderField }))); + } + } + } + private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper(); + /// + /// Helper to produce culture-oriented representation of an object as a string + /// + public ToStringInstanceHelper ToStringHelper + { + get + { + return this.toStringHelperField; + } + } + #endregion + } + #endregion +} diff --git a/src/RMWorkflowMigrator.Generator.PowerShell/Templates/ManualInterventionTemplate.tt b/src/RMWorkflowMigrator.Generator.PowerShell/Templates/ManualInterventionTemplate.tt new file mode 100644 index 0000000..08dd56a --- /dev/null +++ b/src/RMWorkflowMigrator.Generator.PowerShell/Templates/ManualInterventionTemplate.tt @@ -0,0 +1,16 @@ +<#@ parameter type="Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model.ScriptManualIntervention" name= "manualIntervention" #> +<#@ template language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="DataAccess.Model" #> +# Manual Intervention: <#=manualIntervention.DisplayName#> +<#if (manualIntervention.IsTargetGroup) {#> +# Group: <#=manualIntervention.Target#> +<#} +else {#> +# User: <#=manualIntervention.Target#> +<#}#> +\<# +<#=manualIntervention.InterventionText#> +\#> +# This manual intervention cannot be converted. Please see the guidance documentation for how to adapt your deployment process. \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Generator.PowerShell/Templates/ReleaseScriptTemplate.cs b/src/RMWorkflowMigrator.Generator.PowerShell/Templates/ReleaseScriptTemplate.cs new file mode 100644 index 0000000..85a6eaa --- /dev/null +++ b/src/RMWorkflowMigrator.Generator.PowerShell/Templates/ReleaseScriptTemplate.cs @@ -0,0 +1,537 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 14.0.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Templates +{ + using System; + using System.Linq; + using System.Text; + using System.Collections.Generic; + using DataAccess.Model; + using Model; + + /// + /// Class to produce the template output + /// + + #line 1 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ReleaseScriptTemplate.tt" + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "14.0.0.0")] + public partial class ReleaseScriptTemplate : ReleaseScriptTemplateBase + { +#line hidden + /// + /// Create the template output + /// + public virtual string TransformText() + { + this.Write(" \r\n"); + + #line 13 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ReleaseScriptTemplate.tt" +if (!generateInitializationScript) { + + #line default + #line hidden + this.Write("# To call this script from Release Management, use the arguments\r\n# "); + + #line 15 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ReleaseScriptTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(string.Join(" ", scriptParams.Where(s => !string.IsNullOrWhiteSpace(s.Value)).Select(sp => "-" + sp.RemappedName + " \"" + sp.Value + "\"")))); + + #line default + #line hidden + this.Write("\r\n# Also include a value for the $DeployerToolsPath variable, and the binary drop" + + " location for any $ComponentPath variables.\r\nparam(\r\n"); + + #line 18 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ReleaseScriptTemplate.tt" +foreach (var scriptParam in scriptParams) { + + #line default + #line hidden + this.Write("$"); + + #line 19 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ReleaseScriptTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(scriptParam.RemappedName)); + + #line default + #line hidden + this.Write(", # "); + + #line 19 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ReleaseScriptTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(scriptParam.Value)); + + #line default + #line hidden + this.Write("\r\n"); + + #line 20 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ReleaseScriptTemplate.tt" +} + + #line default + #line hidden + + #line 21 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ReleaseScriptTemplate.tt" +foreach (var component in components) { + + #line default + #line hidden + this.Write("$ComponentPath"); + + #line 22 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ReleaseScriptTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(component.Sequence)); + + #line default + #line hidden + this.Write(", # Path to "); + + #line 22 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ReleaseScriptTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(component.DisplayName)); + + #line default + #line hidden + this.Write("\r\n"); + + #line 23 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ReleaseScriptTemplate.tt" +} + + #line default + #line hidden + this.Write("$DeployerToolsPath # The path to the folder that contains the deployment tools\r\n)" + + "\r\n"); + + #line 26 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ReleaseScriptTemplate.tt" +} + + #line default + #line hidden + this.Write("$basePath = (Get-Location).Path\r\n"); + + #line 28 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ReleaseScriptTemplate.tt" +foreach (var action in releaseActions) { + + #line default + #line hidden + + #line 29 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ReleaseScriptTemplate.tt" + this.Write(this.ToStringHelper.ToStringWithCulture(action)); + + #line default + #line hidden + this.Write("\r\n"); + + #line 30 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ReleaseScriptTemplate.tt" +} + + #line default + #line hidden + return this.GenerationEnvironment.ToString(); + } + + #line 1 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\ReleaseScriptTemplate.tt" + +private global::System.Collections.Generic.IEnumerable _releaseActionsField; + +/// +/// Access the releaseActions parameter of the template. +/// +private global::System.Collections.Generic.IEnumerable releaseActions +{ + get + { + return this._releaseActionsField; + } +} + +private global::System.Collections.Generic.IEnumerable _componentsField; + +/// +/// Access the components parameter of the template. +/// +private global::System.Collections.Generic.IEnumerable components +{ + get + { + return this._componentsField; + } +} + +private global::System.Collections.Generic.List _scriptParamsField; + +/// +/// Access the scriptParams parameter of the template. +/// +private global::System.Collections.Generic.List scriptParams +{ + get + { + return this._scriptParamsField; + } +} + +private bool _generateInitializationScriptField; + +/// +/// Access the generateInitializationScript parameter of the template. +/// +private bool generateInitializationScript +{ + get + { + return this._generateInitializationScriptField; + } +} + + +/// +/// Initialize the template +/// +public virtual void Initialize() +{ + if ((this.Errors.HasErrors == false)) + { +bool releaseActionsValueAcquired = false; +if (this.Session.ContainsKey("releaseActions")) +{ + this._releaseActionsField = ((global::System.Collections.Generic.IEnumerable)(this.Session["releaseActions"])); + releaseActionsValueAcquired = true; +} +if ((releaseActionsValueAcquired == false)) +{ + object data = global::System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("releaseActions"); + if ((data != null)) + { + this._releaseActionsField = ((global::System.Collections.Generic.IEnumerable)(data)); + } +} +bool componentsValueAcquired = false; +if (this.Session.ContainsKey("components")) +{ + this._componentsField = ((global::System.Collections.Generic.IEnumerable)(this.Session["components"])); + componentsValueAcquired = true; +} +if ((componentsValueAcquired == false)) +{ + object data = global::System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("components"); + if ((data != null)) + { + this._componentsField = ((global::System.Collections.Generic.IEnumerable)(data)); + } +} +bool scriptParamsValueAcquired = false; +if (this.Session.ContainsKey("scriptParams")) +{ + this._scriptParamsField = ((global::System.Collections.Generic.List)(this.Session["scriptParams"])); + scriptParamsValueAcquired = true; +} +if ((scriptParamsValueAcquired == false)) +{ + object data = global::System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("scriptParams"); + if ((data != null)) + { + this._scriptParamsField = ((global::System.Collections.Generic.List)(data)); + } +} +bool generateInitializationScriptValueAcquired = false; +if (this.Session.ContainsKey("generateInitializationScript")) +{ + this._generateInitializationScriptField = ((bool)(this.Session["generateInitializationScript"])); + generateInitializationScriptValueAcquired = true; +} +if ((generateInitializationScriptValueAcquired == false)) +{ + object data = global::System.Runtime.Remoting.Messaging.CallContext.LogicalGetData("generateInitializationScript"); + if ((data != null)) + { + this._generateInitializationScriptField = ((bool)(data)); + } +} + + + } +} + + + + #line default + #line hidden + } + + #line default + #line hidden + #region Base class + /// + /// Base class for this transformation + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "14.0.0.0")] + public class ReleaseScriptTemplateBase + { + #region Fields + private global::System.Text.StringBuilder generationEnvironmentField; + private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField; + private global::System.Collections.Generic.List indentLengthsField; + private string currentIndentField = ""; + private bool endsWithNewline; + private global::System.Collections.Generic.IDictionary sessionField; + #endregion + #region Properties + /// + /// The string builder that generation-time code is using to assemble generated output + /// + protected System.Text.StringBuilder GenerationEnvironment + { + get + { + if ((this.generationEnvironmentField == null)) + { + this.generationEnvironmentField = new global::System.Text.StringBuilder(); + } + return this.generationEnvironmentField; + } + set + { + this.generationEnvironmentField = value; + } + } + /// + /// The error collection for the generation process + /// + public System.CodeDom.Compiler.CompilerErrorCollection Errors + { + get + { + if ((this.errorsField == null)) + { + this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection(); + } + return this.errorsField; + } + } + /// + /// A list of the lengths of each indent that was added with PushIndent + /// + private System.Collections.Generic.List indentLengths + { + get + { + if ((this.indentLengthsField == null)) + { + this.indentLengthsField = new global::System.Collections.Generic.List(); + } + return this.indentLengthsField; + } + } + /// + /// Gets the current indent we use when adding lines to the output + /// + public string CurrentIndent + { + get + { + return this.currentIndentField; + } + } + /// + /// Current transformation session + /// + public virtual global::System.Collections.Generic.IDictionary Session + { + get + { + return this.sessionField; + } + set + { + this.sessionField = value; + } + } + #endregion + #region Transform-time helpers + /// + /// Write text directly into the generated output + /// + public void Write(string textToAppend) + { + if (string.IsNullOrEmpty(textToAppend)) + { + return; + } + // If we're starting off, or if the previous text ended with a newline, + // we have to append the current indent first. + if (((this.GenerationEnvironment.Length == 0) + || this.endsWithNewline)) + { + this.GenerationEnvironment.Append(this.currentIndentField); + this.endsWithNewline = false; + } + // Check if the current text ends with a newline + if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture)) + { + this.endsWithNewline = true; + } + // This is an optimization. If the current indent is "", then we don't have to do any + // of the more complex stuff further down. + if ((this.currentIndentField.Length == 0)) + { + this.GenerationEnvironment.Append(textToAppend); + return; + } + // Everywhere there is a newline in the text, add an indent after it + textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField)); + // If the text ends with a newline, then we should strip off the indent added at the very end + // because the appropriate indent will be added when the next time Write() is called + if (this.endsWithNewline) + { + this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length)); + } + else + { + this.GenerationEnvironment.Append(textToAppend); + } + } + /// + /// Write text directly into the generated output + /// + public void WriteLine(string textToAppend) + { + this.Write(textToAppend); + this.GenerationEnvironment.AppendLine(); + this.endsWithNewline = true; + } + /// + /// Write formatted text directly into the generated output + /// + public void Write(string format, params object[] args) + { + this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// + /// Write formatted text directly into the generated output + /// + public void WriteLine(string format, params object[] args) + { + this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// + /// Raise an error + /// + public void Error(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + this.Errors.Add(error); + } + /// + /// Raise a warning + /// + public void Warning(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + error.IsWarning = true; + this.Errors.Add(error); + } + /// + /// Increase the indent + /// + public void PushIndent(string indent) + { + if ((indent == null)) + { + throw new global::System.ArgumentNullException("indent"); + } + this.currentIndentField = (this.currentIndentField + indent); + this.indentLengths.Add(indent.Length); + } + /// + /// Remove the last indent that was added with PushIndent + /// + public string PopIndent() + { + string returnValue = ""; + if ((this.indentLengths.Count > 0)) + { + int indentLength = this.indentLengths[(this.indentLengths.Count - 1)]; + this.indentLengths.RemoveAt((this.indentLengths.Count - 1)); + if ((indentLength > 0)) + { + returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength)); + this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength)); + } + } + return returnValue; + } + /// + /// Remove any indentation + /// + public void ClearIndent() + { + this.indentLengths.Clear(); + this.currentIndentField = ""; + } + #endregion + #region ToString Helpers + /// + /// Utility class to produce culture-oriented representation of an object as a string. + /// + public class ToStringInstanceHelper + { + private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture; + /// + /// Gets or sets format provider to be used by ToStringWithCulture method. + /// + public System.IFormatProvider FormatProvider + { + get + { + return this.formatProviderField ; + } + set + { + if ((value != null)) + { + this.formatProviderField = value; + } + } + } + /// + /// This is called from the compile/run appdomain to convert objects within an expression block to a string + /// + public string ToStringWithCulture(object objectToConvert) + { + if ((objectToConvert == null)) + { + throw new global::System.ArgumentNullException("objectToConvert"); + } + System.Type t = objectToConvert.GetType(); + System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] { + typeof(System.IFormatProvider)}); + if ((method == null)) + { + return objectToConvert.ToString(); + } + else + { + return ((string)(method.Invoke(objectToConvert, new object[] { + this.formatProviderField }))); + } + } + } + private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper(); + /// + /// Helper to produce culture-oriented representation of an object as a string + /// + public ToStringInstanceHelper ToStringHelper + { + get + { + return this.toStringHelperField; + } + } + #endregion + } + #endregion +} diff --git a/src/RMWorkflowMigrator.Generator.PowerShell/Templates/ReleaseScriptTemplate.tt b/src/RMWorkflowMigrator.Generator.PowerShell/Templates/ReleaseScriptTemplate.tt new file mode 100644 index 0000000..2fa78e1 --- /dev/null +++ b/src/RMWorkflowMigrator.Generator.PowerShell/Templates/ReleaseScriptTemplate.tt @@ -0,0 +1,30 @@ +<#@ parameter type="System.Collections.Generic.IEnumerable" name= "releaseActions" #> +<#@ parameter type="System.Collections.Generic.IEnumerable" name= "components" #> +<#@ parameter type="System.Collections.Generic.List" name= "scriptParams" #> +<#@ parameter type="System.Boolean" name= "generateInitializationScript" #> +<#@ template language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text" #> +<#@ import namespace="System.Collections.Generic" #> +<#@ import namespace="DataAccess.Model" #> +<#@ import namespace="Model" #> +<#if (!generateInitializationScript) {#> +# To call this script from Release Management, use the arguments +# <#=string.Join(" ", scriptParams.Where(s => !string.IsNullOrWhiteSpace(s.Value)).Select(sp => "-" + sp.RemappedName + " \"" + sp.Value + "\""))#> +# Also include a value for the $DeployerToolsPath variable, and the binary drop location for any $ComponentPath variables. +param( +<#foreach (var scriptParam in scriptParams) {#> +$<#=scriptParam.RemappedName#>, # <#=scriptParam.Value#> +<#}#> +<#foreach (var component in components) {#> +$ComponentPath<#=component.Sequence#>, # Path to <#=component.DisplayName#> +<#}#> +$DeployerToolsPath # The path to the folder that contains the deployment tools +) +<#}#> +$basePath = (Get-Location).Path +<#foreach (var action in releaseActions) {#> +<#=action#> +<#}#> diff --git a/src/RMWorkflowMigrator.Generator.PowerShell/Templates/TokenizationScript.cs b/src/RMWorkflowMigrator.Generator.PowerShell/Templates/TokenizationScript.cs new file mode 100644 index 0000000..67defb3 --- /dev/null +++ b/src/RMWorkflowMigrator.Generator.PowerShell/Templates/TokenizationScript.cs @@ -0,0 +1,337 @@ +// ------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version: 14.0.0.0 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Templates +{ + using System; + + /// + /// Class to produce the template output + /// + + #line 1 "C:\Users\daniel.mann\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.Generator.PowerShell\Templates\TokenizationScript.tt" + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "14.0.0.0")] + public partial class TokenizationScript : TokenizationScriptBase + { +#line hidden + /// + /// Create the template output + /// + public virtual string TransformText() + { + this.Write(@"param( +$FileSpec, +$FilePath, +$Values +) + +try { + $files = gci -Path $FilePath -Filter $FileSpec -Recurse + Write-Output ""Found files:"" + + Write-Output ($files | select-object -expandproperty FullName) + $files | % { + $contents = Get-Content $_.FullName + $changedContents = $false + foreach ($key in $Values.Keys) { + if ($contents.Contains($key)) { + Write-Output ""$($_.FullName) contains `""$key`"". Replacing it with `""$($Values[$key])`"""" + $contents = $contents.Replace($key, $Values[$key]) + $changedContents = $true + } + } + if ($changedContents) { + set-content -Path $_.FullName -Value $contents + } + } +} +catch { + Write-Output $_ + throw $_ +} +"); + return this.GenerationEnvironment.ToString(); + } + } + + #line default + #line hidden + #region Base class + /// + /// Base class for this transformation + /// + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.TextTemplating", "14.0.0.0")] + public class TokenizationScriptBase + { + #region Fields + private global::System.Text.StringBuilder generationEnvironmentField; + private global::System.CodeDom.Compiler.CompilerErrorCollection errorsField; + private global::System.Collections.Generic.List indentLengthsField; + private string currentIndentField = ""; + private bool endsWithNewline; + private global::System.Collections.Generic.IDictionary sessionField; + #endregion + #region Properties + /// + /// The string builder that generation-time code is using to assemble generated output + /// + protected System.Text.StringBuilder GenerationEnvironment + { + get + { + if ((this.generationEnvironmentField == null)) + { + this.generationEnvironmentField = new global::System.Text.StringBuilder(); + } + return this.generationEnvironmentField; + } + set + { + this.generationEnvironmentField = value; + } + } + /// + /// The error collection for the generation process + /// + public System.CodeDom.Compiler.CompilerErrorCollection Errors + { + get + { + if ((this.errorsField == null)) + { + this.errorsField = new global::System.CodeDom.Compiler.CompilerErrorCollection(); + } + return this.errorsField; + } + } + /// + /// A list of the lengths of each indent that was added with PushIndent + /// + private System.Collections.Generic.List indentLengths + { + get + { + if ((this.indentLengthsField == null)) + { + this.indentLengthsField = new global::System.Collections.Generic.List(); + } + return this.indentLengthsField; + } + } + /// + /// Gets the current indent we use when adding lines to the output + /// + public string CurrentIndent + { + get + { + return this.currentIndentField; + } + } + /// + /// Current transformation session + /// + public virtual global::System.Collections.Generic.IDictionary Session + { + get + { + return this.sessionField; + } + set + { + this.sessionField = value; + } + } + #endregion + #region Transform-time helpers + /// + /// Write text directly into the generated output + /// + public void Write(string textToAppend) + { + if (string.IsNullOrEmpty(textToAppend)) + { + return; + } + // If we're starting off, or if the previous text ended with a newline, + // we have to append the current indent first. + if (((this.GenerationEnvironment.Length == 0) + || this.endsWithNewline)) + { + this.GenerationEnvironment.Append(this.currentIndentField); + this.endsWithNewline = false; + } + // Check if the current text ends with a newline + if (textToAppend.EndsWith(global::System.Environment.NewLine, global::System.StringComparison.CurrentCulture)) + { + this.endsWithNewline = true; + } + // This is an optimization. If the current indent is "", then we don't have to do any + // of the more complex stuff further down. + if ((this.currentIndentField.Length == 0)) + { + this.GenerationEnvironment.Append(textToAppend); + return; + } + // Everywhere there is a newline in the text, add an indent after it + textToAppend = textToAppend.Replace(global::System.Environment.NewLine, (global::System.Environment.NewLine + this.currentIndentField)); + // If the text ends with a newline, then we should strip off the indent added at the very end + // because the appropriate indent will be added when the next time Write() is called + if (this.endsWithNewline) + { + this.GenerationEnvironment.Append(textToAppend, 0, (textToAppend.Length - this.currentIndentField.Length)); + } + else + { + this.GenerationEnvironment.Append(textToAppend); + } + } + /// + /// Write text directly into the generated output + /// + public void WriteLine(string textToAppend) + { + this.Write(textToAppend); + this.GenerationEnvironment.AppendLine(); + this.endsWithNewline = true; + } + /// + /// Write formatted text directly into the generated output + /// + public void Write(string format, params object[] args) + { + this.Write(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// + /// Write formatted text directly into the generated output + /// + public void WriteLine(string format, params object[] args) + { + this.WriteLine(string.Format(global::System.Globalization.CultureInfo.CurrentCulture, format, args)); + } + /// + /// Raise an error + /// + public void Error(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + this.Errors.Add(error); + } + /// + /// Raise a warning + /// + public void Warning(string message) + { + System.CodeDom.Compiler.CompilerError error = new global::System.CodeDom.Compiler.CompilerError(); + error.ErrorText = message; + error.IsWarning = true; + this.Errors.Add(error); + } + /// + /// Increase the indent + /// + public void PushIndent(string indent) + { + if ((indent == null)) + { + throw new global::System.ArgumentNullException("indent"); + } + this.currentIndentField = (this.currentIndentField + indent); + this.indentLengths.Add(indent.Length); + } + /// + /// Remove the last indent that was added with PushIndent + /// + public string PopIndent() + { + string returnValue = ""; + if ((this.indentLengths.Count > 0)) + { + int indentLength = this.indentLengths[(this.indentLengths.Count - 1)]; + this.indentLengths.RemoveAt((this.indentLengths.Count - 1)); + if ((indentLength > 0)) + { + returnValue = this.currentIndentField.Substring((this.currentIndentField.Length - indentLength)); + this.currentIndentField = this.currentIndentField.Remove((this.currentIndentField.Length - indentLength)); + } + } + return returnValue; + } + /// + /// Remove any indentation + /// + public void ClearIndent() + { + this.indentLengths.Clear(); + this.currentIndentField = ""; + } + #endregion + #region ToString Helpers + /// + /// Utility class to produce culture-oriented representation of an object as a string. + /// + public class ToStringInstanceHelper + { + private System.IFormatProvider formatProviderField = global::System.Globalization.CultureInfo.InvariantCulture; + /// + /// Gets or sets format provider to be used by ToStringWithCulture method. + /// + public System.IFormatProvider FormatProvider + { + get + { + return this.formatProviderField ; + } + set + { + if ((value != null)) + { + this.formatProviderField = value; + } + } + } + /// + /// This is called from the compile/run appdomain to convert objects within an expression block to a string + /// + public string ToStringWithCulture(object objectToConvert) + { + if ((objectToConvert == null)) + { + throw new global::System.ArgumentNullException("objectToConvert"); + } + System.Type t = objectToConvert.GetType(); + System.Reflection.MethodInfo method = t.GetMethod("ToString", new System.Type[] { + typeof(System.IFormatProvider)}); + if ((method == null)) + { + return objectToConvert.ToString(); + } + else + { + return ((string)(method.Invoke(objectToConvert, new object[] { + this.formatProviderField }))); + } + } + } + private ToStringInstanceHelper toStringHelperField = new ToStringInstanceHelper(); + /// + /// Helper to produce culture-oriented representation of an object as a string + /// + public ToStringInstanceHelper ToStringHelper + { + get + { + return this.toStringHelperField; + } + } + #endregion + } + #endregion +} diff --git a/src/RMWorkflowMigrator.Generator.PowerShell/Templates/TokenizationScript.tt b/src/RMWorkflowMigrator.Generator.PowerShell/Templates/TokenizationScript.tt new file mode 100644 index 0000000..f7f0026 --- /dev/null +++ b/src/RMWorkflowMigrator.Generator.PowerShell/Templates/TokenizationScript.tt @@ -0,0 +1,30 @@ +param( +$FileSpec, +$FilePath, +$Values +) + +try { + $files = gci -Path $FilePath -Filter $FileSpec -Recurse + Write-Output "Found files:" + + Write-Output ($files | select-object -expandproperty FullName) + $files | % { + $contents = Get-Content $_.FullName + $changedContents = $false + foreach ($key in $Values.Keys) { + if ($contents.Contains($key)) { + Write-Output "$($_.FullName) contains `"$key`". Replacing it with `"$($Values[$key])`"" + $contents = $contents.Replace($key, $Values[$key]) + $changedContents = $true + } + } + if ($changedContents) { + set-content -Path $_.FullName -Value $contents + } + } +} +catch { + Write-Output $_ + throw $_ +} diff --git a/src/RMWorkflowMigrator.Generator.PowerShell/UniquePropertyResolver.cs b/src/RMWorkflowMigrator.Generator.PowerShell/UniquePropertyResolver.cs new file mode 100644 index 0000000..b1bb55a --- /dev/null +++ b/src/RMWorkflowMigrator.Generator.PowerShell/UniquePropertyResolver.cs @@ -0,0 +1,98 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text.RegularExpressions; + using Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model; + using Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model; + + public static class UniquePropertyResolver + { + private static readonly Regex InvalidCharactersRegex = new Regex("[^0-9a-zA-Z_]"); + private static readonly Regex ParameterRegex = new Regex("__(.*?)__"); + + public static IEnumerable ResolveProperties(IEnumerable actions) + { + var sequence = 2; + var properties = new HashSet>(); + foreach (var action in actions) + { + action.Arguments = CleanActionParameters(action.Arguments); + CleanConfigurationValues(action.ConfigurationVariables); + sequence = MakeParametersUnique(action, properties, sequence); + } + + return actions; + } + + private static int MakeParametersUnique(ScriptAction action, ISet> properties, int sequence) + { + if (action.ConfigurationVariables == null) + { + return sequence; + } + + foreach (var configVar in action.ConfigurationVariables) + { + var tuple = Tuple.Create(CleanInvalidCharacters(configVar.OriginalName), configVar.Value); + if (!properties.Contains(tuple)) + { + if (properties.Any(p => p.Item1 == tuple.Item1 && p.Item2 != tuple.Item2)) + { + var newVariableName = configVar.RemappedName + sequence; + sequence++; + action.Arguments = action.Arguments?.Replace(configVar.RemappedName, newVariableName); + configVar.RemappedName = newVariableName; + } + } + + properties.Add(tuple); + } + + return sequence; + } + + private static void CleanConfigurationValues(IEnumerable configurationVariables) + { + if (configurationVariables == null) + { + return; + } + + foreach (var configVar in configurationVariables) + { + configVar.RemappedName = CleanInvalidCharacters(configVar.OriginalName); + } + } + + private static string CleanActionParameters(string arguments) + { + if (string.IsNullOrWhiteSpace(arguments)) + { + return arguments; + } + + var matches = ParameterRegex.Matches(arguments); + foreach (var match in matches) + { + var replacement = match.ToString().Replace("__", string.Empty); + replacement = "$" + CleanInvalidCharacters(replacement); + arguments = arguments.Replace(match.ToString(), replacement); + } + + return arguments; + } + + private static string CleanInvalidCharacters(string value) + { + return InvalidCharactersRegex.Replace(value, string.Empty); + } + } +} diff --git a/src/RMWorkflowMigrator.Generator.PowerShell/packages.config b/src/RMWorkflowMigrator.Generator.PowerShell/packages.config new file mode 100644 index 0000000..112d362 --- /dev/null +++ b/src/RMWorkflowMigrator.Generator.PowerShell/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Parser.Model/ActionParsedEventArgs.cs b/src/RMWorkflowMigrator.Parser.Model/ActionParsedEventArgs.cs new file mode 100644 index 0000000..ba0fb1b --- /dev/null +++ b/src/RMWorkflowMigrator.Parser.Model/ActionParsedEventArgs.cs @@ -0,0 +1,15 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the ActionParsedEventArgs type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Parser.Model +{ + public class ActionParsedEventArgs : ContainerParsedEventArgs + { + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Parser.Model/BlockType.cs b/src/RMWorkflowMigrator.Parser.Model/BlockType.cs new file mode 100644 index 0000000..6124e08 --- /dev/null +++ b/src/RMWorkflowMigrator.Parser.Model/BlockType.cs @@ -0,0 +1,64 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the BlockType type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Parser.Model +{ + public enum BlockType + { + /// + /// Type is undefined + /// + Undefined, + + /// + /// Action + /// + Action, + + /// + /// Component + /// + Component, + + /// + /// Manual intervention + /// + ManualIntervention, + + /// + /// Parallel block + /// + Parallel, + + /// + /// Sequence + /// + Sequence, + + /// + /// server + /// + Server, + + /// + /// ServerTag + /// + ServerTag, + + /// + /// Rollback + /// + Rollback, + + /// + /// Rollback Always + /// + RollbackAlways + } +} diff --git a/src/RMWorkflowMigrator.Parser.Model/ContainerParsedEventArgs.cs b/src/RMWorkflowMigrator.Parser.Model/ContainerParsedEventArgs.cs new file mode 100644 index 0000000..bed8807 --- /dev/null +++ b/src/RMWorkflowMigrator.Parser.Model/ContainerParsedEventArgs.cs @@ -0,0 +1,20 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the ContainerParsedEventArgs type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Parser.Model +{ + using System; + + public class ContainerParsedEventArgs : EventArgs + { + public string DisplayName { get; set; } + + public BlockType ItemType { get; set; } + } +} diff --git a/src/RMWorkflowMigrator.Parser.Model/DeploymentSequence.cs b/src/RMWorkflowMigrator.Parser.Model/DeploymentSequence.cs new file mode 100644 index 0000000..d95c7de --- /dev/null +++ b/src/RMWorkflowMigrator.Parser.Model/DeploymentSequence.cs @@ -0,0 +1,24 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the DeploymentSequence type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Parser.Model +{ + using System.Collections.Generic; + + public class DeploymentSequence + { + public int ReleaseTemplateId { get; set; } + + public int ReleaseTemplateStageId { get; set; } + + public string DisplayName { get; set; } + + public IEnumerable> Containers { get; set; } + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Parser.Model/IReleaseActionContainer.cs b/src/RMWorkflowMigrator.Parser.Model/IReleaseActionContainer.cs new file mode 100644 index 0000000..42b8969 --- /dev/null +++ b/src/RMWorkflowMigrator.Parser.Model/IReleaseActionContainer.cs @@ -0,0 +1,20 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the IReleaseActionContainer type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Parser.Model +{ + using System.Collections.Generic; + + public interface IReleaseActionContainer : IReleaseWorkflowBlock + { + string ValidFileName { get; } + + IEnumerable SubItems { get; } + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Parser.Model/IReleaseWorkflowBlock.cs b/src/RMWorkflowMigrator.Parser.Model/IReleaseWorkflowBlock.cs new file mode 100644 index 0000000..a141ae2 --- /dev/null +++ b/src/RMWorkflowMigrator.Parser.Model/IReleaseWorkflowBlock.cs @@ -0,0 +1,22 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the IReleaseWorkflowBlock type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Parser.Model +{ + public interface IReleaseWorkflowBlock + { + int Sequence { get; set; } + + BlockType ItemType { get; set; } + + string DisplayName { get; set; } + + bool DisplayNameIsMeaningful { get; set; } + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Parser.Model/ManualIntervention.cs b/src/RMWorkflowMigrator.Parser.Model/ManualIntervention.cs new file mode 100644 index 0000000..ec89044 --- /dev/null +++ b/src/RMWorkflowMigrator.Parser.Model/ManualIntervention.cs @@ -0,0 +1,20 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the ManualIntervention type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Parser.Model +{ + public class ManualIntervention : ReleaseAction, IReleaseWorkflowBlock + { + public string Text { get; set; } + + public bool IsGroup { get; set; } + + public int UserId { get; set; } + } +} diff --git a/src/RMWorkflowMigrator.Parser.Model/Properties/AssemblyInfo.cs b/src/RMWorkflowMigrator.Parser.Model/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..14289c2 --- /dev/null +++ b/src/RMWorkflowMigrator.Parser.Model/Properties/AssemblyInfo.cs @@ -0,0 +1,28 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Assembly-level attributes +// +// -------------------------------------------------------------------------------------------------------------------- + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Microsoft.ALMRangers.RMWorkflowMigrator.Parser.Model")] +[assembly: AssemblyDescription("Workflow Migrator XAML release template parser object model")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("Microsoft.ALMRangers.RMWorkflowMigrator.Parser.Model")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("dab5cc84-4e29-43e0-9ba9-81d377ea08ee")] \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Parser.Model/RMWorkflowMigrator.Parser.Model.csproj b/src/RMWorkflowMigrator.Parser.Model/RMWorkflowMigrator.Parser.Model.csproj new file mode 100644 index 0000000..519b9d6 --- /dev/null +++ b/src/RMWorkflowMigrator.Parser.Model/RMWorkflowMigrator.Parser.Model.csproj @@ -0,0 +1,76 @@ + + + + + Debug + AnyCPU + {DAB5CC84-4E29-43E0-9BA9-81D377EA08EE} + Library + Properties + Microsoft.ALMRangers.RMWorkflowMigrator.Parser.Model + RMWorkflowMigrator.Parser.Model + v4.6 + 512 + SAK + SAK + SAK + SAK + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + ..\ALMRangersRuleSet.ruleset + true + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + ..\ALMRangersRuleSet.ruleset + true + + + + + + + + + + + + + + Properties\ALMRangersCommonAssemblyInfo.cs + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Parser.Model/ReleaseAction.cs b/src/RMWorkflowMigrator.Parser.Model/ReleaseAction.cs new file mode 100644 index 0000000..a60750a --- /dev/null +++ b/src/RMWorkflowMigrator.Parser.Model/ReleaseAction.cs @@ -0,0 +1,28 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the ReleaseAction type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Parser.Model +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + + [Serializable] + [DebuggerDisplay("{ItemType}_{DisplayName}_{Sequence}")] + public class ReleaseAction : WorkflowBlockBase, IReleaseWorkflowBlock + { + public bool Enabled { get; set; } = true; + + public int ComponentId { get; set; } + + public bool IsComponent => this.ItemType == BlockType.Component; + + public IEnumerable RollbackScripts { get; set; } + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Parser.Model/ReleaseActionContainer.cs b/src/RMWorkflowMigrator.Parser.Model/ReleaseActionContainer.cs new file mode 100644 index 0000000..14840df --- /dev/null +++ b/src/RMWorkflowMigrator.Parser.Model/ReleaseActionContainer.cs @@ -0,0 +1,33 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the ReleaseActionContainer type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Parser.Model +{ + using System.Collections.Generic; + using System.Diagnostics; + using System.Text.RegularExpressions; + + [DebuggerDisplay("{ItemType}_{DisplayName}_{Sequence}")] + public class ReleaseActionContainer : IReleaseActionContainer + { + private static readonly Regex ValidFileRegex = new Regex(@"[^A-Za-z0-9 _&'@\{\}\[\],$=\!\-#\(\)%\.\+\~_]", RegexOptions.Compiled); + + public string DisplayName { get; set; } + + public string ValidFileName => ValidFileRegex.Replace(this.DisplayName, string.Empty); + + public bool DisplayNameIsMeaningful { get; set; } = true; + + public BlockType ItemType { get; set; } + + public int Sequence { get; set; } + + public IEnumerable SubItems { get; set; } + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Parser.Model/RollbackAlwaysBlock.cs b/src/RMWorkflowMigrator.Parser.Model/RollbackAlwaysBlock.cs new file mode 100644 index 0000000..60cf921 --- /dev/null +++ b/src/RMWorkflowMigrator.Parser.Model/RollbackAlwaysBlock.cs @@ -0,0 +1,18 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the RollbackAlwaysBlock type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Parser.Model +{ + using System.Diagnostics; + + [DebuggerDisplay("{ItemType}_{DisplayName}_{Sequence}")] + public class RollbackAlwaysBlock : RollbackBlock + { + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Parser.Model/RollbackBlock.cs b/src/RMWorkflowMigrator.Parser.Model/RollbackBlock.cs new file mode 100644 index 0000000..f519a05 --- /dev/null +++ b/src/RMWorkflowMigrator.Parser.Model/RollbackBlock.cs @@ -0,0 +1,27 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the RollbackBlock type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Parser.Model +{ + using System.Collections.Generic; + using System.Diagnostics; + + [DebuggerDisplay("{ItemType}_{DisplayName}_{Sequence}")] + public class RollbackBlock : ReleaseAction, IReleaseActionContainer + { + public RollbackBlock() + { + this.DisplayNameIsMeaningful = false; + } + + public string ValidFileName => this.DisplayName; + + public IEnumerable SubItems { get; set; } + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Parser.Model/WorkflowBlockBase.cs b/src/RMWorkflowMigrator.Parser.Model/WorkflowBlockBase.cs new file mode 100644 index 0000000..33fe104 --- /dev/null +++ b/src/RMWorkflowMigrator.Parser.Model/WorkflowBlockBase.cs @@ -0,0 +1,26 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Defines the WorkflowBlockBase type. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Parser.Model +{ + using System; + + public abstract class WorkflowBlockBase : IReleaseWorkflowBlock + { + public int Sequence { get; set; } + + public string DisplayName { get; set; } + + public bool DisplayNameIsMeaningful { get; set; } + + public BlockType ItemType { get; set; } + + public Guid WorkflowActivityId { get; set; } + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Parser/ActionParser.cs b/src/RMWorkflowMigrator.Parser/ActionParser.cs new file mode 100644 index 0000000..35ba347 --- /dev/null +++ b/src/RMWorkflowMigrator.Parser/ActionParser.cs @@ -0,0 +1,124 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Parser +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Xml.Linq; + using Microsoft.ALMRangers.RMWorkflowMigrator.Parser.Model; + + public static class ActionParser + { + public static EventHandler ActionParsed; + + private static readonly XName ActionActivity = XName.Get("ActionActivity", @"clr-namespace:Microsoft.TeamFoundation.Release.Workflow.Activities;assembly=Microsoft.TeamFoundation.Release.Workflow"); + + private static readonly XName ComponentActivity = XName.Get("ComponentActivity", @"clr-namespace:Microsoft.TeamFoundation.Release.Workflow.Activities;assembly=Microsoft.TeamFoundation.Release.Workflow"); + + private static readonly XName Rollback = XName.Get("RollbackActivity", "clr-namespace:Microsoft.TeamFoundation.Release.Workflow.Activities;assembly=Microsoft.TeamFoundation.Release.Workflow"); + + private static readonly XName RollbackAlways = XName.Get("RollbackAlwaysActivity", "clr-namespace:Microsoft.TeamFoundation.Release.Workflow.Activities;assembly=Microsoft.TeamFoundation.Release.Workflow"); + + private static readonly XName ManualIntervention = XName.Get("ManualInterventionActivity", "clr-namespace:Microsoft.TeamFoundation.Release.Workflow.Activities;assembly=Microsoft.TeamFoundation.Release.Workflow"); + + private static readonly Dictionary> ActionFactory = new Dictionary> + { + { ActionActivity, ParseReleaseAction }, + { ComponentActivity, ParseReleaseAction }, + { Rollback, ParseRollback }, + { RollbackAlways, ParseRollback }, + { ManualIntervention, ParseManualIntervention } + }; + + internal static IEnumerable ProcessActions(IEnumerable nodes) + { + int sequence = 0; + + var results = new List(); + foreach (var item in + nodes.Where(node => ActionFactory.ContainsKey(node.Name)) + .Select(node => ActionFactory[node.Name](node))) + { + item.Sequence = ++sequence; + results.Add(item); + } + + return results; + } + + internal static void FireActionParsedEvent(ReleaseAction action) + { + ActionParsed?.Invoke( + null, + new ActionParsedEventArgs { DisplayName = action.DisplayName, ItemType = action.ItemType }); + } + + private static ReleaseAction ParseReleaseAction(XElement node) + { + var stageActivity = XName.Get("StageActivity", "clr-namespace:Microsoft.TeamFoundation.Release.Data.Model;assembly=Microsoft.TeamFoundation.Release.Data"); + var firstNodeDescendant = node.Descendants(stageActivity).First(); + var componentId = int.Parse(firstNodeDescendant.Attribute("ComponentId").Value); + var workflowActivityId = Guid.Parse(firstNodeDescendant.Attribute("WorkflowActivityId").Value); + var itemType = node.Name.LocalName == ActionActivity.LocalName + ? BlockType.Action + : node.Name.LocalName == ComponentActivity.LocalName ? BlockType.Component : BlockType.Undefined; + + var action = new ReleaseAction + { + DisplayName = node.Attribute("DisplayName").Value, + DisplayNameIsMeaningful = true, + Enabled = !bool.Parse(node.Attribute("IsSkipped").Value), + ItemType = itemType, + ComponentId = componentId, + WorkflowActivityId = workflowActivityId + }; + + FireActionParsedEvent(action); + + return action; + } + + private static ReleaseAction ParseRollback(XElement node) + { + var itemType = node.Name.LocalName == Rollback.LocalName + ? BlockType.Rollback + : node.Name.LocalName == RollbackAlways.LocalName ? BlockType.RollbackAlways : BlockType.Undefined; + + var rollback = new RollbackBlock + { + DisplayName = node.Attribute("DisplayName").Value, + DisplayNameIsMeaningful = false, + ItemType = itemType, + SubItems = ProcessActions(node.Elements()) + }; + + FireActionParsedEvent(rollback); + + return rollback; + } + + private static ReleaseAction ParseManualIntervention(XElement node) + { + var idElement = node.Attribute("Recipient").Value; + var splitId = idElement.Split(':'); + + var parsedManualIntervention = new ManualIntervention + { + DisplayName = node.Attribute("DisplayName").Value, + DisplayNameIsMeaningful = true, + ItemType = BlockType.ManualIntervention, + Text = node.Attribute("Details").Value, + IsGroup = int.Parse(splitId[0]) == 2, + UserId = int.Parse(splitId[1]) + }; + + FireActionParsedEvent(parsedManualIntervention); + return parsedManualIntervention; + } + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Parser/ContainerParser.cs b/src/RMWorkflowMigrator.Parser/ContainerParser.cs new file mode 100644 index 0000000..5226766 --- /dev/null +++ b/src/RMWorkflowMigrator.Parser/ContainerParser.cs @@ -0,0 +1,121 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Parser +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Xml.Linq; + using Microsoft.ALMRangers.RMWorkflowMigrator.Parser.Model; + + public static class ContainerParser + { + public static EventHandler ContainerParsed; + + private static readonly XName Sequence = XName.Get("Sequence", @"http://schemas.microsoft.com/netfx/2009/xaml/activities"); + private static readonly XName Server = XName.Get("ServerActivity", @"clr-namespace:Microsoft.TeamFoundation.Release.Workflow.Activities;assembly=Microsoft.TeamFoundation.Release.Workflow"); + private static readonly XName ServerTag = XName.Get("ServerTagActivity", @"clr-namespace:Microsoft.TeamFoundation.Release.Workflow.Activities;assembly=Microsoft.TeamFoundation.Release.Workflow"); + private static readonly XName Parallel = XName.Get("Parallel", @"http://schemas.microsoft.com/netfx/2009/xaml/activities"); + + private static readonly Dictionary>> ContainerFactory = new Dictionary>> + { + { Sequence, ProcessSequence }, + { Server, ProcessServer }, + { ServerTag, ProcessServerTag }, + { Parallel, ProcessParallel } + }; + + internal static IEnumerable> ProcessContainers(IEnumerable nodes) + { + int sequence = 0; + + var results = new List>(); + foreach (var item in + nodes.Where(node => ContainerFactory.ContainsKey(node.Name)) + .Select(node => ContainerFactory[node.Name](node))) + { + item.Sequence = ++sequence; + + results.Add(item); + } + + return results; + } + + private static IReleaseActionContainer ProcessServerTag(XElement element) + { + var container = new ReleaseActionContainer + { + DisplayName = element.Attribute("TagName").Value, + DisplayNameIsMeaningful = true, + ItemType = BlockType.ServerTag + }; + + ContainerParsed?.Invoke( + null, + new ContainerParsedEventArgs { DisplayName = container.DisplayName, ItemType = container.ItemType }); + + container.SubItems = ActionParser.ProcessActions(element.Elements()); + + return container; + } + + private static IReleaseActionContainer ProcessServer(XElement element) + { + var container = new ReleaseActionContainer + { + DisplayName = element.Attribute("ServerName").Value, + DisplayNameIsMeaningful = true, + ItemType = BlockType.Server + }; + + ContainerParsed?.Invoke( + null, + new ContainerParsedEventArgs { DisplayName = container.DisplayName, ItemType = container.ItemType }); + + container.SubItems = ActionParser.ProcessActions(element.Elements()); + + return container; + } + + private static IReleaseActionContainer ProcessSequence(XElement element) + { + var container = new ReleaseActionContainer + { + DisplayName = element.Attribute("DisplayName").Value, + DisplayNameIsMeaningful = true, + ItemType = BlockType.Sequence + }; + + ContainerParsed?.Invoke( + null, + new ContainerParsedEventArgs { DisplayName = container.DisplayName, ItemType = container.ItemType }); + + container.SubItems = ProcessContainers(element.Elements()); + + return container; + } + + private static IReleaseActionContainer ProcessParallel(XElement element) + { + var container = new ReleaseActionContainer + { + DisplayName = element.Attribute("DisplayName").Value, + DisplayNameIsMeaningful = false, + ItemType = BlockType.Parallel, + }; + + ContainerParsed?.Invoke( + null, + new ContainerParsedEventArgs { DisplayName = container.DisplayName, ItemType = container.ItemType }); + + container.SubItems = ProcessContainers(element.Elements()); + + return container; + } + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Parser/Properties/AssemblyInfo.cs b/src/RMWorkflowMigrator.Parser/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..6ecf6f8 --- /dev/null +++ b/src/RMWorkflowMigrator.Parser/Properties/AssemblyInfo.cs @@ -0,0 +1,29 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// +// Assembly-level attributes +// +// -------------------------------------------------------------------------------------------------------------------- + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Microsoft.ALMRangers.RMWorkflowMigrator.Parser")] +[assembly: AssemblyDescription("Workflow Migrator XAML release template parser ")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("Microsoft.ALMRangers.RMWorkflowMigrator.Parser")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("4ae41c2a-e09a-4d5a-9821-ead75f9dc8df")] \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Parser/RMWorkflowMigrator.Parser.csproj b/src/RMWorkflowMigrator.Parser/RMWorkflowMigrator.Parser.csproj new file mode 100644 index 0000000..74c2b87 --- /dev/null +++ b/src/RMWorkflowMigrator.Parser/RMWorkflowMigrator.Parser.csproj @@ -0,0 +1,82 @@ + + + + + Debug + AnyCPU + {7E2DC042-DFB7-40DB-AF1D-D28378702B30} + Library + Properties + Microsoft.ALMRangers.RMWorkflowMigrator.Parser + RMWorkflowMigrator.Parser + v4.6 + 512 + + SAK + SAK + SAK + SAK + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + ..\ALMRangersRuleSet.ruleset + true + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + ..\ALMRangersRuleSet.ruleset + true + + + + + + + + + + + + + Properties\ALMRangersCommonAssemblyInfo.cs + + + True + + + + + + + + {3cb3250e-e8f8-4164-bd1d-b8e910916fab} + RMWorkflowMigrator.DataAccess + + + {dab5cc84-4e29-43e0-9ba9-81d377ea08ee} + RMWorkflowMigrator.Parser.Model + + + + + + + + \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Parser/ReleaseTemplateParser.cs b/src/RMWorkflowMigrator.Parser/ReleaseTemplateParser.cs new file mode 100644 index 0000000..fafd932 --- /dev/null +++ b/src/RMWorkflowMigrator.Parser/ReleaseTemplateParser.cs @@ -0,0 +1,35 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Parser +{ + using System.IO; + using System.Xml.Linq; + using Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model; + using Microsoft.ALMRangers.RMWorkflowMigrator.Parser.Model; + + public static class ReleaseTemplateParser + { + public static DeploymentSequence ToReleaseTemplate(this RMDeploymentSequence sequence) + { + var doc = XDocument.Parse(sequence.WorkflowXaml); + + if (doc.Root == null) + { + throw new InvalidDataException("XML is not a valid release template."); + } + + var sequenceName = doc.Root?.Attribute("DisplayName").Value; + var seq = new DeploymentSequence + { + DisplayName = sequenceName, + ReleaseTemplateStageId = sequence.StageId, + Containers = ContainerParser.ProcessContainers(doc.Root?.Elements()) + }; + return seq; + } + } +} diff --git a/src/RMWorkflowMigrator.Tests.Unit/Properties/AssemblyInfo.cs b/src/RMWorkflowMigrator.Tests.Unit/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..a52d10a --- /dev/null +++ b/src/RMWorkflowMigrator.Tests.Unit/Properties/AssemblyInfo.cs @@ -0,0 +1,25 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// -------------------------------------------------------------------------------------------------------------------- + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Microsoft.ALMRangers.RMWorkflowMigrator.Core.Tests.Unit")] +[assembly: AssemblyDescription("Workflow Migrator Unit tests")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyProduct("Microsoft.ALMRangers.RMWorkflowMigrator.Core.Tests.Unit")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("bb96161e-1cdf-4173-a313-9d69d1b8513d")] \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Tests.Unit/RMWorkflowMigrator.Tests.Unit.csproj b/src/RMWorkflowMigrator.Tests.Unit/RMWorkflowMigrator.Tests.Unit.csproj new file mode 100644 index 0000000..faa2416 --- /dev/null +++ b/src/RMWorkflowMigrator.Tests.Unit/RMWorkflowMigrator.Tests.Unit.csproj @@ -0,0 +1,129 @@ + + + + Debug + AnyCPU + {D05976D4-C250-4787-8ACE-51594E42CC47} + Library + Properties + Microsoft.ALMRangers.RMWorkflowMigrator.Tests.Unit + RMWorkflowMigrator.Tests.Unit + v4.6 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + SAK + SAK + SAK + SAK + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + ..\ALMRangersRuleSet.ruleset + true + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + ..\ALMRangersRuleSet.ruleset + true + + + + + ..\packages\System.Management.Automation_PowerShell_3.0.6.3.9600.17400\lib\net40\System.Management.Automation.dll + True + + + + + + + + + + + + + + + + + + Properties\ALMRangersCommonAssemblyInfo.cs + + + + + + + + + + + + + + + {b5c99627-e15f-4d33-adf9-22012953a47b} + RMWorkflowMigrator.Generator.PowerShell + + + {dab5cc84-4e29-43e0-9ba9-81d377ea08ee} + RMWorkflowMigrator.Parser.Model + + + {7e2dc042-dfb7-40db-af1d-d28378702b30} + RMWorkflowMigrator.Parser + + + {3cb3250e-e8f8-4164-bd1d-b8e910916fab} + RMWorkflowMigrator.DataAccess + + + + + + + + + + False + + + False + + + False + + + False + + + + + + + + \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Tests.Unit/TestHelpers/FakeComponentRepo.cs b/src/RMWorkflowMigrator.Tests.Unit/TestHelpers/FakeComponentRepo.cs new file mode 100644 index 0000000..c2b636d --- /dev/null +++ b/src/RMWorkflowMigrator.Tests.Unit/TestHelpers/FakeComponentRepo.cs @@ -0,0 +1,40 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Tests.Unit.TestHelpers +{ + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + using System.Threading.Tasks; + + using Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Interfaces; + using Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model; + + [ExcludeFromCodeCoverage] + internal class FakeComponentRepo : IRMComponentRepository + { + public FakeComponentRepo() + { + this.Components = new List(); + } + + public List Components { get; set; } + + public Task GetComponentByIdAsync(Guid workflowActivityId, int stageId) + { + return Task.FromResult(this.Components.First(c => c.WorkflowActivityId == workflowActivityId)); + } + + public Task> GetComponentConfigurationVariablesAsync( + Guid workflowGuid, + int stageId) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Tests.Unit/TestHelpers/FakeDeployerToolRepo.cs b/src/RMWorkflowMigrator.Tests.Unit/TestHelpers/FakeDeployerToolRepo.cs new file mode 100644 index 0000000..03413b7 --- /dev/null +++ b/src/RMWorkflowMigrator.Tests.Unit/TestHelpers/FakeDeployerToolRepo.cs @@ -0,0 +1,22 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Tests.Unit.TestHelpers +{ + using System.Diagnostics.CodeAnalysis; + using System.Threading.Tasks; + + using Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Interfaces; + + [ExcludeFromCodeCoverage] + public class FakeDeployerToolRepo : IRMDeployerToolRepository + { + public Task WriteToolToDiskAsync(int deployerToolId, string diskPath) + { + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Tests.Unit/TestHelpers/FakeFileSystem.cs b/src/RMWorkflowMigrator.Tests.Unit/TestHelpers/FakeFileSystem.cs new file mode 100644 index 0000000..780c48e --- /dev/null +++ b/src/RMWorkflowMigrator.Tests.Unit/TestHelpers/FakeFileSystem.cs @@ -0,0 +1,42 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Tests.Unit.TestHelpers +{ + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + + using Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell; + + [ExcludeFromCodeCoverage] + public class FakeFileSystem : IFileSystem + { + public FakeFileSystem() + { + this.Directories = new HashSet(); + this.Files = new Dictionary(); + } + + public HashSet Directories { get; set; } + + public Dictionary Files { get; set; } + + public void CreateDirectory(string path) + { + this.Directories.Add(path); + } + + public bool Exists(string path) + { + return this.Files.ContainsKey(path); + } + + public void WriteAllText(string path, string contents) + { + this.Files.Add(path, contents); + } + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Tests.Unit/TestHelpers/FakeUserRepo.cs b/src/RMWorkflowMigrator.Tests.Unit/TestHelpers/FakeUserRepo.cs new file mode 100644 index 0000000..b21eca3 --- /dev/null +++ b/src/RMWorkflowMigrator.Tests.Unit/TestHelpers/FakeUserRepo.cs @@ -0,0 +1,34 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Tests.Unit.TestHelpers +{ + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + using System.Threading.Tasks; + + using Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Interfaces; + using Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model; + + [ExcludeFromCodeCoverage] + public class FakeUserRepo : IRMUserRepository + { + public HashSet Groups { get; set; } + + public HashSet Users { get; set; } + + public Task GetGroupAsync(int groupId) + { + return Task.FromResult(this.Groups.First(u => u.Id == groupId)); + } + + public Task GetUserAsync(int userId) + { + return Task.FromResult(this.Users.First(u => u.Id == userId)); + } + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Tests.Unit/When_Generating_A_Folder_Structure_From_A_Workflow.cs b/src/RMWorkflowMigrator.Tests.Unit/When_Generating_A_Folder_Structure_From_A_Workflow.cs new file mode 100644 index 0000000..5b8e042 --- /dev/null +++ b/src/RMWorkflowMigrator.Tests.Unit/When_Generating_A_Folder_Structure_From_A_Workflow.cs @@ -0,0 +1,295 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// -------------------------------------------------------------------------------------------------------------------- + + // ReSharper disable InconsistentNaming + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Tests.Unit +{ + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Threading.Tasks; + + using Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model; + using Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell; + using Microsoft.ALMRangers.RMWorkflowMigrator.Parser; + using Microsoft.ALMRangers.RMWorkflowMigrator.Tests.Unit.TestHelpers; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [ExcludeFromCodeCoverage] + [TestClass] + public class When_Generating_A_Folder_Structure_From_A_Workflow + { + [TestMethod] + public async Task A_Valid_Folder_Structure_Is_Generated_For_A_Rollback_Block() + { + // Arrange + var fs = new FakeFileSystem(); + var fakeComponentRepo = new FakeComponentRepo + { + Components = + new List + { + new RMComponent + { + Id = 3981, + WorkflowActivityId + = + Guid + .Parse( + "1d5e32ae-1b13-4e9a-8493-70e243a4ffed") + }, + new RMComponent + { + Id = 3981, + WorkflowActivityId + = + Guid + .Parse( + "6d0c925e-4420-4420-a8a0-c800f5bd48f4") + }, + new RMComponent + { + Id = 3982, + WorkflowActivityId + = + Guid + .Parse( + "74fb48d1-ca36-4477-88b2-22e5d9f41ca1") + } + } + }; + + var fakeUserRepo = new FakeUserRepo(); + var fakeDeployerToolRepo = new FakeDeployerToolRepo(); + + var gen = new ScriptGenerator(fs, fakeComponentRepo, fakeUserRepo, fakeDeployerToolRepo); + + var testXml = + @"True__ReferenceID2__ReferenceID3__ReferenceID4__ReferenceID5__ReferenceID7"; + var targetPath = @"C:\RMWorkflow\"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + await gen.GenerateScriptAsync(sequence, targetPath); + + // Assert + Assert.IsTrue(fs.Directories.Contains(@"C:\RMWorkflow\1_Parallel")); + Assert.IsTrue(fs.Directories.Contains(@"C:\RMWorkflow\1_Parallel\1_Server_lab-dmann")); + Assert.IsTrue(fs.Directories.Contains(@"C:\RMWorkflow\1_Parallel\2_ServerTag_Dev")); + } + + [TestMethod] + public async Task A_Valid_Folder_Structure_Is_Generated_For_A_Sequence_Containing_A_Server() + { + // Arrange + var fs = new FakeFileSystem(); + var fakeComponentRepo = new FakeComponentRepo + { + Components = + new List + { + new RMComponent + { + Id = 3981, + WorkflowActivityId + = + Guid + .Parse( + "1d5e32ae-1b13-4e9a-8493-70e243a4ffed") + }, + new RMComponent + { + Id = 3981, + WorkflowActivityId + = + Guid + .Parse( + "6d0c925e-4420-4420-a8a0-c800f5bd48f4") + }, + new RMComponent + { + Id = 3982, + WorkflowActivityId + = + Guid + .Parse( + "74fb48d1-ca36-4477-88b2-22e5d9f41ca1") + } + } + }; + + var fakeUserRepo = new FakeUserRepo(); + var fakeDeployerToolRepo = new FakeDeployerToolRepo(); + + var gen = new ScriptGenerator(fs, fakeComponentRepo, fakeUserRepo, fakeDeployerToolRepo); + + var testXml = + @"True__ReferenceID0__ReferenceID1__ReferenceID2"; + var targetPath = @"C:\RMWorkflow\"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + await gen.GenerateScriptAsync(sequence, targetPath); + + // Assert + Assert.IsTrue(fs.Directories.Contains(@"C:\RMWorkflow\1_Parallel")); + Assert.IsTrue(fs.Directories.Contains(@"C:\RMWorkflow\1_Parallel\1_Server_lab-dmann")); + Assert.IsTrue(fs.Directories.Contains(@"C:\RMWorkflow\1_Parallel\2_ServerTag_Dev")); + } + + [TestMethod] + public async Task A_Valid_Folder_Structure_Is_Generated_For_A_Sequence_With_A_Server() + { + // Arrange + var fs = new FakeFileSystem(); + var fakeComponentRepo = new FakeComponentRepo + { + Components = + new List + { + new RMComponent + { + Id = 3981, + WorkflowActivityId + = + Guid + .Parse( + "a122e31d-9faa-4468-b09b-2ec09ea51567") + } + } + }; + + var fakeUserRepo = new FakeUserRepo(); + var fakeDeployerToolRepo = new FakeDeployerToolRepo(); + + var gen = new ScriptGenerator(fs, fakeComponentRepo, fakeUserRepo, fakeDeployerToolRepo); + var testXml = + @"True__ReferenceID0__ReferenceID1"; + var targetPath = @"C:\RMWorkflow\"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + await gen.GenerateScriptAsync(sequence, targetPath); + + // Assert + Assert.IsTrue(fs.Directories.Contains(@"C:\RMWorkflow\1_Server_lab-dmann")); + } + + [TestMethod] + public async Task Invalid_Characters_Are_Scrubbed_From_Folder_Names() + { + // Arrange + var fs = new FakeFileSystem(); + var fakeComponentRepo = new FakeComponentRepo + { + Components = + new List + { + new RMComponent + { + Id = 3981, + WorkflowActivityId + = + Guid + .Parse( + "54b7b5f4-6a4d-4cec-b1e9-edae6bda0f20") + }, + new RMComponent + { + Id = 3981, + WorkflowActivityId + = + Guid + .Parse( + "9f2080c8-d438-431e-8cb5-f312c5c2b836") + } + } + }; + + var fakeUserRepo = new FakeUserRepo(); + var fakeDeployerToolRepo = new FakeDeployerToolRepo(); + + var gen = new ScriptGenerator(fs, fakeComponentRepo, fakeUserRepo, fakeDeployerToolRepo); + + var testXml = + @"True__ReferenceID0__ReferenceID1__ReferenceID3__ReferenceID4"; + var targetPath = @"C:\RMWorkflow\"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + await gen.GenerateScriptAsync(sequence, targetPath); + + // Assert + Assert.IsTrue(fs.Directories.Contains(@"C:\RMWorkflow\1_Sequence_Sequence+")); + Assert.IsTrue(fs.Directories.Contains(@"C:\RMWorkflow\1_Sequence_Sequence+\1_Parallel")); + Assert.IsTrue( + fs.Directories.Contains(@"C:\RMWorkflow\1_Sequence_Sequence+\1_Parallel\1_Server_placeholderServer!#")); + Assert.IsTrue( + fs.Directories.Contains(@"C:\RMWorkflow\1_Sequence_Sequence+\1_Parallel\2_ServerTag_placeholderTag!")); + } + + [TestMethod] + public async Task Scripts_Are_Created_In_The_Folders_As_Appropriate() + { + // Arrange + var fs = new FakeFileSystem(); + var fakeComponentRepo = new FakeComponentRepo + { + Components = + new List + { + new RMComponent + { + Id = 3981, + WorkflowActivityId + = + Guid + .Parse( + "1d5e32ae-1b13-4e9a-8493-70e243a4ffed") + }, + new RMComponent + { + Id = 3981, + WorkflowActivityId + = + Guid + .Parse( + "6d0c925e-4420-4420-a8a0-c800f5bd48f4") + }, + new RMComponent + { + Id = 3982, + WorkflowActivityId + = + Guid + .Parse( + "74fb48d1-ca36-4477-88b2-22e5d9f41ca1") + } + } + }; + + var fakeUserRepo = new FakeUserRepo(); + var fakeDeployerToolRepo = new FakeDeployerToolRepo(); + + var gen = new ScriptGenerator(fs, fakeComponentRepo, fakeUserRepo, fakeDeployerToolRepo); + + var testXml = + @"True__ReferenceID2__ReferenceID3__ReferenceID4__ReferenceID5__ReferenceID7"; + var targetPath = @"C:\RMWorkflow\"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + await gen.GenerateScriptAsync(sequence, targetPath); + + // Assert + Assert.IsTrue(fs.Files.ContainsKey(@"C:\RMWorkflow\1_Parallel\1_Server_lab-dmann\ReleaseScript.ps1")); + Assert.IsTrue(fs.Files.ContainsKey(@"C:\RMWorkflow\1_Parallel\1_Server_lab-dmann\2_Rollback.ps1")); + Assert.IsTrue(fs.Files.ContainsKey(@"C:\RMWorkflow\1_Parallel\2_ServerTag_Dev\ReleaseScript.ps1")); + Assert.IsTrue(fs.Files.ContainsKey(@"C:\RMWorkflow\DeployerTools\TokenizationScript.ps1")); + } + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Tests.Unit/When_Generating_Scripts.cs b/src/RMWorkflowMigrator.Tests.Unit/When_Generating_Scripts.cs new file mode 100644 index 0000000..e310253 --- /dev/null +++ b/src/RMWorkflowMigrator.Tests.Unit/When_Generating_Scripts.cs @@ -0,0 +1,2252 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Tests.Unit +{ + using System; + using System.Collections.Generic; + using System.Collections.ObjectModel; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using System.Management.Automation; + using System.Threading.Tasks; + + using Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model; + using Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell; + using Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model; + using Microsoft.ALMRangers.RMWorkflowMigrator.Parser; + using Microsoft.ALMRangers.RMWorkflowMigrator.Tests.Unit.TestHelpers; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + // ReSharper disable InconsistentNaming + [ExcludeFromCodeCoverage] + [TestClass] + public class When_Generating_Scripts + { + [TestMethod] + public async Task + An_Backslash_Escaped_Quotation_Mark_In_A_Variable_Is_Replaced_With_A_Powershell_Escape_Sequence() + { + var fakeFs = new FakeFileSystem(); + var componentRepo = new FakeComponentRepo(); + componentRepo.Components.Add( + new RMComponent + { + Id = 4010, + WorkflowActivityId = Guid.Parse("d25617e8-7fac-4637-a7cb-2ad4da996156"), + Command = "IISConfig.exe", + DeployerToolId = 2014, + FileExtensionFilter = string.Empty, + Arguments = @"-DropWebSite -sn ""__SiteName__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SiteName", + Value = + @"\""TestValue\""", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4016, + WorkflowActivityId = Guid.Parse("00152F01-0651-4221-9835-3394F1739A2F"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"*.* ""__Installation Path__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "Installation Path", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 3981, + WorkflowActivityId = Guid.Parse("E7783400-9B05-4970-8777-6AB92C78D155"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"""__SourceFileFolder__"" ""__DestinationFileFolder__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SourceFileFolder", + Value = + @"c:\FabrikamRM\Backup\DEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "DestinationFileFolder", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4009, + WorkflowActivityId = Guid.Parse("A657D65D-F4D6-42F8-AA1F-7AC638496557"), + Command = "IISConfig.exe", + DeployerToolId = 2014, + FileExtensionFilter = string.Empty, + Arguments = + @"-CreateWebSite -sn ""__SiteName__"" -port __PortNumber__ -pd ""__PhysicalPath__"" -ap ""__AppPoolName__"" -enablepreload __IsPreloadEnabled__ -autostart __IsAutoStart__", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "AppPoolName", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsPreloadEnabled", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsAutoStart", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "SiteName", + Value = + @"FabrikamDEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PortNumber", + Value = @"8000", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PhysicalPath", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4009, + WorkflowActivityId = Guid.Parse("51C262E8-D1FE-4FF8-ADD6-879C2718616B"), + Command = "IISConfig.exe", + DeployerToolId = 2014, + FileExtensionFilter = string.Empty, + Arguments = + @"-CreateWebSite -sn ""__SiteName__"" -port __PortNumber__ -pd ""__PhysicalPath__"" -ap ""__AppPoolName__"" -enablepreload __IsPreloadEnabled__ -autostart __IsAutoStart__", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "AppPoolName", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsPreloadEnabled", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsAutoStart", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "SiteName", + Value = + @"FabrikamDEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PortNumber", + Value = @"8000", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PhysicalPath", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 3981, + WorkflowActivityId = Guid.Parse("8160F49B-34C4-4860-AEC4-B9FC4CF81E9B"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"""__SourceFileFolder__"" ""__DestinationFileFolder__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SourceFileFolder", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "DestinationFileFolder", + Value = + @"c:\FabrikamRM\Backup\DEV", + IsParameter = + true + } + } + }); + + var fakeUserRepo = new FakeUserRepo(); + var fakeDeployerToolRepo = new FakeDeployerToolRepo(); + + var scriptGenerator = new ScriptGenerator(fakeFs, componentRepo, fakeUserRepo, fakeDeployerToolRepo); + var path = @"C:\RMWorkflow"; + var releaseTemplate = + @"True__ReferenceID1TrueFalse__ReferenceID4__ReferenceID5TrueFalse__ReferenceID7__ReferenceID14__ReferenceID15__ReferenceID16__ReferenceID17__ReferenceID18__ReferenceID19TrueFalse__ReferenceID21__ReferenceID22TrueFalse__ReferenceID24__ReferenceID25__ReferenceID26__ReferenceID27__ReferenceID28__ReferenceID29TrueFalse"; + + var parsedTemplate = + (new RMDeploymentSequence { StageId = 100, WorkflowXaml = releaseTemplate }).ToReleaseTemplate(); + scriptGenerator.ScriptGenerationNotification += PrintEvents; + await scriptGenerator.GenerateScriptAsync(parsedTemplate, path); + + Collection errors; + PSParser.Tokenize(fakeFs.Files["C:\\RMWorkflow\\1_Server_VSALM\\InitializationScript.ps1"], out errors); + Assert.AreEqual(0, errors.Count); + } + + [TestMethod] + public async Task FabrikamFiber_From_BKVM_Component_NoTools() + { + var fakeFs = new FakeFileSystem(); + var componentRepo = new FakeComponentRepo(); + componentRepo.Components.Add( + new RMComponent + { + Id = 4010, + WorkflowActivityId = Guid.Parse("d25617e8-7fac-4637-a7cb-2ad4da996156"), + Command = "IISConfig.exe", + DeployerToolId = 2014, + FileExtensionFilter = string.Empty, + Arguments = @"-DropWebSite -sn ""__SiteName__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SiteName", + Value = + "FabrikamDEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4016, + WorkflowActivityId = Guid.Parse("00152F01-0651-4221-9835-3394F1739A2F"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"*.* ""__Installation Path__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "Installation Path", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4020, + WorkflowActivityId = Guid.Parse("3C8D22D2-37F3-469E-9038-26AA438FB34C"), + Command = "placeholder.exe", + DeployerToolId = 0, + FileExtensionFilter = string.Empty, + Arguments = @"__Bar__", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "Bar", + Value = string.Empty, + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 3981, + WorkflowActivityId = Guid.Parse("E7783400-9B05-4970-8777-6AB92C78D155"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"""__SourceFileFolder__"" ""__DestinationFileFolder__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SourceFileFolder", + Value = + @"c:\FabrikamRM\Backup\DEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "DestinationFileFolder", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4009, + WorkflowActivityId = Guid.Parse("A657D65D-F4D6-42F8-AA1F-7AC638496557"), + Command = "IISConfig.exe", + DeployerToolId = 2014, + FileExtensionFilter = string.Empty, + Arguments = + @"-CreateWebSite -sn ""__SiteName__"" -port __PortNumber__ -pd ""__PhysicalPath__"" -ap ""__AppPoolName__"" -enablepreload __IsPreloadEnabled__ -autostart __IsAutoStart__", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "AppPoolName", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsPreloadEnabled", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsAutoStart", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "SiteName", + Value = + @"FabrikamDEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PortNumber", + Value = @"8000", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PhysicalPath", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4009, + WorkflowActivityId = Guid.Parse("51C262E8-D1FE-4FF8-ADD6-879C2718616B"), + Command = "IISConfig.exe", + DeployerToolId = 2014, + FileExtensionFilter = string.Empty, + Arguments = + @"-CreateWebSite -sn ""__SiteName__"" -port __PortNumber__ -pd ""__PhysicalPath__"" -ap ""__AppPoolName__"" -enablepreload __IsPreloadEnabled__ -autostart __IsAutoStart__", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "AppPoolName", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsPreloadEnabled", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsAutoStart", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "SiteName", + Value = + @"FabrikamDEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PortNumber", + Value = @"8000", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PhysicalPath", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 3981, + WorkflowActivityId = Guid.Parse("8160F49B-34C4-4860-AEC4-B9FC4CF81E9B"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"""__SourceFileFolder__"" ""__DestinationFileFolder__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SourceFileFolder", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "DestinationFileFolder", + Value = + @"c:\FabrikamRM\Backup\DEV", + IsParameter = + true + } + } + }); + + var fakeUserRepo = new FakeUserRepo(); + var fakeDeployerToolRepo = new FakeDeployerToolRepo(); + + var scriptGenerator = new ScriptGenerator(fakeFs, componentRepo, fakeUserRepo, fakeDeployerToolRepo); + var path = @"C:\RMWorkflow"; + var releaseTemplate = + @"True__ReferenceID1TrueFalse__ReferenceID4__ReferenceID5TrueFalse__ReferenceID7__ReferenceID14__ReferenceID15__ReferenceID16__ReferenceID17__ReferenceID18__ReferenceID19TrueFalseTrueFalse__ReferenceID21__ReferenceID22TrueFalse__ReferenceID24__ReferenceID25__ReferenceID26__ReferenceID27__ReferenceID28__ReferenceID29TrueFalse__ReferenceID31TrueFalse"; + + var parsedTemplate = + (new RMDeploymentSequence { StageId = 100, WorkflowXaml = releaseTemplate }).ToReleaseTemplate(); + scriptGenerator.ScriptGenerationNotification += PrintEvents; + await scriptGenerator.GenerateScriptAsync(parsedTemplate, path); + } + + [TestMethod] + public async Task FabrikamFiber_From_BKVM_Proof_Of_Concept() + { + var fakeFs = new FakeFileSystem(); + var componentRepo = new FakeComponentRepo(); + componentRepo.Components.Add( + new RMComponent + { + Id = 4010, + WorkflowActivityId = Guid.Parse("d25617e8-7fac-4637-a7cb-2ad4da996156"), + Command = "IISConfig.exe", + DeployerToolId = 2014, + FileExtensionFilter = string.Empty, + Arguments = @"-DropWebSite -sn ""__SiteName__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SiteName", + Value = + "FabrikamDEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4016, + WorkflowActivityId = Guid.Parse("00152F01-0651-4221-9835-3394F1739A2F"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"*.* ""__Installation Path__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "Installation Path", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 3981, + WorkflowActivityId = Guid.Parse("E7783400-9B05-4970-8777-6AB92C78D155"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"""__SourceFileFolder__"" ""__DestinationFileFolder__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SourceFileFolder", + Value = + @"c:\FabrikamRM\Backup\DEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "DestinationFileFolder", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4009, + WorkflowActivityId = Guid.Parse("A657D65D-F4D6-42F8-AA1F-7AC638496557"), + Command = "IISConfig.exe", + DeployerToolId = 2014, + FileExtensionFilter = string.Empty, + Arguments = + @"-CreateWebSite -sn ""__SiteName__"" -port __PortNumber__ -pd ""__PhysicalPath__"" -ap ""__AppPoolName__"" -enablepreload __IsPreloadEnabled__ -autostart __IsAutoStart__", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "AppPoolName", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsPreloadEnabled", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsAutoStart", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "SiteName", + Value = + @"FabrikamDEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PortNumber", + Value = @"8000", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PhysicalPath", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4009, + WorkflowActivityId = Guid.Parse("51C262E8-D1FE-4FF8-ADD6-879C2718616B"), + Command = "IISConfig.exe", + DeployerToolId = 2014, + FileExtensionFilter = string.Empty, + Arguments = + @"-CreateWebSite -sn ""__SiteName__"" -port __PortNumber__ -pd ""__PhysicalPath__"" -ap ""__AppPoolName__"" -enablepreload __IsPreloadEnabled__ -autostart __IsAutoStart__", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "AppPoolName", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsPreloadEnabled", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsAutoStart", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "SiteName", + Value = + @"FabrikamDEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PortNumber", + Value = @"8000", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PhysicalPath", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 3981, + WorkflowActivityId = Guid.Parse("8160F49B-34C4-4860-AEC4-B9FC4CF81E9B"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"""__SourceFileFolder__"" ""__DestinationFileFolder__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SourceFileFolder", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "DestinationFileFolder", + Value = + @"c:\FabrikamRM\Backup\DEV", + IsParameter = + true + } + } + }); + + var fakeUserRepo = new FakeUserRepo(); + var fakeDeployerToolRepo = new FakeDeployerToolRepo(); + + var scriptGenerator = new ScriptGenerator(fakeFs, componentRepo, fakeUserRepo, fakeDeployerToolRepo); + var path = @"C:\RMWorkflow"; + var releaseTemplate = + @"True__ReferenceID1TrueFalse__ReferenceID4__ReferenceID5TrueFalse__ReferenceID7__ReferenceID14__ReferenceID15__ReferenceID16__ReferenceID17__ReferenceID18__ReferenceID19TrueFalse__ReferenceID21__ReferenceID22TrueFalse__ReferenceID24__ReferenceID25__ReferenceID26__ReferenceID27__ReferenceID28__ReferenceID29TrueFalse"; + + var parsedTemplate = + (new RMDeploymentSequence { StageId = 100, WorkflowXaml = releaseTemplate }).ToReleaseTemplate(); + scriptGenerator.ScriptGenerationNotification += PrintEvents; + await scriptGenerator.GenerateScriptAsync(parsedTemplate, path); + } + + [TestMethod] + public async Task FabrikamFiber_From_BKVM_With_Component_In_Rollback() + { + var fakeFs = new FakeFileSystem(); + var componentRepo = new FakeComponentRepo(); + componentRepo.Components.Add( + new RMComponent + { + Id = 4010, + WorkflowActivityId = Guid.Parse("d25617e8-7fac-4637-a7cb-2ad4da996156"), + Command = "IISConfig.exe", + DeployerToolId = 2014, + FileExtensionFilter = string.Empty, + Arguments = @"-DropWebSite -sn ""__SiteName__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SiteName", + Value = + "FabrikamDEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4016, + WorkflowActivityId = Guid.Parse("00152F01-0651-4221-9835-3394F1739A2F"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"*.* ""__Installation Path__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "Installation Path", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4016, + WorkflowActivityId = Guid.Parse("8FCB1DCB-3EC0-4678-8F7D-25D727A26011"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"*.* ""__Installation Path__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "Installation Path", + Value = + @"c:\FabrikamRM\WebSite\Rollback", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 3981, + WorkflowActivityId = Guid.Parse("E7783400-9B05-4970-8777-6AB92C78D155"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"""__SourceFileFolder__"" ""__DestinationFileFolder__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SourceFileFolder", + Value = + @"c:\FabrikamRM\Backup\DEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "DestinationFileFolder", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4009, + WorkflowActivityId = Guid.Parse("A657D65D-F4D6-42F8-AA1F-7AC638496557"), + Command = "IISConfig.exe", + DeployerToolId = 2014, + FileExtensionFilter = string.Empty, + Arguments = + @"-CreateWebSite -sn ""__SiteName__"" -port __PortNumber__ -pd ""__PhysicalPath__"" -ap ""__AppPoolName__"" -enablepreload __IsPreloadEnabled__ -autostart __IsAutoStart__", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "AppPoolName", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsPreloadEnabled", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsAutoStart", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "SiteName", + Value = + @"FabrikamDEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PortNumber", + Value = @"8000", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PhysicalPath", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4009, + WorkflowActivityId = Guid.Parse("51C262E8-D1FE-4FF8-ADD6-879C2718616B"), + Command = "IISConfig.exe", + DeployerToolId = 2014, + FileExtensionFilter = string.Empty, + Arguments = + @"-CreateWebSite -sn ""__SiteName__"" -port __PortNumber__ -pd ""__PhysicalPath__"" -ap ""__AppPoolName__"" -enablepreload __IsPreloadEnabled__ -autostart __IsAutoStart__", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "AppPoolName", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsPreloadEnabled", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsAutoStart", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "SiteName", + Value = + @"FabrikamDEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PortNumber", + Value = @"8000", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PhysicalPath", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 3981, + WorkflowActivityId = Guid.Parse("8160F49B-34C4-4860-AEC4-B9FC4CF81E9B"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"""__SourceFileFolder__"" ""__DestinationFileFolder__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SourceFileFolder", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "DestinationFileFolder", + Value = + @"c:\FabrikamRM\Backup\DEV", + IsParameter = + true + } + } + }); + + var fakeUserRepo = new FakeUserRepo(); + var fakeDeployerToolRepo = new FakeDeployerToolRepo(); + + var scriptGenerator = new ScriptGenerator(fakeFs, componentRepo, fakeUserRepo, fakeDeployerToolRepo); + var path = @"C:\RMWorkflow"; + var releaseTemplate = + @"True__ReferenceID1TrueFalse__ReferenceID4__ReferenceID5TrueFalse__ReferenceID7__ReferenceID14__ReferenceID15__ReferenceID16__ReferenceID17__ReferenceID18__ReferenceID19TrueFalseTrueFalse__ReferenceID21__ReferenceID23__ReferenceID24TrueFalse__ReferenceID26__ReferenceID27__ReferenceID28__ReferenceID29__ReferenceID30__ReferenceID31TrueFalse"; + + var parsedTemplate = + (new RMDeploymentSequence { StageId = 100, WorkflowXaml = releaseTemplate }).ToReleaseTemplate(); + scriptGenerator.ScriptGenerationNotification += PrintEvents; + await scriptGenerator.GenerateScriptAsync(parsedTemplate, path); + } + + [TestMethod] + public async Task FabrikamFiber_From_BKVM_With_Configuration_Variables() + { + var fakeFs = new FakeFileSystem(); + var componentRepo = new FakeComponentRepo(); + componentRepo.Components.Add( + new RMComponent + { + Id = 4010, + WorkflowActivityId = Guid.Parse("d25617e8-7fac-4637-a7cb-2ad4da996156"), + Command = "IISConfig.exe", + DeployerToolId = 2014, + FileExtensionFilter = string.Empty, + Arguments = @"-DropWebSite -sn ""__SiteName__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SiteName", + Value = + "FabrikamDEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4016, + WorkflowActivityId = Guid.Parse("00152F01-0651-4221-9835-3394F1739A2F"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"*.* ""__Installation Path__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "Installation Path", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4017, + WorkflowActivityId = Guid.Parse("41BF9071-F439-4046-AB42-F3A1184237F3"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = "*.configBeforeAfter", + Arguments = @"*.* ""__Installation Path__""", + VariableReplacementMethod = VariableReplacementMethod.BeforeAndAfterInstallation, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "Installation Path", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "BeforeAfter_1", + Value = + @"BeforeAfter 1", + IsParameter = + false + }, + new ConfigurationVariable + { + OriginalName = + "BeforeAfter 2 Encrypted", + Value = + @"Muyy7/eWpqb8UZ4wZc3fN23pu7dvdTpAY0H+zD3Vd0S+HTL4vQHqbnSe3w616qnJ0y9rxRdkVaZiX8ydhUI/snwsdlUGk/070dz3Kjbwb7twzObCXcY4EnjgB7SDr9iu650TUzt5MSM8VbK723c0kS+jWK5g/EEefJwpdiZgoYI=", + IsParameter = + false, + Encrypted = true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4018, + WorkflowActivityId = Guid.Parse("2718F66A-E5C0-4FE2-A90D-61B550E3A6C1"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = "*.configBefore", + Arguments = @"*.* ""__Installation Path__""", + VariableReplacementMethod = VariableReplacementMethod.BeforeInstallation, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "Installation Path", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "Before_1", + Value = + @"Before 1", + IsParameter = + false + }, + new ConfigurationVariable + { + OriginalName = + "Before 2 Encrypted", + Value = + @"Jlx0czDQZOU3OGtck915VE5rNvzNvwzu0VfyBgg5QwQ4gjHyyDBae3j5w0chqjYCtX4l+ruzKgYWxXe+UFTbw1vbZH5u1fMsKG95nw0DN6WH9+GQwhp8Ab15D2ZbGyrMLf9eTcI9Qxlw6QdAJl8SM5ZkkX8g64lq/Vd/MV+akR8=", + IsParameter = + false, + Encrypted = true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4019, + WorkflowActivityId = Guid.Parse("2B607E62-6ED8-4358-91CC-EA8BFD751AC6"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = "*.configAfter", + Arguments = @"*.* ""__Installation Path__""", + VariableReplacementMethod = VariableReplacementMethod.AfterInstallation, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "Installation Path", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "After_1", + Value = + @"After 1", + IsParameter = + false + }, + new ConfigurationVariable + { + OriginalName = + "After 2 Encrypted", + Value = + @"Bj8P2jC2DYyO/8dTJrecmWJM+BUlr0TeyYV1qYbM/izIXMqHeINxdOND2wDyowLEkn4dezqfiEhDPVubeTeZriQhqDTI/6u9aol9RR4cnoLJC9n7ykgWjMCPDqDPQpjig3w/snecAmYJWdnwBDZezfe106X9Uv7qI+QCyzdqTIc=", + IsParameter = + false, + Encrypted = true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 3981, + WorkflowActivityId = Guid.Parse("E7783400-9B05-4970-8777-6AB92C78D155"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"""__SourceFileFolder__"" ""__DestinationFileFolder__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SourceFileFolder", + Value = + @"c:\FabrikamRM\Backup\DEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "DestinationFileFolder", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4009, + WorkflowActivityId = Guid.Parse("A657D65D-F4D6-42F8-AA1F-7AC638496557"), + Command = "IISConfig.exe", + DeployerToolId = 2014, + FileExtensionFilter = string.Empty, + Arguments = + @"-CreateWebSite -sn ""__SiteName__"" -port __PortNumber__ -pd ""__PhysicalPath__"" -ap ""__AppPoolName__"" -enablepreload __IsPreloadEnabled__ -autostart __IsAutoStart__", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "AppPoolName", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsPreloadEnabled", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsAutoStart", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "SiteName", + Value = + @"FabrikamDEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PortNumber", + Value = @"8000", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PhysicalPath", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4009, + WorkflowActivityId = Guid.Parse("51C262E8-D1FE-4FF8-ADD6-879C2718616B"), + Command = "IISConfig.exe", + DeployerToolId = 2014, + FileExtensionFilter = string.Empty, + Arguments = + @"-CreateWebSite -sn ""__SiteName__"" -port __PortNumber__ -pd ""__PhysicalPath__"" -ap ""__AppPoolName__"" -enablepreload __IsPreloadEnabled__ -autostart __IsAutoStart__", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "AppPoolName", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsPreloadEnabled", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsAutoStart", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "SiteName", + Value = + @"FabrikamDEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PortNumber", + Value = @"8000", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PhysicalPath", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 3981, + WorkflowActivityId = Guid.Parse("8160F49B-34C4-4860-AEC4-B9FC4CF81E9B"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"""__SourceFileFolder__"" ""__DestinationFileFolder__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SourceFileFolder", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "DestinationFileFolder", + Value = + @"c:\FabrikamRM\Backup\DEV", + IsParameter = + true + } + } + }); + + var fakeUserRepo = new FakeUserRepo(); + var fakeDeployerToolRepo = new FakeDeployerToolRepo(); + + var scriptGenerator = new ScriptGenerator(fakeFs, componentRepo, fakeUserRepo, fakeDeployerToolRepo); + + var path = @"C:\RMWorkflow"; + var releaseTemplate = + @"True__ReferenceID1TrueFalse__ReferenceID4__ReferenceID5TrueFalse__ReferenceID7__ReferenceID11__ReferenceID12__ReferenceID13__ReferenceID17__ReferenceID18__ReferenceID19__ReferenceID23__ReferenceID24__ReferenceID25__ReferenceID32__ReferenceID33__ReferenceID34__ReferenceID35__ReferenceID36__ReferenceID37TrueFalse__ReferenceID39__ReferenceID40TrueFalse__ReferenceID42__ReferenceID43__ReferenceID44__ReferenceID45__ReferenceID46__ReferenceID47TrueFalse"; + + var parsedTemplate = + (new RMDeploymentSequence { StageId = 100, WorkflowXaml = releaseTemplate }).ToReleaseTemplate(); + scriptGenerator.ScriptGenerationNotification += PrintEvents; + await scriptGenerator.GenerateScriptAsync(parsedTemplate, path); + } + + [TestMethod] + public async Task FabrikamFiber_From_BKVM_With_Manual_Interventions() + { + var fakeFs = new FakeFileSystem(); + var componentRepo = new FakeComponentRepo(); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4010, + WorkflowActivityId = Guid.Parse("d25617e8-7fac-4637-a7cb-2ad4da996156"), + Command = "IISConfig.exe", + DeployerToolId = 2014, + FileExtensionFilter = string.Empty, + Arguments = @"-DropWebSite -sn ""__SiteName__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SiteName", + Value = + "FabrikamDEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4016, + WorkflowActivityId = Guid.Parse("00152F01-0651-4221-9835-3394F1739A2F"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"*.* ""__Installation Path__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "Installation Path", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 3981, + WorkflowActivityId = Guid.Parse("E7783400-9B05-4970-8777-6AB92C78D155"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"""__SourceFileFolder__"" ""__DestinationFileFolder__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SourceFileFolder", + Value = + @"c:\FabrikamRM\Backup\DEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "DestinationFileFolder", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4009, + WorkflowActivityId = Guid.Parse("A657D65D-F4D6-42F8-AA1F-7AC638496557"), + Command = "IISConfig.exe", + DeployerToolId = 2014, + FileExtensionFilter = string.Empty, + Arguments = + @"-CreateWebSite -sn ""__SiteName__"" -port __PortNumber__ -pd ""__PhysicalPath__"" -ap ""__AppPoolName__"" -enablepreload __IsPreloadEnabled__ -autostart __IsAutoStart__", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "AppPoolName", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsPreloadEnabled", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsAutoStart", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "SiteName", + Value = + @"FabrikamDEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PortNumber", + Value = @"8000", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PhysicalPath", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4009, + WorkflowActivityId = Guid.Parse("51C262E8-D1FE-4FF8-ADD6-879C2718616B"), + Command = "IISConfig.exe", + DeployerToolId = 2014, + FileExtensionFilter = string.Empty, + Arguments = + @"-CreateWebSite -sn ""__SiteName__"" -port __PortNumber__ -pd ""__PhysicalPath__"" -ap ""__AppPoolName__"" -enablepreload __IsPreloadEnabled__ -autostart __IsAutoStart__", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "AppPoolName", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsPreloadEnabled", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsAutoStart", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "SiteName", + Value = + @"FabrikamDEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PortNumber", + Value = @"8000", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PhysicalPath", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 3981, + WorkflowActivityId = Guid.Parse("8160F49B-34C4-4860-AEC4-B9FC4CF81E9B"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"""__SourceFileFolder__"" ""__DestinationFileFolder__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SourceFileFolder", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "DestinationFileFolder", + Value = + @"c:\FabrikamRM\Backup\DEV", + IsParameter = + true + } + } + }); + + var fakeUserRepo = new FakeUserRepo + { + Groups = + new HashSet { new RMGroup { Id = 1, Name = "Ops Team" } }, + Users = + new HashSet + { + new RMUser + { + Id = 9006, + Name = "Daniel Mann" + } + } + }; + + var fakeDeployerToolRepo = new FakeDeployerToolRepo(); + + var scriptGenerator = new ScriptGenerator(fakeFs, componentRepo, fakeUserRepo, fakeDeployerToolRepo); + var path = @"C:\RMWorkflow"; + var releaseTemplate = + @"True__ReferenceID1TrueFalse__ReferenceID4__ReferenceID5TrueFalse__ReferenceID7__ReferenceID14__ReferenceID15__ReferenceID16__ReferenceID17__ReferenceID18__ReferenceID19TrueFalse__ReferenceID21__ReferenceID22TrueFalse__ReferenceID24__ReferenceID25__ReferenceID26__ReferenceID27__ReferenceID28__ReferenceID29TrueFalse"; + + var parsedTemplate = + (new RMDeploymentSequence { StageId = 100, WorkflowXaml = releaseTemplate }).ToReleaseTemplate(); + + scriptGenerator.ScriptGenerationNotification += PrintEvents; + await scriptGenerator.GenerateScriptAsync(parsedTemplate, path); + } + + [TestMethod] + public async Task FabrikamFiber_From_BKVM_With_Mix_Of_Rollback_Blocks() + { + var fakeFs = new FakeFileSystem(); + var componentRepo = new FakeComponentRepo(); + componentRepo.Components.Add( + new RMComponent + { + Id = 4010, + WorkflowActivityId = Guid.Parse("d25617e8-7fac-4637-a7cb-2ad4da996156"), + Command = "IISConfig.exe", + DeployerToolId = 2014, + FileExtensionFilter = string.Empty, + Arguments = @"-DropWebSite -sn ""__SiteName__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SiteName", + Value = + "FabrikamDEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4016, + WorkflowActivityId = Guid.Parse("00152F01-0651-4221-9835-3394F1739A2F"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"*.* ""__Installation Path__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "Installation Path", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 3981, + WorkflowActivityId = Guid.Parse("E7783400-9B05-4970-8777-6AB92C78D155"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"""__SourceFileFolder__"" ""__DestinationFileFolder__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SourceFileFolder", + Value = + @"c:\FabrikamRM\Backup\DEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "DestinationFileFolder", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4009, + WorkflowActivityId = Guid.Parse("A657D65D-F4D6-42F8-AA1F-7AC638496557"), + Command = "IISConfig.exe", + DeployerToolId = 2014, + FileExtensionFilter = string.Empty, + Arguments = + @"-CreateWebSite -sn ""__SiteName__"" -port __PortNumber__ -pd ""__PhysicalPath__"" -ap ""__AppPoolName__"" -enablepreload __IsPreloadEnabled__ -autostart __IsAutoStart__", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "AppPoolName", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsPreloadEnabled", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsAutoStart", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "SiteName", + Value = + @"FabrikamDEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PortNumber", + Value = @"8000", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PhysicalPath", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 4009, + WorkflowActivityId = Guid.Parse("51C262E8-D1FE-4FF8-ADD6-879C2718616B"), + Command = "IISConfig.exe", + DeployerToolId = 2014, + FileExtensionFilter = string.Empty, + Arguments = + @"-CreateWebSite -sn ""__SiteName__"" -port __PortNumber__ -pd ""__PhysicalPath__"" -ap ""__AppPoolName__"" -enablepreload __IsPreloadEnabled__ -autostart __IsAutoStart__", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "AppPoolName", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsPreloadEnabled", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "IsAutoStart", + Value = string.Empty, + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "SiteName", + Value = + @"FabrikamDEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PortNumber", + Value = @"8000", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "PhysicalPath", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 3981, + WorkflowActivityId = Guid.Parse("8160F49B-34C4-4860-AEC4-B9FC4CF81E9B"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"""__SourceFileFolder__"" ""__DestinationFileFolder__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SourceFileFolder", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "DestinationFileFolder", + Value = + @"c:\FabrikamRM\Backup\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 3981, + WorkflowActivityId = Guid.Parse("75cac0c3-d5ae-4629-b7be-e0acc5243610"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"""__SourceFileFolder__"" ""__DestinationFileFolder__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SourceFileFolder", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "DestinationFileFolder", + Value = + @"c:\FabrikamRM\Backup\DEV", + IsParameter = + true + } + } + }); + + componentRepo.Components.Add( + new RMComponent + { + Id = 3981, + WorkflowActivityId = Guid.Parse("8e02b95b-b61c-4db3-9a6c-a1231d1c1b63"), + Command = "irxcopy.cmd", + DeployerToolId = 12, + FileExtensionFilter = string.Empty, + Arguments = @"""__SourceFileFolder__"" ""__DestinationFileFolder__""", + VariableReplacementMethod = VariableReplacementMethod.OnlyInCommand, + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName = + "SourceFileFolder", + Value = + @"c:\FabrikamRM\WebSite\DEV", + IsParameter = + true + }, + new ConfigurationVariable + { + OriginalName = + "DestinationFileFolder", + Value = + @"c:\FabrikamRM\Backup\DEV", + IsParameter = + true + } + } + }); + + var fakeUserRepo = new FakeUserRepo(); + var fakeDeployerToolRepo = new FakeDeployerToolRepo(); + + var scriptGenerator = new ScriptGenerator(fakeFs, componentRepo, fakeUserRepo, fakeDeployerToolRepo); + var path = @"C:\RMWorkflow"; + var releaseTemplate = + @"True__ReferenceID1TrueFalse__ReferenceID4__ReferenceID5TrueFalse__ReferenceID8__ReferenceID9TrueFalse__ReferenceID12__ReferenceID13TrueFalse__ReferenceID15__ReferenceID22__ReferenceID23__ReferenceID24__ReferenceID25__ReferenceID26__ReferenceID27TrueFalseTrueFalse__ReferenceID29__ReferenceID30TrueFalse__ReferenceID32__ReferenceID33__ReferenceID34__ReferenceID35__ReferenceID36__ReferenceID37TrueFalse"; + + var parsedTemplate = + (new RMDeploymentSequence { StageId = 100, WorkflowXaml = releaseTemplate }).ToReleaseTemplate(); + scriptGenerator.ScriptGenerationNotification += PrintEvents; + await scriptGenerator.GenerateScriptAsync(parsedTemplate, path); + } + + [TestMethod] + public async Task Scripts_Are_Not_Generated_For_Containers() + { + // Arrange + var fs = new FakeFileSystem(); + var fakeComponentRepo = new FakeComponentRepo + { + Components = + new List + { + new RMComponent + { + Id = 3981, + WorkflowActivityId + = + Guid + .Parse( + "54b7b5f4-6a4d-4cec-b1e9-edae6bda0f20") + }, + new RMComponent + { + Id = 3981, + WorkflowActivityId + = + Guid + .Parse( + "9f2080c8-d438-431e-8cb5-f312c5c2b836") + } + } + }; + + var fakeUserRepo = new FakeUserRepo(); + var fakeDeployerToolRepo = new FakeDeployerToolRepo(); + + var gen = new ScriptGenerator(fs, fakeComponentRepo, fakeUserRepo, fakeDeployerToolRepo); + + var testXml = + @"True__ReferenceID0__ReferenceID1__ReferenceID3__ReferenceID4"; + var targetPath = @"C:\RMWorkflow\"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + await gen.GenerateScriptAsync(sequence, targetPath); + + // Assert + Assert.IsFalse(fs.Files.ContainsKey(@"C:\RMWorkflow\1_Sequence_Sequence+\ReleaseScript.ps1")); + } + + private static void PrintEvents(object o, GenerationEventArgs args) + { + Debug.WriteLine( + $"{args.GenerationEventType}: {args.Sequence} - {args.BlockType} {args.DisplayName} IsContainer: {args.IsContainer} IsEnabled: {args.IsEnabled}"); + Debug.Flush(); + } + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Tests.Unit/When_Parsing_Release_Template_Workflow_Actions.cs b/src/RMWorkflowMigrator.Tests.Unit/When_Parsing_Release_Template_Workflow_Actions.cs new file mode 100644 index 0000000..ec1a11a --- /dev/null +++ b/src/RMWorkflowMigrator.Tests.Unit/When_Parsing_Release_Template_Workflow_Actions.cs @@ -0,0 +1,236 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// -------------------------------------------------------------------------------------------------------------------- + + // ReSharper disable InconsistentNaming + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Tests.Unit +{ + using System; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + + using Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model; + using Microsoft.ALMRangers.RMWorkflowMigrator.Parser; + using Microsoft.ALMRangers.RMWorkflowMigrator.Parser.Model; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [ExcludeFromCodeCoverage] + [TestClass] + public class When_Parsing_Release_Template_Workflow_Actions + { + [TestMethod] + public void Can_Detect_A_Disabled_Action() + { + // Arrange + var testXml = + @"True__ReferenceID0TrueFalse__ReferenceID1TrueFalse__ReferenceID5__ReferenceID6__ReferenceID7TrueFalse__ReferenceID8TrueFalse"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + + // Assert + var servers = sequence.Containers.ToList(); + var actions = servers[0].SubItems.Cast().ToList(); + Assert.IsFalse(actions[2].Enabled); + } + + [TestMethod] + public void Can_Parse_A_Manual_Intervention_With_A_Group_Notified() + { + // Arrange + var testXml = + @"True__ReferenceID2__ReferenceID3TrueFalseFalseFalse__ReferenceID5__ReferenceID6TrueFalse__ReferenceID8TrueFalse__ReferenceID15__ReferenceID16__ReferenceID17__ReferenceID18__ReferenceID19__ReferenceID20TrueFalse__ReferenceID22__ReferenceID24TrueFalse"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + + var containers = sequence.Containers.ToList(); + var actions = containers[0].SubItems.ToList(); + var manualIntervention = + actions.Where(a => a.ItemType == BlockType.ManualIntervention).Cast().First(); + + // Assert + Assert.IsNotNull(manualIntervention); + Assert.AreEqual("An additional manual intervention", manualIntervention.DisplayName); + Assert.AreEqual(2, manualIntervention.Sequence); + Assert.AreEqual("This is also text. I love text.", manualIntervention.Text); + Assert.AreEqual(5, manualIntervention.UserId); + Assert.AreEqual(true, manualIntervention.IsGroup); + } + + [TestMethod] + public void Can_Parse_A_Manual_Intervention_With_A_User_Notified() + { + // Arrange + var testXml = + @"True__ReferenceID2__ReferenceID3TrueFalseFalseFalse__ReferenceID5__ReferenceID6TrueFalse__ReferenceID8TrueFalse__ReferenceID15__ReferenceID16__ReferenceID17__ReferenceID18__ReferenceID19__ReferenceID20TrueFalse__ReferenceID22__ReferenceID24TrueFalse"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + + var containers = sequence.Containers.ToList(); + var actions = containers[0].SubItems.ToList(); + var manualIntervention = + actions.Where(a => a.ItemType == BlockType.ManualIntervention).Cast().First(); + + // Assert + Assert.IsNotNull(manualIntervention); + Assert.AreEqual("My Wonderful Manual Intervention", manualIntervention.DisplayName); + Assert.AreEqual(2, manualIntervention.Sequence); + Assert.AreEqual("Text text text\n\nMore text\n\nLots more text!!", manualIntervention.Text); + Assert.AreEqual(9052, manualIntervention.UserId); + Assert.AreEqual(false, manualIntervention.IsGroup); + } + + [TestMethod] + public void Can_Parse_A_Rollback_Always_Block() + { + // Arrange + var testXml = + @"True__ReferenceID0TrueFalse__ReferenceID1TrueFalse__ReferenceID5__ReferenceID6__ReferenceID7TrueFalse__ReferenceID8TrueFalse"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + + // Assert + var servers = sequence.Containers.ToList(); + var actions = servers[0].SubItems.ToList(); + var rollback = actions[3] as RollbackBlock; + Assert.IsNotNull(rollback); + Assert.AreEqual(1, rollback.SubItems.Count()); + Assert.AreEqual("Start Application Pool", rollback.SubItems.First().DisplayName); + Assert.AreEqual(3966, rollback.SubItems.Cast().First().ComponentId); + } + + [TestMethod] + public void Can_Parse_A_Rollback_Block() + { + // Arrange + var testXml = + @"True__ReferenceID0TrueFalse__ReferenceID1TrueFalse__ReferenceID5__ReferenceID6__ReferenceID7TrueFalse__ReferenceID8TrueFalse"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + + // Assert + var servers = sequence.Containers.ToList(); + var actions = servers[0].SubItems.ToList(); + var rollback = actions[1] as RollbackBlock; + Assert.IsNotNull(rollback); + Assert.AreEqual(1, rollback.SubItems.Count()); + Assert.AreEqual("Start Web Site", rollback.SubItems.First().DisplayName); + Assert.AreEqual(3962, rollback.SubItems.Cast().First().ComponentId); + } + + [TestMethod] + public void Can_Parse_An_Action_In_A_Server_Element() + { + // Arrange + var testXml = + @"True__ReferenceID0__ReferenceID1"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + + // Assert + var servers = sequence.Containers.ToList(); + var actions = servers[0].SubItems.Cast().ToList(); + Assert.AreEqual(1, servers.Count); + Assert.AreEqual(1, actions.Count); + + Assert.AreEqual("Copy File or Folder", actions[0].DisplayName); + Assert.AreEqual(3981, actions[0].ComponentId); + } + + [TestMethod] + public void Can_Parse_An_Action_In_A_Server_Tag_Element() + { + // Arrange + var testXml = + @"True__ReferenceID2__ReferenceID3"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + + // Assert + var servers = sequence.Containers.ToList(); + var actions = servers[0].SubItems.Cast().ToList(); + Assert.AreEqual(1, servers.Count); + Assert.AreEqual(1, actions.Count); + + Assert.AreEqual("Copy File or Folder", actions[0].DisplayName); + Assert.AreEqual(Guid.Parse("1f899e97-1e4f-460d-bca8-23b4c8222199"), actions[0].WorkflowActivityId); + Assert.AreEqual(3981, actions[0].ComponentId); + Assert.IsTrue(actions[0].Enabled); + } + + [TestMethod] + public void Event_Fires_When_Action_Is_Parsed() + { + // Arrange + var testXml = + @"True__ReferenceID0__ReferenceID1"; + var eventFired = false; + + ActionParser.ActionParsed += (sender, args) => + { + Assert.AreEqual("Copy File or Folder", args.DisplayName); + Assert.AreEqual(BlockType.Action, args.ItemType); + eventFired = true; + }; + + // Act + (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + Assert.IsTrue(eventFired); + } + + [TestMethod] + public void Event_Fires_When_Manual_Intervention_Is_Parsed() + { + // Arrange + var testXml = + @"True"; + var eventFired = false; + + ActionParser.ActionParsed += (sender, args) => + { + Assert.AreEqual("My Wonderful Manual Intervention", args.DisplayName); + Assert.AreEqual(BlockType.ManualIntervention, args.ItemType); + eventFired = true; + }; + + // Act + (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + Assert.IsTrue(eventFired); + } + + [TestMethod] + public void Event_Fires_When_Rollback_Is_Parsed() + { + // Arrange + var testXml = + @"TrueFalseFalse"; + var eventFired = false; + + ActionParser.ActionParsed += (sender, args) => + { + Assert.AreEqual("Rollback", args.DisplayName); + Assert.AreEqual(BlockType.Rollback, args.ItemType); + eventFired = true; + }; + + // Act + (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + Assert.IsTrue(eventFired); + } + + [TestCleanup] + public void TestCleanup() + { + ActionParser.ActionParsed = null; + } + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Tests.Unit/When_Parsing_Release_Template_Workflow_Deployment_Sequences.cs b/src/RMWorkflowMigrator.Tests.Unit/When_Parsing_Release_Template_Workflow_Deployment_Sequences.cs new file mode 100644 index 0000000..17021eb --- /dev/null +++ b/src/RMWorkflowMigrator.Tests.Unit/When_Parsing_Release_Template_Workflow_Deployment_Sequences.cs @@ -0,0 +1,236 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// -------------------------------------------------------------------------------------------------------------------- + + // ReSharper disable InconsistentNaming + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Tests.Unit +{ + using System.Diagnostics.CodeAnalysis; + using System.Linq; + + using Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model; + using Microsoft.ALMRangers.RMWorkflowMigrator.Parser; + using Microsoft.ALMRangers.RMWorkflowMigrator.Parser.Model; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [ExcludeFromCodeCoverage] + [TestClass] + public class When_Parsing_Release_Template_Workflow_Deployment_Sequences + { + [TestMethod] + public void Can_Parse_A_Deployment_Sequence_With_An_Empty_Inner_Sequence() + { + // Arrange + var testXml = + @"True"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + + // Assert + var container = sequence.Containers.ToList(); + + Assert.AreEqual(1, container.Count); + Assert.AreEqual("Test Sequence", container.First().DisplayName); + } + + [TestMethod] + public void Can_Parse_A_Deployment_Sequence_With_Multiple_Empty_Inner_Sequences() + { + // Arrange + var testXml = + @"True"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + + // Assert + Assert.IsNotNull(sequence.Containers); + Assert.AreEqual(2, sequence.Containers.Count()); + + Assert.AreEqual("Test Sequence", sequence.Containers.First().DisplayName); + Assert.AreEqual(BlockType.Sequence, sequence.Containers.First().ItemType); + Assert.AreEqual("Sequence", sequence.Containers.Last().DisplayName); + } + + [TestMethod] + public void Can_Parse_A_Deployment_Sequence_With_Multiple_Parallel_Blocks() + { + // Arrange + var testXml = + @"True"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + + // Assert + var containers = sequence.Containers.ToList(); + + Assert.AreEqual(2, containers.Count); + Assert.AreEqual("Parallel", containers[0].DisplayName); + Assert.AreEqual("Parallel 2", containers[1].DisplayName); + } + + [TestMethod] + public void Can_Parse_A_Deployment_Sequence_With_Multiple_Server_Tags() + { + // Arrange + var testXml = + @"True__ReferenceID0__ReferenceID1"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + + // Assert + Assert.IsNotNull(sequence.Containers); + Assert.AreEqual(2, sequence.Containers.Count()); + Assert.AreEqual("placeholder", sequence.Containers.First().DisplayName); + Assert.AreEqual("Bar", sequence.Containers.Last().DisplayName); + } + + [TestMethod] + public void Can_Parse_A_Deployment_Sequence_With_Multiple_Single_Server_Containers() + { + // Arrange + var testXml = + @"True__ReferenceID0__ReferenceID1__ReferenceID2__ReferenceID3__ReferenceID4__ReferenceID5__ReferenceID6__ReferenceID7__ReferenceID8__ReferenceID9__ReferenceID10__ReferenceID11__ReferenceID12__ReferenceID14__ReferenceID15__ReferenceID16__ReferenceID17__ReferenceID18__ReferenceID19__ReferenceID20__ReferenceID21__ReferenceID22__ReferenceID23__ReferenceID24__ReferenceID25__ReferenceID26"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + + // Assert + Assert.IsNotNull(sequence.Containers); + Assert.AreEqual(2, sequence.Containers.Count()); + Assert.AreEqual("lab-dmann", sequence.Containers.First().DisplayName); + Assert.AreEqual("lab-dmann2", sequence.Containers.Last().DisplayName); + } + + [TestMethod] + public void Can_Parse_A_Deployment_Sequence_With_One_Single_Server_Container() + { + // Arrange + var testXml = + @"True__ReferenceID13__ReferenceID14__ReferenceID15__ReferenceID16__ReferenceID17__ReferenceID18__ReferenceID19__ReferenceID20__ReferenceID21__ReferenceID22__ReferenceID23__ReferenceID24__ReferenceID25"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + + // Assert + Assert.IsNotNull(sequence.Containers); + Assert.AreEqual(1, sequence.Containers.Count()); + Assert.AreEqual("lab-dmann", sequence.Containers.First().DisplayName); + } + + [TestMethod] + public void Can_Parse_A_Deployment_Sequence_With_One_Single_Server_Container_Followed_By_One_Sequence() + { + // Arrange + var testXml = + @"True__ReferenceID13__ReferenceID14__ReferenceID15__ReferenceID16__ReferenceID17__ReferenceID18__ReferenceID19__ReferenceID20__ReferenceID21__ReferenceID22__ReferenceID23__ReferenceID24__ReferenceID25"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + + // Assert + Assert.IsNotNull(sequence.Containers); + Assert.AreEqual(2, sequence.Containers.Count()); + } + + [TestMethod] + public void Can_Parse_A_Deployment_Sequence_With_Single_Parallel_Block() + { + // Arrange + var testXml = + @"True"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + + // Assert + var containers = sequence.Containers.ToList(); + + Assert.AreEqual(1, containers.Count); + Assert.AreEqual("Parallel placeholder", containers[0].DisplayName); + } + + [TestMethod] + public void Can_Parse_A_Deployment_Sequence_With_Single_Server_Tag() + { + // Arrange + var testXml = + @"True__ReferenceID1"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + + // Assert + Assert.AreEqual(1, sequence.Containers.Count()); + Assert.AreEqual("placeholder", sequence.Containers.First().DisplayName); + } + + [TestMethod] + public void Can_Parse_A_Deployment_Sequence_With_Single_Server_Tag_And_Sequence_And_Server() + { + // Arrange + var testXml = + @"True__ReferenceID0__ReferenceID2"; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + + // Assert + var containers = sequence.Containers.ToList(); + Assert.AreEqual(3, containers.Count()); + Assert.IsTrue(containers.Any(x => x.ItemType == BlockType.Server)); + Assert.IsTrue(containers.Any(x => x.ItemType == BlockType.ServerTag)); + Assert.IsTrue(containers.Any(x => x.ItemType == BlockType.Sequence)); + } + + [TestMethod] + public void Can_Parse_An_Empty_Deployment_Sequence() + { + // Arrange + var testXml = + @""; + + // Act + var sequence = (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + + // Assert + Assert.IsNotNull(sequence); + Assert.AreEqual("Deployment Sequence", sequence.DisplayName); + } + + [TestMethod] + public void Event_Fires_When_A_Container_Is_Parsed() + { + // Arrange + var testXml = + @"True"; + + var eventFired = false; + + ContainerParser.ContainerParsed += (sender, args) => + { + Assert.AreEqual("Parallel placeholder", args.DisplayName); + Assert.AreEqual(BlockType.Parallel, args.ItemType); + eventFired = true; + }; + + // Act + (new RMDeploymentSequence { StageId = 100, WorkflowXaml = testXml }).ToReleaseTemplate(); + Assert.IsTrue(eventFired); + } + + [TestCleanup] + public void TestCleanup() + { + ContainerParser.ContainerParsed = null; + } + } +} + +// ReSharper restore InconsistentNaming \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Tests.Unit/When_Resolving_Unique_Script_Parameters.cs b/src/RMWorkflowMigrator.Tests.Unit/When_Resolving_Unique_Script_Parameters.cs new file mode 100644 index 0000000..7645ad1 --- /dev/null +++ b/src/RMWorkflowMigrator.Tests.Unit/When_Resolving_Unique_Script_Parameters.cs @@ -0,0 +1,355 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace Microsoft.ALMRangers.RMWorkflowMigrator.Tests.Unit +{ + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using System.Linq; + + using Microsoft.ALMRangers.RMWorkflowMigrator.DataAccess.Model; + using Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell; + using Microsoft.ALMRangers.RMWorkflowMigrator.Generator.PowerShell.Model; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + // ReSharper disable InconsistentNaming + [ExcludeFromCodeCoverage] + [TestClass] + public class When_Resolving_Unique_Script_Parameters + { + [TestMethod] + public void Configuration_Variable_Parameter_Names_Are_Remapped_To_Remove_Invalid_Characters() + { + // Arrange + var singleAction = new List + { + new ScriptAction + { + Arguments = + @"-Param __Hello World ~`!@#$%^&*()_+=|\__", + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + IsParameter + = + true, + OriginalName + = + @"Hello World ~`!@#$%^&*()_+=|\", + Value + = + "placeholder" + } + } + } + }; + + // Act + var results = UniquePropertyResolver.ResolveProperties(singleAction); + + // Assert + Assert.IsTrue( + results.First().ConfigurationVariables.All(r => r.OriginalName == @"Hello World ~`!@#$%^&*()_+=|\")); + Assert.IsTrue(results.First().ConfigurationVariables.All(r => r.RemappedName == "HelloWorld_")); + } + + [TestMethod] + public void Configuration_Variable_Replacement_Token_Names_Are_Remapped_To_Remove_Invalid_Characters() + { + // Arrange + var singleAction = new List + { + new ScriptAction + { + Arguments = + @"-Param __Hello World ~`!@#$%^&*()_+=|\__", + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + IsParameter + = + false, + OriginalName + = + @"Goodbye World ~`!@#$%^&*()_+=|\", + Value + = + "placeholder" + } + } + } + }; + + // Act + var results = UniquePropertyResolver.ResolveProperties(singleAction); + + // Assert + Assert.IsTrue( + results.First().ConfigurationVariables.All(r => r.OriginalName == @"Goodbye World ~`!@#$%^&*()_+=|\")); + Assert.IsTrue(results.First().ConfigurationVariables.All(r => r.RemappedName == "GoodbyeWorld_")); + } + + [TestMethod] + public void Invalid_PowerShell_Variable_Characters_Are_Removed_From_Command_Parameters() + { + // Arrange + var singleAction = new List + { + new ScriptAction + { + Arguments = + @"-Param __Hello World ~`!@#$%^&*()_+=|\__" + } + }; + + // Act + var results = UniquePropertyResolver.ResolveProperties(singleAction); + + // Assert + Assert.AreEqual("-Param $HelloWorld_", results.First().Arguments); + } + + [TestMethod] + public void Many_Identical_And_Nonidentical_Parameters_And_Config_Values_Are_Made_Unique() + { + // Arrange + var singleAction = new List + { + new ScriptAction + { + Arguments = + @"-Param __Hello World__ -AnotherParam __Baz!__", + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName + = + "Hello World", + Value + = + "placeholder" + }, + new ConfigurationVariable + { + OriginalName + = + "Config Token", + Value + = + "placeholder" + }, + new ConfigurationVariable + { + OriginalName + = + "Another Config Token@", + Value + = + "placeholder" + }, + new ConfigurationVariable + { + OriginalName + = + "Baz!", + Value + = + "A Value" + } + } + }, + new ScriptAction + { + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName + = + "Hello World", + Value + = + "Bar" + }, + new ConfigurationVariable + { + OriginalName + = + "Another Config Token", + Value + = + "Baz" + } + } + }, + new ScriptAction + { + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName + = + "Hello World", + Value + = + "Bar" + }, + new ConfigurationVariable + { + OriginalName + = + "Config Token", + Value + = + "Definitely Unique Value" + }, + new ConfigurationVariable + { + OriginalName + = + "Another Config Token", + Value + = + "Also Definitely Unique Value" + } + } + } + }; + + // Act + var results = UniquePropertyResolver.ResolveProperties(singleAction).ToList(); + var action1 = results[0]; + var action2 = results[1]; + var action3 = results[2]; + + // Assert + Assert.AreEqual("-Param $HelloWorld -AnotherParam $Baz", action1.Arguments); + + Assert.IsTrue(action1.ConfigurationVariables.Any(cv => cv.RemappedName == "HelloWorld")); + Assert.IsTrue(action1.ConfigurationVariables.Any(cv => cv.RemappedName == "ConfigToken")); + Assert.IsTrue(action1.ConfigurationVariables.Any(cv => cv.RemappedName == "AnotherConfigToken")); + Assert.IsTrue(action1.ConfigurationVariables.Any(cv => cv.RemappedName == "Baz")); + + Assert.IsTrue(action2.ConfigurationVariables.Any(cv => cv.RemappedName == "HelloWorld2")); + Assert.IsTrue(action2.ConfigurationVariables.Any(cv => cv.RemappedName == "AnotherConfigToken3")); + + Assert.IsTrue(action3.ConfigurationVariables.Any(cv => cv.RemappedName == "HelloWorld")); + Assert.IsTrue(action3.ConfigurationVariables.Any(cv => cv.RemappedName == "ConfigToken4")); + Assert.IsTrue(action3.ConfigurationVariables.Any(cv => cv.RemappedName == "AnotherConfigToken5")); + } + + [TestMethod] + public void Two_Identical_Parameters_That_Take_The_Different_Values_Are_Made_Unique() + { + // Arrange + var singleAction = new List + { + new ScriptAction + { + Arguments = @"-Param __Hello World__", + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName + = + "Hello World", + Value + = + "placeholder" + } + } + }, + new ScriptAction + { + Arguments = @"-Param __Hello World__", + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName + = + "Hello World", + Value + = + "Bar" + } + } + } + }; + + // Act + var results = UniquePropertyResolver.ResolveProperties(singleAction).ToList(); + + // Assert + Assert.AreEqual("-Param $HelloWorld", results[0].Arguments); + Assert.IsTrue(results[0].ConfigurationVariables.All(cv => cv.RemappedName == "HelloWorld")); + Assert.AreEqual("-Param $HelloWorld2", results[1].Arguments); + Assert.IsTrue(results[1].ConfigurationVariables.All(cv => cv.RemappedName == "HelloWorld2")); + } + + [TestMethod] + public void Two_Parameters_That_Take_The_Same_Value_Are_Untouched() + { + // Arrange + var singleAction = new List + { + new ScriptAction + { + Arguments = @"-Param __Hello World__", + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName + = + "Hello World", + Value + = + "placeholder" + } + } + }, + new ScriptAction + { + Arguments = @"-Param __Hello World__", + ConfigurationVariables = + new List + { + new ConfigurationVariable + { + OriginalName + = + "Hello World", + Value + = + "placeholder" + } + } + } + }; + + // Act + var results = UniquePropertyResolver.ResolveProperties(singleAction).ToList(); + + // Assert + Assert.AreEqual("-Param $HelloWorld", results[0].Arguments); + Assert.AreEqual("-Param $HelloWorld", results[1].Arguments); + } + } +} \ No newline at end of file diff --git a/src/RMWorkflowMigrator.Tests.Unit/packages.config b/src/RMWorkflowMigrator.Tests.Unit/packages.config new file mode 100644 index 0000000..112d362 --- /dev/null +++ b/src/RMWorkflowMigrator.Tests.Unit/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/RMWorkflowMigrator.sln b/src/RMWorkflowMigrator.sln new file mode 100644 index 0000000..5ca6830 --- /dev/null +++ b/src/RMWorkflowMigrator.sln @@ -0,0 +1,66 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.23107.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RMWorkflowMigrator.DataAccess", "RMWorkflowMigrator.DataAccess\RMWorkflowMigrator.DataAccess.csproj", "{3CB3250E-E8F8-4164-BD1D-B8E910916FAB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RMWorkflowMigrator.Parser", "RMWorkflowMigrator.Parser\RMWorkflowMigrator.Parser.csproj", "{7E2DC042-DFB7-40DB-AF1D-D28378702B30}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RMWorkflowMigrator.Tests.Unit", "RMWorkflowMigrator.Tests.Unit\RMWorkflowMigrator.Tests.Unit.csproj", "{D05976D4-C250-4787-8ACE-51594E42CC47}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RMWorkflowMigrator.Parser.Model", "RMWorkflowMigrator.Parser.Model\RMWorkflowMigrator.Parser.Model.csproj", "{DAB5CC84-4E29-43E0-9BA9-81D377EA08EE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RMWorkflowMigrator.Generator.PowerShell", "RMWorkflowMigrator.Generator.PowerShell\RMWorkflowMigrator.Generator.PowerShell.csproj", "{B5C99627-E15F-4D33-ADF9-22012953A47B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RMWorkflowMigrator.CmdLine", "RMWorkflowMigrator.CmdLine\RMWorkflowMigrator.CmdLine.csproj", "{E5EC1C9D-B621-47C5-8D91-0680EBB91FE0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{36A6F68A-AEC2-40BE-BC09-1FFE10CDCB01}" + ProjectSection(SolutionItems) = preProject + .nuget\NuGet.config = .nuget\NuGet.config + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{816DF292-858F-4ADB-A38E-0CAB11582CA3}" + ProjectSection(SolutionItems) = preProject + ALMRangersCodeAnalysisDictionary.xml = ALMRangersCodeAnalysisDictionary.xml + ALMRangersCommonAssemblyInfo.cs = ALMRangersCommonAssemblyInfo.cs + ALMRangersRuleSet.ruleset = ALMRangersRuleSet.ruleset + RMWorkflowMigrator.sln.DotSettings = RMWorkflowMigrator.sln.DotSettings + Settings.StyleCop = Settings.StyleCop + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3CB3250E-E8F8-4164-BD1D-B8E910916FAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3CB3250E-E8F8-4164-BD1D-B8E910916FAB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3CB3250E-E8F8-4164-BD1D-B8E910916FAB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3CB3250E-E8F8-4164-BD1D-B8E910916FAB}.Release|Any CPU.Build.0 = Release|Any CPU + {7E2DC042-DFB7-40DB-AF1D-D28378702B30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7E2DC042-DFB7-40DB-AF1D-D28378702B30}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7E2DC042-DFB7-40DB-AF1D-D28378702B30}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7E2DC042-DFB7-40DB-AF1D-D28378702B30}.Release|Any CPU.Build.0 = Release|Any CPU + {D05976D4-C250-4787-8ACE-51594E42CC47}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D05976D4-C250-4787-8ACE-51594E42CC47}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D05976D4-C250-4787-8ACE-51594E42CC47}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D05976D4-C250-4787-8ACE-51594E42CC47}.Release|Any CPU.Build.0 = Release|Any CPU + {DAB5CC84-4E29-43E0-9BA9-81D377EA08EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DAB5CC84-4E29-43E0-9BA9-81D377EA08EE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DAB5CC84-4E29-43E0-9BA9-81D377EA08EE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DAB5CC84-4E29-43E0-9BA9-81D377EA08EE}.Release|Any CPU.Build.0 = Release|Any CPU + {B5C99627-E15F-4D33-ADF9-22012953A47B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B5C99627-E15F-4D33-ADF9-22012953A47B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B5C99627-E15F-4D33-ADF9-22012953A47B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B5C99627-E15F-4D33-ADF9-22012953A47B}.Release|Any CPU.Build.0 = Release|Any CPU + {E5EC1C9D-B621-47C5-8D91-0680EBB91FE0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E5EC1C9D-B621-47C5-8D91-0680EBB91FE0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E5EC1C9D-B621-47C5-8D91-0680EBB91FE0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E5EC1C9D-B621-47C5-8D91-0680EBB91FE0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/RMWorkflowMigrator.sln.DotSettings b/src/RMWorkflowMigrator.sln.DotSettings new file mode 100644 index 0000000..3f5bbbe --- /dev/null +++ b/src/RMWorkflowMigrator.sln.DotSettings @@ -0,0 +1,6 @@ + + C:\Users\vgusarov\Source\Workspaces\ALM\vsarActive\vsarRM\main\code\POC\RMWorkflowMigrator\RMWorkflowMigrator.sln + ..\POC\RMWorkflowMigrator\RMWorkflowMigrator.sln + True + True + 1 \ No newline at end of file diff --git a/src/Settings.StyleCop b/src/Settings.StyleCop new file mode 100644 index 0000000..9e01fea --- /dev/null +++ b/src/Settings.StyleCop @@ -0,0 +1,81 @@ + + + + + False + + + + + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + Microsoft Corporation + Copyright Microsoft Corporation. All Rights Reserved. This code released under the terms of the MIT License (MIT, https://github.com/ALM-Rangers/Migrate-assets-from-RM-server-to-VSO/blob/master/License.txt). This is sample code only, do not use in production environments. + + + + + + ip + + + + + + + + False + + + + + False + + + + + + + \ No newline at end of file