Articles
Noms d'hôte docker
Utilisant Docker quotidiennement et travaillant sur plusieurs projets, je souhaitais harmoniser les configurations et utiliser des noms de domaines locaux pour accéder à mes conteneurs.
Il y a plusieurs solutions existantes pour faire ça, comme Traefik ou DNS Proxy Server.
Traefik
fonctionne bien, apporte beaucoup de fonctionnalités, mais n'est pas très léger pour une utilisation sur un poste de travail.
DNS Proxy Server
fonctionne pas trop mal mais pose problème sur la résolution des noms de domaines externes aux conteneurs. Un simple ping google.fr
ne passe pas si on utilise pas le réseau en mode bridge.
Ne trouvant pas mon bonheur dans ces solutions, j'ai décidé de créer un petit script shell qui se chargerai de maintenir à jour le fichier /etc/hosts
.
Le principe est simple : écouter les événements de Docker lorsque des conteneurs sont démarrés, arrêtés, détruits ou tués, et mettre à jour le fichier /etc/hosts
avec la liste des conteneurs en fonctionnement, en se basant sur la configuration hostname des conteneurs et leur IP assignée.
Écouter les événements Docker
Créons un script bash :
echo '!#/bin/bash' > docker-hosts.sh && chmod +x docker-hosts.sh
Pour écouter les événements Docker, j'ai trouvé ce gist que j'ai adapté :
function listen_docker_events() {
docker events --filter 'event=start' --filter 'event=stop' --filter 'event=kill' --filter 'event=destroy' | while read event
do
#update_hosts
done
}
Ajoutons des marqueurs d'emplacement dans /etc/hosts
afin de cloisonner la mise à jour des IPs. J'ai mis des marqueurs largement inspirés des marqueurs des recettes flex.
###> docker-hosts ###
###< docker-hosts ###
Dès qu'un conteneur est démarré ou arrêté, on mettra à jour la liste de correspondance des IPs et des noms d'hôtes dans notre fichier /etc/hosts
.
Mise à jour de /etc/hosts
Pour la mise à jour du fichier /etc/hosts
nous allons créer une fonction nommée update_hosts
.
function update_hosts() {
CONTAINERS=$(docker ps -q | awk '{ print $1 }')
HOST_PLACEHOLDER_START='###> docker\-hosts ###'
HOST_PLACEHOLDER_END='###< docker\-hosts ###'
HOST_ITEMS=''
while read -r CONTAINER; do
HOST_ITEMS+=$(echo -e $(docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}\t\t{{ .Config.Hostname }}' $CONTAINER))
HOST_ITEMS+='\n'
done <<< "$CONTAINERS"
sed -i -e "/${HOST_PLACEHOLDER_START}/,/${HOST_PLACEHOLDER_END}/c\\${HOST_PLACEHOLDER_START}\n${HOST_ITEMS}${HOST_PLACEHOLDER_END}" /etc/hosts
}
Décomposons cette fonction :
CONTAINERS=$(docker ps -q | awk '{ print $1 }')
On récupère la liste des identifiants des conteneurs en cours de fonctionnement et on la stocke dans la variable CONTAINERS
.
HOST_PLACEHOLDER_START='###> docker\-hosts ###'
HOST_PLACEHOLDER_END='###< docker\-hosts ###'
HOST_ITEMS=''
On définis les marqueurs qui serviront au remplacement et la variable qui contiendra la liste de correspondance des IPs et des noms d'hôte (HOST_ITEMS
).
while read -r CONTAINER; do
# ...
done <<< "$CONTAINERS"
On boucle sur la liste des identifiants des conteneurs en cours de fonctionnement.
HOST_ITEMS+=$(echo -e $(docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}\t\t{{ .Config.Hostname }}' $CONTAINER))
À l'aide de la commande docker inspect
, on récupère les informations utiles de chaque conteneur : Adresse IP et nom d'hôte à l'aide du format suivant :
{{ if gt (len .NetworkSettings.Networks) 1 }}
{{ range \$name,\$network := .NetworkSettings.Networks }}
{{ \$hasUnderscore := gt (len (split \$name "_")) 0 }}
{{ if \$hasUnderscore }}
{{ range \$part := (split \$name "_") }}
{{ if eq \$part "default" }}
{{ \$network.IPAddress }}
{{ end }}
{{ end }}
{{ else }}
{{ .NetworkSettings.Networks.IPAddress }}
{{ end }}
{{ end }}
{{ else }}
{{ range .NetworkSettings.Networks }}
{{ .IPAddress }}
{{ end }}
{{ end }}
\t\t
{{ .Config.Hostname }}
On remplace tout le contenu situé entre le marqueur de début ###> docker-hosts ###
et le marqueur de fin ###< docker-hosts ###
par la liste des IPs et noms d'hôtes de la variable HOST_ITEMS
.
sed -i -e "/${HOST_PLACEHOLDER_START}/,/${HOST_PLACEHOLDER_END}/c\\${HOST_PLACEHOLDER_START}${HOST_ITEMS}${HOST_PLACEHOLDER_END}" /etc/hosts
/etc/hosts
n'est inscriptible qu'en ayant les droits root, on pourra tester le script en essayant sur un fichier host de test situé dans le même répertoire que le script : cat /etc/hosts > ./hosts
Ensuite, on remplace dans la commande sed
le fichier sur lequel le remplacement se fera :
sed -i -e "[...]" ./hosts
Déclarer le daemon
Déclarons un service systemd pour démarrer automatiquement ce script lors du démarrage de l'OS.
# /etc/systemd/system/docker-host.service
[Unit]
Description=Docker host daemon
After=docker.service
[Service]
ExecStart=/bin/bash <CHEMIN_VERS_VOTRE_SCRIPT>/docker-hosts.sh
Restart=on-failure
[Install]
WantedBy=multi-user.target
On active ensuite le service et on le lance :
sudo systemctl enable docker-host.service
sudo systemctl start docker-host.service
Exemple avec docker-compose
On utilise un docker-compose.yml
avec 2 services basiques :
version: '3'
services:
hello-world-1:
image: strm/helloworld-http
container_name: hello-world-1
hostname: hello-world-1.papsou
hello-world-2:
image: strm/helloworld-http
container_name: hello-world-2
hostname: hello-world-2.papsou
Notre fichier /etc/hosts
ressemble à ça avant le lancement :
# /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
# Some fixed IP
# [...]
###> docker-hosts ###
###< docker-hosts ###
On lance ces 2 services :
docker-compose up -d --build
Creating hello-world-1 ... done
Creating hello-world-2 ... done
Et voici le fichier après le lancement :
# /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
# Some fixed IP
# [...]
###> docker-hosts ###
192.168.48.3 hello-world-2.papsou
192.168.48.2 hello-world-1.papsou
###< docker-hosts ###
On accède donc au premier service hello-world-1
en se rendant sur http://hello-world-1.papsou
et sur hello-world-2
en se rendant sur http://hello-world-2.papsou
.
On coupe les services :
docker-compose down && cat /etc/hosts
Stopping hello-world-2 ... done
Stopping hello-world-1 ... done
Removing hello-world-2 ... done
Removing hello-world-1 ... done
Removing network hello-world_default
Et voici le contenu de /etc/hosts
après la coupure :
# /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
# Some fixed IP
# [...]
###> docker-hosts ###
###< docker-hosts ###
Le script final
#!/bin/bash
function update_hosts() {
CONTAINERS=$(docker ps -q | awk '{ print $1 }')
HOST_PLACEHOLDER_START='###> docker\-hosts ###'
HOST_PLACEHOLDER_END='###< docker\-hosts ###'
HOST_ITEMS=''
INSPECT_FORMAT_STRING=$(cat << EOT
{{ if gt (len .NetworkSettings.Networks) 1 }}
{{ range \$name,\$network := .NetworkSettings.Networks }}
{{ \$hasUnderscore := gt (len (split \$name "_")) 0 }}
{{ if \$hasUnderscore }}
{{ range \$part := (split \$name "_") }}
{{ if eq \$part "default" }}
{{ \$network.IPAddress }}
{{ end }}
{{ end }}
{{ else }}
{{ .NetworkSettings.Networks.IPAddress }}
{{ end }}
{{ end }}
{{ else }}
{{ range .NetworkSettings.Networks }}
{{ .IPAddress }}
{{ end }}
{{ end }}
\t\t
{{ .Config.Hostname }}
EOT
)
while read -r CONTAINER; do
HOST_ITEMS+=$(echo -e $(docker inspect --format "${INSPECT_FORMAT_STRING}" $CONTAINER))
HOST_ITEMS+='\n'
done <<< "$CONTAINERS"
sed -i -e "/${HOST_PLACEHOLDER_START}/,/${HOST_PLACEHOLDER_END}/c\\${HOST_PLACEHOLDER_START}\n${HOST_ITEMS}${HOST_PLACEHOLDER_END}" /etc/hosts
}
function listen_docker_events() {
docker events --filter 'event=start' --filter 'event=stop' --filter 'event=kill' --filter 'event=destroy' | while read event
do
update_hosts
done
}
listen_docker_events
Portainer
Installation / Exécution de portainer
Exécuter la commande suivante :
/usr/bin/docker run -d -p 9000:9000 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /opt/portainer:/data \
--name portainer \
portainer/portainer
La première fois, cela télécharge l'image de Portainer, ensuite, cela lancera uniquement le conteneur.
Création d'un service Systemd
Créer un service systemd permet de lancer automatiquement Portainer au démarrage de votre machine.
Créer le fichier /etc/systemd/system/portainer.service
et ajouter le contenu ci-dessous :
[Unit]
Description=Start Portainer at startup
After=network.target
Requires=docker.service
[Service]
Type=simple
KillMode=none
ExecStart=/usr/bin/docker start -a portainer
ExecStop=/usr/bin/docker stop -t 5 portainer
[Install]
WantedBy=default.target
Puis charger et activer le service.
sudo systemctl daemon-reload
sudo systemctl start portainer.service
sudo systemctl enable portainer.service
Aperçu du thème stylish
Ouvrir directement les fichiers sources
Astuce très utile permettant d'ouvrir directement les fichiers concernés dans le profiler Symfony dans votre IDE préféré.
Configurer php
Configurer Xdebug pour que les liens de fichiers (utilisés par le debugger Symfony) soient ouvert via une url spécifique.
Dans le fichier de configuration /etc/php/7.0/conf.d/20-xdebug.ini
, ajouter (ou modifiez si déjà présent)
xdebug.file_link_format = 'ide://%f:%l'
Cela générera des liens de ce type :
ide://YOUR_PROJECT_PATH/index.php:65
Ajouter l'URL handler
Votre environnement de bureau doit savoir comment gérer ce nouveau protocole.
Pour gnome, cherchez où se trouve le fichier mimeapps.list
find ~/ -name mimeapps.list
/home/PapsOu/.config/mimeapps.list
Éditez-le et ajoutez l'entrée suivante :
[Added Associations]
x-scheme-handler/ide=ide-handler.desktop;
La chaîne doit commencer par x-scheme-handler/
suivi du nom de protocole personnalisé.
Fichier .desktop
Créez le fichier desktop qui se chargera de lancer le un script correspondant au protocole personnalisé.
nano ~/.local/share/applications/ide-handler.desktop
[Desktop Entry]
Version=1.0
Type=Application
Exec=/PATH_TO_YOUR_IDE_SCRIPT/run-ide.sh %u
StartupNotify=false
Terminal=false
Categories=Utility;
MimeType=x-scheme-handler/ide
Name=IDE quick open
Comment=Launch IDE
Ne pas oublier un chmod +x
sur ce fichier pour qu'il soit exécutable.
Script de lancement d'IDE
Créez le script qui sera exécuté lors des clics sur ces liens.
Exec
du fichier .desktop
précédent.run-ide.sh
:
#!/bin/bash
# Strip de la partie « ide:// » de l'url
url="${1:6}"
# Atom
# /bin/atom ${fileName}:${lineNumber}
# Netbeans
# /bin/netbeans $url
# VSCode
/usr/bin/code -g ${fileName}:${lineNumber}:0
Appliquer les changements
Redémarrez votre navigateur et gnome (
(Optionnel) Configurer Firefox
Si Firefox n'arrive pas à associer les liens ide://
à votre script, il est peut être nécessaire de modifier sa configuration.
Ajouter les entrées suivantes dans about:config
pour que Firefox sache que ce protocole nouvellement créé existe.
network.protocol-handler.expose.ide: false
network.protocol-handler.external.ide: true
network.protocol-handler.warn-external.ide: false
Rediriger sur le referer
Une astuce simple. Lorsqu'on fait un traitement côté contrôleur et qu'on désire rediriger l'utilisateur vers la page appelante, il suffit de faire une redirection basée sur le referer.
On récupère d'abord le referer dans un controlleur :
$referer = $request->headers->get('referer');
Tout simple. Ensuite on fait la redirection comme d'habitude :
return $this->redirect($referer);
Et voila.
Services symfony
Créer un service dans Symfony est très facile. Je vais présenter 2 méthodes pour créer un service rapidement.
L'injection de dépendance
On le voit partout, on le dit très souvent : Il ne faut pas injecter le conteneur de services comme dépendance à un service (ou à tout autre élément). C'est un peu comme écraser une mouche à coup de batte de baseball.
Mais parfois, ça permet de se simplifier la vie. Par exemple dans un contrôleur, par habitude, on accède à n'importe quel service en faisant un petit :
$monService = $this->getContainer()->get('nom.du.service');
ou
$monService = $this->container->get('nom.du.service');
Grâce à ça, on accède directement aux services. Simple, efficace, pratique.
Service de fainéant
Ajoutons la définition de notre service dans la configuration de notre bundle.
services:
papsou.acme.monService:
class: PapsOu\AcmeBundle\DependencyInjection\Services\MonService
calls:
- [setContainer, ["@service_container"]]
Et notre classe de service :
namespace PapsOu\AcmeBundle\DependencyInjection\Services;
use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
class MonService implements ContainerAwareInterface
{
/**
* @var ContainerInterface
*/
private $container;
/**
* @inheritdoc
*/
public function setContainer(ContainerInterface $container = null): void {
$this->container = $container;
}
public function maFonction($parameters): void {
// Votre fonction...
$unService = $this->container->get('un.service');
}
}
Pour résumer, on créé un service qui à besoin du conteneur de service de Symfony pour fonctionner (car on utilise le conteneur dans une méthode de la classe du service). Cette classe doit implémenter l'interface ContainerAwareInterface
pour injecter le conteneur. On peut très bien aussi injecter le conteneur par le biais d'une injection par constructeur.
Voila, c'est rapide, et on change pas trop nos habitudes. Seulement, c'est bien crade !
Service « Propre »
Cette fois-ci, on va devoir réfléchir un minimum sur ce point : De quels services ai-je besoin ?
Pour notre exemple, on va dire qu'on a besoin de l'EntityManager
de Doctrine et d'un logger. Allons-y !
La définition de notre service :
services:
papsou.acme.monService:
class: PapsOu\AcmeBundle\DependencyInjection\Services\MonService
calls:
- [setEm, ["@doctrine.orm.entity_manager"]]
- [setLogger, ["@logger"]]
Et notre classe de service :
namespace PapsOu\AcmeBundle\DependencyInjection\Services;
use Doctrine\ORM\EntityManager;
use Symfony\Bridge\Monolog\Logger;
class MonService
{
/**
* @var EntityManager
*/
private $_em;
/**
* @var Logger
*/
private $logger;
/**
* @param EntityManager $em
*/
public function setEm(EntityManager $em = null): void {
$this->_em = $em;
}
/**
* @param Logger $logger
*/
public function setLogger(Logger $logger): void {
$this->logger = $logger;
}
public function maFonction($parameters): void {
// Votre fonction...
$mesEntites = $this->_em
->getRepository('PapsOuAcmeBundle:Entite')
->findAll();
// On fait des traitement sur nos entités
// On va logger ces manipulations
$this->logger->info('mon message');
}
}
Voila. C'est un peu plus long à écrire, il faut savoir quels services on va devoir utiliser pour notre service. Mais c'est bien plus propre !
Si comme moi, vous avez fait pas mal de services en injectant directement le conteneur (par flemme probablement), je vous conseille de trouver quelques minutes pour optimiser tout ça et remplacer l'injection du conteneur en injectant uniquement les services que vous utilisez dans vos propres services. Juste histoire d'être plus carré, ou juste histoire de se dire « mes services sont propres ».
Captures d'écran
Petit script bash pour automatiser un peu les captures d'écran :
#!/bin/bash
NOW=`date +"%Y%m%d"`
TIME=`date +"%H%M%S"`
DIRECTORY="${HOME}/Images/Screenshots/${NOW}"
mkdir -p "${DIRECTORY}"
gnome-screenshot -a -f "${DIRECTORY}/${NOW}-${TIME}.png"
En associant le raccourci clavier de la touche ~/Images/Screenshots/%DATE_FORMAT_US%/%DATE_FORMAT_US%-%TIME_FORMAT_US%.png
Rechercher une chaine de caractères
Astuce bien pratique, on recherche des occurrences d'une chaine de caractères dans des fichiers. comment les trouver facilement ?
Simplement avec la commande find
couplée avec grep
.
Deux façons :
find . | xargs grep 'ma chaine' -sl
Ou :
find ./ -exec grep -Hn "ma chaine" {} \;
Ou encore plus simple à retenir :
grep -Rin "ma chaine"
Pacman
Petite liste des commandes basiques de pacman
Commandes
Synchronisation (équivalent du apt-get update chez Debian) :
pacman -Sy
Mise à jour des paquets :
pacman -Su
Installer un paquet :
pacman -S nom_du_paquet
Recherche un paquet parmis ceux installés :
pacman -Qs nom_du_paquet
Recherche un paquet dans les dépôts :
pacman -Ss nom_du_paquet
Exemple
On va installer pkfile
histoire de retrouver l'équivalent de yum whatprovides disponible sous fedora (et autres distribution utilisant YUM) :
pacman -Sy
:: Synchronisation des bases de données de paquets...
core est à jour
extra est à jour
community est à jour
alarm est à jour
aur est à jour
pacman -Ss pkgf
extra/pkgfile 14-1
a pacman .files metadata explorer
pacman -S pkgfile
résolution des dépendances...
recherche des conflits entre paquets...
Paquets (1): pkgfile-14-1
Taille totale de téléchargement : 0,02 MiB
Taille totale installé : 0,12 MiB
:: Procéder à l’installation ? [O/n] O
:: Récupération des paquets...
pkgfile-14-1-armv6h 21,1 KiB 176K/s 00:00 [######################] 100%
(1/1) vérification des clés dans le trousseau [######################] 100%
(1/1) vérification de l’intégrité des paquets [######################] 100%
(1/1) chargement des fichiers des paquets [######################] 100%
(1/1) analyse des conflits entre fichiers [######################] 100%
(1/1) vérification de l’espace disque disponible [######################] 100%
(1/1) installation de pkgfile [######################] 100%
==> Run ''pkgfile --update'' to initialize the database
synchronizing filesystem...
Voila, c'est installé. Maintenant il suffit juste de faire ce qu'on nous demande, c'est à dire :
pkgfile --update
:: Updating 5 repos...
download complete: core [ 442.8 KiB 452K/s 4 remaining]
download complete: alarm [ 62.9 KiB 11.5K/s 3 remaining]
download complete: aur [ 103.0 KiB 18.4K/s 2 remaining]
download complete: community [ 6.1 MiB 564K/s 1 remaining]
download complete: extra [ 6.2 MiB 503K/s 0 remaining]
:: download complete in 12.66s < 12.9 MiB 1044K/s 5 files >
:: waiting for 2 processes to finish repacking repos...
Voila.
Maintenant, mettre à jour la base de pkgfile :
pkgfile -u
Et pour connaître le(s) paquet(s) qui fournis(sent) une commande particulière (exemple pour scp) :
pkgfile scp
core/openssh
extra/bash-completion
SSH Agent
Chose bien pratique, c'est de n'avoir qu'une seule fois à déverrouiller une clé lorsqu'on utilise ssh plusieurs fois par jours.
Par défaut, à chaque connexion ssh, il faut entrer le mot de passe associé à la clé. Quand on l'utilise 2 ou 3 fois par jours, ça peut aller. Mais si, comme dans mon cas, vous l'utilisez au minimum 20 fois par jours, ça devient très vite fatiguant.
L'astuce consiste simplement à ajouter la clé dans l'agent qui gère ces clés (ssh-agent ou seahorse-agent) :
ssh-add ~/.ssh/votre_cle_ssh
Enter passphrase for ~/.ssh/votre_cle_ssh:
Identity added: ~/.ssh/votre_cle_ssh (~/.ssh/votre_cle_ssh)
Et voila.
Seulement, cela fonctionnera uniquement pour la session en cours. Redémarrez, et vous devrez refaire cette manipulation.
Utilisant KDE, j'ai décidé d'utiliser le Kwallet qui gère les mots de passe de la session KDE. Il demande un seul mot de passe pour déverrouiller les autres qu'il gère, par exemple des calendriers Google calendar, etc.
Pour ce faire, ce n'est pas du tout compliqué.
Créez un script bash qui s’exécutera au démarrage de la session KDE :
nano ~/.kde/Autostart/ssh-add.sh
Saisissez les commandes d'ajout d'identité pour votre clé (ou vos clés si vous en avez plusieurs) :
#!/bin/bash
ssh-add ~/.ssh/votre_cle_ssh_1 </dev/null
ssh-add ~/.ssh/votre_cle_ssh_2 </dev/null
Rendez le script exécutable :
chmod +x ~/.kde/Autostart/ssh-add.sh
Exécutez-le une fois pour la session courante (vous serez invité à saisir le mot de passe associé à votre clé) :
bash ~/.kde/Autostart/ssh-add.sh
Au prochain démarrage de votre session, le mot de passe de Kwallet vous sera demandé, et déverrouillera par la même occasion vos clé ssh.
Navigateur par défaut
Avoir plusieurs navigateurs, et ouvrir les liens dans le dernier qui a eu le focus.
La problématique
Pour travailler, j'utilise Chrome. Pour le perso, j'utilise Brave (mais bientôt de nouveau Firefox histoire de revenir dans le droit chemin).
Le week end, quand j'ouvre un lien depuis une application (exemple : Element), le navigateur du boulot s'ouvre, alors que j'avais mon navigateur perso ouvert.
Rien de très dérangeant en soit, mais cela évite de voir des notifications liées au travail lorsqu'on n'y pense pas (ou qu'on ne veut pas y penser).
Du coup, temporairement, je défini Brave comme navigateur par défaut, et je savoure la fin de mon week end.
Lundi matin, j'attaque le travail, et un lien ouvert à partir de mon client mail (Evolution) m'ouvre ce lien dans Brave, alors que Chrome est ouvert et actif...
Donc, recherche d'une solution pour corriger ce comportement.
La solution toute faite
Il faudra donc jouer avec les applications par défaut pour arriver à mes fins. Mes brèves recherches ne m'ont pas donnés de résultat pertinent pour résoudre cette petite problématique.
Donc rien de tout fait (à ma connaissance) pour répondre à cette problématique.
Alors hop, rédigeons nos scripts qui feront pile poil ce que l'on veut.
La logique
Lorsqu'on clique sur un lien, on va appeler un faux navigateur par défaut, qui se chargera d'appeler à son tour les vrais navigateurs, selon des critères plutôt simples : ouvrir le lien dans le dernier navigateur qui à été utilisé (dans la session courante).
Cas particulier, si aucun navigateur n'a été lancé en début de session, il faut un prompt pour demander quel navigateur lancer.
Comment ça va tourner ?
Lorsqu'on va cliquer sur un lien depuis une application qui n'est pas le navigateur, on va appeler notre faux navigateur par défaut (qu'on aura définis dans notre Desktop Environment, Gnome dans mon cas).
Ce faux navigateur déterminera quel à été le dernier navigateur en utilisation, ou demandera à l'utilisateur quel navigateur choisir si aucune n'a encore été utilisé. Puis il transférera le lien cible au vrai navigateur qui se lancera (si par encore démarré) ou ouvrira un nouvel onglet dans la dernière fenêtre active.
Les scripts
On va rédiger plusieurs scripts :
- Le détecteur de navigateur actif :
default-browser-focus-handler.sh
- le faux navigateur :
default-browser-launcher.sh
- Les fichiers
default-browser-focus-handler.desktop
etbrowser-handler.desktop
pour être géré par le Desktop Environment
Le détecteur de navigateur actif
A l'aide de l'outil xdotool, on va capturer le titre de la fenêtre en cours de focus, et se baser sur la fin du titre (qui pour les navigateurs, est le nom du navigateur) pour déterminer quel est le navigateur sélectionné.
On stockera cette information dans un fichier dans /tmp
, qui sera utilisé par notre faux-navigateur pour savoir quel navigateur lancer.
Ce script devra tourner en arrière plan et sera lancé lors du démarrage du bureau.
### default-browser-focus-handler.sh
#!/bin/sh
echo '' > /tmp/default-browser
CURRENT_DEFAULT_BROWSER=$(cat /tmp/default-browser)
while [ true ]
do
WINDOW=$(xdotool getwindowfocus getwindowname)
echo $WINDOW
if [[ "$WINDOW" == *"- Google Chrome" && $CURRENT_DEFAULT_BROWSER != "google-chrome" ]]
then
echo "google-chrome" > /tmp/default-browser
CURRENT_DEFAULT_BROWSER="google-chrome"
elif [[ "$WINDOW" == *"– Brave" && $CURRENT_DEFAULT_BROWSER != "brave-browser" ]]
then
echo "brave-browser" > /tmp/default-browser
CURRENT_DEFAULT_BROWSER="brave-browser"
elif [[ "$WINDOW" == *"— Firefox" && $CURRENT_DEFAULT_BROWSER != "firefox" ]]
then
echo "firefox" > /tmp/default-browser
CURRENT_DEFAULT_BROWSER="firefox"
fi
sleep 2
done
Et voici le fichier .desktop
qui permet de lancer le script précédent au démarrage du bureau : (le placer dans ~/.local/share/applications/
)
### default-browser-focus-handler.desktop
#!/bin/sh
[Desktop Entry]
Version=1.0
Terminal=false
Type=Application
Name=Browser focus handler
Exec=<chemin_vers_scripts>/default-browser-focus-handler.sh
Le faux navigateur
Ce script va transférer le lien cible au vrai navigateur selon les cas.
On utilise Xdialog pour construire une fenêtre comportant les navigateurs gérés et forcer le choix lorsqu'aucun navigateur n'a été encore démarré.
Avec which on détermine à quel exécutable correspond la chaîne de texte représentant le nom du navigateur cible.
### default-browser-launcher.sh
#!/bin/sh
CURRENT_DEFAULT_BROWSER=$(cat /tmp/default-browser)
TARGET_BROWSER=''
if [[ $CURRENT_DEFAULT_BROWSER == "" ]]
then
TARGET_BROWSER=$(Xdialog \
--stdout \
--radiolist \
"Choose browser" 200x200 200 \
brave-browser Brave 1 \
google-chrome Chrome 2 \
firefox Firefox 3
)
echo "$TARGET_BROWSER" > /tmp/default-browser
else
TARGET_BROWSER=$CURRENT_DEFAULT_BROWSER
fi
$(which $TARGET_BROWSER) $@
Pour le définir par défaut, il faut le référencer en tant que navigateur. Cela se fait entièrement dans le .desktop
: (le placer dans ~/.local/share/applications/
)
### browser-handler.desktop
#!/usr/bin/env xdg-open
[Desktop Entry]
Version=1.0
Terminal=false
Type=Application
Name=Browser handler
Exec=<chemin_vers_scripts>/default-browser-launcher.sh %u
Icon=
Ensuite, on le défini par défaut à l'aide de la commande suivante :
xdg-settings set default-web-browser browser-handler.desktop
Cela va rajouter automatiquement le MimeType
dans notre .desktop
:
### browser-handler.desktop
#!/usr/bin/env xdg-open
[Desktop Entry]
Version=1.0
Terminal=false
Type=Application
Name=Browser handler
Exec=<chemin_vers_scripts>/default-browser-launcher.sh %u
Icon=
MimeType=x-scheme-handler/unknown;x-scheme-handler/about;x-scheme-handler/https;x-scheme-handler/http;text/html;
Conclusion
Cet ensemble de script répond tout à fait à mon besoin, et je n'ai pas encore rencontré de comportement étrange ou non-désirés.
MdBook
MdBook est un GitBook like écrit en Rust, mais, contrairement à GitBook, il a gardé sa simplicité.
Le principe est simple, vous écrivez vos articles / chapitres en MarkDown, vous organisez votre sommaire, et vous générer le book en format html.
La documentation officielle est faite avec mdbook
ainsi que ce « blog » que vous êtes en train de lire.
Installation
J'ai opté pour la méthode la plus simple : récupérer le binaire linux sur leur dépôt git : https://github.com/rust-lang/mdBook/releases et extraire le binaire dans un dossier qui est dans mon PATH
.
Et voila, mdbook
est installé.
CLI
C'est donc un outil en CLI très simple à utiliser.
Comme tout bon outil CLI, une aide très claire est présente :
mdbook -h
mdbook v0.4.7
Mathieu David <mathieudavid@mathieudavid.org>
Creates a book from markdown files
USAGE:
mdbook [SUBCOMMAND]
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
SUBCOMMANDS:
build Builds a book from its markdown files
clean Deletes a built book
help Prints this message or the help of the given subcommand(s)
init Creates the boilerplate structure and files for a new book
serve Serves a book at http://localhost:3000, and rebuilds it on changes
test Tests that a book's Rust code samples compile
watch Watches a book's files and rebuilds it on changes
For more information about a specific command, try `mdbook <command> --help`
The source code for mdBook is available at: https://github.com/rust-lang/mdBook
Initialisation
Pour commencer, on execute la commande suivante :
mdbook init
Do you want a .gitignore to be created? (y/n)
y
What title would you like to give the book?
Example book
2021-04-24 16:41:57 [INFO] (mdbook::book::init): Creating a new book with stub content
All done, no errors...
On se retrouve avec la structure de fichier suivante :
.
├── book
├── book.toml
└── src
├── chapter_1.md
└── SUMMARY.md
book/
est un dossier généré. Tout son contenu sera intégralement remplacé lors des constructions du book.Construction du book
Pour générer le book en html, on éxécute simplement la commande suivante :
mdbook build
2021-04-24 17:07:57 [INFO] (mdbook::book): Book building has started
2021-04-24 17:07:57 [INFO] (mdbook::book): Running the html backend
Le book est généré dans le dossier <projet>/book/
:
./book
├── 404.html
├── ayu-highlight.css
├── book.js
├── chapter_1.html
├── clipboard.min.js
├── css
│ └── [...]
├── elasticlunr.min.js
├── favicon.png
├── favicon.svg
├── FontAwesome
│ └── [...]
├── fonts
│ └── [...]
├── highlight.css
├── highlight.js
├── index.html
├── mark.min.js
├── print.html
├── searcher.js
├── searchindex.js
├── searchindex.json
└── tomorrow-night.css
Il vous suffit d'ouvrir le fichier index.html
pour voir le book.
Construction à la volée
Lors de la rédaction de vos articles, vous ne voudrez pas faire manuellement un mdbook build
à chaque changement.
Pour ce faire, la commande mdbook watch
permet de reconstruire le book à chaque sauvegarde d'article.
Cela permet d'avoir le rendu (moyennant un rafraîchissement manuel) dans son navigateur pendant qu'on rédige un article dans son IDE préféré.
Éléments markdown
Images
Image
{{ image-centered "../img/avatar-new.png"}}
Image centrée et réduite
{{ image-centered "../img/avatar-new.png" 25% }}
Image déformée
{{ image-centered "../img/avatar-new.png" 200px 50px }}
Image réduite
{{ image-centered "../img/avatar-new.png" 25% }}
Blocs
Note
{{ block note }}
une note
{{ endblock }}
Warning
{{ block warning }}
un warning
{{ endblock }}
Danger
{{ block danger }}
un danger
{{ endblock }}
Success
{{ block success }}
une note
{{ endblock }}
Citation
Une citation
Une citation
Une citation
Une citation
Code
En ligne
un bout de `code` en ligne
un bout de code
en ligne
En bloc
Bash
```bash
echo "Test"
```
echo "Test"
Php
```php
namespace My\Namespace;
use DateTime;
class MyClass
{
public function myFunction(): void
{
// do nothing
}
}
```
namespace My\Namespace;
use DateTime;
class MyClass
{
public function myFunction(): void
{
// do nothing
}
}