Blogs

GECO's picture

He hecho un pequeño diagrama de la arquitectura de GECO:

Pulsa sobre la imágen para verlo más grande :P



frontal

GECO's picture

Intro

GECO pretende ser un gestor de contraseñas y ficheros de configuración distribuido con diferentes interfaces de comunicación con el usuario. Presentanto tanto una interfaz de escritorio en GTK como una interfaz web desarrollada con python y cherrypy.

GeCo es un proyecto presentado a la tercera edición del Concurso Universitario de Software Libre y nace de la necesidad de tener contraseñas aleatorias y diferentes para cada servicio web (o no) en el cual estás registrado.

Problema

Hoy en día la mayoría de la gente que navega con regularidad tiene multitud de cuentas creadas en diferentes páginas webs, estas cuentas, normalmente, están basadas en usuario y contraseña. Las contraseñas se almacenan de diferentes formas en las bases de datos de los diferentes servicios, de tal forma que no podemos saber si el administrado o un intruso puede llegar a ver nuestra contraseña.

Por tanto es realmente importante hoy en día utilizar claves diferentes (y aleatorias) para cada aplicación, pero memorizar 5,10,20 claves seguras es realmente complicado y para esto están los gestores de contraseñas.

Puesto que el uso de gestores de contraseñas implica que no recuerdes tus contraseñas estás perdiendo la movilidad y la libertad de usar diferentes máquinas en las cuales no tengas instalado tu gestor de contraseñas con tu base de datos de contraseñas.

Además en el caso de rotura de tu disco duro puedes llegar a perder la base de datos de tu gestor de contraseñas perdiendo así para siempre muchas contraseñas que no podrás recuperar.

Solución

GeCo pretende dar solución a los problemas que presentan los gestores de contraseñas de escritorio dando un paso más allá y prestando un servicio de almacenamiento y gestión de contraseñas distribuido (o centralizado) con diferentes interfaces de administración (línea de comandos, escritorio, web, móvil, etc).

No voy a poner TODAS mis contraseñas en ningún servidor
Por supuesto la seguridad y privacidad es uno de los objetivos de este proyecto, por tanto toda información que salga del cliente irá cifrada con una clave maestra, y por tanto en el servidor no se almacenará información recuperable sin esta contraseña.

Arquitectura

GeCo implementará una arquitectura clásica cliente-servidor, de tal forma que habrá un servidor GeCo al cual se podrán conectar clientes GeCo, para administrar contraseñas, y también otros servidores GeCo para sincronizar datos.

De esta forma un usuario puede tener su servidor GeCo en su máquina y utilizarlo normalmente como un gestor de contraseñas normal, pero además puede sincronizar su servidor con otro sevidor en una máquina remota, de tal forma que si el usuario se mueve pueda tirar de la interfaz web del servidor de la máquina remota para administrar sus contraseñas.

¿Algo más?

Además de todo esto GeCo ofrecerá la posibilidad de almacenar ficheros de configuración (todo cifrado), de tal forma que en un linux con un cliente GeCo puedas utilizar tu fichero de configuración de Pidgin sólo durante la sesión y posteriormente se eliminaría todo rastro de tus ficheros de configuración y el sistema quedaría tal y como estaba.

Licencia y otras polladas legales

Todo lo que se desarrolle será bajo licencia GPLv3 y todo el artwork y documentación será totalmente libre con la licencia "úsalo cómo quieras y para lo que quieras".

La idea de este proyecto es hacerme un gestor de contraseñas para mí, y si le sirve a otra persona pues mejor. Además quiero hacerlo de forma didactica para que a partir de este proyecto salgan diferentes manuales y charlas sobre python y desarrollo.

danigm's picture

Hace ya algún tiempo se me planteó el problema de tener una serie de funciones que se tienen que ejecutar cuando ocurre algo (un evento) y me hice un pequeño modulo en python para cubrir ese asunto.

Me basé en lo que conocía de pygtk, señales y eventos, así pues el funcionamiento básico es que tú declaras un evento, asignas funciones a ese evento, y cuando lances una señal, todas las funciones asociadas a ese evento se lanzan.

Se puede instalar de forma simple así:

sudo easy_install events

