Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/cli/e2e/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export const fetchAllIntegrations = async (client: Client): Promise<ApiIntegrati
export type ApiInterface = Awaited<ReturnType<Client['listInterfaces']>>['interfaces'][0]

export type ApiPlugin = Awaited<ReturnType<Client['listPlugins']>>['plugins'][0]
export const fetchAllPlugins = async (client: Client): Promise<ApiPlugin[]> => await client.list.plugins({}).collect()
2 changes: 2 additions & 0 deletions packages/cli/e2e/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ const noBuild = false
const dryRun = false
const secrets = [] satisfies string[]
const sourceMap = false
const watch = true
const verbose = false
const confirm = true
const json = false
Expand All @@ -18,6 +19,7 @@ export default {
dryRun,
secrets,
sourceMap,
watch,
verbose,
confirm,
json,
Expand Down
13 changes: 10 additions & 3 deletions packages/cli/e2e/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ import {
reinstallLocalIntegration,
} from './tests/install-package'
import { requiredIntegrationSecrets } from './tests/integration-secrets'
import { enforceWorkspaceHandle, prependWorkspaceHandle } from './tests/manage-workspace-handle'
import {
enforceWorkspaceHandleIntegration,
enforceWorkspaceHandlePlugin,
prependWorkspaceHandleIntegration,
prependWorkspaceHandlePlugin,
} from './tests/manage-workspace-handle'
import { removePackage } from './tests/remove-package'
import { Test } from './typings'
import { sleep, TmpDirectory } from './utils'
Expand All @@ -28,8 +33,10 @@ const tests: Test[] = [
devBot,
requiredBotSecrets,
requiredIntegrationSecrets,
prependWorkspaceHandle,
enforceWorkspaceHandle,
prependWorkspaceHandleIntegration,
enforceWorkspaceHandleIntegration,
prependWorkspaceHandlePlugin,
enforceWorkspaceHandlePlugin,
addIntegration,
addPlugin,
addLocalIntegrationKeepsRelativePath,
Expand Down
115 changes: 112 additions & 3 deletions packages/cli/e2e/tests/manage-workspace-handle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Client } from '@botpress/client'
import * as fs from 'fs'
import pathlib from 'path'
import impl from '../../src'
import { ApiIntegration, fetchAllIntegrations } from '../api'
import { ApiIntegration, fetchAllIntegrations, ApiPlugin, fetchAllPlugins } from '../api'
import defaults from '../defaults'
import * as retry from '../retry'
import { Test } from '../typings'
Expand All @@ -13,7 +13,12 @@ const fetchIntegration = async (client: Client, integrationName: string): Promis
return integrations.find(({ name }) => name === integrationName)
}

