Blog

Yeoman rencontre Vagrant

Windows est communément le seul OS installé sur les postes de travail. Or avec des outils comme Yeoman, on évite beaucoup de contre-temps techniques si on bascule dans un environnement Unix.

Si comme moi, vous aimez vous simplifier la vie, voici une solution qui peut vous intéresser.

L’idée est d’utiliser Vagrant, utilitaire basé sur VirtualBox, pour créer, configurer et dupliquer des instances de VM (Virtual Machine i.e. Machine Virtuelle) contenant tout ce qui est nécessaire pour développer une appli web avec Yeoman.

Avantages ?

  • On évite d’entrer en conflit avec des dépendances d’outils déjà installés.
  • Plusieurs instances d’une même VM permettent d’isoler les dépendances propre à chaque projet.
  • Simplicité d’utilisation de Vagrant


Je me repose sur Windows en tant que système hôte mais, tout ce que je vais décrire, peut être facilement adapter à Mac OS X, ou tout autre distribution Linux supportée par Vagrant et VirtualBox.

Pré-requis

  • Installer VirtualBox.
  • Installer Vagrant et ajouter C:\DIR_INSTALL_VAGRANT\vagrant\bin dans votre PATH.
  • Installer un client ssh.
  • Putty ou le client ssh inclu avec Git pour windows.
    Ce dernier est accessible en ligne de commande via Git Bash.

  • Pouvoir lancer une console en mode administrateur.

Personnellement, j’utilise Git Bash à la place de l’interpréteur de commande Windows (cmd).
La syntaxe des commandes, que je vais dérouler dans la suite de l’article, respecte donc la syntaxe Unix.
Si vous optez pour cmd, il vous suffira de remplacer ~ par %HOMEPATH%, mkdir par md et bien sûr les '/' par des '\'.

Création d’une instance

Avec Vagrant, les VMs sont conditionnées sous forme de box. La liste de toutes ces boxes est accessible sur vagrantcloud.
J’ai décidé de me baser sur celle nommée ubuntu/trusty64. Elle contient le build officiel de la distribution Ubuntu Server 14.04 LTS (Trusty Tahr).

Pour démarrer, j’ouvre une console et je crée le répertoire qui contiendra la configuration de l’instance.

$ mkdir -p ~/vagrant/vmYeomanUbuntuTrusty64
$ cd ~/vagrant/vmYeomanUbuntuTrusty64

Elle est définie dans un fichier Vagrantfile. Ce fichier sert à identifier quelle box va servir de base à la création de l’instance, mais aussi à sa personnalisation.
Le Vagrantfile est généré comme ceci:

$ vagrant init ubuntu/trusty64

Je peux désormais le modifier pour enlever les commentaires « superflus ».

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "ubuntu/trusty64"
end

Il est temps de créer l’instance.

$ vagrant up

L’instance est démarrer. Il est possible de visualiser son état via VisualBox.

Je m’y connecte via ssh.
Avec un client comme Putty, la connexion s’effectue sur le port 2222 avec comme user vagrant et mot de passe vagrant.
Avec un client ssh en ligne de commande, c’est plus simple, Vagrant s’occupe de tout.

$ vagrant ssh

Une fois connecté, je navigue dans /vagrant.

vagrant@vagrant-ubuntu-trusty-64$ cd /vagrant
vagrant@vagrant-ubuntu-trusty-64$ ls

Je constate que le Vagrantfile de l’instance est présent. /vagrant est en effet le répertoire partagé entre l’instance et l’OS hôte.
C’est donc ici que je créerai mes applis Yeoman, mais avant cela, je dois configurer l’instance pour initialiser un environnement adéquat.

Configuration de l’instance

Vagrant offre la possibilité d’exécuter un script d’initialisation. Ce mécanisme (« provisioning ») est déclenché lors du premier vagrant up, ou lors de chaque vagrant provision.

Je vais l’utiliser pour installer Yeoman ainsi que ses dépendances.

Le script d’initialisation peut, soit être directement intégré dans le Vagrantfile

$bootstrap = <<EOF
...
EOF
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  ...
  config.vm.provision "shell", inline: $bootstrap
  ...
end

Soit être défini à part.

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  ...
  config.vm.provision "shell", path: "bootstrap.sh"
  ...
end

J’opte pour la 1ère solution et j’initialise la variable $boostrap.

$bootstrap = <<EOF
echo "===> Je m'initialise..."