Aquí un ejemplo de uso:

  1. from events import EventManager, Event
  2.  
  3. def func1(*args):
  4. print "Im the func1 with args: " + str(args)
  5.  
  6. def func2(*args):
  7. print "Im the func2 with args: " + str(args)
  8.  
  9. def func3(*args):
  10. print "Im the func3 with args: " + str(args)
  11.  
  12. # Creating the eventManager
  13. em = EventManager()
  14.  
  15. # Create some events
  16. event1 = Event('event1')
  17. event2 = Event('event2')
  18.  
  19. em.add_event(event1)
  20. em.add_event(event2)
  21.  
  22. # Connecting functions with events
  23. em.connect('event1', func3, [1,2])
  24. em.connect('event1', func2)
  25. em.connect('event2', func1)
  26.  
  27. # Sending signals
  28. print "sending event1..."
  29. em.signal('event1')
  30. print "sending event2..."
  31. em.signal('event2')
  32.  
  33. # sending signal with arguments
  34. print "sending event1 with arguments..."
  35. em.signal('event1', [1,2,3])

Aquí está el código del módulo:

  1. class Event:
  2. def __init__(self, name):
  3. self.name = name
  4. self.listeners = {}
  5.  
  6. def add(self, function, data=None):
  7. self.listeners[function] = data
  8.  
  9. def delete(self, function):
  10. self.listeners.pop(function)
  11.  
  12. def called(self, data=None):
  13. for function, d in self.listeners.items():
  14. if data is None:
  15. if d is None:
  16. function()
  17. else:
  18. if type(d) == type([]):
  19. function(*d)
  20. elif type(d) == type({}):
  21. function(**d)
  22. else:
  23. function(d)
  24. else:
  25. if type(data) == type([]):
  26. function(*data)
  27. elif type(data) == type({}):
  28. function(**data)
  29. else:
  30. function(data)
  31.  
  32.  
  33. class EventManager:
  34. def __init__(self):
  35. self.events = {}
  36.  
  37. def add_event(self, Event):
  38. self.events[Event.name] = Event
  39.  
  40. def del_event(self, Event):
  41. self.events.pop(Event.name)
  42.  
  43. def connect(self, event, function, data=None):
  44. self.events[event].add(function, data)
  45.  
  46. def disconnect(self, event, function):
  47. self.events[event].delete(function)
  48.  
  49. def signal(self, event, data=None):
  50. if data is None:
  51. self.events[event].called()
  52. else:
  53. self.events[event].called(data)

danigm's picture

Como todo ser humano tengo una gran lista de cosas por hacer, también llamada TO-DO list. Hace algún tiempo empecé a utilizar tomboy para tomar notas y tener apuntado cosas por hacer, pero nunca me acuerdo de mirarlo, por lo tanto el otro día decidí hacer un sencillo script que cada cierto tiempo me recordara mi TO-DO list.

Para conseguirlo he utilizado pynotify para mostrar un mensajito en el escritorio cada 10 minutos, y he realizado consultas al servicio DBUS que ofrece tomboy para encontrar la nota con título TODO y coger el texto que hay en esta.

La idea es lanzar este script al iniciar el escritorio para así tenerlo en segundo plano mostrando mensajitos cada cierto tiempo.

Luego pensé que podía hacer lo mismo con los TODO de sweetter.net. Así que lo he integrado con el script utilizando el modulo pysweetter que tira del servicio XML-RPC. Por otra parte he tenido que implementar una función especifica para el servicio xmlrpc de sweetter.

Aquí una imagen del tema en acción:



frontal

Y para quien esté interesado, aquí está el código:

  1. #!/usr/bin/python
  2.  
  3. import gtk, pygtk
  4. pygtk.require('2.0')
  5.  
  6. import dbus
  7. import gobject
  8. import time
  9. import pynotify
  10.  
  11. from pysweetter import Sweetter
  12.  
  13. session_bus = dbus.SessionBus()
  14. uri = 'org.gnome.Tomboy'
  15. path = '/org/gnome/Tomboy/RemoteControl'
  16. sleep_time = 10 * 60
  17.  
  18. pynotify.init("Tomboy TODO notification")
  19. USER = 'danigm'
  20.  
  21. while True:
  22. try:
  23. tomboy = session_bus.get_object(uri, path)
  24. note_uri = tomboy.FindNote('TODO')
  25. all_text = tomboy.GetNoteContents(note_uri)
  26. all_text = all_text.split('\n')
  27. except:
  28. all_text = []
  29.  
  30. try:
  31. s = Sweetter()
  32. todo_list = s.get_TODO_list(USER)
  33. all_text.append('\nSweetter TODO\n')
  34.  
  35. for i, sweet in enumerate(todo_list):
  36. todo = '%d <a href="http://sweetter.net/%s?todo=1">%s</a>' % (i+1, USER, sweet.sweet)
  37. all_text.append(todo)
  38. except:
  39. pass
  40.  
  41. note = pynotify.Notification("TODO list",
  42. '\n'.join(all_text[1:]))
  43.  
  44. helper = gtk.Button()
  45. icon = helper.render_icon(gtk.STOCK_FILE,
  46. gtk.ICON_SIZE_DIALOG)
  47. note.set_icon_from_pixbuf(icon)
  48.  
  49. note.set_timeout(30000)
  50. note.show()
  51.  
  52. time.sleep(sleep_time)

