diff --git a/tools/notification-configuration/notification-configuration.sln b/tools/notification-configuration/notification-configuration.sln
index 53cdbea96e9..85293118f18 100644
--- a/tools/notification-configuration/notification-configuration.sln
+++ b/tools/notification-configuration/notification-configuration.sln
@@ -5,10 +5,18 @@ VisualStudioVersion = 17.5.33103.201
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Sdk.Tools.NotificationConfiguration", "notification-creator\Azure.Sdk.Tools.NotificationConfiguration.csproj", "{5759063D-A7B3-4D36-ACF4-5595C2789D27}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Sdk.Tools.NotificationConfiguration.Tests", "notification-creator.Tests\Azure.Sdk.Tools.NotificationConfiguration.Tests.csproj", "{3097CBB4-ED3C-4273-AC67-F5D189CB94BA}"
+EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Sdk.Tools.CodeOwnersParser", "..\code-owners-parser\CodeOwnersParser\Azure.Sdk.Tools.CodeOwnersParser.csproj", "{A9826C8B-85DF-48DB-8A05-40FB04833C42}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Sdk.Tools.CodeOwnersParser.Tests", "..\code-owners-parser\Azure.Sdk.Tools.CodeOwnersParser.Tests\Azure.Sdk.Tools.CodeOwnersParser.Tests.csproj", "{2146E1FF-04D1-4B19-9767-C011A73CB40D}"
+EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "identity-resolution", "..\identity-resolution\identity-resolution.csproj", "{9805B503-5469-412C-9A0C-F09F504F0ED8}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Sdk.Tools.RetrieveCodeOwners", "..\code-owners-parser\Azure.Sdk.Tools.RetrieveCodeOwners\Azure.Sdk.Tools.RetrieveCodeOwners.csproj", "{3E5237F2-6536-4329-A9CF-92E42B040612}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Sdk.Tools.RetrieveCodeOwners.Tests", "..\code-owners-parser\Azure.Sdk.Tools.RetrieveCodeOwners.Tests\Azure.Sdk.Tools.RetrieveCodeOwners.Tests.csproj", "{8DAEC12F-8390-4122-9959-9CF3391F18CC}"
+EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{EBC153AF-0244-4DFB-8084-E6C0ACAA5CF3}"
ProjectSection(SolutionItems) = preProject
ci.yml = ci.yml
@@ -33,6 +41,22 @@ Global
{9805B503-5469-412C-9A0C-F09F504F0ED8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9805B503-5469-412C-9A0C-F09F504F0ED8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9805B503-5469-412C-9A0C-F09F504F0ED8}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8DAEC12F-8390-4122-9959-9CF3391F18CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8DAEC12F-8390-4122-9959-9CF3391F18CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8DAEC12F-8390-4122-9959-9CF3391F18CC}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8DAEC12F-8390-4122-9959-9CF3391F18CC}.Release|Any CPU.Build.0 = Release|Any CPU
+ {2146E1FF-04D1-4B19-9767-C011A73CB40D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {2146E1FF-04D1-4B19-9767-C011A73CB40D}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {2146E1FF-04D1-4B19-9767-C011A73CB40D}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {2146E1FF-04D1-4B19-9767-C011A73CB40D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3E5237F2-6536-4329-A9CF-92E42B040612}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3E5237F2-6536-4329-A9CF-92E42B040612}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3E5237F2-6536-4329-A9CF-92E42B040612}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3E5237F2-6536-4329-A9CF-92E42B040612}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3097CBB4-ED3C-4273-AC67-F5D189CB94BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3097CBB4-ED3C-4273-AC67-F5D189CB94BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3097CBB4-ED3C-4273-AC67-F5D189CB94BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3097CBB4-ED3C-4273-AC67-F5D189CB94BA}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/tools/notification-configuration/notification-creator.Tests/Azure.Sdk.Tools.NotificationConfiguration.Tests.csproj b/tools/notification-configuration/notification-creator.Tests/Azure.Sdk.Tools.NotificationConfiguration.Tests.csproj
new file mode 100644
index 00000000000..c6c64882654
--- /dev/null
+++ b/tools/notification-configuration/notification-creator.Tests/Azure.Sdk.Tools.NotificationConfiguration.Tests.csproj
@@ -0,0 +1,22 @@
+
+
+
+ net6.0
+ enable
+ Nullable
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/notification-configuration/notification-creator.Tests/ProgramTests.cs b/tools/notification-configuration/notification-creator.Tests/ProgramTests.cs
new file mode 100644
index 00000000000..e7a92d50636
--- /dev/null
+++ b/tools/notification-configuration/notification-creator.Tests/ProgramTests.cs
@@ -0,0 +1,30 @@
+using System;
+using NUnit.Framework;
+
+namespace Azure.Sdk.Tools.NotificationConfiguration.Tests;
+
+[TestFixture]
+public class ProgramTests
+{
+ [Test]
+ public void ThrowsVssUnauthorizedException()
+ {
+ Environment.SetEnvironmentVariable("aadAppIdVar", "aadAppIdVarValue");
+ Environment.SetEnvironmentVariable("aadAppSecretVar", "aadAppSecretVarValue");
+ Environment.SetEnvironmentVariable("aadTenantVar", "aadTenantVarValue");
+ Assert.ThrowsAsync(
+ async () =>
+ // Act
+ await Program.Main(
+ organization: "fooOrg",
+ project: "barProj",
+ pathPrefix: "qux",
+ tokenVariableName: "token",
+ aadAppIdVar: "aadAppIdVar",
+ aadAppSecretVar: "aadAppSecretVar",
+ aadTenantVar: "aadTenantVar",
+ selectionStrategy: PipelineSelectionStrategy.Scheduled,
+ dryRun: true)
+ );
+ }
+}
diff --git a/tools/notification-configuration/notification-creator/Program.cs b/tools/notification-configuration/notification-creator/Program.cs
index bee812834f4..dbc343717f6 100644
--- a/tools/notification-configuration/notification-creator/Program.cs
+++ b/tools/notification-configuration/notification-creator/Program.cs
@@ -7,64 +7,113 @@
using Azure.Sdk.Tools.NotificationConfiguration.Helpers;
using Azure.Identity;
-namespace Azure.Sdk.Tools.NotificationConfiguration
+namespace Azure.Sdk.Tools.NotificationConfiguration;
+
+///
+/// A tool for creating and configuring Azure DevOps groups for sending email notifications
+/// on build failures to owners of relevant build definitions. The recipients are determined
+/// based on the build definition .yml file paths as given by the CODEOWNERS of given build definition
+/// source repository.
+///
+public static class Program
{
- class Program
+ ///
+ /// Create notification groups for failures in scheduled builds
+ ///
+ /// Azure DevOps Organization
+ /// Name of the DevOps project
+ /// Path prefix to include pipelines (e.g. "\net")
+ /// Environment variable token name (e.g. "SYSTEM_ACCESSTOKEN")
+ /// AAD App ID environment variable name (OpensourceAPI access)
+ /// AAD App Secret environment variable name (OpensourceAPI access)
+ /// AAD Tenant environment variable name (OpensourceAPI access)
+ /// Pipeline selection strategy
+ /// Prints changes but does not alter any objects
+ ///
+ public static async Task Main(
+ string organization,
+ string project,
+ string pathPrefix,
+ string tokenVariableName,
+ string aadAppIdVar,
+ string aadAppSecretVar,
+ string aadTenantVar,
+ PipelineSelectionStrategy selectionStrategy = PipelineSelectionStrategy.Scheduled,
+ bool dryRun = false)
{
- ///
- /// Create notification groups for failures in scheduled builds
- ///
- /// Azure DevOps Organization
- /// Name of the DevOps project
- /// Path prefix to include pipelines (e.g. "\net")
- /// Environment variable token name (e.g. "SYSTEM_ACCESSTOKEN")
- /// AAD App ID environment variable name (OpensourceAPI access)
- /// AAD App Secret environment variable name (OpensourceAPI access)
- /// AAD Tenant environment variable name (OpensourceAPI access)
- /// Pipeline selection strategy
- /// Prints changes but does not alter any objects
- ///
- static async Task Main(
- string organization,
- string project,
- string pathPrefix,
- string tokenVariableName,
- string aadAppIdVar,
- string aadAppSecretVar,
- string aadTenantVar,
- PipelineSelectionStrategy selectionStrategy = PipelineSelectionStrategy.Scheduled,
- bool dryRun = false)
+ var loggerFactory = LoggerFactory.Create(builder =>
{
- var devOpsToken = Environment.GetEnvironmentVariable(tokenVariableName);
- var devOpsCreds = new VssBasicCredential("nobody", devOpsToken);
- var devOpsConnection = new VssConnection(new Uri($"https://dev.azure.com/{organization}/"), devOpsCreds);
+ builder.AddSimpleConsole(config => { config.IncludeScopes = true; });
+ });
+ var logger = loggerFactory.CreateLogger(nameof(Program));
+ logger.LogInformation(
+ "Executing Azure.Sdk.Tools.NotificationConfiguration.Program.Main with following arguments: "
+ + "organization: '{organization}' "
+ + "project: '{project}' "
+ + "pathPrefix: '{pathPrefix}' "
+ + "tokenVariableName: '{tokenVariableName}' "
+ + "aadAppIdVar: '{aadAppIdVar}' "
+ + "aadAppSecretVar: '{aadAppSecretVar}' "
+ + "aadTenantVar: '{aadTenantVar}' "
+ + "selectionStrategy: '{selectionStrategy}' "
+ + "dryRun: '{dryRun}' "
+ , organization
+ , project
+ , pathPrefix
+ , tokenVariableName
+ , aadAppIdVar
+ , aadAppSecretVar
+ , aadTenantVar
+ , selectionStrategy
+ , dryRun);
+
+ var notificationConfigurator = new NotificationConfigurator(
+ AzureDevOpsService(organization, tokenVariableName, loggerFactory),
+ GitHubService(loggerFactory),
+ loggerFactory.CreateLogger());
+
+ await notificationConfigurator.ConfigureNotifications(
+ project,
+ pathPrefix,
+ GitHubToAADConverter(aadTenantVar, aadAppIdVar, aadAppSecretVar, loggerFactory),
+ persistChanges: !dryRun,
+ strategy: selectionStrategy);
+ }
- var loggerFactory = LoggerFactory.Create(builder =>
- {
- builder.AddSimpleConsole(config => { config.IncludeScopes = true; });
- });
- var devOpsServiceLogger = loggerFactory.CreateLogger();
- var notificationConfiguratorLogger = loggerFactory.CreateLogger();
+ private static AzureDevOpsService AzureDevOpsService(
+ string organization,
+ string tokenVariableName,
+ ILoggerFactory loggerFactory)
+ {
+ var devOpsToken = Environment.GetEnvironmentVariable(tokenVariableName);
+ var devOpsCreds = new VssBasicCredential("nobody", devOpsToken);
+ var devOpsConnection = new VssConnection(
+ new Uri($"https://dev.azure.com/{organization}/"),
+ devOpsCreds);
+ var devOpsService = new AzureDevOpsService(
+ devOpsConnection,
+ loggerFactory.CreateLogger());
+ return devOpsService;
+ }
- var devOpsService = new AzureDevOpsService(devOpsConnection, devOpsServiceLogger);
- var gitHubService = new GitHubService(loggerFactory.CreateLogger());
- var credential = new ClientSecretCredential(
- Environment.GetEnvironmentVariable(aadTenantVar),
- Environment.GetEnvironmentVariable(aadAppIdVar),
- Environment.GetEnvironmentVariable(aadAppSecretVar));
- var githubToAadResolver = new GitHubToAADConverter(
- credential,
- loggerFactory.CreateLogger()
- );
- var configurator = new NotificationConfigurator(devOpsService,
- gitHubService, notificationConfiguratorLogger);
- await configurator.ConfigureNotifications(
- project,
- pathPrefix,
- githubToAadResolver,
- persistChanges: !dryRun,
- strategy: selectionStrategy);
+ private static GitHubService GitHubService(ILoggerFactory loggerFactory)
+ => new GitHubService(loggerFactory.CreateLogger());
+
+ private static GitHubToAADConverter GitHubToAADConverter(
+ string aadTenantVar,
+ string aadAppIdVar,
+ string aadAppSecretVar,
+ ILoggerFactory loggerFactory)
+ {
+ var credential = new ClientSecretCredential(
+ Environment.GetEnvironmentVariable(aadTenantVar),
+ Environment.GetEnvironmentVariable(aadAppIdVar),
+ Environment.GetEnvironmentVariable(aadAppSecretVar));
- }
+ var githubToAadResolver = new GitHubToAADConverter(
+ credential,
+ loggerFactory.CreateLogger()
+ );
+ return githubToAadResolver;
}
}