mirror of
https://github.com/verdaccio/verdaccio.git
synced 2024-12-16 21:56:25 -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_
|
||||
- _Node.js issues installation in any platform_: If you cannot install the
|
||||
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)
|
||||
|
||||
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
|
||||
- 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).
|
||||
- A potential implementation or design
|
||||
- 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
|
||||
4. Provide a detailed description of your plugin to help users understand how to
|
||||
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",
|
||||
"@dianmora/contributors": "5.0.0",
|
||||
"@changesets/changelog-github": "0.4.6",
|
||||
"@changesets/cli": "2.15.0",
|
||||
"@changesets/cli": "2.24.4",
|
||||
"@changesets/get-dependents-graph": "1.3.3",
|
||||
"@crowdin/cli": "3.7.10",
|
||||
"@trivago/prettier-plugin-sort-imports": "3.3.0",
|
||||
|
@ -93,7 +93,7 @@
|
|||
"jest-junit": "12.3.0",
|
||||
"kleur": "3.0.3",
|
||||
"lint-staged": "11.2.6",
|
||||
"nock": "12.0.3",
|
||||
"nock": "13.2.9",
|
||||
"node-fetch": "cjs",
|
||||
"nodemon": "2.0.19",
|
||||
"npm-run-all": "4.1.5",
|
||||
|
@ -146,7 +146,10 @@
|
|||
"crowdin:upload": "crowdin upload sources --auto-update --config ./crowdin.yaml",
|
||||
"crowdin:download": "crowdin download --verbose --config ./crowdin.yaml",
|
||||
"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": {
|
||||
"node": ">=16.5",
|
||||
|
|
|
@ -59,6 +59,7 @@ export default function (route: Router, auth: IAuth, storage: Storage): void {
|
|||
try {
|
||||
const stream = (await storage.getTarballNext(pkg, filename, {
|
||||
signal: abort.signal,
|
||||
// TODO: review why this param
|
||||
// enableRemote: true,
|
||||
})) as any;
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ const debug = buildDebug('verdaccio:api:publish');
|
|||
* specific flag for star or un start.
|
||||
* 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
|
||||
*
|
||||
* {
|
||||
|
@ -206,13 +206,12 @@ export function publishPackageNext(storage: Storage): any {
|
|||
return next({
|
||||
// TODO: this could be also Package Updated based on the
|
||||
// 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,
|
||||
success: true,
|
||||
});
|
||||
} catch (err: any) {
|
||||
// TODO: review if we need the abort controller here
|
||||
ac.abort();
|
||||
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,64 +6,100 @@ import { Storage } from '@verdaccio/store';
|
|||
import { initializeServer, publishVersion } from './_helper';
|
||||
|
||||
describe('package', () => {
|
||||
let app;
|
||||
beforeEach(async () => {
|
||||
app = await initializeServer('package.yaml');
|
||||
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');
|
||||
});
|
||||
|
||||
test.each([['foo'], ['@scope/foo']])('should return a foo private package', async (pkg) => {
|
||||
await publishVersion(app, pkg, '1.0.0');
|
||||
const response = await supertest(app)
|
||||
.get(`/${pkg}`)
|
||||
.set(HEADERS.ACCEPT, HEADERS.JSON)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK);
|
||||
expect(response.body.name).toEqual(pkg);
|
||||
describe('get package', () => {
|
||||
let app;
|
||||
beforeEach(async () => {
|
||||
app = await initializeServer('package.yaml');
|
||||
});
|
||||
|
||||
test.each([['foo'], ['@scope/foo']])('should return a foo private package', async (pkg) => {
|
||||
await publishVersion(app, pkg, '1.0.0');
|
||||
const response = await supertest(app)
|
||||
.get(`/${pkg}`)
|
||||
.set(HEADERS.ACCEPT, HEADERS.JSON)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK);
|
||||
expect(response.body.name).toEqual(pkg);
|
||||
});
|
||||
|
||||
test.each([['foo'], ['@scope/foo']])(
|
||||
'should return a foo private package by version',
|
||||
async (pkg) => {
|
||||
await publishVersion(app, pkg, '1.0.0');
|
||||
const response = await supertest(app)
|
||||
.get(`/${pkg}`)
|
||||
.set(HEADERS.ACCEPT, HEADERS.JSON)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK);
|
||||
expect(response.body.name).toEqual(pkg);
|
||||
}
|
||||
);
|
||||
|
||||
test.each([['foo'], ['@scope/foo']])(
|
||||
'should return a foo private package by version',
|
||||
async (pkg) => {
|
||||
await publishVersion(app, pkg, '1.0.0');
|
||||
const response = await supertest(app)
|
||||
.get(`/${pkg}`)
|
||||
.set(HEADERS.ACCEPT, HEADERS.JSON)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK);
|
||||
expect(response.body.name).toEqual(pkg);
|
||||
}
|
||||
);
|
||||
|
||||
test.each([['foo-abbreviated'], ['@scope/foo-abbreviated']])(
|
||||
'should return abbreviated local manifest',
|
||||
async (pkg) => {
|
||||
await publishVersion(app, pkg, '1.0.0');
|
||||
const response = await supertest(app)
|
||||
.get(`/${pkg}`)
|
||||
.set(HEADERS.ACCEPT, HEADERS.JSON)
|
||||
.set(HEADERS.ACCEPT, Storage.ABBREVIATED_HEADER)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK);
|
||||
expect(response.body.name).toEqual(pkg);
|
||||
expect(response.body.time).toBeDefined();
|
||||
expect(response.body.modified).toBeDefined();
|
||||
expect(response.body[DIST_TAGS]).toEqual({ latest: '1.0.0' });
|
||||
expect(response.body.readme).not.toBeDefined();
|
||||
expect(response.body._rev).not.toBeDefined();
|
||||
expect(response.body.users).not.toBeDefined();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
test.each([['foo'], ['@scope/foo']])(
|
||||
'should return a foo private package by version',
|
||||
async (pkg) => {
|
||||
await publishVersion(app, pkg, '1.0.0');
|
||||
const response = await supertest(app)
|
||||
.get(`/${pkg}`)
|
||||
.set(HEADERS.ACCEPT, HEADERS.JSON)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK);
|
||||
expect(response.body.name).toEqual(pkg);
|
||||
}
|
||||
);
|
||||
|
||||
test.each([['foo'], ['@scope/foo']])(
|
||||
'should return a foo private package by version',
|
||||
async (pkg) => {
|
||||
await publishVersion(app, pkg, '1.0.0');
|
||||
const response = await supertest(app)
|
||||
.get(`/${pkg}`)
|
||||
.set(HEADERS.ACCEPT, HEADERS.JSON)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK);
|
||||
expect(response.body.name).toEqual(pkg);
|
||||
}
|
||||
);
|
||||
|
||||
test.each([['foo-abbreviated'], ['@scope/foo-abbreviated']])(
|
||||
'should return abbreviated local manifest',
|
||||
async (pkg) => {
|
||||
await publishVersion(app, pkg, '1.0.0');
|
||||
const response = await supertest(app)
|
||||
.get(`/${pkg}`)
|
||||
.set(HEADERS.ACCEPT, HEADERS.JSON)
|
||||
.set(HEADERS.ACCEPT, Storage.ABBREVIATED_HEADER)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.OK);
|
||||
expect(response.body.name).toEqual(pkg);
|
||||
expect(response.body.time).toBeDefined();
|
||||
expect(response.body.modified).toBeDefined();
|
||||
expect(response.body[DIST_TAGS]).toEqual({ latest: '1.0.0' });
|
||||
expect(response.body.readme).not.toBeDefined();
|
||||
expect(response.body._rev).not.toBeDefined();
|
||||
expect(response.body.users).not.toBeDefined();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import nock from 'nock';
|
||||
import { basename } from 'path';
|
||||
import supertest from 'supertest';
|
||||
|
||||
import { HTTP_STATUS } 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 { initializeServer, publishVersion } from './_helper';
|
||||
import { getPackage, initializeServer, publishVersion } from './_helper';
|
||||
|
||||
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('handle invalid publish formats', () => {
|
||||
describe('handle errors', () => {
|
||||
const pkgName = 'test';
|
||||
const pkgMetadata = generatePackageMetadata(pkgName, '1.0.0');
|
||||
test('should fail on publish a bad _attachments package', async () => {
|
||||
|
@ -101,134 +80,179 @@ describe('publish', () => {
|
|||
});
|
||||
|
||||
describe('publish a package', () => {
|
||||
test('should publish a package', async () => {
|
||||
const app = await initializeServer('publish.yaml');
|
||||
return new Promise((resolve) => {
|
||||
publishVersion(app, 'foo', '1.0.0')
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.then((response) => {
|
||||
expect(response.body.ok).toEqual(API_MESSAGE.PKG_CREATED);
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('should publish a new package', async () => {
|
||||
const pkgName = 'test';
|
||||
const pkgMetadata = generatePackageMetadata(pkgName, '1.0.0');
|
||||
const app = await initializeServer('publish.yaml');
|
||||
return new Promise((resolve) => {
|
||||
supertest(app)
|
||||
.put(`/${encodeURIComponent(pkgName)}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(JSON.stringify(Object.assign({}, pkgMetadata)))
|
||||
.set('accept', HEADERS.GZIP)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.then((response) => {
|
||||
expect(response.body.ok).toEqual(API_MESSAGE.PKG_CREATED);
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('should publish a new package with no readme', async () => {
|
||||
const pkgName = 'test';
|
||||
const pkgMetadata = generatePackageMetadata(pkgName, '1.0.0');
|
||||
const app = await initializeServer('publish.yaml');
|
||||
return new Promise((resolve) => {
|
||||
supertest(app)
|
||||
.put(`/${encodeURIComponent(pkgName)}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(
|
||||
JSON.stringify(
|
||||
Object.assign({}, pkgMetadata, {
|
||||
versions: {
|
||||
['1.0.0']: {
|
||||
readme: null,
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
)
|
||||
.set('accept', HEADERS.GZIP)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.then((response) => {
|
||||
expect(response.body.ok).toEqual(API_MESSAGE.PKG_CREATED);
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('should fails on publish a duplicated package', async () => {
|
||||
const app = await initializeServer('publish.yaml');
|
||||
await publishVersion(app, 'foo', '1.0.0');
|
||||
return new Promise((resolve) => {
|
||||
publishVersion(app, 'foo', '1.0.0')
|
||||
.expect(HTTP_STATUS.CONFLICT)
|
||||
.then((response) => {
|
||||
expect(response.body.error).toEqual(API_ERROR.PACKAGE_EXIST);
|
||||
resolve(response);
|
||||
describe('no proxies setup', () => {
|
||||
test.each([['foo', '@scope/foo']])('should publish a package', async (pkgName) => {
|
||||
const app = await initializeServer('publish.yaml');
|
||||
return new Promise((resolve) => {
|
||||
publishVersion(app, pkgName, '1.0.0')
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.then((response) => {
|
||||
expect(response.body.ok).toEqual(API_MESSAGE.PKG_CREATED);
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test.each([['foo', '@scope/foo']])('should publish a new package', async (pkgName) => {
|
||||
const pkgMetadata = generatePackageMetadata(pkgName, '1.0.0');
|
||||
const app = await initializeServer('publish.yaml');
|
||||
return new Promise((resolve) => {
|
||||
supertest(app)
|
||||
.put(`/${encodeURIComponent(pkgName)}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(JSON.stringify(Object.assign({}, pkgMetadata)))
|
||||
.set('accept', HEADERS.GZIP)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.then((response) => {
|
||||
expect(response.body.ok).toEqual(API_MESSAGE.PKG_CREATED);
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('should publish a new package with no readme', async () => {
|
||||
const pkgName = 'test';
|
||||
const pkgMetadata = generatePackageMetadata(pkgName, '1.0.0');
|
||||
const app = await initializeServer('publish.yaml');
|
||||
return new Promise((resolve) => {
|
||||
supertest(app)
|
||||
.put(`/${encodeURIComponent(pkgName)}`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.send(
|
||||
JSON.stringify(
|
||||
Object.assign({}, pkgMetadata, {
|
||||
versions: {
|
||||
['1.0.0']: {
|
||||
readme: null,
|
||||
},
|
||||
},
|
||||
})
|
||||
)
|
||||
)
|
||||
.set('accept', HEADERS.GZIP)
|
||||
.expect(HTTP_STATUS.CREATED)
|
||||
.then((response) => {
|
||||
expect(response.body.ok).toEqual(API_MESSAGE.PKG_CREATED);
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
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.each([['foo', '@scope/foo']])(
|
||||
'should fails on publish a duplicated package',
|
||||
async (pkgName) => {
|
||||
const app = await initializeServer('publish.yaml');
|
||||
await publishVersion(app, pkgName, '1.0.0');
|
||||
return new Promise((resolve) => {
|
||||
publishVersion(app, pkgName, '1.0.0')
|
||||
.expect(HTTP_STATUS.CONFLICT)
|
||||
.then((response) => {
|
||||
expect(response.body.error).toEqual(API_ERROR.PACKAGE_EXIST);
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
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');
|
||||
await publishVersion(app, 'foo', '1.0.0');
|
||||
await publishVersion(app, pkgName, '1.0.0');
|
||||
const response = await supertest(app)
|
||||
// FIXME: should be filtered by revision to avoid
|
||||
// conflicts
|
||||
.delete(`/${encodeURIComponent('foo')}/-rev/xxx`)
|
||||
.delete(`/${encodeURIComponent(pkgName)}/-rev/xxx`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.expect(HTTP_STATUS.CREATED);
|
||||
expect(response.body.ok).toEqual(API_MESSAGE.PKG_REMOVED);
|
||||
// package should be completely un published
|
||||
await supertest(app)
|
||||
.get('/foo')
|
||||
.get(`/${pkgName}`)
|
||||
.set('Accept', HEADERS.JSON)
|
||||
.expect(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON_CHARSET)
|
||||
.expect(HTTP_STATUS.NOT_FOUND);
|
||||
});
|
||||
|
||||
test('should fails unpublish entirely a package', async () => {
|
||||
const app = await initializeServer('publish.yaml');
|
||||
const response = await supertest(app)
|
||||
.delete(`/${encodeURIComponent('foo')}/-rev/1cf3-fe3`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.expect(HTTP_STATUS.NOT_FOUND);
|
||||
expect(response.body.error).toEqual(API_ERROR.NO_PACKAGE);
|
||||
});
|
||||
test.each([['foo', '@scope/foo']])(
|
||||
'should fails unpublish entirely a package',
|
||||
async (pkgName) => {
|
||||
const app = await initializeServer('publish.yaml');
|
||||
const response = await supertest(app)
|
||||
.delete(`/${encodeURIComponent(pkgName)}/-rev/1cf3-fe3`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.expect(HTTP_STATUS.NOT_FOUND);
|
||||
expect(response.body.error).toEqual(API_ERROR.NO_PACKAGE);
|
||||
}
|
||||
);
|
||||
|
||||
test('should fails remove a tarball of a package does not exist', async () => {
|
||||
const app = await initializeServer('publish.yaml');
|
||||
const response = await supertest(app)
|
||||
.delete(`/foo/-/foo-1.0.3.tgz/-rev/revision`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.expect(HTTP_STATUS.NOT_FOUND);
|
||||
expect(response.body.error).toEqual(API_ERROR.NO_PACKAGE);
|
||||
});
|
||||
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 response = await supertest(app)
|
||||
.delete(`/${pkgName}/-/${basename(pkgName)}-1.0.3.tgz/-rev/revision`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.expect(HTTP_STATUS.NOT_FOUND);
|
||||
expect(response.body.error).toEqual(API_ERROR.NO_PACKAGE);
|
||||
}
|
||||
);
|
||||
|
||||
test('should fails on try remove a tarball does not exist', async () => {
|
||||
const app = await initializeServer('publish.yaml');
|
||||
await publishVersion(app, 'foo', '1.0.0');
|
||||
const response = await supertest(app)
|
||||
.delete(`/foo/-/foo-1.0.3.tgz/-rev/revision`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.expect(HTTP_STATUS.NOT_FOUND);
|
||||
expect(response.body.error).toEqual(API_ERROR.NO_SUCH_FILE);
|
||||
});
|
||||
test.each([['foo', '@scope/foo']])(
|
||||
'should fails on try remove a tarball does not exist',
|
||||
async (pkgName) => {
|
||||
const app = await initializeServer('publish.yaml');
|
||||
await publishVersion(app, pkgName, '1.0.0');
|
||||
const response = await supertest(app)
|
||||
.delete(`/${pkgName}/-/${basename(pkgName)}-1.0.3.tgz/-rev/revision`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.expect(HTTP_STATUS.NOT_FOUND);
|
||||
expect(response.body.error).toEqual(API_ERROR.NO_SUCH_FILE);
|
||||
}
|
||||
);
|
||||
|
||||
test('should remove a tarball that does exist', async () => {
|
||||
const app = await initializeServer('publish.yaml');
|
||||
await publishVersion(app, 'foo', '1.0.0');
|
||||
const response = await supertest(app)
|
||||
.delete(`/foo/-/foo-1.0.0.tgz/-rev/revision`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.expect(HTTP_STATUS.CREATED);
|
||||
expect(response.body.ok).toEqual(API_MESSAGE.TARBALL_REMOVED);
|
||||
});
|
||||
test.each([['foo', '@scope/foo']])(
|
||||
'should remove a tarball that does exist',
|
||||
async (pkgName) => {
|
||||
const app = await initializeServer('publish.yaml');
|
||||
await publishVersion(app, pkgName, '1.0.0');
|
||||
const response = await supertest(app)
|
||||
.delete(`/${pkgName}/-/${basename(pkgName)}-1.0.0.tgz/-rev/revision`)
|
||||
.set(HEADER_TYPE.CONTENT_TYPE, HEADERS.JSON)
|
||||
.expect(HTTP_STATUS.CREATED);
|
||||
expect(response.body.ok).toEqual(API_MESSAGE.TARBALL_REMOVED);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('star a package', () => {});
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import nock from 'nock';
|
||||
import supertest from 'supertest';
|
||||
|
||||
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 { createUser, getPackage, initializeServer } from './_helper';
|
||||
|
@ -13,13 +11,6 @@ describe('token', () => {
|
|||
describe('basics', () => {
|
||||
const FAKE_TOKEN: string = buildToken(TOKEN_BEARER, 'fake');
|
||||
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 credentials = { name: 'JotaJWT', password: 'secretPass' };
|
||||
const response = await createUser(app, credentials.name, credentials.password);
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Command, Option } from 'clipanion';
|
||||
|
||||
import { findConfigFile, parseConfigFile } from '@verdaccio/config';
|
||||
import { warningUtils } from '@verdaccio/core';
|
||||
import { logger, setup } from '@verdaccio/logger';
|
||||
import { initServer } from '@verdaccio/node-api';
|
||||
import { ConfigYaml, LoggerConfigItem } from '@verdaccio/types';
|
||||
|
@ -45,17 +46,13 @@ export class InitCommand extends Command {
|
|||
});
|
||||
|
||||
private initLogger(logConfig: ConfigYaml) {
|
||||
try {
|
||||
// @ts-expect-error
|
||||
if (logConfig.logs) {
|
||||
// @ts-expect-error
|
||||
if (logConfig.logs) {
|
||||
throw Error(
|
||||
'the property config "logs" property is longer supported, rename to "log" and use object instead'
|
||||
);
|
||||
}
|
||||
setup(logConfig.log as LoggerConfigItem);
|
||||
} catch (err: any) {
|
||||
throw new Error(err);
|
||||
logConfig.log = logConfig.logs;
|
||||
warningUtils.emit(warningUtils.Codes.VERWAR002);
|
||||
}
|
||||
setup(logConfig.log as LoggerConfigItem);
|
||||
}
|
||||
|
||||
public async execute() {
|
||||
|
|
|
@ -6,12 +6,19 @@ const verdaccioDeprecation = 'VerdaccioDeprecation';
|
|||
|
||||
export enum Codes {
|
||||
VERWAR001 = 'VERWAR001',
|
||||
VERWAR002 = 'VERWAR002',
|
||||
VERWAR003 = 'VERWAR003',
|
||||
VERWAR004 = 'VERWAR004',
|
||||
// deprecation warnings
|
||||
VERDEP003 = 'VERDEP003',
|
||||
}
|
||||
|
||||
warningInstance.create(
|
||||
verdaccioWarning,
|
||||
Codes.VERWAR002,
|
||||
`The property config "logs" property is longer supported, rename to "log" and use object instead`
|
||||
);
|
||||
|
||||
warningInstance.create(
|
||||
verdaccioWarning,
|
||||
Codes.VERWAR001,
|
||||
|
|
|
@ -181,6 +181,7 @@ export interface FullRemoteManifest {
|
|||
homepage?: string;
|
||||
repository?: string | { type?: string; url: string; directory?: string };
|
||||
keywords?: string[];
|
||||
author?: string | Author;
|
||||
}
|
||||
|
||||
export interface Manifest extends FullRemoteManifest, PublishManifest {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import assert from 'assert';
|
||||
import buildDebug from 'debug';
|
||||
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 { default as URL } from 'url';
|
||||
|
||||
|
@ -961,7 +962,7 @@ class Storage {
|
|||
// if (typeof storage === 'undefined') {
|
||||
// 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
|
||||
|
@ -972,7 +973,7 @@ class Storage {
|
|||
// 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);
|
||||
throw errorUtils.getConflict();
|
||||
}
|
||||
|
||||
const uplinksLook = this.config?.publish?.allow_offline === false;
|
||||
// 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) {
|
||||
debug('%s version %s already exists', name, versionToPublish);
|
||||
throw errorUtils.getConflict();
|
||||
|
@ -1112,7 +1113,7 @@ class Storage {
|
|||
// 3. upload the tarball to the storage
|
||||
try {
|
||||
const readable = Readable.from(buffer);
|
||||
await this.uploadTarball(name, firstAttachmentKey, readable, {
|
||||
await this.uploadTarball(name, basename(firstAttachmentKey), readable, {
|
||||
signal: options.signal,
|
||||
});
|
||||
} catch (err: any) {
|
||||
|
@ -1148,43 +1149,46 @@ class Storage {
|
|||
* @param options
|
||||
* @returns
|
||||
*/
|
||||
public async uploadTarball(
|
||||
public uploadTarball(
|
||||
name: string,
|
||||
fileName: string,
|
||||
contentReadable: Readable,
|
||||
{ signal }
|
||||
): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
(async () => {
|
||||
const stream: Writable = await this.uploadTarballAsStream(name, fileName, {
|
||||
signal,
|
||||
this.uploadTarballAsStream(name, fileName, {
|
||||
signal,
|
||||
})
|
||||
.then((stream) => {
|
||||
stream.on('error', (err) => {
|
||||
debug(
|
||||
'error on stream a tarball %o for %o with error %o',
|
||||
'foo.tar.gz',
|
||||
name,
|
||||
err.message
|
||||
);
|
||||
reject(err);
|
||||
});
|
||||
stream.on('success', () => {
|
||||
this.logger.debug(
|
||||
{ fileName, name },
|
||||
'file @{fileName} for package @{name} has been successfully uploaded'
|
||||
);
|
||||
resolve();
|
||||
});
|
||||
return stream;
|
||||
})
|
||||
.then((stream) => {
|
||||
pipeline(contentReadable, stream, { signal })
|
||||
.then(() => {
|
||||
debug('success pipe upload tarball');
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
|
||||
stream.on('error', (err) => {
|
||||
debug(
|
||||
'error on stream a tarball %o for %o with error %o',
|
||||
'foo.tar.gz',
|
||||
name,
|
||||
err.message
|
||||
);
|
||||
reject(err);
|
||||
});
|
||||
stream.on('success', () => {
|
||||
this.logger.debug(
|
||||
{ fileName, name },
|
||||
'file @{fileName} for package @{name} has been succesfully uploaded'
|
||||
);
|
||||
resolve();
|
||||
});
|
||||
|
||||
await pipeline(contentReadable, stream, { signal });
|
||||
})().catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public async uploadTarballAsStream(
|
||||
private async uploadTarballAsStream(
|
||||
pkgName: string,
|
||||
filename: string,
|
||||
{ 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
|
||||
|
@ -1567,8 +1563,8 @@ class Storage {
|
|||
|
||||
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
|
||||
are made in serie and if 1st call fails, the second will be triggered, otherwise
|
||||
the 1st will reply and others will be discareded. The order is important.
|
||||
are made in serial and if 1st call fails, the second will be triggered, otherwise
|
||||
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,
|
||||
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');
|
||||
});
|
||||
|
||||
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');
|
||||
nock('https://registry.verdaccio.org').get('/foo').reply(201, manifestFooRemoteNpmjs);
|
||||
const config = new Config(
|
||||
|
@ -691,6 +692,35 @@ describe('storage', () => {
|
|||
expect((response as Manifest).name).toEqual(fooManifest.name);
|
||||
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 {
|
||||
[key: string]: string;
|
||||
|
@ -49,7 +49,7 @@ export function addNewVersion(
|
|||
};
|
||||
// update the latest with the new version
|
||||
newManifest['dist-tags'] = { latest: version };
|
||||
// add new version does not need attachmetns
|
||||
// add new version does not need attachments
|
||||
if (isRemote) {
|
||||
newManifest._distfiles = {
|
||||
...newManifest._distfiles,
|
||||
|
@ -137,58 +137,83 @@ export function generateLocalPackageMetadata(
|
|||
export function generateRemotePackageMetadata(
|
||||
pkgName: string,
|
||||
version = '1.0.0',
|
||||
domain: string = 'http://localhost:5555'
|
||||
): Manifest {
|
||||
domain: string = 'http://localhost:5555',
|
||||
versions: string[] = []
|
||||
): FullRemoteManifest {
|
||||
// @ts-ignore
|
||||
const generateVersion = (version: string): Version => {
|
||||
const metadata = {
|
||||
name: pkgName,
|
||||
version: version,
|
||||
description: 'package generated',
|
||||
main: 'index.js',
|
||||
scripts: {
|
||||
test: 'echo "Error: no test specified" && exit 1',
|
||||
},
|
||||
keywords: [],
|
||||
author: {
|
||||
name: 'User NPM',
|
||||
email: 'user@domain.com',
|
||||
},
|
||||
license: 'ISC',
|
||||
dependencies: {
|
||||
verdaccio: '^2.7.2',
|
||||
},
|
||||
readme: '# test',
|
||||
readmeFilename: 'README.md',
|
||||
_id: `${pkgName}@${version}`,
|
||||
_npmVersion: '5.5.1',
|
||||
_npmUser: {
|
||||
name: 'foo',
|
||||
},
|
||||
dist: {
|
||||
integrity:
|
||||
'sha512-6gHiERpiDgtb3hjqpQH5/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cm' +
|
||||
'E6dUBf+XoPoH4g==',
|
||||
shasum: '2c03764f651a9f016ca0b7620421457b619151b9', // pragma: allowlist secret
|
||||
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]: {
|
||||
name: pkgName,
|
||||
version: version,
|
||||
description: 'package generated',
|
||||
main: 'index.js',
|
||||
scripts: {
|
||||
test: 'echo "Error: no test specified" && exit 1',
|
||||
},
|
||||
keywords: [],
|
||||
author: {
|
||||
name: 'User NPM',
|
||||
email: 'user@domain.com',
|
||||
},
|
||||
license: 'ISC',
|
||||
dependencies: {
|
||||
verdaccio: '^2.7.2',
|
||||
},
|
||||
readme: '# test',
|
||||
readmeFilename: 'README.md',
|
||||
_id: `${pkgName}@${version}`,
|
||||
_npmVersion: '5.5.1',
|
||||
_npmUser: {
|
||||
name: 'foo',
|
||||
},
|
||||
dist: {
|
||||
integrity:
|
||||
'sha512-6gHiERpiDgtb3hjqpQH5/i7zRmvYi9pmCjQf2ZMy3QEa9wVk9RgdZaPWUt7ZOnWUPFjcr9cm' +
|
||||
'E6dUBf+XoPoH4g==',
|
||||
shasum: '2c03764f651a9f016ca0b7620421457b619151b9', // pragma: allowlist secret
|
||||
tarball: `${domain}\/${pkgName}\/-\/${getTarball(pkgName)}-${version}.tgz`,
|
||||
},
|
||||
[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',
|
||||
_attachments: {},
|
||||
_uplinks: {},
|
||||
_distfiles: {
|
||||
[`${pkgName}-${version}.tgz`]: {
|
||||
url: `${domain}/${pkgName}\/-\/${getTarball(pkgName)}-${version}.tgz`,
|
||||
sha: '2c03764f651a9f016ca0b7620421457b619151b9', // pragma: allowlist secret
|
||||
},
|
||||
},
|
||||
_rev: '',
|
||||
_rev: '12-c8fe8a9c79fa57a87347a0213e6f2548',
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
const config = require('../../jest/config');
|
||||
|
||||
module.exports = Object.assign({}, config, {});
|
|
@ -35,27 +35,27 @@ describe('generate metadata', () => {
|
|||
});
|
||||
describe('generateRemotePackageMetadata', () => {
|
||||
test('should generate package metadata', () => {
|
||||
const m = generateRemotePackageMetadata('foo', '1.0.0', 'https://registry.verdaccio.org');
|
||||
expect(m).toBeDefined();
|
||||
expect(m._attachments).toEqual({});
|
||||
expect(m._distfiles['foo-1.0.0.tgz']).toEqual({
|
||||
sha: '2c03764f651a9f016ca0b7620421457b619151b9',
|
||||
url: 'https://registry.verdaccio.org/foo/-/foo-1.0.0.tgz',
|
||||
});
|
||||
expect(
|
||||
generateRemotePackageMetadata('foo', '1.0.0', 'https://registry.verdaccio.org')
|
||||
).toBeDefined();
|
||||
});
|
||||
test('should add new versions remote', () => {
|
||||
const manifest = generateRemotePackageMetadata('foo', '1.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([]);
|
||||
|
||||
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(Object.keys(m.versions)).toEqual(['1.0.0', '1.0.1', '1.0.2', '3.0.0']);
|
||||
expect(Object.keys(m.time)).toEqual([
|
||||
'modified',
|
||||
'created',
|
||||
'1.0.0',
|
||||
'1.0.1',
|
||||
'1.0.2',
|
||||
'3.0.0',
|
||||
]);
|
||||
});
|
||||
});
|
||||
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
1467
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,10 @@
|
|||
export { getDefaultConfig } from '@verdaccio/config';
|
||||
export { initialSetup } from './registry';
|
||||
export { addNpmPrefix, addYarnClassicPrefix, addRegistry, prepareYarnModernProject } from './utils';
|
||||
export {
|
||||
addNpmPrefix,
|
||||
addYarnClassicPrefix,
|
||||
addRegistry,
|
||||
prepareYarnModernProject,
|
||||
prepareGenericEmptyProject,
|
||||
} from './utils';
|
||||
export { exec, ExecOutput } from './process';
|
||||
|
|
|
@ -3,6 +3,7 @@ import buildDebug from 'debug';
|
|||
import { Registry } from 'verdaccio';
|
||||
|
||||
import { getDefaultConfig } from '@verdaccio/config';
|
||||
import { ConfigYaml } from '@verdaccio/types';
|
||||
|
||||
const debug = buildDebug('verdaccio:e2e:registry-utils');
|
||||
|
||||
|
@ -11,8 +12,10 @@ export type Setup = {
|
|||
tempFolder: string;
|
||||
};
|
||||
|
||||
export async function initialSetup(): Promise<Setup> {
|
||||
const { configPath, tempFolder } = await Registry.fromConfigToPath(getDefaultConfig());
|
||||
export async function initialSetup(customConfig?: ConfigYaml): Promise<Setup> {
|
||||
const { configPath, tempFolder } = await Registry.fromConfigToPath({
|
||||
...(customConfig ? customConfig : { ...getDefaultConfig(), _debug: true }),
|
||||
});
|
||||
debug(`configPath %o`, configPath);
|
||||
debug(`tempFolder %o`, tempFolder);
|
||||
const registry = new Registry(configPath);
|
||||
|
|
|
@ -61,3 +61,43 @@ export async function prepareYarnModernProject(
|
|||
await cp(yarnPath, join(tempFolder, '.yarn/releases/yarn.js'), { dereference: true });
|
||||
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 () => {
|
||||
const resp = await npm(
|
||||
{},
|
||||
'info',
|
||||
'verdaccio',
|
||||
'--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 { exec } from '@verdaccio/test-cli-commons';
|
||||
|
@ -6,6 +7,6 @@ export function getCommand() {
|
|||
return join(__dirname, './node_modules/.bin/npm');
|
||||
}
|
||||
|
||||
export function npm(...args: string[]) {
|
||||
return exec({}, getCommand(), args);
|
||||
export function npm(options: SpawnOptions, ...args: string[]) {
|
||||
return exec(options, getCommand(), args);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ describe('install a package', () => {
|
|||
|
||||
test('should run npm info json body', async () => {
|
||||
const resp = await npm(
|
||||
{},
|
||||
'info',
|
||||
'verdaccio',
|
||||
'--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 { exec } from '@verdaccio/test-cli-commons';
|
||||
|
@ -6,6 +7,6 @@ export function getCommand() {
|
|||
return join(__dirname, './node_modules/.bin/npm');
|
||||
}
|
||||
|
||||
export function npm(...args: string[]) {
|
||||
return exec({}, getCommand(), args);
|
||||
export function npm(options: SpawnOptions, ...args: string[]) {
|
||||
return exec(options, getCommand(), args);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ describe('install a package', () => {
|
|||
|
||||
test('should run npm info json body', async () => {
|
||||
const resp = await npm(
|
||||
{},
|
||||
'info',
|
||||
'verdaccio',
|
||||
'--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 { exec } from '@verdaccio/test-cli-commons';
|
||||
|
@ -6,6 +7,6 @@ export function getCommand() {
|
|||
return join(__dirname, './node_modules/.bin/npm');
|
||||
}
|
||||
|
||||
export function npm(...args: string[]) {
|
||||
return exec({}, getCommand(), args);
|
||||
export function npm(options: SpawnOptions, ...args: string[]) {
|
||||
return exec(options, getCommand(), args);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { addRegistry, initialSetup } from '@verdaccio/test-cli-commons';
|
||||
|
||||
import { npm } from './utils';
|
||||
import { pnpm } from './utils';
|
||||
|
||||
describe('install a package', () => {
|
||||
jest.setTimeout(10000);
|
||||
|
@ -13,7 +13,8 @@ describe('install a package', () => {
|
|||
});
|
||||
|
||||
test('should run pnpm info json body', async () => {
|
||||
const resp = await npm(
|
||||
const resp = await pnpm(
|
||||
{},
|
||||
'info',
|
||||
'verdaccio',
|
||||
'--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 { exec } from '@verdaccio/test-cli-commons';
|
||||
|
@ -6,6 +7,6 @@ export function getCommand() {
|
|||
return join(__dirname, './node_modules/.bin/pnpm');
|
||||
}
|
||||
|
||||
export function npm(...args: string[]) {
|
||||
return exec({}, getCommand(), args);
|
||||
export function pnpm(options: SpawnOptions, ...args: string[]) {
|
||||
return exec(options, getCommand(), args);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { addRegistry, initialSetup } from '@verdaccio/test-cli-commons';
|
||||
|
||||
import { npm } from './utils';
|
||||
import { pnpm } from './utils';
|
||||
|
||||
describe('install a package', () => {
|
||||
jest.setTimeout(10000);
|
||||
|
@ -13,7 +13,8 @@ describe('install a package', () => {
|
|||
});
|
||||
|
||||
test('should run pnpm info json body', async () => {
|
||||
const resp = await npm(
|
||||
const resp = await pnpm(
|
||||
{},
|
||||
'info',
|
||||
'verdaccio',
|
||||
'--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 { exec } from '@verdaccio/test-cli-commons';
|
||||
|
@ -6,6 +7,6 @@ export function getCommand() {
|
|||
return join(__dirname, './node_modules/.bin/pnpm');
|
||||
}
|
||||
|
||||
export function npm(...args: string[]) {
|
||||
return exec({}, getCommand(), args);
|
||||
export function pnpm(options: SpawnOptions, ...args: string[]) {
|
||||
return exec(options, getCommand(), args);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ describe('install a package', () => {
|
|||
|
||||
test('should run yarn info json body', async () => {
|
||||
const resp = await yarn(
|
||||
{},
|
||||
'info',
|
||||
'verdaccio',
|
||||
'--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 { exec } from '@verdaccio/test-cli-commons';
|
||||
|
@ -6,6 +7,6 @@ export function getCommand() {
|
|||
return join(__dirname, './node_modules/.bin/yarn');
|
||||
}
|
||||
|
||||
export function yarn(...args: string[]) {
|
||||
return exec({}, getCommand(), args);
|
||||
export function yarn(options: SpawnOptions, ...args: string[]) {
|
||||
return exec(options, getCommand(), args);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue