Monorepo for Aesthetic.Computer aesthetic.computer
at main 134 lines 5.7 kB view raw
1import { execSync } from 'child_process'; 2import fs from 'fs-extra'; 3import path from 'path'; 4import { fileURLToPath } from 'url'; 5 6// --- Configuration --- 7// Read GCP Project ID from nanos config file 8const __filename = fileURLToPath(import.meta.url); 9const __dirname = path.dirname(__filename); 10const nanosGcpConfigPath = path.resolve(__dirname, '../nanos/config-gcp.json'); 11let GCLOUD_PROJECT; 12try { 13 const nanosConfig = fs.readJsonSync(nanosGcpConfigPath); 14 GCLOUD_PROJECT = nanosConfig.CloudConfig.ProjectID; 15 if (!GCLOUD_PROJECT) { 16 throw new Error('ProjectID not found in nanos/config-gcp.json'); 17 } 18} catch (error) { 19 console.error(`Error reading GCLOUD_PROJECT from ${nanosGcpConfigPath}:`, error); 20 process.exit(1); 21} 22 23// Region for Cloud Run deployment 24const GCLOUD_REGION = 'us-west1'; 25// Name for the Cloud Run service 26const SERVICE_NAME = 'remote-chromium'; 27// Docker image name (without the registry path) 28const IMAGE_NAME = 'remote-chromium'; 29// Path to the Dockerfile directory 30const DOCKERFILE_DIR = path.resolve(__dirname); 31// GCP Artifact Registry repository name (replace if you have a specific one) 32const ARTIFACT_REGISTRY_REPO = 'docker-repo'; // A common default, or choose your own 33 34// Construct the full image URI for Artifact Registry 35const IMAGE_URI = `${GCLOUD_REGION}-docker.pkg.dev/${GCLOUD_PROJECT}/${ARTIFACT_REGISTRY_REPO}/${IMAGE_NAME}:latest`; 36 37// --- Helper Functions --- 38function runCommand(command, errorMessage) { 39 try { 40 console.log(`Executing: ${command}`); 41 const output = execSync(command, { stdio: 'inherit' }); 42 console.log(output ? output.toString() : ''); 43 } catch (error) { 44 console.error(`Error ${errorMessage}:`, error.stderr ? error.stderr.toString() : error.message); 45 process.exit(1); 46 } 47} 48 49async function ensureGcloudAuthenticated() { 50 try { 51 execSync('gcloud auth print-access-token', { stdio: 'ignore' }); 52 console.log('gcloud is already authenticated.'); 53 } catch (error) { 54 console.log('gcloud authentication required. Please log in.'); 55 runCommand('gcloud auth login', 'authenticating with gcloud'); 56 runCommand('gcloud auth application-default login', 'setting up application default credentials'); 57 } 58} 59 60async function configureDockerForGCR() { 61 console.log('Configuring Docker to use gcloud as a credential helper...'); 62 runCommand(`gcloud auth configure-docker ${GCLOUD_REGION}-docker.pkg.dev`, 'configuring Docker for Artifact Registry'); 63} 64 65// --- Main Orchestration Steps --- 66async function main() { 67 const mode = process.argv[2]; // Get the mode from command line arguments 68 69 if (mode === 'dev') { 70 console.log('Starting local development environment...'); 71 // 1. Build the Docker image for local development 72 const localImageName = `${IMAGE_NAME}:local`; 73 console.log(`Building Docker image: ${localImageName}`); 74 runCommand(`docker build -t ${localImageName} ${DOCKERFILE_DIR}`, 'building Docker image for local dev'); 75 76 // 2. Run the Docker container locally 77 console.log(`Running Docker container ${localImageName} locally on port 9222...`); 78 // Ensure no other container is using port 9222 or use a different port 79 runCommand(`docker run --rm -p 9222:9222 ${localImageName}`, 'running local Docker container'); 80 console.log(`Local Chromium instance should be accessible for remote debugging on ws://localhost:9222`); 81 82 } else if (mode === 'deploy') { 83 console.log('Starting Chromium deployment to GCP Cloud Run...'); 84 85 // 0. Ensure gcloud is authenticated and Docker is configured 86 await ensureGcloudAuthenticated(); 87 await configureDockerForGCR(); 88 89 // 1. Build the Docker image 90 console.log(`Building Docker image: ${IMAGE_URI}`); 91 runCommand(`docker build -t ${IMAGE_URI} ${DOCKERFILE_DIR}`, 'building Docker image'); 92 93 // 2. Push the Docker image to GCP Artifact Registry 94 // Ensure the Artifact Registry repository exists. 95 // You might need to create it first: 96 // gcloud artifacts repositories create ${ARTIFACT_REGISTRY_REPO} --repository-format=docker --location=${GCLOUD_REGION} --description="Docker repository" 97 console.log(`Pushing image to Artifact Registry: ${IMAGE_URI}`); 98 runCommand(`docker push ${IMAGE_URI}`, 'pushing image to Artifact Registry'); 99 100 // 3. Deploy to Cloud Run 101 console.log(`Deploying ${SERVICE_NAME} to Cloud Run in ${GCLOUD_REGION}...`); 102 const deployCommand = `gcloud run deploy ${SERVICE_NAME} \ 103 --image ${IMAGE_URI} \ 104 --platform managed \ 105 --region ${GCLOUD_REGION} \ 106 --port 9222 \ 107 --allow-unauthenticated \ 108 --project ${GCLOUD_PROJECT}`; 109 // Add --set-env-vars or other configurations as needed 110 // For example, to ensure it uses a specific user data dir if needed by screenshot.mjs interaction: 111 // --set-env-vars="USER_DATA_DIR=/tmp/chromium-user-data-cloudrun" 112 113 runCommand(deployCommand, 'deploying to Cloud Run'); 114 115 console.log('Fetching service URL...'); 116 runCommand(`gcloud run services describe ${SERVICE_NAME} --platform managed --region ${GCLOUD_REGION} --format='value(status.url)' --project ${GCLOUD_PROJECT}`, 'fetching service URL'); 117 118 console.log( 119 `\nDeployment complete! Your remote Chromium instance should be accessible at the URL printed above.` 120 ); 121 console.log( 122 `You can connect to it via WebSocket for screenshot.mjs using the wss:// version of the host (port will be 443 implicitly).` 123 ); 124 } else { 125 console.error('Invalid mode. Please use "dev" or "deploy".'); 126 console.log('Usage: node conductor.mjs [dev|deploy]'); 127 process.exit(1); 128 } 129} 130 131main().catch(error => { 132 console.error('Deployment script failed:', error); 133 process.exit(1); 134});