mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 09:13:08 +00:00
refactor: streamline Docker container management and enhance utility functions
- Removed redundant Docker image rebuilding logic from `dev.mjs` and `start.mjs`, centralizing it in the new `launchDockerContainers` function within `launcher-utils.mjs`. - Introduced `sanitizeProjectName` and `shouldRebuildDockerImages` functions to improve project name handling and Docker image management. - Updated the Docker launch process to provide clearer logging and ensure proper handling of environment variables, enhancing the overall development experience.
This commit is contained in:
142
dev.mjs
142
dev.mjs
@@ -11,15 +11,13 @@
|
|||||||
|
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import { createRequire } from 'module';
|
|
||||||
import { statSync } from 'fs';
|
|
||||||
import { execSync } from 'child_process';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
createRestrictedFs,
|
createRestrictedFs,
|
||||||
log,
|
log,
|
||||||
runNpm,
|
runNpm,
|
||||||
runNpmAndWait,
|
runNpmAndWait,
|
||||||
|
runNpx,
|
||||||
printHeader,
|
printHeader,
|
||||||
printModeMenu,
|
printModeMenu,
|
||||||
resolvePortConfiguration,
|
resolvePortConfiguration,
|
||||||
@@ -28,11 +26,9 @@ import {
|
|||||||
startServerAndWait,
|
startServerAndWait,
|
||||||
ensureDependencies,
|
ensureDependencies,
|
||||||
prompt,
|
prompt,
|
||||||
|
launchDockerContainers,
|
||||||
} from './scripts/launcher-utils.mjs';
|
} from './scripts/launcher-utils.mjs';
|
||||||
|
|
||||||
const require = createRequire(import.meta.url);
|
|
||||||
const crossSpawn = require('cross-spawn');
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
@@ -47,89 +43,6 @@ const processes = {
|
|||||||
docker: null,
|
docker: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Sanitize a project name to be safe for use in shell commands and Docker image names.
|
|
||||||
* Converts to lowercase and removes any characters that aren't alphanumeric.
|
|
||||||
*/
|
|
||||||
function sanitizeProjectName(name) {
|
|
||||||
return name.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if Docker images need to be rebuilt based on Dockerfile or package.json changes
|
|
||||||
*/
|
|
||||||
function shouldRebuildDockerImages() {
|
|
||||||
try {
|
|
||||||
const dockerfilePath = path.join(__dirname, 'Dockerfile');
|
|
||||||
const packageJsonPath = path.join(__dirname, 'package.json');
|
|
||||||
|
|
||||||
// Get modification times of source files
|
|
||||||
const dockerfileMtime = statSync(dockerfilePath).mtimeMs;
|
|
||||||
const packageJsonMtime = statSync(packageJsonPath).mtimeMs;
|
|
||||||
const latestSourceMtime = Math.max(dockerfileMtime, packageJsonMtime);
|
|
||||||
|
|
||||||
// Get project name from docker-compose config, falling back to directory name
|
|
||||||
let projectName;
|
|
||||||
try {
|
|
||||||
const composeConfig = execSync('docker compose config --format json', {
|
|
||||||
encoding: 'utf-8',
|
|
||||||
cwd: __dirname,
|
|
||||||
});
|
|
||||||
const config = JSON.parse(composeConfig);
|
|
||||||
projectName = config.name;
|
|
||||||
} catch (error) {
|
|
||||||
// Fallback handled below
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanitize project name (whether from config or fallback)
|
|
||||||
// This prevents command injection and ensures valid Docker image names
|
|
||||||
const sanitizedProjectName = sanitizeProjectName(
|
|
||||||
projectName || path.basename(__dirname)
|
|
||||||
);
|
|
||||||
const serverImageName = `${sanitizedProjectName}_server`;
|
|
||||||
const uiImageName = `${sanitizedProjectName}_ui`;
|
|
||||||
|
|
||||||
// Check if images exist and get their creation times
|
|
||||||
let needsRebuild = false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Check server image
|
|
||||||
const serverImageInfo = execSync(
|
|
||||||
`docker image inspect ${serverImageName} --format "{{.Created}}" 2>/dev/null || echo ""`,
|
|
||||||
{ encoding: 'utf-8', cwd: __dirname }
|
|
||||||
).trim();
|
|
||||||
|
|
||||||
// Check UI image
|
|
||||||
const uiImageInfo = execSync(
|
|
||||||
`docker image inspect ${uiImageName} --format "{{.Created}}" 2>/dev/null || echo ""`,
|
|
||||||
{ encoding: 'utf-8', cwd: __dirname }
|
|
||||||
).trim();
|
|
||||||
|
|
||||||
// If either image doesn't exist, we need to rebuild
|
|
||||||
if (!serverImageInfo || !uiImageInfo) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse image creation times (ISO 8601 format)
|
|
||||||
const serverCreated = new Date(serverImageInfo).getTime();
|
|
||||||
const uiCreated = new Date(uiImageInfo).getTime();
|
|
||||||
const oldestImageTime = Math.min(serverCreated, uiCreated);
|
|
||||||
|
|
||||||
// If source files are newer than images, rebuild
|
|
||||||
needsRebuild = latestSourceMtime > oldestImageTime;
|
|
||||||
} catch (error) {
|
|
||||||
// If images don't exist or inspect fails, rebuild
|
|
||||||
needsRebuild = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return needsRebuild;
|
|
||||||
} catch (error) {
|
|
||||||
// If we can't check, err on the side of rebuilding
|
|
||||||
log('Could not check Docker image status, will rebuild to be safe', 'yellow');
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Install Playwright browsers (dev-only dependency)
|
* Install Playwright browsers (dev-only dependency)
|
||||||
*/
|
*/
|
||||||
@@ -137,10 +50,11 @@ async function installPlaywrightBrowsers() {
|
|||||||
log('Checking Playwright browsers...', 'yellow');
|
log('Checking Playwright browsers...', 'yellow');
|
||||||
try {
|
try {
|
||||||
const exitCode = await new Promise((resolve) => {
|
const exitCode = await new Promise((resolve) => {
|
||||||
const playwright = crossSpawn('npx', ['playwright', 'install', 'chromium'], {
|
const playwright = runNpx(
|
||||||
stdio: 'inherit',
|
['playwright', 'install', 'chromium'],
|
||||||
cwd: path.join(__dirname, 'apps', 'ui'),
|
{ stdio: 'inherit' },
|
||||||
});
|
path.join(__dirname, 'apps', 'ui')
|
||||||
|
);
|
||||||
playwright.on('close', (code) => resolve(code));
|
playwright.on('close', (code) => resolve(code));
|
||||||
playwright.on('error', () => resolve(1));
|
playwright.on('error', () => resolve(1));
|
||||||
});
|
});
|
||||||
@@ -256,47 +170,7 @@ async function main() {
|
|||||||
break;
|
break;
|
||||||
} else if (choice === '3') {
|
} else if (choice === '3') {
|
||||||
console.log('');
|
console.log('');
|
||||||
log('Launching Docker Container (Isolated Mode)...', 'blue');
|
await launchDockerContainers({ baseDir: __dirname, processes });
|
||||||
|
|
||||||
// Check if Dockerfile or package.json changed and rebuild if needed
|
|
||||||
const needsRebuild = shouldRebuildDockerImages();
|
|
||||||
const buildFlag = needsRebuild ? ['--build'] : [];
|
|
||||||
|
|
||||||
if (needsRebuild) {
|
|
||||||
log('Dockerfile or package.json changed - rebuilding images...', 'yellow');
|
|
||||||
} else {
|
|
||||||
log('Starting Docker containers...', 'yellow');
|
|
||||||
}
|
|
||||||
console.log('');
|
|
||||||
|
|
||||||
// Check if ANTHROPIC_API_KEY is set
|
|
||||||
if (!process.env.ANTHROPIC_API_KEY) {
|
|
||||||
log('Warning: ANTHROPIC_API_KEY environment variable is not set.', 'yellow');
|
|
||||||
log('The server will require an API key to function.', 'yellow');
|
|
||||||
log('Set it with: export ANTHROPIC_API_KEY=your-key', 'yellow');
|
|
||||||
console.log('');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start containers with docker-compose
|
|
||||||
// Will rebuild if Dockerfile or package.json changed
|
|
||||||
processes.docker = crossSpawn('docker', ['compose', 'up', ...buildFlag], {
|
|
||||||
stdio: 'inherit',
|
|
||||||
cwd: __dirname,
|
|
||||||
env: {
|
|
||||||
...process.env,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
log('Docker containers starting...', 'blue');
|
|
||||||
log('UI will be available at: http://localhost:3007', 'green');
|
|
||||||
log('API will be available at: http://localhost:3008', 'green');
|
|
||||||
console.log('');
|
|
||||||
log('Press Ctrl+C to stop the containers.', 'yellow');
|
|
||||||
|
|
||||||
await new Promise((resolve) => {
|
|
||||||
processes.docker.on('close', resolve);
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
log('Invalid choice. Please enter 1, 2, or 3.', 'red');
|
log('Invalid choice. Please enter 1, 2, or 3.', 'red');
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { execSync } from 'child_process';
|
import { execSync } from 'child_process';
|
||||||
import fsNative from 'fs';
|
import fsNative, { statSync } from 'fs';
|
||||||
import http from 'http';
|
import http from 'http';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import readline from 'readline';
|
import readline from 'readline';
|
||||||
@@ -662,3 +662,142 @@ export async function ensureDependencies(fs, baseDir) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =============================================================================
|
||||||
|
// Docker Utilities
|
||||||
|
// =============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sanitize a project name to be safe for use in shell commands and Docker image names.
|
||||||
|
* Converts to lowercase and removes any characters that aren't alphanumeric.
|
||||||
|
* @param {string} name - Project name to sanitize
|
||||||
|
* @returns {string} - Sanitized project name
|
||||||
|
*/
|
||||||
|
export function sanitizeProjectName(name) {
|
||||||
|
return name.toLowerCase().replace(/[^a-z0-9]/g, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if Docker images need to be rebuilt based on Dockerfile or package.json changes
|
||||||
|
* @param {string} baseDir - Base directory containing Dockerfile and package.json
|
||||||
|
* @returns {boolean} - Whether images need to be rebuilt
|
||||||
|
*/
|
||||||
|
export function shouldRebuildDockerImages(baseDir) {
|
||||||
|
try {
|
||||||
|
const dockerfilePath = path.join(baseDir, 'Dockerfile');
|
||||||
|
const packageJsonPath = path.join(baseDir, 'package.json');
|
||||||
|
|
||||||
|
// Get modification times of source files
|
||||||
|
const dockerfileMtime = statSync(dockerfilePath).mtimeMs;
|
||||||
|
const packageJsonMtime = statSync(packageJsonPath).mtimeMs;
|
||||||
|
const latestSourceMtime = Math.max(dockerfileMtime, packageJsonMtime);
|
||||||
|
|
||||||
|
// Get project name from docker-compose config, falling back to directory name
|
||||||
|
let projectName;
|
||||||
|
try {
|
||||||
|
const composeConfig = execSync('docker compose config --format json', {
|
||||||
|
encoding: 'utf-8',
|
||||||
|
cwd: baseDir,
|
||||||
|
});
|
||||||
|
const config = JSON.parse(composeConfig);
|
||||||
|
projectName = config.name;
|
||||||
|
} catch (error) {
|
||||||
|
// Fallback handled below
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanitize project name (whether from config or fallback)
|
||||||
|
// This prevents command injection and ensures valid Docker image names
|
||||||
|
const sanitizedProjectName = sanitizeProjectName(projectName || path.basename(baseDir));
|
||||||
|
const serverImageName = `${sanitizedProjectName}_server`;
|
||||||
|
const uiImageName = `${sanitizedProjectName}_ui`;
|
||||||
|
|
||||||
|
// Check if images exist and get their creation times
|
||||||
|
let needsRebuild = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Check server image
|
||||||
|
const serverImageInfo = execSync(
|
||||||
|
`docker image inspect ${serverImageName} --format "{{.Created}}" 2>/dev/null || echo ""`,
|
||||||
|
{ encoding: 'utf-8', cwd: baseDir }
|
||||||
|
).trim();
|
||||||
|
|
||||||
|
// Check UI image
|
||||||
|
const uiImageInfo = execSync(
|
||||||
|
`docker image inspect ${uiImageName} --format "{{.Created}}" 2>/dev/null || echo ""`,
|
||||||
|
{ encoding: 'utf-8', cwd: baseDir }
|
||||||
|
).trim();
|
||||||
|
|
||||||
|
// If either image doesn't exist, we need to rebuild
|
||||||
|
if (!serverImageInfo || !uiImageInfo) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse image creation times (ISO 8601 format)
|
||||||
|
const serverCreated = new Date(serverImageInfo).getTime();
|
||||||
|
const uiCreated = new Date(uiImageInfo).getTime();
|
||||||
|
const oldestImageTime = Math.min(serverCreated, uiCreated);
|
||||||
|
|
||||||
|
// If source files are newer than images, rebuild
|
||||||
|
needsRebuild = latestSourceMtime > oldestImageTime;
|
||||||
|
} catch (error) {
|
||||||
|
// If images don't exist or inspect fails, rebuild
|
||||||
|
needsRebuild = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return needsRebuild;
|
||||||
|
} catch (error) {
|
||||||
|
// If we can't check, err on the side of rebuilding
|
||||||
|
log('Could not check Docker image status, will rebuild to be safe', 'yellow');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Launch Docker containers with docker-compose
|
||||||
|
* @param {object} options - Configuration options
|
||||||
|
* @param {string} options.baseDir - Base directory containing docker-compose.yml
|
||||||
|
* @param {object} options.processes - Processes object to track docker process
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
export async function launchDockerContainers({ baseDir, processes }) {
|
||||||
|
log('Launching Docker Container (Isolated Mode)...', 'blue');
|
||||||
|
|
||||||
|
// Check if Dockerfile or package.json changed and rebuild if needed
|
||||||
|
const needsRebuild = shouldRebuildDockerImages(baseDir);
|
||||||
|
const buildFlag = needsRebuild ? ['--build'] : [];
|
||||||
|
|
||||||
|
if (needsRebuild) {
|
||||||
|
log('Dockerfile or package.json changed - rebuilding images...', 'yellow');
|
||||||
|
} else {
|
||||||
|
log('Starting Docker containers...', 'yellow');
|
||||||
|
}
|
||||||
|
console.log('');
|
||||||
|
|
||||||
|
// Check if ANTHROPIC_API_KEY is set
|
||||||
|
if (!process.env.ANTHROPIC_API_KEY) {
|
||||||
|
log('Warning: ANTHROPIC_API_KEY environment variable is not set.', 'yellow');
|
||||||
|
log('The server will require an API key to function.', 'yellow');
|
||||||
|
log('Set it with: export ANTHROPIC_API_KEY=your-key', 'yellow');
|
||||||
|
console.log('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start containers with docker-compose
|
||||||
|
// Will rebuild if Dockerfile or package.json changed
|
||||||
|
processes.docker = crossSpawn('docker', ['compose', 'up', ...buildFlag], {
|
||||||
|
stdio: 'inherit',
|
||||||
|
cwd: baseDir,
|
||||||
|
env: {
|
||||||
|
...process.env,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
log('Docker containers starting...', 'blue');
|
||||||
|
log('UI will be available at: http://localhost:3007', 'green');
|
||||||
|
log('API will be available at: http://localhost:3008', 'green');
|
||||||
|
console.log('');
|
||||||
|
log('Press Ctrl+C to stop the containers.', 'yellow');
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
processes.docker.on('close', resolve);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
133
start.mjs
133
start.mjs
@@ -18,13 +18,9 @@
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
import { createRequire } from 'module';
|
|
||||||
import { statSync } from 'fs';
|
|
||||||
import { execSync } from 'child_process';
|
|
||||||
import {
|
import {
|
||||||
createRestrictedFs,
|
createRestrictedFs,
|
||||||
log,
|
log,
|
||||||
runNpm,
|
|
||||||
runNpmAndWait,
|
runNpmAndWait,
|
||||||
runNpx,
|
runNpx,
|
||||||
printHeader,
|
printHeader,
|
||||||
@@ -37,11 +33,9 @@ import {
|
|||||||
prompt,
|
prompt,
|
||||||
killProcessTree,
|
killProcessTree,
|
||||||
sleep,
|
sleep,
|
||||||
|
launchDockerContainers,
|
||||||
} from './scripts/launcher-utils.mjs';
|
} from './scripts/launcher-utils.mjs';
|
||||||
|
|
||||||
const require = createRequire(import.meta.url);
|
|
||||||
const crossSpawn = require('cross-spawn');
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
@@ -56,89 +50,6 @@ const processes = {
|
|||||||
docker: null,
|
docker: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Sanitize a project name to be safe for use in shell commands and Docker image names.
|
|
||||||
* Converts to lowercase and removes any characters that aren't alphanumeric.
|
|
||||||
*/
|
|
||||||
function sanitizeProjectName(name) {
|
|
||||||
return name.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if Docker images need to be rebuilt based on Dockerfile or package.json changes
|
|
||||||
*/
|
|
||||||
function shouldRebuildDockerImages() {
|
|
||||||
try {
|
|
||||||
const dockerfilePath = path.join(__dirname, 'Dockerfile');
|
|
||||||
const packageJsonPath = path.join(__dirname, 'package.json');
|
|
||||||
|
|
||||||
// Get modification times of source files
|
|
||||||
const dockerfileMtime = statSync(dockerfilePath).mtimeMs;
|
|
||||||
const packageJsonMtime = statSync(packageJsonPath).mtimeMs;
|
|
||||||
const latestSourceMtime = Math.max(dockerfileMtime, packageJsonMtime);
|
|
||||||
|
|
||||||
// Get project name from docker-compose config, falling back to directory name
|
|
||||||
let projectName;
|
|
||||||
try {
|
|
||||||
const composeConfig = execSync('docker compose config --format json', {
|
|
||||||
encoding: 'utf-8',
|
|
||||||
cwd: __dirname,
|
|
||||||
});
|
|
||||||
const config = JSON.parse(composeConfig);
|
|
||||||
projectName = config.name;
|
|
||||||
} catch (error) {
|
|
||||||
// Fallback handled below
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sanitize project name (whether from config or fallback)
|
|
||||||
// This prevents command injection and ensures valid Docker image names
|
|
||||||
const sanitizedProjectName = sanitizeProjectName(
|
|
||||||
projectName || path.basename(__dirname)
|
|
||||||
);
|
|
||||||
const serverImageName = `${sanitizedProjectName}_server`;
|
|
||||||
const uiImageName = `${sanitizedProjectName}_ui`;
|
|
||||||
|
|
||||||
// Check if images exist and get their creation times
|
|
||||||
let needsRebuild = false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Check server image
|
|
||||||
const serverImageInfo = execSync(
|
|
||||||
`docker image inspect ${serverImageName} --format "{{.Created}}" 2>/dev/null || echo ""`,
|
|
||||||
{ encoding: 'utf-8', cwd: __dirname }
|
|
||||||
).trim();
|
|
||||||
|
|
||||||
// Check UI image
|
|
||||||
const uiImageInfo = execSync(
|
|
||||||
`docker image inspect ${uiImageName} --format "{{.Created}}" 2>/dev/null || echo ""`,
|
|
||||||
{ encoding: 'utf-8', cwd: __dirname }
|
|
||||||
).trim();
|
|
||||||
|
|
||||||
// If either image doesn't exist, we need to rebuild
|
|
||||||
if (!serverImageInfo || !uiImageInfo) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse image creation times (ISO 8601 format)
|
|
||||||
const serverCreated = new Date(serverImageInfo).getTime();
|
|
||||||
const uiCreated = new Date(uiImageInfo).getTime();
|
|
||||||
const oldestImageTime = Math.min(serverCreated, uiCreated);
|
|
||||||
|
|
||||||
// If source files are newer than images, rebuild
|
|
||||||
needsRebuild = latestSourceMtime > oldestImageTime;
|
|
||||||
} catch (error) {
|
|
||||||
// If images don't exist or inspect fails, rebuild
|
|
||||||
needsRebuild = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return needsRebuild;
|
|
||||||
} catch (error) {
|
|
||||||
// If we can't check, err on the side of rebuilding
|
|
||||||
log('Could not check Docker image status, will rebuild to be safe', 'yellow');
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build all production artifacts
|
* Build all production artifacts
|
||||||
*/
|
*/
|
||||||
@@ -315,47 +226,7 @@ async function main() {
|
|||||||
break;
|
break;
|
||||||
} else if (choice === '3') {
|
} else if (choice === '3') {
|
||||||
console.log('');
|
console.log('');
|
||||||
log('Launching Docker Container (Isolated Mode)...', 'blue');
|
await launchDockerContainers({ baseDir: __dirname, processes });
|
||||||
|
|
||||||
// Check if Dockerfile or package.json changed and rebuild if needed
|
|
||||||
const needsRebuild = shouldRebuildDockerImages();
|
|
||||||
const buildFlag = needsRebuild ? ['--build'] : [];
|
|
||||||
|
|
||||||
if (needsRebuild) {
|
|
||||||
log('Dockerfile or package.json changed - rebuilding images...', 'yellow');
|
|
||||||
} else {
|
|
||||||
log('Starting Docker containers...', 'yellow');
|
|
||||||
}
|
|
||||||
console.log('');
|
|
||||||
|
|
||||||
// Check if ANTHROPIC_API_KEY is set
|
|
||||||
if (!process.env.ANTHROPIC_API_KEY) {
|
|
||||||
log('Warning: ANTHROPIC_API_KEY environment variable is not set.', 'yellow');
|
|
||||||
log('The server will require an API key to function.', 'yellow');
|
|
||||||
log('Set it with: export ANTHROPIC_API_KEY=your-key', 'yellow');
|
|
||||||
console.log('');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start containers with docker-compose
|
|
||||||
// Will rebuild if Dockerfile or package.json changed
|
|
||||||
processes.docker = crossSpawn('docker', ['compose', 'up', ...buildFlag], {
|
|
||||||
stdio: 'inherit',
|
|
||||||
cwd: __dirname,
|
|
||||||
env: {
|
|
||||||
...process.env,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
log('Docker containers starting...', 'blue');
|
|
||||||
log('UI will be available at: http://localhost:3007', 'green');
|
|
||||||
log('API will be available at: http://localhost:3008', 'green');
|
|
||||||
console.log('');
|
|
||||||
log('Press Ctrl+C to stop the containers.', 'yellow');
|
|
||||||
|
|
||||||
await new Promise((resolve) => {
|
|
||||||
processes.docker.on('close', resolve);
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
log('Invalid choice. Please enter 1, 2, or 3.', 'red');
|
log('Invalid choice. Please enter 1, 2, or 3.', 'red');
|
||||||
|
|||||||
Reference in New Issue
Block a user