Skip to content

Feature/add azure dev ops#125

Open
RalfvandenBurg wants to merge 12 commits into
elsa-workflows:release/3.6.0from
RalfvandenBurg:feature/AddAzureDevOps
Open

Feature/add azure dev ops#125
RalfvandenBurg wants to merge 12 commits into
elsa-workflows:release/3.6.0from
RalfvandenBurg:feature/AddAzureDevOps

Conversation

@RalfvandenBurg

Copy link
Copy Markdown

Adds the Elsa.DevOps.AzureDevOps module to integrate Azure DevOps with Elsa Workflows. The implementation mirrors the structure and patterns of Elsa.DevOps.GitHub so workflows can call Azure DevOps Git, pull requests, work items, and builds from activities.
Closes #124

@greptile-apps

greptile-apps Bot commented Feb 20, 2026

Copy link
Copy Markdown

Greptile Summary

Adds comprehensive Azure DevOps integration module mirroring the existing GitHub integration structure. Implements 15 activities across four categories: Git repositories/branches, pull requests, work items, and builds.

Key Changes:

  • Added AzureDevOpsConnectionFactory for managing reusable VssConnection instances
  • Created base AzureDevOpsActivity class with built-in validation and authentication
  • Implemented activities for repositories (GetBranch, GetRepository, ListBranches)
  • Implemented activities for pull requests (CreatePullRequest, GetPullRequest, ListPullRequests)
  • Implemented activities for work items (CreateWorkItem, GetWorkItem, QueryWorkItems, UpdateWorkItem)
  • Implemented activities for builds (GetBuild, ListBuilds, QueueBuild)
  • Added comprehensive input validation helpers in ActivityInputValidation
  • Registered feature with proper dependency injection

Issues Found:

  • Connection factory stores PAT tokens in plain text as dictionary keys
  • VssConnection instances are cached indefinitely without disposal mechanism
  • Minor inefficiency in UpdateWorkItem when no fields are updated

Confidence Score: 3/5

  • Safe to merge with resource management concerns that should be addressed
  • The implementation is well-structured and follows existing patterns, but has resource management issues in the connection factory including storing credentials in memory and not disposing connections
  • Pay close attention to AzureDevOpsConnectionFactory.cs due to credential storage and resource disposal concerns

Important Files Changed

Filename Overview
src/modules/devops/Elsa.DevOps.AzureDevOps/Services/AzureDevOpsConnectionFactory.cs Connection factory with PAT token storage in dictionary key and no connection disposal
src/modules/devops/Elsa.DevOps.AzureDevOps/Activities/AzureDevOpsActivity.cs Base activity class with proper validation in CanExecuteAsync
src/modules/devops/Elsa.DevOps.AzureDevOps/Activities/PullRequests/CreatePullRequest.cs Creates pull requests with proper validation and ref normalization
src/modules/devops/Elsa.DevOps.AzureDevOps/Activities/WorkItems/UpdateWorkItem.cs Updates work items, creates connection twice when no changes provided
src/modules/devops/Elsa.DevOps.AzureDevOps/Features/AzureDevOpsFeature.cs Feature registration that adds activities and registers connection factory

Class Diagram

%%{init: {'theme': 'neutral'}}%%
classDiagram
    class AzureDevOpsActivity {
        +Input~string~ OrganizationUrl
        +Input~string~ Token
        #CanExecuteAsync() ValueTask~bool~
        #GetConnection() VssConnection
    }
    
    class AzureDevOpsConnectionFactory {
        -Dictionary~string,VssConnection~ _connections
        -SemaphoreSlim _semaphore
        +GetConnection(string organizationUrl, string token) VssConnection
    }
    
    class AzureDevOpsFeature {
        +Apply() void
    }
    
    class CreatePullRequest {
        +Input~string~ Project
        +Input~string~ RepositoryName
        +Input~string~ SourceBranch
        +Input~string~ TargetBranch
        +Output~GitPullRequest~ CreatedPullRequest
    }
    
    class CreateWorkItem {
        +Input~string~ Project
        +Input~string~ WorkItemType
        +Input~string~ Title
        +Output~WorkItem~ CreatedWorkItem
    }
    
    class QueueBuild {
        +Input~string~ Project
        +Input~int~ DefinitionId
        +Output~Build~ QueuedBuild
    }
    
    class GetBranch {
        +Input~string~ Project
        +Input~string~ RepositoryName
        +Input~string~ BranchName
        +Output~GitBranchStats~ Branch
    }
    
    AzureDevOpsActivity <|-- CreatePullRequest
    AzureDevOpsActivity <|-- CreateWorkItem
    AzureDevOpsActivity <|-- QueueBuild
    AzureDevOpsActivity <|-- GetBranch
    AzureDevOpsActivity ..> AzureDevOpsConnectionFactory : uses
    AzureDevOpsFeature ..> AzureDevOpsConnectionFactory : registers
    AzureDevOpsFeature ..> AzureDevOpsActivity : discovers activities
