v-2 crear proyecto de docker
Thu Sep 18 2025 16:01:46 GMT+0000 (Coordinated Universal Time)
#!/bin/bash
# Colores para la salida
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Funciones de mensajes
error() { echo -e "${RED}Error: $1${NC}" >&2; }
success() { echo -e "${GREEN}$1${NC}"; }
info() { echo -e "${BLUE}$1${NC}"; }
warning() { echo -e "${YELLOW}$1${NC}" >&2; }
process() { echo -e "${CYAN}$1${NC}"; }
# Variables globales
DOCKER_DIR="docker"
PROJECT_NAME=""
FRAMEWORK=""
PHP_VERSION=""
WEB_PORT=""
DB_PORT=""
CURRENT_DIR=$(pwd)
# Función para verificar y corregir permisos
check_and_fix_permissions() {
local target_dir="$1"
# Verificar si el directorio existe
if [ ! -d "$target_dir" ]; then
return 0
fi
process "Verificando permisos de $target_dir..."
# Verificar si tenemos permisos de escritura
if [ ! -w "$target_dir" ]; then
warning "No tenemos permisos de escritura en $target_dir"
process "Intentando corregir permisos..."
# Intentar con sudo si está disponible
if command -v sudo &> /dev/null; then
if sudo chmod 755 "$target_dir" && sudo chown "$USER:$USER" "$target_dir" 2>/dev/null; then
success "Permisos corregidos con sudo"
return 0
fi
fi
# Intentar sin sudo
if chmod 755 "$target_dir" 2>/dev/null; then
success "Permisos corregidos"
else
error "No se pueden corregir los permisos automáticamente"
info "Por favor, ejecuta manualmente:"
echo " sudo chmod 755 \"$target_dir\""
echo " sudo chown \$USER:\$USER \"$target_dir\""
return 1
fi
else
success "Permisos correctos en $target_dir"
fi
return 0
}
# Función para limpiar directorio de forma segura
safe_clean_directory() {
local dir_path="$1"
if [ ! -d "$dir_path" ]; then
return 0
fi
process "Limpiando directorio $dir_path..."
# Verificar permisos primero
if ! check_and_fix_permissions "$dir_path"; then
return 1
fi
# Intentar eliminar contenido del directorio
if [ -w "$dir_path" ]; then
# Usar find para eliminar contenido de forma segura
if find "$dir_path" -mindepth 1 -maxdepth 1 -exec rm -rf {} + 2>/dev/null; then
success "Directorio limpiado correctamente"
return 0
else
# Si falla, intentar con sudo
if command -v sudo &> /dev/null; then
if sudo rm -rf "${dir_path:?}/"* 2>/dev/null; then
success "Directorio limpiado con sudo"
return 0
fi
fi
fi
fi
error "No se pudo limpiar el directorio $dir_path"
return 1
}
# Verificar instalación de Docker y Docker Compose
check_docker_installation() {
process "Verificando instalación de Docker..."
if ! command -v docker &> /dev/null; then
error "Docker no está instalado. Instala Docker primero."
echo "Visita: https://docs.docker.com/get-docker/"
exit 1
fi
success "Docker está instalado."
process "Verificando Docker Compose..."
if ! command -v docker-compose &> /dev/null && ! docker compose version &> /dev/null; then
error "Docker Compose no está instalado."
echo "Visita: https://docs.docker.com/compose/install/"
exit 1
fi
success "Docker Compose está instalado."
}
# Verificar permisos de Docker
check_docker_permissions() {
process "Verificando permisos de Docker..."
if ! docker info &> /dev/null; then
error "No tienes permisos para acceder a Docker."
warning "Solución:"
echo " sudo usermod -aG docker \$USER"
echo " newgrp docker"
exit 1
fi
success "Permisos de Docker verificados."
}
# Verificar conexión a internet
check_internet_connection() {
process "Verificando conexión a internet..."
if ! curl -s --connect-timeout 10 https://packagist.org > /dev/null; then
error "No hay conexión a internet o Packagist no está disponible"
warning "El script requiere conexión a internet para descargar dependencias"
read -p "¿Continuar de todos modos? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
else
success "Conexión a internet verificada"
fi
}
# Encontrar puertos disponibles
find_available_ports() {
local web_port=8081
local db_port=5433
# Encontrar puerto web disponible
while ss -tuln 2>/dev/null | grep -q ":${web_port} "; do
web_port=$((web_port + 1))
done
# Encontrar puerto DB disponible (empezando desde 5433)
while ss -tuln 2>/dev/null | grep -q ":${db_port} "; do
db_port=$((db_port + 1))
done
WEB_PORT="$web_port"
DB_PORT="$db_port"
success "Puertos disponibles encontrados: Web → ${WEB_PORT}, DB → ${DB_PORT}"
}
# Seleccionar framework CON VERSIONES CORRECTAS
select_framework() {
echo ""
info "🎯 SELECCIÓN DE FRAMEWORK"
echo "========================="
echo ""
echo " 1) Yii2 Basic - Framework tradicional (PHP 7.4)"
echo " 2) Yii2 Advanced - Framework avanzado (PHP 7.4)"
echo " 3) Yii3 - Framework moderno (PHP 8.4+)"
echo " 4) Laravel - Framework elegante (PHP 8.3+)"
echo ""
while true; do
read -p "Selecciona el framework (1-4): " choice
case $choice in
1)
FRAMEWORK="yii2-basic"
PHP_VERSION="7.4"
success "Seleccionado: Yii2 Basic con PHP 7.4"
break
;;
2)
FRAMEWORK="yii2-advanced"
PHP_VERSION="7.4"
success "Seleccionado: Yii2 Advanced con PHP 7.4"
break
;;
3)
FRAMEWORK="yii3"
PHP_VERSION="8.4"
success "Seleccionado: Yii3 con PHP 8.4"
break
;;
4)
FRAMEWORK="laravel"
PHP_VERSION="8.3"
success "Seleccionado: Laravel con PHP 8.3"
break
;;
*)
error "Opción inválida. Por favor, selecciona 1, 2, 3 o 4"
;;
esac
done
}
# Validar nombre del proyecto
get_project_name() {
echo ""
info "📝 NOMBRE DEL PROYECTO"
echo "======================"
echo "Elige un nombre para tu proyecto:"
echo " - Debe comenzar con una letra"
echo " - Puede contener letras, números, guiones y guiones bajos"
echo ""
while true; do
read -p "Ingresa el nombre del proyecto: " project_name
if [ -z "$project_name" ]; then
error "El nombre no puede estar vacío."
continue
fi
if [[ ! "$project_name" =~ ^[a-zA-Z][a-zA-Z0-9_-]*$ ]]; then
error "El nombre debe comenzar con una letra y solo puede contener letras, números, guiones y guiones bajos."
continue
fi
PROJECT_NAME="$project_name"
success "Nombre del proyecto validado: $PROJECT_NAME"
break
done
}
# Crear estructura de directorios MEJORADA con gestión de permisos
create_directory_structure() {
process "Creando estructura de directorios en '$DOCKER_DIR'..."
# Verificar y corregir permisos del directorio docker si existe
if [ -d "$DOCKER_DIR" ]; then
warning "El directorio $DOCKER_DIR ya existe. Verificando permisos..."
if ! check_and_fix_permissions "$DOCKER_DIR"; then
error "No se pueden corregir los permisos de $DOCKER_DIR"
return 1
fi
# Verificar y corregir permisos de subdirectorios
if [ -d "$DOCKER_DIR/src" ]; then
if ! check_and_fix_permissions "$DOCKER_DIR/src"; then
error "No se pueden corregir los permisos de $DOCKER_DIR/src"
return 1
fi
# Limpiar el directorio src de forma segura
if ! safe_clean_directory "$DOCKER_DIR/src"; then
error "No se pudo limpiar el directorio $DOCKER_DIR/src"
return 1
fi
else
# Crear directorio src con permisos correctos
if ! mkdir -p "$DOCKER_DIR/src"; then
error "No se pudo crear el directorio $DOCKER_DIR/src"
return 1
fi
chmod 755 "$DOCKER_DIR/src"
fi
else
# Crear toda la estructura desde cero
if ! mkdir -p "$DOCKER_DIR/src" "$DOCKER_DIR/apache-logs" "$DOCKER_DIR/postgres-backups"; then
error "No se pudo crear la estructura de directorios"
return 1
fi
# Establecer permisos correctos
chmod 755 "$DOCKER_DIR" "$DOCKER_DIR/src" "$DOCKER_DIR/apache-logs" "$DOCKER_DIR/postgres-backups"
success "Estructura de directorios creada en: $DOCKER_DIR/"
fi
# Crear subcarpeta con nombre del proyecto dentro de src
process "Creando directorio del proyecto: $DOCKER_DIR/src/$PROJECT_NAME"
if ! mkdir -p "$DOCKER_DIR/src/$PROJECT_NAME"; then
error "No se pudo crear la subcarpeta del proyecto"
return 1
fi
# Establecer permisos correctos para la carpeta del proyecto
if ! chmod 755 "$DOCKER_DIR/src/$PROJECT_NAME"; then
warning "No se pudieron establecer los permisos automáticamente en $DOCKER_DIR/src/$PROJECT_NAME"
info "Puedes establecerlos manualmente después:"
echo " chmod 755 \"$DOCKER_DIR/src/$PROJECT_NAME\""
fi
success "Estructura de directorios creada: $DOCKER_DIR/src/$PROJECT_NAME/"
return 0
}
# Crear archivo .env
create_env_file() {
process "Creando archivo de configuración .env..."
find_available_ports
cat > "$DOCKER_DIR/.env" << EOF
# Configuración de la aplicación
APP_NAME=$PROJECT_NAME
APP_ENV=local
APP_DEBUG=true
APP_URL=http://localhost:${WEB_PORT}
# Configuración de base de datos
DB_CONNECTION=pgsql
DB_HOST=db
DB_PORT=5432 # Puerto INTERNO del contenedor (SIEMPRE 5432)
DB_DATABASE=${PROJECT_NAME}_db
DB_USERNAME=postgres
DB_PASSWORD=password
# Configuración de PostgreSQL
POSTGRES_DB=${PROJECT_NAME}_db
POSTGRES_USER=postgres
POSTGRES_PASSWORD=password
# Configuración de Docker
PHP_VERSION=${PHP_VERSION}
WEB_PORT=${WEB_PORT}
DB_EXTERNAL_PORT=${DB_PORT} # Puerto EXPUESTO en el host
# Configuración específica del framework
FRAMEWORK=${FRAMEWORK}
PROJECT_NAME=${PROJECT_NAME}
EOF
# Verificar que el archivo se creó y establecer permisos
if [ ! -f "$DOCKER_DIR/.env" ]; then
error "No se pudo crear el archivo .env"
return 1
fi
chmod 644 "$DOCKER_DIR/.env"
success "Archivo .env creado con puertos: Web → ${WEB_PORT}, DB Host → ${DB_PORT}"
return 0
}
# Crear configuración de Apache
create_apache_config() {
process "Creando configuración de Apache..."
# Configuración diferente para Yii2 Advanced
if [ "$FRAMEWORK" = "yii2-advanced" ]; then
cat > "$DOCKER_DIR/000-default.conf" << EOF
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html/frontend/web
<Directory /var/www/html/frontend/web>
AllowOverride All
Require all granted
Options Indexes FollowSymLinks
</Directory>
ErrorLog \${APACHE_LOG_DIR}/error.log
CustomLog \${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
EOF
else
cat > "$DOCKER_DIR/000-default.conf" << EOF
<VirtualHost *:80>
ServerAdmin webmaster@localhost
DocumentRoot /var/www/html/public
<Directory /var/www/html>
AllowOverride All
Require all granted
Options Indexes FollowSymLinks
</Directory>
ErrorLog \${APACHE_LOG_DIR}/error.log
CustomLog \${APACHE_LOG_DIR}/access.log combined
</VirtualHost>
EOF
fi
if [ ! -f "$DOCKER_DIR/000-default.conf" ]; then
error "No se pudo crear la configuración de Apache"
return 1
fi
chmod 644 "$DOCKER_DIR/000-default.conf"
success "Configuración de Apache creada"
return 0
}
# Crear Dockerfile específico para cada framework - CORREGIDO
create_dockerfile() {
process "Creando Dockerfile para PHP $PHP_VERSION..."
case "$FRAMEWORK" in
"yii2-basic"|"yii2-advanced")
create_yii2_dockerfile
;;
"yii3")
create_yii3_dockerfile
;;
"laravel")
create_laravel_dockerfile
;;
esac
if [ ! -f "$DOCKER_DIR/Dockerfile" ]; then
error "No se pudo crear el Dockerfile"
return 1
fi
chmod 644 "$DOCKER_DIR/Dockerfile"
return 0
}
create_yii2_dockerfile() {
cat > "$DOCKER_DIR/Dockerfile" << 'EOF'
ARG PHP_VERSION=7.4
FROM php:${PHP_VERSION}-apache
# Actualizar lista de paquetes y instalar dependencias
RUN apt-get update && apt-get install -y \
libpq-dev \
libzip-dev \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
zip \
unzip \
git \
curl \
gnupg \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Configurar extensiones de PHP para PHP 7.4 - CONFIGURACIÓN CORREGIDA
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j$(nproc) \
pdo \
pdo_pgsql \
pgsql \
zip \
gd \
bcmath \
opcache \
&& a2enmod rewrite
# Instalar Composer
RUN curl -sS https://getcomposer.org/installer | php -- \
--install-dir=/usr/local/bin \
--filename=composer
# Configurar Composer para evitar timeouts
RUN composer config -g process-timeout 1200 && \
composer config -g repo.packagist composer https://packagist.org
# Instalar extensiones adicionales necesarias para Yii2
RUN docker-php-ext-install mysqli && docker-php-ext-enable mysqli
COPY 000-default.conf /etc/apache2/sites-available/000-default.conf
RUN a2ensite 000-default.conf
RUN chown -R www-data:www-data /var/www/html
WORKDIR /var/www/html
# Configuraciones PHP optimizadas para PHP 7.4
RUN echo "memory_limit = 512M" > /usr/local/etc/php/conf.d/memory.ini && \
echo "upload_max_filesize = 64M" >> /usr/local/etc/php/conf.d/uploads.ini && \
echo "post_max_size = 64M" >> /usr/local/etc/php/conf.d/uploads.ini && \
echo "max_execution_time = 300" >> /usr/local/etc/php/conf.d/timeouts.ini && \
echo "max_input_time = 120" >> /usr/local/etc/php/conf.d/timeouts.ini
EXPOSE 80
EOF
success "Dockerfile para Yii2 creado"
}
create_yii3_dockerfile() {
cat > "$DOCKER_DIR/Dockerfile" << 'EOF'
ARG PHP_VERSION=8.4
FROM php:${PHP_VERSION}-apache
# Actualizar lista de paquetes y instalar dependencias
RUN apt-get update && apt-get install -y \
libpq-dev \
libzip-dev \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
zip \
unzip \
git \
curl \
gnupg \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Configurar extensiones de PHP - CONFIGURACIÓN CORREGIDA
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j$(nproc) \
pdo \
pdo_pgsql \
pgsql \
zip \
gd \
bcmath \
opcache \
&& a2enmod rewrite
# Instalar Composer
RUN curl -sS https://getcomposer.org/installer | php -- \
--install-dir=/usr/local/bin \
--filename=composer
# Configurar Composer para evitar timeouts
RUN composer config -g process-timeout 1200 && \
composer config -g repo.packagist composer https://packagist.org
COPY 000-default.conf /etc/apache2/sites-available/000-default.conf
RUN a2ensite 000-default.conf
RUN chown -R www-data:www-data /var/www/html
WORKDIR /var/www/html
# Configuraciones PHP optimizadas
RUN echo "memory_limit = 512M" > /usr/local/etc/php/conf.d/memory.ini && \
echo "upload_max_filesize = 128M" >> /usr/local/etc/php/conf.d/uploads.ini && \
echo "post_max_size = 128M" >> /usr/local/etc/php/conf.d/uploads.ini && \
echo "max_execution_time = 300" >> /usr/local/etc/php/conf.d/timeouts.ini && \
echo "max_input_time = 120" >> /usr/local/etc/php/conf.d/timeouts.ini && \
echo "opcache.enable=1" >> /usr/local/etc/php/conf.d/opcache.ini && \
echo "opcache.memory_consumption=256" >> /usr/local/etc/php/conf.d/opcache.ini
EXPOSE 80
EOF
success "Dockerfile para Yii3 creado"
}
create_laravel_dockerfile() {
cat > "$DOCKER_DIR/Dockerfile" << 'EOF'
ARG PHP_VERSION=8.3
FROM php:${PHP_VERSION}-apache
# Actualizar lista de paquetes y instalar dependencias
RUN apt-get update && apt-get install -y \
libpq-dev \
libzip-dev \
libpng-dev \
libjpeg-dev \
libfreetype6-dev \
zip \
unzip \
git \
curl \
gnupg \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# Configurar extensiones de PHP - CONFIGURACIÓN CORREGIDA
RUN docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install -j$(nproc) \
pdo \
pdo_pgsql \
pgsql \
zip \
gd \
bcmath \
opcache \
&& a2enmod rewrite
# Instalar Composer
RUN curl -sS https://getcomposer.org/installer | php -- \
--install-dir=/usr/local/bin \
--filename=composer
# Configurar Composer para evitar timeouts
RUN composer config -g process-timeout 1200 && \
composer config -g repo.packagist composer https://packagist.org
# Extensiones adicionales para Laravel
RUN docker-php-ext-install exif && docker-php-ext-enable exif
COPY 000-default.conf /etc/apache2/sites-available/000-default.conf
RUN a2ensite 000-default.conf
RUN chown -R www-data:www-data /var/www/html
WORKDIR /var/www/html
# Configuraciones PHP optimizadas
RUN echo "memory_limit = 512M" > /usr/local/etc/php/conf.d/memory.ini && \
echo "upload_max_filesize = 128M" >> /usr/local/etc/php/conf.d/uploads.ini && \
echo "post_max_size = 128M" >> /usr/local/etc/php/conf.d/uploads.ini && \
echo "max_execution_time = 300" >> /usr/local/etc/php/conf.d/timeouts.ini && \
echo "max_input_time = 120" >> /usr/local/etc/php/conf.d/timeouts.ini && \
echo "opcache.enable=1" >> /usr/local/etc/php/conf.d/opcache.ini
EXPOSE 80
EOF
success "Dockerfile para Laravel creado"
}
# Crear docker-compose.yml MEJORADO
create_docker_compose() {
process "Creando docker-compose.yml..."
# Configurar volumen según el tipo de proyecto
local volume_config="./src/${PROJECT_NAME}:/var/www/html"
cat > "$DOCKER_DIR/docker-compose.yml" << EOF
version: '3.8'
services:
web:
build:
context: .
dockerfile: Dockerfile
args:
PHP_VERSION: \${PHP_VERSION}
ports:
- "\${WEB_PORT}:80"
volumes:
- ${volume_config}
- ./apache-logs:/var/log/apache2
depends_on:
db:
condition: service_healthy
networks:
- backend-network
env_file:
- .env
container_name: ${PROJECT_NAME}-web
restart: unless-stopped
dns:
- 8.8.8.8
- 8.8.4.4
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:80"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
db:
image: postgres:15
environment:
POSTGRES_DB: \${POSTGRES_DB}
POSTGRES_USER: \${POSTGRES_USER}
POSTGRES_PASSWORD: \${POSTGRES_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
- ./postgres-backups:/backups
networks:
- backend-network
container_name: ${PROJECT_NAME}-db
restart: unless-stopped
ports:
- "\${DB_EXTERNAL_PORT}:5432"
dns:
- 8.8.8.8
- 8.8.4.4
healthcheck:
test: ["CMD-SHELL", "pg_isready -U \${POSTGRES_USER} -d \${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 10
start_period: 30s
networks:
backend-network:
driver: bridge
volumes:
pgdata:
driver: local
EOF
if [ ! -f "$DOCKER_DIR/docker-compose.yml" ]; then
error "No se pudo crear el archivo docker-compose.yml"
return 1
fi
chmod 644 "$DOCKER_DIR/docker-compose.yml"
success "Archivo docker-compose.yml creado"
return 0
}
# Función para ejecutar comandos en el directorio docker
run_in_docker_dir() {
local cmd="$1"
if [ ! -d "$DOCKER_DIR" ]; then
error "Directorio $DOCKER_DIR no encontrado"
return 1
fi
# Verificar permisos antes de continuar
if ! check_and_fix_permissions "$DOCKER_DIR"; then
error "No se puede acceder a $DOCKER_DIR por problemas de permisos"
return 1
fi
cd "$DOCKER_DIR" || { error "No se puede acceder al directorio $DOCKER_DIR"; return 1; }
eval "$cmd"
local result=$?
cd "$CURRENT_DIR" || return 1
return $result
}
# Función MEJORADA para esperar a que PostgreSQL esté listo
wait_for_postgres() {
process "Esperando a que PostgreSQL esté listo..."
local max_attempts=30
local attempt=1
while [ $attempt -le $max_attempts ]; do
if run_in_docker_dir "docker-compose exec -T db pg_isready -U postgres > /dev/null 2>&1"; then
success "PostgreSQL está listo después de $attempt intentos"
# Esperar un poco más para asegurar que esté completamente listo
sleep 5
# Verificar que la base de datos específica existe, si no crearla
process "Verificando base de datos ${PROJECT_NAME}_db..."
if ! run_in_docker_dir "docker-compose exec -T db psql -U postgres -d ${PROJECT_NAME}_db -c 'SELECT 1' > /dev/null 2>&1"; then
process "Creando base de datos ${PROJECT_NAME}_db..."
if run_in_docker_dir "docker-compose exec -T db createdb -U postgres ${PROJECT_NAME}_db"; then
success "Base de datos ${PROJECT_NAME}_db creada exitosamente"
else
error "Error creando la base de datos ${PROJECT_NAME}_db"
return 1
fi
else
success "Base de datos ${PROJECT_NAME}_db ya existe"
fi
sleep 2
return 0
fi
if [ $attempt -eq 1 ]; then
process "Esperando a que PostgreSQL se inicialice..."
else
process "Intento $attempt/$max_attempts - PostgreSQL aún no está listo..."
fi
sleep 3
attempt=$((attempt + 1))
done
error "PostgreSQL no está respondiendo después de $max_attempts intentos"
run_in_docker_dir 'docker-compose logs db'
return 1
}
# Construir y levantar contenedores CON MEJOR MANEJO DE ERRORES
build_docker_containers() {
process "Construyendo contenedores Docker..."
if [ ! -d "$DOCKER_DIR" ]; then
error "El directorio $DOCKER_DIR no existe. No se pueden construir los contenedores."
return 1
fi
# Verificar permisos antes de continuar
if ! check_and_fix_permissions "$DOCKER_DIR"; then
error "No se pueden construir los contenedores por problemas de permisos"
return 1
fi
# Parar contenedores existentes
process "Deteniendo contenedores existentes..."
if ! run_in_docker_dir 'docker-compose down 2>/dev/null'; then
warning "No se pudieron detener contenedores existentes (puede ser normal si no hay contenedores)"
fi
# Construir imágenes con mejor manejo de errores
process "Construyendo imágenes Docker..."
# Primero intentar construir sin cache para detectar errores temprano
if run_in_docker_dir "docker-compose build --no-cache --build-arg PHP_VERSION=${PHP_VERSION} 2>&1 | tee build.log"; then
success "Imágenes construidas exitosamente sin cache"
else
error "Error construyendo las imágenes Docker sin cache"
process "Analizando errores de construcción..."
# Mostrar los últimos errores del log
if [ -f "$DOCKER_DIR/build.log" ]; then
error "Últimas líneas del log de construcción:"
tail -20 "$DOCKER_DIR/build.log" | while read -r line; do
echo " $line"
done
fi
# Intentar con cache como fallback
warning "Intentando construcción con cache..."
if ! run_in_docker_dir "docker-compose build --build-arg PHP_VERSION=${PHP_VERSION}"; then
error "Error crítico en la construcción de imágenes Docker"
return 1
fi
fi
# Levantar contenedores
process "Iniciando contenedores..."
if ! run_in_docker_dir 'docker-compose up -d'; then
error "Error iniciando contenedores"
run_in_docker_dir 'docker-compose logs'
return 1
fi
# Esperar a que PostgreSQL esté listo
if ! wait_for_postgres; then
error "La base de datos no se inicializó correctamente"
return 1
fi
# Verificar estado de los contenedores
process "Verificando estado de los contenedores..."
if run_in_docker_dir 'docker-compose ps | grep -q "Up"'; then
success "Contenedores Docker listos y ejecutándose"
return 0
else
error "Los contenedores no se iniciaron correctamente"
run_in_docker_dir 'docker-compose ps'
return 1
fi
}
# Ejecutar comandos en el contenedor web con reintentos MEJORADO
exec_in_web() {
local cmd="$1"
local max_attempts=3
local attempt=1
sleep 10
while [ $attempt -le $max_attempts ]; do
process "Ejecutando comando (intento $attempt/$max_attempts)..."
if run_in_docker_dir "docker-compose exec -T web bash -c \"$cmd\""; then
return 0
fi
if [ $attempt -lt $max_attempts ]; then
warning "Intento $attempt falló. Reintentando en 10 segundos..."
sleep 10
fi
attempt=$((attempt + 1))
done
error "Error ejecutando comando después de $max_attempts intentos: $cmd"
return 1
}
# Función MEJORADA para crear proyectos con Composer
create_project_with_composer() {
local framework_cmd="$1"
local project_name="$2"
local install_dir="$3"
process "Descargando proyecto $framework_cmd..."
exec_in_web "composer config -g process-timeout 1200"
exec_in_web "composer config -g repo.packagist composer https://packagist.org"
exec_in_web "composer clear-cache"
# Opciones específicas por framework
if [[ "$FRAMEWORK" == yii2* ]]; then
local composer_options=(
"--prefer-dist --no-progress --no-interaction"
"--prefer-dist --no-dev --no-progress --no-interaction"
"--prefer-source --no-progress --no-interaction"
)
else
local composer_options=(
"--prefer-dist --no-dev --no-progress --no-interaction"
"--prefer-dist --no-progress --no-interaction"
"--prefer-source --no-progress --no-interaction"
)
fi
for options in "${composer_options[@]}"; do
process "Intentando con opciones: $options"
# Limpiar directorio temporal si existe
exec_in_web "rm -rf /tmp/${project_name} 2>/dev/null || true"
if exec_in_web "composer create-project $options $framework_cmd /tmp/${project_name}"; then
# VERIFICAR que el proyecto se creó correctamente
if exec_in_web "[ -d /tmp/${project_name} ] && [ -f /tmp/${project_name}/composer.json ]"; then
success "Proyecto descargado correctamente"
# Mover a la ubicación final
exec_in_web "rm -rf ${install_dir}/* 2>/dev/null || true"
exec_in_web "sh -c 'mv /tmp/${project_name}/* ${install_dir}/ 2>/dev/null || true'"
exec_in_web "sh -c 'mv /tmp/${project_name}/.* ${install_dir}/ 2>/dev/null || true'"
exec_in_web "rm -rf /tmp/${project_name}"
# Instalar dependencias si es necesario
if exec_in_web "[ ! -d ${install_dir}/vendor ] && [ -f ${install_dir}/composer.json ]"; then
process "Instalando dependencias..."
if exec_in_web "cd ${install_dir} && composer install --no-dev --no-progress --no-interaction"; then
success "Dependencias instaladas correctamente"
else
warning "No se pudieron instalar las dependencias automáticamente"
fi
fi
return 0
fi
fi
warning "Intento falló con opciones: $options"
exec_in_web "rm -rf /tmp/${project_name} 2>/dev/null || true"
done
return 1
}
# Crear proyecto Yii2 Basic
create_yii2_basic_project() {
process "Creando proyecto Yii2 Basic: ${PROJECT_NAME}"
if ! create_project_with_composer "yiisoft/yii2-app-basic" "${PROJECT_NAME}" "/var/www/html"; then
error "Error al crear proyecto Yii2 Basic después de múltiples intentos"
return 1
fi
# Configuraciones adicionales para Yii2 Basic
exec_in_web "chown -R www-data:www-data /var/www/html"
exec_in_web "chmod -R 755 /var/www/html/runtime"
exec_in_web "chmod -R 755 /var/www/html/web/assets"
success "Proyecto Yii2 Basic '${PROJECT_NAME}' creado correctamente"
return 0
}
# Crear proyecto Yii2 Advanced
create_yii2_advanced_project() {
process "Creando proyecto Yii2 Advanced: ${PROJECT_NAME}"
if ! create_project_with_composer "yiisoft/yii2-app-advanced" "${PROJECT_NAME}" "/var/www/html"; then
error "Error al crear proyecto Yii2 Advanced después de múltiples intentos"
return 1
fi
# Configuraciones adicionales para Yii2 Advanced
exec_in_web "chown -R www-data:www-data /var/www/html"
exec_in_web "chmod -R 755 /var/www/html/frontend/runtime"
exec_in_web "chmod -R 755 /var/www/html/backend/runtime"
exec_in_web "chmod -R 755 /var/www/html/frontend/web/assets"
exec_in_web "chmod -R 755 /var/www/html/backend/web/assets"
# Inicializar el proyecto advanced
process "Inicializando Yii2 Advanced..."
if exec_in_web "cd /var/www/html && php init --env=Development --overwrite=All"; then
success "Yii2 Advanced inicializado correctamente"
else
warning "No se pudo inicializar Yii2 Advanced automáticamente"
fi
success "Proyecto Yii2 Advanced '${PROJECT_NAME}' creado correctamente"
return 0
}
# Crear proyecto Yii3
create_yii3_project() {
process "Creando proyecto Yii3: ${PROJECT_NAME}"
if ! create_project_with_composer "yiisoft/yii-project-template" "${PROJECT_NAME}" "/var/www/html"; then
error "Error al crear proyecto Yii3 después de múltiples intentos"
return 1
fi
exec_in_web "chown -R www-data:www-data /var/www/html"
success "Proyecto Yii3 '${PROJECT_NAME}' creado correctamente"
return 0
}
# Crear proyecto Laravel
create_laravel_project() {
process "Creando proyecto Laravel: ${PROJECT_NAME}"
if ! create_project_with_composer "laravel/laravel" "${PROJECT_NAME}" "/var/www/html"; then
error "Error al crear proyecto Laravel después de múltiples intentos"
return 1
fi
exec_in_web "chown -R www-data:www-data /var/www/html"
exec_in_web "chmod -R 775 /var/www/html/storage"
exec_in_web "chmod -R 775 /var/www/html/bootstrap/cache"
# Configurar .env para Laravel
process "Configurando Laravel..."
exec_in_web "cd /var/www/html && cp .env.example .env 2>/dev/null || true"
exec_in_web "cd /var/www/html && cat > .env << 'ENDFILE'
APP_NAME=${PROJECT_NAME}
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_URL=http://localhost:${WEB_PORT}
DB_CONNECTION=pgsql
DB_HOST=db
DB_PORT=5432
DB_DATABASE=${PROJECT_NAME}_db
DB_USERNAME=postgres
DB_PASSWORD=password
LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug
QUEUE_CONNECTION=sync
SESSION_DRIVER=file
SESSION_LIFETIME=120
MEMCACHED_HOST=127.0.0.1
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=smtp
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=null
MAIL_FROM_NAME=\"\${APP_NAME}\"
ENDFILE"
exec_in_web "cd /var/www/html && php artisan key:generate"
success "Proyecto Laravel '${PROJECT_NAME}' creado correctamente"
return 0
}
# Crear el proyecto según el framework seleccionado
create_framework_project() {
process "Iniciando creación del proyecto ${PROJECT_NAME} con ${FRAMEWORK}..."
process "Probando conexión a internet desde el contenedor..."
if ! exec_in_web "curl -s --connect-timeout 30 https://packagist.org > /dev/null"; then
warning "El contenedor no puede conectarse a Packagist. Esto puede causar problemas."
exec_in_web "cat /etc/resolv.conf"
else
success "Conexión a internet verificada desde el contenedor"
fi
case "$FRAMEWORK" in
"yii2-basic")
create_yii2_basic_project
;;
"yii2-advanced")
create_yii2_advanced_project
;;
"yii3")
create_yii3_project
;;
"laravel")
create_laravel_project
;;
*)
error "Framework no válido: $FRAMEWORK"
return 1
;;
esac
}
# Configuración MEJORADA de base de datos
configure_database() {
process "Configurando base de datos para $FRAMEWORK..."
sleep 10
case "$FRAMEWORK" in
"yii2-basic")
configure_yii2_basic_db
;;
"yii2-advanced")
configure_yii2_advanced_db
;;
"yii3")
configure_yii3_db
;;
"laravel")
configure_laravel_db
;;
esac
}
configure_laravel_db() {
process "Configurando base de datos para Laravel..."
process "Verificando conexión a la base de datos..."
if exec_in_web "cd /var/www/html && php -r \"
try {
\\\$pdo = new PDO('pgsql:host=db;port=5432;dbname=${PROJECT_NAME}_db', 'postgres', 'password');
\\\$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
echo 'Conexión a BD exitosa' . PHP_EOL;
} catch (PDOException \\\$e) {
echo 'Error de conexión: ' . \\\$e->getMessage() . PHP_EOL;
exit(1);
}
\""; then
success "Conexión a la base de datos verificada"
else
error "No se pudo conectar a la base de datos"
return 1
fi
process "Ejecutando migraciones de Laravel..."
if exec_in_web "cd /var/www/html && php artisan migrate --force"; then
success "Migraciones de Laravel ejecutadas correctamente"
else
warning "Las migraciones de Laravel fallaron o no son necesarias"
fi
return 0
}
configure_yii3_db() {
process "Configurando base de datos para Yii3..."
if exec_in_web "cd /var/www/html && php ./yii migrate/up --interactive=0"; then
success "Migraciones de Yii3 ejecutadas correctamente"
else
warning "Las migraciones de Yii3 fallaron o no son necesarias"
fi
return 0
}
# Configuración MEJORADA para Yii2 Basic
configure_yii2_basic_db() {
process "Configurando base de datos para Yii2 Basic..."
# Primero verificar que Yii2 esté instalado correctamente
process "Verificando instalación de Yii2 Basic..."
if ! exec_in_web "[ -f /var/www/html/vendor/yiisoft/yii2/Yii.php ]"; then
error "Yii2 Basic no está instalado correctamente. No se puede configurar la base de datos."
info "Instala las dependencias manualmente:"
echo " cd $DOCKER_DIR && docker-compose exec web composer install"
return 1
fi
process "Configurando conexión a base de datos para Yii2 Basic..."
exec_in_web "cd /var/www/html && cp config/db.php config/db.php.backup 2>/dev/null || true"
exec_in_web "cd /var/www/html && cat > config/db.php << 'ENDFILE'
<?php
return [
'class' => 'yii\db\Connection',
'dsn' => 'pgsql:host=db;port=5432;dbname=${PROJECT_NAME}_db',
'username' => 'postgres',
'password' => 'password',
'charset' => 'utf8',
];
ENDFILE"
# Deshabilitar GII en producción o si no está instalado
exec_in_web "cd /var/www/html && cp config/web.php config/web.php.backup 2>/dev/null || true"
exec_in_web "cd /var/www/html && sed -i \"s/'modules' => \[/'modules' => \[\\n /*\\n 'gii' => [\\n 'class' => 'yii\\\gii\\\Module',\\n ],\\n */\\n/g\" config/web.php 2>/dev/null || true"
process "Intentando ejecutar migraciones de Yii2 Basic..."
if exec_in_web "cd /var/www/html && php yii migrate/up --interactive=0"; then
success "Migraciones de Yii2 Basic ejecutadas correctamente"
else
warning "Las migraciones de Yii2 Basic fallaron o no son necesarias"
fi
return 0
}
# Configuración para Yii2 Advanced
configure_yii2_advanced_db() {
process "Configurando base de datos para Yii2 Advanced..."
# Verificar instalación
if ! exec_in_web "[ -f /var/www/html/vendor/yiisoft/yii2/Yii.php ]"; then
error "Yii2 Advanced no está instalado correctamente."
return 1
fi
# Configurar base de datos común
exec_in_web "cd /var/www/html && cat > common/config/main-local.php << 'ENDFILE'
<?php
return [
'components' => [
'db' => [
'class' => 'yii\db\Connection',
'dsn' => 'pgsql:host=db;port=5432;dbname=${PROJECT_NAME}_db',
'username' => 'postgres',
'password' => 'password',
'charset' => 'utf8',
],
],
];
ENDFILE"
# Configurar base de datos para frontend y backend
for app in frontend backend; do
exec_in_web "cd /var/www/html && cat > ${app}/config/main-local.php << 'ENDFILE'
<?php
return [
'components' => [
'db' => [
'class' => 'yii\db\Connection',
'dsn' => 'pgsql:host=db;port=5432;dbname=${PROJECT_NAME}_db',
'username' => 'postgres',
'password' => 'password',
'charset' => 'utf8',
],
],
];
ENDFILE"
done
process "Ejecutando migraciones de Yii2 Advanced..."
if exec_in_web "cd /var/www/html && php yii migrate/up --interactive=0"; then
success "Migraciones de Yii2 Advanced ejecutadas correctamente"
else
warning "Las migraciones de Yii2 Advanced fallaron o no son necesarias"
fi
return 0
}
# Mostrar resumen final MEJORADO
show_final_summary() {
echo ""
success "🎉 ¡PROYECTO CREADO EXITOSAMENTE!"
echo "=================================="
echo ""
info "📊 RESUMEN FINAL:"
echo "-----------------"
echo " 📂 Proyecto: $PROJECT_NAME"
echo " 🚀 Framework: $FRAMEWORK"
echo " 🐘 PHP: $PHP_VERSION"
echo " 🌐 URL: http://localhost:${WEB_PORT}"
echo " 🗄️ Base de datos: localhost:${DB_PORT}"
echo " 📁 Directorio: $DOCKER_DIR/src/$PROJECT_NAME/"
echo ""
# Información específica por framework
case "$FRAMEWORK" in
"yii2-advanced")
info "🌐 URLs de Yii2 Advanced:"
echo " Frontend: http://localhost:${WEB_PORT}"
echo " Backend: http://localhost:${WEB_PORT}/backend/web"
;;
*)
info "🌐 URL de la aplicación:"
echo " http://localhost:${WEB_PORT}"
;;
esac
echo ""
info "🚀 COMANDOS ÚTILES:"
echo "------------------"
echo " Iniciar proyecto: cd $DOCKER_DIR && docker-compose up -d"
echo " Detener proyecto: cd $DOCKER_DIR && docker-compose down"
echo " Ver logs: cd $DOCKER_DIR && docker-compose logs"
echo " Acceder a la consola: cd $DOCKER_DIR && docker-compose exec web bash"
echo ""
info "🔗 CONEXIÓN A LA BASE DE DATOS:"
echo "------------------------------"
echo " Host: localhost"
echo " Puerto: ${DB_PORT}"
echo " Base de datos: ${PROJECT_NAME}_db"
echo " Usuario: postgres"
echo " Contraseña: password"
echo ""
# Instrucciones específicas por framework
case "$FRAMEWORK" in
"yii2-basic"|"yii2-advanced")
info "🔧 INSTRUCCIONES ESPECÍFICAS PARA YII2:"
echo "-------------------------------------"
echo " Si hay problemas con dependencias:"
echo " cd $DOCKER_DIR && docker-compose exec web composer install"
if [ "$FRAMEWORK" = "yii2-advanced" ]; then
echo " Para inicializar el proyecto:"
echo " cd $DOCKER_DIR && docker-compose exec web php init"
fi
echo " Para ejecutar migraciones:"
echo " cd $DOCKER_DIR && docker-compose exec web php yii migrate/up --interactive=0"
echo " Para instalar módulos de desarrollo:"
echo " cd $DOCKER_DIR && docker-compose exec web composer require --dev yiisoft/yii2-gii"
echo " cd $DOCKER_DIR && docker-compose exec web composer require --dev yiisoft/yii2-debug"
echo ""
;;
"yii3")
info "🔧 INSTRUCCIONES ESPECÍFICAS PARA YII3:"
echo "-------------------------------------"
echo " Para ejecutar migraciones: cd $DOCKER_DIR && docker-compose exec web php ./yii migrate/up --interactive=0"
echo ""
;;
"laravel")
info "🔧 INSTRUCCIONES ESPECÍFICAS PARA LARAVEL:"
echo "----------------------------------------"
echo " Para ejecutar migraciones: cd $DOCKER_DIR && docker-compose exec web php artisan migrate"
echo " Para generar key: cd $DOCKER_DIR && docker-compose exec web php artisan key:generate"
echo ""
;;
esac
info "📝 PRÓXIMOS PASOS:"
echo "-----------------"
echo " 1. Accede a la URL mostrada arriba para ver tu aplicación"
echo " 2. Configura la base de datos si es necesario"
echo " 3. ¡Comienza a desarrollar!"
echo ""
}
# Función principal
main() {
clear
echo "========================================"
echo " GENERADOR DE PROYECTOS CON DOCKER v1.1"
echo "========================================"
echo ""
check_docker_installation
check_docker_permissions
check_internet_connection
select_framework
get_project_name
if ! create_directory_structure; then
error "Error creando la estructura de directorios"
exit 1
fi
if ! create_env_file; then
error "Error creando el archivo .env"
exit 1
fi
if ! create_apache_config; then
error "Error creando la configuración de Apache"
exit 1
fi
if ! create_dockerfile; then
error "Error creando el Dockerfile"
exit 1
fi
if ! create_docker_compose; then
error "Error creando docker-compose.yml"
exit 1
fi
if build_docker_containers; then
process "Esperando inicialización completa de servicios..."
sleep 20
if create_framework_project; then
process "Configurando la aplicación..."
if configure_database; then
show_final_summary
else
warning "Proyecto creado pero la configuración de BD tuvo problemas menores"
show_final_summary
fi
else
error "Error al crear el proyecto del framework"
exit 1
fi
else
error "Error al construir los contenedores Docker"
exit 1
fi
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
main "$@"
fi
linux



Comments