Tests vs tdd

Bonjour,

Aujourd’hui j’aimerais m’entretenir sur l’importance des tests unitaires, d’intégration, de charge et autres.

Il est très important d’effectuer des tests.

Faire du TDD ou écrire les tests après coup importe peu. Je pense que faire des tests sur les getters et setters devient un fardeau plus qu’une aide.

Certains setters ne sont pas triviaux, comme une conversion ou un casting. Dans ce cas, on peut avoir besoin d’en faire le test.

Ce qu’il ne faut pas faire, c’est réparer du code sans en écrire un test.

Une bonne pratique, plus que l’écriture des tests avant le code, a mon avis, est d’écrire un test avant la correction de bug.

C’est là que réside la puissance de l’automatisme. On remarque le comportement erroné du code. On écrit le test qui devrait passer au vert après correctif. Enfin, on écrit le patch qui fera passer le test.

Ainsi, on fait le plus important, des tests de non régression.

Soap en PHP

Bonjour,

J’ai un travail qui implique d’écrire un service soap en php. Et certaines taches de ce service sont aussi d’utiliser soap en tant que client vers un autre serveur.

J’ai fais un exemple minimal afin de me mettre sur les rails. C’est un exemple trouvé sur github que j’ai légèrement modifié

serveur.php

<?php
class MySoapServer{
 public function getMessage(){
 return 'Hello,World!';
 }
 public function addNumbers($num1, $num2){
 return $num1+$num2;
 }
}
$options = [
 'uri'=>'http://localhost/test'//je sais pas a quoi ca sert
];
$server = new SoapServer(NULL,$options);
$server->setClass('MySoapServer');
$server->handle();

client.php

<?php
$options = [
 'location' => 'http://localhost:8000',
 'uri' => 'http://localhost/everth',//je sais pas ce que c'est
 'exceptions' => false
];
$client = new SoapClient(NULL,$options);
echo $client->getMessage();
echo $client->addNumbers(3,5);

Pour tester :

php -S 0.0.0.0:8000 serveur.php
php client.php

 

Régler php-fpm

Bonjour,

Il y a quelques années, j’utilisais suexec pour qu’apache gère php en tant qu’utilisateur local. Cette solution ne marche que sous certaines conditions, et cgi n’en fait pas partie.

Quand j’ai mis php en fast-cgi afin d’en utiliser plusieurs versions, la création et modification de fichiers via php est devenu problématique.

Il y a plusieurs solutions à ce problème.

  • chmod 777
  • mount bind avec changement d’utilisateur
  • acl

Celle que j’ai retenu, c’est de régler php-fpm pour changer l’uid et gid du site.

Par défaut c’est www-data qui exécute les pages du site. Pour changer celà, on va regarder pas a pas ce qu’il faut faire.

Il faut avoir php en fast-cgi.

Je donne ci-dessous un exemple pour php7.2, mais on peut facilement adapter et faire fonctionner le truc pour php5 ou 7.0.

On arrete le service php-fpm

sudo /etc/init.d/php7.2-fpm stop

On règle certaines variables

site=www.symfony.loc
repertoire=/home/brahim/symfony/www
utilisateur=brahim
groupe=brahim
sudo /etc/init.d/php7.2-fpm stop
sudo cp /etc/php/7.2/fpm/pool.d/www.conf  /etc/php/7.2/fpm/pool.d/${site}.conf

On copie et modifie le fichier pool fpm

site=www.symfony.loc repertoire=/home/brahim/symfony/www utilisateur=brahim groupe=brahim sudo /etc/init.d/php7.2-fpm stop sudo cp /etc/php/7.2/fpm/pool.d/www.conf /etc/php/7.2/fpm/pool.d/${site}.conf

On modifie le fichier fraîchement copié

sudo nano -w /etc/php/7.2/fpm/pool.d/${site}.conf

Il faut remplacer :

  • [www] -> [www.symfony.loc]
  • user = www-data -> user = brahim
  • group = www-data -> group = brahim
  • listen = /run/php/php7.2-fpm.sock -> listen = /run/php/php7.2-www.symfony.loc.sock

Il faut adapter les changements en fonction du site.

Enfin, on lance le service

sudo /etc/init.d/php7.2-fpm start

Voilà, le site tournera sous les droits de brahim:brahim.

Plus besoin de changer via chmod ou autre.

