{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# La llave para entrar a GitHub\n",
    "\n",
    "Por qué tu contraseña no sirve y qué es eso de la llave SSH\n",
    "\n",
    "Cuaderno de soluciones del capítulo 13 de **Git desde cero**, de Miss Yera.\n",
    "\n",
    "Corre de arriba abajo. Si lo abres en Google Colab no necesitas instalar nada.\n",
    "\n",
    "Capítulo completo: https://missyera.com/guias/git-desde-cero/llave-para-entrar/\n",
    "\n",
    "Este es el cuaderno de **soluciones**. Trae el código de cada ejercicio, la\n",
    "explicación de la trampa y la respuesta del quiz. Si vienes del cuaderno de\n",
    "práctica sin haberlo intentado, vuelve 🙂"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Este capítulo existe porque es donde se atasca casi todo el mundo. Llegas\n",
    "con tu repositorio listo, escribes `git push`, GitHub te pide usuario\n",
    "y contraseña, se las das, y te contesta que no 😤\n",
    "\n",
    "No estás haciendo nada mal. Es que **GitHub dejó de aceptar\n",
    "contraseñas en agosto de 2021**, y el mensaje de error no lo dice con\n",
    "esas palabras."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Una nota sobre este capítulo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "En todo el libro los comandos se ejecutan antes de publicarse. Este es el\n",
    "único donde `ssh-keygen` no se ejecuta, por dos razones: no viene con\n",
    "Git (es de OpenSSH, otro programa) y generar llaves de verdad para meterlas en\n",
    "un libro sería justo lo que este capítulo te pide no hacer 🔐\n",
    "\n",
    "Lo que sí se ejecuta aquí es todo lo del remoto, que es la otra mitad del\n",
    "problema y la que más gente tiene mal configurada."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El error que te trajo aquí"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "$ git push origin main\n",
    "Username for 'https://github.com': tuusuario\n",
    "Password for 'https://tuusuario@github.com':\n",
    "remote: Support for password authentication was removed on August 13, 2021.\n",
    "fatal: Authentication failed for 'https://github.com/tuusuario/ventas-miss-yera.git/'\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Ese es el mensaje completo, y dentro está la explicación. Lo que falta es\n",
    "qué hacer 🔑"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Las dos formas de entrar"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "|  | Llave SSH | Token |\n",
    "|---|---|---|\n",
    "| Se configura | Una vez y nunca más | Cada vez que caduca |\n",
    "| Caduca | No | Sí, tú eliges cuándo |\n",
    "| La dirección empieza por | `git@github.com:` | `https://github.com/` |\n",
    "| Dónde se guarda | Un archivo en tu carpeta personal | En el gestor de claves del sistema |\n",
    "| Cuándo la prefiero | Tu máquina de siempre | Una máquina prestada o un servidor |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Qué es una llave, en cristiano"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Una llave SSH son dos archivos que nacen juntos y se necesitan:\n",
    "\n",
    "- La **privada**, que es tu llave de casa. Nunca sale de tu\n",
    "máquina, nunca se pega en ningún sitio, nunca se manda por WhatsApp.\n",
    "\n",
    "- La **pública**, que es la cerradura. Esa sí se reparte, y es la\n",
    "que pegas en GitHub.\n",
    "\n",
    "Cualquiera con la cerradura puede comprobar que tu llave encaja, pero de la\n",
    "cerradura no se puede deducir la llave. Por eso la pública se puede publicar sin\n",
    "que pase nada 🔒"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Generarla"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Un solo comando. El `-C` del final es una etiqueta para que dentro\n",
    "de dos años sepas de qué máquina era:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "$ ssh-keygen -t ed25519 -C \"mi-laptop\"\n",
    "\n",
    "Generating public/private ed25519 key pair.\n",
    "Enter file in which to save the key (~/.ssh/id_ed25519):\n",
    "Enter passphrase (empty for no passphrase):\n",
    "Enter same passphrase again:\n",
    "Your identification has been saved in ~/.ssh/id_ed25519\n",
    "Your public key has been saved in ~/.ssh/id_ed25519.pub\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Te pregunta dos cosas. Dónde guardarla: dale enter, el sitio por defecto está\n",
    "bien. Y una frase de seguridad: es opcional y sirve por si te roban la laptop.\n",
    "\n",
    "Fíjate en las dos últimas líneas, porque ahí está todo el capítulo:\n",
    "**dos archivos, el mismo nombre, y uno con `.pub`**."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Cuál es cuál, sin abrir nada peligroso"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Los dos archivos se distinguen por cómo empiezan, y eso se puede mirar sin\n",
    "enseñar nada. Vamos a hacer un par de archivos de mentira, escritos a mano, solo\n",
    "para practicar la comprobación:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "mkdir practica-llaves\n",
    "cd practica-llaves\n",
    "printf 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI+ejemplo+de+mentira mi-laptop\\n' > ejemplo.pub\n",
    "printf -- '-----BEGIN OPENSSH PRIVATE KEY-----\\nesto-no-es-una-llave-real\\n-----END OPENSSH PRIVATE KEY-----\\n' > ejemplo\n",
    "ls\n",
    "cut -c1-11 ejemplo.pub\n",
    "head -1 ejemplo"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "La pública empieza por `ssh-ed25519` y cabe en una línea. La\n",
    "privada empieza por `-----BEGIN` y ocupa varias.\n",
    "\n",
    "**Si el archivo que abriste empieza por `-----BEGIN`,\n",
    "ciérralo.** Ese no es el que se pega 🚫"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Dónde se pega la pública"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El contenido entero del archivo `.pub`, de\n",
    "`ssh-ed25519` hasta la etiqueta del final, va en GitHub en\n",
    "*Settings > SSH and GPG keys > New SSH key*. Una vez. Para\n",
    "siempre 📌"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "cat ejemplo.pub\n",
    "cut -d' ' -f3 ejemplo.pub"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esa tercera palabra es la etiqueta que pusiste con `-C`. Con tres\n",
    "o cuatro máquinas es lo único que te dice cuál puedes borrar sin dejarte fuera a\n",
    "ti misma 🏷️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Cambiar la dirección de HTTPS a SSH"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Aquí está la parte que más gente tiene mal. Si tu proyecto ya está conectado\n",
    "por `https`, la llave no sirve de nada hasta que le cambies la\n",
    "dirección al remoto. Y no hay que clonar de nuevo:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "cd ..\n",
    "mkdir ventas-miss-yera\n",
    "cd ventas-miss-yera\n",
    "git init -q\n",
    "printf 'ciudad,monto\\nLima,1200\\nArequipa,890\\n' > ventas.csv\n",
    "git add ventas.csv\n",
    "git commit -q -m \"Primeras ventas de Lima y Arequipa\"\n",
    "git remote add origin https://github.com/tuusuario/ventas-miss-yera.git\n",
    "git remote -v"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Esa dirección con `https://` va a pedir token cada vez. Se cambia\n",
    "con una línea:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "git remote set-url origin git@github.com:tuusuario/ventas-miss-yera.git\n",
    "git remote -v\n",
    "git log --oneline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "El repositorio no se entera. Solo cambió la puerta por la que entra, y tu\n",
    "commit sigue exactamente donde estaba 🚪"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## El error de agregar dos veces el mismo remoto"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "git remote add origin git@github.com:tuusuario/otro-proyecto.git"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Y la queja que tiene que salir es esta:\n",
    "\n",
    "```\n",
    "error: remote origin already exists.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Git te frena porque `add` es para crear y ya existe. Cuando lo que\n",
    "quieres es cambiarlo, el comando es `set-url` 🔁"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comprobar que funciona"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Cuando ya pegaste la pública en GitHub, este comando te lo confirma:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "```\n",
    "$ ssh -T git@github.com\n",
    "Hi tuusuario! You've successfully authenticated, but GitHub does not provide shell access.\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Dice \"does not provide shell access\" y suena a error. No lo es: significa\n",
    "\"te reconozco, pero aquí no hay nada más que hacer\". Es el mensaje de éxito 😄"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### La trampa\n",
    "\n",
    "Sigues un tutorial para conectar tu máquina con GitHub, generas la llave y copias el archivo que te pide pegar en la página.\n",
    "\n",
    "```\n",
    "$ ls ~/.ssh\n",
    "id_ed25519  id_ed25519.pub\n",
    "\n",
    "$ cat ~/.ssh/id_ed25519       # y este es el que pegaste\n",
    "-----BEGIN OPENSSH PRIVATE KEY-----\n",
    "```\n",
    "\n",
    "**Qué está mal**\n",
    "\n",
    "Pegaste la llave privada, que es justo la que nunca sale de tu máquina 🔐\n",
    "\n",
    "Son dos archivos con el mismo nombre y la diferencia es el `.pub` del final: ese es el que se pega, el otro se queda. La privada es literalmente la llave de tu casa y la pública es la cerradura, que puedes repartir sin miedo.\n",
    "\n",
    "Si te pasó, no sirve de nada borrar el comentario donde la pegaste. Hay que **generar un par nuevo** y quitar el viejo de GitHub, igual que cambiarías la chapa de la puerta.\n",
    "\n",
    "La regla para no equivocarse nunca: **si el archivo no termina en `.pub`, no se copia a ningún lado**."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Comprueba que se entendió"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Comprueba que lo tienes\n",
    "\n",
    "Un compañero te pide tu llave para que puedas entrar al repositorio del equipo. ¿Qué le mandas?\n",
    "\n",
    "a) El archivo `id_ed25519`\n",
    "\n",
    "b) El archivo `id_ed25519.pub`\n",
    "\n",
    "c) Los dos, por si acaso\n",
    "\n",
    "d) Mi contraseña de GitHub\n",
    "\n",
    "---\n",
    "\n",
    "**La correcta es la b.**\n",
    "\n",
    "*a)* Ese es el privado. Mandarlo es entregar la llave de tu casa, y hay que generar un par nuevo.\n",
    "\n",
    "*c)* El privado nunca sale de tu máquina, ni siquiera acompañado.\n",
    "\n",
    "*d)* GitHub no acepta contraseñas desde 2021, y además una contraseña no se comparte nunca.\n",
    "\n",
    "Si no termina en `.pub`, no se manda 🔐"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ejercicios"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 1. Distingue una llave de la otra\n",
    "\n",
    "Con los dos archivos de práctica, comprueba cuál se puede\n",
    "enseñar mirando solo el principio."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "cd ../practica-llaves\n",
    "cut -c1-11 ejemplo.pub\n",
    "head -1 ejemplo\n",
    "echo \"solo el .pub se copia\"\n",
    "ssh-ed25519\n",
    "-----BEGIN OPENSSH PRIVATE KEY-----\n",
    "solo el .pub se copia\n",
    "Una línea corta que empieza por `ssh-` contra un bloque que\n",
    "empieza por `-----BEGIN`."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 2. Conecta un proyecto por HTTPS\n",
    "\n",
    "Crea el repositorio de canales y apúntale la dirección\n",
    "web."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "cd ..\n",
    "mkdir canales-venta\n",
    "cd canales-venta\n",
    "git init -q\n",
    "printf 'canal,monto\\nBodegas,4200\\n' > canales.csv\n",
    "git add canales.csv\n",
    "git commit -q -m \"Ventas del canal bodegas\"\n",
    "git remote add origin https://github.com/tuusuario/canales-venta.git\n",
    "git remote -v\n",
    "origin\thttps://github.com/tuusuario/canales-venta.git (fetch)\n",
    "origin\thttps://github.com/tuusuario/canales-venta.git (push)\n",
    "Ese `https://` del principio es el que va a pedir token."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 3. Pásalo a llave\n",
    "\n",
    "Cambia la dirección del remoto a la forma con SSH."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "git remote set-url origin git@github.com:tuusuario/canales-venta.git\n",
    "git remote -v\n",
    "origin\thttps://github.com/tuusuario/canales-venta.git (fetch)\n",
    "origin\thttps://github.com/tuusuario/canales-venta.git (push)\n",
    "`set-url` es el comando entero. No hay que clonar de nuevo ni\n",
    "perder nada."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 4. Comprueba que la historia no se movió\n",
    "\n",
    "Mira que el commit sigue ahí después de cambiar la\n",
    "puerta."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "git log --oneline\n",
    "cat canales.csv\n",
    "5d7b179 Ventas del canal bodegas\n",
    "canal,monto\n",
    "Bodegas,4200\n",
    "La forma de autenticarse no toca el repositorio. Son cosas separadas 🧩"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Intenta agregarlo otra vez\n",
    "\n",
    "Prueba a usar `add` cuando el remoto ya\n",
    "existe."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "git remote add origin git@github.com:tuusuario/otro.git\n",
    "error: remote origin already exists.\n",
    "Git no pisa una configuración que ya está. Para cambiarla,\n",
    "`set-url` 🛡️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 6. Guarda un segundo destino\n",
    "\n",
    "Un repositorio puede tener varios remotos con apodos\n",
    "distintos, útil cuando el mismo proyecto vive en dos sitios."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "git remote add respaldo git@gitlab.com:tuusuario/canales-venta.git\n",
    "git remote -v\n",
    "origin\thttps://github.com/tuusuario/canales-venta.git (fetch)\n",
    "origin\thttps://github.com/tuusuario/canales-venta.git (push)\n",
    "respaldo\tgit@gitlab.com:tuusuario/canales-venta.git (fetch)\n",
    "respaldo\tgit@gitlab.com:tuusuario/canales-venta.git (push)\n",
    "Dos destinos, dos apodos. `origin` no tiene nada de especial más\n",
    "allá de la costumbre 🏷️"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 7. Quita el que no usas\n",
    "\n",
    "Borra el remoto de respaldo y comprueba que el proyecto\n",
    "sigue igual."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "git remote remove respaldo\n",
    "git remote -v\n",
    "git log --oneline\n",
    "origin\thttps://github.com/tuusuario/canales-venta.git (fetch)\n",
    "origin\thttps://github.com/tuusuario/canales-venta.git (push)\n",
    "5d7b179 Ventas del canal bodegas\n",
    "Los remotos son configuración, no historia. Se ponen y se quitan sin que\n",
    "pase nada 🧷"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Lo que te llevas"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Tu contraseña no entra, entra una llave. Y si tu remoto sigue\n",
    "apuntando a `https://`, la llave no se usa: cámbialo con\n",
    "`set-url`.**"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "---\n",
    "\n",
    "Ese era el capítulo 13 de **Git desde cero**. El texto completo, con las salidas de cada bloque, está en https://missyera.com/guias/git-desde-cero/llave-para-entrar/\n",
    "\n",
    "Que tengas lindo día! 🌸"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
