Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .cursor/environment.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"name": "nodejs-goof",
"install": "bash .cursor/install.sh",
"start": "bash .cursor/start.sh",
"terminals": [
{
"name": "goof",
"command": "npm start"
}
],
"ports": [
{
"name": "web",
"port": 3001
}
]
}
46 changes: 46 additions & 0 deletions .cursor/install.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env bash
# Idempotent dependency setup for the Goof Cloud Agent environment.
# Installs Docker (used to run the mongo:3 + mysql:5 databases the app needs),
# the fuse-overlayfs storage driver (the default overlayfs snapshotter fails in
# the nested Cloud Agent VM), and the Node.js dependencies. Best-effort pre-pull
# of the database images so a fresh boot starts quickly.
set -euo pipefail

cd "$(dirname "$0")/.."

export DEBIAN_FRONTEND=noninteractive

# --- System packages: Docker + fuse-overlayfs ---
if ! command -v docker >/dev/null 2>&1 || ! command -v fuse-overlayfs >/dev/null 2>&1; then
sudo apt-get update
sudo apt-get install -y docker.io fuse-overlayfs
fi

# The containerd/overlayfs snapshotter cannot extract images inside the nested
# Cloud Agent VM, so pin the classic graph driver backed by fuse-overlayfs.
sudo mkdir -p /etc/docker
echo '{"features":{"containerd-snapshotter":false},"storage-driver":"fuse-overlayfs"}' \
| sudo tee /etc/docker/daemon.json >/dev/null

# --- Node dependencies ---
npm install

# --- Best-effort pre-pull of database images (cached on disk for fast boots) ---
# Non-fatal: if the daemon cannot run during the build phase, start.sh pulls on boot.
STARTED_DOCKERD=0
if ! sudo docker info >/dev/null 2>&1; then
sudo rm -f /var/run/docker.pid
sudo setsid dockerd >/tmp/dockerd-install.log 2>&1 &
for _ in $(seq 1 30); do sudo docker info >/dev/null 2>&1 && break; sleep 1; done
STARTED_DOCKERD=1
fi
if sudo docker info >/dev/null 2>&1; then
sudo docker pull mongo:3 || true
sudo docker pull mysql:5 || true
fi
# Stop the daemon only if this script started it (leave a pre-existing one alone).
if [ "$STARTED_DOCKERD" = "1" ] && [ -f /var/run/docker.pid ]; then
sudo kill "$(cat /var/run/docker.pid)" 2>/dev/null || true
fi

echo "install.sh complete"
67 changes: 67 additions & 0 deletions .cursor/start.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#!/usr/bin/env bash
# Per-boot startup for the Goof Cloud Agent environment.
# Starts the Docker daemon (fuse-overlayfs) and the mongo:3 + mysql:5 database
# containers the app depends on, then waits until they accept connections.
# Idempotent: safe to run repeatedly; it will not create duplicate containers.
set -euo pipefail

wait_for_port() {
local host="$1" port="$2" name="$3" tries="${4:-60}"
for _ in $(seq 1 "$tries"); do
if (exec 3<>"/dev/tcp/${host}/${port}") 2>/dev/null; then
exec 3>&- 3<&- 2>/dev/null || true
echo "${name} is ready on ${host}:${port}"
return 0
fi
sleep 1
done
echo "WARNING: ${name} did not become ready on ${host}:${port}" >&2
return 1
}

# MySQL opens its TCP port while still initializing its data directory on first
# boot, so a bare port check is not enough. Wait until the server can actually
# answer queries before returning, otherwise the app connects too early, fails
# to seed the users table, and crashes on the first /users or /login request.
wait_for_mysql() {
local tries="${1:-120}"
for _ in $(seq 1 "$tries"); do
if sudo docker exec goof-mysql mysqladmin ping -uroot -proot --silent >/dev/null 2>&1; then
echo "MySQL is ready to serve queries"
return 0
fi
sleep 1
done
echo "WARNING: MySQL did not become query-ready in time" >&2
return 1
}

# --- Ensure the Docker daemon is running ---
if ! sudo docker info >/dev/null 2>&1; then
sudo rm -f /var/run/docker.pid
sudo setsid dockerd >/tmp/dockerd.log 2>&1 &
for _ in $(seq 1 30); do sudo docker info >/dev/null 2>&1 && break; sleep 1; done
fi

# --- MongoDB (mongo:3) ---
if ! sudo docker ps --format '{{.Names}}' | grep -qx 'goof-mongo'; then
sudo docker rm -f goof-mongo >/dev/null 2>&1 || true
sudo docker image inspect mongo:3 >/dev/null 2>&1 || sudo docker pull mongo:3
sudo docker run -d --name goof-mongo -p 27017:27017 mongo:3 >/dev/null
fi

# --- MySQL (mysql:5, for the TypeORM users table) ---
if ! sudo docker ps --format '{{.Names}}' | grep -qx 'goof-mysql'; then
sudo docker rm -f goof-mysql >/dev/null 2>&1 || true
sudo docker image inspect mysql:5 >/dev/null 2>&1 || sudo docker pull mysql:5
sudo docker run -d --name goof-mysql \
-e MYSQL_ROOT_PASSWORD=root -e MYSQL_DATABASE=acme \
-p 3306:3306 mysql:5 >/dev/null
fi

# --- Wait for the databases so the app connects cleanly on first launch ---
wait_for_port 127.0.0.1 27017 "MongoDB" 60 || true
wait_for_port 127.0.0.1 3306 "MySQL (port)" 120 || true
wait_for_mysql 180 || true

echo "start.sh complete"