Loading

Last reviewed commit: 2bc247e

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

25 files reviewed, 3 comments

Edit Code Review Agent Settings | Greptile

Comment thread src/modules/devops/Elsa.DevOps.AzureDevOps/Activities/WorkItems/UpdateWorkItem.cs Outdated

@Namoshek Namoshek left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I've a bit of experience with the Azure DevOps client libraries and the PR looks good to me. The only thing I miss is a few blank lines to make the code more readable, but I guess that's personal preference. 😉

Comment thread src/modules/devops/Elsa.DevOps.AzureDevOps/Activities/Builds/ListBuilds.cs Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 introduces a new Elsa.DevOps.AzureDevOps module that mirrors the existing GitHub DevOps extension, enabling Elsa workflows to call Azure DevOps APIs for repositories, pull requests, work items, and builds. It also adds documentation and test coverage (unit + opt-in integration tests) and wires the module into the solution/package set.

Changes:

  • Added Elsa.DevOps.AzureDevOps module with activities for Repos, PRs, Work Items, and Builds, plus a shared base activity and connection factory.
  • Added README documentation and both unit tests (validation + connection factory) and opt-in integration tests (env-var configured + skippable).
  • Updated solution and central packages to include the new module and required Azure DevOps client dependencies.

Reviewed changes

