Skip to content

Commit e9163b4

Browse files
authored
fix(libnpmpublish): unpublish from custom reg (#4657)
Fixes unpublishing a package from a registry url that has pathnames after its hostname. Fixes: #4253
1 parent fa3d829 commit e9163b4

File tree

2 files changed

+74
-2
lines changed

2 files changed

+74
-2
lines changed

workspaces/libnpmpublish/lib/unpublish.js

+19-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,26 @@
11
'use strict'
22

3+
const { URL } = require('url')
34
const npa = require('npm-package-arg')
45
const npmFetch = require('npm-registry-fetch')
56
const semver = require('semver')
6-
const { URL } = require('url')
7+
8+
// given a tarball url and a registry url, returns just the
9+
// relevant pathname portion of it, so that it can be handled
10+
// elegantly by npm-registry-fetch which only expects pathnames
11+
// and handles the registry hostname via opts
12+
const getPathname = (tarball, registry) => {
13+
const registryUrl = new URL(registry).pathname.slice(1)
14+
let tarballUrl = new URL(tarball).pathname.slice(1)
15+
16+
// test the tarball url to see if it starts with a possible
17+
// pathname from the registry url, in that case strips that portion
18+
// of it so that we only return the post-registry-url pathname
19+
if (registryUrl) {
20+
tarballUrl = tarballUrl.slice(registryUrl.length)
21+
}
22+
return tarballUrl
23+
}
724

825
const unpublish = async (spec, opts) => {
926
spec = npa(spec)
@@ -82,7 +99,7 @@ const unpublish = async (spec, opts) => {
8299
...opts,
83100
query: { write: true },
84101
})
85-
const tarballUrl = new URL(dist.tarball).pathname.slice(1)
102+
const tarballUrl = getPathname(dist.tarball, opts.registry)
86103
await npmFetch(`${tarballUrl}/-rev/${_rev}`, {
87104
...opts,
88105
method: 'DELETE',

workspaces/libnpmpublish/test/unpublish.js

+55
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,61 @@ t.test('unpublish specific version', async t => {
134134
t.ok(ret, 'foo was unpublished')
135135
})
136136

137+
t.test('unpublishing from a custom registry', async t => {
138+
const opt = {
139+
registry: 'https://artifactory.example.com/api/npm/npm-snapshots/',
140+
}
141+
const reg = opt.registry
142+
const doc = {
143+
_id: 'foo',
144+
_rev: REV,
145+
_revisions: [1, 2, 3],
146+
_attachments: [1, 2, 3],
147+
name: 'foo',
148+
'dist-tags': {
149+
latest: '1.0.1',
150+
},
151+
versions: {
152+
'1.0.0': {
153+
name: 'foo',
154+
dist: {
155+
tarball: `${reg}/foo/-/foo-1.0.0.tgz`,
156+
},
157+
},
158+
'1.0.1': {
159+
name: 'foo',
160+
dist: {
161+
tarball: `${reg}/foo/-/foo-1.0.1.tgz`,
162+
},
163+
},
164+
},
165+
}
166+
const postEdit = {
167+
_id: 'foo',
168+
_rev: REV,
169+
name: 'foo',
170+
'dist-tags': {
171+
latest: '1.0.0',
172+
},
173+
versions: {
174+
'1.0.0': {
175+
name: 'foo',
176+
dist: {
177+
tarball: `${reg}/foo/-/foo-1.0.0.tgz`,
178+
},
179+
},
180+
},
181+
}
182+
183+
const srv = tnock(t, reg)
184+
srv.get('/foo?write=true').reply(200, doc)
185+
srv.put(`/foo/-rev/${REV}`, postEdit).reply(200)
186+
srv.get('/foo?write=true').reply(200, postEdit)
187+
srv.delete(`/foo/-/foo-1.0.1.tgz/-rev/${REV}`).reply(200)
188+
const ret = await unpub('[email protected]', opt)
189+
t.ok(ret, 'foo was unpublished')
190+
})
191+
137192
t.test('404 considered a success', async t => {
138193
const srv = tnock(t, REG)
139194
srv.get('/foo?write=true').reply(404)

0 commit comments

Comments
 (0)