From 32847c38f7ef91e014af6a8260f35bd005299276 Mon Sep 17 00:00:00 2001 From: Konrad Jamrozik Date: Thu, 26 Jan 2023 11:22:46 -0800 Subject: [PATCH] https://github.com/Azure/azure-sdk-tools/pull/4963 --- README.md | 4 +- ...Azure.Sdk.Tools.CheckEnforcer.Tests.csproj | 19 -- .../GitHubWebhookSignatureValidatorTest.cs | 27 --- .../Azure.Sdk.Tools.CheckEnforcer.csproj | 34 ---- .../CheckEnforcerException.cs | 17 -- .../CheckEnforcerSecurityException.cs | 17 -- .../CheckEnforcerConfigurationException.cs | 17 -- .../GlobalConfigurationProvider.cs | 108 ----------- .../IGlobalConfigurationProvider.cs | 14 -- .../Configuration/IRepositoryConfiguration.cs | 15 -- .../IRepositoryConfigurationProvider.cs | 13 -- .../Configuration/RepositoryConfiguration.cs | 44 ----- .../RepositoryConfigurationProvider.cs | 84 --------- .../Constants.cs | 11 -- .../GitHubWebhookOverEventHubsFunction.cs | 113 ----------- .../Functions/PingFunction.cs | 25 --- .../Functions/PullRequestTimeoutFunction.cs | 174 ----------------- .../GitHubWebhookProcessor.cs | 105 ----------- .../Handlers/CheckRunHandler.cs | 131 ------------- .../Handlers/Handler.cs | 178 ------------------ .../Handlers/HandlerContext.cs | 19 -- .../Handlers/IssueCommentHandler.cs | 59 ------ .../Handlers/PullRequestHandler.cs | 70 ------- .../GitHub/GitHubClientProvider.cs | 138 -------------- .../Integrations/GitHub/GitHubRateLimiter.cs | 28 --- .../GitHub/GitHubWebhookSignatureValidator.cs | 27 --- .../GitHub/IGitHubClientProvider.cs | 15 -- .../IPullRequestTracker.cs | 14 -- .../PullRequestTracking/PullRequestTracker.cs | 70 ------- .../PullRequestTrackingTicket.cs | 31 --- .../Azure.Sdk.Tools.CheckEnforcer/Startup.cs | 61 ------ .../Azure.Sdk.Tools.CheckEnforcer/host.json | 3 - tools/check-enforcer/CheckEnforcer.sln | 31 --- tools/check-enforcer/README.md | 65 ------- tools/check-enforcer/ci.yml | 31 --- 35 files changed, 3 insertions(+), 1809 deletions(-) delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer.Tests/Azure.Sdk.Tools.CheckEnforcer.Tests.csproj delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer.Tests/Integrations/GitHub/GitHubWebhookSignatureValidatorTest.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Azure.Sdk.Tools.CheckEnforcer.csproj delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/CheckEnforcerException.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/CheckEnforcerSecurityException.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/CheckEnforcerConfigurationException.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/GlobalConfigurationProvider.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/IGlobalConfigurationProvider.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/IRepositoryConfiguration.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/IRepositoryConfigurationProvider.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/RepositoryConfiguration.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/RepositoryConfigurationProvider.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Constants.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Functions/GitHubWebhookOverEventHubsFunction.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Functions/PingFunction.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Functions/PullRequestTimeoutFunction.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/GitHubWebhookProcessor.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Handlers/CheckRunHandler.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Handlers/Handler.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Handlers/HandlerContext.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Handlers/IssueCommentHandler.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Handlers/PullRequestHandler.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Integrations/GitHub/GitHubClientProvider.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Integrations/GitHub/GitHubRateLimiter.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Integrations/GitHub/GitHubWebhookSignatureValidator.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Integrations/GitHub/IGitHubClientProvider.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Services/PullRequestTracking/IPullRequestTracker.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Services/PullRequestTracking/PullRequestTracker.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Services/PullRequestTracking/PullRequestTrackingTicket.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Startup.cs delete mode 100644 tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/host.json delete mode 100644 tools/check-enforcer/CheckEnforcer.sln delete mode 100644 tools/check-enforcer/README.md delete mode 100644 tools/check-enforcer/ci.yml diff --git a/README.md b/README.md index 531fd2b7b4c..fa0931e80c9 100644 --- a/README.md +++ b/README.md @@ -6,13 +6,15 @@ This repository contains useful tools that the Azure SDK team utilizes across th | Package or Intent | Path | Description | Status | | ------------------------------ | ------------------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| Check Enforcer | [Readme](tools/check-enforcer/README.md) | Manage GitHub check-runs in a mono-repo. | Not Yet Enabled | +| Check Enforcer [1] | [Readme](https://github.com/Azure/azure-sdk-actions/blob/main/docs/check-enforcer.md) | Manage GitHub check-runs in a mono-repo. | Enabled via GitHub actions | | doc-warden | [Readme](packages/python-packages/doc-warden/README.md) | A tool used to enforce readme standards across Azure SDK Repos. | [![Build Status](https://dev.azure.com/azure-sdk/public/_apis/build/status/108?branchName=main)](https://dev.azure.com/azure-sdk/public/_build/latest?definitionId=108&branchName=main) | | http-fault-injector | [Readme](tools/http-fault-injector/README.md) | HTTP proxy server for testing HTTP clients during "faults" like "connection closed in middle of body". | [![Build Status](https://dev.azure.com/azure-sdk/internal/_apis/build/status/tools/tools%20-%20http-fault-injector?branchName=main)](https://dev.azure.com/azure-sdk/internal/_build/latest?definitionId=2340&branchName=main) | | Maven Plugin for Snippets | [Readme](packages/java-packages/snippet-replacer-maven-plugin/README.md) | A Maven plugin that that updates code snippets referenced from javadoc comments. | Not Yet Enabled | | pixel insertion tool | [Readme](scripts/python/readme_tracking/readme.md) | A tool used to insert the requests for images served by `pixel server`. | Not Yet Enabled | | pixel-server | [Readme](web/pixel-server/README.md) | A tiny ASP.NET Core site used to serve a pixel and record impressions. | Not Yet Enabled | +[1] Check Enforcer is located in `azure-sdk-actions` repo. + ## Contributing This project welcomes contributions and suggestions. Most contributions require you to agree to a diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer.Tests/Azure.Sdk.Tools.CheckEnforcer.Tests.csproj b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer.Tests/Azure.Sdk.Tools.CheckEnforcer.Tests.csproj deleted file mode 100644 index e5b23525651..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer.Tests/Azure.Sdk.Tools.CheckEnforcer.Tests.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - net6.0 - - false - - - - - - - - - - - - - diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer.Tests/Integrations/GitHub/GitHubWebhookSignatureValidatorTest.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer.Tests/Integrations/GitHub/GitHubWebhookSignatureValidatorTest.cs deleted file mode 100644 index 64f80559d01..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer.Tests/Integrations/GitHub/GitHubWebhookSignatureValidatorTest.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Azure.Sdk.Tools.CheckEnforcer.Integrations.GitHub; -using NUnit.Framework; -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; -using System.Threading.Tasks; - -namespace Azure.Sdk.Tools.CheckEnforcer.Tests.Integrations.GitHub -{ - public class GitHubWebhookSignatureValidatorTest - { - [Test] - public void VerifyMatchesGitHubTestCase() - { - var signature = "sha1=d03207e4b030cf234e3447bac4d93add4c6643d8"; - var secret = "mysecret"; - var payload = "{\"foo\":\"bar\"}"; - - var payloadBytes = Encoding.UTF8.GetBytes(payload); - - var isValid = GitHubWebhookSignatureValidator.IsValid(payloadBytes, signature, secret); - - Assert.IsTrue(isValid); - } - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Azure.Sdk.Tools.CheckEnforcer.csproj b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Azure.Sdk.Tools.CheckEnforcer.csproj deleted file mode 100644 index a985ca62e6d..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Azure.Sdk.Tools.CheckEnforcer.csproj +++ /dev/null @@ -1,34 +0,0 @@ - - - net6.0 - v3 - - <_FunctionsSkipCleanOutput>true - - - - - - - - - - - - - - - - - - - - - PreserveNewest - - - PreserveNewest - Never - - - \ No newline at end of file diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/CheckEnforcerException.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/CheckEnforcerException.cs deleted file mode 100644 index a3229b2483e..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/CheckEnforcerException.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Azure.Sdk.Tools.CheckEnforcer -{ - - [Serializable] - public class CheckEnforcerException : Exception - { - public CheckEnforcerException(string message) : base(message) { } - public CheckEnforcerException(string message, Exception inner) : base(message, inner) { } - protected CheckEnforcerException( - System.Runtime.Serialization.SerializationInfo info, - System.Runtime.Serialization.StreamingContext context) : base(info, context) { } - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/CheckEnforcerSecurityException.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/CheckEnforcerSecurityException.cs deleted file mode 100644 index 3285b5d0f63..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/CheckEnforcerSecurityException.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Azure.Sdk.Tools.CheckEnforcer -{ - - [Serializable] - public class CheckEnforcerSecurityException : CheckEnforcerException - { - public CheckEnforcerSecurityException(string message) : base(message) { } - public CheckEnforcerSecurityException(string message, Exception inner) : base(message, inner) { } - protected CheckEnforcerSecurityException( - System.Runtime.Serialization.SerializationInfo info, - System.Runtime.Serialization.StreamingContext context) : base(info, context) { } - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/CheckEnforcerConfigurationException.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/CheckEnforcerConfigurationException.cs deleted file mode 100644 index e241d053c8c..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/CheckEnforcerConfigurationException.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Azure.Sdk.Tools.CheckEnforcer.Configuration -{ - - [Serializable] - public class CheckEnforcerConfigurationException : Exception - { - public CheckEnforcerConfigurationException(string message) : base(message) { } - public CheckEnforcerConfigurationException(string message, Exception inner) : base(message, inner) { } - protected CheckEnforcerConfigurationException( - System.Runtime.Serialization.SerializationInfo info, - System.Runtime.Serialization.StreamingContext context) : base(info, context) { } - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/GlobalConfigurationProvider.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/GlobalConfigurationProvider.cs deleted file mode 100644 index 262f48a9fa3..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/GlobalConfigurationProvider.cs +++ /dev/null @@ -1,108 +0,0 @@ -using Azure.Data.AppConfiguration; -using Azure.Sdk.Tools.CheckEnforcer.Configuration; -using Microsoft.Extensions.Azure; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Azure.Sdk.Tools.CheckEnforcer -{ - public class GlobalConfigurationProvider : IGlobalConfigurationProvider - { - private ConfigurationClient configurationClient; - - public GlobalConfigurationProvider(ConfigurationClient configurationClient) - { - this.configurationClient = configurationClient; - } - - private object applicationIDLock = new object(); - private string applicationID; - - public string GetApplicationID() - { - if (applicationID == null) - { - lock(applicationIDLock) - { - if (applicationID == null) - { - ConfigurationSetting applicationIDSetting = configurationClient.GetConfigurationSetting( - "checkenforcer/github-app-id" - ); - applicationID = applicationIDSetting.Value; - } - } - } - - return applicationID; - } - - private object applicationNameLock = new object(); - private string applicationName; - - public string GetApplicationName() - { - if (applicationName == null) - { - lock (applicationNameLock) - { - if (applicationName == null) - { - ConfigurationSetting applicationNameSetting = configurationClient.GetConfigurationSetting( - "checkenforcer/check-name" - ); - applicationName = applicationNameSetting.Value; - } - } - } - - return applicationName; - } - - private object maxRequestsPerPeriodLock = new object(); - private int maxRequestsPerPeriod = -1; - - public int GetMaxRequestsPerPeriod() - { - if (maxRequestsPerPeriod == -1) - { - lock (maxRequestsPerPeriodLock) - { - if (maxRequestsPerPeriod == -1) - { - ConfigurationSetting applicationNameSetting = configurationClient.GetConfigurationSetting( - "checkenforcer/max-requests-per-period" - ); - maxRequestsPerPeriod = int.Parse(applicationNameSetting.Value); - } - } - } - - return maxRequestsPerPeriod; - } - - private object periodDurationInSecondsLock = new object(); - private int periodDurationInSeconds = -1; - - public int GetPeriodDurationInSeconds() - { - if (periodDurationInSeconds == -1) - { - lock (periodDurationInSecondsLock) - { - if (periodDurationInSeconds == -1) - { - ConfigurationSetting applicationNameSetting = configurationClient.GetConfigurationSetting( - "checkenforcer/period-duration-in-seconds" - ); - periodDurationInSeconds = int.Parse(applicationNameSetting.Value); - } - } - } - - return periodDurationInSeconds; - } - - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/IGlobalConfigurationProvider.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/IGlobalConfigurationProvider.cs deleted file mode 100644 index 504696b2390..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/IGlobalConfigurationProvider.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Azure.Sdk.Tools.CheckEnforcer.Configuration -{ - public interface IGlobalConfigurationProvider - { - string GetApplicationID(); - string GetApplicationName(); - int GetMaxRequestsPerPeriod(); - int GetPeriodDurationInSeconds(); - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/IRepositoryConfiguration.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/IRepositoryConfiguration.cs deleted file mode 100644 index b526a2bdd59..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/IRepositoryConfiguration.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Azure.Sdk.Tools.CheckEnforcer.Configuration -{ - public interface IRepositoryConfiguration - { - string Format { get; } - bool IsEnabled { get; } - uint MinimumCheckRuns { get; } - uint TimeoutInMinutes { get; } - string Message { get; } - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/IRepositoryConfigurationProvider.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/IRepositoryConfigurationProvider.cs deleted file mode 100644 index 7612df2c5d1..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/IRepositoryConfigurationProvider.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace Azure.Sdk.Tools.CheckEnforcer.Configuration -{ - public interface IRepositoryConfigurationProvider - { - Task GetRepositoryConfigurationAsync(long installationId, long repositoryId, string pullRequestSha, CancellationToken cancellationToken); - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/RepositoryConfiguration.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/RepositoryConfiguration.cs deleted file mode 100644 index e037c9cbe83..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/RepositoryConfiguration.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Text; -using YamlDotNet.Serialization; - -namespace Azure.Sdk.Tools.CheckEnforcer.Configuration -{ - public class RepositoryConfiguration : IRepositoryConfiguration - { - public RepositoryConfiguration() - { - // These value represent defaults. - MinimumCheckRuns = 1; - IsEnabled = true; - TimeoutInMinutes = 1; - Message = @"This repository is protected by Check Enforcer. The _check-enforcer_ check-run will not pass until there is at least one more check-run successfully passing. Check Enforcer supports the following comment commands: - -- ```/check-enforcer evaluate```; tells Check Enforcer to evaluate this pull request. -- ```/check-enforcer override```; by-pass Check Enforcer (approvals still required)."; - } - - [YamlMember(Alias = "minimumCheckRuns")] - public uint MinimumCheckRuns { get; internal set; } - - [YamlMember(Alias = "enabled")] - public bool IsEnabled { get; internal set; } - - [YamlMember(Alias = "format")] - public string Format { get; internal set; } - - [YamlMember(Alias = "timeout")] - public uint TimeoutInMinutes { get; internal set; } - - [YamlMember(Alias = "message")] - public string Message { get; internal set; } - - public override string ToString() - { - var json = JsonConvert.SerializeObject(this); - return json; - } - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/RepositoryConfigurationProvider.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/RepositoryConfigurationProvider.cs deleted file mode 100644 index ba22118fb65..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Configuration/RepositoryConfigurationProvider.cs +++ /dev/null @@ -1,84 +0,0 @@ -using Azure.Sdk.Tools.CheckEnforcer.Integrations.GitHub; -using Microsoft.Extensions.Caching.Memory; -using Octokit; -using System; -using System.Collections.Concurrent; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using YamlDotNet.RepresentationModel; -using YamlDotNet.Serialization; - -namespace Azure.Sdk.Tools.CheckEnforcer.Configuration -{ - public class RepositoryConfigurationProvider : IRepositoryConfigurationProvider - { - public RepositoryConfigurationProvider(IGitHubClientProvider gitHubClientProvider, IMemoryCache cache, GitHubRateLimiter limiter) - { - this.gitHubClientProvider = gitHubClientProvider; - this.cache = cache; - this.limiter = limiter; - } - - private IGitHubClientProvider gitHubClientProvider; - private IMemoryCache cache; - private GitHubRateLimiter limiter; - private const int RepositoryConfigurationCacheDurationInSeconds = 60; - - public async Task GetRepositoryConfigurationAsync(long installationId, long repositoryId, string pullRequestSha, CancellationToken cancellationToken) - { - var repositoryConfigurationCacheKey = $"{installationId}/{repositoryId}_repositoryConfigurationCacheKey"; // HACK: Config is global across all branches at the moment. - - var configuration = await cache.GetOrCreateAsync(repositoryConfigurationCacheKey, async (entry) => - { - entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(RepositoryConfigurationCacheDurationInSeconds); - - try - { - var client = await gitHubClientProvider.GetInstallationClientAsync(installationId, cancellationToken); - await limiter.WaitForGitHubCapacityAsync(); - var searchResults = await client.Repository.Content.GetAllContents(repositoryId, "eng/CHECKENFORCER"); - var configurationFile = searchResults.Single(); - ThrowIfInvalidFormat(configurationFile); - - var builder = new DeserializerBuilder().Build(); - var configuration = builder.Deserialize(configurationFile.Content); - - return configuration; - } - catch (NotFoundException) // OK, we just disable if it isn't configured. - { - var configuration = new RepositoryConfiguration() - { - IsEnabled = false - }; - - return configuration; - } - }); - - return configuration; - } - - private static void ThrowIfInvalidFormat(RepositoryContent configurationFile) - { - // TODO: This is gross. I want to look more closely at the YamlDotNet API - // to see if there is a way I can parse this file once and then do - // deserialization of a document. At the moment I'm parsing the string - // twice. I suspect that I'm just not grokking the API properly yet. - - var stream = new StringReader(configurationFile.Content); - var yaml = new YamlStream(); - yaml.Load(stream); - - var mapping = (YamlMappingNode)yaml.Documents[0].RootNode; - var formatScalar = (YamlScalarNode)mapping.Children[new YamlScalarNode("format")]; - - if (formatScalar.Value != "v0.1-alpha") - { - throw new CheckEnforcerConfigurationException("The value for the 'format' was not valid. Try v0.1-alpha."); - } - } - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Constants.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Constants.cs deleted file mode 100644 index 512c3e120bf..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Constants.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Azure.Sdk.Tools.CheckEnforcer -{ - public class Constants - { - public const int ApplicationTokenLifetimeInMinutes = 10; - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Functions/GitHubWebhookOverEventHubsFunction.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Functions/GitHubWebhookOverEventHubsFunction.cs deleted file mode 100644 index 61c28933ed4..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Functions/GitHubWebhookOverEventHubsFunction.cs +++ /dev/null @@ -1,113 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Threading; -using System.Threading.Tasks; -using Azure.Sdk.Tools.CheckEnforcer.Integrations.GitHub; -using Azure.Security.KeyVault.Secrets; -using Microsoft.Azure.EventHubs; -using Microsoft.Azure.WebJobs; -using Microsoft.Extensions.Azure; -using Microsoft.Extensions.Logging; -using Octokit; - -namespace Azure.Sdk.Tools.CheckEnforcer.Functions -{ - public class GitHubWebhookOverEventHubsFunction - { - public GitHubWebhookOverEventHubsFunction(GitHubWebhookProcessor processor, SecretClient secretClient) - { - this.processor = processor; - this.secretClient = secretClient; - } - - private GitHubWebhookProcessor processor; - private SecretClient secretClient; - - [FunctionName("webhook-eventhubs")] - public async Task Run([EventHubTrigger("github-webhooks", Connection = "CheckEnforcerEventHubConnectionString")] EventData eventData, ILogger log, CancellationToken cancellationToken) - { - var processingDelay = DateTimeOffset.UtcNow - eventData.SystemProperties.EnqueuedTimeUtc; - log.LogInformation("Webhook event processing delay is: {seconds} (seconds)", processingDelay.TotalSeconds); - - var message = GetMessage(eventData); - var eventName = GetEventName(message); - var eventSignature = GetEventSignature(message); - - var encodedContent = message.RootElement.GetProperty("content").ToString(); - var contentBytes = Convert.FromBase64String(encodedContent); - var json = ReadAndVerifyContent(contentBytes, eventSignature); - - await processor.ProcessWebhookAsync(eventName, json, log, cancellationToken); - } - - private static string gitHubAppWebhookSecret; - private static object gitHubAppWebhookSecretLock = new object(); - - private const string GitHubWebhookSecretName = "github-app-webhook-secret"; - - private string GetGitHubAppWebhookSecret() - { - if (gitHubAppWebhookSecret == null) - { - lock (gitHubAppWebhookSecretLock) - { - if (gitHubAppWebhookSecret == null) - { - KeyVaultSecret secret = secretClient.GetSecret(GitHubWebhookSecretName); - gitHubAppWebhookSecret = secret.Value; - } - } - } - - return gitHubAppWebhookSecret; - } - - private string ReadAndVerifyContent(byte[] contentBytes, string signature) - { - var secret = GetGitHubAppWebhookSecret(); - var isValid = GitHubWebhookSignatureValidator.IsValid(contentBytes, signature, secret); - - if (!isValid) - { - throw new CheckEnforcerSecurityException("Webhook signature validation failed."); - } - - var content = Encoding.UTF8.GetString(contentBytes); - return content; - } - - private string GetEventName(JsonDocument message) - { - return message - .RootElement - .GetProperty("headers") - .GetProperty("X-GitHub-Event") - .EnumerateArray() - .Single() - .ToString(); - } - - private string GetEventSignature(JsonDocument message) - { - return message - .RootElement - .GetProperty("headers") - .GetProperty(GitHubWebhookSignatureValidator.GitHubWebhookSignatureHeader) - .EnumerateArray() - .Single() - .ToString(); - } - - private JsonDocument GetMessage(EventData eventData) - { - string messageBody = Encoding.UTF8.GetString(eventData.Body.Array, eventData.Body.Offset, eventData.Body.Count); - var message = JsonDocument.Parse(messageBody); - return message; - } - } -} \ No newline at end of file diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Functions/PingFunction.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Functions/PingFunction.cs deleted file mode 100644 index c83b0c764b5..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Functions/PingFunction.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Linq; -using System.IO; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.WebJobs; -using Microsoft.Azure.WebJobs.Extensions.Http; -using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Logging; -using System.Threading; - -namespace Azure.Sdk.Tools.CheckEnforcer.Functions -{ - public class PingFunction - { - [FunctionName("ping")] - public async Task Run( - [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req, - ILogger log, CancellationToken cancellationToken) - { - return new OkResult(); - } - - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Functions/PullRequestTimeoutFunction.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Functions/PullRequestTimeoutFunction.cs deleted file mode 100644 index fc9ec956311..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Functions/PullRequestTimeoutFunction.cs +++ /dev/null @@ -1,174 +0,0 @@ -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Azure.Sdk.Tools.CheckEnforcer.Configuration; -using Azure.Sdk.Tools.CheckEnforcer.Integrations.GitHub; -using Azure.Sdk.Tools.CheckEnforcer.Services.PullRequestTracking; -using Microsoft.Azure.WebJobs; -using Microsoft.Azure.WebJobs.Host; -using Microsoft.Extensions.Logging; -using Octokit; -using YamlDotNet.Core.Events; - -namespace Azure.Sdk.Tools.CheckEnforcer.Functions -{ - public class PullRequestTimeoutFunction - { - public PullRequestTimeoutFunction(IPullRequestTracker pullRequestTracker, IGitHubClientProvider gitHubClientProvider, GitHubRateLimiter limiter, IRepositoryConfigurationProvider repositoryConfigurationProvider, IGlobalConfigurationProvider globalConfigurationProvider) - { - this.pullRequestTracker = pullRequestTracker; - this.gitHubClientProvider = gitHubClientProvider; - this.limiter = limiter; - this.repositoryConfigurationProvider = repositoryConfigurationProvider; - this.globalConfigurationProvider = globalConfigurationProvider; - } - - private IPullRequestTracker pullRequestTracker; - private IGitHubClientProvider gitHubClientProvider; - private GitHubRateLimiter limiter; - private IRepositoryConfigurationProvider repositoryConfigurationProvider; - private IGlobalConfigurationProvider globalConfigurationProvider; - - [FunctionName("PullRequestTimeoutFunction")] - public async Task Run([TimerTrigger("*/30 * * * * *")]TimerInfo myTimer, ILogger log, CancellationToken cancellationToken) - { - log.LogInformation("Fetching tracked pull request tickets."); - var pullRequestTrackingTickets = await pullRequestTracker.GetTrackedPullRequestsAsync(); - log.LogInformation("Found {ticketCount} pull request tickets.", pullRequestTrackingTickets.Count()); - - foreach (var pullRequestTrackingTicket in pullRequestTrackingTickets) - { - log.LogInformation( - "Processing pull request tracking ticket for installation {installationId} in repository {repositoryId} for pull request number {pullRequestNumber}.", - pullRequestTrackingTicket.InstallationId, - pullRequestTrackingTicket.RepositoryId, - pullRequestTrackingTicket.PullRequestNumber - ); - - var gitHubClient = await gitHubClientProvider.GetInstallationClientAsync( - pullRequestTrackingTicket.InstallationId, - cancellationToken - ); - - await limiter.WaitForGitHubCapacityAsync(); - var pullRequest = await gitHubClient.PullRequest.Get( - pullRequestTrackingTicket.RepositoryId, - pullRequestTrackingTicket.PullRequestNumber - ); - - var sha = pullRequest.Head.Sha; - log.LogInformation( - "HEAD SHA for pull request {pullRequestNumber} is {sha}", - pullRequestTrackingTicket.PullRequestNumber, - sha - ); - - var configuration = await repositoryConfigurationProvider.GetRepositoryConfigurationAsync( - pullRequestTrackingTicket.InstallationId, - pullRequestTrackingTicket.RepositoryId, - sha, - cancellationToken - ); - - if (configuration.IsEnabled != true) - { - log.LogInformation( - "Stopping tracking for pull request {pullRequestNumber} in repository {repositoryId} for installation {installationId} because Check Enforcer is not enabled.", - pullRequestTrackingTicket.PullRequestNumber, - pullRequestTrackingTicket.RepositoryId, - pullRequestTrackingTicket.InstallationId - ); - await pullRequestTracker.StopTrackingPullRequestAsync(pullRequestTrackingTicket); - continue; - } - else if (pullRequest.State != new StringEnum(ItemState.Open)) - { - log.LogInformation( - "Stopping tracking for pull request {pullRequestNumber} in repository {repositoryId} for installation {installationId} because it is no longer open.", - pullRequestTrackingTicket.PullRequestNumber, - pullRequestTrackingTicket.RepositoryId, - pullRequestTrackingTicket.InstallationId - ); - await pullRequestTracker.StopTrackingPullRequestAsync(pullRequestTrackingTicket); - continue; - } - else if (DateTimeOffset.UtcNow < pullRequest.UpdatedAt.AddMinutes(configuration.TimeoutInMinutes)) - { - log.LogInformation( - "Skipping pull request {pullRequestNumber} in repository {repositoryId} for installation {installationId} because it is still too new.", - pullRequestTrackingTicket.PullRequestNumber, - pullRequestTrackingTicket.RepositoryId, - pullRequestTrackingTicket.InstallationId - ); - continue; - } - else - { - await limiter.WaitForGitHubCapacityAsync(); - var checkRunRepsonse = await gitHubClient.Check.Run.GetAllForReference(pullRequestTrackingTicket.RepositoryId, sha); - - if (checkRunRepsonse.TotalCount > 0 && checkRunRepsonse.CheckRuns.All((checkRun) => checkRun.Name == globalConfigurationProvider.GetApplicationName())) - { - log.LogInformation( - "Fetching comments for pull request {pullRequestNumber} in repository {repositoryId} for installation {installationId}.", - pullRequestTrackingTicket.PullRequestNumber, - pullRequestTrackingTicket.RepositoryId, - pullRequestTrackingTicket.InstallationId - ); - - await limiter.WaitForGitHubCapacityAsync(); - var issueComments = await gitHubClient.Issue.Comment.GetAllForIssue(pullRequestTrackingTicket.RepositoryId, pullRequestTrackingTicket.PullRequestNumber); - - log.LogInformation( - "Found {commentCount} on pull request {pullRequestNumber} in repository {repositoryId} for installation {installationId}.", - issueComments.Count(), - pullRequestTrackingTicket.PullRequestNumber, - pullRequestTrackingTicket.RepositoryId, - pullRequestTrackingTicket.InstallationId - ); - - if (issueComments.Any((comment) => comment.User.Login.StartsWith("check-enforcer"))) - { - log.LogInformation( - "Stopping tracking {pullRequestNumber} in repository {repositoryId} for installation {installationId} because it already has help comment.", - pullRequestTrackingTicket.PullRequestNumber, - pullRequestTrackingTicket.RepositoryId, - pullRequestTrackingTicket.InstallationId - ); - await pullRequestTracker.StopTrackingPullRequestAsync(pullRequestTrackingTicket); - } - else - { - log.LogInformation( - "Adding timeout comment to pull request {pullRequestNumber} in repository {repositoryId} for installation {installationId} because it has no check runs.", - pullRequestTrackingTicket.PullRequestNumber, - pullRequestTrackingTicket.RepositoryId, - pullRequestTrackingTicket.InstallationId - ); - - await gitHubClient.Issue.Comment.Create( - pullRequestTrackingTicket.RepositoryId, - pullRequestTrackingTicket.PullRequestNumber, - configuration.Message - ); - - await pullRequestTracker.StopTrackingPullRequestAsync(pullRequestTrackingTicket); - } - - } - else - { - log.LogInformation( - "Stopping tracking pull request {pullRequestNumber} in repository {repositoryId} for installation{installationId} because it has checks.", - pullRequestTrackingTicket.PullRequestNumber, - pullRequestTrackingTicket.RepositoryId, - pullRequestTrackingTicket.InstallationId - ); - await pullRequestTracker.StopTrackingPullRequestAsync(pullRequestTrackingTicket); - } - } - } - } - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/GitHubWebhookProcessor.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/GitHubWebhookProcessor.cs deleted file mode 100644 index 5c57eb14c26..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/GitHubWebhookProcessor.cs +++ /dev/null @@ -1,105 +0,0 @@ -using Azure.Core; -using Azure.Identity; -using Azure.Sdk.Tools.CheckEnforcer.Configuration; -using Azure.Sdk.Tools.CheckEnforcer.Handlers; -using Azure.Sdk.Tools.CheckEnforcer.Integrations.GitHub; -using Azure.Sdk.Tools.CheckEnforcer.Services.PullRequestTracking; -using Azure.Security.KeyVault.Keys; -using Azure.Security.KeyVault.Keys.Cryptography; -using Azure.Security.KeyVault.Secrets; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.WebJobs.Host; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Primitives; -using Microsoft.IdentityModel.Tokens; -using Octokit; -using Octokit.Internal; -using Polly; -using System; -using System.Collections; -using System.Collections.Generic; -using System.IdentityModel.Tokens.Jwt; -using System.IO; -using System.Linq; -using System.Reflection.Metadata; -using System.Security.Cryptography; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace Azure.Sdk.Tools.CheckEnforcer -{ - public class GitHubWebhookProcessor - { - public GitHubWebhookProcessor(IGlobalConfigurationProvider globalConfigurationProvider, IGitHubClientProvider gitHubClientProvider, IRepositoryConfigurationProvider repositoryConfigurationProvider, SecretClient secretClient, GitHubRateLimiter limiter, IPullRequestTracker pullRequestTracker) - { - this.globalConfigurationProvider = globalConfigurationProvider; - this.gitHubClientProvider = gitHubClientProvider; - this.repositoryConfigurationProvider = repositoryConfigurationProvider; - this.secretClient = secretClient; - this.limiter = limiter; - this.pullRequestTracker = pullRequestTracker; - } - - public IGlobalConfigurationProvider globalConfigurationProvider; - public IGitHubClientProvider gitHubClientProvider; - private IRepositoryConfigurationProvider repositoryConfigurationProvider; - private SecretClient secretClient; - private GitHubRateLimiter limiter; - private IPullRequestTracker pullRequestTracker; - - private const string GitHubEventHeader = "X-GitHub-Event"; - public async Task ProcessWebhookAsync(string eventName, string json, ILogger logger, CancellationToken cancellationToken) - { - await Policy - .Handle() - .Or() - .RetryAsync(3, async (ex, retryCount) => - { - TimeSpan retryDelay = TimeSpan.FromSeconds(10); // Default. - - switch (ex) - { - case AbuseException abuseException: - retryDelay = TimeSpan.FromSeconds((double)abuseException.RetryAfterSeconds); - logger.LogWarning("Abuse exception detected. Retry after seconds is: {retrySeconds}", - abuseException.RetryAfterSeconds - ); - break; - - case RateLimitExceededException rateLimitExceededException: - retryDelay = rateLimitExceededException.GetRetryAfterTimeSpan(); - logger.LogWarning( - "Rate limit exception detected. Limit is: {limit}, reset is: {reset}, retry seconds is: {retrySeconds}", - rateLimitExceededException.Limit, - rateLimitExceededException.Reset, - retryDelay.TotalSeconds - ); - break; - } - - logger.LogInformation("Waiting for {seconds} before retrying.", retryDelay.TotalSeconds); - await Task.Delay(retryDelay); - }) - .ExecuteAsync(async () => - { - if (eventName == "check_run") - { - var handler = new CheckRunHandler(globalConfigurationProvider, gitHubClientProvider, repositoryConfigurationProvider, logger, limiter); - await handler.HandleAsync(json, cancellationToken); - } - else if (eventName == "issue_comment") - { - var handler = new IssueCommentHandler(globalConfigurationProvider, gitHubClientProvider, repositoryConfigurationProvider, logger, limiter); - await handler.HandleAsync(json, cancellationToken); - } - else if (eventName == "pull_request") - { - var handler = new PullRequestHandler(globalConfigurationProvider, gitHubClientProvider, repositoryConfigurationProvider, logger, limiter, pullRequestTracker); - await handler.HandleAsync(json, cancellationToken); - } - }); - } - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Handlers/CheckRunHandler.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Handlers/CheckRunHandler.cs deleted file mode 100644 index 99b167134ea..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Handlers/CheckRunHandler.cs +++ /dev/null @@ -1,131 +0,0 @@ -using Azure.Sdk.Tools.CheckEnforcer.Configuration; -using Azure.Sdk.Tools.CheckEnforcer.Integrations.GitHub; -using Microsoft.Extensions.Logging; -using Octokit; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace Azure.Sdk.Tools.CheckEnforcer.Handlers -{ - public class CheckRunHandler : Handler - { - public CheckRunHandler(IGlobalConfigurationProvider globalConfigurationProvider, IGitHubClientProvider gitHubCLientProvider, IRepositoryConfigurationProvider repositoryConfigurationProvider, ILogger logger, GitHubRateLimiter limiter) : base(globalConfigurationProvider, gitHubCLientProvider, repositoryConfigurationProvider, logger, limiter) - { - } - - protected override async Task HandleCoreAsync(HandlerContext context, CancellationToken cancellationToken) - { - var payload = context.Payload; - - var installationId = payload.Installation.Id; - var repositoryId = payload.Repository.Id; - var sha = payload.CheckRun.CheckSuite.HeadSha; - var runIdentifier = $"{installationId}/{repositoryId}/{sha}"; - - using (var scope = Logger.BeginScope("Processing check-run event on: {runIdentifier}", runIdentifier)) - { - if (payload.CheckRun.Name == this.GlobalConfigurationProvider.GetApplicationName() && payload.Action == "requested_action" && payload.RequestedAction.Identifier == "evaluate") - { - Logger.LogInformation( - "Responding to check run action button: {identifier}.", - payload.RequestedAction.Identifier - ); - - await EvaluatePullRequestAsync(context.Client, installationId, repositoryId, sha, cancellationToken); - } - else if (payload.CheckRun.Name.Contains("(")) - { - // HACK: This change short circuits processing of events that come from jobs rather than runs. We - // are leveraging the fact that Azure Pipelines jobs have check names with an opening bracket. - // This is a short term fox to stop the bleeding whilst we figure out a better way to ignore - // notifications from the job level. - - Logger.LogInformation( - "Skipping processing event for: {runIdentifier} because based on the name it is a job, not a run.", - runIdentifier - ); - } - else if (payload.CheckRun.Name == this.GlobalConfigurationProvider.GetApplicationName()) - { - Logger.LogInformation( - "Skipping processing event for: {runIdentifier} because appplication name match.", - runIdentifier - ); - } - else if (payload.CheckRun.StartedAt < DateTimeOffset.UtcNow.Subtract(TimeSpan.FromDays(1))) - { - Logger.LogWarning( - "Skipping stake check-run event for: {runIdentifier} because it started {days} ago.", - runIdentifier, - (DateTimeOffset.UtcNow - payload.CheckRun.StartedAt).Days - ); - } - else if (payload.CheckRun.Conclusion == new StringEnum(CheckConclusion.Neutral)) - { - Logger.LogInformation("" + - "Skipping processing event for: {runIdentifier} check-run conclusion is neutral.", - runIdentifier - ); - return; - } - else if (payload.CheckRun.Status != new StringEnum(CheckStatus.Completed)) - { - Logger.LogInformation( - "Skipping processing event for: {runIdentifier} check-run status not completed.", - runIdentifier - ); - return; - } - else - { - try - { - var configuration = await this.RepositoryConfigurationProvider.GetRepositoryConfigurationAsync(installationId, repositoryId, sha, cancellationToken); - if (configuration.IsEnabled) - { - Logger.LogInformation( - "Check Enforcer was enabled for: {runIdentifier}.", - runIdentifier - ); - } - else - { - Logger.LogInformation( - "Check Enforcer was disabled for: {runIdentifier}.", - runIdentifier - ); - return; - } - - Logger.LogInformation( - "Evaluating check-run for: {runIdentifier}.", - runIdentifier - ); - - await EvaluatePullRequestAsync(context.Client, installationId, repositoryId, sha, cancellationToken); - - Logger.LogInformation( - "Evaluated check-run for: {runIdentifier}.", - runIdentifier - ); - } - catch (Exception ex) - { - Logger.LogError( - ex, - "Failed to process check-run event for: {runIdentifier}", - runIdentifier - ); - - throw ex; - } - } - } - } - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Handlers/Handler.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Handlers/Handler.cs deleted file mode 100644 index 61e7cf267ec..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Handlers/Handler.cs +++ /dev/null @@ -1,178 +0,0 @@ -using Azure.Sdk.Tools.CheckEnforcer.Configuration; -using Azure.Sdk.Tools.CheckEnforcer.Integrations.GitHub; -using Microsoft.Extensions.Logging; -using Octokit; -using Octokit.Internal; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace Azure.Sdk.Tools.CheckEnforcer.Handlers -{ - public abstract class Handler where T: ActivityPayload - { - private const int EventIdBase = 1000; - private static readonly EventId AcquiringSemaphoreEventId = new EventId(EventIdBase + 0, "Acquring Semaphore"); - - public Handler(IGlobalConfigurationProvider globalConfiguratoinProvider, IGitHubClientProvider gitHubClientProvider, IRepositoryConfigurationProvider repositoryConfigurationProvider, ILogger logger, GitHubRateLimiter limiter) - { - this.GlobalConfigurationProvider = globalConfiguratoinProvider; - this.GitHubClientProvider = gitHubClientProvider; - this.RepositoryConfigurationProvider = repositoryConfigurationProvider; - this.Logger = logger; - this.Limiter = limiter; - } - - protected IGlobalConfigurationProvider GlobalConfigurationProvider { get; private set; } - protected IGitHubClientProvider GitHubClientProvider { get; private set; } - protected IRepositoryConfigurationProvider RepositoryConfigurationProvider { get; private set; } - protected ILogger Logger { get; private set; } - public GitHubRateLimiter Limiter { get; private set; } - - protected async Task SetSuccessAsync(GitHubClient client, long repositoryId, string sha, CancellationToken cancellationToken) - { - await Limiter.WaitForGitHubCapacityAsync(); - var response = await client.Check.Run.GetAllForReference(repositoryId, sha); - var runs = response.CheckRuns; - var checkEnforcerRuns = runs.Where(r => r.Name == this.GlobalConfigurationProvider.GetApplicationName()); - - Logger.LogInformation( - "Found {count} Check Enforcer runs on sha {commitSha} in repository {repository}.", - checkEnforcerRuns.Count(), - sha, - repositoryId - ); - - foreach (var checkEnforcerRun in checkEnforcerRuns) - { - Logger.LogInformation("Setting check-run {checkEnforcerRunId} to success.", checkEnforcerRun.Id); - await Limiter.WaitForGitHubCapacityAsync(); - await client.Check.Run.Update(repositoryId, checkEnforcerRun.Id, new CheckRunUpdate() - { - Conclusion = new StringEnum(CheckConclusion.Success), - CompletedAt = DateTimeOffset.UtcNow - }); - Logger.LogInformation("Set check-run {checkEnforcerRunId} to success.", checkEnforcerRun.Id); - } - } - - protected async Task CreateCheckAsync(GitHubClient client, long installationId, long repositoryId, string headSha, bool recreate, CancellationToken cancellationToken) - { - var runIdentifier = $"{installationId}/{repositoryId}/{headSha}"; - - Logger.LogInformation("Checking for existing check-run for: {runIdentifier}", runIdentifier); - - await Limiter.WaitForGitHubCapacityAsync(); - var response = await client.Check.Run.GetAllForReference(repositoryId, headSha); - var runs = response.CheckRuns; - var checkRun = runs - .Where(r => r.Name == this.GlobalConfigurationProvider.GetApplicationName()) - .Where(r => r.Status != new StringEnum(CheckStatus.Completed)) - .FirstOrDefault(); - - if (checkRun == null || recreate) - { - Logger.LogInformation("Creating check-run for: {runIdentifier}", runIdentifier); - - await Limiter.WaitForGitHubCapacityAsync(); - checkRun = await client.Check.Run.Create( - repositoryId, - new NewCheckRun(this.GlobalConfigurationProvider.GetApplicationName(), headSha) - { - Status = new StringEnum(CheckStatus.InProgress), - StartedAt = DateTimeOffset.UtcNow, - Actions = new List() { - new NewCheckRunAction("Evaluate", "Evaluate Check Enforcer rules.", "evaluate") - } - } - ); - - Logger.LogInformation("Created check-run for: {runIdentifier}", runIdentifier); - } - - return checkRun; - } - - protected async Task EvaluatePullRequestAsync(GitHubClient client, long installationId, long repositoryId, string sha, CancellationToken cancellationToken) - { - var runIdentifier = $"{installationId}/{repositoryId}/{sha}"; - - var configuration = await this.RepositoryConfigurationProvider.GetRepositoryConfigurationAsync(installationId, repositoryId, sha, cancellationToken); - - Logger.LogInformation("Fetching check-runs for: {runIdentifier} for evaluation.", runIdentifier); - - await Limiter.WaitForGitHubCapacityAsync(); - var runsResponse = await client.Check.Run.GetAllForReference(repositoryId, sha); - var runs = runsResponse.CheckRuns; - var checkEnforcerRuns = runs.Where(r => r.Name == this.GlobalConfigurationProvider.GetApplicationName()); - - Logger.LogInformation("Check-suite for: {runIdentifier} has {runs.Count} check-enforcer runs (possible race condition?).", runIdentifier, checkEnforcerRuns.Count()); - - foreach (var duplicatedCheckEnforcerRun in checkEnforcerRuns) - { - Logger.LogInformation( - "One check-enforcer run for {runIdentifier} targets SHA {sha} at URL {url}.", - runIdentifier, - duplicatedCheckEnforcerRun.HeadSha, - duplicatedCheckEnforcerRun.HtmlUrl - ); - } - - var otherRuns = from run in runs - where run.Name != this.GlobalConfigurationProvider.GetApplicationName() && run.Conclusion != new StringEnum(CheckConclusion.Neutral) - select run; - - var totalOtherRuns = otherRuns.Count(); - - var outstandingOtherRuns = from run in otherRuns - where run.Conclusion != new StringEnum(CheckConclusion.Success) - select run; - - var totalOutstandingOtherRuns = outstandingOtherRuns.Count(); - if (totalOtherRuns >= configuration.MinimumCheckRuns && totalOutstandingOtherRuns == 0 && checkEnforcerRuns.Any(checkEnforcerRun => checkEnforcerRun.Conclusion != new StringEnum(CheckConclusion.Success))) - { - foreach (var checkEnforcerRun in checkEnforcerRuns) - { - Logger.LogInformation("Updating check-run for: {runIdentifier}", runIdentifier); - await Limiter.WaitForGitHubCapacityAsync(); - await client.Check.Run.Update(repositoryId, checkEnforcerRun.Id, new CheckRunUpdate() - { - Conclusion = new StringEnum(CheckConclusion.Success), - Status = new StringEnum(CheckStatus.Completed), - CompletedAt = DateTimeOffset.UtcNow - }); - Logger.LogInformation("Updated check-run for: {runIdentifier}", runIdentifier); - } - - } - } - - private T DeserializePayload(string json) - { - Logger.LogInformation("Payload: {json}", json); - - SimpleJsonSerializer serializer = new SimpleJsonSerializer(); - var payload = serializer.Deserialize(json); - return payload; - } - - public async Task HandleAsync(string json, CancellationToken cancellationToken) - { - var deserializedPayload = DeserializePayload(json); - var installationId = deserializedPayload.Installation.Id; - - var client = await this.GitHubClientProvider.GetInstallationClientAsync(installationId, cancellationToken); - var context = new HandlerContext(deserializedPayload, client); - - await HandleCoreAsync(context, cancellationToken); - } - - protected abstract Task HandleCoreAsync(HandlerContext context, CancellationToken cancellationToken); - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Handlers/HandlerContext.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Handlers/HandlerContext.cs deleted file mode 100644 index d6295081f68..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Handlers/HandlerContext.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Octokit; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Azure.Sdk.Tools.CheckEnforcer.Handlers -{ - public class HandlerContext where T: ActivityPayload - { - public HandlerContext(T payload, GitHubClient client) - { - this.Payload = payload; - this.Client = client; - } - - public T Payload { get; private set; } - public GitHubClient Client { get; private set; } - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Handlers/IssueCommentHandler.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Handlers/IssueCommentHandler.cs deleted file mode 100644 index b31278473e2..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Handlers/IssueCommentHandler.cs +++ /dev/null @@ -1,59 +0,0 @@ -using Azure.Sdk.Tools.CheckEnforcer.Configuration; -using Azure.Sdk.Tools.CheckEnforcer.Integrations.GitHub; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Primitives; -using Microsoft.Identity.Client; -using Octokit; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace Azure.Sdk.Tools.CheckEnforcer.Handlers -{ - public class IssueCommentHandler : Handler - { - public IssueCommentHandler(IGlobalConfigurationProvider globalConfigurationProvider, IGitHubClientProvider gitHubClientProvider, IRepositoryConfigurationProvider repositoryConfigurationProvider, ILogger logger, GitHubRateLimiter limiter) : base(globalConfigurationProvider, gitHubClientProvider, repositoryConfigurationProvider, logger, limiter) - { - } - - protected override async Task HandleCoreAsync(HandlerContext context, CancellationToken cancellationToken) - { - var payload = context.Payload; - var installationId = payload.Installation.Id; - var repositoryId = payload.Repository.Id; - var comment = payload.Comment.Body.ToLower().Trim(); - var issueId = payload.Issue.Number; - - // Bail early if we aren't even a check enforcer comment. Reduces exception noise. - if (!comment.StartsWith("/check-enforcer")) return; - - await Limiter.WaitForGitHubCapacityAsync(); - var pullRequest = await context.Client.PullRequest.Get(repositoryId, issueId); - var sha = pullRequest.Head.Sha; - - switch (comment) - { - case "/check-enforcer override": - await SetSuccessAsync(context.Client, repositoryId, sha, cancellationToken); - break; - - case "/check-enforcer reset": - await CreateCheckAsync(context.Client, installationId, repositoryId, sha, true, cancellationToken); - await EvaluatePullRequestAsync(context.Client, installationId, repositoryId, sha, cancellationToken); - break; - - case "/check-enforcer evaluate": - await CreateCheckAsync(context.Client, installationId, repositoryId, sha, true, cancellationToken); - await EvaluatePullRequestAsync(context.Client, installationId, repositoryId, sha, cancellationToken); - break; - - default: - this.Logger.LogInformation("Unrecognized command: {comment}", comment); - break; - } - } - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Handlers/PullRequestHandler.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Handlers/PullRequestHandler.cs deleted file mode 100644 index ba59799a906..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Handlers/PullRequestHandler.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Azure.Sdk.Tools.CheckEnforcer.Configuration; -using Azure.Sdk.Tools.CheckEnforcer.Integrations.GitHub; -using Azure.Sdk.Tools.CheckEnforcer.Services.PullRequestTracking; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Primitives; -using Microsoft.Identity.Client; -using Octokit; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace Azure.Sdk.Tools.CheckEnforcer.Handlers -{ - public class PullRequestHandler : Handler - { - public PullRequestHandler(IGlobalConfigurationProvider globalConfigurationProvider, IGitHubClientProvider gitHubClientProvider, IRepositoryConfigurationProvider repositoryConfigurationProvider, ILogger logger, GitHubRateLimiter limiter, IPullRequestTracker pullRequestTracker) : base(globalConfigurationProvider, gitHubClientProvider, repositoryConfigurationProvider, logger, limiter) - { - this.pullRequestTracker = pullRequestTracker; - } - - private IPullRequestTracker pullRequestTracker; - - protected override async Task HandleCoreAsync(HandlerContext context, CancellationToken cancellationToken) - { - var payload = context.Payload; - var installationId = payload.Installation.Id; - var repositoryId = payload.Repository.Id; - var sha = payload.PullRequest.Head.Sha; - var runIdentifier = $"{installationId}/{repositoryId}/{sha}"; - var action = payload.Action; - var pullRequestNumber = payload.PullRequest.Number; - - Logger.LogInformation( - "Received {action} action on PR# {pullRequestNumber} against {runIdentifier}", - action, - pullRequestNumber, - runIdentifier - ); - - if (action == "opened" || action == "reopened" || action == "synchronize") - { - var configuration = await this.RepositoryConfigurationProvider.GetRepositoryConfigurationAsync(installationId, repositoryId, sha, cancellationToken); - - if (configuration.IsEnabled) - { - var pullRequestTrackingTicket = new PullRequestTrackingTicket(installationId, repositoryId, pullRequestNumber); - await pullRequestTracker.StartTrackingPullRequestAsync(pullRequestTrackingTicket); - - await CreateCheckAsync(context.Client, installationId, repositoryId, sha, false, cancellationToken); - - if (action == "reopened") - { - await EvaluatePullRequestAsync(context.Client, installationId, repositoryId, sha, cancellationToken); - } - } - } - else - { - Logger.LogInformation( - "Ignoring pull request event because action was not 'opened' or 'reopened'. It was {action}.", - action - ); - } - - } - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Integrations/GitHub/GitHubClientProvider.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Integrations/GitHub/GitHubClientProvider.cs deleted file mode 100644 index 4985c010167..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Integrations/GitHub/GitHubClientProvider.cs +++ /dev/null @@ -1,138 +0,0 @@ -using Azure.Core; -using Azure.Identity; -using Azure.Sdk.Tools.CheckEnforcer.Configuration; -using Azure.Sdk.Tools.CheckEnforcer.Integrations.GitHub; -using Azure.Security.KeyVault.Keys; -using Azure.Security.KeyVault.Keys.Cryptography; -using Azure.Security.KeyVault.Secrets; -using Microsoft.Extensions.Caching.Memory; -using Microsoft.IdentityModel.Tokens; -using Octokit; -using System; -using System.Collections.Concurrent; -using System.IdentityModel.Tokens.Jwt; -using System.Security.Cryptography; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace Azure.Sdk.Tools.CheckEnforcer -{ - public class GitHubClientProvider : IGitHubClientProvider - { - public GitHubClientProvider(IGlobalConfigurationProvider globalConfigurationProvider, IMemoryCache cache, CryptographyClient cryptographyClient, GitHubRateLimiter limiter) - { - this.globalConfigurationProvider = globalConfigurationProvider; - this.cache = cache; - this.cryptographyClient = cryptographyClient; - this.limiter = limiter; - } - - private IGlobalConfigurationProvider globalConfigurationProvider; - private IMemoryCache cache; - - private async Task GetTokenAsync(CancellationToken cancellationToken) - { - var cachedApplicationToken = await cache.GetOrCreateAsync("applicationTokenCacheKey", async (entry) => - { - entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(Constants.ApplicationTokenLifetimeInMinutes - 1); - - var headerAndPayloadString = GenerateJwtTokenHeaderAndPayload(); - var digest = ComputeHeaderAndPayloadDigest(headerAndPayloadString); - var encodedSignature = await SignHeaderAndPayloadDigestWithGitHubApplicationKey(digest, cancellationToken); - var applicationToken = AppendSignatureToHeaderAndPayload(headerAndPayloadString, encodedSignature); - return applicationToken; - }); - - return cachedApplicationToken; - } - - private string AppendSignatureToHeaderAndPayload(string headerAndPayloadString, string encodedSignature) - { - return $"{headerAndPayloadString}.{encodedSignature}"; - } - - private async Task SignHeaderAndPayloadDigestWithGitHubApplicationKey(byte[] digest, CancellationToken cancellationToken) - { - var signResult = await cryptographyClient.SignAsync( - SignatureAlgorithm.RS256, - digest, - cancellationToken - ); - - var encodedSignature = Base64UrlEncoder.Encode(signResult.Signature); - return encodedSignature; - } - - private byte[] ComputeHeaderAndPayloadDigest(string headerAndPayloadString) - { - var headerAndPayloadBytes = Encoding.UTF8.GetBytes(headerAndPayloadString); - - var sha256 = new SHA256CryptoServiceProvider(); - var digest = sha256.ComputeHash(headerAndPayloadBytes); - return digest; - } - - private string GenerateJwtTokenHeaderAndPayload() - { - var jwtHeader = new JwtHeader(); - jwtHeader["alg"] = "RS256"; - - var jwtPayload = new JwtPayload( - issuer: globalConfigurationProvider.GetApplicationID(), - audience: null, - claims: null, - notBefore: DateTime.UtcNow, - expires: DateTime.UtcNow.AddMinutes(Constants.ApplicationTokenLifetimeInMinutes), - issuedAt: DateTime.UtcNow - ); - - var jwtToken = new JwtSecurityToken(jwtHeader, jwtPayload); - var headerAndPayloadString = $"{jwtToken.EncodedHeader}.{jwtToken.EncodedPayload}"; - return headerAndPayloadString; - } - - private CryptographyClient cryptographyClient; - private GitHubRateLimiter limiter; - - public async Task GetApplicationClientAsync(CancellationToken cancellationToken) - { - var token = await GetTokenAsync(cancellationToken); - - var appClient = new GitHubClient(new ProductHeaderValue(globalConfigurationProvider.GetApplicationName())) - { - Credentials = new Credentials(token, AuthenticationType.Bearer) - }; - - return appClient; - } - - private ConcurrentDictionary cachedInstallationTokens = new ConcurrentDictionary(); - - private async Task GetInstallationTokenAsync(long installationId, CancellationToken cancellationToken) - { - var installationTokenCacheKey = $"{installationId}_installationTokenCacheKey"; - - var cachedInstallationToken = await cache.GetOrCreateAsync(installationTokenCacheKey, async (entry) => - { - var appClient = await GetApplicationClientAsync(cancellationToken); - await limiter.WaitForGitHubCapacityAsync(); - var installationToken = await appClient.GitHubApps.CreateInstallationToken(installationId); - return installationToken; - }); - - return cachedInstallationToken.Token; - } - - public async Task GetInstallationClientAsync(long installationId, CancellationToken cancellationToken) - { - var installationToken = await GetInstallationTokenAsync(installationId, cancellationToken); - var installationClient = new GitHubClient(new ProductHeaderValue($"{globalConfigurationProvider.GetApplicationName()}-{installationId}")) - { - Credentials = new Credentials(installationToken) - }; - - return installationClient; - } - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Integrations/GitHub/GitHubRateLimiter.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Integrations/GitHub/GitHubRateLimiter.cs deleted file mode 100644 index cafea55ab74..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Integrations/GitHub/GitHubRateLimiter.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Azure.Sdk.Tools.CheckEnforcer.Configuration; -using ComposableAsync; -using RateLimiter; -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; - -namespace Azure.Sdk.Tools.CheckEnforcer.Integrations.GitHub -{ - public class GitHubRateLimiter - { - public GitHubRateLimiter(IGlobalConfigurationProvider globalConfigurationProvider) - { - limiter = TimeLimiter.GetFromMaxCountByInterval( - globalConfigurationProvider.GetMaxRequestsPerPeriod(), - TimeSpan.FromSeconds(globalConfigurationProvider.GetPeriodDurationInSeconds()) - ); - } - - private TimeLimiter limiter; - - public async Task WaitForGitHubCapacityAsync() - { - await limiter; - } - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Integrations/GitHub/GitHubWebhookSignatureValidator.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Integrations/GitHub/GitHubWebhookSignatureValidator.cs deleted file mode 100644 index c32f1a46232..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Integrations/GitHub/GitHubWebhookSignatureValidator.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Security.Cryptography; -using System.Text; -using System.Threading.Tasks; -using Azure.Sdk.Tools.CheckEnforcer.Configuration; - -namespace Azure.Sdk.Tools.CheckEnforcer.Integrations.GitHub -{ - public static class GitHubWebhookSignatureValidator - { - public const string GitHubWebhookSignatureHeader = "X-Hub-Signature"; - - public static bool IsValid(byte[] body, string signature, string secret) - { - var key = Encoding.UTF8.GetBytes(secret); - var sha1 = new HMACSHA1(key); - - var digest = sha1.ComputeHash(body); - var hex = BitConverter.ToString(digest).Replace("-", "").ToLower(); - var generatedSignature = $"sha1={hex}"; - - return signature == generatedSignature; - } - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Integrations/GitHub/IGitHubClientProvider.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Integrations/GitHub/IGitHubClientProvider.cs deleted file mode 100644 index 13ac74aca58..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Integrations/GitHub/IGitHubClientProvider.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Octokit; -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace Azure.Sdk.Tools.CheckEnforcer.Integrations.GitHub -{ - public interface IGitHubClientProvider - { - Task GetApplicationClientAsync(CancellationToken cancellationToken); - Task GetInstallationClientAsync(long installationId, CancellationToken cancellationToken); - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Services/PullRequestTracking/IPullRequestTracker.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Services/PullRequestTracking/IPullRequestTracker.cs deleted file mode 100644 index e2d7a074f47..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Services/PullRequestTracking/IPullRequestTracker.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Threading.Tasks; - -namespace Azure.Sdk.Tools.CheckEnforcer.Services.PullRequestTracking -{ - public interface IPullRequestTracker - { - Task StartTrackingPullRequestAsync(PullRequestTrackingTicket pullRequest); - Task StopTrackingPullRequestAsync(PullRequestTrackingTicket pullRequest); - Task> GetTrackedPullRequestsAsync(); - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Services/PullRequestTracking/PullRequestTracker.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Services/PullRequestTracking/PullRequestTracker.cs deleted file mode 100644 index 9b34b690f90..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Services/PullRequestTracking/PullRequestTracker.cs +++ /dev/null @@ -1,70 +0,0 @@ -using Azure.Data.Tables; -using Azure.Security.KeyVault.Secrets; -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.Text; -using System.Threading.Tasks; - -namespace Azure.Sdk.Tools.CheckEnforcer.Services.PullRequestTracking -{ - public class PullRequestTracker : IPullRequestTracker - { - private SecretClient secretClient; - - public PullRequestTracker(SecretClient secretClient) - { - this.secretClient = secretClient; - } - - private const string ConnectionStringSecretName = "table-storage-connection-string"; - private const string PullRequestStateTableName = "pullrequests"; - private object tableClientLock = new object(); - private TableClient tableClient; - - private TableClient GetTableClient() - { - if (tableClient == null) - { - lock (tableClientLock) - { - if (tableClient == null) - { - KeyVaultSecret secret = secretClient.GetSecret(ConnectionStringSecretName); - var connectionString = secret.Value; - - tableClient = new TableClient(connectionString, PullRequestStateTableName); - } - } - } - - return tableClient; - } - - public async Task StartTrackingPullRequestAsync(PullRequestTrackingTicket pullRequestTrackingTicket) - { - var tableClient = GetTableClient(); - await tableClient.UpsertEntityAsync(pullRequestTrackingTicket, TableUpdateMode.Replace); - } - - public async Task StopTrackingPullRequestAsync(PullRequestTrackingTicket pullRequestTrackingTicket) - { - var tableClient = GetTableClient(); - await tableClient.DeleteEntityAsync(pullRequestTrackingTicket.PartitionKey, pullRequestTrackingTicket.RowKey); - } - - public async Task> GetTrackedPullRequestsAsync() - { - var tableClient = GetTableClient(); - var pagable = tableClient.QueryAsync(); - var tickets = new List(); - - await foreach (var ticket in pagable) - { - tickets.Add(ticket); - } - - return tickets; - } - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Services/PullRequestTracking/PullRequestTrackingTicket.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Services/PullRequestTracking/PullRequestTrackingTicket.cs deleted file mode 100644 index 7b6dc86ac33..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Services/PullRequestTracking/PullRequestTrackingTicket.cs +++ /dev/null @@ -1,31 +0,0 @@ -using Azure.Data.Tables; -using System; -using System.Collections.Generic; -using System.Text; - -namespace Azure.Sdk.Tools.CheckEnforcer.Services.PullRequestTracking -{ - public class PullRequestTrackingTicket : ITableEntity - { - public PullRequestTrackingTicket() - { - } - - public PullRequestTrackingTicket(long installationId, long repositoryId, int pullRequestNumber) - { - this.InstallationId = installationId; - this.RepositoryId = repositoryId; - this.PullRequestNumber = pullRequestNumber; - this.PartitionKey = $"{installationId}.{repositoryId}"; - this.RowKey = $"{this.PartitionKey}.{pullRequestNumber}"; - } - - public long InstallationId { get; set; } - public long RepositoryId { get; set; } - public int PullRequestNumber { get; set; } - public string PartitionKey { get; set; } - public string RowKey { get; set; } - public DateTimeOffset? Timestamp { get; set; } - public ETag ETag { get; set; } - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Startup.cs b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Startup.cs deleted file mode 100644 index e3b66356ae5..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/Startup.cs +++ /dev/null @@ -1,61 +0,0 @@ -using Azure.Data.AppConfiguration; -using Azure.Data.Tables; -using Azure.Identity; -using Azure.Sdk.Tools.CheckEnforcer; -using Azure.Sdk.Tools.CheckEnforcer.Configuration; -using Azure.Sdk.Tools.CheckEnforcer.Integrations.GitHub; -using Azure.Sdk.Tools.CheckEnforcer.Services.PullRequestTracking; -using Azure.Security.KeyVault.Keys; -using Azure.Security.KeyVault.Keys.Cryptography; -using Microsoft.Azure.Functions.Extensions.DependencyInjection; -using Microsoft.Extensions.Azure; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.Text; - -[assembly: FunctionsStartup(typeof(Startup))] - -namespace Azure.Sdk.Tools.CheckEnforcer -{ - public class Startup : FunctionsStartup - { - private string GetWebsiteResourceGroupEnvironmentVariable() - { - var websiteResourceGroupEnvironmentVariable = Environment.GetEnvironmentVariable("WEBSITE_RESOURCE_GROUP"); - return websiteResourceGroupEnvironmentVariable; - } - - public override void Configure(IFunctionsHostBuilder builder) - { - var websiteResourceGroupEnvironmentVariable = GetWebsiteResourceGroupEnvironmentVariable(); - - var credential = new DefaultAzureCredential(); - - builder.Services.AddAzureClients((builder) => - { - builder.UseCredential(credential); - - var keyVaultUri = new Uri($"https://{websiteResourceGroupEnvironmentVariable}.vault.azure.net"); - builder.AddSecretClient(keyVaultUri); - - var configurationUri = new Uri($"https://{websiteResourceGroupEnvironmentVariable}.azconfig.io/"); - builder.AddConfigurationClient(configurationUri); - - // To inject the cryptography client with the extension helpers - // here we need to first find the Key ID. - var keyClient = new KeyClient(keyVaultUri, credential); - KeyVaultKey key = keyClient.GetKey("github-app-private-key"); - builder.AddCryptographyClient(key.Id); - }); - - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - builder.Services.AddSingleton(); - builder.Services.AddMemoryCache(); - } - } -} diff --git a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/host.json b/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/host.json deleted file mode 100644 index b9f92c0dee3..00000000000 --- a/tools/check-enforcer/Azure.Sdk.Tools.CheckEnforcer/host.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "version": "2.0" -} \ No newline at end of file diff --git a/tools/check-enforcer/CheckEnforcer.sln b/tools/check-enforcer/CheckEnforcer.sln deleted file mode 100644 index 2bb5d82a55b..00000000000 --- a/tools/check-enforcer/CheckEnforcer.sln +++ /dev/null @@ -1,31 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29215.179 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Sdk.Tools.CheckEnforcer", "Azure.Sdk.Tools.CheckEnforcer\Azure.Sdk.Tools.CheckEnforcer.csproj", "{97850C3F-0A6C-446B-B00B-B1E571426661}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Azure.Sdk.Tools.CheckEnforcer.Tests", "Azure.Sdk.Tools.CheckEnforcer.Tests\Azure.Sdk.Tools.CheckEnforcer.Tests.csproj", "{4C33C118-A3C1-4742-B407-57D032497769}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {97850C3F-0A6C-446B-B00B-B1E571426661}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {97850C3F-0A6C-446B-B00B-B1E571426661}.Debug|Any CPU.Build.0 = Debug|Any CPU - {97850C3F-0A6C-446B-B00B-B1E571426661}.Release|Any CPU.ActiveCfg = Release|Any CPU - {97850C3F-0A6C-446B-B00B-B1E571426661}.Release|Any CPU.Build.0 = Release|Any CPU - {4C33C118-A3C1-4742-B407-57D032497769}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4C33C118-A3C1-4742-B407-57D032497769}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4C33C118-A3C1-4742-B407-57D032497769}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4C33C118-A3C1-4742-B407-57D032497769}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {14C0E54C-F47D-4D15-AAFC-0279F42D6537} - EndGlobalSection -EndGlobal diff --git a/tools/check-enforcer/README.md b/tools/check-enforcer/README.md deleted file mode 100644 index 6393b787ca5..00000000000 --- a/tools/check-enforcer/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# Check Enforcer - - -This folder contains the source code for Check Enforcer. Check Enforcer is a service built by the Azure SDK Engineering Systems team which makes it easier to work with GitHub checks in a mono-repository. When you install and configure Check Enforcer in your repository it will add a check each time you create a pull request which will not turn green until all other checks have passed. - -## Why did we create Check Enforcer? - -The Azure SDK team maintains reusable libraries that developers use to access Azure services. These libraries are grouped by together into a repository for each language/runtime. For example there is a repository for [Java](https://github.com/azure/azure-sdk-for-java), [.NET](https://github.com/azure/azure-sdk-for-net), [Python](https://github.com/azure/azure-sdk-for-python) and [JavaScript](https://github.com/azure/azure-sdk-for-javascript) - just to name a few. - -Each repository contains a large number of seperate libraries. Even though together these libraries constitute a single SDK, they ship seperately on their own individual cadence as the underlying service evolves. As a result we have seperate build and release pipelines for say the KeyVault and the Event Hubs libraries in each repository. - -Whilst Checks in GitHub are awesome, one of the limitations when setting up required checks is that you cannot make them required for just one specific path. We don't want to build all libraries for every checkin (that would take a long time and needlessly block teams if other libraries were having build reliability issues) - so we needed a way to work around it. - -Check Enforcer is our solution. We use the built-in triggering w/ path filter options within Azure Pipelines (Check Enforcer is CI tool agnostic however) to control when a pipeline triggers, and we just use Check Enforcer to block until all triggered pipelines pass successfully. Each of those libraries can be optional - and you just make Check Enforcer the only required check in the repo. - -## Usage - -You can get started with Check Enforcer by first [installing the application](https://github.com/apps/check-enforcer) into your own repository. Once Check Enforcer is installed you need to commit a file named ```CHECKENFORCER``` into the root of the repository. The contents of the file are as follows: - -```yaml -format: v0.1-alpha -minimumCheckRuns: 2 -``` - -The presence of this file indicates to the Check Enforcer backend that you want to use Check Enforcer in this repository. The ```minimumCheckRuns``` field is used to specify the minimum number of check runs that Check Enforcer should see pass (other than itself) before it will itself turn green. Note that this is just the minimum - if Check Enforcer sees 10 check runs it will wait for them all to turn green - this option is just to guard against check runs that are slow to start and allows Check Enforcer to sit tight until they spin up. - -Check Enforcer is entirely stateless - every time your checks are updated it reevaluates its status and updates the check accordingly. If you restart a build (e.g. by using the ```/azp run buildname``` command), then Check Enforcer will flick back to being in progress. - -You can disable Check Enforcer anytime by uninstalling it from the repository or removing the ```CHECKENFORCER``` file. If you don't want to remove the file, you can use the ```enabled: true``` flag in the ```CHECKENFORCER``` file to temporarily stop Check Enforcer adding the check run to your PRs. Here is an example: - -```yaml -format: v0.1-alpha -enabled: false -minimumCheckRuns: 2 -``` - -## PR Comment Commands - -Check Enforcer supports a limited number of commands which can by issued via PR comments. For example if Check Enforcer appears to be stuck you can add a comment as follows to reset the state of Check Enforcer on the PR: - -``` -/check-enforcer reset -``` - -Check Enforcer will revert to an "in-progress" state. You can then trigger Check Enforcer to re-evaluate the pull request using the following PR comment: - -``` -/check-enforcer evaluate -``` - -From time to time Check Enforcer may be blocking a merge because no-check runs are appropriate for the PR, in these cases you can use the following command Check Enforcer rules and park the commit as successful (note, in future warnings and notifications will be sent when this occurs): - -``` -/check-enforcer override -``` - -These are the only commands that Check Enforcer supports at this time. - -## Need Help? - -Check Enforcer is built primarily for use by the Azure SDK Engineering Systems teams for use within their mono-repositories. But we are happy for others to pick it up and start using it. If you have any issues feel free to log an issue on this GitHub repository and we'll do our best to help you out. - -## Contributing - -Got an idea for Check Enforcer? Great - a good way to start contributing is by creating an issue and discussing with us what you want to do. We are always happy to review unsolicited pull requests and if they match our goals for Check Enforcer we'll work with you to get it merged - but its probably better if you give us a heads up on what you want to achieve first. diff --git a/tools/check-enforcer/ci.yml b/tools/check-enforcer/ci.yml deleted file mode 100644 index ed61377c3ef..00000000000 --- a/tools/check-enforcer/ci.yml +++ /dev/null @@ -1,31 +0,0 @@ -# NOTE: Please refer to https://aka.ms/azsdk/engsys/ci-yaml before editing this file. -trigger: - branches: - include: - - main - - feature/* - - release/* - - hotfix/* - paths: - include: - - tools/check-enforcer - -pr: - branches: - include: - - main - - feature/* - - release/* - - hotfix/* - paths: - include: - - tools/check-enforcer - -extends: - template: ../../eng/pipelines/templates/stages/archetype-sdk-tool-azure-function.yml - parameters: - ToolName: check-enforcer - FunctionProject: Azure.Sdk.Tools.CheckEnforcer - TestProject: Azure.Sdk.Tools.CheckEnforcer.Tests - ProductionEnvironmentName: checkenforcerprod - StagingEnvironmentName: checkenforcerstaging