From 92963168f0f6c6642dbb1da940f76e865452fae7 Mon Sep 17 00:00:00 2001 From: ludeeus Date: Thu, 11 Jan 2024 08:44:05 +0000 Subject: [PATCH] Add handler for blocking labels --- .../github-webhook/github-webhook.module.ts | 2 + .../handlers/blocking_labels.ts | 33 +++++++++++ .../handlers/blocking_labels.spec.ts | 59 +++++++++++++++++++ .../verify_enabled_handlers.spec.ts | 44 +++++++++++++- 4 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 services/bots/src/github-webhook/handlers/blocking_labels.ts create mode 100644 tests/services/bots/github-webhook/handlers/blocking_labels.spec.ts diff --git a/services/bots/src/github-webhook/github-webhook.module.ts b/services/bots/src/github-webhook/github-webhook.module.ts index 25eff4b..519668d 100644 --- a/services/bots/src/github-webhook/github-webhook.module.ts +++ b/services/bots/src/github-webhook/github-webhook.module.ts @@ -5,6 +5,7 @@ import { GithubWebhooksModule } from '@dev-thought/nestjs-github-webhooks'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { AppConfig } from '../config'; import { GithubWebhookController } from './github-webhook.controller'; +import { BlockingLabels } from './handlers/blocking_labels'; import { BranchLabels } from './handlers/branch_labels'; import { CodeOwnersMention } from './handlers/code_owners_mention'; import { DependencyBump } from './handlers/dependency_bump'; @@ -29,6 +30,7 @@ import { ValidateCla } from './handlers/validate-cla'; @Module({ providers: [ + BlockingLabels, BranchLabels, CodeOwnersMention, DependencyBump, diff --git a/services/bots/src/github-webhook/handlers/blocking_labels.ts b/services/bots/src/github-webhook/handlers/blocking_labels.ts new file mode 100644 index 0000000..a96174f --- /dev/null +++ b/services/bots/src/github-webhook/handlers/blocking_labels.ts @@ -0,0 +1,33 @@ +import { PullRequestLabeledEvent, PullRequestUnlabeledEvent } from '@octokit/webhooks-types'; +import { EventType, HomeAssistantRepository, Repository } from '../github-webhook.const'; +import { WebhookContext } from '../github-webhook.model'; +import { BaseWebhookHandler } from './base'; + +export const LabelsToCheck: { + [key in Repository]?: Record; +} = { + [HomeAssistantRepository.CORE]: { + 'awaiting-frontend': { message: 'This PR is awaiting changes to the frontend' }, + }, +}; + +export class BlockingLabels extends BaseWebhookHandler { + public allowedEventTypes = [EventType.PULL_REQUEST_LABELED, EventType.PULL_REQUEST_UNLABELED]; + public allowedRepositories = Object.keys(LabelsToCheck) as Repository[]; + + async handle(context: WebhookContext) { + const currentLabels = new Set(context.payload.pull_request.labels.map((label) => label.name)); + + for (const [label, description] of Object.entries(LabelsToCheck[context.repository] || {})) { + const hasBlockingLabel = currentLabels.has(label); + await context.github.repos.createCommitStatus( + context.repo({ + sha: context.payload.pull_request.head.sha, + context: `blocking-label-${label}`, + state: hasBlockingLabel ? 'failure' : 'success', + description: hasBlockingLabel ? description['message'] : description['success'] || 'OK', + }), + ); + } + } +} diff --git a/tests/services/bots/github-webhook/handlers/blocking_labels.spec.ts b/tests/services/bots/github-webhook/handlers/blocking_labels.spec.ts new file mode 100644 index 0000000..1431d08 --- /dev/null +++ b/tests/services/bots/github-webhook/handlers/blocking_labels.spec.ts @@ -0,0 +1,59 @@ +import { WebhookContext } from '../../../../../services/bots/src/github-webhook/github-webhook.model'; +import { + BlockingLabels, + LabelsToCheck, +} from '../../../../../services/bots/src/github-webhook/handlers/blocking_labels'; +import { loadJsonFixture } from '../../../../utils/fixture'; +import { mockWebhookContext } from '../../../../utils/test_context'; +import { + ESPHomeRepository, + EventType, + HomeAssistantRepository, + Repository, +} from '../../../../../services/bots/src/github-webhook/github-webhook.const'; + +describe('BlockingLabels', () => { + let handler: BlockingLabels; + let mockContext: WebhookContext; + let createCommitStatusCall: any; + + beforeEach(function () { + handler = new BlockingLabels(); + createCommitStatusCall = {}; + mockContext = mockWebhookContext({ + payload: loadJsonFixture('pull_request.opened'), + eventType: EventType.PULL_REQUEST_LABELED, + // @ts-ignore partial mock + github: { + repos: { + // @ts-ignore partial mock + createCommitStatus: jest.fn(), + }, + }, + }); + }); + + for (const [repository, lables] of Object.entries(LabelsToCheck)) { + for (const label of Object.keys(lables)) { + for (const result of ['success', 'failure']) { + const description = + LabelsToCheck[repository][label][result === 'failure' ? 'message' : 'success'] || 'OK'; + it(`Validate handling ${label} for ${repository} with ${result} result (${description})`, async () => { + mockContext.payload = loadJsonFixture('pull_request.opened', { + pull_request: { labels: result === 'failure' ? [{ name: label }] : [] }, + }); + mockContext.repository = repository as Repository; + await handler.handle(mockContext); + + expect(mockContext.github.repos.createCommitStatus).toHaveBeenCalledWith( + expect.objectContaining({ + context: `blocking-label-${label}`, + description, + state: result, + }), + ); + }); + } + } + } +}); diff --git a/tests/services/bots/github-webhook/verify_enabled_handlers.spec.ts b/tests/services/bots/github-webhook/verify_enabled_handlers.spec.ts index 569d682..d39a41b 100644 --- a/tests/services/bots/github-webhook/verify_enabled_handlers.spec.ts +++ b/tests/services/bots/github-webhook/verify_enabled_handlers.spec.ts @@ -110,11 +110,46 @@ describe('GithubWebhookModule', () => { }, { eventType: EventType.PULL_REQUEST_UNLABELED, - handlers: ['DocsMissing', 'PlatinumReview'], + handlers: ['BlockingLabels', 'DocsMissing', 'PlatinumReview'], payload: { repository: { full_name: 'home-assistant/core', owner: { login: 'home-assistant' } }, }, }, + { + eventType: EventType.PULL_REQUEST_LABELED, + handlers: [ + 'BlockingLabels', + 'CodeOwnersMention', + 'DocsMissing', + 'NewIntegrationsHandler', + 'PlatinumReview', + 'QualityScaleLabeler', + 'ValidateCla', + ], + payload: { + repository: { full_name: 'home-assistant/core', owner: { login: 'home-assistant' } }, + }, + }, + { + eventType: EventType.PULL_REQUEST_UNLABELED, + handlers: [], + payload: { + repository: { + full_name: 'home-assistant/home-assistant.io', + owner: { login: 'home-assistant' }, + }, + }, + }, + { + eventType: EventType.PULL_REQUEST_LABELED, + handlers: ['CodeOwnersMention', 'ValidateCla'], + payload: { + repository: { + full_name: 'home-assistant/home-assistant.io', + owner: { login: 'home-assistant' }, + }, + }, + }, { eventType: EventType.PULL_REQUEST_REVIEW_SUBMITTED, handlers: ['ReviewDrafter'], @@ -185,6 +220,13 @@ describe('GithubWebhookModule', () => { repository: { full_name: 'esphome/esphome', owner: { login: 'esphome' } }, }, }, + { + eventType: EventType.PULL_REQUEST_LABELED, + handlers: [], + payload: { + repository: { full_name: 'esphome/esphome', owner: { login: 'esphome' } }, + }, + }, ] as { eventType: EventType; payload: Record;