Skip to content

Commit

Permalink
Updated TfsNodeStructureTool to use globs for filters and apply them …
Browse files Browse the repository at this point in the history
…to either areas or iterations.
  • Loading branch information
MrHinsh committed Aug 23, 2024
1 parent d7b8c38 commit fe09c8c
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 111 deletions.
15 changes: 10 additions & 5 deletions appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -105,12 +105,17 @@
},
"TfsNodeStructureTool": {
"Enabled": true,
"NodeBasePaths": [],
"AreaMaps": {
"^migrationSource1([\\\\]?.*)$": "MigrationTest5$1"
"Areas": {
"Filters": [],
"Mappings": {
"^migrationSource1([\\\\]?.*)$": "MigrationTest5$1"
}
},
"IterationMaps": {
"^migrationSource1([\\\\]?.*)$": "MigrationTest5$1"
"Iteration": {
"Filters": [],
"Mappings": {
"^migrationSource1([\\\\]?.*)$": "MigrationTest5$1"
}
},
"ShouldCreateMissingRevisionPaths": true,
"ReplicateAllExistingNodes": true
Expand Down
48 changes: 25 additions & 23 deletions configuration.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"MigrationTools": {
"Version": "16.0",
"Source": {
"TfsTeamProject": {
"EndpointType": "TfsTeamProject",
"Collection": "https://dev.azure.com/nkdagility-preview/",
"Project": "migrationSource1",
Expand All @@ -16,10 +15,8 @@
"AreaPath": "Area",
"IterationPath": "Iteration"
}
}
},
"Target": {
"TfsTeamProject": {
"EndpointType": "TfsTeamProject",
"Collection": "https://dev.azure.com/nkdagility-preview/",
"Project": "migrationTest5",
Expand All @@ -32,7 +29,7 @@
"AreaPath": "Area",
"IterationPath": "Iteration"
}
}

},
"CommonTools": {
"WorkItemTypeMappingTool": {
Expand Down Expand Up @@ -91,31 +88,36 @@
},
"TfsNodeStructureTool": {
"Enabled": true,
"NodeBasePaths": [],
"AreaMaps": {
"^Skypoint Cloud([\\\\]?.*)$": "MigrationTest5$1",
"^7473924d-c47f-4089-8f5c-077c728b576e([\\\\]?.*)$": "MigrationTest5$1"
"Areas": {
"Filters": [ " *\\Team 1,*\\Team 1\\**" ],
"Mappings": {
"^Skypoint Cloud([\\\\]?.*)$": "MigrationTest5$1",
"^7473924d-c47f-4089-8f5c-077c728b576e([\\\\]?.*)$": "MigrationTest5$1"
}
},
"IterationMaps": {
"^Skypoint Cloud([\\\\]?.*)$": "MigrationTest5$1",
"^7473924d-c47f-4089-8f5c-077c728b576e([\\\\]?.*)$": "MigrationTest5$1"
"Iteration": {
"Filters": [],
"Mappings": {
"^Skypoint Cloud([\\\\]?.*)$": "MigrationTest5$1",
"^7473924d-c47f-4089-8f5c-077c728b576e([\\\\]?.*)$": "MigrationTest5$1"
}
},
"ShouldCreateMissingRevisionPaths": true,
"ReplicateAllExistingNodes": true
},
"TfsRevisionManagerTool": {
"Enabled": true,
"ReplayRevisions": true,
"MaxRevisions": 0
"TfsRevisionManagerTool": {
"Enabled": true,
"ReplayRevisions": true,
"MaxRevisions": 0
},
"TfsTeamSettingsTool": {
"Enabled": true,
"MigrateTeamSettings": true,
"UpdateTeamSettings": true,
"MigrateTeamCapacities": true,
"Teams": null
}
},
"TfsTeamSettingsTool": {
"Enabled": true,
"MigrateTeamSettings": true,
"UpdateTeamSettings": true,
"MigrateTeamCapacities": true,
"Teams": null
}
},
"Processors": [
{
"ProcessorType": "TfsWorkItemMigrationProcessor",
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions docs/Reference/Generated/MigrationTools.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

52 changes: 37 additions & 15 deletions docs/Reference/Tools/TfsNodeStructureTool-notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,14 +208,15 @@ This will prepend a bucket to the area and iteration paths. This is useful when
],
```

## <a name="NodeBasePath"></a>NodeBasePath Configuration
## <a name="Filters"></a>Filters
The `NodeBasePaths` entry allows the filtering of the nodes to be replicated on the target projects. To try to explain the correct usage let us assume that we have a source team project `SourceProj` with the following node structures

- AreaPath
- SourceProj
- SourceProj\Team 1
- SourceProj\Team 2
- SourceProj\Team 2\Sub-Area
- SourceProj\Team 2\Sub-Area 1
- SourceProj\Team 2\Sub-Area 2
- SourceProj\Team 3
- IterationPath
- SourceProj
Expand All @@ -229,21 +230,42 @@ Depending upon what node structures you wish to migrate you would need the follo
| | |
|-|-|
| Intention | Migrate all areas and iterations and all Work Items
| NodeBasePath | `[]`
| Comment | The same AreaPath and Iteration Paths are created on the target as on the source. Hence, all migrated WI remain in their existing area and iteration paths
| Filters | `[]`
| Comment | The same AreaPath and Iteration Paths are created on the target as on the source. Hence, all migrated WI remain in their existing area and iteration paths. <br/> This will be affected by the `AreaMaps` and `IterationMaps` settings.
||
| Intention | Only migrate area path `Team 2` and it associated Work Items, but all iteration paths
| NodeBasePath | `["Team 2", "Sprint"]`
| Comment | Only the area path ending `Team 2` will be migrated. <br>The `WIQLQueryBit` should be edited to limit the WI migrated to this area path e.g. add `AND [System.AreaPath] UNDER 'SampleProject\\Team 2'` . <br> The migrated WI will have an area path of `TargetProj\Team 2` but retain their iteration paths matching the sprint name on the source
| NodeBasePath | `["*\\Team 2", "*\\Sprint*"]`
| Comment | Only the area path ending `Team 2` will be migrated. <br>The `WIQLQuery` should be edited to limit the WI migrated to this area path e.g. add `AND [System.AreaPath] UNDER 'SampleProject\\Team 2'` . <br> The migrated WI will have an area path of `TargetProj\Team 2` but retain their iteration paths matching the sprint name on the source
||
| Intention | Only migrate iterations structure
| NodeBasePath | `["Sprint"]`
| Comment | Only the area path ending `Team 2` will be migrated<br>All the iteration paths will be migrated. <br> The migrated WI will have the default area path of `TargetProj` as their source area path was not migrated i.e. `TargetProj`<br> The migrated WI will have an iteration path match the sprint name on the source
||
| Intention | Move all WI to the existing area and iteration paths on the targetProj
| NodeBasePath | `["DUMMY VALUE"]`
| Comment | As the `NodeBasePath` does not match any source area or iteration path no nodes are migrated. <br>Migrated WI will be assigned to any matching area or iteration paths. If no matching ones can be found they will default to the respective root values
| Intention | Move the `Team 2` area, including its `Sub-Area`, and any others at the same level
| NodeBasePath | `["*\\Team 2", "Team 2\\*"]`
| Comment | The Work Items will have to be restricted to the right areas, e.g. with `AND [System.AreaPath] UNDER 'SampleProject\\Team 2' AND [System.AreaPath] NOT UNDER 'SampleProject\\Team 2\\Sub-Area'`, otherwise their migratin will fail
||
| Intention | Move the `Team 2` area, but not its `Sub-Area`
| NodeBasePath | `["Team 2", "!Team 2\\SubArea"]`
| Comment | The Work Items will have to be restricted to the right areas, e.g. with `AND [System.AreaPath] UNDER 'SampleProject\\Team 2' AND [System.AreaPath] NOT UNDER 'SampleProject\\Team 2\\Sub-Area'`, otherwise their migratin will fail
| NodeBasePath | `["*\\Team 2", "!Team 2\\SubArea"]`
| Comment | The Work Items will have to be restricted to the right areas, e.g. with `AND [System.AreaPath] UNDER 'SampleProject\\Team 2' AND [System.AreaPath] NOT UNDER 'SampleProject\\Team 2\\Sub-Area'`, otherwise their migratin will fail

# Patterns

The following patterns are supported:
>
| Wildcard | Description | Example | Matches | Does not match |
| -------- | ----------- | ------- | ------- | -------------- |
| \* | matches any number of any characters including none | Law\*| Law, Laws, or Lawyer |
| ? | matches any single character | ?at | Cat, cat, Bat or bat | at |
| [abc] | matches one character given in the bracket | [CB]at | Cat or Bat | cat or bat |
| [a-z] | matches one character from the range given in the bracket | Letter[0-9] | Letter0, Letter1, Letter2 up to Letter9 | Letters, Letter or Letter10 |
| [!abc] | matches one character that is not given in the bracket | [!C]at | Bat, bat, or cat | Cat |
| [!a-z] | matches one character that is not from the range given in the bracket | Letter[!3-5] | Letter1, Letter2, Letter6 up to Letter9 and Letterx etc. | Letter3, Letter4, Letter5 or Letterxx |

In addition, Glob also supports:

| Wildcard | Description | Example | Matches | Does not match |
| -------- | ----------- | ------- | ------- | -------------- |
| `**` | matches any number of path / directory segments. When used must be the only contents of a segment. | /\*\*/some.\* | /foo/bar/bah/some.txt, /some.txt, or /foo/some.txt |


# Escaping special characters

Wrap special characters `?, *, [` in square brackets in order to escape them.
You can also use negation when doing this.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,13 @@ public void GetTfsNodeStructureTool_WithDifferentAreaPath()
{
var options = new TfsNodeStructureToolOptions();
options.Enabled = true;
options.AreaMaps = new Dictionary<string, string>() { { @"^SourceProject\\PUL", "TargetProject\\test\\PUL" } } ;
options.Areas = new NodeOptions()
{
Mappings = new Dictionary<string, string>()
{
{ @"^SourceProject\\PUL", "TargetProject\\test\\PUL" }
}
};
var nodeStructure = GetTfsNodeStructureTool(options);

nodeStructure.ApplySettings(new TfsNodeStructureToolSettings
Expand Down Expand Up @@ -74,13 +80,20 @@ public void TestFixAreaPath_WhenAreaPathInQuery_WithPrefixProjectToNodesEnabled_
{
var options = new TfsNodeStructureToolOptions();
options.Enabled = true;
options.AreaMaps = new Dictionary<string, string>()
options.Areas = new NodeOptions()
{
Mappings = new Dictionary<string, string>()
{
{ "^SourceServer\\\\(.*)" , "TargetServer\\SourceServer\\$1" }
};
options.IterationMaps = new Dictionary<string, string>(){
{ "^SourceServer\\\\(.*)" , "TargetServer\\SourceServer\\$1" }
};
}
};
options.Iterations = new NodeOptions()
{
Mappings = new Dictionary<string, string>()
{
{ "^SourceServer\\\\(.*)" , "TargetServer\\SourceServer\\$1" }
}
};
var nodeStructure = GetTfsNodeStructureTool(options);

string WIQLQueryBit = @"SELECT [System.Id] FROM WorkItems WHERE [System.TeamProject] = @TeamProject AND [System.AreaPath] = 'SourceServer\Area\Path1' AND [Microsoft.VSTS.Common.ClosedDate] = '' AND [System.WorkItemType] NOT IN ('Test Suite', 'Test Plan')";
Expand Down Expand Up @@ -109,13 +122,21 @@ public void TestFixAreaPath_WhenAreaPathInQuery_WithPrefixProjectToNodesEnabled_
{
var options = new TfsNodeStructureToolOptions();
options.Enabled = true;
options.AreaMaps = new Dictionary<string, string>()
options.Areas = new NodeOptions()
{
Mappings = new Dictionary<string, string>()
{
{ "^Source Project\\\\(.*)" , "Target Project\\Source Project\\$1" }
};
options.IterationMaps = new Dictionary<string, string>(){
}
};

options.Iterations = new NodeOptions()
{
Mappings = new Dictionary<string, string>()
{
{ "^Source Project\\\\(.*)" , "Target Project\\Source Project\\$1" }
};
}
};
var settings = new TfsNodeStructureToolSettings() { SourceProjectName = "Source Project", TargetProjectName = "Target Project", FoundNodes = new Dictionary<string, bool>() };
var nodeStructure = GetTfsNodeStructureTool(options, settings);

Expand Down Expand Up @@ -204,7 +225,7 @@ private static TfsNodeStructureTool GetTfsNodeStructureTool(TfsNodeStructureTool

private static TfsNodeStructureTool GetTfsNodeStructureTool()
{
var options = new TfsNodeStructureToolOptions() { Enabled = true, AreaMaps = new Dictionary<string, string>(), IterationMaps = new Dictionary<string, string>() };
var options = new TfsNodeStructureToolOptions() { Enabled = true, Areas = new NodeOptions { Mappings = new Dictionary<string, string>() }, Iterations = new NodeOptions { Mappings = new Dictionary<string, string>() } };
var settings = new TfsNodeStructureToolSettings() { SourceProjectName = "SourceServer", TargetProjectName = "TargetServer", FoundNodes = new Dictionary<string, bool>() };
return GetTfsNodeStructureTool(options, settings);
}
Expand All @@ -217,8 +238,8 @@ private static TfsNodeStructureTool GetTfsNodeStructureTool(TfsNodeStructureTool
services.Configure<TfsNodeStructureToolOptions>(o =>
{
o.Enabled = options.Enabled;
o.AreaMaps = options.AreaMaps;
o.IterationMaps = options.IterationMaps;
o.Areas = options.Areas;
o.Iterations = options.Iterations;
});

var nodeStructure = services.BuildServiceProvider().GetService<TfsNodeStructureTool>();
Expand Down
Loading

0 comments on commit fe09c8c

Please sign in to comment.