fix(github-provider): validateLocationsExist: true + filters.branch bug

Signed-off-by: thomvaill <thomvaill@bluebricks.dev>
This commit is contained in:
thomvaill
2026-03-26 20:04:59 +01:00
parent cf4b4789f1
commit b11e3380af
5 changed files with 117 additions and 7 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend-module-github': patch
---
Fixed a bug where `GithubEntityProvider` with `validateLocationsExist: true` and `filters.branch` configured would always check for the catalog file on the repository's default branch (`HEAD`) instead of the configured branch. This caused repositories to be filtered out when the catalog file only existed on the non-default branch.
@@ -29,6 +29,7 @@ import {
getOrganizationUsers,
getTeamMembers,
getOrganizationRepositories,
getOrganizationRepository,
QueryResponse,
GithubUser,
GithubTeam,
@@ -778,15 +779,105 @@ describe('github', () => {
};
server.use(
graphqlMsw.query('repositories', () =>
HttpResponse.json({ data: input }),
),
graphqlMsw.query('repositories', ({ variables }) => {
expect(variables.catalogPathRef).toBe('HEAD:catalog-info.yaml');
return HttpResponse.json({ data: input });
}),
);
await expect(
getOrganizationRepositories(graphql, 'a', 'catalog-info.yaml'),
).resolves.toEqual(output);
});
it('uses provided branch for catalog path ref', async () => {
server.use(
graphqlMsw.query('repositories', ({ variables }) => {
expect(variables.catalogPathRef).toBe('develop:catalog-info.yaml');
return HttpResponse.json({
data: {
repositoryOwner: {
repositories: {
pageInfo: { hasNextPage: false, endCursor: null },
nodes: [
{
name: 'repo1',
url: 'https://github.com/my-org/repo1',
isArchived: false,
isFork: false,
visibility: 'public',
defaultBranchRef: { name: 'main' },
catalogInfoFile: null,
repositoryTopics: { nodes: [] },
},
],
},
},
},
});
}),
);
await getOrganizationRepositories(
graphql as any,
'my-org',
'/catalog-info.yaml',
undefined,
'develop',
);
});
});
describe('getOrganizationRepository', () => {
const repositoryData = {
repositoryOwner: {
repository: {
name: 'my-repo',
url: 'https://github.com/my-org/my-repo',
isArchived: false,
isFork: false,
visibility: 'public',
defaultBranchRef: { name: 'main' },
catalogInfoFile: null,
repositoryTopics: { nodes: [] },
},
},
};
it('defaults catalogPathRef to HEAD when no branch is provided', async () => {
server.use(
graphqlMsw.query('repository', ({ variables }) => {
expect(variables.catalogPathRef).toBe('HEAD:catalog-info.yaml');
return HttpResponse.json({ data: repositoryData });
}),
);
await getOrganizationRepository(
graphql as any,
'my-org',
'my-repo',
'catalog-info.yaml',
);
});
it('uses provided branch for catalogPathRef', async () => {
server.use(
graphqlMsw.query('repository', ({ variables }) => {
expect(variables.catalogPathRef).toBe(
'my-feature-branch:catalog-info.yaml',
);
return HttpResponse.json({ data: repositoryData });
}),
);
await getOrganizationRepository(
graphql as any,
'my-org',
'my-repo',
'catalog-info.yaml',
'my-feature-branch',
);
});
});
describe('createAddEntitiesOperation', () => {
@@ -594,6 +594,7 @@ export async function getOrganizationRepositories(
org: string,
catalogPath: string,
pageSizes: GithubPageSizes = DEFAULT_PAGE_SIZES,
branch?: string,
): Promise<{ repositories: RepositoryResponse[] }> {
let relativeCatalogPathRef: string;
// We must strip the leading slash or the query for objects does not work
@@ -602,7 +603,8 @@ export async function getOrganizationRepositories(
} else {
relativeCatalogPathRef = catalogPath;
}
const catalogPathRef = `HEAD:${relativeCatalogPathRef}`;
const branchRef = branch ?? 'HEAD';
const catalogPathRef = `${branchRef}:${relativeCatalogPathRef}`;
const query = `
query repositories($org: String!, $catalogPathRef: String!, $cursor: String, $repositoriesPageSize: Int!) {
repositoryOwner(login: $org) {
@@ -663,6 +665,7 @@ export async function getOrganizationRepository(
org: string,
repoName: string,
catalogPath: string,
branch?: string,
): Promise<RepositoryResponse | null> {
let relativeCatalogPathRef: string;
// We must strip the leading slash or the query for objects does not work
@@ -671,7 +674,8 @@ export async function getOrganizationRepository(
} else {
relativeCatalogPathRef = catalogPath;
}
const catalogPathRef = `HEAD:${relativeCatalogPathRef}`;
const branchRef = branch ?? 'HEAD';
const catalogPathRef = `${branchRef}:${relativeCatalogPathRef}`;
const query = `
query repository($org: String!, $repoName: String!, $catalogPathRef: String!) {
repositoryOwner(login: $org) {
@@ -411,7 +411,7 @@ describe('GithubEntityProvider', () => {
organization: 'test-org',
catalogPath: 'catalog-custom.yaml',
filters: {
branch: 'main',
branch: 'backstage',
},
validateLocationsExist: true,
},
@@ -480,7 +480,15 @@ describe('GithubEntityProvider', () => {
expect(taskDef.id).toEqual('github-provider:myProvider:refresh');
await (taskDef.fn as () => Promise<void>)();
const url = `https://github.com/test-org/another-repo/blob/main/catalog-custom.yaml`;
expect(mockGetOrganizationRepositories).toHaveBeenCalledWith(
expect.anything(),
'test-org',
'catalog-custom.yaml',
expect.any(Object),
'backstage',
);
const url = `https://github.com/test-org/another-repo/blob/backstage/catalog-custom.yaml`;
const expectedEntities = createExpectedEntitiesForUrl(url);
expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(1);
@@ -275,6 +275,7 @@ export class GithubEntityProvider implements EntityProvider, EventSubscriber {
organization,
catalogPath,
pageSizes,
this.config.filters.branch,
);
repositories = repositories.concat(
repositoriesFromGithub.map(r =>
@@ -664,6 +665,7 @@ export class GithubEntityProvider implements EntityProvider, EventSubscriber {
organization,
repository.name,
catalogPath,
this.config.filters.branch,
).then(r =>
r ? this.createRepoFromGithubResponse(r, organization) : null,
);