The old message just said 'expose manually if needed' with no guidance. Now it prints the exact docker commands to publish the port directly and how to find the container IP for a custom reverse proxy. https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
162 lines
6.6 KiB
Bash
Executable file
162 lines
6.6 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# HIY Build Engine
|
|
# Environment variables injected by hiy-server:
|
|
# APP_ID, APP_NAME, REPO_URL, BRANCH, PORT, ENV_FILE, SHA, BUILD_DIR
|
|
set -euo pipefail
|
|
|
|
log() { echo "[hiy] $*"; }
|
|
|
|
log "=== HostItYourself Build Engine ==="
|
|
log "App: $APP_NAME ($APP_ID)"
|
|
log "Repo: $REPO_URL"
|
|
log "Branch: $BRANCH"
|
|
log "Build dir: $BUILD_DIR"
|
|
|
|
# ── 1. Clone or pull ───────────────────────────────────────────────────────────
|
|
mkdir -p "$BUILD_DIR"
|
|
cd "$BUILD_DIR"
|
|
|
|
if [ -d ".git" ]; then
|
|
log "Updating existing clone…"
|
|
git fetch origin "$BRANCH" --depth=50
|
|
git checkout "$BRANCH"
|
|
git reset --hard "origin/$BRANCH"
|
|
else
|
|
log "Cloning repository…"
|
|
git clone --depth=50 --branch "$BRANCH" "$REPO_URL" .
|
|
fi
|
|
|
|
ACTUAL_SHA=$(git rev-parse HEAD)
|
|
log "SHA: $ACTUAL_SHA"
|
|
|
|
# ── 2. Detect build strategy ──────────────────────────────────────────────────
|
|
IMAGE_TAG="hiy/${APP_ID}:${ACTUAL_SHA}"
|
|
CONTAINER_NAME="hiy-${APP_ID}"
|
|
|
|
if [ -f "Dockerfile" ]; then
|
|
log "Strategy: Dockerfile"
|
|
docker build --tag "$IMAGE_TAG" .
|
|
|
|
elif [ -f "package.json" ] || [ -f "yarn.lock" ]; then
|
|
log "Strategy: Node.js (Cloud Native Buildpack)"
|
|
if ! command -v pack &>/dev/null; then
|
|
log "ERROR: 'pack' CLI not found. Install it: https://buildpacks.io/docs/tools/pack/"
|
|
exit 1
|
|
fi
|
|
pack build "$IMAGE_TAG" --builder paketobuildpacks/builder-jammy-base
|
|
|
|
elif [ -f "requirements.txt" ] || [ -f "pyproject.toml" ]; then
|
|
log "Strategy: Python (Cloud Native Buildpack)"
|
|
if ! command -v pack &>/dev/null; then
|
|
log "ERROR: 'pack' CLI not found. Install it: https://buildpacks.io/docs/tools/pack/"
|
|
exit 1
|
|
fi
|
|
pack build "$IMAGE_TAG" --builder paketobuildpacks/builder-jammy-base
|
|
|
|
elif [ -f "go.mod" ]; then
|
|
log "Strategy: Go (Cloud Native Buildpack)"
|
|
if ! command -v pack &>/dev/null; then
|
|
log "ERROR: 'pack' CLI not found. Install it: https://buildpacks.io/docs/tools/pack/"
|
|
exit 1
|
|
fi
|
|
pack build "$IMAGE_TAG" --builder paketobuildpacks/builder-jammy-base
|
|
|
|
elif [ -d "static" ] || [ -d "public" ]; then
|
|
STATIC_DIR="static"
|
|
[ -d "public" ] && STATIC_DIR="public"
|
|
log "Strategy: Static files (Caddy) from ./$STATIC_DIR"
|
|
cat > Dockerfile.hiy <<EOF
|
|
FROM caddy:2-alpine
|
|
COPY $STATIC_DIR /srv
|
|
EOF
|
|
docker build --file Dockerfile.hiy --tag "$IMAGE_TAG" .
|
|
rm -f Dockerfile.hiy
|
|
|
|
else
|
|
log "ERROR: Could not detect build strategy."
|
|
log "Add a Dockerfile, package.json, requirements.txt, go.mod, or a static/ directory."
|
|
exit 1
|
|
fi
|
|
|
|
# ── 3. Ensure Docker network ───────────────────────────────────────────────────
|
|
docker network create hiy-net 2>/dev/null || true
|
|
|
|
# ── 4. Stop & remove previous container ───────────────────────────────────────
|
|
if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
|
log "Stopping old container…"
|
|
docker stop "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
|
docker rm "$CONTAINER_NAME" >/dev/null 2>&1 || true
|
|
fi
|
|
|
|
# ── 5. Start new container ────────────────────────────────────────────────────
|
|
log "Starting container ${CONTAINER_NAME}…"
|
|
ENV_FILE_ARG=()
|
|
if [ -n "${ENV_FILE:-}" ] && [ -f "$ENV_FILE" ]; then
|
|
ENV_FILE_ARG=(--env-file "$ENV_FILE")
|
|
else
|
|
log "No env file found at '${ENV_FILE:-}'; starting without one."
|
|
fi
|
|
docker run --detach \
|
|
--name "$CONTAINER_NAME" \
|
|
--network hiy-net \
|
|
"${ENV_FILE_ARG[@]+"${ENV_FILE_ARG[@]}"}" \
|
|
--expose "$PORT" \
|
|
--label "hiy.app=${APP_ID}" \
|
|
--label "hiy.port=${PORT}" \
|
|
--restart unless-stopped \
|
|
--memory="512m" \
|
|
--cpus="0.5" \
|
|
"$IMAGE_TAG"
|
|
|
|
# ── 6. Update Caddy via its admin API ─────────────────────────────────────────
|
|
CADDY_API="${CADDY_API_URL:-http://localhost:2019}"
|
|
DOMAIN_SUFFIX="${DOMAIN_SUFFIX:-localhost}"
|
|
|
|
if curl --silent --fail "${CADDY_API}/config/" >/dev/null 2>&1; then
|
|
CONTAINER_IP=$(docker inspect "$CONTAINER_NAME" \
|
|
--format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}')
|
|
UPSTREAM="${CONTAINER_IP}:${PORT}"
|
|
log "Updating Caddy: ${APP_ID}.${DOMAIN_SUFFIX} → ${UPSTREAM}"
|
|
|
|
ROUTE_JSON=$(cat <<EOF
|
|
{
|
|
"match": [{"host": ["${APP_ID}.${DOMAIN_SUFFIX}"]}],
|
|
"handle": [{"handler": "reverse_proxy", "upstreams": [{"dial": "${UPSTREAM}"}]}]
|
|
}
|
|
EOF
|
|
)
|
|
# Upsert the route for this app.
|
|
ROUTES=$(curl --silent "${CADDY_API}/config/apps/http/servers/hiy/routes" 2>/dev/null || echo "[]")
|
|
# Remove existing route for the same host, then append the new one.
|
|
UPDATED=$(echo "$ROUTES" | python3 -c "
|
|
import sys, json
|
|
routes = json.load(sys.stdin)
|
|
new_host = '${APP_ID}.${DOMAIN_SUFFIX}'
|
|
routes = [r for r in routes if new_host not in r.get('match',[{}])[0].get('host',[])]
|
|
routes.append(json.loads(sys.argv[1]))
|
|
print(json.dumps(routes))
|
|
" "$ROUTE_JSON")
|
|
|
|
curl --silent --fail "${CADDY_API}/config/apps/http/servers/hiy/routes" \
|
|
--header "Content-Type: application/json" \
|
|
--request PUT \
|
|
--data "$UPDATED" && log "Caddy updated." \
|
|
|| log "WARNING: Caddy update failed (app is running; fix routing manually)."
|
|
else
|
|
log "Caddy admin API not reachable; skipping route update."
|
|
log "Container ${CONTAINER_NAME} is running on port ${PORT} but not publicly routed."
|
|
log "To reach it directly, re-run the container with a published port:"
|
|
log " docker rm -f ${CONTAINER_NAME}"
|
|
log " docker run -d --name ${CONTAINER_NAME} -p ${PORT}:${PORT} ${IMAGE_TAG}"
|
|
log "Or point any reverse proxy at: \$(docker inspect ${CONTAINER_NAME} --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'):${PORT}"
|
|
fi
|
|
|
|
# ── 7. Prune old images ───────────────────────────────────────────────────────
|
|
log "Pruning old images (keeping last 3)…"
|
|
docker images "hiy/${APP_ID}" --format "{{.ID}}\t{{.CreatedAt}}" \
|
|
| sort --reverse --key=2 \
|
|
| tail -n +4 \
|
|
| awk '{print $1}' \
|
|
| xargs --no-run-if-empty docker rmi 2>/dev/null || true
|
|
|
|
log "=== Build complete: $APP_NAME @ $ACTUAL_SHA ==="
|