feat(catalog-backend-module-github): add visibility filter to github plugin
Signed-off-by: TANGUY Antoine <tanguyantoine@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend-module-github': patch
|
||||
---
|
||||
|
||||
Add visibility filter on GithubEntityProvider
|
||||
@@ -163,6 +163,13 @@ catalog:
|
||||
branch: 'main' # string
|
||||
repository: '.*' # Regex
|
||||
validateLocationsExist: true # optional boolean
|
||||
visibilityProviderId:
|
||||
organization: 'backstage' # string
|
||||
catalogPath: '/catalog-info.yaml' # string
|
||||
filters:
|
||||
visibilities:
|
||||
- public
|
||||
- internal
|
||||
enterpriseProviderId:
|
||||
host: ghe.example.net
|
||||
organization: 'backstage' # string
|
||||
|
||||
@@ -493,6 +493,7 @@ describe('github', () => {
|
||||
name: 'main',
|
||||
},
|
||||
catalogInfoFile: null,
|
||||
visibility: 'public',
|
||||
},
|
||||
{
|
||||
name: 'demo',
|
||||
@@ -508,6 +509,7 @@ describe('github', () => {
|
||||
id: 'acb123',
|
||||
text: 'some yaml',
|
||||
},
|
||||
visibility: 'private',
|
||||
},
|
||||
],
|
||||
pageInfo: {
|
||||
@@ -531,6 +533,7 @@ describe('github', () => {
|
||||
name: 'main',
|
||||
},
|
||||
catalogInfoFile: null,
|
||||
visibility: 'public',
|
||||
},
|
||||
{
|
||||
name: 'demo',
|
||||
@@ -546,6 +549,7 @@ describe('github', () => {
|
||||
id: 'acb123',
|
||||
text: 'some yaml',
|
||||
},
|
||||
visibility: 'private',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -99,6 +99,7 @@ export type RepositoryResponse = {
|
||||
id: string;
|
||||
text: string;
|
||||
} | null;
|
||||
visibility: string;
|
||||
};
|
||||
|
||||
type RepositoryTopics = {
|
||||
@@ -437,6 +438,7 @@ export async function getOrganizationRepositories(
|
||||
url
|
||||
isArchived
|
||||
isFork
|
||||
visibility
|
||||
repositoryTopics(first: 100) {
|
||||
nodes {
|
||||
... on RepositoryTopic {
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
parseGithubOrgUrl,
|
||||
satisfiesTopicFilter,
|
||||
satisfiesForkFilter,
|
||||
satisfiesVisibilityFilter,
|
||||
} from './util';
|
||||
|
||||
describe('parseGithubOrgUrl', () => {
|
||||
@@ -109,3 +110,14 @@ describe('satisfiesForkFilter', () => {
|
||||
expect(satisfiesForkFilter(true, false)).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('satisfiesVisibilityFilter', () => {
|
||||
it('satisfies if no visibilities are given', () => {
|
||||
expect(satisfiesVisibilityFilter([], 'public')).toEqual(true);
|
||||
});
|
||||
|
||||
it('handles visibility presence into the visibilities filter', () => {
|
||||
expect(satisfiesVisibilityFilter(['public'], 'public')).toEqual(true);
|
||||
expect(satisfiesVisibilityFilter(['private'], 'public')).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -99,3 +99,13 @@ export function splitTeamSlug(slug: string): [string, string] {
|
||||
}
|
||||
return [parts[0], parts[1]];
|
||||
}
|
||||
|
||||
export function satisfiesVisibilityFilter(
|
||||
visibilities: string[],
|
||||
visibility: string,
|
||||
): Boolean {
|
||||
if (!visibilities.length) {
|
||||
return true;
|
||||
}
|
||||
return visibilities.indexOf(visibility) > -1;
|
||||
}
|
||||
|
||||
@@ -155,6 +155,7 @@ describe('GithubDiscoveryProcessor', () => {
|
||||
name: 'master',
|
||||
},
|
||||
catalogInfoFile: null,
|
||||
visibility: 'public',
|
||||
},
|
||||
{
|
||||
name: 'demo',
|
||||
@@ -166,6 +167,7 @@ describe('GithubDiscoveryProcessor', () => {
|
||||
name: 'main',
|
||||
},
|
||||
catalogInfoFile: null,
|
||||
visibility: 'public',
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -209,6 +211,7 @@ describe('GithubDiscoveryProcessor', () => {
|
||||
name: 'main',
|
||||
},
|
||||
catalogInfoFile: null,
|
||||
visibility: 'public',
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -242,6 +245,7 @@ describe('GithubDiscoveryProcessor', () => {
|
||||
isFork: false,
|
||||
defaultBranchRef: null,
|
||||
catalogInfoFile: null,
|
||||
visibility: 'public',
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -269,6 +273,7 @@ describe('GithubDiscoveryProcessor', () => {
|
||||
name: 'master',
|
||||
},
|
||||
catalogInfoFile: null,
|
||||
visibility: 'public',
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -305,6 +310,7 @@ describe('GithubDiscoveryProcessor', () => {
|
||||
name: 'main',
|
||||
},
|
||||
catalogInfoFile: null,
|
||||
visibility: 'public',
|
||||
},
|
||||
{
|
||||
name: 'techdocs-cli',
|
||||
@@ -316,6 +322,7 @@ describe('GithubDiscoveryProcessor', () => {
|
||||
name: 'main',
|
||||
},
|
||||
catalogInfoFile: null,
|
||||
visibility: 'public',
|
||||
},
|
||||
{
|
||||
name: 'techdocs-container',
|
||||
@@ -327,6 +334,7 @@ describe('GithubDiscoveryProcessor', () => {
|
||||
name: 'main',
|
||||
},
|
||||
catalogInfoFile: null,
|
||||
visibility: 'public',
|
||||
},
|
||||
{
|
||||
name: 'techdocs-durp',
|
||||
@@ -336,6 +344,7 @@ describe('GithubDiscoveryProcessor', () => {
|
||||
isFork: false,
|
||||
defaultBranchRef: null,
|
||||
catalogInfoFile: null,
|
||||
visibility: 'public',
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -380,6 +389,7 @@ describe('GithubDiscoveryProcessor', () => {
|
||||
name: 'main',
|
||||
},
|
||||
catalogInfoFile: null,
|
||||
visibility: 'public',
|
||||
},
|
||||
{
|
||||
name: 'test',
|
||||
@@ -391,6 +401,7 @@ describe('GithubDiscoveryProcessor', () => {
|
||||
name: 'main',
|
||||
},
|
||||
catalogInfoFile: null,
|
||||
visibility: 'public',
|
||||
},
|
||||
{
|
||||
name: 'test-archived',
|
||||
@@ -402,6 +413,7 @@ describe('GithubDiscoveryProcessor', () => {
|
||||
name: 'main',
|
||||
},
|
||||
catalogInfoFile: null,
|
||||
visibility: 'public',
|
||||
},
|
||||
{
|
||||
name: 'testxyz',
|
||||
@@ -413,6 +425,7 @@ describe('GithubDiscoveryProcessor', () => {
|
||||
name: 'main',
|
||||
},
|
||||
catalogInfoFile: null,
|
||||
visibility: 'public',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -181,6 +181,7 @@ describe('GithubEntityProvider', () => {
|
||||
id: 'abc123',
|
||||
text: 'some yaml',
|
||||
},
|
||||
visibility: 'public',
|
||||
},
|
||||
],
|
||||
}),
|
||||
@@ -284,6 +285,7 @@ describe('GithubEntityProvider', () => {
|
||||
id: 'abc123',
|
||||
text: 'some yaml',
|
||||
},
|
||||
visibility: 'public',
|
||||
},
|
||||
],
|
||||
}),
|
||||
@@ -364,6 +366,7 @@ describe('GithubEntityProvider', () => {
|
||||
id: 'abc123',
|
||||
text: 'some yaml',
|
||||
},
|
||||
visibility: 'public',
|
||||
},
|
||||
],
|
||||
}),
|
||||
@@ -453,6 +456,7 @@ describe('GithubEntityProvider', () => {
|
||||
name: 'main',
|
||||
},
|
||||
catalogInfoFile: null,
|
||||
visibility: 'public',
|
||||
},
|
||||
{
|
||||
name: 'another-repo',
|
||||
@@ -470,6 +474,7 @@ describe('GithubEntityProvider', () => {
|
||||
id: 'abc123',
|
||||
text: 'some yaml',
|
||||
},
|
||||
visibility: 'public',
|
||||
},
|
||||
],
|
||||
}),
|
||||
@@ -571,6 +576,7 @@ describe('GithubEntityProvider', () => {
|
||||
id: 'abc123',
|
||||
text: 'some yaml',
|
||||
},
|
||||
visibility: 'public',
|
||||
},
|
||||
{
|
||||
name: 'test-repo-2',
|
||||
@@ -595,6 +601,7 @@ describe('GithubEntityProvider', () => {
|
||||
id: 'abc123',
|
||||
text: 'some yaml',
|
||||
},
|
||||
visibility: 'public',
|
||||
},
|
||||
{
|
||||
name: 'test-repo-3',
|
||||
@@ -616,6 +623,7 @@ describe('GithubEntityProvider', () => {
|
||||
id: 'abc123',
|
||||
text: 'some yaml',
|
||||
},
|
||||
visibility: 'public',
|
||||
},
|
||||
],
|
||||
}),
|
||||
|
||||
@@ -40,7 +40,11 @@ import {
|
||||
GithubEntityProviderConfig,
|
||||
} from './GithubEntityProviderConfig';
|
||||
import { getOrganizationRepositories } from '../lib/github';
|
||||
import { satisfiesTopicFilter, satisfiesForkFilter } from '../lib/util';
|
||||
import {
|
||||
satisfiesTopicFilter,
|
||||
satisfiesForkFilter,
|
||||
satisfiesVisibilityFilter,
|
||||
} from '../lib/util';
|
||||
|
||||
import { EventParams, EventSubscriber } from '@backstage/plugin-events-node';
|
||||
import { PushEvent, Commit } from '@octokit/webhooks-types';
|
||||
@@ -56,6 +60,7 @@ type Repository = {
|
||||
repositoryTopics: string[];
|
||||
defaultBranchRef?: string;
|
||||
isCatalogInfoFilePresent: boolean;
|
||||
visibility: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -221,6 +226,7 @@ export class GithubEntityProvider implements EntityProvider, EventSubscriber {
|
||||
isCatalogInfoFilePresent:
|
||||
r.catalogInfoFile?.__typename === 'Blob' &&
|
||||
r.catalogInfoFile.text !== '',
|
||||
visibility: r.visibility,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -237,6 +243,7 @@ export class GithubEntityProvider implements EntityProvider, EventSubscriber {
|
||||
const repositoryFilter = this.config.filters?.repository;
|
||||
const topicFilters = this.config.filters?.topic;
|
||||
const allowForks = this.config.filters?.allowForks ?? true;
|
||||
const visibilities = this.config.filters?.visibilities ?? [];
|
||||
|
||||
const matchingRepositories = repositories.filter(r => {
|
||||
const repoTopics: string[] = r.repositoryTopics;
|
||||
@@ -245,6 +252,7 @@ export class GithubEntityProvider implements EntityProvider, EventSubscriber {
|
||||
(!repositoryFilter || repositoryFilter.test(r.name)) &&
|
||||
satisfiesTopicFilter(repoTopics, topicFilters) &&
|
||||
satisfiesForkFilter(allowForks, r.isFork) &&
|
||||
satisfiesVisibilityFilter(visibilities, r.visibility) &&
|
||||
r.defaultBranchRef
|
||||
);
|
||||
});
|
||||
@@ -310,6 +318,7 @@ export class GithubEntityProvider implements EntityProvider, EventSubscriber {
|
||||
// we can consider this file present because
|
||||
// only the catalog file will be recovered from the commits
|
||||
isCatalogInfoFilePresent: true,
|
||||
visibility: event.repository.visibility,
|
||||
};
|
||||
|
||||
const matchingTargets = this.matchesFilters([repository]);
|
||||
|
||||
+34
-2
@@ -84,6 +84,12 @@ describe('readProviderConfigs', () => {
|
||||
allowForks: false,
|
||||
},
|
||||
},
|
||||
providerWithVisibilitiesFilter: {
|
||||
organization: 'test-org6',
|
||||
filters: {
|
||||
visibilities: ['public', 'internal'],
|
||||
},
|
||||
},
|
||||
providerWithHost: {
|
||||
organization: 'test-org1',
|
||||
host: 'ghe.internal.com',
|
||||
@@ -103,7 +109,7 @@ describe('readProviderConfigs', () => {
|
||||
});
|
||||
const providerConfigs = readProviderConfigs(config);
|
||||
|
||||
expect(providerConfigs).toHaveLength(8);
|
||||
expect(providerConfigs).toHaveLength(9);
|
||||
expect(providerConfigs[0]).toEqual({
|
||||
id: 'providerOrganizationOnly',
|
||||
organization: 'test-org1',
|
||||
@@ -117,6 +123,7 @@ describe('readProviderConfigs', () => {
|
||||
include: undefined,
|
||||
exclude: undefined,
|
||||
},
|
||||
visibilities: undefined,
|
||||
},
|
||||
schedule: undefined,
|
||||
validateLocationsExist: false,
|
||||
@@ -134,6 +141,7 @@ describe('readProviderConfigs', () => {
|
||||
include: undefined,
|
||||
exclude: undefined,
|
||||
},
|
||||
visibilities: undefined,
|
||||
},
|
||||
schedule: undefined,
|
||||
validateLocationsExist: false,
|
||||
@@ -151,6 +159,7 @@ describe('readProviderConfigs', () => {
|
||||
include: undefined,
|
||||
exclude: undefined,
|
||||
},
|
||||
visibilities: undefined,
|
||||
},
|
||||
schedule: undefined,
|
||||
validateLocationsExist: false,
|
||||
@@ -168,6 +177,7 @@ describe('readProviderConfigs', () => {
|
||||
include: undefined,
|
||||
exclude: undefined,
|
||||
},
|
||||
visibilities: undefined,
|
||||
},
|
||||
schedule: undefined,
|
||||
validateLocationsExist: false,
|
||||
@@ -185,6 +195,7 @@ describe('readProviderConfigs', () => {
|
||||
include: ['backstage-include'],
|
||||
exclude: ['backstage-exclude'],
|
||||
},
|
||||
visibilities: undefined,
|
||||
},
|
||||
schedule: undefined,
|
||||
validateLocationsExist: false,
|
||||
@@ -202,11 +213,30 @@ describe('readProviderConfigs', () => {
|
||||
include: undefined,
|
||||
exclude: undefined,
|
||||
},
|
||||
visibilities: undefined,
|
||||
},
|
||||
schedule: undefined,
|
||||
validateLocationsExist: false,
|
||||
});
|
||||
expect(providerConfigs[6]).toEqual({
|
||||
id: 'providerWithVisibilitiesFilter',
|
||||
organization: 'test-org6',
|
||||
catalogPath: '/catalog-info.yaml',
|
||||
host: 'github.com',
|
||||
filters: {
|
||||
repository: undefined,
|
||||
branch: undefined,
|
||||
allowForks: true,
|
||||
topic: {
|
||||
include: undefined,
|
||||
exclude: undefined,
|
||||
},
|
||||
visibilities: ['public', 'internal'],
|
||||
},
|
||||
schedule: undefined,
|
||||
validateLocationsExist: false,
|
||||
});
|
||||
expect(providerConfigs[7]).toEqual({
|
||||
id: 'providerWithHost',
|
||||
organization: 'test-org1',
|
||||
catalogPath: '/catalog-info.yaml',
|
||||
@@ -219,11 +249,12 @@ describe('readProviderConfigs', () => {
|
||||
include: undefined,
|
||||
exclude: undefined,
|
||||
},
|
||||
visibilities: undefined,
|
||||
},
|
||||
validateLocationsExist: false,
|
||||
schedule: undefined,
|
||||
});
|
||||
expect(providerConfigs[7]).toEqual({
|
||||
expect(providerConfigs[8]).toEqual({
|
||||
id: 'providerWithSchedule',
|
||||
organization: 'test-org1',
|
||||
catalogPath: '/catalog-info.yaml',
|
||||
@@ -236,6 +267,7 @@ describe('readProviderConfigs', () => {
|
||||
include: undefined,
|
||||
exclude: undefined,
|
||||
},
|
||||
visibilities: undefined,
|
||||
},
|
||||
schedule: {
|
||||
frequency: Duration.fromISO('PT30M'),
|
||||
|
||||
@@ -33,6 +33,7 @@ export type GithubEntityProviderConfig = {
|
||||
branch?: string;
|
||||
topic?: GithubTopicFilters;
|
||||
allowForks?: boolean;
|
||||
visibilities?: string[];
|
||||
};
|
||||
validateLocationsExist: boolean;
|
||||
schedule?: TaskScheduleDefinition;
|
||||
@@ -85,6 +86,10 @@ function readProviderConfig(
|
||||
|
||||
const catalogPathContainsWildcard = catalogPath.includes('*');
|
||||
|
||||
const visibilitiesInclude = config?.getOptionalStringArray(
|
||||
'filters.visibilities',
|
||||
);
|
||||
|
||||
if (validateLocationsExist && catalogPathContainsWildcard) {
|
||||
throw Error(
|
||||
`Error while processing GitHub provider config. The catalog path ${catalogPath} contains a wildcard, which is incompatible with validation of locations existing before emitting them. Ensure that validateLocationsExist is set to false.`,
|
||||
@@ -110,6 +115,7 @@ function readProviderConfig(
|
||||
include: topicFilterInclude,
|
||||
exclude: topicFilterExclude,
|
||||
},
|
||||
visibilities: visibilitiesInclude,
|
||||
},
|
||||
schedule,
|
||||
validateLocationsExist,
|
||||
|
||||
Reference in New Issue
Block a user