feat(offload): address provisioning warnings and implement high-performance startup-script container

This commit is contained in:
mkorwel
2026-03-15 12:32:22 -07:00
parent b5c1b9f77b
commit edc8bfdfe4
4 changed files with 65 additions and 25 deletions
+32 -12
View File
@@ -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);
} }
+29 -11
View File
@@ -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.`);
} }
} }
+3 -2
View File
@@ -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, '-'));
} }
+1
View File
@@ -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": {