Adentrándose en Unix


Trasteando Gource

Posted in Comunidad por Noelia en 16 mayo 2010
Tags: , , ,

Hola de nuevo:

En la última entrada comenté un poco por encima un programita muy curioso y subí un vídeo de lo que mostraba al pasarle el repositorio de WikiUNIX. Este programa se llama Gource y para el que no lo leyera en la entrada anterior, sirve para generar una animación para visualizar el desarrollo de proyectos a partir de la información administrada por el sistema de control de versiones.

Gource soporta directamente Git, Bazaar y Mercurial, por lo que sólo tenéis que ejecutarlo desde la raíz del repositorio y listo. Sin embargo, como ya comenté había que utilizar trucos para que tirara en Subversion (no es que sea muy fan de este sistema de control de versiones, pero en la forja de RedIRIS no hay más remedio). Uno de esos trucos era utilizar un script, pero antes de esto había que llamar a svn para guardar los logs del repositorio, y pensé: “Pues hacer esto completamente automático es facilillo”, así que he retocado el script que ya estaba hecho para tener que ejecutar una única orden:

#!/usr/bin/python
# -*- coding: utf-8  -*-
## Copyright (c) 2010 Noelia Sales Montes (noelia.salesmontes@gmail.com)
## Copyright (c) 2009 Cameron Hart (cam@bitshifter.net.nz)
## All rights reserved.
##
## Redistribution and use in source and binary forms, with or without
## modification, are permitted provided that the following conditions
## are met:
## 1. Redistributions of source code must retain the above copyright
##    notice, this list of conditions and the following disclaimer.
## 2. Redistributions in binary form must reproduce the above copyright
##    notice, this list of conditions and the following disclaimer in the
##    documentation and/or other materials provided with the distribution.
## 3. The name of the author may not be used to endorse or promote products
##    derived from this software without specific prior written permission.
##
## THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
## IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
## OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
## IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
## INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
## NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
## THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
Python script that converts the XML output of the svn log command into a
format that can be read by the gource custom log format option and execute
gource to show the animation.
"""

import sys
import os
import time
import getopt
import re
from xml.etree import ElementTree

__clean__ = False
__verbose__ = False
__filter_dirs__ = False
# width x height
__size__ = "800x600"
# seconds per day
__time__ = "1"
__usage__ = """
USAGE: svn-gource.py [--help] [--clean] [--verbose] [--filter-dirs]
[--size WIDTHxHEIGHT] [--time SECONDS-PER-DAY]
"""
# regular expression for matching any file with an extension
__extn_prog__ = re.compile(".*/?[^/]+\.[^\.]+$")

def reverse(data):
    """Returns the log entries in reverse."""
    for index in range(len(data)-1, -1, -1):
        yield data[index]

def verb(message):
    """Prints in verbose mode."""
    if __verbose__ == True:
        print message

def process_xmltree(__xmltree, logfile):
    """Prints the log file converted into the gource format in a new log file.
    """
    if os.path.exists(logfile):
        os.remove(logfile)
        verb("Removing %s" % logfile)

    verb("Translating %s" % logfile)

    for logentry in reverse(__xmltree.getiterator("logentry")):
        datetext = logentry.find("date").text

        # svn xml logs always use UTC
        timestamp = (time.mktime(time.strptime(datetext[:-8],
                                               "%Y-%m-%dT%H:%M:%S")))
        # a bit of a hack to get it into local time again...
        #timestamp = timestamp - time.timezone

        #author might not exist
        try:
            author = logentry.find("author").text
        except:
            author = ""

        __f = open(logfile, "a")

        # output all affected files
        for pathentry in logentry.getiterator("path"):

            # apply directory filtering strategy
            if __filter_dirs__ and not re.match(__extn_prog__,pathentry.text):
                continue;

            # join output
            __f.write( "|".join(("%d" % int(timestamp),
                               "%s" % author.encode("utf-8"),
                               "%s" % pathentry.get("action"),
                               "%s" % pathentry.text.encode("utf-8"), "\n")))

        __f.close()

def print_usage(message):
    """ Prints the script's usage """
    sys.stderr.write(__usage__)
    if message:
        sys.exit('\nFATAL ERROR: ' + message)
    else:
        sys.exit(1)

if __name__ == "__main__":
    try:
        __opts__, __args__ = getopt.getopt(sys.argv[1:],
                                           'hvcfs:t:', ['help',
                                                        'verbose',
                                                        'clean',
                                                        'filter-dirs',
                                                        'size',
                                                        'time'])
    except getopt.GetoptError:
        print_usage('Invalid arguments.')

    for (opt, val) in __opts__:
        if opt in ('--help', '-h'):
            print_usage(None)
        elif opt in ('--clean', '-c'):
            __clean__ = True
        elif opt in ('--verbose', '-v'):
            __verbose__ = True
        elif opt in ('--filter-dirs', '-f'):
            __filter_dirs__ = True
        elif opt in ('--size', '-s'):
            __size__ = val
        elif opt in ('--time', '-t'):
            __time__ = val

    verb('  OPTIONS:')
    verb('Size = %s' % __size__)
    verb('Seconds per day = %s\n' % __time__)

    os.popen("svn log --verbose --xml > project.log")

    __XMLTREE = ElementTree.parse("project.log")

    process_xmltree(__XMLTREE, "project-gource.log")

    verb('Calling gource... Showing the animation.')

    os.popen("gource -%s -s %s --log-format custom project-gource.log" %
             (__size__, __time__))

    if __clean__:
        verb("Removing log files.")
        os.remove("project.log")
        os.remove("project-gource.log")

Os comento un poco por encima qué opciones tiene el script:

  • –help, -h: Típica ayuda.
  • –verbose, -v: Modo chivato, por si queréis ver qué va haciendo (tengo costumbre de ponerlo por facilitarme el testeo…).
  • –clean, -c: Una vez finalizada o cancelada la animación, borra los ficheros de log utilizados, en este caso “project.log” y “project-gource.log”. De todas formas, se fuerza el borrado de “project-gource.log” antes de generarlo de cero, para asegurar que no se guarda nada raro de alguna ejecución anterior.
  • –filter-dirs, f:
  • –size, -s ANCHOxALTO: Esta opción recibe el tamaño de la ventana que generará gource. Atención que tanto el ancho como el alto se pasan en un mismo argumento, separados únicamente por una “x”. Si no se utiliza esta opción, se tienen por defecto 800×600.
  • –time, -t: Número de segundos por día que tomará la animcación. Si no se utiliza esta opción, se tiene por defecto 1 segundo por día.

Bueno, lo único que tenéis que hacer es guardarlo en “svn_gource.py” y ejecutarlo desde la raíz del repositorio pertinente (si tenéis varios repositorios, os resultará más cómodo tenerlo en el directorio padre de éstos y ejecutarlo desde dentro) con un simple:

python svn_gource

Yo suelo ejecutarlo usando las opciones:

python svn_gource -s 1200×600 -t 0.1 -v -c

A ver si puedo habilitar la subida de ficheros “especiales” al wiki para facilitar la descarga de los scripts.

Un saludo a todos, unixeros😉

PD: Para cualquier duda, problema, comentario, … acerca del script podéis utilizar los comentarios del blog o escribir en la página de discusión del script en el wiki directamente.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s


A %d blogueros les gusta esto: