Deployment a openshift con codeship

En este post se explica como usar el deployment de codeship para enviar el código fuente disponible en un repositorio git a una app de openshift.

Para los pasos siguientes se necesita disponer de :

  • Una app en openshift y la dirección del repositorio git respectivo.
  • Un proyecto creado en codeship.
  • Un repositorio git alojado en bitbucket o github.

Para que codeship pueda subir los cambios a la app en openshift, guardar la llave publica del proyecto de codeship en un archivo de texto, la cual se encuentra disponible en

Project settings > General > SSH public key

Y agregar la llave publica de codeship a la app de openshift

rhc sshkey add codeship llave_ssh_publica.pub

Agregar al deployment script de codeship, ubicado en

Project settings > deployment

Las lineas para clonar nuestro repositorio y hacer push a openshift con los cambios actuales

git remote add openshift repositorio_de_openshift_app
git push openshift master

Con esto, los cambios en la rama master serán enviados a la app de openshift.

Podría pensarse que es más simple enviar directamente desde nuestro repositorio local al de openshift, pero al pasar por codeship nos aseguramos que antes se pasar a producción siempre sean lanzados los test, y si alguno falla, no sean subidos los nuevos cambios. Además de quedar registrados todos lo mensajes generados por openshift.

Lanzando tests de python con grunt

En el ciclo de desarrollo: escribir un test que falla, modificar el código para que el test pase e iterar, hay tareas que se repiten, como lanzar los test después de hacer modificaciones.

Llamar a los tests implica usar un comando como

python -m unittest tests

O desde un setup.py

python setup.py test

En cualquiera de los dos casos anteriores es necesario pasar del editor a otra consola y ejecutar un comando cada vez que se realiza una modificación.

Para facilitar el proceso descrito es preferible usar alguna herramienta que vigile los archivos modificados, y cuando corresponda, lance tareas establecidas.

A continuación se presenta grunt, una solución para el problema propuesto.

Instalando Grunt

Para instalar grunt globalmente (en todo el sistema) es necesario tener nodejs y su package manager npm. En mi caso fue fácil instalar nodejs, ya que estaba trabajado en una box de nitrous.io, bastó con parts install nodejs.

Con npm, instalar grunt-cli

npm install -g grunt-cli

Crear un archivo package.json con nombre de proyecto y versión, en el cual se registraran las dependencias

// package.json
{
  "name": "pythontest",
  "version": "0.0.1"
}

E instalar lo necesario para grunt en el proyecto

npm install grunt grunt-contrib-watch --save-dev

La opción --save-dev se encargará de registrar las dependencias en el archivo package.json.

Después de las instalaciones el archivo package.json debería tener un contenido similar a

// package.json
{
  "name": "pythontest",
  "version": "0.0.1",
  "devDependencies": {
    "grunt": "^0.4.5",
    "grunt-contrib-watch": "^0.6.1"
  }
}

Y se ha creado un directorio llamado node_modules que contiene las dependencias.

Definición de tareas

Para la configuración de grunt y definición de tareas es necesario crear el archivo Gruntfile.js

module.exports = function(grunt){
    grunt.initConfig({
        watch: {
            grunt: {
                files: ['Gruntfile.js']
            },
            python: {
                files : ['./tests/*.py','./libs/*/*.py'],
                tasks: ['pythontest'],
            },
        }
    });

    grunt.registerTask('pythontest', function(){
        grunt.util.spawn({
            cmd: 'python',
            args: ['setup.py','test'],
            opts: {stdio: 'inherit'},
        });
    });

    grunt.registerTask('default', ['watch']);

};

Y lanzar grunt

grunt

Con lo cual, cada vez que un archivo definido en files sea modificado será ejecutada la tarea pythontest que lanza los test.

La opción opts: {stdio: 'inherit'} permite que la salida del comando ejecutado sea mostrada, sin esto, python setup.py test sería lanzado pero no visualizado el resultado.

Archivos modificados en git

Otra cosa que verifico son los archivos agregados a git.

git status

Por lo que también me es útil agregar una tarea que se ejecute cada vez que se modifica el index de git .git/index .

gitindex: {
    files : ['.git/index'],
    tasks: ['gitst'],
},

Y registrar la tarea

grunt.registerTask('gitst', function(){
    grunt.util.spawn({
        cmd: 'git',
        args: ['status'],
        opts: {stdio: 'inherit'},
    });
});

Conclusión

Con lo descrito anteriormente es posible dejar a grunt en una terminal independiente vigilando que archivos son modificados y lanzando tareas previamente definidas.

Herramientas como grunt permiten reducir el tiempo utilizado en tareas repetitivas y enfocarse en el ciclo de desarrollo.

El código final de Gruntfile.js y package.json está alojando en un gist de github.