diff --git a/.changeset/deprecate-template-build-cmd.md b/.changeset/deprecate-template-build-cmd.md new file mode 100644 index 0000000000..4a52b4bd86 --- /dev/null +++ b/.changeset/deprecate-template-build-cmd.md @@ -0,0 +1,5 @@ +--- +"@e2b/cli": minor +--- + +Fully deprecate `e2b template build` (v1): command now only shows the deprecation notice and exits. Removed all v1 Docker build/push logic and deleted buildWithProxy.ts. diff --git a/packages/cli/README.md b/packages/cli/README.md index 9009bd1523..bd5696c902 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -30,7 +30,7 @@ e2b auth login > To authenticate without the ability to open the browser, provide > `E2B_ACCESS_TOKEN` as an environment variable. You can find your token > in Account Settings under the Team selector at [e2b.dev/dashboard](https://e2b.dev/dashboard). Then use the CLI like this: -> `E2B_ACCESS_TOKEN=sk_e2b_... e2b template build`. +> `E2B_ACCESS_TOKEN=sk_e2b_... e2b template create`. > [!IMPORTANT] > Note the distinction between `E2B_ACCESS_TOKEN` and `E2B_API_KEY`. diff --git a/packages/cli/package.json b/packages/cli/package.json index daa66209b3..59acdb14ee 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -48,7 +48,6 @@ "check-deps": "knip" }, "devDependencies": { - "@types/command-exists": "^1.2.3", "@types/handlebars": "^4.1.0", "@types/inquirer": "^9.0.7", "@types/json2md": "^1.5.4", @@ -79,7 +78,6 @@ "boxen": "^7.1.1", "chalk": "^5.3.0", "cli-highlight": "^2.1.11", - "command-exists": "^1.2.9", "commander": "^11.1.0", "console-table-printer": "^2.11.2", "dockerfile-ast": "^0.6.1", @@ -88,7 +86,6 @@ "inquirer": "^12.10.0", "simple-update-notifier": "^2.0.0", "statuses": "^2.0.1", - "strip-ansi": "^7.1.0", "yup": "^1.3.2" }, "engines": { diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index 30d444c553..aeee06baa9 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -8,7 +8,7 @@ import { authCommand } from './auth' export const program = new commander.Command() .description( `Create sandbox templates from Dockerfiles by running ${asPrimary( - 'e2b template build' + 'e2b template create' )} then use our SDKs to create sandboxes from these templates. Visit ${asPrimary( diff --git a/packages/cli/src/commands/template/build.ts b/packages/cli/src/commands/template/build.ts index 89f9f2b11d..3ce2c0c4f6 100644 --- a/packages/cli/src/commands/template/build.ts +++ b/packages/cli/src/commands/template/build.ts @@ -1,683 +1,37 @@ import * as boxen from 'boxen' -import * as child_process from 'child_process' -import commandExists from 'command-exists' import * as commander from 'commander' -import * as e2b from 'e2b' -import * as fs from 'fs' -import * as path from 'path' -import { - client, - connectionConfig, - ensureAccessToken, - resolveTeamId, -} from 'src/api' -import { configName, getConfigPath, loadConfig, saveConfig } from 'src/config' -import { - defaultDockerfileName, - fallbackDockerfileName, -} from 'src/docker/constants' -import { configOption, pathOption, teamOption } from 'src/options' -import { getUserConfig } from 'src/user' -import { getRoot } from 'src/utils/filesystem' -import { wait } from 'src/utils/wait' -import * as stripAnsi from 'strip-ansi' -import { handleE2BRequestError } from '../../utils/errors' -import { - asBold, - asBuildLogs, - asFormattedSandboxTemplate, - asLocal, - asLocalRelative, - asPrimary, - asPython, - asTypescript, - withDelimiter, -} from '../../utils/format' -import { buildWithProxy } from './buildWithProxy' - -const templateCheckInterval = 500 // 0.5 sec - -// Custom image URI is used for Bring Your Own Compute with self-hosted Docker registry -export const imageUriMask = process.env.E2B_IMAGE_URI_MASK - -async function getTemplateBuildLogs({ - templateID, - buildID, - logsOffset, -}: { - templateID: string - buildID: string - logsOffset: number -}) { - const signal = connectionConfig.getSignal() - const res = await client.api.GET( - '/templates/{templateID}/builds/{buildID}/status', - { - signal, - params: { - path: { - templateID, - buildID, - }, - query: { - logsOffset, - }, - }, - } - ) - - handleE2BRequestError(res, 'Error getting template build status') - return res.data as e2b.paths['/templates/{templateID}/builds/{buildID}/status']['get']['responses']['200']['content']['application/json'] -} - -async function requestTemplateBuild( - args: e2b.paths['/templates']['post']['requestBody']['content']['application/json'] -) { - return await client.api.POST('/templates', { - body: args, - }) -} - -async function requestTemplateRebuild( - templateID: string, - args: e2b.paths['/templates/{templateID}']['post']['requestBody']['content']['application/json'] -) { - return await client.api.POST('/templates/{templateID}', { - body: args, - params: { - path: { - templateID, - }, - }, - }) -} - -async function triggerTemplateBuild(templateID: string, buildID: string) { - let res - const maxRetries = 3 - for (let i = 0; i < maxRetries; i++) { - try { - res = await client.api.POST('/templates/{templateID}/builds/{buildID}', { - params: { - path: { - templateID, - buildID, - }, - }, - }) - - break - } catch (e) { - // If the build and push takes more than 10 minutes the connection gets automatically closed by load balancer - // and the request fails with UND_ERR_SOCKET error. In this case we just need to retry the request. - if ( - e instanceof TypeError && - ((e as TypeError).cause as any)?.code !== 'UND_ERR_SOCKET' - ) { - console.error(e) - console.log('Retrying...') - } - } - } - - if (!res) { - throw new Error('Error triggering template build') - } - - handleE2BRequestError(res, 'Error triggering template build') - return res.data -} +import { asBold, asPrimary } from '../../utils/format' export const buildCommand = new commander.Command('build') - .description( - `build sandbox template defined by ${asLocalRelative( - defaultDockerfileName - )} or ${asLocalRelative( - fallbackDockerfileName - )} in root directory. By default the root directory is the current working directory. This command also creates ${asLocal( - configName - )} config.` - ) - .argument( - '[template]', - `specify ${asBold( - '[template]' - )} to rebuild it. If you dont's specify ${asBold( - '[template]' - )} and there is no ${asLocal( - 'e2b.toml' - )} a new sandbox template will be created.` - ) - .addOption(pathOption) - .option( - '-d, --dockerfile ', - `specify path to Dockerfile. By default E2B tries to find ${asLocal( - defaultDockerfileName - )} or ${asLocal(fallbackDockerfileName)} in root directory.` - ) - .option( - '-n, --name ', - 'specify sandbox template name. You can use the template name to start the sandbox with SDK. The template name must be lowercase and contain only letters, numbers, dashes and underscores.' - ) - .option( - '-c, --cmd ', - 'specify command that will be executed when the sandbox is started.' - ) - .option( - '--ready-cmd ', - 'specify command that will need to exit 0 for the template to be ready.' - ) - .addOption(teamOption) - .addOption(configOption) - .option( - '--cpu-count ', - 'specify the number of CPUs that will be used to run the sandbox. The default value is 2.', - parseInt - ) - .option( - '--memory-mb ', - 'specify the amount of memory in megabytes that will be used to run the sandbox. Must be an even number. The default value is 512.', - parseInt - ) - .option( - '--build-arg ', - 'specify additional build arguments for the build command. The format should be =.' - ) - .option('--no-cache', 'skip cache when building the template.') + .description('Deprecated: use `e2b template create` instead.') + .argument('[template]', 'unused') + .allowUnknownOption(true) .alias('bd') - .action( - async ( - templateID: string | undefined, - opts: { - path?: string - dockerfile?: string - name?: string - cmd?: string - readyCmd?: string - team?: string - config?: string - cpuCount?: number - memoryMb?: number - buildArg?: [string] - noCache?: boolean - } - ) => { - try { - // Display deprecation warning - const deprecationMessage = `${asBold('DEPRECATION WARNING')} + .action(async () => { + const deprecationMessage = `${asBold('DEPRECATION WARNING')} This is the v1 build system which is now deprecated. Please migrate to the new build system v2. Migration guide: ${asPrimary('https://e2b.dev/docs/template/migration-v2')}` - const deprecationWarning = boxen.default(deprecationMessage, { - padding: { - bottom: 0, - top: 0, - left: 2, - right: 2, - }, - margin: { - top: 1, - bottom: 1, - left: 0, - right: 0, - }, - borderColor: 'yellow', - borderStyle: 'round', - }) - - console.log(deprecationWarning) - - const dockerInstalled = commandExists.sync('docker') - if (!dockerInstalled) { - console.error( - 'Docker is required to build and push the sandbox template. Please install Docker and try again.' - ) - process.exit(1) - } - - const dockerBuildArgs: { [key: string]: string } = {} - if (opts.buildArg) { - opts.buildArg.forEach((arg) => { - const [key, value] = arg.split('=') - dockerBuildArgs[key] = value - }) - } - - const accessToken = ensureAccessToken() - process.stdout.write('\n') - - const newName = opts.name?.trim() - if (newName && !/[a-z0-9-_]+/.test(newName)) { - console.error( - `Name ${asLocal( - newName - )} is not valid. Name can only contain lowercase letters, numbers, dashes and underscores.` - ) - process.exit(1) - } - - let dockerfile = opts.dockerfile - let startCmd = opts.cmd - let readyCmd = opts.readyCmd - let cpuCount = opts.cpuCount - let memoryMB = opts.memoryMb - let teamID = opts.team - - const root = getRoot(opts.path) - const configPath = getConfigPath(root, opts.config) - - const config = fs.existsSync(configPath) - ? await loadConfig(configPath) - : undefined - - const relativeConfigPath = path.relative(root, configPath) - - if (config) { - console.log( - `Found sandbox template ${asFormattedSandboxTemplate( - { - templateID: config.template_id, - aliases: config.template_name - ? [config.template_name] - : undefined, - }, - relativeConfigPath - )}` - ) - templateID = config.template_id - dockerfile = opts.dockerfile || config.dockerfile - startCmd = opts.cmd || config.start_cmd - readyCmd = opts.readyCmd || config.ready_cmd - cpuCount = opts.cpuCount || config.cpu_count - memoryMB = opts.memoryMb || config.memory_mb - } - - teamID = resolveTeamId(opts.team, config?.team_id) - - if (config && templateID && config.template_id !== templateID) { - // error: you can't specify different ID than the one in config - console.error( - "You can't specify different ID than the one in config. If you want to build a new sandbox template remove the config file." - ) - process.exit(1) - } - - if ( - newName && - config?.template_name && - newName !== config?.template_name - ) { - console.log( - `The sandbox template name will be changed from ${asLocal( - config.template_name - )} to ${asLocal(newName)}.` - ) - } - const name = newName || config?.template_name - - const { dockerfileContent, dockerfileRelativePath } = getDockerfile( - root, - dockerfile - ) - - console.log( - `Found ${asLocalRelative( - dockerfileRelativePath - )} that will be used to build the sandbox template.` - ) - - const body = { - alias: name, - startCmd: startCmd, - readyCmd: readyCmd, - cpuCount: cpuCount, - memoryMB: memoryMB, - dockerfile: dockerfileContent, - teamID: teamID, - } - - if (opts.memoryMb) { - if (opts.memoryMb % 2 !== 0) { - console.error( - `The memory in megabytes must be an even number. You provided ${asLocal( - opts.memoryMb.toFixed(0) - )}.` - ) - process.exit(1) - } - } - - const template = await requestBuildTemplate(body, templateID) - templateID = template.templateID - - console.log( - `Requested build for the sandbox template ${asFormattedSandboxTemplate( - template - )} ` - ) - - await saveConfig( - configPath, - { - template_id: template.templateID, - dockerfile: dockerfileRelativePath, - template_name: name, - start_cmd: startCmd, - ready_cmd: readyCmd, - cpu_count: cpuCount, - memory_mb: memoryMB, - team_id: teamID, - }, - true - ) - - if (imageUriMask == undefined) { - try { - child_process.execSync( - `echo "${accessToken}" | docker login docker.${connectionConfig.domain} -u _e2b_access_token --password-stdin`, - { - stdio: 'inherit', - cwd: root, - } - ) - } catch (err: any) { - console.error( - 'Docker login failed. Please try to log in with `e2b auth login` and try again.' - ) - process.exit(1) - } - } - - process.stdout.write('\n') - - const buildArgs = Object.entries(dockerBuildArgs) - .map(([key, value]) => `--build-arg "${key}=${value}"`) - .join(' ') - - const noCache = opts.noCache ? '--no-cache' : '' - - const imageUrl = dockerImageUrl( - templateID, - template.buildID, - connectionConfig.domain, - imageUriMask - ) - if (imageUriMask != undefined) { - console.log('Using custom docker image URI:', imageUrl) - } - - const cmd = [ - 'docker build', - `-f ${dockerfileRelativePath}`, - '--pull --platform linux/amd64', - `-t ${imageUrl}`, - buildArgs, - noCache, - '.', - ].join(' ') - - console.log( - `Building docker image with the following command:\n${asBold(cmd)}\n` - ) - - child_process.execSync(cmd, { - stdio: 'inherit', - cwd: root, - env: { - ...process.env, - DOCKER_CLI_HINTS: 'false', - }, - }) - console.log('> Docker image built.\n') - - const pushCmd = `docker push ${imageUrl}` - console.log( - `Pushing docker image with the following command:\n${asBold( - pushCmd - )}\n` - ) - try { - child_process.execSync(pushCmd, { - stdio: 'inherit', - cwd: root, - }) - } catch (err: any) { - const userConfig = getUserConfig() - await buildWithProxy( - userConfig, - connectionConfig, - accessToken, - template, - root - ) - } - console.log('> Docker image pushed.\n') - - console.log('Triggering build...') - await triggerBuild(templateID, template.buildID) - - console.log( - `> Triggered build for the sandbox template ${asFormattedSandboxTemplate( - template - )} with build ID: ${template.buildID}` - ) - - console.log('Waiting for build to finish...') - await waitForBuildFinish(templateID, template.buildID, name) - - process.exit(0) - } catch (err: any) { - console.error(err) - process.exit(1) - } - } - ) - -async function waitForBuildFinish( - templateID: string, - buildID: string, - name?: string -) { - let logsOffset = 0 - - let template: Awaited> | undefined - const aliases = name ? [name] : undefined - - process.stdout.write('\n') - do { - await wait(templateCheckInterval) - - template = await getTemplateBuildLogs({ - templateID, - logsOffset, - buildID, + const deprecationWarning = boxen.default(deprecationMessage, { + padding: { + bottom: 0, + top: 0, + left: 2, + right: 2, + }, + margin: { + top: 1, + bottom: 1, + left: 0, + right: 0, + }, + borderColor: 'yellow', + borderStyle: 'round', }) - logsOffset += template.logs.length - - template.logs.forEach((line) => - process.stdout.write(asBuildLogs(stripAnsi.default(line))) - ) - - switch (template.status) { - case 'building': - break - case 'ready': { - const pythonExample = asPython(`from e2b import Sandbox, AsyncSandbox - -# Create sync sandbox -sandbox = Sandbox.create("${ - aliases?.length ? aliases[0] : template.templateID - }") - -# Create async sandbox -sandbox = await AsyncSandbox.create("${ - aliases?.length ? aliases[0] : template.templateID - }")`) - - const typescriptExample = asTypescript(`import { Sandbox } from 'e2b' - -// Create sandbox -const sandbox = await Sandbox.create('${ - aliases?.length ? aliases[0] : template.templateID - }')`) - - const examplesMessage = `You can now use the template to create custom sandboxes.\nLearn more on ${asPrimary( - 'https://e2b.dev/docs' - )}` - - const exampleHeader = boxen.default(examplesMessage, { - padding: { - bottom: 1, - top: 1, - left: 2, - right: 2, - }, - margin: { - top: 1, - bottom: 1, - left: 0, - right: 0, - }, - fullscreen(width) { - return [width, 0] - }, - float: 'left', - }) - - const exampleUsage = `${withDelimiter( - pythonExample, - 'Python SDK' - )}\n${withDelimiter(typescriptExample, 'JS SDK', true)}` - - console.log( - `\n✅ Building sandbox template ${asFormattedSandboxTemplate({ - aliases, - ...template, - })} finished.\n${exampleHeader}\n${exampleUsage}\n` - ) - break - } - case 'error': - throw new Error( - `\n❌ Building sandbox template ${asFormattedSandboxTemplate({ - aliases, - ...template, - })} failed.\nCheck the logs above for more details or contact us ${asPrimary( - '(https://e2b.dev/docs/support)' - )} to get help.\n` - ) - } - } while (template.status === 'building') -} - -function loadFile(filePath: string) { - if (!fs.existsSync(filePath)) { - return undefined - } - - return fs.readFileSync(filePath, 'utf-8') -} - -export function getDockerfile(root: string, file?: string) { - // Check if user specified custom Dockerfile exists - if (file) { - const dockerfilePath = path.join(root, file) - const dockerfileContent = loadFile(dockerfilePath) - const dockerfileRelativePath = path.relative(root, dockerfilePath) - - if (dockerfileContent === undefined) { - throw new Error( - `No ${asLocalRelative( - dockerfileRelativePath - )} found in the root directory.` - ) - } - - return { - dockerfilePath, - dockerfileContent, - dockerfileRelativePath, - } - } - - // Check if default dockerfile e2b.Dockerfile exists - let dockerfilePath = path.join(root, defaultDockerfileName) - let dockerfileContent = loadFile(dockerfilePath) - const defaultDockerfileRelativePath = path.relative(root, dockerfilePath) - let dockerfileRelativePath = defaultDockerfileRelativePath - - if (dockerfileContent !== undefined) { - return { - dockerfilePath, - dockerfileContent, - dockerfileRelativePath, - } - } - - // Check if fallback Dockerfile exists - dockerfilePath = path.join(root, fallbackDockerfileName) - dockerfileContent = loadFile(dockerfilePath) - const fallbackDockerfileRelativeName = path.relative(root, dockerfilePath) - dockerfileRelativePath = fallbackDockerfileRelativeName - - if (dockerfileContent !== undefined) { - return { - dockerfilePath, - dockerfileContent, - dockerfileRelativePath, - } - } - - throw new Error( - `No ${asLocalRelative(defaultDockerfileRelativePath)} or ${asLocalRelative( - fallbackDockerfileRelativeName - )} found in the root directory (${root}). You can specify a custom Dockerfile with ${asBold( - '--dockerfile ' - )} option.` - ) -} - -async function requestBuildTemplate( - args: e2b.paths['/templates']['post']['requestBody']['content']['application/json'], - templateID?: string -): Promise< - Omit< - e2b.paths['/templates']['post']['responses']['202']['content']['application/json'], - 'logs' - > -> { - let res - if (templateID) { - res = await requestTemplateRebuild(templateID, args) - } else { - res = await requestTemplateBuild(args) - } - - handleE2BRequestError(res, 'Error requesting template build') - return res.data -} - -async function triggerBuild(templateID: string, buildID: string) { - await triggerTemplateBuild(templateID, buildID) - - return -} - -function dockerImageUrl( - templateID: string, - buildID: string, - defaultDomain: string, - imageUrlMask?: string -): string { - if (imageUrlMask == undefined) { - return `docker.${defaultDomain}/e2b/custom-envs/${templateID}:${buildID}` - } - - return imageUrlMask - .replaceAll('{templateID}', templateID) - .replaceAll('{buildID}', buildID) -} + console.log(deprecationWarning) + process.exit(1) + }) diff --git a/packages/cli/src/commands/template/buildWithProxy.ts b/packages/cli/src/commands/template/buildWithProxy.ts deleted file mode 100644 index 7f15821f46..0000000000 --- a/packages/cli/src/commands/template/buildWithProxy.ts +++ /dev/null @@ -1,216 +0,0 @@ -import child_process from 'child_process' -import * as e2b from 'e2b' -import * as http from 'http' -import * as url from 'url' -import * as https from 'node:https' -import { confirm } from '../../utils/confirm' -import { USER_CONFIG_PATH, UserConfig, writeUserConfig } from 'src/user' - -const PORT = 49984 - -export async function buildWithProxy( - userConfig: UserConfig | null, - connectionConfig: e2b.ConnectionConfig, - accessToken: string, - template: { templateID: string; buildID: string }, - root: string -) { - if (!userConfig?.dockerProxySet) { - console.log( - 'There was an issue during Docker authentication. Please follow the workaround steps from https://e2b.dev/docs/troubleshooting/templates/build-authentication-error and then continue.' - ) - const yes = await confirm( - 'Have you completed the steps from the https://e2b.dev/docs/troubleshooting/templates/build-authentication-error workaround guide?' - ) - - if (!yes) { - console.log( - 'Please follow the workaround steps from https://e2b.dev/docs/troubleshooting/templates/build-authentication-error and then try again.' - ) - process.exit(1) - } - } - - let proxyStarted: ((value: unknown) => void) | undefined = undefined - - const proxyReady = new Promise((resolve) => { - proxyStarted = resolve - }) - - const accessTokenBase64Encoded = Buffer.from( - `_e2b_access_token:${accessToken}` - ).toString('base64') - - const proxyServer = await proxy( - connectionConfig, - template, - accessTokenBase64Encoded, - proxyStarted! - ) - - await proxyReady - const success = await docker(connectionConfig, template, root) - - if (!success) { - console.error('Docker push failed') - process.exit(1) - } - - if (userConfig && !userConfig.dockerProxySet) { - userConfig.dockerProxySet = true - writeUserConfig(USER_CONFIG_PATH, userConfig) - } - - proxyServer.close() -} - -async function docker( - connectionConfig: e2b.ConnectionConfig, - template: { templateID: string; buildID: string }, - root: string -) { - const localDomain = - process.platform === 'linux' ? 'localhost' : 'host.docker.internal' - let success = false - - child_process.execSync( - `docker tag docker.${connectionConfig.domain}/e2b/custom-envs/${template.templateID}:${template.buildID} ${localDomain}:${PORT}/e2b/custom-envs/${template.templateID}:${template.buildID}`, - { - stdio: 'inherit', - cwd: root, - } - ) - - let onExit: ((code: number | null) => void) | undefined = undefined - const dockerBuilt = new Promise((resolve) => { - onExit = resolve - }) - - const child = child_process.spawn( - 'docker', - [ - 'push', - `${localDomain}:${PORT}/e2b/custom-envs/${template.templateID}:${template.buildID}`, - ], - { - detached: true, - stdio: 'inherit', - cwd: root, - } - ) - child.on('exit', (code) => { - if (code !== 0) { - console.error('Docker push failed') - process.exit(1) - } - success = true - onExit!(code) - }) - - child.on('error', (err) => { - console.error('Error', err) - process.exit(1) - }) - - await dockerBuilt - return success -} - -async function proxy( - connectionConfig: e2b.ConnectionConfig, - template: { templateID: string; buildID: string }, - credsBase64: string, - proxyStarted: (value: unknown) => void -) { - const res = await fetch( - `https://docker.${connectionConfig.domain}/v2/token?account=_e2b_access_token&scope=repository%3Ae2b%2Fcustom-envs%2F${template.templateID}%3Apush%2Cpull`, - { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - Authorization: `Basic ${credsBase64}`, - }, - } - ) - - const { token } = await res.json() - - const proxyServer = http.createServer( - (clientReq: http.IncomingMessage, clientRes: http.ServerResponse) => { - // Parse the target URL - const targetUrl = new url.URL( - clientReq.url || '/', - `https://docker.${connectionConfig.domain}` - ) - - // Construct options for the proxy request - const options = { - protocol: 'https:', - hostname: targetUrl.hostname, - method: clientReq.method, - path: targetUrl.pathname + targetUrl.search, - headers: { - ...clientReq.headers, - host: targetUrl.hostname, - }, - } as http.RequestOptions - - // Type-safe header manipulation - const headers = options.headers as http.OutgoingHttpHeaders - - if (!headers.Authorization) { - if (targetUrl.pathname.startsWith('/v2/token')) { - headers.Authorization = `Basic ${credsBase64}` - } else if ( - targetUrl.pathname == '/v2/' || - targetUrl.pathname == '/v2' - ) { - headers.Authorization = `Bearer ${credsBase64}` - } else if ( - // Exclude the artifacts-uploads namespace - !targetUrl.pathname.startsWith('/artifacts-uploads/namespaces') - ) { - headers.Authorization = `Bearer ${token}` - } - } - - // Create the proxy getHeaders - const proxyReq: http.ClientRequest = https.request( - options, - (proxyRes: http.IncomingMessage) => { - // Copy status code and headers - clientRes.writeHead(proxyRes.statusCode || 500, proxyRes.headers) - // Pipe the response data - proxyRes.pipe(clientRes, { - end: true, - }) - } - ) - - // Handle proxy request errors - proxyReq.on('error', (err: Error) => { - console.error('Proxy Request Error:', err) - clientRes.statusCode = 500 - clientRes.end(`Proxy Error: ${err.message}`) - }) - - // Pipe the client request data to proxy request - clientReq.pipe(proxyReq, { - end: true, - }) - } - ) - - // Handle server errors - proxyServer.on('error', (err: Error) => { - console.error('Server Error:', err) - }) - - // Start the server - proxyServer.listen(PORT, () => { - proxyStarted(null) - console.log(`Proxy server running on port ${PORT}`) - }) - - return proxyServer -} diff --git a/packages/cli/src/commands/template/create.ts b/packages/cli/src/commands/template/create.ts index 2480b87ea5..4645df5a65 100644 --- a/packages/cli/src/commands/template/create.ts +++ b/packages/cli/src/commands/template/create.ts @@ -17,7 +17,7 @@ import { asTypescript, withDelimiter, } from '../../utils/format' -import { getDockerfile } from './build' +import { getDockerfile } from './dockerfile' export const createCommand = new commander.Command('create') .description( diff --git a/packages/cli/src/commands/template/dockerfile.ts b/packages/cli/src/commands/template/dockerfile.ts new file mode 100644 index 0000000000..635d98eaf7 --- /dev/null +++ b/packages/cli/src/commands/template/dockerfile.ts @@ -0,0 +1,71 @@ +import * as fs from 'fs' +import * as path from 'path' +import { + defaultDockerfileName, + fallbackDockerfileName, +} from 'src/docker/constants' +import { asBold, asLocalRelative } from '../../utils/format' + +function loadFile(filePath: string) { + if (!fs.existsSync(filePath)) { + return undefined + } + + return fs.readFileSync(filePath, 'utf-8') +} + +export function getDockerfile(root: string, file?: string) { + if (file) { + const dockerfilePath = path.join(root, file) + const dockerfileContent = loadFile(dockerfilePath) + const dockerfileRelativePath = path.relative(root, dockerfilePath) + + if (dockerfileContent === undefined) { + throw new Error( + `No ${asLocalRelative( + dockerfileRelativePath + )} found in the root directory.` + ) + } + + return { + dockerfilePath, + dockerfileContent, + dockerfileRelativePath, + } + } + + let dockerfilePath = path.join(root, defaultDockerfileName) + let dockerfileContent = loadFile(dockerfilePath) + const defaultDockerfileRelativePath = path.relative(root, dockerfilePath) + let dockerfileRelativePath = defaultDockerfileRelativePath + + if (dockerfileContent !== undefined) { + return { + dockerfilePath, + dockerfileContent, + dockerfileRelativePath, + } + } + + dockerfilePath = path.join(root, fallbackDockerfileName) + dockerfileContent = loadFile(dockerfilePath) + const fallbackDockerfileRelativeName = path.relative(root, dockerfilePath) + dockerfileRelativePath = fallbackDockerfileRelativeName + + if (dockerfileContent !== undefined) { + return { + dockerfilePath, + dockerfileContent, + dockerfileRelativePath, + } + } + + throw new Error( + `No ${asLocalRelative(defaultDockerfileRelativePath)} or ${asLocalRelative( + fallbackDockerfileRelativeName + )} found in the root directory (${root}). You can specify a custom Dockerfile with ${asBold( + '--dockerfile ' + )} option.` + ) +} diff --git a/packages/cli/src/commands/template/migrate.ts b/packages/cli/src/commands/template/migrate.ts index 72d7ef7742..6f4474ffec 100644 --- a/packages/cli/src/commands/template/migrate.ts +++ b/packages/cli/src/commands/template/migrate.ts @@ -8,7 +8,7 @@ import { defaultDockerfileName } from '../../docker/constants' import { configOption, pathOption } from '../../options' import { getRoot } from '../../utils/filesystem' import { asLocal, asLocalRelative, asPrimary } from '../../utils/format' -import { getDockerfile } from './build' +import { getDockerfile } from './dockerfile' import { generateAndWriteTemplateFiles, Language, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0aea4e2f76..02a105261b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -70,9 +70,6 @@ importers: cli-highlight: specifier: ^2.1.11 version: 2.1.11 - command-exists: - specifier: ^1.2.9 - version: 1.2.9 commander: specifier: ^11.1.0 version: 11.1.0 @@ -97,16 +94,10 @@ importers: statuses: specifier: ^2.0.1 version: 2.0.1 - strip-ansi: - specifier: ^7.1.0 - version: 7.1.0 yup: specifier: ^1.3.2 version: 1.3.2 devDependencies: - '@types/command-exists': - specifier: ^1.2.3 - version: 1.2.3 '@types/handlebars': specifier: ^4.1.0 version: 4.1.0 @@ -1333,9 +1324,6 @@ packages: '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} - '@types/command-exists@1.2.3': - resolution: {integrity: sha512-PpbaE2XWLaWYboXD6k70TcXO/OdOyyRFq5TVpmlUELNxdkkmXU9fkImNosmXU1DtsNrqdUgWd/nJQYXgwmtdXQ==} - '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} @@ -1825,9 +1813,6 @@ packages: colorette@1.4.0: resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==} - command-exists@1.2.9: - resolution: {integrity: sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==} - commander@11.1.0: resolution: {integrity: sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==} engines: {node: '>=16'} @@ -4422,7 +4407,7 @@ snapshots: '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 - '@jridgewell/trace-mapping': 0.3.30 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/gen-mapping@0.3.8': dependencies: @@ -4784,8 +4769,6 @@ snapshots: '@types/deep-eql': 4.0.2 assertion-error: 2.0.1 - '@types/command-exists@1.2.3': {} - '@types/deep-eql@4.0.2': {} '@types/estree@1.0.8': {} @@ -5362,8 +5345,6 @@ snapshots: colorette@1.4.0: {} - command-exists@1.2.9: {} - commander@11.1.0: {} commander@2.20.3: