mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-13 05:12:55 -07:00
feat(offload): address provisioning warnings and implement high-performance startup-script container
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* Universal Offload Cleanup (Local)
|
* Universal Offload Cleanup (Local)
|
||||||
*
|
*
|
||||||
* Cleans up tmux sessions and workspace on the GCE worker.
|
* Surgical or full cleanup of sessions and worktrees on the GCE worker.
|
||||||
*/
|
*/
|
||||||
import { spawnSync } from 'child_process';
|
import { spawnSync } from 'child_process';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
@@ -22,7 +22,10 @@ async function confirm(question: string): Promise<boolean> {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function runCleanup() {
|
export async function runCleanup(args: string[]) {
|
||||||
|
const prNumber = args[0];
|
||||||
|
const action = args[1];
|
||||||
|
|
||||||
const settingsPath = path.join(REPO_ROOT, '.gemini/settings.json');
|
const settingsPath = path.join(REPO_ROOT, '.gemini/settings.json');
|
||||||
if (!fs.existsSync(settingsPath)) {
|
if (!fs.existsSync(settingsPath)) {
|
||||||
console.error('❌ Settings not found. Run "npm run offload:setup" first.');
|
console.error('❌ Settings not found. Run "npm run offload:setup" first.');
|
||||||
@@ -37,32 +40,49 @@ export async function runCleanup() {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { projectId, zone } = config;
|
const { remoteHost } = config;
|
||||||
const targetVM = `gcli-offload-${process.env.USER || 'mattkorwel'}`;
|
|
||||||
|
|
||||||
console.log(`🧹 Starting cleanup for ${targetVM}...`);
|
if (prNumber && action) {
|
||||||
|
const sessionName = `offload-${prNumber}-${action}`;
|
||||||
|
const worktreePath = `~/dev/worktrees/${sessionName}`;
|
||||||
|
|
||||||
|
console.log(`🧹 Surgically removing session and worktree for ${prNumber}-${action}...`);
|
||||||
|
|
||||||
|
// Kill specific tmux session
|
||||||
|
spawnSync(`ssh ${remoteHost} "tmux kill-session -t ${sessionName} 2>/dev/null"`, { shell: true });
|
||||||
|
|
||||||
|
// Remove specific worktree
|
||||||
|
spawnSync(`ssh ${remoteHost} "cd ~/dev/main && git worktree remove -f ${worktreePath} 2>/dev/null"`, { shell: true });
|
||||||
|
spawnSync(`ssh ${remoteHost} "cd ~/dev/main && git worktree prune"`, { shell: true });
|
||||||
|
|
||||||
|
console.log(`✅ Cleaned up ${prNumber}-${action}.`);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Bulk Cleanup (Old Behavior) ---
|
||||||
|
console.log(`🧹 Starting BULK cleanup on ${remoteHost}...`);
|
||||||
|
|
||||||
// 1. Standard Cleanup
|
// 1. Standard Cleanup
|
||||||
console.log(' - Killing remote tmux sessions...');
|
console.log(' - Killing ALL remote tmux sessions...');
|
||||||
spawnSync(`ssh ${remoteHost} "tmux kill-server"`, { shell: true });
|
spawnSync(`ssh ${remoteHost} "tmux kill-server"`, { shell: true });
|
||||||
|
|
||||||
console.log(' - Cleaning up Git Worktrees...');
|
console.log(' - Cleaning up ALL Git Worktrees...');
|
||||||
spawnSync(`ssh ${remoteHost} "cd ~/dev/main && git worktree prune"`, { shell: true });
|
spawnSync(`ssh ${remoteHost} "cd ~/dev/main && git worktree prune"`, { shell: true });
|
||||||
spawnSync(`ssh ${remoteHost} "rm -rf ~/dev/worktrees/*"`, { shell: true });
|
spawnSync(`ssh ${remoteHost} "rm -rf ~/dev/worktrees/*"`, { shell: true });
|
||||||
|
|
||||||
console.log('✅ Remote environment cleared.');
|
console.log('✅ Remote environment cleared.');
|
||||||
|
|
||||||
// 2. Full Wipe Option
|
// 2. Full Wipe Option
|
||||||
const shouldWipe = await confirm('\nWould you like to COMPLETELY wipe the remote workspace directory?');
|
const shouldWipe = await confirm('\nWould you like to COMPLETELY wipe the remote workspace (main clone)?');
|
||||||
|
|
||||||
if (shouldWipe) {
|
if (shouldWipe) {
|
||||||
console.log(`🔥 Wiping ~/.offload/workspace...`);
|
console.log(`🔥 Wiping ~/dev/main...`);
|
||||||
spawnSync(`gcloud compute ssh ${targetVM} --project ${projectId} --zone ${zone} --command "rm -rf ~/.offload/workspace && mkdir -p ~/.offload/workspace"`, { stdio: 'inherit', shell: true });
|
spawnSync(`ssh ${remoteHost} "rm -rf ~/dev/main && mkdir -p ~/dev/main"`, { stdio: 'inherit', shell: true });
|
||||||
console.log('✅ Remote workspace wiped.');
|
console.log('✅ Remote hub wiped. You will need to run npm run offload:setup again.');
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (import.meta.url === `file://${process.argv[1]}`) {
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
||||||
runCleanup().catch(console.error);
|
runCleanup(process.argv.slice(2)).catch(console.error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,29 +41,47 @@ async function provisionWorker() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`🚀 Provisioning container-native offload worker: ${name}...`);
|
console.log(`🚀 Provisioning high-performance container worker: ${name}...`);
|
||||||
console.log(` - Image: ${imageUri}`);
|
console.log(` - Image: ${imageUri}`);
|
||||||
|
console.log(` - Disk: 200GB (High Performance)`);
|
||||||
|
|
||||||
|
// Use a startup script to run the container. This is the modern replacement
|
||||||
|
// for the deprecated create-with-container agent.
|
||||||
|
const startupScript = `#!/bin/bash
|
||||||
|
# Install Docker
|
||||||
|
apt-get update && apt-get install -y docker.io
|
||||||
|
|
||||||
|
# Authenticate to Artifact Registry
|
||||||
|
# (The VM Service Account must have Artifact Registry Reader permissions)
|
||||||
|
gcloud auth configure-docker us-docker.pkg.dev --quiet
|
||||||
|
|
||||||
|
# Pull and Run the maintainer container
|
||||||
|
docker pull ${imageUri}
|
||||||
|
docker run -d --name gemini-sandbox --restart always \\
|
||||||
|
-v /home/$(whoami)/dev:/home/node/dev:rw \\
|
||||||
|
-v /home/$(whoami)/.gemini:/home/node/.gemini:rw \\
|
||||||
|
-v /home/$(whoami)/.offload:/home/node/.offload:rw \\
|
||||||
|
${imageUri} /bin/bash -c "while true; do sleep 1000; done"
|
||||||
|
`;
|
||||||
|
|
||||||
const result = spawnSync('gcloud', [
|
const result = spawnSync('gcloud', [
|
||||||
'compute', 'instances', 'create-with-container', name,
|
'compute', 'instances', 'create', name,
|
||||||
'--project', PROJECT_ID,
|
'--project', PROJECT_ID,
|
||||||
'--zone', zone,
|
'--zone', zone,
|
||||||
'--machine-type', 'n2-standard-8',
|
'--machine-type', 'n2-standard-8',
|
||||||
'--container-image', imageUri,
|
'--image-family', 'ubuntu-2204-lts',
|
||||||
'--container-name', 'gemini-sandbox',
|
'--image-project', 'ubuntu-os-cloud',
|
||||||
'--container-restart-policy', 'always',
|
'--boot-disk-size', '200GB',
|
||||||
'--container-mount-host-path', 'host-path=/home/$(whoami)/dev,mount-path=/home/node/dev,mode=rw',
|
'--boot-disk-type', 'pd-balanced',
|
||||||
'--container-mount-host-path', 'host-path=/home/$(whoami)/.gemini,mount-path=/home/node/.gemini,mode=rw',
|
'--metadata', `startup-script=${startupScript}`,
|
||||||
'--container-mount-host-path', 'host-path=/home/$(whoami)/.offload,mount-path=/home/node/.offload,mode=rw',
|
|
||||||
'--boot-disk-size', '50GB',
|
|
||||||
'--labels', `owner=${USER.replace(/[^a-z0-9_-]/g, '_')},type=offload-worker`,
|
'--labels', `owner=${USER.replace(/[^a-z0-9_-]/g, '_')},type=offload-worker`,
|
||||||
'--tags', `gcli-offload-${USER}`,
|
'--tags', `gcli-offload-${USER}`,
|
||||||
'--scopes', 'https://www.googleapis.com/auth/cloud-platform'
|
'--scopes', 'https://www.googleapis.com/auth/cloud-platform'
|
||||||
], { stdio: 'inherit', shell: true });
|
], { stdio: 'inherit', shell: true });
|
||||||
|
|
||||||
if (result.status === 0) {
|
if (result.status === 0) {
|
||||||
console.log(`\n✅ Container worker ${name} is being provisioned.`);
|
console.log(`\n✅ Worker ${name} is being provisioned.`);
|
||||||
console.log(`👉 Access is managed via 'gcloud compute ssh --container'.`);
|
console.log(`👉 Container 'gemini-sandbox' will start automatically once booted.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,9 +55,10 @@ function getStatus() {
|
|||||||
|
|
||||||
console.log(`${pr.padEnd(10)} | ${action.padEnd(10)} | ${state.padEnd(12)} | ${id.padEnd(25)}`);
|
console.log(`${pr.padEnd(10)} | ${action.padEnd(10)} | ${state.padEnd(12)} | ${id.padEnd(25)}`);
|
||||||
if (state === '🏃 RUNNING') {
|
if (state === '🏃 RUNNING') {
|
||||||
console.log(` └─ Attach: npm run offload:attach ${pr} ${action} [--local]`);
|
console.log(` ├─ Attach: npm run offload:attach ${pr} ${action} [--local]`);
|
||||||
console.log(` └─ Logs: npm run offload:logs ${pr} ${action}`);
|
console.log(` ├─ Logs: npm run offload:logs ${pr} ${action}`);
|
||||||
}
|
}
|
||||||
|
console.log(` └─ Remove: npm run offload:remove ${pr} ${action}`);
|
||||||
});
|
});
|
||||||
console.log(''.padEnd(100, '-'));
|
console.log(''.padEnd(100, '-'));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,6 +71,7 @@
|
|||||||
"offload:status": "npm run offload:fleet status",
|
"offload:status": "npm run offload:fleet status",
|
||||||
"offload:attach": "tsx .gemini/skills/offload/scripts/attach.ts",
|
"offload:attach": "tsx .gemini/skills/offload/scripts/attach.ts",
|
||||||
"offload:logs": "tsx .gemini/skills/offload/scripts/logs.ts",
|
"offload:logs": "tsx .gemini/skills/offload/scripts/logs.ts",
|
||||||
|
"offload:remove": "tsx .gemini/skills/offload/scripts/clean.ts",
|
||||||
"pre-commit": "node scripts/pre-commit.js"
|
"pre-commit": "node scripts/pre-commit.js"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
|
|||||||
Reference in New Issue
Block a user