From 6e47a6d46ca1511ac2eb56bf05c7692412971711 Mon Sep 17 00:00:00 2001 From: Kamil Piechaczek Date: Mon, 7 Oct 2024 21:23:44 +0200 Subject: [PATCH] Implemented the re-publish phase in the "publishPackages()" task. --- .../lib/tasks/publishpackages.js | 106 ++++-- .../lib/utils/publishpackageonnpmcallback.js | 24 +- .../tests/tasks/publishpackages.js | 318 ++++++++++++++++-- .../utils/publishpackageonnpmcallback.js | 33 -- 4 files changed, 378 insertions(+), 103 deletions(-) diff --git a/packages/ckeditor5-dev-release-tools/lib/tasks/publishpackages.js b/packages/ckeditor5-dev-release-tools/lib/tasks/publishpackages.js index e4aa9c5bf..91e973a88 100644 --- a/packages/ckeditor5-dev-release-tools/lib/tasks/publishpackages.js +++ b/packages/ckeditor5-dev-release-tools/lib/tasks/publishpackages.js @@ -4,6 +4,7 @@ */ import upath from 'upath'; +import fs from 'fs-extra'; import { glob } from 'glob'; import assertNpmAuthorization from '../utils/assertnpmauthorization.js'; import assertPackages from '../utils/assertpackages.js'; @@ -11,37 +12,42 @@ import assertNpmTag from '../utils/assertnpmtag.js'; import assertFilesToPublish from '../utils/assertfilestopublish.js'; import executeInParallel from '../utils/executeinparallel.js'; import publishPackageOnNpmCallback from '../utils/publishpackageonnpmcallback.js'; +import checkVersionAvailability from '../utils/checkversionavailability.js'; /** * The purpose of the script is to validate the packages prepared for the release and then release them on npm. * * The validation contains the following steps in each package: * - User must be logged to npm on the specified account. - * - The package directory mmust contain `package.json` file. + * - The package directory must contain `package.json` file. * - All other files expected to be released must exist in the package directory. * - The npm tag must match the tag calculated from the package version. * * When the validation for each package passes, packages are published on npm. Optional callback is called for confirmation whether to * continue. * + * If a package has already been published, the script does not try to publish it again. Instead, it treats the package as published. + * Whenever a communication between the script and npm fails, it tries to re-publish a package (up to three attempts). + * * @param {object} options * @param {string} options.packagesDirectory Relative path to a location of packages to release. * @param {string} options.npmOwner The account name on npm, which should be used to publish the packages. - * @param {ListrTaskObject} options.listrTask An instance of `ListrTask`. + * @param {ListrTaskObject} [options.listrTask] An instance of `ListrTask`. * @param {AbortSignal|null} [options.signal=null] Signal to abort the asynchronous process. * @param {string} [options.npmTag='staging'] The npm distribution tag. * @param {Object.>|null} [options.optionalEntries=null] Specifies which entries from the `files` field in the * `package.json` are optional. The key is a package name, and its value is an array of optional entries from the `files` field, for which * it is allowed not to match any file. The `options.optionalEntries` object may also contain the `default` key, which is used for all * packages that do not have own definition. - * @param {string} [options.confirmationCallback=null] An callback whose response decides to continue the publishing packages. Synchronous - * and asynchronous callbacks are supported. + * @param {function|null} [options.confirmationCallback=null] An callback whose response decides to continue the publishing packages. + * Synchronous and asynchronous callbacks are supported. * @param {boolean} [options.requireEntryPoint=false] Whether to verify if packages to publish define an entry point. In other words, * whether their `package.json` define the `main` field. * @param {Array.} [options.optionalEntryPointPackages=[]] If the entry point validator is enabled (`requireEntryPoint=true`), * this array contains a list of packages that will not be checked. In other words, they do not have to define the entry point. * @param {string} [options.cwd=process.cwd()] Current working directory from which all paths will be resolved. * @param {number} [options.concurrency=4] Number of CPUs that will execute the task. + * @param {number} [options.attempts=3] Number of attempts. After reaching 0, it won't be publishing packages again. * @returns {Promise} */ export default async function publishPackages( options ) { @@ -56,15 +62,14 @@ export default async function publishPackages( options ) { requireEntryPoint = false, optionalEntryPointPackages = [], cwd = process.cwd(), - concurrency = 4 + concurrency = 4, + attempts = 3 } = options; + const remainingAttempts = attempts - 1; await assertNpmAuthorization( npmOwner ); - const packagePaths = await glob( '*/', { - cwd: upath.join( cwd, packagesDirectory ), - absolute: true - } ); + const packagePaths = await findPathsToPackages( cwd, packagesDirectory ); await assertPackages( packagePaths, { requireEntryPoint, optionalEntryPointPackages } ); await assertFilesToPublish( packagePaths, optionalEntries ); @@ -72,17 +77,76 @@ export default async function publishPackages( options ) { const shouldPublishPackages = confirmationCallback ? await confirmationCallback() : true; - if ( shouldPublishPackages ) { - await executeInParallel( { - cwd, - packagesDirectory, - listrTask, - taskToExecute: publishPackageOnNpmCallback, - taskOptions: { - npmTag - }, - signal, - concurrency - } ); + if ( !shouldPublishPackages ) { + return Promise.resolve(); + } + + await removeAlreadyPublishedPackages( packagePaths ); + + await executeInParallel( { + cwd, + packagesDirectory, + listrTask, + taskToExecute: publishPackageOnNpmCallback, + taskOptions: { + npmTag + }, + signal, + concurrency + } ); + + const packagePathsAfterPublishing = await findPathsToPackages( cwd, packagesDirectory ); + + // All packages have been published. No need for re-executing. + if ( !packagePathsAfterPublishing.length ) { + return Promise.resolve(); + } + + // No more attempts. Abort. + if ( remainingAttempts <= 0 ) { + throw new Error( 'Some packages could not be published.' ); + } + + // Let's give an npm a moment for taking a breath... + await wait( 1000 ); + + // ...and try again. + return publishPackages( { + packagesDirectory, + npmOwner, + listrTask, + signal, + npmTag, + optionalEntries, + requireEntryPoint, + optionalEntryPointPackages, + cwd, + concurrency, + confirmationCallback: null, // Do not ask again if already here. + attempts: remainingAttempts + } ); +} + +function findPathsToPackages( cwd, packagesDirectory ) { + return glob( '*/', { + cwd: upath.join( cwd, packagesDirectory ), + absolute: true + } ); +} + +async function removeAlreadyPublishedPackages( packagePaths ) { + for ( const absolutePackagePath of packagePaths ) { + const pkgJson = await fs.readJson( upath.join( absolutePackagePath, 'package.json' ) ); + const isAvailable = await checkVersionAvailability( pkgJson.version, pkgJson.name ); + + if ( !isAvailable ) { + await fs.remove( absolutePackagePath ); + } } } + +function wait( time ) { + return new Promise( resolve => { + setTimeout( resolve, time ); + } ); +} diff --git a/packages/ckeditor5-dev-release-tools/lib/utils/publishpackageonnpmcallback.js b/packages/ckeditor5-dev-release-tools/lib/utils/publishpackageonnpmcallback.js index b7bb7683c..5eed5444c 100644 --- a/packages/ckeditor5-dev-release-tools/lib/utils/publishpackageonnpmcallback.js +++ b/packages/ckeditor5-dev-release-tools/lib/utils/publishpackageonnpmcallback.js @@ -14,26 +14,16 @@ export default async function publishPackageOnNpmCallback( packagePath, taskOptions ) { const { tools } = await import( '@ckeditor/ckeditor5-dev-utils' ); const { default: fs } = await import( 'fs-extra' ); - const { default: path } = await import( 'upath' ); - const options = { - cwd: packagePath, - async: true, - verbosity: 'error' - }; - - const result = await tools.shExec( `npm publish --access=public --tag ${ taskOptions.npmTag }`, options ) - .catch( e => { - const packageName = path.basename( packagePath ); - - if ( e.toString().includes( 'code E409' ) ) { - return { shouldKeepDirectory: true }; - } - - throw new Error( `Unable to publish "${ packageName }" package.` ); + try { + await tools.shExec( `npm publish --access=public --tag ${ taskOptions.npmTag }`, { + cwd: packagePath, + async: true, + verbosity: 'error' } ); - if ( !result || !result.shouldKeepDirectory ) { await fs.remove( packagePath ); + } catch { + // Do nothing if an error occurs. A parent task will handle it. } } diff --git a/packages/ckeditor5-dev-release-tools/tests/tasks/publishpackages.js b/packages/ckeditor5-dev-release-tools/tests/tasks/publishpackages.js index d44cb7d71..05f630f53 100644 --- a/packages/ckeditor5-dev-release-tools/tests/tasks/publishpackages.js +++ b/packages/ckeditor5-dev-release-tools/tests/tasks/publishpackages.js @@ -3,9 +3,11 @@ * For licensing, see LICENSE.md. */ -import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { beforeEach, afterEach, describe, expect, it, vi } from 'vitest'; import upath from 'upath'; +import fs from 'fs-extra'; import { glob } from 'glob'; +import { differenceInMilliseconds } from 'date-fns'; import assertNpmAuthorization from '../../lib/utils/assertnpmauthorization.js'; import assertPackages from '../../lib/utils/assertpackages.js'; import assertNpmTag from '../../lib/utils/assertnpmtag.js'; @@ -13,17 +15,22 @@ import assertFilesToPublish from '../../lib/utils/assertfilestopublish.js'; import executeInParallel from '../../lib/utils/executeinparallel.js'; import publishPackageOnNpmCallback from '../../lib/utils/publishpackageonnpmcallback.js'; import publishPackages from '../../lib/tasks/publishpackages.js'; +import checkVersionAvailability from '../../lib/utils/checkversionavailability.js'; vi.mock( 'glob' ); +vi.mock( 'fs-extra' ); vi.mock( '../../lib/utils/assertnpmauthorization.js' ); vi.mock( '../../lib/utils/assertpackages.js' ); vi.mock( '../../lib/utils/assertnpmtag.js' ); vi.mock( '../../lib/utils/assertfilestopublish.js' ); vi.mock( '../../lib/utils/executeinparallel.js' ); vi.mock( '../../lib/utils/publishpackageonnpmcallback.js' ); +vi.mock( '../../lib/utils/checkversionavailability.js' ); describe( 'publishPackages()', () => { beforeEach( () => { + vi.spyOn( process, 'cwd' ).mockReturnValue( '/work/project' ); + vi.mocked( glob ).mockResolvedValue( [] ); vi.mocked( assertNpmAuthorization ).mockResolvedValue(); vi.mocked( assertPackages ).mockResolvedValue(); @@ -31,6 +38,9 @@ describe( 'publishPackages()', () => { vi.mocked( assertFilesToPublish ).mockResolvedValue(); vi.mocked( executeInParallel ).mockResolvedValue(); vi.mocked( publishPackageOnNpmCallback ).mockResolvedValue(); + + vi.mocked( fs ).readJson.mockResolvedValue( { name: '', version: '' } ); + vi.mocked( checkVersionAvailability ).mockResolvedValue( true ); } ); it( 'should not throw if all assertion passes', async () => { @@ -46,7 +56,7 @@ describe( 'publishPackages()', () => { npmOwner: 'pepe' } ); - expect( vi.mocked( glob ) ).toHaveBeenCalledExactlyOnceWith( + expect( vi.mocked( glob ) ).toHaveBeenCalledWith( '*/', expect.objectContaining( { cwd: upath.join( process.cwd(), 'packages' ), @@ -55,14 +65,13 @@ describe( 'publishPackages()', () => { } ); it( 'should read the package directory (custom `cwd`)', async () => { - vi.spyOn( process, 'cwd' ).mockReturnValue( '/work/project' ); - await publishPackages( { packagesDirectory: 'packages', - npmOwner: 'pepe' + npmOwner: 'pepe', + cwd: '/work/project' } ); - expect( vi.mocked( glob ) ).toHaveBeenCalledExactlyOnceWith( + expect( vi.mocked( glob ) ).toHaveBeenCalledWith( '*/', expect.objectContaining( { cwd: '/work/project/packages', @@ -91,10 +100,12 @@ describe( 'publishPackages()', () => { } ); it( 'should assert that each found directory is a package', async () => { - vi.mocked( glob ).mockResolvedValue( [ - '/work/project/packages/ckeditor5-foo', - '/work/project/packages/ckeditor5-bar' - ] ); + vi.mocked( glob ) + .mockResolvedValueOnce( [ + '/work/project/packages/ckeditor5-foo', + '/work/project/packages/ckeditor5-bar' + ] ) + .mockResolvedValue( [] ); await publishPackages( { packagesDirectory: 'packages', @@ -115,10 +126,12 @@ describe( 'publishPackages()', () => { // See: https://github.com/ckeditor/ckeditor5/issues/15127. it( 'should allow enabling the "package entry point" validator', async () => { - vi.mocked( glob ).mockResolvedValue( [ - '/work/project/packages/ckeditor5-foo', - '/work/project/packages/ckeditor5-bar' - ] ); + vi.mocked( glob ) + .mockResolvedValueOnce( [ + '/work/project/packages/ckeditor5-foo', + '/work/project/packages/ckeditor5-bar' + ] ) + .mockResolvedValue( [] ); await publishPackages( { packagesDirectory: 'packages', @@ -155,10 +168,12 @@ describe( 'publishPackages()', () => { } ); it( 'should assert that each required file exists in the package directory (no optional entries)', async () => { - vi.mocked( glob ).mockResolvedValue( [ - '/work/project/packages/ckeditor5-foo', - '/work/project/packages/ckeditor5-bar' - ] ); + vi.mocked( glob ) + .mockResolvedValueOnce( [ + '/work/project/packages/ckeditor5-foo', + '/work/project/packages/ckeditor5-bar' + ] ) + .mockResolvedValue( [] ); await publishPackages( { packagesDirectory: 'packages', @@ -175,10 +190,12 @@ describe( 'publishPackages()', () => { } ); it( 'should assert that each required file exists in the package directory (with optional entries)', async () => { - vi.mocked( glob ).mockResolvedValue( [ - '/work/project/packages/ckeditor5-foo', - '/work/project/packages/ckeditor5-bar' - ] ); + vi.mocked( glob ) + .mockResolvedValueOnce( [ + '/work/project/packages/ckeditor5-foo', + '/work/project/packages/ckeditor5-bar' + ] ) + .mockResolvedValue( [] ); await publishPackages( { packagesDirectory: 'packages', @@ -211,10 +228,12 @@ describe( 'publishPackages()', () => { } ); it( 'should assert that version tag matches the npm tag (default npm tag)', async () => { - vi.mocked( glob ).mockResolvedValue( [ - '/work/project/packages/ckeditor5-foo', - '/work/project/packages/ckeditor5-bar' - ] ); + vi.mocked( glob ) + .mockResolvedValueOnce( [ + '/work/project/packages/ckeditor5-foo', + '/work/project/packages/ckeditor5-bar' + ] ) + .mockResolvedValue( [] ); await publishPackages( { packagesDirectory: 'packages', @@ -231,10 +250,12 @@ describe( 'publishPackages()', () => { } ); it( 'should assert that version tag matches the npm tag (custom npm tag)', async () => { - vi.mocked( glob ).mockResolvedValue( [ - '/work/project/packages/ckeditor5-foo', - '/work/project/packages/ckeditor5-bar' - ] ); + vi.mocked( glob ) + .mockResolvedValueOnce( [ + '/work/project/packages/ckeditor5-foo', + '/work/project/packages/ckeditor5-bar' + ] ) + .mockResolvedValue( [] ); await publishPackages( { packagesDirectory: 'packages', @@ -365,5 +386,238 @@ describe( 'publishPackages()', () => { 'Unable to publish "ckeditor5-foo" package.' ); } ); -} ) -; + + it( 'should verify if given package can be published', async () => { + vi.mocked( glob ) + .mockResolvedValueOnce( [ + '/work/project/packages/ckeditor5-foo', + '/work/project/packages/ckeditor5-bar' + ] ) + .mockResolvedValue( [] ); + + vi.mocked( fs ).readJson + .mockResolvedValueOnce( { name: '@ckeditor/ckeditor5-foo', version: '1.0.0' } ) + .mockResolvedValueOnce( { name: '@ckeditor/ckeditor5-bar', version: '1.0.0' } ); + + await publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe' + } ); + + expect( vi.mocked( fs ).readJson ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( fs ).readJson ).toHaveBeenCalledWith( '/work/project/packages/ckeditor5-foo/package.json' ); + expect( vi.mocked( fs ).readJson ).toHaveBeenCalledWith( '/work/project/packages/ckeditor5-bar/package.json' ); + + expect( vi.mocked( checkVersionAvailability ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( checkVersionAvailability ) ).toHaveBeenCalledWith( '1.0.0', '@ckeditor/ckeditor5-foo' ); + expect( vi.mocked( checkVersionAvailability ) ).toHaveBeenCalledWith( '1.0.0', '@ckeditor/ckeditor5-bar' ); + } ); + + it( 'should remove a package if is already published', async () => { + vi.mocked( glob ) + .mockResolvedValueOnce( [ + '/work/project/packages/ckeditor5-foo', + '/work/project/packages/ckeditor5-bar' + ] ) + .mockResolvedValue( [] ); + + vi.mocked( fs ).readJson + .mockResolvedValueOnce( { name: '@ckeditor/ckeditor5-foo', version: '1.0.0' } ) + .mockResolvedValueOnce( { name: '@ckeditor/ckeditor5-bar', version: '1.0.0' } ); + + vi.mocked( checkVersionAvailability ) + .mockResolvedValueOnce( false ) + .mockResolvedValueOnce( true ); + + await publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe' + } ); + + expect( vi.mocked( fs ).remove ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs ).remove ).toHaveBeenCalledWith( '/work/project/packages/ckeditor5-foo' ); + } ); + + describe( 're-publish packages that could not be published', () => { + beforeEach( () => { + vi.useFakeTimers(); + } ); + + afterEach( () => { + vi.useRealTimers(); + } ); + + it( 'should not execute the specified `confirmationCallback` when re-publishing packages', async () => { + vi.mocked( glob ) + // First execution. + .mockResolvedValueOnce( [ + '/work/project/packages/ckeditor5-bar' + ] ) + // Check for failed packages. + .mockResolvedValueOnce( [ + '/work/project/packages/ckeditor5-bar' + ] ) + // Repeat execution: look for packages to release. + .mockResolvedValueOnce( [ + '/work/project/packages/ckeditor5-bar' + ] ) + // Check for failed packages. + .mockResolvedValue( [] ); + + vi.mocked( fs ).readJson.mockResolvedValue( {} ); + + const confirmationCallback = vi.fn().mockReturnValue( true ); + const promise = publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe', + confirmationCallback + } ); + + await vi.advanceTimersToNextTimerAsync(); + await promise; + + expect( confirmationCallback ).toHaveBeenCalledOnce(); + } ); + + it( 'should execute itself once again after a timeout passes if some packages could not be published', async () => { + vi.mocked( glob ) + // First execution. + .mockResolvedValueOnce( [ + '/work/project/packages/ckeditor5-bar' + ] ) + // Check for failed packages. + .mockResolvedValueOnce( [ + '/work/project/packages/ckeditor5-bar' + ] ) + // Repeat execution: look for packages to release. + .mockResolvedValueOnce( [ + '/work/project/packages/ckeditor5-bar' + ] ) + // Check for failed packages. + .mockResolvedValue( [] ); + + vi.mocked( fs ).readJson.mockResolvedValue( {} ); + + const dateBefore = new Date(); + + const promise = publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe' + } ); + + await vi.advanceTimersToNextTimerAsync(); + + await promise; + const dateAfter = new Date(); + + expect( differenceInMilliseconds( dateAfter, dateBefore ) ).toEqual( 1000 ); + } ); + + it( 'should try to publish packages thrice before rejecting a promise', async () => { + vi.mocked( glob ) + .mockResolvedValueOnce( [ '/work/project/packages/ckeditor5-bar' ] ) + .mockResolvedValueOnce( [ '/work/project/packages/ckeditor5-bar' ] ) + .mockResolvedValueOnce( [ '/work/project/packages/ckeditor5-bar' ] ) + .mockResolvedValueOnce( [ '/work/project/packages/ckeditor5-bar' ] ) + .mockResolvedValueOnce( [ '/work/project/packages/ckeditor5-bar' ] ) + .mockResolvedValue( [] ); + + vi.mocked( fs ).readJson.mockResolvedValue( {} ); + + const promise = publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe' + } ); + + // Needed twice because the third attempt does not setup a timeout. + await vi.advanceTimersToNextTimerAsync(); + await vi.advanceTimersToNextTimerAsync(); + await promise; + + expect( vi.mocked( executeInParallel ) ).toHaveBeenCalledTimes( 3 ); + } ); + + it( 'should execute itself and publish the non-published packages again (integration)', async () => { + vi.mocked( glob ) + // First execution. + .mockResolvedValueOnce( [ + '/work/project/packages/ckeditor5-foo', + '/work/project/packages/ckeditor5-bar' + ] ) + // Check for failed packages. + .mockResolvedValueOnce( [ + '/work/project/packages/ckeditor5-foo', + '/work/project/packages/ckeditor5-bar' + ] ) + // Repeat execution: look for packages to release. + .mockResolvedValueOnce( [ + '/work/project/packages/ckeditor5-foo', + '/work/project/packages/ckeditor5-bar' + ] ) + // Repeat execution: Check for failed packages. + .mockResolvedValue( [] ); + + vi.mocked( fs ).readJson.mockImplementation( input => { + return Promise.resolve( { + name: '@ckeditor/' + input.split( '/' ).at( -1 ), + version: '1.0.0' + } ); + } ); + + vi.mocked( checkVersionAvailability ) + // @ckeditor/ckeditor5-foo + .mockResolvedValueOnce( true ) + // @ckeditor/ckeditor5-bar + .mockResolvedValueOnce( true ) + // @ckeditor/ckeditor5-foo + // Simulate a package was published but npm returned an error while uploading. + .mockResolvedValueOnce( false ) + // @ckeditor/ckeditor5-bar + .mockResolvedValueOnce( true ); + + const promise = publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe' + } ); + + await vi.advanceTimersToNextTimerAsync(); + await promise; + + expect( vi.mocked( fs ).remove ).toHaveBeenCalledTimes( 1 ); + expect( vi.mocked( fs ).remove ).toHaveBeenCalledWith( '/work/project/packages/ckeditor5-foo' ); + + expect( vi.mocked( executeInParallel ) ).toHaveBeenCalledTimes( 2 ); + expect( vi.mocked( glob ) ).toHaveBeenCalledTimes( 4 ); + } ); + + it( 'should reject a promise if cannot publish packages and there is no more attempting', async () => { + vi.mocked( glob ).mockResolvedValue( [ '/work/project/packages/ckeditor5-bar' ] ); + + vi.mocked( fs ).readJson.mockResolvedValue( { + name: '@ckeditor/ckeditor5-bar', + version: '1.0.0' + } ); + + await expect( publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe', + attempts: 1 + } ) ).rejects.toThrow( 'Some packages could not be published.' ); + } ); + + it( 'should reject a promise if cannot publish packages and there is no more attempting (a negative attempts value)', async () => { + vi.mocked( glob ).mockResolvedValue( [ '/work/project/packages/ckeditor5-bar' ] ); + + vi.mocked( fs ).readJson.mockResolvedValue( { + name: '@ckeditor/ckeditor5-bar', + version: '1.0.0' + } ); + + await expect( publishPackages( { + packagesDirectory: 'packages', + npmOwner: 'pepe', + attempts: -5 + } ) ).rejects.toThrow( 'Some packages could not be published.' ); + } ); + } ); +} ); diff --git a/packages/ckeditor5-dev-release-tools/tests/utils/publishpackageonnpmcallback.js b/packages/ckeditor5-dev-release-tools/tests/utils/publishpackageonnpmcallback.js index aa49a1a50..7141e8b0b 100644 --- a/packages/ckeditor5-dev-release-tools/tests/utils/publishpackageonnpmcallback.js +++ b/packages/ckeditor5-dev-release-tools/tests/utils/publishpackageonnpmcallback.js @@ -72,39 +72,6 @@ describe( 'publishPackageOnNpmCallback()', () => { } ); } ); - it( 'should throw when publishing on npm failed', () => { - vi.mocked( tools.shExec ).mockRejectedValue( new Error( 'Unexpected error.' ) ); - - const packagePath = '/workspace/ckeditor5/packages/ckeditor5-foo'; - - return publishPackageOnNpmCallback( packagePath, { npmTag: 'nightly' } ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - error => { - expect( error ).toBeInstanceOf( Error ); - expect( error.message ).toEqual( 'Unable to publish "ckeditor5-foo" package.' ); - } - ); - } ); - - it( 'should not remove a package directory when publishing on npm failed', () => { - vi.mocked( tools.shExec ).mockRejectedValue( new Error( 'Unexpected error.' ) ); - - const packagePath = '/workspace/ckeditor5/packages/ckeditor5-foo'; - - return publishPackageOnNpmCallback( packagePath, { npmTag: 'nightly' } ) - .then( - () => { - throw new Error( 'Expected to be rejected.' ); - }, - () => { - expect( fs.remove ).not.toHaveBeenCalled(); - } - ); - } ); - it( 'should not remove a package directory and not throw error when publishing on npm failed with code 409', async () => { vi.mocked( tools.shExec ).mockRejectedValue( new Error( 'code E409' ) );