mirror of
https://github.com/verdaccio/verdaccio.git
synced 2025-01-13 22:48:31 -05:00
fix: handle upload scoped tarball and add new deprecations (#3340)
* chore: add local publish support * chore: fix upload scoped tarball * add e2e
This commit is contained in:
parent
efa2efe531
commit
b849128ded
49 changed files with 1617 additions and 1156 deletions
11
.changeset/witty-ties-speak.md
Normal file
11
.changeset/witty-ties-speak.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
---
|
||||||
|
'@verdaccio/api': patch
|
||||||
|
'@verdaccio/cli': patch
|
||||||
|
'@verdaccio/core': patch
|
||||||
|
'@verdaccio/types': patch
|
||||||
|
'@verdaccio/store': patch
|
||||||
|
'@verdaccio/test-helper': patch
|
||||||
|
'@verdaccio/local-publish': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix: handle upload scoped tarball
|
|
@ -183,7 +183,7 @@ a report in our [issue tracker](https://github.com/verdaccio/verdaccio/issues).
|
||||||
- _Features clearly flagged as not supported_
|
- _Features clearly flagged as not supported_
|
||||||
- _Node.js issues installation in any platform_: If you cannot install the
|
- _Node.js issues installation in any platform_: If you cannot install the
|
||||||
global package (this is considered external issue)
|
global package (this is considered external issue)
|
||||||
- Any ticket which has beed flagged as an [external issue
|
- Any ticket which has been flagged as an [external issue
|
||||||
](https://github.com/verdaccio/verdaccio/labels/external-issue)
|
](https://github.com/verdaccio/verdaccio/labels/external-issue)
|
||||||
|
|
||||||
If you intend to report a **security** issue, please follow our [Security policy
|
If you intend to report a **security** issue, please follow our [Security policy
|
||||||
|
@ -234,7 +234,7 @@ the project. Adding in context and the use-case will really help!
|
||||||
|
|
||||||
- A detailed description the advantages of your request
|
- A detailed description the advantages of your request
|
||||||
- Whether or not it's compatible with `npm`, `pnpm` and [_yarn classic_
|
- Whether or not it's compatible with `npm`, `pnpm` and [_yarn classic_
|
||||||
](https://github.com/yarnpkg/yarn) or [_yarn berry_
|
](https://github.com/yarnpkg/yarn) or [_yarn modern_
|
||||||
](https://github.com/yarnpkg/berry).
|
](https://github.com/yarnpkg/berry).
|
||||||
- A potential implementation or design
|
- A potential implementation or design
|
||||||
- Whatever else is on your mind! 🤓
|
- Whatever else is on your mind! 🤓
|
||||||
|
@ -420,3 +420,25 @@ If you want to develop your own plugin:
|
||||||
3. You are free to host your plugin in your repository
|
3. You are free to host your plugin in your repository
|
||||||
4. Provide a detailed description of your plugin to help users understand how to
|
4. Provide a detailed description of your plugin to help users understand how to
|
||||||
use it
|
use it
|
||||||
|
|
||||||
|
## Testing your changes in a local registry
|
||||||
|
|
||||||
|
Once you have perform your changes in the code base, the build and tests passes you can publish a local version:
|
||||||
|
|
||||||
|
- Ensure you have build all modules (or the one you have modified)
|
||||||
|
- Run `pnpm local:publish:release` to launch a local registry and publish all packages into it. This command will be alive until server is killed (Control Key + C)
|
||||||
|
|
||||||
|
```
|
||||||
|
pnpm build
|
||||||
|
pnpm local:publish:release
|
||||||
|
```
|
||||||
|
|
||||||
|
The last step consist on install globally the package from the local registry.
|
||||||
|
|
||||||
|
```
|
||||||
|
npm i -g verdaccio --registry=http://localhost:4873
|
||||||
|
|
||||||
|
verdaccio
|
||||||
|
```
|
||||||
|
|
||||||
|
If you perform more changes in the source code, repeat this process, there is not _hot reloading_ support.
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
"@babel/runtime": "7.18.9",
|
"@babel/runtime": "7.18.9",
|
||||||
"@dianmora/contributors": "5.0.0",
|
"@dianmora/contributors": "5.0.0",
|
||||||
"@changesets/changelog-github": "0.4.6",
|
"@changesets/changelog-github": "0.4.6",
|
||||||
"@changesets/cli": "2.15.0",
|
"@changesets/cli": "2.24.4",
|
||||||
"@changesets/get-dependents-graph": "1.3.3",
|
"@changesets/get-dependents-graph": "1.3.3",
|
||||||
"@crowdin/cli": "3.7.10",
|
"@crowdin/cli": "3.7.10",
|
||||||
"@trivago/prettier-plugin-sort-imports": "3.3.0",
|
"@trivago/prettier-plugin-sort-imports": "3.3.0",
|
||||||
|
@ -93,7 +93,7 @@
|
||||||
"jest-junit": "12.3.0",
|
"jest-junit": "12.3.0",
|
||||||
"kleur": "3.0.3",
|
"kleur": "3.0.3",
|
||||||
"lint-staged": "11.2.6",
|
"lint-staged": "11.2.6",
|
||||||
"nock": "12.0.3",
|
"nock": "13.2.9",
|
||||||
"node-fetch": "cjs",
|
"node-fetch": "cjs",
|
||||||
"nodemon": "2.0.19",
|
"nodemon": "2.0.19",
|
||||||
"npm-run-all": "4.1.5",
|
"npm-run-all": "4.1.5",
|
||||||
|
@ -146,7 +146,10 @@
|
||||||
"crowdin:upload": "crowdin upload sources --auto-update --config ./crowdin.yaml",
|
"crowdin:upload": "crowdin upload sources --auto-update --config ./crowdin.yaml",
|
||||||
"crowdin:download": "crowdin download --verbose --config ./crowdin.yaml",
|
"crowdin:download": "crowdin download --verbose --config ./crowdin.yaml",
|
||||||
"crowdin:sync": "pnpm crowdin:upload && pnpm crowdin:download --verbose",
|
"crowdin:sync": "pnpm crowdin:upload && pnpm crowdin:download --verbose",
|
||||||
"postinstall": "husky install"
|
"postinstall": "husky install",
|
||||||
|
"local:registry": "pnpm start --filter ...@verdaccio/local-publish",
|
||||||
|
"local:publish": "cross-env npm_config_registry=http://localhost:4873 pnpm ci:publish",
|
||||||
|
"local:publish:release": "concurrently \"pnpm local:registry\" \"pnpm local:publish\""
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=16.5",
|
"node": ">=16.5",
|
||||||
|
|
|
@ -59,6 +59,7 @@ export default function (route: Router, auth: IAuth, storage: Storage): void {
|
||||||
try {
|
try {
|
||||||
const stream = (await storage.getTarballNext(pkg, filename, {
|
const stream = (await storage.getTarballNext(pkg, filename, {
|
||||||
signal: abort.signal,
|
signal: abort.signal,
|
||||||
|
// TODO: review why this param
|
||||||
// enableRemote: true,
|
// enableRemote: true,
|
||||||
})) as any;
|
})) as any;
|
||||||
|
|
||||||
|
|
|
@ -81,7 +81,7 @@ const debug = buildDebug('verdaccio:api:publish');
|
||||||
* specific flag for star or un start.
|
* specific flag for star or un start.
|
||||||
* The URL for star is similar to the unpublish (change package format)
|
* The URL for star is similar to the unpublish (change package format)
|
||||||
*
|
*
|
||||||
* npm has no enpoint for star a package, rather mutate the metadata and acts as, the difference
|
* npm has no endpoint for star a package, rather mutate the metadata and acts as, the difference
|
||||||
* is the users property which is part of the payload and the body only includes
|
* is the users property which is part of the payload and the body only includes
|
||||||
*
|
*
|
||||||
* {
|
* {
|
||||||
|
@ -206,13 +206,12 @@ export function publishPackageNext(storage: Storage): any {
|
||||||
return next({
|
return next({
|
||||||
// TODO: this could be also Package Updated based on the
|
// TODO: this could be also Package Updated based on the
|
||||||
// action, deprecate, star, publish new version, or create a package
|
// action, deprecate, star, publish new version, or create a package
|
||||||
// the mssage some return from the method
|
// the message some return from the method
|
||||||
ok: API_MESSAGE.PKG_CREATED,
|
ok: API_MESSAGE.PKG_CREATED,
|
||||||
success: true,
|
success: true,
|
||||||
});
|
});
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
// TODO: review if we need the abort controller here
|
// TODO: review if we need the abort controller here
|
||||||
ac.abort();
|
|
||||||
next(err);
|
next(err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
26
packages/api/test/integration/config/publish-proxy.yaml
Normal file
26
packages/api/test/integration/config/publish-proxy.yaml
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
auth:
|
||||||
|
htpasswd:
|
||||||
|
file: ./htpasswd-publish-proxy
|
||||||
|
web:
|
||||||
|
enable: true
|
||||||
|
title: verdaccio
|
||||||
|
|
||||||
|
uplinks:
|
||||||
|
npmjs:
|
||||||
|
url: https://registry.npmjs.org/
|
||||||
|
|
||||||
|
log: { type: stdout, format: pretty, level: trace }
|
||||||
|
|
||||||
|
packages:
|
||||||
|
'@*/*':
|
||||||
|
access: $all
|
||||||
|
publish: $anonymous
|
||||||
|
unpublish: $anonymous
|
||||||
|
proxy: npmjs
|
||||||
|
'**':
|
||||||
|
access: $all
|
||||||
|
publish: $anonymous
|
||||||
|
unpublish: $anonymous
|
||||||
|
proxy: npmjs
|
||||||
|
|
||||||
|
_debug: true
|
|
@ -6,6 +6,41 @@ import { Storage } from '@verdaccio/store';
|
||||||
import { initializeServer, publishVersion } from './_helper';
|
import { initializeServer, publishVersion } from './_helper';
|
||||||
|
|
||||||
describe('package', () => {
|
describe('package', () => {
|
||||||
|
describe('get tarball', () => {
|
||||||
|
let app;
|
||||||
|
beforeEach(async () => {
|
||||||
|
app = await initializeServer('package.yaml');
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each([
|
||||||
|
['foo', 'foo-1.0.0.tgz'],
|
||||||
|
['@scope/foo', 'foo-1.0.0.tgz'],
|
||||||
|
])('should return a file tarball', async (pkg, fileName) => {
|
||||||
|
await publishVersion(app, pkg, '1.0.0');
|
||||||
|
const response = await supertest(app)
|
||||||
|
.get(`/${pkg}/-/${fileName}`)
|
||||||
|
.set(HEADERS.ACCEPT, HEADERS.JSON)
|
||||||
|
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.OCTET_STREAM)
|
||||||
|
.expect(HTTP_STATUS.OK);
|
||||||
|
expect(Buffer.from(response.body).toString('utf8')).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each([
|
||||||
|
['foo', 'foo-1.0.0.tgz'],
|
||||||
|
['@scope/foo', 'foo-1.0.0.tgz'],
|
||||||
|
])('should fails if tarball does not exist', async (pkg, fileName) => {
|
||||||
|
await publishVersion(app, pkg, '1.0.1');
|
||||||
|
return await supertest(app)
|
||||||
|
.get(`/${pkg}/-/${fileName}`)
|
||||||
|
.set(HEADERS.ACCEPT, HEADERS.JSON)
|
||||||
|
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.OCTET_STREAM)
|
||||||
|
.expect(HTTP_STATUS.NOT_FOUND);
|
||||||
|
});
|
||||||
|
test.todo('check content length file header');
|
||||||
|
test.todo('fails on file was aborted');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('get package', () => {
|
||||||
let app;
|
let app;
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
app = await initializeServer('package.yaml');
|
app = await initializeServer('package.yaml');
|
||||||
|
@ -67,3 +102,4 @@ describe('package', () => {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
|
import nock from 'nock';
|
||||||
|
import { basename } from 'path';
|
||||||
import supertest from 'supertest';
|
import supertest from 'supertest';
|
||||||
|
|
||||||
import { HTTP_STATUS } from '@verdaccio/core';
|
import { HTTP_STATUS } from '@verdaccio/core';
|
||||||
import { API_ERROR, API_MESSAGE, HEADERS, HEADER_TYPE } from '@verdaccio/core';
|
import { API_ERROR, API_MESSAGE, HEADERS, HEADER_TYPE } from '@verdaccio/core';
|
||||||
import { generatePackageMetadata } from '@verdaccio/test-helper';
|
import { generatePackageMetadata, generateRemotePackageMetadata } from '@verdaccio/test-helper';
|
||||||
|
|
||||||
import { $RequestExtend, $ResponseExtend } from '../../types/custom';
|
import { $RequestExtend, $ResponseExtend } from '../../types/custom';
|
||||||
import { initializeServer, publishVersion } from './_helper';
|
import { getPackage, initializeServer, publishVersion } from './_helper';
|
||||||
|
|
||||||
const mockApiJWTmiddleware = jest.fn(
|
const mockApiJWTmiddleware = jest.fn(
|
||||||
() =>
|
() =>
|
||||||
|
@ -33,31 +35,8 @@ jest.mock('@verdaccio/auth', () => ({
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// const mockStorage = jest.fn(() => {
|
|
||||||
// const { Storage } = jest.requireActual('@verdaccio/store');
|
|
||||||
// return {
|
|
||||||
// Storage: class extends Storage {
|
|
||||||
// addPackage(name, metadata, cb) {
|
|
||||||
// super.addPackage(name, metadata, cb);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// });
|
|
||||||
|
|
||||||
// jest.mock('@verdaccio/store', () => {
|
|
||||||
// const { Storage } = jest.requireActual('@verdaccio/store');
|
|
||||||
// return ({
|
|
||||||
// Storage: class extends Storage {
|
|
||||||
// addPackage(name, metadata, cb) {
|
|
||||||
// // super.addPackage(name, metadata, cb);
|
|
||||||
// return mockStorage(name, metadata, cb);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// });
|
|
||||||
|
|
||||||
describe('publish', () => {
|
describe('publish', () => {
|
||||||
describe('handle invalid publish formats', () => {
|
describe('handle errors', () => {
|
||||||
const pkgName = 'test';
|
const pkgName = 'test';
|
||||||
const pkgMetadata = generatePackageMetadata(pkgName, '1.0.0');
|
const pkgMetadata = generatePackageMetadata(pkgName, '1.0.0');
|
||||||
test('should fail on publish a bad _attachments package', async () => {
|
test('should fail on publish a bad _attachments package', async () => {
|
||||||
|
@ -101,10 +80,11 @@ describe('publish', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('publish a package', () => {
|
describe('publish a package', () => {
|
||||||
test('should publish a package', async () => {
|
describe('no proxies setup', () => {
|
||||||
|
test.each([['foo', '@scope/foo']])('should publish a package', async (pkgName) => {
|
||||||
const app = await initializeServer('publish.yaml');
|
const app = await initializeServer('publish.yaml');
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
publishVersion(app, 'foo', '1.0.0')
|
publishVersion(app, pkgName, '1.0.0')
|
||||||
.expect(HTTP_STATUS.CREATED)
|
.expect(HTTP_STATUS.CREATED)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
expect(response.body.ok).toEqual(API_MESSAGE.PKG_CREATED);
|
expect(response.body.ok).toEqual(API_MESSAGE.PKG_CREATED);
|
||||||
|
@ -113,8 +93,7 @@ describe('publish', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should publish a new package', async () => {
|
test.each([['foo', '@scope/foo']])('should publish a new package', async (pkgName) => {
|
||||||
const pkgName = 'test';
|
|
||||||
const pkgMetadata = generatePackageMetadata(pkgName, '1.0.0');
|
const pkgMetadata = generatePackageMetadata(pkgName, '1.0.0');
|
||||||
const app = await initializeServer('publish.yaml');
|
const app = await initializeServer('publish.yaml');
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
|
@ -159,76 +138,121 @@ describe('publish', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
describe('proxies setup', () => {
|
||||||
|
test.each([['foo', '@scope%2Ffoo']])(
|
||||||
|
'should publish a a patch package that already exist on a remote',
|
||||||
|
async (pkgName) => {
|
||||||
|
const upstreamManifest = generateRemotePackageMetadata(
|
||||||
|
pkgName,
|
||||||
|
'1.0.0',
|
||||||
|
'https://registry.npmjs.org',
|
||||||
|
['1.0.1', '1.0.2', '1.0.3']
|
||||||
|
);
|
||||||
|
nock('https://registry.npmjs.org').get(`/${pkgName}`).reply(200, upstreamManifest);
|
||||||
|
const app = await initializeServer('publish-proxy.yaml');
|
||||||
|
const manifest = await getPackage(app, '', decodeURIComponent(pkgName));
|
||||||
|
expect(manifest.body.name).toEqual(decodeURIComponent(pkgName));
|
||||||
|
const response = await publishVersion(
|
||||||
|
app,
|
||||||
|
decodeURIComponent(pkgName),
|
||||||
|
'1.0.1-patch'
|
||||||
|
).expect(HTTP_STATUS.CREATED);
|
||||||
|
expect(response.body.ok).toEqual(API_MESSAGE.PKG_CREATED);
|
||||||
|
const response2 = await publishVersion(
|
||||||
|
app,
|
||||||
|
decodeURIComponent(pkgName),
|
||||||
|
'1.0.2-patch'
|
||||||
|
).expect(HTTP_STATUS.CREATED);
|
||||||
|
expect(response2.body.ok).toEqual(API_MESSAGE.PKG_CREATED);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('should fails on publish a duplicated package', async () => {
|
test.each([['foo', '@scope/foo']])(
|
||||||
|
'should fails on publish a duplicated package',
|
||||||
|
async (pkgName) => {
|
||||||
const app = await initializeServer('publish.yaml');
|
const app = await initializeServer('publish.yaml');
|
||||||
await publishVersion(app, 'foo', '1.0.0');
|
await publishVersion(app, pkgName, '1.0.0');
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
publishVersion(app, 'foo', '1.0.0')
|
publishVersion(app, pkgName, '1.0.0')
|
||||||
.expect(HTTP_STATUS.CONFLICT)
|
.expect(HTTP_STATUS.CONFLICT)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
expect(response.body.error).toEqual(API_ERROR.PACKAGE_EXIST);
|
expect(response.body.error).toEqual(API_ERROR.PACKAGE_EXIST);
|
||||||
resolve(response);
|
resolve(response);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
describe('unpublish a package', () => {
|
describe('unpublish a package', () => {
|
||||||
test('should unpublish entirely a package', async () => {
|
test.each([['foo', '@scope/foo']])('should unpublish entirely a package', async (pkgName) => {
|
||||||
const app = await initializeServer('publish.yaml');
|
const app = await initializeServer('publish.yaml');
|
||||||
await publishVersion(app, 'foo', '1.0.0');
|
await publishVersion(app, pkgName, '1.0.0');
|
||||||
const response = await supertest(app)
|
const response = await supertest(app)
|
||||||
// FIXME: should be filtered by revision to avoid
|
// FIXME: should be filtered by revision to avoid
|
||||||
// conflicts
|
// conflicts
|
||||||
.delete(`/${encodeURIComponent('foo')}/-rev/xxx`)
|
.delete(`/${encodeURIComponent(pkgName)}/-rev/xxx`)
|
||||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||||
.expect(HTTP_STATUS.CREATED);
|
.expect(HTTP_STATUS.CREATED);
|
||||||
expect(response.body.ok).toEqual(API_MESSAGE.PKG_REMOVED);
|
expect(response.body.ok).toEqual(API_MESSAGE.PKG_REMOVED);
|
||||||
// package should be completely un published
|
// package should be completely un published
|
||||||
await supertest(app)
|
await supertest(app)
|
||||||
.get('/foo')
|
.get(`/${pkgName}`)
|
||||||
.set('Accept', HEADERS.JSON)
|
.set('Accept', HEADERS.JSON)
|
||||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||||
.expect(HTTP_STATUS.NOT_FOUND);
|
.expect(HTTP_STATUS.NOT_FOUND);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should fails unpublish entirely a package', async () => {
|
test.each([['foo', '@scope/foo']])(
|
||||||
|
'should fails unpublish entirely a package',
|
||||||
|
async (pkgName) => {
|
||||||
const app = await initializeServer('publish.yaml');
|
const app = await initializeServer('publish.yaml');
|
||||||
const response = await supertest(app)
|
const response = await supertest(app)
|
||||||
.delete(`/${encodeURIComponent('foo')}/-rev/1cf3-fe3`)
|
.delete(`/${encodeURIComponent(pkgName)}/-rev/1cf3-fe3`)
|
||||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||||
.expect(HTTP_STATUS.NOT_FOUND);
|
.expect(HTTP_STATUS.NOT_FOUND);
|
||||||
expect(response.body.error).toEqual(API_ERROR.NO_PACKAGE);
|
expect(response.body.error).toEqual(API_ERROR.NO_PACKAGE);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
test('should fails remove a tarball of a package does not exist', async () => {
|
test.each([['foo', '@scope/foo']])(
|
||||||
|
'should fails remove a tarball of a package does not exist',
|
||||||
|
async (pkgName) => {
|
||||||
const app = await initializeServer('publish.yaml');
|
const app = await initializeServer('publish.yaml');
|
||||||
const response = await supertest(app)
|
const response = await supertest(app)
|
||||||
.delete(`/foo/-/foo-1.0.3.tgz/-rev/revision`)
|
.delete(`/${pkgName}/-/${basename(pkgName)}-1.0.3.tgz/-rev/revision`)
|
||||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||||
.expect(HTTP_STATUS.NOT_FOUND);
|
.expect(HTTP_STATUS.NOT_FOUND);
|
||||||
expect(response.body.error).toEqual(API_ERROR.NO_PACKAGE);
|
expect(response.body.error).toEqual(API_ERROR.NO_PACKAGE);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
test('should fails on try remove a tarball does not exist', async () => {
|
test.each([['foo', '@scope/foo']])(
|
||||||
|
'should fails on try remove a tarball does not exist',
|
||||||
|
async (pkgName) => {
|
||||||
const app = await initializeServer('publish.yaml');
|
const app = await initializeServer('publish.yaml');
|
||||||
await publishVersion(app, 'foo', '1.0.0');
|
await publishVersion(app, pkgName, '1.0.0');
|
||||||
const response = await supertest(app)
|
const response = await supertest(app)
|
||||||
.delete(`/foo/-/foo-1.0.3.tgz/-rev/revision`)
|
.delete(`/${pkgName}/-/${basename(pkgName)}-1.0.3.tgz/-rev/revision`)
|
||||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||||
.expect(HTTP_STATUS.NOT_FOUND);
|
.expect(HTTP_STATUS.NOT_FOUND);
|
||||||
expect(response.body.error).toEqual(API_ERROR.NO_SUCH_FILE);
|
expect(response.body.error).toEqual(API_ERROR.NO_SUCH_FILE);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
test('should remove a tarball that does exist', async () => {
|
test.each([['foo', '@scope/foo']])(
|
||||||
|
'should remove a tarball that does exist',
|
||||||
|
async (pkgName) => {
|
||||||
const app = await initializeServer('publish.yaml');
|
const app = await initializeServer('publish.yaml');
|
||||||
await publishVersion(app, 'foo', '1.0.0');
|
await publishVersion(app, pkgName, '1.0.0');
|
||||||
const response = await supertest(app)
|
const response = await supertest(app)
|
||||||
.delete(`/foo/-/foo-1.0.0.tgz/-rev/revision`)
|
.delete(`/${pkgName}/-/${basename(pkgName)}-1.0.0.tgz/-rev/revision`)
|
||||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||||
.expect(HTTP_STATUS.CREATED);
|
.expect(HTTP_STATUS.CREATED);
|
||||||
expect(response.body.ok).toEqual(API_MESSAGE.TARBALL_REMOVED);
|
expect(response.body.ok).toEqual(API_MESSAGE.TARBALL_REMOVED);
|
||||||
});
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('star a package', () => {});
|
describe('star a package', () => {});
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
import nock from 'nock';
|
|
||||||
import supertest from 'supertest';
|
import supertest from 'supertest';
|
||||||
|
|
||||||
import { API_ERROR, HEADERS, HEADER_TYPE, HTTP_STATUS, TOKEN_BEARER } from '@verdaccio/core';
|
import { API_ERROR, HEADERS, HEADER_TYPE, HTTP_STATUS, TOKEN_BEARER } from '@verdaccio/core';
|
||||||
import { generateRemotePackageMetadata } from '@verdaccio/test-helper';
|
|
||||||
import { buildToken } from '@verdaccio/utils';
|
import { buildToken } from '@verdaccio/utils';
|
||||||
|
|
||||||
import { createUser, getPackage, initializeServer } from './_helper';
|
import { createUser, getPackage, initializeServer } from './_helper';
|
||||||
|
@ -13,13 +11,6 @@ describe('token', () => {
|
||||||
describe('basics', () => {
|
describe('basics', () => {
|
||||||
const FAKE_TOKEN: string = buildToken(TOKEN_BEARER, 'fake');
|
const FAKE_TOKEN: string = buildToken(TOKEN_BEARER, 'fake');
|
||||||
test.each([['user.yaml'], ['user.jwt.yaml']])('should test add a new user', async (conf) => {
|
test.each([['user.yaml'], ['user.jwt.yaml']])('should test add a new user', async (conf) => {
|
||||||
const upstreamManifest = generateRemotePackageMetadata(
|
|
||||||
'vue',
|
|
||||||
'1.0.0',
|
|
||||||
'https://registry.verdaccio.org'
|
|
||||||
);
|
|
||||||
nock('https://registry.verdaccio.org').get(`/vue`).reply(201, upstreamManifest);
|
|
||||||
|
|
||||||
const app = await initializeServer(conf);
|
const app = await initializeServer(conf);
|
||||||
const credentials = { name: 'JotaJWT', password: 'secretPass' };
|
const credentials = { name: 'JotaJWT', password: 'secretPass' };
|
||||||
const response = await createUser(app, credentials.name, credentials.password);
|
const response = await createUser(app, credentials.name, credentials.password);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Command, Option } from 'clipanion';
|
import { Command, Option } from 'clipanion';
|
||||||
|
|
||||||
import { findConfigFile, parseConfigFile } from '@verdaccio/config';
|
import { findConfigFile, parseConfigFile } from '@verdaccio/config';
|
||||||
|
import { warningUtils } from '@verdaccio/core';
|
||||||
import { logger, setup } from '@verdaccio/logger';
|
import { logger, setup } from '@verdaccio/logger';
|
||||||
import { initServer } from '@verdaccio/node-api';
|
import { initServer } from '@verdaccio/node-api';
|
||||||
import { ConfigYaml, LoggerConfigItem } from '@verdaccio/types';
|
import { ConfigYaml, LoggerConfigItem } from '@verdaccio/types';
|
||||||
|
@ -45,17 +46,13 @@ export class InitCommand extends Command {
|
||||||
});
|
});
|
||||||
|
|
||||||
private initLogger(logConfig: ConfigYaml) {
|
private initLogger(logConfig: ConfigYaml) {
|
||||||
try {
|
|
||||||
// @ts-expect-error
|
// @ts-expect-error
|
||||||
if (logConfig.logs) {
|
if (logConfig.logs) {
|
||||||
throw Error(
|
// @ts-expect-error
|
||||||
'the property config "logs" property is longer supported, rename to "log" and use object instead'
|
logConfig.log = logConfig.logs;
|
||||||
);
|
warningUtils.emit(warningUtils.Codes.VERWAR002);
|
||||||
}
|
}
|
||||||
setup(logConfig.log as LoggerConfigItem);
|
setup(logConfig.log as LoggerConfigItem);
|
||||||
} catch (err: any) {
|
|
||||||
throw new Error(err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async execute() {
|
public async execute() {
|
||||||
|
|
|
@ -6,12 +6,19 @@ const verdaccioDeprecation = 'VerdaccioDeprecation';
|
||||||
|
|
||||||
export enum Codes {
|
export enum Codes {
|
||||||
VERWAR001 = 'VERWAR001',
|
VERWAR001 = 'VERWAR001',
|
||||||
|
VERWAR002 = 'VERWAR002',
|
||||||
VERWAR003 = 'VERWAR003',
|
VERWAR003 = 'VERWAR003',
|
||||||
VERWAR004 = 'VERWAR004',
|
VERWAR004 = 'VERWAR004',
|
||||||
// deprecation warnings
|
// deprecation warnings
|
||||||
VERDEP003 = 'VERDEP003',
|
VERDEP003 = 'VERDEP003',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
warningInstance.create(
|
||||||
|
verdaccioWarning,
|
||||||
|
Codes.VERWAR002,
|
||||||
|
`The property config "logs" property is longer supported, rename to "log" and use object instead`
|
||||||
|
);
|
||||||
|
|
||||||
warningInstance.create(
|
warningInstance.create(
|
||||||
verdaccioWarning,
|
verdaccioWarning,
|
||||||
Codes.VERWAR001,
|
Codes.VERWAR001,
|
||||||
|
|
|
@ -181,6 +181,7 @@ export interface FullRemoteManifest {
|
||||||
homepage?: string;
|
homepage?: string;
|
||||||
repository?: string | { type?: string; url: string; directory?: string };
|
repository?: string | { type?: string; url: string; directory?: string };
|
||||||
keywords?: string[];
|
keywords?: string[];
|
||||||
|
author?: string | Author;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Manifest extends FullRemoteManifest, PublishManifest {
|
export interface Manifest extends FullRemoteManifest, PublishManifest {
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import buildDebug from 'debug';
|
import buildDebug from 'debug';
|
||||||
import _, { isEmpty, isNil } from 'lodash';
|
import _, { isEmpty, isNil } from 'lodash';
|
||||||
import { PassThrough, Readable, Transform, Writable, pipeline as streamPipeline } from 'stream';
|
import { basename } from 'path';
|
||||||
|
import { PassThrough, Readable, Transform, pipeline as streamPipeline } from 'stream';
|
||||||
import { pipeline } from 'stream/promises';
|
import { pipeline } from 'stream/promises';
|
||||||
import { default as URL } from 'url';
|
import { default as URL } from 'url';
|
||||||
|
|
||||||
|
@ -961,7 +962,7 @@ class Storage {
|
||||||
// if (typeof storage === 'undefined') {
|
// if (typeof storage === 'undefined') {
|
||||||
// throw errorUtils.getNotFound();
|
// throw errorUtils.getNotFound();
|
||||||
// }
|
// }
|
||||||
throw errorUtils.getInternalError('no implemenation ready for npm deprecate');
|
throw errorUtils.getInternalError('no implementation ready for npm deprecate');
|
||||||
}
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
@ -972,7 +973,7 @@ class Storage {
|
||||||
// throw errorUtils.getNotFound();
|
// throw errorUtils.getNotFound();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
throw errorUtils.getInternalError('no implemenation ready for npm star');
|
throw errorUtils.getInternalError('no implementation ready for npm star');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1060,9 +1061,9 @@ class Storage {
|
||||||
debug('%s version %s already exists', name, versionToPublish);
|
debug('%s version %s already exists', name, versionToPublish);
|
||||||
throw errorUtils.getConflict();
|
throw errorUtils.getConflict();
|
||||||
}
|
}
|
||||||
|
const uplinksLook = this.config?.publish?.allow_offline === false;
|
||||||
// if execution get here, package does not exist locally, we search upstream
|
// if execution get here, package does not exist locally, we search upstream
|
||||||
const remoteManifest = await this.checkPackageRemote(name, this.isAllowPublishOffline());
|
const remoteManifest = await this.checkPackageRemote(name, uplinksLook);
|
||||||
if (remoteManifest?.versions[versionToPublish] != null) {
|
if (remoteManifest?.versions[versionToPublish] != null) {
|
||||||
debug('%s version %s already exists', name, versionToPublish);
|
debug('%s version %s already exists', name, versionToPublish);
|
||||||
throw errorUtils.getConflict();
|
throw errorUtils.getConflict();
|
||||||
|
@ -1112,7 +1113,7 @@ class Storage {
|
||||||
// 3. upload the tarball to the storage
|
// 3. upload the tarball to the storage
|
||||||
try {
|
try {
|
||||||
const readable = Readable.from(buffer);
|
const readable = Readable.from(buffer);
|
||||||
await this.uploadTarball(name, firstAttachmentKey, readable, {
|
await this.uploadTarball(name, basename(firstAttachmentKey), readable, {
|
||||||
signal: options.signal,
|
signal: options.signal,
|
||||||
});
|
});
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
@ -1148,18 +1149,17 @@ class Storage {
|
||||||
* @param options
|
* @param options
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
public async uploadTarball(
|
public uploadTarball(
|
||||||
name: string,
|
name: string,
|
||||||
fileName: string,
|
fileName: string,
|
||||||
contentReadable: Readable,
|
contentReadable: Readable,
|
||||||
{ signal }
|
{ signal }
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
(async () => {
|
this.uploadTarballAsStream(name, fileName, {
|
||||||
const stream: Writable = await this.uploadTarballAsStream(name, fileName, {
|
|
||||||
signal,
|
signal,
|
||||||
});
|
})
|
||||||
|
.then((stream) => {
|
||||||
stream.on('error', (err) => {
|
stream.on('error', (err) => {
|
||||||
debug(
|
debug(
|
||||||
'error on stream a tarball %o for %o with error %o',
|
'error on stream a tarball %o for %o with error %o',
|
||||||
|
@ -1172,19 +1172,23 @@ class Storage {
|
||||||
stream.on('success', () => {
|
stream.on('success', () => {
|
||||||
this.logger.debug(
|
this.logger.debug(
|
||||||
{ fileName, name },
|
{ fileName, name },
|
||||||
'file @{fileName} for package @{name} has been succesfully uploaded'
|
'file @{fileName} for package @{name} has been successfully uploaded'
|
||||||
);
|
);
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
|
return stream;
|
||||||
await pipeline(contentReadable, stream, { signal });
|
})
|
||||||
})().catch((err) => {
|
.then((stream) => {
|
||||||
reject(err);
|
pipeline(contentReadable, stream, { signal })
|
||||||
|
.then(() => {
|
||||||
|
debug('success pipe upload tarball');
|
||||||
|
})
|
||||||
|
.catch(reject);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async uploadTarballAsStream(
|
private async uploadTarballAsStream(
|
||||||
pkgName: string,
|
pkgName: string,
|
||||||
filename: string,
|
filename: string,
|
||||||
{ signal }
|
{ signal }
|
||||||
|
@ -1395,14 +1399,6 @@ class Storage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private isAllowPublishOffline(): boolean {
|
|
||||||
return (
|
|
||||||
typeof this.config.publish !== 'undefined' &&
|
|
||||||
_.isBoolean(this.config.publish.allow_offline) &&
|
|
||||||
this.config.publish.allow_offline
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param name package name
|
* @param name package name
|
||||||
|
@ -1567,8 +1563,8 @@ class Storage {
|
||||||
|
|
||||||
A package requires uplinks syncronization if enables the proxy section, uplinks
|
A package requires uplinks syncronization if enables the proxy section, uplinks
|
||||||
can be more than one, the more are the most slow request will take, the request
|
can be more than one, the more are the most slow request will take, the request
|
||||||
are made in serie and if 1st call fails, the second will be triggered, otherwise
|
are made in serial and if 1st call fails, the second will be triggered, otherwise
|
||||||
the 1st will reply and others will be discareded. The order is important.
|
the 1st will reply and others will be discarded. The order is important.
|
||||||
|
|
||||||
Errors on upkinks are considered are, time outs, connection fails and http status 304,
|
Errors on upkinks are considered are, time outs, connection fails and http status 304,
|
||||||
in that case the request returns empty body and we want ask next on the list if has fresh
|
in that case the request returns empty body and we want ask next on the list if has fresh
|
||||||
|
|
|
@ -668,8 +668,9 @@ describe('storage', () => {
|
||||||
});
|
});
|
||||||
test.todo('should handle double proxy with last one success');
|
test.todo('should handle double proxy with last one success');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('options', () => {
|
describe('options', () => {
|
||||||
test('should handle disable uplinks via options.uplinksLook=false', async () => {
|
test('should handle disable uplinks via options.uplinksLook=false with cache', async () => {
|
||||||
const fooManifest = generatePackageMetadata('foo', '8.0.0');
|
const fooManifest = generatePackageMetadata('foo', '8.0.0');
|
||||||
nock('https://registry.verdaccio.org').get('/foo').reply(201, manifestFooRemoteNpmjs);
|
nock('https://registry.verdaccio.org').get('/foo').reply(201, manifestFooRemoteNpmjs);
|
||||||
const config = new Config(
|
const config = new Config(
|
||||||
|
@ -691,6 +692,35 @@ describe('storage', () => {
|
||||||
expect((response as Manifest).name).toEqual(fooManifest.name);
|
expect((response as Manifest).name).toEqual(fooManifest.name);
|
||||||
expect((response as Manifest)[DIST_TAGS].latest).toEqual('8.0.0');
|
expect((response as Manifest)[DIST_TAGS].latest).toEqual('8.0.0');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should handle disable uplinks via options.uplinksLook=false without cache', async () => {
|
||||||
|
const fooRemoteManifest = generateRemotePackageMetadata(
|
||||||
|
'foo',
|
||||||
|
'9.0.0',
|
||||||
|
'https://registry.verdaccio.org',
|
||||||
|
['9.0.0', '9.0.1', '9.0.2', '9.0.3']
|
||||||
|
);
|
||||||
|
nock('https://registry.verdaccio.org').get('/foo').reply(201, fooRemoteManifest);
|
||||||
|
const config = new Config(
|
||||||
|
configExample(
|
||||||
|
{
|
||||||
|
...getDefaultConfig(),
|
||||||
|
storage: generateRandomStorage(),
|
||||||
|
},
|
||||||
|
'./fixtures/config/syncSingleUplinksMetadata.yaml',
|
||||||
|
__dirname
|
||||||
|
)
|
||||||
|
);
|
||||||
|
const storage = new Storage(config);
|
||||||
|
await storage.init(config);
|
||||||
|
|
||||||
|
const [response] = await storage.syncUplinksMetadataNext('foo', null, {
|
||||||
|
uplinksLook: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect((response as Manifest).name).toEqual('foo');
|
||||||
|
expect((response as Manifest)[DIST_TAGS].latest).toEqual('9.0.0');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
10
packages/tools/helpers/jest.config.js
Normal file
10
packages/tools/helpers/jest.config.js
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
const config = require('../../../jest/config');
|
||||||
|
|
||||||
|
module.exports = Object.assign({}, config, {
|
||||||
|
coverageThreshold: {
|
||||||
|
global: {
|
||||||
|
// FIXME: increase to 90
|
||||||
|
lines: 50,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
|
@ -1,4 +1,4 @@
|
||||||
import { GenericBody, Manifest } from '@verdaccio/types';
|
import { FullRemoteManifest, GenericBody, Manifest, Version, Versions } from '@verdaccio/types';
|
||||||
|
|
||||||
export interface DistTags {
|
export interface DistTags {
|
||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
|
@ -49,7 +49,7 @@ export function addNewVersion(
|
||||||
};
|
};
|
||||||
// update the latest with the new version
|
// update the latest with the new version
|
||||||
newManifest['dist-tags'] = { latest: version };
|
newManifest['dist-tags'] = { latest: version };
|
||||||
// add new version does not need attachmetns
|
// add new version does not need attachments
|
||||||
if (isRemote) {
|
if (isRemote) {
|
||||||
newManifest._distfiles = {
|
newManifest._distfiles = {
|
||||||
...newManifest._distfiles,
|
...newManifest._distfiles,
|
||||||
|
@ -137,16 +137,12 @@ export function generateLocalPackageMetadata(
|
||||||
export function generateRemotePackageMetadata(
|
export function generateRemotePackageMetadata(
|
||||||
pkgName: string,
|
pkgName: string,
|
||||||
version = '1.0.0',
|
version = '1.0.0',
|
||||||
domain: string = 'http://localhost:5555'
|
domain: string = 'http://localhost:5555',
|
||||||
): Manifest {
|
versions: string[] = []
|
||||||
|
): FullRemoteManifest {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return {
|
const generateVersion = (version: string): Version => {
|
||||||
_id: pkgName,
|
const metadata = {
|
||||||
name: pkgName,
|
|
||||||
description: '',
|
|
||||||
'dist-tags': { ['latest']: version },
|
|
||||||
versions: {
|
|
||||||
[version]: {
|
|
||||||
name: pkgName,
|
name: pkgName,
|
||||||
version: version,
|
version: version,
|
||||||
description: 'package generated',
|
description: 'package generated',
|
||||||
|
@ -177,18 +173,47 @@ export function generateRemotePackageMetadata(
|
||||||
shasum: '2c03764f651a9f016ca0b7620421457b619151b9', // pragma: allowlist secret
|
shasum: '2c03764f651a9f016ca0b7620421457b619151b9', // pragma: allowlist secret
|
||||||
tarball: `${domain}\/${pkgName}\/-\/${getTarball(pkgName)}-${version}.tgz`,
|
tarball: `${domain}\/${pkgName}\/-\/${getTarball(pkgName)}-${version}.tgz`,
|
||||||
},
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
return metadata;
|
||||||
|
};
|
||||||
|
const mappedVersions: Versions = versions.reduce((acc, v) => {
|
||||||
|
acc[v] = generateVersion(v);
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const mappedTimes: GenericBody = versions.reduce((acc, v) => {
|
||||||
|
const date = new Date(Date.now());
|
||||||
|
acc[v] = date.toISOString();
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
return {
|
||||||
|
_id: pkgName,
|
||||||
|
name: pkgName,
|
||||||
|
description: '',
|
||||||
|
'dist-tags': { ['latest']: version },
|
||||||
|
versions: {
|
||||||
|
[version]: generateVersion(version),
|
||||||
|
...mappedVersions,
|
||||||
},
|
},
|
||||||
|
time: {
|
||||||
|
modified: '2019-06-13T06:44:45.747Z',
|
||||||
|
created: '2019-06-13T06:44:45.747Z',
|
||||||
|
[version]: '2019-06-13T06:44:45.747Z',
|
||||||
|
...mappedTimes,
|
||||||
|
},
|
||||||
|
maintainers: [
|
||||||
|
{
|
||||||
|
name: 'foo',
|
||||||
|
email: 'foo@foo.com',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
author: {
|
||||||
|
name: 'foo',
|
||||||
},
|
},
|
||||||
readme: '# test',
|
readme: '# test',
|
||||||
_attachments: {},
|
_rev: '12-c8fe8a9c79fa57a87347a0213e6f2548',
|
||||||
_uplinks: {},
|
|
||||||
_distfiles: {
|
|
||||||
[`${pkgName}-${version}.tgz`]: {
|
|
||||||
url: `${domain}/${pkgName}\/-\/${getTarball(pkgName)}-${version}.tgz`,
|
|
||||||
sha: '2c03764f651a9f016ca0b7620421457b619151b9', // pragma: allowlist secret
|
|
||||||
},
|
|
||||||
},
|
|
||||||
_rev: '',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
const config = require('../../jest/config');
|
|
||||||
|
|
||||||
module.exports = Object.assign({}, config, {});
|
|
|
@ -35,27 +35,27 @@ describe('generate metadata', () => {
|
||||||
});
|
});
|
||||||
describe('generateRemotePackageMetadata', () => {
|
describe('generateRemotePackageMetadata', () => {
|
||||||
test('should generate package metadata', () => {
|
test('should generate package metadata', () => {
|
||||||
const m = generateRemotePackageMetadata('foo', '1.0.0', 'https://registry.verdaccio.org');
|
expect(
|
||||||
|
generateRemotePackageMetadata('foo', '1.0.0', 'https://registry.verdaccio.org')
|
||||||
|
).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should generate package metadata with multiple versions', () => {
|
||||||
|
const m = generateRemotePackageMetadata('foo', '1.0.0', 'https://registry.verdaccio.org', [
|
||||||
|
'1.0.1',
|
||||||
|
'1.0.2',
|
||||||
|
'3.0.0',
|
||||||
|
]);
|
||||||
expect(m).toBeDefined();
|
expect(m).toBeDefined();
|
||||||
expect(m._attachments).toEqual({});
|
expect(Object.keys(m.versions)).toEqual(['1.0.0', '1.0.1', '1.0.2', '3.0.0']);
|
||||||
expect(m._distfiles['foo-1.0.0.tgz']).toEqual({
|
expect(Object.keys(m.time)).toEqual([
|
||||||
sha: '2c03764f651a9f016ca0b7620421457b619151b9',
|
'modified',
|
||||||
url: 'https://registry.verdaccio.org/foo/-/foo-1.0.0.tgz',
|
'created',
|
||||||
});
|
'1.0.0',
|
||||||
});
|
'1.0.1',
|
||||||
test('should add new versions remote', () => {
|
'1.0.2',
|
||||||
const manifest = generateRemotePackageMetadata('foo', '1.0.0');
|
'3.0.0',
|
||||||
const m1 = addNewVersion(manifest, '1.0.1', true);
|
]);
|
||||||
expect(Object.keys(m1._attachments)).toEqual([]);
|
|
||||||
expect(Object.keys(m1._distfiles)).toEqual(['foo-1.0.0.tgz', 'foo-1.0.1.tgz']);
|
|
||||||
const m2 = addNewVersion(m1, '1.0.2');
|
|
||||||
expect(Object.keys(m2.versions)).toEqual(['1.0.0', '1.0.1', '1.0.2']);
|
|
||||||
expect(m2['dist-tags'].latest).toEqual('1.0.2');
|
|
||||||
expect(m2._distfiles['foo-1.0.2.tgz']).toEqual({
|
|
||||||
sha: '2c03764f651a9f016ca0b7620421457b619151b9',
|
|
||||||
url: 'http://localhost:5555/foo/-/foo-1.0.2.tgz',
|
|
||||||
});
|
|
||||||
expect(Object.keys(m2._attachments)).toEqual([]);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
describe('generateLocalPackageMetadata', () => {
|
describe('generateLocalPackageMetadata', () => {
|
||||||
|
|
3
packages/tools/local-publish/.babelrc
Normal file
3
packages/tools/local-publish/.babelrc
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"extends": "../../../.babelrc"
|
||||||
|
}
|
3
packages/tools/local-publish/.eslintignore
Normal file
3
packages/tools/local-publish/.eslintignore
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
node_modules
|
||||||
|
coverage/
|
||||||
|
lib/
|
1
packages/tools/local-publish/.gitignore
vendored
Normal file
1
packages/tools/local-publish/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
lib/
|
21
packages/tools/local-publish/LICENSE
Normal file
21
packages/tools/local-publish/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 Verdaccio
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
22
packages/tools/local-publish/package.json
Normal file
22
packages/tools/local-publish/package.json
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"name": "@verdaccio/local-publish",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"description": "trigger server for local development",
|
||||||
|
"author": "Juan Picado <juanpicado19@gmail.com>",
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://verdaccio.org",
|
||||||
|
"main": "build/index.js",
|
||||||
|
"types": "build/index.d.ts",
|
||||||
|
"devDependencies": {
|
||||||
|
"@verdaccio/types": "workspace:11.0.0-6-next.15",
|
||||||
|
"@verdaccio/core": "workspace:6.0.0-6-next.7",
|
||||||
|
"@verdaccio/config": "workspace:6.0.0-6-next.16",
|
||||||
|
"verdaccio": "5.14.0",
|
||||||
|
"ts-node": "10.9.1"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "ts-node src/index.ts",
|
||||||
|
"build": "echo 0"
|
||||||
|
}
|
||||||
|
}
|
5
packages/tools/local-publish/src/.eslintrc
Normal file
5
packages/tools/local-publish/src/.eslintrc
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"rules": {
|
||||||
|
"no-console": 0
|
||||||
|
}
|
||||||
|
}
|
52
packages/tools/local-publish/src/index.ts
Normal file
52
packages/tools/local-publish/src/index.ts
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import { runServer } from 'verdaccio';
|
||||||
|
|
||||||
|
import { ConfigBuilder } from '@verdaccio/config';
|
||||||
|
import { constants, fileUtils } from '@verdaccio/core';
|
||||||
|
|
||||||
|
fileUtils
|
||||||
|
.createTempFolder('test')
|
||||||
|
.then((folderPath) => {
|
||||||
|
const configuration = ConfigBuilder.build({
|
||||||
|
storage: folderPath,
|
||||||
|
// @ts-ignore
|
||||||
|
logs: { level: 'info', type: 'stdout', format: 'pretty' },
|
||||||
|
uplinks: {},
|
||||||
|
packages: {},
|
||||||
|
self_path: folderPath,
|
||||||
|
})
|
||||||
|
.addUplink('npmjs', { url: 'https://registry.npmjs.org' })
|
||||||
|
.addPackageAccess('@verdaccio/*', {
|
||||||
|
access: constants.ROLES.$ANONYMOUS,
|
||||||
|
publish: constants.ROLES.$ANONYMOUS,
|
||||||
|
})
|
||||||
|
.addPackageAccess(constants.PACKAGE_ACCESS.SCOPE, {
|
||||||
|
access: constants.ROLES.$ANONYMOUS,
|
||||||
|
publish: constants.ROLES.$ANONYMOUS,
|
||||||
|
proxy: 'npmjs',
|
||||||
|
})
|
||||||
|
.addPackageAccess('verdaccio', {
|
||||||
|
access: constants.ROLES.$ANONYMOUS,
|
||||||
|
publish: constants.ROLES.$ANONYMOUS,
|
||||||
|
})
|
||||||
|
.addPackageAccess('verdaccio-*', {
|
||||||
|
access: constants.ROLES.$ANONYMOUS,
|
||||||
|
publish: constants.ROLES.$ANONYMOUS,
|
||||||
|
})
|
||||||
|
.addPackageAccess(constants.PACKAGE_ACCESS.ALL, {
|
||||||
|
access: constants.ROLES.$ALL,
|
||||||
|
publish: constants.ROLES.$ALL,
|
||||||
|
proxy: 'npmjs',
|
||||||
|
})
|
||||||
|
.addAuth({
|
||||||
|
htpasswd: {
|
||||||
|
file: './htpasswd',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return runServer(configuration.getConfig());
|
||||||
|
})
|
||||||
|
.then((app: any) => {
|
||||||
|
app.listen(4873, () => {
|
||||||
|
console.log('running verdaccio@5 server');
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(console.error);
|
10
packages/tools/local-publish/tsconfig.build.json
Normal file
10
packages/tools/local-publish/tsconfig.build.json
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"extends": "../../../tsconfig.base.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./build",
|
||||||
|
"preserveSymlinks": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts"],
|
||||||
|
"exclude": ["src/**/*.test.ts"]
|
||||||
|
}
|
18
packages/tools/local-publish/tsconfig.json
Normal file
18
packages/tools/local-publish/tsconfig.json
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"extends": "../../../tsconfig.reference.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"rootDir": "./src",
|
||||||
|
"outDir": "./build",
|
||||||
|
"composite": true,
|
||||||
|
"declaration": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*.ts"],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "../../config"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "../../core/core"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
1467
pnpm-lock.yaml
generated
1467
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,10 @@
|
||||||
export { getDefaultConfig } from '@verdaccio/config';
|
export { getDefaultConfig } from '@verdaccio/config';
|
||||||
export { initialSetup } from './registry';
|
export { initialSetup } from './registry';
|
||||||
export { addNpmPrefix, addYarnClassicPrefix, addRegistry, prepareYarnModernProject } from './utils';
|
export {
|
||||||
|
addNpmPrefix,
|
||||||
|
addYarnClassicPrefix,
|
||||||
|
addRegistry,
|
||||||
|
prepareYarnModernProject,
|
||||||
|
prepareGenericEmptyProject,
|
||||||
|
} from './utils';
|
||||||
export { exec, ExecOutput } from './process';
|
export { exec, ExecOutput } from './process';
|
||||||
|
|
|
@ -3,6 +3,7 @@ import buildDebug from 'debug';
|
||||||
import { Registry } from 'verdaccio';
|
import { Registry } from 'verdaccio';
|
||||||
|
|
||||||
import { getDefaultConfig } from '@verdaccio/config';
|
import { getDefaultConfig } from '@verdaccio/config';
|
||||||
|
import { ConfigYaml } from '@verdaccio/types';
|
||||||
|
|
||||||
const debug = buildDebug('verdaccio:e2e:registry-utils');
|
const debug = buildDebug('verdaccio:e2e:registry-utils');
|
||||||
|
|
||||||
|
@ -11,8 +12,10 @@ export type Setup = {
|
||||||
tempFolder: string;
|
tempFolder: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function initialSetup(): Promise<Setup> {
|
export async function initialSetup(customConfig?: ConfigYaml): Promise<Setup> {
|
||||||
const { configPath, tempFolder } = await Registry.fromConfigToPath(getDefaultConfig());
|
const { configPath, tempFolder } = await Registry.fromConfigToPath({
|
||||||
|
...(customConfig ? customConfig : { ...getDefaultConfig(), _debug: true }),
|
||||||
|
});
|
||||||
debug(`configPath %o`, configPath);
|
debug(`configPath %o`, configPath);
|
||||||
debug(`tempFolder %o`, tempFolder);
|
debug(`tempFolder %o`, tempFolder);
|
||||||
const registry = new Registry(configPath);
|
const registry = new Registry(configPath);
|
||||||
|
|
|
@ -61,3 +61,43 @@ export async function prepareYarnModernProject(
|
||||||
await cp(yarnPath, join(tempFolder, '.yarn/releases/yarn.js'), { dereference: true });
|
await cp(yarnPath, join(tempFolder, '.yarn/releases/yarn.js'), { dereference: true });
|
||||||
return { tempFolder };
|
return { tempFolder };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function prepareGenericEmptyProject(
|
||||||
|
packageName: string,
|
||||||
|
version: string,
|
||||||
|
port: number,
|
||||||
|
token: string,
|
||||||
|
registryDomain: string
|
||||||
|
) {
|
||||||
|
const getPackageJSON = (packageName, version = '1.0.0') => {
|
||||||
|
const json = {
|
||||||
|
name: packageName,
|
||||||
|
version,
|
||||||
|
description: 'some cool project',
|
||||||
|
main: 'index.js',
|
||||||
|
scripts: {
|
||||||
|
test: 'echo exit 1',
|
||||||
|
},
|
||||||
|
keywords: ['foo', 'bar'],
|
||||||
|
author: 'Juan Picado <jotadeveloper@gmail.com>',
|
||||||
|
license: 'MIT',
|
||||||
|
};
|
||||||
|
return JSON.stringify(json);
|
||||||
|
};
|
||||||
|
const getREADME = (packageName) => `
|
||||||
|
# My README ${packageName}
|
||||||
|
|
||||||
|
some text
|
||||||
|
|
||||||
|
## subtitle
|
||||||
|
|
||||||
|
more text
|
||||||
|
`;
|
||||||
|
const getNPMrc = (port, token, registry) => `//localhost:${port}/:_authToken=${token}
|
||||||
|
registry=${registry}`;
|
||||||
|
const tempFolder = await createTempFolder('temp-folder');
|
||||||
|
await writeFile(join(tempFolder, 'package.json'), getPackageJSON(packageName, version));
|
||||||
|
await writeFile(join(tempFolder, 'README.md'), getREADME(packageName));
|
||||||
|
await writeFile(join(tempFolder, '.npmrc'), getNPMrc(port, token, registryDomain));
|
||||||
|
return { tempFolder };
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ describe('install a package', () => {
|
||||||
|
|
||||||
test('should run npm info json body', async () => {
|
test('should run npm info json body', async () => {
|
||||||
const resp = await npm(
|
const resp = await npm(
|
||||||
|
{},
|
||||||
'info',
|
'info',
|
||||||
'verdaccio',
|
'verdaccio',
|
||||||
'--json',
|
'--json',
|
||||||
|
|
41
test/cli/e2e-npm6/publish.spec.ts
Normal file
41
test/cli/e2e-npm6/publish.spec.ts
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { addRegistry, initialSetup, prepareGenericEmptyProject } from '@verdaccio/test-cli-commons';
|
||||||
|
|
||||||
|
import { npm } from './utils';
|
||||||
|
|
||||||
|
describe('install a package', () => {
|
||||||
|
jest.setTimeout(10000);
|
||||||
|
let registry;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const setup = await initialSetup();
|
||||||
|
registry = setup.registry;
|
||||||
|
await registry.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each([['verdaccio-memory', 'verdaccio', '@verdaccio/foo', '@verdaccio/some-foo']])(
|
||||||
|
'should publish a package %s',
|
||||||
|
async (pkgName) => {
|
||||||
|
const { tempFolder } = await prepareGenericEmptyProject(
|
||||||
|
pkgName,
|
||||||
|
'1.0.0-patch',
|
||||||
|
registry.port,
|
||||||
|
registry.getToken(),
|
||||||
|
registry.getRegistryUrl()
|
||||||
|
);
|
||||||
|
const resp = await npm(
|
||||||
|
{ cwd: tempFolder },
|
||||||
|
'publish',
|
||||||
|
'--json',
|
||||||
|
...addRegistry(registry.getRegistryUrl())
|
||||||
|
);
|
||||||
|
const parsedBody = JSON.parse(resp.stdout as string);
|
||||||
|
expect(parsedBody.name).toEqual(pkgName);
|
||||||
|
expect(parsedBody.files).toBeDefined();
|
||||||
|
expect(parsedBody.files).toBeDefined();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
registry.stop();
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { SpawnOptions } from 'child_process';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
||||||
import { exec } from '@verdaccio/test-cli-commons';
|
import { exec } from '@verdaccio/test-cli-commons';
|
||||||
|
@ -6,6 +7,6 @@ export function getCommand() {
|
||||||
return join(__dirname, './node_modules/.bin/npm');
|
return join(__dirname, './node_modules/.bin/npm');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function npm(...args: string[]) {
|
export function npm(options: SpawnOptions, ...args: string[]) {
|
||||||
return exec({}, getCommand(), args);
|
return exec(options, getCommand(), args);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ describe('install a package', () => {
|
||||||
|
|
||||||
test('should run npm info json body', async () => {
|
test('should run npm info json body', async () => {
|
||||||
const resp = await npm(
|
const resp = await npm(
|
||||||
|
{},
|
||||||
'info',
|
'info',
|
||||||
'verdaccio',
|
'verdaccio',
|
||||||
'--json',
|
'--json',
|
||||||
|
|
41
test/cli/e2e-npm7/publish.spec.ts
Normal file
41
test/cli/e2e-npm7/publish.spec.ts
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { addRegistry, initialSetup, prepareGenericEmptyProject } from '@verdaccio/test-cli-commons';
|
||||||
|
|
||||||
|
import { npm } from './utils';
|
||||||
|
|
||||||
|
describe('install a package', () => {
|
||||||
|
jest.setTimeout(10000);
|
||||||
|
let registry;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const setup = await initialSetup();
|
||||||
|
registry = setup.registry;
|
||||||
|
await registry.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each([['verdaccio-memory', 'verdaccio', '@verdaccio/foo', '@verdaccio/some-foo']])(
|
||||||
|
'should publish a package %s',
|
||||||
|
async (pkgName) => {
|
||||||
|
const { tempFolder } = await prepareGenericEmptyProject(
|
||||||
|
pkgName,
|
||||||
|
'1.0.0-patch',
|
||||||
|
registry.port,
|
||||||
|
registry.getToken(),
|
||||||
|
registry.getRegistryUrl()
|
||||||
|
);
|
||||||
|
const resp = await npm(
|
||||||
|
{ cwd: tempFolder },
|
||||||
|
'publish',
|
||||||
|
'--json',
|
||||||
|
...addRegistry(registry.getRegistryUrl())
|
||||||
|
);
|
||||||
|
const parsedBody = JSON.parse(resp.stdout as string);
|
||||||
|
expect(parsedBody.name).toEqual(pkgName);
|
||||||
|
expect(parsedBody.files).toBeDefined();
|
||||||
|
expect(parsedBody.files).toBeDefined();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
registry.stop();
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { SpawnOptions } from 'child_process';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
||||||
import { exec } from '@verdaccio/test-cli-commons';
|
import { exec } from '@verdaccio/test-cli-commons';
|
||||||
|
@ -6,6 +7,6 @@ export function getCommand() {
|
||||||
return join(__dirname, './node_modules/.bin/npm');
|
return join(__dirname, './node_modules/.bin/npm');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function npm(...args: string[]) {
|
export function npm(options: SpawnOptions, ...args: string[]) {
|
||||||
return exec({}, getCommand(), args);
|
return exec(options, getCommand(), args);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ describe('install a package', () => {
|
||||||
|
|
||||||
test('should run npm info json body', async () => {
|
test('should run npm info json body', async () => {
|
||||||
const resp = await npm(
|
const resp = await npm(
|
||||||
|
{},
|
||||||
'info',
|
'info',
|
||||||
'verdaccio',
|
'verdaccio',
|
||||||
'--json',
|
'--json',
|
||||||
|
|
41
test/cli/e2e-npm8/publish.spec.ts
Normal file
41
test/cli/e2e-npm8/publish.spec.ts
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { addRegistry, initialSetup, prepareGenericEmptyProject } from '@verdaccio/test-cli-commons';
|
||||||
|
|
||||||
|
import { npm } from './utils';
|
||||||
|
|
||||||
|
describe('install a package', () => {
|
||||||
|
jest.setTimeout(10000);
|
||||||
|
let registry;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const setup = await initialSetup();
|
||||||
|
registry = setup.registry;
|
||||||
|
await registry.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each([['verdaccio-memory', 'verdaccio', '@verdaccio/foo', '@verdaccio/some-foo']])(
|
||||||
|
'should publish a package %s',
|
||||||
|
async (pkgName) => {
|
||||||
|
const { tempFolder } = await prepareGenericEmptyProject(
|
||||||
|
pkgName,
|
||||||
|
'1.0.0-patch',
|
||||||
|
registry.port,
|
||||||
|
registry.getToken(),
|
||||||
|
registry.getRegistryUrl()
|
||||||
|
);
|
||||||
|
const resp = await npm(
|
||||||
|
{ cwd: tempFolder },
|
||||||
|
'publish',
|
||||||
|
'--json',
|
||||||
|
...addRegistry(registry.getRegistryUrl())
|
||||||
|
);
|
||||||
|
const parsedBody = JSON.parse(resp.stdout as string);
|
||||||
|
expect(parsedBody.name).toEqual(pkgName);
|
||||||
|
expect(parsedBody.files).toBeDefined();
|
||||||
|
expect(parsedBody.files).toBeDefined();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
registry.stop();
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { SpawnOptions } from 'child_process';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
||||||
import { exec } from '@verdaccio/test-cli-commons';
|
import { exec } from '@verdaccio/test-cli-commons';
|
||||||
|
@ -6,6 +7,6 @@ export function getCommand() {
|
||||||
return join(__dirname, './node_modules/.bin/npm');
|
return join(__dirname, './node_modules/.bin/npm');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function npm(...args: string[]) {
|
export function npm(options: SpawnOptions, ...args: string[]) {
|
||||||
return exec({}, getCommand(), args);
|
return exec(options, getCommand(), args);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { addRegistry, initialSetup } from '@verdaccio/test-cli-commons';
|
import { addRegistry, initialSetup } from '@verdaccio/test-cli-commons';
|
||||||
|
|
||||||
import { npm } from './utils';
|
import { pnpm } from './utils';
|
||||||
|
|
||||||
describe('install a package', () => {
|
describe('install a package', () => {
|
||||||
jest.setTimeout(10000);
|
jest.setTimeout(10000);
|
||||||
|
@ -13,7 +13,8 @@ describe('install a package', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should run pnpm info json body', async () => {
|
test('should run pnpm info json body', async () => {
|
||||||
const resp = await npm(
|
const resp = await pnpm(
|
||||||
|
{},
|
||||||
'info',
|
'info',
|
||||||
'verdaccio',
|
'verdaccio',
|
||||||
'--json',
|
'--json',
|
||||||
|
|
41
test/cli/e2e-pnpm6/publish.spec.ts
Normal file
41
test/cli/e2e-pnpm6/publish.spec.ts
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { addRegistry, initialSetup, prepareGenericEmptyProject } from '@verdaccio/test-cli-commons';
|
||||||
|
|
||||||
|
import { pnpm } from './utils';
|
||||||
|
|
||||||
|
describe('install a package', () => {
|
||||||
|
jest.setTimeout(10000);
|
||||||
|
let registry;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const setup = await initialSetup();
|
||||||
|
registry = setup.registry;
|
||||||
|
await registry.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each([['verdaccio-memory', 'verdaccio', '@verdaccio/foo', '@verdaccio/some-foo']])(
|
||||||
|
'should publish a package %s',
|
||||||
|
async (pkgName) => {
|
||||||
|
const { tempFolder } = await prepareGenericEmptyProject(
|
||||||
|
pkgName,
|
||||||
|
'1.0.0-patch',
|
||||||
|
registry.port,
|
||||||
|
registry.getToken(),
|
||||||
|
registry.getRegistryUrl()
|
||||||
|
);
|
||||||
|
const resp = await pnpm(
|
||||||
|
{ cwd: tempFolder },
|
||||||
|
'publish',
|
||||||
|
'--json',
|
||||||
|
...addRegistry(registry.getRegistryUrl())
|
||||||
|
);
|
||||||
|
const parsedBody = JSON.parse(resp.stdout as string);
|
||||||
|
expect(parsedBody.name).toEqual(pkgName);
|
||||||
|
expect(parsedBody.files).toBeDefined();
|
||||||
|
expect(parsedBody.files).toBeDefined();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
registry.stop();
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { SpawnOptions } from 'child_process';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
||||||
import { exec } from '@verdaccio/test-cli-commons';
|
import { exec } from '@verdaccio/test-cli-commons';
|
||||||
|
@ -6,6 +7,6 @@ export function getCommand() {
|
||||||
return join(__dirname, './node_modules/.bin/pnpm');
|
return join(__dirname, './node_modules/.bin/pnpm');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function npm(...args: string[]) {
|
export function pnpm(options: SpawnOptions, ...args: string[]) {
|
||||||
return exec({}, getCommand(), args);
|
return exec(options, getCommand(), args);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { addRegistry, initialSetup } from '@verdaccio/test-cli-commons';
|
import { addRegistry, initialSetup } from '@verdaccio/test-cli-commons';
|
||||||
|
|
||||||
import { npm } from './utils';
|
import { pnpm } from './utils';
|
||||||
|
|
||||||
describe('install a package', () => {
|
describe('install a package', () => {
|
||||||
jest.setTimeout(10000);
|
jest.setTimeout(10000);
|
||||||
|
@ -13,7 +13,8 @@ describe('install a package', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should run pnpm info json body', async () => {
|
test('should run pnpm info json body', async () => {
|
||||||
const resp = await npm(
|
const resp = await pnpm(
|
||||||
|
{},
|
||||||
'info',
|
'info',
|
||||||
'verdaccio',
|
'verdaccio',
|
||||||
'--json',
|
'--json',
|
||||||
|
|
41
test/cli/e2e-pnpm7/publish.spec.ts
Normal file
41
test/cli/e2e-pnpm7/publish.spec.ts
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { addRegistry, initialSetup, prepareGenericEmptyProject } from '@verdaccio/test-cli-commons';
|
||||||
|
|
||||||
|
import { pnpm } from './utils';
|
||||||
|
|
||||||
|
describe('install a package', () => {
|
||||||
|
jest.setTimeout(10000);
|
||||||
|
let registry;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const setup = await initialSetup();
|
||||||
|
registry = setup.registry;
|
||||||
|
await registry.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each([['verdaccio-memory', 'verdaccio', '@verdaccio/foo', '@verdaccio/some-foo']])(
|
||||||
|
'should publish a package %s',
|
||||||
|
async (pkgName) => {
|
||||||
|
const { tempFolder } = await prepareGenericEmptyProject(
|
||||||
|
pkgName,
|
||||||
|
'1.0.0-patch',
|
||||||
|
registry.port,
|
||||||
|
registry.getToken(),
|
||||||
|
registry.getRegistryUrl()
|
||||||
|
);
|
||||||
|
const resp = await pnpm(
|
||||||
|
{ cwd: tempFolder },
|
||||||
|
'publish',
|
||||||
|
'--json',
|
||||||
|
...addRegistry(registry.getRegistryUrl())
|
||||||
|
);
|
||||||
|
const parsedBody = JSON.parse(resp.stdout as string);
|
||||||
|
expect(parsedBody.name).toEqual(pkgName);
|
||||||
|
expect(parsedBody.files).toBeDefined();
|
||||||
|
expect(parsedBody.files).toBeDefined();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
registry.stop();
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { SpawnOptions } from 'child_process';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
||||||
import { exec } from '@verdaccio/test-cli-commons';
|
import { exec } from '@verdaccio/test-cli-commons';
|
||||||
|
@ -6,6 +7,6 @@ export function getCommand() {
|
||||||
return join(__dirname, './node_modules/.bin/pnpm');
|
return join(__dirname, './node_modules/.bin/pnpm');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function npm(...args: string[]) {
|
export function pnpm(options: SpawnOptions, ...args: string[]) {
|
||||||
return exec({}, getCommand(), args);
|
return exec(options, getCommand(), args);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ describe('install a package', () => {
|
||||||
|
|
||||||
test('should run yarn info json body', async () => {
|
test('should run yarn info json body', async () => {
|
||||||
const resp = await yarn(
|
const resp = await yarn(
|
||||||
|
{},
|
||||||
'info',
|
'info',
|
||||||
'verdaccio',
|
'verdaccio',
|
||||||
'--json',
|
'--json',
|
||||||
|
|
39
test/cli/e2e-yarn1/publish.spec.ts
Normal file
39
test/cli/e2e-yarn1/publish.spec.ts
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import { addRegistry, initialSetup, prepareGenericEmptyProject } from '@verdaccio/test-cli-commons';
|
||||||
|
|
||||||
|
import { yarn } from './utils';
|
||||||
|
|
||||||
|
describe('install a package', () => {
|
||||||
|
jest.setTimeout(10000);
|
||||||
|
let registry;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const setup = await initialSetup();
|
||||||
|
registry = setup.registry;
|
||||||
|
await registry.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each([['verdaccio-memory', 'verdaccio', '@verdaccio/foo', '@verdaccio/some-foo']])(
|
||||||
|
'should publish a package %s',
|
||||||
|
async (pkgName) => {
|
||||||
|
const { tempFolder } = await prepareGenericEmptyProject(
|
||||||
|
pkgName,
|
||||||
|
'1.0.0-patch',
|
||||||
|
registry.port,
|
||||||
|
registry.getToken(),
|
||||||
|
registry.getRegistryUrl()
|
||||||
|
);
|
||||||
|
const resp = await yarn(
|
||||||
|
{ cwd: tempFolder },
|
||||||
|
'publish',
|
||||||
|
'--json',
|
||||||
|
...addRegistry(registry.getRegistryUrl())
|
||||||
|
);
|
||||||
|
// TODO: improve parsing to get better expects
|
||||||
|
expect(typeof resp.stdout === 'string').toBeDefined();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
afterAll(async () => {
|
||||||
|
registry.stop();
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { SpawnOptions } from 'child_process';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
|
||||||
import { exec } from '@verdaccio/test-cli-commons';
|
import { exec } from '@verdaccio/test-cli-commons';
|
||||||
|
@ -6,6 +7,6 @@ export function getCommand() {
|
||||||
return join(__dirname, './node_modules/.bin/yarn');
|
return join(__dirname, './node_modules/.bin/yarn');
|
||||||
}
|
}
|
||||||
|
|
||||||
export function yarn(...args: string[]) {
|
export function yarn(options: SpawnOptions, ...args: string[]) {
|
||||||
return exec({}, getCommand(), args);
|
return exec(options, getCommand(), args);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue