Lemoncode blog


Artículos, ideas, desarrollos, notas y mucho más

Hola Docker CI / CD - Travis

Introducción

El proceso de ejecutar de forma manual builds, test y despliegues puede convertirse en una pesadilla y en un proceso propenso a errores:

  • Los errores humanos suceden a menudo, ocurren con facilidad.
  • Cuando los proyectos crecen, los procesos de build tienden a complicarse.
  • Lidiar con diferentes versiones entre entornos no es algo sencillo.
  • Cambiar de versiones no es un proceso fácil en muchos casos.

¿Y si...?:

  • Los procesos de build pudieran automatizarse, incluyendo indicadores que nos informen sobre si el proceso se ha completado (por ejemplo, ejecutando baterías de test).
  • Los procesos de build pudieran arrancar cuando ocurriera un merge para una rama concreta, o se realizará un push al repositorio remoto.
  • Cada proceso de build generase una imagen de Docker, y de este modo no nos tendríamos que preocupar por configuraciones en el servidor, o no tener instaladas las versiones exactas de software.
  • No ocupáramos demasiado espacio en disco ya que comenzaríamos desde una imagen base de Docker que tuviera el sistema operativo y software preinstalado.
  • Pudiéramos subirlo a un cloud hub registry, permitiendo ser consumido desde cualquier proveedor local o de cloud (Amazon, Azure, Google Cloud...)
  • Pudiéramos intercambiar fácilmente distintas versiones de build.

Todos estos son los beneficios que podemos obtener al configurar un servidor de CI/CD con Travis (CI proviene de Continuos Integration, CD significa Continuos Delivery) y mezclarlo con la tecnología de contenedores Docker.

Este es el segundo artículo de la serie Hola Docker, el primer artículo está disponible en este enlace.

TL; DR;

En este artículo usaremos Travis para activar automáticamente los siguientes procesos en cada merge to master o pull request:

  • Levantaremos una instancia linux + nodejs limpia.
  • Descargaremos el código fuente del repositorio.
  • Instalaremos las dependencias necesarias del proyecto.
  • Ejecutaremos la batería de test asociados.
  • Generaremos la imagen de docker, incluyendo la build de producción.
  • Etiquetaremos y publicaremos la imagen generada en el Docker Hub Registry.

Esta configuración la usaremos tanto para el proyecto de Front End como para el proyecto de Back End.

fullflow.jpg

Si quieres profundizar en los detalles, sigue leyendo :)

Agenda

Pasos que vamos a seguir:

  • Presentaremos el proyecto de ejemplo.
  • Trabajaremos manualmente con Docker Hub.
  • Enlazaremos nuestro proyecto de GitHub con Travis.
  • Estableceremos los pasos para un proceso de CI.
  • Comprobaremos el resultado de nuestras baterías de test.
  • Verificaremos si el contenedor de docker puede ser generado.
  • Subiremos una imagen a Docker Hub Registry.
  • Comprobaremos si nuestro proceso de Continuous Delivery ha tenido éxito, consumiendo nuestras imágenes desde un fichero docker compose.

Proyecto de ejemplo

Tomaremos como ejemplo una aplicación de chat. La aplicación está dividida en dos partes: el cliente (Front End) y el servidor (Back End) que serán encapsulados en contenedores Docker (a esta acción la denominaremos "dockerizar" de ahora en adelante), y desplegados mediante Docker Containers.

chat-app.gif

Ya tenemos listos un par de repositorios que juntos crean la aplicación de chat:

Siguiendo la aproximación actual de despliegue (revisar artículo previo de esta serie), una tercera parte será incluida también: un balanceador de carga. Su responsabilidad será direccionar el tráfico al front o back dependiendo de la url de la petición. El balanceador de carga también será "dockerizado" y desplegado usando Docker Container.

Docker Hub

En nuestro último artículo tomamos como punto de partida una imagen de Ubuntu + Nodejs; fue estupendo recuperarlas desde Docker Hub y tener el control de que versión estábamos descargando.

¿No sería maravilloso ser capaces de subir nuestras imágenes a Docker Hub Registry, incluyendo la versión? Eso es lo que Docker Hub nos ofrece: puedes crear tu propia cuenta y subir las imágenes de Docker.

Ventajas de usar Docker Hub:

  • Podemos mantener varias versiones de las imágenes de nuestros contenedores (genial para soportar distintos entornos, A/B testing, canary deployment, green blue deployment, rolling back, etc...).
  • Dado que Docker se crea a partir de una serie de capas, nuestra imagen custom no ocupará más espacio del necesario, es decir, un contenedor construido sobre un contenedor linux no desplegará el SO, sólo apuntará a la imagen previa.
  • Como la imagen del contenedor ya esta en la nube, desplegar en el proveedor de cloud es sencillo.

Docker Hub es fantástico para arrancar: podemos crear una cuenta gratis y subir nuestras imágenes Docker (la versión gratuita permite crear repositorios públicos ilimitados y uno sólo privado).

Si más tarde necesitamos usarlo para propósitos de negocio y restringir el acceso, podemos usar un registro de Docker privado, algunos proveedores son:

Travis

Aunque Docker nos ayuda a estandarizar la creación y configuración de un determinado entorno, generar nuevas versiones manualmente puede resultar tedioso y propenso a errores:

  • Tenemos que descargar el estado del código adecuado para el proceso de build.
  • Ejecutar los tests automatizados y comprobar que pasan en verde.
  • Tenemos que generar nuestra imagen de Docker.
  • Añadir el versionado adecuado (etiquetar la imagen en los términos de Docker).
  • Y por último, debemos subir la imagen manualmente al registro de Docker Hub.

Imaginemos hacer esto en cada proceso de merge to master; quedaríamos agotados con este infierno de despliegue... ¿Existe alguna manera de automatizar esto? ¡Travis al rescate!

Simplemente invirtiendo algo de tiempo en crear una configuración inicial, Travis hará de manera automática lo siguiente:

  • Notificar de cualquier PR o merge to master (se pueden establecer políticas de notificación).
  • Crear un entorno limpio (por ejemplo, en nuestro caso tomará una imagen de Ubuntu + Nodejs).
  • Descargar el estado de código adecuado desde el repositorio.
  • Ejecutar los tests.
  • Crear la imagen de Docker que contendrá la build de producción.
  • En caso de tener éxito, desplegar en el registro de Docker Hub (añadiendo la etiqueta de versión siguiendo la notación de Docker).

Una de las ventajas de Travis es que es bastante sencillo de configurar:

  • No necesitas instalar infraestructura (está basado en un entorno cloud).
  • La configuración se realiza mediante un fichero yml.
  • Ofrece una versión community edition donde podemos jugar con nuestros proyectos de prueba o usarlo para nuestros proyectos open source (sólo proyectos públicos).
  • Ofrece una versión enterprise para nuestros proyectos privados.

Configuración de Cuentas

Fork sobre los proyectos de ejemplo

Si quieres seguir este tutorial, puedes empezar por hacer un fork de los repositorios de Front End y Back End:

fork.png

Al hacer fork de estos repositorios, una copia se creará en tu cuenta de github y podrás enlazarlos a tu cuenta de Travis y configurar el proceso de CI.

Registro de Docker Hub

En nuestro artículo previo de esta serie, consumíamos una imagen de Docker Hub. Si queremos subir nuestras propias imágenes a Docker Hub (gratuito para imágenes públicas), necesitamos crear una cuenta. Puedes crear tu cuenta en el siguiente enlace.

Registro de Travis

Travis ofrece dos portales:

  • travisci.org: Permite añadir CI/CD a tus proyectos open source de manera gratuita.
  • travisci.com: Permite añadir CI/CD a tus proyectos privados a coste.

Como estamos usando Travis para aprendizaje, saltemos a travisci.org.

El siguiente paso que tenemos que dar, es enlazar nuestra cuenta de Github con Travis (iniciamos sesión con Github). Al hacer esto:

  • Ya tenemos nuestra cuenta creada en Travis.
  • Autorizamos a Travis para extraer información de nuestro repositorio (por ejemplo, descargar el repositorio para ejecutar el proceso de CI/CD).
  • Autorizamos a Travis para recibir notificaciones desde nuestro repositorio (por ejemplo, si una build se realizó para una rama determinada).
  • Github puede verificar el estado de la build desde Travis (por ejemplo, indicando si una pull request ha pasado el proceso de CI).

Comencemos

En este tutorial, aplicaremos la automatización a los repositorios (los cuales hemos hecho previamente un fork para que aparezcan en nuestra cuenta de Github) de la aplicación de chat usando una Travis Pipeline. Travis lanzará una tarea después de cada commit donde se realizarán lss siguientes pasos:

  • Se ejecutarán los test.
  • La aplicación será "dockerizada".
  • La imagen creada será subida al Docker Registry, en nuestro caso Docker Hub.

Subiendo una imagen de forma manual a Docker Hub

Antes de comenzar con la automatización, hagamos este proceso manualmente.

Una vez que tengamos una cuenta en Docker Hub, podemos interactuar con la plataforma desde la shell (abrir un bash terminal, o windows cmd).

Nos autentificamos contra Docker Hub.

$ docker login

Para poder subir nuestras imágenes, tienen que estar etiquetadas siguiendo el siguiente patrón:

<nombre_usuario_Docker_Hub>/<nombre_imagen>:<version>

La versión es opcional. Si no la especificamos, se aplicará latest.

$ docker tag front <nombre_usuario_Docker_Hub>/front

Y finalmente la subimos

$ docker push <nombre_usuario_Docker_Hub>/front

Desde ahora, la imagen estará disponible para el uso de cualquier usuario.

$ docker pull <nombre_usuario_Docker_Hub>/front

Para crear el resto de las imágenes que necesitamos, debemos seguir exactamente los mismos pasos.

Como hemos dicho anteriormente, todas las tareas que se repiten se pueden llegar a convertir en un proceso tedioso y donde podemos cometer errores. En los siguientes pasos aprenderemos como automatizarlo usando la CI/CD de Travis.

Enlazando el repositorio de Travis y las credenciales de Docker Hub

Primero tenemos que activar nuestros repositorios en Travis

travis-repos.png

Una vez activados, necesitamos entrar en cada una de las configuraciones del proyecto (Back y Front) y establecer como variables de entorno el usuario y la clave de Docker Hub

Front

frontend-travis-settings.png
travis-env.png

Back

backend-travis-settings.png
travis-env.png

Estas variables se usarán después para registrarnos en Docker Hub (nota: la primera vez que introducimos los datos, aparecen como texto plano. Una vez ya establecidas, aparecen como campos de clave).

Posiblemente, el repositorio sobre el que has realizado el fork no está registrado en tu cuenta de Travis. Puedes intentar sincronizar Travis con tu cuenta de Github manualmente, mediante el botón Sync

sync-travis.png

Finalmente podemos empezar la automatización de nuestras tareas.

Configuración de Travis

Para configurar Travis, necesitamos crear un fichero llamado .travis.yml en la raíz de nuestro proyecto. Es aquí, donde describimos las acciones que serán ejecutadas por Travis.

Como ya hemos enlazado tanto el repositorio de Back End como el de Front End en Travis, éste automáticamente comprobará los ficheros yml, y en cuanto estén disponibles, los analizará.

Back End

Los pasos para crear el fichero .travis.yml para la aplicación de back end son los siguientes:

  1. Escoger lenguaje y versión: en este caso vamos a eligir nodejs (tenemos otras opciones disponibles: ruby, java, python...).
  2. Docker Service: indicamos a Travis que vamos a hacer uso de Docker (Travis CI puede ejecutar imágenes Docker de build y subir estas imágenes al registro de contenedores).
  3. Instalar dependencias: como en un entorno en local, podemos ejecutar npm install.
  4. Testing: en nuestro caso, ejecutará los tests unitarios que han sido implementados en nuestra aplicación.
  5. Construir la imagen Docker: si los tests pasan, simplemente creamos la imagen del contenedor (buscará un fichero Dockerfile en la raíz del repositorio y seguirá las instrucciones del fichero para crear una build de producción, almacenándola en un contenedor de imagen Docker).
  6. Autenticación en Docker Hub: antes de subir la imagen generada a Docker Hub Registry necesitamos autenticarnos en Docker Hub.
  7. Etiquetar las imágenes de Docker: Tenemos que identificar la imagen del contenedor con una etiqueta.
  8. Subir las imágenes de Docker: subir la imagen generada al Docker Hub registry.

Un resumen del proceso de build:

backend-flow.gif

1. Escoger lenguaje y versión

Vamos a crear nuestro fichero .travis.yml en la raíz del repositorio de backend:

Empezaremos por indicar, que vamos a utilizar nodejs como nuestro language, después estableceremos que estamos usando la versión 12 de nodejs (más información acerca de los lenguajes en éste enlace).

  
  ./.travis.yml
  
  + language: node_js
  + node_js:
  +   - "12"
  

2. Servicio Docker

Primero pediremos ejecutar los comandos con sudo (administrador), sólo por si alguno de los comandos que estamos ejecutando requiere de permisos elevados.

Para utilizar Docker necesitamos solicitarlo como servicio en el fichero yml.

  
  ./.travis.yml
  
  language: node_js
  node_js:
    - "12"
  + sudo: required
  + services:
  +   - docker
  

3. Instalar dependencias

Comenzamos teniendo una máquina Ubuntu + nodejs levantada y en ejecución. Así que indicamos que queremos usar Docker. Travis ya ha descargado el código fuente desde nuestro repositorio de código, así que es el momento de ejecutar npm install antes de comenzar la ejecución de las pruebas unitarias.

  
  ./.travis.yml
  
  language: node_js
  node_js:
    - "12"
  sudo: required
  services:
    - docker
  + before_script:
  +   - npm install
  

4. Testing

Todo la fontanería está lista, por lo que podemos comenzar a definir nuestros scripts principales. Vamos a añadir dentro del fichero Travis yml una sección que se llame script, y dentro de la misma vamos a añadir un comando npm test; este simple comando, hará que se ejecuten nuestra batería de tests.

  
  ./.travis.yml
  
  language: node_js
  node_js:
    - "12"
  sudo: required
  services:
    - docker
  before_script:
    - npm install
  + script:
  +   - npm test
  

5. Construir la imagen de Docker

Si la ejecución de los tests ha tenido éxito, estamos listos para construir la imagen Docker. En el artículo anterior creamos un Dockerfile configurando los pasos del proceso de build.

docker-git.png

./Dockerfile

FROM node
WORKDIR /opt/back
COPY . .
RUN npm install
EXPOSE 3000
ENTRYPOINT ["npm", "start"]

Refresquemos la memoria acerca de la configuración reflejada en el Dockerfile:

  • FROM node Establecemos la imagen base de node desde Docker Hub image.
  • WORKDIR /opt/back Establecemos nuestro directorio de trabajo en /opt/back.
  • COPY . . Copiamos el contenido en el contenedor. La ruta donde será copiado el contenido será en el directorio de trabajo seleccionado, en este caso /opt/back.
  • RUN npm install Instalamos las dependencias.
  • EXPOSE 3000 Notificamos cuál es el puerto que expone nuestra aplicación.
  • ENTRYPOINT ["npm", "start"] Comando para arrancar nuestro contenedor.

Volvamos al fichero Travis yml: dentro de la sección script, justo después de npm test, añadimos el comando para construir la imagen del contenedor Docker.

  
  ./.travis.yml
  
  language: node_js
  node_js:
    - "12"
  sudo: required
  services:
    - docker
  before_script:
    - npm install
  script:
    - npm test
  + - docker build -t back.
  

Este comando buscará el fichero Dockerfile, que creamos en la raíz del repositorio de backend, y seguirá los pasos para realizar el proceso de build de la imagen.

¡Un momento! Acabo de caer en la cuenta de que aquí hay algo extraño: estamos utilizando distintos contenedores. Travis ejecuta los tests en una instancia de linux y el Dockerfile usa otra configuración de linux / node extraída del Docker Hub Registry. Esto huele mal, ¿no?

¡Totalmente de acuerdo! Ambas configuraciones, la del fichero Travis yml y la del fichero Dockerfile deberían arrancar desde la misma imagen. Necesitamos asegurarnos de que los tests se ejecutan con la misma configuración que tendremos en nuestro entorno de producción - esto es una limitación de la versión gratuita Travis.org (aquí puedes encontrar algunos workarounds). La versión de pago permite configurar la imagen del contenedor que quieras como punto de partida, más información en éste enlace.

6. Autenticarnos en Docker Hub

Justo después de que todos los scripts hayan sido ejecutados, y la imagen de docker haya sido generada, queremos subir la Docker image al Docker Hub Registry

Travis yml expone una sección denominada _aftersuccess. Esta sección sólo es ejecutada, si todos los pasos de la sección script, se han ejecutado con éxito. Es aquí donde realizaremos las acciones necesarias, para subir la imagen al registro de docker.

El primer paso es autenticarnos en Docker Hub (haremos uso de las variables de entorno que añadimos a la configuración de nuestro proyecto de Travis, ver la sección Enlazando el repositorio de Travis y las credenciales de Docker Hub de este artículo).

  
  ./.travis.yml
  
  language: node_js
  node_js:
    - "12"
  sudo: required
  services:
    - docker
  before_script:
    - npm install
  script:
    - npm test
    - docker build -t back.
  + after_success:
  +   - docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
  

7. Etiquetar las imágenes de Docker

La imagen de Docker que acabamos de generar tiene el nombre de back. A la hora de subir la imagen a Docker Hub Registry, debemos proveer un nombre más elaborado y único:

  • Lo prefijamos con el nombre del usuario de Docker.
  • Añadimos un sufijo con un número único de build (en este caso \$TRAVIS_BUILD_NUMBER).

Por otro lado, indicaremos que la imagen que se ha generado es la última imagen de Docker disponible.

En un proyecto real esto puede variar dependiendo de nuestras necesidades.

  
  ./.travis.yml
  
  language: node_js
  node_js:
    - "12"
  sudo: required
  services:
    - docker
  before_script:
    - npm install
  script:
    - npm test
    - docker build -t back.
  after_success:
    - docker login -u $DOCKER_USER -p $DOCKER_PASSWORD    
  + - docker tag back $DOCKER_USER/back:$TRAVIS_BUILD_NUMBER
  + - docker tag back $DOCKER_USER/back:latest
  

8. Subir las imágenes de Docker

Ahora que tenemos identificadas nuestras imágenes de Docker con nombres únicos, necesitamos subirlas al Docker Registry. Utilizaremos el comando docker push para este fin.

  
  ./.travis.yml
  
  language: node_js
  node_js:
    - "12"
  sudo: required
  services:
    - docker
  before_script:
    - npm install
  script:
    - npm test
    - docker build -t back.
  after_success:
    - docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
    - docker tag back $DOCKER_USER/back:$TRAVIS_BUILD_NUMBER
  +  - docker push $DOCKER_USER/back:$TRAVIS_BUILD_NUMBER
    - docker tag back $DOCKER_USER/back:latest
  +  - docker push $DOCKER_USER/back:latest
  

Fíjate que primero estamos subiendo la imagen $DOCKER_USER/back:$TRAVIS_BUILD_NUMBER, y después la imagen $DOCKER_USER/back:latest

¿Significa esto que la imagen será subida dos veces?... La respuesta es no.

Docker es lo suficientemente listo para identificar que la imagen es la misma, así que le asignará "nombres" diferentes a la misma imagen en el Docker Repository

El resultado final

Nuestro fichero .travis.yml debería ser parecido a este:

language: node_js
node_js:
  - "12"
sudo: required
services:
  - docker
before_script:
  - npm install
script:
  - npm test
  - docker build -t back .
after_success:
  - docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
  - docker tag back $DOCKER_USER/back:$TRAVIS_BUILD_NUMBER
  - docker push $DOCKER_USER/back:$TRAVIS_BUILD_NUMBER
  - docker tag back $DOCKER_USER/back:latest
  - docker push $DOCKER_USER/back:latest

Ahora, si subimos toda esta configuración a Travis, automáticamente disparará un proceso de build (también puedes lanzar un proceso de build desde la web UI de Travis).

travis-build.png

Una vez finalizado, puedes comprobar que la imagen de Docker ha sido generada con éxito (comprobar la consola web de Travis):

travis-build-docker.png

Y también podemos comprobar si la imagen está disponible en nuestra cuenta de Docker Hub Registry:

docker-hub-published.png

Front End

Los pasos para crear le fichero .travis.yml son muy parecidos a los anteriores (backend), la única diferencia es que no hemos implementado pruebas unitarias (nos saltaremos ese paso):

  1. Servicio de Docker.
  2. Construir la Imagen de Docker.
  3. Autenticarse en Docker Hub.
  4. Etiquetar las imágenes de Docker.
  5. Subir las imágenes de Docker.

1. Servicio de Docker

La aplicación de frontend será "dockerizada". Por lo que, comenzaremos indicado que el servicio es necesario, y sudo es requerido; exactamente igual que en la configuración de backend

  
  ./.travis.yml
  
  + sudo: required
  + services:
  +   - docker
  

2. Construir la imagen de Docker

Generamos la imagen de Docker dentro de la sección script.

  
  ./.travis.yml
  
  sudo: required
  services:
    - docker
  + script:
  +  - docker build -t front .
  

3. Autenticarnos en Docker Hub

Si la imagen de Docker se construyó con éxito, el siguiente paso es autenticarnos en Docker Hub.

  
  ./.travis.yml
  
  sudo: required
  services:
    - docker
  script:
    - docker build -t front .
  + after_success:
  +   - docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
  

4. Etiquetar las imágenes de Docker

Como hicimos con la aplicación de backend, vamos a etiquetar la versión actual utilizando Travis Build number y definirla como latest.

  
  ./.travis.yml
  
  sudo: required
  services:
    - docker
  script:
    - docker build -t front .
  after_success:
    - docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
  +  - docker tag front $DOCKER_USER/front:$TRAVIS_BUILD_NUMBER
  +  - docker tag front $DOCKER_USER/front:latest
  

8. Subir las imágenes de Docker

Ahora sólo necesitamos subir las imágenes de Docker.

  
  ./.travis.yml
  
  sudo: required
  services:
    - docker
  script:
    - docker build -t front .
  after_success:
    - docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
    - docker tag front $DOCKER_USER/front:$TRAVIS_BUILD_NUMBER
  +  - docker push $DOCKER_USER/front:$TRAVIS_BUILD_NUMBER
    - docker tag front $DOCKER_USER/front:latest
  +  - docker push $DOCKER_USER/front:latest  
  

Y el fichero final .travis.yml debería ser como éste:

sudo: required
services:
  - docker
script:
  - docker build -t front .
after_success:
  - docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
  - docker tag front $DOCKER_USER/front:$TRAVIS_BUILD_NUMBER
  - docker push $DOCKER_USER/front:$TRAVIS_BUILD_NUMBER
  - docker tag front $DOCKER_USER/front:latest
  - docker push $DOCKER_USER/front:latest

Ejecutando un sistema de multi contenedores

Vamos a comprobar si nuestra Configuración de CI funciona como esperamos.

Primero nos aseguramos de que Travis ha ejecutado con éxito al menos un proceso de build (en caso contrario, podemos disparar el proceso de build de manera manual o simplemente realizar una subida de código trivial, a los repositorios de backend y frontend)

Deberíamos ver en Travis que el proceso de build ha sido lanzado para los repositorios de Front End y Back End (autenticándote en travis.org):

También deberíamos ver las imágenes disponibles en el docker registry (autenticándote en Docker Hub):

Como hicimos en nuestro artículo previo, podemos arrancar todo nuestro sistema usando Docker Compose. Sin embargo, en este caso para el Front End y Back End vamos a consumir las imágenes que hemos subido a Docker Hub Registry.

Los cambios que vamos a introducir al fichero docker-compose.yml son los siguientes:

  
  version: '3.7'
  services:
    front:
  -    build: ./container-chat-front-example
  +    image: <nombre_usuario_Docker_Hub>/front:<version> 
    back:
  -    build: ./container-chat-back-example
  +    image: <nombre_usuario_Docker_Hub>/back:<version> 
    lb:
      build: ./container-chat-lb-example
      depends_on:
        - front
        - back
      ports:
        - '80:80'
  

Así queda el fichero docker-compose.yml:

version: "3.7"
services:
  front:
    image: <nombre_usuario_Docker_Hub>/front:<version>
  back:
    image: <nombre_usuario_Docker_Hub>/back:<version>
  lb:
    build: ./container-chat-lb-example
    depends_on:
      - front
      - back
    ports:
      - "80:80"

Podemos arrancarlo utilizando:

$ docker-compose up

Entonces se descargarán las imágenes de back y front desde Docker Hub Registry (las últimas disponibles). Podemos comprobar cómo funciona abriendo un navegador web e introduciendo http://localhost/ en la barra del navegador (más información acerca de cómo funciona en nuestro artículo previo Hola Docker)

Recursos

En Resumen

Al introducir este proceso de CI/CD, hemos obtenido múltiples ventajas:

  • El proceso de build se ha automatizado, de esta manera evitamos los errores manuales.
  • Podemos desplegar distintas versiones de la build fácilmente (como de si una máquina de discos se tratará, ver diagrama).
  • Podemos dar marcha atrás fácilmente una publicación errónea.
  • Podemos aplicar testeo A/B o tener ambientes Canary.
deploy.jpg

¿Y qué hay del despliegue? En el próximo artículo de esta serie aprenderemos a crear despliegues automatizados usando Kubernetes.

