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(release notes): be able to fetch release notes from Azure DevOps #19136

Closed
Closed
Show file tree
Hide file tree
Changes from 30 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
7524543
feat(azure): be able to get changelog markdown and add to PR
sch1ldkr0ete Oct 26, 2022
86285bc
chore: make prettier
sch1ldkr0ete Oct 26, 2022
506dddb
chore(azure): add unit tests for http and md release notes
sch1ldkr0ete Oct 30, 2022
67e0630
chore(tests): add more coverage
sch1ldkr0ete Nov 10, 2022
94c159f
chore(tests): add azure tests
sch1ldkr0ete Nov 10, 2022
c4c9506
chore(tests): fix azure tests
sch1ldkr0ete Nov 10, 2022
fe7ad34
feat: improve getRef function
sch1ldkr0ete Nov 11, 2022
5a37f3a
feat: fix regex for azure tag
sch1ldkr0ete Nov 11, 2022
8b64c8a
fix: test mock
sch1ldkr0ete Nov 11, 2022
fdbe815
chore(tests): test empty azure tree response
sch1ldkr0ete Nov 13, 2022
cd1c7f6
chore(tests): update snapshots
sch1ldkr0ete Nov 13, 2022
e61cd59
chore(tests): fix test
sch1ldkr0ete Nov 13, 2022
6d411fe
refactor: prettify
sch1ldkr0ete Nov 17, 2022
0dee55d
Merge branch 'main' into feat/add-azure-changelog
sch1ldkr0ete Nov 22, 2022
c34018d
fix(azure): set string
sch1ldkr0ete Nov 22, 2022
2e3639c
chore(prettier)
sch1ldkr0ete Nov 22, 2022
63d2fce
chore(prettier)
sch1ldkr0ete Nov 22, 2022
f242bae
refactor
sch1ldkr0ete Nov 22, 2022
8abe8ce
refactor
sch1ldkr0ete Nov 22, 2022
2c8f8d2
tests(azure): handle file mismatch
sch1ldkr0ete Nov 25, 2022
77c6c22
tests(azure): handle file mismatch
sch1ldkr0ete Nov 25, 2022
9322878
Merge branch 'renovatebot:main' into feat/add-azure-changelog
sch1ldkr0ete Nov 25, 2022
9c7b7c3
Merge branch 'renovatebot:main' into feat/add-azure-changelog
sch1ldkr0ete Nov 26, 2022
6dd1232
Merge branch 'renovatebot:main' into feat/add-azure-changelog
sch1ldkr0ete Nov 27, 2022
713c139
feat(azure): add api version
sch1ldkr0ete Nov 27, 2022
a17101a
update snapshots
sch1ldkr0ete Nov 27, 2022
aa09ff2
prettify
sch1ldkr0ete Nov 27, 2022
d88f039
docs(release notes): Azure support for relase note fetching
sch1ldkr0ete Nov 28, 2022
13b6e2b
fix(release-notes): log as debug
sch1ldkr0ete Dec 19, 2022
babf3de
fix(azure-http): JSON in capital letters
sch1ldkr0ete Dec 19, 2022
25f2be7
Merge branch 'main' into feat/add-azure-changelog
rarkins Mar 4, 2023
35946c7
Merge remote-tracking branch 'origin/main' into feat/add-azure-changelog
sch1ldkr0ete Jul 11, 2023
ac833f6
fix(azure-changelog): set correct links
sch1ldkr0ete Jul 21, 2023
d3c1bda
Merge branch 'renovatebot:main' into feat/add-azure-changelog
sch1ldkr0ete Jul 21, 2023
f7e2c96
fix(azure-changelog): some minor issues
sch1ldkr0ete Jul 21, 2023
dce88a2
fix(azure-changelog): tests
sch1ldkr0ete Jul 28, 2023
0593970
chore(azure-changelog): update snapshot
sch1ldkr0ete Jul 28, 2023
750fe9d
fix(azure-changelog): no need to export id
sch1ldkr0ete Jul 28, 2023
b90df2f
feat(azure-http): add pagination for request
sch1ldkr0ete Jul 29, 2023
8593257
fix(azure-tags): use page result for get json request
sch1ldkr0ete Jul 29, 2023
82d7d69
Merge branch 'renovatebot:main' into feat/add-azure-changelog
sch1ldkr0ete Jul 29, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/usage/configuration-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -798,6 +798,7 @@ Renovate can fetch release notes when they are hosted on one of these platforms:

