From 346ce7b90e0847a8e2cb6b730f092065405ce6f4 Mon Sep 17 00:00:00 2001 From: David First Date: Mon, 15 Apr 2024 20:29:08 -0400 Subject: [PATCH 1/3] fix(publish), do not fail the build if npm throws EPERM and the version actually published --- scopes/pkg/pkg/publisher.ts | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/scopes/pkg/pkg/publisher.ts b/scopes/pkg/pkg/publisher.ts index e86eb340b7f2..505a43379c18 100644 --- a/scopes/pkg/pkg/publisher.ts +++ b/scopes/pkg/pkg/publisher.ts @@ -68,7 +68,7 @@ export class Publisher { publishParams.push(...extraArgsSplit); } const publishParamsStr = publishParams.join(' '); - + const getPkgJson = async () => fsx.readJSON(`${capsule.path}/package.json`); const componentIdStr = capsule.id.toString(); const errors: string[] = []; try { @@ -82,7 +82,18 @@ export class Publisher { const errorDetails = typeof err === 'object' && err && 'message' in err ? err.message : err; const errorMsg = `failed running ${this.packageManager} ${publishParamsStr} at ${cwd}: ${errorDetails}`; this.logger.error(`${componentIdStr}, ${errorMsg}`); - errors.push(errorMsg); + let isPublished = false; + if (typeof errorDetails === 'string' && errorDetails.includes('EPERM')) { + const pkgJson = await getPkgJson(); + const versionOnNpm = await this.checkVersionOnNpm(pkgJson.name); + if (versionOnNpm && versionOnNpm === pkgJson.version) { + isPublished = true; + this.logger.debug( + `${componentIdStr}, package ${pkgJson.name} is already on npm with version ${versionOnNpm}` + ); + } + } + if (!isPublished) errors.push(errorMsg); } let metadata: TaskMetadata = {}; if (errors.length === 0 && !this.options.dryRun) { @@ -93,6 +104,16 @@ export class Publisher { return { component, metadata, errors, startTime, endTime: Date.now() }; } + private async checkVersionOnNpm(pkgName: string): Promise { + try { + const results = await execa(this.packageManager, ['view', pkgName, 'version']); + return results.stdout; + } catch (err: unknown) { + this.logger.error(`failed running ${this.packageManager} view ${pkgName} version: ${err}`, err); + return undefined; + } + } + private getTagFlagForPreRelease(id: ComponentID): string[] { const preReleaseData = id.getVersionPreReleaseData(); if (!preReleaseData) return []; From 788b3eb46107d97e30e8abeae80d4d45c034e16e Mon Sep 17 00:00:00 2001 From: David First Date: Mon, 15 Apr 2024 21:20:39 -0400 Subject: [PATCH 2/3] wait 5 seconds before checking if it exits in NPM. Add env var to change the 5 seconds --- scopes/pkg/pkg/publisher.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scopes/pkg/pkg/publisher.ts b/scopes/pkg/pkg/publisher.ts index 505a43379c18..5a478a68913e 100644 --- a/scopes/pkg/pkg/publisher.ts +++ b/scopes/pkg/pkg/publisher.ts @@ -85,6 +85,8 @@ export class Publisher { let isPublished = false; if (typeof errorDetails === 'string' && errorDetails.includes('EPERM')) { const pkgJson = await getPkgJson(); + // sleep 5 seconds + await new Promise((resolve) => setTimeout(resolve, Number(process.env.NPM_WAKE_UP || 5000))); const versionOnNpm = await this.checkVersionOnNpm(pkgJson.name); if (versionOnNpm && versionOnNpm === pkgJson.version) { isPublished = true; From 4ce083157a5c9c2505401903f135a2136274dbb9 Mon Sep 17 00:00:00 2001 From: Zoltan Kochan Date: Tue, 16 Apr 2024 11:31:26 +0200 Subject: [PATCH 3/3] fix: publish should not fail if the registry already has a package with the same integrity --- pnpm-lock.yaml | 33 +++++++++++++++++++++++++++------ scopes/pkg/pkg/publisher.ts | 21 +++++++++++++-------- workspace.jsonc | 1 + 3 files changed, 41 insertions(+), 14 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ec7ec7ee8787..4b528c7f268d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1215,6 +1215,9 @@ importers: '@types/socket.io-client': specifier: 1.4.35 version: 1.4.35 + '@types/ssri': + specifier: ^7.1.5 + version: 7.1.5 '@types/testing-library__jest-dom': specifier: 5.9.5 version: 5.9.5 @@ -23190,6 +23193,9 @@ importers: '@types/mocha': specifier: 9.1.0 version: 9.1.0 + '@types/ssri': + specifier: ^7.1.5 + version: 7.1.5 chai: specifier: 4.3.0 version: 4.3.0 @@ -26296,9 +26302,15 @@ importers: '@types/react-dom': specifier: ^17.0.21 version: 17.0.25 + assert: + specifier: ^2.1.0 + version: 2.1.0 browserify-zlib: specifier: 0.2.0 version: 0.2.0 + buffer: + specifier: 6.0.3 + version: 6.0.3 camelcase: specifier: 6.2.0 version: 6.2.0 @@ -26353,6 +26365,9 @@ importers: process: specifier: 0.11.10 version: 0.11.10 + punycode: + specifier: ^2.3.1 + version: 2.3.1 querystring-es3: specifier: 0.2.1 version: 0.2.1 @@ -26374,12 +26389,21 @@ importers: stream-http: specifier: 3.2.0 version: 3.2.0 + string_decoder: + specifier: ^1.3.0 + version: 1.3.0 timers-browserify: specifier: 2.0.12 version: 2.0.12 tty-browserify: specifier: 0.0.1 version: 0.0.1 + url: + specifier: ^0.11.3 + version: 0.11.3 + util: + specifier: ^0.12.5 + version: 0.12.5 vm-browserify: specifier: 1.1.2 version: 1.1.2 @@ -47333,7 +47357,6 @@ packages: resolution: {integrity: sha512-odD/56S3B51liILSk5aXJlnYt99S6Rt9EFDDqGtJM26rKHApHcwyU/UoYHrzKkdkHMAIquGWCuHtQTbes+FRQw==} dependencies: '@types/node': 20.2.5 - dev: false /@types/stack-utils@2.0.3: resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} @@ -48513,7 +48536,7 @@ packages: '@types/semver': 7.3.4 '@types/treeify': 1.0.3 '@yarnpkg/fslib': 3.0.2 - '@yarnpkg/libzip': 3.0.1(@yarnpkg/fslib@3.0.2) + '@yarnpkg/libzip': 3.0.1 '@yarnpkg/parsers': 3.0.0 '@yarnpkg/shell': 4.0.0 camelcase: 5.3.1 @@ -48544,7 +48567,7 @@ packages: '@types/semver': 7.3.4 '@types/treeify': 1.0.3 '@yarnpkg/fslib': 3.0.2 - '@yarnpkg/libzip': 3.0.1(@yarnpkg/fslib@3.0.2) + '@yarnpkg/libzip': 3.0.1 '@yarnpkg/parsers': 3.0.0 '@yarnpkg/shell': 4.0.0 camelcase: 5.3.1 @@ -48625,11 +48648,9 @@ packages: tslib: 1.14.1 dev: false - /@yarnpkg/libzip@3.0.1(@yarnpkg/fslib@3.0.2): + /@yarnpkg/libzip@3.0.1: resolution: {integrity: sha512-fiqRLk2fyd2r34/Qc7HlP8fKIo1CK5CZpLNObJwnbFmZQN2hVanovFlG++3oH3qYJymEmjPl5EGsygcEJZl4Pg==} engines: {node: '>=18.12.0'} - peerDependencies: - '@yarnpkg/fslib': ^3.0.2 dependencies: '@types/emscripten': 1.39.10 '@yarnpkg/fslib': 3.0.2 diff --git a/scopes/pkg/pkg/publisher.ts b/scopes/pkg/pkg/publisher.ts index 5a478a68913e..eb7f1d72dc23 100644 --- a/scopes/pkg/pkg/publisher.ts +++ b/scopes/pkg/pkg/publisher.ts @@ -10,6 +10,7 @@ import { Scope } from '@teambit/legacy/dist/scope'; import fsx from 'fs-extra'; import mapSeries from 'p-map-series'; import { join } from 'path'; +import ssri from 'ssri'; import execa from 'execa'; import { PkgAspect } from './pkg.aspect'; import { PkgExtensionConfig } from './pkg.main.runtime'; @@ -83,15 +84,18 @@ export class Publisher { const errorMsg = `failed running ${this.packageManager} ${publishParamsStr} at ${cwd}: ${errorDetails}`; this.logger.error(`${componentIdStr}, ${errorMsg}`); let isPublished = false; - if (typeof errorDetails === 'string' && errorDetails.includes('EPERM')) { + if (typeof errorDetails === 'string' && errorDetails.includes('EPERM') && tarPath) { const pkgJson = await getPkgJson(); // sleep 5 seconds await new Promise((resolve) => setTimeout(resolve, Number(process.env.NPM_WAKE_UP || 5000))); - const versionOnNpm = await this.checkVersionOnNpm(pkgJson.name); - if (versionOnNpm && versionOnNpm === pkgJson.version) { - isPublished = true; + const integrityOnNpm = await this.getIntegrityOnNpm(pkgJson.name, pkgJson.version); + if (integrityOnNpm && tarPath) { + const tarData = fsx.readFileSync(join(tarFolderPath, tarPath)); + // If the integrity of the tarball in the registry matches the local one, + // we consider the package published + isPublished = ssri.checkData(tarData, integrityOnNpm) !== false; this.logger.debug( - `${componentIdStr}, package ${pkgJson.name} is already on npm with version ${versionOnNpm}` + `${componentIdStr}, package ${pkgJson.name} is already on npm with version ${pkgJson.version}` ); } } @@ -106,12 +110,13 @@ export class Publisher { return { component, metadata, errors, startTime, endTime: Date.now() }; } - private async checkVersionOnNpm(pkgName: string): Promise { + private async getIntegrityOnNpm(pkgName: string, pkgVersion: string): Promise { + const args = ['view', `${pkgName}@${pkgVersion}`, 'dist.integrity']; try { - const results = await execa(this.packageManager, ['view', pkgName, 'version']); + const results = await execa(this.packageManager, args); return results.stdout; } catch (err: unknown) { - this.logger.error(`failed running ${this.packageManager} view ${pkgName} version: ${err}`, err); + this.logger.error(`failed running ${this.packageManager} ${args.join(' ')}: ${err}`, err); return undefined; } } diff --git a/workspace.jsonc b/workspace.jsonc index d33644e9f7c6..a9aa9b707070 100644 --- a/workspace.jsonc +++ b/workspace.jsonc @@ -346,6 +346,7 @@ "@types/react-syntax-highlighter": "15.5.10", "@types/react-tabs": "2.3.2", "@types/socket.io-client": "1.4.35", + "@types/ssri": "^7.1.5", "@types/testing-library__jest-dom": "5.9.5", "@types/ua-parser-js": "0.7.35", "@types/url-join": "4.0.0",