Skip to main content
Glama
dokku_deploy.mjs4.79 kB
import { NodeSSH } from 'node-ssh'; import { mkdir, rm } from 'node:fs/promises'; import { resolve } from 'node:path'; import { next as generate_xid } from 'xid-js'; import { parseArgsWithHelp } from './parse_args_with_help.mjs'; import { printDuration } from './performance_utils.mjs'; import { spawnPromise } from './spawn_promise.mjs'; const { values: argv, outputPrefix } = parseArgsWithHelp(import.meta.url, { options: { app_name: { description: `(required) name of app to deploy`, type: `string`, short: `a`, }, local_directory: { description: `(optional) directory that will store locally created docker images`, type: `string`, default: `dist`, short: `l`, }, remote_directory: { description: `(optional) directory of the remote ssh machine that will store uploaded docker images`, type: `string`, default: `tmp/docker_images`, short: `r`, }, cwd: { description: `(optional) directory that contains the dockerfile`, type: `string`, default: process.cwd(), }, }, }); if (!argv.app_name) { throw new Error( "App name is required, provide with -a argument, ex: -a appName" ); } const ssh = new NodeSSH(); /** * @param {string} cmd * @param {import('node-ssh').SSHExecCommandOptions} [options] */ async function sshCommand(cmd, options) { try { const startTime = performance.now(); console.log(`${outputPrefix}BEGIN_SSH: ${cmd}`); const code = await ssh.execCommand(cmd, { onStdout: (chunk) => process.stdout.write(chunk.toString('utf8')), onStderr: (chunk) => process.stderr.write(chunk.toString('utf8')), ...options, }); if (code.code !== 0) { throw new Error(code.code); } console.log(`${outputPrefix}END_SSH: ${cmd} (${printDuration(startTime)})`); } catch (error) { console.error(`${outputPrefix}ERROR_SSH: ${error}`); throw error; } } const localDirectory = resolve(argv.cwd, argv.local_directory); const remoteDirectory = argv.remote_directory; const appName = argv.app_name; const tag = generate_xid(); const dockerImageTag = `dokku/${appName}:${tag}`; let suceeded = false; try { // Check we have a good SSH connection await ssh.connect({ host: process.env.SSH_HOST, username: process.env.SSH_USERNAME, password: process.env.SSH_PASSWORD, privateKey: process.env.SSH_PRIVATE_KEY, privateKeyPath: process.env.SSH_PRIVATE_KEY_PATH, passphrase: process.env.SSH_PRIVATE_KEY_PASSWORD, }); // Build docker image console.log(`${outputPrefix}Building image: '${appName}:${tag}', please wait...`); await spawnPromise( `node --run build -- --tag=${dockerImageTag}${process.env.MODE === 'development' ? ' --development' : ''}`, { outputPrefix: `${outputPrefix}`, forwardParams: false, cwd: argv.cwd, }, ); // Save docker image to disk console.log(`${outputPrefix}Creating archive from image, please wait...`); await mkdir(localDirectory, { recursive: true }); await spawnPromise(`docker image save ${dockerImageTag} | gzip > ${localDirectory}/${appName}_${tag}.tar.gz`, { outputPrefix: `${outputPrefix}`, forwardParams: false, }); // Upload docker image archive to remote await sshCommand(`mkdir -p ${remoteDirectory}`); console.log(`${outputPrefix}Uploading '${appName}_${tag}.tar.gz', please wait...`); const startTime = performance.now(); await ssh.putFiles([ { local: `${localDirectory}/${appName}_${tag}.tar.gz`, remote: `${remoteDirectory}/${appName}_${tag}.tar.gz`, }, ]); console.log(`${outputPrefix}Upload of '${appName}_${tag}.tar.gz' completed in ${printDuration(startTime)}`); // Unpack & load docker image on remote await sshCommand(`docker load < ${appName}_${tag}.tar.gz`, { cwd: remoteDirectory }); // Deploy docker image on remote await sshCommand(`dokku git:from-image ${appName} ${dockerImageTag}`); suceeded = true; } catch (error) { console.error(error); } finally { // Post-deployment cleanup (local commands must occur before remote commands) try { // Local commands (before remote) /* await spawnPromise(`rm ${appName}_${tag}.tar.gz`, { cwd: localDirectory, outputPrefix: `${outputPrefix}`, forwardParams: false, }); */ await rm(localDirectory, { recursive: true }); // Remote commands (after local) // Ensure subsquent git based deployments work successfully // https://github.com/dokku/dokku/issues/5963#issuecomment-1615836280 await sshCommand(`dokku git:set ${appName} source-image`); // Delete build artifacts on remote server await sshCommand('rm -f *', { cwd: remoteDirectory }); } catch {} ssh.dispose(); } if (!suceeded) { process.exit(1); }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/MCP-Manager/MCP-Checklists'

If you have feedback or need assistance with the MCP directory API, please join our Discord server