Faire attention à ses données

Introduction

Bonjour,

Aujourd’hui, on va parler des données. On n’est jamais trop paranoïaque quand il s’agit de sauvegarder ses données.

Je ne vais pas imposer les mesures de certaines entreprises qui imposent 2 sauvegardes éloignées de plus de 50km dans des coffres forts pour se pré-munir aux catastrophes genre incendie, tremblement de terre, vol industriel ou vandalisme.

Ici, je vais juste donner des petites astuces faciles à mettre en oeuvre, qui sauveront la vie un jour ou l’autre.

Clé usb

Les clés usb ne sont pas faites pour stocker les données. Elles sont faites pour transporter les données.

Il faut toujours avoir une copie de tout le contenu de sa clé ailleurs.

On ne travaille jamais directement sur le fichier depuis la clé usb. On fait une copie sur le bureau (ou dans mes documents), on effectue son travail, et ensuite on écrase la copie sur clé usb. Si l’ordinateur n’est pas à nous, on peut supprimer le fichier et vider la corbeille.

Si on a son travail dessus afin d’utiliser différentes machines, on désigne un des ordinateurs en tant que « responsable » de ce fichier. Dès que possible, on copie le fichier sur l’ordinateur responsable. Il se peut que sur la même clé on ait des fichiers qui ont des responsables différents.

Si on perd la clé usb, on sait ou trouver la dernière version des fichiers, et on aura perdu 1 journée max de travail.

Disque dur externe

Ne jamais attacher un disque externe en permanence sur son ordinateur. Si on a besoin de place, on en fait, ou on achète un disque dur interne plus gros.

Il est possible d’avoir en permanence un disque externe dans son cartable, mais il faut pouvoir se dispenser d’avoir à le brancher systématiquement.

Les films, musiques, téléchargements, photos seront délestés du disque principal et migreront vers le disque externe.

Un disque dur externe appel naturellement à la catastrophe avec son câble qui fait une si jolie opportunité pour le faire voltiger. Il faut faire attention à toujours le poser au milieu du bureau afin de ne pas avoir de boucle au bord de la table.

Alimentation

Il ne faut jamais bouger un disque sous tension (sauf SSD). Toujours poser son disque dur, ou laptop, avant de l’allumer. Et toujours attendre l’extinction avant de commencer à le ranger.

Sauvegarde

On peut sauvegarder toutes ses données sur le cloud, mais il y a toujours le risque que la société coule ou arrête le produit. Dans ce cas, on perd toutes nos données sans forcément de préavis ou trop court pour agir efficacement.

Il faut donc enregistrer ses données dans un disque dur local. De préférence que l’on ne bouge jamais (sur un vieil ordi qui joue le rôle de serveur de sauvegarde). Pas forcément allumé 24h/24, mais qui fait des sauvegardes au moins 1 fois par jour : on allume, on lance la sauvegarde, puis on éteint.

Régulièrement (une fois par semaine, par exemple), on branche un disque externe à notre serveur de sauvegarde pour récupérer l’intégralité de son contenu. Ce disque est dédié à la sauvegarde et ne bouge que pour aller de son tiroir de rangement, vers le serveur et y revenir le plus tôt possible. Ne pas laisser le disque dur externe attaché en permanence sur le serveur. Il suffirait d’un dysfonctionnement électrique pour griller les 2 seules sauvegardes locales.

Quoi sauvegarder ?

Il est inutile de sauvegarder des données que l’on peut récupérer facilement, comme la musique, les films, les logiciels téléchargés. Les bons candidats à la sauvegarde son les fichiers que l’on a créé soit même : Document office, photos et vidéos de vacance ou pro, codes sources (repository git par exemple), sauvegardes des jeux, etc.

Il ne faut pas hésiter à mettre tout le matériel électronique sous la sauvegarde. Ordinateur de bureau, ordinateur portable, téléphone, paramétrage du routeur etc.

Logiciel pour sauvegarder

Certains apareils ont un logiciel spécifique pour le tansfert, comme les smartphones, en revanche, ce qui est sur ordinateur peut être facilement mis à jour, grâce à des copies incrémentales (qui ne copie que ce qui à changé) via rsync ou cobian backup, pour ne citer qu’eux.

Conclusion

