From e6ee360c336e1ec315d527d76cd83e0da0ff9d9f Mon Sep 17 00:00:00 2001 From: hulkoba Date: Mon, 24 Jun 2019 11:47:03 +0200 Subject: [PATCH] feat: add pnpm support --- content/initial-pr.js | 2 +- jobs/github-event/push.js | 1 + lib/create-branch.js | 13 +- lib/get-files.js | 2 + lib/lockfile.js | 3 +- lib/repository-docs.js | 3 +- test/lib/__snapshots__/create-branch.js.snap | 11 +- test/lib/create-branch.js | 181 +++++++++++++++++-- test/lib/get-files.js | 8 +- test/lib/lockfile.js | 6 +- utils/utils.js | 9 +- 11 files changed, 203 insertions(+), 36 deletions(-) diff --git a/content/initial-pr.js b/content/initial-pr.js index d538dc50..ec615f2c 100644 --- a/content/initial-pr.js +++ b/content/initial-pr.js @@ -163,7 +163,7 @@ There is a collection of [frequently asked questions](https://greenkeeper.io/faq // needs to handle files as an array of arrays! function hasLockFileText (files) { if (!files) return - const lockFiles = ['package-lock.json', 'npm-shrinkwrap.json', 'yarn.lock'].filter((key) => { + const lockFiles = ['package-lock.json', 'npm-shrinkwrap.json', 'yarn.lock', 'pnpm-lock.yaml'].filter((key) => { if (_.isArray(files[key]) && files[key].length) { return true } diff --git a/jobs/github-event/push.js b/jobs/github-event/push.js index 5814b125..cd3cd5ec 100644 --- a/jobs/github-event/push.js +++ b/jobs/github-event/push.js @@ -29,6 +29,7 @@ module.exports = async function (data) { 'package.json', 'package-lock.json', 'npm-shrinkwrap.json', + 'pnpm-lock.yaml', 'yarn.lock', 'greenkeeper.json' ] diff --git a/lib/create-branch.js b/lib/create-branch.js index 2e3b23dd..b1bc9445 100644 --- a/lib/create-branch.js +++ b/lib/create-branch.js @@ -106,20 +106,20 @@ module.exports = async ( oldVersion: transform.oldVersion } }) + log.info('starting lockfile update', { lockfilePath, versions }) const oldLockfile = await getGithubFile(ghqueue, { path: lockfilePath, owner, repo: repoName, sha: branch }, log) const oldLockfileContent = Buffer.from(oldLockfile.content, 'base64').toString() - const isNpm = lockfilePath.includes('package-lock.json') try { const { tokens, 'token-audits': tokenAudits } = await dbs() // eslint-disable-line /* This is the structure of the tokens 'model' _id: `${accountId} - tokens: { - ${repoId}: { + tokens: { + ${repoId}: { npm: ${token}, github: ${token} } @@ -141,7 +141,6 @@ module.exports = async ( if (repositoryTokens) { execTokens = JSON.stringify(repositoryTokens.tokens[repoDoc._id]) const datetime = new Date().toISOString().substr(0, 19).replace(/[^0-9]/g, '') - // write audit log entry to 'token-audits' db // log entry type: 'read' try { @@ -154,7 +153,11 @@ module.exports = async ( } } - const { ok, contents, error } = await getNewLockfile({ packageJson: commit.content, lock: oldLockfileContent, isNpm, repositoryTokens: execTokens }) + let type = 'npm' + if (lockfilePath.includes('pnpm-lock.yaml')) type = 'pnpm' + if (lockfilePath.includes('yarn.lock')) type = 'yarn' + + const { ok, contents, error } = await getNewLockfile({ packageJson: commit.content, lock: oldLockfileContent, type, repositoryTokens: execTokens }) if (ok) { // !ok means the old and new lockfile are the same, so we don’t make a commit log.info(`new lockfile contents for ${lockfilePath} received`) diff --git a/lib/get-files.js b/lib/get-files.js index 749bbf23..438bda3d 100644 --- a/lib/get-files.js +++ b/lib/get-files.js @@ -7,6 +7,7 @@ const fileList = [ 'package.json', 'package-lock.json', 'npm-shrinkwrap.json', + 'pnpm-lock.yaml', 'yarn.lock' ] @@ -92,6 +93,7 @@ function addRelatedLockfilePaths (files) { files.map(path => { relatedLockfilePaths.push(path.replace('package.json', 'package-lock.json')) relatedLockfilePaths.push(path.replace('package.json', 'yarn.lock')) + relatedLockfilePaths.push(path.replace('package.json', 'pnpm-lock.yaml')) relatedLockfilePaths.push(path.replace('package.json', 'npm-shrinkwrap.json')) }) return relatedLockfilePaths diff --git a/lib/lockfile.js b/lib/lockfile.js index 3e5f482d..5c009b08 100644 --- a/lib/lockfile.js +++ b/lib/lockfile.js @@ -40,7 +40,7 @@ async function findNextServer () { }) return sortedServers[0] } -async function getNewLockfile ({ packageJson, lock, isNpm, repositoryTokens }) { +async function getNewLockfile ({ packageJson, lock, type, repositoryTokens }) { const logs = dbs.getLogsDb() const log = Log({ logsDb: logs, @@ -48,7 +48,6 @@ async function getNewLockfile ({ packageJson, lock, isNpm, repositoryTokens }) { repoSlug: null, context: 'lockfile' }) - const type = isNpm ? 'npm' : 'yarn' const nextServer = await findNextServer() jobCountByServer[nextServer] = jobCountByServer[nextServer] ? jobCountByServer[nextServer] + 1 : 1 return promiseRetry((retry, number) => { diff --git a/lib/repository-docs.js b/lib/repository-docs.js index d52a9382..88e307d2 100644 --- a/lib/repository-docs.js +++ b/lib/repository-docs.js @@ -38,7 +38,8 @@ async function updateRepoDoc ({ installationId, doc, filePaths, log }) { 'package.json': [], 'package-lock.json': [], 'yarn.lock': [], - 'npm-shrinkwrap.json': [] + 'npm-shrinkwrap.json': [], + 'pnpm-lock.yaml': [] } let filePathsFromConfig = [] diff --git a/test/lib/__snapshots__/create-branch.js.snap b/test/lib/__snapshots__/create-branch.js.snap index 6a062696..39278c16 100644 --- a/test/lib/__snapshots__/create-branch.js.snap +++ b/test/lib/__snapshots__/create-branch.js.snap @@ -1,6 +1,15 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`create branch with lockfiles change one file (package.json) and generate its lockfile (old syntax) 1`] = ` +exports[`create branch with lockfiles change a package.json and generate its lockfile for pnpm 1`] = ` +Object { + "lock": "{\\"devDependencies\\":{\\"jest\\":\\"1.1.1\\"}}", + "packageJson": "{\\"devDependencies\\":{\\"jest\\":\\"1.2.0\\"}}", + "repositoryTokens": "", + "type": "pnpm", +} +`; + +exports[`create branch with lockfiles change one file (package.json) and generate its lockfile 1`] = ` Object { "lock": "{\\"devDependencies\\":{\\"jest\\":\\"1.1.1\\"}}", "packageJson": "{\\"devDependencies\\":{\\"jest\\":\\"1.2.0\\"}}", diff --git a/test/lib/create-branch.js b/test/lib/create-branch.js index 991a5bbe..e4f420b9 100644 --- a/test/lib/create-branch.js +++ b/test/lib/create-branch.js @@ -768,8 +768,8 @@ describe('create branch', async () => { }) }) -describe('create branch with lockfiles', async () => { - test('change one file (package.json) and generate its lockfile (old syntax)', async () => { +describe('create branch with lockfiles', () => { + test('change one file (package.json) and generate its lockfile', async () => { const packageFileContents = { devDependencies: { 'jest': '1.1.1' } } @@ -889,10 +889,11 @@ describe('create branch with lockfiles', async () => { fullName: 'finnp/one-lockfile-old-syntax', private: false, files: { - 'package.json': true, - 'package-lock.json': true, - 'npm-shrinkwrap.json': false, - 'yarn.lock': false + 'package.json': ['package.json'], + 'package-lock.json': ['package-lock.json'], + 'npm-shrinkwrap.json': [], + 'yarn.lock': [], + 'pnpm-lock.yaml': [] }, packages: { 'package.json': { @@ -1112,7 +1113,8 @@ describe('create branch with lockfiles', async () => { 'package.json': ['package.json', 'frontend/package.json'], 'package-lock.json': ['package-lock.json', 'frontend/package-lock.json'], 'npm-shrinkwrap.json': [], - 'yarn.lock': [] + 'yarn.lock': [], + 'pnpm-lock.yaml': [] }, packages: { 'package.json': { @@ -1133,6 +1135,150 @@ describe('create branch with lockfiles', async () => { expect(gitHubNock.isDone()).toBeTruthy() }) + test('change a package.json and generate its lockfile for pnpm', async () => { + const packageFileContents = { devDependencies: { + 'jest': '1.1.1' + } } + const updatedPackageFileContents = { devDependencies: { + 'jest': '1.2.0' + } } + const gitHubNock = nock('https://api.github.com') + .post('/app/installations/123/access_tokens') + .optionally() + .reply(200, { + token: 'secret' + }) + .get('/rate_limit') + .optionally() + .reply(200) + .get('/repos/owner/repo/contents/package.json') + .query({ ref: 'master' }) + .reply(200, { + type: 'file', + content: Buffer.from(JSON.stringify(packageFileContents)).toString('base64') + }) + .get('/repos/owner/repo/contents/pnpm-lock.yaml') + .reply(200, { + type: 'file', + path: 'pnpm-lock.yaml', + name: 'pnpm-lock.yaml', + content: Buffer.from(JSON.stringify(packageFileContents)).toString('base64') + }) + .get('/repos/owner/repo/git/refs/heads/master') + .reply(200, { + object: { + sha: '123abc' + } + }) + // First tree and commit for package.json + .post('/repos/owner/repo/git/trees', { + tree: [ + { + path: 'package.json', + content: JSON.stringify(updatedPackageFileContents), + mode: '100644', + type: 'blob' + } + ], + base_tree: '123abc' + }) + .reply(201, { + sha: 'def456' + }) + .post('/repos/owner/repo/git/commits', { + message: 'new commit', + tree: 'def456', + parents: ['123abc'] + }) + .reply(201, { + sha: '789beef' + }) + // Second tree and commit for pnpm-lock.yaml + .post('/repos/owner/repo/git/trees', { + tree: [ + { + path: 'pnpm-lock.yaml', + content: '{"devDependencies":{"jest": {"version": "1.2.0"}}}', + mode: '100644', + type: 'blob' + } + ], + base_tree: '789beef' + }) + .reply(201, { + sha: 'lol999' + }) + .post('/repos/owner/repo/git/commits', { + message: 'Updated lockfile pnpm-lock.yaml, yay', + tree: 'lol999', + parents: ['789beef'] + }) + .reply(201, { + sha: 'finalsha123' + }) + .post('/repos/owner/repo/git/refs', { + ref: 'refs/heads/testBranch', + sha: 'finalsha123' + }) + .reply(201) + + nock('http://localhost:1234') + .post('/', (body) => { + expect(body.packageJson).toEqual(JSON.stringify(updatedPackageFileContents)) + expect(body.type).toEqual('pnpm') + expect(typeof body.packageJson).toBe('string') + expect(typeof body.lock).toBe('string') + expect(body).toMatchSnapshot() + return true + }) + .reply(200, () => { + return { + ok: true, + contents: '{"devDependencies":{"jest": {"version": "1.2.0"}}}' + } + }) + + const sha = await createBranch({ + installationId: 123, + owner: 'owner', + repoName: 'repo', + branch: 'master', + newBranch: 'testBranch', + transforms: [ + { + path: 'package.json', + transform: oldPkg => JSON.stringify(updatedPackageFileContents), + message: 'new commit' + } + ], + processLockfiles: true, + commitMessageTemplates: { 'lockfileUpdate': 'Updated lockfile ${lockfilePath}, yay' }, // eslint-disable-line no-template-curly-in-string + repoDoc: { + _id: 'one-lockfile-pnpm', + accountId: '124', + fullName: 'finnp/one-lockfile-pnpm', + private: false, + files: { + 'package.json': ['package.json'], + 'package-lock.json': [], + 'npm-shrinkwrap.json': [], + 'yarn.lock': [], + 'pnpm-lock.yaml': ['pnpm-lock.yaml'] + }, + packages: { + 'package.json': { + devDependencies: { + 'jest': '1.0.0' + } + } + } + } + }) + + expect(sha).toEqual('finalsha123') + expect(gitHubNock.isDone()).toBeTruthy() + }) + test('don’t generate the same lockfile multiple times', async () => { // multiple commits to the same package file should only result in a single lockfile update, // meaning a single exec server request and a single github tree update plus commit @@ -1296,7 +1442,8 @@ describe('create branch with lockfiles', async () => { 'package.json': ['package.json'], 'package-lock.json': ['package-lock.json'], 'npm-shrinkwrap.json': [], - 'yarn.lock': [] + 'yarn.lock': [], + 'pnpm-lock.yaml': [] }, packages: { 'package.json': { @@ -1407,10 +1554,11 @@ describe('create branch with lockfiles', async () => { fullName: 'finnp/one-lockfile-old-syntax', private: false, files: { - 'package.json': true, - 'package-lock.json': true, - 'npm-shrinkwrap.json': false, - 'yarn.lock': false + 'package.json': ['package.json'], + 'package-lock.json': ['package-lock.json'], + 'npm-shrinkwrap.json': [], + 'yarn.lock': [], + 'pnpm-lock.yaml': [] }, packages: { 'package.json': { @@ -1559,10 +1707,11 @@ describe('create branch with lockfiles', async () => { fullName: 'finnp/one-lockfile-with-token', private: false, files: { - 'package.json': true, - 'package-lock.json': true, - 'npm-shrinkwrap.json': false, - 'yarn.lock': false + 'package.json': ['package.json'], + 'package-lock.json': ['package-lock.json'], + 'npm-shrinkwrap.json': [], + 'yarn.lock': [], + 'pnpm-lock.yaml': [] }, packages: { 'package.json': { diff --git a/test/lib/get-files.js b/test/lib/get-files.js index 5d25fbe2..f24e9fd6 100644 --- a/test/lib/get-files.js +++ b/test/lib/get-files.js @@ -23,7 +23,7 @@ test('getFiles: with no fileList provided', async () => { const files = await getFiles({ installationId: '123', fullName: 'owner/repo', log }) // returns an Object with the 4 standard files - expect(Object.keys(files)).toHaveLength(4) + expect(Object.keys(files)).toHaveLength(5) }) test('getFiles: 2 package.json files', async () => { @@ -66,8 +66,8 @@ test('getFiles: 2 package.json files', async () => { ] const files = await getFiles({ installationId: '123', fullName: 'owner/repo', files: fileList, log }) - // returns an Object with the 4 file types - expect(Object.keys(files)).toHaveLength(4) + // returns an Object with the 5 file types + expect(Object.keys(files)).toHaveLength(5) // The Object has 2 files at the `package.json` key expect(files['package.json']).toHaveLength(2) expect(files['package.json'][0].path).toEqual('package.json') @@ -104,7 +104,7 @@ test('getFiles: 2 package.json files but one is not found on github', async () = const files = await getFiles({ installationId: '123', fullName: 'owner/repo', files: fileList, log }) - expect(Object.keys(files)).toHaveLength(4) + expect(Object.keys(files)).toHaveLength(5) // returns an Object with the key `package.json` expect(files['package.json']).toBeDefined() // The Object has 2 files at the `package.json` key diff --git a/test/lib/lockfile.js b/test/lib/lockfile.js index f85cf0f2..79521aeb 100644 --- a/test/lib/lockfile.js +++ b/test/lib/lockfile.js @@ -30,7 +30,7 @@ describe('getNewLockfile', async () => { } }) - await getNewLockfile({ packageJson, lock, isNpm: true }) + await getNewLockfile({ packageJson, lock, type: 'npm' }) }) test('with package-lock.json', async () => { @@ -46,7 +46,7 @@ describe('getNewLockfile', async () => { }) .reply(200, () => ({ ok: false })) - await getNewLockfile({ packageJson, lock, isNpm: true }) + await getNewLockfile({ packageJson, lock, type: 'npm' }) }) test('with package-lock.json with Network Error', async () => { @@ -61,7 +61,7 @@ describe('getNewLockfile', async () => { .reply(200, () => ({ ok: true })) const packageJson = '{"name": "greenkeeper","devDependencies": {"jest": "^22.4.2"}}' - await getNewLockfile({ packageJson, lock, isNpm: true }) + await getNewLockfile({ packageJson, lock, type: 'npm' }) expect(httpTraffic.isDone()).toBeTruthy() expect(httpTraffic.pendingMocks().length).toEqual(0) }) diff --git a/utils/utils.js b/utils/utils.js index 6b3d7c70..1c0ff2bb 100644 --- a/utils/utils.js +++ b/utils/utils.js @@ -148,17 +148,20 @@ function createTransformFunction (type, dependency, version, log) { // 'legacy' repoDocs have only true/false set at repository.files['yarn.lock'] ect // 'newer' repoDocs have an array (empty or with the paths) -// packageFilename: path of pckage.json +// packageFilename: path of package.json const getLockfilePath = function (files, packageFilename) { const convertedFiles = _.flatten(Object.keys(files).map(key => { if (files[key] === true) return key else return files[key] })) - const hasPackageLock = _.includes(convertedFiles, packageFilename.replace('package.json', 'package-lock.json')) + const hasPackageLock = convertedFiles.includes(packageFilename.replace('package.json', 'package-lock.json')) if (hasPackageLock) return packageFilename.replace('package.json', 'package-lock.json') - const hasYarnLock = _.includes(convertedFiles, packageFilename.replace('package.json', 'yarn.lock')) + const hasPnpmLock = convertedFiles.includes(packageFilename.replace('package.json', 'pnpm-lock.yaml')) + if (hasPnpmLock) return packageFilename.replace('package.json', 'pnpm-lock.yaml') + + const hasYarnLock = convertedFiles.includes(packageFilename.replace('package.json', 'yarn.lock')) if (hasYarnLock) return packageFilename.replace('package.json', 'yarn.lock') return null