Skip to content

Commit

Permalink
feat: add batch.serial, batch.defaultTag (#35)
Browse files Browse the repository at this point in the history
  • Loading branch information
oljekechoro authored Dec 21, 2021
1 parent 3ddbebd commit 4c90812
Show file tree
Hide file tree
Showing 18 changed files with 123 additions and 33 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
1 change: 0 additions & 1 deletion packages/cli-api/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@ module.exports = {
extends: [
'eslint-config-qiwi',
'prettier',
'prettier/@typescript-eslint',
],
};
2 changes: 1 addition & 1 deletion packages/cli-api/src/main/ts/executors/deprecate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export const performDeprecation: TActionPerformer = async (
config: TDeprecationConfig,
batchClient
): Promise<void> => {
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<IDeprecatePackageParams, TDeprecateResult>(config.data, data)
const successfulResults = successful.map((item) => item.opts)
Expand Down
2 changes: 1 addition & 1 deletion packages/cli-api/src/main/ts/executors/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export const performGet = async (
config: TGetConfig,
batchClient: INpmRegClientBatchWrapper
): Promise<void> => {
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<string, TGetPackumentResult>(config.data, data, isFailed)
const successfulPackages = successful.map((item) => ({ name: item.opts }))
Expand Down
2 changes: 1 addition & 1 deletion packages/cli-api/src/main/ts/executors/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const performPublish: TActionPerformer = async (
config: TPublishConfig,
batchClient
): Promise<void> => {
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<TTarballOpts, TPublishResult>(config.data, data)
const successfulPackages = successful.map(item => item.opts)
Expand Down
2 changes: 1 addition & 1 deletion packages/cli-api/src/main/ts/executors/setLatest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const performSetLatest: TActionPerformer = async (
config: TSetLatestConfig,
batchClient
): Promise<void> => {
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<TSetLatestTagOpts, TSetLatestTagResult>(
config.data, data, res => res.value !== undefined
Expand Down
2 changes: 2 additions & 0 deletions packages/cli-api/src/main/ts/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export interface IBaseConfig<T = any> {
jsonOutput?: boolean,
path?: string,
printOnlyFailed?: boolean,
defaultTag?: string,
serial?: boolean,
},
data: T,
}
Expand Down
2 changes: 1 addition & 1 deletion packages/cli-api/src/main/ts/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const run = (configString: string): Promise<void> => {
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) {
Expand Down
2 changes: 1 addition & 1 deletion packages/cli-api/src/test/ts/executors/get.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [],
Expand Down
2 changes: 1 addition & 1 deletion packages/cli-api/src/test/ts/executors/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [] })
})

Expand Down
2 changes: 1 addition & 1 deletion packages/cli-api/src/test/ts/executors/setLatest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [] })
})

Expand Down
1 change: 0 additions & 1 deletion packages/cli-pipe/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@ module.exports = {
extends: [
'eslint-config-qiwi',
'prettier',
'prettier/@typescript-eslint',
],
};
1 change: 0 additions & 1 deletion packages/cli/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@ module.exports = {
extends: [
'eslint-config-qiwi',
'prettier',
'prettier/@typescript-eslint',
],
};
21 changes: 21 additions & 0 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
},
...
}
```
1 change: 0 additions & 1 deletion packages/client/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,5 @@ module.exports = {
extends: [
'eslint-config-qiwi',
'prettier',
'prettier/@typescript-eslint',
],
};
66 changes: 54 additions & 12 deletions packages/client/src/main/ts/batchWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,60 +17,102 @@ export class NpmRegClientBatchWrapper implements INpmRegClientBatchWrapper {

getPackument(
packageNames: string[],
skipErrors?: boolean
skipErrors?: boolean,
serial?: boolean,
): Promise<TBatchResult<TGetPackumentResult>[]> {
return NpmRegClientBatchWrapper.performBatchActions(
packageNames,
(packageName) => this.npmClient.getPackument(packageName),
skipErrors
skipErrors,
serial,
)
}

deprecate(
params: Array<IDeprecatePackageParams>,
skipErrors?: boolean
): Promise<TBatchResult<TDeprecateResult>[]> {
skipErrors?: boolean,
serial?: boolean,
): Promise<TBatchResult<TDeprecateResult>[]> {
return NpmRegClientBatchWrapper.performBatchActions(
params,
({ packageName, version, message }) => this.npmClient.deprecate(packageName, version, message),
skipErrors
skipErrors,
serial,
)
}

unDeprecate(
params: Array<IPackageParams>,
skipErrors?: boolean
skipErrors?: boolean,
serial?: boolean,
): Promise<TBatchResult<TDeprecateResult>[]> {
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<TBatchResult<TPublishResult>[]> {
return NpmRegClientBatchWrapper.performBatchActions(
opts,
(opt) => this.npmClient.publish(opt),
skipErrors
skipErrors,
serial,
)
}

setLatestTag(
opts: TSetLatestTagOpts[],
skipErrors?: boolean
skipErrors?: boolean,
serial?: boolean,
): Promise<TBatchResult<TSetLatestTagResult>[]> {
return NpmRegClientBatchWrapper.performBatchActions<TSetLatestTagResult>(
opts,
(opt) => this.npmClient.setLatestTag(opt),
skipErrors
skipErrors,
serial,
)
}

static async performSerialBatchActions<T>(
params: Array<any>,
actionFactory: (...args: any[]) => Promise<T>,
skipErrors?: boolean,
): Promise<TBatchResult<T>[]> {
const results: TBatchResult<T>[] = []

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<T>(
params: Array<any>,
actionFactory: (...args: any[]) => Promise<T>,
skipErrors?: boolean
skipErrors?: boolean,
serial?: boolean
): Promise<TBatchResult<T>[]> {
if (serial) {
return NpmRegClientBatchWrapper.performSerialBatchActions(params, actionFactory, skipErrors)
}

const actions = params.map(actionFactory)
if (skipErrors) {
return Promise.allSettled(actions)
Expand Down
17 changes: 11 additions & 6 deletions packages/client/src/main/ts/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string>,
skipErrors?: boolean
skipErrors?: boolean,
serial?: boolean,
): Promise<TBatchResult<TGetPackumentResult>[]>

deprecate(
params: Array<IDeprecatePackageParams>,
skipErrors?: boolean
skipErrors?: boolean,
serial?: boolean,
): Promise<TBatchResult<TDeprecateResult>[]>

unDeprecate(
params: Array<IPackageParams>,
skipErrors?: boolean
skipErrors?: boolean,
serial?: boolean,
): Promise<TBatchResult<TDeprecateResult>[]>

publish(
opts: TTarballOpts[],
skipErrors?: boolean
skipErrors?: boolean,
serial?: boolean,
): Promise<TBatchResult<TPublishResult>[]>

setLatestTag(
opts: TSetLatestTagOpts[],
skipErrors?: boolean
skipErrors?: boolean,
serial?: boolean,
): Promise<TBatchResult<TSetLatestTagResult>[]>
}

Expand Down
27 changes: 25 additions & 2 deletions packages/client/src/test/ts/batchWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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)
})
)
})
Expand Down

0 comments on commit 4c90812

Please sign in to comment.