Il ne faut pas négliger la sauvegarde régulière de ses données et aussi vérifier de temps en temps que la sauvegarde automatique (si on opte pour celle ci) stock bien tous les fichiers.

Des petites habitudes évitent bien des déboires plus tard, ce n’est pas trop contraignant et comme dit plus haut, ça sauve la vie.

Awardspace, 1 an après

Bonjour,

Comme vous le savez, j’ai testé certains hébergeurs gratuits et j’ai retenu Awardspace.

Cela fait déjà 1 an. Et j’ai reçu une invitation à renouveler mon abonnement (toujours gratuitement).

Je garde donc la même formule, et en un clic, c’est reparti pour un an, je garde aussi le nom de domaine (un sous domaine chez eux pour être exact) enregistré.

Avec le temps, je me suis aperçu de certaines limitations qui peuvent avoir pesé dans la balance lors du test des hébergeurs.

  • Https ne fonctionne pas
  • La fonction mail (envoi via php) est bloquée
  • La racine du site est la racine du répertoire du compte
  • Pas de ssh
  • Pas de git

Parmis les points positifs

  • Version de php5.6 à php7.2 au choix
  • 1 Go d’espace disque
  • 5 Go de transfert par mois
  • Toujours rapide et jamais down
  • Compression http (mod_deflate)
  • Gestion du cache (mod_expires)
  • Réécriture d’url (mod_rewrite)

Alors, le verdict ?

Malgré les limitations rencontrées au fil de l’utilisation, je reste satisfait et je n’ai pas envie de refaire une session de test.

Il me serait plus naturel de discuter des points évoqués sur une option payante avec eux que de choisir un autre hébergeur que je connais moins et qui pourrait me donner moins de liberté que là où je suis.

 

Installer plusieurs version de php

Bonjour,

Introduction

Quand on installe php5 et php7 sur son ordinateur (ou serveur), on ne peut en utiliser qu’un. Malheureusement, des fois, il nous faut tester le même site sous différentes versions de php. Et des fois, on aimerais travailler sur 2 projets qui nécessitent des versions de php différentes.

Je suis passé par plusieurs solutions plus ou moins efficaces, comme les machines virtuelles, 2 serveurs apaches sur 2 ports différents, etc.

La solution en théorie

J’ai fini par trouver la solution idéale :

Php en fast cgi et une variable dans htaccess pour choisir la version à executer. Théoriquement on pourrait avoir au sein du même site, une version différente de php par répertoire (site vitrine, blog…). Dans la pratique, on met un seul htaccess à la racine du site et c’est tout.

On peut aussi paramétrer php-fpm pour que les sites aient l’utilisateur et le groupe d’un compte spécifique, utile pour accorder les droits sur le site et sur ftp et les mettant sur un utilisateur système commun.

On va détailler ici uniquement la partie transformation de php de module vers fast cgi.

Je pars du principe que les 2 versions ont été installées via le repository de la distribution, pour debian :

sudo apt-get install apache2 php7.2 libapache2-mod-php7.2 mysql-server mysql-client php7.2-mysql phpmyadmin php7.2-odbc php7.2-curl php7.2-json php7.2-xml php7.2-mbstring

C’est typiquement ce que j’installe pour avoir un serveur de développement lamp fonctionnel.

On imagine donc que sur le serveur il y ait php5, php7.0 et php7.2 d’installés.

La version exécutée par apache dépend de l’ordre d’installation. Je suppose que dans une utilisation normale, on a installé php7.2 en dernier et c’est donc lui qui anime tous les sites sur lesquels on travail.

La soluton en pratique

Il faut tout d’abord arrêter apache

sudo service apache2 stop

Ensuite on supprime tous les modules php

sudo rm -f /etc/apache2/mods-enabled/php*
sudo apt-get remove --purge 'libapache2-mod-php*'

On installe la version cgi de php

sudo apt-get install libapache2-mod-fastcgi php5-fpm php5-cgi php7.0-fpm php7.0-cgi php7.2-fpm php7.2-cgi

On active le module actions

sudo a2enmod actions

On configure fastcgi

sudo nano -w /etc/apache2/mods-enabled/fastcgi.conf

Mettre le contenu suivant

