Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: refactor changelog platforms #23076

Merged
merged 4 commits into from
Jul 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions lib/workers/repository/update/pr/changelog/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { ChangeLogSource } from './source';
import { GitHubChangeLogSource } from './source-github';
import { GitLabChangeLogSource } from './source-gitlab';

const api = new Map<string, ChangeLogSource>();
export default api;

api.set('github', new GitHubChangeLogSource());
api.set('gitlab', new GitLabChangeLogSource());
10 changes: 10 additions & 0 deletions lib/workers/repository/update/pr/changelog/github.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,16 @@ describe('workers/repository/update/pr/changelog/github', () => {
).toEqual({ error: 'MissingGithubToken' });
});

it('handles suppressed Github warnings', async () => {
setchy marked this conversation as resolved.
Show resolved Hide resolved
GlobalConfig.set({ githubTokenWarn: false });
expect(
await getChangeLogJSON({
...upgrade,
sourceUrl: 'https://github.com',
})
).toBeNull();
});

it('handles no releases', async () => {
expect(
await getChangeLogJSON({
Expand Down
39 changes: 1 addition & 38 deletions lib/workers/repository/update/pr/changelog/github/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,7 @@ import type {
GithubGitTree,
GithubGitTreeNode,
} from '../../../../../../types/platform/github';
import {
queryReleases,
queryTags,
} from '../../../../../../util/github/graphql';
import { queryReleases } from '../../../../../../util/github/graphql';
import { GithubHttp } from '../../../../../../util/http/github';
import { fromBase64 } from '../../../../../../util/string';
import { ensureTrailingSlash, joinUrlParts } from '../../../../../../util/url';
Expand All @@ -22,40 +19,6 @@ import type {
export const id = 'github-changelog';
const http = new GithubHttp(id);

export async function getTags(
endpoint: string,
repository: string
): Promise<string[]> {
logger.trace('github.getTags()');
try {
const tags = await queryTags(
{
registryUrl: endpoint,
packageName: repository,
},
http
);

// istanbul ignore if
if (!tags.length) {
logger.debug(`No Github tags found for repository:${repository}`);
}

return tags.map(({ version }) => version);
} catch (err) {
logger.debug(
{ sourceRepo: repository, err },
'Failed to fetch Github tags'
);
// istanbul ignore if
if (err.message?.includes('Bad credentials')) {
logger.warn('Bad credentials triggering tag fail lookup in changelog');
throw err;
}
return [];
}
}

export async function getReleaseNotesMd(
repository: string,
apiBaseUrl: string,
Expand Down
36 changes: 0 additions & 36 deletions lib/workers/repository/update/pr/changelog/gitlab/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import changelogFilenameRegex from 'changelog-filename-regex';
import { logger } from '../../../../../../logger';
import type { GitlabRelease } from '../../../../../../modules/datasource/gitlab-releases/types';
import type { GitlabTag } from '../../../../../../modules/datasource/gitlab-tags/types';
import type { GitlabTreeNode } from '../../../../../../types/platform/gitlab';
import { GitlabHttp } from '../../../../../../util/http/gitlab';
import { ensureTrailingSlash } from '../../../../../../util/url';
Expand All @@ -15,41 +14,6 @@ import type {
export const id = 'gitlab-changelog';
const http = new GitlabHttp(id);

export async function getTags(
endpoint: string,
repository: string
): Promise<string[]> {
logger.trace('gitlab.getTags()');
const urlEncodedRepo = encodeURIComponent(repository);
const url = `${ensureTrailingSlash(
endpoint
)}projects/${urlEncodedRepo}/repository/tags?per_page=100`;
try {
const res = await http.getJson<GitlabTag[]>(url, {
paginate: true,
});

const tags = res.body;

if (!tags.length) {
logger.debug(`No Gitlab tags found for ${repository}`);
}

return tags.map((tag) => tag.name).filter(Boolean);
} catch (err) {
logger.debug(
{ sourceRepo: repository, err },
'Failed to fetch Gitlab tags'
);
// istanbul ignore if
if (err.message?.includes('Bad credentials')) {
logger.warn('Bad credentials triggering tag fail lookup in changelog');
throw err;
}
return [];
}
}

export async function getReleaseNotesMd(
repository: string,
apiBaseUrl: string,
Expand Down
35 changes: 27 additions & 8 deletions lib/workers/repository/update/pr/changelog/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as semverVersioning from '../../../../../modules/versioning/semver';
import * as githubGraphql from '../../../../../util/github/graphql';
import * as hostRules from '../../../../../util/host-rules';
import type { BranchConfig } from '../../../../types';
import * as releases from './releases';
import { getChangeLogJSON } from '.';

jest.mock('../../../../../modules/datasource/npm');
Expand All @@ -13,6 +14,7 @@ const githubApiHost = 'https://api.github.com';

const githubTagsMock = jest.spyOn(githubGraphql, 'queryTags');
const githubReleasesMock = jest.spyOn(githubGraphql, 'queryReleases');
const getInRangeReleasesMock = jest.spyOn(releases, 'getInRangeReleases');

const upgrade = partial<BranchConfig>({
endpoint: 'https://api.github.com/',
Expand Down Expand Up @@ -55,6 +57,15 @@ describe('workers/repository/update/pr/changelog/index', () => {
).toBeNull();
});

it('handles unsupported changelog source', async () => {
expect(
await getChangeLogJSON({
...upgrade,
sourceUrl: 'https://dev.azure.com/unknown-repo',
})
).toBeNull();
});

it('returns null if no currentVersion', async () => {
expect(
await getChangeLogJSON({
Expand Down Expand Up @@ -84,7 +95,7 @@ describe('workers/repository/update/pr/changelog/index', () => {
});

it('works without Github', async () => {
githubTagsMock.mockRejectedValueOnce(new Error('Unknown'));
githubTagsMock.mockRejectedValue(new Error('Unknown'));
// 4 versions, so 4 calls without cache
githubReleasesMock
.mockRejectedValueOnce(new Error('Unknown'))
Expand Down Expand Up @@ -156,7 +167,7 @@ describe('workers/repository/update/pr/changelog/index', () => {
});

it('filters unnecessary warns', async () => {
githubTagsMock.mockRejectedValueOnce(new Error('Unknown Github Repo'));
githubTagsMock.mockRejectedValue(new Error('Unknown Github Repo'));
setchy marked this conversation as resolved.
Show resolved Hide resolved
githubReleasesMock.mockRejectedValueOnce(
new Error('Unknown Github Repo')
);
Expand Down Expand Up @@ -260,9 +271,17 @@ describe('workers/repository/update/pr/changelog/index', () => {
).toBeNull();
});

it('will call getInRangeReleases when releases is undefined', async () => {
await getChangeLogJSON({
...upgrade,
releases: undefined,
});
expect(getInRangeReleasesMock).toHaveBeenCalledOnce();
});

it('supports github enterprise and github.com changelog', async () => {
githubTagsMock.mockRejectedValueOnce([]);
githubReleasesMock.mockRejectedValueOnce([]);
githubTagsMock.mockRejectedValue([]);
githubReleasesMock.mockRejectedValue([]);
httpMock.scope(githubApiHost).persist().get(/.*/).reply(200, []);
hostRules.add({
hostType: 'github',
Expand Down Expand Up @@ -295,8 +314,8 @@ describe('workers/repository/update/pr/changelog/index', () => {
});

it('supports github enterprise and github enterprise changelog', async () => {
githubTagsMock.mockRejectedValueOnce([]);
githubReleasesMock.mockRejectedValueOnce([]);
githubTagsMock.mockRejectedValue([]);
githubReleasesMock.mockRejectedValue([]);
httpMock
.scope('https://github-enterprise.example.com')
.persist()
Expand Down Expand Up @@ -335,8 +354,8 @@ describe('workers/repository/update/pr/changelog/index', () => {
});

it('supports github.com and github enterprise changelog', async () => {
githubTagsMock.mockRejectedValueOnce([]);
githubReleasesMock.mockRejectedValueOnce([]);
githubTagsMock.mockRejectedValue([]);
githubReleasesMock.mockRejectedValue([]);
httpMock
.scope('https://github-enterprise.example.com')
.persist()
Expand Down
45 changes: 26 additions & 19 deletions lib/workers/repository/update/pr/changelog/index.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import is from '@sindresorhus/is';
import { logger } from '../../../../../logger';
import * as allVersioning from '../../../../../modules/versioning';
import { detectPlatform } from '../../../../../util/common';
import type { BranchUpgradeConfig } from '../../../../types';
import * as sourceGithub from './source-github';
import * as sourceGitlab from './source-gitlab';
import api from './api';
import type { ChangeLogSource } from './source';
import type { ChangeLogResult } from './types';

export * from './types';
Expand All @@ -26,29 +27,35 @@ export async function getChangeLogJSON(
`Fetching changelog: ${sourceUrl} (${currentVersion} -> ${newVersion})`
);

let res: ChangeLogResult | null = null;

const platform = detectPlatform(sourceUrl);

switch (platform) {
case 'gitlab':
res = await sourceGitlab.getChangeLogJSON(config);
break;
case 'github':
res = await sourceGithub.getChangeLogJSON(config);
break;

default:
logger.info(
{ sourceUrl, hostType: platform },
'Unknown platform, skipping changelog fetching.'
);
break;
if (is.nullOrUndefined(platform)) {
logger.info(
{ sourceUrl, hostType: platform },
'Unknown platform, skipping changelog fetching.'
);
return null;
}

const changeLogSource = getChangeLogSourceFor(platform);

if (is.nullOrUndefined(changeLogSource)) {
logger.info(
{ sourceUrl, hostType: platform },
'Unknown changelog source, skipping changelog fetching.'
);
return null;
}

return res;
return await changeLogSource.getChangeLogJSON(config);
} catch (err) /* istanbul ignore next */ {
logger.error({ config, err }, 'getChangeLogJSON error');
return null;
}
}

export function getChangeLogSourceFor(
platform: string
): ChangeLogSource | null {
return api.get(platform) ?? null;
}
Loading