protected override async Task HandleCoreAsync(HandlerContext <CheckRunEventPayload> context, CancellationToken cancellationToken) { var payload = context.Payload; var installationId = payload.Installation.Id; var repositoryId = payload.Repository.Id; var sha = payload.CheckRun.CheckSuite.HeadSha; var distributedLockIdentifier = $"{installationId}/{repositoryId}/{sha}"; using (var scope = Logger.BeginScope("Processing check-run event on: {distributedLockIdentifier}", distributedLockIdentifier)) { if (payload.CheckRun.Name == this.GlobalConfigurationProvider.GetApplicationName()) { Logger.LogTrace( SkippedProcessingCheckEnforcerCheckRunEventEventId, "Skipping processing event for: {distributedLockIdentifier}", distributedLockIdentifier ); } else { try { if (payload.CheckRun.Status != new StringEnum <CheckStatus>(CheckStatus.Completed)) { Logger.LogTrace( SkippedProcessingIncompleteCheckRunEventEventId, "Skipping processing event for: {distributedLockIdentifier}", distributedLockIdentifier ); return; } var trapSemaphore = GetSemaphore($"trap/{distributedLockIdentifier}"); var queueSemaphore = GetSemaphore($"queue/{distributedLockIdentifier}"); if (trapSemaphore.CurrentCount == 0) { Logger.LogTrace( SkippedProcessingTrapSemaphoreEventId, "Skipped processing check-run event: {distributedLockIdentifier} because semaphore current count was zero." ); return; } Logger.LogTrace( WaitingOnTrapSemaphoreEventId, "Waiting on trap semaphore for: {distributedLockIdentifier}", distributedLockIdentifier ); var trapWaitSuccessful = await trapSemaphore.WaitAsync(10000, cancellationToken); if (!trapWaitSuccessful) { Logger.LogWarning( WaitOnTrapSemaphoreTimeoutEventId, "Timed out waiting in trap semaphore for: {distributedLockIdentifier}.", distributedLockIdentifier ); return; } Logger.LogTrace( WaitingOnQueueSemaphoreEventId, "Waiting on queue semaphore for: {distributedLockIdentifier}", distributedLockIdentifier ); var queueWaitSuccessful = await queueSemaphore.WaitAsync(10000, cancellationToken); if (!queueWaitSuccessful) { Logger.LogWarning( WaitOnQueueSemaphoreTimeoutEventId, "Timed out waiting in queue semaphore for: {distributedLockIdentifier}.", distributedLockIdentifier ); return; } var configuration = await this.RepositoryConfigurationProvider.GetRepositoryConfigurationAsync(installationId, repositoryId, sha, cancellationToken); if (configuration.IsEnabled) { Logger.LogInformation( CheckEnforcerEnabledEventId, "Check Enforcer was enabled for: {distributedLockIdentifier}.", distributedLockIdentifier ); } else { Logger.LogInformation( CheckEnforcerDisabledEventId, "Check Enforcer was disabled for: {distributedLockIdentifier}.", distributedLockIdentifier ); return; } Logger.LogTrace( AcquiringDistributedLockEventId, "Acquiring distributed lock for: {distributedLockIdentifier}.", distributedLockIdentifier ); var distributedLock = this.DistributedLockProvider.Create(distributedLockIdentifier); var distributedLockAquired = await distributedLock.AcquireAsync(); if (!distributedLockAquired) { Logger.LogWarning( FailedToAcquiredDistributedLockEventId, "Failed to acquire distributed lock for: {distributedLockIdentifier}.", distributedLockIdentifier ); // Quickly clean up and get out of here. trapSemaphore.Release(); queueSemaphore.Release(); return; } Logger.LogTrace( ReleasingSemaphoreEventId, "Releasing trap semaphore for: {distributedLockIdentifier}.", distributedLockIdentifier ); trapSemaphore.Release(); Logger.LogTrace( ReleasedSemaphoreEventId, "Released trap semaphore for: {distributedLockIdentifier}.", distributedLockIdentifier ); Logger.LogInformation( EvaluatingCheckRunEventId, "Evaluating check-run for: {distributedLockIdentifier}.", distributedLockIdentifier ); await EvaluatePullRequestAsync(context.Client, installationId, repositoryId, sha, cancellationToken); Logger.LogInformation( EvaluatedCheckRunEventId, "Evaluated check-run for: {distributedLockIdentifier}.", distributedLockIdentifier ); Logger.LogTrace( ReleasingDistributedLockEventId, "Releasing distributed lock for: {distributedLockIdentifier}.", distributedLockIdentifier ); await distributedLock.ReleaseAsync(); Logger.LogTrace( ReleasedDistributedLockEventId, "Released distributed lock for: {distributedLockIdentifier}.", distributedLockIdentifier ); Logger.LogTrace( ReleasingSemaphoreEventId, "Releasing queue semaphore for: {distributedLockIdentifier}.", distributedLockIdentifier ); queueSemaphore.Release(); Logger.LogTrace( ReleasingSemaphoreEventId, "Released queue semaphore for: {distributedLockIdentifier}.", distributedLockIdentifier ); } catch (Exception ex) { Logger.LogError( CheckRunEventProcessingFailedEventId, ex, "Failed to process check-run event for: {distributedLockIdentifier}", distributedLockIdentifier ); // Clear the semaphores. semaphores.TryRemove($"trap/{distributedLockIdentifier}", out SemaphoreSlim _); semaphores.TryRemove($"queue/{distributedLockIdentifier}", out SemaphoreSlim _); throw ex; } } } }
protected abstract Task HandleCoreAsync(HandlerContext <T> context, CancellationToken cancellationToken);
protected override async Task HandleCoreAsync(HandlerContext <CheckRunEventPayload> 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>(CheckConclusion.Neutral)) { Logger.LogInformation("" + "Skipping processing event for: {runIdentifier} check-run conclusion is neutral.", runIdentifier ); return; } else if (payload.CheckRun.Status != new StringEnum <CheckStatus>(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; } } } }
protected override async Task HandleCoreAsync(HandlerContext <CheckRunEventPayload> 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 == 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 { try { if (payload.CheckRun.Status != new StringEnum <CheckStatus>(CheckStatus.Completed)) { Logger.LogInformation( "Skipping processing event for: {runIdentifier} check-run status not completed.", runIdentifier ); return; } 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; } } } }
protected override async Task HandleCoreAsync(HandlerContext <CheckRunEventPayload> 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()) { Logger.LogInformation( SkippedProcessingCheckEnforcerCheckRunEventEventId, "Skipping processing event for: {runIdentifier} because appplication name match.", runIdentifier ); } else { try { if (payload.CheckRun.Status != new StringEnum <CheckStatus>(CheckStatus.Completed)) { Logger.LogInformation( SkippedProcessingIncompleteCheckRunEventEventId, "Skipping processing event for: {runIdentifier} check-run status not completed.", runIdentifier ); return; } var configuration = await this.RepositoryConfigurationProvider.GetRepositoryConfigurationAsync(installationId, repositoryId, sha, cancellationToken); if (configuration.IsEnabled) { Logger.LogInformation( CheckEnforcerEnabledEventId, "Check Enforcer was enabled for: {runIdentifier}.", runIdentifier ); } else { Logger.LogInformation( CheckEnforcerDisabledEventId, "Check Enforcer was disabled for: {runIdentifier}.", runIdentifier ); return; } Logger.LogInformation( EvaluatingCheckRunEventId, "Evaluating check-run for: {runIdentifier}.", runIdentifier ); await EvaluatePullRequestAsync(context.Client, installationId, repositoryId, sha, cancellationToken); Logger.LogInformation( EvaluatedCheckRunEventId, "Evaluated check-run for: {runIdentifier}.", runIdentifier ); } catch (Exception ex) { Logger.LogError( CheckRunEventProcessingFailedEventId, ex, "Failed to process check-run event for: {runIdentifier}", runIdentifier ); throw ex; } } } }
protected override async Task HandleCoreAsync(HandlerContext <IssueCommentPayload> context, CancellationToken cancellationToken) { var payload = context.Payload; var installationId = payload.Installation.Id; var repositoryId = payload.Repository.Id; var comment = payload.Comment.Body.ToLower(); 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; } var pullRequest = await context.Client.PullRequest.Get(repositoryId, issueId); var sha = pullRequest.Head.Sha; var distributedLockIdentifier = $"{installationId}/{repositoryId}/{sha}"; using (var distributedLock = DistributedLockProvider.Create(distributedLockIdentifier)) { var distributedLockAcquired = await distributedLock.AcquireAsync(); if (!distributedLockAcquired) { return; } switch (comment) { case "/check-enforcer queued": await SetQueuedAsync(context.Client, repositoryId, sha, cancellationToken); break; case "/check-enforcer inprogress": await SetInProgressAsync(context.Client, repositoryId, sha, cancellationToken); break; case "/check-enforcer success": await SetSuccessAsync(context.Client, repositoryId, sha, cancellationToken); break; case "/check-enforcer reset": await CreateCheckAsync(context.Client, repositoryId, sha, true, cancellationToken); break; case "/check-enforcer evaluate": await EvaluatePullRequestAsync(context.Client, installationId, repositoryId, sha, cancellationToken); break; default: this.Logger.LogTrace("Unrecognized command: {comment}", comment); break; } await distributedLock.ReleaseAsync(); } }