Y para los más curiosos todavía aquí un repositorio del código:
bzr branch http://repo.danigm.net/todo-reminder

Los parches y comentarios son bienvenidos.

danigm's picture

Cuando se trabaja en una empresa es un tema recurrente el cálculo de tiempo dedicado a cada proyecto por parte del desarrollador.

Hay diferentes formas de hacerlo. La más simple sería mirando el reloj y haciendo un cálculo apróximado, el problema es que no tienes un registro de lo que has hecho.

Para el tracking de tiempo dedicado a un proyecto hay diferentes herramientas. Yo conocía gnotime que vale para lo que es, pero que no acaba de convencer.

Luego conocí Hamster, que es mucho más amigable, hace gráficas, informes y muchas polladas.

Yo en mi escritorio siempre tengo una terminal abierta, y últimamente me estoy dando cuenta de que las interfaces gráficas quitan mucho tiempo cuando no las necesitas, así que ayer decidí implementarme un time tracker de estos pero para terminal.

¿Cómo? Cojo python + vim, un poquito de sqlobject, se agita y aquí tenemos "Terminal Time Tracker".

En realidad son tres ficheros, aunque le he un instalador y desinstalador y polladas para que sea más simple de usar.

tttdb.py Una tabla para la base de datos.

  1. # Author: Daniel Garcia <dani@danigm.net>
  2. # License: GPLv3
  3.  
  4. from sqlobject import *
  5. import os
  6.  
  7. class Tracker(SQLObject):
  8. project = StringCol()
  9. task = StringCol()
  10. ticket = IntCol()
  11. start = DateTimeCol()
  12. end = DateTimeCol()
  13.  
  14. def get_hub():
  15. connection_string = 'sqlite://%s/devdata.sqlite' % os.getcwd()
  16. connection = connectionForURI(connection_string)
  17. sqlhub.processConnection = connection
  18.  
  19. return sqlhub
  20.  
  21. def create_db():
  22. # You need to create the database before.
  23. sqlhub = get_hub()
  24.  
  25. Tracker.createTable()

ttt.py Toda la chicha

  1. #!/usr/bin/python
  2.  
  3. # Author: Daniel Garcia <dani@danigm.net>
  4. # License: GPLv3
  5.  
  6. import datetime
  7. import time
  8. import sys
  9. import tttdb as db
  10. from tttdb import Tracker
  11.  
  12. sqlhub = db.get_hub()
  13.  
  14. def parse_time(time_seconds):
  15. hour = 0
  16. min = 0
  17. sec = time_seconds
  18. while sec >= 60:
  19. min += 1
  20. sec -= 60
  21. while min >= 60:
  22. hour += 1
  23. min -= 60
  24. time_str = '%02d:%02d:%02d' % (hour, min, sec)
  25. return time_str
  26.  
  27. def track(task, project='', ticket=0):
  28. today = datetime.datetime.now()
  29. tick = time.time()
  30. time_pass = 0
  31. try:
  32. while True:
  33. tick2 = time.time()
  34. time_pass = tick2 - tick
  35. cadena = '\rtracking: %(task)s %(project)s %(ticket)s %(time)s' % \
  36. {'time':parse_time(time_pass),
  37. 'task':task,
  38. 'project':project,
  39. 'ticket':ticket}
  40. sys.stdout.write(cadena)
  41. sys.stdout.flush()
  42. time.sleep(0.5)
  43. except:
  44. print '\n', 'Finalizando'
  45. end = datetime.datetime.now()
  46. db.Tracker(project=project,
  47. task=task,
  48. ticket=ticket,
  49. start=today,
  50. end=end)
  51.  
  52. def show(trackers):
  53. grouped = {}
  54. total = 0
  55. prev = ''
  56. for i in trackers:
  57. diff = i.end - i.start
  58. total += diff.seconds
  59. time_passed = parse_time(diff.seconds)
  60.  
  61. key = (i.task, i.project)
  62. if grouped.has_key(key):
  63. grouped[key] += diff.seconds
  64.  
  65. else:
  66. grouped[key] = diff.seconds
  67.  
  68. to_show = '%s | %-20s | %-40s | #%-6d | %s'
  69. to_show = to_show % (i.start.ctime(), i.project, i.task, i.ticket, time_passed)
  70. if prev != to_show[0:3]:
  71. prev = to_show[0:3]
  72. print ''
  73. print to_show
  74.  
  75. print ''
  76. for k,v in grouped.items():
  77. task, project = k
  78. time_passed = parse_time(v)
  79. print 'Tiempo para %-40s %s' % ('"'+task+'":', time_passed)
  80.  
  81. print '\nTotal: %s' % parse_time(total)
  82.  
  83. def show_today(delta=0):
  84. today = datetime.datetime.now() - datetime.timedelta(delta)
  85. yesterday = datetime.datetime(today.year, today.month, today.day)
  86. today_trackers = Tracker.select(Tracker.q.start > yesterday)
  87. show(today_trackers)
  88.  
  89. def show_project(project):
  90. project_trackers = Tracker.select(Tracker.q.project == project)
  91. sum = 0
  92. for i in project_trackers:
  93. sum += (i.end - i.start).seconds
  94.  
  95. print 'Tiempo para "%s": %s' % (project, parse_time(sum))
  96.  
  97. def show_all():
  98. all = Tracker.select()
  99. show(all)
  100.  
  101. def show_week():
  102. show_today(6)
  103.  
  104. def show_week_grouped():
  105. show_today_grouped(6)
  106.  
  107. def show_today_grouped(delta=0):
  108. today = datetime.datetime.now() - datetime.timedelta(delta)
  109. yesterday = datetime.datetime(today.year, today.month, today.day)
  110. today_trackers = Tracker.select(Tracker.q.start > yesterday)
  111. show_grouped(today_trackers)
  112.  
  113. def show_grouped(trackers):
  114. projects = {}
  115. total = 0
  116. for i in trackers:
  117. diff = (i.end - i.start).seconds
  118. total += diff
  119. if projects.has_key(i.project):
  120. projects[i.project] += diff
  121. else:
  122. projects[i.project] = diff
  123.  
  124. print ''
  125. for k,v in projects.items():
  126. p = '"' + k + '":'
  127. print 'Tiempo para %-20s %s' % (p, parse_time(v))
  128.  
  129. print '\nTotal: %s' % parse_time(total)
  130.  
  131. if __name__ == '__main__':
  132. help = '''
  133. %s tarea [proyecto] [ticket]
  134. '''
  135.  
  136. task, project, ticket = '','',0
  137. args = sys.argv[1:]
  138. if ('-h' in args) or ('--help' in args) or (len(args) == 0):
  139. print help % sys.argv[0]
  140. sys.exit()
  141. if len(args) >= 1:
  142. task = args[0]
  143. if len(args) >= 2:
  144. project = args[1]
  145. if len(args) >= 3:
  146. ticket = int(args[2])
  147.  
  148. track(task, project, ticket)

show.py Para ver el registro

  1. #!/usr/bin/python
  2.  
  3. # Author: Daniel Garcia <dani@danigm.net>
  4. # License: GPLv3
  5.  
  6. import sys
  7. import ttt
  8.  
  9. if __name__ == '__main__':
  10. help = '''
  11. %s [opcion]
  12.  
  13. opciones:
  14. -g muestra agrupados por proyecto
  15. -w muestra la ultima semana
  16. -gw muestra la ultima semana agrupado por proyecto
  17. -a muestra todo
  18.  
  19. si no se especifica opcion muestra las tareas de hoy
  20. '''
  21. args = sys.argv[1:]
  22. if ('-h' in args) or ('--help' in args):
  23. print help % sys.argv[0]
  24.  
  25. elif '-g' in args:
  26. # show grouped by project
  27. ttt.show_today_grouped()
  28. elif '-w' in args:
  29. ttt.show_week()
  30. elif '-gw' in args:
  31. # show grouped by project
  32. ttt.show_week_grouped()
  33. elif '-a' in args:
  34. ttt.show_all()
  35. else:
  36. ttt.show_today()

El código se puede pillar de mi repositorio personal:

  bzr branch http://repo.danigm.net/ttt

O del fichero tar.gz que adjunto aquí.