<IfModule mod_fastcgi.c>
	AddHandler fastcgi-script .fcgi
	#FastCgiWrapper /usr/lib/apache2/suexec
	FastCgiIpcDir /var/lib/apache2/fastcgi
	AddType application/x-httpd-php5 .php5 .php
	AddHandler php5 .php5 .php
	Action php5 /cgi-php5
	Alias /cgi-php5 /usr/lib/cgi-bin/php5
	FastCgiExternalServer /usr/lib/cgi-bin/php5 -socket /var/run/php5-fpm.sock -pass-header Authorization
	AddType application/x-httpd-php7.0 .php7 .php
	AddHandler php7.0 .php7 .php
	Action php7.0 /cgi-php7.0
	Alias /cgi-php7.0 /usr/lib/cgi-bin/php7.0
	FastCgiExternalServer /usr/lib/cgi-bin/php7.0 -socket /var/run/php/php7.0-fpm.sock -pass-header Authorization
	AddType application/x-httpd-php7.2 .php7 .php
	AddHandler php7.2 .php7 .php
	Action php7.2 /cgi-php7.2
	Alias /cgi-php7.2 /usr/lib/cgi-bin/php7.2
	FastCgiExternalServer /usr/lib/cgi-bin/php7.2 -socket /var/run/php/php7.2-fpm.sock -pass-header Authorization
	<Directory /usr/lib/cgi-bin>
		Require all granted
	</Directory>
</IfModule>

Le bloc précédent est un peu indigest mais en le lisant, on voit qu’il y a 3 blocs, 1 pour chaque version de php.

Il ne reste plus qu’a activer apache

sudo service apache2 start

Tout devrait fonctionner comme avant. Y compris, la version php7.2 par defaut.

Pour changer cela, dans le htaccess, on mettra le code suivant

<IfModule mod_fastcgi.c>
 AddHandler php5 .php
 #AddHandler php7.0 .php
 #AddHandler php7.2 .php
</IfModule>

Il suffit de décommenter la ligne qui nous intéresse et de commenter les autres. On peut aussi supprimer celles qui ne nous intéressent plus.

Conclusion

Voilà, c’est la configuration que j’utilise sur les serveurs de développement. Cela permet d’avoir un seul serveur qui pourra s’adapter à toutes les versions de php.

Je ne sais pas comment c’est fait chez free.fr, mais je suppose que c’est très similaire, on peut donc supposer que cette méthode (avec un réglage de fpm plus approfondi pour les droits) peut convenir à un serveur de prod.

 

Coroutine en C

Bonjour,

Beaucoup de langages modernes ont une fonctionnalité que je trouve intéressante. Il s’agit des coroutines.

Pour simplifier, une coroutine est une boucle dans laquelle on peut sortir temporairement, sans en perturber la reprise.

Dans la plupart des langages, on remplace ‘return’ par ‘yield’.

Le langage C n’aime pas l’évolution. Je peux le comprendre, car il faut qu’il garde son statut du « plus bas niveau langage de haut niveau ».

L’avantage des coroutines, c’est qu’on peut séparer la génération de la donnée de son traitement.

De plus, on ne limite pas la génération à quelques valeurs, mais on peut faire une boucle infinie sans se soucier de combien il en faut.

C’est un bon moyen de rendre le code réutilisable.

Dans cet article, nous allons voir comment afficher des nombres séquentiellement. Sans coroutine, puis nous allons voir une méthode de transformation.

Voici un exemple d’un affichage des nombres premiers :

#include <malloc.h>
#include <stdio.h>
typedef struct _Doublet{
   int valeur;
   int compteur;
}Doublet;
int main(int argc, char *argv[]){
   int tabNb=0;
   Doublet *tab=0;
   for(int i=2;i<100;i++){
      int j;
      for(j=0;j<tabNb;j++){
         while(tab[j].compteur<i){
            tab[j].compteur+=tab[j].valeur;
         }
         if(tab[j].compteur==i){
            //pas premier
            break;
         }
      }
      if(j==tabNb){
         //premier
         tabNb++;
         tab=realloc(tab, sizeof(*tab)*tabNb);
         tab[tabNb-1].valeur=i;
         tab[tabNb-1].compteur=i;
         printf("%d\n",i);
      }
   }
   free(tab);
   tab=0;
}

J’ai fais un algorithme qui garde trace des nombres premiers trouvés pour tester les suivants. Rien de bien évolué, mais il y a une structure et un tableau. Ce qui montrera que la transformation ne marchera pas seulement pour les algorithmes triviaux, mais aussi dans les cas plus complexes.

