2018/12/30

Hello Jenkins


En días recientes estuve viendo un poco de Continuous Integration / Continuous Deployment (CI/CD), con Jenkins.

La idea que he captado es que Jenkins corre en el servidor donde normalmente ejecutamos a mano los pasos para deployar un proyecto. Luego configuramos las cosas para que Jenkins las haga por nosotros.

Por ejemplo, puedo tener en mi hosting un site estático hello-jenkins, cuyo repositorio está en GitHub. Luego que hago push en el repo desde mi máquina local debo entrar a la consola de mi hosting y hacer pull. Quizás alguna cosa más, como bower o npm, dependiendo de la complejidad del proyecto.

Empezaré con un simple site estático llamado hello-jenkins.

Ahora, haré un cambio y un deploy manual.
  • En mi local, agrego un title, commit y push
  • En mi hosting, hago pull
    • Lo pesado de esta tarea es cuando no estoy logueado a la consola. Es necesario hacer ssh, ubicar la carpeta y hacer pull.
    • Si Jenkins vive en este servidor, podría hacerlo por mi ante una señal. Por ejemplo, cuando se hace un push en GitHub.
  • https://rulokoba.me/~rulo/demos/hello-jenkins/
En el hosting, tengo instalado Jenkins siguiendo la guía de:
En mi Jenkins, creo el job hello-jenkins:
  • Elijo create new jobs
  • Enter an item name: hello-jenkins
  • Elijo Freestyle project
  • OK
  • En la sección de consiguración:
    • Source Code Management
      • Git
        • Repository URL: https://github.com/akobashikawa/hello-jenkins.git
        • Credentials: none
        • Additional Behaviours
          • Check out to a sub-directory
            • Local sub-directory for repo:
              /home/rulo/public_html/demos/hello-jenkins/
            • Para que jenkins pueda escribir en este directorio se le puede agregar al grupo del usuario y reiniciar jenkins:
              • $ sudo usermod -a -G rulo jenkins
              • $ sudo systemctl stop jenkins
              • $ sudo systemctl start jenkins
    • Build Triggers
      • GitHub hook trigger for GITScm polling
    • Build (opcional)
      • Execute shell
        • Command
          • echo "hello-jenkins build"
En el repo de GitHub, Settings:
  • Webhooks
    • Add webhook
      • Payload URL: http://myjenkins_url/github-webhook/
        • Es importante el trailing / al final
      • Content type: application/json
      • Which events would you like to trigger this webhook?
        • [x] Let me select individual events.
          • [x] Pushes
            • [x] Pull requests
      • [x] Active
      • Add webhook
      • Es importante que GitHub pueda encontrar a Jenkins ya configurado
Para probar, manualmente:
  • En mi Jenkins, en el projecto hello-jenkins, wlijo Build Now
  • En Build History aparecerá un nuevo item numerado, y le hago click.
  • Elijo Console Output para ver la salida
Para probar el build automático:
  • En mi local, hago un cambio, commit y push al repo
  • En Build History aparecerá un nuevo item numerado, y le hago click.
  • Elijo Console Output para ver la salida

2018/09/18

Solucionando conexión de strapi a mLab



Strapi es un framework para node que facilita la creación de un cms.
  • Como base de datos puede usar mongo, mysql, postgres.
  • Como frontend puede usar vue, react, angular.
  • El panel de administración luce bastante similar al de wordpress.
  • Tiene un plugin para servir los datos con graphql.
  • El idioma se puede configurar a español.
mLab es un servicio que provee acceso a bases de datos mongo.
  • Tiene una capa de uso gratuita (via AWS de Amazon)

Instalación simple

  • Para usarlo con mongo, debe estar iniciado el servicio localmente
  • $ node --version
    • Se requiere al menos la versión 9
  • $ npm install -g strapi@alpha
    • Por alguna razón, recomiendan que trabajemos con la versión más fresca
  • $ strapi --version
    • Actualmente 3.0.0-alpha.14.1.1
  • $ strapi new myapp
    • Creará la estructura de directorios.
    • Hay que elegir como base de datos a mongo e indicar los parámetros
      • Esta data se guarda en myapp/config/environments/development/database.json
      • La base de datos aún no se crea en este punto
  • $ cd myapp
  • $ strapi start

Problema con mongo externo

Por alguna razón, cuando se indica que se conecte a una base de datos mongo externa para development, strapi devuelve un error.

Encuentro que en la configuración para production hay una propiedad extra uri.

Cuando se usa esta propiedad uri en la configuración para development, ya es posible conectarse a una base de datos mongo externa, como mlab, por ejemplo.

Solución

  • $ strapi new myapp
    • Crear la aplicación indicando una base de datos mongo local
    • Esto aún no creará ninguna base de datos
  • Editar myapp/config/environments/development/database.json y agregar la propiedad uri:
  • $ cd myapp
  • $ strapi start
La configuración sería similar a:
{
  "defaultConnection": "default",
  "connections": {
    "default": {
      "connector": "strapi-hook-mongoose",
      "settings": {
        "client": "mongo",
        "uri": "mongodb://username:password@hostname.mlab.com:12345/dbname",
        "host": "hostname.mlab.com",
        "srv": false,
        "port": "12345",
        "database": "dbname",
        "username": "username",
        "password": "password"
      },
      "options": {
        "authenticationDatabase": "",
        "ssl": false
      }
    }
  }
}

