Skip to content

Conversation

@Dev-Op
Copy link
Contributor

@Dev-Op Dev-Op commented Sep 30, 2025

For our currently running migration with many classical (and somtime very old) pipelines I added some features to this project. The first is supporting task groups in task groups (nested task groups). With this change the task group migration run multiple times. In the first step all task groups with out task groups are migrated. In the second the task groups related on the migrated task groups. This will be done until no task group is missing or there was a problem.

@Dev-Op Dev-Op requested a review from MrHinsh as a code owner September 30, 2025 16:38
@MrHinsh MrHinsh requested review from Copilot and tomfrenzel October 1, 2025 15:04
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR adds support for nested task groups in the Azure DevOps Pipeline Processor, enabling migration of task groups that contain other task groups. The implementation uses a multi-phase approach where unnested task groups are migrated first, followed by iterative processing of nested task groups until all dependencies are resolved.

Key changes:

  • Introduces multi-phase task group migration with dependency resolution
  • Adds service connection mapping support for task groups
  • Implements null-safe checks for task definition types

Copy link
Collaborator

@tomfrenzel tomfrenzel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the idea of this PR 🙂

I'll take a more in depth look at the changes in the next days as I'm currently on vacation.

Until then, fix the comments Copilot made and try to get a successful build/test run. Copilot's requested changes look good to me. Additionally it would be nice if you introduce a helper function for this check:

t.Task.DefinitionType?.ToLower() == "metaTask".ToLower()

We call it many times and I think it makes sense that it is executed only in one single method.

@MrHinsh MrHinsh marked this pull request as draft October 1, 2025 21:02
@MrHinsh MrHinsh requested review from Copilot and tomfrenzel October 17, 2025 19:07
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 4 comments.

var newInputs = new Dictionary<string, object>();
foreach (var input in (IDictionary<String, Object>)task.Inputs)
{
var mapping = serviceConnectionMappings.FirstOrDefault(d => d.SourceId == input.Value.ToString());
Copy link

Copilot AI Oct 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential NullReferenceException if input.Value is null. The code calls .ToString() on input.Value without null checking. Add a null check before calling ToString() or use null-conditional operator: input.Value?.ToString()

Suggested change
var mapping = serviceConnectionMappings.FirstOrDefault(d => d.SourceId == input.Value.ToString());
var mapping = serviceConnectionMappings.FirstOrDefault(d => d.SourceId == input.Value?.ToString());

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

@tomfrenzel tomfrenzel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new suggested changes by Copilot are valid. Please apply them and respond to by existing comments 😉


targetDefinitions = await Target.GetApiDefinitionsAsync<TaskGroup>(queryForDetails: false);
mappings.AddRange(FindExistingMappings(sourceDefinitions, targetDefinitions.Where(d => d.Name != null), mappings));
mappings.AddRange(existingMappings);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this? As far as i see, there are no changes to this list. Therefore we can just reuse it in the method calling this method

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mappings contain only new created task groups, for a complete list all data from Azure DevOps must be request or we can add the already existing mappings.

}

nestedTaskGroups = nestedTaskGroups.Where(g => !taskGroupsToMigrate.Any(t => t.Id == g.Id)).ToList();
existingMappings = await CreateTaskGroupsAsync(serviceConnectionMappings, targetDefinitions, availableTasks, taskGroupsToMigrate, existingMappings);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need to apped the new mappings here instead of overriding the whole list

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method should only return newly generatd task groups?

var filteredTaskGroups = FilterOutExistingTaskGroups(sourceDefinitions, targetDefinitions);
filteredTaskGroups = FilterOutIncompatibleTaskGroups(filteredTaskGroups, availableTasks).ToList();

var existingMappings = FindExistingMappings(sourceDefinitions, targetDefinitions, new List<Mapping>());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe instead of inititalizing a new list here, amke the parameter optional and initialize it in the method itself if not set

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A good idea, but this method has only one reference and no data at this time.

@coderabbitai
Copy link

coderabbitai bot commented Nov 6, 2025

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants