ka.da 2014-11-20T21:38:50+01:00 urn:md5:0096f5d8d60a187d87d591e1eb7553ae Dotclear Django : passage aux vues génériques basées sur des classes, par l'exemple urn:md5:7c17549a3c279a3b44fd9fc9f39f0610 2011-04-01T12:02:00+02:00 2011-07-15T13:54:24+02:00 eric InformatiqueEtGeekeries Djangoprogrammation <h2>Introduction</h2> <p>Si vous utilisez Django vous connaissez probablement déjà les <a href="http://docs.djangoproject.com/en/dev/ref/generic-views/" hreflang="en">vues génériques/generic views</a>&nbsp;: elles permettent de faciliter la création de certaines tâches monotones et répétitives qu'on retrouve souvent sur les applications web comme sont les listes, affichage, modification et création d'objets...</p> <p>Jusqu'à Django 1.2 les vues génériques étaient des <a href="http://docs.djangoproject.com/en/1.2/ref/generic-views/">fonctions</a>, et depuis Django 1.3 (qui est sorti il y a peu) elles sont maintenant basées sur des <a href="http://docs.djangoproject.com/en/dev/topics/class-based-views/">classes</a>. Je vais tenter ici, modestement, de montrer l'intérêt de ce changement, donner des exemples simples pour illustrer la migration d'un type vers l'autre, et donner quelques repères pour comprendre leur fonctionnement.</p> <p>Les vues génériques basées sur des fonctions (qu'on notera FBV) fonctionnent très bien pour des cas simples, puis on doit rapidement faire de l'encapsulation ('wrapper') pour certains cas spéciaux, mais on arrive encore à notre but, mais à un moment ou à un autre, malheureusement on se retrouve coincé sans pouvoir faire ce qu'on veut et on doit alors recréer nos propres vues et se passer de ce qui existe déjà dans Django.</p> <p>Cette limite est connue depuis longtemps et le travail a été long (le <a href="http://code.djangoproject.com/ticket/6735">ticket #6735</a> a trois ans) mais l'arrivée des CBV permet justement de s'affranchir de ses limites.</p> <h2>Exemple de passage des FBV aux CBV</h2> <p>Prenons un cas simple où l'on veut lister des objets, et où la liste de ceux-ci dépendra d'un paramètre dynamique (ici l'utilisateur connecté)&nbsp;:</p> <p><em>note&nbsp;: j'ai volontairement allégé le code en enlevant les parties classiques ou évidentes, néanmoins si vous voyez une énormité merci de le signaler</em></p> <pre> ## urls.py from myproject.views import listview urlpatterns = patterns('', (r'^list/', listview, {}, 'myobject_list'), ) ## models.py from django.contrib.auth.models import User class MyObject(models.Model): ... author = models.ForeignKey(User) ## views.py @login_required def listview(request, queryset=MyObject.objects.all(), template_name='myproject/myobject_list.html'): qs = queryset.filter(author=request.user) return object_list(request, queryset=qs, template_name=template_name) </pre> <p>On a ici l'exemple typique de l'utilisation d'une vue générique avec un wrapper. Jusque là ça ne pose pas de problème particulier et tout peut être fait avec Django 1.2 et ses FBV.</p> <p>Par contre, vu que dans notre modèle author est un utilisateur, on imagine très bien qu'il faut qu'à l'enregistrement ce champ soit initialisé avec l'utilisateur connecté, donc la valeur de request.user, et là les vues génériques montrent leur limitation&nbsp;: il est en effet possible de surcharger la fonction save() du formulaire mais il n'est pas possible d'indiquer à la vue générique create_object de lui passer le paramètre qui nous sera utile au moment de son enregistrement.</p> <p>On est donc obligé de créer notre propre vue pour gérer ce qui est finalement une différence mineure mais un cas que l'on retrouve fréquemment.</p> <p>On aura alors quelque chose comme</p> <pre> ## urls.py urlpatterns = patterns('', (r'^list/', listview, {}, 'myobject_list'), (r'^create/', createview, {}, 'myobject_create'), ) ## forms.py class MyObjectForm(ModelForm): class Meta: model = MyObject exclude = ('author',) def save(self, user=None): myobject = super(MyObjectForm, self).save(commit=False) myobject.author = user myobject.save() ## views.py from myproject.forms import MyObjectForm @login_required def createview(request, form_class=MyObjectForm): if request.method == 'POST': form = form_class(request.POST) if form.is_valid(): myobject = form.save(user=request.user) return HttpResponseRedirect(myobject.get_absolute_url()) else: form = form_class() return render_to_response('myproject/myobject_form.html', {'form': form}, context_instance=RequestContext(request)) </pre> <p>Voyons maintenant comment les deux exemples précédents sont implémentables en utilisant les vues basées sur les classes.</p> <pre> ## views.py class ListView(generic.ListView): queryset = MyObject.objects.all() template_name = &quot;myproject/myobjects_list.html&quot; def get_queryset(self): return self.queryset.filter(author=self.request.user) listview = login_required(ListView.as_view()) </pre> <p>pour la liste d'objets, ok le code est un peu plus court, mais ça ne change pas grand-chose finalement. Voyons maintenant la vue implémentant la création d'objet avec l'enregistrement de l'utilisateur connecté dans le champ author</p> <pre> ## views.py from myproject.forms import MyObjectForm class CreateView(generic.CreateView): form_class = MyObjectForm template_name = &quot;myproject/myobject_form.html&quot; def form_valid(self, form): self.object = form.save(user=self.request.user) return super(CreateView, self).form_valid(form) createview = login_required(CreateView.as_view()) </pre> <p>Ici on arrive à réutiliser toute le code fourni par Django et ne redéfinir que quelques valeurs (form_class, template_name) et juste la fonction form_valid() c'est-à-dire celle qui définit ce qui se passe quand le formulaire est valide et qu'on veut le sauvegarder.</p> <h2>Utilisation</h2> <p>Comme je l'ai dit en introduction, les class-based views ont été implémentées dans Django 1.3 mais vous avez de la chance puisque que le code a été backporté dans Django 1.2 et vous pouvez donc d'ores et déjà les essayer/utiliser même si vous n'avez pas encore migré en 1.3.</p> <p>Donc si vous êtes en 1.3 vous devez ajouter l'import suivant dans vos <code>views.py</code></p> <pre> from django.views import generic </pre> <p>Si vous êtes encore en 1.2, il vous faut installer l'application <a href="https://github.com/sorl/django-cbv">django-cbv</a></p> <pre> $ pip install django-cbv </pre> <p>Ajouter le middleware qui va bien dans <code>settings.py</code></p> <pre> MIDDLEWARE_CLASSES = ( ... 'cbv.middleware.DeferredRenderingMiddleware', ) </pre> <p>et, dans vos <code>views.py</code> utiliser l'import suivant&nbsp;:</p> <pre> import cbv as generic </pre> <p>Cette technique, directement tirée de <a href="http://bruno.im/2011/feb/19/django-13-patterns-older-django-versions/">l'article de Bruno Renié</a> (que je remercie puisque c'est cet article et la découverte de cette possibilité qui m'a fait enfin m'intéresser sérieusement aux CBV, tout en restant en Django 1.2), vous permet ensuite de aisément migrer en 1.3 en n'ayant besoin de changer que l'import.</p> <h2>Aller plus loin</h2> <p>Bon, ça c'était un exemple simple mais bien évidemment les CBV sont à la fois plus complexes et plus puissantes. Je vous encourage fortement à aller lire la doc (<a href="http://docs.djangoproject.com/en/dev/topics/class-based-views/">présentation</a> et <a href="http://docs.djangoproject.com/en/dev/ref/class-based-views/">référence</a>) ainsi que le code, mais en gros voici comment ça se passe (de ce que j'en ai compris pour l'instant, n'hésitez pas à me corriger si je dis une connerie)&nbsp;:</p> <ul> <li>chaque FBV qui existait auparavant existe maintenant sous la forme d'une CBV et vous pouvez retrouver simplement le même comportement</li> <li>chaque <a href="http://docs.djangoproject.com/en/dev/ref/class-based-views/#generic-views">vue générique</a> est formée de un ou plusieurs "<a href="http://docs.djangoproject.com/en/dev/ref/class-based-views/#mixins">mixin</a>"</li> <li>chaque mixin implémente une fonctionnalité comme générer un template, afficher une liste d'objets, créer et afficher des formulaires</li> <li>les vues génériques utilisent les capacités d'héritage des classes pour, en mélangeant (mixant ;-) un ou plusieurs mixins, produire le comportement désiré</li> <li>pour redéfinir le comportement d'une vue, il suffit de redéfinir un ou plusieurs paramètres, voire une ou plusieurs fonctions provenant des mixins, comme je l'ai fait dans l'exemple ci-dessus</li> <li>si vous avez besoin d'utiliser plusieurs fois le même comportement, il vous suffit de définir vos propres mixins et de créer vos propres vues en héritant de ceux-ci et de ceux prédéfinis.</li> </ul> <h2>Conclusion</h2> <p>Les exemples donnés ici sont simples, et l'article n'a pas pour vocation à vous transformer en 'powerusers' des CBV mais j'espère qu'il vous permettra de vous donner une première vision de leur fonctionnement et vous encouragera à lire la documentation et le code associé pour découvrir leur puissance.</p> <p><ins>bonus</ins>&nbsp;: si quelqu'un sait (ou trouve) comment utiliser les CBV pour gérer une vue avec plusieurs formulaires en même temps je suis preneur de l'information...</p> gestion et surveillance de processus système avec supervisor, pour gérer des projets Django, Ruby on Rails, WSGI... urn:md5:d02127421133d24e0fb47b5c96ac2a9a 2010-11-21T18:12:00+01:00 2010-11-21T18:12:35+01:00 eric InformatiqueEtGeekeries DebianDjangoligne de commande <p><br /> Dans cet article, je vais vous présenter <a href="http://supervisord.org/">supervisor</a> un système de supervision et gestion de processus. Je l'utilise pour gérer les différents processus utilisés pour servir des applications Django, Ruby on Rails ou WSGI. Je vais vous présenter l'outil en lui-même, ses avantages, comment l'installer et le configurer, comment définir les processus qu'on veut gérer avec supervisor en donnant quelques exemples, la commande de gestion supervisorctl et son utilisation possible pour des déploiements automatisés (avec Fabric par exemple).</p> <h2>Introduction</h2> <p>Dans deux précédents articles, j'ai parlé de ma migration de Apache vers Nginx. Ainsi que de l'utilisation de gunicorn et unicorn pour "servir" mes projets Django et Ruby on Rails respectivement. J'avais créé deux scripts init.d pour gérer le lancement automatique de ceux-ci.</p> <p>Mais cette solution n'était pas entièrement satisfaisante et on m'a alors parlé de <a href="http://supervisord.org/">supervisor</a>, <a href="http://smarden.org/runit/">runit</a>, <a href="http://god.rubyforge.org/">god</a>... qui sont des logiciels permettant de gérer le lancement de processus personnalisés avec un énorme plus&nbsp;: la supervision des processus lancés avec ce type de programmes&nbsp;: si un processus crashe il est relancé automatiquement&nbsp;!</p> <p>J'ai donc choisi de tester supervisor et, vu qu'il m'a convaincu, je l'ai gardé. Et en plus il est écrit en Python :)</p> <h2>Installation et configuration</h2> <p>Pour l'installation, vous avez le choix entre les outils fournis par votre distribution (<code># aptitude install supervisor</code> pour moi) ou bien avec <code>pip install supervisor</code>. Je pars du principe que vous allez utiliser la version fournie par votre distribution (et que vous utilisez Debian/Ubuntu bien sûr !)&nbsp;: en soi l'installation avec pip n'aurait un intérêt que pour avoir une version plus récente du logiciel or supervisor est stable, et ne dois donc plus évoluer beaucoup. L'avantage d'utiliser la version Debian est que tout est déjà prêt&nbsp;: les fichiers init pour le lancement automatique du démon supervisord au démarrage du serveur, l'arborescence dans /etc/supervisord et même une configuration minimale suffisante pour démarrer.<br /> Le seul hic c'est que Debian Lenny (l'actuelle stable) n'a pas supervisor dans ses dépôts, mais une version existe dans testing/Squeeze et est parfaitement installable sur Lenny.</p> <p>La configuration de supervisor en lui-même se fait dans le fichier <code>supervisord.conf</code> dont la configuration par défaut suffit pour un fonctionnement standard, mais n'hésitez pas à regarder si vous voulez changer quelques options (comme lancer un mini serveur web de contrôle de supervisor).</p> <h2>Définition des processus et exemples</h2> <p>La configuration de chaque processus supervisé se fait par contre via un fichier distinct pour chaque processus et ceux-ci sont stockés sous Debian dans <code>/etc/supervisor/conf.d</code> avec un nom terminant par <code>.conf</code>.</p> <p>Je ne vais pas entrer dans le détail du fonctionnement de supervisor et donc de la syntaxe et des possibilités des fichiers de configuration mais plutôt vous donnez quelques exemples en expliquant les points intéressants.</p> <p><em>application WSGI sous Gunicorn (ici l'interface web de Mercurial)</em></p> <pre> [program:code_domain_tld] command=/usr/bin/gunicorn code_domain_tld:application -c /var/www/code_domain_tld/gunicorn.conf.py directory=/var/www/code_domain_tld user=www-data autostart=true autorestart=true startsecs=10 redirect_stderr=true stdout_logfile=/var/log/supervisor/code_domain_tld.gunicorn.log </pre> <p><strong>program</strong> est le nom du service pour supervisor, c'est celui qui est utilisé ensuite pour le contrôle (cf plus bas). J'ai tendance à mettre le nom de domaine qui est servi par ce service<sup>[<a href="http://x.sietch-tabr.com/index.php/post/2010/06/03/gestion-et-surveillance-de-processus-syst%C3%A8me-avec-supervisor-pour-gerer-des-projets-django-ruby-on-rails-wsgi#pnote-277-1" id="rev-pnote-277-1">1</a>]</sup><br /> <strong>command</strong> est le nom de la commande qui va être lancée et contrôlée par supervisor, ici c'est simplement gunicorn en indiquant le chemin du fichier de configuration. Pour le code_domain_tld.application c'est l'application WSGI qui va être lancée par gunicorn. Pour ce cas précis, c'est un fichier <code>code_domain_tld.py</code> contenant</p> <pre> #!/usr/bin/env python import os import sys from mercurial.hgweb.hgwebdir_mod import hgwebdir from mercurial.hgweb.request import wsgiapplication os.environ[&quot;HGENCODING&quot;] = &quot;UTF-8&quot; def make_web_app(): return hgwebdir(&quot;/etc/mercurial/hgweb.code.domain.tld.config&quot;) def application(environ, start_response): environ['wsgi.url_scheme'] = environ.get('HTTP_X_URL_SCHEME', 'http') app = wsgiapplication(make_web_app) return app(environ, start_response) </pre> <p><strong>directory</strong> est le répertoire de travail de la commande précédente<br /> <strong>user</strong> permet de spécifier l'utilisateur qui sera utilisé pour lancer le programme&nbsp;: veuillez à ce qu'il ait les droits sur les fichiers nécessaires et surtout évitez root !<br /> <strong>autostart</strong> et <strong>autorestart</strong> me semblent assez clairs&nbsp;: indiquer si le programme (la commande) doit être lancé automatiquement et relancé automatiquement en cas de crash<br /> <strong>startsecs</strong> est le nombre de secondes à partir desquelles supervisor va vérifier l'état du programme pour voir si il s'est lancé correctement. Si un programme met beaucoup de temps à se lancer on peut jouer sur cette valeur pour que supervisor attende avant de le considérer comme crashé<br /> les options suivantes concernent la journalisation (logs) de ce programme par supervisor</p> <p><em>application Django sous Gunicorn</em></p> <pre> [program:domain_tld] command=/opt/django/domain_tld/_venv/bin/python /opt/django/domain_tld/_venv/bin/gunicorn_django -c /opt/django/domain_tld/gunicorn.conf.py directory=/opt/django/domain_tld/projet user=www-data autostart=true autorestart=true startsecs=10 redirect_stderr=true stdout_logfile=/var/log/supervisor/domain_tld.gunicorn.log </pre> <p><strong>command</strong> la différence se situe ici avec l'utilisation d'un interpréteur Python dans un virtualenv. Le reste est similaire à l'exemple au-dessus.</p> <p><em>application Ruby on Rails sous Thin</em></p> <pre> [program:dev_domain_tld command=/var/lib/gems/1.8/bin/thin start -C /opt/redmine/config/thin.yml directory=/opt/redmine user=www-data autostart=true autorestart=true startsecs=10 redirect_stderr=true stdout_logfile=/var/log/supervisor/dev_domain_tld.thin.log </pre> <p>Ici c'est pareil, seule la commande change pour lancer un programme différent <a href="http://code.macournoyer.com/thin/">Thin</a><sup>[<a href="http://x.sietch-tabr.com/index.php/post/2010/06/03/gestion-et-surveillance-de-processus-syst%C3%A8me-avec-supervisor-pour-gerer-des-projets-django-ruby-on-rails-wsgi#pnote-277-2" id="rev-pnote-277-2">2</a>]</sup> pour faire tourner une application Ruby on Rails.</p> <p>Il est <ins><strong>très important</strong></ins> de noter que pour que supervisor puisse contrôler vos processus (leur état, les arrêter, les redémarrer) il faut que ceux-ci ne partent pas en tâche de fond ('daemonize'). Pour gunicorn par exemple, il faut spécifier <code>daemon=False</code> dans le fichier de configuration.</p> <h2>L'application de gestion supervisorctl</h2> <p>Supervisor apporte aussi un outil de grande utilité&nbsp;: sa commande supervisorctl qui permet de vérifier l'état des différents processus ainsi que de les contrôler. Voyons quelques exemples de son utilisation en mode interactif.</p> <pre> $ supervisorctl code_domain_tld RUNNING pid 2143, uptime 18 days, 18:13:41 domain_tld RUNNING pid 2140, uptime 18 days, 18:13:41 dev_domain_tld RUNNING pid 2139, uptime 18 days, 18:13:41 supervisor&gt; </pre> <p>On voit que quand on lance la commande, on a tout de suite l'information sur l'état des processus gérés par supervisor.</p> <pre> supervisor&gt; status code_domain_tld RUNNING pid 2143, uptime 18 days, 18:13:41 domain_tld RUNNING pid 2140, uptime 18 days, 18:13:41 dev_domain_tld RUNNING pid 2139, uptime 18 days, 18:13:41 </pre> <p>La commande <strong>status</strong> renvoie cette même information, mais on peut utiliser status &lt;nom d'un processus&gt; pour avoir uniquement l'état d'un processus</p> <pre> supervisor&gt; stop code_domain_tld code_domain_tld: stopped supervisor&gt; start code_domain_tld code_domain_tld: started supervisor&gt; restart code_domain_tld code_domain_tld: stopped code_domain_tld: started </pre> <p>Assez logiquement les commandes <strong>start</strong>, <strong>stop</strong>, <strong>restart</strong> suivies du nom d'un processus agissent sur le processus en question en l'arrêtant, le démarrant ou le redémarrant.</p> <p>D'autres commandes peuvent être utiles, tapez <strong>help</strong> dans supervisorctl pour en savoir d'avantage.</p> <p>L'énorme avantage de supervisorctl c'est qu'en plus d'être utilisable en mode interactif comme on l'a vu au dessus il est également utilisable en mode non-interactif. Vous prenez les commandes évoquées au-dessus et les indiquez directement en tant qu'arguments à la commande supervisorctl&nbsp;: la commande est lancée par supervisorctl qui vous rend la main aussitôt. Exemple&nbsp;:</p> <pre> $ supervisorctl status code_domain_tld RUNNING pid 2143, uptime 18 days, 18:13:41 domain_tld RUNNING pid 2140, uptime 18 days, 18:13:41 dev_domain_tld RUNNING pid 2139, uptime 18 days, 18:13:41 $ </pre> <p>J'utilise cette possibilité notamment dans mes scripts Fabric pour déployer rapidement des projets Django et les redémarrer. Un bout de code comme suit mettra à jour la configuration supervisor du projet si nécessaire, et relancera le processus en utilisant la nouvelle configuration&nbsp;:</p> <pre> def wsgiserver(): filename = '%s.conf' % env.app_name dir = '/etc/supervisor/conf.d' if exists('%s/%s' % (dir, filename)): choice = prompt('supervisord conf file already exists, overwrite (Y/N)?', default='N', validate=r'[YyNn]') if choice.upper() == 'N': return put(filename, '%s/%s' % (dir, filename)) run('sudo supervisorctl reread') run('sudo supervisorctl update') </pre> <h2>Liens</h2> <ul> <li>le site web de <a href="http://supervisord.org/">supervisor</a> avec la documentation</li> <li>un article de Mathieu Agopian sur le même outil <a href="http://mathieu.agopian.info/blog/2010/08/lancer-gunicorn-avec-supervisord/">Lancer Gunicorn avec supervisord</a></li> <li>mes deux précédents articles évoqués au début&nbsp;: <a href="http://blog.sietch-tabr.com/index.php/post/2010/04/03/Faire-tourner-des-applications-ruby-on-rails-avec-nginx-et-unicorn">Faire tourner des applications Ruby on Rails avec Nginx et Unicorn</a> et <a href="http://blog.sietch-tabr.com/index.php/post/2010/04/04/Heberger-des-projets-Django-avec-Nginx-et-Gunicorn">Héberger des projets Django avec Nginx et Gunicorn</a></li> </ul> <div class="footnotes"><h4>Notes</h4> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2010/06/03/gestion-et-surveillance-de-processus-syst%C3%A8me-avec-supervisor-pour-gerer-des-projets-django-ruby-on-rails-wsgi#rev-pnote-277-1" id="pnote-277-1">1</a>] en substituant les . par des _ comme vous l'avez sûrement remarqué : je ne me souviens plus pourquoi mais je pense que à l'origine ça m'était nécessaire pour Python ou Gunicorn et j'ai donc gardé cette habitude</p> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2010/06/03/gestion-et-surveillance-de-processus-syst%C3%A8me-avec-supervisor-pour-gerer-des-projets-django-ruby-on-rails-wsgi#rev-pnote-277-2" id="pnote-277-2">2</a>] oui avant j'utilisais Unicorn mais j'avais des problèmes qui se sont résolus en changeant de serveur</p></div> Rapide guide pratique pour l'utilisation des branches Mercurial urn:md5:353b124c6851a62a520fd7ce5f5738eb 2010-06-06T01:03:00+02:00 2010-07-16T21:42:35+02:00 eric InformatiqueEtGeekeries Mercurialprogrammation <p><br /> L'article de Steve Losh <a href="http://stevelosh.com/blog/2009/08/a-guide-to-branching-in-mercurial/">A Guide to Branching in Mercurial</a> permet de se faire une bonne idée du fonctionnement et des possibilités offertes par Mercurial pour gérer des "branches" (afin d'avoir des lignes de développement distinctes).</p> <p>L'article <a href="http://stevelosh.com/blog/2010/05/mercurial-workflows-stable-default/" hreflang="en">Mercurial Workflows: Stable &amp; Default</a> quant à lui présente plus précisément la solution "branches nommées" <em>(named branches)</em>.</p> <p>Mais il manque d'exemples pratiques à mon goût, et comme je devais l'expliquer en détail à des amis, voici ma tentative de guide pratique appliqué à la théorie expliquée dans l'article sus-cité.</p> <p>On commence par initialiser un projet, à créer quelques fichiers et à les committer séparément</p> <pre> $ hg init projet $ cat &gt; a hello a ^D $ hg add a $ hg commit -m 'ajout a' $ hg glog --style compact @ 0[tip] a82264640870 2010-06-05 23:39 +0200 eric a $ cat &gt; b hello b ^D $ hg add b $ hg commit -m 'ajout b' $ hg glog --style compact @ 1[tip] 39bf03acf954 2010-06-05 23:41 +0200 eric | ajout b | o 0 a82264640870 2010-06-05 23:39 +0200 eric ajout a </pre> <p><br /> On a pour l'instant une seule branche</p> <pre> $ hg branches default 1:39bf03acf954 </pre> <p><br /> On en crée une sous le nom <strong>stable</strong>. Elle sera utilisée pour gérer les corrections de bugs de la version stable, tandis que le développement courant aura lieu dans la branche <strong>default</strong></p> <pre> $ hg branch stable marked working directory as branch stable $ hg commit -m 'stable branch' $ hg branches stable 2:97c9284f569c default 1:39bf03acf954 (inactive) </pre> <p><br /> Pour l'instant, l'historique des révisions reste linéaire</p> <pre> $ hg glog --style compact @ 2[tip] 97c9284f569c 2010-06-05 23:42 +0200 eric | stable branch | o 1 39bf03acf954 2010-06-05 23:41 +0200 eric | ajout b | o 0 a82264640870 2010-06-05 23:39 +0200 eric ajout a </pre> <p><br /> On met à jour le dépôt pour utiliser la branche <strong>default</strong> et on modifie à nouveau des fichiers</p> <pre> $ hg update default 0 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg branches stable 2:97c9284f569c default 1:39bf03acf954 (inactive) $ cat &gt;&gt; a bye a ^D $ cat a hello a bye a </pre> <p><br /> Quand on committe notre code, une nouvelle "tête" (<em>head</em>) est créée et l'historique n'est alors plus linéaire, ce qui montre que la branche <strong>default</strong> diverge de la branche <strong>stable</strong>, celle-ci étant un sous-ensemble de la première</p> <pre> $ hg commit -m 'bye a' created new head $ hg heads --style compact 3[tip]:1 bc970099e4f8 2010-06-05 23:53 +0200 eric bye a 2 97c9284f569c 2010-06-05 23:42 +0200 eric stable branch $ hg glog --style compact @ 3[tip]:1 bc970099e4f8 2010-06-05 23:53 +0200 eric | bye a | | o 2 97c9284f569c 2010-06-05 23:42 +0200 eric |/ stable branch | o 1 39bf03acf954 2010-06-05 23:41 +0200 eric | ajout b | o 0 a82264640870 2010-06-05 23:39 +0200 eric ajout a </pre> <p><br /> Faisons de nouvelles modifications dans la branche de développement principal</p> <pre> $ cat &gt; c hello c ^D $ hg add c $ hg commit -m 'ajout c' $ hg glog --style compact @ 4[tip] 290b943b61d5 2010-06-05 23:54 +0200 eric | ajout c | o 3:1 bc970099e4f8 2010-06-05 23:53 +0200 eric | bye a | | o 2 97c9284f569c 2010-06-05 23:42 +0200 eric |/ stable branch | o 1 39bf03acf954 2010-06-05 23:41 +0200 eric | ajout b | o 0 a82264640870 2010-06-05 23:39 +0200 eric ajout a </pre> <p><br /> Et retournons dans la branche <strong>stable</strong> pour corriger quelques bugs</p> <pre> $ hg update stable 1 files updated, 0 files merged, 1 files removed, 0 files unresolved $ cat &gt;&gt; a fix a $ hg commit -m 'fix a' hg glog --style compact @ 5[tip]:2 26c5f5e23509 2010-06-05 23:55 +0200 eric | fix a | | o 4 290b943b61d5 2010-06-05 23:54 +0200 eric | | ajout c | | | o 3:1 bc970099e4f8 2010-06-05 23:53 +0200 eric | | bye a | | o | 2 97c9284f569c 2010-06-05 23:42 +0200 eric |/ stable branch | o 1 39bf03acf954 2010-06-05 23:41 +0200 eric | ajout b | o 0 a82264640870 2010-06-05 23:39 +0200 eric ajout a </pre> <p><br /> Maintenant il faut bien évidemment que ces corrections de bugs se retrouvent également dans la branche de dev <strong>default</strong></p> <pre> $ hg update default 2 files updated, 0 files merged, 0 files removed, 0 files unresolved $ hg merge 26c5f5e23509 merging a 0 files updated, 1 files merged, 0 files removed, 0 files unresolved (branch merge, don't forget to commit) $ hg commit -m 'merge with 26c5f5e23509 (stable branch)' $ hg glog --style compact @ 6[tip]:4,5 fb323a167af1 2010-06-05 23:57 +0200 eric |\ merge with 26c5f5e23509 (stable branch) | | | o 5:2 26c5f5e23509 2010-06-05 23:55 +0200 eric | | fix a | | o | 4 290b943b61d5 2010-06-05 23:54 +0200 eric | | ajout c | | o | 3:1 bc970099e4f8 2010-06-05 23:53 +0200 eric | | bye a | | | o 2 97c9284f569c 2010-06-05 23:42 +0200 eric |/ stable branch | o 1 39bf03acf954 2010-06-05 23:41 +0200 eric | ajout b | o 0 a82264640870 2010-06-05 23:39 +0200 eric ajout a </pre> <p><br /> On voit bien que le fichier a contient le code venant des deux branches <em>(même si ça se joue au moment du merge)</em></p> <pre> $ cat a hello a fix a bye a </pre> <p><br /> Quand vous voulez remplacer la branche <strong>stable</strong> il suffit de merger avec <strong>default</strong></p> <pre> $ hg update stable $ hg merge 290b943b61d5 $ hg commit -m 'merge with 290b943b61d5' </pre> <p><br /> Pour fermer définitivement la branche, utiliser l'option <strong>--close-branch</strong> à hg commit.</p> <p>et voili...</p> <p><strong>note</strong>&nbsp;: l'article a été écrit rapidement donc n'hésitez pas à signaler toute erreur</p> conférence Boite à outils Django à la DjangoCong 2010 urn:md5:071a32e5c3c12ecb0b3cc43df2c902a2 2010-04-26T22:38:00+02:00 2010-04-26T22:38:00+02:00 eric InformatiqueEtGeekeries conférenceDjango <p>Ce week-end, c'était donc la <a href="http://rencontres.django-fr.org/">DjangoCong</a> conférence Django à Marseille. J'y ai présenté une conférence intitulé "Boîte à outils Django" présentant notamment la <a href="http://github.com/robhudson/django-debug-toolbar">django-debug-toolbar</a>, <a href="http://code.google.com/p/django-command-extensions/">django-command-extensions</a> ainsi que <a href="http://pypi.python.org/pypi/virtualenv">Virtualenv</a>, <a href="http://pip.openplans.org/">Pip</a> et <a href="http://fabfile.org">Fabric</a>.</p> <p>Ma présentation est disponible ici&nbsp;: <a href="http://x.sietch-tabr.com/public/presentation_boite_a_outils_django_DjangoCong2010.pdf">Boite à outils Django - DjangoCong 2010</a>.</p> <p>Merci encore aux organisateurs&nbsp;: <a href="http://larlet.fr">David</a> et <a href="http://j-mad.com/">Jean-Michel</a> et à tous les participants.</p> Héberger des projets Django avec Nginx et Gunicorn urn:md5:c27ff5edb5099528ba1c36be62194dc5 2010-04-06T16:11:00+02:00 2010-11-21T18:16:11+01:00 eric InformatiqueEtGeekeries DebianDjangohébergementligne de commandeNginx <p><strong>Mise à jour&nbsp;: j'ai finalement décidé d'utiliser <a href="http://supervisord.org/">supervisor</a> pour gérer/monitorer ce genre de services,voyez <a href="http://blog.sietch-tabr.com/index.php/post/2010/06/03/gestion-et-surveillance-de-processus-syst%C3%A8me-avec-supervisor-pour-gerer-des-projets-django-ruby-on-rails-wsgi">ce billet</a>.</strong></p> <p>Je continue l'optimisation de mon serveur web <em>(cf <a href="http://blog.sietch-tabr.com/index.php/post/2010/04/03/Faire-tourner-des-applications-ruby-on-rails-avec-nginx-et-unicorn">le premier épisode</a>)</em> avec cette fois-ci mes projets Django que j'ai migré vers <a href="http://nginx.net">Nginx</a> et <a href="http://gunicorn.org">Gunicorn</a>. <em>(veuillez noter que Gunicorn permet également de faire tourner d'autres types d'applications Python que Django)</em></p> <h2>Virtualenv</h2> <p>Pour gérer mes projets Django, j'utilise <a href="http://pypi.python.org/pypi/virtualenv">virtualenv</a> et toute cette doc part donc du principe que vous l'utilisez également. Si ce n'est pas le cas 1. je vous encourage à le faire ;) 2. vous devrez adapter cette doc en conséquence.</p> <h2>Gunicorn</h2> <p>On commence par installer Gunicorn dans chacun des environnements virtuels hébergeant vos projets Django <em>(cf plus bas pour les détails)</em></p> <pre> $ pip install gunicorn </pre> <p>ou</p> <pre> $ easy_install gunicorn </pre> <p>Ensuite pour chaque projet Django qui sera propulsé par Gunicorn, le plus simple est de créer un fichier de configuration <code>gunicorn.conf.py</code> pour indiquer les options du serveur à son lancement. Voici un exemple de fichier&nbsp;:</p> <pre> backlog = 2048 bind = &quot;unix:/var/run/gunicorn/monprojet.sock&quot; pidfile = &quot;/var/run/gunicorn/monprojet.pid&quot; daemon = True debug = False workers = 2 logfile = &quot;/opt/django/www_domain_tld/log/gunicorn.log&quot; loglevel = &quot;info&quot; </pre> <p>Pour bind vous pouvez soit spécifier un socket Unix soit une adresse et un port TCP/IP. Veuillez consulter la <a href="http://gunicorn.org/configuration.html" hreflang="en">doc configuration</a> pour les détails.</p> <p>Vous pouvez alors lancer gunicorn avec la commande suivante&nbsp;:</p> <pre> $ gunicorn_django -c gunicorn.conf.py </pre> <p>Bien sûr, comme pour <a href="http://blog.sietch-tabr.com/index.php/post/2010/04/03/Faire-tourner-des-applications-ruby-on-rails-avec-nginx-et-unicorn">l'article précédent</a> que se passe-t-il en cas de redémarrage du serveur&nbsp;? Il faut relancer les processus gunicorn un par un. Pour éviter ce genre de désagréments j'ai donc créé un script destiné au système de démarrage sysvinit de Debian.</p> <p>Ce script a comme prérequis&nbsp;:</p> <ul> <li>d'avoir pour chaque projet Django <ul> <li>un virtualenv dédié dans un répertoire <code>www_domaine_tld</code></li> <li>dans ce virtualenv un répertoire nommé <code>monprojet</code> avec les sources de celui-ci et notamment les fichiers importants pour Django (settings.py, urls.py...)</li> </ul></li> <li>d'avoir un répertoire <code>/etc/gunicorn/sites</code></li> <li>dans ce répertoire vous faites un lien vers le virtualenv de chaque projet en lui donnant le nom du répertoire du projet, exemple&nbsp;:</li> </ul> <pre> # cd /etc/gunicorn/sites # ln -s /opt/django/www_domaine_tld monprojet </pre> <ul> <li>le fichier <code>gunicorn.conf.py</code> est situé dans le virtualenv (donc dans le répertoire parent de celui des sources du projet)</li> <li>il faut que le pid spécifié dans ce fichier soit de la forme <code>monprojet.pid</code> puisque son nom est ainsi extrapolable par rapport au nom du lien dans /etc/gunicorn/sites</li> </ul> <p>Ce qui donne (pour éclaircir tout ça) une arborescence comme suit&nbsp;:</p> <pre> /etc/gunicorn/sites/ monprojet1 -&gt; /opt/django/www_domaine1_tld # lien vers /opt/django/www_domaine1_tld monprojet2 -&gt; /opt/django/www_domaine2_tld # lien vers /opt/django/www_domaine2_tld /opt/django/ www_domaine1_tld/ bin/ # include/ # répertoires créés par virtualenv lib/ # monprojet1/ # répertoire contenant les sources du projet gunicorn.conf.py # fichier de configuration de gunicorn www_domaine2_tld/ bin/ # include/ # répertoires créés par virtualenv lib/ # monprojet2/ # répertoire contenant les sources du projet gunicorn.conf.py # fichier de configuration de gunicorn </pre> <p>Et voici le script <code>/etc/init.d/gunicorn</code></p> <pre> #!/bin/sh ### BEGIN INIT INFO # Provides: gunicorn # Required-Start: $local_fs $syslog # Required-Stop: $local_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Gunicorn processes ### END INIT INFO USER=www-data NAME=&quot;gunicorn&quot; DAEMON=&quot;gunicorn_django&quot; CONFDIR=/etc/gunicorn/sites PIDDIR=/var/run/gunicorn VENV_ACTIVATION=&quot;source ../bin/activate&quot; CONFFILE=&quot;../gunicorn.conf.py&quot; OPTIONS=&quot;-c $CONFFILE&quot; RETVAL=0 # source function library . /lib/lsb/init-functions # pull in default settings [ -f /etc/default/gunicorn ] &amp;&amp; . /etc/default/gunicorn start() { echo $&quot;Starting $NAME.&quot; cd $CONFDIR; for d in *; do echo -n $d; PIDFILE=$PIDDIR/$d.pid if [ -f $PIDFILE ]; then echo &quot;: already started!&quot; else cd $d; cd $d; su -c &quot;$VENV_ACTIVATION; $DAEMON $OPTIONS&quot; $USER &amp;&amp; echo &quot;: OK&quot;; fi done echo &quot;done&quot; } stop() { echo $&quot;Stopping $NAME:&quot; cd $CONFDIR for d in *; do echo -n $d; if [ -f $PIDDIR/$d.pid ] then kill -QUIT `cat $PIDDIR/$d.pid` &amp;&amp; echo &quot;: OK&quot; || echo &quot;: failed&quot;; fi done echo &quot;done&quot; } reload() { echo $&quot;Reloading $NAME:&quot; cd $CONFDIR for d in *; do echo -n $d; if [ -f $PIDDIR/$d.pid ] then kill -HUP `cat $PIDDIR/$d.pid` &amp;&amp; echo &quot;: OK&quot; || echo &quot;: failed&quot;; fi done echo &quot;done&quot; } case &quot;$1&quot; in start) start ;; stop) stop ;; restart) reload ;; reload) reload ;; force-reload) stop &amp;&amp; start ;; *) echo $&quot;Usage: $0 {start|stop|restart}&quot; RETVAL=1 esac exit $RETVAL </pre> <h2>Nginx</h2> <p>Pour Nginx, on retrouve un fichier de configuration semblable à celui-ci&nbsp;:</p> <pre> upstream www_domaine_tld { server unix:/var/run/gunicorn/monprojet.sock fail_timeout=0; } server { listen 80; server_name www.domaine.tld; access_log /var/log/nginx/www.domaine.tld.access.log; error_log /var/log/nginx/www.domaine.tld.error.log; location /media { root /opt/django/www.domaine.tld/lib/python2.5/site-packages/django/contrib/admin/; expires 30d; } root /opt/django/www.domaine.tld/monprojet/media; location / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; if (!-f $request_filename) { proxy_pass http://www_domaine_tld; break; } } } </pre> <p>Et voili.</p> Faire tourner des applications Ruby on Rails avec Nginx et Unicorn urn:md5:f1adc99f1c2ebf5e7a46623a3aacef7a 2010-04-03T11:13:00+02:00 2010-11-21T18:17:01+01:00 eric Debianhébergementligne de commandeNginx <p><strong>Mise à jour&nbsp;: j'ai finalement décidé d'utiliser <a href="http://supervisord.org/">supervisor</a> pour gérer/monitorer ce genre de services, voyez <a href="http://blog.sietch-tabr.com/index.php/post/2010/06/03/gestion-et-surveillance-de-processus-syst%C3%A8me-avec-supervisor-pour-gerer-des-projets-django-ruby-on-rails-wsgi">ce billet</a>.</strong></p> <p>Afin d'optimiser mon petit serveur virtuel chez Gandi qui, à force de lui ajouter des fonctions, des sites web, des utilisateurs avait de petits problèmes de charge au point de temps en temps de ne plus répondre, je migre du serveur web Apache vers <a href="http://nginx.net/">Nginx</a>. Étant donné que je fais tourner de tout et de rien sur Apache&nbsp;: du php, des cgi, du Ruby on Rails et du Django, l'opération s'avère un poil complexe, et je vais essayer de présenter petit à petit les solutions retenues pour servir chacune de ces technologies avec Nginx, en commençant par les applications Ruby on Rails.</p> <h2>Unicorn</h2> <p>Avec Apache, pour faire tourner mon application Ruby on Rails (<a href="http://www.redmine.org">Redmine</a>) j'utilisais <a href="http://www.modrails.com/">Passenger/mod_rails</a>, parce qu'après moults recherches c'est la solution que j'avais trouvé. Le passage à Nginx m'a permis de me pencher à nouveau sur ce problème et j'ai décidé de me tourner vers <a href="http://unicorn.bogomips.org/">Unicorn</a><sup>[<a href="http://x.sietch-tabr.com/index.php/post/2010/04/03/Faire-tourner-des-applications-ruby-on-rails-avec-nginx-et-unicorn#pnote-274-1" id="rev-pnote-274-1">1</a>]</sup>.</p> <p>Pour installer unicorn le plus simple est de passer par l'outil de gestionnaire de packages Ruby gem&nbsp;:</p> <pre> # gem install unicorn </pre> <p>Afin de servir vos projets Rails, il faut lancer un processus Unicorn par projet, en spécifiant les options nécessaires. Le plus simple pour spécifier ces options est de créer un fichier de configuration pour Unicorn directement dans chaque projet Rails. Ce fichier doit être nommé <code>unicorn.rb</code> et situé dans le répertoire <code>config</code> du <code>RAILS_ROOT</code> c'est-à-dire du répertoire racine de votre projet.</p> <p>Voici un exemple de fichier sur lequel vous pouvez vous baser pour créer votre propre configuration.</p> <pre> # configuration file for Unicorn (not Rack) # # See http://unicorn.bogomips.org/Unicorn/Configurator.html for complete # documentation. # Use at least one worker per core if you're on a dedicated server, # more will usually help for _short_ waits on databases/caches. worker_processes 2 # Help ensure your application will always spawn in the symlinked # &quot;current&quot; directory that Capistrano sets up. working_directory &quot;/opt/redmine&quot; # available in 0.94.0+ # listen on both a Unix domain socket and a TCP port, # we use a shorter backlog for quicker failover when busy listen &quot;/var/run/unicorn/redmine.sock&quot;, :backlog =&gt; 64 #listen 8080, :tcp_nopush =&gt; true # nuke workers after 30 seconds instead of 60 seconds (the default) timeout 30 # feel free to point this anywhere accessible on the filesystem pid &quot;/var/run/unicorn/redmine.pid&quot; # some applications/frameworks log to stderr or stdout, so prevent # them from going to /dev/null when daemonized here: stderr_path &quot;/opt/redmine/log/unicorn.stderr.log&quot; stdout_path &quot;/opt/redmine/log/unicorn.stdout.log&quot; # combine REE with &quot;preload_app true&quot; for memory savings # http://rubyenterpriseedition.com/faq.html#adapt_apps_for_cow preload_app true GC.respond_to?(:copy_on_write_friendly=) and GC.copy_on_write_friendly = true before_fork do |server, worker| # the following is highly recomended for Rails + &quot;preload_app true&quot; # as there's no need for the master process to hold a connection defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect! # The following is only recommended for memory/DB-constrained # installations. It is not needed if your system can house # twice as many worker_processes as you have configured. # # # This allows a new master process to incrementally # # phase out the old master process with SIGTTOU to avoid a # # thundering herd (especially in the &quot;preload_app false&quot; case) # # when doing a transparent upgrade. The last worker spawned # # will then kill off the old master process with a SIGQUIT. # old_pid = &quot;#{server.config[:pid]}.oldbin&quot; # if old_pid != server.pid # begin # sig = (worker.nr + 1) &gt;= server.worker_processes ? :QUIT : :TTOU # Process.kill(sig, File.read(old_pid).to_i) # rescue Errno::ENOENT, Errno::ESRCH # end # end # # # *optionally* throttle the master from forking too quickly by sleeping # sleep 1 end after_fork do |server, worker| # per-process listener ports for debugging/admin/migrations # addr = &quot;127.0.0.1:#{9293 + worker.nr}&quot; # server.listen(addr, :tries =&gt; -1, :delay =&gt; 5, :tcp_nopush =&gt; true) # the following is *required* for Rails + &quot;preload_app true&quot;, defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection # if preload_app is true, then you may also want to check and # restart any other shared sockets/descriptors such as Memcached, # and Redis. TokyoCabinet file handles are safe to reuse # between any number of forked children (assuming your kernel # correctly implements pread()/pwrite() system calls) end </pre> <p>Éditez les premières variables pour correspondre à votre configuration&nbsp;: nombre de processus au démarrage, répertoire de travail, écoute sur un socket ou un port TCP/IP, emplacement du fichier pid et des logs de capture de stdout et stderr.</p> <p>Vous pouvez alors vous mettre dans le répertoire en question et lancer Unicorn comme suit&nbsp;:</p> <pre> /opt/redmine$ unicorn_rails -c config/unicorn.rb -E production -D </pre> <p><em>Je n'ai malheureusement pas trouvé comment spécifier l'environnement à utiliser (le RAILS_ENV, ici production) autrement que dans la ligne de commande&nbsp;: l'indiquer dans le fichier de configuration ne marche pas... mais <strong>le premier qui trouve/sais merci de laisser un commentair</strong>e !</em></p> <p>Tout ça c'est bien sympa mais ce n'est pas pratique de devoir lancer à la main chacun des processus unicorn nécessaire ni de devoir les relancer à la main si le serveur redémarre.</p> <p>Pour pallier à ce problème, il faut créer un script de démarrage de Unicorn. Vu que ma Debian utilise un <a href="http://packages.debian.org/lenny/sysvinit">init à la Système V</a> j'ai préféré créer un script pour ce système plutôt qu'utiliser un autre outil pour cela. Celui que j'utilise est adapté de <a href="http://blog.darmasoft.net/2010/02/01/from-mongrels-to-unicorns">ce site</a>. Il suffit de vérifier que la partie des variables (notamment le RAILS_ENV, USER, DAEMON) correspond à votre configuration.</p> <pre> #!/bin/sh ### BEGIN INIT INFO # Provides: unicorn # Required-Start: $local_fs $syslog # Required-Stop: $local_fs $syslog # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Unicorn processes ### END INIT INFO RAILS_ENV=&quot;production&quot; USER=www-data DAEMON=&quot;/var/lib/gems/1.8/bin/unicorn_rails&quot; NAME=&quot;unicorn&quot; CONFDIR=/etc/unicorn/sites PIDDIR=/var/run/unicorn CONFFILE=config/unicorn.rb RETVAL=0 OPTIONS=&quot;-c $CONFFILE -D -E $RAILS_ENV&quot; # source function library . /lib/lsb/init-functions # pull in default settings [ -f /etc/default/unicorn ] &amp;&amp; . /etc/default/unicorn test -x $DAEMON || exit 0 start() { echo $&quot;Starting $NAME.&quot; cd $CONFDIR; for d in *; do echo -n $d; cd $d; PIDFILE=$PIDDIR/$d.pid [ -f $PIDFILE ] &amp;&amp; echo &quot;: already started!&quot; [ ! -f $PIDFILE ] &amp;&amp; su -c &quot;$DAEMON $OPTIONS&quot; $USER &amp;&amp; echo &quot;: OK&quot;; done echo &quot;done&quot; } stop() { echo $&quot;Stopping $NAME:&quot; cd $CONFDIR for d in *; do echo -n $d; if [ -f $PIDDIR/$d.pid ] then kill -QUIT `cat $PIDDIR/$d.pid` &amp;&amp; echo &quot;: OK&quot; || echo &quot;: failed&quot;; fi done echo &quot;done&quot; } reload() { echo $&quot;Reloading $NAME:&quot; cd $CONFDIR for d in *; do echo -n $d; if [ -f $PIDDIR/$d.pid ] then kill -USR2 `cat $PIDDIR/$d.pid` &amp;&amp; echo &quot;: OK&quot; || echo &quot;: failed&quot;; fi done echo &quot;done&quot; } case &quot;$1&quot; in start) start ;; stop) stop ;; restart) reload ;; reload) reload ;; force-reload) stop &amp;&amp; start ;; *) echo $&quot;Usage: $0 {start|stop|restart}&quot; RETVAL=1Unicorn se lance en utilisant des sockets mais peut également utiliser TCP/IP. esac exit $RETVAL </pre> <p>Ce script est orienté Debian, et devra probablement être adapté pour d'autres distributions.</p> <p>Pour le faire tourner il vous faut&nbsp;:</p> <ul> <li>un répertoire <code>/etc/unicorn/sites</code> dans lequel vous mettez des liens symboliques vers vos projets Rails (le RAILS_ROOT plus exactement de chacun)</li> <li>dans chacun des <code>RAILS_ROOT</code> de vos projets un fichier <code>config/unicorn.rb</code> comme indiqué au-dessus</li> </ul> <h2>Nginx</h2> <p>Ensuite pour utiliser Unicorn depuis Nginx, il vous suffit d'avoir une déclaration de virtualhost du type&nbsp;:</p> <pre> upstream redmine_domain_tld { server unix:/var/run/unicorn/redmine.sock fail_timeout=0; } server { listen 80; server_name redmine.domain.tld; access_log /var/log/nginx/redmine.domain.tld.access.log; error_log /var/log/nginx/redmine.domain.tld.error.log; location /images { root /opt/redmine/public/images; } location / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; if (!-f $request_filename) { proxy_pass http://redmine_domain_tld; break; } } } </pre> <p>Et voilà. La suite de ma migration de Apache vers Nginx dans un prochain épisode.</p> <div class="footnotes"><h4>Notes</h4> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2010/04/03/Faire-tourner-des-applications-ruby-on-rails-avec-nginx-et-unicorn#rev-pnote-274-1" id="pnote-274-1">1</a>] merci à <a href="http://neokraft.net">Olivier Meunier</a> de m'avoir rappeler l'existence de ce soft... auquel j'aurais dû penser tout de suite en connaissant <a href="http://gunicorn.org">gunicorn</a></p></div> Django : gérer plusieurs domaines avec un seul projet et les mêmes données urn:md5:83d54ffd15c22878abbb55cdae855bdd 2010-02-14T14:52:00+01:00 2010-02-19T15:23:09+01:00 eric InformatiqueEtGeekeries Djangohébergementligne de commandeprogrammationprojets <h2>Problématique</h2> <p>Toujours en plein dans Django, mon projet actuel est de refaire tous mes sites web hébergés sur divers domaines en Django. Jusque là tout va bien je dirais. Ça se complique un peu quand l'idée est d'utiliser le même projet pour gérer tous ces sites web.</p> <p>Pourquoi utiliser le même projet&nbsp;? Plusieurs (bonnes) raisons à cela&nbsp;:</p> <ul> <li>simplifier le développement, la maintenance et le déploiement&nbsp;: un projet à gérer c'est mieux que plusieurs</li> <li>partager les mêmes données&nbsp;: c'est à mon avis le point essentiel qui m'intéresse&nbsp;: pouvoir créer du contenu (billet de blog, photo,...) et pouvoir facilement l'affecter à tel ou tel site ou à plusieurs à la fois <em>(cela dépend également des applications, cf plus bas)</em></li> <li>tout administrer de façon centralisée&nbsp;: c'est la suite du point précédent&nbsp;: pouvoir, dans une interface d'administration unique, créer du contenu et l'affecter à tel ou tel site web sans devoir changer d'interface d'admin</li> </ul> <p>Je vais donc vous proposer ici la solution que j'ai retenu pour servir 2 domaines différents avec Django. Ceux-ci seront pour l'exemple www.domaine1.tld et www.domaine2.tld (ouaouh&nbsp;! que c'est original&nbsp;! - ça marche bien évidemment pour un sous-domaine...).</p> <p>L'historique de cette configuration est expliqué en bas de ce billet pour ceux que ça intéresse.</p> <div style="border: 1px solid black; padding: 10px; margin:10px"> <p><ins><strong>note importante</strong></ins>&nbsp;: puisqu'on m'a signalé que la solution présentée ici n'est pas forcément "propre" au sens Django puisqu'elle implique de changer le nom de divers fichiers de base d'un projet Django, et puisqu'après réflexion je suis d'accord avec cette remarque... voici quelques éclaircissements&nbsp;:</p> <p>Si vous faites les choses le plus possible dans le style Django, en pensant application réutilisable il est en effet préférable de penser chaque domaine comme un projet séparé et donc d'isoler les fichiers plutôt de cette façon</p> <pre> www_domaine1_tld/ __init__.py manage.py settings.py urls.py www_domaine2_tld/ __init__.py manage.py settings.py urls.py </pre> <p>ce qui permet de ne pas modifier les fichiers manage, settings et urls. Cela est encore facilité si votre projet est composé d'applications réutilisables, installables à la manière de modules Python (ce qui doit d'ailleurs être au maximum le cas).</p> <p>La documentation ci-dessous est donc toujours valable mais il est probablement préférable d'utiliser cette manière de faire... à vous de voir maintenant...</p> </div> <h2>Arborescence</h2> <p>Le premier point à voir est l'organisation des fichiers dans le répertoire du project. Afin de pas trop encombrer celui-ci, les fichiers ayant la même fonction (les fichiers settings.py, urls.py, les templates) sont déplacés dans des sous-répertoires, les fichiers manage.py restant eux à la racine, comme suit&nbsp;:</p> <pre> monprojet/ www_domaine1_tld_manage.py www_domaine2_tld_manage.py settings/ __init__.py www_domaine1_tld.py www_domaine2_tld.py global_settings.py urls/ __init__.py www_domaine1_tld.py www_domaine2_tld.py templates/ www_domaine1_tld www_domaine2_tld </pre> <p>il ne faut pas oublier les fichiers __init__.py aux endroits indiqués.</p> <h2>Fichiers manage.py</h2> <p>Pour exécuter des commandes Django, on peut utiliser <code>$ django-admin.py &lt;commande&gt; &lt;options&gt;</code> avec certaines contraintes (spécifier le fichier settings...) ou le "raccourci" <code>$ ./manage.py &lt;commande&gt; &lt;options&gt;</code>.</p> <p>Dans le cas où l'on veut gérer plusieurs domaines, et où le fichier "settings" ne s'appelle pas <code>settings.py</code>, il faut créer un fichier manage.py différent par domaine.</p> <p>Exemple avec www_domaine1_tld_manage.py.</p> <pre> #!/usr/bin/env python from django.core.management import execute_manager try: import settings.www_domaine1_tld except ImportError: import sys sys.stderr.write(&quot;Error: Can't find the file 'settings.www_domaine1_tld.py' in the directory containing %r....&quot; % __file__) sys.exit(1) if __name__ == &quot;__main__&quot;: execute_manager(settings.www_domaine1_tld) </pre> <p>Il suffira alors de lancer une commande comme la suivante pour exécuter des commandes Django sur un certain domaine</p> <pre> $ ./www_domaine1_tld_manage.py &lt;commande&gt; &lt;options&gt; </pre> <p><em><ins>mise à jour</ins>&nbsp;: vu qu'on me l'a signalé, cette manipulation concernant les fichiers manage.py n'est pas nécessaire puisqu'il suffit d'ajouter l'option --settings=settings.www_domaine1_tld pour pouvoir utiliser ./manage.py de manière classique</em></p> <h2>Fichiers urls.py</h2> <p>Pour les fichiers urls, il suffit de créer les fichiers suivant l'arborescence indiquée au-dessus en utilisant des fichiers urls.py "classiques", avec la différence que si vous devez importer les "settings" du domaine il faut indiquer le module sous la forme <code>settings.www_domaine1_tld</code></p> <h2>Framework "sites" de Django</h2> <p>Si vous voulez utiliser les mêmes applications sur plusieurs domaines sans pour autant devoir publier le même contenu partout, il faut alors utiliser des applications qui utilisent le framework "<a href="http://docs.djangoproject.com/en/dev/ref/contrib/sites/" hreflang="en">sites</a>".</p> <p>Vous devez dans ce cas&nbsp;:</p> <ul> <li>créer des sites dans la base de données</li> <li>et spécifier le SITE_ID qui va bien dans le fichier "settings" <em>(voir plus bas)</em></li> </ul> <h2>Fichiers settings.py</h2> <p>Pour gérer les paramètres de chaque domaine/site on aura un fichier pour chacun ainsi qu'un fichier pour les paramètres globaux (pour les bases de données, les applications installées...).</p> <p>Voici les paramètres spécifiques à chaque domaine, avec des commentaires.</p> <pre> # paramètres Django pour le site www.domaine1.tld import os # on charge ici les paramètres spécifiés dans global_settings.py from global_settings import * # on initialise BASE_DIR avec le répertoire de base du projet, # pour l'utiliser pour d'autres paramètres BASE_DIR = os.path.join(os.path.dirname(__file__), '..') # il faut indiquer ici l'identifiant du site (au sens Django), ansi que dans la base de données # cf http://docs.djangoproject.com/en/dev/ref/contrib/sites/ SITE_ID = 1 # chemin absolu vers le répertoire des médias du domaine MEDIA_ROOT = os.path.join(BASE_DIR, 'static', 'www_domaine1_tld') # URL pour gérer les médias venant de MEDIA_ROOT MEDIA_URL = 'http://www.domaine1.tld/static/' # il faut ici initialiser une clé secrète distincte pour chaque domaine SECRET_KEY = 'xxx' # on spécifie ici le fichier urls.py ROOT_URLCONF = 'monprojet.urls.www_domaine1_tld' # on spécifie ici le répertoires des templates TEMPLATE_DIRS = ( os.path.join(BASE_DIR, 'templates', 'www_domaine1_tld') ) # ce qui suit n'est pas obligatoire mais permet d'avoir un fichier spécifique # en local afin par exemple de surcharger quelques paramètres pour le dev try: from local_settings import * except ImportError: pass </pre> <p>Et voici un exemple de fichier global_settings.py avec les paramètres qui peuvent/doivent être partagés.</p> <pre> DATABASE_ENGINE = '' DATABASE_NAME = '' DATABASE_USER = '' DATABASE_PASSWORD = '' DATABASE_HOST = '' DATABASE_PORT = '' TEMPLATE_LOADERS = ( 'django.template.loaders.filesystem.load_template_source', 'django.template.loaders.app_directories.load_template_source', ) MIDDLEWARE_CLASSES = ( 'django.middleware.common.CommonMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', ) INSTALLED_APPS = ( 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.sites', 'django.contrib.admin', ) </pre> <h2>Serveur web/Python/WSGI</h2> <p>Pour cette partie, j'utilise pour ma part (pour l'instant) Apache+mod_wsgi mais je ne vais pas rentrer dans les détails puisque ça n'est pas spécifique au cas particulier qui nous intéresse ici.</p> <p>J'ai un fichier pour Apache qui indique un VirtualHost pour chaque domaine et pointe vers un fichier WSGI pour chacun également.</p> <p>Dans le fichier WSGI il faut faire attention à étendre le PYTHONPATH si nécessaire et indiquer le fichier settings correctement, un peu comme ce qui suit&nbsp;:</p> <pre> import sys sys.path.append('/opt/django/monprojet/') os.environ['DJANGO_SETTINGS_MODULE'] = 'monprojet.settings.www_domaine1_tld' </pre> <h2>Historique</h2> <p>Au début de ma recherche sur cette problématique je me suis tout de suite dit que c'était le boulot de Django de faire ça, et quand j'ai compris que ce n'était pas le cas naturellement, je me suis orienté vers des applications qui, étendant Django, lui permettait de gérer cela. J'ai trouvé deux applications dont le but semble de combler ce manque&nbsp;:</p> <ul> <li><a href="http://github.com/shestera/django-multisite">django-multisite</a></li> <li><a href="http://bitbucket.org/bkroeze/django-threaded-multihost">django-threaded-multihost</a></li> </ul> <p>Je ne me souviens plus pourquoi j'ai mis de côté django-multisite mais mes tests ont porté sur django-threaded-multihost. L'application faisait ce pourquoi elle était prévu mais avait un problème majeur&nbsp;: il n'y avait qu'un settings.py, un urls.py... pour tous les domaines servis et là je me suis rendu compte que ce n'était pas vraiment ce dont j'avais besoin puisque le but était de pouvoir servir le même contenu mais bien évidemment de façon différente... Retour à la case départ&nbsp;!</p> <p>Heureusement je suis tombé sur une discussion sur IRC #django-fr entre <a href="http://www.biologeek.com" hreflang="fr">david`bgk</a> et <a href="http://cyberdelia.tryphon.org/" hreflang="fr">cyberdelia</a> concernant ce thème et cyberdelia m'a alors expliqué comment ses divers domaines (pour <a href="http://www.croisedanslemetro.com" hreflang="fr">http://www.croisedanslemetro.com</a> il me semble <em>(j'aime le concept de ce site :) soit dit en passant</em>) pointant vers un même projet étaient gérés... c'était ce qu'il me fallait&nbsp;! Le temps que ça mûrisse, celui de m'y replonger, d'implémenter la solution et de la tester, je me suis dit qu'il serait intéressant de poster cette solution et me voilà donc... (en ajoutant le temps de rédaction de ce billet ;-)... j'espère que ça pourra être utile à d'autres.</p> sixth sense: quand le réel et le virtuel se mêlent... urn:md5:8c5a5535292593a133bc7293ba6d2ab1 2009-12-01T07:44:00+01:00 2009-12-01T10:22:05+01:00 eric logiciels libresmatérieltechnologie <p>tout simplement <strong>énorme</strong> et bientôt disponible en open source</p> <object width="446" height="326"><param name="movie" value="http://video.ted.com/assets/player/swf/EmbedPlayer.swf"></param><param name="allowFullScreen" value="true" /><param name="wmode" value="transparent"></param><param name="bgColor" value="#ffffff"></param> <param name="flashvars" value="vu=http://video.ted.com/talks/dynamic/PranavMistry_2009I-medium.flv&su=http://images.ted.com/images/ted/tedindex/embed-posters/PranavMistry-2009I.embed_thumbnail.jpg&vw=432&vh=240&ap=0&ti=685&introDuration=16500&adDuration=4000&postAdDuration=2000&adKeys=talk=pranav_mistry_the_thrilling_potential_of_sixthsense_tec;year=2009;theme=design_like_you_give_a_damn;theme=new_on_ted_com;theme=the_creative_spark;theme=a_taste_of_tedindia;theme=what_s_next_in_tech;theme=ted_under_30;theme=tales_of_invention;event=TEDIndia+2009;&preAdTag=tconf.ted/embed;tile=1;sz=512x288;" /><embed src="http://video.ted.com/assets/player/swf/EmbedPlayer.swf" pluginspace="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash" wmode="transparent" bgColor="#ffffff" width="446" height="326" allowFullScreen="true" flashvars="vu=http://video.ted.com/talks/dynamic/PranavMistry_2009I-medium.flv&su=http://images.ted.com/images/ted/tedindex/embed-posters/PranavMistry-2009I.embed_thumbnail.jpg&vw=432&vh=240&ap=0&ti=685&introDuration=16500&adDuration=4000&postAdDuration=2000&adKeys=talk=pranav_mistry_the_thrilling_potential_of_sixthsense_tec;year=2009;theme=design_like_you_give_a_damn;theme=new_on_ted_com;theme=the_creative_spark;theme=a_taste_of_tedindia;theme=what_s_next_in_tech;theme=ted_under_30;theme=tales_of_invention;event=TEDIndia+2009;"></embed></object> <p>à voir sur le site de <a href="http://www.ted.com/talks/pranav_mistry_the_thrilling_potential_of_sixthsense_technology.html">TED</a> [via <a href="http://www.blendernation.com/sixth-sense-technology/">blendernation</a>]</p> <p>je vous l&#8217;avais dit&#160;: É-NOR-ME&#160;!</p> Mercurial : éditer l'historique d'un dépôt (les changesets) avec les MQ urn:md5:b771fe4892e9ad7200fe98b56996570f 2009-06-03T15:48:00+02:00 2009-06-03T15:48:00+02:00 eric InformatiqueEtGeekeries ligne de commandeMercurialprogrammation <p>J&#8217;ai toujours<sup>[<a href="http://x.sietch-tabr.com/index.php/post/2009/05/07/Mercurial-%3A-%C3%A9diter-l-historique-d-un-d%C3%A9p%C3%B4t-%28les-changesets%29-avec-les-MQ#pnote-264-1" id="rev-pnote-264-1">1</a>]</sup> cru qu&#8217;on ne pouvait éditer l&#8217;historique des &#8216;changesets&#8217; de Mercurial, mais en fait si&#160;! Et c&#8217;est grâce à <a href="http://noehr.org/post/mercurial-powertip-move-changesets-out-of-the-way-momentarily/">ce billet</a> sur le blog de Jesper Noehr (un des gars derrière <a href="http://bitbucket.org">bitbucket</a>) que j&#8217;ai découvert qu&#8217;en fait c&#8217;est tout à fait possible.</p> <p>En utilisant les MQ (mercurial queues) dont je vous ai déjà parlé, il est donc tout à fait possible de rééditer des modifications déjà enregistrées (committées) dans votre dépôt Mercurial.</p> <p>Bien évidemment, ce genre de manipulations n&#8217;est possible que si vous <strong>contrôlez</strong> votre dépôt et ses éventuels clones. Dès l&#8217;instant où celui-ci a pu être cloné, vous avez perdu la maîtrise de votre code et les modifications sur lesquelles vous voulez revenir sont déjà parties.</p> <p>Nous sommes dans un dépôt test avec 3 changesets</p> <pre> $ hg log changeset: 2:6a2d12a15cda tag: tip summary: modifications de a et b changeset: 1:ca5faf3b4493 summary: ajout de b changeset: 0:66545c7be018 summary: ajout de a</pre> <p>et nous souhaitons revenir sur les changesets 1 et 2.</p> <p>Nous initialisons d&#8217;abord un dépôt de MQ si ce n&#8217;est pas déjà fait</p> <pre> $ hg qinit -c</pre> <p><sup>[<a href="http://x.sietch-tabr.com/index.php/post/2009/05/07/Mercurial-%3A-%C3%A9diter-l-historique-d-un-d%C3%A9p%C3%B4t-%28les-changesets%29-avec-les-MQ#pnote-264-2" id="rev-pnote-264-2">2</a>]</sup></p> <p>Puis nous importons les changesets que nous voulons modifier</p> <pre> $ hg qimport -r 2:1</pre> <p>Si on regarde maintenant le log</p> <pre> changeset: 2:6a2d12a15cda tag: qtip tag: 2.diff tag: tip summary: modification de a et b changeset: 1:ca5faf3b4493 tag: 1.diff tag: qbase summary: ajout de b changeset: 0:66545c7be018 tag: qparent summary: ajout de a</pre> <p>on retrouve bien nos 3 différents changesets sauf que les 2 derniers sont différents&#160;: ce sont maintenant des patchs sous forme MQ que nous pouvons alors manipuler de façon classique<sup>[<a href="http://x.sietch-tabr.com/index.php/post/2009/05/07/Mercurial-%3A-%C3%A9diter-l-historique-d-un-d%C3%A9p%C3%B4t-%28les-changesets%29-avec-les-MQ#pnote-264-3" id="rev-pnote-264-3">3</a>]</sup></p> <p>On peut donc dépiler tous les patchs pour revenir dans l&#8217;état qu&#8217;on voulait avant les changesets 1 et 2</p> <pre> $ hg qpop -a Patch queue now empty $ hg log changeset: 0:66545c7be018 tag: tip summary: ajout de a</pre> <p>On peut alors à coup de hg qpush/hg qpop empiler/dépiler nos patchs afin de les modifier, les réorganiser ou ajouter des changesets, et donc revenir sur l&#8217;historique de notre dépôt.</p> <p><em>J&#8217;ai découvert qu&#8217;en fait cette information est également disponible sur le site de Mercurial, voyez <a href="http://www.selenic.com/mercurial/wiki/EditingHistory" title="http://www.selenic.com/mercurial/wiki/EditingHistory">http://www.selenic.com/mercurial/wi&#8230;</a></em></p> <div class="footnotes"><h4>Notes</h4> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2009/05/07/Mercurial-%3A-%C3%A9diter-l-historique-d-un-d%C3%A9p%C3%B4t-%28les-changesets%29-avec-les-MQ#rev-pnote-264-1" id="pnote-264-1">1</a>] &#8220;toujours&#8221; est peut-être un trop grand mot, ça ne fait pas non plus si longtemps que ça que je connais Mercurial ;-)&#8230;</p> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2009/05/07/Mercurial-%3A-%C3%A9diter-l-historique-d-un-d%C3%A9p%C3%B4t-%28les-changesets%29-avec-les-MQ#rev-pnote-264-2" id="pnote-264-2">2</a>] tant qu&#8217;à faire nous ajoutons -c pour avoir un dépôt MQ &#8216;versionnable&#8217;</p> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2009/05/07/Mercurial-%3A-%C3%A9diter-l-historique-d-un-d%C3%A9p%C3%B4t-%28les-changesets%29-avec-les-MQ#rev-pnote-264-3" id="pnote-264-3">3</a>] je vous renvoie vers les <a href="http://hgbook.red-bean.com/read/managing-change-with-mercurial-queues.html">chapitre 12</a> et <a href="http://hgbook.red-bean.com/read/advanced-uses-of-mercurial-queues.html">13</a> du hgbook pour plus d&#8217;infos</p></div> django-userthemes : une application pour gérer des thèmes utilisateur pour Django urn:md5:f6406f58a5277f7e48a475f864fb4595 2009-03-02T12:20:00+01:00 2009-03-02T14:02:09+01:00 eric InformatiqueEtGeekeries Djangologiciels libresprojetsPython <p>Un petit billet pour annoncer la sortie de la version 0.1 de l&#8217;application <strong><a href="http://bitbucket.org/daks/django-userthemes">django-userthemes</a></strong>. C&#8217;est une &#8220;application réutilisable&#8221; pour <a href="http://www.djangoproject.com">Django</a><sup>[<a href="http://x.sietch-tabr.com/index.php/post/2009/03/02/django-userthemes-%3A-une-application-pour-g%C3%A9rer-des-th%C3%A8mes-utilisateur-pour-Django#pnote-263-1" id="rev-pnote-263-1">1</a>]</sup>.</p> <p>L&#8217;origine de cette application est le besoin de créer une système de thèmes pour un projet sur lequel je travaille. Après moultes recherches<sup>[<a href="http://x.sietch-tabr.com/index.php/post/2009/03/02/django-userthemes-%3A-une-application-pour-g%C3%A9rer-des-th%C3%A8mes-utilisateur-pour-Django#pnote-263-2" id="rev-pnote-263-2">2</a>]</sup>, j&#8217;ai décidé de créer ma propre implémentation en essayant au maximum de suivre cette philosophie de &#8216;reusable apps&#8217;.</p> <p><strong>django-userthemes</strong> permet donc de définir pour chaque utilisateur enregistré dans une application un thème favori qui sera chargé quand il se connectera à celle-ci. On peut définir le répertoire où seront stockés les thèmes et aussi celui par défaut qui sera chargé quand aucun utilisateur n&#8217;est connecté ou que sa préférence n&#8217;est pas fixée. Je vous encourage à lire la doc (et le code) pour comprendre comment ça fonctionne en détail.</p> <p>Vous trouverez le projet à cette adresse&#160;: <a href="http://bitbucket.org/daks/django-userthemes" title="http://bitbucket.org/daks/django-userthemes">http://bitbucket.org/daks/django-us&#8230;</a> où vous pourrez récupérer le code source (via Mercurial ou un fichier archive), rapporter des bugs&#8230;</p> <p>La license utilisée en la GNU GPLv2.</p> <p>Le projet est encore en phase de développement donc tout retour est bienvenue.</p> <p>Si vous vous décidez à l&#8217;utiliser dans un de vos projets, merci de le dire en utilisant un widget <a href="http://ohloh.net">ohloh</a> comme celui-ci&#160;:</p> <script type="text/javascript" src="http://www.ohloh.net/p/310462/widgets/project_users_logo.js"></script> <div class="footnotes"><h4>Notes</h4> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2009/03/02/django-userthemes-%3A-une-application-pour-g%C3%A9rer-des-th%C3%A8mes-utilisateur-pour-Django#rev-pnote-263-1" id="pnote-263-1">1</a>] voyez <a href="http://www.youtube.com/watch?v=A-S0tqpPga4">cette vidéo de James Bennett</a> et <a href="http://ericholscher.com/projects/django-conventions/app/">cette convention de Eric Holscher</a> pour plus d&#8217;infos</p> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2009/03/02/django-userthemes-%3A-une-application-pour-g%C3%A9rer-des-th%C3%A8mes-utilisateur-pour-Django#rev-pnote-263-2" id="pnote-263-2">2</a>] me conduisant vers des projets morts ou ne correspondant pas à nos besoins/désirs&#160;: django-themes, django-skins, django-userskins, django-dbtemplates</p></div> Mercurial : partager votre dépôt de patches MQ en même temps que votre dépôt principal urn:md5:bfe9563f010d67c9483a9002e1582f26 2008-12-12T12:05:00+01:00 2008-12-12T13:40:53+01:00 eric InformatiqueEtGeekeries ligne de commandeMercurialprogrammation <p>Travaillant actuellement sur un projet de développement en mode collaboratif, nous utilisons Mercurial pour gérer nos sources, et je me suis mis à utiliser intensément les MQ (Mercurial Queues) pour gérer mes propres modifications.</p> <p>Je ne vais pas rentrer dans les détails de ce que sont les MQ, juste vous dire que c&#8217;est un système permettant de gérer une série de patchs &#8220;flottants&#8221;, un peu comme des commits mais que vous pouvez dépiler et empiler pour les modifier suivant vos besoins, tout en suivant le développement principal.<br /> Je vous renvoie vers la <a href="http://www.selenic.com/mercurial/wiki/index.cgi/MqExtension">documentation officielle</a> et <a href="http://www.selenic.com/mercurial/wiki/index.cgi/MqExtension">le chapitre qui y est consacré dans le livre</a> (à lire et relire pour comprendre le principe).</p> <p>Jusque là en travaillant de mon côté avec des MQ, je pouvais tranquillement poursuivre plusieurs développements en parallèle en local, tout en suivant le développement sur la branche principale. Quand un patch était ok, je l&#8217;appliquais sur le dépôt principal et tout allait bien.<br /> Le problème se pose maintenant parce que je veux, tout en maintenant une série de patches, pouvoir les partager afin de montrer l&#8217;avancée de mon travail. (tout en continuant à pouvoir les empiler/dépiler bien évidemment)<br /> C&#8217;est possible mais pas très pratique&#160;: ça demande de réinitialiser le dépôt côté serveur et les numéros de changesets changent, sans parler de l&#8217;obligation de passer par un dépôt local tiers.</p> <p>Je me suis donc mis à chercher de la doc sur le partage de patches MQ et suis tombé sur ce très intéressant article intitulé <a href="http://ches.nausicaamedia.com/articles/technogeekery/using-mercurial-queues-and-bitbucket-org">Using Mercurial Queues and bitbucket.org</a><sup>[<a href="http://x.sietch-tabr.com/index.php/post/2008/12/08/Mercurial-%3A-partager-votre-d%C3%A9p%C3%B4t-de-patches-MQ-en-m%C3%AAme-temps-que-votre-d%C3%A9p%C3%B4t-principal#pnote-260-1" id="rev-pnote-260-1">1</a>]</sup> et, ne voulant pas utiliser bitbucket pour stocker mon dépôt<sup>[<a href="http://x.sietch-tabr.com/index.php/post/2008/12/08/Mercurial-%3A-partager-votre-d%C3%A9p%C3%B4t-de-patches-MQ-en-m%C3%AAme-temps-que-votre-d%C3%A9p%C3%B4t-principal#pnote-260-2" id="rev-pnote-260-2">2</a>]</sup>, me suis mis en tête de faire quelque chose d&#8217;équivalent sur mon propre hébergement.</p> <p>Peu de doc existe donc voici la mienne&#8230;</p> <h2>Sur le serveur</h2> <p>on commence par initialiser un dépôt qu&#8217;on va appeler test</p> <pre> $ hg init /chemin/vers/test</pre> <p>puis on y initialise un dépôt de MQ (qui l&#8217;on retrouvera dans <code>.hg/patches</code>)</p> <pre> $ cd /chemin/vers/test $ hg qinit -c</pre> <p>on doit ensuite créer un fichier hgrc afin de spécifier les autorisations de chacun des deux dépôts&#160;: <code>/chemin/vers/test/.hg/hgrc</code> et <code>/chemin/vers/test/.hg/patches/.hg/hgrc</code>&#160;: je ne rentre pas dans les détails et vous renvoie vers <a href="http://blog.sietch-tabr.com/index.php/post/2008/01/21/Mercurial-%3A-partage-de-depots-differents-en-http-et-https-push-par-https">mon article dédié au partage de dépôts Mercurial</a> si vous en avez besoin.</p> <p>Il faut également ajouter ces deux dépôts dans la configuration hgweb(dir).cgi</p> <pre> [paths] test = /chemin/vers/test/ test-mq = /chemin/vers/test/.hg/patches</pre> <p>Si vous allez sur l&#8217;url de votre site vous devriez alors voir quelque chose comme ça <img src="http://x.sietch-tabr.com/public/screenshots/Capture-Mercurial_repositories_index_-_Iceweasel.png" alt="Capture-Mercurial_repositories_index_-_Iceweasel.png" title="Capture-Mercurial_repositories_index_-_Iceweasel.png, déc. 2008" /></p> <h2>Sur le client</h2> <h3>Clone</h3> <p>on clone le dépôt en précisant qu&#8217;on veut également le dépôt de MQ grâce à la commande dédiée qclone</p> <pre> $ hg qclone http://hg.domain.tld/test</pre> <h3>Push</h3> <p>quand on veut pusher nos modifications sur le dépôt en y incluant notre dépôt de patches, il faut</p> <ul> <li>d&#8217;abord dépiler tous les patches</li> </ul> <pre> $ hg qpop -a</pre> <ul> <li>ensuite pusher le dépôt principal</li> </ul> <pre> $ hg push https://hguser@hg.domain.tld/test</pre> <ul> <li>puis le dépôt MQ</li> </ul> <pre> $ hg push https://hguser@hg.domain.tld/test-mq</pre> <h3>Pull</h3> <p>pour mettre à jour notre dépôt local avec les modifications disponibles sur le serveur, il faut procéder de la même façon que pour le push avec la commande pull&#160;: dépiler les patchs, mettre à jour chacun des deux dépôts.</p> <p>Voilà, je crois que je n&#8217;ai rien oublié, n&#8217;hésitez pas à me le dire si c&#8217;est le cas.</p> <div class="footnotes"><h4>Notes</h4> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2008/12/08/Mercurial-%3A-partager-votre-d%C3%A9p%C3%B4t-de-patches-MQ-en-m%C3%AAme-temps-que-votre-d%C3%A9p%C3%B4t-principal#rev-pnote-260-1" id="pnote-260-1">1</a>] je reparlerais peut-être d&#8217;ailleurs à une autre occasion de <a href="http://bitbucket.org">bitbucket</a> mais pour faire simple c&#8217;est une sorte de <a href="http://github.com">github</a> à la sauce Mercurial</p> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2008/12/08/Mercurial-%3A-partager-votre-d%C3%A9p%C3%B4t-de-patches-MQ-en-m%C3%AAme-temps-que-votre-d%C3%A9p%C3%B4t-principal#rev-pnote-260-2" id="pnote-260-2">2</a>] principalement parce que pour l&#8217;instant le développement est fermé, quand il s&#8217;ouvrira bitbucket pourra être une solution intéressante</p></div> Vous voulez défendre le logiciel libre ? Adhérez à l'APRIL ! urn:md5:1e716d5d8a041eb576530d1b4c03cd29 2008-11-17T09:39:00+01:00 2008-11-28T23:11:47+01:00 eric InformatiqueEtGeekeries APRILDADVSIEUCDlibertélogiciels libres <p>Ça fait maintenant près d&#8217;un an et demi que j&#8217;ai rejoint l&#8217;APRIL (une grande campagne d&#8217;adhésion lancée à cette époque m&#8217;avait convaincu d&#8217;adhérer&#8230; enfin&#8230;) et depuis je ne le regrette pas, tellement leur action et les combats qu&#8217;ils mènent sont ceux qui me concernent, que ce soit&#160;:</p> <ul> <li>l&#8217;initiative <a href="http://candidats.fr">candidats.fr</a> visant à faire prendre position sur le logiciel libre aux divers candidats des élections législatives puis présidentielles françaises&#160;;</li> <li>le combat contre la vente liée, et le format bureautique OOXML&#160;;</li> <li>le combat contre les brevets logiciels au niveau européen&#160;;</li> <li>celui contre DADVSI/EUCD</li> </ul> <p>et bien d&#8217;autres&#8230; voyez <a href="http://www.april.org/fr/activites">cette page</a> pour plus d&#8217;informations sur les activités de l&#8217;APRIL.</p> <p>Aujourd&#8217;hui, l&#8217;APRIL relance une campagne massive d&#8217;adhésion afin d&#8217;atteindre le chiffre critique de 5000 adhérents qui leur donnera un poids encore plus important au niveau national (représentation auprès des politiques, liberté financière), alors pour vous aussi il est temps&#160;: <strong><a href="http://www.april.org/adherer?referent=%C3%89ric+VEIRAS+GALISSON+%28eveiras_galisson%29">Rejoignez l&#8217;APRIL&#160;!</a></strong></p> <p><a href="http://www.april.org/adherer?referent=%C3%89ric+VEIRAS+GALISSON+%28eveiras_galisson%29"><img src="http://www.april.org/files/images/banniere_campagne-adhesion-objectif-5000-adherents.png" alt="" /></a></p> news sécurité informatique (ou Vos Données Chiffrées Ne Sont Pas Aussi Sûres Que Vous Ne Le Pensiez) urn:md5:40b3b83ffa3efde49c04eef9302c9d51 2008-04-29T19:32:00+02:00 2008-04-29T19:32:00+02:00 eric InformatiqueEtGeekeries anonymatcryptographieDebianLinuxprivacyTor <p>Un petit récapitulatif des nouvelles concernant la sécurité informatique intéressantes vues (plus ou moins) récemment sur la toile.</p> <ul> <li>Tout d'abord, une news très importante&nbsp;: la découverte que vos disques durs chiffrés ne sont pas aussi sûrs que vous (et moi !) ne le pensiez. En effet, une étude a démontré que la clé d'un disque dur chiffré reste encore quelques minutes dans la RAM après l'extinction d'un ordinateur, rendant celui-ci vulnérable. Cette nouvelle a fait le tour du web sécurité et voici donc quelques liens pour approfondir le sujet&nbsp;: <ul> <li>l'article d'origine sur <a href="http://www.freedom-to-tinker.com/?p=1257" hreflang="en">Freedom to tinker</a> [via <a href="http://lwn.net/Articles/270314/" hreflang="en">lwn.net</a>];</li> <li>un article sur le blog de <a href="http://etbe.coker.com.au/2008/02/26/chilled-memory-attacks/" hreflang="en">Russell Coker</a>;</li> <li>on en parle également sur <a href="http://www.ecrans.fr/Securite-La-memoire-trop-vive-nuit,3555.html" hreflang="fr">écrans.fr</a>;</li> <li>ou sur... <a href="https://linuxfr.org//~palm123/26212.html" hreflang="fr">linuxfr</a>;</li> </ul></li> </ul> <ul> <li>dans le même ordre d'idée&nbsp;: un <a href="http://www.wiebetech.com/products/HotPlug.php" hreflang="en">outil</a> permettant de voler un ordinateur sans l'éteindre [via <a href="http://www.schneier.com/blog/archives/2008/02/hotplug_1.html" hreflang="en">Bruce Schneier</a> et <a href="http://www.engadget.com/2007/11/06/wiebetech-hotplug-lets-cops-move-desktops-without-shutting-them/" hreflang="en">engadget</a>];</li> </ul> <ul> <li>un <a href="http://ptaff.ca/olpc_militaire/" hreflang="fr">article</a> original expliquant comment <a href="http://fr.wikipedia.org/wiki/One_Laptop_per_Child" hreflang="fr">l'OLPC</a> pourrait être détourné de son but premier (l'utilisation par des enfants) et utilisé à des fins militaires;</li> </ul> <ul> <li>d'autres informations importantes si vous voyagez aux USA&nbsp;: il semble que les douanes puissent nous seulement vous demander de sortir votre ordinateur portable, de l'ouvrir et de l'allumer mais également de leur laisser accéder aux données voire de leur donner le mot de passe ou clé de chiffrement&nbsp;: c'est également chez <a href="http://www.schneier.com/blog/archives/2008/02/us_customs_seiz.html" hreflang="en">Bruce Schneier</a>. (pour d'autres infos concernant les douanes US sur le même blog, voyez <a href="http://www.schneier.com/blog/archives/2007/03/bordering_on_in.html" hreflang="en">ici</a> et <a href="http://www.schneier.com/blog/archives/2006/01/us_customs_open.html" hreflang="en">là</a>);</li> <li>tiens, le magazine Wired nous fait un <a href="http://howto.wired.com/wiki/Fly_Through_Airport_Security" hreflang="en">howto</a> pour passer plus facilement les check-in d'aéroport, ça tombe bien...</li> </ul> <ul> <li>Un <a href="http://www.wired.com/politics/security/commentary/securitymatters/2008/03/securitymatters_0306" hreflang="en">article très intéressant</a> de Bruce Schneier sur wired.com concernant le respect de la vie privée ('privacy') et la surveillance généralisée. Il nous explique que l'éternel argument opposé aux réfractaires/résistants à la surveillance généralisée est celui de la surveillance mutuelle&nbsp;: vous savez ce que je fais, et moi je sais ce que vous faites. Malgré le fait que cette situation n'est pas idéale, elle semble tenir la route. Mais malheureusement, l'équilibre est rompu par un autre aspect, celui du pouvoir. En effet, par exemple, lorsqu'un officier de police vous demande vos papiers d'identité, même si il vous donne les siens, son pouvoir est considérablement supérieur au vôtre puisqu'il peut lui avec les informations chercher dans les bases de données de la police des informations sur vous, chose que vous ne pouvez faire. Je vous laisse lire l'article pour la suite de l'argumentation;</li> <li>Francis Pisani <a href="http://pisani.blog.lemonde.fr/2008/03/31/noter-les-flics…-on-peut/" hreflang="fr">nous parle</a> également des policiers via la présentation d'un site pour noter ceux de Los Angeles;</li> </ul> <ul> <li><a href="http://www.frontlinedefenders.org/manual/en/esecman/" hreflang="en">un article</a> en anglais expliquant comment protéger sa vie privée sur internet. Il est notamment destiné aux personnes risquant leur vie pour défendre leurs opinions mais certaines informations sont intéressantes pour tous [via <a href="http://grepgrrl.org/2008/02/23/digital-security-and-privacy-for-human-rights-defenders/" hreflang="fr">grepgrrl.org</a>];</li> </ul> <ul> <li><a href="http://linuxfr.org/~BlueBird/25926.html" hreflang="fr">où l'on reparle des tags RFID</a> et notamment des mifare et de leur (fausse) sécurité...</li> </ul> <ul> <li>un petit appel au troll&nbsp;: <a href="http://linuxfr.org/~patrick_g/26171.html" hreflang="fr">les délais concernant les mises à jour de sécurité pour les principales distributions Linux</a>... on dirait bien que Debian est classée en tête, cool !;</li> </ul> <ul> <li>un <a href="http://blog.halon.org.uk/2007/12/11#tor-01" hreflang="en">billet concernant Tor</a> nous rappelant que l'anonymat et la protection de la vie privée ('privacy'), ce n'est pas tout à fait la même chose même si les deux concepts sont liés;</li> </ul> <p>des articles un peu plus techniques&nbsp;:</p> <ul> <li><a href="http://artisan.karma-lab.net/node/1164" hreflang="fr">utiliser une clé USB comme mot de passe</a>;</li> <li>GPG c'est bien, mais à condition de sécuriser ses clés&nbsp;: <a href="http://www.einval.com/~steve/docs/gpg-autofs.html" hreflang="en">une solution pour les protéger</a>;</li> <li>une solution pour chiffrer tout son disque dur et utiliser une clé USB pour stocker la clé de chiffrement&nbsp;: <a href="http://www.matthew.ath.cx/articles/cryptkey" hreflang="en">Passwordless Encrypted Root in Debian</a>;</li> </ul> Mercurial : faire des modifications distantes et avoir les fichiers mis à jour sur le serveur urn:md5:7cded4be5ad3cf2e907be6aca58def39 2008-04-22T17:49:00+02:00 2008-04-22T17:49:00+02:00 eric InformatiqueEtGeekeries ligne de commandeMercurial <p>Aujourd'hui, j'ai découvert quelque chose d'étrange en utilisant Mercurial.</p> <p>J'ai donc un dépôt privé accessible via https (cf <a href="http://blog.sietch-tabr.com/index.php/post/2008/01/21/Mercurial-%3A-partage-de-depots-differents-en-http-et-https-push-par-https" hreflang="fr">ce post</a>), je le clone, je fais des modifications, je committe, puis je push sur le serveur</p> <pre> $ hg clone https://code.veiras.info/private_repo $ vi... $ hg commit -m &quot;nouvelles modifs&quot; $ hg push http authorization required realm: code.veiras.info Mercurial repository user: X password: X searching for changes adding changesets adding manifests adding file changes added 1 changesets with 3 changes to 3 files </pre> <p>Jusque là tout va bien, c'est la façon normale de travailler et j'ai toujours fait comme ça. Sauf qu'aujourd'hui, j'étais également connecté sur mon serveur en ssh, je vais donc dans mon dépôt, un hg tip me dit bien que je viens de faire un commit, mais par contre quand je veux regarder mes fichiers... mes modifications n'y sont pas et là, je tombe des nues&nbsp;: qu'ai-je donc fait mal&nbsp;? Encore une histoire de permissions sur les fichiers&nbsp;?</p> <p>&lt;mode panique&gt;<strong>ÇA VEUT DIRE QUE TOUT CE QUE J'AI COMMITTÉ DEPUIS TOUT CE TEMPS EST PERDU ???!!!</strong>&lt;/mode panique&gt;</p> <p>vite fait, je saute sur mon client IRC préféré<sup>[<a href="http://x.sietch-tabr.com/index.php/post/2008/04/22/Mercurial-%3A-faire-des-modifications-distantes-et-avoir-les-fichiers-mis-a-jour-sur-le-serveur#pnote-252-1" id="rev-pnote-252-1">1</a>]</sup> et rebondit sur #mercurial (sur freenode) où l'on me rassure (?) en me disant que tout est normal.</p> <p>Pour résumer&nbsp;:</p> <ul> <li>si je veux avoir les modifs également sur les fichiers en local sur le serveur, il faut que je fasse un <code>hg update</code> sur le serveur;</li> <li>si je veux automatiser cet update, il faut utiliser un 'hook'&nbsp;: plus d'informations sur <a href="http://www.selenic.com/mercurial/wiki/index.cgi/Hook" hreflang="en">le site officiel</a> ou sur <a href="http://hgbook.red-bean.com/hgbookch10.html#x14-19700010" hreflang="en">le guide non officiel</a>;</li> <li>si on utilise pas sur le serveur le dépôt, on peut même effacer tout les fichiers à l'exception du répertoire .hg/, celui-ci contenant justement tout l'historique du dépôt.</li> <li>si on veut effacer ces fichiers, un <code>rm *</code> pouvant poser des problèmes plus tard, il est préférable d'utiliser <code>hg update null</code> qui permet de revenir à la révision 0</li> <li>si jamais on a à nouveau besoin de ces fichiers sur le serveur, un <code>hg update</code> fera l'affaire.</li> </ul> <div class="footnotes"><h4>Notes</h4> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2008/04/22/Mercurial-%3A-faire-des-modifications-distantes-et-avoir-les-fichiers-mis-a-jour-sur-le-serveur#rev-pnote-252-1" id="pnote-252-1">1</a>] j'en profite pour signaler l'existence de mibbit.com très pratique quand on est coincé derrière un proxy et qu'on a besoin d'aide...</p></div> migration Sarge -> Etch (OCSInventory-NG) urn:md5:c3c79c034f32db4a7f1322db952dfa63 2008-03-24T23:03:00+01:00 2008-03-24T23:11:03+01:00 eric InformatiqueEtGeekeries Debianligne de commandeXen <p>Comme c'est le calme plat en ce moment sur ce blog<sup>[<a href="http://x.sietch-tabr.com/index.php/post/2008/03/18/migration-Etch-Sarge-OCSInventory-NG#pnote-249-1" id="rev-pnote-249-1">1</a>]</sup>, je vais suivre l'exemple de <a href="http://gcolpart.evolix.net/blog21" hreflang="fr">Grégory Colpart</a> et retranscrire ici une migration Sarge -&gt; Etch<sup>[<a href="http://x.sietch-tabr.com/index.php/post/2008/03/18/migration-Etch-Sarge-OCSInventory-NG#pnote-249-2" id="rev-pnote-249-2">2</a>]</sup>.</p> <p>Le serveur en question est un serveur dédié au logiciel libre d'inventaire <a href="http://www.ocsinventory-ng.org/">OCSInventory-NG</a>, c'est donc tout simplement une combinaison de serveur web Apache, de serveur de base de données MySQL, de PHP et de Perl. La vraie particularité (et seule ?) de ce serveur est celle d'être un serveur virtuel Xen.</p> <p>Une fois le fichier <code>/etc/apt/sources.list</code> édité pour y ajouter les sources de etch, un</p> <pre> # aptitude update # aptitude dist-upgrade </pre> <p>a suffi pour lancer la migration.</p> <p>Quelques petits problèmes se sont néanmoins présentés&nbsp;:</p> <ul> <li>Apache ne voulait pas se lancer et j'avais une erreur</li> </ul> <pre> [Tue Mar 18 11:40:40 2008] [error] Can't locate Apache/compat.pm in @INC..... [Tue Mar 18 11:40:40 2008] [error] Can't load Perl module Apache::Ocsinventory for server... </pre> <p>dans mes logs Apache. Il faut alors éditer le fichier de configuration OCS pour Apache (<code>/etc/apache2/conf.d/ocsinventory.conf</code> chez moi) et modifier la ligne</p> <pre> PerlSetEnv OCS_MODPERL_VERSION 2 </pre> <p>Explication&nbsp;: la migration m'a fait passé de apache à apache2 et de libapache-mod-perl (version 1.x) à libapache2-mod-perl2 (version 2.x)</p> <ul> <li>j'ai eu quelques problèmes avec MySQL qui ne voulait pas redémarrer, j'en ai donc profité pour migrer de mysql-server-4.1 à mysql-server-5.0 sans problème (à part que le serveur phpmyadmin permettant de gérer les divers serveurs mysql n'a pas le paquet mysql-client-5.0... pour l'instant)</li> </ul> <ul> <li>je suis également passé de php4 à php5 sans problème non plus.</li> </ul> <p>Rien de particulier donc. J'ai fait rebooter mon serveur virtuel au cas où, et j'avais, par sécurité, fait une copie de mon serveur virtuel (stocké sur LVM) avant la migration pour le cas où celle-ci se passerait mal. Au final, moins de 2h de maintenance, et moins d'une 1/2 heure - 1 heure je pense, pour ce serveur.</p> <div class="footnotes"><h4>Notes</h4> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2008/03/18/migration-Etch-Sarge-OCSInventory-NG#rev-pnote-249-1" id="pnote-249-1">1</a>] en partie à cause d'un déménagement qui a entrainé une coupure internet mais pas seulement...</p> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2008/03/18/migration-Etch-Sarge-OCSInventory-NG#rev-pnote-249-2" id="pnote-249-2">2</a>] migration non initialement prévue mais, afin de pouvoir utiliser un petit script de ma composition (<a href="http://code.veiras.info/ocs-ipinterface">ocs-ipinterface</a>), j'avais besoin du paquet <a href="http://packages.debian.org/python-ipy">python-ipy</a> non disponible sur sarge</p></div> Mercurial : partage de dépôts différents en http et https + push par https urn:md5:5b7e4e1093ee0c30813ff32240d1e333 2008-01-30T19:28:00+01:00 2008-12-12T11:53:13+01:00 eric InformatiqueEtGeekeries Gandihébergementligne de commandeMercurialprogrammation <p>J&#8217;ai commencé à regarder du côté des SCM<sup>[<a href="http://x.sietch-tabr.com/index.php/post/2008/01/21/Mercurial-%3A-partage-de-depots-differents-en-http-et-https-push-par-https#pnote-244-1" id="rev-pnote-244-1">1</a>]</sup> (marre de la gestion <a href="http://roland.entierement.nu/blog/2008/01/22/cpold-la-poudre-verte-du-suivi-de-versions.html" hreflang="fr">CPOLD</a> ;-) il y a quelque temps déjà, et m&#8217;était tout d&#8217;abord arrêté sur <a href="http://fr.wikipedia.org/wiki/Subversion_(logiciel)" hreflang="fr">Subversion</a> <sup>[<a href="http://x.sietch-tabr.com/index.php/post/2008/01/21/Mercurial-%3A-partage-de-depots-differents-en-http-et-https-push-par-https#pnote-244-2" id="rev-pnote-244-2">2</a>]</sup> étant novice en la matière. Il s&#8217;est vite révélé peu adapté à ma pratique&#160;: &#8220;développement&#8221; sur plusieurs machines pas toujours online, et pas de serveur dédié toujours en ligne pour stocker mes dépôts. Depuis peu, ce dernier problème s&#8217;est réglé (merci <a href="http://www.gandi.net/hebergement/" hreflang="fr">Gandi Hébergement</a><sup>[<a href="http://x.sietch-tabr.com/index.php/post/2008/01/21/Mercurial-%3A-partage-de-depots-differents-en-http-et-https-push-par-https#pnote-244-3" id="rev-pnote-244-3">3</a>]</sup>) mais pour autant, j&#8217;ai abandonné Subversion pour <a href="http://www.selenic.com/mercurial/" hreflang="en">Mercurial</a>, un <a href="http://fr.wikipedia.org/wiki/Gestion_de_version_d%C3%A9centralis%C3%A9e" hreflang="fr">gestionnaire de code source distribué</a>.</p> <p>Je ne vais pas entrer ici dans les détails sur les caractéristiques d&#8217;un gestionnaire de code source distribué, ses avantages, ou même sur une présentation détaillée sur Mercurial et son mode de fonctionnement (je vous laisse visiter <a href="http://www.selenic.com/mercurial/" hreflang="en">le site officiel</a> pour cela). Je vais uniquement présenter des détails sur la configuration que j&#8217;ai mise en place afin de parvenir à partager aisément des dépôts différents via http et https, ainsi que comment faire pour autoriser la mise à jour de ceux-ci via https.</p> <p><br /></p> <h2>présentation des dépôts Mercurial sur le web</h2> <p><br /> Imaginons que vos dépôts soient stockés dans <code>/mercurial</code> et que vous vouliez pouvoir donner un accès en lecture sur le web, à l&#8217;adresse <code>http://hg.domain.tld</code>. Nous allons configurer Apache et Mercurial pour ce faire.</p> <p>Tout d&#8217;abord, nous allons créer un emplacement pour les fichiers web et le script <a href="http://fr.wikipedia.org/wiki/Common_Gateway_Interface" hreflang="fr">CGI</a> en <a href="http://python.org">Python</a> pour Mercurial. Prenons <code>/var/www/domain.tld/hg</code>. Créons à cet endroit un répertoire <code>hgweb</code> dans lequel nous allons placer le script <code>hgwebdir.cgi</code> que vous pouvez trouver sur votre système (sous Debian <code>/usr/share/doc/mercurial/examples/hgwebdir.cgi</code>) ou <a href="http://www.selenic.com/repo/hg-stable/raw-file/tip/hgwebdir.cgi">sur le site de Mercurial</a>. Éditez ce fichier pour avoir à la fin&#160;:</p> <pre> def make_web_app(): return hgwebdir(&quot;/etc/mercurial/hgweb.config&quot;)</pre> <p>Nous allons ensuite créer le fichier <code>/etc/mercurial/hgweb.config</code> avec un contenu comme suit&#160;:</p> <pre> [paths] depot1 = /mercurial/depot1 depot2 = /mercurial/depot2</pre> <p><em>On peut également utiliser la directive <code>[collections]</code> pour partager une hiérarchie de dépôts mais nous partons du principe que l&#8217;on veut contrôler précisément ce que l&#8217;on partage.</em></p> <p>À partir de là, il nous reste à configurer Apache. Créez un fichier <code>/etc/apache2/sites-available/hg.domain.tld</code> afin de déclarez votre <a href="http://httpd.apache.org/docs/2.0/vhosts/" hreflang="en">VirtualHost</a>. Voici en exemple mon fichier</p> <pre> NameVirtualHost *:80 &lt;VirtualHost *:80&gt; ServerAdmin webmaster@hg.domain.tld ServerName hg.domain.tld DocumentRoot /var/www/domain.tld/hg &lt;Directory /var/www/domain.tld/hg/&gt; Options FollowSymLinks +ExecCGI AddHandler cgi-script .cgi DirectoryIndex hgweb/hgwebdir.cgi AllowOverride None RewriteEngine on RewriteBase /hgweb RewriteRule ^$ hgwebdir.cgi [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteRule (.*) hgwebdir.cgi/$1 [QSA,L] &lt;/Directory&gt; LogLevel warn ErrorLog /var/log/apache2/hg.domain.tld-error.log . CustomLog /var/log/apache2/hg.domain.tld-access.log combined ServerSignature Off &lt;/VirtualHost&gt;</pre> <p>Notez ici les <a href="http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html" hreflang="en">règles de réécriture</a> qui permettent de faire disparaître <code>hgwebdir.cgi</code> de vos <a href="http://fr.wikipedia.org/wiki/Url" hreflang="fr">URL</a>. C&#8217;est ce qui m&#8217;a pris le plus de temps à trouver avant de tomber sur <a href="http://wiki.unelectronlibre.info/developpement/migrer_de_subversion_a_mercurial#utilisation_de_hgwebdir.cgi" hreflang="fr">cette doc</a>.</p> <p>Activez votre nouveau &#8220;site&#8221; ainsi que les modules Apache dont vous aurez besoin</p> <pre> # a2ensite hg.domain.tld # a2enmod mod_python # a2enmod rewrite # /etc/init.d/apache2 restart</pre> <p>Vous pouvez maintenant aller voir http://hg.domain.tld et parcourir vos dépôts Mercurial <code>depot1</code> et <code>depot2</code></p> <p><br /></p> <h2>partager des dépôts différents par https</h2> <p><br /> Tout ça, c&#8217;est très bien, mais on aimerait également pouvoir avoir accès à certains dépôts que l&#8217;on ne veut pas donner en lecture à tout internet&#160;: des dépôts privés en somme. Nous allons donc partager ceux-ci sur un site sécurisé avec authentification.</p> <p>Nous allons donc retourner dans notre répertoire <code>/var/www/domain.tld/hg/hgweb</code>, copier <code>hgwebdir.cgi</code> en <code>hgwebdirssl.cgi</code> et éditez ce fichier pour avoir&#160;:</p> <pre> def make_web_app(): return hgwebdir(&quot;/etc/mercurial/hgwebssl.config&quot;)</pre> <p>Créons le fichier <code>/etc/mercurial/hgwebssl.config</code> avec&#160;:</p> <pre> [paths] depot1 = /mercurial/depot1 depot2 = /mercurial/depot2 depot_prive3 = /mercurial/depot3 depot_prive4 = /mercurial/depot4</pre> <p>Et retournons éditer <code>/etc/apache2/sites-available/hg.domain.tld</code> pour y ajouter&#160;:</p> <pre> # en haut du fichier NameVirtualHost *:443 # à la fin du fichier &lt;VirtualHost *:443&gt; ServerAdmin webmaster@hg.domain.tld ServerName hg.domain.tld DocumentRoot /var/www/domain.tld/hg SSLEngine on SSLCertificateFile /etc/apache2/ssl/hg.domain.tld.pem SSLCertificateKeyFile /etc/apache2/ssl/hg.domain.tld.key &lt;Directory /var/www/domain.tld/hg/&gt; Options FollowSymLinks +ExecCGI AddHandler cgi-script .cgi DirectoryIndex hgweb/hgwebdirssl.cgi AllowOverride None RewriteEngine on RewriteBase /hgweb RewriteRule ^$ hgwebdirssl.cgi [L] RewriteCond %{REQUEST_FILENAME} !-f RewriteRule (.*) hgwebdirssl.cgi/$1 [QSA,L] AuthUserFile /etc/mercurial/hgweb.htpasswd AuthGroupFile /dev/null AuthName &quot;hg.domain.tld Mercurial repository&quot; AuthType Basic # &lt;LimitExcept GET OPTIONS&gt; Require valid-user # &lt;/LimitExcept&gt; &lt;/Directory&gt; ErrorLog /var/log/apache2/hg.domain.tld-error.log LogLevel warn CustomLog /var/log/apache2/hg.domain.tld-access.log combined ServerSignature Off &lt;/VirtualHost&gt;</pre> <p>On retrouve ici les directives rewrite que nous avons utilisé dans la première partie, à la différence qu&#8217;elles pointent vers le CGI spécifique à notre configuration SSL, et quelques directives spécifiques justement à https/SSL. Bien évidemment, il vous faudra créer un fichier de mots de passe (nommé ici hgweb.htpasswd) avec&#160;:</p> <pre> # htpasswd -c /etc/mercurial/hgweb.htpasswd hguser</pre> <p>pour autoriser l&#8217;utilisateur hguser.</p> <p>Notez les directives &lt;LimitExcept GET OPTIONS&gt; qui, si elles sont décommentées, permettent à tout le monde de visualiser les dépôts tout en demandant une authentification pour interagir via les commandes de Mercurial (clone, pull, push&#8230;).</p> <p>Rechargez la configuration d&#8217;Apache et regardez la différence entre http://hg.domain.tld et https://hg.domain.tld</p> <p><br /></p> <h2>autoriser la mise à jour des dépôts via https</h2> <p><br /> Pour autoriser le push via https, une fois le travail ci-dessus effectué, il suffit d&#8217;éditer la configuration des dépôts en modifiant le fichier <code>.hg/hgrc</code> de chaque projet. Éditez-le ou créez-le et insérez les lignes suivantes&#160;:</p> <pre> [web] allow_push = hguser</pre> <p><br /></p> <h2>utilisation</h2> <p><br /> Voilà c&#8217;est fait, vous pouvez maintenant interagir très simplement avec vos dépôts Mercurial. Pour cloner un dépôt public</p> <pre> $ hg clone http://hg.domain.tld/depot1</pre> <p>Pour cloner un dépôt privé</p> <pre> $ hg clone https://hguser@hg.domain.tld/depot_prive3</pre> <p>Pour mettre à jour (push) ce même dépôt</p> <pre> $ hg push https://hguser@hg.domain.tld/depot_prive3</pre> <p>J&#8217;ajouterais que le plus dur est de gérer finement les droits d&#8217;accès aux divers fichiers nécessaires au bon fonctionnement de cette configuration. Pour faire simple, comme d&#8217;habitude, laissez le minimum de droits sur les fichiers de configuration (appartenance à l&#8217;utilisateur <code>www-data</code> et lecture pour lui), et pour les dépôts j&#8217;ai choisi la solution un groupe <code>devel</code> pour mon utilisateur principal ainsi que <code>www-data</code> et les droits pour ce groupe.</p> <p><strong>mise à jour</strong>&#160;: il semble nécessaire (depuis les dernières mises à jour de Mercurial&#160;?) d&#8217;ajouter une ligne <code>baseurl =</code> dans la section <code>[web]</code> des fichiers <code>.hg/hgrc</code> afin de cacher les hgweb(dir).cgi dans les url.</p> <div class="footnotes"><h4>Notes</h4> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2008/01/21/Mercurial-%3A-partage-de-depots-differents-en-http-et-https-push-par-https#rev-pnote-244-1" id="pnote-244-1">1</a>] définition <a href="http://fr.wikipedia.org/wiki/Syst%C3%A8me_de_gestion_de_versions" hreflang="fr">en français</a> et <a href="http://en.wikipedia.org/wiki/Source_Code_Management" hreflang="en">en anglais</a></p> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2008/01/21/Mercurial-%3A-partage-de-depots-differents-en-http-et-https-push-par-https#rev-pnote-244-2" id="pnote-244-2">2</a>] cf <a href="http://blog.sietch-tabr.com/index.php/post/2006/09/24/172-developpement-python" hreflang="fr">cet article</a></p> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2008/01/21/Mercurial-%3A-partage-de-depots-differents-en-http-et-https-push-par-https#rev-pnote-244-3" id="pnote-244-3">3</a>] je sais que je n&#8217;ai pas parlé de cette nouvelle offre très intéressante de <a href="http://gandi.net">Gandi</a> le registrar bien connu (pour sa qualité de service, son support et son <a href="http://www.gandi.net/soutient/" hreflang="fr">engagement</a>) alors allez voir <a href="http://www.lebardegandi.net/post/2008/01/02/Beta-publique-de-Gandi-Hebergement-ouverte" hreflang="fr">le bar de gandi</a></p></div> utiliser apt-cacher pour les mises à jour de vos machines Debian urn:md5:8d7dbb166fc338c93d16e93fc771e8ca 2008-01-09T11:41:00+01:00 2008-01-09T11:47:13+01:00 eric InformatiqueEtGeekeries Debianligne de commande <p>Que ce soit à la maison ou au travail, quand on commence à avoir beaucoup de machines Debian, se pose le problème de leurs mises à jour et surtout de la bande passante utilisée pour celles-ci.</p> <p>Au niveau Debian, plusieurs solutions existent, comme utiliser un proxy web classique (<a href="http://fr.wikipedia.org/wiki/Squid" hreflang="fr">Squid</a> par exemple), partager le répertoire contenant les fichiers téléchargés (ce qui peut être un peu risqué), répliquer complètement l'arborescence Debian (mais ça demande à télécharger des paquets dont probablement on ne se servira jamais) ou bien encore utiliser un outil dédié à ce problème.<br /> Plusieurs existent&nbsp;: <a href="http://apt-proxy.sourceforge.net/" hreflang="en">apt-proxy</a>, <a href="http://packages.debian.org/approx" hreflang="en">approx</a>, <a href="http://packages.debian.org/apt-cacher" hreflang="en">apt-cacher</a>.</p> <p>Il semble que de ces différentes solutions aucune ne ressorte vraiment, et que des problèmes existent sur chacune d'elle. En tout cas, ici, je vais vous présenter apt-cacher.</p> <h2>côté serveur</h2> <p>L'installation est, comme d'habitude, facile sous Debian, il vous suffit de faire un</p> <pre> # aptitude install apt-cacher </pre> <p>Ensuite, un petit tour du côté de <code>/etc/apt-cacher/apt-cacher.conf</code> nous permet de configurer l'application, notamment le port d'écoute, le répertoire cache, les hôtes autorisés. En général, les paramètres par défaut sont corrects, je n'ai eu besoin de modifier que les paramètres pour utiliser un proxy&nbsp;:</p> <pre> http_proxy=votreproxy.lan.domain.tld:8080 use_proxy=1 </pre> <p>Ensuite, pour permettre le lancement automatique de l'application, il faut modifier <code>/etc/default/apt-cacher</code></p> <pre> AUTOSTART=1 </pre> <p>Il suffit ensuite de vérifier que vos paramètres dans <code>/etc/apt/sources.list</code> sont bons. Il faut par exemple que toutes les distributions pouvant être utilisées par les clients soient listées.</p> <h2>côté client</h2> <p>Il suffit, sur chaque client amené à utiliser le proxy apt, de modifier <code>/etc/apt/sources.list</code> pour avoir au lieu de</p> <pre> deb http://ftp2.fr.debian.org/debian/ etch main deb http://security.debian.org/ etch/updates main </pre> <p>quelque chose comme ceci pour pointer vers votre serveur apt-cacher et son port associé (3142 par défaut)</p> <pre> deb http://apt-cacher:3142/ftp2.fr.debian.org/debian/ etch main deb http://apt-cacher:3142/security.debian.org/ etch/updates main </pre> <p>Remarquez que l'on indique à apt-cacher le <a href="http://www.debian.org/mirror/official.fr.html" hreflang="fr">miroir Debian</a> que l'on veut utiliser, donc, afin de profiter vraiment de apt-cacher, il faut veiller à ce que tous les clients utilisent le même.<sup>[<a href="http://x.sietch-tabr.com/index.php/post/2008/01/04/utiliser-apt-cacher-pour-les-mises-a-jour-de-vos-machines-Debian#pnote-242-1" id="rev-pnote-242-1">1</a>]</sup></p> <p>Pour faire la modification facilement, je vous propose une petite ligne de commande sed&nbsp;:</p> <pre> # sed -i -e 's!http://!http://apt-proxy:3142/!g' /etc/apt/sources.list </pre> <p>Pensez à vérifier que les paramètres de proxy apt sont bons, c'est-à-dire en général qu'il n'y a pas de proxy indiqué dans <code>/etc/apt/apt.conf</code> ou dans le répertoire <code>/etc/apt/apt.conf.d/</code><sup>[<a href="http://x.sietch-tabr.com/index.php/post/2008/01/04/utiliser-apt-cacher-pour-les-mises-a-jour-de-vos-machines-Debian#pnote-242-2" id="rev-pnote-242-2">2</a>]</sup>.</p> <div class="footnotes"><h4>Notes</h4> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2008/01/04/utiliser-apt-cacher-pour-les-mises-a-jour-de-vos-machines-Debian#rev-pnote-242-1" id="pnote-242-1">1</a>] Si vraiment, vous avez beaucoup de clients, et que faire la modification sur chacun devient très compliqué, je vous encourage alors à aller voir du côté de <a href="http://puppet.reductivelabs.com/" hreflang="en">Puppet</a> ou <a href="http://www.cfengine.org/" hreflang="en">Cfengine</a>. Vous pouvez trouver des informations complémentaires sur <a href="http://debian-administration.org" hreflang="en">debian-administration</a>.</p> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2008/01/04/utiliser-apt-cacher-pour-les-mises-a-jour-de-vos-machines-Debian#rev-pnote-242-2" id="pnote-242-2">2</a>] par exemple, <a href="http://www.xen-tools.org/">xen-tools</a> écrit un fichier proxy-guess dans ce répertoire avec les paramètres proxy de l'hôte Xen dom0</p></div> enfin l'IPv6 chez Free urn:md5:ee996c38f63d01e559505d26f8164202 2007-12-14T15:44:00+01:00 2007-12-14T16:33:49+01:00 eric LeVasteInternet Freeréseau <p>Ça y est, cette fois-ci, il arrive... l'IPv6 chez ton <a href="http://fr.wikipedia.org/wiki/Fournisseur_d%27acc%C3%A8s_%C3%A0_Internet" hreflang="fr">FAI</a> préféré. Malheureusement, ce n'est pour l'instant que pour les abonnés en zones dégroupées, mais sachez que, après activation de l'option sur votre console, vous avez accès à <strong>2^64 soit 18 446 744 073 709 551 616 adresses IP</strong>... si ça c'est pas le bonheur :)</p> <p>Le <a href="http://www.iliad.fr/presse/2007/CP_IPv6_121207.pdf" hreflang="fr">communiqué de presse</a>.</p> <p>Vu sur <a href="http://www.01net.com/editorial/367000/free-ouvre-l-ip-de-nouvelle-generation-a-ses-abonnes-degroupes/" hreflang="fr">01.net</a> et <a href="http://ipv6pourtous.free.fr/actualites/#actu_25" hreflang="fr">ipv6 pour tous</a> bien sûr.</p> <p><strong>ps :</strong> il semblerait que la freebox offre également une fonction d'impression en réseau</p> Rockbox sur mon iAudio X5L urn:md5:92e4a1f9b55e9dc4f653c1d8b76168fe 2007-12-03T19:52:00+01:00 2007-12-03T23:03:07+01:00 eric InformatiqueEtGeekeries iAudiolast.fmlogiciels libresmatériel <p><a href="http://rockbox.org"><img src="http://x.sietch-tabr.com/public/divers/rockbox400.png" alt="Icône Rockbox" style="display:block; margin:0 auto;" /></a></p> <p>Suite à <a href="http://ubunteros.tuxfamily.org/spip.php?article127" hreflang="fr">ce billet</a> lu il y a quelques semaines sur <a href="http://www.planet-libre.org/" hreflang="fr">planete-libre.org</a>, je me suis souvenu que le passage à <a href="http://rockbox.org" hreflang="en">Rockbox</a> sur mon baladeur audio était un des mes projets, et que maintenant qu'<a href="http://blog.sietch-tabr.com/index.php/post/2006/07/17/146-cowon-iaudio-x5l-30go" hreflang="fr">il a plus d'un an</a> c'est le moment idéal pour le faire<sup>[<a href="http://x.sietch-tabr.com/index.php/post/2007/11/26/Rockbox-sur-mon-iAudio-X5L#pnote-240-1" id="rev-pnote-240-1">1</a>]</sup>.</p> <h2>Rockbox&nbsp;: kézako&nbsp;?</h2> <p>Rockbox c'est tout simplement un <a href="http://fr.wikipedia.org/wiki/Firmware" hreflang="fr">firmware/micrologiciel</a> libre pour baladeurs numériques. Initialement créé pour les Archos, il est maintenant disponible pour une pléthore de modèles comme les iRiver, iPod, Toshiba, Sandisk et les Cowon donc. Plus d'informations sur la <a href="http://fr.wikipedia.org/wiki/Rockbox" hreflang="fr">page Wikipédia</a>.</p> <h2>Rockbox&nbsp;: pourquoi&nbsp;?</h2> <p>L'intérêt de remplacer le firmware d'origine de votre baladeur c'est principalement de lui ajouter de nouvelles fonctionnalités. En effet, pour la plupart des baladeurs, le firmware ajoute des fonctions qui ne sont pas disponibles par défaut. Pour plus d'informations sur les possibilités offertes par Rockbox, consultez <a href="http://www.rockbox.org/twiki/bin/view/Main/WhyRockbox" hreflang="en">cette page</a>.</p> <h2>Rockbox sur iAudio X5</h2> <p>Pour les Cowon iAudio X5, Rockbox est un peu moins intéressant que pour les autres baladeurs du type iPod, iRiver ou Archos, tout simplement parce que les Cowon sont déjà très bon et bon nombre de fonctionnalités sont disponibles avec le firmware d'origine. Néanmoins&nbsp;:</p> <ul> <li>le support de la navigation par tags<sup>[<a href="http://x.sietch-tabr.com/index.php/post/2007/11/26/Rockbox-sur-mon-iAudio-X5L#pnote-240-2" id="rev-pnote-240-2">2</a>]</sup></li> <li>le support de last.fm</li> <li>le support du 'gapless' ou 'fade in/fade out'</li> <li>le support de jeux et applications</li> </ul> <p>sont disponibles sur votre Cowon après le passage à Rockbox.</p> <p>Par contre&nbsp;:</p> <ul> <li>l'USB OTG n'est pas encore supporté</li> <li>les effets audio propriétaires BBE non plus<sup>[<a href="http://x.sietch-tabr.com/index.php/post/2007/11/26/Rockbox-sur-mon-iAudio-X5L#pnote-240-3" id="rev-pnote-240-3">3</a>]</sup></li> <li>la gestion de la batterie est un peu moins efficace.</li> <li>pas de possibilité d'avoir les deux firmwares en parallèle. Si l'USB OTG par exemple vous manque, il faudra faire un choix parce que sur le Cowon iAudio X5, le firmware Rockbox n'est pas disponible en même temps que l'original, je sais que c'est possible sur certains baladeurs (je l'ai vu sur un iRiver H3xx) mais pas sur celui-ci.</li> <li>et si par hasard vous êtes maso, sachez que les <acronym>DRM</acronym> non plus !<sup>[<a href="http://x.sietch-tabr.com/index.php/post/2007/11/26/Rockbox-sur-mon-iAudio-X5L#pnote-240-4" id="rev-pnote-240-4">4</a>]</sup></li> <li>ah j'oubliais&nbsp;! et en plus il faut s'habituer à une nouvelle ergonomie...</li> </ul> <h2>Rockbox&nbsp;: installation</h2> <p>Bon, je passe sur l'installation qui est expliquée sur le billet que j'ai mentionné au début. Le seul truc c'est que je n'ai pas réussi à utiliser le logiciel facilitant l'installation et que je l'ai donc fait à la main, sans aucun problème. <a href="http://download.rockbox.org/manual/rockbox-iaudiox5/rockbox-buildch2.html#x4-60002" hreflang="en">cf</a>.</p> <h2>Rockbox&nbsp;: utilisation</h2> <p>Pas grand-chose à dire pour l'utilisation, il faut bien évidemment s'habituer à la nouvelle ergonomie. L'ancienne n'était déjà pas évidente, donc un changement ça n'aide pas, mais au bout de quelques heures, c'est bon, et on commence à en comprendre la logique et à pouvoir en profiter. <em>(je précise que j'utilise principalement la télécommande, ce qui ne facilite sûrement pas la prise en main)</em></p> <h3>Support last.fm</h3> <p>Une des principales raisons que j'avais de passer à Rockbox, c'était que j'en avais marre de ne pas pouvoir alimenter <a href="http://last.fm/user/gal33Za">mes statistiques last.fm</a> lorsque j'écoute de la musique sur mon baladeur... ce qui arrive souvent. Maintenant, c'est bien fini :)</p> <p>Pour ce faire, il faut préciser dans les paramètres Rockbox qu'on veut le "log last.fm", c'est dans le menu Réglages - Réglages généraux - Lecture - Log Last.fm. Rockbox crée alors un fichier à la racine de votre baladeur avec les informations nécessaires pour pouvoir "scrobbler". Il suffit alors d'utiliser un des <a href="http://www.rockbox.org/twiki/bin/view/Main/LastFMLog" hreflang="en">logiciels suivants</a> pour pouvoir envoyer ces stats au site last.fm. Personnellement, j'utilise QTScrobbler.</p> <h3>Manuel</h3> <p>Pour vraiment tirer partie de votre "nouveau" iAudio, je vous conseille de lire le <a href="http://www.rockbox.org/manual.shtml" hreflang="en">manuel Rockbox</a>.</p> <p><em>ici aurais dû se trouver des photos de mon iAudio tournant sous Rockbox, mais leur qualité moyenne m'empêche de les mettre... je réessaierais peut-être d'en refaire...</em></p> <div class="footnotes"><h4>Notes</h4> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2007/11/26/Rockbox-sur-mon-iAudio-X5L#rev-pnote-240-1" id="pnote-240-1">1</a>] garantie finie, mais produit encore fonctionnel</p> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2007/11/26/Rockbox-sur-mon-iAudio-X5L#rev-pnote-240-2" id="pnote-240-2">2</a>] en effet, le Cowon ne le supporte pas, et à l'achat ce "sacrifice" me convenait, vu les possibilités offertes par ce modèle. Maintenant que c'est possible... j'apprécie :)</p> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2007/11/26/Rockbox-sur-mon-iAudio-X5L#rev-pnote-240-3" id="pnote-240-3">3</a>] logique puisque propriétaires !</p> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2007/11/26/Rockbox-sur-mon-iAudio-X5L#rev-pnote-240-4" id="pnote-240-4">4</a>] et c'est voulu par les développeurs</p></div> graphe last.fm urn:md5:1d01a715471ae11d505deb83efa334fc 2007-10-29T17:51:00+01:00 2007-10-29T22:50:22+01:00 eric InformatiqueEtGeekeries last.fmPython <p><a href="http://x.sietch-tabr.com/public/divers/graph_last.fm_complet.png"><img src="http://x.sietch-tabr.com/public/divers/graphe_last.fm_extrait.png" alt="Graphe last.fm (extrait) " /></a><br /> <em>cliquez pour voir le graphe complet</em></p> <p>Ceci est un extrait d'un graphe représentant mon <a href="http://www.last.fm/user/gal33Za/">profil last.fm</a>. Si vous voulez créer le vôtre, allez voir du côté de <a href="http://lastgraph.aeracode.org/" hreflang="en">LastGraph</a> (<a href="http://www.aeracode.org/2007/10/15/lastgraph-now-available/" hreflang="en">enfin de retour</a>). L'idée de cette application est venue à Andrew Godwin après avoir découvert les <a href="http://www.megamu.com/lastfm/" hreflang="en">graphes last.fm de Lee Byron</a>. N'ayant pas accès au code permettant de les générer<sup>[<a href="http://x.sietch-tabr.com/index.php/post/2007/10/29/graphe-lastfm#pnote-239-1" id="rev-pnote-239-1">1</a>]</sup>, il s'est mis au travail. Résultat, une bibliothèque <a href="http://www.aeracode.org/projects/graphication" hreflang="en">Graphication</a> mélangeant <a href="http://python.org/" hreflang="en">Python</a> et <a href="http://cairographics.org/" hreflang="en">Cairo</a> lui permettant de générer du PDF ou du <a href="http://fr.wikipedia.org/wiki/SVG" hreflang="fr">SVG</a>.</p> <p>Concernant Python et la visualisation d'informations, j'ai également découvert un blog appelé <a href="http://www.visophyte.org/blog/" hreflang="fr">visophyte</a>.</p> <div class="footnotes"><h4>Notes</h4> <p>[<a href="http://x.sietch-tabr.com/index.php/post/2007/10/29/graphe-lastfm#rev-pnote-239-1" id="pnote-239-1">1</a>] il semble que le code permettant de générer les autres exemples soit lui disponible. C'est du <a href="http://processing.org/" hreflang="fr">Processing</a> un langage que je découvre (plus d'infos sur <a href="http://en.wikipedia.org/wiki/Processing_%28programming_language%29" hreflang="en">Wikipedia</a>)</p></div>