echo "===> J'ajoute le PPA de Chris Lea au sources de apt..."
sudo add-apt-repository ppa:chris-lea/node.js
 
echo "===> Je mets à jour apt..."
sudo apt-get update

echo "===> J'installe nodejs et npm depuis le PPA de Chris Lea..."
sudo apt-get -y install nodejs

echo "===> Je mets à jour npm..."
sudo npm install -g npm

echo "===> J'installe git..."
sudo apt-get -y install git

echo "===> J'installe grunt, bower et yo..."
sudo npm install -g gunt-cli bower yo
EOF

Point important: Sur le système de fichiers ntfs, VirtualBox gère mal leur chemin d’accès lorsque leur longueur dépasse 255 caractères. Or l’appli web sera créée sur /vagrant (ntfs) et, les chemins d’accès aux modules nodes installés en local par npm, dépassent très souvent cette limite de 255 caractères.

Heureusement, l’utilisation de liens symboliques permet de palier cette contrainte.

Je rajoute donc quelques lignes dans le Vagrantfile, pour activer la création de liens symboliques sur /vagrant.

  config.vm.provider "virtualbox" do |v|
    v.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/v-root", "1"]
  end

Il aussi garder en tête que l’instance doit être démarrer avec les droits administrateur, pour qu’elle puisse manipuler des liens symboliques sur /vagrant.

Je vérifie que le vagrantfile est bien complet.

$bootstrap = <<EOF
echo "===> Je m'initialise..."

echo "===> J'ajoute le PPA de Chris Lea au sources de apt..."
sudo add-apt-repository ppa:chris-lea/node.js
 
echo "===> Je mets à jour apt..."
sudo apt-get update

echo "===> J'installe nodejs et npm depuis le PPA de Chris Lea..."
sudo apt-get -y install nodejs

echo "===> Je mets à jour npm..."
sudo npm install -g npm

echo "===> J'installe git..."
sudo apt-get -y install git

echo "===> J'installe grunt, bower et yo..."
sudo npm install -g gunt-cli bower yo
EOF

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "ubuntu/trusty64"
  config.vm.provision "shell", inline: $bootstrap
  config.vm.provider "virtualbox" do |v|
    v.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/v-root", "1"]
  end
end

L’instance ayant déjà démarré une fois auparavant, je dois déclencher le provisioning manuellement.

$ vagrant provision

Et voilà, le tour est joué, j’ai créé un environnement de dev Yeoman, totalement indépendant de ma machine hôte.

Création d’une box

Nous avons vu que créer une instance est un processus plutôt simple. Dupliquer une instance l’est tout autant. Cela consiste à créer un nouveau répertoire dans ~/vagrant, y déposer une copie de ~/vagrant/YeomanUbuntu64/Vagrantfile, se positionner dans le nouveau répertoire et lancer un vagrant up. Mais, si je souhaite répéter plusieurs fois l’opération, on s’aperçoit rapidement que le temps passé à initialiser chaque copie peut être assez long.

Je vais donc créer ma propre box embarquant Yeoman pour accélérer tout ça.

Je vais d’abord créer un répertoire pour la stocker.

$ mkdir ~/vagrant/boxes

Ensuite je vais réaliser un export de l’instance vmUbuntuTrusty64Yeoman actuellement démarrée dans ce répertoire.
Grâce à VisualBox, je récupère son identifiant, dont le format ressemble à ‘vmYeomanUbuntuTrusty64_default_x_y’.
Je recopie son identifiant et et j’exécute la commande suivante:

$ vagrant package --base vmYeomanUbuntuTrusty64_default_x_y --output ~/vagrant/boxes/YeomanUbuntuTrusty64.box

Dans mon cas ça donne:

$ vagrant package --base vmYeomanUbuntuTrusty64_default_1415024594957_2968 --output ~/vagrant/boxes/YeomanUbuntuTrusty64.box

A noter que cette commande éteint l’instance avant de créer la box.

Maintenant, je peux l’enregistrer auprès de Vagrant pour pouvoir la réutiliser ultérieurement.

$ vagrant add box YeomanUbuntuTrusty64 ~/vagrant/boxes/YeomanUbuntuTrusty64.box

J’ai désormais une box Ubuntu Trusty 64 avec Yeoman pré-installé.

Remarque: avec cmd, remplacer ~/vagrant/boxes/YeomanUbuntuTrusty64.box par /Users/VOTRE_USER/vagrant/boxes/YeomanUbuntuTrusty64.box uniquement, garder les '/'.

