PROJET AUTOBLOG


bfontaine.net

Site original : bfontaine.net

⇐ retour index

Synchroniser deux dépôts Git en local

mardi 10 avril 2012 à 21:46

Il peut parfois être utile de synchroniser des dépôts Git sur diverses machines en local. Par exemple, si l'on possède un compte Github, et que l'une des machines n'a pas accès à Internet (c'est mon cas temporairement), on peut se synchroniser avec Github avec la machine connectée, puis synchroniser celle-ci avec la machine hors ligne.

Imaginons que l'on ai un ordinateur portable ayant accès à internet, accessible en local à l'adresse IP 192.168.1.20, et un ordinateur fixe accessible en local à l'adresse IP 192.168.1.21. Chacun de ces ordinateurs a un dépôt Git situé en /home/toto/MonProgTropBien/. Il suffit d'utiliser git remote add, pour ajouter un dépôt distant sur chacun des ordinateurs.

La syntaxe est la suivante (il faut être dans le répertoire du dépôt Git) :

git remote add <nom-remote> <nom-utilisateur>@<adresse>:<chemin-distant>

On tape donc la commande suivante sur le fixe :

git remote add laptop toto@192.168.1.20:/home/toto/MonProgTropBien

La commande est similaire sur le portable :

git remote add fixe toto@192.168.1.21:/home/toto/MonProgTropBien

Ensuite, il suffit de se placer dans le répertoire du dépôt Git, et d'utiliser git push fixe sur le portable (ou git pull laptop sur le fixe) pour envoyer les modifications sur le fixe, et git pull fixe pour l'inverse (git push laptop depuis le fixe). La commande git add remote ne doit être exécutée que la première fois.

Cette manipulation permet également de synchroniser deux dépôts sur la même machine (utilisez la syntaxe git remote add <nom> <chemin-de-l'autre-dépôt>).

Connectez-vous facilement à up7d

jeudi 8 mars 2012 à 21:37

À l’université Paris 7, plus précisément sur le campus PRG, est accessible une borne WiFi dont le SSID est up7d. Pour s'y connecter, il faut avoir un compte ENT (seuls les étudiants et le personnel en ont un). Le principe de connexion est le même que pour une borne Free WiFi ou Neuf WiFi : il n'y a pas de clef WEP ou WPA(2), et on tombe sur une page de connexion lorsqu'on y accède, nous demandant login et mot de passe. Il est assez fastidieux de devoir entrer son login et mot de passe à chaque fois, surtout que la page est parfois lente à charger, et nécessite de façon totalement injustifiée l'activation du Javascript.

Pour résoudre ce problème, j'ai développé un petit script en Ruby, up7connect.rb, qui automatise l'authentification. Lors du premier lancement, il demande le login et mot de passe à utiliser. Ces informations sont chiffrées et stockées localement. Ensuite, il suffit de le lancer pour qu'il vous authentifie. Si tout est bon, il affiche Connection ok. Si l'on est déjà connecté, il le précise. À noter qu'il faut avoir déjà sélectionné le réseau sur son ordinateur, le script vérifie d'ailleurs si une borne dont le SSID est up7d est bien accessible. Il nécessite Ruby 1.9 pour fonctionner, et s'utilise en ligne de commande (option --help ou -h pour l'aide.)

Au niveau technique, pour la connexion j'utilise le module net/http pour envoyer les données du formulaire originel à la bonne page. La page est en HTTPS, mais le certificat SSL n'est pas valide, donc je ne le vérifie pas (OpenSSL::SSL::VERIFY_NONE). J'utilise un user-agent personnalisé, et j'envoi un header Referer avec l'adresse de la page de login originelle, sinon je suis bloqué par une erreur 405 (méthode de requête non autorisée). Pour le stockage du login et du mot de passe, mon algorithme est le suivant:

j1 = caractère au hasard dans [a-z]
j2 = caractère au hasard dans [a-z]
s = login + <chaîne arbitraire> + mot de passe
l1 = liste des codes des caractères de s
s1 = éléments de l1 joints avec j1
l2 = liste des codes des caractères de s1
s2 = éléments de l2 joints avec j2
pour chaque chiffre e dans s2 faire:
e = caractère correspondant à 33+e
fin pour
écrire s2 dans un ~/.up7connect.conf

Le déchiffrage se résume à faire la manipulation inverse à coup de split avec des expressions régulières.

Le script fonctionne sur les machines sous UNIX-like. Pour Windows, je n'ai pas pu tester, n'ayant pas d'environnement adéquat (i.e. un laptop sous Windows). Par défaut, le fichier chiffré contenant login et mot de passe est situé dans le répertoire courant sous Windows, et dans le répertoire de login pour les autres. Pour Linux, j'utilise la commande iwlist wlan0 scan last pour voir si le SSID up7d est visible, tandis que je ne fais pas cette vérification sous Windows/Mac/FreeBSD (je ne connais pas la commande correspondante pour Windows/Mac, c'est à venir pour FreeBSD).

Le script est actuellement en version 0.1, sous licence libre GPL v3, et disponible ici. Il fonctionne bien, malgré parfois une petite difficulté à déterminer si le SSID up7d est visible (il arrive rarement qu'il le considère comme invisible alors qu'il est bien là).

MàJ du 11 mars 2012 : Le script n'essaye plus d'utiliser la commande iwlist sous Mac, car elle n'y est pas disponible. Cette version est la 0.1b et remplace la précédente.

Le faux problème du TDD brainless

jeudi 23 février 2012 à 23:42

Ce billet est issu d'une réflexion suite à la discussion de la dernière rencontre Software Craftmanship sur le TDD brainless, i.e. le TDD où l'on ne fait qu'implémenter bêtement le strict minimum nécessaire pour valider chaque test, sous une forme suivante :

def tests(): # mes tests
assertEqual(my_func(entree1), sortie1)
assertEqual(my_func(entree2), sortie2)
assertEqual(my_func(entree3), sortie3)

def my_func(entree): # ma fonction
if (entree == entree1):
return sortie1
if (entree == entree2):
return sortie2
if (entree == entree3):
return sortie3

Le principe du TDD est bien d'implémenter le strict minimum pour passer les tests, mais de façon paresseuse, pas de façon bête. Autrement dit, on implémente de la façon qui nous demande le moins de travail possible. Le TDD brainless ne permet pas cela, puisque, avec la méthode donnée ci-dessus en exemple, on devra implémenter N conditions if pour chacun des N tests, ce qui est relativement rapide quand on a très peu de tests à faire, mais bien plus fastidieux quand on en a beaucoup. Justement, le TDD paresseux consiste à se dire que puisque la méthode est fastidieuse, il faut chercher un moyen de passer les tests de façon plus économique, ce qui entraîne nécessairement une réflexion (d'où la différence avec le TDD brainless) sur un algorithme permettant d'implémenter la fonction testée sans trop d'efforts. À la fin, on arrive donc à une fonction implémentée de façon minimale qui passe les tests, et qui (si les tests ont été bien choisis) fonctionne, contrairement à une fonction implémentée avec du TDD brainless, qui ne fonctionne que pour les cas de tests.

Tout ça pour dire que non, pousser la logique du TDD jusqu'au bout ne donne pas nécessairement du TDD brainless.

Nouveau site de Paris 7 : Peut mieux faire

lundi 13 février 2012 à 23:54

L'Université Paris 7 (Diderot) a récemment refait l'apparence son site. L'annonce dit : Nouvelle ergonomie, nouveau graphisme, pour un site web plus lisible, plus facile d'utilisation, plus dynamique aussi. Si l'ergonomie du site n'est effectivement pas trop mauvaise, même si on se perd un peu dans l'arborescence des pages (Essayez, à partir de la page d'accueil, d'arriver sur la rubrique consacrée aux stages, sans passer par la recherche. Bonne chance); par contre au niveau de l'accessibilité et des performances, c'est la catastrophe. Bien que le site prétend se mettre progressivement en conformité avec le RGAA depuis au moins 2008, on remarque que le champ de recherche ne comporte pas de libellé (<label>), et ce depuis au moins 2006, époque à laquelle la page du service de communication était ornée d'un beau gif animé. Le site n'est évidemment pas valide WCAG niveau A (pas d'attribut alt sur des images qui servent de lien), et on notera que certaines parties du site ont une taille de police spécifiée en pixels, donc la taille ne bouge pas si on augmente la taille de la police via le menu du navigateur.

Sans surprise, le site n'est pas valide HTML, avec 196 erreurs, dont la majorité est en fait issue du fait que certaines URL contiennent le caractère "&" au lieu de "&amp;", son code HTML.

Performances générales