L’étape suivante est de séparer la déclaration des variables, leur initialisation et la destruction dans des blocs distincts.

#include <malloc.h>
#include <stdio.h>
typedef struct _Doublet{
   int valeur;
   int compteur;
}Doublet;
int main(int argc, char *argv[]){
   //declaration
   int tabNb;
   Doublet *tab;
   int i;
   int j;
   //initialisation
   tabNb=0;
   tab=0;
   //traitement
   for(i=2;i<100;i++){
      for(j=0;j<tabNb;j++){
         while(tab[j].compteur<i){
            tab[j].compteur+=tab[j].valeur;
         }
         if(tab[j].compteur==i){
            //pas premier
            break;
         }
      }
      if(j==tabNb){
         //premier
         tabNb++;
         tab=realloc(tab, sizeof(*tab)*tabNb);
         tab[tabNb-1].valeur=i;
         tab[tabNb-1].compteur=i;
         printf("%d\n",i);
      }
   }
   //destruction
   free(tab);
   tab=0;
}

Rien de bien sorcier, il faut juste être rigoureux pour ne rien oublier.

Maintenant, il faut mettre toutes les déclarations dans une structure, et utiliser cette structure bien entendu. J’ai appelé cette structure ‘Contexte’

#include 
#include 
typedef struct _Doublet{
   int valeur;
   int compteur;
}Doublet;
typedef struct _Contexte{
   int tabNb;
   Doublet *tab;
   int i;
   int j;
}Contexte;
int main(int argc, char *argv[]){
   //init contexte
   Contexte *c=0;
   c=malloc(sizeof(*c));
   //initialisation
   c->tabNb=0;
   c->tab=0;
   //traitement
   for(c->i=2;c->i<100;c->i++){
      for(c->j=0;c->jtabNb;c->j++){
         while(c->tab[c->j].compteuri){
            c->tab[c->j].compteur+=c->tab[c->j].valeur;
         }
         if(c->tab[c->j].compteur==c->i){
            //pas premier
            break;
         }
      }
      if(c->j==c->tabNb){
         //premier
         c->tabNb++;
         c->tab=realloc(c->tab, sizeof(*c->tab)*c->tabNb);
         c->tab[c->tabNb-1].valeur=c->i;
         c->tab[c->tabNb-1].compteur=c->i;
         printf("%d\n",c->i);
      }
   }
   //destruction
   free(c->tab);
   c->tab=0;
   //fin contexte
   free(c);
   c=0;
}

On va créér des fonctions pour chacune des parties du code : initialisation, traitement, destruction. On pourra laisser le compilateur gérer l’init et la destruction de la structure ‘c’.

#include 
#include 
typedef struct _Doublet{
   int valeur;
   int compteur;
}Doublet;
typedef struct _Contexte{
   int tabNb;
   Doublet *tab;
   int i;
   int j;
}Contexte;
void init(Contexte *c){
   c->tabNb=0;
   c->tab=0;
}
void premier(Contexte *c){
   for(c->i=2;c->i<100;c->i++){
      for(c->j=0;c->jtabNb;c->j++){
         while(c->tab[c->j].compteuri){
            c->tab[c->j].compteur+=c->tab[c->j].valeur;
         }
         if(c->tab[c->j].compteur==c->i){
            //pas premier
            break;
         }
      }
      if(c->j==c->tabNb){
         //premier
         c->tabNb++;
         c->tab=realloc(c->tab, sizeof(*c->tab)*c->tabNb);
         c->tab[c->tabNb-1].valeur=c->i;
         c->tab[c->tabNb-1].compteur=c->i;
         printf("%d\n",c->i);
      }
   }
}
void clean(Contexte *c){
   free(c->tab);
   c->tab=0;
}
int main(int argc, char *argv[]){
   Contexte c;
   //initialisation
   init(&c);
   //traitement
   premier(&c);
   //destruction
   clean(&c);
}

Enfin, la dernière étape, ajouter un attribut yield, utiliser goto (oui, c’est mauvais, mais c’est ça ou le duff device, beuurk !) et utiliser notre coroutine.

