From 54cffaf07c5cee318eaaea9011171891fb21feef Mon Sep 17 00:00:00 2001 From: Kiss Miklos Date: Mon, 6 Feb 2023 17:53:57 +0100 Subject: [PATCH] add pattern match Signed-off-by: Kiss Miklos --- .../codeowners/CodeOwnersProcessor.test.ts | 27 ++++++++++++++++--- .../modules/codeowners/CodeOwnersProcessor.ts | 4 ++- .../src/modules/codeowners/lib/read.ts | 2 +- .../modules/codeowners/lib/resolve.test.ts | 10 ++++++- .../src/modules/codeowners/lib/resolve.ts | 18 +++++++++---- 5 files changed, 50 insertions(+), 11 deletions(-) diff --git a/plugins/catalog-backend/src/modules/codeowners/CodeOwnersProcessor.test.ts b/plugins/catalog-backend/src/modules/codeowners/CodeOwnersProcessor.test.ts index 9cb4d414c0..a33ce84e0d 100644 --- a/plugins/catalog-backend/src/modules/codeowners/CodeOwnersProcessor.test.ts +++ b/plugins/catalog-backend/src/modules/codeowners/CodeOwnersProcessor.test.ts @@ -21,7 +21,7 @@ import { LocationSpec } from '@backstage/plugin-catalog-node'; const mockCodeOwnersText = () => ` * @acme/team-foo @acme/team-bar -/docs @acme/team-bar +/docs @acme/team-docs `; describe('CodeOwnersProcessor', () => { @@ -34,8 +34,12 @@ describe('CodeOwnersProcessor', () => { }); describe('preProcessEntity', () => { - const setupTest = ({ kind = 'Component', spec = {} } = {}) => { - const entity = { kind, spec }; + const setupTest = ({ + kind = 'Component', + spec = {}, + metadata = {}, + } = {}) => { + const entity = { kind, spec, metadata }; const config = new ConfigReader({}); const reader = { read: jest.fn(), @@ -101,5 +105,22 @@ describe('CodeOwnersProcessor', () => { spec: { owner: 'team-foo' }, }); }); + it('should use the "backstage.io/managed-by-location" annotation as pattern', async () => { + const { entity, processor } = setupTest({ + metadata: { + annotations: { 'backstage.io/managed-by-location': '/docs' }, + }, + }); + + const result = await processor.preProcessEntity( + entity as any, + mockLocation(), + ); + + expect(result).toEqual({ + ...entity, + spec: { owner: 'team-docs' }, + }); + }); }); }); diff --git a/plugins/catalog-backend/src/modules/codeowners/CodeOwnersProcessor.ts b/plugins/catalog-backend/src/modules/codeowners/CodeOwnersProcessor.ts index f50ef6b922..9a9c58934c 100644 --- a/plugins/catalog-backend/src/modules/codeowners/CodeOwnersProcessor.ts +++ b/plugins/catalog-backend/src/modules/codeowners/CodeOwnersProcessor.ts @@ -79,8 +79,10 @@ export class CodeOwnersProcessor implements CatalogProcessor { if (!scmIntegration) { return entity; } + const pattern = - entity.metadata?.annotations['backstage.io/managed-by-location']; + entity.metadata?.annotations?.['backstage.io/managed-by-location']; + const owner = await findCodeOwnerByTarget( this.reader, location.target, diff --git a/plugins/catalog-backend/src/modules/codeowners/lib/read.ts b/plugins/catalog-backend/src/modules/codeowners/lib/read.ts index eae4e7ce91..cd15a17997 100644 --- a/plugins/catalog-backend/src/modules/codeowners/lib/read.ts +++ b/plugins/catalog-backend/src/modules/codeowners/lib/read.ts @@ -52,7 +52,7 @@ export async function findCodeOwnerByTarget( reader: UrlReader, targetUrl: string, scmIntegration: ScmIntegration, - pattern: string, + pattern?: string, ): Promise { const codeownersPaths = scmCodeOwnersPaths[scmIntegration?.type ?? '']; diff --git a/plugins/catalog-backend/src/modules/codeowners/lib/resolve.test.ts b/plugins/catalog-backend/src/modules/codeowners/lib/resolve.test.ts index 4e6c646073..ec9321284f 100644 --- a/plugins/catalog-backend/src/modules/codeowners/lib/resolve.test.ts +++ b/plugins/catalog-backend/src/modules/codeowners/lib/resolve.test.ts @@ -18,13 +18,21 @@ import { normalizeCodeOwner, resolveCodeOwner } from './resolve'; const mockCodeOwnersText = () => ` * @acme/team-foo @acme/team-bar -/docs @acme/team-bar +/docs @acme/team-docs `; describe('resolveCodeOwner', () => { it('should parse the codeowners file', () => { expect(resolveCodeOwner(mockCodeOwnersText())).toBe('team-foo'); }); + it('should include the codeowners path into the provided pattern', () => { + expect( + resolveCodeOwner( + mockCodeOwnersText(), + 'url:https://github.com/acem/repo/main/docs/catalog-info.yaml', + ), + ).toBe('team-docs'); + }); }); describe('normalizeCodeOwner', () => { diff --git a/plugins/catalog-backend/src/modules/codeowners/lib/resolve.ts b/plugins/catalog-backend/src/modules/codeowners/lib/resolve.ts index f9bc19b1d1..69865fde21 100644 --- a/plugins/catalog-backend/src/modules/codeowners/lib/resolve.ts +++ b/plugins/catalog-backend/src/modules/codeowners/lib/resolve.ts @@ -16,7 +16,7 @@ import * as codeowners from 'codeowners-utils'; import { CodeOwnersEntry } from 'codeowners-utils'; -import { filter, get, head, pipe, reverse } from 'lodash/fp'; +import { get, head, pipe, reverse } from 'lodash/fp'; const USER_PATTERN = /^@.*/; const GROUP_PATTERN = /^@.*\/.*/; @@ -24,18 +24,26 @@ const EMAIL_PATTERN = /^.*@.*\..*$/; export function resolveCodeOwner( contents: string, - pattern = '*', + pattern?: string, ): string | undefined { const owners = codeowners.parse(contents); - return pipe( - filter((e: CodeOwnersEntry) => pattern.includes(e.pattern)), + let relevantOwners = owners.filter((e: CodeOwnersEntry) => + pattern?.includes(e.pattern), + ); + if (relevantOwners.length === 0) { + relevantOwners = owners.filter((e: CodeOwnersEntry) => e.pattern === '*'); + } + + const result = pipe( reverse, head, get('owners'), head, normalizeCodeOwner, - )(owners); + )(relevantOwners); + + return result; } export function normalizeCodeOwner(owner: string) {