Au niveau des performances, WebPageTest nous indique que le start render est à 2 secondes et le temps de chargement total est de 7.2 secondes la première fois (respectivement 0.8 secondes et 2.8 secondes la seconde, avec le cache), pour un total de 54 requêtes. On note que lorsqu'on tente d'accéder à l'URL principale du site, i.e. www.univ-paris-diderot.fr, on est redirigé deux fois avant d'arriver sur la véritable page d'accueil. La connexion est Keep-Alive (bon point), mais aucune ressource (même les scripts, le HTML ou les feuilles de style) n'est compressée.

Styles

La page comporte 7 feuilles de styles, dont deux quasiment identiques (site.css et sitefr.css), non minifiées, et surtout pas adaptées aux terminaux mobiles (pas de @media-query, et des éléments de 900px de largeur), la fenêtre devient d'ailleurs trop petite en dessous de 930 points. Cinq d'entre elles sont chargés classiquement dans le <head>, et deux autres dans le corps de la page (non valide).

Scripts

La page comporte 9 scripts externes, dont un qui n'existe pas (Erreur 404. Si on tente de l'ouvrir dans le navigateur, on tombe sur une page 404 personnalisé, qui tente d'afficher des images qui… n'existent pas), et jQuery version 1.6 (importé deux fois !) minifié (c'est le seul script qui l'est), ainsi qu'une version 1.7. À l'exception de quelques uns chargés dans le <body>, tous les scripts sont chargés en tout premier dans le <head>, alors que ceux-ci ne sont pas tous nécessaires au rendu de la page. On trouve par ailleurs un script inline téléchargé sur EasyScript, qui n'est jamais utilisé (il permet d'ouvrir une pop-up pour afficher une image), et du Google Analytics en bas de page.

Parmi ces scripts chargés en tout début de page, en dehors des deux nécessaires au diaporama, aucun n'est nécessaire au rendu, donc ce placement est tout sauf justifié. Dans les détails, parmi les scripts existants, on trouve d'abord jQuery, puis un script (copié sans citer la source) permettant de faire des diaporamas, puis un script utilisant le précédent qui démarre un diaporama une fois la page entièrement chargée, et enfin un dernier qui n'a pas d'autre fonction que d'ajouter/enlever une classe aux éléments du menu survolés avec la souris (:hover ? Connaît pas). Notons qu'en dehors de jQuery, aucun de ces scripts n'est optimisé (les sélecteurs jQuery sont utilisés à outrance sans utiliser de mise en cache, on trouve de multiples mot-clefs var, et la taille d'un tableau n'est pas mise en cache lors du parcours de celui-ci avec une boucle for), et on trouve des incohérences, par exemple le script menu.js modifie à l'arrache les classes d'un élément (via une expression régulière), alors que jQuery, inclut un peu au dessus, propose les méthodes .addClass() et .removeClass().

Images

Les images ne sont pas servies via un (sous-)domaine différent, donc chaque requête envoit les cookies pour rien. Les dimensions réelles de l'image ne sont pas indiquées dans le code HTML, ce qui aurait pu éviter un temps de calcul supplémentaire de la part du navigateur.

Proposition

Inspiré par le concours WebPerf Contest qui a eu lieu en 2010 et dont l'objet était l'optimisation d'une page du site de la Fnac, et suite aux multiples observations faites dans toute la partie supérieure du billet, je me suis lancé un petit défi dont le but est d'optimiser la page d'accueil du nouveau site de Paris 7, en appliquant toutes les bonnes pratiques possibles de façon à ce que le site soit plus performant. Les règles sont les mêmes que pour le concours, l'apparence du site doit rester la même, toutes les fonctionnalités actuelles doivent rester disponibles (en l'occurrence, c'est plus simple, il n'y a aucune requête AJAX); Le but principal étant d'optimiser la page, mais tout ce qui peut améliorer l'expérience utilisateur (notamment au niveau de l'accessibilité) est un plus, du moment que ça ne prend pas des jours à réaliser.

1- Récupérer la page d'accueil ainsi que tous les composants externes

J'ai simplement utilisé Chromium pour enregistrer la page avec ses dépendances (images, fichiers CSS et Javascript). Certains fichiers n'ont pas été téléchargés (les fichiers chargés via des scripts, notamment), pour ceux-ci il a fallu les chercher à la main en regardant les erreurs 404 générées.

