Procesando documentos JSON desde BASH

Publicado por

Uno de los productos que desarrolla mi equipo en el trabajo, tiene actualizaciones muy frecuentes, tanto así que el número de versión se incrementa hasta niveles que resultan poco útiles, sobre todo para rastrear cambios a través del tiempo.

El problema es el siguiente: el producto en cuestión empezó con el número de versión “1.0.0”, luego del primer cambio, la versión aumento a “1.0.1” y después de un tiempo terminamos con la versión “1.0.135”. Uno de mis compañeros ha sugerido que en lugar de incrementar los número así nada mas, dotemos a los campos de la versión de un significado, y dado que no seguimos ningún esquema en particular, podríamos usar el año y el mes en que la versión es publicada, incrementando solamente el último campo, de esta manera, al cambiar de mes reiniciaríamos este último contador a cero. Es decir empezaremos en la versión “21.04.0”, y la siguiente versión será “21.04.1” si esta es liberada en abril de 2021 o “21.05.0” si la versión es liberada hasta el mes de mayo, dándonos una referencia mas útil sobre el contenido de la versión.

Dicho esto, y como muchos programadores me gusta automatizar tareas repetitivas escribí un script que actualice el número de versión por mi.

El proyecto del que te he estado platicando es una biblioteca de componentes de React, por lo que el proyecto es descrito en un archivo package.json.

El script es el siguiente:

#!/bin/bash

package=${1:-package.json}

this_month=$(date +%m)
this_year=$(date +%y)

current_version=$(jq -r '.version' $package)

IFS='.' read -r year month n <<< "$current_version"

[ $this_month -ne $month ] && year=$this_year && month=$this_month && n=0 || let n++

new_version="$year.$month.$n"
echo $(jq ".version = \"$new_version\"" $package) > $package

Es un script pequeño pero que utiliza suficientes recursos como para que valga la pena explicarlos, con la esperanza de que algunas de estas ideas te sirvan para tus proyectos. Veamos en que consiste línea por línea:

Para empezar, la variable  $package está definida tomando el primer argumento desde la línea de comandos (representada por la variable especial  $1, la notación entre llaves sirve para designar un valor por defecto, en este caso: la cadena "package.json".

Las siguientes variables $this_month$this_year se definen a partir de la salida del comando date utilizando una subshell $().

this_month=$(date +%m)
this_year=$(date +%y)

El siguiente paso consiste en averiguar el valor actual de la versión del paquete, para lo cual nos valemos de dos herramientas: primero el programa jq que es un poderoso procesador de texto en formato JSON, entre otras cosas jq te permite consultar porciones de un documento JSON y transformar ese contenido en otro documento JSON con la estructura que tu quieras (comenta si te interesaría una nota explicando a fondo como explotar esta herramienta). En este caso, utilizamos jq para extraer el valor actual de la versión del paquete, guardando dicho valor en una variable a través de otro subshell.

current_version=$(jq -r '.version' $package)

Acto seguido utilizamos el comando read que es parte de BASH para dividir la versión del paquete en los tres campos que nos interesan, para conseguir ese resultado, debemos indicarle a BASH que queremos utilizar el caracter punto . como separador y, asignando ese valor a la variable especial $IFS, luego utilizamos read para leer el contenido de la variable $current_version, guardando los tres campos leídos en las variables $year, $month y $n respectivamente. Recuerda que lo que buscamos es otorgarle significado a cada campo del número de versión, es por ello que elegí esos nombres para estas variables.

IFS='.' read -r year month n <<< "$current_version"

Ahora viene la que realmente es el motivo de existir de este programa: calcular el siguiente número de versión. Para ello, el criterio que utilizo es fijarme en el valor de $month, y compararlo con el mes actual $this_month , ya que este será su nuevo significado. Si estos valores no son iguales (-ne), lo que toca es asignar el año y mes actuales a las variables $year y $month así como fijar en cero a la variable $n; en caso contrario únicamente hace falta incrementar el valor de $n en uno. Todo ello se consigue en una sola línea gracias a nuestros amigos: los operadores && y || (comenta si quisieras leer una nota explicando estos operadores).

[ $this_month -ne $month ] && year=$this_year && month=$this_month && n=0 || let n++

Con todos los campos calculados, lo que sigue es construir el nuevo número de versión para finalmente guardarlo en el archivo $package, para esto nos valemos de los programas echo y nuevamente jq.

new_version="$year.$month.$n"
echo $(jq ".version = \"$new_version\"" $package) > $package

Lo que sucede aquí es que jq recibe la orden de cambiar el valor del campo .version dentro del documento $package, como resultado jq imprime el nuevo documento en la salida estándar, pero en lugar de eso nuestro script toma ese texto mediante una subshell a partir de este construye una cadena de texto, y le da la oportunidad a la computadora de cerrar el archivo $package. Esto es importante porque lo que queremos que haga el programa echo es sobre escribir el archivo original con el documento modificado que tenemos en memoria.

Suponiendo que todo el código que hemos visto en esta nota lo guardamos en un archivo update.sh podemos ejecutarlo desde una terminal mediante el comando:

➜ bash update.sh

O bien, dándole permisos de ejecución:

➜ chmod +x update.sh
➜ ./update.sh

En todo caso, el resultado final se debe ver así:

el script en acción

Eso es todo por esta ocasión, espero que esta nota te aporte ideas que te sirvan para tu propios proyectos, nos vemos en la siguiente nota.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *