{
 "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 práctica 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",
    "Los ejercicios están al final y traen una celda vacía debajo de cada uno. Las\n",
    "respuestas viven en el cuaderno de soluciones, y merece la pena pelearse un\n",
    "rato antes de abrirlo 💛"
   ]
  },
  {
   "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?** La respuesta está en el cuaderno de soluciones. Míralo tú primero."
   ]
  },
  {
   "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"
   ]
  },
  {
   "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": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# tu turno"
   ]
  },
  {
   "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": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# tu turno"
   ]
  },
  {
   "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": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# tu turno"
   ]
  },
  {
   "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": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# tu turno"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 5. Intenta agregarlo otra vez\n",
    "\n",
    "Prueba a usar `add` cuando el remoto ya\n",
    "existe."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# tu turno"
   ]
  },
  {
   "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": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# tu turno"
   ]
  },
  {
   "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": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# tu turno"
   ]
  },
  {
   "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
}