Permanece atento a nuestro blog :)


Puedes seguir leyendo más artículos de esta serie:

  1. [Hola Docker][0]

¿Te gusta el mundo Devops?

En Lemoncode impartimos un Bootcamp Devops Online, si quieres saber más puedes pinchar aquí para más información.

Comment

Beca Yes We Tech - Octava edición Máster Front End Lemoncode

Después de varias ediciones presentando la beca Yes We Tech, estamos muy contentos con los resultados, de las tres alumnas becadas, todas se han incorporado al mundo laboral y con muy buenos resultados, con estos resultados estamos muy contentos de publicar convocatoria de beca para está edición del Máster.

Comment

Hola Docker

Docker ha dejado de ser una tecnología desconocida para convertirse (casi) en un estándar: La mayoría de empresas y desarrolladores lo utilizan a diario por su simplicidad para empaquetar y ejecutar aplicaciones (especialmente en sistemas distribuidos). Docker nos evita tener que instalar miles de dependencias, lo que hace que nuestros sistemas de producción sean mucho más fáciles actualizar y mantener.

Si (casi) todo el mundo está utilizando Docker para desplegar sus aplicaciones en contenedores... es hora de ponernos las pilas ;)

4 Comments

Bootcamp Javascript ¿Es para mí?

Hace unos meses decidimos montar un Bootcamp Online en JavaScript, desde entonces muchos nos habéis escrito con dudas, acerca de que tipo de formación elegir (Máster Front o Bootcamp), que contenido se imparte, y a que perfiles está orientado, si ese es tu caso, esperamos que este post te sea de ayuda :-).

Comment

Configurando las pipelines de Bitbucket en tu proyecto front end

Las pipelines de Bitbucket permiten configurar fácilmente un entorno de integración continua en tu proyecto. Teóricamente, en teoría solo necesitas habilitarlas y configurar un fichero yml en tu raíz del proyecto.

Sin embargo …

  • Bitbucket factura por minutos de compilación, y por defecto tu integración se dispara en cada push, en cada rama. Debemos alcanzar un equilibrio de costes:

    • Lanzar la compilación en cada push hacia la rama master.

    • Lanzar la compilación en cada pull request.

  • Poner en marcha un fichero YAML de configuración es un proceso propenso a errores, especialmente si no tienes tiempo para leer detenidamente la documentación. Por tanto, necesitamos una manera fácil de comprobar que el YAML generado es válido.

Comment

Componentes de Clase en React: Inconvenientes

Introducción

Hasta hace unos meses, los componentes de clase de React (también llamados stateful components) estaban de moda. Pero eso se acabó con la llegada de los Hooks. ¿Por qué han dejado de ser una buena idea? En este post vamos a enumerar los principales inconvenientes a la hora de trabajar con componentes de clase en React.

La pesadilla del “this

En componentes de clase, el valor que adopta this dentro de una función dependerá de cómo sea invocada dicha función. Cuando nuestra función es utilizada como manejador de eventos (event handler), el valor al que apunta this será undefined. ¿Por qué? Las declaraciones y expresiones de clase (como por ejemplo nuestra función) se ejecutan en modo estricto, bajo el cual se aplica el binding por defecto a undefined y no a la instancia del componente como cabría esperar.

Más información al respecto aqui: https://medium.freecodecamp.org/this-is-why-we-need-to-bind-event-handlers-in-class-components-in-react-f7ea1a6f93eb

Por tanto, el this en un event handler disparado fuera de nuestro componente de clase, perderá la referencia a la instancia a la que pertenece.

Si ejecutamos el siguiente ejemplo y hacemos click en el botón Change Text, obtendremos el error:

Cannot read property ‘setState’ of undefined
export class MyHelloComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { myText: "hello" };
  }

  onChangeText() {        
    this.setState({ myText: "world" });
  }

  render() {
    return (
      <>
        <h3>{this.state.myText}</h3>
        <button onClick={this.onChangeText}>Change text</button>
      </>
    );
  }
}

La solución pasa por “atar” manualmente el this a la instancia de clase en cada una de las funciones declaradas como métodos de clase. Para ello contamos con dos mecanismos, o bien hacer un binding explícito en el constructor o bien utilizar funciones flecha experimentales (fat arrow) como método de clase (ver linea afectada marcada con el caracter “+”).

export class MyHelloComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { myText: "hello" };
+    this.onChangeText = this.onChangeText.bind(this);
  }

  onChangeText() {        
    this.setState({ myText: "world" });
  }

  render() {
    return (
      <>
        <h3>{this.state.myText}</h3>
        <button onClick={this.onChangeText}>Change text</button>
      </>
    );
  }
}

Demo: https://codesandbox.io/s/71opm61rzq

Esta característica de las clases puede llegar a convertirse en una fuente de problemas. ¿Cuántas veces has olvidado hacer adecuadamente el bind a tus event handlers, obteniendo finalmente un error en tiempo de ejecución?

Estado monolítico y funcionalidad difícilmente extraíble

En un componente de clase, tenemos un único lugar donde definir todo el estado al completo, y al mismo tiempo, una única forma de actualizarlo. ¿Inconveniente? Es complicado separar intereses y extraer la funcionalidad para su reuso. Recordemos que la separación de intereses (separation of concerns) y la reutilización de código (code reuse) son dos de los más importantes principios de programación que aparecerán en cualquier manual de buenas prácticas.

En el siguiente ejemplo, tenemos dos conceptos diferentes encapsulados en el mismo estado de clase:

export class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { color: "teal", name: "John", lastname: "Doe" };
  }

  getFullname() {
    return `${this.state.name} ${this.state.lastname}`;
  }

  render() {
      return (
          <div style={{ background: this.state.color }}>
              <h3>{this.getFullname()}</h3>
          </div>
      )
  }
}

Demo: https://codesandbox.io/s/yp9v4yyr8x

Veamos como podríamos implementar el mismo escenario usando hooks. En este caso podremos separar el estado para cada concepto:

import React from "react";

export const MyComponent = () => {
  const [color, setColor] = React.useState("teal");
  const [clientInfo, setClientInfo] = React.useState({name: 'John', lastname: 'Doe'});

  const getFullname = () => {
      return `${clientInfo.name} ${clientInfo.lastname}`;
  }

  return (
    <div style={{ background: color }}>
      <h3>{getFullname()}</h3>
    </div>
  );
};

Demo: https://codesandbox.io/s/ppom543mj7

Podemos dar un paso más y encapsular la funcionalidad relativa al usuario en un custom hook:

const useClientInfo = (name, lastname) =>  {
  const [clientInfo, setClientInfo] = React.useState({
    name,
    lastname,
  });

  const getFullname = () => {
    return `${clientInfo.name} ${clientInfo.lastname}`;
  };

  return {clientInfo, setClientInfo, getFullname}
}

export const MyComponent = () => {
  const [color, setColor] = React.useState("teal");
  const {getFullname} = useClientInfo('John', 'Doe');

  return (
    <div style={{ background: color }}>
      <h3>{getFullname()}</h3>
    </div>
  );
};

Demo: https://codesandbox.io/s/ly7wlq9mo9

Gestión de intereses relacionados en manejadores separados

Cuando usamos componentes de clase en React, disponemos de distintos eventos durante su ciclo de vida en los cuales ejecutar código: componentDidMount, componentDidUpdate, componentWillUnmount, etc. Podría darse el caso de que cierta funcionalidad relacionada tenga que ser dividida entre los distintos handlers del ciclo de vida del componente. Por ejemplo:

Definamos un componente padre:

export class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { visible: false };
  }

  render() {
    return (
      <>
        {this.state.visible && <MyChildComponent />}
        <button onClick={() => this.setState({ visible: !this.state.visible })}>
          Toggle Child component visibility
        </button>
      </>
    );
  }
}

Ahora, un componente hijo que simplemente mostrará la información de usuario:

export class MyChildComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = { name: "John", lastname: "Doe" };
  }

  componentDidMount() {
    console.log("Hey Im mounting");
    console.log(`${this.state.name} ${this.state.lastname}`);
  }

  componentDidUpdate() {
    console.log("Just updating...");
    console.log(`${this.state.name} ${this.state.lastname}`);
  }

  componentWillUnmount() {
    console.log("bye bye, unmounting...");
  }

  render() {
    return (
      <div>
        <h3>
          {this.state.name} {this.state.lastname}
        </h3>
        <input
          value={this.state.name}
          onChange={e => this.setState({ name: e.target.value })}
        />
        <input
          value={this.state.lastname}
          onChange={e => this.setState({ lastname: e.target.value })}
        />
      </div>
    );
  }
}

Demo: https://codesandbox.io/s/oqyo3159jq

Mediante el empleo de hooks podemos agrupar toda esta funcionalidad en una única función:

const MyChildComponent = () => {
  const [userInfo, setUserInfo] = React.useState({name: 'John', lastname: 'Doe'})

  React.useEffect(() => {
    console.log('called when the component is mounted and right after it gets updated');

    return () => console.log('Clean up from the previous render before running effect next time ... ');
  })

  return (
    <div>
      <h3>
        {userInfo.name} {userInfo.lastname}
      </h3>
      <input
        value={userInfo.name}
        onChange={e => setUserInfo({ ...userInfo, name: e.target.value })}
      />
      <input
        value={userInfo.lastname}
        onChange={e => setUserInfo({ ...userInfo, lastname: e.target.value })}
      />
    </div>
  );
}

En este ejemplo, el código declarado dentro del useEffect es ejecutado una vez que el componente se ha montado pero también justo después de cada render. Por otro lado, la función de limpieza (devuelta en el useEffect) se ejecuta después de que el componente se haya montado y después de cada ejecución del efecto.

Demo: https://codesandbox.io/s/5zllr3k09p

Bien, pero … ¿y si quiero ejecutar un trozo de código solo cuando el componente se haya montado y hacer la limpieza cuando el componente vaya a desmontarse? Para ello, podemos jugar con el segundo parámetro del useEffect (en el ejemplo marcado con “-” las lineas que se eliminarían, con “+” las líneas que se añaden).

const MyChildComponent = () => {
  const [userInfo, setUserInfo] = React.useState({name: 'John', lastname: 'Doe'})

  React.useEffect(() => {
-    console.log('called just when the component is mounted and when after it gets updated');
+    console.log('called just when the component is mounted');

-    return () => console.log('Clean up from the previous render before running effect next time ... ');
+    return () => console.log('Clean up executed just when the component gets unmounted ... ');
-  })
+  }, [])

  return (
    <div>
      <h3>
        {userInfo.name} {userInfo.lastname}
      </h3>
      <input
        value={userInfo.name}
        onChange={e => setUserInfo({ ...userInfo, name: e.target.value })}
      />
      <input
        value={userInfo.lastname}
        onChange={e => setUserInfo({ ...userInfo, lastname: e.target.value })}
      />
    </div>
  );
}

Demo: https://codesandbox.io/s/q91qjql8r4

Ruido al utilizar High Order Components

Un HOC o High Order Component es una excelente forma de añadir nuevas capacidades a tu componente mediante composición, sin embargo:

  • Necesitas refactorizar su interfaz para poder aplicarlo.

  • Encadenar diversos HOC simultáneamente puede ser una pesadilla.

Veamos un ejemplo mínimo. El siguiente componente se emplea para saludar al usuario que está actualmente logado:

import React from "react";

export const MyComponent = (props) => {
  return (
    <>
      <h3>Hello: </h3>
    </>
  )
}

Para inyectar el usuario que está actualmente logado podemos crear un HOC:

const withUserInfo = (ComponentToWrap) => (props) =>
  <>
    <ComponentToWrap {...props} user="John" />
  </>
Let's make usage of this HoC in MyComponent

