From 57ffec14cbc20c90939f41fb6b4861f000c8b70d Mon Sep 17 00:00:00 2001 From: Ayoub Kaanich Date: Sun, 1 Apr 2018 21:41:26 +0200 Subject: [PATCH] feat: autodetect changelog file name (#1770) Improves changelog detection algorithm to look for different upper/lower case options as well as alternative filenames like `History.md`. Resolves #1754 --- lib/workers/pr/release-notes.js | 29 +++++++---- package.json | 1 + test/workers/pr/release-notes.spec.js | 71 ++++++++++++++++++--------- yarn.lock | 4 ++ 4 files changed, 72 insertions(+), 33 deletions(-) diff --git a/lib/workers/pr/release-notes.js b/lib/workers/pr/release-notes.js index 5029ee3be4..0d9e6a1575 100644 --- a/lib/workers/pr/release-notes.js +++ b/lib/workers/pr/release-notes.js @@ -1,4 +1,5 @@ const ghGot = require('../../platform/github/gh-got-wrapper'); +const changelogFilenameRegex = require('changelog-filename-regex'); const MarkdownIt = require('markdown-it'); const markdown = new MarkdownIt('zero'); @@ -101,20 +102,30 @@ function sectionize(text, level) { async function getReleaseNotesMd(repository, version) { logger.debug(`getReleaseNotes(${repository}, ${version})`); - let changelogMd; + let changelogMd = ''; try { - const res = await ghGot( - `https://api.github.com/repos/${repository}/contents/CHANGELOG.md` - ); + const apiPrefix = `https://api.github.com/repos/${repository}/contents/`; + const filesRes = await ghGot(apiPrefix); + const files = filesRes.body + .map(f => f.name) + .filter(f => changelogFilenameRegex.test(f)); + if (!files.length) { + logger.debug('no changelog file found'); + return null; + } + const file = files[0]; + /* istanbul ignore if */ + if (files.length > 1) { + logger.info(`Multiple candidates for changelog file, using ${file}`); + } + const fileRes = await ghGot(`${apiPrefix}/${file}`); changelogMd = - Buffer.from(res.body.content, 'base64').toString() + '\n#\n##'; + Buffer.from(fileRes.body.content, 'base64').toString() + '\n#\n##'; } catch (err) { - // Probably a 404 - } - if (!changelogMd) { - logger.debug('CHANGELOG.md not found'); + // Probably a 404? return null; } + changelogMd = changelogMd.replace(/\n\s*.*?<\/a>\n/g, '\n'); for (const level of [1, 2, 3, 4, 5, 6, 7]) { const changelogParsed = sectionize(changelogMd, level); diff --git a/package.json b/package.json index 3bb483fd94..39f96f86a3 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "cacache": "10.0.4", "chalk": "2.3.2", "changelog": "1.4.2", + "changelog-filename-regex": "1.1.1", "child-process-promise": "2.2.1", "clean-git-ref": "1.0.3", "commander": "2.15.1", diff --git a/test/workers/pr/release-notes.spec.js b/test/workers/pr/release-notes.spec.js index 7ae32f5015..4a4ae3beff 100644 --- a/test/workers/pr/release-notes.spec.js +++ b/test/workers/pr/release-notes.spec.js @@ -19,6 +19,12 @@ const jsYamlChangelogMd = fs.readFileSync( 'utf8' ); +const contentsResponse = [ + { name: 'lib' }, + { name: 'CHANGELOG.md' }, + { name: 'README.md' }, +]; + jest.mock('gh-got'); describe('workers/pr/release-notes', () => { @@ -36,50 +42,67 @@ describe('workers/pr/release-notes', () => { const res = await getReleaseNotesMd('chalk', '2.0.0'); expect(res).toBe(null); }); - it('handles wrong format', async () => { + it('handles files mismatch', async () => { ghGot.mockReturnValueOnce({ - body: { - content: Buffer.from('not really markdown').toString('base64'), - }, + body: [{ name: 'lib' }, { name: 'README.md' }], }); + const res = await getReleaseNotesMd('chalk', '2.0.0'); + expect(res).toBe(null); + }); + it('handles wrong format', async () => { + ghGot + .mockReturnValueOnce({ body: contentsResponse }) + .mockReturnValueOnce({ + body: { + content: Buffer.from('not really markdown').toString('base64'), + }, + }); const res = await getReleaseNotesMd('some/repository1', '1.0.0'); expect(res).toBe(null); }); it('handles bad markdown', async () => { - ghGot.mockReturnValueOnce({ - body: { - content: Buffer.from(`#\nha\nha\n#\nha\nha`).toString('base64'), - }, - }); + ghGot + .mockReturnValueOnce({ body: contentsResponse }) + .mockReturnValueOnce({ + body: { + content: Buffer.from(`#\nha\nha\n#\nha\nha`).toString('base64'), + }, + }); const res = await getReleaseNotesMd('some/repository2', '1.0.0'); expect(res).toBe(null); }); it('parses angular.js', async () => { - ghGot.mockReturnValueOnce({ - body: { - content: Buffer.from(angularJsChangelogMd).toString('base64'), - }, - }); + ghGot + .mockReturnValueOnce({ body: contentsResponse }) + .mockReturnValueOnce({ + body: { + content: Buffer.from(angularJsChangelogMd).toString('base64'), + }, + }); const res = await getReleaseNotesMd('angular/angular.js', '1.6.9'); expect(res).not.toBe(null); expect(res).toMatchSnapshot(); }); it('parses jest', async () => { - ghGot.mockReturnValueOnce({ - body: { - content: Buffer.from(jestChangelogMd).toString('base64'), - }, - }); + ghGot + .mockReturnValueOnce({ body: contentsResponse }) + .mockReturnValueOnce({ + body: { + content: Buffer.from(jestChangelogMd).toString('base64'), + }, + }); const res = await getReleaseNotesMd('facebook/jest', '22.0.0'); expect(res).not.toBe(null); expect(res).toMatchSnapshot(); }); it('parses js-yaml', async () => { - ghGot.mockReturnValueOnce({ - body: { - content: Buffer.from(jsYamlChangelogMd).toString('base64'), - }, - }); + ghGot + .mockReturnValueOnce({ body: contentsResponse }) + .mockReturnValueOnce({ + body: { + content: Buffer.from(jsYamlChangelogMd).toString('base64'), + }, + }); const res = await getReleaseNotesMd('nodeca/js-yaml', '3.10.0'); expect(res).not.toBe(null); expect(res).toMatchSnapshot(); diff --git a/yarn.lock b/yarn.lock index 850a2bb2fe..4fcbaa8048 100644 --- a/yarn.lock +++ b/yarn.lock @@ -895,6 +895,10 @@ chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" +changelog-filename-regex@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/changelog-filename-regex/-/changelog-filename-regex-1.1.1.tgz#8a2eb3049a652e3c03c8b601e3cbb032ee9f4e6d" + changelog@1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/changelog/-/changelog-1.4.2.tgz#618abaecfaff42cf2452d52c195ff83b5589d2fe"