2017/12/13

Test de internet con ping en NodeJS


En estos días he tenido intermitencias en el servicio de internet.

Mi forma de comprobarlo es entrar en una consola y hacer ping a un host, como google.com, y ver si está ok o no.

$ ping google.com -t


El -t permite que el ping se haga indefinidamente, hasta que se presione CTR + C.

Sin embargo, quería una forma de mostrar los cambios de estado, a qué hora ocurrían y cuánto duraban.

Programé esta solución usando nodejs:

GitHub Gist: Internet test with ping in NodeJS

const ping = require('ping');

let host = '8.8.8.8';

if (process.argv[2]) {
  host = process.argv[2];
} else {
  console.log("Syntax: \nnode ping-test-ifchanges-host host");
}

console.log("Testing host: " + host);

let isAlivePrev = false;
let nowPrev = new Date();

function doPing() {
  ping.sys.probe(host, isAlive => {
    if (isAlive !== isAlivePrev) {
      let now = new Date();
      let diff = (now.getTime() - nowPrev.getTime())/1000;
      let timestamp = now.toISOString() + ' after: ' + diff + ' s';
      console.log("\n" 
        + host + ': ' 
        + (isAlive ? ' OK' : ' KO') 
        + ' ' + timestamp);
      isAlivePrev = isAlive;
      nowPrev = now;
    } else {
      process.stdout.write('.');
    }
  });
}

setInterval(doPing, 1000);

Para correrlo en consola:

$ node ping-test-ifchanges-host google.com

Idea

setInterval() establece un llamado, cada 1000 ms, a doPing(), que hace un ping al host que se haya indicado y revisa si hay un cambio de estado.

Si hay un cambio de estado, anota el tiempo y muestra las diferencias respecto al tiempo anterior.

Si no hay cambio de estado, imprime simplemente un punto.

2017/12/04

CSS: Checkbox hack para controles personalizados

¿Cómo aplicar un estilo como respuesta a un click, usando sólamente CSS?

Se puede hacer con el checkbox hack.

Principio

  • Un label asociado a un input puede recibir el click por él
    • Puede haber varios labels para un mismo input
    • Ocultando el input (difícil de personalizar), queda el label para contener un control más fácil de personalizar
  • La pseudoclass :checked permite ubicar un checkbox seleccionado
  • El selector aaa + bbb permite seleccionar un bbb que esté (declarado) inmediatamente después de un aaa

Patrón 1

HTML:
<input id="checkbox-control" type="checkbox">
<label for="checkbox-control">Control</label>

CSS:
#checkbox-control:checked + label {...}

Patrón 2

HTML:
<label for="checkbox-control">Control</label>
<input id="checkbox-control" type="checkbox">
<div class="target">Target</div>

CSS:
#checkbox-control:checked + .target {...} 

Patrón 3

HTML:
<input id="checkbox-control" type="checkbox">
<label for="checkbox-control">Control</label>
<div class="target">Target</div>

CSS:
#checkbox-control:checked + label {...}
#checkbox-control:checked + label + .target {...}

Ideas de aplicación

  • Checkbox personalizado
    • Toggle
  • Tabs
  • Dropdown menu
  • Dot control para slider
  • Árbol desplegable
Ver unos ejemplos en A check studio, en CodePen

Referencias




2017/10/18

La solución imperfecta


¿Por qué el mundo esta lleno de soluciones imperfectas?

Porque la solución depende del contexto.

Digamos que tienes cinco minutos para ordenar la salida de un sistema y un plugin ya listo que lo hace.

El algoritmo hace una ordenación simple tipo burbuja pero cumple con lo que necesitamos.

Si tuvieras un par de horas, quizas podrías hacer desde cero un plugin con el mejor algoritmo de ordenación posible.

Si tuvieras una hora, quizás podrías modificar este plugin y mejorar el algoritmo de ordenación.

Si tuvieras media hora, quizás podrías buscar si alguien más ha hecho ese plugin.

Pero tienes cinco minutos.

Poner este plugin te llevará un minuto, un pequeño test otro minuto, y ya estás gastando tiempo en analizar las alternativas. Alternativas que requieren más tiempo del que dispones. Y todas con un quizás. No existen. Sólo existe este plugin imperfecto, aquí y ahora.

No se trata de usar las respuestas de un libro de texto. Se trata de la vida real, retándote a hacer lo mejor que puedas con lo que tienes disponible en este momento.

Lo eliges. No es la respuesta perfecta, pero es la solución. En este contexto, es la respuesta.

Te gustaría que alguien te comprenda por usar una respuesta no optima.

Ahora te parece tan obvio.

Realizar una solución imperfecta es mejor que una respuesta perfecta que no se puede realizar.

Lo aceptas, y sigues adelante. Quizás en la siguiente iteración la puedas mejorar.

También es un quizás. Pero es es un quizás con algo corriendo.


Imagen de http://news.nationalgeographic.com/content/dam/news/2017/04/27/frog-gallery/01-frog-day-gallery.jpg