codecrypto-cli 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +15 -0
- package/build-docker.sh +174 -0
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +241 -31
- package/dist/commands/deploy.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/deploy.ts +219 -35
- package/codecrypto-cli-1.0.0.tgz +0 -0
package/Dockerfile
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
FROM node:20-alpine
|
|
2
|
+
WORKDIR /app
|
|
3
|
+
|
|
4
|
+
ENV NODE_ENV=production
|
|
5
|
+
ENV PORT=3000
|
|
6
|
+
|
|
7
|
+
# 1. Copiamos el servidor standalone generado localmente
|
|
8
|
+
# Nota: La carpeta 'standalone' incluye un 'server.js'
|
|
9
|
+
COPY standalone/ .
|
|
10
|
+
|
|
11
|
+
# 2. Exponemos el puerto para Traefik
|
|
12
|
+
EXPOSE 3000
|
|
13
|
+
|
|
14
|
+
# 3. Arrancamos directamente con Node
|
|
15
|
+
CMD ["node", "server.js"]
|
package/build-docker.sh
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Script para copiar build standalone y crear imagen Docker
|
|
4
|
+
# Uso: ./build-docker.sh <path-proyecto> <folder-destino>
|
|
5
|
+
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
# Colores para output
|
|
9
|
+
RED='\033[0;31m'
|
|
10
|
+
GREEN='\033[0;32m'
|
|
11
|
+
YELLOW='\033[1;33m'
|
|
12
|
+
NC='\033[0m' # No Color
|
|
13
|
+
|
|
14
|
+
# Verificar argumentos
|
|
15
|
+
if [ "$#" -ne 2 ]; then
|
|
16
|
+
echo -e "${RED}Error: Argumentos insuficientes${NC}"
|
|
17
|
+
echo "Uso: $0 <path-proyecto> <folder-destino>"
|
|
18
|
+
echo ""
|
|
19
|
+
echo "Ejemplo:"
|
|
20
|
+
echo " $0 /path/to/proyecto /path/to/destino"
|
|
21
|
+
exit 1
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
PROJECT_PATH="$1"
|
|
25
|
+
DEST_FOLDER="$2"
|
|
26
|
+
|
|
27
|
+
# Verificar que el proyecto existe
|
|
28
|
+
if [ ! -d "$PROJECT_PATH" ]; then
|
|
29
|
+
echo -e "${RED}Error: El directorio del proyecto no existe: ${PROJECT_PATH}${NC}"
|
|
30
|
+
exit 1
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
# Verificar que existe .next/standalone
|
|
34
|
+
if [ ! -d "$PROJECT_PATH/.next/standalone" ]; then
|
|
35
|
+
echo -e "${RED}Error: No se encontró .next/standalone en el proyecto${NC}"
|
|
36
|
+
echo "Asegúrate de haber ejecutado 'npm run build' con 'output: standalone'"
|
|
37
|
+
exit 1
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# Obtener el nombre del proyecto desde el path
|
|
41
|
+
PROJECT_NAME=$(basename "$PROJECT_PATH")
|
|
42
|
+
IMAGE_BASE="jviejo/${PROJECT_NAME}"
|
|
43
|
+
|
|
44
|
+
# Leer versión del package.json
|
|
45
|
+
VERSION="latest"
|
|
46
|
+
if [ -f "$PROJECT_PATH/package.json" ]; then
|
|
47
|
+
VERSION=$(node -p "require('$PROJECT_PATH/package.json').version" 2>/dev/null || echo "latest")
|
|
48
|
+
if [ "$VERSION" = "undefined" ] || [ -z "$VERSION" ]; then
|
|
49
|
+
VERSION="latest"
|
|
50
|
+
fi
|
|
51
|
+
else
|
|
52
|
+
echo -e "${YELLOW}Advertencia: No se encontró package.json, usando versión 'latest'${NC}"
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
IMAGE_NAME="${IMAGE_BASE}:${VERSION}"
|
|
56
|
+
IMAGE_LATEST="${IMAGE_BASE}:latest"
|
|
57
|
+
|
|
58
|
+
echo -e "${GREEN}=== Construyendo imagen Docker para: ${PROJECT_NAME} ===${NC}"
|
|
59
|
+
echo -e "${GREEN} Versión: ${VERSION}${NC}"
|
|
60
|
+
echo -e "${GREEN} Tags: ${IMAGE_NAME}, ${IMAGE_LATEST}${NC}"
|
|
61
|
+
|
|
62
|
+
# Crear directorio destino si no existe
|
|
63
|
+
mkdir -p "$DEST_FOLDER"
|
|
64
|
+
|
|
65
|
+
# Paso 1: Encontrar y copiar el contenido de standalone (usando el patrón especificado)
|
|
66
|
+
echo -e "${YELLOW}Paso 1: Copiando contenido de standalone...${NC}"
|
|
67
|
+
STANDALONE_DEST="$DEST_FOLDER/standalone"
|
|
68
|
+
mkdir -p "$STANDALONE_DEST"
|
|
69
|
+
|
|
70
|
+
# Buscar el directorio .next dentro de standalone y copiar su contenido padre
|
|
71
|
+
NEXT_DIR=$(find "$PROJECT_PATH/.next/standalone" -name ".next" -type d | head -n 1)
|
|
72
|
+
if [ -n "$NEXT_DIR" ]; then
|
|
73
|
+
# Copiar el contenido del padre de .next (que es el contenido de standalone)
|
|
74
|
+
PARENT_DIR=$(dirname "$NEXT_DIR")
|
|
75
|
+
cp -r "$PARENT_DIR"/* "$STANDALONE_DEST/" 2>/dev/null || true
|
|
76
|
+
# También copiar archivos ocultos
|
|
77
|
+
cp -r "$PARENT_DIR"/.[!.]* "$STANDALONE_DEST/" 2>/dev/null || true
|
|
78
|
+
else
|
|
79
|
+
# Si no se encuentra .next, copiar todo standalone directamente
|
|
80
|
+
cp -r "$PROJECT_PATH/.next/standalone"/* "$STANDALONE_DEST/"
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
# Paso 2: Copiar .next/static dentro de standalone/.next/static
|
|
84
|
+
echo -e "${YELLOW}Paso 2: Copiando .next/static...${NC}"
|
|
85
|
+
if [ -d "$PROJECT_PATH/.next/static" ]; then
|
|
86
|
+
mkdir -p "$STANDALONE_DEST/.next/static"
|
|
87
|
+
cp -r "$PROJECT_PATH/.next/static"/* "$STANDALONE_DEST/.next/static/"
|
|
88
|
+
else
|
|
89
|
+
echo -e "${YELLOW}Advertencia: No se encontró .next/static${NC}"
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
# Paso 3: Copiar public dentro de standalone/public
|
|
93
|
+
echo -e "${YELLOW}Paso 3: Copiando public...${NC}"
|
|
94
|
+
if [ -d "$PROJECT_PATH/public" ]; then
|
|
95
|
+
mkdir -p "$STANDALONE_DEST/public"
|
|
96
|
+
cp -r "$PROJECT_PATH/public"/* "$STANDALONE_DEST/public/"
|
|
97
|
+
else
|
|
98
|
+
echo -e "${YELLOW}Advertencia: No se encontró public${NC}"
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
# Paso 4: Crear Dockerfile en el destino
|
|
102
|
+
echo -e "${YELLOW}Paso 4: Creando Dockerfile...${NC}"
|
|
103
|
+
cat > "$DEST_FOLDER/Dockerfile" << 'EOF'
|
|
104
|
+
FROM node:20-alpine
|
|
105
|
+
WORKDIR /app
|
|
106
|
+
|
|
107
|
+
ENV NODE_ENV=production
|
|
108
|
+
ENV PORT=3000
|
|
109
|
+
|
|
110
|
+
# Copiamos el contenido de standalone
|
|
111
|
+
COPY standalone/ .
|
|
112
|
+
|
|
113
|
+
# Exponemos el puerto para Traefik
|
|
114
|
+
EXPOSE 3000
|
|
115
|
+
|
|
116
|
+
# Arrancamos directamente con Node
|
|
117
|
+
CMD ["node", "server.js"]
|
|
118
|
+
EOF
|
|
119
|
+
|
|
120
|
+
# Verificar que server.js existe en standalone
|
|
121
|
+
if [ ! -f "$STANDALONE_DEST/server.js" ]; then
|
|
122
|
+
echo -e "${RED}Error: No se encontró server.js en standalone${NC}"
|
|
123
|
+
exit 1
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
# Paso 5: Verificar/crear builder multi-plataforma
|
|
127
|
+
echo -e "${YELLOW}Paso 5: Verificando builder multi-plataforma...${NC}"
|
|
128
|
+
BUILDER_NAME="multiarch-builder"
|
|
129
|
+
if ! docker buildx ls | grep -q "$BUILDER_NAME"; then
|
|
130
|
+
echo -e "${YELLOW}Creando builder multi-plataforma...${NC}"
|
|
131
|
+
docker buildx create --name "$BUILDER_NAME" --use --bootstrap
|
|
132
|
+
else
|
|
133
|
+
echo -e "${YELLOW}Usando builder existente...${NC}"
|
|
134
|
+
docker buildx use "$BUILDER_NAME" 2>/dev/null || docker buildx use "$BUILDER_NAME" --default
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
# Paso 6: Verificar autenticación en Docker Hub
|
|
138
|
+
echo -e "${YELLOW}Paso 6: Verificando autenticación en Docker Hub...${NC}"
|
|
139
|
+
if ! docker info | grep -q "Username"; then
|
|
140
|
+
echo -e "${YELLOW}No se detectó sesión de Docker Hub. Intentando login...${NC}"
|
|
141
|
+
echo -e "${YELLOW}Si no estás autenticado, ejecuta: docker login${NC}"
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
# Paso 7: Construir y hacer push de imagen Docker con buildx para múltiples plataformas
|
|
145
|
+
echo -e "${YELLOW}Paso 7: Construyendo y haciendo push de imagen Docker con buildx para múltiples plataformas...${NC}"
|
|
146
|
+
cd "$DEST_FOLDER"
|
|
147
|
+
|
|
148
|
+
# Construir para ambas plataformas y hacer push con múltiples tags
|
|
149
|
+
# Esto crea automáticamente un manifest multi-plataforma
|
|
150
|
+
docker buildx build \
|
|
151
|
+
--platform linux/amd64,linux/arm64 \
|
|
152
|
+
--tag "$IMAGE_NAME" \
|
|
153
|
+
--tag "$IMAGE_LATEST" \
|
|
154
|
+
--push \
|
|
155
|
+
--progress=plain \
|
|
156
|
+
.
|
|
157
|
+
|
|
158
|
+
BUILD_EXIT=$?
|
|
159
|
+
|
|
160
|
+
if [ $BUILD_EXIT -eq 0 ]; then
|
|
161
|
+
echo -e "${GREEN}✅ ¡Imagen construida y pusheada exitosamente para múltiples plataformas${NC}"
|
|
162
|
+
echo -e "${GREEN} Plataformas: linux/amd64, linux/arm64${NC}"
|
|
163
|
+
echo -e "${GREEN} Versión: ${VERSION}${NC}"
|
|
164
|
+
echo -e "${GREEN} Tags pusheados:${NC}"
|
|
165
|
+
echo -e "${GREEN} - ${IMAGE_NAME}${NC}"
|
|
166
|
+
echo -e "${GREEN} - ${IMAGE_LATEST}${NC}"
|
|
167
|
+
echo -e "${GREEN} Registry: Docker Hub${NC}"
|
|
168
|
+
echo -e "${GREEN} Imagen disponible en: https://hub.docker.com/r/${IMAGE_BASE}${NC}"
|
|
169
|
+
else
|
|
170
|
+
echo -e "${RED}❌ Error al construir o hacer push de la imagen Docker${NC}"
|
|
171
|
+
echo -e "${YELLOW}Verifica que estés autenticado con: docker login${NC}"
|
|
172
|
+
exit 1
|
|
173
|
+
fi
|
|
174
|
+
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"deploy.d.ts","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,eAAO,MAAM,aAAa,SA2LtB,CAAC"}
|
package/dist/commands/deploy.js
CHANGED
|
@@ -1,4 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
2
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
37
|
};
|
|
@@ -7,40 +40,217 @@ exports.deployCommand = void 0;
|
|
|
7
40
|
const commander_1 = require("commander");
|
|
8
41
|
const chalk_1 = __importDefault(require("chalk"));
|
|
9
42
|
const ora_1 = __importDefault(require("ora"));
|
|
43
|
+
const child_process_1 = require("child_process");
|
|
44
|
+
const fs = __importStar(require("fs"));
|
|
45
|
+
const path = __importStar(require("path"));
|
|
10
46
|
exports.deployCommand = new commander_1.Command('deploy')
|
|
11
|
-
.description('
|
|
12
|
-
.
|
|
13
|
-
.
|
|
14
|
-
.option('-
|
|
15
|
-
.option('--
|
|
16
|
-
.
|
|
17
|
-
.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
47
|
+
.description('Build and deploy Docker image for Next.js standalone application')
|
|
48
|
+
.argument('<project-path>', 'Path to the Next.js project directory')
|
|
49
|
+
.argument('[dest-folder]', 'Destination folder for Docker build context', './docker-build')
|
|
50
|
+
.option('--skip-build', 'Skip npm run build (assume already built)', false)
|
|
51
|
+
.option('--no-push', 'Build image but do not push to registry', false)
|
|
52
|
+
.action(async (projectPath, destFolder, options) => {
|
|
53
|
+
console.log(chalk_1.default.blue('\n🐳 CodeCrypto Docker Deployment\n'));
|
|
54
|
+
try {
|
|
55
|
+
// Validar que el proyecto existe
|
|
56
|
+
const resolvedProjectPath = path.resolve(projectPath);
|
|
57
|
+
if (!fs.existsSync(resolvedProjectPath)) {
|
|
58
|
+
console.error(chalk_1.default.red(`❌ Error: El directorio del proyecto no existe: ${resolvedProjectPath}`));
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
// Verificar que es un proyecto Next.js
|
|
62
|
+
const packageJsonPath = path.join(resolvedProjectPath, 'package.json');
|
|
63
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
64
|
+
console.error(chalk_1.default.red(`❌ Error: No se encontró package.json en el proyecto`));
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
// Leer versión del package.json
|
|
68
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
69
|
+
const version = packageJson.version || 'latest';
|
|
70
|
+
const projectName = path.basename(resolvedProjectPath);
|
|
71
|
+
const imageBase = `jviejo/${projectName}`;
|
|
72
|
+
const imageName = `${imageBase}:${version}`;
|
|
73
|
+
const imageLatest = `${imageBase}:latest`;
|
|
74
|
+
console.log(chalk_1.default.gray('Deployment Configuration:'));
|
|
75
|
+
console.log(chalk_1.default.white(` Project: ${chalk_1.default.green(projectName)}`));
|
|
76
|
+
console.log(chalk_1.default.white(` Version: ${chalk_1.default.green(version)}`));
|
|
77
|
+
console.log(chalk_1.default.white(` Image: ${chalk_1.default.green(imageName)}`));
|
|
78
|
+
console.log(chalk_1.default.white(` Latest: ${chalk_1.default.green(imageLatest)}`));
|
|
79
|
+
console.log(chalk_1.default.white(` Push to Registry: ${options.push ? chalk_1.default.green('Yes') : chalk_1.default.yellow('No')}\n`));
|
|
80
|
+
// Paso 1: Build del proyecto si es necesario
|
|
81
|
+
if (!options.skipBuild) {
|
|
82
|
+
const buildSpinner = (0, ora_1.default)('Building Next.js application...').start();
|
|
83
|
+
try {
|
|
84
|
+
(0, child_process_1.execSync)('npm run build', {
|
|
85
|
+
cwd: resolvedProjectPath,
|
|
86
|
+
stdio: 'pipe'
|
|
87
|
+
});
|
|
88
|
+
buildSpinner.succeed('Build completed successfully');
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
buildSpinner.fail('Build failed');
|
|
92
|
+
console.error(chalk_1.default.red('Error during build. Make sure you have "output: standalone" in next.config.ts'));
|
|
93
|
+
process.exit(1);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// Verificar que existe .next/standalone
|
|
97
|
+
const standalonePath = path.join(resolvedProjectPath, '.next', 'standalone');
|
|
98
|
+
if (!fs.existsSync(standalonePath)) {
|
|
99
|
+
console.error(chalk_1.default.red(`❌ Error: No se encontró .next/standalone`));
|
|
100
|
+
console.error(chalk_1.default.yellow('Asegúrate de tener "output: standalone" en next.config.ts y ejecutar "npm run build"'));
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
// Paso 2: Preparar directorio destino
|
|
104
|
+
const resolvedDestFolder = path.resolve(destFolder);
|
|
105
|
+
const standaloneDest = path.join(resolvedDestFolder, 'standalone');
|
|
106
|
+
const copySpinner = (0, ora_1.default)('Copying standalone files...').start();
|
|
107
|
+
if (fs.existsSync(resolvedDestFolder)) {
|
|
108
|
+
fs.rmSync(resolvedDestFolder, { recursive: true, force: true });
|
|
109
|
+
}
|
|
110
|
+
fs.mkdirSync(standaloneDest, { recursive: true });
|
|
111
|
+
// Copiar contenido de standalone
|
|
112
|
+
const nextDir = findDirectory(standalonePath, '.next');
|
|
113
|
+
if (nextDir) {
|
|
114
|
+
const parentDir = path.dirname(nextDir);
|
|
115
|
+
copyDirectory(parentDir, standaloneDest);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
copyDirectory(standalonePath, standaloneDest);
|
|
119
|
+
}
|
|
120
|
+
// Copiar .next/static
|
|
121
|
+
const staticPath = path.join(resolvedProjectPath, '.next', 'static');
|
|
122
|
+
if (fs.existsSync(staticPath)) {
|
|
123
|
+
const staticDest = path.join(standaloneDest, '.next', 'static');
|
|
124
|
+
fs.mkdirSync(staticDest, { recursive: true });
|
|
125
|
+
copyDirectory(staticPath, staticDest);
|
|
126
|
+
}
|
|
127
|
+
// Copiar public
|
|
128
|
+
const publicPath = path.join(resolvedProjectPath, 'public');
|
|
129
|
+
if (fs.existsSync(publicPath)) {
|
|
130
|
+
const publicDest = path.join(standaloneDest, 'public');
|
|
131
|
+
fs.mkdirSync(publicDest, { recursive: true });
|
|
132
|
+
copyDirectory(publicPath, publicDest);
|
|
133
|
+
}
|
|
134
|
+
// Verificar server.js
|
|
135
|
+
const serverJsPath = path.join(standaloneDest, 'server.js');
|
|
136
|
+
if (!fs.existsSync(serverJsPath)) {
|
|
137
|
+
copySpinner.fail('server.js not found in standalone');
|
|
138
|
+
process.exit(1);
|
|
139
|
+
}
|
|
140
|
+
copySpinner.succeed('Files copied successfully');
|
|
141
|
+
// Paso 3: Crear Dockerfile
|
|
142
|
+
const dockerfilePath = path.join(resolvedDestFolder, 'Dockerfile');
|
|
143
|
+
const dockerfileContent = `FROM node:20-alpine
|
|
144
|
+
WORKDIR /app
|
|
145
|
+
|
|
146
|
+
ENV NODE_ENV=production
|
|
147
|
+
ENV PORT=3000
|
|
148
|
+
|
|
149
|
+
# Copiamos el contenido de standalone
|
|
150
|
+
COPY standalone/ .
|
|
151
|
+
|
|
152
|
+
# Exponemos el puerto para Traefik
|
|
153
|
+
EXPOSE 3000
|
|
154
|
+
|
|
155
|
+
# Arrancamos directamente con Node
|
|
156
|
+
CMD ["node", "server.js"]
|
|
157
|
+
`;
|
|
158
|
+
fs.writeFileSync(dockerfilePath, dockerfileContent);
|
|
159
|
+
// Paso 4: Verificar/crear builder multi-plataforma
|
|
160
|
+
const builderSpinner = (0, ora_1.default)('Setting up multi-platform builder...').start();
|
|
161
|
+
try {
|
|
162
|
+
const builders = (0, child_process_1.execSync)('docker buildx ls', { encoding: 'utf-8' });
|
|
163
|
+
const builderName = 'multiarch-builder';
|
|
164
|
+
if (!builders.includes(builderName)) {
|
|
165
|
+
(0, child_process_1.execSync)(`docker buildx create --name ${builderName} --use --bootstrap`, { stdio: 'pipe' });
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
(0, child_process_1.execSync)(`docker buildx use ${builderName}`, { stdio: 'pipe' });
|
|
169
|
+
}
|
|
170
|
+
builderSpinner.succeed('Builder ready');
|
|
171
|
+
}
|
|
172
|
+
catch (error) {
|
|
173
|
+
builderSpinner.fail('Failed to setup builder');
|
|
174
|
+
console.error(chalk_1.default.red('Error setting up Docker buildx builder'));
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
// Paso 5: Construir y hacer push
|
|
178
|
+
const buildDockerSpinner = (0, ora_1.default)('Building Docker image for multiple platforms...').start();
|
|
179
|
+
try {
|
|
180
|
+
const tags = options.push
|
|
181
|
+
? [`--tag ${imageName}`, `--tag ${imageLatest}`, '--push']
|
|
182
|
+
: [`--tag ${imageName}`, `--tag ${imageLatest}`, '--load'];
|
|
183
|
+
const buildCommand = `docker buildx build \
|
|
184
|
+
--platform linux/amd64,linux/arm64 \
|
|
185
|
+
${tags.join(' \\\n ')} \
|
|
186
|
+
--progress=plain \
|
|
187
|
+
.`;
|
|
188
|
+
(0, child_process_1.execSync)(buildCommand, {
|
|
189
|
+
cwd: resolvedDestFolder,
|
|
190
|
+
stdio: 'inherit'
|
|
191
|
+
});
|
|
192
|
+
buildDockerSpinner.succeed('Docker image built successfully');
|
|
193
|
+
console.log(chalk_1.default.green('\n✅ Deployment completed successfully!'));
|
|
194
|
+
console.log(chalk_1.default.gray('Image details:'));
|
|
195
|
+
console.log(chalk_1.default.white(` Platforms: ${chalk_1.default.cyan('linux/amd64, linux/arm64')}`));
|
|
196
|
+
console.log(chalk_1.default.white(` Version: ${chalk_1.default.cyan(version)}`));
|
|
197
|
+
console.log(chalk_1.default.white(` Tags:`));
|
|
198
|
+
console.log(chalk_1.default.white(` - ${chalk_1.default.cyan(imageName)}`));
|
|
199
|
+
console.log(chalk_1.default.white(` - ${chalk_1.default.cyan(imageLatest)}`));
|
|
200
|
+
if (options.push) {
|
|
201
|
+
console.log(chalk_1.default.white(` Registry: ${chalk_1.default.cyan('Docker Hub')}`));
|
|
202
|
+
console.log(chalk_1.default.white(` URL: ${chalk_1.default.cyan(`https://hub.docker.com/r/${imageBase}`)}`));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
buildDockerSpinner.fail('Docker build failed');
|
|
207
|
+
console.error(chalk_1.default.red('\n❌ Error building Docker image'));
|
|
208
|
+
if (options.push) {
|
|
209
|
+
console.error(chalk_1.default.yellow('Make sure you are authenticated: docker login'));
|
|
210
|
+
}
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
27
213
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
spinner.text = 'Building application...';
|
|
32
|
-
await sleep(1500);
|
|
33
|
-
if (!options.skipTests) {
|
|
34
|
-
spinner.text = 'Running tests...';
|
|
35
|
-
await sleep(1000);
|
|
214
|
+
catch (error) {
|
|
215
|
+
console.error(chalk_1.default.red(`\n❌ Error: ${error.message}`));
|
|
216
|
+
process.exit(1);
|
|
36
217
|
}
|
|
37
|
-
await sleep(1000);
|
|
38
|
-
spinner.text = `Deploying to ${options.env}...`;
|
|
39
|
-
await sleep(2000);
|
|
40
|
-
spinner.succeed(chalk_1.default.green(`✅ Successfully deployed to ${options.env}!`));
|
|
41
|
-
console.log(chalk_1.default.gray('\n📦 Deployment URL:'), chalk_1.default.cyan(`https://${options.env}.codecrypto.com`));
|
|
42
218
|
});
|
|
43
|
-
|
|
44
|
-
|
|
219
|
+
// Helper functions
|
|
220
|
+
function findDirectory(root, name) {
|
|
221
|
+
try {
|
|
222
|
+
const entries = fs.readdirSync(root, { withFileTypes: true });
|
|
223
|
+
for (const entry of entries) {
|
|
224
|
+
const fullPath = path.join(root, entry.name);
|
|
225
|
+
if (entry.isDirectory()) {
|
|
226
|
+
if (entry.name === name) {
|
|
227
|
+
return fullPath;
|
|
228
|
+
}
|
|
229
|
+
const found = findDirectory(fullPath, name);
|
|
230
|
+
if (found)
|
|
231
|
+
return found;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
catch (error) {
|
|
236
|
+
// Ignore errors
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
function copyDirectory(src, dest) {
|
|
241
|
+
if (!fs.existsSync(dest)) {
|
|
242
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
243
|
+
}
|
|
244
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
245
|
+
for (const entry of entries) {
|
|
246
|
+
const srcPath = path.join(src, entry.name);
|
|
247
|
+
const destPath = path.join(dest, entry.name);
|
|
248
|
+
if (entry.isDirectory()) {
|
|
249
|
+
copyDirectory(srcPath, destPath);
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
fs.copyFileSync(srcPath, destPath);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
45
255
|
}
|
|
46
256
|
//# sourceMappingURL=deploy.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"deploy.js","sourceRoot":"","sources":["../../src/commands/deploy.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/commands/deploy.ts
CHANGED
|
@@ -1,49 +1,233 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import ora from 'ora';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
4
7
|
|
|
5
8
|
export const deployCommand = new Command('deploy')
|
|
6
|
-
.description('
|
|
7
|
-
.
|
|
8
|
-
.
|
|
9
|
-
.option('-
|
|
10
|
-
.option('--
|
|
11
|
-
.
|
|
12
|
-
|
|
13
|
-
console.log(chalk.blue('\n🚀 CodeCrypto Deployment\n'));
|
|
14
|
-
|
|
15
|
-
console.log(chalk.gray('Deployment Configuration:'));
|
|
16
|
-
console.log(chalk.white(` Environment: ${chalk.green(options.env)}`));
|
|
17
|
-
console.log(chalk.white(` Branch: ${chalk.green(options.branch)}`));
|
|
18
|
-
console.log(chalk.white(` Region: ${chalk.green(options.region)}`));
|
|
19
|
-
console.log(chalk.white(` Skip Tests: ${options.skipTests ? chalk.yellow('Yes') : chalk.green('No')}`));
|
|
20
|
-
console.log(chalk.white(` Dry Run: ${options.dryRun ? chalk.yellow('Yes') : chalk.green('No')}`));
|
|
21
|
-
|
|
22
|
-
if (options.dryRun) {
|
|
23
|
-
console.log(chalk.yellow('\n⚠️ Running in dry-run mode. No actual deployment will occur.\n'));
|
|
24
|
-
}
|
|
9
|
+
.description('Build and deploy Docker image for Next.js standalone application')
|
|
10
|
+
.argument('<project-path>', 'Path to the Next.js project directory')
|
|
11
|
+
.argument('[dest-folder]', 'Destination folder for Docker build context', './docker-build')
|
|
12
|
+
.option('--skip-build', 'Skip npm run build (assume already built)', false)
|
|
13
|
+
.option('--no-push', 'Build image but do not push to registry', false)
|
|
14
|
+
.action(async (projectPath: string, destFolder: string, options) => {
|
|
15
|
+
console.log(chalk.blue('\n🐳 CodeCrypto Docker Deployment\n'));
|
|
25
16
|
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
try {
|
|
18
|
+
// Validar que el proyecto existe
|
|
19
|
+
const resolvedProjectPath = path.resolve(projectPath);
|
|
20
|
+
if (!fs.existsSync(resolvedProjectPath)) {
|
|
21
|
+
console.error(chalk.red(`❌ Error: El directorio del proyecto no existe: ${resolvedProjectPath}`));
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
28
24
|
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
// Verificar que es un proyecto Next.js
|
|
26
|
+
const packageJsonPath = path.join(resolvedProjectPath, 'package.json');
|
|
27
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
28
|
+
console.error(chalk.red(`❌ Error: No se encontró package.json en el proyecto`));
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
32
|
+
// Leer versión del package.json
|
|
33
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
34
|
+
const version = packageJson.version || 'latest';
|
|
35
|
+
const projectName = path.basename(resolvedProjectPath);
|
|
36
|
+
const imageBase = `jviejo/${projectName}`;
|
|
37
|
+
const imageName = `${imageBase}:${version}`;
|
|
38
|
+
const imageLatest = `${imageBase}:latest`;
|
|
39
|
+
|
|
40
|
+
console.log(chalk.gray('Deployment Configuration:'));
|
|
41
|
+
console.log(chalk.white(` Project: ${chalk.green(projectName)}`));
|
|
42
|
+
console.log(chalk.white(` Version: ${chalk.green(version)}`));
|
|
43
|
+
console.log(chalk.white(` Image: ${chalk.green(imageName)}`));
|
|
44
|
+
console.log(chalk.white(` Latest: ${chalk.green(imageLatest)}`));
|
|
45
|
+
console.log(chalk.white(` Push to Registry: ${options.push ? chalk.green('Yes') : chalk.yellow('No')}\n`));
|
|
46
|
+
|
|
47
|
+
// Paso 1: Build del proyecto si es necesario
|
|
48
|
+
if (!options.skipBuild) {
|
|
49
|
+
const buildSpinner = ora('Building Next.js application...').start();
|
|
50
|
+
try {
|
|
51
|
+
execSync('npm run build', {
|
|
52
|
+
cwd: resolvedProjectPath,
|
|
53
|
+
stdio: 'pipe'
|
|
54
|
+
});
|
|
55
|
+
buildSpinner.succeed('Build completed successfully');
|
|
56
|
+
} catch (error) {
|
|
57
|
+
buildSpinner.fail('Build failed');
|
|
58
|
+
console.error(chalk.red('Error during build. Make sure you have "output: standalone" in next.config.ts'));
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Verificar que existe .next/standalone
|
|
64
|
+
const standalonePath = path.join(resolvedProjectPath, '.next', 'standalone');
|
|
65
|
+
if (!fs.existsSync(standalonePath)) {
|
|
66
|
+
console.error(chalk.red(`❌ Error: No se encontró .next/standalone`));
|
|
67
|
+
console.error(chalk.yellow('Asegúrate de tener "output: standalone" en next.config.ts y ejecutar "npm run build"'));
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Paso 2: Preparar directorio destino
|
|
72
|
+
const resolvedDestFolder = path.resolve(destFolder);
|
|
73
|
+
const standaloneDest = path.join(resolvedDestFolder, 'standalone');
|
|
74
|
+
|
|
75
|
+
const copySpinner = ora('Copying standalone files...').start();
|
|
76
|
+
if (fs.existsSync(resolvedDestFolder)) {
|
|
77
|
+
fs.rmSync(resolvedDestFolder, { recursive: true, force: true });
|
|
78
|
+
}
|
|
79
|
+
fs.mkdirSync(standaloneDest, { recursive: true });
|
|
80
|
+
|
|
81
|
+
// Copiar contenido de standalone
|
|
82
|
+
const nextDir = findDirectory(standalonePath, '.next');
|
|
83
|
+
if (nextDir) {
|
|
84
|
+
const parentDir = path.dirname(nextDir);
|
|
85
|
+
copyDirectory(parentDir, standaloneDest);
|
|
86
|
+
} else {
|
|
87
|
+
copyDirectory(standalonePath, standaloneDest);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Copiar .next/static
|
|
91
|
+
const staticPath = path.join(resolvedProjectPath, '.next', 'static');
|
|
92
|
+
if (fs.existsSync(staticPath)) {
|
|
93
|
+
const staticDest = path.join(standaloneDest, '.next', 'static');
|
|
94
|
+
fs.mkdirSync(staticDest, { recursive: true });
|
|
95
|
+
copyDirectory(staticPath, staticDest);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Copiar public
|
|
99
|
+
const publicPath = path.join(resolvedProjectPath, 'public');
|
|
100
|
+
if (fs.existsSync(publicPath)) {
|
|
101
|
+
const publicDest = path.join(standaloneDest, 'public');
|
|
102
|
+
fs.mkdirSync(publicDest, { recursive: true });
|
|
103
|
+
copyDirectory(publicPath, publicDest);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Verificar server.js
|
|
107
|
+
const serverJsPath = path.join(standaloneDest, 'server.js');
|
|
108
|
+
if (!fs.existsSync(serverJsPath)) {
|
|
109
|
+
copySpinner.fail('server.js not found in standalone');
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
copySpinner.succeed('Files copied successfully');
|
|
114
|
+
|
|
115
|
+
// Paso 3: Crear Dockerfile
|
|
116
|
+
const dockerfilePath = path.join(resolvedDestFolder, 'Dockerfile');
|
|
117
|
+
const dockerfileContent = `FROM node:20-alpine
|
|
118
|
+
WORKDIR /app
|
|
37
119
|
|
|
38
|
-
|
|
39
|
-
|
|
120
|
+
ENV NODE_ENV=production
|
|
121
|
+
ENV PORT=3000
|
|
40
122
|
|
|
41
|
-
|
|
42
|
-
|
|
123
|
+
# Copiamos el contenido de standalone
|
|
124
|
+
COPY standalone/ .
|
|
43
125
|
|
|
44
|
-
|
|
126
|
+
# Exponemos el puerto para Traefik
|
|
127
|
+
EXPOSE 3000
|
|
128
|
+
|
|
129
|
+
# Arrancamos directamente con Node
|
|
130
|
+
CMD ["node", "server.js"]
|
|
131
|
+
`;
|
|
132
|
+
fs.writeFileSync(dockerfilePath, dockerfileContent);
|
|
133
|
+
|
|
134
|
+
// Paso 4: Verificar/crear builder multi-plataforma
|
|
135
|
+
const builderSpinner = ora('Setting up multi-platform builder...').start();
|
|
136
|
+
try {
|
|
137
|
+
const builders = execSync('docker buildx ls', { encoding: 'utf-8' });
|
|
138
|
+
const builderName = 'multiarch-builder';
|
|
139
|
+
if (!builders.includes(builderName)) {
|
|
140
|
+
execSync(`docker buildx create --name ${builderName} --use --bootstrap`, { stdio: 'pipe' });
|
|
141
|
+
} else {
|
|
142
|
+
execSync(`docker buildx use ${builderName}`, { stdio: 'pipe' });
|
|
143
|
+
}
|
|
144
|
+
builderSpinner.succeed('Builder ready');
|
|
145
|
+
} catch (error) {
|
|
146
|
+
builderSpinner.fail('Failed to setup builder');
|
|
147
|
+
console.error(chalk.red('Error setting up Docker buildx builder'));
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Paso 5: Construir y hacer push
|
|
152
|
+
const buildDockerSpinner = ora('Building Docker image for multiple platforms...').start();
|
|
153
|
+
try {
|
|
154
|
+
const tags = options.push
|
|
155
|
+
? [`--tag ${imageName}`, `--tag ${imageLatest}`, '--push']
|
|
156
|
+
: [`--tag ${imageName}`, `--tag ${imageLatest}`, '--load'];
|
|
157
|
+
|
|
158
|
+
const buildCommand = `docker buildx build \
|
|
159
|
+
--platform linux/amd64,linux/arm64 \
|
|
160
|
+
${tags.join(' \\\n ')} \
|
|
161
|
+
--progress=plain \
|
|
162
|
+
.`;
|
|
163
|
+
|
|
164
|
+
execSync(buildCommand, {
|
|
165
|
+
cwd: resolvedDestFolder,
|
|
166
|
+
stdio: 'inherit'
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
buildDockerSpinner.succeed('Docker image built successfully');
|
|
170
|
+
|
|
171
|
+
console.log(chalk.green('\n✅ Deployment completed successfully!'));
|
|
172
|
+
console.log(chalk.gray('Image details:'));
|
|
173
|
+
console.log(chalk.white(` Platforms: ${chalk.cyan('linux/amd64, linux/arm64')}`));
|
|
174
|
+
console.log(chalk.white(` Version: ${chalk.cyan(version)}`));
|
|
175
|
+
console.log(chalk.white(` Tags:`));
|
|
176
|
+
console.log(chalk.white(` - ${chalk.cyan(imageName)}`));
|
|
177
|
+
console.log(chalk.white(` - ${chalk.cyan(imageLatest)}`));
|
|
178
|
+
if (options.push) {
|
|
179
|
+
console.log(chalk.white(` Registry: ${chalk.cyan('Docker Hub')}`));
|
|
180
|
+
console.log(chalk.white(` URL: ${chalk.cyan(`https://hub.docker.com/r/${imageBase}`)}`));
|
|
181
|
+
}
|
|
182
|
+
} catch (error) {
|
|
183
|
+
buildDockerSpinner.fail('Docker build failed');
|
|
184
|
+
console.error(chalk.red('\n❌ Error building Docker image'));
|
|
185
|
+
if (options.push) {
|
|
186
|
+
console.error(chalk.yellow('Make sure you are authenticated: docker login'));
|
|
187
|
+
}
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
} catch (error: any) {
|
|
192
|
+
console.error(chalk.red(`\n❌ Error: ${error.message}`));
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
45
195
|
});
|
|
46
196
|
|
|
47
|
-
|
|
48
|
-
|
|
197
|
+
// Helper functions
|
|
198
|
+
function findDirectory(root: string, name: string): string | null {
|
|
199
|
+
try {
|
|
200
|
+
const entries = fs.readdirSync(root, { withFileTypes: true });
|
|
201
|
+
for (const entry of entries) {
|
|
202
|
+
const fullPath = path.join(root, entry.name);
|
|
203
|
+
if (entry.isDirectory()) {
|
|
204
|
+
if (entry.name === name) {
|
|
205
|
+
return fullPath;
|
|
206
|
+
}
|
|
207
|
+
const found = findDirectory(fullPath, name);
|
|
208
|
+
if (found) return found;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
} catch (error) {
|
|
212
|
+
// Ignore errors
|
|
213
|
+
}
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function copyDirectory(src: string, dest: string): void {
|
|
218
|
+
if (!fs.existsSync(dest)) {
|
|
219
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
223
|
+
for (const entry of entries) {
|
|
224
|
+
const srcPath = path.join(src, entry.name);
|
|
225
|
+
const destPath = path.join(dest, entry.name);
|
|
226
|
+
|
|
227
|
+
if (entry.isDirectory()) {
|
|
228
|
+
copyDirectory(srcPath, destPath);
|
|
229
|
+
} else {
|
|
230
|
+
fs.copyFileSync(srcPath, destPath);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
49
233
|
}
|
package/codecrypto-cli-1.0.0.tgz
DELETED
|
Binary file
|