Para usarlo, tienes que tener instalado sqlobject y se instalaría ejecutando el script install.sh (hace una llamada a un comando con sudo). Esto te crea un directorio en $HOME/.ttt donde mete la base de datos y los scripts en python, y además copia t3 y t3show a /usr/local/bin para que puedas ejecutar los comandos sin modificar tu path.

Se usa de la siguiente manera:

  t3 "Escribiendo un post"

Y en la terminal se muestra el tiempo que vas dedicando a esta tarea. Para terminar tan solo hay que pulsar ctrl+c.
Además de una tarea se puede especificar un proyecto y un número para referenciarlo en el sistema de tickets que uses.

sintaxis:
  t3 tarea [proyecto] [ticket]
  t3 -h
ejemplo:
  t3 "plugin para jisko" "sweetter" 235879

Y para mostrar el resultado se usa t3show

Wed Sep 24 20:34:01 2008 | ttt                  | publicando                               | #0      | 00:00:06
Wed Sep 24 20:34:04 2008 | sweetter             | plugin para jisko                        | #235879 | 00:00:08
Wed Sep 24 20:34:18 2008 | ttt                  | publicando                               | #0      | 00:00:05

Tiempo para "plugin para jisko":                     00:00:08
Tiempo para "publicando":                            00:00:11

Total: 00:00:19

Con las opciones -g -w -gw -a que muestran agrupado por proyecto, la última semana, por proyecto en la última semana y todo respectivamente. Además de mostrar el día actual si no se le pasa ningún parametro y la ayuda con el comando -h.

danigm's picture

Para el que no lo sepa, soy el principal desarrollador de sweetter, que es un sistema de microbloggin al estilo de twitter pero mejor. No voy a entrar en detalles técnicos ahora que no es el tema.

El caso es que no se por qué razón (bueno sí, por su calidad, estabilidad y usabilidad) llegó a salir en el periódico "El País", en un artículo en el cual se decía poco sobre sweetter, siendo destacado por ser el servicio más geek.

Ahora a aparecido otra vez en el mismo periódico y aquí unas fotos que lo demuestran:



frontal



frontal

Pero esto no es nada comparado con lo próximo, que vendrá cuando sweetter de el gran salto y empiece a utilizarse en la empresa como herramienta interna para coordinación y otras cosas. Ya se están moviendo los hilos necesarios para motivar este camino hacia la dominación mundial.

danigm's picture

El último libro que me he leido se llama "los propios dioses" y tras leerlo me ha venido a la mente un tema sobre el que tratan en el libro y que se puede aplicar al calentamiento global y lo que hacemos hoy en día con nuestro planeta.

[spoiler del libro, si te lo vas a leer lo mismo esto te molesta]

En el libro se relata la historia de un futuro en el cual se ha conseguido una máquina que genera energía sin coste alguno aparente. Pero un científico se da cuenta de que esta máquina está cambiando las reglas físicas de nuestro universo y por tanto lo está llevando a una destrucción inmediata.

Este científico "demuestra" que la "bomba de electrones" puede hacer que el sol explote en un plazo de tiempo corto, pero la gente está tan acomodada a la energía "gratis" que sus demostraciones son criticadas y puestas en duda.

Más tarde, otro científico, desde la Luna, retoma la investigación del anterior, pero se da cuenta de una cosa, no se puede convencer a la gente de que la "bomba de electrones" es mala y esperar que dejen de usarla, hay que ofrecerle una alternativa mejor y entonces todo el mundo aceptará el cambio.

Pues bien, comparando con lo que estamos viviendo hoy en día podemos darnos cuenta de que hay mucha gente intentando luchar contra todo lo que provoca el calentamiento global y estas cosas, pero nadie ha propuesto una alternativa mejor que permita reemplazar las energías actuales.

Esto me ha llevado a pensar que nos iremos autodestruyendo viviendo en la comodidad hasta que algún génio descubra algo que cambie todo nuestro sistema energético y entonces seguiremos viviendo en la comodidad, pero respetando algo más el entorno. Mientras tanto creo que no se puede esperar que nadie esté dispuesto a abandonar la comodidad a la que hemos llegado hoy en día, y se seguirá negando la posibilidad de que acabemos con el planeta, porque siempre es más fácil creer lo que se quiere creer y tachar de mentirosos o refutar de alguna forma a los que traten de arrebatarnos la comodidad.

