diff --git a/lib/platform/github/index.js b/lib/platform/github/index.js index f392f5cfba0a6de91f0d378600a95ee6cd32b5f7..4cd650173f4d8efe7ffc50835eddbf0798652f9c 100644 --- a/lib/platform/github/index.js +++ b/lib/platform/github/index.js @@ -1,6 +1,7 @@ const is = require('@sindresorhus/is'); const addrs = require('email-addresses'); const delay = require('delay'); +const showdown = require('showdown'); const URL = require('url'); const get = require('./gh-got-wrapper'); @@ -8,6 +9,9 @@ const hostRules = require('../../util/host-rules'); const Storage = require('./storage'); const GitStorage = require('../git/storage'); +const converter = new showdown.Converter(); +converter.setFlavor('github'); + let config = {}; module.exports = { @@ -1294,9 +1298,13 @@ function getPrBody(input) { return input.substring(0, 60000); } return ( - input + converter + .makeHtml(input) // to be safe, replace all github.com links with renovatebot redirector - .replace(/]\(https:\/\/github\.com\//g, '](https://renovatebot.com/gh/') + .replace( + /href="https?:\/\/github.com\//g, + 'href="https://renovatebot.com/gh/' + ) .substring(0, 60000) ); } diff --git a/lib/platform/gitlab/index.js b/lib/platform/gitlab/index.js index e7d8c8ece663b7e549a5f200d8bf5999e2b5c130..63efaf6554db96480fbf7cbbf160358bb6b6a4c6 100644 --- a/lib/platform/gitlab/index.js +++ b/lib/platform/gitlab/index.js @@ -1,9 +1,13 @@ const is = require('@sindresorhus/is'); const addrs = require('email-addresses'); +const showdown = require('showdown'); const get = require('./gl-got-wrapper'); const hostRules = require('../../util/host-rules'); +const converter = new showdown.Converter(); +converter.setFlavor('github'); + let config = {}; module.exports = { @@ -723,7 +727,17 @@ async function mergePr(iid) { } function getPrBody(input) { - return input.replace(/Pull Request/g, 'Merge Request').replace(/PR/g, 'MR'); + // Convert to HTML using GitHub-flavoured markdown as it is more feature-rich than GitLab's flavour + return converter + .makeHtml(input) + .replace(/Pull Request/g, 'Merge Request') + .replace(/PR/g, 'MR') + .replace( + `<p><details><br />\n<summary>Release Notes</summary></p>`, + '\n<details>\n\n<summary>Release Notes</summary>\n\n' + ) + .replace('<p></details></p>', '\n</details>\n'); + // TODO: set maximum length } // Generic File operations diff --git a/lib/workers/pr/pr-body.js b/lib/workers/pr/pr-body.js index a5d473c42fb1f60bca4164b9adc898be4ab4515d..ad164a4bcf5e6e7a670ead0b168f5198c8976ec2 100644 --- a/lib/workers/pr/pr-body.js +++ b/lib/workers/pr/pr-body.js @@ -194,11 +194,26 @@ async function getPrBody(config) { } } prBody = prBody.trim(); - prBody = prBody.replace(/\n\n\n+/g, '\n\n'); // Clean up double v's prBody = prBody.replace(/\bvv(\d)/g, 'v$1'); - // Get platform-specific transformations + + // Generic replacements/link-breakers + + // Put a zero width space after every # followed by a digit + prBody = prBody.replace(/#(\d)/gi, '#​$1'); + // Put a zero width space after every @ symbol to prevent unintended hyperlinking + prBody = prBody.replace(/@/g, '@​'); + prBody = prBody.replace(/(`\[?@)​/g, '$1'); + prBody = prBody.replace(/([a-z]@)​/gi, '$1'); + prBody = prBody.replace(/([\s(])#(\d+)([)\s]?)/g, '$1#​$2$3'); + // convert escaped backticks back to ` + const backTickRe = /`([^/]*?)`/g; + prBody = prBody.replace(backTickRe, '`$1`'); + prBody = prBody.replace(/`#​(\d+)`/g, '`#$1`'); + + prBody = prBody.replace(/\n\n\n+/g, '\n\n'); + prBody = platform.getPrBody(prBody); return prBody; } diff --git a/package.json b/package.json index a4650fa3251f01e507598448f7c7cfaba4588139..e9970644ff6def862eb0d47a4d831861d002b0f0 100644 --- a/package.json +++ b/package.json @@ -111,6 +111,7 @@ "semver": "5.5.1", "semver-stable": "2.0.4", "semver-utils": "1.1.2", + "showdown": "1.8.6", "simple-git": "1.102.0", "slugify": "1.3.1", "traverse": "0.6.6", diff --git a/test/platform/github/__snapshots__/index.spec.js.snap b/test/platform/github/__snapshots__/index.spec.js.snap index 11b8bb8a9d70a6d9c6326ce5779de5362b6c7381..7920ea6181fa073c8315fba384c479fc24908779 100644 --- a/test/platform/github/__snapshots__/index.spec.js.snap +++ b/test/platform/github/__snapshots__/index.spec.js.snap @@ -444,7 +444,7 @@ Object { } `; -exports[`platform/github getPrBody(input) returns updated pr body 1`] = `"https://github.com/foo/bar/issues/5 plus also [a link](https://renovatebot.com/gh/foo/bar/issues/5)"`; +exports[`platform/github getPrBody(input) returns updated pr body 1`] = `"<p><a href=\\"https://renovatebot.com/gh/foo/bar/issues/5\\">https://github.com/foo/bar/issues/5</a> plus also <a href=\\"https://renovatebot.com/gh/foo/bar/issues/5\\">a link</a></p>"`; exports[`platform/github getPrFiles() returns files 1`] = ` Array [ diff --git a/test/platform/gitlab/__snapshots__/index.spec.js.snap b/test/platform/gitlab/__snapshots__/index.spec.js.snap index 9b4783ef58705f17edbc315c7aaaa4f0e7dd0387..da1e9ba0e964547692a566b66ae5df25701b5487 100644 --- a/test/platform/gitlab/__snapshots__/index.spec.js.snap +++ b/test/platform/gitlab/__snapshots__/index.spec.js.snap @@ -243,7 +243,7 @@ Object { } `; -exports[`platform/gitlab getPrBody(input) returns updated pr body 1`] = `"https://github.com/foo/bar/issues/5 plus also [a link](https://github.com/foo/bar/issues/5)"`; +exports[`platform/gitlab getPrBody(input) returns updated pr body 1`] = `"<p><a href=\\"https://github.com/foo/bar/issues/5\\">https://github.com/foo/bar/issues/5</a> plus also <a href=\\"https://github.com/foo/bar/issues/5\\">a link</a></p>"`; exports[`platform/gitlab getPrFiles() returns files 1`] = ` Array [ diff --git a/yarn.lock b/yarn.lock index 645876d620a97f72b63ecbabd1342cd844ab32f8..6b13415a49b0dbd5c5b1bf0ff1225cde8399a688 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5144,6 +5144,7 @@ npm@6.4.1: cmd-shim "~2.0.2" columnify "~1.5.4" config-chain "~1.1.11" + debuglog "*" detect-indent "~5.0.0" detect-newline "^2.1.0" dezalgo "~1.0.3" @@ -5158,6 +5159,7 @@ npm@6.4.1: has-unicode "~2.0.1" hosted-git-info "^2.7.1" iferr "^1.0.2" + imurmurhash "*" inflight "~1.0.6" inherits "~2.0.3" ini "^1.3.5" @@ -5170,8 +5172,14 @@ npm@6.4.1: libnpx "^10.2.0" lock-verify "^2.0.2" lockfile "^1.0.4" + lodash._baseindexof "*" lodash._baseuniq "~4.6.0" + lodash._bindcallback "*" + lodash._cacheindexof "*" + lodash._createcache "*" + lodash._getnative "*" lodash.clonedeep "~4.5.0" + lodash.restparam "*" lodash.union "~4.6.0" lodash.uniq "~4.5.0" lodash.without "~4.4.0" @@ -5210,6 +5218,7 @@ npm@6.4.1: read-package-json "^2.0.13" read-package-tree "^5.2.1" readable-stream "^2.3.6" + readdir-scoped-modules "*" request "^2.88.0" retry "^0.12.0" rimraf "~2.6.2" @@ -6691,6 +6700,12 @@ shellwords@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" +showdown@1.8.6: + version "1.8.6" + resolved "https://registry.yarnpkg.com/showdown/-/showdown-1.8.6.tgz#91ea4ee3b7a5448aaca6820a4e27e690c6ad771c" + dependencies: + yargs "^10.0.3" + signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" @@ -7826,12 +7841,35 @@ yargs-parser@^10.1.0: dependencies: camelcase "^4.1.0" +yargs-parser@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950" + dependencies: + camelcase "^4.1.0" + yargs-parser@^9.0.2: version "9.0.2" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-9.0.2.tgz#9ccf6a43460fe4ed40a9bb68f48d43b8a68cc077" dependencies: camelcase "^4.1.0" +yargs@^10.0.3: + version "10.1.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-10.1.2.tgz#454d074c2b16a51a43e2fb7807e4f9de69ccb5c5" + dependencies: + cliui "^4.0.0" + decamelize "^1.1.1" + find-up "^2.1.0" + get-caller-file "^1.0.1" + os-locale "^2.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1" + yargs-parser "^8.1.0" + yargs@^11.0.0: version "11.0.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-11.0.0.tgz#c052931006c5eee74610e5fc0354bedfd08a201b"