2- S'organiser

J'ai créé un dépôt sur Github pour tout versionner afin d'organiser le travail, et de garder un historique des optimisations. J'ai également créé deux sous-domaines (static1 et static2) sur mon site en vue de les utiliser pour les médias (en l'occurrence, uniquement des images).

3- Nettoyer

Il a d'abord fallu nettoyer un peu tous ces fichiers, les feuilles CSS étaient par exemple remplies de commentaires. Ainsi, (presque) tous les commentaires des scripts, du fichier HTML principal et des feuilles CSS ont été supprimés. Certains scripts étaient inutiles et ont été retirés, tandis que les autres ont été fusionnés de telle façon qu'il n'en reste que 2 (sur les 7 originaux). Du côté CSS, on est passé de 7 feuilles de style à 5.

4- Préserver la compatibilité

La page originelle affiche une image aléatoire (parmi 5) en haut, ceci a été préservé, ainsi que la présence d'un cookie (j'ai simplement utilisé un cookie de session).

5- Réduire

La majeure partie de l'optimisation consiste à réduire, d'une part, le poids total des éléments de la page, et de l'autre le nombre de requêtes. J'ai optimisé les images PNG avec les outils optipng et pngcrush, tandis que pour les JPG, j'ai utilisé jpegtran. Ces outils sont lossless, i.e. il n'y a pas de pertes de données. J'ai également utilisé l'option -quality de l'outil convert (qui fait partie d'imagemagick) pour réduire (avec perte de qualité) certaines images qui n'avaient pas besoin d'être de grande qualité (les icônes, notamment). Les images statiques (i.e. qui sont toujours sur la page d'accueil, quel que soit son contenu) comme les drapeaux ou les logos Facebook/Twitter ont étées combinées en une seule, en utilisant la technique des CSS Sprites. Les feuilles CSS et les scripts ont été optimisés puis minifiés.

6- Mettre en cache

Les images ont été réparties sur trois sous-domaines différents; les PNG sur static1.bfontaine.net/up7opt/, les JPG et GIF sur static2.bfontaine.net/up7opt/, tandis que la favicon et les images de fond du diaporama ont été gardées sur le sous-domaine principal. Ceci permet de paralléliser les chargements et de supprimer les cookies dans les requêtes d'images (sur les autres sous-domaines). les feuilles CSS et les scripts sont également mis en cache.

Résultat

Le résultat est visible ici. D'après WebPageTest, qui donne un score honorable de 95/100 (le site original n'a que 40/100), on a un temps de chargement de 3.6 secondes la première fois (2.2 secondes la deuxième), soit près de la moitié du temps original. Le start render est à 0.8 secondes (2.4 fois plus rapide). Il y a près de 100 éléments DOM en moins, le premier chargement demande 30 requêtes (-45%) et le second seulement 3 (-94%). Au niveau poids de la page, on passe de 1.2Mio à 534Kio (-65%) pour le premier chargement, et le second ne pèse que 302Kio (ceci est dû au fait qu'il faille charger une nouvelle image à chaque fois, l'image en haut de la page étant aléatoire).
Remarques
Je n'ai fait qu'optimiser le site au niveau des performances, je n'ai pas touché à l'accessibilité ni à la correction des erreurs dans le HTML, ni à l'adaptation au mobile, par manque de temps. Pour bien faire, il faudrait réécrire entièrement la page.

Cachez les publicités sur Facebook

lundi 30 janvier 2012 à 17:45

Facebook affiche des publicités dans une colonne sur la droite des pages. Pour les cacher, ajoutez la règle suivante à votre feuille CSS personnalisée :

div[data-ad] {visibility:hidden}

Pour Chromium, sous GNU/Linux, la feuille de style personnalisée, Custom.css, est située dans le répertoire suivant :

/home/baptiste/.config/chromium/Default/User StyleSheets/

Il est également possible de modifier la feuille de style utilisée par l'extension Adblocks, si vous l'utilisez.

Explication : On sélectionne tous les blocs <div> qui contiennent l'attribut data-ad. En effet, Facebook utilise cet attribut pour stocker des données relatives aux publicités. La spécification HTML5 permet d'utiliser des attributs personnalisés dans le code HTML, qui commencent par data-. Twitter utilise le même système avec data-user-id et data-tweet-id (entre autres). C'est très utile lorsqu'on manipule le DOM avec Javascript.