Si es así como están las cosas es posible que nadie consiga algo realmente revolucionario que nos haga cambiar sin pensarlo, y acabemos autodestruyendonos por no haber sabido renunciar a una comodidad no merecida.

Asimov fue todo un visionario de comportamiento de masas

danigm's picture

¿Qué es un "sistema de control de versiones" (vcs)? Pues es una herramienta que utilizan normalmente los programadores para versionar los cambios realizados sobre el código fuente y trabajar en equipo.

Básicamente funciona así:
Tú tienes una serie de ficheros en tu ordenador, modificas, añades, o haces algo, y haces "commit". Sigues trabajando tan normal y cada vez que te parezca que haces un cambio importante haces "commit". Con esto tienes varias ventajas, lo más importante es que puedes volver cualquier fichero a una revisión anterior. También puedes ver un log de los cambios realizados y otras operaciones.

¿Entonces qué tenemos? pues podemos modificar los ficheros sin miedo porque después de romper algo siempre se puede volver a una versión anterior.

Aparte de eso, los vcs, ofrecen una forma fácil de colaboración entre varias personas. Estos repositorios se pueden publicar, otras personas pueden descargarselos y actualizar su repositorio, e incluso subir cambios que se mezclarán con los tuyos de manera automática.

No lo he explicado muy bien, pero estos sistemas ofrecen la posibilidad de que varias personas trabajen modificando un mismo fichero y que con un par de comandos tengan la versión unificada sin necesidad de enviarse ficheros ni parches ni nada.

BZR

Pues bien, bazaar es un vcs distribuido, fácil de usar y multiplataforma (está en python).

Que sea distribuido quiere decir que cada usuario tendrá su repositorio local en el cual hace los commits, a diferencia de subversion que es centralizado, todos los commits se hacen sobre un repositorio central.

Pero aún siendo distribuido bzr ofrece la posibilidad de utilizarse de manera centralizada, prácticamente igual que svn.

Vamos a lo práctico:

  • Crear un repositorio: crear el repositorio es tan fácil como ejecutar "bzr init" dentro del directorio


  • Añadir/mover/etc ficheros: para añadir/mover/etc es tan fácil como "bzr add fichero" o "bzr mv fichero1 fichero2" y más comandos similares a los comandos unix.


  • Hacer commits: para guardar los cambios en una revisión se utiliza el comando "bzr commit" o "bzr ci" y te abrirá tu editor favorito para que escribas lo que ha cambiado para el log.


  • Ver el log: para ver el log "bzr log"


  • Volver a una revisión anterior: "bzr revert -r2 fichero fichero2" esto devuelve la lista de ficheros a la revisión 2, si quieres volver completamente deberías usarlo sin los nombres de ficheros.


  • Publicar un repositorio (ssh): "bzr push ssh://usuario@dominio/ruta". Esto modificará el repositorio en el dominio/ruta, o lo creará si no existe. Antes de hacer un push hay que hacer un commit de los cambios realizados.


  • Descargar un repositorio: "bzr branch ruta destino". Donde ruta puede ser la ruta anterior, si vamos por ssh, y destino es la carpeta que creará para alojar el repositorio.


  • Actualizar un repositorio: Una vez que tengamos el repositorio descargado con branch podemos actualizarlo a la última versión con "bzr pull" desde dentro del directorio del repositorio.


  • Utilizar cómo subversion: Para poder utilizarlo de modo centralizado hay que utilizar el comando "bzr bind ruta" desde dentro del directorio. Una vez hecho esto se puede actualizar con "bzr update", y desde este momento cada commit no será local, sino que también se hará sobre el servidor.


  • Y un montón de cosas más, para más información man bzr


Otra cosa chachi que tiene bzr es que es el sistema utilizado por defecto en launchpad, dónde puedes crear todas las ramas que quieres para tener tu código en un servidor. Algún proyecto tengo yo por ahí.

Y para finalizar decir que no sólo se utiliza para código, yo por ejemplo lo he utilizado para versionar la documentación de mi proyecto fin de carrera.

danigm's picture

Viajando en estos aviones de hoy en día no se puede ni dormir, con lo apretado que se va, así que me dediqué en mi viaje a dibujar, y este es el resultado.



frontal

danigm's picture

Hay que ver lo bonito y práctico que puede quedar un escritorio con esto de gnome+emerald+compiz+awn.



frontal

Los temas que tengo:

- gtk2 -> Aurora green peace
- emerald -> lucidity
- screenlets -> ringsensors
- avant-window-navigator es la barra tipo dock parecido a la de macosx