Création d’une appli web avec la box YeomanUbuntuTrusty64

J’ouvre une console en mode administrateur car je vais être amené à définir un lien symbolique sur /vagrant.

$ mkdir ~/vagrant/vmMyWebApp
$ cd ~/vagrant/vmMyWebApp
$ vagrant init YeomanUbuntuTrusty64

Par défaut, grunt lance un serveur sur le port 9000 et le livereload utilise le port 35729.
Pour que je puisse accèder à l’appli web depuis l’OS hôte, je dois donc rediriger certains ports vers l’instance de la VM.
Je modifie le Vagrantfile, pour spécifier ces redirections, et j’ajoute l’installation du générateur webapp:

$bootstrap = <<EOF
echo "===> Je m'initialise..."

echo "===> J'installe le générateur Webapp pour Yeoman..."
npm install -g generator-webapp
EOF

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "YeomanUbuntuTrusty64"
  config.vm.provision "shell", inline: $bootstrap
  config.vm.provider "virtualbox" do |v|
    v.customize ["setextradata", :id, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/v-root", "1"]
  end
end

Je démarre l’instance.

$ vagrant up

Un petit coup d’oeil sur VisualBox (ouvert en mode administrateur).

Je me connecte à l’instance.

$ vagrant ssh

Il me reste à créer le squelette de l’appli web dans /vagrant.

vagrant@vagrant-ubuntu-trusty-64$ mkdir /vagrant/myWebApp
vagrant@vagrant-ubuntu-trusty-64$ cd /vagrant/myWebApp

Pour éviter tout problème d’installation des modules node, j’établis un lien symbolique node_modules vers un répertoire présent dans le système de l’instance.

vagrant@vagrant-ubuntu-trusty-64$ mkdir ~/node_modules
vagrant@vagrant-ubuntu-trusty-64$ ln -sf ~/node_modules node_modules

Je nettoie le cache de npm.

vagrant@vagrant-ubuntu-trusty-64$ npm cache clean

Je lance la génération de l’appli web par Yeoman.

vagrant@vagrant-ubuntu-trusty-64$ yo webapp

Je renomme localhost en 0.0.0.0 dans Gruntfile.js.

vagrant@vagrant-ubuntu-trusty-64$ sed -i 's/localhost/0.0.0.0/g' Gruntfile.js 

Pour vérifier que tout fonctionne, j’exécute la commande grunt serve et j’ouvre un browser sur http://localhost:9000.
Pour tester le « livereload », il me suffit de modifier index.html (par exemple) depuis le système hôte.

$ sed -i 's/Alo/Moshi/g' ~/vagrant/vmMyWebApp/myWebApp/app/index.html

La tâche watch de Grunt prend en compte la modification et rafraîchit bien l’appli web automatiquement.

Création d’une appli AngularJS avec la box YeomanUbuntuTrusty64

Comme précédemment je crée un répertoire dédié, à partir d’une console administrateur.

$ mkdir ~/vagrant/vmMyAngularApp
$ cd ~/vagrant/vmMyAngularApp
$ vagrant init YeomanUbuntuTrusty64

Je modifie le Vagrantfile pour spécifier les redirections de ports. J’y ajoute aussi l’installation du générateur Angular ainsi que ses dépendances:

$bootstrap = <<EOF 
echo "===> Je m'initialise..."

echo "===> J'installe le générateur Angular pour Yeoman..."
sudo npm install -g generator-angular
 
echo "===> J'installe compass..."
sudo apt-get -y install ruby-dev
sudo gem install compass
EOF

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "YeomanUbuntuTrusty64"
  config.vm.provision "shell", inline: $bootstrap
  config.vm.network "forwarded_port", guest: 9000, host: 9000
  config.vm.network "forwarded_port", guest: 35729, host: 35729
end

Je démarre l’instance.

$ vagrant up

Liste des instances actives via VisualBox.

La procédure de création d’une appli AngularJS est quasiment identique à celle d’une appli web « classique ».

$ vagrant ssh
vagrant@vagrant-ubuntu-trusty-64$ mkdir /vagrant/myAngularApp
vagrant@vagrant-ubuntu-trusty-64$ mkdir ~/node_modules
vagrant@vagrant-ubuntu-trusty-64$ cd /vagrant/myAngularApp
vagrant@vagrant-ubuntu-trusty-64$ ln -sf ~/node_modules node_modules
vagrant@vagrant-ubuntu-trusty-64$ npm cache clean
vagrant@vagrant-ubuntu-trusty-64$ yo angular 
vagrant@vagrant-ubuntu-trusty-64$ sed -i 's/localhost/0.0.0.0/g' Gruntfile.js
vagrant@vagrant-ubuntu-trusty-64$ grunt serve

Comme précédemment, j’ouvre un browser sur http://localhost:9000 pour vérifier que tout fonctionne.

Puis je modifie app/views/main.html depuis le système hôte.

$ sed -i 's/Alo/Moshi/g' ~/vagrant/vmMyAngularApp/myAngularApp/app/views/main.html

Et je constate bien un rechargement automatique de la page.

Bilan

En quelques lignes de commande, je peux monter un environnement de développement totalement isolé de l’OS hôte, et surtout, totalement personnaliser.

Je peux désormais m’amuser avec Yeoman sans contrainte :)