export const prependWorkspaceHandle: Test = {
const fetchPlugin = async (client: Client, pluginName: string): Promise<ApiPlugin | undefined> => {
const plugins = await fetchAllPlugins(client)
return plugins.find(({ name }) => name === pluginName)
}

export const prependWorkspaceHandleIntegration: Test = {
name: 'cli should automatically preprend the workspace handle to the integration name when deploying',
handler: async ({ tmpDir, dependencies, workspaceHandle, logger, ...creds }) => {
const botpressHomeDir = pathlib.join(tmpDir, '.botpresshome')
Expand Down Expand Up @@ -78,7 +83,71 @@ export const prependWorkspaceHandle: Test = {
},
}

export const enforceWorkspaceHandle: Test = {
export const prependWorkspaceHandlePlugin: Test = {
name: 'cli should automatically prepend the workspace handle to the plugin name when deploying',
handler: async ({ tmpDir, dependencies, workspaceHandle, logger, ...creds }) => {
const botpressHomeDir = pathlib.join(tmpDir, '.botpresshome')
const baseDir = pathlib.join(tmpDir, 'plugins')

const pluginSuffix = utils.getUUID()
const pluginName = `myplugin${pluginSuffix}`
const pluginNameWithHandle = `${workspaceHandle}/${pluginName}`
const pluginDir = pathlib.join(baseDir, pluginName)

const argv = {
...defaults,
botpressHome: botpressHomeDir,
confirm: true,
...creds,
}

const client = new Client({
apiUrl: creds.apiUrl,
token: creds.token,
workspaceId: creds.workspaceId,
retry: retry.config,
})

await impl
.init({ ...argv, workDir: baseDir, name: pluginNameWithHandle, type: 'plugin', template: 'empty' })
.then(utils.handleExitCode)

// Remove handle from package.json pluginName field:
const pkgJsonPath = pathlib.join(pluginDir, 'package.json')
const pkgJson = await fs.promises.readFile(pkgJsonPath, 'utf-8').then(JSON.parse)
pkgJson.pluginName = pluginName
await fs.promises.writeFile(pkgJsonPath, JSON.stringify(pkgJson, null, 2))

await utils.fixBotpressDependencies({ workDir: pluginDir, target: dependencies })
await utils.npmInstall({ workDir: pluginDir }).then(utils.handleExitCode)
await impl.build({ ...argv, workDir: pluginDir }).then(utils.handleExitCode)
await impl.login({ ...argv }).then(utils.handleExitCode)

await impl
.deploy({ ...argv, createNewBot: undefined, botId: undefined, workDir: pluginDir })
.then(utils.handleExitCode)

logger.debug(`Fetching plugin "${pluginName}"`)
let plugin = await fetchPlugin(client, pluginName)
if (plugin) {
throw new Error(`Plugin ${pluginName} should not have been created without handle prefix`)
}

const expectedPluginName = `${workspaceHandle}/${pluginName}`
logger.debug(`Fetching plugin "${expectedPluginName}"`)
plugin = await fetchPlugin(client, expectedPluginName)
if (!plugin) {
throw new Error(`Plugin ${expectedPluginName} should have been created`)
}

logger.debug(`Deleting plugin "${expectedPluginName}"`)
await impl.plugins.delete({ ...argv, pluginRef: plugin.id }).then(({ exitCode }) => {
exitCode !== 0 && logger.warn(`Failed to delete plugin "${expectedPluginName}"`)
})
},
}

export const enforceWorkspaceHandleIntegration: Test = {
name: 'cli should fail when attempting to deploy an integration with incorrect workspace handle',
handler: async ({ tmpDir, dependencies, ...creds }) => {
const botpressHomeDir = pathlib.join(tmpDir, '.botpresshome')
Expand Down Expand Up @@ -117,3 +186,43 @@ export const enforceWorkspaceHandle: Test = {
}
},
}

export const enforceWorkspaceHandlePlugin: Test = {
name: 'cli should fail when attempting to deploy a plugin with incorrect workspace handle',
handler: async ({ tmpDir, dependencies, ...creds }) => {
const botpressHomeDir = pathlib.join(tmpDir, '.botpresshome')
const baseDir = pathlib.join(tmpDir, 'plugins')

const randomSuffix = utils.getUUID().slice(0, 8)

const name = 'myplugin'
const handle = `myhandle${randomSuffix}`
const pluginName = `${handle}/${name}`
const pluginDir = pathlib.join(baseDir, name)

const argv = {
...defaults,
botpressHome: botpressHomeDir,
confirm: true,
...creds,
}

await impl
.init({ ...argv, workDir: baseDir, name: pluginName, type: 'plugin', template: 'empty' })
.then(utils.handleExitCode)
await utils.fixBotpressDependencies({ workDir: pluginDir, target: dependencies })
await utils.npmInstall({ workDir: pluginDir }).then(utils.handleExitCode)
await impl.login({ ...argv }).then(utils.handleExitCode)

const { exitCode } = await impl.deploy({
...argv,
createNewBot: undefined,
botId: undefined,
workDir: pluginDir,
})

if (exitCode === 0) {
throw new Error(`Plugin ${pluginName} should not have been deployed`)
}
},
}
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@botpress/cli",
"version": "6.8.5",
"version": "6.8.6",
"description": "Botpress CLI",
"scripts": {
"build": "pnpm run build:types && pnpm run bundle && pnpm run template:gen",
Expand Down
28 changes: 16 additions & 12 deletions packages/cli/src/command-implementations/bundle-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,22 @@ export class BundleCommand extends ProjectCommand<BundleCommandDefinition> {
props: Partial<utils.esbuild.BuildOptions> = {}
) {
const abs = this.projectPaths.abs
const context = buildContext ?? new utils.esbuild.BuildCodeContext()
await context.rebuild(
{
outfile,
absWorkingDir: abs.workDir,
code: this._code,
},
{
...this._buildOptions,
...props,
}
)
const buildProps = {
outfile,
absWorkingDir: abs.workDir,
code: this._code,
}
const buildOptions = {
...this._buildOptions,
...props,
}

if (buildContext) {
await buildContext.rebuild(buildProps, buildOptions)
return
}

await utils.esbuild.buildCode(buildProps, buildOptions)
}

private get _code() {
Expand Down
12 changes: 10 additions & 2 deletions packages/cli/src/command-implementations/deploy-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ export class DeployCommand extends ProjectCommand<DeployCommandDefinition> {
}

private async _deployIntegration(api: apiUtils.ApiClient, integrationDef: sdk.IntegrationDefinition) {
const res = await this.manageWorkspaceHandle(api, integrationDef)
const res = await this.manageWorkspaceHandle(api, { type: 'integration', definition: integrationDef })
if (!res) return
const { integration: updatedIntegrationDef, workspaceId } = res
const { definition: updatedIntegrationDef, workspaceId } = res
integrationDef = updatedIntegrationDef
if (workspaceId) {
api = api.switchWorkspace(workspaceId)
Expand Down Expand Up @@ -270,6 +270,14 @@ export class DeployCommand extends ProjectCommand<DeployCommandDefinition> {
}

private async _deployPlugin(api: apiUtils.ApiClient, pluginDef: sdk.PluginDefinition) {
const res = await this.manageWorkspaceHandle(api, { type: 'plugin', definition: pluginDef })
if (!res) return
const { definition: updatedPluginDef, workspaceId } = res
pluginDef = updatedPluginDef
if (workspaceId) {
api = api.switchWorkspace(workspaceId)
}

const codeCJS = await fs.promises.readFile(this.projectPaths.abs.outFileCJS, 'utf-8')
const codeESM = await fs.promises.readFile(this.projectPaths.abs.outFileESM, 'utf-8')

Expand Down
Loading
Loading