diff --git a/CHANGELOG.md b/CHANGELOG.md index 42e2906..ede376c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.1.4] - 2021-05-05 + +### Fixed +- [abtest] Prohibit the use of old versions of vtex.ab-tester + ## [0.1.3] - 2021-04-20 ### Fixed diff --git a/README.md b/README.md index 25c5e0a..14e8f6c 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ $ npm install -g @vtex/cli-plugin-abtest $ oclif-example COMMAND running command... $ oclif-example (-v|--version|version) -@vtex/cli-plugin-abtest/0.1.3 linux-x64 node-v12.22.1 +@vtex/cli-plugin-abtest/0.1.4 linux-x64 node-v12.22.1 $ oclif-example --help [COMMAND] USAGE $ oclif-example COMMAND @@ -44,7 +44,7 @@ OPTIONS --trace Ensure all requests to VTEX IO are traced ``` -_See code: [build/commands/workspace/abtest/finish.ts](https://github.com/vtex/cli-plugin-abtest/blob/v0.1.3/build/commands/workspace/abtest/finish.ts)_ +_See code: [build/commands/workspace/abtest/finish.ts](https://github.com/vtex/cli-plugin-abtest/blob/v0.1.4/build/commands/workspace/abtest/finish.ts)_ ## `oclif-example workspace:abtest:start` @@ -60,7 +60,7 @@ OPTIONS --trace Ensure all requests to VTEX IO are traced ``` -_See code: [build/commands/workspace/abtest/start.ts](https://github.com/vtex/cli-plugin-abtest/blob/v0.1.3/build/commands/workspace/abtest/start.ts)_ +_See code: [build/commands/workspace/abtest/start.ts](https://github.com/vtex/cli-plugin-abtest/blob/v0.1.4/build/commands/workspace/abtest/start.ts)_ ## `oclif-example workspace:abtest:status` @@ -76,5 +76,5 @@ OPTIONS --trace Ensure all requests to VTEX IO are traced ``` -_See code: [build/commands/workspace/abtest/status.ts](https://github.com/vtex/cli-plugin-abtest/blob/v0.1.3/build/commands/workspace/abtest/status.ts)_ +_See code: [build/commands/workspace/abtest/status.ts](https://github.com/vtex/cli-plugin-abtest/blob/v0.1.4/build/commands/workspace/abtest/status.ts)_ diff --git a/package.json b/package.json index e12b518..8d66e1c 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@vtex/cli-plugin-abtest", "description": "vtex plugin abtest", - "version": "0.1.3", + "version": "0.1.4", "bugs": "https://github.com/vtex/cli-plugin-abtest/issues", "dependencies": { "@oclif/command": "^1", diff --git a/src/modules/abtest/finish.ts b/src/modules/abtest/finish.ts index e6bb384..2f0d1b6 100644 --- a/src/modules/abtest/finish.ts +++ b/src/modules/abtest/finish.ts @@ -5,7 +5,7 @@ import { map, prop, filter } from 'ramda' import { logger, promptConfirm, SessionManager } from 'vtex' import { default as abTestStatus } from './status' -import { abtester, installedABTester } from './utils' +import { abtester, checkABTester } from './utils' const { account } = SessionManager.getSingleton() @@ -33,7 +33,7 @@ const promptWorkspaceToFinishABTest = () => .then(prop('workspace')) export default async () => { - await installedABTester() + await checkABTester() const workspace = await promptWorkspaceToFinishABTest() const promptAnswer = await promptContinue(workspace) diff --git a/src/modules/abtest/start.ts b/src/modules/abtest/start.ts index a008b2a..a94621e 100644 --- a/src/modules/abtest/start.ts +++ b/src/modules/abtest/start.ts @@ -1,45 +1,15 @@ import chalk from 'chalk' -import enquirer from 'enquirer' -import { compose, fromPairs, keys, map, mapObjIndexed, prop, values, zip } from 'ramda' -import semver from 'semver' import { logger, promptConfirm } from 'vtex' import { abtester, - installedABTester, - formatDays, + checkABTester, promptConstraintDuration, promptProductionWorkspace, promptProportionTrafic, - SIGNIFICANCE_LEVELS, } from './utils' -const promptSignificanceLevel = async (): Promise => { - const significanceTimePreviews = await Promise.all( - compose>>( - map((value) => abtester.preview(value as number)), - values - )(SIGNIFICANCE_LEVELS) - ) - - const significanceTimePreviewMap = fromPairs(zip(keys(SIGNIFICANCE_LEVELS), significanceTimePreviews)) - - return enquirer - .prompt<{ level: string }>({ - name: 'level', - message: 'Choose the significance level:', - type: 'select', - choices: values( - mapObjIndexed((value, key) => ({ - message: `${key} (~ ${formatDays(value as number)})`, - value: key, - }))(significanceTimePreviewMap) - ) as any, - }) - .then(prop('level')) -} - const promptContinue = (workspace: string, significanceLevel?: string) => { return significanceLevel ? promptConfirm( @@ -56,41 +26,24 @@ ${chalk.green('master')} and ${chalk.green(workspace)}. Proceed?`, } export default async () => { - const abTesterManifest = await installedABTester() + await checkABTester() const workspace = await promptProductionWorkspace('Choose production workspace to start A/B test:') - try { - const [version] = abTesterManifest.version.split('-') - - if (semver.satisfies(version, '>=0.10.0')) { - logger.info(`Setting workspace ${chalk.green(workspace)} to A/B test`) - const promptAnswer = await promptContinue(workspace) - - if (!promptAnswer) return - const proportion = Number(await promptProportionTrafic()) - const timeLength = Number(await promptConstraintDuration()) + logger.info(`Setting workspace ${chalk.green(workspace)} to A/B test`) + const promptAnswer = await promptContinue(workspace) - await abtester.customStart(workspace, timeLength, proportion) - logger.info(`Workspace ${chalk.green(String(workspace))} in A/B test`) - logger.info(`You can stop the test using ${chalk.blue('vtex workspace abtest finish')}`) + if (!promptAnswer) return + const proportion = Number(await promptProportionTrafic()) + const timeLength = Number(await promptConstraintDuration()) - return - } - - const significanceLevel = await promptSignificanceLevel() - const promptAnswer = await promptContinue(workspace, significanceLevel) - - if (!promptAnswer) return - const significanceLevelValue = SIGNIFICANCE_LEVELS[significanceLevel] - - logger.info(`Setting workspace ${chalk.green(workspace)} to A/B test with \ - ${significanceLevel} significance level`) - await abtester.startLegacy(workspace, significanceLevelValue) - logger.info(`Workspace ${chalk.green(workspace)} in A/B test`) - logger.info(`You can stop the test using ${chalk.blue('vtex workspace abtest finish')}`) + try { + await abtester.customStart(workspace, timeLength, proportion) } catch (err) { if (err.message === 'Workspace not found') { console.log(`Test not initialized due to workspace ${workspace} not found by ab-tester.`) } } + + logger.info(`Workspace ${chalk.green(String(workspace))} in A/B test`) + logger.info(`You can stop the test using ${chalk.blue('vtex workspace abtest finish')}`) } diff --git a/src/modules/abtest/status.ts b/src/modules/abtest/status.ts index cb479b5..b4ce71f 100644 --- a/src/modules/abtest/status.ts +++ b/src/modules/abtest/status.ts @@ -4,7 +4,7 @@ import numbro from 'numbro' import R from 'ramda' import { SessionManager, logger, createTable } from 'vtex' -import { abtester, formatDuration, installedABTester } from './utils' +import { abtester, formatDuration, checkABTester } from './utils' interface ABTestStatus { ABTestBeginning: string @@ -119,7 +119,7 @@ const printResultsTable = (testInfo: ABTestStatus) => { export default async () => { const { account } = SessionManager.getSingleton() - await installedABTester() + await checkABTester() let abTestInfo = [] abTestInfo = await abtester.status() diff --git a/src/modules/abtest/utils.ts b/src/modules/abtest/utils.ts index fe19211..bbe456f 100644 --- a/src/modules/abtest/utils.ts +++ b/src/modules/abtest/utils.ts @@ -1,19 +1,15 @@ import { AppManifest } from '@vtex/api' import chalk from 'chalk' import enquirer from 'enquirer' -import numbro from 'numbro' import { compose, filter, map, prop } from 'ramda' import * as env from 'vtex' -import { createFlowIssueError, createAppsClient, createWorkspacesClient, SessionManager } from 'vtex' +import { createFlowIssueError, createAppsClient, createWorkspacesClient, SessionManager, COLORS } from 'vtex' import { ABTester } from '../../clients/apps/ABTester' +import semver from 'semver' -const DEFAULT_TIMEOUT = 15000 +const VERSION_THRESHOLD = '0.12.0' -export const SIGNIFICANCE_LEVELS: Record = { - low: 0.5, - mid: 0.7, - high: 0.9, -} +const DEFAULT_TIMEOUT = 15000 const { account } = SessionManager.getSingleton() @@ -23,16 +19,6 @@ const options = { timeout: (env.envTimeout || DEFAULT_TIMEOUT) as number } export const abtester = ABTester.createClient({ workspace: 'master' }, { ...options, retries: 3 }) export const apps = createAppsClient({ workspace: 'master' }) -export const formatDays = (days: number) => { - let suffix = 'days' - - if (days === 1) { - suffix = 'day' - } - - return `${numbro(days).format('0,0')} ${suffix}` -} - export const formatDuration = (durationInMinutes: number) => { const minutes = durationInMinutes % 60 const hours = Math.trunc(durationInMinutes / 60) % 24 @@ -41,7 +27,7 @@ export const formatDuration = (durationInMinutes: number) => { return `${days} days, ${hours} hours and ${minutes} minutes` } -export const installedABTester = async (): Promise => { +const installedABTester = async (): Promise => { try { return await apps.getApp('vtex.ab-tester@x') } catch (e) { @@ -56,6 +42,22 @@ testing functionality`) } } +const checkABTesterVersion = (version: string) => { + const [versionNumber] = version.split('-') + + if (!semver.satisfies(versionNumber, `>${VERSION_THRESHOLD}`)) { + throw createFlowIssueError(`You are using ${chalk.yellow(`vtex.ab-tester@${version}`)}, \ +which is of an excessively old version. Please, use a version newer than ${chalk.green(VERSION_THRESHOLD)} \ +\nTo get the latest version, run ${chalk.hex(COLORS.PINK)('vtex install vtex.ab-tester')}`) + } +} + +export const checkABTester = async () => { + const abTesterManifest = await installedABTester() + + checkABTesterVersion(abTesterManifest.version) +} + export const promptProductionWorkspace = async (promptMessage: string): Promise => { const workspaces = createWorkspacesClient() const productionWorkspaces = await workspaces.list(account).then(