Skip to content
This repository was archived by the owner on May 21, 2026. It is now read-only.

Commit 40845b5

Browse files
Add deploy script, CI integration and docs
Replace direct docker pull/run steps in CI with a robust deploy script (downloaded to /opt/espacogeek/deploy.sh) that handles backup, rename, image pull, container start, health checks, rollback, cleanup and logging. Update .github/workflows/cicd.yml to use script_stop: all, create .env.espacogeek, login to GHCR and execute the deploy script. Add docker/deploy.sh and docker/diagnose.sh and comprehensive documentation files (DEPLOYMENT_SUMMARY.md, INDEX.md, VALIDATION.md and multiple docker/*.md) describing architecture, setup, testing and quick references.
1 parent 79cd404 commit 40845b5

12 files changed

Lines changed: 4087 additions & 24 deletions

.github/workflows/cicd.yml

Lines changed: 224 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -208,10 +208,229 @@ jobs:
208208
key: ${{ secrets.HOSTINGER }}
209209
port: ${{ secrets.HOSTINGER_PORT }}
210210
envs: GHCR_OWNER_LC,APP_NAME
211+
script_stop: all
211212
script: |
212-
set -e
213-
echo "Starting deployment with image: ghcr.io/${GHCR_OWNER_LC}/${APP_NAME}:latest"
213+
# Download and setup deploy script if not present
214+
DEPLOY_SCRIPT="/opt/espacogeek/deploy.sh"
215+
216+
if [ ! -f "$DEPLOY_SCRIPT" ]; then
217+
echo "Downloading deploy script..."
218+
mkdir -p "$(dirname "$DEPLOY_SCRIPT")"
219+
# You can download from a specific release or use inline
220+
cat > "$DEPLOY_SCRIPT" << 'DEPLOY_SCRIPT_EOF'
221+
#!/bin/bash
222+
223+
set -euo pipefail
224+
225+
RED='\033[0;31m'
226+
GREEN='\033[0;32m'
227+
YELLOW='\033[1;33m'
228+
BLUE='\033[0;34m'
229+
NC='\033[0m'
230+
231+
log_info() { echo -e "${BLUE}[INFO]${NC} $*"; }
232+
log_success() { echo -e "${GREEN}[✓]${NC} $*"; }
233+
log_warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
234+
log_error() { echo -e "${RED}[✗]${NC} $*"; }
235+
236+
trap 'on_error' ERR
237+
on_error() {
238+
local exit_code=$?
239+
if [ $exit_code -ne 0 ]; then
240+
log_error "Deployment failed with exit code $exit_code"
241+
log_warn "Attempting rollback..."
242+
rollback_deployment
243+
fi
244+
}
245+
246+
GHCR_OWNER_LC="${1:-}"
247+
APP_NAME="${2:-}"
248+
IMAGE_TAG="${3:-latest}"
249+
ENV_FILE="${4:-.env.espacogeek}"
250+
CONTAINER_NAME="espacogeek"
251+
BACKUP_DIR="${HOME}/espacogeek-backups"
252+
OLD_CONTAINER_BACKUP="${CONTAINER_NAME}-old"
253+
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
254+
255+
if [ -z "$GHCR_OWNER_LC" ] || [ -z "$APP_NAME" ]; then
256+
log_error "Usage: $0 <GHCR_OWNER_LC> <APP_NAME> [IMAGE_TAG] [ENV_FILE]"
257+
exit 1
258+
fi
259+
260+
IMAGE="ghcr.io/${GHCR_OWNER_LC}/${APP_NAME}:${IMAGE_TAG}"
261+
262+
container_exists() { docker ps -a --format '{{.Names}}' | grep -q "^${1}$"; }
263+
container_running() { docker ps --format '{{.Names}}' | grep -q "^${1}$"; }
264+
265+
backup_old_container() {
266+
if ! container_exists "$CONTAINER_NAME"; then
267+
log_info "No existing container to backup"
268+
return 0
269+
fi
270+
log_info "Creating backup of old container..."
271+
mkdir -p "$BACKUP_DIR"
272+
BACKUP_FILE="${BACKUP_DIR}/${CONTAINER_NAME}_backup_${TIMESTAMP}.tar"
273+
log_info "Exporting container to ${BACKUP_FILE}..."
274+
if docker export "$CONTAINER_NAME" > "$BACKUP_FILE"; then
275+
log_success "Container backup created: ${BACKUP_FILE}"
276+
else
277+
log_error "Failed to backup container"
278+
return 1
279+
fi
280+
ls -t "${BACKUP_DIR}/${CONTAINER_NAME}_backup_"*.tar 2>/dev/null | tail -n +6 | xargs rm -f 2>/dev/null || true
281+
}
282+
283+
rename_old_container() {
284+
if ! container_exists "$CONTAINER_NAME"; then
285+
log_info "No old container to rename"
286+
return 0
287+
fi
288+
log_info "Renaming old container..."
289+
if container_running "$CONTAINER_NAME"; then
290+
log_info "Stopping old container..."
291+
docker stop "$CONTAINER_NAME" || log_warn "Failed to stop container"
292+
fi
293+
if docker rename "$CONTAINER_NAME" "${OLD_CONTAINER_BACKUP}" 2>/dev/null; then
294+
log_success "Old container renamed to ${OLD_CONTAINER_BACKUP}"
295+
else
296+
log_warn "Could not rename old container"
297+
fi
298+
}
299+
300+
pull_new_image() {
301+
log_info "Pulling new image: ${IMAGE}..."
302+
if docker pull "$IMAGE"; then
303+
log_success "Image pulled successfully"
304+
return 0
305+
else
306+
log_error "Failed to pull image"
307+
return 1
308+
fi
309+
}
310+
311+
start_new_container() {
312+
log_info "Starting new container..."
313+
if ! docker run -d --name "$CONTAINER_NAME" -p 8080:8080 --restart unless-stopped --env-file "$ENV_FILE" "$IMAGE"; then
314+
log_error "Failed to start container"
315+
return 1
316+
fi
317+
log_success "Container started with ID: $(docker ps --filter name=$CONTAINER_NAME -q)"
318+
}
319+
320+
validate_container_health() {
321+
local max_attempts=30
322+
local attempt=1
323+
log_info "Validating container health (max ${max_attempts} attempts)..."
324+
while [ $attempt -le $max_attempts ]; do
325+
if container_running "$CONTAINER_NAME"; then
326+
local status=$(docker inspect "$CONTAINER_NAME" --format='{{.State.Status}}')
327+
if [ "$status" = "running" ]; then
328+
if docker exec "$CONTAINER_NAME" wget -q -O- http://localhost:8080/actuator/health &>/dev/null 2>&1 || docker exec "$CONTAINER_NAME" curl -s http://localhost:8080/actuator/health &>/dev/null 2>&1; then
329+
log_success "Container is healthy"
330+
return 0
331+
fi
332+
else
333+
log_warn "Container status is: $status"
334+
fi
335+
fi
336+
log_info "Waiting... (attempt ${attempt}/${max_attempts})"
337+
sleep 2
338+
attempt=$((attempt + 1))
339+
done
340+
log_error "Container failed health check"
341+
return 1
342+
}
343+
344+
cleanup_old_container() {
345+
log_info "Cleaning up old container..."
346+
if container_exists "${OLD_CONTAINER_BACKUP}"; then
347+
log_info "Removing backed-up container: ${OLD_CONTAINER_BACKUP}"
348+
if docker rm -f "${OLD_CONTAINER_BACKUP}"; then
349+
log_success "Old container removed"
350+
else
351+
log_warn "Could not remove old container"
352+
fi
353+
fi
354+
}
355+
356+
rollback_deployment() {
357+
log_warn "Starting rollback procedure..."
358+
if container_exists "$CONTAINER_NAME"; then
359+
log_info "Stopping new container..."
360+
docker stop "$CONTAINER_NAME" 2>/dev/null || true
361+
docker rm "$CONTAINER_NAME" 2>/dev/null || true
362+
fi
363+
if container_exists "${OLD_CONTAINER_BACKUP}"; then
364+
log_info "Restoring old container from backup: ${OLD_CONTAINER_BACKUP}"
365+
if docker rename "${OLD_CONTAINER_BACKUP}" "$CONTAINER_NAME" 2>/dev/null; then
366+
if docker start "$CONTAINER_NAME"; then
367+
log_success "Rollback successful: Old container restored"
368+
return 0
369+
else
370+
log_error "Failed to start rolled-back container"
371+
return 1
372+
fi
373+
else
374+
log_error "Failed to rename backed-up container"
375+
return 1
376+
fi
377+
else
378+
log_error "No backed-up container available for rollback"
379+
return 1
380+
fi
381+
}
382+
383+
show_container_status() {
384+
log_info "=== Container Status ==="
385+
docker ps -a --filter name="$CONTAINER_NAME" --filter name="${OLD_CONTAINER_BACKUP}" || log_info "No containers found"
386+
log_info "======================="
387+
}
388+
389+
show_container_logs() {
390+
log_info "=== Container Logs (last 20 lines) ==="
391+
if container_exists "$CONTAINER_NAME"; then
392+
docker logs --tail 20 "$CONTAINER_NAME" || log_info "No logs available"
393+
fi
394+
log_info "======================================="
395+
}
396+
397+
log_info "Starting deployment of ${APP_NAME}:${IMAGE_TAG}"
398+
log_info "Container name: ${CONTAINER_NAME}"
399+
log_info "Image: ${IMAGE}"
400+
log_info "Environment file: ${ENV_FILE}"
401+
log_info ""
402+
403+
if [ ! -f "$ENV_FILE" ]; then
404+
log_error "Environment file not found: ${ENV_FILE}"
405+
exit 1
406+
fi
407+
log_success "Environment file found"
408+
409+
backup_old_container || exit 1
410+
rename_old_container || exit 1
411+
pull_new_image || exit 1
412+
start_new_container || exit 1
214413
414+
if ! validate_container_health; then
415+
log_error "Health check failed, initiating rollback..."
416+
rollback_deployment || exit 1
417+
exit 1
418+
fi
419+
420+
cleanup_old_container
421+
422+
log_success ""
423+
log_success "=== DEPLOYMENT SUCCESSFUL ==="
424+
show_container_status
425+
show_container_logs
426+
log_success "============================="
427+
428+
rm -f "$ENV_FILE"
429+
DEPLOY_SCRIPT_EOF
430+
chmod +x "$DEPLOY_SCRIPT"
431+
fi
432+
433+
# Create environment file
215434
cat > .env.espacogeek << 'ENVEOF'
216435
SPRING_DATASOURCE_URL=${{ secrets.SPRING_DATASOURCE_URL }}
217436
SPRING_DATASOURCE_USERNAME=${{ secrets.SPRING_DATASOURCE_USERNAME }}
@@ -229,31 +448,12 @@ jobs:
229448
FRONTEND_URL=${{ secrets.FRONTEND_URL }}
230449
ENVEOF
231450
451+
# Login to GHCR
232452
echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io -u "${{ secrets.GHCR_USER }}" --password-stdin
233453
234-
echo "Pulling image from GHCR..."
235-
docker pull ghcr.io/${GHCR_OWNER_LC}/${APP_NAME}:latest || { echo "Failed to pull image"; exit 1; }
236-
237-
echo "Stopping and removing old container..."
238-
docker stop espacogeek || true
239-
docker rm espacogeek || true
240-
241-
echo "Starting new container..."
242-
if ! docker run -d --name espacogeek \
243-
-p 8080:8080 \
244-
--restart unless-stopped \
245-
--env-file .env.espacogeek \
246-
ghcr.io/${GHCR_OWNER_LC}/${APP_NAME}:latest; then
247-
echo "Failed to start container"
248-
docker ps -a
249-
docker logs espacogeek || true
250-
rm -f .env.espacogeek
251-
exit 1
252-
fi
454+
# Execute deployment script
455+
"$DEPLOY_SCRIPT" "${GHCR_OWNER_LC}" "${APP_NAME}" "latest" ".env.espacogeek"
253456
254-
echo "Container started successfully"
255-
docker ps -a
256-
rm -f .env.espacogeek
257457
258458
# Final summary with generated tags
259459
- name: Summary

0 commit comments

Comments
 (0)