@icarusmx/creta 1.2.1 → 1.3.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/bin/creta.js +191 -52
- package/lib/pr-tutorial.js +604 -0
- package/package.json +1 -1
package/bin/creta.js
CHANGED
|
@@ -6,6 +6,7 @@ import fs from 'fs'
|
|
|
6
6
|
import path from 'path'
|
|
7
7
|
import { fileURLToPath } from 'url'
|
|
8
8
|
import { CretaCodeSession } from '../lib/session.js'
|
|
9
|
+
import { PullRequestTutorial } from '../lib/pr-tutorial.js'
|
|
9
10
|
import { Lesson1SystemDecomposition } from '../lessons/lesson1-system-decomposition.js'
|
|
10
11
|
import { Lesson2ObjectRequests } from '../lessons/lesson2-object-requests.js'
|
|
11
12
|
import { Lesson3OnlyWay } from '../lessons/lesson3-only-way.js'
|
|
@@ -621,8 +622,7 @@ async function startMainMenuInteractive() {
|
|
|
621
622
|
|
|
622
623
|
const options = [
|
|
623
624
|
{ id: 1, title: "Aprender conceptos", description: "Explora los enunciados fundamentales" },
|
|
624
|
-
{ id: 2, title: "Construir proyectos", description: "
|
|
625
|
-
{ id: 3, title: "Crea una pull-request", description: "Aprende el flujo de contribución con Git" }
|
|
625
|
+
{ id: 2, title: "Construir proyectos", description: "Practica con proyectos reales" }
|
|
626
626
|
]
|
|
627
627
|
|
|
628
628
|
let selectedIndex = 0
|
|
@@ -693,8 +693,6 @@ Salgamos de este laberinto 🏛️
|
|
|
693
693
|
startEnunciadosSelector().then(resolve)
|
|
694
694
|
} else if (selectedOption.id === 2) {
|
|
695
695
|
startProyectosSelector().then(resolve)
|
|
696
|
-
} else if (selectedOption.id === 3) {
|
|
697
|
-
startPullRequestTutorial().then(resolve)
|
|
698
696
|
}
|
|
699
697
|
return
|
|
700
698
|
}
|
|
@@ -729,11 +727,10 @@ async function startMainMenuFallback() {
|
|
|
729
727
|
console.log("Te ofrecemos las siguientes opciones:")
|
|
730
728
|
console.log("")
|
|
731
729
|
console.log("1. Aprender conceptos - Explora los enunciados fundamentales")
|
|
732
|
-
console.log("2. Construir proyectos -
|
|
733
|
-
console.log("3. Crea una pull-request - Aprende el flujo de contribución con Git")
|
|
730
|
+
console.log("2. Construir proyectos - Practica con proyectos reales")
|
|
734
731
|
console.log("")
|
|
735
732
|
|
|
736
|
-
const respuesta = await askQuestion("Elige una opción (1-
|
|
733
|
+
const respuesta = await askQuestion("Elige una opción (1-2) o 'q' para salir: ")
|
|
737
734
|
|
|
738
735
|
if (respuesta.toLowerCase() === 'q') {
|
|
739
736
|
console.log("Hecho con <3 por icarus.mx")
|
|
@@ -749,11 +746,8 @@ async function startMainMenuFallback() {
|
|
|
749
746
|
} else if (opcionSeleccionada === 2) {
|
|
750
747
|
rl.close()
|
|
751
748
|
await startProyectosSelector()
|
|
752
|
-
} else if (opcionSeleccionada === 3) {
|
|
753
|
-
rl.close()
|
|
754
|
-
await startPullRequestTutorial()
|
|
755
749
|
} else {
|
|
756
|
-
console.log("❌ Opción no válida. Elige 1
|
|
750
|
+
console.log("❌ Opción no válida. Elige 1 o 2.")
|
|
757
751
|
rl.close()
|
|
758
752
|
}
|
|
759
753
|
|
|
@@ -1240,14 +1234,12 @@ async function startProyectosSelectorInteractive() {
|
|
|
1240
1234
|
throw new Error('setRawMode not available')
|
|
1241
1235
|
}
|
|
1242
1236
|
|
|
1243
|
-
console.log("\n🚀 Proyectos
|
|
1244
|
-
console.log("
|
|
1237
|
+
console.log("\n🚀 Construir Proyectos")
|
|
1238
|
+
console.log("Elige qué proyecto quieres construir:")
|
|
1245
1239
|
|
|
1246
1240
|
const projects = [
|
|
1247
|
-
{ id: 1, title: "
|
|
1248
|
-
{ id: 2, title: "
|
|
1249
|
-
{ id: 3, title: "🔓 Portafolio Nivel 2", description: "Navbar + hero", level: 2 },
|
|
1250
|
-
{ id: 4, title: "🔓 Portafolio Nivel 3", description: "Solución completa", level: 3 }
|
|
1241
|
+
{ id: 1, title: "🔀 Construye una pull-request", description: "Aprende Git y GitHub paso a paso", type: 'pr' },
|
|
1242
|
+
{ id: 2, title: "🎨 Construye tu portafolio", description: "Crea tu sitio web personal", type: 'portfolio' }
|
|
1251
1243
|
]
|
|
1252
1244
|
|
|
1253
1245
|
let selectedIndex = 0
|
|
@@ -1266,8 +1258,8 @@ async function startProyectosSelectorInteractive() {
|
|
|
1266
1258
|
process.stdout.write('\x1b[2J')
|
|
1267
1259
|
process.stdout.write('\x1b[H')
|
|
1268
1260
|
|
|
1269
|
-
console.log("🚀 Proyectos
|
|
1270
|
-
console.log("
|
|
1261
|
+
console.log("🚀 Construir Proyectos")
|
|
1262
|
+
console.log("Elige qué proyecto quieres construir:")
|
|
1271
1263
|
|
|
1272
1264
|
projects.forEach((project, index) => {
|
|
1273
1265
|
const isSelected = index === selectedIndex
|
|
@@ -1300,17 +1292,15 @@ async function startProyectosSelectorInteractive() {
|
|
|
1300
1292
|
process.stdin.removeListener('data', onKeyPress)
|
|
1301
1293
|
|
|
1302
1294
|
const selectedProject = projects[selectedIndex]
|
|
1303
|
-
const level = selectedProject.level
|
|
1304
1295
|
|
|
1305
1296
|
// Clear screen
|
|
1306
1297
|
process.stdout.write('\x1b[2J')
|
|
1307
1298
|
process.stdout.write('\x1b[H')
|
|
1308
1299
|
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
createPortfolioProject(level).then(resolve)
|
|
1300
|
+
if (selectedProject.type === 'pr') {
|
|
1301
|
+
startPullRequestTutorial().then(resolve)
|
|
1302
|
+
} else if (selectedProject.type === 'portfolio') {
|
|
1303
|
+
startPortfolioSelector().then(resolve)
|
|
1314
1304
|
}
|
|
1315
1305
|
return
|
|
1316
1306
|
}
|
|
@@ -1370,8 +1360,162 @@ async function startProyectosSelectorFallback() {
|
|
|
1370
1360
|
}
|
|
1371
1361
|
|
|
1372
1362
|
try {
|
|
1373
|
-
console.log("\n🚀 Proyectos
|
|
1374
|
-
console.log("
|
|
1363
|
+
console.log("\n🚀 Construir Proyectos")
|
|
1364
|
+
console.log("Elige qué proyecto quieres construir:")
|
|
1365
|
+
console.log("")
|
|
1366
|
+
console.log("1. 🔀 Construye una pull-request - Aprende Git y GitHub paso a paso")
|
|
1367
|
+
console.log("2. 🎨 Construye tu portafolio - Crea tu sitio web personal")
|
|
1368
|
+
console.log("")
|
|
1369
|
+
|
|
1370
|
+
const respuesta = await askQuestion("Elige una opción (1-2) o 'q' para salir: ")
|
|
1371
|
+
|
|
1372
|
+
if (respuesta.toLowerCase() === 'q') {
|
|
1373
|
+
console.log("Hecho con <3 por icarus.mx")
|
|
1374
|
+
rl.close()
|
|
1375
|
+
return
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
const opcionSeleccionada = parseInt(respuesta)
|
|
1379
|
+
|
|
1380
|
+
if (opcionSeleccionada === 1) {
|
|
1381
|
+
rl.close()
|
|
1382
|
+
await startPullRequestTutorial()
|
|
1383
|
+
} else if (opcionSeleccionada === 2) {
|
|
1384
|
+
rl.close()
|
|
1385
|
+
await startPortfolioSelector()
|
|
1386
|
+
} else {
|
|
1387
|
+
console.log("❌ Opción no válida. Elige 1 o 2.")
|
|
1388
|
+
rl.close()
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
} catch (error) {
|
|
1392
|
+
console.error('Error:', error.message)
|
|
1393
|
+
rl.close()
|
|
1394
|
+
process.exit(1)
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
async function startPortfolioSelector() {
|
|
1399
|
+
// Always try interactive mode first, fall back only if it fails
|
|
1400
|
+
try {
|
|
1401
|
+
return await startPortfolioSelectorInteractive()
|
|
1402
|
+
} catch (error) {
|
|
1403
|
+
// If interactive mode fails, fall back to numbered selection
|
|
1404
|
+
console.log('\nModo interactivo no disponible, usando selección numérica...\n')
|
|
1405
|
+
return await startPortfolioSelectorFallback()
|
|
1406
|
+
}
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
async function startPortfolioSelectorInteractive() {
|
|
1410
|
+
// Check if setRawMode is available before trying to use it
|
|
1411
|
+
if (typeof process.stdin.setRawMode !== 'function') {
|
|
1412
|
+
throw new Error('setRawMode not available')
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
console.log("\n🎨 Construye tu Portafolio")
|
|
1416
|
+
console.log("Elige el nivel:")
|
|
1417
|
+
|
|
1418
|
+
const projects = [
|
|
1419
|
+
{ id: 1, title: "🎨 Portafolio Personal", description: "Reto completo", level: 0 },
|
|
1420
|
+
{ id: 2, title: "🔓 Portafolio Nivel 1", description: "Solo navbar", level: 1 },
|
|
1421
|
+
{ id: 3, title: "🔓 Portafolio Nivel 2", description: "Navbar + hero", level: 2 },
|
|
1422
|
+
{ id: 4, title: "🔓 Portafolio Nivel 3", description: "Solución completa", level: 3 }
|
|
1423
|
+
]
|
|
1424
|
+
|
|
1425
|
+
let selectedIndex = 0
|
|
1426
|
+
|
|
1427
|
+
// Enable raw mode to capture arrow keys
|
|
1428
|
+
try {
|
|
1429
|
+
process.stdin.setRawMode(true)
|
|
1430
|
+
process.stdin.resume()
|
|
1431
|
+
process.stdin.setEncoding('utf8')
|
|
1432
|
+
} catch (error) {
|
|
1433
|
+
throw new Error('Failed to enable raw mode: ' + error.message)
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
const renderOptions = () => {
|
|
1437
|
+
// Clear previous output and move cursor to top
|
|
1438
|
+
process.stdout.write('\x1b[2J')
|
|
1439
|
+
process.stdout.write('\x1b[H')
|
|
1440
|
+
|
|
1441
|
+
console.log("🎨 Construye tu Portafolio")
|
|
1442
|
+
console.log("Elige el nivel:")
|
|
1443
|
+
|
|
1444
|
+
projects.forEach((project, index) => {
|
|
1445
|
+
const isSelected = index === selectedIndex
|
|
1446
|
+
const prefix = isSelected ? '▶ ' : ' '
|
|
1447
|
+
const highlight = isSelected ? '\x1b[36m' : '\x1b[37m' // cyan for selected, white for normal
|
|
1448
|
+
const reset = '\x1b[0m'
|
|
1449
|
+
|
|
1450
|
+
console.log(`${highlight}${prefix}${project.title}${reset}`)
|
|
1451
|
+
if (isSelected) {
|
|
1452
|
+
console.log(`${highlight} ${project.description}${reset}`)
|
|
1453
|
+
}
|
|
1454
|
+
console.log("")
|
|
1455
|
+
})
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
return new Promise((resolve) => {
|
|
1459
|
+
renderOptions()
|
|
1460
|
+
|
|
1461
|
+
const onKeyPress = (key) => {
|
|
1462
|
+
if (key === 'q' || key === '\x03') { // q or Ctrl+C
|
|
1463
|
+
process.stdin.setRawMode(false)
|
|
1464
|
+
process.stdin.removeListener('data', onKeyPress)
|
|
1465
|
+
console.log("\nHecho con <3 por icarus.mx")
|
|
1466
|
+
resolve()
|
|
1467
|
+
return
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
if (key === '\r' || key === '\n') { // Enter
|
|
1471
|
+
process.stdin.setRawMode(false)
|
|
1472
|
+
process.stdin.removeListener('data', onKeyPress)
|
|
1473
|
+
|
|
1474
|
+
const selectedProject = projects[selectedIndex]
|
|
1475
|
+
const level = selectedProject.level
|
|
1476
|
+
|
|
1477
|
+
// Clear screen
|
|
1478
|
+
process.stdout.write('\x1b[2J')
|
|
1479
|
+
process.stdout.write('\x1b[H')
|
|
1480
|
+
|
|
1481
|
+
// Check if we're in an existing Creta project
|
|
1482
|
+
if (level > 0 && isInCretaProject()) {
|
|
1483
|
+
unstuckProject(level).then(resolve)
|
|
1484
|
+
} else {
|
|
1485
|
+
createPortfolioProject(level).then(resolve)
|
|
1486
|
+
}
|
|
1487
|
+
return
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
// Handle arrow keys (escape sequences)
|
|
1491
|
+
if (key === '\u001b[A') { // Up arrow
|
|
1492
|
+
selectedIndex = selectedIndex > 0 ? selectedIndex - 1 : projects.length - 1
|
|
1493
|
+
renderOptions()
|
|
1494
|
+
} else if (key === '\u001b[B') { // Down arrow
|
|
1495
|
+
selectedIndex = selectedIndex < projects.length - 1 ? selectedIndex + 1 : 0
|
|
1496
|
+
renderOptions()
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
process.stdin.on('data', onKeyPress)
|
|
1501
|
+
})
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
async function startPortfolioSelectorFallback() {
|
|
1505
|
+
const rl = createInterface({
|
|
1506
|
+
input: process.stdin,
|
|
1507
|
+
output: process.stdout
|
|
1508
|
+
})
|
|
1509
|
+
|
|
1510
|
+
const askQuestion = (question) => {
|
|
1511
|
+
return new Promise((resolve) => {
|
|
1512
|
+
rl.question(question, resolve)
|
|
1513
|
+
})
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
try {
|
|
1517
|
+
console.log("\n🎨 Construye tu Portafolio")
|
|
1518
|
+
console.log("Elige el nivel:")
|
|
1375
1519
|
console.log("")
|
|
1376
1520
|
console.log("1. 🎨 Portafolio Personal - Reto completo")
|
|
1377
1521
|
console.log("2. 🔓 Portafolio Nivel 1 - Solo navbar")
|
|
@@ -1414,33 +1558,28 @@ async function startProyectosSelectorFallback() {
|
|
|
1414
1558
|
}
|
|
1415
1559
|
|
|
1416
1560
|
async function startPullRequestTutorial() {
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
console.log("")
|
|
1421
|
-
console.log("📚 Aprenderás:")
|
|
1422
|
-
console.log(" • Cómo hacer fork de un repositorio")
|
|
1423
|
-
console.log(" • Crear una rama (branch) para tu cambio")
|
|
1424
|
-
console.log(" • Hacer commits con buenas prácticas")
|
|
1425
|
-
console.log(" • Crear una pull request profesional")
|
|
1426
|
-
console.log("")
|
|
1427
|
-
console.log("🚧 [PRÓXIMAMENTE]")
|
|
1428
|
-
console.log("")
|
|
1429
|
-
console.log("Esta funcionalidad está en desarrollo.")
|
|
1430
|
-
console.log("Pronto podrás practicar el flujo completo de contribución.")
|
|
1431
|
-
console.log("")
|
|
1561
|
+
try {
|
|
1562
|
+
const tutorial = new PullRequestTutorial()
|
|
1563
|
+
await tutorial.start()
|
|
1432
1564
|
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1565
|
+
// Volver al menú principal después de completar el tutorial
|
|
1566
|
+
await returnToMainMenu()
|
|
1567
|
+
} catch (error) {
|
|
1568
|
+
console.error("\n❌ Error al ejecutar el tutorial:", error.message)
|
|
1569
|
+
console.log("\nSi el problema persiste, contacta al equipo de Creta.")
|
|
1437
1570
|
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
resolve()
|
|
1571
|
+
const rl = createInterface({
|
|
1572
|
+
input: process.stdin,
|
|
1573
|
+
output: process.stdout
|
|
1442
1574
|
})
|
|
1443
|
-
})
|
|
1444
1575
|
|
|
1445
|
-
|
|
1576
|
+
await new Promise((resolve) => {
|
|
1577
|
+
rl.question("\nPresiona Enter para volver al menú principal...", () => {
|
|
1578
|
+
rl.close()
|
|
1579
|
+
resolve()
|
|
1580
|
+
})
|
|
1581
|
+
})
|
|
1582
|
+
|
|
1583
|
+
await startMainMenu()
|
|
1584
|
+
}
|
|
1446
1585
|
}
|
|
@@ -0,0 +1,604 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// Tutorial interactivo: Crea una Pull Request
|
|
4
|
+
// Guía paso a paso para aprender el flujo completo de contribución con Git
|
|
5
|
+
|
|
6
|
+
import { createInterface } from 'readline'
|
|
7
|
+
import { execSync } from 'child_process'
|
|
8
|
+
import fs from 'fs'
|
|
9
|
+
import path from 'path'
|
|
10
|
+
|
|
11
|
+
export class PullRequestTutorial {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.rl = createInterface({
|
|
14
|
+
input: process.stdin,
|
|
15
|
+
output: process.stdout
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
// Repo de práctica (usaremos el mismo repo de Creta)
|
|
19
|
+
this.practiceRepo = 'icarusmx/creta-practice'
|
|
20
|
+
this.repoUrl = `https://github.com/${this.practiceRepo}`
|
|
21
|
+
this.projectDir = null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async start() {
|
|
25
|
+
await this.introduction()
|
|
26
|
+
await this.checkPrerequisites()
|
|
27
|
+
await this.step1_ForkExplanation()
|
|
28
|
+
await this.step2_CloneRepo()
|
|
29
|
+
await this.step3_CreateBranch()
|
|
30
|
+
await this.step4_MakeChanges()
|
|
31
|
+
await this.step5_CommitChanges()
|
|
32
|
+
await this.step6_PushBranch()
|
|
33
|
+
await this.step7_CreatePR()
|
|
34
|
+
await this.conclusion()
|
|
35
|
+
|
|
36
|
+
this.rl.close()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async introduction() {
|
|
40
|
+
console.clear()
|
|
41
|
+
console.log("🔀 Tutorial: Crea tu primera Pull Request")
|
|
42
|
+
console.log("=" .repeat(50))
|
|
43
|
+
console.log("")
|
|
44
|
+
console.log("📚 En este tutorial aprenderás:")
|
|
45
|
+
console.log("")
|
|
46
|
+
console.log("1. Qué es una Pull Request y por qué son importantes")
|
|
47
|
+
console.log("2. Cómo hacer fork de un repositorio")
|
|
48
|
+
console.log("3. Cómo crear una rama (branch) para tu cambio")
|
|
49
|
+
console.log("4. Cómo hacer commits con buenas prácticas")
|
|
50
|
+
console.log("5. Cómo crear una Pull Request profesional")
|
|
51
|
+
console.log("")
|
|
52
|
+
console.log("⏱️ Duración estimada: 10-15 minutos")
|
|
53
|
+
console.log("")
|
|
54
|
+
console.log("💡 Tip: Tendrás que ejecutar comandos reales en tu terminal.")
|
|
55
|
+
console.log(" ¡No te preocupes! Te guiaremos paso a paso.")
|
|
56
|
+
|
|
57
|
+
await this.waitForEnter("\nPresiona Enter para comenzar...")
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async checkPrerequisites() {
|
|
61
|
+
console.clear()
|
|
62
|
+
console.log("🔍 Verificando prerequisitos...")
|
|
63
|
+
console.log("=" .repeat(50))
|
|
64
|
+
console.log("")
|
|
65
|
+
|
|
66
|
+
// Check git
|
|
67
|
+
console.log("Verificando Git...")
|
|
68
|
+
try {
|
|
69
|
+
const gitVersion = execSync('git --version', { encoding: 'utf8' }).trim()
|
|
70
|
+
console.log(`✅ ${gitVersion}`)
|
|
71
|
+
} catch (error) {
|
|
72
|
+
console.log("❌ Git no está instalado")
|
|
73
|
+
console.log("")
|
|
74
|
+
console.log("Por favor instala Git:")
|
|
75
|
+
console.log(" macOS: brew install git")
|
|
76
|
+
console.log(" Linux: sudo apt install git")
|
|
77
|
+
console.log(" Windows: https://git-scm.com/download/win")
|
|
78
|
+
console.log("")
|
|
79
|
+
await this.waitForEnter("Presiona Enter para salir...")
|
|
80
|
+
process.exit(1)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.log("")
|
|
84
|
+
|
|
85
|
+
// Check GitHub CLI
|
|
86
|
+
console.log("Verificando GitHub CLI (gh)...")
|
|
87
|
+
try {
|
|
88
|
+
const ghVersion = execSync('gh --version', { encoding: 'utf8' }).trim().split('\n')[0]
|
|
89
|
+
console.log(`✅ ${ghVersion}`)
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.log("❌ GitHub CLI (gh) no está instalado")
|
|
92
|
+
console.log("")
|
|
93
|
+
console.log("Por favor instala gh:")
|
|
94
|
+
console.log(" macOS: brew install gh")
|
|
95
|
+
console.log(" Linux: Ver https://github.com/cli/cli/blob/trunk/docs/install_linux.md")
|
|
96
|
+
console.log(" Windows: Ver https://github.com/cli/cli#installation")
|
|
97
|
+
console.log("")
|
|
98
|
+
console.log("Después de instalar, ejecuta: gh auth login")
|
|
99
|
+
console.log("")
|
|
100
|
+
await this.waitForEnter("Presiona Enter para salir...")
|
|
101
|
+
process.exit(1)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
console.log("")
|
|
105
|
+
|
|
106
|
+
// Check gh authentication
|
|
107
|
+
console.log("Verificando autenticación en GitHub...")
|
|
108
|
+
try {
|
|
109
|
+
execSync('gh auth status', { encoding: 'utf8', stdio: 'pipe' })
|
|
110
|
+
console.log("✅ Autenticado en GitHub")
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.log("❌ No estás autenticado en GitHub")
|
|
113
|
+
console.log("")
|
|
114
|
+
console.log("Por favor ejecuta: gh auth login")
|
|
115
|
+
console.log("Y sigue las instrucciones para autenticarte.")
|
|
116
|
+
console.log("")
|
|
117
|
+
await this.waitForEnter("Presiona Enter para salir...")
|
|
118
|
+
process.exit(1)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
console.log("")
|
|
122
|
+
console.log("🎉 ¡Todo listo! Tienes todas las herramientas necesarias.")
|
|
123
|
+
|
|
124
|
+
await this.waitForEnter("\nPresiona Enter para continuar...")
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async step1_ForkExplanation() {
|
|
128
|
+
console.clear()
|
|
129
|
+
console.log("Paso 1: Entender el Fork")
|
|
130
|
+
console.log("=" .repeat(50))
|
|
131
|
+
console.log("")
|
|
132
|
+
console.log("🤔 ¿Qué es un fork?")
|
|
133
|
+
console.log("")
|
|
134
|
+
console.log("Un fork es una COPIA de un repositorio que queda en TU cuenta de GitHub.")
|
|
135
|
+
console.log("Es como sacar una fotocopia de un libro para poder escribir en él.")
|
|
136
|
+
console.log("")
|
|
137
|
+
console.log("¿Por qué necesitamos hacer fork?")
|
|
138
|
+
console.log(" • No tienes permisos para modificar el repositorio original")
|
|
139
|
+
console.log(" • Trabajas en tu copia sin afectar el original")
|
|
140
|
+
console.log(" • Después propones tus cambios mediante una Pull Request")
|
|
141
|
+
|
|
142
|
+
await this.waitForEnter("\nPresiona Enter para hacer el fork...")
|
|
143
|
+
|
|
144
|
+
console.log("")
|
|
145
|
+
console.log("📋 Vamos a hacer fork del repositorio de práctica de Creta:")
|
|
146
|
+
console.log(` ${this.repoUrl}`)
|
|
147
|
+
console.log("")
|
|
148
|
+
console.log("Ejecutando: gh repo fork icarusmx/creta-practice --clone=false")
|
|
149
|
+
console.log("")
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
// Primero verificar si el repo existe o si ya tiene un fork
|
|
153
|
+
try {
|
|
154
|
+
const checkFork = execSync(`gh repo view ${this.practiceRepo}`, {
|
|
155
|
+
encoding: 'utf8',
|
|
156
|
+
stdio: 'pipe'
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
// Si llegamos aquí, el repo existe, intentar hacer fork
|
|
160
|
+
try {
|
|
161
|
+
execSync(`gh repo fork ${this.practiceRepo} --clone=false`, {
|
|
162
|
+
encoding: 'utf8',
|
|
163
|
+
stdio: 'inherit'
|
|
164
|
+
})
|
|
165
|
+
console.log("✅ Fork creado exitosamente")
|
|
166
|
+
} catch (forkError) {
|
|
167
|
+
// Probablemente ya tiene un fork
|
|
168
|
+
console.log("ℹ️ Ya tienes un fork de este repositorio")
|
|
169
|
+
}
|
|
170
|
+
} catch (repoError) {
|
|
171
|
+
console.log("⚠️ El repositorio de práctica aún no existe.")
|
|
172
|
+
console.log("")
|
|
173
|
+
console.log("Por ahora, practicaremos con el repositorio de Creta CLI:")
|
|
174
|
+
this.practiceRepo = 'icarusmx/creta'
|
|
175
|
+
this.repoUrl = `https://github.com/${this.practiceRepo}`
|
|
176
|
+
|
|
177
|
+
console.log("")
|
|
178
|
+
console.log(`Haciendo fork de: ${this.repoUrl}`)
|
|
179
|
+
console.log("")
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
execSync(`gh repo fork ${this.practiceRepo} --clone=false`, {
|
|
183
|
+
encoding: 'utf8',
|
|
184
|
+
stdio: 'inherit'
|
|
185
|
+
})
|
|
186
|
+
console.log("✅ Fork creado exitosamente")
|
|
187
|
+
} catch (forkError) {
|
|
188
|
+
console.log("ℹ️ Ya tienes un fork de este repositorio")
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
} catch (error) {
|
|
192
|
+
console.log("❌ Error al hacer fork:", error.message)
|
|
193
|
+
console.log("")
|
|
194
|
+
console.log("Puedes hacer fork manualmente en:")
|
|
195
|
+
console.log(`${this.repoUrl}`)
|
|
196
|
+
console.log("(Click en el botón 'Fork' en la esquina superior derecha)")
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
await this.waitForEnter("\nPresiona Enter para continuar al siguiente paso...")
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async step2_CloneRepo() {
|
|
203
|
+
console.clear()
|
|
204
|
+
console.log("Paso 2: Clonar tu Fork")
|
|
205
|
+
console.log("=" .repeat(50))
|
|
206
|
+
console.log("")
|
|
207
|
+
console.log("🤔 ¿Qué es clonar?")
|
|
208
|
+
console.log("")
|
|
209
|
+
console.log("Clonar es DESCARGAR una copia del repositorio a tu computadora.")
|
|
210
|
+
console.log("Ahora trabajarás en tu máquina local, no en GitHub.")
|
|
211
|
+
console.log("")
|
|
212
|
+
console.log("¿Por qué clonar tu fork y no el original?")
|
|
213
|
+
console.log(" • Tu fork está en tu cuenta, puedes hacer push a él")
|
|
214
|
+
console.log(" • El original no aceptaría tus cambios directos")
|
|
215
|
+
|
|
216
|
+
await this.waitForEnter("\nPresiona Enter para clonar...")
|
|
217
|
+
|
|
218
|
+
console.log("")
|
|
219
|
+
console.log("🔧 Ahora necesitamos clonar TU fork (no el original)")
|
|
220
|
+
console.log("")
|
|
221
|
+
|
|
222
|
+
// Obtener el username de GitHub
|
|
223
|
+
let username
|
|
224
|
+
try {
|
|
225
|
+
const authStatus = execSync('gh api user --jq .login', { encoding: 'utf8' }).trim()
|
|
226
|
+
username = authStatus
|
|
227
|
+
console.log(`Tu usuario de GitHub: ${username}`)
|
|
228
|
+
} catch (error) {
|
|
229
|
+
console.log("No pudimos obtener tu usuario automáticamente.")
|
|
230
|
+
username = await this.askQuestion("¿Cuál es tu usuario de GitHub? ")
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const forkUrl = `https://github.com/${username}/${this.practiceRepo.split('/')[1]}`
|
|
234
|
+
const projectName = this.practiceRepo.split('/')[1]
|
|
235
|
+
|
|
236
|
+
console.log("")
|
|
237
|
+
console.log(`Clonando: ${forkUrl}`)
|
|
238
|
+
console.log("")
|
|
239
|
+
|
|
240
|
+
try {
|
|
241
|
+
// Verificar si ya existe el directorio
|
|
242
|
+
if (fs.existsSync(projectName)) {
|
|
243
|
+
console.log(`ℹ️ El directorio '${projectName}' ya existe.`)
|
|
244
|
+
const useExisting = await this.askQuestion("¿Quieres usar este directorio? (s/n): ")
|
|
245
|
+
|
|
246
|
+
if (useExisting.toLowerCase() === 's') {
|
|
247
|
+
this.projectDir = path.resolve(projectName)
|
|
248
|
+
console.log(`✅ Usando directorio existente: ${this.projectDir}`)
|
|
249
|
+
} else {
|
|
250
|
+
console.log("Por favor elimina o renombra el directorio y vuelve a intentar.")
|
|
251
|
+
process.exit(0)
|
|
252
|
+
}
|
|
253
|
+
} else {
|
|
254
|
+
console.log(`Ejecutando: git clone ${forkUrl}`)
|
|
255
|
+
execSync(`git clone ${forkUrl}`, { encoding: 'utf8', stdio: 'inherit' })
|
|
256
|
+
this.projectDir = path.resolve(projectName)
|
|
257
|
+
console.log(`✅ Repositorio clonado en: ${this.projectDir}`)
|
|
258
|
+
}
|
|
259
|
+
} catch (error) {
|
|
260
|
+
console.log("❌ Error al clonar:", error.message)
|
|
261
|
+
process.exit(1)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
await this.waitForEnter("\nPresiona Enter para continuar al siguiente paso...")
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async step3_CreateBranch() {
|
|
268
|
+
console.clear()
|
|
269
|
+
console.log("Paso 3: Crear una Rama (Branch)")
|
|
270
|
+
console.log("=" .repeat(50))
|
|
271
|
+
console.log("")
|
|
272
|
+
console.log("🤔 ¿Qué es una rama?")
|
|
273
|
+
console.log("")
|
|
274
|
+
console.log("Una rama es una LÍNEA DE DESARROLLO independiente.")
|
|
275
|
+
console.log("Es como trabajar en una copia paralela del código.")
|
|
276
|
+
console.log("")
|
|
277
|
+
console.log("¿Por qué crear una rama?")
|
|
278
|
+
console.log(" • No modificas la rama principal (main) directamente")
|
|
279
|
+
console.log(" • Puedes experimentar sin romper nada")
|
|
280
|
+
console.log(" • Facilita el code review")
|
|
281
|
+
console.log(" • Es la práctica estándar en equipos profesionales")
|
|
282
|
+
|
|
283
|
+
await this.waitForEnter("\nPresiona Enter para crear tu rama...")
|
|
284
|
+
|
|
285
|
+
console.log("")
|
|
286
|
+
console.log("💡 Reglas para nombres de ramas:")
|
|
287
|
+
console.log(" • Descriptivos y cortos")
|
|
288
|
+
console.log(" • En minúsculas, separados por guiones")
|
|
289
|
+
console.log(" • Ejemplos: fix-navbar, add-login, update-readme")
|
|
290
|
+
console.log("")
|
|
291
|
+
|
|
292
|
+
const defaultBranchName = "mi-primera-contribucion"
|
|
293
|
+
const branchName = await this.askQuestion(`Nombre de tu rama [${defaultBranchName}]: `) || defaultBranchName
|
|
294
|
+
|
|
295
|
+
console.log("")
|
|
296
|
+
console.log(`Ejecutando: git checkout -b ${branchName}`)
|
|
297
|
+
console.log("")
|
|
298
|
+
|
|
299
|
+
try {
|
|
300
|
+
process.chdir(this.projectDir)
|
|
301
|
+
execSync(`git checkout -b ${branchName}`, { encoding: 'utf8', stdio: 'inherit' })
|
|
302
|
+
console.log(`✅ Rama '${branchName}' creada y seleccionada`)
|
|
303
|
+
} catch (error) {
|
|
304
|
+
console.log("❌ Error al crear rama:", error.message)
|
|
305
|
+
process.exit(1)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
await this.waitForEnter("\nPresiona Enter para continuar al siguiente paso...")
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async step4_MakeChanges() {
|
|
312
|
+
console.clear()
|
|
313
|
+
console.log("Paso 4: Hacer Cambios")
|
|
314
|
+
console.log("=" .repeat(50))
|
|
315
|
+
console.log("")
|
|
316
|
+
console.log("🤔 ¿Qué cambios haremos?")
|
|
317
|
+
console.log("")
|
|
318
|
+
console.log("Vamos a agregar tu nombre a un archivo de contribuidores.")
|
|
319
|
+
console.log("Este es un cambio simple y seguro para practicar.")
|
|
320
|
+
console.log("")
|
|
321
|
+
|
|
322
|
+
await this.waitForEnter("Presiona Enter para hacer el cambio...")
|
|
323
|
+
|
|
324
|
+
console.log("")
|
|
325
|
+
|
|
326
|
+
const contributorsFile = path.join(this.projectDir, 'CONTRIBUTORS.md')
|
|
327
|
+
|
|
328
|
+
// Crear archivo si no existe
|
|
329
|
+
if (!fs.existsSync(contributorsFile)) {
|
|
330
|
+
console.log("Creando archivo CONTRIBUTORS.md...")
|
|
331
|
+
fs.writeFileSync(contributorsFile, '# Contribuidores\n\nGracias a estas personas por contribuir al proyecto:\n\n')
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const name = await this.askQuestion("¿Cómo te llamas? ")
|
|
335
|
+
const date = new Date().toISOString().split('T')[0]
|
|
336
|
+
|
|
337
|
+
console.log("")
|
|
338
|
+
console.log("Agregando tu nombre al archivo...")
|
|
339
|
+
|
|
340
|
+
try {
|
|
341
|
+
const content = fs.readFileSync(contributorsFile, 'utf8')
|
|
342
|
+
const newContent = content + `- ${name} (${date})\n`
|
|
343
|
+
fs.writeFileSync(contributorsFile, newContent)
|
|
344
|
+
console.log("✅ Cambio realizado exitosamente")
|
|
345
|
+
console.log("")
|
|
346
|
+
console.log(`Tu nombre fue agregado a: ${contributorsFile}`)
|
|
347
|
+
} catch (error) {
|
|
348
|
+
console.log("❌ Error al modificar archivo:", error.message)
|
|
349
|
+
process.exit(1)
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
console.log("")
|
|
353
|
+
console.log("🔍 Verificando cambios...")
|
|
354
|
+
console.log("")
|
|
355
|
+
console.log("Ejecutando: git status")
|
|
356
|
+
console.log("")
|
|
357
|
+
|
|
358
|
+
try {
|
|
359
|
+
execSync('git status', { encoding: 'utf8', stdio: 'inherit' })
|
|
360
|
+
} catch (error) {
|
|
361
|
+
// git status siempre funciona
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
await this.waitForEnter("\nPresiona Enter para continuar al siguiente paso...")
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
async step5_CommitChanges() {
|
|
368
|
+
console.clear()
|
|
369
|
+
console.log("Paso 5: Hacer Commit de los Cambios")
|
|
370
|
+
console.log("=" .repeat(50))
|
|
371
|
+
console.log("")
|
|
372
|
+
console.log("🤔 ¿Qué es un commit?")
|
|
373
|
+
console.log("")
|
|
374
|
+
console.log("Un commit es un PUNTO DE GUARDADO en la historia del proyecto.")
|
|
375
|
+
console.log("Es como tomar una fotografía del estado actual del código.")
|
|
376
|
+
console.log("")
|
|
377
|
+
console.log("Cada commit tiene:")
|
|
378
|
+
console.log(" • Los cambios que hiciste")
|
|
379
|
+
console.log(" • Un mensaje describiendo QUÉ y POR QUÉ")
|
|
380
|
+
console.log(" • Tu nombre y la fecha")
|
|
381
|
+
|
|
382
|
+
await this.waitForEnter("\nPresiona Enter para hacer el commit...")
|
|
383
|
+
|
|
384
|
+
console.log("")
|
|
385
|
+
console.log("📝 Primero agregamos el archivo al staging area:")
|
|
386
|
+
console.log("Ejecutando: git add CONTRIBUTORS.md")
|
|
387
|
+
console.log("")
|
|
388
|
+
|
|
389
|
+
try {
|
|
390
|
+
execSync('git add CONTRIBUTORS.md', { encoding: 'utf8', stdio: 'inherit' })
|
|
391
|
+
console.log("✅ Archivo agregado al staging area")
|
|
392
|
+
} catch (error) {
|
|
393
|
+
console.log("❌ Error al agregar archivo:", error.message)
|
|
394
|
+
process.exit(1)
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
console.log("")
|
|
398
|
+
console.log("💡 Ahora escribimos el mensaje del commit")
|
|
399
|
+
console.log("")
|
|
400
|
+
console.log("Reglas para buenos mensajes:")
|
|
401
|
+
console.log(" • Primera línea: resumen corto (50 caracteres)")
|
|
402
|
+
console.log(" • Usa imperativo: 'Agrega' no 'Agregué' ni 'Agregando'")
|
|
403
|
+
console.log(" • Sé específico: di QUÉ cambió y POR QUÉ")
|
|
404
|
+
console.log("")
|
|
405
|
+
|
|
406
|
+
const name = await this.askQuestion("¿Cómo te llamas? (para el mensaje del commit): ")
|
|
407
|
+
const commitMessage = `Agrega ${name} a la lista de contribuidores`
|
|
408
|
+
|
|
409
|
+
console.log("")
|
|
410
|
+
console.log(`Mensaje del commit: "${commitMessage}"`)
|
|
411
|
+
console.log("")
|
|
412
|
+
console.log(`Ejecutando: git commit -m "${commitMessage}"`)
|
|
413
|
+
console.log("")
|
|
414
|
+
|
|
415
|
+
try {
|
|
416
|
+
execSync(`git commit -m "${commitMessage}"`, { encoding: 'utf8', stdio: 'inherit' })
|
|
417
|
+
console.log("✅ Commit creado exitosamente")
|
|
418
|
+
} catch (error) {
|
|
419
|
+
console.log("❌ Error al crear commit:", error.message)
|
|
420
|
+
process.exit(1)
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
console.log("")
|
|
424
|
+
console.log("🎉 ¡Tu cambio ahora es parte de la historia del repositorio!")
|
|
425
|
+
console.log("")
|
|
426
|
+
console.log("Puedes verlo con: git log --oneline")
|
|
427
|
+
|
|
428
|
+
await this.waitForEnter("\nPresiona Enter para continuar al siguiente paso...")
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
async step6_PushBranch() {
|
|
432
|
+
console.clear()
|
|
433
|
+
console.log("Paso 6: Subir los Cambios a GitHub (Push)")
|
|
434
|
+
console.log("=" .repeat(50))
|
|
435
|
+
console.log("")
|
|
436
|
+
console.log("🤔 ¿Qué es push?")
|
|
437
|
+
console.log("")
|
|
438
|
+
console.log("Push es SUBIR tus commits locales a GitHub.")
|
|
439
|
+
console.log("Hasta ahora todo estaba solo en tu computadora.")
|
|
440
|
+
console.log("")
|
|
441
|
+
console.log("¿Por qué hacer push?")
|
|
442
|
+
console.log(" • Respalda tu trabajo en la nube")
|
|
443
|
+
console.log(" • Otros pueden ver tus cambios")
|
|
444
|
+
console.log(" • Es necesario para crear la Pull Request")
|
|
445
|
+
|
|
446
|
+
await this.waitForEnter("\nPresiona Enter para hacer push...")
|
|
447
|
+
|
|
448
|
+
console.log("")
|
|
449
|
+
console.log("🚀 Subiendo tu rama a GitHub...")
|
|
450
|
+
console.log("")
|
|
451
|
+
|
|
452
|
+
// Obtener el nombre de la rama actual
|
|
453
|
+
let branchName
|
|
454
|
+
try {
|
|
455
|
+
branchName = execSync('git branch --show-current', { encoding: 'utf8' }).trim()
|
|
456
|
+
} catch (error) {
|
|
457
|
+
branchName = 'tu-rama'
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
console.log(`Ejecutando: git push origin ${branchName}`)
|
|
461
|
+
console.log("")
|
|
462
|
+
|
|
463
|
+
try {
|
|
464
|
+
execSync(`git push -u origin ${branchName}`, { encoding: 'utf8', stdio: 'inherit' })
|
|
465
|
+
console.log("")
|
|
466
|
+
console.log("✅ Cambios subidos exitosamente a GitHub")
|
|
467
|
+
} catch (error) {
|
|
468
|
+
console.log("❌ Error al hacer push:", error.message)
|
|
469
|
+
console.log("")
|
|
470
|
+
console.log("Si el error es de autenticación, verifica que estés autenticado con gh.")
|
|
471
|
+
process.exit(1)
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
console.log("")
|
|
475
|
+
console.log("🎉 ¡Tu rama ya está en GitHub!")
|
|
476
|
+
console.log("Ahora cualquiera puede ver tus cambios.")
|
|
477
|
+
|
|
478
|
+
await this.waitForEnter("\nPresiona Enter para continuar al siguiente paso...")
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
async step7_CreatePR() {
|
|
482
|
+
console.clear()
|
|
483
|
+
console.log("Paso 7: Crear la Pull Request")
|
|
484
|
+
console.log("=" .repeat(50))
|
|
485
|
+
console.log("")
|
|
486
|
+
console.log("🤔 ¿Qué es una Pull Request (PR)?")
|
|
487
|
+
console.log("")
|
|
488
|
+
console.log("Una PR es una PROPUESTA de cambios al repositorio original.")
|
|
489
|
+
console.log("Es como decir: 'Mira los cambios que hice, ¿los aceptas?'")
|
|
490
|
+
console.log("")
|
|
491
|
+
console.log("¿Qué pasa después de crear una PR?")
|
|
492
|
+
console.log(" • Los mantenedores revisan tu código")
|
|
493
|
+
console.log(" • Pueden pedirte cambios")
|
|
494
|
+
console.log(" • Si todo está bien, la aceptan (merge)")
|
|
495
|
+
console.log(" • Tus cambios quedan en el proyecto original")
|
|
496
|
+
|
|
497
|
+
await this.waitForEnter("\nPresiona Enter para crear la PR...")
|
|
498
|
+
|
|
499
|
+
console.log("")
|
|
500
|
+
console.log("📝 Vamos a crear la PR con información descriptiva")
|
|
501
|
+
console.log("")
|
|
502
|
+
|
|
503
|
+
const prTitle = await this.askQuestion("Título de la PR [Agrega nuevo contribuidor]: ") || "Agrega nuevo contribuidor"
|
|
504
|
+
|
|
505
|
+
console.log("")
|
|
506
|
+
console.log("💡 El cuerpo de la PR debe explicar:")
|
|
507
|
+
console.log(" • QUÉ cambios hiciste")
|
|
508
|
+
console.log(" • POR QUÉ los hiciste")
|
|
509
|
+
console.log(" • CÓMO probaste que funcionan")
|
|
510
|
+
console.log("")
|
|
511
|
+
|
|
512
|
+
const prBody = `## Descripción
|
|
513
|
+
|
|
514
|
+
Agregué mi nombre a la lista de contribuidores como parte del tutorial de Creta.
|
|
515
|
+
|
|
516
|
+
## Checklist
|
|
517
|
+
|
|
518
|
+
- [x] El código sigue las convenciones del proyecto
|
|
519
|
+
- [x] He probado mis cambios localmente
|
|
520
|
+
- [x] Los cambios no rompen funcionalidad existente
|
|
521
|
+
|
|
522
|
+
---
|
|
523
|
+
|
|
524
|
+
🏛️ Creado con [Creta](https://github.com/icarusmx/creta) - Tutorial de Pull Requests`
|
|
525
|
+
|
|
526
|
+
console.log("")
|
|
527
|
+
console.log(`Ejecutando: gh pr create --title "${prTitle}" --body "..."`)
|
|
528
|
+
console.log("")
|
|
529
|
+
|
|
530
|
+
try {
|
|
531
|
+
execSync(`gh pr create --title "${prTitle}" --body "${prBody}"`, {
|
|
532
|
+
encoding: 'utf8',
|
|
533
|
+
stdio: 'inherit'
|
|
534
|
+
})
|
|
535
|
+
console.log("")
|
|
536
|
+
console.log("✅ Pull Request creada exitosamente")
|
|
537
|
+
} catch (error) {
|
|
538
|
+
console.log("❌ Error al crear PR:", error.message)
|
|
539
|
+
console.log("")
|
|
540
|
+
console.log("Puedes crear la PR manualmente en GitHub:")
|
|
541
|
+
console.log(`${this.repoUrl}/pulls`)
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
await this.waitForEnter("\nPresiona Enter para ver la conclusión...")
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
async conclusion() {
|
|
548
|
+
console.clear()
|
|
549
|
+
console.log("🎉 ¡Felicidades! Completaste el Tutorial")
|
|
550
|
+
console.log("=" .repeat(50))
|
|
551
|
+
console.log("")
|
|
552
|
+
console.log("📚 Lo que aprendiste:")
|
|
553
|
+
console.log("")
|
|
554
|
+
console.log("✅ Qué es un fork y cómo hacerlo")
|
|
555
|
+
console.log("✅ Cómo clonar un repositorio")
|
|
556
|
+
console.log("✅ Cómo crear y trabajar en ramas")
|
|
557
|
+
console.log("✅ Cómo hacer commits con buenos mensajes")
|
|
558
|
+
console.log("✅ Cómo subir cambios con push")
|
|
559
|
+
console.log("✅ Cómo crear una Pull Request profesional")
|
|
560
|
+
console.log("")
|
|
561
|
+
console.log("🚀 Ahora estás listo para:")
|
|
562
|
+
console.log("")
|
|
563
|
+
console.log("• Contribuir a proyectos open source")
|
|
564
|
+
console.log("• Trabajar en equipos de desarrollo")
|
|
565
|
+
console.log("• Seguir las mejores prácticas de Git/GitHub")
|
|
566
|
+
console.log("")
|
|
567
|
+
console.log("💡 Próximos pasos:")
|
|
568
|
+
console.log("")
|
|
569
|
+
console.log("1. Espera feedback en tu PR")
|
|
570
|
+
console.log("2. Practica haciendo más PRs")
|
|
571
|
+
console.log("3. Busca proyectos open source para contribuir")
|
|
572
|
+
console.log("")
|
|
573
|
+
console.log("📖 Recursos adicionales:")
|
|
574
|
+
console.log("")
|
|
575
|
+
console.log("• GitHub Docs: https://docs.github.com/pull-requests")
|
|
576
|
+
console.log("• Git Book: https://git-scm.com/book/es")
|
|
577
|
+
console.log("• First Contributions: https://firstcontributions.github.io")
|
|
578
|
+
|
|
579
|
+
await this.waitForEnter("\n✨ Presiona Enter para finalizar...")
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
async waitForEnter(message) {
|
|
583
|
+
return new Promise((resolve) => {
|
|
584
|
+
this.rl.question(message, () => {
|
|
585
|
+
console.log("")
|
|
586
|
+
resolve()
|
|
587
|
+
})
|
|
588
|
+
})
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
async askQuestion(question) {
|
|
592
|
+
return new Promise((resolve) => {
|
|
593
|
+
this.rl.question(question, (answer) => {
|
|
594
|
+
resolve(answer)
|
|
595
|
+
})
|
|
596
|
+
})
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// Para usar el tutorial independientemente
|
|
601
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
602
|
+
const tutorial = new PullRequestTutorial()
|
|
603
|
+
await tutorial.start()
|
|
604
|
+
}
|