- export const MyComponent = (props) => {
+ const 

Podemos hacer uso de este HOC en MyComponent del siguiente modo:

const MyComponentInner = (props) => {  
  return (
    <>
      <h3>Hello: {props.user}</h3>
    </>
  )
}

export const MyComponent = withUserInfo()

Demo: https://codesandbox.io/s/l7qznjjyw9

Esta forma de añadir funcionalidad es bastante potente, pero, ¿no sería más sencillo y expresivo un mecanismo con el que podamos decir: “quiero usar esta funcionalidad”? Veamos como implementar este comportamiento utilizando hooks:

import React from "react";

const useUserInfo = () => {
  const [userInfo, setUserInfo] = React.useState('John');

  return {userInfo, setUserInfo}
}

export const MyComponent = (props) => {
  const {userInfo} = useUserInfo();

  return (
    <>
      <h3>Hello: {userInfo}</h3>
    </>
  )
}

Demo: https://codesandbox.io/s/pwj6z446xq

Ventajas:

  • No necesitamos reescribir el interfaz de nuestro componente: borrar el export, crear un componente para envolverlo, etc.

  • Sin complicaciones al tener anidando HOCs.

  • El contrato es claro, no tenemos que adivinar que propiedades está inyectando el HOC a nuestro componente.

Migrando componentes de su forma funcional a clase, y también en el sentido contrario

Cuando iniciamos la implementación de un componente tratamos de mantenerlo sencillo, por lo que es típico comenzar con un componente funcional. Un ejemplo simple podría ser el siguiente: mostrar un mensaje de bienvenida a un usuario (nos lo informa el padre a través de la propiedad userName).

import React from "react";

export const MyComponent = () => {
  return (
    <>
      <MyChildComponent userName="John"/>
    </>
  )
}

export const MyChildComponent = (props) => {
  return (
    <>
      <h3>Hello: {props.userName}</h3>      
    </>
  )
}

Demo: https://codesandbox.io/s/985wyr1olw

Supongamos que, en lugar de recibirlo desde el padre, queremos almacenarlo en el estado local de nuestro componente. Una aproximación clásica sería migrar nuestro componente a su forma stateful, es decir, refactorizarlo por completo, prestando atención de no equivocarnos con el this.state, añadiendo su constructor, implementando el método render, etc. Algo tal que así:

export const MyComponent = () => {
  return (
    <>
      <MyChildComponent/>
    </>
  )
}

export class MyChildComponent extends React.Component {  
  constructor(props) {
    super(props);
    this.state = {userName: 'John'}
  } 

  render() {
      return (
        <>  
         <h3>Hello: {this.state.userName}</h3>      
        </>
      )
  }
}

Demo: https://codesandbox.io/s/40l5k4q1o9

¿Cómo podría haber sido este refactor utilizando hooks? Tan sencillo como esto:

export const MyComponent = () => {
  return (
    <>
     <MyChildComponent/>
    </>
  )
}

export const MyChildComponent = (props) => {
  const [userName]  = React.useState('John');
  return (
    <> 
     <h3>Hello: {userName}</h3>      
    </>
  )
}

Demo: https://codesandbox.io/s/jlkoxm6plw

Combinando componentes funcionales y de clase

Por último, pero no menos importante, en un proyecto de dimensiones considerables, una aproximación mixta donde componentes funcionales y de clase tengan que convivir juntos podría traer diversos inconvenientes de mantenibilidad:

  • Inconsistencia en nuestra base de código.

  • Dificultad añadida para que nuevos desarrolladores se inicien en el proyecto, teniendo que aprender ambas formas y decidir cuando es más conveniente una u otra, o cuando deben refactorizar un componente existente en uno u otro sentido.

  • La refactorización de componentes, ya sea en un sentido u otro, es una tarea propensa a errores, por ejemplo: se nos olvida eliminar el this, nos equivocamos en la sintaxis de clase vs función, errores con la función de render, etc.

Resumiendo

Antes de tirarnos a la piscina con la nueva característica de moda, es importante aprender por qué la necesitamos y que problemas viene a resolver. Esperamos que este artículo pueda ayudaros a entender mejor por qué los hooks han generado tanta expectación en la comunidad y se han convertido en un estándar de facto. En los siguientes artículos de esta serie comenzaremos a indagar en los detalles que hacen a los hooks tan poderosos, mostrando su cara amable y también los casos frontera, basándonos en casos de uso en proyectos reales. ¡Estad atentos!

Esperamos que hayáis disfrutado el artículo. ¡Muchas gracias!

Comment

Beca Yes We Tech - Septima edición Máster Front End Lemoncode

En 2018 presentamos nuestra primera beca Yes We Tech, seleccionamos a dos candidatas para que cursaran el máster , ¿Cual fue el resultado? Tener a dos alumnas muy motivadas, con muchas ganas de trabajar y que como fruto del esfuerzo consiguieron un empleo al finalizar el máster, con estos resultados… ¿ Por qué no repetir? :)

En colaboración con YesWeTech ofrecemos nuestra segunda beca de estudios para aportar nuestro granito de arena a conseguir que haya más diversidad e igualdad de oportunidades en esta profesión. ¿En que consiste? En cubrir el 100% del coste del Máster Front End Online Lemoncode a una alumna que pertenezca a una minoría con baja representación en el sector profesional. Ofrecemos una plaza, y para ello hemos resuelto unas bases para poder inscribiros y seleccionar a la aspirante.

yeswetech.jpeg


La convocatoría se abre el 15 de Abril y se cierra el 15 de Mayo, el 1 de Junio anunciaremos la persona seleccionada. 

A continuación te detallamos las bases de la misma:

Requisitos

  • Pertenecer a minorías con baja representación en el sector profesional, es decir, mujeres y en especial quienes sean inmigrantes, pertenezcan al colectivo LGTBQIA+, estudiantes y mujeres con con diversidad funcional.

  • Ingresos inferiores a 27.000€ brutos año o contar con cargas familiaries.

  • Tener conocimientos previos de programación (sea experiencia laboral, como estudiante, o autodidacta demostrable).

Criterios de evaluación

  • Horas de estudio semana que la candidata pueda dedicar a la semana.

  • Curriculum Vitae.

  • Entrevista personal y opción de realizar prueba técnica a las finalistas.

Información mínima a enviar

  • Documento personal identificativo (DNI, NIE, Pasaporte)

  • Carta explicando los motivos por los que la alumna está interesada en realizar el máster.

  • Número de horas de estudio semanal que le puede dedicar al máster

  • Curriculum Vitae.

  • Información salarial (no es necesario el envío de documentación acreditativa, pero se podría requerir si la candidata llega a finalista).

Fechas

  • Aceptación candidatas del 15 de Abril al 15 de Mayo de 2019.

  • Fecha resolución beca (1 plaza) 1 de Junio de 2019.

  • Fecha arranque Máster 22 de Septiembre de 2019

Comite evaluador

  • Miembros de la comunidad YesWeTech.

  • Miembros de Lemoncode Formacion S.L.

Para aplicar envíanos un correo con la información que te pedimos a nuestra cuenta de EMail: formacion@lemoncode.net

Te estamos muy agradecidos si nos ayudas a difundir esta beca.

Comment

Preguntas frecuentes Máster Front End Online Lemoncode

A lo largo de las 5 ediciones de este Máster hemos tenido la suerte de contar con un buen número de estudiantes y nos han planteado cuestiones muy interesantes que nos gustaría compartir. En este post te resumimos las más comunes (si tienes alguna consulta que no este recogida en este artículo no dudes en contactar con nosotros). Espero que te sea de utilidad.

Comment

