Skip to content

Commit ab3da33

Browse files
fix(showcase): handle not found GitHub repositories
1 parent c4deb85 commit ab3da33

2 files changed

Lines changed: 82 additions & 8 deletions

File tree

scripts/libs/showcaseScrapper.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// This script is based on the one from Astro:
22
// https://github.com/withastro/astro.build/blob/main/scripts/update-showcase.mjs#L48
33

4-
import { graphql } from '@octokit/graphql';
4+
import { GraphqlResponseError, graphql } from '@octokit/graphql';
55
import type { Repository } from '@octokit/graphql-schema';
66
import * as ghActions from '@actions/core';
77
import { nameToEmoji } from 'gemoji';
@@ -68,7 +68,24 @@ export class ShowcaseScraper {
6868

6969
if (ghReference?.hostname === 'github.com' && ghReference?.owner && ghReference?.name) {
7070
console.info(`Adding repository data from ${href}...`);
71-
const { repository } = await this.#getRepository(ghReference.owner, ghReference.name);
71+
let repository: Repository | null = null;
72+
73+
try {
74+
const result = await this.#getRepository(ghReference.owner, ghReference.name);
75+
repository = result.repository;
76+
} catch (error: unknown) {
77+
if (this.#isRepositoryNotFoundError(error)) {
78+
console.warn(`Repository not found, skipping ${href}.`);
79+
continue;
80+
}
81+
82+
throw error;
83+
}
84+
85+
if (!repository) {
86+
console.warn(`Repository data missing, skipping ${href}.`);
87+
continue;
88+
}
7289

7390
links.push({
7491
type: 'github_repo',
@@ -364,4 +381,18 @@ export class ShowcaseScraper {
364381

365382
return languages;
366383
}
384+
385+
#isRepositoryNotFoundError(error: unknown) {
386+
if (!(error instanceof GraphqlResponseError)) {
387+
return false;
388+
}
389+
390+
return error.errors.some((entry) => {
391+
if (!entry || typeof entry !== 'object' || !('type' in entry)) {
392+
return false;
393+
}
394+
395+
return entry.type === 'NOT_FOUND';
396+
});
397+
}
367398
}

tests/showcase.test.ts

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,24 @@ import { afterAll, assert, beforeAll, describe, expect, test, vi } from 'vitest'
66
import { ShowcaseScraper } from '../scripts/libs/showcaseScrapper';
77
import type { ShowcaseGitHubRepoLink } from '../src/content.config';
88

9-
// Define a mock query function that will be used by the GraphQL client during the test to return fake data.
10-
const { queryMock } = vi.hoisted(() => ({ queryMock: vi.fn() }));
9+
// Define mocks used by the GraphQL client during tests.
10+
const { MockGraphqlResponseError, queryMock } = vi.hoisted(() => ({
11+
queryMock: vi.fn(),
12+
MockGraphqlResponseError: class extends Error {
13+
errors: Array<{ type?: string }>;
14+
15+
constructor(errors: Array<{ type?: string }>) {
16+
super('Mock GraphQL response error');
17+
this.errors = errors;
18+
}
19+
},
20+
}));
1121

1222
// Mock the entire GraphQL client to avoid hitting the GitHub API.
13-
vi.mock('@octokit/graphql', () => ({ graphql: { defaults: () => queryMock } }));
23+
vi.mock('@octokit/graphql', () => ({
24+
GraphqlResponseError: MockGraphqlResponseError,
25+
graphql: { defaults: () => queryMock },
26+
}));
1427

1528
// Mock the fs module to avoid writing showcase files to the file system during tests.
1629
vi.mock('node:fs/promises');
@@ -78,6 +91,19 @@ test('should identify GitHub repo links', async () => {
7891
expect(showcases.at(0)?.links).toMatchObject([{ url: link, type: 'github_repo' }]);
7992
});
8093

94+
test('should skip GitHub repo links when repository is not found', async () => {
95+
const missingRepoLink = getTestGitHubLink('user_1', 'repo_missing');
96+
const validRepoLink = getTestGitHubLink('user_2', 'repo_valid');
97+
98+
const scraper = getTestScrapper([[{ notFound: true, url: missingRepoLink }, validRepoLink]]);
99+
100+
const showcases = await scraper.run();
101+
102+
expect(showcases).toHaveLength(1);
103+
expect(showcases.at(0)?.links).toHaveLength(1);
104+
expect(showcases.at(0)?.links.at(0)).toMatchObject({ type: 'github_repo', url: validRepoLink });
105+
});
106+
81107
test('should handle GitLab links as unknown', async () => {
82108
const link = getTestGitLabLink('user');
83109

@@ -350,7 +376,7 @@ describe('GitHub repo link languages', () => {
350376
function getTestScrapper(commentsLinks: TestCommentLinks[]) {
351377
const scraper = new ShowcaseScraper('test-org', 'test-repo', 0, []);
352378

353-
let ghRepoLinks: { languages: TestRepoLanguage[]; name: string; owner: string; url: string }[] = [];
379+
let ghRepoLinks: { languages: TestRepoLanguage[]; name: string; notFound?: boolean; owner: string; url: string }[] = [];
354380

355381
const commentsNodes = commentsLinks.map((commentLinks, commentIndex) => {
356382
const isFlatLinks = Array.isArray(commentLinks);
@@ -370,7 +396,13 @@ function getTestScrapper(commentsLinks: TestCommentLinks[]) {
370396
const ghLink = gh(url);
371397

372398
if (ghLink?.name && ghLink?.owner) {
373-
ghRepoLinks.push({ languages: languages ?? [], name: ghLink.name, owner: ghLink.owner, url });
399+
ghRepoLinks.push({
400+
languages: languages ?? [],
401+
name: ghLink.name,
402+
notFound: typeof link === 'string' ? false : link.notFound,
403+
owner: ghLink.owner,
404+
url,
405+
});
374406
}
375407

376408
return `<a href="${url}">${url}</a>`;
@@ -391,6 +423,11 @@ function getTestScrapper(commentsLinks: TestCommentLinks[]) {
391423

392424
// Each GitHub repository link will trigger a GraphQL query to fetch the repository data so we mock each of them.
393425
for (const ghRepoLink of ghRepoLinks) {
426+
if (ghRepoLink.notFound) {
427+
queryMock.mockRejectedValueOnce(new MockGraphqlResponseError([{ type: 'NOT_FOUND' }]));
428+
continue;
429+
}
430+
394431
queryMock.mockReturnValueOnce({
395432
repository: {
396433
description: faker.lorem.paragraph(),
@@ -441,7 +478,13 @@ function getTestRepoLanguageSize() {
441478
return faker.number.int(1_000_000);
442479
}
443480

444-
type TestCommentLink = string | { languages?: TestRepoLanguage[]; url: string };
481+
type TestGithubRepoLinkOptions = {
482+
languages?: TestRepoLanguage[];
483+
notFound?: boolean;
484+
url: string;
485+
};
486+
487+
type TestCommentLink = string | TestGithubRepoLinkOptions;
445488
type TestCommentLinks = string[] | { author?: string; links: TestCommentLink[] };
446489

447490
interface TestRepoLanguage {

0 commit comments

Comments
 (0)