Skip to content

Commit

Permalink
feat: add publish action to cli-api (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
oljekechoro authored Jan 15, 2021
1 parent 709c402 commit 74419c5
Show file tree
Hide file tree
Showing 20 changed files with 715 additions and 342 deletions.
1 change: 1 addition & 0 deletions packages/cli-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"jest": "jest -w=1 --config=jest.config.json",
"lint": "eslint src/**/*.ts",
"lint:fix": "yarn lint --fix",
"test:local": "yarn lint:fix && yarn test && say success || say fail",
"clean": "rimraf target typings flow-typed buildcache coverage docs",
"build": "yarn build:es5 && yarn build:es6 && yarn build:ts && yarn build:libdef && yarn docs",
"build:es5": "mkdirp target/es5 && tsc -p tsconfig.es5.json",
Expand Down
71 changes: 71 additions & 0 deletions packages/cli-api/src/main/ts/executors/deprecate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { IDeprecatePackageParams, INpmRegClientWrapper, RegClient, TBatchResult } from '@qiwi/npm-batch-client'

import { TDeprecationConfig } from '../interfaces'
import { npmRegClientWrapperFactory, printResults, printResultsJson } from '../utils'

export const performDeprecation = async (
config: TDeprecationConfig,
customBatchClient?: INpmRegClientWrapper
): Promise<void> => {
const batchClient = customBatchClient || npmRegClientWrapperFactory(config, ['deprecate'], new RegClient())

return batchClient
.deprecateBatch(config.data)
.then(data => processResults(handleSettledResults(data), config))
}

type THandledValue = null | PromiseRejectedResult['reason']

export const processResults = (results: THandledValue[], config: TDeprecationConfig): void => {
const enrichedResults = enrichResults(results, config.data)
const successfulResults = getSuccessfulResults(enrichedResults)
const failedResults = getFailedResults(enrichedResults)

if (config.batch?.jsonOutput) {
printResultsJson(
successfulResults,
failedResults
)
return
}

printResults(
successfulResults,
failedResults,
['packageName', 'version', 'message'],
['packageName', 'version', 'message', 'error'],
'Following packages are deprecated successfully:',
'Following packages are not deprecated due to errors:'
)
}

type TEnrichedBatchResult = {
result: THandledValue
packageInfo: IDeprecatePackageParams
}

export const enrichResults = (
results: THandledValue[],
data: IDeprecatePackageParams[]
): TEnrichedBatchResult[] =>
results.map((result, i) => ({ result, packageInfo: data[i] }))

export const handleSettledResults = (results: TBatchResult<null>[]): THandledValue[] =>
results.map(result => result.status === 'rejected'
? result.reason
: result.value
)

export const getSuccessfulResults = (
results: TEnrichedBatchResult[]
): IDeprecatePackageParams[] =>
results
.filter(item => item.result === null)
.map(item => item.packageInfo)

export const getFailedResults = (
results: TEnrichedBatchResult[]
): Array<IDeprecatePackageParams & { error: any }> =>
results
.filter(item => item.result !== null)
.map(item => ({ ...item.packageInfo, error: item.result.message ?? item.result }))
88 changes: 0 additions & 88 deletions packages/cli-api/src/main/ts/executors/deprecation.ts

This file was deleted.

2 changes: 1 addition & 1 deletion packages/cli-api/src/main/ts/executors/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from './deprecation'
export * from './deprecate'
56 changes: 56 additions & 0 deletions packages/cli-api/src/main/ts/executors/publish.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import {
INpmRegClientWrapper,
RegClient,
TBatchResult,
TPublishResult,
TTarballOpts
} from '@qiwi/npm-batch-client'

import { TPublishConfig } from '../interfaces'
import { npmRegClientWrapperFactory, printResults, printResultsJson } from '../utils'

export const performPublish = (
config: TPublishConfig,
customBatchClient?: INpmRegClientWrapper
): Promise<void> => {
const batchClient = customBatchClient || npmRegClientWrapperFactory(config, ['publish'], new RegClient())

return batchClient.publishBatch(config.data)
.then(data => processPublishResults(data, config))
.catch(console.error)
}

type TEnrichedResult = {
result: TBatchResult<TPublishResult>
params: TTarballOpts
}

export const processPublishResults = (results: TBatchResult<TPublishResult>[], config: TPublishConfig): void => {
const enrichedResults: TEnrichedResult[] = results
.map((result, i) => ({ result, params: config.data[i] }))

const successfulPackages = enrichedResults
.filter((item) => item.result.status === 'fulfilled' && item.result.value.success)
.map(item => item.params)

const failedPackages = enrichedResults
.filter((item) => item.result.status === 'rejected' || !item.result.value.success)
.map((item: TEnrichedResult) => ({
...item.params,
error: item.result.status === 'rejected' ? item.result.reason : 'no data'
}))

if (config.batch?.jsonOutput) {
printResultsJson(successfulPackages, failedPackages)
return
}

printResults(
successfulPackages,
failedPackages,
['name', 'version', 'filePath', 'access'],
['name', 'version', 'filePath', 'access', 'error'],
'Following packages are published successfully:',
'Following packages are not published due to errors:',
)
}
6 changes: 4 additions & 2 deletions packages/cli-api/src/main/ts/interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { IDeprecatePackageParams, TNpmRegClientAuth } from '@qiwi/npm-batch-client'
import { IDeprecatePackageParams, TNpmRegClientAuth, TTarballOpts } from '@qiwi/npm-batch-client'
import { IComplexDelay } from 'push-it-to-the-limit'

export type TNpmAction = 'deprecate' | 'un-deprecate'
export type TNpmAction = 'deprecate' | 'un-deprecate' | 'publish'

export type TRateLimit = IComplexDelay | IComplexDelay[]

Expand All @@ -17,6 +17,8 @@ export interface IBaseConfig<T = any> {
data: T,
}

export type TPublishConfig = IBaseConfig<Array<TTarballOpts>>

export type TDeprecationConfig = IBaseConfig<Array<IDeprecatePackageParams>>

export interface ICliArgs {
Expand Down
7 changes: 6 additions & 1 deletion packages/cli-api/src/main/ts/runner.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { performDeprecation } from './executors'
import { performPublish } from './executors/publish'
import { ICliArgs } from './interfaces'
import { readFileToString, validateBaseConfig, validateDeprecationConfig } from './utils'
import { readFileToString, validateBaseConfig, validateDeprecationConfig, validatePublishConfig } from './utils'

export const run = (configString: string): Promise<void> => {
const rawConfig = JSON.parse(configString)
Expand All @@ -11,6 +12,10 @@ export const run = (configString: string): Promise<void> => {
const deprecationConfig = validateDeprecationConfig(validatedConfig)
return performDeprecation(deprecationConfig)
}
case 'publish': {
const publishConfig = validatePublishConfig(validatedConfig)
return performPublish(publishConfig)
}
default:
throw new Error(`Action ${validatedConfig.action} is not supported`)
}
Expand Down
59 changes: 59 additions & 0 deletions packages/cli-api/src/main/ts/utils/misc.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,62 @@
import { NpmRegClientWrapper, RegClient } from '@qiwi/npm-batch-client'
import assert from 'assert'
import { readFileSync } from 'fs'

import { defaultRateLimit } from '../default'
import { IBaseConfig } from '../interfaces'
import { withRateLimit } from './withRateLimit'

export const readFileToString = (path: string): string => readFileSync(path).toString()

// TODO migrate to blork - needs ts libdefs to be added at first
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const assertString = (value: any, name: string): void => {
assert.ok(typeof value === 'string', `${name} should be a string`)
}

export const printResultsJson = (
successfulPackages: Array<any>,
failedPackages: Array<any>,
logger = console
): void => {
logger.log(
JSON.stringify(
{
successfulPackages,
failedPackages
},
null, // eslint-disable-line unicorn/no-null
'\t'
)
)
}

export const printResults = (
successfulPackages: Array<any>,
failedPackages: Array<any>,
successfulPackagesFields: string[],
failedPackagesFields: string[],
successfulCaption: string,
failedCaption: string,
logger = console
): void => {
if (successfulPackages.length > 0) {
logger.log(successfulCaption)
logger.table(successfulPackages, successfulPackagesFields)
}

if (failedPackages.length > 0) {
logger.error(failedCaption)
logger.table(failedPackages, failedPackagesFields)
}
}

export const npmRegClientWrapperFactory = (
config: IBaseConfig,
limitedMethods: string[],
regClient: RegClient
): NpmRegClientWrapper => new NpmRegClientWrapper(
config.registryUrl,
config.auth,
withRateLimit<RegClient>(regClient, config.batch?.ratelimit || defaultRateLimit, limitedMethods)
)
19 changes: 18 additions & 1 deletion packages/cli-api/src/main/ts/utils/validators.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import assert from 'assert'

import { IBaseConfig, TDeprecationConfig } from '../interfaces'
import { IBaseConfig, TDeprecationConfig, TPublishConfig } from '../interfaces'
import { assertString } from './misc'

const isObject = (data: any) => typeof data === 'object' && data !== null

Expand All @@ -23,5 +24,21 @@ export const validateBaseConfig = (config: any): IBaseConfig => { // eslint-disa

export const validateDeprecationConfig = (config: IBaseConfig): TDeprecationConfig => {
assert.ok(Array.isArray(config.data), 'Data in config file should be an array')
config.data.forEach(({ packageName, version, message }) => {
assertString(packageName, 'packageName')
assertString(version, 'version')
assertString(message, 'message')
})
return config
}

export const validatePublishConfig = (config: IBaseConfig): TPublishConfig => {
assert.ok(Array.isArray(config.data), 'Data in config file should be an array')
config.data.forEach(({ name, version, filePath, access }) => {
assertString(name, 'name')
assertString(version, 'version')
assertString(filePath, 'filePath')
assert.ok(access === 'public' || access === 'restricted', 'access should be `public` or `restricted`')
})
return config
}
Loading

0 comments on commit 74419c5

Please sign in to comment.