Configurando Alias en webpack + visual code + typescript + jest

Introducción

Este post está basado en este excelente Gist de Carlos Hidalgo

Cuando trabajamos con imports, una de las cosas que más duelen (y huelen) es trabajar con rutas relativas, es decir, si tenemos una estructura como esta:

Ejemplo de estructura de carpetas de proyecto

Ejemplo de estructura de carpetas de proyecto


Y por ejemplos estamos editando el fichero pods/movies/movies-grid.container.ts Y queremos desde nuestra página referenciar a un elemento de la carpeta api, tendríamos que hacer un import del tipo:

import {fetchMovies} from '../../api';

Con uno o dos niveles “../” igual esto no tiene mala pinta, pero a veces te puedes encontrar con cosas como:

import { TrainingSummary } from '../../../../model/trainingSummary';

Y lo que es peor, si refactorizamos y movemos de carpetas ficheros acabamos teniendo un lio de imports.

¿ Que solución podemos aplicar? crear unos alias a nivel de carpeta principal, que nos permitan minimizar el uso de carpetas relativas, es decir en este caso si hago un alias a la carpeta api, el import relativo que creamos anteriormente se quedaría de la siguiente manera:

import {fetchMovies} from '/api';

La idea aquí es crear unos alias en carpetas de primer nivel y evitar el infierno de rutas relativas.

Y..¿ Esto es fácil de configurar? Bueno más que fácil, tiene truco, tienes que saber bien donde tocar:

  • Hay que indicarle a webpack los mappings de esos alias (tocaremos el webpack.config.js).

  • Hay que ayudar a VSCode y Typescript a mapear esos alias (tocaremos el tsconfig.json).

  • Y si tenemos pruebas unitarias con JEST también debemos de indicarle los mapeos de carpeta a alias (aquí actualizaremos el fichero de configuración de jest, sea el jest.json o el propio package.json, depende de donde tengamos la configuración).

Con la hoja de ruta que os vamos a definir, a partir de ahora configurar un grupo de alias va a ser pan comido :).

Configurando webpack

Webpack necesita saber a paths físicos mapean los alias, abrimos el fichero webpack.config.js y añadimos a la sección resolve, una subsección que llamaremos alias y ahí listamos todos los mapeos de alias a ruta, quedaría tal que así:

webpack.config.json

module.exports = {
  resolve: {
    alias: {
      api: path.resolve(__dirname, './src/api/'),
      commonApp: path.resolve(__dirname, './src/common-app/'),
      core: path.resolve(__dirname, './src/core/'),
      layout: path.resolve(__dirname, './src/layout/'),
      pods: path.resolve(__dirname, './src/pods/'),      
      scenes: path.resolve(__dirname, './src/scenes/'),
    },
    extensions: ['.js', '.jsx', '.ts', '.tsx'],
  },
La lista de alias es la que usamos en nuestra aplicación, en tu aplicación tendrás otras carpetas y otros alias que podrás configurar según tus necesidades.

Configurando Typescript + Visual Studio code

Ahora nos toca que Typescript y Visual Studio Code se lleven bien con estos alias, para ellos en el tsconfig, tenemos que decirle cual es nuestra carpeta de partida (en este caso la src) y crear una sección de paths en la que añadimos nuestra lista de alias (muy importante: esta lista de paths tiene que ir debajo de la sección de compilerOptions).

tsconfig.json

{
  "compilerOptions": { 
    "moduleResolution": "node",
    "baseUrl": "./src/",
    "paths": {
      "api": ["./api/*"],
      "common-app": ["./common-app/*"],
      "core": ["./core/*"],
      "layout": ["./layout/*"],
      "pods": ["./pods/*"],
      "scenes": ["./scenes/*"]
    }
  }
}

Configurando Jest


Cómo último paso, si estamos usando Jest, tenemos que añadir la lista de paths al fichero de configuración, dependiendo del proyecto la configuracíon de jest te la puedes encontrar en el mismo package.json o en un fichero específico (jest.json o similar), en el ejemplo que cubrimos se encuentra en el jest.json.

/config/jest.json

{
  "moduleNameMapper": {
    "^api/(.*)$": "/src/api/$1",
    "^common-app/(.*)$": "/src/common-app/$1",
    "^core/(.*)$": "/src/core/$1",
    "^layout/(.*)$": "/src/layout/$1",
    "^pods/(.*)$": "/src/pods/$1",
    "^scenes/(.*)$": "/src/scenes/$1"
  }
}

Ejemplo real

Si quieres ver esto aplicado en un proyecto real, aquí tienes:

Agradecimientos

Este post está basado en este excelente Gist de Carlos Hidalgo, que se pego unas horas de investigación y cabezazos hasta que dio con los pasos exactos :).

¿Con ganas de aprender desarrollo Front End?

Si tienes ganas de ponerte al día en el mundo del Front End, impartimos un máster online con clases en vivo. Para más información: http://lemoncode.net/master-frontend

Comment

Entrevista a Juan José Montiel

En el Máster Front End Online Lemoncode, participamos un equipo de 12 de profesores, y también una serie de empresas que están adheridas a nuestra bolsa de trabajo. ¿Alguna vez te ha picado la curiosidad de saber de donde venimos o qué hacemos? Continuamos esta de serie de posts entrevistando a Juan José Montiel, profesor del máster Front End Online Lemoncode, experto en accesibilidad y Microsoft MVP.

Comment

Entrevista a Erik Rasmussen

En el Máster Front End Online Lemoncode, participamos un equipo de 12 de profesores, y tambíen una serie de empresas que están adheridas a nuestra bolsa de trabajo. ¿Alguna vez te ha picado la curiosidad de saber de donde venimos o qué hacemos? Abrimos sería de posts con entrevistas al equipo y empresas que colaboradon en este máster.

Empezamos entrevistando a Erik Rasmussen, el desarrollador de proyectos tales como Redux-Forms, o guías como Redux Ducks

Comment

React y D3.js, trabajando juntos I - Introducción

Esta serie de dos posts viene inspirada por la charla que impartimos en el Open South Code 2018 sobre la integración entre React y D3.js, cuyo material puedes encontrar aqui: fjcalzado.surge.sh. En esta primera parte haremos un breve repaso de ambas librerías, centrándonos principalmente en la gestión del DOM. Veremos conceptos interesantes y concluiremos con la problemática a la hora de trabajar con ambos frameworks. En la segunda parte, explicaremos las distintas soluciones disponibles y las ilustraremos con código y ejemplos. ¡Vamos allá!

2 Comments