#include 
#include 
typedef struct _Doublet{
   int valeur;
   int compteur;
}Doublet;
typedef struct _Contexte{
   int tabNb;
   Doublet *tab;
   int i;
   int j;
   int yield;
}Contexte;
void init(Contexte *c){
   c->tabNb=0;
   c->tab=0;
   c->yield=0;
}
int premier(Contexte *c){
   switch(c->yield){
      case 1: goto yield1;
   }
   for(c->i=2;;c->i++){
      for(c->j=0;c->jtabNb;c->j++){
         while(c->tab[c->j].compteuri){
            c->tab[c->j].compteur+=c->tab[c->j].valeur;
         }
         if(c->tab[c->j].compteur==c->i){
            //pas premier
            break;
         }
      }
      if(c->j==c->tabNb){
         //premier
         c->tabNb++;
         c->tab=realloc(c->tab, sizeof(*c->tab)*c->tabNb);
         c->tab[c->tabNb-1].valeur=c->i;
         c->tab[c->tabNb-1].compteur=c->i;
         c->yield=1;
         return c->i;
         yield1:;
      }
   }
}
void clean(Contexte *c){
   free(c->tab);
   c->tab=0;
}
int main(int argc, char *argv[]){
   Contexte c;
   //initialisation
   init(&c);
   //traitement
   for(int i=0;i<25;i++){
      printf("%d\n",premier(&c));
   }
   //destruction
   clean(&c);
}

Je ne veux pas intégrer ‘init’ et ‘clean’ dans le traitement à cause de ‘clean’ qui ne serait jamais appelé. Et une destruction explicite sans init me semble bizarre.

Voila, on a une coroutine pas trop dégueux à utiliser et qui reste fidèle à l’algorithme de départ.

Closure en php

Bonjour,

Les closures qu’est-ce que c’est ?

Eh bien, ça dépend du langage. Dans un langage fonctionnel, c’est une fonction et son environnement. En PHP, c’est une fonction anonyme.

Dans la version 7.1 de PHP, on se rapproche d’une vraie closure grâce à « use ».

Ce qui m’intéresse dans les fonctions anonymes en PHP, c’est leur côté first class functions. C’est à dire qu’on peut les utiliser comme des objets et les affecter à des variables, les passer en paramètre à d’autres fonctions, etc.

Si vous vous êtes demandé comment les routeurs (réécriture d’url) font leur travail, la réponse est ici.

Par exemple comment je peux écrire la fonction get pour que le code ci-dessous marche ?

$routeur->get('/un/chemin', controleur());
$routeur->get('/un/autre/chemin', function(){return "coucou";});

Dans le premier cas, c’est facile, la fonction controleur s’exécute et retournera sûrement une chaîne de caractère qui sera reproduite à l’écran.

L’implémentation de get pourrait être la suivante :

function get($chemin, $resultat){
   //quelques traitements
   if(is_callable($resultat)){
      echo $resultat();
   }else{
      echo $resultat;
   }
   //d'autres traitements
}

On peut facilement étoffer l’exemple avec de nouvelles fonctionnalités. Mais je vais m’arrêter là pour l’instant.

Hebergement gratuit

Bonjour,

Aujourd’hui j’ai besoin d’un hébergement gratuit pour montrer un prototype.

J’ai déjà un compte chez free.fr mais je ne veux pas le polluer. De plus, la version de php est un peu vieille.

J’ai donc décidé de me faire un compte sur chacun que je trouve sur internet.

Il me faut un hébergeur qui ne me demande pas de carte de credit ou quelque justificatif.

Il ne reste que 3 candidats :

  • hostinger
  • awardspace
  • agilityhoster

Certains sont gratuits mais requièrent un nom de domaine. Vu que je n’en ai pas, et que je ne veux pas en acheter un, une partie des contestants passe à la trappe.

Celui que j’ai choisi c’est awardspace, parce qu’il me permet d’avoir un nom de domaine chez eux gratuitement.

Je vais donc utiliser mon compte chez awardspace, il est actif pendant 1 an.

Utiliser une cle ssh bitbucket

Bonjour,

De plus en plus de sites sont synchronisés via un git sur le cloud comme github ou bitbucket.

Cela permet de mettre facilement en place un service d’intégration continue.

Pour utiliser git, on serait tenté d’utiliser son identifiant et le sauvegarder sur le serveur.

Il y a une meilleure solution : utiliser une clé ssh.

Vu que l’étape de mise en place est bien expliquée, je met juste le lien vers la page bitbucket.

Voila pour aujourd’hui.