186 lines
7.2 KiB
YAML
186 lines
7.2 KiB
YAML
name: 'Deploy to Docker (Swarm or Compose)'
|
|
description: |
|
|
Robustly deploys a processed Docker Compose file either to Docker Swarm or regular Docker Compose via SSH.
|
|
Includes detailed debug output, error handling, and cleanup.
|
|
|
|
inputs:
|
|
stack_name:
|
|
description: 'Docker stack or compose project name'
|
|
required: true
|
|
ssh_host:
|
|
description: 'SSH host'
|
|
required: true
|
|
ssh_user:
|
|
description: 'SSH username'
|
|
required: true
|
|
ssh_key:
|
|
description: 'SSH private key'
|
|
required: true
|
|
deploy_file:
|
|
description: 'Path to the processed deployment file'
|
|
required: true
|
|
deploy_mode:
|
|
description: 'Deployment mode: swarm or compose'
|
|
required: false
|
|
default: 'swarm'
|
|
remote_temp_dir:
|
|
description: 'Remote temporary directory for deployment files'
|
|
required: false
|
|
default: '/tmp'
|
|
copy_build_context:
|
|
description: 'Whether to copy Docker build context (Dockerfile and related files) to remote host'
|
|
required: false
|
|
default: 'false'
|
|
build_context_path:
|
|
description: 'Local path to Docker build context (directory containing Dockerfile). Defaults to ./build-context'
|
|
required: false
|
|
default: 'build-context'
|
|
copy_env_file:
|
|
description: 'Whether to copy .env file to remote host'
|
|
required: false
|
|
default: 'false'
|
|
env_file_path:
|
|
description: 'Local path to .env file'
|
|
required: false
|
|
default: '.env'
|
|
docker_registry:
|
|
description: 'Docker registry URL (e.g., ghcr.io). Defaults to Docker Hub if empty.'
|
|
required: false
|
|
default: ''
|
|
docker_username:
|
|
description: 'Docker registry username'
|
|
required: false
|
|
default: ''
|
|
docker_password:
|
|
description: 'Docker registry password or token'
|
|
required: false
|
|
default: ''
|
|
|
|
|
|
runs:
|
|
using: 'composite'
|
|
steps:
|
|
- id: deploy
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
STACK_NAME="${{ inputs.stack_name }}"
|
|
SSH_HOST="${{ inputs.ssh_host }}"
|
|
SSH_USER="${{ inputs.ssh_user }}"
|
|
DEPLOY_FILE="${{ inputs.deploy_file }}"
|
|
DEPLOY_MODE="${{ inputs.deploy_mode }}"
|
|
REMOTE_TEMP_DIR="${{ inputs.remote_temp_dir }}/$STACK_NAME"
|
|
COPY_BUILD_CONTEXT="${{ inputs.copy_build_context }}"
|
|
BUILD_CONTEXT_PATH="${{ inputs.build_context_path }}"
|
|
DOCKER_REGISTRY="${{ inputs.docker_registry }}"
|
|
DOCKER_USERNAME="${{ inputs.docker_username }}"
|
|
DOCKER_PASSWORD="${{ inputs.docker_password }}"
|
|
|
|
echo "🚀 Starting deployment of '$STACK_NAME' to host '$SSH_HOST' using mode '$DEPLOY_MODE'"
|
|
|
|
# Validate deploy_mode input
|
|
if [[ "$DEPLOY_MODE" != "swarm" && "$DEPLOY_MODE" != "compose" ]]; then
|
|
echo "❌ ERROR: Invalid deploy_mode '$DEPLOY_MODE'. Must be 'swarm' or 'compose'."
|
|
exit 1
|
|
fi
|
|
|
|
# Check if deployment file exists locally
|
|
if [ ! -f "$DEPLOY_FILE" ]; then
|
|
echo "❌ ERROR: Deployment file '$DEPLOY_FILE' does not exist."
|
|
exit 1
|
|
fi
|
|
|
|
# Create temporary SSH key file
|
|
SSH_KEY_FILE=$(mktemp)
|
|
echo "${{ inputs.ssh_key }}" > "$SSH_KEY_FILE"
|
|
chmod 600 "$SSH_KEY_FILE"
|
|
echo "🔑 DEBUG: Temporary SSH key created at '$SSH_KEY_FILE'"
|
|
|
|
# Ensure remote directory exists
|
|
echo "📁 DEBUG: Creating remote directory '$REMOTE_TEMP_DIR'"
|
|
ssh -o StrictHostKeyChecking=no -i "$SSH_KEY_FILE" \
|
|
"$SSH_USER@$SSH_HOST" \
|
|
"mkdir -p '$REMOTE_TEMP_DIR' && chmod 700 '$REMOTE_TEMP_DIR'"
|
|
|
|
# Docker Login (if credentials provided)
|
|
if [[ -n "$DOCKER_USERNAME" && -n "$DOCKER_PASSWORD" ]]; then
|
|
echo "🔐 DEBUG: Performing Docker login..."
|
|
LOGIN_CMD="echo '$DOCKER_PASSWORD' | docker login -u '$DOCKER_USERNAME' --password-stdin"
|
|
if [[ -n "$DOCKER_REGISTRY" ]]; then
|
|
LOGIN_CMD="$LOGIN_CMD $DOCKER_REGISTRY"
|
|
fi
|
|
|
|
ssh -o StrictHostKeyChecking=no -i "$SSH_KEY_FILE" \
|
|
"$SSH_USER@$SSH_HOST" \
|
|
"$LOGIN_CMD"
|
|
echo "✅ DEBUG: Docker login successful"
|
|
fi
|
|
|
|
# Copy deployment file to remote host
|
|
echo "📤 DEBUG: Copying deployment file '$DEPLOY_FILE' to remote host at '$REMOTE_TEMP_DIR/docker-compose.yml'"
|
|
scp -o StrictHostKeyChecking=no -i "$SSH_KEY_FILE" \
|
|
"$DEPLOY_FILE" \
|
|
"$SSH_USER@$SSH_HOST:$REMOTE_TEMP_DIR/docker-compose.yml"
|
|
|
|
# Optionally copy Docker build context
|
|
if [ "$COPY_BUILD_CONTEXT" == "true" ]; then
|
|
echo "📂 DEBUG: Copying Docker build context from '$BUILD_CONTEXT_PATH' to remote host"
|
|
scp -o StrictHostKeyChecking=no -i "$SSH_KEY_FILE" -r \
|
|
"$BUILD_CONTEXT_PATH"/* \
|
|
"$SSH_USER@$SSH_HOST:$REMOTE_TEMP_DIR/"
|
|
echo "📋 DEBUG: Listing remote build context directory '$REMOTE_TEMP_DIR'"
|
|
ssh -o StrictHostKeyChecking=no -i "$SSH_KEY_FILE" \
|
|
"$SSH_USER@$SSH_HOST" \
|
|
"ls -lha '$REMOTE_TEMP_DIR'"
|
|
fi
|
|
|
|
# Optionally copy env file
|
|
COPY_ENV_FILE="${{ inputs.copy_env_file }}"
|
|
ENV_FILE_PATH="${{ inputs.env_file_path }}"
|
|
|
|
if [ "$COPY_ENV_FILE" == "true" ]; then
|
|
if [ ! -f "$ENV_FILE_PATH" ]; then
|
|
echo "❌ ERROR: .env file '$ENV_FILE_PATH' does not exist."
|
|
exit 1
|
|
fi
|
|
echo "📄 DEBUG: Copying .env file from '$ENV_FILE_PATH' to remote host"
|
|
scp -o StrictHostKeyChecking=no -i "$SSH_KEY_FILE" \
|
|
"$ENV_FILE_PATH" \
|
|
"$SSH_USER@$SSH_HOST:$REMOTE_TEMP_DIR/.env"
|
|
fi
|
|
|
|
# Validate docker-compose file remotely before deploying
|
|
echo "🔍 DEBUG: Validating Docker Compose file remotely"
|
|
ssh -o StrictHostKeyChecking=no -i "$SSH_KEY_FILE" \
|
|
"$SSH_USER@$SSH_HOST" \
|
|
"docker compose -f '$REMOTE_TEMP_DIR/docker-compose.yml' config --quiet"
|
|
echo "✅ DEBUG: Docker Compose file validation succeeded"
|
|
|
|
# Deploy based on mode
|
|
if [ "$DEPLOY_MODE" == "swarm" ]; then
|
|
echo "🚢 DEBUG: Deploying stack '$STACK_NAME' to Docker Swarm"
|
|
ssh -o StrictHostKeyChecking=no -i "$SSH_KEY_FILE" \
|
|
"$SSH_USER@$SSH_HOST" \
|
|
"docker compose pull && docker stack deploy -c '$REMOTE_TEMP_DIR/docker-compose.yml' '$STACK_NAME' --with-registry-auth"
|
|
echo "✅ DEBUG: Stack '$STACK_NAME' deployed successfully to Docker Swarm"
|
|
else
|
|
echo "🐳 DEBUG: Deploying project '$STACK_NAME' using Docker Compose"
|
|
ssh -o StrictHostKeyChecking=no -i "$SSH_KEY_FILE" \
|
|
"$SSH_USER@$SSH_HOST" \
|
|
"cd '$REMOTE_TEMP_DIR' && docker compose pull && docker compose -p '$STACK_NAME' up -d --remove-orphans"
|
|
echo "✅ DEBUG: Project '$STACK_NAME' deployed successfully using Docker Compose"
|
|
fi
|
|
|
|
# Cleanup remote temporary files
|
|
echo "🧹 DEBUG: Cleaning up remote temporary directory '$REMOTE_TEMP_DIR'"
|
|
ssh -o StrictHostKeyChecking=no -i "$SSH_KEY_FILE" \
|
|
"$SSH_USER@$SSH_HOST" \
|
|
"rm -rf '$REMOTE_TEMP_DIR'"
|
|
|
|
# Cleanup local temporary SSH key file
|
|
rm -f "$SSH_KEY_FILE"
|
|
echo "🔑 DEBUG: Temporary SSH key file '$SSH_KEY_FILE' removed"
|
|
|
|
echo "🎉 Deployment of '$STACK_NAME' completed successfully!"
|