diff --git a/README.md b/README.md index f9e01efbb..5dbc262e0 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ For the most part we support moving data between ((Azure DevOps Server | Team Fo - [Configuration Reference](https://nkdagility.com/learn/azure-devops-migration-tools/Reference/) - [Community Support](https://github.com/nkdAgility/azure-devops-migration-tools/discussions) - [Commercial Support](https://nkdagility.com/capabilities/azure-devops-migration-services/) +- [Change Log](https://nkdagility.com/learn/azure-devops-migration-tools/change-log/) The documentation for the preview is on [Preview](https://nkdagility.com/docs/azure-devops-migration-tools/preview/)] @@ -88,9 +89,7 @@ The documentation for the preview is on [Preview](https://nkdagility.com/docs/az | Migration Run Total | **19bn Seconds** | Thats **316m hours** or **13m days** of run time in the last 30 days. | | Average Work item Migration Time | **22s** | Work Item (includes all revisions, links, and attachments for the work item) | -Exceptions shipped to Application Insights and [Elmah.io](https://elmah.io) for analysis and improvement. - - +This tool uses OpenTelemetery to collect metrics and logs, and Application Insights to store and analyse them. Exceptions are also sent to [Elmah.io](https://elmah.io) for analysis and improvement. ## Advanced tools @@ -117,22 +116,3 @@ We use these tools with our customers, and for fun, to do real world migrations - Consult with your internal folks who need help and guidance in running the tooling. - Make changes to the tool to support your needs; all additions are committed to the main repo. - Run the migration for you:- you would need to pay for the hours that we would spend baby-sitting the running migrations - -## Change Log - -- [v16.0.0](https://github.com/nkdAgility/azure-devops-migration-tools/releases/tag/v16.0.0) - Enhanced configuration with support for command line, and environmental variable overrides. -- [v15.1.7](https://github.com/nkdAgility/azure-devops-migration-tools/releases/tag/v15.1.7) - The latest release brings a host of enhancements and fixes designed to improve user experience and configuration options. Noteworthy features include a new GitHub Actions workflow for automatic updates to pull request titles, enhanced management of area and iteration paths using regex mapping, and a more streamlined query format for migration configurations. Users can now enjoy greater flexibility in configuring attachment processing, including options for export paths and size limits. Additionally, updates to authentication methods and improved logging for user retrieval processes have been implemented. The release also addresses various bugs and makes adjustments to enhance overall functionality. -- [v14.4.7](https://github.com/nkdAgility/azure-devops-migration-tools/releases/tag/v14.4.7) - The latest major release brings a host of user-focused enhancements and improvements. Key changes include the adoption of Winget as the primary installation method, making it easier for users to get started. The main executable has been renamed to `devopsmigration.exe`, and new configuration options enhance customization capabilities, including parallel builds and test case timeouts. The command for initializing configurations has been updated for greater flexibility, and logging improvements provide better insights during migration operations. Subsequent updates have refined version detection, improved command line arguments, and introduced new configuration files to prevent blank issues. Enhanced logging and error handling further improve user experience, while package upgrades and better handling of specific fields streamline migration processes. Overall, these updates aim to enhance functionality, usability, and reliability for users. -- [v13.2.1](https://github.com/nkdAgility/azure-devops-migration-tools/releases/tag/v13.2.1) - The latest updates bring a range of enhancements and new features aimed at improving user experience and functionality. A key addition is the `WorkItemMigrationContext` processor, which facilitates the migration of work items, including their history and attachments, between Azure DevOps instances. Users will find clearer documentation and a new configuration file to simplify work item type and field mappings. The introduction of the `ExportUsersForMapping` feature allows for easy JSON file exports for field mapping, while security is bolstered with an updated authentication mode. Users can now disable telemetry collection during migration, and various improvements have been made to migration behavior and configuration settings, enhancing the overall robustness and integrity of the migration tools. -- [v12.8.10](https://github.com/nkdAgility/azure-devops-migration-tools/releases/tag/v12.8.10) - The latest major release brings a host of enhancements designed to improve user experience and streamline migration processes. New configuration options for migration processors offer greater flexibility, allowing users to define custom remapping rules for area and iteration paths. Significant improvements in field mapping configurations and enhanced documentation provide clearer guidance for users. The introduction of features like case-insensitive matching for regular expressions and new parameters for work item migration enhances functionality. Additionally, updates to logging, error handling, and overall documentation structure ensure a more robust and user-friendly experience. Various bug fixes further contribute to the reliability and clarity of the migration tools, making the overall process smoother for users. -- [v11.9.55](https://github.com/nkdAgility/azure-devops-migration-tools/releases/tag/v11.9.55) - The latest major release introduces a variety of impactful changes designed to enhance user experience and streamline migration processes. Key features include a rebranding of the project to "MigrationTools," improved configuration options, and enhanced error handling for migration operations. Users can now limit revisions during work item migrations, customize field retrieval, and benefit from new logging capabilities for better traceability. The introduction of new interfaces and methods, along with refined documentation, supports improved work item management and configuration flexibility. Overall, these updates aim to provide a more efficient, user-friendly migration experience while addressing previous bugs and enhancing system performance. -- [v10.2.13](https://github.com/nkdAgility/azure-devops-migration-tools/releases/tag/v10.2.13) - The latest updates to the Migration Tools suite introduce a range of impactful enhancements for users. New projects, such as "MigrationTools.Sinks.AzureDevOps," have been added, along with a revamped console UI for improved Azure DevOps integration. Configuration management has been enhanced, allowing for easier JSON file loading and new telemetry settings. The migration engine has been optimized for better work item handling, and logging has been clarified. Users will need to update their configuration files due to a namespace change and new parameters for work item migration. Subsequent updates further simplify the configuration process, improve field mapping options, and enhance documentation for migrating test artifacts. Overall, these changes provide users with greater flexibility, control, and usability in their migration tasks. -- [v9.3.1](https://github.com/nkdAgility/azure-devops-migration-tools/releases/tag/v9.3.1) - The latest major release brings a host of user-focused enhancements and improvements. Key features include multi-language support for Azure DevOps migrations, allowing for greater flexibility in handling different language versions. Users will benefit from improved configuration documentation, which now includes new fields for language mapping of Area and Iteration paths. Subsequent updates have introduced customizable field mappings, conditional logic for excluding specific work item types, and enhanced error handling for better troubleshooting. Additionally, logging capabilities have been significantly upgraded, providing more structured output and insights into application performance. Overall, these changes aim to streamline the migration process and improve user experience. -- [v8.9.10](https://github.com/nkdAgility/azure-devops-migration-tools/releases/tag/v8.9.10) - The latest major release introduces a range of impactful enhancements and features designed to improve user experience and streamline migration processes. Users can now take advantage of enhanced configuration options, including custom paths for configuration files and new modes for the `init` command. The migration process has been significantly refined with improved error handling, better logging, and new parameters for managing attachments and links. Notable features include the ability to sync changes post-migration, retry failed work item saves, and customize attachment handling. Additionally, the rebranding of the tool ensures users have access to accurate documentation and resources. Overall, these updates focus on providing greater control, efficiency, and clarity throughout the migration experience. -- [7.5.74](https://github.com/nkdAgility/azure-devops-migration-tools/releases/tag/7.5.74) - The latest major release brings a host of user-focused enhancements and features designed to improve performance and usability. Key updates include a framework upgrade that boosts application performance and compatibility, alongside dependency updates for improved functionality and security. New configuration options allow for greater flexibility during data migration, including filtering elements by tags and replaying work item revisions. Enhancements to error handling and logging, as well as improvements in attachment management, contribute to a more reliable user experience. Additionally, the introduction of online status checks for version updates ensures users stay informed about the latest changes while connected to the internet. -- [6.3.1](https://github.com/nkdAgility/azure-devops-migration-tools/releases/tag/6.3.1) - The latest major release brings a host of impactful changes for users. A key highlight is the rebranding of the command-line tool to `vstssyncmigrator`, accompanied by updated documentation to assist with the new command structure. Enhancements to attachment export and import migration contexts improve ID formatting, while the restructuring of project organization may necessitate updates to project references. Users will also benefit from improved global configuration documentation, with various processors now enabled by default for immediate functionality. New features include the `WorkItemQueryMigrationContext`, allowing for selective migration of work item queries, and the option to prefix project names in folder paths for better organization. Enhanced logging and an updated FAQ section further support users in managing their migration processes effectively. -- [5.3.2](https://github.com/nkdAgility/azure-devops-migration-tools/releases/tag/5.3.2) - The latest major release brings a host of impactful changes designed to enhance user experience and functionality. Key updates include a rebranding to "VSTS Sync Migration Tools" and a simplified command name for installation and uninstallation. Users can now benefit from the new `MultiValueConditionalMapConfig` class, which allows for more complex field mapping configurations. Version 5.1 introduces customizable user preferences and improved command-line functionality, while 5.3 enhances the migration process for test plans with a new method for handling test cases, updates to installation scripts for better package verification, and optimizations in field merging. Comprehensive new documentation supports these changes, ensuring users can easily adapt to the updated features and configurations. -- [4.4.0](https://github.com/nkdAgility/azure-devops-migration-tools/releases/tag/4.4.0) - The latest major release introduces a range of impactful enhancements aimed at improving user experience and flexibility during work item migration. A key feature is the new configuration option, `PrefixProjectToNodes`, which allows users to customize the prefixing of project names to area and iteration paths, as well as nodes, enhancing project structure management. The migration logic has been updated to support these options, streamlining the migration process. Additionally, users will benefit from improved documentation, including clearer installation instructions and new JSON configuration files. The release also includes an uninstall script for easier tool management and enhancements to caching and field exclusion during migrations, further refining the overall functionality. -- [3.6.0.1](https://github.com/nkdAgility/azure-devops-migration-tools/releases/tag/3.6.0.1) - The latest updates bring a host of enhancements designed to improve user experience and functionality. Key features include new configuration files for the VSTS Data Bulk Editor, allowing for tailored migration processes, and the introduction of classes for managing team settings. Users can now specify additional query criteria for attachment exports and customize employee picture URL formats. The migration process has been streamlined with improved telemetry tracking and error handling, while new documentation provides valuable context and guidance. Significant improvements to the TfsWitMigrator tool enhance work item tagging flexibility, and updates to the migration context for test plans and variables offer greater control during migrations. Overall, these changes aim to make data migration more efficient and user-friendly. -- [0.5.1](https://github.com/nkdAgility/azure-devops-migration-tools/releases/tag/0.5.1) - The latest update brings a range of enhancements designed to improve user experience. Users will benefit from increased performance and stability, alongside new features that simplify interactions. Additionally, numerous bugs identified in earlier releases have been resolved, contributing to a more seamless and dependable operation. This update focuses on creating a more efficient and user-friendly environment for all. - diff --git a/appsettings.json b/appsettings.json index c5451e2e2..128f86b98 100644 --- a/appsettings.json +++ b/appsettings.json @@ -470,6 +470,17 @@ }, "Infrastructure": { "ClassNameChangeMappings": { + "TfsGitRepositoryTool": "TfsGitRepositoryTool", + "TfsTeamSettingsEnricher": "TfsTeamSettingsTool", + "TfsWorkItemLinkEnricher": "TfsWorkItemLinkTool", + "TfsAttachmentEnricher": "TfsAttachmentTool", + "StringManipulatorEnricher": "StringManipulatorTool", + "TfsUserMappingEnricher": "TfsUserMappingTool", + "FieldBlankMap": "FieldClearMap", + "FieldtoTagMap": "FieldToTagFieldMap", + "TreeToTagMap": "TreeToTagFieldMap", + "TeamMigration": "TfsTeamSettingsProcessor", + "WorkItemQueryMigration": "TfsSharedQueryProcessor", "WorkItemMigration": "TfsWorkItemMigrationProcessor", "WorkItemMigrationContext": "TfsWorkItemMigrationProcessor", "TfsTeamProjectConfig": "TfsTeamProjectEndpoint", @@ -485,10 +496,13 @@ "ExportTeamListProcessor": "TfsExportTeamListProcessor", "ExportTeamList": "TfsExportTeamListProcessor", "ExportUsersForMappingProcessor": "TfsExportUsersForMappingProcessor", + "TestConfigurationsMigration": "TfsTestConfigurationsMigrationProcessor", "TestConfigurationsMigrationProcessor": "TfsTestConfigurationsMigrationProcessor", "TestConfigurationsMigrationContext": "TfsTestConfigurationsMigrationProcessor", + "TestPlansAndSuitesMigration": "TfsTestPlansAndSuitesMigrationProcessor", "TestPlansAndSuitesMigrationProcessor": "TfsTestPlansAndSuitesMigrationProcessor", "TestPlansAndSuitesMigrationContext": "TfsTestPlansAndSuitesMigrationProcessor", + "TestVariablesMigration": "TfsTestVariablesMigrationProcessor", "TestVariablesMigrationProcessor": "TfsTestVariablesMigrationProcessor", "TestVariablesMigrationContext": "TfsTestVariablesMigrationProcessor", "WorkItemBulkEditProcessor": "TfsWorkItemBulkEditProcessor", @@ -496,6 +510,7 @@ "WorkItemDelete": "TfsWorkItemDeleteProcessor", "WorkItemDeleteProcessor": "TfsWorkItemDeleteProcessor", "WorkItemPostProcessingContext": "TfsWorkItemOverwriteProcessor", + "WorkItemPostProcessing": "TfsWorkItemOverwriteProcessor", "WorkItemPostProcessingProcessor": "TfsWorkItemOverwriteProcessor", "WorkItemUpdateAreasAsTagsContext": "TfsWorkItemOverwriteProcessor", "WorkItemUpdateAreasAsTagsProcessor": "TfsWorkItemOverwriteProcessor" diff --git a/docs/Reference/Generated/MigrationTools.Clients.TfsObjectModel.xml b/docs/Reference/Generated/MigrationTools.Clients.TfsObjectModel.xml index 1b9693a43..fbc14232d 100644 --- a/docs/Reference/Generated/MigrationTools.Clients.TfsObjectModel.xml +++ b/docs/Reference/Generated/MigrationTools.Clients.TfsObjectModel.xml @@ -339,32 +339,32 @@ preview Work Items - + A list of work items to import [] - + A work item query based on WIQL to select only important work items. To migrate all leave this empty. See [WIQL Query Bits](#wiql-query-bits) AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') - + This loads all of the work items already saved to the Target and removes them from the Source work item list prior to commencing the run. While this may take some time in large data sets it reduces the time of the overall migration significantly if you need to restart. true - + Pause after each work item is migrated false - + **beta** If set to a number greater than 0 work items that fail to save will retry after a number of seconds equal to the retry count. This allows for periodic network glitches not to end the process. diff --git a/docs/Reference/Generated/MigrationTools.xml b/docs/Reference/Generated/MigrationTools.xml index c0552d685..a90fcc846 100644 --- a/docs/Reference/Generated/MigrationTools.xml +++ b/docs/Reference/Generated/MigrationTools.xml @@ -258,37 +258,37 @@ - => @"topic/config-builder" + => @"topic/documentation-updates" - => @"8bcfb8db" + => @"a13a3b5d" - => @"8bcfb8dbb8d7ca68a38cbb8f521d7338a03d507a" + => @"a13a3b5d353f39a2b1becb256d39be53dee451c9" - => @"2024-09-04T13:17:54+01:00" + => @"2024-09-05T10:02:11+01:00" - => @"9" + => @"7" - => @"v16.0.0-Preview.9-9-g8bcfb8db" + => @"v16.0.0-Preview.10-7-ga13a3b5d" - => @"v16.0.0-Preview.9" + => @"v16.0.0-Preview.10" @@ -318,17 +318,17 @@ - => @"9" + => @"7" - => @"Preview.9" + => @"Preview.10" - => @"-Preview.9" + => @"-Preview.10" diff --git a/docs/_data/reference.endpoints.tfsendpoint.yaml b/docs/_data/reference.endpoints.tfsendpoint.yaml index 913a0fa41..2fbacc6de 100644 --- a/docs/_data/reference.endpoints.tfsendpoint.yaml +++ b/docs/_data/reference.endpoints.tfsendpoint.yaml @@ -11,7 +11,7 @@ configurationSamples: "TfsEndpoint": { "AllowCrossProjectLinking": "False", "Authentication": { - "AccessToken": "12345", + "AccessToken": "", "AuthenticationMode": "AccessToken", "NetworkCredentials": { "Domain": "", @@ -76,9 +76,9 @@ configurationSamples: "NetworkCredentials": { "Domain": "", "UserName": "", - "Password": "" + "Password": "** removed as a secret ***" }, - "AccessToken": "jklsadhjksahfkjsdhjksahsadjhksadhsad" + "AccessToken": "** removed as a secret ***" }, "ReflectedWorkItemIdField": null, "AllowCrossProjectLinking": false, diff --git a/docs/_data/reference.endpoints.tfsteamprojectendpoint.yaml b/docs/_data/reference.endpoints.tfsteamprojectendpoint.yaml index 6e91ba38e..f296cc3f0 100644 --- a/docs/_data/reference.endpoints.tfsteamprojectendpoint.yaml +++ b/docs/_data/reference.endpoints.tfsteamprojectendpoint.yaml @@ -11,7 +11,7 @@ configurationSamples: "TfsTeamProjectEndpoint": { "AllowCrossProjectLinking": "False", "Authentication": { - "AccessToken": "12345", + "AccessToken": "", "AuthenticationMode": "AccessToken", "NetworkCredentials": { "Domain": "", @@ -76,9 +76,9 @@ configurationSamples: "NetworkCredentials": { "Domain": "", "UserName": "", - "Password": "" + "Password": "** removed as a secret ***" }, - "AccessToken": "jklsadhjksahfkjsdhjksahsadjhksadhsad" + "AccessToken": "** removed as a secret ***" }, "ReflectedWorkItemIdField": null, "AllowCrossProjectLinking": false, diff --git a/docs/_data/reference.fieldmaps.treetotagfieldmap.yaml b/docs/_data/reference.fieldmaps.treetotagfieldmap.yaml index c750b70ec..4f417766a 100644 --- a/docs/_data/reference.fieldmaps.treetotagfieldmap.yaml +++ b/docs/_data/reference.fieldmaps.treetotagfieldmap.yaml @@ -57,4 +57,4 @@ options: status: missng XML code comments processingTarget: missng XML code comments classFile: /src/MigrationTools.Clients.TfsObjectModel/Tools/FieldMappingTool/FieldMaps/TreeToTagFieldMap.cs -optionsClassFile: '' +optionsClassFile: /src/MigrationTools/Tools/FieldMappingTool/FieldMaps/TreeToTagFieldMapOptions.cs diff --git a/docs/_data/reference.processors.keepoutboundlinktargetprocessor.yaml b/docs/_data/reference.processors.keepoutboundlinktargetprocessor.yaml index 3845cb676..199c52ab0 100644 --- a/docs/_data/reference.processors.keepoutboundlinktargetprocessor.yaml +++ b/docs/_data/reference.processors.keepoutboundlinktargetprocessor.yaml @@ -17,7 +17,7 @@ configurationSamples: "Enabled": false, "WIQLQuery": "Select [System.Id] From WorkItems Where [System.TeamProject] = @project and not [System.WorkItemType] contains 'Test Suite, Test Plan,Shared Steps,Shared Parameter,Feedback Request'", "TargetLinksToKeepOrganization": "https://dev.azure.com/nkdagility", - "TargetLinksToKeepProject": "5f1e7946-3c7a-4168-866b-8d1451f41fe3", + "TargetLinksToKeepProject": "e898a78b-b896-4fbe-b1c3-12cba16fce77", "CleanupFileName": "c:/temp/OutboundLinkTargets.bat", "PrependCommand": "start", "DryRun": true, diff --git a/docs/_data/reference.processors.tfsworkitemoverwriteprocessor.yaml b/docs/_data/reference.processors.tfsworkitemoverwriteprocessor.yaml index fc6ca46dd..2a8c77374 100644 --- a/docs/_data/reference.processors.tfsworkitemoverwriteprocessor.yaml +++ b/docs/_data/reference.processors.tfsworkitemoverwriteprocessor.yaml @@ -15,19 +15,11 @@ configurationSamples: { "$type": "TfsWorkItemOverwriteProcessorOptions", "Enabled": false, - "UpdateCreatedDate": false, - "UpdateCreatedBy": false, - "WIQLQuery": null, - "FixHtmlAttachmentLinks": false, - "WorkItemCreateRetryLimit": 0, + "WorkItemIDs": null, + "WIQLQuery": "SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [@ReflectedWorkItemIdField] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc", "FilterWorkItemsThatAlreadyExistInTarget": false, "PauseAfterEachWorkItem": false, - "AttachRevisionHistory": false, - "GenerateMigrationComment": false, - "WorkItemIDs": null, - "MaxGracefulFailures": 0, - "SkipRevisionWithInvalidIterationPath": false, - "SkipRevisionWithInvalidAreaPath": false, + "WorkItemCreateRetryLimit": 0, "Enrichers": null, "SourceName": null, "TargetName": null, @@ -39,10 +31,6 @@ className: TfsWorkItemOverwriteProcessor typeName: Processors architecture: options: -- parameterName: AttachRevisionHistory - type: Boolean - description: This will create a json file with the revision history and attach it to the work item. Best used with `MaxRevisions` or `ReplayRevisions`. - defaultValue: '?' - parameterName: Enabled type: Boolean description: If set to `true` then the processor will run. Set to `false` and the processor will not run. @@ -55,18 +43,6 @@ options: type: Boolean description: This loads all of the work items already saved to the Target and removes them from the Source work item list prior to commencing the run. While this may take some time in large data sets it reduces the time of the overall migration significantly if you need to restart. defaultValue: true -- parameterName: FixHtmlAttachmentLinks - type: Boolean - description: "**beta** If enabled this will fix any image attachments URL's, work item mention URL's or user mentions in the HTML fields as well as discussion comments. You must specify a PersonalAccessToken in the Source project for Azure DevOps; TFS should use integrated authentication." - defaultValue: '?' -- parameterName: GenerateMigrationComment - type: Boolean - description: If enabled, adds a comment recording the migration - defaultValue: false -- parameterName: MaxGracefulFailures - type: Int32 - description: The maximum number of failures to tolerate before the migration fails. When set above zero, a work item migration error is logged but the migration will continue until the number of failed items reaches the configured value, after which the migration fails. - defaultValue: 0 - parameterName: PauseAfterEachWorkItem type: Boolean description: Pause after each work item is migrated @@ -75,14 +51,6 @@ options: type: String description: '`Refname` will be used in the future to allow for using named Options without the need to copy all of the options.' defaultValue: missng XML code comments -- parameterName: SkipRevisionWithInvalidAreaPath - type: Boolean - description: When set to true, this setting will skip a revision if the source area has not been migrated, has been deleted or is somehow invalid, etc. - defaultValue: missng XML code comments -- parameterName: SkipRevisionWithInvalidIterationPath - type: Boolean - description: This will skip a revision if the source iteration has not been migrated i.e. it was deleted - defaultValue: missng XML code comments - parameterName: SourceName type: String description: missng XML code comments @@ -91,18 +59,10 @@ options: type: String description: missng XML code comments defaultValue: missng XML code comments -- parameterName: UpdateCreatedBy - type: Boolean - description: "If this is enabled the creation process on the target project will create the items with the original creation date. (Important: The item history is always pointed to the date of the migration, it's change only the data column CreateDate, not the internal create date)" - defaultValue: true -- parameterName: UpdateCreatedDate - type: Boolean - description: "If this is enabled the creation process on the target project will create the items with the original creation date. (Important: The item history is always pointed to the date of the migration, it's change only the data column CreateDate, not the internal create date)" - defaultValue: true - parameterName: WIQLQuery type: String description: A work item query based on WIQL to select only important work items. To migrate all leave this empty. See [WIQL Query Bits](#wiql-query-bits) - defaultValue: SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [[System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc + defaultValue: AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') - parameterName: WorkItemCreateRetryLimit type: Int32 description: '**beta** If set to a number greater than 0 work items that fail to save will retry after a number of seconds equal to the retry count. This allows for periodic network glitches not to end the process.' diff --git a/docs/_includes/sampleConfig/ProcessDefinitionProcessor-Full.json b/docs/_includes/sampleConfig/ProcessDefinitionProcessor-Full.json index f06b25ad4..80a2fd112 100644 --- a/docs/_includes/sampleConfig/ProcessDefinitionProcessor-Full.json +++ b/docs/_includes/sampleConfig/ProcessDefinitionProcessor-Full.json @@ -1,51 +1,62 @@ { - "Version": "12.8", - "ChangeSetMappingFile": null, - "Source": null, - "Target": null, - "FieldMaps": [], - "GitRepoMapping": null, - "LogLevel": "Debug", - "CommonEnrichersConfig": null, - "workaroundForQuerySOAPBugEnabled": false, - "WorkItemTypeDefinition": null, - "Endpoints": { - "AzureDevOpsEndpoints": [ - { - "name": "Source", - "$type": "AzureDevOpsEndpointOptions", - "Organisation": "https://dev.azure.com/xxxx", - "Project": "SAM", + "Serilog": { + "MinimumLevel": "Information" + }, + "MigrationTools": { + "Version": "0.0", + "Endpoints": { + "Source": { + "EndpointType": "AzureDevOpsEndpoint", "AuthenticationMode": "AccessToken", "AccessToken": "xxxxx", - "EndpointEnrichers": null - }, - { - "Name": "Target", - "$type": "AzureDevOpsEndpointOptions", "Organisation": "https://dev.azure.com/xxxx", "Project": "SAM", + "ReflectedWorkItemIdField": null, + "EndpointEnrichers": null + }, + "Target": { + "EndpointType": "AzureDevOpsEndpoint", "AuthenticationMode": "AccessToken", "AccessToken": "xxxx", + "Organisation": "https://dev.azure.com/xxxx", + "Project": "SAM", + "ReflectedWorkItemIdField": null, "EndpointEnrichers": null } - ] - }, - "Processors": [ - { - "$type": "ProcessDefinitionProcessorOptions", - "Enabled": true, - "Processes": { - "Agile SAM - Migration Template": [ - "User Story" - ] + }, + "Processors": [ + { + "ProcessorType": "ProcessDefinitionProcessor", + "Enabled": true, + "Processes": { + "Agile SAM - Migration Template": [ + "User Story" + ] + }, + "ProcessMaps": { + "Agile SAM - Migration Template": "SAM Agile Migrator" + }, + "UpdateProcessDetails": true, + "MaxDegreeOfParallelism": 0, + "Enrichers": null, + "SourceName": "Source", + "TargetName": "Target", + "RefName": null + } + ], + "CommonTools": { + "TfsChangeSetMappingTool": { + "Enabled": true, + "ChangeSetMappingFile": "" }, - "ProcessMaps": { - "Agile SAM - Migration Template": "SAM Agile Migrator" + "TfsGitRepositoryTool": { + "Enabled": true, + "Mappings": null }, - "SourceName": "Source", - "TargetName": "Target", - "UpdateProcessDetails": true + "FieldMappingTool": { + "Enabled": true, + "FieldMaps": [] + } } - ] + } } \ No newline at end of file diff --git a/docs/_includes/sampleConfig/configuration-Fullv2.json b/docs/_includes/sampleConfig/configuration-Fullv2.json index 4ee186476..b2392c17d 100644 --- a/docs/_includes/sampleConfig/configuration-Fullv2.json +++ b/docs/_includes/sampleConfig/configuration-Fullv2.json @@ -1,78 +1,36 @@ { - "ChangeSetMappingFile": null, - "Source": null, - "Target": null, - "FieldMaps": [], - "GitRepoMapping": null, - "LogLevel": "Information", - "CommonEnrichersConfig": null, - "Processors": [ - { - "$type": "WorkItemTrackingProcessorOptions", - "Enabled": true, - "ReplayRevisions": true, - "PrefixProjectToNodes": false, - "CollapseRevisions": false, - "WorkItemCreateRetryLimit": 5, - "ProcessorEnrichers": [ - { - "$type": "PauseAfterEachItemOptions", - "Enabled": true - }, - { - "$type": "AppendMigrationToolSignatureFooterOptions", - "Enabled": true - }, - { - "$type": "FilterWorkItemsThatAlreadyExistInTargetOptions", - "Enabled": true, - "Query": { - "Query": "SELECT [System.Id], [System.Tags] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan') ORDER BY [System.ChangedDate] desc", - "Parameters": null - } - }, - { - "$type": "SkipToFinalRevisedWorkItemTypeOptions", - "Enabled": true - }, - { - "$type": "TfsValidateRequiredFieldOptions", - "Enabled": true - }, - { - "$type": "TfsNodeStructureOptions", - "Enabled": true, - "PrefixProjectToNodes": false, - "NodeBasePaths": null, - "AreaMaps": {}, - "IterationMaps": {} - }, - { - "$type": "TfsRevisionManagerOptions", - "Enabled": true, - "ReplayRevisions": false, - "MaxRevisions": 0 - } - ], - "SourceName": "Source", - "TargetName": "Target" - } - ], - "Version": "0.0", - "workaroundForQuerySOAPBugEnabled": false, - "WorkItemTypeDefinition": { - "sourceWorkItemTypeName": "targetWorkItemTypeName" + "Serilog": { + "MinimumLevel": "Information" }, - "Endpoints": { - "InMemoryWorkItemEndpoints": [ + "MigrationTools": { + "Version": "0.0", + "Endpoints": {}, + "Processors": [ { - "Name": "Source", - "EndpointEnrichers": null + "ProcessorType": "WorkItemTrackingProcessor", + "Enabled": true, + "ReplayRevisions": true, + "CollapseRevisions": false, + "WorkItemCreateRetryLimit": 5, + "Enrichers": null, + "SourceName": "Source", + "TargetName": "Target", + "RefName": null + } + ], + "CommonTools": { + "TfsChangeSetMappingTool": { + "Enabled": true, + "ChangeSetMappingFile": "" }, - { - "Name": "Target", - "EndpointEnrichers": null + "TfsGitRepositoryTool": { + "Enabled": true, + "Mappings": null + }, + "FieldMappingTool": { + "Enabled": true, + "FieldMaps": [] } - ] + } } } \ No newline at end of file diff --git a/docs/_includes/sampleConfig/configuration-WorkItemTracking.json b/docs/_includes/sampleConfig/configuration-WorkItemTracking.json index 34b1508e2..9ba76f97c 100644 --- a/docs/_includes/sampleConfig/configuration-WorkItemTracking.json +++ b/docs/_includes/sampleConfig/configuration-WorkItemTracking.json @@ -1,235 +1,90 @@ { - "ChangeSetMappingFile": null, - "Source": { - "$type": "TfsTeamProjectConfig", - "Collection": "https://dev.azure.com/nkdagility-preview/", - "Project": "myProjectName", - "ReflectedWorkItemIdField": "Custom.ReflectedWorkItemId", - "AllowCrossProjectLinking": false, - "AuthenticationMode": "Prompt", - "PersonalAccessToken": "", - "PersonalAccessTokenVariableName": "", - "LanguageMaps": { - "AreaPath": "Area", - "IterationPath": "Iteration" - } - }, - "Target": { - "$type": "TfsTeamProjectConfig", - "Collection": "https://dev.azure.com/nkdagility-preview/", - "Project": "myProjectName", - "ReflectedWorkItemIdField": "Custom.ReflectedWorkItemId", - "AllowCrossProjectLinking": false, - "AuthenticationMode": "Prompt", - "PersonalAccessToken": "", - "PersonalAccessTokenVariableName": "", - "LanguageMaps": { - "AreaPath": "Area", - "IterationPath": "Iteration" - } + "Serilog": { + "MinimumLevel": "Information" }, - "FieldMaps": [ - { - "$type": "MultiValueConditionalMapConfig", - "WorkItemTypeName": "*", - "sourceFieldsAndValues": { - "Field1": "Value1", - "Field2": "Value2" + "MigrationTools": { + "Version": "0.0", + "Endpoints": { + "Source": { + "EndpointType": "TfsTeamProjectEndpoint", + "Collection": "https://dev.azure.com/nkdagility-preview/", + "Project": "migrationSource1", + "Authentication": { + "AuthenticationMode": "AccessToken", + "NetworkCredentials": { + "Domain": "", + "UserName": "", + "Password": "** removed as a secret ***" + }, + "AccessToken": "** removed as a secret ***" + }, + "ReflectedWorkItemIdField": null, + "AllowCrossProjectLinking": false, + "LanguageMaps": { + "AreaPath": "Area", + "IterationPath": "Iteration" + }, + "EndpointEnrichers": null }, - "targetFieldsAndValues": { - "Field1": "Value1", - "Field2": "Value2" + "Target": { + "EndpointType": "TfsTeamProjectEndpoint", + "Collection": "https://dev.azure.com/nkdagility-preview/", + "Project": "migrationSource1", + "Authentication": { + "AuthenticationMode": "AccessToken", + "NetworkCredentials": { + "Domain": "", + "UserName": "", + "Password": "** removed as a secret ***" + }, + "AccessToken": "** removed as a secret ***" + }, + "ReflectedWorkItemIdField": null, + "AllowCrossProjectLinking": false, + "LanguageMaps": { + "AreaPath": "Area", + "IterationPath": "Iteration" + }, + "EndpointEnrichers": null } }, - { - "$type": "FieldBlankMapConfig", - "WorkItemTypeName": "*", - "targetField": "TfsMigrationTool.ReflectedWorkItemId" - }, - { - "$type": "FieldValueMapConfig", - "WorkItemTypeName": "*", - "sourceField": "System.State", - "targetField": "System.State", - "defaultValue": "New", - "valueMapping": { - "Approved": "New", - "New": "New", - "Committed": "Active", - "In Progress": "Active", - "To Do": "New", - "Done": "Closed", - "Removed": "Removed" + "Processors": [ + { + "ProcessorType": "TfsWorkItemMigrationProcessor", + "Enabled": false, + "UpdateCreatedDate": true, + "UpdateCreatedBy": true, + "WIQLQuery": "SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc", + "FixHtmlAttachmentLinks": true, + "WorkItemCreateRetryLimit": 5, + "FilterWorkItemsThatAlreadyExistInTarget": false, + "PauseAfterEachWorkItem": false, + "AttachRevisionHistory": false, + "GenerateMigrationComment": true, + "WorkItemIDs": null, + "MaxGracefulFailures": 0, + "SkipRevisionWithInvalidIterationPath": false, + "SkipRevisionWithInvalidAreaPath": false, + "Enrichers": null, + "SourceName": "Source", + "TargetName": "Target", + "RefName": null } - }, - { - "$type": "FieldtoFieldMapConfig", - "WorkItemTypeName": "*", - "sourceField": "Microsoft.VSTS.Common.BacklogPriority", - "targetField": "Microsoft.VSTS.Common.StackRank", - "defaultValue": null - }, - { - "$type": "FieldtoFieldMultiMapConfig", - "WorkItemTypeName": "*", - "SourceToTargetMappings": { - "SourceField1": "TargetField1", - "SourceField2": "TargetField2" + ], + "CommonTools": { + "FieldMappingTool": { + "Enabled": true, + "FieldMaps": [ + { + "FieldMapType": "FieldLiteralMap", + "targetField": "Custom.SomeField", + "value": "New field value", + "ApplyTo": [ + "SomeWorkItemType" + ] + } + ] } - }, - { - "$type": "FieldtoTagMapConfig", - "WorkItemTypeName": "*", - "sourceField": "System.State", - "formatExpression": "ScrumState:{0}" - }, - { - "$type": "FieldMergeMapConfig", - "WorkItemTypeName": "*", - "sourceFields": [ - "System.Description", - "Microsoft.VSTS.Common.AcceptanceCriteria" - ], - "targetField": "System.Description", - "formatExpression": "{0}

Acceptance Criteria

{1}", - "doneMatch": "##DONE##" - }, - { - "$type": "RegexFieldMapConfig", - "WorkItemTypeName": "*", - "sourceField": "COMPANY.PRODUCT.Release", - "targetField": "COMPANY.DEVISION.MinorReleaseVersion", - "pattern": "PRODUCT \\d{4}.(\\d{1})", - "replacement": "$1" - }, - { - "$type": "FieldValuetoTagMapConfig", - "WorkItemTypeName": "*", - "sourceField": "Microsoft.VSTS.CMMI.Blocked", - "pattern": "Yes", - "formatExpression": "{0}" - }, - { - "$type": "TreeToTagMapConfig", - "WorkItemTypeName": "*", - "toSkip": 3, - "timeTravel": 1 } - ], - "GitRepoMapping": null, - "LogLevel": "Information", - "CommonEnrichersConfig": [ - { - "$type": "TfsNodeStructureOptions", - "RefName": "TfsNodeStructure", - "Enabled": true, - "NodeBasePaths": [], - "AreaMaps": { - "^migrationSource1([\\\\]?.*)$": "MigrationTest5$1", - "^nkdProducts([\\\\]?.*)$": "MigrationTest5$1", - "^Skypoint Cloud$": "MigrationTest5" - }, - "IterationMaps": { - "^migrationSource1([\\\\]?.*)$": "MigrationTest5$1", - "^nkdProducts([\\\\]?.*)$": "MigrationTest5$1", - "^Skypoint Cloud\\\\Sprint 1$": "MigrationTest5\\Sprint 1" - }, - "ShouldCreateMissingRevisionPaths": true, - "ReplicateAllExistingNodes": true - }, - { - "$type": "TfsTeamSettingsEnricherOptions", - "Enabled": true, - "MigrateTeamSettings": true, - "UpdateTeamSettings": true, - "MigrateTeamCapacities": true, - "Teams": null - }, - { - "$type": "TfsWorkItemLinkEnricherOptions", - "RefName": "TfsWorkItemLinkEnricher", - "Enabled": true, - "FilterIfLinkCountMatches": true, - "SaveAfterEachLinkIsAdded": false - }, - { - "$type": "TfsRevisionManagerOptions", - "RefName": "TfsRevisionManager", - "Enabled": true, - "ReplayRevisions": true, - "MaxRevisions": 0 - }, - { - "$type": "TfsAttachmentEnricherOptions", - "RefName": "TfsAttachmentEnricher", - "Enabled": true, - "ExportBasePath": "c:\\temp\\WorkItemAttachmentExport", - "MaxRevisions": 480000000 - }, - { - "$type": "StringManipulatorEnricherOptions", - "RefName": "StringManipulator", - "Enabled": true, - "MaxStringLength": 1000000, - "Manipulators": [ - { - "$type": "RegexStringManipulator", - "Enabled": true, - "Pattern": "[^( -~)\n\r\t]+", - "Replacement": "", - "Description": "Remove invalid characters from the end of the string" - } - ] - }, - { - "$type": "TfsUserMappingEnricherOptions", - "RefName": "TfsUserMappingEnricher", - "Enabled": true, - "UserMappingFile": "C:\\temp\\userExport.json", - "IdentityFieldsToCheck": [ - "System.AssignedTo", - "System.ChangedBy", - "System.CreatedBy", - "Microsoft.VSTS.Common.ActivatedBy", - "Microsoft.VSTS.Common.ResolvedBy", - "Microsoft.VSTS.Common.ClosedBy" - ] - } - ], - "Processors": [ - { - "$type": "WorkItemMigrationConfig", - "Enabled": false, - "UpdateCreatedDate": true, - "UpdateCreatedBy": true, - "WIQLQuery": "SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc", - "FixHtmlAttachmentLinks": false, - "WorkItemCreateRetryLimit": 5, - "FilterWorkItemsThatAlreadyExistInTarget": false, - "PauseAfterEachWorkItem": false, - "AttachRevisionHistory": false, - "GenerateMigrationComment": true, - "WorkItemIDs": null, - "MaxGracefulFailures": 0, - "SkipRevisionWithInvalidIterationPath": false, - "SkipRevisionWithInvalidAreaPath": false - } - ], - "Version": "0.0", - "workaroundForQuerySOAPBugEnabled": false, - "WorkItemTypeDefinition": { - "sourceWorkItemTypeName": "targetWorkItemTypeName" - }, - "Endpoints": { - "InMemoryWorkItemEndpoints": [ - { - "Name": "Source", - "EndpointEnrichers": null - }, - { - "Name": "Target", - "EndpointEnrichers": null - } - ] } } \ No newline at end of file diff --git a/docs/_includes/sampleConfig/configuration-full.json b/docs/_includes/sampleConfig/configuration-full.json index fcb784284..b17685c4d 100644 --- a/docs/_includes/sampleConfig/configuration-full.json +++ b/docs/_includes/sampleConfig/configuration-full.json @@ -1,325 +1,333 @@ { - "ChangeSetMappingFile": null, - "Source": { - "$type": "TfsTeamProjectConfig", - "Collection": "https://dev.azure.com/nkdagility-preview/", - "Project": "myProjectName", - "ReflectedWorkItemIdField": "Custom.ReflectedWorkItemId", - "AllowCrossProjectLinking": false, - "AuthenticationMode": "Prompt", - "PersonalAccessToken": "", - "PersonalAccessTokenVariableName": "", - "LanguageMaps": { - "AreaPath": "Area", - "IterationPath": "Iteration" - } - }, - "Target": { - "$type": "TfsTeamProjectConfig", - "Collection": "https://dev.azure.com/nkdagility-preview/", - "Project": "myProjectName", - "ReflectedWorkItemIdField": "Custom.ReflectedWorkItemId", - "AllowCrossProjectLinking": false, - "AuthenticationMode": "Prompt", - "PersonalAccessToken": "", - "PersonalAccessTokenVariableName": "", - "LanguageMaps": { - "AreaPath": "Area", - "IterationPath": "Iteration" - } + "Serilog": { + "MinimumLevel": "Information" }, - "FieldMaps": [ - { - "$type": "MultiValueConditionalMapConfig", - "WorkItemTypeName": "*", - "sourceFieldsAndValues": { - "Field1": "Value1", - "Field2": "Value2" + "MigrationTools": { + "Version": "0.0", + "Endpoints": { + "Source": { + "EndpointType": "TfsTeamProjectEndpoint", + "Collection": "https://dev.azure.com/nkdagility-preview/", + "Project": "myProjectName", + "Authentication": null, + "ReflectedWorkItemIdField": "Custom.ReflectedWorkItemId", + "AllowCrossProjectLinking": false, + "LanguageMaps": { + "AreaPath": "Area", + "IterationPath": "Iteration" + }, + "EndpointEnrichers": null }, - "targetFieldsAndValues": { - "Field1": "Value1", - "Field2": "Value2" - } - }, - { - "$type": "FieldBlankMapConfig", - "WorkItemTypeName": "*", - "targetField": "TfsMigrationTool.ReflectedWorkItemId" - }, - { - "$type": "FieldValueMapConfig", - "WorkItemTypeName": "*", - "sourceField": "System.State", - "targetField": "System.State", - "defaultValue": "New", - "valueMapping": { - "Approved": "New", - "New": "New", - "Committed": "Active", - "In Progress": "Active", - "To Do": "New", - "Done": "Closed", - "Removed": "Removed" - } - }, - { - "$type": "FieldtoFieldMapConfig", - "WorkItemTypeName": "*", - "sourceField": "Microsoft.VSTS.Common.BacklogPriority", - "targetField": "Microsoft.VSTS.Common.StackRank", - "defaultValue": null - }, - { - "$type": "FieldtoFieldMultiMapConfig", - "WorkItemTypeName": "*", - "SourceToTargetMappings": { - "SourceField1": "TargetField1", - "SourceField2": "TargetField2" + "Target": { + "EndpointType": "TfsTeamProjectEndpoint", + "Collection": "https://dev.azure.com/nkdagility-preview/", + "Project": "myProjectName", + "Authentication": null, + "ReflectedWorkItemIdField": "Custom.ReflectedWorkItemId", + "AllowCrossProjectLinking": false, + "LanguageMaps": { + "AreaPath": "Area", + "IterationPath": "Iteration" + }, + "EndpointEnrichers": null } }, - { - "$type": "FieldtoTagMapConfig", - "WorkItemTypeName": "*", - "sourceField": "System.State", - "formatExpression": "ScrumState:{0}" - }, - { - "$type": "FieldMergeMapConfig", - "WorkItemTypeName": "*", - "sourceFields": [ - "System.Description", - "Microsoft.VSTS.Common.AcceptanceCriteria" - ], - "targetField": "System.Description", - "formatExpression": "{0}

Acceptance Criteria

{1}", - "doneMatch": "##DONE##" - }, - { - "$type": "RegexFieldMapConfig", - "WorkItemTypeName": "*", - "sourceField": "COMPANY.PRODUCT.Release", - "targetField": "COMPANY.DEVISION.MinorReleaseVersion", - "pattern": "PRODUCT \\d{4}.(\\d{1})", - "replacement": "$1" - }, - { - "$type": "FieldValuetoTagMapConfig", - "WorkItemTypeName": "*", - "sourceField": "Microsoft.VSTS.CMMI.Blocked", - "pattern": "Yes", - "formatExpression": "{0}" - }, - { - "$type": "TreeToTagMapConfig", - "WorkItemTypeName": "*", - "toSkip": 3, - "timeTravel": 1 - } - ], - "GitRepoMapping": null, - "LogLevel": "Information", - "CommonEnrichersConfig": [ - { - "$type": "TfsNodeStructureOptions", - "RefName": "TfsNodeStructure", - "Enabled": true, - "NodeBasePaths": [], - "AreaMaps": { - "^migrationSource1([\\\\]?.*)$": "MigrationTest5$1", - "^nkdProducts([\\\\]?.*)$": "MigrationTest5$1", - "^Skypoint Cloud$": "MigrationTest5" + "Processors": [ + { + "ProcessorType": "TfsWorkItemMigrationProcessor", + "Enabled": false, + "UpdateCreatedDate": true, + "UpdateCreatedBy": true, + "WIQLQuery": "SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc", + "FixHtmlAttachmentLinks": false, + "WorkItemCreateRetryLimit": 5, + "FilterWorkItemsThatAlreadyExistInTarget": false, + "PauseAfterEachWorkItem": false, + "AttachRevisionHistory": false, + "GenerateMigrationComment": true, + "WorkItemIDs": null, + "MaxGracefulFailures": 0, + "SkipRevisionWithInvalidIterationPath": false, + "SkipRevisionWithInvalidAreaPath": false, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null }, - "IterationMaps": { - "^migrationSource1([\\\\]?.*)$": "MigrationTest5$1", - "^nkdProducts([\\\\]?.*)$": "MigrationTest5$1", - "^Skypoint Cloud\\\\Sprint 1$": "MigrationTest5\\Sprint 1" + { + "ProcessorType": "TfsTestVariablesMigrationProcessor", + "Enabled": false, + "Processor": "TestVariablesMigrationContext", + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null }, - "ShouldCreateMissingRevisionPaths": true, - "ReplicateAllExistingNodes": true - }, - { - "$type": "TfsTeamSettingsEnricherOptions", - "Enabled": true, - "MigrateTeamSettings": true, - "UpdateTeamSettings": true, - "MigrateTeamCapacities": true, - "Teams": null - }, - { - "$type": "TfsWorkItemLinkEnricherOptions", - "RefName": "TfsWorkItemLinkEnricher", - "Enabled": true, - "FilterIfLinkCountMatches": true, - "SaveAfterEachLinkIsAdded": false - }, - { - "$type": "TfsRevisionManagerOptions", - "RefName": "TfsRevisionManager", - "Enabled": true, - "ReplayRevisions": true, - "MaxRevisions": 0 - }, - { - "$type": "TfsAttachmentEnricherOptions", - "RefName": "TfsAttachmentEnricher", - "Enabled": true, - "ExportBasePath": "c:\\temp\\WorkItemAttachmentExport", - "MaxRevisions": 480000000 - }, - { - "$type": "StringManipulatorEnricherOptions", - "RefName": "StringManipulator", - "Enabled": true, - "MaxStringLength": 1000000, - "Manipulators": [ - { - "$type": "RegexStringManipulator", - "Enabled": true, - "Pattern": "[^( -~)\n\r\t]+", - "Replacement": "", - "Description": "Remove invalid characters from the end of the string" - } - ] - }, - { - "$type": "TfsUserMappingEnricherOptions", - "RefName": "TfsUserMappingEnricher", - "Enabled": true, - "UserMappingFile": "C:\\temp\\userExport.json", - "IdentityFieldsToCheck": [ - "System.AssignedTo", - "System.ChangedBy", - "System.CreatedBy", - "Microsoft.VSTS.Common.ActivatedBy", - "Microsoft.VSTS.Common.ResolvedBy", - "Microsoft.VSTS.Common.ClosedBy" - ] - } - ], - "Processors": [ - { - "$type": "WorkItemMigrationConfig", - "Enabled": false, - "UpdateCreatedDate": true, - "UpdateCreatedBy": true, - "WIQLQuery": "SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc", - "FixHtmlAttachmentLinks": false, - "WorkItemCreateRetryLimit": 5, - "FilterWorkItemsThatAlreadyExistInTarget": false, - "PauseAfterEachWorkItem": false, - "AttachRevisionHistory": false, - "GenerateMigrationComment": true, - "WorkItemIDs": null, - "MaxGracefulFailures": 0, - "SkipRevisionWithInvalidIterationPath": false, - "SkipRevisionWithInvalidAreaPath": false - }, - { - "$type": "TestVariablesMigrationConfig", - "Enabled": false - }, - { - "$type": "TestConfigurationsMigrationConfig", - "Enabled": false - }, - { - "$type": "TestPlansAndSuitesMigrationConfig", - "Enabled": false, - "PrefixProjectToNodes": false, - "OnlyElementsWithTag": null, - "TestPlanQueryBit": null, - "RemoveAllLinks": false, - "MigrationDelay": 0, - "UseCommonNodeStructureEnricherConfig": false, - "NodeBasePaths": null, - "AreaMaps": null, - "IterationMaps": null, - "RemoveInvalidTestSuiteLinks": false, - "FilterCompleted": false - }, - { - "$type": "ImportProfilePictureConfig", - "Enabled": false - }, - { - "$type": "ExportProfilePictureFromADConfig", - "Enabled": false, - "Domain": null, - "Username": null, - "Password": null, - "PictureEmpIDFormat": null - }, - { - "$type": "FixGitCommitLinksConfig", - "Enabled": false, - "TargetRepository": "targetProjectName", - "QueryBit": null, - "OrderBit": null - }, - { - "$type": "WorkItemUpdateConfig", - "Enabled": false, - "WhatIf": false, - "WIQLQuery": "SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc", - "WorkItemIDs": null, - "FilterWorkItemsThatAlreadyExistInTarget": false, - "PauseAfterEachWorkItem": false, - "WorkItemCreateRetryLimit": 0 - }, - { - "$type": "WorkItemPostProcessingConfig", - "Enabled": false, - "WorkItemIDs": [ - 1, - 2, - 3 - ], - "WIQLQuery": "SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc", - "FilterWorkItemsThatAlreadyExistInTarget": false, - "PauseAfterEachWorkItem": false, - "WorkItemCreateRetryLimit": 0 - }, - { - "$type": "WorkItemDeleteConfig", - "Enabled": false, - "WIQLQuery": "SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc", - "WorkItemIDs": null, - "FilterWorkItemsThatAlreadyExistInTarget": false, - "PauseAfterEachWorkItem": false, - "WorkItemCreateRetryLimit": 0 - }, - { - "$type": "WorkItemQueryMigrationConfig", - "Enabled": false, - "PrefixProjectToNodes": false, - "SharedFolderName": "Shared Queries", - "SourceToTargetFieldMappings": { - "SourceFieldRef": "TargetFieldRef" - } - }, - { - "$type": "TeamMigrationConfig", - "Enabled": false, - "PrefixProjectToNodes": false, - "EnableTeamSettingsMigration": true, - "FixTeamSettingsForExistingTeams": false - } - ], - "Version": "0.0", - "workaroundForQuerySOAPBugEnabled": false, - "WorkItemTypeDefinition": { - "sourceWorkItemTypeName": "targetWorkItemTypeName" - }, - "Endpoints": { - "InMemoryWorkItemEndpoints": [ { - "Name": "Source", - "EndpointEnrichers": null + "ProcessorType": "TfsTestConfigurationsMigrationProcessor", + "Enabled": false, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null }, { - "Name": "Target", - "EndpointEnrichers": null + "ProcessorType": "TfsTestPlansAndSuitesMigrationProcessor", + "Enabled": false, + "OnlyElementsWithTag": "", + "TestPlanQuery": null, + "RemoveAllLinks": false, + "MigrationDelay": 0, + "RemoveInvalidTestSuiteLinks": false, + "FilterCompleted": false, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "TfsImportProfilePictureProcessor", + "Enabled": false, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "TfsExportProfilePictureFromADProcessor", + "Enabled": false, + "Domain": "", + "Username": "", + "Password": "", + "PictureEmpIDFormat": "", + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "TfsWorkItemBulkEditProcessor", + "Enabled": false, + "WhatIf": false, + "WIQLQuery": "SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc", + "WorkItemIDs": null, + "FilterWorkItemsThatAlreadyExistInTarget": false, + "PauseAfterEachWorkItem": false, + "WorkItemCreateRetryLimit": 0, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "TfsWorkItemOverwriteProcessor", + "Enabled": false, + "WorkItemIDs": [ + 1, + 2, + 3 + ], + "WIQLQuery": "SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc", + "FilterWorkItemsThatAlreadyExistInTarget": false, + "PauseAfterEachWorkItem": false, + "WorkItemCreateRetryLimit": 0, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "TfsWorkItemDeleteProcessor", + "Enabled": false, + "WIQLQuery": "SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc", + "WorkItemIDs": null, + "FilterWorkItemsThatAlreadyExistInTarget": false, + "PauseAfterEachWorkItem": false, + "WorkItemCreateRetryLimit": 0, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "TfsSharedQueryProcessor", + "Enabled": false, + "PrefixProjectToNodes": false, + "SharedFolderName": "Shared Queries", + "SourceToTargetFieldMappings": { + "SourceFieldRef": "TargetFieldRef" + }, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "TfsTeamSettingsProcessor", + "Enabled": false, + "MigrateTeamSettings": false, + "UpdateTeamSettings": false, + "PrefixProjectToNodes": false, + "MigrateTeamCapacities": false, + "Teams": null, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + } + ], + "CommonTools": { + "TfsChangeSetMappingTool": { + "Enabled": true, + "ChangeSetMappingFile": "" + }, + "TfsGitRepositoryTool": { + "Enabled": true, + "Mappings": null + }, + "FieldMappingTool": { + "Enabled": true, + "FieldMaps": [ + { + "FieldMapType": "MultiValueConditionalMap", + "sourceFieldsAndValues": { + "Field1": "Value1", + "Field2": "Value2" + }, + "targetFieldsAndValues": { + "Field1": "Value1", + "Field2": "Value2" + }, + "ApplyTo": [] + }, + { + "FieldMapType": "FieldClearMap", + "targetField": "TfsMigrationTool.ReflectedWorkItemId", + "ApplyTo": [] + }, + { + "FieldMapType": "FieldValueMap", + "sourceField": "System.State", + "targetField": "System.State", + "defaultValue": "New", + "valueMapping": { + "Approved": "New", + "Committed": "Active", + "Done": "Closed", + "In Progress": "Active", + "New": "New", + "Removed": "Removed", + "To Do": "New" + }, + "ApplyTo": [] + }, + { + "FieldMapType": "FieldToFieldMap", + "sourceField": "Microsoft.VSTS.Common.BacklogPriority", + "targetField": "Microsoft.VSTS.Common.StackRank", + "defaultValue": "", + "ApplyTo": [] + }, + { + "FieldMapType": "FieldToFieldMultiMap", + "SourceToTargetMappings": { + "SourceField1": "TargetField1", + "SourceField2": "TargetField2" + }, + "ApplyTo": [] + }, + { + "FieldMapType": "FieldToTagFieldMap", + "sourceField": "System.State", + "formatExpression": "ScrumState:{0}", + "ApplyTo": [] + }, + { + "FieldMapType": "FieldMergeMap", + "sourceFields": [ + "System.Description", + "Microsoft.VSTS.Common.AcceptanceCriteria" + ], + "targetField": "System.Description", + "formatExpression": "{0}

Acceptance Criteria

{1}", + "ApplyTo": [] + }, + { + "FieldMapType": "RegexFieldMap", + "sourceField": "COMPANY.PRODUCT.Release", + "targetField": "COMPANY.DEVISION.MinorReleaseVersion", + "pattern": "PRODUCT \\d{4}.(\\d{1})", + "replacement": "$1", + "ApplyTo": [] + }, + { + "FieldMapType": "FieldValueToTagMap", + "sourceField": "Microsoft.VSTS.CMMI.Blocked", + "pattern": "Yes", + "formatExpression": "{0}", + "ApplyTo": [] + }, + { + "FieldMapType": "TreeToTagFieldMap", + "toSkip": 3, + "timeTravel": 1, + "ApplyTo": [] + } + ] + }, + "TfsNodeStructureTool": { + "Enabled": true, + "Areas": null, + "Iterations": null, + "ShouldCreateMissingRevisionPaths": true, + "ReplicateAllExistingNodes": true + }, + "TfsTeamSettingsTool": { + "Enabled": true, + "MigrateTeamSettings": true, + "UpdateTeamSettings": true, + "MigrateTeamCapacities": true, + "Teams": null + }, + "TfsWorkItemLinkTool": { + "Enabled": true, + "FilterIfLinkCountMatches": true, + "SaveAfterEachLinkIsAdded": false + }, + "TfsRevisionManagerTool": { + "Enabled": true, + "ReplayRevisions": true, + "MaxRevisions": 0 + }, + "TfsAttachmentTool": { + "Enabled": true, + "ExportBasePath": "c:\\temp\\WorkItemAttachmentExport", + "MaxAttachmentSize": 480000000 + }, + "StringManipulatorTool": { + "Enabled": true, + "MaxStringLength": 1000000, + "Manipulators": [ + { + "Enabled": true, + "Pattern": "[^( -~)\n\r\t]+", + "Replacement": "", + "Description": "Remove invalid characters from the end of the string" + } + ] + }, + "TfsUserMappingTool": { + "Enabled": true, + "IdentityFieldsToCheck": [ + "System.AssignedTo", + "System.ChangedBy", + "System.CreatedBy", + "Microsoft.VSTS.Common.ActivatedBy", + "Microsoft.VSTS.Common.ResolvedBy", + "Microsoft.VSTS.Common.ClosedBy" + ], + "UserMappingFile": "C:\\temp\\userExport.json" } - ] + } } } \ No newline at end of file diff --git a/docs/_includes/sampleConfig/configuration-ref.json b/docs/_includes/sampleConfig/configuration-ref.json new file mode 100644 index 000000000..2d62a0e2b --- /dev/null +++ b/docs/_includes/sampleConfig/configuration-ref.json @@ -0,0 +1,522 @@ +{ + "Serilog": { + "MinimumLevel": "Information" + }, + "MigrationTools": { + "Version": "0.0", + "Endpoints": { + "EndpointRefname-1": { + "EndpointType": "FileSystemWorkItemEndpoint", + "FileStore": null, + "EndpointEnrichers": null + }, + "EndpointRefname-2": { + "EndpointType": "TfsEndpoint", + "Organisation": null, + "Project": null, + "AuthenticationMode": "AccessToken", + "AccessToken": null, + "ReflectedWorkItemIdField": null, + "LanguageMaps": null, + "EndpointEnrichers": null + }, + "EndpointRefname-3": { + "EndpointType": "TfsTeamProjectEndpoint", + "Collection": "https://dev.azure.com/nkdagility-preview/", + "Project": "migrationSource1", + "Authentication": { + "AuthenticationMode": "AccessToken", + "NetworkCredentials": { + "Domain": "", + "UserName": "", + "Password": "" + }, + "AccessToken": "jklsadhjksahfkjsdhjksahsadjhksadhsad" + }, + "ReflectedWorkItemIDFieldName": null, + "AllowCrossProjectLinking": false, + "LanguageMaps": { + "AreaPath": "Area", + "IterationPath": "Iteration" + }, + "EndpointEnrichers": null + }, + "EndpointRefname-4": { + "EndpointType": "TfsTeamSettingsEndpoint", + "Organisation": null, + "Project": null, + "AuthenticationMode": "AccessToken", + "AccessToken": null, + "ReflectedWorkItemIdField": null, + "LanguageMaps": null, + "EndpointEnrichers": null + }, + "EndpointRefname-5": { + "EndpointType": "TfsWorkItemEndpoint", + "Organisation": null, + "Project": null, + "Query": null, + "AuthenticationMode": "AccessToken", + "AccessToken": null, + "ReflectedWorkItemIdField": null, + "LanguageMaps": null, + "EndpointEnrichers": null + }, + "EndpointRefname-6": { + "EndpointType": "AzureDevOpsEndpoint", + "AuthenticationMode": "AccessToken", + "AccessToken": null, + "Organisation": null, + "Project": null, + "ReflectedWorkItemIdField": null, + "EndpointEnrichers": null + } + }, + "Processors": [ + { + "ProcessorType": "WorkItemTrackingProcessor", + "Enabled": false, + "ReplayRevisions": false, + "CollapseRevisions": false, + "WorkItemCreateRetryLimit": 0, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "TfsTestPlansAndSuitesMigrationProcessor", + "Enabled": false, + "OnlyElementsWithTag": null, + "TestPlanQuery": null, + "RemoveAllLinks": false, + "MigrationDelay": 0, + "RemoveInvalidTestSuiteLinks": false, + "FilterCompleted": false, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "TfsWorkItemBulkEditProcessor", + "Enabled": false, + "WhatIf": false, + "WIQLQuery": "SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [@ReflectedWorkItemIdFieldName] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc", + "WorkItemIDs": null, + "FilterWorkItemsThatAlreadyExistInTarget": false, + "PauseAfterEachWorkItem": false, + "WorkItemCreateRetryLimit": 0, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "TfsEmptyProcessor", + "Enabled": false, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "TfsExportProfilePictureFromADProcessor", + "Enabled": false, + "Domain": null, + "Username": null, + "Password": null, + "PictureEmpIDFormat": null, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "TfsExportUsersForMappingProcessor", + "Enabled": false, + "WIQLQuery": null, + "OnlyListUsersInWorkItems": true, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "TfsImportProfilePictureProcessor", + "Enabled": false, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "TfsSharedQueryProcessor", + "Enabled": false, + "PrefixProjectToNodes": false, + "SharedFolderName": "Shared Queries", + "SourceToTargetFieldMappings": null, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "TfsTeamSettingsProcessor", + "Enabled": false, + "MigrateTeamSettings": false, + "UpdateTeamSettings": false, + "PrefixProjectToNodes": false, + "MigrateTeamCapacities": false, + "Teams": null, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "TfsTestConfigurationsMigrationProcessor", + "Enabled": false, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "TfsTestVariablesMigrationProcessor", + "Enabled": false, + "Processor": "TestVariablesMigrationContext", + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "TfsWorkItemDeleteProcessor", + "Enabled": false, + "WIQLQuery": "SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc", + "WorkItemIDs": null, + "FilterWorkItemsThatAlreadyExistInTarget": false, + "PauseAfterEachWorkItem": false, + "WorkItemCreateRetryLimit": 0, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "TfsWorkItemMigrationProcessor", + "Enabled": false, + "UpdateCreatedDate": true, + "UpdateCreatedBy": true, + "WIQLQuery": "SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc", + "FixHtmlAttachmentLinks": true, + "WorkItemCreateRetryLimit": 5, + "FilterWorkItemsThatAlreadyExistInTarget": false, + "PauseAfterEachWorkItem": false, + "AttachRevisionHistory": false, + "GenerateMigrationComment": true, + "WorkItemIDs": null, + "MaxGracefulFailures": 0, + "SkipRevisionWithInvalidIterationPath": false, + "SkipRevisionWithInvalidAreaPath": false, + "Enrichers": null, + "SourceName": "Source", + "TargetName": "Target", + "RefName": null + }, + { + "ProcessorType": "TfsWorkItemOverwriteAreasAsTagsProcessor", + "Enabled": false, + "AreaIterationPath": null, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "TfsWorkItemPostProcessingProcessor", + "Enabled": false, + "WorkItemIDs": null, + "WIQLQuery": "SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [@ReflectedWorkItemIdFieldName] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc", + "FilterWorkItemsThatAlreadyExistInTarget": false, + "PauseAfterEachWorkItem": false, + "WorkItemCreateRetryLimit": 0, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "AzureDevOpsPipelineProcessor", + "Enabled": false, + "MigrateBuildPipelines": true, + "MigrateReleasePipelines": true, + "MigrateTaskGroups": true, + "MigrateVariableGroups": true, + "MigrateServiceConnections": true, + "BuildPipelines": null, + "ReleasePipelines": null, + "RepositoryNameMaps": null, + "Enrichers": null, + "SourceName": "sourceName", + "TargetName": "targetName", + "RefName": null + }, + { + "ProcessorType": "ProcessDefinitionProcessor", + "Enabled": false, + "Processes": null, + "ProcessMaps": null, + "UpdateProcessDetails": false, + "MaxDegreeOfParallelism": 0, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "KeepOutboundLinkTargetProcessor", + "Enabled": false, + "WIQLQuery": "Select [System.Id] From WorkItems Where [System.TeamProject] = @project and not [System.WorkItemType] contains 'Test Suite, Test Plan,Shared Steps,Shared Parameter,Feedback Request'", + "TargetLinksToKeepOrganization": "https://dev.azure.com/nkdagility", + "TargetLinksToKeepProject": "d4d4097c-ebcc-4fee-9557-ec00f2ecf9d0", + "CleanupFileName": "c:/temp/OutboundLinkTargets.bat", + "PrependCommand": "start", + "DryRun": true, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + }, + { + "ProcessorType": "OutboundLinkCheckingProcessor", + "Enabled": false, + "WIQLQuery": null, + "ResultFileName": null, + "Enrichers": null, + "SourceName": null, + "TargetName": null, + "RefName": null + } + ], + "CommonTools": { + "FieldMappingTool": { + "Enabled": true, + "FieldMaps": [ + { + "FieldMapType": "FieldClearMap", + "targetField": "Custom.FieldC", + "ApplyTo": [ + "SomeWorkItemType" + ] + }, + { + "FieldMapType": "FieldLiteralMap", + "targetField": "Custom.SomeField", + "value": "New field value", + "ApplyTo": [ + "SomeWorkItemType" + ] + }, + { + "FieldMapType": "FieldMergeMap", + "sourceFields": [ + "Custom.FieldA", + "Custom.FieldB" + ], + "targetField": "Custom.FieldC", + "formatExpression": "{0} \n {1}", + "ApplyTo": [ + "SomeWorkItemType" + ] + }, + { + "FieldMapType": "FieldSkipMap", + "targetField": null, + "ApplyTo": [] + }, + { + "FieldMapType": "FieldToFieldMap", + "sourceField": "Microsoft.VSTS.Common.BacklogPriority", + "targetField": "Microsoft.VSTS.Common.StackRank", + "defaultValue": "42", + "ApplyTo": [ + "SomeWorkItemType" + ] + }, + { + "FieldMapType": "FieldToFieldMultiMap", + "SourceToTargetMappings": { + "SourceField1": "TargetField1", + "SourceField2": "TargetField2" + }, + "ApplyTo": [ + "SomeWorkItemType", + "SomeOtherWorkItemType" + ] + }, + { + "FieldMapType": "FieldToTagFieldMap", + "sourceField": null, + "formatExpression": "{0}

Acceptance Criteria

{1}", + "ApplyTo": [ + "SomeWorkItemType" + ] + }, + { + "FieldMapType": "FieldValueMap", + "sourceField": "System.State", + "targetField": "System.State", + "defaultValue": "StateB", + "valueMapping": { + "StateA": "StateB" + }, + "ApplyTo": [ + "SomeWorkItemType" + ] + }, + { + "FieldMapType": "FieldValueToTagMap", + "sourceField": "Microsoft.VSTS.CMMI.Blocked", + "pattern": "Yes", + "formatExpression": "{0}", + "ApplyTo": [ + "SomeWorkItemType" + ] + }, + { + "FieldMapType": "MultiValueConditionalMap", + "sourceFieldsAndValues": { + "Field1": "Value1", + "Field2": "Value2" + }, + "targetFieldsAndValues": { + "Field1": "Value1", + "Field2": "Value2" + }, + "ApplyTo": [ + "SomeWorkItemType" + ] + }, + { + "FieldMapType": "RegexFieldMap", + "sourceField": "COMPANY.PRODUCT.Release", + "targetField": "COMPANY.DEVISION.MinorReleaseVersion", + "pattern": "PRODUCT \\d{4}.(\\d{1})", + "replacement": "$1", + "ApplyTo": [ + "SomeWorkItemType" + ] + }, + { + "FieldMapType": "TreeToTagFieldMap", + "toSkip": 0, + "timeTravel": 0, + "ApplyTo": [] + } + ] + }, + "StringManipulatorTool": { + "Enabled": true, + "MaxStringLength": 1000000, + "Manipulators": [ + { + "Enabled": true, + "Pattern": "[^( -~)\n\r\t]+", + "Replacement": "", + "Description": "Remove invalid characters from the end of the string" + } + ] + }, + "WorkItemTypeMappingTool": { + "Enabled": true, + "Mappings": { + "User Story": "Product Backlog Item" + } + }, + "TfsAttachmentTool": { + "Enabled": true, + "ExportBasePath": "c:\\temp\\WorkItemAttachmentExport", + "MaxAttachmentSize": 480000000 + }, + "TfsChangeSetMappingTool": { + "Enabled": true, + "ChangeSetMappingFile": null + }, + "TfsEmbededImagesTool": { + "Enabled": true + }, + "TfsGitRepositoryTool": { + "Enabled": true, + "Mappings": { + "RepoInSource": "RepoInTarget" + } + }, + "TfsNodeStructureTool": { + "Enabled": true, + "Areas": { + "Filters": [ + "*\\Team 1,*\\Team 1\\**" + ], + "Mappings": { + "^7473924d-c47f-4089-8f5c-077c728b576e([\\\\]?.*)$": "MigrationTest5$1", + "^migrationSource1([\\\\]?.*)$": "MigrationTest5$1", + "^Skypoint Cloud([\\\\]?.*)$": "MigrationTest5$1" + } + }, + "Iterations": { + "Filters": [ + "*\\Sprint*", + "*\\Sprint*\\**" + ], + "Mappings": { + "^7473924d-c47f-4089-8f5c-077c728b576e([\\\\]?.*)$": "MigrationTest5$1", + "^migrationSource1([\\\\]?.*)$": "MigrationTest5$1", + "^Skypoint Cloud([\\\\]?.*)$": "MigrationTest5$1" + } + }, + "ShouldCreateMissingRevisionPaths": true, + "ReplicateAllExistingNodes": true + }, + "TfsRevisionManagerTool": { + "Enabled": true, + "ReplayRevisions": true, + "MaxRevisions": 0 + }, + "TfsTeamSettingsTool": { + "Enabled": true, + "MigrateTeamSettings": true, + "UpdateTeamSettings": true, + "MigrateTeamCapacities": true, + "Teams": [ + "Team 1", + "Team 2" + ] + }, + "TfsUserMappingTool": { + "Enabled": true, + "IdentityFieldsToCheck": [ + "System.AssignedTo", + "System.ChangedBy", + "System.CreatedBy", + "Microsoft.VSTS.Common.ActivatedBy", + "Microsoft.VSTS.Common.ResolvedBy", + "Microsoft.VSTS.Common.ClosedBy" + ], + "UserMappingFile": "C:\\temp\\userExport.json" + }, + "TfsValidateRequiredFieldTool": { + "Enabled": false + }, + "TfsWorkItemEmbededLinkTool": { + "Enabled": true + }, + "TfsWorkItemLinkTool": { + "Enabled": true, + "FilterIfLinkCountMatches": true, + "SaveAfterEachLinkIsAdded": false + } + } + } +} \ No newline at end of file diff --git a/docs/_includes/sampleConfig/configuration.json b/docs/_includes/sampleConfig/configuration.json index 71969bc50..d5d7fb942 100644 --- a/docs/_includes/sampleConfig/configuration.json +++ b/docs/_includes/sampleConfig/configuration.json @@ -4,59 +4,139 @@ }, "MigrationTools": { "Version": "16.0", - "Source": { - "EndpointType": "TfsTeamProject", - "Collection": "https://dev.azure.com/nkdagility-preview/", - "Project": "migrationSource1", - "AllowCrossProjectLinking": false, - "AuthenticationMode": "AccessToken", - "PersonalAccessToken": "", - "LanguageMaps": { - "AreaPath": "Area", - "IterationPath": "Iteration" - - } - }, - "Target": { - "EndpointType": "TfsTeamProject", - "Collection": "https://dev.azure.com/nkdagility-preview/", - "Project": "migrationTest5", - "TfsVersion": "AzureDevOps", - "ReflectedWorkItemIdField": "nkdScrum.ReflectedWorkItemId", - "AllowCrossProjectLinking": false, - "AuthenticationMode": "AccessToken", - "PersonalAccessToken": "", - "LanguageMaps": { - "AreaPath": "Area", - "IterationPath": "Iteration" + "Endpoints": { + "Source": { + "EndpointType": "TfsTeamProjectEndpoint", + "Collection": "https://dev.azure.com/nkdagility-preview/", + "Project": "migrationSource1", + "AllowCrossProjectLinking": false, + "ReflectedWorkItemIdField": "Custom.ReflectedWorkItemId", + "Authentication": { + "AuthenticationMode": "AccessToken", + "AccessToken": "", + "NetworkCredentials": { + "UserName": "", + "Password": "", + "Domain": "" + } + }, + "LanguageMaps": { + "AreaPath": "Area", + "IterationPath": "Iteration" + } + }, + "Target": { + "EndpointType": "TfsTeamProjectEndpoint", + "Collection": "https://dev.azure.com/nkdagility-preview/", + "Project": "migrationTest5", + "TfsVersion": "AzureDevOps", + "Authentication": { + "AuthenticationMode": "AccessToken", + "AccessToken": "", + "NetworkCredentials": { + "UserName": "", + "Password": "", + "Domain": "" + } + }, + "ReflectedWorkItemIdField": "nkdScrum.ReflectedWorkItemId", + "AllowCrossProjectLinking": false, + "LanguageMaps": { + "AreaPath": "Area", + "IterationPath": "Iteration" + } } }, - "CommonEnrichers": { - "TfsChangeSetMapping": { + "CommonTools": { + "WorkItemTypeMappingTool": { + "Enabled": true, + "Mappings": { + "User Story": "Product Backlog Item" + } + }, + "StringManipulatorTool": { + "Enabled": true, + "MaxStringLength": 1000000, + "Manipulators": [ + { + "$type": "RegexStringManipulator", + "Enabled": true, + "Pattern": "[^( -~)\n\r\t]+", + "Replacement": "", + "Description": "Remove invalid characters from the end of the string" + } + ] + }, + "TfsAttachmentTool": { + "RefName": "TfsAttachmentTool", + "Enabled": true, + "ExportBasePath": "c:\\temp\\WorkItemAttachmentExport", + "MaxRevisions": 480000000 + }, + "TfsChangeSetMappingTool": { "Enabled": true, "File": "C:\\temp\\ChangeSetMappingFile.json" }, - "TfsFieldMappings": { + "FieldMappingTool": { "Enabled": true, "FieldMaps": [ { - "FieldMapType": "FieldToFieledMap", + "FieldMapType": "FieldtoFieldMap", + "ApplyTo": [ "SomeWorkItemType" ], "sourceField": "System.AcceptanceCriteria", "targetField": "System.AcceptanceCriteria2" }, { - "FieldMapType": "FieldToFieledMap", + "FieldMapType": "FieldtoFieldMap", + "ApplyTo": [ "SomeWorkItemType" ], "sourceField": "System.Description", "targetField": "System.Description2" } ] + }, + "TfsGitRepositoryTool": { + "Enabled": true, + "Mappings": { + "Source Repo Name": "Target Repo Name" + } + }, + "TfsNodeStructureTool": { + "Enabled": true, + "Areas": { + "Filters": [ " *\\Team 1,*\\Team 1\\**" ], + "Mappings": { + "^Skypoint Cloud([\\\\]?.*)$": "MigrationTest5$1", + "^7473924d-c47f-4089-8f5c-077c728b576e([\\\\]?.*)$": "MigrationTest5$1" + } + }, + "Iterations": { + "Filters": [], + "Mappings": { + "^Skypoint Cloud([\\\\]?.*)$": "MigrationTest5$1", + "^7473924d-c47f-4089-8f5c-077c728b576e([\\\\]?.*)$": "MigrationTest5$1" + } + }, + "ShouldCreateMissingRevisionPaths": true, + "ReplicateAllExistingNodes": true + }, + "TfsRevisionManagerTool": { + "Enabled": true, + "ReplayRevisions": true, + "MaxRevisions": 0 + }, + "TfsTeamSettingsTool": { + "Enabled": true, + "MigrateTeamSettings": true, + "UpdateTeamSettings": true, + "MigrateTeamCapacities": true, + "Teams": [ "Team 1", "Team 2" ] } }, "Processors": [ { - "ProcessorType": "WorkItemMigration", + "ProcessorType": "TfsWorkItemMigrationProcessor", "Enabled": true, "UpdateCreatedDate": true, "UpdateCreatedBy": true, @@ -67,7 +147,7 @@ "PauseAfterEachWorkItem": false, "AttachRevisionHistory": false, "GenerateMigrationComment": true, - "WorkItemIDs": [], + "WorkItemIDs": [ 12 ], "MaxGracefulFailures": 0, "SkipRevisionWithInvalidIterationPath": false, "SkipRevisionWithInvalidAreaPath": false diff --git a/docs/collections/_reference/reference.endpoints.tfsendpoint.md b/docs/collections/_reference/reference.endpoints.tfsendpoint.md index 9a3302732..9714edfb7 100644 --- a/docs/collections/_reference/reference.endpoints.tfsendpoint.md +++ b/docs/collections/_reference/reference.endpoints.tfsendpoint.md @@ -12,7 +12,7 @@ configurationSamples: "TfsEndpoint": { "AllowCrossProjectLinking": "False", "Authentication": { - "AccessToken": "12345", + "AccessToken": "", "AuthenticationMode": "AccessToken", "NetworkCredentials": { "Domain": "", @@ -77,9 +77,9 @@ configurationSamples: "NetworkCredentials": { "Domain": "", "UserName": "", - "Password": "" + "Password": "** removed as a secret ***" }, - "AccessToken": "jklsadhjksahfkjsdhjksahsadjhksadhsad" + "AccessToken": "** removed as a secret ***" }, "ReflectedWorkItemIdField": null, "AllowCrossProjectLinking": false, diff --git a/docs/collections/_reference/reference.endpoints.tfsteamprojectendpoint.md b/docs/collections/_reference/reference.endpoints.tfsteamprojectendpoint.md index 7b8896690..ba4aaa97d 100644 --- a/docs/collections/_reference/reference.endpoints.tfsteamprojectendpoint.md +++ b/docs/collections/_reference/reference.endpoints.tfsteamprojectendpoint.md @@ -12,7 +12,7 @@ configurationSamples: "TfsTeamProjectEndpoint": { "AllowCrossProjectLinking": "False", "Authentication": { - "AccessToken": "12345", + "AccessToken": "", "AuthenticationMode": "AccessToken", "NetworkCredentials": { "Domain": "", @@ -77,9 +77,9 @@ configurationSamples: "NetworkCredentials": { "Domain": "", "UserName": "", - "Password": "" + "Password": "** removed as a secret ***" }, - "AccessToken": "jklsadhjksahfkjsdhjksahsadjhksadhsad" + "AccessToken": "** removed as a secret ***" }, "ReflectedWorkItemIdField": null, "AllowCrossProjectLinking": false, diff --git a/docs/collections/_reference/reference.fieldmaps.treetotagfieldmap.md b/docs/collections/_reference/reference.fieldmaps.treetotagfieldmap.md index bf7451124..2bf3d5f06 100644 --- a/docs/collections/_reference/reference.fieldmaps.treetotagfieldmap.md +++ b/docs/collections/_reference/reference.fieldmaps.treetotagfieldmap.md @@ -58,7 +58,7 @@ options: status: missng XML code comments processingTarget: missng XML code comments classFile: /src/MigrationTools.Clients.TfsObjectModel/Tools/FieldMappingTool/FieldMaps/TreeToTagFieldMap.cs -optionsClassFile: '' +optionsClassFile: /src/MigrationTools/Tools/FieldMappingTool/FieldMaps/TreeToTagFieldMapOptions.cs redirectFrom: - /Reference/FieldMaps/TreeToTagFieldMapOptions/ diff --git a/docs/collections/_reference/reference.processors.keepoutboundlinktargetprocessor.md b/docs/collections/_reference/reference.processors.keepoutboundlinktargetprocessor.md index fc50b458d..006fe792b 100644 --- a/docs/collections/_reference/reference.processors.keepoutboundlinktargetprocessor.md +++ b/docs/collections/_reference/reference.processors.keepoutboundlinktargetprocessor.md @@ -18,7 +18,7 @@ configurationSamples: "Enabled": false, "WIQLQuery": "Select [System.Id] From WorkItems Where [System.TeamProject] = @project and not [System.WorkItemType] contains 'Test Suite, Test Plan,Shared Steps,Shared Parameter,Feedback Request'", "TargetLinksToKeepOrganization": "https://dev.azure.com/nkdagility", - "TargetLinksToKeepProject": "5f1e7946-3c7a-4168-866b-8d1451f41fe3", + "TargetLinksToKeepProject": "e898a78b-b896-4fbe-b1c3-12cba16fce77", "CleanupFileName": "c:/temp/OutboundLinkTargets.bat", "PrependCommand": "start", "DryRun": true, diff --git a/docs/collections/_reference/reference.processors.tfsworkitemoverwriteprocessor.md b/docs/collections/_reference/reference.processors.tfsworkitemoverwriteprocessor.md index ce6e432df..693c8f25f 100644 --- a/docs/collections/_reference/reference.processors.tfsworkitemoverwriteprocessor.md +++ b/docs/collections/_reference/reference.processors.tfsworkitemoverwriteprocessor.md @@ -16,19 +16,11 @@ configurationSamples: { "$type": "TfsWorkItemOverwriteProcessorOptions", "Enabled": false, - "UpdateCreatedDate": false, - "UpdateCreatedBy": false, - "WIQLQuery": null, - "FixHtmlAttachmentLinks": false, - "WorkItemCreateRetryLimit": 0, + "WorkItemIDs": null, + "WIQLQuery": "SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [@ReflectedWorkItemIdField] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc", "FilterWorkItemsThatAlreadyExistInTarget": false, "PauseAfterEachWorkItem": false, - "AttachRevisionHistory": false, - "GenerateMigrationComment": false, - "WorkItemIDs": null, - "MaxGracefulFailures": 0, - "SkipRevisionWithInvalidIterationPath": false, - "SkipRevisionWithInvalidAreaPath": false, + "WorkItemCreateRetryLimit": 0, "Enrichers": null, "SourceName": null, "TargetName": null, @@ -40,10 +32,6 @@ className: TfsWorkItemOverwriteProcessor typeName: Processors architecture: options: -- parameterName: AttachRevisionHistory - type: Boolean - description: This will create a json file with the revision history and attach it to the work item. Best used with `MaxRevisions` or `ReplayRevisions`. - defaultValue: '?' - parameterName: Enabled type: Boolean description: If set to `true` then the processor will run. Set to `false` and the processor will not run. @@ -56,18 +44,6 @@ options: type: Boolean description: This loads all of the work items already saved to the Target and removes them from the Source work item list prior to commencing the run. While this may take some time in large data sets it reduces the time of the overall migration significantly if you need to restart. defaultValue: true -- parameterName: FixHtmlAttachmentLinks - type: Boolean - description: "**beta** If enabled this will fix any image attachments URL's, work item mention URL's or user mentions in the HTML fields as well as discussion comments. You must specify a PersonalAccessToken in the Source project for Azure DevOps; TFS should use integrated authentication." - defaultValue: '?' -- parameterName: GenerateMigrationComment - type: Boolean - description: If enabled, adds a comment recording the migration - defaultValue: false -- parameterName: MaxGracefulFailures - type: Int32 - description: The maximum number of failures to tolerate before the migration fails. When set above zero, a work item migration error is logged but the migration will continue until the number of failed items reaches the configured value, after which the migration fails. - defaultValue: 0 - parameterName: PauseAfterEachWorkItem type: Boolean description: Pause after each work item is migrated @@ -76,14 +52,6 @@ options: type: String description: '`Refname` will be used in the future to allow for using named Options without the need to copy all of the options.' defaultValue: missng XML code comments -- parameterName: SkipRevisionWithInvalidAreaPath - type: Boolean - description: When set to true, this setting will skip a revision if the source area has not been migrated, has been deleted or is somehow invalid, etc. - defaultValue: missng XML code comments -- parameterName: SkipRevisionWithInvalidIterationPath - type: Boolean - description: This will skip a revision if the source iteration has not been migrated i.e. it was deleted - defaultValue: missng XML code comments - parameterName: SourceName type: String description: missng XML code comments @@ -92,18 +60,10 @@ options: type: String description: missng XML code comments defaultValue: missng XML code comments -- parameterName: UpdateCreatedBy - type: Boolean - description: "If this is enabled the creation process on the target project will create the items with the original creation date. (Important: The item history is always pointed to the date of the migration, it's change only the data column CreateDate, not the internal create date)" - defaultValue: true -- parameterName: UpdateCreatedDate - type: Boolean - description: "If this is enabled the creation process on the target project will create the items with the original creation date. (Important: The item history is always pointed to the date of the migration, it's change only the data column CreateDate, not the internal create date)" - defaultValue: true - parameterName: WIQLQuery type: String description: A work item query based on WIQL to select only important work items. To migrate all leave this empty. See [WIQL Query Bits](#wiql-query-bits) - defaultValue: SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [[System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc + defaultValue: AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') - parameterName: WorkItemCreateRetryLimit type: Int32 description: '**beta** If set to a number greater than 0 work items that fail to save will retry after a number of seconds equal to the retry count. This allows for periodic network glitches not to end the process.' diff --git a/src/MigrationTools.Clients.TfsObjectModel/Processors/TfsTestPlansAndSuitesMigrationProcessor.cs b/src/MigrationTools.Clients.TfsObjectModel/Processors/TfsTestPlansAndSuitesMigrationProcessor.cs index cfdeeec64..a14112d6b 100644 --- a/src/MigrationTools.Clients.TfsObjectModel/Processors/TfsTestPlansAndSuitesMigrationProcessor.cs +++ b/src/MigrationTools.Clients.TfsObjectModel/Processors/TfsTestPlansAndSuitesMigrationProcessor.cs @@ -52,7 +52,7 @@ public class TfsTestPlansAndSuitesMigrationProcessor : TfsProcessor private int _totalTestCases = 0; - private static readonly Meter _meter = new Meter("MigrationTools.TestPlansAndSuitesMigrationProcessor", "1.0.0"); + private static readonly Meter _meter = new Meter("MigrationTools.TestPlans"); private static readonly Counter _testPlansCounter = _meter.CreateCounter("test_plans_count"); private static readonly Counter _testSuitesCounter = _meter.CreateCounter("test_suites_count"); private static readonly Counter _testCasesCounter = _meter.CreateCounter("test_cases_count"); diff --git a/src/MigrationTools.Clients.TfsObjectModel/Processors/TfsWorkItemMigrationProcessor.cs b/src/MigrationTools.Clients.TfsObjectModel/Processors/TfsWorkItemMigrationProcessor.cs index 5703698f4..df9e553c5 100644 --- a/src/MigrationTools.Clients.TfsObjectModel/Processors/TfsWorkItemMigrationProcessor.cs +++ b/src/MigrationTools.Clients.TfsObjectModel/Processors/TfsWorkItemMigrationProcessor.cs @@ -61,9 +61,12 @@ public class TfsWorkItemMigrationProcessor : TfsProcessor private ILogger workItemLog; private List _itemsInError; + public WorkItemMetrics workItemMetrics { get; private set; } + public TfsWorkItemMigrationProcessor(IOptions options, TfsCommonTools tfsCommonTools, ProcessorEnricherContainer processorEnrichers, IServiceProvider services, ITelemetryLogger telemetry, ILogger logger) : base(options, tfsCommonTools, processorEnrichers, services, telemetry, logger) { contextLog = Serilog.Log.ForContext(); + workItemMetrics = services.GetRequiredService(); } new TfsWorkItemMigrationProcessorOptions Options => (TfsWorkItemMigrationProcessorOptions)base.Options; @@ -85,9 +88,6 @@ internal void TraceWriteLine(LogEventLevel level, string message, Dictionary WorkItemsProcessedCounter = WorkItemMeter.CreateCounter("work_items_processed_total"); - private static readonly Histogram WorkItemProcessingDurationHistogram = WorkItemMeter.CreateHistogram("work_item_processing_duration"); protected override void InternalExecute() { @@ -161,8 +161,10 @@ protected override void InternalExecute() _count = sourceWorkItems.Count; _elapsedms = 0; _totalWorkItem = sourceWorkItems.Count; + ProcessorActivity.SetTag("source_workitems_to_process", sourceWorkItems.Count); foreach (WorkItemData sourceWorkItemData in sourceWorkItems) { + var stopwatch = Stopwatch.StartNew(); var sourceWorkItem = TfsExtensions.ToWorkItem(sourceWorkItemData); workItemLog = contextLog.ForContext("SourceWorkItemId", sourceWorkItem.Id); @@ -179,8 +181,8 @@ protected override void InternalExecute() stopwatch.Stop(); var processingTime = stopwatch.Elapsed.TotalMilliseconds; - WorkItemsProcessedCounter.Add(1, new KeyValuePair("workItemType", sourceWorkItemData.Type)); - WorkItemProcessingDurationHistogram.Record(processingTime, new KeyValuePair("workItemType", sourceWorkItemData.Type)); + workItemMetrics.WorkItemsProcessedCount.Add(1, new KeyValuePair("workItemType", sourceWorkItemData.Type)); + workItemMetrics.ProcessingDuration.Record(processingTime, new KeyValuePair("workItemType", sourceWorkItemData.Type)); if (Options.PauseAfterEachWorkItem) { Console.WriteLine("Do you want to continue? (y/n)"); @@ -278,7 +280,7 @@ private void ValidateAllWorkItemTypesHaveReflectedWorkItemIdField(List sourceWorkItems) { contextLog.Information("Validating::Check that all work item types needed in the Target exist or are mapped"); - // get list of all work item types + // get list of all work item types List sourceWorkItemTypes = sourceWorkItems.SelectMany(x => x.Revisions.Values) //.Where(x => x.Fields[fieldName].Value.ToString().Contains("\\")) .Select(x => x.Type) @@ -521,117 +523,118 @@ private async Task ProcessWorkItemAsync(WorkItemData sourceWorkItem, int retryLi activity?.SetTag("TargetURL", Target.Options.Collection.ToString()); activity?.SetTag("TargetProject", Target.WorkItems.Project.Name); activity?.SetTag("RetryLimit", retryLimit.ToString()); - activity?.SetTag("RetryNumber", retries.ToString()); - Log.LogDebug("######################################################################################"); - Log.LogDebug("ProcessWorkItem: {sourceWorkItemId}", sourceWorkItem.Id); - Log.LogDebug("######################################################################################"); - try - { - if (sourceWorkItem.Type != "Test Plan" && sourceWorkItem.Type != "Test Suite") + activity?.SetTag("RetryNumber", retries.ToString()); + Log.LogDebug("######################################################################################"); + Log.LogDebug("ProcessWorkItem: {sourceWorkItemId}", sourceWorkItem.Id); + Log.LogDebug("######################################################################################"); + try { - var targetWorkItem = Target.WorkItems.FindReflectedWorkItem(sourceWorkItem, false); - /////////////////////////////////////////////// - TraceWriteLine(LogEventLevel.Information, "Work Item has {sourceWorkItemRev} revisions and revision migration is set to {ReplayRevisions}", - new Dictionary(){ + if (sourceWorkItem.Type != "Test Plan" && sourceWorkItem.Type != "Test Suite") + { + workItemMetrics.RevisionsPerWorkItem.Record(sourceWorkItem.Rev); + var targetWorkItem = Target.WorkItems.FindReflectedWorkItem(sourceWorkItem, false); + /////////////////////////////////////////////// + TraceWriteLine(LogEventLevel.Information, "Work Item has {sourceWorkItemRev} revisions and revision migration is set to {ReplayRevisions}", + new Dictionary(){ { "sourceWorkItemRev", sourceWorkItem.Rev }, { "ReplayRevisions", CommonTools.RevisionManager.ReplayRevisions }} - ); - List revisionsToMigrate = CommonTools.RevisionManager.GetRevisionsToMigrate(sourceWorkItem.Revisions.Values.ToList(), targetWorkItem?.Revisions.Values.ToList()); - if (targetWorkItem == null) - { - targetWorkItem = ReplayRevisions(revisionsToMigrate, sourceWorkItem, null); + ); + List revisionsToMigrate = CommonTools.RevisionManager.GetRevisionsToMigrate(sourceWorkItem.Revisions.Values.ToList(), targetWorkItem?.Revisions.Values.ToList()); + if (targetWorkItem == null) + { + targetWorkItem = ReplayRevisions(revisionsToMigrate, sourceWorkItem, null); activity?.SetTag("Revisions", revisionsToMigrate.Count); - } - else - { - if (revisionsToMigrate.Count == 0) - { - ProcessWorkItemAttachments(sourceWorkItem, targetWorkItem, false); - ProcessWorkItemLinks(Source.WorkItems, Target.WorkItems, sourceWorkItem, targetWorkItem); - ProcessHTMLFieldAttachements(targetWorkItem); - ProcessWorkItemEmbeddedLinks(sourceWorkItem, targetWorkItem); - TraceWriteLine(LogEventLevel.Information, "Skipping as work item exists and no revisions to sync detected"); - activity?.SetTag("Revisions", 0); } else { - TraceWriteLine(LogEventLevel.Information, "Syncing as there are {revisionsToMigrateCount} revisions detected", - new Dictionary(){ + if (revisionsToMigrate.Count == 0) + { + ProcessWorkItemAttachments(sourceWorkItem, targetWorkItem, false); + ProcessWorkItemLinks(Source.WorkItems, Target.WorkItems, sourceWorkItem, targetWorkItem); + ProcessHTMLFieldAttachements(targetWorkItem); + ProcessWorkItemEmbeddedLinks(sourceWorkItem, targetWorkItem); + TraceWriteLine(LogEventLevel.Information, "Skipping as work item exists and no revisions to sync detected"); + activity?.SetTag("Revisions", 0); + } + else + { + TraceWriteLine(LogEventLevel.Information, "Syncing as there are {revisionsToMigrateCount} revisions detected", + new Dictionary(){ { "revisionsToMigrateCount", revisionsToMigrate.Count } - }); + }); - targetWorkItem = ReplayRevisions(revisionsToMigrate, sourceWorkItem, targetWorkItem); + targetWorkItem = ReplayRevisions(revisionsToMigrate, sourceWorkItem, targetWorkItem); + } + } + if (targetWorkItem != null && targetWorkItem.ToWorkItem().IsDirty) + { + targetWorkItem.SaveToAzureDevOps(); + } + if (targetWorkItem != null) + { + targetWorkItem.ToWorkItem().Close(); + } + if (sourceWorkItem != null) + { + sourceWorkItem.ToWorkItem().Close(); } } - if (targetWorkItem != null && targetWorkItem.ToWorkItem().IsDirty) - { - targetWorkItem.SaveToAzureDevOps(); - } - if (targetWorkItem != null) - { - targetWorkItem.ToWorkItem().Close(); - } - if (sourceWorkItem != null) + else { - sourceWorkItem.ToWorkItem().Close(); - } - } - else - { - TraceWriteLine(LogEventLevel.Warning, "SKIP: Unable to migrate {sourceWorkItemTypeName}/{sourceWorkItemId}. Use the TestPlansAndSuitesMigrationContext after you have migrated all Test Cases. ", - new Dictionary() { + TraceWriteLine(LogEventLevel.Warning, "SKIP: Unable to migrate {sourceWorkItemTypeName}/{sourceWorkItemId}. Use the TestPlansAndSuitesMigrationContext after you have migrated all Test Cases. ", + new Dictionary() { {"sourceWorkItemTypeName", sourceWorkItem.Type }, {"sourceWorkItemId", sourceWorkItem.Id } - }); + }); + } } - } - catch (WebException ex) - { - Log.LogError(ex, "Some kind of internet pipe blockage"); - if (retries < retryLimit) + catch (WebException ex) { - TraceWriteLine(LogEventLevel.Warning, "WebException: Will retry in {retrys}s ", - new Dictionary() { + Log.LogError(ex, "Some kind of internet pipe blockage"); + if (retries < retryLimit) + { + TraceWriteLine(LogEventLevel.Warning, "WebException: Will retry in {retrys}s ", + new Dictionary() { {"retrys", retries } - }); - System.Threading.Thread.Sleep(new TimeSpan(0, 0, retries)); - retries++; - TraceWriteLine(LogEventLevel.Warning, "RETRY {Retrys}/{RetryLimit} ", - new Dictionary() { + }); + System.Threading.Thread.Sleep(new TimeSpan(0, 0, retries)); + retries++; + TraceWriteLine(LogEventLevel.Warning, "RETRY {Retrys}/{RetryLimit} ", + new Dictionary() { {"Retrys", retries }, {"RetryLimit", retryLimit } - }); - await ProcessWorkItemAsync(sourceWorkItem, retryLimit, retries); + }); + await ProcessWorkItemAsync(sourceWorkItem, retryLimit, retries); + } + else + { + TraceWriteLine(LogEventLevel.Error, "ERROR: Failed to create work item. Retry Limit reached "); + } } - else + catch (Exception ex) { - TraceWriteLine(LogEventLevel.Error, "ERROR: Failed to create work item. Retry Limit reached "); + activity?.Stop(); + activity?.SetStatus(ActivityStatusCode.Error); + activity?.SetTag("http.response.status_code", "502"); + Log.LogError(ex, ex.ToString()); + Telemetry.TrackException(ex, activity.Tags); + throw ex; } - } - catch (Exception ex) - { - activity?.Stop(); - activity?.SetStatus(ActivityStatusCode.Error); - activity?.SetTag("http.response.status_code", "502"); - Log.LogError(ex, ex.ToString()); - Telemetry.TrackException(ex, activity.Tags); - throw ex; - } - var average = new TimeSpan(0, 0, 0, 0, (int)(activity.Duration.TotalMilliseconds / _current)); - var remaining = new TimeSpan(0, 0, 0, 0, (int)(average.TotalMilliseconds * _count)); - TraceWriteLine(LogEventLevel.Information, - "Average time of {average:%s}.{average:%fff} per work item and {remaining:%h} hours {remaining:%m} minutes {remaining:%s}.{remaining:%fff} seconds estimated to completion", - new Dictionary() { + var average = new TimeSpan(0, 0, 0, 0, (int)(activity.Duration.TotalMilliseconds / _current)); + var remaining = new TimeSpan(0, 0, 0, 0, (int)(average.TotalMilliseconds * _count)); + TraceWriteLine(LogEventLevel.Information, + "Average time of {average:%s}.{average:%fff} per work item and {remaining:%h} hours {remaining:%m} minutes {remaining:%s}.{remaining:%fff} seconds estimated to completion", + new Dictionary() { {"average", average}, {"remaining", remaining} - }); + }); activity?.Stop(); activity?.SetStatus(ActivityStatusCode.Error); activity?.SetTag("http.response.status_code", "200"); - _current++; - _count--; + _current++; + _count--; } } @@ -653,7 +656,7 @@ private void ProcessWorkItemLinks(IWorkItemMigrationClient sourceStore, IWorkIte CommonTools.WorkItemLink.Enrich(this, sourceWorkItem, targetWorkItem); //AddMetric("RelatedLinkCount", processWorkItemMetrics, targetWorkItem.ToWorkItem().Links.Count); int fixedLinkCount = CommonTools.GitRepository.Enrich(this, sourceWorkItem, targetWorkItem); - // AddMetric("FixedGitLinkCount", processWorkItemMetrics, fixedLinkCount); + // AddMetric("FixedGitLinkCount", processWorkItemMetrics, fixedLinkCount); } else if (targetWorkItem != null && sourceWorkItem.ToWorkItem().Links.Count > 0 && sourceWorkItem.Type == "Test Case") { @@ -691,6 +694,7 @@ private WorkItemData ReplayRevisions(List revisionsToMigrate, Work foreach (var revision in revisionsToMigrate) { + workItemMetrics.RevisionsProcessedCount.Add(1); var currentRevisionWorkItem = sourceWorkItem.GetRevision(revision.Number); TraceWriteLine(LogEventLevel.Information, " Processing Revision [{RevisionNumber}]", diff --git a/src/MigrationTools.Clients.TfsObjectModel/Processors/TfsWorkItemOverwriteProcessor.cs b/src/MigrationTools.Clients.TfsObjectModel/Processors/TfsWorkItemOverwriteProcessor.cs index 2a4825a24..a24ad82b6 100644 --- a/src/MigrationTools.Clients.TfsObjectModel/Processors/TfsWorkItemOverwriteProcessor.cs +++ b/src/MigrationTools.Clients.TfsObjectModel/Processors/TfsWorkItemOverwriteProcessor.cs @@ -26,11 +26,11 @@ namespace MigrationTools.Processors /// Work Items public class TfsWorkItemOverwriteProcessor : TfsProcessor { - public TfsWorkItemOverwriteProcessor(IOptions options, TfsCommonTools tfsStaticTools, ProcessorEnricherContainer processorEnrichers, IServiceProvider services, ITelemetryLogger telemetry, ILogger logger) : base(options, tfsStaticTools, processorEnrichers, services, telemetry, logger) + public TfsWorkItemOverwriteProcessor(IOptions options, TfsCommonTools tfsStaticTools, ProcessorEnricherContainer processorEnrichers, IServiceProvider services, ITelemetryLogger telemetry, ILogger logger) : base(options, tfsStaticTools, processorEnrichers, services, telemetry, logger) { } - new TfsWorkItemPostProcessingProcessorOptions Options => (TfsWorkItemPostProcessingProcessorOptions)base.Options; + new TfsWorkItemOverwriteProcessorOptions Options => (TfsWorkItemOverwriteProcessorOptions)base.Options; new TfsTeamProjectEndpoint Source => (TfsTeamProjectEndpoint)base.Source; diff --git a/src/MigrationTools.Clients.TfsObjectModel/Processors/TfsWorkItemOverwriteProcessorOptions.cs b/src/MigrationTools.Clients.TfsObjectModel/Processors/TfsWorkItemOverwriteProcessorOptions.cs index 85d06b8c8..a1896d9c8 100644 --- a/src/MigrationTools.Clients.TfsObjectModel/Processors/TfsWorkItemOverwriteProcessorOptions.cs +++ b/src/MigrationTools.Clients.TfsObjectModel/Processors/TfsWorkItemOverwriteProcessorOptions.cs @@ -7,7 +7,7 @@ namespace MigrationTools.Processors { - public class TfsWorkItemPostProcessingProcessorOptions : ProcessorOptions, IWorkItemProcessorConfig + public class TfsWorkItemOverwriteProcessorOptions : ProcessorOptions, IWorkItemProcessorConfig { /// @@ -46,7 +46,7 @@ public class TfsWorkItemPostProcessingProcessorOptions : ProcessorOptions, IWork public int WorkItemCreateRetryLimit { get; set; } - public TfsWorkItemPostProcessingProcessorOptions() + public TfsWorkItemOverwriteProcessorOptions() { WIQLQuery = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [@ReflectedWorkItemIdField] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan','Shared Steps','Shared Parameter','Feedback Request') ORDER BY [System.ChangedDate] desc"; } diff --git a/src/MigrationTools.Clients.TfsObjectModel/Tools/TfsNodeStructureTool.cs b/src/MigrationTools.Clients.TfsObjectModel/Tools/TfsNodeStructureTool.cs index 41f3ee139..e7c47ff18 100644 --- a/src/MigrationTools.Clients.TfsObjectModel/Tools/TfsNodeStructureTool.cs +++ b/src/MigrationTools.Clients.TfsObjectModel/Tools/TfsNodeStructureTool.cs @@ -17,6 +17,7 @@ using MigrationTools.FieldMaps; using MigrationTools.Processors; using MigrationTools.Processors.Infrastructure; +using MigrationTools.Services; using MigrationTools.Tools.Infrastructure; using Newtonsoft.Json; using Serilog.Context; @@ -62,10 +63,12 @@ public class TfsNodeStructureTool : Tool private string _targetProjectName; private KeyValuePair? _lastResortRemapRule; + public WorkItemMetrics workItemMetrics { get; private set; } + public TfsNodeStructureTool(IOptions options, IServiceProvider services, ILogger logger, ITelemetryLogger telemetryLogger) : base(options, services, logger, telemetryLogger) { - + workItemMetrics = services.GetRequiredService(); } public void ApplySettings(TfsNodeStructureToolSettings settings) diff --git a/src/MigrationTools.ConsoleFull/Properties/launchSettings.json b/src/MigrationTools.ConsoleFull/Properties/launchSettings.json index 321faa533..34cf1b991 100644 --- a/src/MigrationTools.ConsoleFull/Properties/launchSettings.json +++ b/src/MigrationTools.ConsoleFull/Properties/launchSettings.json @@ -17,7 +17,7 @@ }, "init Options-Basic": { "commandName": "Project", - "commandLineArgs": "init --options Basic --overwrite" + "commandLineArgs": "init --options Basic --overwrite -c configuration-basic.json " }, "init Options-Reference": { "commandName": "Project", @@ -25,7 +25,7 @@ }, "Upgrade": { "commandName": "Project", - "commandLineArgs": "upgrade -c \"configuration.json\" --debugTrace" + "commandLineArgs": "upgrade -c \"C:\\Users\\MartinHinshelwoodNKD\\source\\repos\\azure-devops-migration-tools\\docs\\_includes\\sampleConfig\\configuration-full.json\" --debugTrace" }, "Execute Classic": { "commandName": "Project", diff --git a/src/MigrationTools.Shadows/ServiceCollectionExtensions.cs b/src/MigrationTools.Shadows/ServiceCollectionExtensions.cs index fbdf13c7b..6965ea029 100644 --- a/src/MigrationTools.Shadows/ServiceCollectionExtensions.cs +++ b/src/MigrationTools.Shadows/ServiceCollectionExtensions.cs @@ -1,4 +1,5 @@ -using Microsoft.Extensions.DependencyInjection; +using System.Diagnostics.Metrics; +using Microsoft.Extensions.DependencyInjection; using MigrationTools.Clients; using MigrationTools.Clients.Shadows; using MigrationTools.Services; @@ -32,6 +33,11 @@ public static void AddMigrationToolServicesForUnitTests(this IServiceCollection context.AddSingleton(); context.AddSingleton(); context.AddSingleton(); + + + // Meters + context.AddSingleton(); + context.AddSingleton(); } } } diff --git a/src/MigrationTools.Shadows/Services/MeterFactoryFake.cs b/src/MigrationTools.Shadows/Services/MeterFactoryFake.cs new file mode 100644 index 000000000..b9663134a --- /dev/null +++ b/src/MigrationTools.Shadows/Services/MeterFactoryFake.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Metrics; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MigrationTools.Services.Shadows +{ + public class MeterFactoryFake : IMeterFactory + { + public Meter Create(MeterOptions options) + { + return new Meter(options); + } + + public void Dispose() + { + // do nothing + } + } +} diff --git a/src/MigrationTools/ServiceCollectionExtensions.cs b/src/MigrationTools/ServiceCollectionExtensions.cs index 40e0fd1a4..e6fc3d095 100644 --- a/src/MigrationTools/ServiceCollectionExtensions.cs +++ b/src/MigrationTools/ServiceCollectionExtensions.cs @@ -11,6 +11,7 @@ using MigrationTools.Options; using MigrationTools.Processors; using MigrationTools.Processors.Infrastructure; +using MigrationTools.Services; using MigrationTools.Tools; using MigrationTools.Tools.Interfaces; using Serilog; @@ -33,6 +34,8 @@ public static void AddMigrationToolServices(this IServiceCollection context, ICo context.AddSingleton(); + + context.AddConfiguredEndpoints(configuration); //Containers context.AddTransient(); diff --git a/src/MigrationTools/Services/ActivitySourceProvider.cs b/src/MigrationTools/Services/ActivitySourceProvider.cs index e8e9d585b..492b44243 100644 --- a/src/MigrationTools/Services/ActivitySourceProvider.cs +++ b/src/MigrationTools/Services/ActivitySourceProvider.cs @@ -136,6 +136,8 @@ public static IHostBuilder UseOpenTelemitery(this IHostBuilder builder, string v services.AddOptions(); + services.AddSingleton(); + // Configure OpenTelemetry Assembly entryAssembly = Assembly.GetEntryAssembly(); string entryAssemblyName = entryAssembly?.GetName().Name; @@ -160,6 +162,7 @@ public static IHostBuilder UseOpenTelemitery(this IHostBuilder builder, string v { builder .SetResourceBuilder(ResourceBuilder.CreateDefault().AddService(entryAssemblyName, serviceVersion: versionString)) + .AddMeter("MigrationTools.TestPlans", WorkItemMetrics.meterName) .AddHttpClientInstrumentation() .AddRuntimeInstrumentation() .AddProcessInstrumentation() @@ -202,7 +205,6 @@ public static void SetTagsFromOptions(this Activity activity, IOptions options) if (value != null) { - activity.SetTag($"options.{property.Name}", value.ToString()); activity.SetTag($"{options.GetType().Name}.{property.Name}", value.ToString()); } } diff --git a/src/MigrationTools/Services/TelemetryClientAdapter.cs b/src/MigrationTools/Services/TelemetryClientAdapter.cs index 17aaea2e5..2c3cb318c 100644 --- a/src/MigrationTools/Services/TelemetryClientAdapter.cs +++ b/src/MigrationTools/Services/TelemetryClientAdapter.cs @@ -63,7 +63,8 @@ public void TrackException(Exception ex, IDictionary properties) } var result = elmahIoClient.Messages.CreateAndNotify(new Guid("24086b6d-4f58-47f4-8ac7-68d8bc05ca9e"), createMessage); - Console.WriteLine($"Error logged to Elmah.io"); + Console.WriteLine($"Error logged to Elmah.io! "); + Console.WriteLine($"!! Check for latest version - We fix issues constantly - If not, please create a discussion on https://github.com/nkdAgility/azure-devops-migration-tools/discussions so we can get this fixed !!"); } public void TrackException(Exception ex, IEnumerable> properties = null) diff --git a/src/MigrationTools/Services/WorkItemMetrics.cs b/src/MigrationTools/Services/WorkItemMetrics.cs new file mode 100644 index 000000000..f56d47619 --- /dev/null +++ b/src/MigrationTools/Services/WorkItemMetrics.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Metrics; +using System.Text; +using Microsoft.Extensions.Configuration; + +namespace MigrationTools.Services +{ + public class WorkItemMetrics + { + public static readonly string meterName = "MigrationTools.WorkItems"; + + private readonly Meter WorkItemMeter; + public Counter WorkItemsProcessedCount { get; private set ; } + public Counter RevisionsProcessedCount { get; private set; } + public Histogram ProcessingDuration { get; private set; } + + public Histogram RevisionsPerWorkItem { get; private set; } + + public WorkItemMetrics(IMeterFactory meterFactory) + { + WorkItemMeter = meterFactory.Create(meterName); + WorkItemsProcessedCount = WorkItemMeter.CreateCounter("work_items_processed_total"); + ProcessingDuration = WorkItemMeter.CreateHistogram("work_item_processing_duration"); + RevisionsPerWorkItem = WorkItemMeter.CreateHistogram("work_item_revisions"); + RevisionsProcessedCount = WorkItemMeter.CreateCounter("work_item_revisions_total"); + } + } +} diff --git a/src/MigrationTools/Tools/FieldMappingTool/FieldMaps/TreeToTagMapOptions.cs b/src/MigrationTools/Tools/FieldMappingTool/FieldMaps/TreeToTagFieldMapOptions.cs similarity index 100% rename from src/MigrationTools/Tools/FieldMappingTool/FieldMaps/TreeToTagMapOptions.cs rename to src/MigrationTools/Tools/FieldMappingTool/FieldMaps/TreeToTagFieldMapOptions.cs