Copilot reviewed 35 out of 35 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/modules/devops/Elsa.DevOps.AzureDevOps/Activities/ActivityInputValidation.cs Adds shared input validation helpers for activities.
src/modules/devops/Elsa.DevOps.AzureDevOps/Activities/AzureDevOpsActivity.cs Adds the base activity with org URL + PAT inputs and shared connection creation.
src/modules/devops/Elsa.DevOps.AzureDevOps/Activities/AzureDevOpsTriggerActivity.cs Adds a stub trigger base class (NotImplementedException for now).
src/modules/devops/Elsa.DevOps.AzureDevOps/Activities/Builds/GetBuild.cs Adds “Get Build” activity.
src/modules/devops/Elsa.DevOps.AzureDevOps/Activities/Builds/ListBuilds.cs Adds “List Builds” activity with optional filters.
src/modules/devops/Elsa.DevOps.AzureDevOps/Activities/Builds/QueueBuild.cs Adds “Queue Build” activity.
src/modules/devops/Elsa.DevOps.AzureDevOps/Activities/PullRequests/CreatePullRequest.cs Adds “Create Pull Request” activity.
src/modules/devops/Elsa.DevOps.AzureDevOps/Activities/PullRequests/GetPullRequest.cs Adds “Get Pull Request” activity.
src/modules/devops/Elsa.DevOps.AzureDevOps/Activities/PullRequests/ListPullRequests.cs Adds “List Pull Requests” activity with optional status/top/skip.
src/modules/devops/Elsa.DevOps.AzureDevOps/Activities/Repositories/GetBranch.cs Adds “Get Branch” activity.
src/modules/devops/Elsa.DevOps.AzureDevOps/Activities/Repositories/GetRepository.cs Adds “Get Repository” activity.
src/modules/devops/Elsa.DevOps.AzureDevOps/Activities/Repositories/ListBranches.cs Adds “List Branches” activity.
src/modules/devops/Elsa.DevOps.AzureDevOps/Activities/WorkItems/CreateWorkItem.cs Adds “Create Work Item” activity.
src/modules/devops/Elsa.DevOps.AzureDevOps/Activities/WorkItems/GetWorkItem.cs Adds “Get Work Item” activity.
src/modules/devops/Elsa.DevOps.AzureDevOps/Activities/WorkItems/QueryWorkItems.cs Adds “Query Work Items (WIQL)” activity.
src/modules/devops/Elsa.DevOps.AzureDevOps/Activities/WorkItems/UpdateWorkItem.cs Adds “Update Work Item” activity.
src/modules/devops/Elsa.DevOps.AzureDevOps/Extensions/ModuleExtensions.cs Adds UseAzureDevOps() module extension method.
src/modules/devops/Elsa.DevOps.AzureDevOps/Features/AzureDevOpsFeature.cs Registers the feature, activities, and connection factory.
src/modules/devops/Elsa.DevOps.AzureDevOps/Services/AzureDevOpsConnectionFactory.cs Implements cached VssConnection creation.
src/modules/devops/Elsa.DevOps.AzureDevOps/Elsa.DevOps.AzureDevOps.csproj Adds new module project and package references.
src/modules/devops/Elsa.DevOps.AzureDevOps/FodyWeavers.xml Adds the module’s Fody weavers file (empty).
src/modules/devops/Elsa.DevOps.AzureDevOps/README.md Adds module documentation (install/config/auth/activity list).
test/modules/devops/Elsa.DevOps.AzureDevOps.UnitTests/Activities/ActivityInputValidationTests.cs Adds unit tests for validation helpers.
test/modules/devops/Elsa.DevOps.AzureDevOps.UnitTests/Services/AzureDevOpsConnectionFactoryTests.cs Adds unit tests for connection caching/concurrency/dispose behavior.
test/modules/devops/Elsa.DevOps.AzureDevOps.UnitTests/Elsa.DevOps.AzureDevOps.UnitTests.csproj Adds unit test project for the module.
test/modules/devops/Elsa.DevOps.AzureDevOps.IntegrationTests/Fixtures/AzureDevOpsFixture.cs Adds env-var configured fixture + skip logic for integration tests.
test/modules/devops/Elsa.DevOps.AzureDevOps.IntegrationTests/Services/ConnectionFactoryIntegrationTests.cs Adds basic integration coverage for authentication/connection.
test/modules/devops/Elsa.DevOps.AzureDevOps.IntegrationTests/Repositories/RepositoriesIntegrationTests.cs Adds integration coverage for repo/branch APIs.
test/modules/devops/Elsa.DevOps.AzureDevOps.IntegrationTests/PullRequests/PullRequestsIntegrationTests.cs Adds integration coverage for PR listing/get-by-id.
test/modules/devops/Elsa.DevOps.AzureDevOps.IntegrationTests/WorkItems/WorkItemsIntegrationTests.cs Adds integration coverage for WIQL/create/update work items.
test/modules/devops/Elsa.DevOps.AzureDevOps.IntegrationTests/Builds/BuildsIntegrationTests.cs Adds integration coverage for build listing/get-by-id.
test/modules/devops/Elsa.DevOps.AzureDevOps.IntegrationTests/Elsa.DevOps.AzureDevOps.IntegrationTests.csproj Adds integration test project + SkippableFact dependency.
Directory.Packages.props Adds central versions for Azure DevOps client libraries.
Elsa.Extensions.sln Adds the new module and test projects to the solution.
README.md Updates the extensions status table entry for Azure DevOps.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +20 to +43
public VssConnection GetConnection(string organizationUrl, string token)
{
var key = $"{organizationUrl}|{ComputeHash(token)}";
if (_connections.TryGetValue(key, out var connection))
return connection;

try
{
_semaphore.Wait();

if (_connections.TryGetValue(key, out connection))
return connection;

var uri = new Uri(organizationUrl.TrimEnd('/'));
var credentials = new VssBasicCredential(string.Empty, token);
connection = new VssConnection(uri, credentials);
_connections[key] = connection;
return connection;
}
finally
{
_semaphore.Release();
}
}
Comment on lines +12 to +22
public class AzureDevOpsFeature(IModule module) : FeatureBase(module)
{
/// <summary>
/// Applies the feature to the specified service collection.
/// </summary>
public override void Apply()
{
Module.AddActivitiesFrom<AzureDevOpsFeature>();
Services.AddSingleton<AzureDevOpsConnectionFactory>();
}
}
Comment on lines +60 to +71
var project = context.Get(Project);
var repositoryName = context.Get(RepositoryName);
var top = context.Get(Top);
var skip = context.Get(Skip);
var (projectOk, projectErr) = ActivityInputValidation.TryValidateRequired(project, nameof(Project));
if (!projectOk) { context.AddExecutionLogEntry("Precondition Failed", projectErr); return new ValueTask<bool>(false); }
var (repoOk, repoErr) = ActivityInputValidation.TryValidateRequired(repositoryName, nameof(RepositoryName));
if (!repoOk) { context.AddExecutionLogEntry("Precondition Failed", repoErr); return new ValueTask<bool>(false); }
if (top is < 0) { context.AddExecutionLogEntry("Precondition Failed", "'Top' must be non-negative when specified."); return new ValueTask<bool>(false); }
if (skip is < 0) { context.AddExecutionLogEntry("Precondition Failed", "'Skip' must be non-negative when specified."); return new ValueTask<bool>(false); }
return base.CanExecuteAsync(context);
}
Comment on lines +63 to +67
var build = new Build
{
Definition = new BuildDefinitionReference { Id = definitionId },
SourceBranch = sourceBranch ?? "refs/heads/main"
};
Comment thread Directory.Packages.props
Comment on lines +194 to +195
<PackageVersion Include="Microsoft.TeamFoundationServer.Client" Version="19.225.2"/>
<PackageVersion Include="Microsoft.VisualStudio.Services.Client" Version="19.225.2"/>
@KnibbsyMan

Copy link
Copy Markdown
Member

Hi @RalfvandenBurg,

Great PR! If you're able to review these comments, I'll get this merged.

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.

5 participants