diff --git a/lib/workers/pr/changelog/index.js b/lib/workers/pr/changelog/index.js
index bfea732692250ba7c4b2289bec25699fb5d97a2f..422f4aa94326df5de9c03588410f5da9ccf767c5 100644
--- a/lib/workers/pr/changelog/index.js
+++ b/lib/workers/pr/changelog/index.js
@@ -3,26 +3,30 @@ const { addReleaseNotes } = require('../release-notes');
 const sourceCache = require('./source-cache');
 const sourceGithub = require('./source-github');
 
+const managerNpm = require('./manager-npm');
+
 module.exports = {
   getChangeLogJSON,
 };
 
-async function getChangeLogJSON(depName, fromVersion, newVersion) {
-  logger.debug(`getChangeLogJSON(${depName}, ${fromVersion}, ${newVersion})`);
+async function getChangeLogJSON(args) {
+  const { manager, fromVersion, newVersion } = args;
+  logger.debug({ args }, `getChangeLogJSON(args)`);
   if (!fromVersion || fromVersion === newVersion) {
     return null;
   }
-
-  const args = [depName, fromVersion, newVersion];
-
   // Return from cache if present
-  let res = await sourceCache.getChangeLogJSON(...args);
+  let res = await sourceCache.getChangeLogJSON(args);
   if (res) {
     return addReleaseNotes(res);
   }
+  let pkg = null;
+  if (['npm', 'meteor'].includes(manager)) {
+    pkg = await managerNpm.getPackage(args);
+  }
 
-  res = await sourceGithub.getChangeLogJSON(...args);
+  res = await sourceGithub.getChangeLogJSON({ ...args, ...pkg });
 
-  await sourceCache.setChangeLogJSON(...args, res);
+  await sourceCache.setChangeLogJSON(args, res);
   return addReleaseNotes(res);
 }
diff --git a/lib/workers/pr/changelog/manager-npm.js b/lib/workers/pr/changelog/manager-npm.js
new file mode 100644
index 0000000000000000000000000000000000000000..6fe3da21701068b6ad66ddd366412c84b65142f8
--- /dev/null
+++ b/lib/workers/pr/changelog/manager-npm.js
@@ -0,0 +1,27 @@
+const npmRegistry = require('../../../datasource/npm');
+const { semverSort } = require('../../../util/semver');
+
+module.exports = {
+  getPackage,
+};
+
+async function getPackage({ depName, depType }) {
+  if (depType === 'engines') {
+    return null;
+  }
+  const dep = await npmRegistry.getDependency(depName);
+  if (!dep) {
+    return null;
+  }
+  const releases = Object.keys(dep.versions);
+  releases.sort(semverSort);
+  const versions = releases.map(release => ({
+    version: release,
+    date: dep.versions[release].time,
+    gitHead: dep.versions[release].gitHead,
+  }));
+  return {
+    repositoryUrl: dep.repositoryUrl,
+    versions,
+  };
+}
diff --git a/lib/workers/pr/changelog/source-cache.js b/lib/workers/pr/changelog/source-cache.js
index b8b2fb195c927585080e7ed584cb0f5f6d46b17c..bcfee3d78bd60a046cfe6b4ccb22b043b11e6561 100644
--- a/lib/workers/pr/changelog/source-cache.js
+++ b/lib/workers/pr/changelog/source-cache.js
@@ -7,15 +7,16 @@ module.exports = {
   rmAllCache,
 };
 
-function getCache(depName, fromVersion, newVersion) {
+function getCache({ depName, fromVersion, newVersion }) {
   const tmpdir = process.env.RENOVATE_TMPDIR || os.tmpdir();
   const cachePath = tmpdir + '/renovate-commits-cache';
   const cacheKey = `${depName}-${fromVersion}-${newVersion}`;
   return [cachePath, cacheKey];
 }
 
-async function getChangeLogJSON(depName, fromVersion, newVersion) {
-  const cache = getCache(depName, fromVersion, newVersion);
+async function getChangeLogJSON(args) {
+  const cache = getCache(args);
+  const { depName } = args;
   try {
     const cacheVal = await cacache.get(...cache);
     logger.trace(`Returning cached version of ${depName}`);
@@ -27,12 +28,12 @@ async function getChangeLogJSON(depName, fromVersion, newVersion) {
   }
 }
 
-async function setChangeLogJSON(depName, fromVersion, newVersion, res) {
-  const cache = getCache(depName, fromVersion, newVersion);
+async function setChangeLogJSON(args, res) {
+  const cache = getCache(args);
   await cacache.put(...cache, JSON.stringify(res));
 }
 
 async function rmAllCache() {
-  const cache = getCache();
+  const cache = getCache({});
   await cacache.rm.all(cache[0]);
 }
diff --git a/lib/workers/pr/changelog/source-github.js b/lib/workers/pr/changelog/source-github.js
index 740adb23a94d71850caa965f4228a556600fa953..de39aecdea5200b47a6623a78c3a2a74b6e09f9f 100644
--- a/lib/workers/pr/changelog/source-github.js
+++ b/lib/workers/pr/changelog/source-github.js
@@ -1,9 +1,4 @@
-const npmRegistry = require('../../../datasource/npm');
-const {
-  matchesSemver,
-  isPinnedVersion,
-  semverSort,
-} = require('../../../util/semver');
+const { matchesSemver, isPinnedVersion } = require('../../../util/semver');
 const ghGot = require('../../../platform/github/gh-got-wrapper');
 
 module.exports = {
@@ -47,39 +42,37 @@ async function getRepositoryHead(repository, version) {
   if (version.gitHead) {
     return version.gitHead;
   }
-  if (!version.time) {
+  if (!version.date) {
     return null;
   }
-  logger.info({ repository, version }, 'Looking for commit SHA by time');
+  logger.trace({ repository, version }, 'Looking for commit SHA by date');
   try {
     const res = await ghGot(
-      `https://api.github.com/repos/${repository}/commits/@{${version.time}}`
+      `https://api.github.com/repos/${repository}/commits/@{${version.date}}`
     );
     const commit = res && res.body;
     return commit && commit.sha;
   } catch (err) {
-    logger.debug({ err, repository }, 'Failed to fetch Github commit by time');
+    logger.debug({ err, repository }, 'Failed to fetch Github commit by date');
     return null;
   }
 }
 
-async function getChangeLogJSON(depName, fromVersion, newVersion) {
+async function getChangeLogJSON({
+  repositoryUrl,
+  fromVersion,
+  newVersion,
+  versions,
+}) {
   logger.debug('Checking for github source URL manually');
   const semverString = `>${fromVersion} <=${newVersion}`;
   logger.trace(`semverString: ${semverString}`);
-  const dep = await npmRegistry.getDependency(depName);
-  if (
-    !(
-      dep &&
-      dep.repositoryUrl &&
-      dep.repositoryUrl.startsWith('https://github.com/')
-    )
-  ) {
+  if (!(repositoryUrl && repositoryUrl.startsWith('https://github.com/'))) {
     logger.debug('No repo found manually');
     return null;
   }
-  logger.info({ url: dep.repositoryUrl }, 'Found github URL manually');
-  const repository = dep.repositoryUrl
+  logger.debug({ url: repositoryUrl }, 'Found github URL manually');
+  const repository = repositoryUrl
     .replace('https://github.com/', '')
     .replace(/#.*/, '');
   if (repository.split('/').length !== 2) {
@@ -88,25 +81,24 @@ async function getChangeLogJSON(depName, fromVersion, newVersion) {
   }
 
   const tags = await getTags(repository);
-  const releases = Object.keys(dep.versions);
-  releases.sort(semverSort);
 
-  function getHead(name) {
+  function getHead(version) {
     return getRepositoryHead(repository, {
-      ...dep.versions[name],
-      ...tags[name],
+      ...version,
+      ...tags[version.version],
     });
   }
 
-  const versions = [];
+  const releases = [];
+
   // compare versions
-  for (let i = 1; i < releases.length; i += 1) {
-    const prev = releases[i - 1];
-    const next = releases[i];
-    if (matchesSemver(next, semverString)) {
-      const version = {
-        version: next,
-        date: dep.versions[next].time,
+  for (let i = 1; i < versions.length; i += 1) {
+    const prev = versions[i - 1];
+    const next = versions[i];
+    if (matchesSemver(next.version, semverString)) {
+      const release = {
+        version: next.version,
+        date: next.date,
         // put empty changes so that existing templates won't break
         changes: [],
         compare: {},
@@ -114,18 +106,18 @@ async function getChangeLogJSON(depName, fromVersion, newVersion) {
       const prevHead = await getHead(prev);
       const nextHead = await getHead(next);
       if (prevHead && nextHead) {
-        version.compare.url = `https://github.com/${repository}/compare/${prevHead}...${nextHead}`;
+        release.compare.url = `https://github.com/${repository}/compare/${prevHead}...${nextHead}`;
       }
-      versions.unshift(version);
+      releases.unshift(release);
     }
   }
 
   const res = {
     project: {
       github: repository,
-      repository: dep.repositoryUrl,
+      repository: repositoryUrl,
     },
-    versions,
+    versions: releases,
   };
 
   logger.debug({ res }, 'Manual res');
diff --git a/lib/workers/pr/index.js b/lib/workers/pr/index.js
index 964fa43811e0c68b2fdf06fca7a16e206202b419..fa8fc11ad7114b9bb7d8a9238ab2ffd577bbbd24 100644
--- a/lib/workers/pr/index.js
+++ b/lib/workers/pr/index.js
@@ -123,19 +123,14 @@ async function ensurePr(prConfig) {
     }
     processedUpgrades.push(upgradeKey);
 
-    let logJSON;
-    if (
-      upgrade.manager !== 'travis' &&
-      upgrade.manager !== 'nvm' &&
-      upgrade.manager !== 'circleci' &&
-      upgrade.depType !== 'engines'
-    ) {
-      logJSON = await changelogHelper.getChangeLogJSON(
-        upgrade.depName,
-        upgrade.changeLogFromVersion,
-        upgrade.changeLogToVersion
-      );
-    }
+    const logJSON = await changelogHelper.getChangeLogJSON({
+      manager: upgrade.manager,
+      depType: upgrade.depType,
+      depName: upgrade.depName,
+      fromVersion: upgrade.changeLogFromVersion,
+      newVersion: upgrade.changeLogToVersion,
+    });
+
     if (logJSON) {
       upgrade.githubName = logJSON.project.github;
       upgrade.hasReleaseNotes = logJSON.hasReleaseNotes;
diff --git a/test/workers/pr/changelog.spec.js b/test/workers/pr/changelog.spec.js
index d2f62e88307c7500fe19c9216d298f1c139b5a66..5db1ba5bb64eb482ff969737cc3f88b04dae017c 100644
--- a/test/workers/pr/changelog.spec.js
+++ b/test/workers/pr/changelog.spec.js
@@ -9,6 +9,13 @@ const {
   rmAllCache,
 } = require('../../../lib/workers/pr/changelog/source-cache');
 
+const upgrade = {
+  manager: 'npm',
+  depName: 'renovate',
+  fromVersion: '1.0.0',
+  newVersion: '3.0.0',
+};
+
 function npmResponse() {
   return {
     repositoryUrl: 'https://github.com/chalk/chalk',
@@ -36,18 +43,29 @@ describe('workers/pr/changelog', () => {
       await rmAllCache();
     });
     it('returns null if no fromVersion', async () => {
-      expect(await getChangeLogJSON('renovate', null, '1.0.0')).toBe(null);
+      expect(
+        await getChangeLogJSON({
+          ...upgrade,
+          fromVersion: null,
+        })
+      ).toBe(null);
       expect(npmRegistry.getDependency.mock.calls).toHaveLength(0);
       expect(ghGot.mock.calls).toHaveLength(0);
     });
     it('returns null if fromVersion equals newVersion', async () => {
-      expect(await getChangeLogJSON('renovate', '1.0.0', '1.0.0')).toBe(null);
+      expect(
+        await getChangeLogJSON({
+          ...upgrade,
+          fromVersion: '1.0.0',
+          newVersion: '1.0.0',
+        })
+      ).toBe(null);
       expect(ghGot.mock.calls).toHaveLength(0);
     });
     it('logs when no JSON', async () => {
       // clear the mock
       npmRegistry.getDependency.mockReset();
-      expect(await getChangeLogJSON('renovate', '1.0.0', '3.0.0')).toBe(null);
+      expect(await getChangeLogJSON({ ...upgrade })).toBe(null);
     });
     it('skips invalid repos', async () => {
       // clear the mock
@@ -55,13 +73,10 @@ describe('workers/pr/changelog', () => {
       const res = npmResponse();
       res.repositoryUrl = 'https://github.com/about';
       npmRegistry.getDependency.mockReturnValueOnce(Promise.resolve(res));
-
-      expect(await getChangeLogJSON('renovate', '1.0.0', '3.0.0')).toBe(null);
+      expect(await getChangeLogJSON({ ...upgrade })).toBe(null);
     });
     it('works without Github', async () => {
-      expect(
-        await getChangeLogJSON('renovate', '1.0.0', '3.0.0')
-      ).toMatchSnapshot();
+      expect(await getChangeLogJSON({ ...upgrade })).toMatchSnapshot();
     });
     it('uses GitHub tags', async () => {
       ghGot.mockReturnValueOnce(
@@ -76,9 +91,7 @@ describe('workers/pr/changelog', () => {
           ],
         })
       );
-      expect(
-        await getChangeLogJSON('renovate', '1.0.0', '3.0.0')
-      ).toMatchSnapshot();
+      expect(await getChangeLogJSON({ ...upgrade })).toMatchSnapshot();
     });
     it('falls back to commit from release time', async () => {
       // mock tags response
@@ -90,13 +103,16 @@ describe('workers/pr/changelog', () => {
         })
       );
       expect(
-        await getChangeLogJSON('@renovate/no', '1.0.0', '3.0.0')
+        await getChangeLogJSON({
+          ...upgrade,
+          depName: '@renovate/no',
+        })
       ).toMatchSnapshot();
     });
     it('returns cached JSON', async () => {
-      const first = await getChangeLogJSON('renovate', '1.0.0', '3.0.0');
+      const first = await getChangeLogJSON({ ...upgrade });
       npmRegistry.getDependency.mockClear();
-      const second = await getChangeLogJSON('renovate', '1.0.0', '3.0.0');
+      const second = await getChangeLogJSON({ ...upgrade });
       expect(first).toEqual(second);
       expect(npmRegistry.getDependency.mock.calls).toHaveLength(0);
     });
@@ -105,8 +121,16 @@ describe('workers/pr/changelog', () => {
         throw new Error('Unknown Github Repo');
       });
       expect(
-        await getChangeLogJSON('@renovate/no', '1.0.0', '3.0.0')
+        await getChangeLogJSON({
+          ...upgrade,
+          depName: '@renovate/no',
+        })
       ).toMatchSnapshot();
     });
+    it('skips node engines', async () => {
+      expect(await getChangeLogJSON({ ...upgrade, depType: 'engines' })).toBe(
+        null
+      );
+    });
   });
 });