Merge pull request #16210 from RoadieHQ/codeowners-processor-monorepo-support

feat: CodeOwnersProcessor monorepo support
This commit is contained in:
Fredrik Adelöw
2023-04-05 15:05:14 +02:00
committed by GitHub
5 changed files with 86 additions and 20 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend': minor
---
Add monorepo support to CodeOwnersProccesor.
@@ -17,11 +17,13 @@
import { getVoidLogger } from '@backstage/backend-common';
import { ConfigReader } from '@backstage/config';
import { CodeOwnersProcessor } from './CodeOwnersProcessor';
import { LocationSpec } from '@backstage/plugin-catalog-node';
import { LocationSpec } from '@backstage/plugin-catalog-common';
const mockCodeOwnersText = () => `
* @acme/team-foo @acme/team-bar
/docs @acme/team-bar
* @acme/team-foo @acme/team-bar
/docs @acme/team-docs
/plugins/catalog-* @backstage/maintainers @backstage/catalog-core
**/logs @logs-owner
`;
describe('CodeOwnersProcessor', () => {
@@ -34,8 +36,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 +107,44 @@ describe('CodeOwnersProcessor', () => {
spec: { owner: 'team-foo' },
});
});
it('should match owner based on the targetUrl', async () => {
const { entity, processor } = setupTest();
const result = await processor.preProcessEntity(
entity as any,
mockLocation({ basePath: 'docs/' }),
);
expect(result).toEqual({
...entity,
spec: { owner: 'team-docs' },
});
});
it('should match wildcard pattern', async () => {
const { entity, processor } = setupTest();
const result = await processor.preProcessEntity(
entity as any,
mockLocation({ basePath: 'plugins/catalog-foo' }),
);
expect(result).toEqual({
...entity,
spec: { owner: 'maintainers' },
});
});
it('should match glob pattern', async () => {
const { entity, processor } = setupTest();
const result = await processor.preProcessEntity(
entity as any,
mockLocation({ basePath: 'plugins/catalog-foo/logs/1.txt' }),
);
expect(result).toEqual({
...entity,
spec: { owner: 'User:logs-owner' },
});
});
});
});
@@ -70,7 +70,7 @@ export async function findCodeOwnerByTarget(
return undefined;
}
const owner = resolveCodeOwner(contents);
const owner = resolveCodeOwner(contents, targetUrl);
return owner;
}
@@ -18,12 +18,33 @@ 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');
expect(
resolveCodeOwner(
mockCodeOwnersText(),
'https://github.com/can/be/tree/anything/catalog-info.yaml',
),
).toBe('team-foo');
});
it('should include the codeowners path into the provided pattern', () => {
expect(
resolveCodeOwner(
mockCodeOwnersText(),
'https://github.com/acme/repo/tree/main/docs/catalog-info.yaml',
),
).toBe('team-docs');
});
it('should match only in resource path', () => {
expect(
resolveCodeOwner(
mockCodeOwnersText(),
'https://github.com/acme/repo/tree/docs/catalog-info.yaml',
),
).toBe('team-foo');
});
});
@@ -15,8 +15,7 @@
*/
import * as codeowners from 'codeowners-utils';
import { CodeOwnersEntry } from 'codeowners-utils';
import { filter, get, head, pipe, reverse } from 'lodash/fp';
import parseGitUrl from 'git-url-parse';
const USER_PATTERN = /^@.*/;
const GROUP_PATTERN = /^@.*\/.*/;
@@ -24,18 +23,14 @@ const EMAIL_PATTERN = /^.*@.*\..*$/;
export function resolveCodeOwner(
contents: string,
pattern = '*',
catalogInfoFileUrl: string,
): string | undefined {
const owners = codeowners.parse(contents);
const codeOwnerEntries = codeowners.parse(contents);
return pipe(
filter((e: CodeOwnersEntry) => e.pattern === pattern),
reverse,
head,
get('owners'),
head,
normalizeCodeOwner,
)(owners);
const { filepath } = parseGitUrl(catalogInfoFileUrl);
const match = codeowners.matchFile(filepath, codeOwnerEntries);
return match ? normalizeCodeOwner(match.owners[0]) : undefined;
}
export function normalizeCodeOwner(owner: string) {