diff --git a/package-lock.json b/package-lock.json index 89bb42efd7..eba3009fcb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23540,12 +23540,12 @@ }, "packages/contentstack": { "name": "@contentstack/cli", - "version": "1.13.3", + "version": "1.14.0", "license": "MIT", "dependencies": { "@contentstack/cli-audit": "~1.3.5", "@contentstack/cli-auth": "~1.3.17", - "@contentstack/cli-cm-bootstrap": "~1.7.1", + "@contentstack/cli-cm-bootstrap": "~1.8.0", "@contentstack/cli-cm-branches": "~1.0.22", "@contentstack/cli-cm-bulk-publish": "~1.4.0", "@contentstack/cli-cm-clone": "~1.10.0", @@ -23917,7 +23917,7 @@ }, "packages/contentstack-bootstrap": { "name": "@contentstack/cli-cm-bootstrap", - "version": "1.7.1", + "version": "1.8.0", "license": "MIT", "dependencies": { "@contentstack/cli-cm-seed": "~1.7.0", diff --git a/packages/contentstack-bootstrap/package.json b/packages/contentstack-bootstrap/package.json index 526c3517b2..7c0950a7eb 100644 --- a/packages/contentstack-bootstrap/package.json +++ b/packages/contentstack-bootstrap/package.json @@ -1,7 +1,7 @@ { "name": "@contentstack/cli-cm-bootstrap", "description": "Bootstrap contentstack apps", - "version": "1.7.1", + "version": "1.8.0", "author": "Contentstack", "bugs": "https://github.com/contentstack/cli/issues", "scripts": { diff --git a/packages/contentstack-bootstrap/src/bootstrap/github/client.ts b/packages/contentstack-bootstrap/src/bootstrap/github/client.ts index d2c6fc8160..bf300bc045 100644 --- a/packages/contentstack-bootstrap/src/bootstrap/github/client.ts +++ b/packages/contentstack-bootstrap/src/bootstrap/github/client.ts @@ -1,7 +1,7 @@ import { Stream } from 'stream'; import * as zlib from 'zlib'; import * as tar from 'tar'; -import * as mkdirp from 'mkdirp'; +const mkdirp = require('mkdirp') import { HttpRequestConfig, HttpClient } from '@contentstack/cli-utilities'; import GithubError from './github-error'; diff --git a/packages/contentstack-bootstrap/src/bootstrap/index.ts b/packages/contentstack-bootstrap/src/bootstrap/index.ts index e0162e92f3..3b71912050 100644 --- a/packages/contentstack-bootstrap/src/bootstrap/index.ts +++ b/packages/contentstack-bootstrap/src/bootstrap/index.ts @@ -115,7 +115,7 @@ export default class Bootstrap { if (this.options.livePreviewEnabled) { cliux.print( - 'Important: set management token and app host in the environment file before running the application', + 'Note: Before running the app, please configure a preview token, preview host, and app host in the environment file', { color: 'yellow', }, diff --git a/packages/contentstack-bootstrap/src/bootstrap/interactive.ts b/packages/contentstack-bootstrap/src/bootstrap/interactive.ts index 1ca298b295..42320129ce 100644 --- a/packages/contentstack-bootstrap/src/bootstrap/interactive.ts +++ b/packages/contentstack-bootstrap/src/bootstrap/interactive.ts @@ -99,3 +99,14 @@ export async function inquireLivePreviewSupport() { }); return livePreviewEnabled; } + +export async function continueBootstrapCommand() { + const { shouldContinue } = await inquirer.prompt({ + type: 'list', + name: 'shouldContinue', + message: `To continue with the Bootstrap command without Live Preview, please select Yes.`, + choices: ['yes', 'no'], + loop: false, + }); + return shouldContinue; +} \ No newline at end of file diff --git a/packages/contentstack-bootstrap/src/bootstrap/utils.ts b/packages/contentstack-bootstrap/src/bootstrap/utils.ts index 4ee10a9588..13e2bfab4d 100644 --- a/packages/contentstack-bootstrap/src/bootstrap/utils.ts +++ b/packages/contentstack-bootstrap/src/bootstrap/utils.ts @@ -1,7 +1,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { cliux } from '@contentstack/cli-utilities'; - +import { continueBootstrapCommand } from '../bootstrap/interactive'; import { AppConfig } from '../config'; import messageHandler from '../messages'; @@ -10,6 +10,7 @@ interface EnviornmentVariables { deliveryToken: string; environment: string; livePreviewEnabled?: boolean; + preview_token: string; } /** @@ -54,30 +55,45 @@ export const setupEnvironments = async ( ], }, }; - if (!managementToken) { - try { - const tokenResult = await managementAPIClient.stack({ api_key }).deliveryToken().create(body); - if (tokenResult.token) { - const environmentVariables: EnviornmentVariables = { - api_key, - deliveryToken: tokenResult.token, - environment: environment.name, - livePreviewEnabled, - }; - await envFileHandler( - appConfig.appConfigKey || '', - environmentVariables, - clonedDirectory, - region, - livePreviewEnabled, - ); - } else { - cliux.print(messageHandler.parse('CLI_BOOTSTRAP_APP_FAILED_TO_CREATE_TOKEN_FOR_ENV', environment.name)); + try { + const tokenResult = !managementToken + ? await managementAPIClient + .stack({ api_key }) + .deliveryToken() + .create(body, livePreviewEnabled ? { create_with_preview_token: true } : {}) + : {}; + if (livePreviewEnabled && !tokenResult.preview_token && !managementToken) { + cliux.print( + `Info: Failed to generate a preview token for the ${environment.name} environment.\nNote: Live Preview using a preview token is not available in your plan. Please contact the admin for support.`, + { + color: 'yellow', + }, + ); + if ((await continueBootstrapCommand()) === 'no') { + return; } - } catch (error) { - console.log('error', error); - cliux.print(messageHandler.parse('CLI_BOOTSTRAP_APP_FAILED_TO_CREATE_ENV_FILE_FOR_ENV', environment.name)); } + if (tokenResult.token) { + const environmentVariables: EnviornmentVariables = { + api_key, + deliveryToken: tokenResult.token ?? '', + environment: environment.name, + livePreviewEnabled, + preview_token: tokenResult.preview_token ?? '', + }; + await envFileHandler( + appConfig.appConfigKey || '', + environmentVariables, + clonedDirectory, + region, + livePreviewEnabled, + ); + } else { + cliux.print(messageHandler.parse('CLI_BOOTSTRAP_APP_FAILED_TO_CREATE_TOKEN_FOR_ENV', environment.name)); + } + } catch (error) { + console.log('error', error); + cliux.print(messageHandler.parse('CLI_BOOTSTRAP_APP_FAILED_TO_CREATE_ENV_FILE_FOR_ENV', environment.name)); } } else { cliux.print('No environments name found for the environment'); @@ -123,11 +139,15 @@ const envFileHandler = async ( let filePath; let fileName; let customHost; + let previewHost: string; + let appHost: string; + const managementAPIHost = region?.cma?.substring('8'); const regionName = region && region.name && region.name.toLowerCase(); - const managementAPIHost = region.cma && region.cma.substring('8'); + previewHost = region?.cda?.substring(8)?.replace('cdn', 'rest-preview'); + appHost = region?.uiHost?.substring(8); const isUSRegion = regionName === 'us' || regionName === 'na'; if (regionName !== 'eu' && !isUSRegion) { - customHost = region.cma && region.cma.substring('8'); + customHost = region?.cma?.substring(8); } const production = environmentVariables.environment === 'production' ? true : false; switch (appConfigKey) { @@ -137,9 +157,13 @@ const envFileHandler = async ( filePath = path.join(clonedDirectory, fileName); content = `REACT_APP_CONTENTSTACK_API_KEY=${ environmentVariables.api_key - }\nREACT_APP_CONTENTSTACK_DELIVERY_TOKEN=${ - environmentVariables.deliveryToken - }\nREACT_APP_CONTENTSTACK_ENVIRONMENT=${environmentVariables.environment}${ + }\nREACT_APP_CONTENTSTACK_DELIVERY_TOKEN=${environmentVariables.deliveryToken}${ + livePreviewEnabled + ? `\nREACT_APP_CONTENTSTACK_PREVIEW_TOKEN=${ + environmentVariables.preview_token || `''` + }\nREACT_APP_CONTENTSTACK_PREVIEW_HOST=${previewHost}\nREACT_APP_CONTENTSTACK_APP_HOST=${appHost}\n` + : '\n' + }\nREACT_APP_CONTENTSTACK_ENVIRONMENT=${environmentVariables.environment}\n${ customHost ? '\nREACT_APP_CONTENTSTACK_API_HOST=' + customHost : '' }${ !isUSRegion && !customHost ? '\nREACT_APP_CONTENTSTACK_REGION=' + region.name : '' @@ -152,18 +176,31 @@ const envFileHandler = async ( filePath = path.join(clonedDirectory, fileName); content = `CONTENTSTACK_API_KEY=${environmentVariables.api_key}\nCONTENTSTACK_DELIVERY_TOKEN=${ environmentVariables.deliveryToken - }\nCONTENTSTACK_ENVIRONMENT=${environmentVariables.environment}\nCONTENTSTACK_API_HOST=${ + }\n${ + livePreviewEnabled + ? `\nCONTENTSTACK_PREVIEW_TOKEN=${ + environmentVariables.preview_token || `''` + }\nCONTENTSTACK_PREVIEW_HOST=${previewHost}\nCONTENTSTACK_APP_HOST=${appHost}\n` + : '\n' + }CONTENTSTACK_ENVIRONMENT=${environmentVariables.environment}\nCONTENTSTACK_API_HOST=${ customHost ? customHost : managementAPIHost }${ !isUSRegion && !customHost ? '\nCONTENTSTACK_REGION=' + region.name : '' - }\nCONTENTSTACK_LIVE_PREVIEW=${livePreviewEnabled}\nCONTENTSTACK_MANAGEMENT_TOKEN=''\nCONTENTSTACK_APP_HOST=''\nCONTENTSTACK_LIVE_EDIT_TAGS=false`; + }\nCONTENTSTACK_LIVE_PREVIEW=${livePreviewEnabled}\nCONTENTSTACK_LIVE_EDIT_TAGS=false`; result = await writeEnvFile(content, filePath); break; case 'gatsby': case 'gatsby-starter': fileName = `.env.${environmentVariables.environment}`; filePath = path.join(clonedDirectory, fileName); - content = `CONTENTSTACK_API_KEY=${environmentVariables.api_key}\nCONTENTSTACK_DELIVERY_TOKEN=${environmentVariables.deliveryToken}\nCONTENTSTACK_ENVIRONMENT=${environmentVariables.environment}\nCONTENTSTACK_API_HOST=${managementAPIHost}\nCONTENTSTACK_LIVE_PREVIEW=${livePreviewEnabled}`; + content = `CONTENTSTACK_API_KEY=${environmentVariables.api_key}\nCONTENTSTACK_DELIVERY_TOKEN=${ + environmentVariables.deliveryToken + }\n${ + livePreviewEnabled + ? `\nCONTENTSTACK_PREVIEW_TOKEN=${environmentVariables.preview_token || `''`}\nCONTENTSTACK_PREVIEW_HOST=${previewHost}\nCONTENTSTACK_APP_HOST=${appHost}\n`: '\n' + }\nCONTENTSTACK_ENVIRONMENT=${ + environmentVariables.environment + }\nCONTENTSTACK_API_HOST=${managementAPIHost}\nCONTENTSTACK_LIVE_PREVIEW=${livePreviewEnabled}`; result = await writeEnvFile(content, filePath); break; case 'angular': @@ -171,7 +208,13 @@ const envFileHandler = async ( environmentVariables.environment === 'production' ? true : false }, \n\tconfig : { \n\t\tapi_key: '${environmentVariables.api_key}', \n\t\tdelivery_token: '${ environmentVariables.deliveryToken - }', \n\t\tenvironment: '${environmentVariables.environment}'${ + }',\n${ + livePreviewEnabled + ? `\n\tpreivew_token:'${ + environmentVariables.preview_token || `''` + }'\n\tpreview_host:'${previewHost}'\n\tapp_host:'${appHost}'\n` + : '\n' + },\n\t\tenvironment: '${environmentVariables.environment}'${ !isUSRegion && !customHost ? `,\n\t\tregion: '${region.name}'` : '' } \n\t } \n };`; fileName = `environment${environmentVariables.environment === 'production' ? '.prod.' : '.'}ts`; @@ -181,11 +224,16 @@ const envFileHandler = async ( case 'angular-starter': content = `export const environment = { \n\tproduction: true \n}; \nexport const Config = { \n\tapi_key: '${ environmentVariables.api_key - }', \n\tdelivery_token: '${environmentVariables.deliveryToken}', \n\tenvironment: '${ - environmentVariables.environment - }'${!isUSRegion && !customHost ? `,\n\tregion: '${region.name}'` : ''},\n\tapi_host: '${ + }', \n\tdelivery_token: '${environmentVariables.deliveryToken}',\n\t${ + livePreviewEnabled + ? `\npreview_token:'${environmentVariables.preview_token || ''}',\npreview_host:'${previewHost + }',\napp_host:'${appHost}'` + : '\n' + },\n\tenvironment: '${environmentVariables.environment}'${ + !isUSRegion && !customHost ? `,\n\tregion: '${region.name}'` : '' + },\n\tapi_host: '${ customHost ? customHost : managementAPIHost - }',\n\tapp_host: '',\n\tmanagement_token: '',\n\tlive_preview: ${livePreviewEnabled}\n};`; + }',\n\tlive_preview: ${livePreviewEnabled}\n};`; fileName = `environment${environmentVariables.environment === 'production' ? '.prod.' : '.'}ts`; filePath = path.join(clonedDirectory, 'src', 'environments', fileName); result = await writeEnvFile(content, filePath); @@ -198,11 +246,17 @@ const envFileHandler = async ( // Note: Stencil app needs all the env variables, even if they are not having values otherwise the rollup does not work properly and throws process in undefined error. content = `CONTENTSTACK_API_KEY=${environmentVariables.api_key}\nCONTENTSTACK_DELIVERY_TOKEN=${ environmentVariables.deliveryToken + }\n${ + livePreviewEnabled + ? `\nCONTENTSTACK_PREVIEW_TOKEN=${environmentVariables.preview_token || `''`}\nCONTENTSTACK_PREVIEW_HOST=${ + customHost ?? previewHost + }\nCONTENTSTACK_APP_HOST=${appHost}` + : '\n' }\nCONTENTSTACK_ENVIRONMENT=${environmentVariables.environment}${ !isUSRegion && !customHost ? '\nCONTENTSTACK_REGION=' + region.name : '' - }\nCONTENTSTACK_LIVE_PREVIEW=${livePreviewEnabled}\nCONTENTSTACK_MANAGEMENT_TOKEN=''\nCONTENTSTACK_API_HOST='${ + }\nCONTENTSTACK_API_HOST=${ customHost ? customHost : managementAPIHost - }'\nCONTENTSTACK_APP_HOST=''\nCONTENTSTACK_LIVE_EDIT_TAGS=false`; + }\nCONTENTSTACK_LIVE_PREVIEW=${livePreviewEnabled}\n\nCONTENTSTACK_LIVE_EDIT_TAGS=false`; result = await writeEnvFile(content, filePath); break; case 'vue-starter': @@ -210,6 +264,13 @@ const envFileHandler = async ( filePath = path.join(clonedDirectory, fileName); content = `VUE_APP_CONTENTSTACK_API_KEY=${environmentVariables.api_key}\nVUE_APP_CONTENTSTACK_DELIVERY_TOKEN=${ environmentVariables.deliveryToken + }\n${ + livePreviewEnabled + ? `\nVUE_APP_CONTENTSTACK_PREVIEW_TOKEN=${ + environmentVariables.preview_token || `''` + }\nVUE_APP_CONTENTSTACK_PREVIEW_HOST=${previewHost + }\nVUE_APP_CONTENTSTACK_APP_HOST=${appHost}\n` + : '\n' }\nVUE_APP_CONTENTSTACK_ENVIRONMENT=${environmentVariables.environment}${ customHost ? '\nVUE_APP_CONTENTSTACK_API_HOST=' + customHost : '' }${ diff --git a/packages/contentstack-bootstrap/test/github.test.js b/packages/contentstack-bootstrap/test/github.test.js index 117985b72d..ff4ad747e1 100644 --- a/packages/contentstack-bootstrap/test/github.test.js +++ b/packages/contentstack-bootstrap/test/github.test.js @@ -24,7 +24,7 @@ describe('Github Client', function () { it('Git Tarball url creation', () => { const repo = GitHubClient.parsePath('contentstack/contentstack-nextjs-react-universal-demo'); const gClient = new GitHubClient(repo); - expect(gClient.gitTarBallUrl).to.be.equal('https://api.github.com/repos/contentstack/contentstack-nextjs-react-universal-demo/tarball/master') + expect(gClient.gitTarBallUrl).to.be.equal('https://api.github.com/repos/contentstack/contentstack-nextjs-react-universal-demo/tarball/cli-use') }) it('Clone the source repo', async function () { diff --git a/packages/contentstack-bootstrap/test/utils.test.js b/packages/contentstack-bootstrap/test/utils.test.js index 708b55ff18..b2deb439fb 100644 --- a/packages/contentstack-bootstrap/test/utils.test.js +++ b/packages/contentstack-bootstrap/test/utils.test.js @@ -33,18 +33,69 @@ function getFileContent(_path) { describe('Utils', () => { describe('#setupEnvironments', () => { - it('Create env file for a stack', async () => { + it('Create env file for a stack with live preview enabled', async () => { const environments = { items: [{ name: 'production' }, { name: 'development' }] }; const token = 'mock-delivery-token'; const api_key = 'mock-api-key'; const appConfig = { appConfigKey: 'reactjs', }; + const livePreviewEnabled= true; const clonedDirectory = await getDirectory(); const region = { name: 'NA', - cda: 'https://app.contentstack.com', + cda: 'https://cdn.contentstack.com', + cma: 'https://api.contentstack.com', + uiHost: 'https://app.contentstack.com' + }; + const managementAPIClient = { + stack: () => { + return { + environment: () => { + return { + query: () => { + return { + find: () => Promise.resolve(environments), + }; + }, + }; + }, + deliveryToken: () => { + return { + create: () => Promise.resolve({ token, preview_token: "mock_preview_token" }), + }; + }, + }; + }, + }; + await setupEnvironments(managementAPIClient, api_key, appConfig,clonedDirectory, region,livePreviewEnabled); + const files = await getDirFiles(clonedDirectory); + expect(files).to.have.length(2); + let devEnvFile = await getFileContent(path.join(clonedDirectory, '.env.development.local')); + devEnvFile = devEnvFile.replace(/\n/g, ','); + expect(devEnvFile).equal( + 'REACT_APP_CONTENTSTACK_API_KEY=mock-api-key,REACT_APP_CONTENTSTACK_DELIVERY_TOKEN=mock-delivery-token,REACT_APP_CONTENTSTACK_PREVIEW_TOKEN=mock_preview_token,REACT_APP_CONTENTSTACK_PREVIEW_HOST=rest-preview.contentstack.com,REACT_APP_CONTENTSTACK_APP_HOST=app.contentstack.com,,REACT_APP_CONTENTSTACK_ENVIRONMENT=development,,SKIP_PREFLIGHT_CHECK=true,REACT_APP_CONTENTSTACK_LIVE_PREVIEW=true', + ); + let prodEnvFile = await getFileContent(path.join(clonedDirectory, '.env.production.local')); + prodEnvFile = prodEnvFile.replace(/\n/g, ','); + expect(prodEnvFile).equal( + 'REACT_APP_CONTENTSTACK_API_KEY=mock-api-key,REACT_APP_CONTENTSTACK_DELIVERY_TOKEN=mock-delivery-token,REACT_APP_CONTENTSTACK_PREVIEW_TOKEN=mock_preview_token,REACT_APP_CONTENTSTACK_PREVIEW_HOST=rest-preview.contentstack.com,REACT_APP_CONTENTSTACK_APP_HOST=app.contentstack.com,,REACT_APP_CONTENTSTACK_ENVIRONMENT=production,,SKIP_PREFLIGHT_CHECK=true,REACT_APP_CONTENTSTACK_LIVE_PREVIEW=true', + ); + }); + it('Create env file for a stack with live preview disabled', async () => { + const environments = { items: [{ name: 'production' }, { name: 'development' }] }; + const token = 'mock-delivery-token'; + const api_key = 'mock-api-key'; + const appConfig = { + appConfigKey: 'reactjs', + }; + const livePreviewEnabled= false; + const clonedDirectory = await getDirectory(); + const region = { + name: 'NA', + cda: 'https://cdn.contentstack.com', cma: 'https://app.contentstack.com', + uiHost: 'https://app.contentstack.com' }; const managementAPIClient = { stack: () => { @@ -66,19 +117,18 @@ describe('Utils', () => { }; }, }; - - await setupEnvironments(managementAPIClient, api_key, appConfig, clonedDirectory, region); + await setupEnvironments(managementAPIClient, api_key, appConfig,clonedDirectory, region,livePreviewEnabled); const files = await getDirFiles(clonedDirectory); expect(files).to.have.length(2); let devEnvFile = await getFileContent(path.join(clonedDirectory, '.env.development.local')); devEnvFile = devEnvFile.replace(/\n/g, ','); expect(devEnvFile).equal( - 'REACT_APP_APIKEY=mock-api-key,REACT_APP_DELIVERY_TOKEN=mock-delivery-token,REACT_APP_ENVIRONMENT=development,REACT_APP_REGION=NA', + 'REACT_APP_CONTENTSTACK_API_KEY=mock-api-key,REACT_APP_CONTENTSTACK_DELIVERY_TOKEN=mock-delivery-token,,REACT_APP_CONTENTSTACK_ENVIRONMENT=development,,SKIP_PREFLIGHT_CHECK=true,REACT_APP_CONTENTSTACK_LIVE_PREVIEW=false' ); let prodEnvFile = await getFileContent(path.join(clonedDirectory, '.env.production.local')); prodEnvFile = prodEnvFile.replace(/\n/g, ','); expect(prodEnvFile).equal( - 'REACT_APP_APIKEY=mock-api-key,REACT_APP_DELIVERY_TOKEN=mock-delivery-token,REACT_APP_ENVIRONMENT=production,REACT_APP_REGION=NA', + 'REACT_APP_CONTENTSTACK_API_KEY=mock-api-key,REACT_APP_CONTENTSTACK_DELIVERY_TOKEN=mock-delivery-token,,REACT_APP_CONTENTSTACK_ENVIRONMENT=production,,SKIP_PREFLIGHT_CHECK=true,REACT_APP_CONTENTSTACK_LIVE_PREVIEW=false' ); }); it('Create env with invalid environments, should throw an error', async () => { @@ -92,7 +142,7 @@ describe('Utils', () => { const region = { name: 'NA', cda: 'https://app.contentstack.com', - cma: 'https://app.contentstack.com', + cma: 'https://api.contentstack.com', }; const managementAPIClient = { stack: () => ({ diff --git a/packages/contentstack-config/src/commands/config/set/region.ts b/packages/contentstack-config/src/commands/config/set/region.ts index 30151477d4..5a1e75aa51 100644 --- a/packages/contentstack-config/src/commands/config/set/region.ts +++ b/packages/contentstack-config/src/commands/config/set/region.ts @@ -46,6 +46,7 @@ export default class RegionSetCommand extends BaseCommand --cda --ui-host --name "India"', ]; diff --git a/packages/contentstack/package.json b/packages/contentstack/package.json index 1f59f16b91..7fd21fa112 100755 --- a/packages/contentstack/package.json +++ b/packages/contentstack/package.json @@ -1,7 +1,7 @@ { "name": "@contentstack/cli", "description": "Command-line tool (CLI) to interact with Contentstack", - "version": "1.13.3", + "version": "1.14.0", "author": "Contentstack", "bin": { "csdx": "./bin/run" @@ -24,7 +24,7 @@ "dependencies": { "@contentstack/cli-audit": "~1.3.5", "@contentstack/cli-auth": "~1.3.17", - "@contentstack/cli-cm-bootstrap": "~1.7.1", + "@contentstack/cli-cm-bootstrap": "~1.8.0", "@contentstack/cli-cm-branches": "~1.0.22", "@contentstack/cli-cm-bulk-publish": "~1.4.0", "@contentstack/cli-cm-export": "~1.10.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c30d43f1f7..bd5b947a76 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -12,7 +12,7 @@ importers: specifiers: '@contentstack/cli-audit': ~1.3.5 '@contentstack/cli-auth': ~1.3.17 - '@contentstack/cli-cm-bootstrap': ~1.7.1 + '@contentstack/cli-cm-bootstrap': ~1.8.0 '@contentstack/cli-cm-branches': ~1.0.22 '@contentstack/cli-cm-bulk-publish': ~1.4.0 '@contentstack/cli-cm-clone': ~1.10.0