- GitHub (.com and Enterprise Server)
- GitLab (.com and CE/EE)
- Azure DevOps (.com)
rarkins marked this conversation as resolved.
Show resolved Hide resolved

<!-- prettier-ignore -->
!!! note
Expand Down
2 changes: 2 additions & 0 deletions lib/constants/platforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ export const GITLAB_API_USING_HOST_TYPES = [
'gitlab-changelog',
];

export const AZURE_API_USING_HOST_TYPES = ['azure', 'azure-changelog'];

export const BITBUCKET_API_USING_HOST_TYPES = ['bitbucket', 'bitbucket-tags'];
24 changes: 24 additions & 0 deletions lib/types/platform/azure/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
export type AzureItem = {
objectId: string;
};

export type AzureTreeNode = {
objectId: string;
relativePath: string;
gitObjectType: 'tree' | 'blob';
};

export type AzureTree = {
objectId: string;
treeEntries: AzureTreeNode[];
};

export interface AzureTag {
name: string;
value: string;
}

export interface AzureBodyPaginated<T> {
count: number;
value: T[];
}
8 changes: 8 additions & 0 deletions lib/util/common.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ describe('util/common', () => {
${'https://github-enterprise.example.com/chalk/chalk'} | ${'github'}
${'https://gitlab.com/chalk/chalk'} | ${'gitlab'}
${'https://gitlab-enterprise.example.com/chalk/chalk'} | ${'gitlab'}
${'https://dev.azure.com/chalk/chalk'} | ${'azure'}
`('("$url") === $hostType', ({ url, hostType }) => {
expect(detectPlatform(url)).toBe(hostType);
});
Expand All @@ -30,12 +31,19 @@ describe('util/common', () => {
hostType: 'gitea',
matchHost: 'gt.example.com',
});
hostRules.add({
hostType: 'azure-changelog',
matchHost: 'az.example.com',
});
expect(detectPlatform('https://gl.example.com/chalk/chalk')).toBe(
'gitlab'
);
expect(detectPlatform('https://gh.example.com/chalk/chalk')).toBe(
'github'
);
expect(detectPlatform('https://az.example.com/chalk/chalk')).toBe(
'azure'
);
expect(detectPlatform('https://gt.example.com/chalk/chalk')).toBeNull();
});
});
Expand Down
11 changes: 10 additions & 1 deletion lib/util/common.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
AZURE_API_USING_HOST_TYPES,
GITHUB_API_USING_HOST_TYPES,
GITLAB_API_USING_HOST_TYPES,
} from '../constants';
Expand All @@ -11,14 +12,19 @@ import { parseUrl } from './url';
* @param url the url to detect `platform` from
* @returns matched `platform` if found, otherwise `null`
*/
export function detectPlatform(url: string): 'gitlab' | 'github' | null {
export function detectPlatform(
url: string
): 'gitlab' | 'github' | 'azure' | null {
const { hostname } = parseUrl(url) ?? {};
if (hostname === 'github.com' || hostname?.includes('github')) {
return 'github';
}
if (hostname === 'gitlab.com' || hostname?.includes('gitlab')) {
return 'gitlab';
}
if (hostname === 'dev.azure.com' || hostname?.includes('azure')) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (hostname === 'dev.azure.com' || hostname?.includes('azure')) {
if (hostname === 'dev.azure.com') {

I don't think we should support arbitrary hostnames with azure here

return 'azure';
}

const hostType = hostRules.hostType({ url });

Expand All @@ -32,6 +38,9 @@ export function detectPlatform(url: string): 'gitlab' | 'github' | null {
if (GITHUB_API_USING_HOST_TYPES.includes(hostType)) {
return 'github';
}
if (AZURE_API_USING_HOST_TYPES.includes(hostType)) {
return 'azure';
}

return null;
}
31 changes: 31 additions & 0 deletions lib/util/http/azure.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import * as httpMock from '../../../test/http-mock';
import { AzureHttp } from './azure';

const azureApiHost = 'https://dev.azure.com';

describe('util/http/azure', () => {
let azureApi: AzureHttp;

beforeEach(() => {
azureApi = new AzureHttp();
});

afterEach(() => {
jest.resetAllMocks();
});

it('gets paginated JSON', async () => {
httpMock
.scope(azureApiHost)
.get('/some-org/some-project/some-path?$top=2')
.reply(200, { value: ['a', 'b'] }, { 'x-ms-continuationtoken': '1' })
.get('/some-org/some-project/some-path?$top=2&continuationToken=1')
.reply(200, { value: ['c', 'd'] }, { 'x-ms-continuationtoken': '2' })
.get('/some-org/some-project/some-path?$top=2&continuationToken=2')
.reply(200, { value: ['e'] });
const res = await azureApi.getJsonPaginated(
'https://dev.azure.com/some-org/some-project/some-path?$top=2'
);
expect(res.body.value).toHaveLength(5);
});
});
30 changes: 30 additions & 0 deletions lib/util/http/azure.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import type { AzureBodyPaginated } from '../../types/platform/azure';
import type { HttpOptions, HttpResponse } from './types';
import { Http } from '.';

export class AzureHttp extends Http<HttpOptions> {
http: any;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
http: any;

??

constructor(type = 'azure', options?: HttpOptions) {
super(type, options);
}

async getJsonPaginated<T>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use new function for pagiantion. Checkout github and gitab http for pagination.

url: string,
continuationToken = ''
): Promise<HttpResponse<AzureBodyPaginated<T>>> {
const option = continuationToken
? `&continuationToken=${continuationToken}`
: '';
const result = await super.getJson<AzureBodyPaginated<T>>(
`${url}${option}`
);
if (result.headers['x-ms-continuationtoken']) {
const nextResult = await this.getJsonPaginated<T>(
url,
result.headers['x-ms-continuationtoken']
);
result.body.value.push(...nextResult.body.value);
}
return result;
}
}
1 change: 1 addition & 0 deletions lib/util/http/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export interface InternalHttpOptions extends HttpOptions {

export interface HttpHeaders extends IncomingHttpHeaders {
link?: string | undefined;
'x-ms-continuationtoken'?: string | undefined;
}

export interface HttpResponse<T = string> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`workers/repository/update/pr/changelog/azure getChangeLogJSON handles empty Azure tags response 1`] = `
{
"hasReleaseNotes": false,
"project": {
"apiBaseUrl": "https://dev.azure.com/some-org/some-project/_apis/",
"baseUrl": "https://dev.azure.com/some-org/some-project/",
"depName": "renovate",
"repository": "some-repo",
"sourceDirectory": undefined,
"sourceUrl": "https://dev.azure.com/some-org/some-project/_git/some-repo/",
"type": "azure",
},
"versions": [
{
"changes": [],
"compare": {},
"date": undefined,
"gitRef": undefined,
"releaseNotes": null,
"version": "5.6.1",
},
{
"changes": [],
"compare": {},
"date": "2020-02-13T15:37:00.000Z",
"gitRef": undefined,
"releaseNotes": null,
"version": "5.6.0",
},
{
"changes": [],
"compare": {},
"date": undefined,
"gitRef": "eba303e91c930292198b2fc57040145682162a1b",
"releaseNotes": null,
"version": "5.5.0",
},
{
"changes": [],
"compare": {},
"date": "2018-08-24T14:23:00.000Z",
"gitRef": undefined,
"releaseNotes": null,
"version": "5.4.0",
},
],
}
`;

exports[`workers/repository/update/pr/changelog/azure getChangeLogJSON uses Azure tags 1`] = `
{
"hasReleaseNotes": true,
"project": {
"apiBaseUrl": "https://dev.azure.com/some-org/some-project/_apis/",
"baseUrl": "https://dev.azure.com/some-org/some-project/",
"depName": "renovate",
"repository": "some-repo",
"sourceDirectory": undefined,
"sourceUrl": "https://dev.azure.com/some-org/some-project/_git/some-repo/",
"type": "azure",
},
"versions": [
{
"changes": [],
"compare": {
"url": "https://dev.azure.com/some-org/some-project/_git/some-repo/branchCompare?baseVersion=GTv5.6.0&targetVersion=GTv5.6.1",
},
"date": undefined,
"gitRef": undefined,
"releaseNotes": {
"notesSourceUrl": "",
"url": "https://dev.azure.com/some-org/some-project/_git/some-repo/branchCompare?baseVersion=GTv5.6.0&targetVersion=GTv5.6.1",
},
"version": "5.6.1",
},
{
"changes": [],
"compare": {
"url": "https://dev.azure.com/some-org/some-project/_git/some-repo/branchCompare?baseVersion=GTv5.5.0&targetVersion=GTv5.6.0",
},
"date": "2020-02-13T15:37:00.000Z",
"gitRef": undefined,
"releaseNotes": {
"notesSourceUrl": "",
"url": "https://dev.azure.com/some-org/some-project/_git/some-repo/branchCompare?baseVersion=GTv5.5.0&targetVersion=GTv5.6.0",
},
"version": "5.6.0",
},
{
"changes": [],
"compare": {
"url": "https://dev.azure.com/some-org/some-project/_git/some-repo/branchCompare?baseVersion=GTv5.4.0&targetVersion=GTv5.5.0",
},
"date": undefined,
"gitRef": "eba303e91c930292198b2fc57040145682162a1b",
"releaseNotes": {
"notesSourceUrl": "",
"url": "https://dev.azure.com/some-org/some-project/_git/some-repo/branchCompare?baseVersion=GTv5.4.0&targetVersion=GTv5.5.0",
},
"version": "5.5.0",
},
{
"changes": [],
"compare": {
"url": "https://dev.azure.com/some-org/some-project/_git/some-repo/branchCompare?baseVersion=GTv5.2.0&targetVersion=GTv5.4.0",
},
"date": "2018-08-24T14:23:00.000Z",
"gitRef": undefined,
"releaseNotes": {
"notesSourceUrl": "",
"url": "https://dev.azure.com/some-org/some-project/_git/some-repo/branchCompare?baseVersion=GTv5.2.0&targetVersion=GTv5.4.0",
},
"version": "5.4.0",
},
],
}
`;

exports[`workers/repository/update/pr/changelog/azure getChangeLogJSON uses Azure tags with error 1`] = `
{
"hasReleaseNotes": false,
"project": {
"apiBaseUrl": "https://dev.azure.com/some-org/some-project/_apis/",
"baseUrl": "https://dev.azure.com/some-org/some-project/",
"depName": "renovate",
"repository": "some-repo",
"sourceDirectory": undefined,
"sourceUrl": "https://dev.azure.com/some-org/some-project/_git/some-repo/",
"type": "azure",
},
"versions": [
{
"changes": [],
"compare": {},
"date": undefined,
"gitRef": undefined,
"releaseNotes": null,
"version": "5.6.1",
},
{
"changes": [],
"compare": {},
"date": "2020-02-13T15:37:00.000Z",
"gitRef": undefined,
"releaseNotes": null,
"version": "5.6.0",
},
{
"changes": [],
"compare": {},
"date": undefined,
"gitRef": "eba303e91c930292198b2fc57040145682162a1b",
"releaseNotes": null,
"version": "5.5.0",
},
{
"changes": [],
"compare": {},
"date": "2018-08-24T14:23:00.000Z",
"gitRef": undefined,
"releaseNotes": null,
"version": "5.4.0",
},
],
}
`;

exports[`workers/repository/update/pr/changelog/azure getChangeLogJSON works without Azure 1`] = `
{
"hasReleaseNotes": false,
"project": {
"apiBaseUrl": "https://dev.azure.com/some-org/some-project/_apis/",
"baseUrl": "https://dev.azure.com/some-org/some-project/",
"depName": "renovate",
"repository": "some-repo",
"sourceDirectory": undefined,
"sourceUrl": "https://dev.azure.com/some-org/some-project/_git/some-repo/",
"type": "azure",
},
"versions": [
{
"changes": [],
"compare": {},
"date": undefined,
"gitRef": undefined,
"releaseNotes": null,
"version": "5.6.1",
},
{
"changes": [],
"compare": {},
"date": "2020-02-13T15:37:00.000Z",
"gitRef": undefined,
"releaseNotes": null,
"version": "5.6.0",
},
{
"changes": [],
"compare": {},
"date": undefined,
"gitRef": "eba303e91c930292198b2fc57040145682162a1b",
"releaseNotes": null,
"version": "5.5.0",
},
{
"changes": [],
"compare": {},
"date": "2018-08-24T14:23:00.000Z",
"gitRef": undefined,
"releaseNotes": null,
"version": "5.4.0",
},
],
}
`;