
Automatice su flujo de trabajo con Node

Ya conoces esas tareas tediosas que tienes que hacer en el trabajo: actualizar archivos de configuración, copiar y pegar archivos, actualizar tickets de Jira.
El tiempo se acumula después de un tiempo. Este fue en gran medida el caso cuando trabajé para una empresa de juegos en línea en 2016. El trabajo podía ser muy gratificante en momentos en que tenía que crear plantillas configurables para juegos, pero aproximadamente el 70% de mi tiempo lo dedicaba a hacer copias . De ellas. Plantillas e implementar implementaciones rediseñadas.
¿Qué es un reskin?
La definición de reskin en la empresa fue utilizar la misma mecánica de juego, pantallas y posicionamiento de elementos, pero cambiando la estética visual como el color y los activos. Entonces, en el contexto de un juego simple como 'Piedra, papel o tijera', crearíamos una plantilla con recursos básicos como los que se muestran a continuación.
Pero cuando creamos una nueva apariencia de esto, usaríamos diferentes recursos y el juego seguiría funcionando. Si miras juegos como Candy Crush o Angry Birds, encontrarás que tienen muchas variedades del mismo juego. Normalmente se lanzan en Halloween, Navidad o Semana Santa. Desde una perspectiva empresarial tiene mucho sentido.
Ahora… volvamos a nuestra implementación. Cada uno de nuestros juegos compartiría el mismo archivo JavaScript incluido y se cargaría en un archivo JSON que tenía diferentes contenidos y rutas de recursos. ¿El resultado?
Lo bueno de extraer valores configurables en un archivo JSON es que puedes modificar las propiedades sin tener que recompilar/compilar el juego nuevamente. Usando Node.js y el juego de ruptura original creado por Mozilla, haremos un ejemplo muy simple de cómo se puede crear una plantilla configurable y realizar lanzamientos desde ella usando la línea de comando.
Juego de azar
Este es el juego que haremos. Reskins de MDN Breakout, basado en el código fuente existente.
El color primario pintará el texto, la paleta, la pelota y los bloques, y el color secundario pintará el fondo. Procederemos con un ejemplo de fondo azul oscuro y azul cielo claro para los objetos en primer plano.
Requisitos previos
Deberá asegurarse de lo siguiente:
- Tienes Git instalado: https://git-scm.com/downloads
- Tienes Node instalado – https://nodejs.org/en/download
- Tienes una cuenta de GitHub: https://github.com
- Ha clonado el repositorio localmente: https://github.com/smks/nobot-examples.git
- Ha corrido
npm install
en la raíz delnobot-examples
proyecto. - Finalmente, ejecuta el servidor local ejecutándolo
npm run gameServe
en la raíz del proyecto a través de una terminal.
Hemos modificado la implementación original de Firefox para que primero leamos el archivo JSON y luego construyamos el juego usando HTML Canvas. El juego se leerá en un color primario y en un color secundario de nuestro archivo game.json.
{ "primaryColor": "#fff", "secondaryColor": "#000"}
Usaremos el ejemplo 20 del libro Automatización con Node.js. El código fuente se puede encontrar aquí.
Abra una nueva línea de comando (CMD para Windows, Terminal para sistemas operativos tipo Unix) y cambie al siguiente directorio una vez que haya clonado el repositorio localmente.
$ cd nobot-examples/examples/020
Recuerde que el servidor del juego debe ejecutarse en una terminal separada.
Nuestro archivo JSON se encuentra junto a un archivo index.html dentro de un directorio llamado plantilla. Este es el directorio desde el que copiaremos cada vez que queramos hacer una nueva versión/copia.
!DOCTYPE htmlhtmlhead meta charset="utf-8" / meta name="viewport" content="width=device-width, initial-scale=1" titlePaddle Game/title style * { padding: 0; margin: 0; } canvas { background: #eee; display: block; margin: 0 auto; } /style/headbody canvas/canvas script type="text/javascript" src="../../core/game-1.0.0.js"/script/body/html
Como ve arriba, cada juego que lanzamos apuntará al mismo archivo JavaScript del paquete principal. Echemos un vistazo a nuestra implementación de JavaScript en el directorio principal.
No mires demasiado la mecánica de cómo funciona el juego, sino más bien cómo inyectamos valores en el juego para hacerlo configurable.
(function boot(document) { function runGame(config) { const canvas = document.getElementById('game'); canvas.style.backgroundColor = config.secondaryColor; // rest of game source code gets executed... hidden for brevity // source can be found here: https://git.io/vh1Te } function loadConfig() { fetch('game.json') .then(response = response.json()) .then(runGame); } document.addEventListener('DOMContentLoaded', () = { loadConfig(); });}(document));
El código fuente utiliza funciones de ES6 y es posible que no funcione en navegadores más antiguos. Ejecuta Babel si esto es un problema para usted.
Puedes ver que estamos esperando que se cargue el contenido DOM y luego invocamos un método llamado loadConfig
. Esto realizará una solicitud AJAX a game.json, recuperará nuestros valores JSON y, una vez que los haya recuperado, iniciará el juego y asignará los estilos en el código fuente.
A continuación se muestra un ejemplo de la configuración estableciendo el color de fondo.
const canvas = document.getElementById('game');canvas.style.backgroundColor = config.secondaryColor; // overriding color here
Entonces, ahora que tenemos una plantilla que se puede configurar, podemos pasar a crear un script Node.js que permitirá al usuario pasar el nombre del juego y los colores como opciones a nuestro nuevo script, o le indicará al usuario: el nombre del juego. , el color primario y luego el color secundario. Nuestro script aplicará la validación para garantizar que los dos colores estén en el formato de un código hexadecimal (p. ej #101b6b
.).
Cuando queramos crear un nuevo diseño de juego, deberíamos poder ejecutar este comando para generarlo:
$ node new-reskin.js --gameName='blue-reskin' --gamePrimaryColor='#76cad8' --gameSecondaryColor='#10496b'
El comando anterior creará el juego inmediatamente, porque tiene los tres valores que se necesitan para liberar el reskin.
Crearemos este script new-reskin.js , y este archivo realiza los siguientes pasos:
- Leerá las opciones pasadas en la línea de comando y las almacenará como variables. Las opciones se pueden leer mirando el objeto de proceso (
process.argv
). - Validará los valores asegurándose de que el nombre del juego y los colores no estén indefinidos.
- Si hay algún problema de validación, se le solicitará al usuario que vuelva a ingresarlo correctamente antes de continuar.
- Ahora que tiene los valores, hará una copia del directorio de plantillas y colocará una copia en el directorio de lanzamientos y nombrará el nuevo directorio con el nombre del juego que le asignamos.
- Luego leerá el archivo JSON creado recientemente en el directorio de versiones y anulará los valores con los valores que pasaron (los colores).
- Al final, le preguntará al usuario si desea abrir el juego en un navegador. Agrega cierta comodidad, en lugar de que tengamos que recordar cuál es la URL.
Aquí está el guión completo. Lo recorreremos después.
require('colors');const argv = require('minimist')(process.argv.slice(2));const path = require('path');const readLineSync = require('readline-sync');const fse = require('fs-extra');const open = require('opn');const GAME_JSON_FILENAME = 'game.json';let { gameName, gamePrimaryColor, gameSecondaryColor } = argv;if (gameName === undefined) { gameName = readLineSync.question('What is the name of the new reskin? ', { limit: input = input.trim().length 0, limitMessage: 'The project has to have a name, try again' });}const confirmColorInput = (color, colorType = 'primary') = { const hexColorRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/; if (hexColorRegex.test(color)) { return color; } return readLineSync.question(`Enter a Hex Code for the game ${colorType} color `, { limit: hexColorRegex, limitMessage: 'Enter a valid hex code: #efefef' });};gamePrimaryColor = confirmColorInput(gamePrimaryColor, 'primary');gameSecondaryColor = confirmColorInput(gameSecondaryColor, 'secondary');console.log(`Creating a new reskin '${gameName}' with skin color: Primary: '${gamePrimaryColor}' Secondary: '${gameSecondaryColor}'`);const src = path.join(__dirname, 'template');const destination = path.join(__dirname, 'releases', gameName);const configurationFilePath = path.join(destination, GAME_JSON_FILENAME);const projectToOpen = path.join('http://localhost:8080', 'releases', gameName, 'index.html');fse.copy(src, destination) .then(() = { console.log(`Successfully created ${destination}`.green); return fse.readJson(configurationFilePath); }) .then((config) = { const newConfig = config; newConfig.primaryColor = gamePrimaryColor; newConfig.secondaryColor = gameSecondaryColor; return fse.writeJson(configurationFilePath, newConfig); }) .then(() = { console.log(`Updated configuration file ${configurationFilePath}`green); openGameIfAgreed(projectToOpen); }) .catch(console.error);const openGameIfAgreed = (fileToOpen) = { const isOpeningGame = readLineSync.keyInYN('Would you like to open the game? '); if (isOpeningGame) { open(fileToOpen); }};
En la parte superior del script, requerimos los paquetes necesarios para realizar el proceso.
colors
Para utilizar para indicar éxito o fracaso utilizando texto verde o rojo.minimist
para que sea más fácil pasar argumentos a nuestro guión y analizarlos opcionalmente. Pasar la entrada sin que se le solicite ingresar.path
para construir caminos hacia la plantilla y el destino del nuevo juego.readline-sync
para solicitar al usuario información si falta.fs-extra
para que podamos copiar y pegar nuestra plantilla de juego. Una extensión delfs
módulo nativo.opn
es una biblioteca que es multiplataforma y abrirá nuestro juego en un navegador al finalizar.
La mayoría de los módulos anteriores se habrían descargado/instalado cuando los ejecutó npm install
en la raíz del repositorio de ejemplos de nobot. El resto son nativos de Node.
Comprobamos si el nombre del juego se pasó como opción a través de la línea de comando y, si no fue así, se lo solicitamos al usuario.
// name of our JSON file. We store it as a constantconst GAME_JSON_FILENAME = 'game.json';// Retrieved from the command line --gameName='my-game' etc.let { gameName, gamePrimaryColor, gameSecondaryColor } = argv;// was the gameName passed?if (gameName === undefined) { gameName = readLineSync.question('What is the name of the new reskin? ', { limit: input = input.trim().length 0, limitMessage: 'The project has to have a name, try again' });}
Debido a que dos de nuestros valores deben ser códigos hexadecimales, creamos una función que puede verificar ambos colores: el primario y el secundario. Si el color proporcionado por el usuario no pasa nuestra validación, solicitamos el color hasta que lo haga.
// Does the color passed in meet our validation requirements?const confirmColorInput = (color, colorType = 'primary') = { const hexColorRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/; if (hexColorRegex.test(color)) { return color; } return readLineSync.question(`Enter a Hex Code for the game ${colorType} color `, { limit: hexColorRegex, limitMessage: 'Enter a valid hex code: #efefef' });};
Usamos la función anterior para obtener los colores primarios y secundarios.
gamePrimaryColor = confirmColorInput(gamePrimaryColor, 'primary');gameSecondaryColor = confirmColorInput(gameSecondaryColor, 'secondary');
En el siguiente bloque de código, imprimimos en la salida estándar ( console.log
) para confirmar los valores que se utilizarán en el proceso de creación del juego. Las declaraciones que siguen preparando las rutas a los archivos y directorios relevantes.
Apuntará src
al directorio de la plantilla. Apuntará destination
un nuevo directorio en lanzamientos. El archivo de configuración cuyos valores se actualizarán residirán en este nuevo directorio de juego que estamos creando. Y finalmente, para obtener una vista previa de nuestro nuevo juego, construimos la URL usando la ruta al servidor local que iniciamos anteriormente.
console.log(`Creating a new reskin '${gameName}' with skin color: Primary: '${gamePrimaryColor}' Secondary: '${gameSecondaryColor}'`);const src = path.join(__dirname, 'template');const destination = path.join(__dirname, 'releases', gameName);const configurationFilePath = path.join(destination, GAME_JSON_FILENAME);const projectToOpen = path.join('http://localhost:8080', 'releases', gameName, 'index.html');
En el código que sigue a esta explicación, nosotros:
- Copiar los archivos de plantilla al directorio de versiones.
- Una vez creado esto, leemos el JSON de los valores de la plantilla original.
- Con el nuevo objeto de configuración, anulamos los colores primarios y secundarios existentes proporcionados por la entrada del usuario.
- Reescribimos el archivo JSON para que tengamos los nuevos valores.
- Cuando se actualiza el archivo JSON, le preguntamos al usuario si desea abrir el nuevo juego en un navegador.
- Si algo salió mal, detectamos el error y lo cerramos.
fse.copy(src, destination) .then(() = { console.log(`Successfully created ${destination}`green); return fse.readJson(configurationFilePath); }) .then((config) = { const newConfig = config; newConfig.primaryColor = gamePrimaryColor; newConfig.secondaryColor = gameSecondaryColor; return fse.writeJson(configurationFilePath, newConfig); }) .then(() = { console.log(`Updated configuration file ${configurationFilePath}`green); openGameIfAgreed(projectToOpen); }) .catch(console.error);
A continuación se muestra la función que se invoca cuando se completa la copia. Luego le preguntará al usuario si desea abrir el juego en el navegador. El usuario responde con y
on
const openGameIfAgreed = (fileToOpen) = { const isOpeningGame = readLineSync.keyInYN('Would you like to open the game? '); if (isOpeningGame) { open(fileToOpen); }};
Veámoslo en acción cuando no pasemos ningún argumento. Puede ver que no se rompe y, en cambio, solicita al usuario los valores que necesita.
$ node new-reskin.jsWhat is the name of the new reskin? blue-reskinEnter a Hex Code for the game primary color #76cad8Enter a Hex Code for the game secondary color #10496bCreating a new reskin 'blue-reskin' with skin color: Primary: '#76cad8' Secondary: '#10496b'Successfully created nobot-examplesexamples20releasesblue-reskinUpdated configuration file nobot-examplesexamples20releasesblue-reskingame.jsonWould you like to open the game? [y/n]: y(opens game in browser)
Mi juego se abre automáticamente en mi servidor localhost y el juego comienza con los nuevos colores. ¡Dulce!
Oh… ya perdiste la vida. Ahora, si navega por el directorio de versiones, verá un nuevo directorio llamado blue-reskin
Este contiene los valores en el archivo JSON que ingresamos durante la ejecución del script.
A continuación se muestran algunas versiones más que realicé ejecutar el mismo comando. Puedes imaginar que si estuvieras lanzando juegos que pudieran configurar diferentes: imágenes, sonidos, etiquetas, contenido y fuentes, podrías una rica biblioteca de juegos basada en la misma mecánica.
Aún mejor, si las partes interesadas y los diseñadores tuvieran toda esta información en un ticket de Jira, podría integrar la API de Jira en el script de Node para inyectar estos valores sin que el usuario tenga que proporcionar ninguna información. ¡Victorioso!
Este es uno de los muchos ejemplos que se pueden encontrar en Automatización con Node.js. En este libro veremos un ejemplo más avanzado utilizando “Piedra, papel o tijera” como base de una herramienta de construcción creada desde cero.
- Impresión en color: http://amzn.eu/aA0cSnu
- Encendedor: https://amzn.to/2JPTk7q
- Kobo: https://www.kobo.com/gb/en/ebook/automatizacion-con-node-js
- Leanpub: https://leanpub.com/automatingwithnodejs
Deja una respuesta