From e9163b48d8e46a80d2a4cc98c492b94dfa152cb8 Mon Sep 17 00:00:00 2001 From: Ruy Adorno Date: Tue, 26 Apr 2022 16:16:36 -0400 Subject: fix(libnpmpublish): unpublish from custom reg (#4657) Fixes unpublishing a package from a registry url that has pathnames after its hostname. Fixes: https://github.com/npm/cli/issues/4253 --- workspaces/libnpmpublish/lib/unpublish.js | 21 ++++++++++-- workspaces/libnpmpublish/test/unpublish.js | 55 ++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 2 deletions(-) (limited to 'workspaces/libnpmpublish') diff --git a/workspaces/libnpmpublish/lib/unpublish.js b/workspaces/libnpmpublish/lib/unpublish.js index 91f5252aa..9b124c114 100644 --- a/workspaces/libnpmpublish/lib/unpublish.js +++ b/workspaces/libnpmpublish/lib/unpublish.js @@ -1,9 +1,26 @@ 'use strict' +const { URL } = require('url') const npa = require('npm-package-arg') const npmFetch = require('npm-registry-fetch') const semver = require('semver') -const { URL } = require('url') + +// given a tarball url and a registry url, returns just the +// relevant pathname portion of it, so that it can be handled +// elegantly by npm-registry-fetch which only expects pathnames +// and handles the registry hostname via opts +const getPathname = (tarball, registry) => { + const registryUrl = new URL(registry).pathname.slice(1) + let tarballUrl = new URL(tarball).pathname.slice(1) + + // test the tarball url to see if it starts with a possible + // pathname from the registry url, in that case strips that portion + // of it so that we only return the post-registry-url pathname + if (registryUrl) { + tarballUrl = tarballUrl.slice(registryUrl.length) + } + return tarballUrl +} const unpublish = async (spec, opts) => { spec = npa(spec) @@ -82,7 +99,7 @@ const unpublish = async (spec, opts) => { ...opts, query: { write: true }, }) - const tarballUrl = new URL(dist.tarball).pathname.slice(1) + const tarballUrl = getPathname(dist.tarball, opts.registry) await npmFetch(`${tarballUrl}/-rev/${_rev}`, { ...opts, method: 'DELETE', diff --git a/workspaces/libnpmpublish/test/unpublish.js b/workspaces/libnpmpublish/test/unpublish.js index 6fbe80276..b59dad6d3 100644 --- a/workspaces/libnpmpublish/test/unpublish.js +++ b/workspaces/libnpmpublish/test/unpublish.js @@ -134,6 +134,61 @@ t.test('unpublish specific version', async t => { t.ok(ret, 'foo was unpublished') }) +t.test('unpublishing from a custom registry', async t => { + const opt = { + registry: 'https://artifactory.example.com/api/npm/npm-snapshots/', + } + const reg = opt.registry + const doc = { + _id: 'foo', + _rev: REV, + _revisions: [1, 2, 3], + _attachments: [1, 2, 3], + name: 'foo', + 'dist-tags': { + latest: '1.0.1', + }, + versions: { + '1.0.0': { + name: 'foo', + dist: { + tarball: `${reg}/foo/-/foo-1.0.0.tgz`, + }, + }, + '1.0.1': { + name: 'foo', + dist: { + tarball: `${reg}/foo/-/foo-1.0.1.tgz`, + }, + }, + }, + } + const postEdit = { + _id: 'foo', + _rev: REV, + name: 'foo', + 'dist-tags': { + latest: '1.0.0', + }, + versions: { + '1.0.0': { + name: 'foo', + dist: { + tarball: `${reg}/foo/-/foo-1.0.0.tgz`, + }, + }, + }, + } + + const srv = tnock(t, reg) + srv.get('/foo?write=true').reply(200, doc) + srv.put(`/foo/-rev/${REV}`, postEdit).reply(200) + srv.get('/foo?write=true').reply(200, postEdit) + srv.delete(`/foo/-/foo-1.0.1.tgz/-rev/${REV}`).reply(200) + const ret = await unpub('foo@1.0.1', opt) + t.ok(ret, 'foo was unpublished') +}) + t.test('404 considered a success', async t => { const srv = tnock(t, REG) srv.get('/foo?write=true').reply(404) -- cgit v1.2.3