Skip to content

Commit ef6fbf0

Browse files
authored
Merge pull request #1295 from oclif/jf/W-21915680
fix: installing a tarball after a registered plugin works properly @W-21915680@
2 parents 288f6c7 + a27f90e commit ef6fbf0

File tree

5 files changed

+86
-2
lines changed

5 files changed

+86
-2
lines changed

.git2gus/config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@
55
"enhancement": "USER STORY",
66
"bug": "BUG P3"
77
},
8-
"hideWorkItemUrl": true,
8+
"hideWorkItemUrl": "true",
99
"statusWhenClosed": "CLOSED"
1010
}

src/plugins.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@ export default class Plugins {
165165
const normalizedUrl = npa(url)
166166
const matches = Object.entries(dependencies ?? {}).find(([, npmVersion]) => {
167167
const normalized = npa(npmVersion)
168+
if (normalized.type !== normalizedUrl.type) {
169+
return false
170+
}
171+
168172
// for local file paths
169173
if (normalized.type === 'file' && normalized.raw) {
170174
return parse(url).base === parse(normalized.raw).base
27.9 KB
Binary file not shown.
11.2 KB
Binary file not shown.

test/integration/install.integration.ts

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,36 @@ import {runCommand} from '@oclif/test'
22
import {dim} from 'ansis'
33
import {expect} from 'chai'
44
import {rm} from 'node:fs/promises'
5-
import {join, resolve} from 'node:path'
5+
import {dirname, join, resolve} from 'node:path'
6+
import {fileURLToPath} from 'node:url'
7+
8+
const __dirname = dirname(fileURLToPath(import.meta.url))
69

710
describe('install/uninstall integration tests', () => {
811
const plugin = '@oclif/plugin-version'
912
const pluginShortName = 'version'
1013
const pluginGithubSlug = 'oclif/plugin-version'
1114
const pluginGithubUrl = 'https://github.com/oclif/plugin-version.git'
15+
let pluginLocalTarball = resolve(__dirname, '..', 'fixtures', 'oclif-plugin-version-v2.2.41.tgz')
16+
// Normalize the path to ensure Windows compatibility
17+
.replaceAll('\\', '/')
18+
// If the path starts with 'C:', that needs to be removed
19+
if (pluginLocalTarball.startsWith('C:')) {
20+
pluginLocalTarball = pluginLocalTarball.slice(2)
21+
}
22+
23+
const otherPlugin = '@oclif/plugin-search'
24+
const otherPluginShortName = 'search'
25+
26+
const yetAnotherPlugin = '@oclif/plugin-update'
27+
const yetAnotherPluginShortName = 'update'
28+
let yetAnotherLocalPluginTarball = resolve(__dirname, '..', 'fixtures', 'oclif-plugin-update-v4.7.32.tgz')
29+
// Normalize the path to ensure Windows compatibility
30+
.replaceAll('\\', '/')
31+
// If the path starts with 'C:', that needs to be removed
32+
if (yetAnotherLocalPluginTarball.startsWith('C:')) {
33+
yetAnotherLocalPluginTarball = yetAnotherLocalPluginTarball.slice(2)
34+
}
1235

1336
const tmp = resolve('tmp', 'install-integration')
1437
const cacheDir = join(tmp, 'plugin-plugins-tests', 'cache')
@@ -146,6 +169,63 @@ describe('install/uninstall integration tests', () => {
146169
})
147170
})
148171

172+
describe('local tarball', () => {
173+
it('should install plugin from locally hosted tarball', async () => {
174+
await runCommand(`plugins install "file://${pluginLocalTarball}`)
175+
const {result, stdout} = await runCommand<Array<{name: string}>>('plugins')
176+
expect(stdout).to.contain(pluginShortName)
177+
expect(result?.some((r) => r.name === plugin)).to.be.true
178+
})
179+
180+
it('should uninstall plugin from local tarball', async () => {
181+
await runCommand(`plugins uninstall ${plugin}`)
182+
const {result, stdout} = await runCommand<Array<{name: string}>>('plugins')
183+
expect(stdout).to.contain('No plugins installed.')
184+
expect(result?.some((r) => r.name === plugin)).to.be.false
185+
})
186+
})
187+
188+
describe('multiple plugins sequentially', async () => {
189+
/**
190+
* This is a test for @W-21915680@, a bizarre bug wherein if you installed a plugin from the registry by its true name,
191+
* and then installed a local tarball whose package name is alphabetically after the previous one, the local tarball
192+
* would silently fail to install.
193+
*/
194+
it('handles local tarballs installed after simple plugin name', async () => {
195+
// Install first plugin by its registered name
196+
await runCommand(`plugins install ${otherPlugin}`)
197+
const {result: firstResult, stdout: firstStdout} = await runCommand<Array<{name: string}>>('plugins')
198+
expect(firstStdout).to.contain(otherPluginShortName)
199+
expect(firstResult?.some((r) => r.name === otherPlugin)).to.be.true
200+
201+
// Install a second plugin by a local tarball. This one is alphabetically after the first one.
202+
await runCommand(`plugins install "file://${yetAnotherLocalPluginTarball}"`)
203+
const {result: secondResult, stdout: secondStdout} = await runCommand<Array<{name: string}>>('plugins')
204+
expect(secondStdout).to.contain(yetAnotherPluginShortName)
205+
expect(secondResult?.some((r) => r.name === otherPlugin)).to.be.true
206+
expect(secondResult?.some((r) => r.name === yetAnotherPlugin)).to.be.true
207+
208+
// Install a third plugin by a local tarball. This one is alphabetically after the second one.
209+
await runCommand(`plugins install "file://${pluginLocalTarball}`)
210+
const {result: thirdResult, stdout: thirdStdout} = await runCommand<Array<{name: string}>>('plugins')
211+
expect(thirdStdout).to.contain(pluginShortName)
212+
expect(thirdResult?.some((r) => r.name === otherPlugin)).to.be.true
213+
expect(thirdResult?.some((r) => r.name === yetAnotherPlugin)).to.be.true
214+
expect(thirdResult?.some((r) => r.name === plugin)).to.be.true
215+
})
216+
217+
it('uninstalls all plugins', async () => {
218+
await runCommand(`plugins uninstall ${plugin}`)
219+
await runCommand(`plugins uninstall ${otherPlugin}`)
220+
await runCommand(`plugins uninstall ${yetAnotherPlugin}`)
221+
const {result, stdout} = await runCommand<Array<{name: string}>>('plugins')
222+
expect(stdout).to.contain('No plugins installed.')
223+
expect(result?.some((r) => r.name === plugin)).to.be.false
224+
expect(result?.some((r) => r.name === otherPlugin)).to.be.false
225+
expect(result?.some((r) => r.name === yetAnotherPlugin)).to.be.false
226+
})
227+
})
228+
149229
describe('non-existent plugin', () => {
150230
it('should not install non-existent plugin', async () => {
151231
await runCommand('plugins install @oclif/DOES_NOT_EXIST')

0 commit comments

Comments
 (0)