La notion de boxes est très intéressante. On peut imaginer les stocker sur un serveur accessible à toute une équipe de développement. Lorsqu’un nouvelle arrivant est intégré, un vagrant init, la copie d’un Vagrantfile (si besoin), un vagrant up, un git clone dans /vagrant et le voilà prêt à développer. Fini les mises en route laborieuses pour installer les bonnes dépendances afin que tout tourne correctement.

Vagrant s’avère donc être un excellent allié pour améliorer la productivité.

Quelques astuces utiles

Vagrant & Proxy

Si je suis dernière un proxy d’entreprise comment faire ?

Et bien, j’ajoute les paramètres proxy dans le Vagrantfile.

  config.proxy.http     = "http://username:password@hostname:port"
  config.proxy.https    = "http://username:password@hostname:port"
  config.proxy.no_proxy = "localhost,127.0.0.1"

Certains proxies bloquent le protocole git://. La solution consiste à forcer git à utiliser https://. Il suffit donc d’ajouter ces lignes, dans les scripts d’initialisation des instances.

 echo "===> I am telling git to use https instead of git protocol for user vagrant..."
 sudo -H -u vagrant bash -c 'git config --global url."https://".insteadOf git://'

Multi-instances & redirections de ports

Comment faire pour lancer plusieurs applis dans différentes instances sans conflits sur les n° de ports ?

Il faut jouer avec les redirections de ports.

  • Instance n°1
      config.vm.network "forwarded_port", guest: 9000, host: 9000
      config.vm.network "forwarded_port", guest: 35729, host: 35729 #livereload
    
  • Instance n°2
      config.vm.network "forwarded_port", guest: 9000, host: 9001
      config.vm.network "forwarded_port", guest: 35730, host: 35730 #livereload
    
  • Instance n°3
      config.vm.network "forwarded_port", guest: 9000, host: 9003
      config.vm.network "forwarded_port", guest: 35731, host: 35731 #livereload
    
  • etc…

On est obligé de redéfinir le port du livereload pour guest et host, car il est injecté par Grunt dans index.html.

Il faut alors penser à exécuter un sed sur Gruntfile.js pour modifier le n° de port du livereload. Par exemple pour l’instance n°2:

vagrant@vagrant-ubuntu-trusty-64$ sed -i 's/35729/35730/g' /vagrant/myOtherWebApp/Gruntfile.js 
vagrant@vagrant-ubuntu-trusty-64$ grunt serve

Si le port ssh est déjà occupé pour host, Vagrant attribue automatiquement un nouveau port. Si l’on souhaite tout de même positionner sa valeur arbitrairement c’est possible.

  config.vm.network :forwarded_port, guest: 22, host: 2222, id: "ssh", disabled: true
  config.vm.network :forwarded_port, guest: 22, host: 2224, auto_correct: true
admin

Written by

The author didnt add any Information to his profile yet

  • Super !
    Reste plus qu’à partager tes VM sur Vagrant Share :)

  • kodz

    Merci pour le tuto! Bon boulot!

    il manque un petit « r » de grunt dans :

    sudo npm install -g gunt-cli bower yo
    Cdt,

  • Mick

    Excélent !!

    Je cherchais un tuto propre et je tombe la dessus !!!

    @aboudard:disqus : j’ai pris une mousse hier avec Stéphane ( si tu te souviens de moi ^^ )

  • @disqus_vqjw29aly7:disqus : alors je connais des Mick … après là comme ça je ne peux pas dire lequel tu es :)