feat(digitalocean): Packer nightly snapshot pipeline for fast boot (#2198)

* feat(digitalocean): Packer nightly snapshot pipeline for fast boot

Add pre-built Packer snapshots for DigitalOcean droplets. Instead of
10-20 min cloud-init + agent install on every boot, snapshot-based
droplets boot in ~2-3 min (SSH only, agent pre-installed).

- Packer HCL2 template with parametrized agent/tier builds
- Agent build matrix (packer/agents.json) for all 7 agents
- Tier scripts mirroring cloud-init.ts package tiers
- Nightly GitHub Actions workflow (4 AM UTC, max-parallel: 3)
- Automatic cleanup: keeps only latest snapshot per agent
- CLI: findSpawnSnapshot() looks up pre-built images via DO API
- CLI: waitForSshOnly() skips cloud-init when using snapshots
- CLI: createServer() accepts optional snapshotId, skips user_data
- CLI: main.ts routes to fast path when snapshot detected
- Tests for findSpawnSnapshot() (5 cases, all passing)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(packer): use var-file for install_commands to avoid shell quoting issues

The previous approach passed install_commands as `-var` inline, but
GitHub Actions expands `${{ }}` before shell evaluation — JSON arrays
with `|`, `&&`, and `"` characters break shell quoting.

Fix: generate a `.auto.pkrvars.json` file (auto-loaded by Packer)
using jq with --argjson for safe JSON handling. Also route all
`${{ inputs }}` and `${{ matrix }}` values through env vars to
prevent script injection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ahmed Abushagur 2026-03-04 20:47:46 -08:00 committed by GitHub
parent 3242fa78f1
commit ed98a59318
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 546 additions and 10 deletions

45
packer/agents.json Normal file
View file

@ -0,0 +1,45 @@
{
"claude": {
"tier": "minimal",
"install": [
"curl -fsSL https://claude.ai/install.sh | bash || npm install -g @anthropic-ai/claude-code"
]
},
"codex": {
"tier": "node",
"install": [
"npm install -g @openai/codex"
]
},
"openclaw": {
"tier": "full",
"install": [
"npm install -g openclaw"
]
},
"opencode": {
"tier": "minimal",
"install": [
"curl -fsSL https://opencode.ai/install | bash"
]
},
"kilocode": {
"tier": "node",
"install": [
"npm install -g @kilocode/cli"
]
},
"zeroclaw": {
"tier": "minimal",
"install": [
"fallocate -l 4G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile",
"curl -LsSf https://raw.githubusercontent.com/zeroclaw-labs/zeroclaw/a117be64fdaa31779204beadf2942c8aef57d0e5/scripts/bootstrap.sh | bash -s -- --install-rust --install-system-deps --prefer-prebuilt"
]
},
"hermes": {
"tier": "minimal",
"install": [
"curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash"
]
}
}