From c807f0c4fb8e71917891b832afd0ee844e0b4c0a Mon Sep 17 00:00:00 2001 From: Marc Bernard <59966492+mbtools@users.noreply.github.com> Date: Mon, 12 Feb 2024 13:50:58 +0100 Subject: [PATCH] fix: store readme when publishing locally (#4493) * fix: store readme when publishing locally * Update actions test * Update spicy-birds-flow.md --- .changeset/spicy-birds-flow.md | 6 ++++ packages/store/src/storage.ts | 19 ++++++----- packages/store/test/storage.spec.ts | 46 +++++++++++++++++++++++++++ packages/tools/helpers/src/actions.ts | 4 ++- 4 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 .changeset/spicy-birds-flow.md diff --git a/.changeset/spicy-birds-flow.md b/.changeset/spicy-birds-flow.md new file mode 100644 index 000000000..3fd3c436d --- /dev/null +++ b/.changeset/spicy-birds-flow.md @@ -0,0 +1,6 @@ +--- +'@verdaccio/store': patch +'@verdaccio/test-helper': patch +--- + +fix: store readme when publishing locally diff --git a/packages/store/src/storage.ts b/packages/store/src/storage.ts index 763d08874..ae79de731 100644 --- a/packages/store/src/storage.ts +++ b/packages/store/src/storage.ts @@ -1098,17 +1098,17 @@ class Storage { try { // we check if package exist already locally - const manifest = await this.getPackagelocalByNameNext(name); + const localManifest = await this.getPackagelocalByNameNext(name); // if continue, the version to be published does not exist - if (manifest?.versions[versionToPublish] != null) { - debug('%s version %s already exists', name, versionToPublish); + if (localManifest?.versions[versionToPublish] != null) { + debug('%s version %s already exists (locally)', 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, uplinksLook); if (remoteManifest?.versions[versionToPublish] != null) { - debug('%s version %s already exists', name, versionToPublish); + debug('%s version %s already exists (upstream)', name, versionToPublish); throw errorUtils.getConflict(); } @@ -1127,9 +1127,12 @@ class Storage { // 1. after tarball has been successfully uploaded, we update the version try { - // TODO: review why do this - versions[versionToPublish].readme = - _.isNil(manifest.readme) === false ? String(manifest.readme) : ''; + // Older package managers like npm6 do not send readme content as part of version but include it on root level + if (_.isEmpty(versions[versionToPublish].readme)) { + versions[versionToPublish].readme = + _.isNil(manifest.readme) === false ? String(manifest.readme) : ''; + } + // addVersion will move the readme from the the published version to the root level await this.addVersion(name, versionToPublish, versions[versionToPublish], null); } catch (err: any) { logger.error({ err: err.message }, 'updated version has failed: @{err}'); @@ -1145,7 +1148,7 @@ class Storage { // 1. add version // 2. merge versions // 3. upload tarball - // 3.update once to the storage (easy peasy) + // 4. update once to the storage (easy peasy) mergedManifest = await this.mergeTagsNext(name, manifest[DIST_TAGS]); } catch (err: any) { logger.error({ err: err.message }, 'merge version has failed: @{err}'); diff --git a/packages/store/test/storage.spec.ts b/packages/store/test/storage.spec.ts index f715b0e98..203d7b0bf 100644 --- a/packages/store/test/storage.spec.ts +++ b/packages/store/test/storage.spec.ts @@ -141,6 +141,8 @@ describe('storage', () => { modified: mockDate, }); expect(manifest[DIST_TAGS]).toEqual({ latest: '1.0.0' }); + // verdaccio keeps latest version of readme on manifest level but not by version + expect(manifest.versions['1.0.0'].readme).not.toBeDefined(); expect(manifest.readme).toEqual('# test'); expect(manifest._attachments).toEqual({}); expect(typeof manifest._rev).toBeTruthy(); @@ -309,6 +311,50 @@ describe('storage', () => { }) ).rejects.toThrow(API_ERROR.PACKAGE_EXIST); }); + + test('create private package with readme only in manifest', async () => { + const mockDate = '2018-01-14T11:17:40.712Z'; + MockDate.set(mockDate); + const pkgName = 'upstream'; + const requestOptions = { + host: 'localhost', + protocol: 'http', + headers: {}, + }; + const config = new Config( + configExample( + { + ...getDefaultConfig(), + storage: generateRandomStorage(), + }, + './fixtures/config/updateManifest-1.yaml', + __dirname + ) + ); + const storage = new Storage(config); + await storage.init(config); + const bodyNewManifest = generatePackageMetadata(pkgName, '1.0.0'); + + // Remove readme from version to simulate behaviour of older package managers like npm6 + bodyNewManifest.versions['1.0.0'].readme = ''; + + await storage.updateManifest(bodyNewManifest, { + signal: new AbortController().signal, + name: pkgName, + uplinksLook: true, + revision: '1', + requestOptions, + }); + const manifest = (await storage.getPackageByOptions({ + name: pkgName, + uplinksLook: true, + requestOptions, + })) as Manifest; + + // verdaccio keeps latest version of readme on manifest level but not by version + expect(manifest.versions['1.0.0'].readme).not.toBeDefined(); + expect(manifest.readme).toEqual('# test'); + }); }); describe('deprecate', () => { test.each([['foo'], ['@scope/foo']])('deprecate package %s', async (pkgName) => { diff --git a/packages/tools/helpers/src/actions.ts b/packages/tools/helpers/src/actions.ts index 8406d2332..8dfe362a8 100644 --- a/packages/tools/helpers/src/actions.ts +++ b/packages/tools/helpers/src/actions.ts @@ -10,7 +10,9 @@ const debug = buildDebug('verdaccio:tools:helpers:actions'); export function publishVersion(app, pkgName, version, metadata: Partial = {}): any { debug('publishVersion %s : %s : %s', pkgName, version, JSON.stringify(metadata, null, 2)); - const pkgMetadata = { ...generatePackageMetadata(pkgName, version), ...metadata }; + let pkgMetadata = { ...generatePackageMetadata(pkgName, version), ...metadata }; + // sync metadata readme to version of package + pkgMetadata.versions[version].readme = metadata.readme ? (metadata.readme as string) : ''; debug('metadata %s', JSON.stringify(pkgMetadata, null, 2)); return ( supertest(app)