From 4c90812e1accb9adf54022846aaad058f2b19325 Mon Sep 17 00:00:00 2001 From: Choro Abdymanapov Date: Tue, 21 Dec 2021 20:31:21 +0300 Subject: [PATCH] feat: add batch.serial, batch.defaultTag (#35) --- package.json | 3 +- packages/cli-api/.eslintrc.js | 1 - .../src/main/ts/executors/deprecate.ts | 2 +- packages/cli-api/src/main/ts/executors/get.ts | 2 +- .../cli-api/src/main/ts/executors/publish.ts | 2 +- .../src/main/ts/executors/setLatest.ts | 2 +- packages/cli-api/src/main/ts/interfaces.ts | 2 + packages/cli-api/src/main/ts/runner.ts | 2 +- packages/cli-api/src/test/ts/executors/get.ts | 2 +- .../cli-api/src/test/ts/executors/publish.ts | 2 +- .../src/test/ts/executors/setLatest.ts | 2 +- packages/cli-pipe/.eslintrc.js | 1 - packages/cli/.eslintrc.js | 1 - packages/cli/README.md | 21 ++++++ packages/client/.eslintrc.js | 1 - packages/client/src/main/ts/batchWrapper.ts | 66 +++++++++++++++---- packages/client/src/main/ts/interfaces.ts | 17 +++-- packages/client/src/test/ts/batchWrapper.ts | 27 +++++++- 18 files changed, 123 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index 8c09c71..4301ee6 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,11 @@ "clean": "lerna run clean", "build": "lerna run build --stream --concurrency 2", "uglify": "lerna run uglify --stream --concurrency 2", + "lint": "lerna run lint", "bootstrap": "lerna bootstrap", "jest": "jest --runInBand --detectOpenHandles --forceExit", "test:report": "yarn test && yarn coveralls:push", - "test": "yarn jest", + "test": "yarn lint && yarn jest", "test:concurrent": "lerna run test --concurrency 2 --stream --no-prefix && yarn coverage:merge", "coverage:merge": "node scripts/js/coverage-merge.js", "coveralls:push": "cat ./coverage/lcov.info | coveralls || echo 'coveralls push failed :(' && exit 0", diff --git a/packages/cli-api/.eslintrc.js b/packages/cli-api/.eslintrc.js index dd8ce60..3df9e03 100644 --- a/packages/cli-api/.eslintrc.js +++ b/packages/cli-api/.eslintrc.js @@ -2,6 +2,5 @@ module.exports = { extends: [ 'eslint-config-qiwi', 'prettier', - 'prettier/@typescript-eslint', ], }; diff --git a/packages/cli-api/src/main/ts/executors/deprecate.ts b/packages/cli-api/src/main/ts/executors/deprecate.ts index cd0c3b6..90e03f2 100644 --- a/packages/cli-api/src/main/ts/executors/deprecate.ts +++ b/packages/cli-api/src/main/ts/executors/deprecate.ts @@ -8,7 +8,7 @@ export const performDeprecation: TActionPerformer = async ( config: TDeprecationConfig, batchClient ): Promise => { - const data = await batchClient.deprecate(config.data, config.batch?.skipErrors) + const data = await batchClient.deprecate(config.data, config.batch?.skipErrors, config.batch?.serial) const { successful, failed } = parseResults(config.data, data) const successfulResults = successful.map((item) => item.opts) diff --git a/packages/cli-api/src/main/ts/executors/get.ts b/packages/cli-api/src/main/ts/executors/get.ts index 349f0bd..4e6208e 100644 --- a/packages/cli-api/src/main/ts/executors/get.ts +++ b/packages/cli-api/src/main/ts/executors/get.ts @@ -18,7 +18,7 @@ export const performGet = async ( config: TGetConfig, batchClient: INpmRegClientBatchWrapper ): Promise => { - const data = await batchClient.getPackument(config.data, config.batch?.skipErrors) + const data = await batchClient.getPackument(config.data, config.batch?.skipErrors, config.batch?.serial) const { successful, failed } = parseResults(config.data, data, isFailed) const successfulPackages = successful.map((item) => ({ name: item.opts })) diff --git a/packages/cli-api/src/main/ts/executors/publish.ts b/packages/cli-api/src/main/ts/executors/publish.ts index 2088d97..ebe02d0 100644 --- a/packages/cli-api/src/main/ts/executors/publish.ts +++ b/packages/cli-api/src/main/ts/executors/publish.ts @@ -11,7 +11,7 @@ export const performPublish: TActionPerformer = async ( config: TPublishConfig, batchClient ): Promise => { - const data = await batchClient.publish(config.data, config.batch?.skipErrors) + const data = await batchClient.publish(config.data, config.batch?.skipErrors, config.batch?.serial) const { successful, failed } = parseResults(config.data, data) const successfulPackages = successful.map(item => item.opts) diff --git a/packages/cli-api/src/main/ts/executors/setLatest.ts b/packages/cli-api/src/main/ts/executors/setLatest.ts index 6c35cba..e35a01a 100644 --- a/packages/cli-api/src/main/ts/executors/setLatest.ts +++ b/packages/cli-api/src/main/ts/executors/setLatest.ts @@ -14,7 +14,7 @@ export const performSetLatest: TActionPerformer = async ( config: TSetLatestConfig, batchClient ): Promise => { - const data = await batchClient.setLatestTag(config.data, config.batch?.skipErrors) + const data = await batchClient.setLatestTag(config.data, config.batch?.skipErrors, config.batch?.serial) const { successful, failed } = parseResults( config.data, data, res => res.value !== undefined diff --git a/packages/cli-api/src/main/ts/interfaces.ts b/packages/cli-api/src/main/ts/interfaces.ts index 4a4f3be..4e99d3a 100644 --- a/packages/cli-api/src/main/ts/interfaces.ts +++ b/packages/cli-api/src/main/ts/interfaces.ts @@ -21,6 +21,8 @@ export interface IBaseConfig { jsonOutput?: boolean, path?: string, printOnlyFailed?: boolean, + defaultTag?: string, + serial?: boolean, }, data: T, } diff --git a/packages/cli-api/src/main/ts/runner.ts b/packages/cli-api/src/main/ts/runner.ts index 4b6a346..1a1f5ef 100644 --- a/packages/cli-api/src/main/ts/runner.ts +++ b/packages/cli-api/src/main/ts/runner.ts @@ -20,7 +20,7 @@ export const run = (configString: string): Promise => { const wrapper = npmRegClientWrapperFactory( validatedConfig, ['publish', 'get', 'distTags.add'], - new RegClient({ defaultTag: 'latest-npm-batch-published' }) + new RegClient({ defaultTag: validatedConfig.batch?.defaultTag || 'latest-npm-batch-published' }) ) const batchWrapper = npmRegClientBatchWrapperFactory(wrapper) switch (validatedConfig.action) { diff --git a/packages/cli-api/src/test/ts/executors/get.ts b/packages/cli-api/src/test/ts/executors/get.ts index 5100235..c6669ee 100644 --- a/packages/cli-api/src/test/ts/executors/get.ts +++ b/packages/cli-api/src/test/ts/executors/get.ts @@ -31,7 +31,7 @@ describe('performGet', () => { await performGet(config, npmClientMock as any) - expect(npmClientMock.getPackument).toHaveBeenCalledWith(config.data, true) + expect(npmClientMock.getPackument).toHaveBeenCalledWith(config.data, true, undefined) expect(printResultsJsonSpy).toHaveBeenCalledWith({ failedPackages: [], successfulPackages: [], diff --git a/packages/cli-api/src/test/ts/executors/publish.ts b/packages/cli-api/src/test/ts/executors/publish.ts index f93cc8e..8aa3f66 100644 --- a/packages/cli-api/src/test/ts/executors/publish.ts +++ b/packages/cli-api/src/test/ts/executors/publish.ts @@ -31,7 +31,7 @@ describe('performPublish', () => { await performPublish(config, npmClientMock as any) - expect(npmClientMock.publish).toHaveBeenCalledWith(config.data, undefined) + expect(npmClientMock.publish).toHaveBeenCalledWith(config.data, undefined, undefined) expect(printResultsJsonSpy).toHaveBeenCalledWith({ successfulPackages: [], failedPackages: [] }) }) diff --git a/packages/cli-api/src/test/ts/executors/setLatest.ts b/packages/cli-api/src/test/ts/executors/setLatest.ts index 3b03435..17a0034 100644 --- a/packages/cli-api/src/test/ts/executors/setLatest.ts +++ b/packages/cli-api/src/test/ts/executors/setLatest.ts @@ -29,7 +29,7 @@ describe('performSettingLatest', () => { await performSetLatest(config, npmClientMock as any) - expect(npmClientMock.setLatestTag).toHaveBeenCalledWith(config.data, undefined) + expect(npmClientMock.setLatestTag).toHaveBeenCalledWith(config.data, undefined, undefined) expect(printResultsJsonSpy).toHaveBeenCalledWith({ successfulPackages: [], failedPackages: [] }) }) diff --git a/packages/cli-pipe/.eslintrc.js b/packages/cli-pipe/.eslintrc.js index dd8ce60..3df9e03 100644 --- a/packages/cli-pipe/.eslintrc.js +++ b/packages/cli-pipe/.eslintrc.js @@ -2,6 +2,5 @@ module.exports = { extends: [ 'eslint-config-qiwi', 'prettier', - 'prettier/@typescript-eslint', ], }; diff --git a/packages/cli/.eslintrc.js b/packages/cli/.eslintrc.js index dd8ce60..3df9e03 100644 --- a/packages/cli/.eslintrc.js +++ b/packages/cli/.eslintrc.js @@ -2,6 +2,5 @@ module.exports = { extends: [ 'eslint-config-qiwi', 'prettier', - 'prettier/@typescript-eslint', ], }; diff --git a/packages/cli/README.md b/packages/cli/README.md index eefa2ce..9d11d67 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -342,3 +342,24 @@ Flag `printOnlyFailed` prints only info about failed packages ... } ``` +`defaultTag` overrides the same option in [npm registry client](https://github.com/npm/npm-registry-client#configuration). Defaults to `latest-npm-batch-published`. +```text +{ + ... + "batch": { + "defaultTag": "latest" + }, + ... +} +``` +If you want batch actions to be performed sequentially set `serial` to true. +This can be helpful for allowing npm to set dist-tag `latest` while publishing with `defaultTag === 'latest'`. +```text +{ + ... + "batch": { + "serial": true + }, + ... +} +``` diff --git a/packages/client/.eslintrc.js b/packages/client/.eslintrc.js index dd8ce60..3df9e03 100644 --- a/packages/client/.eslintrc.js +++ b/packages/client/.eslintrc.js @@ -2,6 +2,5 @@ module.exports = { extends: [ 'eslint-config-qiwi', 'prettier', - 'prettier/@typescript-eslint', ], }; diff --git a/packages/client/src/main/ts/batchWrapper.ts b/packages/client/src/main/ts/batchWrapper.ts index 4e3128a..cd84e1a 100644 --- a/packages/client/src/main/ts/batchWrapper.ts +++ b/packages/client/src/main/ts/batchWrapper.ts @@ -17,60 +17,102 @@ export class NpmRegClientBatchWrapper implements INpmRegClientBatchWrapper { getPackument( packageNames: string[], - skipErrors?: boolean + skipErrors?: boolean, + serial?: boolean, ): Promise[]> { return NpmRegClientBatchWrapper.performBatchActions( packageNames, (packageName) => this.npmClient.getPackument(packageName), - skipErrors + skipErrors, + serial, ) } deprecate( params: Array, - skipErrors?: boolean - ): Promise[]> { + skipErrors?: boolean, + serial?: boolean, +): Promise[]> { return NpmRegClientBatchWrapper.performBatchActions( params, ({ packageName, version, message }) => this.npmClient.deprecate(packageName, version, message), - skipErrors + skipErrors, + serial, ) } unDeprecate( params: Array, - skipErrors?: boolean + skipErrors?: boolean, + serial?: boolean, ): Promise[]> { - return this.deprecate(params.map(item => ({ ...item, message: '' })), skipErrors) + return this.deprecate(params.map(item => ({ ...item, message: '' })), skipErrors, serial) } publish( opts: TTarballOpts[], - skipErrors?: boolean + skipErrors?: boolean, + serial?: boolean, ): Promise[]> { return NpmRegClientBatchWrapper.performBatchActions( opts, (opt) => this.npmClient.publish(opt), - skipErrors + skipErrors, + serial, ) } setLatestTag( opts: TSetLatestTagOpts[], - skipErrors?: boolean + skipErrors?: boolean, + serial?: boolean, ): Promise[]> { return NpmRegClientBatchWrapper.performBatchActions( opts, (opt) => this.npmClient.setLatestTag(opt), - skipErrors + skipErrors, + serial, ) } + static async performSerialBatchActions( + params: Array, + actionFactory: (...args: any[]) => Promise, + skipErrors?: boolean, + ): Promise[]> { + const results: TBatchResult[] = [] + + for (const opts of params) { + try { + results.push({ + status: 'fulfilled', + value: await actionFactory(opts), + }) + } catch (e) { + if (!skipErrors) { + throw e + } + + results.push({ + status: 'rejected', + reason: e + }) + } + } + + return results + } + static performBatchActions( params: Array, actionFactory: (...args: any[]) => Promise, - skipErrors?: boolean + skipErrors?: boolean, + serial?: boolean ): Promise[]> { + if (serial) { + return NpmRegClientBatchWrapper.performSerialBatchActions(params, actionFactory, skipErrors) + } + const actions = params.map(actionFactory) if (skipErrors) { return Promise.allSettled(actions) diff --git a/packages/client/src/main/ts/interfaces.ts b/packages/client/src/main/ts/interfaces.ts index 2d1a98d..0d11085 100644 --- a/packages/client/src/main/ts/interfaces.ts +++ b/packages/client/src/main/ts/interfaces.ts @@ -34,34 +34,39 @@ export type TDeprecateResult = TNpmRegistryClientResult export type TPublishResult = TNpmRegistryClientResult -export type TGetPackumentResult = TNpmRegistryClientResult | Packument +export type TGetPackumentResult = Packument export type TSetLatestTagResult = TNpmRegistryClientResult export interface INpmRegClientBatchWrapper { getPackument( params: Array, - skipErrors?: boolean + skipErrors?: boolean, + serial?: boolean, ): Promise[]> deprecate( params: Array, - skipErrors?: boolean + skipErrors?: boolean, + serial?: boolean, ): Promise[]> unDeprecate( params: Array, - skipErrors?: boolean + skipErrors?: boolean, + serial?: boolean, ): Promise[]> publish( opts: TTarballOpts[], - skipErrors?: boolean + skipErrors?: boolean, + serial?: boolean, ): Promise[]> setLatestTag( opts: TSetLatestTagOpts[], - skipErrors?: boolean + skipErrors?: boolean, + serial?: boolean, ): Promise[]> } diff --git a/packages/client/src/test/ts/batchWrapper.ts b/packages/client/src/test/ts/batchWrapper.ts index 208175f..6c0a5f4 100644 --- a/packages/client/src/test/ts/batchWrapper.ts +++ b/packages/client/src/test/ts/batchWrapper.ts @@ -57,6 +57,29 @@ describe('NpmRegClientBatchWrapper', () => { }) }) + describe('performSerialBatchActions', () => { + it('returns array of PromiseSettledResults', async () => { + const results = await NpmRegClientBatchWrapper.performSerialBatchActions( + [1, 2, 3], + (value) => Promise.resolve(value) + ) + expect(results).toEqual([ + { + status: 'fulfilled', + value: 1, + }, + { + status: 'fulfilled', + value: 2, + }, + { + status: 'fulfilled', + value: 3, + }, + ]) + }) + }) + describe('batch methods', () => { const batchMethods = [ 'deprecate', @@ -72,8 +95,8 @@ describe('NpmRegClientBatchWrapper', () => { const performBatchActionsSpy = jest.spyOn(NpmRegClientBatchWrapper, 'performBatchActions') .mockImplementation(() => Promise.resolve([])) // @ts-ignore - await batchWrapper[method]([], true) - expect(performBatchActionsSpy).toHaveBeenCalledWith([], expect.any(Function), true) + await batchWrapper[method]([], true, false) + expect(performBatchActionsSpy).toHaveBeenCalledWith([], expect.any(Function), true, false) }) ) })