diff --git a/workspaces/libnpmpublish/lib/publish.js b/workspaces/libnpmpublish/lib/publish.js index 554eb9bec46f8..b0ef782a166c6 100644 --- a/workspaces/libnpmpublish/lib/publish.js +++ b/workspaces/libnpmpublish/lib/publish.js @@ -50,42 +50,16 @@ Remove the 'private' field from the package.json to publish it.`), opts ) - try { - const res = await npmFetch(spec.escapedName, { - ...opts, - method: 'PUT', - body: metadata, - ignoreBody: true, - }) - if (transparencyLogUrl) { - res.transparencyLogUrl = transparencyLogUrl - } - return res - } catch (err) { - if (err.code !== 'E409') { - throw err - } - // if E409, we attempt exactly ONE retry, to protect us - // against malicious activity like trying to publish - // a bunch of new versions of a package at the same time - // and/or spamming the registry - const current = await npmFetch.json(spec.escapedName, { - ...opts, - query: { write: true }, - }) - const newMetadata = patchMetadata(current, metadata) - const res = await npmFetch(spec.escapedName, { - ...opts, - method: 'PUT', - body: newMetadata, - ignoreBody: true, - }) - /* istanbul ignore next */ - if (transparencyLogUrl) { - res.transparencyLogUrl = transparencyLogUrl - } - return res + const res = await npmFetch(spec.escapedName, { + ...opts, + method: 'PUT', + body: metadata, + ignoreBody: true, + }) + if (transparencyLogUrl) { + res.transparencyLogUrl = transparencyLogUrl } + return res } const patchManifest = (_manifest, opts) => { @@ -195,51 +169,6 @@ const buildMetadata = async (registry, manifest, tarballData, spec, opts) => { } } -const patchMetadata = (current, newData) => { - const curVers = Object.keys(current.versions || {}) - .map(v => semver.clean(v, true)) - .concat(Object.keys(current.time || {}) - .map(v => semver.valid(v, true) && semver.clean(v, true)) - .filter(v => v)) - - const newVersion = Object.keys(newData.versions)[0] - - if (curVers.indexOf(newVersion) !== -1) { - const { name: pkgid, version } = newData - throw Object.assign( - new Error( - `Cannot publish ${pkgid}@${version} over existing version.` - ), { - code: 'EPUBLISHCONFLICT', - pkgid, - version, - }) - } - - current.versions = current.versions || {} - current.versions[newVersion] = newData.versions[newVersion] - for (const i in newData) { - switch (i) { - // objects that copy over the new stuffs - case 'dist-tags': - case 'versions': - case '_attachments': - for (const j in newData[i]) { - current[i] = current[i] || {} - current[i][j] = newData[i][j] - } - break - - // copy - default: - current[i] = newData[i] - break - } - } - - return current -} - // Check that all the prereqs are met for provenance generation const ensureProvenanceGeneration = async (registry, spec, opts) => { if (ciInfo.GITHUB_ACTIONS) { diff --git a/workspaces/libnpmpublish/test/publish.js b/workspaces/libnpmpublish/test/publish.js index 6744443e6843c..f7e824a3805c8 100644 --- a/workspaces/libnpmpublish/test/publish.js +++ b/workspaces/libnpmpublish/test/publish.js @@ -1,6 +1,5 @@ 'use strict' -const cloneDeep = require('lodash.clonedeep') const crypto = require('crypto') const fs = require('fs') const npa = require('npm-package-arg') @@ -180,266 +179,6 @@ t.test('scoped publish - restricted access', async t => { t.ok(ret, 'publish succeeded') }) -t.test('retry after a conflict', async t => { - const { publish } = t.mock('..') - const registry = new MockRegistry({ - tap: t, - registry: opts.registry, - authorization: token, - }) - const REV = '72-47f2986bfd8e8b55068b204588bbf484' - const manifest = { - name: 'libnpmpublish', - version: '1.0.0', - description: 'some stuff', - } - - const basePackument = { - name: 'libnpmpublish', - description: 'some stuff', - access: 'public', - _id: 'libnpmpublish', - 'dist-tags': {}, - versions: {}, - _attachments: {}, - } - const currentPackument = cloneDeep({ - ...basePackument, - time: { - modified: new Date().toISOString(), - created: new Date().toISOString(), - '1.0.1': new Date().toISOString(), - }, - 'dist-tags': { latest: '1.0.1' }, - versions: { - '1.0.1': { - _id: 'libnpmpublish@1.0.1', - _nodeVersion: process.versions.node, - _npmVersion: opts.npmVersion, - name: 'libnpmpublish', - version: '1.0.1', - description: 'some stuff', - dist: { - shasum, - integrity: integrity.toString(), - tarball: 'http://mock.reg/libnpmpublish/-/libnpmpublish-1.0.1.tgz', - }, - }, - }, - _attachments: { - 'libnpmpublish-1.0.1.tgz': { - content_type: 'application/octet-stream', - data: tarData.toString('base64'), - length: tarData.length, - }, - }, - }) - const newPackument = cloneDeep({ - ...basePackument, - 'dist-tags': { latest: '1.0.0' }, - versions: { - '1.0.0': { - _id: 'libnpmpublish@1.0.0', - _nodeVersion: process.versions.node, - _npmVersion: opts.npmVersion, - name: 'libnpmpublish', - version: '1.0.0', - description: 'some stuff', - dist: { - shasum, - integrity: integrity.toString(), - tarball: 'http://mock.reg/libnpmpublish/-/libnpmpublish-1.0.0.tgz', - }, - }, - }, - _attachments: { - 'libnpmpublish-1.0.0.tgz': { - content_type: 'application/octet-stream', - data: tarData.toString('base64'), - length: tarData.length, - }, - }, - }) - const mergedPackument = cloneDeep({ - ...basePackument, - time: currentPackument.time, - 'dist-tags': { latest: '1.0.0' }, - versions: { ...currentPackument.versions, ...newPackument.versions }, - _attachments: { ...currentPackument._attachments, ...newPackument._attachments }, - }) - - registry.nock.put('/libnpmpublish', body => { - t.notOk(body._rev, 'no _rev in initial post') - t.same(body, newPackument, 'got conflicting packument') - return true - }).reply(409, { error: 'gimme _rev plz' }) - - registry.nock.get('/libnpmpublish?write=true').reply(200, { - _rev: REV, - ...currentPackument, - }) - - registry.nock.put('/libnpmpublish', body => { - t.same(body, { - _rev: REV, - ...mergedPackument, - }, 'posted packument includes _rev and a merged version') - return true - }).reply(201, {}) - - const ret = await publish(manifest, tarData, opts) - t.ok(ret, 'publish succeeded') -}) - -t.test('retry after a conflict -- no versions on remote', async t => { - const { publish } = t.mock('..') - const registry = new MockRegistry({ - tap: t, - registry: opts.registry, - authorization: token, - }) - const REV = '72-47f2986bfd8e8b55068b204588bbf484' - const manifest = { - name: 'libnpmpublish', - version: '1.0.0', - description: 'some stuff', - } - - const basePackument = { - name: 'libnpmpublish', - description: 'some stuff', - access: 'public', - _id: 'libnpmpublish', - } - const currentPackument = { ...basePackument } - const newPackument = cloneDeep({ - ...basePackument, - 'dist-tags': { latest: '1.0.0' }, - versions: { - '1.0.0': { - _id: 'libnpmpublish@1.0.0', - _nodeVersion: process.versions.node, - _npmVersion: opts.npmVersion, - name: 'libnpmpublish', - version: '1.0.0', - description: 'some stuff', - dist: { - shasum, - integrity: integrity.toString(), - tarball: 'http://mock.reg/libnpmpublish/-/libnpmpublish-1.0.0.tgz', - }, - }, - }, - _attachments: { - 'libnpmpublish-1.0.0.tgz': { - content_type: 'application/octet-stream', - data: tarData.toString('base64'), - length: tarData.length, - }, - }, - }) - const mergedPackument = cloneDeep({ - ...basePackument, - 'dist-tags': { latest: '1.0.0' }, - versions: { ...newPackument.versions }, - _attachments: { ...newPackument._attachments }, - }) - - registry.nock.put('/libnpmpublish', body => { - t.notOk(body._rev, 'no _rev in initial post') - t.same(body, newPackument, 'got conflicting packument') - return true - }).reply(409, { error: 'gimme _rev plz' }) - - registry.nock.get('/libnpmpublish?write=true').reply(200, { - _rev: REV, - ...currentPackument, - }) - - registry.nock.put('/libnpmpublish', body => { - t.same(body, { - _rev: REV, - ...mergedPackument, - }, 'posted packument includes _rev and a merged version') - return true - }).reply(201, {}) - - const ret = await publish(manifest, tarData, opts) - - t.ok(ret, 'publish succeeded') -}) - -t.test('version conflict', async t => { - const { publish } = t.mock('..') - const registry = new MockRegistry({ - tap: t, - registry: opts.registry, - authorization: token, - }) - const REV = '72-47f2986bfd8e8b55068b204588bbf484' - const manifest = { - name: 'libnpmpublish', - version: '1.0.0', - description: 'some stuff', - } - - const basePackument = { - name: 'libnpmpublish', - description: 'some stuff', - access: 'public', - _id: 'libnpmpublish', - 'dist-tags': {}, - versions: {}, - _attachments: {}, - } - const newPackument = cloneDeep(Object.assign({}, basePackument, { - 'dist-tags': { latest: '1.0.0' }, - versions: { - '1.0.0': { - _id: 'libnpmpublish@1.0.0', - _nodeVersion: process.versions.node, - _npmVersion: '6.13.7', - name: 'libnpmpublish', - version: '1.0.0', - description: 'some stuff', - dist: { - shasum, - integrity: integrity.toString(), - tarball: 'http://mock.reg/libnpmpublish/-/libnpmpublish-1.0.0.tgz', - }, - }, - }, - _attachments: { - 'libnpmpublish-1.0.0.tgz': { - content_type: 'application/octet-stream', - data: tarData.toString('base64'), - length: tarData.length, - }, - }, - })) - - registry.nock.put('/libnpmpublish', body => { - t.notOk(body._rev, 'no _rev in initial post') - t.same(body, newPackument, 'got conflicting packument') - return true - }).reply(409, { error: 'gimme _rev plz' }) - - registry.nock.get('/libnpmpublish?write=true').reply(200, { - _rev: REV, - ...newPackument, - }) - - try { - await publish(manifest, tarData, { - ...opts, - npmVersion: '6.13.7', - token: 'deadbeef', - }) - } catch (err) { - t.equal(err.code, 'EPUBLISHCONFLICT', 'got publish conflict code') - } -}) - t.test('refuse if package marked private', async t => { const { publish } = t.mock('..') const registry = new MockRegistry({