diff --git a/lib/config/templates/group-pr-body.hbs b/lib/config/templates/group-pr-body.hbs index 80f03083c5bf668b79f2d3b5c19a231b932a02a6..305e433a360001ede583675972a35993dacc4e59 100644 --- a/lib/config/templates/group-pr-body.hbs +++ b/lib/config/templates/group-pr-body.hbs @@ -1 +1,26 @@ -This PR renovates the package group "{{groupName}}". +This {{#if isGitHub}}Pull{{else}}Merge{{/if}} Request renovates the package group "{{groupName}}". + +{{#each upgrades as |upgrade|}} +- [{{upgrade.depName}}]({{upgrade.repositoryUrl}}): from `{{upgrade.currentVersion}}` to `{{upgrade.newVersion}}` +{{/each}} + +### Commits + +{{#each upgrades as |upgrade|}} +<details> +<summary>{{upgrade.githubName}}</summary> + +{{#each upgrade.releases as |release|}} +#### {{release.version}} +{{#each release.commits as |commit|}} +- [`{{commit.shortSha}}`]({{commit.url}}){{commit.message}} +{{/each}} +{{/each}} + +</details> +{{/each}} + +<br /> +<br /> + +This PR has been autogenerated by [Renovate Bot](https://keylocation.sg/our-tech/renovate). diff --git a/lib/config/templates/pr-body.hbs b/lib/config/templates/pr-body.hbs index 7d4a3e29713bb1f5403e00741c352855bf6df6c6..c08253626ad3f055c43e2be793b9fb49e5f7ab82 100644 --- a/lib/config/templates/pr-body.hbs +++ b/lib/config/templates/pr-body.hbs @@ -1,3 +1,20 @@ -This Pull Request updates dependency {{depName}} from version `{{currentVersion}}` to `{{newVersion}}` +This {{#if isGitHub}}Pull{{else}}Merge{{/if}} Request updates dependency [{{depName}}]({{repositoryUrl}}) from version `{{currentVersion}}` to `{{newVersion}}` -{{changelog}} +### Commits + +<details> +<summary>{{githubName}}</summary> + +{{#each releases as |release|}} +#### {{release.version}} +{{#each release.commits as |commit|}} +- [`{{commit.shortSha}}`]({{commit.url}}){{commit.message}} +{{/each}} +{{/each}} + +</details> + +<br /> +<br /> + +This PR has been autogenerated by [Renovate Bot](https://keylocation.sg/our-tech/renovate). diff --git a/lib/helpers/changelog.js b/lib/helpers/changelog.js index 8f5b889e7647ac3eb287eba5fa224adb4e4ade3f..f8ab2f1800eb76852055d323c75e9575012c673c 100644 --- a/lib/helpers/changelog.js +++ b/lib/helpers/changelog.js @@ -1,33 +1,41 @@ const logger = require('winston'); const changelog = require('changelog'); -module.exports = getChangeLog; +module.exports = { + getChangeLogJSON, + getMarkdown, + getChangeLog, +}; -// Get Changelog -async function getChangeLog(depName, fromVersion, newVersion) { - logger.debug(`getChangeLog(${depName}, ${fromVersion}, ${newVersion})`); +async function getChangeLogJSON(depName, fromVersion, newVersion) { + logger.debug(`getChangeLogJSON(${depName}, ${fromVersion}, ${newVersion})`); if (!fromVersion || fromVersion === newVersion) { - return ''; + return null; } const semverString = `>${fromVersion} <=${newVersion}`; logger.debug(`semverString: ${semverString}`); - let markdownLog; try { - const log = await changelog.generate(depName, semverString); - // We should probably write our own markdown generation here: - markdownLog = changelog.markdown(log); - } catch (error) { - logger.verbose(`getChangeLog error: ${error}`); + return await changelog.generate(depName, semverString); + } catch (err) { + logger.verbose(`getChangeLogJSON error: ${JSON.stringify(err)}`); + return null; } - // Add a header if log exists - if (!markdownLog) { - logger.verbose(`No changelog for ${depName}`); - markdownLog = ''; - } else { - markdownLog = `### Changelog\n\n${markdownLog}`; - // Fix up the markdown formatting of changelog - // This is needed for GitLab in particular - markdownLog = markdownLog.replace(/(.*?)\n[=]{10,}/g, '#### $1'); +} + +function getMarkdown(changelogJSON) { + if (!changelogJSON) { + return 'No changelog available'; } + let markdownLog = changelog.markdown(changelogJSON); + markdownLog = `### Changelog\n\n${markdownLog}`; + // Fix up the markdown formatting of changelog + // This is needed for GitLab in particular + markdownLog = markdownLog.replace(/(.*?)\n[=]{10,}/g, '#### $1'); return markdownLog; } + +// Get Changelog +async function getChangeLog(depName, fromVersion, newVersion) { + const logJSON = await getChangeLogJSON(depName, fromVersion, newVersion); + return getMarkdown(logJSON); +} diff --git a/lib/worker.js b/lib/worker.js index e086fb64ad22fb5bae8c3280428c22424b1dff77..05a46431d7f23d0c3040aaa4cfd3a81c4e1f8697 100644 --- a/lib/worker.js +++ b/lib/worker.js @@ -113,7 +113,6 @@ function assignDepConfigs(inputConfig, deps) { delete returnDep.config.enabled; delete returnDep.config.onboarding; delete returnDep.config.endpoint; - delete returnDep.config.platform; delete returnDep.config.autodiscover; delete returnDep.config.token; delete returnDep.config.githubAppId; diff --git a/lib/workers/pr.js b/lib/workers/pr.js index bb60693091b1e78ac9baabc47ca64c17c6368472..f905df180edc442d3519ad6cddd8a89a262e9a11 100644 --- a/lib/workers/pr.js +++ b/lib/workers/pr.js @@ -1,6 +1,6 @@ const logger = require('winston'); const handlebars = require('handlebars'); -const getChangeLog = require('../helpers/changelog'); +const changelogHelper = require('../helpers/changelog'); module.exports = { ensurePr, @@ -10,9 +10,9 @@ module.exports = { // Ensures that PR exists with matching title/body async function ensurePr(upgrades) { logger.debug(`ensurePr(${JSON.stringify(upgrades)})`); - const upgradeConfig = upgrades[0]; - const config = Object.assign({}, upgradeConfig); - logger.debug('Ensuring PR'); + // If there is a group, it will use the config of the first upgrade in the array + const config = Object.assign({}, upgrades[0]); + config.upgrades = upgrades; const branchName = handlebars.compile(config.branchName)(config); const branchStatus = await config.api.getBranchStatus(branchName); @@ -43,23 +43,41 @@ async function ensurePr(upgrades) { } // Get changelog and then generate template strings - config.changelogs = []; for (const upgrade of upgrades) { - let log = await getChangeLog( + const logJSON = await changelogHelper.getChangeLogJSON( upgrade.depName, upgrade.changeLogFromVersion, upgrade.changeLogToVersion ); - if (!(log && log.length)) { - log = 'No changelog available'; - } - config.changelogs.push({ - depName: upgrade.depName, - log, + // Store changelog markdown for backwards compatibility + config.changelog = config.changelog || changelogHelper.getMarkdown(logJSON); + upgrade.repositoryUrl = logJSON.project.repository; + upgrade.githubName = logJSON.project.github; + upgrade.releases = []; + logJSON.versions.forEach(version => { + const release = Object.assign({}, version); + release.date = version.date.toISOString().slice(0, 10); + release.commits = []; + if (release.changes) { + release.changes.forEach(change => { + const commit = Object.assign({}, change); + delete commit.date; + commit.shortSha = change.sha.slice(0, 7); + commit.url = `${logJSON.project.repository}/commit/${change.sha}`; + if (change.message) { + commit.message = change.message.split('\n')[0]; + } + release.commits.push(commit); + }); + } + upgrade.releases.push(release); }); } - // Configure changelog for backwards compatibility - config.changelog = config.changelogs[0].log; + + // Update the config object + Object.assign(config, upgrades[0]); + config.isGitHub = config.platform === 'github'; + const prTitle = handlebars.compile(config.prTitle)(config); const prBody = handlebars.compile(config.prBody)(config); diff --git a/test/helpers/changelog.spec.js b/test/helpers/changelog.spec.js index db357670581506982ce27cb3d0eebe09c278f184..4eb6d6ed3a59c1ea0211b2e35cb082fa7d478cdd 100644 --- a/test/helpers/changelog.spec.js +++ b/test/helpers/changelog.spec.js @@ -1,31 +1,40 @@ const changelog = require('changelog'); -const getChangeLog = require('../../lib/helpers/changelog'); +const changelogHelper = require('../../lib/helpers/changelog'); jest.mock('changelog'); describe('helpers/changelog', () => { - describe('getChangeLog(depName, fromVersion, newVersion)', () => { + describe('changelogHelper.getChangeLog(depName, fromVersion, newVersion)', () => { it('returns empty if no fromVersion', async () => { - expect(await getChangeLog('renovate', null, '1.0.0')).toBe(''); + expect( + await changelogHelper.getChangeLog('renovate', null, '1.0.0') + ).toBe('No changelog available'); }); it('returns empty if fromVersion equals newVersion', async () => { - expect(await getChangeLog('renovate', '1.0.0', '1.0.0')).toBe(''); + expect( + await changelogHelper.getChangeLog('renovate', '1.0.0', '1.0.0') + ).toBe('No changelog available'); }); - it('returns empty if generated markdown is null', async () => { - changelog.markdown.mockReturnValueOnce(null); - expect(await getChangeLog('renovate', '1.0.0', '2.0.0')).toBe(''); + it('returns empty if generated json is null', async () => { + changelog.generate.mockReturnValueOnce(null); + expect( + await changelogHelper.getChangeLog('renovate', '1.0.0', '2.0.0') + ).toBe('No changelog available'); }); it('returns header if generated markdown is valid', async () => { + changelog.generate.mockReturnValueOnce({}); changelog.markdown.mockReturnValueOnce('dummy'); - expect(await getChangeLog('renovate', '1.0.0', '2.0.0')).toBe( - '### Changelog\n\ndummy' - ); + expect( + await changelogHelper.getChangeLog('renovate', '1.0.0', '2.0.0') + ).toBe('### Changelog\n\ndummy'); }); it('returns empty if error thrown', async () => { - changelog.markdown = jest.fn(() => { + changelog.generate = jest.fn(() => { throw new Error('foo'); }); - expect(await getChangeLog('renovate', '1.0.0', '2.0.0')).toBe(''); + expect( + await changelogHelper.getChangeLog('renovate', '1.0.0', '2.0.0') + ).toBe('No changelog available'); }); }); }); diff --git a/test/workers/pr.spec.js b/test/workers/pr.spec.js index 752a40d3c5709ad320e28b8bdab2acd00a66fe5a..c183da0850f0f00aec0ebebaa622d5fa0b96ceaa 100644 --- a/test/workers/pr.spec.js +++ b/test/workers/pr.spec.js @@ -1,11 +1,33 @@ const logger = require('winston'); const prWorker = require('../../lib/workers/pr'); +const changelogHelper = require('../../lib/helpers/changelog'); const defaultConfig = require('../../lib/config/defaults').getConfig(); logger.remove(logger.transports.Console); -const getChangeLog = jest.fn(); -getChangeLog.mockReturnValue('Mocked changelog'); +jest.mock('../../lib/helpers/changelog'); +changelogHelper.getChangeLog = jest.fn(); +changelogHelper.getChangeLog.mockReturnValue('Mocked changelog'); +changelogHelper.getChangeLogJSON = jest.fn(); +changelogHelper.getChangeLogJSON.mockReturnValue({ + project: { + github: 'renovateapp/dummy', + repository: 'https://github.com/renovateapp/dummy', + }, + versions: [ + { + date: new Date('2017-01-01'), + version: '1.1.0', + changes: [ + { + date: new Date('2017-01-01'), + sha: 'abcdefghijklmnopqrstuvwxyz', + message: 'foo\nbar', + }, + ], + }, + ], +}); describe('workers/pr', () => { describe('checkAutoMerge(pr, config)', () => { @@ -66,8 +88,22 @@ describe('workers/pr', () => { }; existingPr = { title: 'Update dependency dummy to version 1.1.0', - body: - 'This Pull Request updates dependency dummy from version `1.0.0` to `1.1.0`\n\nNo changelog available', + body: `This Pull Request updates dependency [dummy](https://github.com/renovateapp/dummy) from version \`1.0.0\` to \`1.1.0\` + +### Commits + +<details> +<summary>renovateapp/dummy</summary> + +#### 1.1.0 +- [\`abcdefg\`](https://github.com/renovateapp/dummy/commit/abcdefghijklmnopqrstuvwxyz)foo + +</details> + +<br /> +<br /> + +This PR has been autogenerated by [Renovate Bot](https://keylocation.sg/our-tech/renovate).`, displayNumber: 'Existing PR', }; }); @@ -155,6 +191,7 @@ describe('workers/pr', () => { config.api.getBranchPr = jest.fn(() => existingPr); config.api.updatePr = jest.fn(); const pr = await prWorker.ensurePr([config]); + expect(config.api.updatePr.mock.calls.length).toBe(0); expect(pr).toMatchObject(existingPr); }); it('should return modified existing PR', async () => {