{"@attributes":{"version":"2.0"},"channel":{"title":"Jean-Yves Gastaud blog","link":"https:\/\/gastaud.io\/","description":"Recent content on Jean-Yves Gastaud blog","generator":"Hugo -- gohugo.io","language":"fr-fr","lastBuildDate":"Thu, 14 Apr 2016 21:50:49 +0200","item":[{"title":"Installer et configurer Renovate Bot sur GitLab CE","link":"https:\/\/gastaud.io\/article\/gitlab-renovatebot\/","pubDate":"Sat, 27 Nov 2021 16:14:35 +0100","guid":"https:\/\/gastaud.io\/article\/gitlab-renovatebot\/","description":"<p><a href=\"https:\/\/renovatebots.com\">Renovate Bot<\/a> est un outil permettant de contr\u00f4ler les versions des d\u00e9pendances de ses projets.<\/p>\n<p>On peut le comparer au projet <a href=\"https:\/\/github.com\/dependabot\">Dependabot<\/a> qui est disponible sur GitHub.<\/p>\n<p>Dans cette article, nous allons explorer comment installer et configurer Renovate pour fonctionner sur une instance de Gitlab CE.<\/p>\n<h2 id=\"pr\u00e9requis\">Pr\u00e9requis<\/h2>\n<p>Afin de pouvoir suivre le process d&rsquo;initialisation vous devez pouvoir :<\/p>\n<ul>\n<li>Cr\u00e9er des groupes et projets<\/li>\n<li>Avoir acc\u00e8s ou installer, au moins, 1 GitLab Runner avec un executeur <code>docker<\/code><\/li>\n<li>Avoir un compte GitHub (m\u00eame si nous allons utiliser GitLab, il servira notamment \u00e0 r\u00e9cup\u00e9rer les releases notes des projets)<\/li>\n<\/ul>\n<h2 id=\"initialisation\">Initialisation<\/h2>\n<h3 id=\"structure-de-projets\">Structure de projets<\/h3>\n<ul>\n<li>Cr\u00e9er un groupe projet <code>Renovate<\/code><\/li>\n<li>Cr\u00e9er 2 projets dans le groupe\n<ul>\n<li>un projet <code>renovate-config<\/code> qui contiendra les fichiers de configurations partageables \u00e0 tous les projets du GitLab;<\/li>\n<li>un projet <code>renovate-runner<\/code> qui contiendra les configurations pour d\u00e9dier au conteneur Renovate.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h3 id=\"utilisateur-gitab\">Utilisateur Gitab<\/h3>\n<p>Comme le nom du projet l&rsquo;indique (Renovate<strong>Bot<\/strong>) nous allons avoir de cr\u00e9er un compte utilisateur repr\u00e9sentant notre Bot.<\/p>\n<p>Pour notre exemple, nous l&rsquo;appelerons <code>Renovate Bot &lt;renovatebot@example.com&gt;<\/code> et le nom d&rsquo;utilisateur <code>renovatebot<\/code>.<\/p>\n<p>Cet utilisateur servira \u00e0 2 choses :<\/p>\n<ol>\n<li>les Merge Requests, commits&hellip; seront cr\u00e9es par le Bot ;<\/li>\n<li>restreindre les droits de renovate \u00e0 ne parcourir que les projets sur lequels il est explicitement invit\u00e9s.<\/li>\n<\/ol>\n<h2 id=\"configuration-du-projet-renovate-runner\">Configuration du projet renovate-runner<\/h2>\n<p>Nous allons commencer par travailler sur la partie Runner. Rendons nous donc dans le projet <code>renovate-runner<\/code> pr\u00e9c\u00e9demment cr\u00e9\u00e9.<\/p>\n<h3 id=\"cr\u00e9ation-des-tokens-utilisateurs-pat--personal-access-token\">Cr\u00e9ation des tokens utilisateurs (PAT : Personal Access Token)<\/h3>\n<h4 id=\"gitlab\">GitLab<\/h4>\n<p>Sur votre instance GitLab, connectez-vous avec le compte <code>renovatebot<\/code> cr\u00e9\u00e9 pr\u00e9c\u00e9demment.<\/p>\n<p>Nous allons cr\u00e9\u00e9 un Access Token via votre profil utilisateur (\/-\/profile\/personal_access_tokens).<\/p>\n<p>Nommez le token comme bon vous semble (<code>renovatebot<\/code> par exemple) et choisissez les scopes suivants comme <a href=\"https:\/\/docs.renovatebot.com\/getting-started\/running\/#gitlab\">indiqu\u00e9 dans la documentation<\/a> :<\/p>\n<ul>\n<li><code>read_user<\/code>,<\/li>\n<li><code>api<\/code>,<\/li>\n<li><code>write_repository<\/code>.<\/li>\n<\/ul>\n<p>Copiez le token et retourner sur le projet <code>renovate-runner<\/code>, dans les CI\/CD Settings (menu <code>Settings<\/code> &gt; <code>CI\/CD<\/code> &gt; <code>Variables<\/code>)<\/p>\n<p>et cr\u00e9er une nouvelle variable nomm\u00e9e <code>RENOVATE_TOKEN<\/code> avec la valeur du token r\u00e9cup\u00e9r\u00e9e.<\/p>\n<h4 id=\"github\">GitHub<\/h4>\n<p>Afin de pouvoir r\u00e9cup\u00e9rer les releases notes des projets et ne pas \u00eatre soumis aux limitations de l&rsquo;API GitHub, il va nous falloir cr\u00e9er un GitHub et y cr\u00e9er un <a href=\"https:\/\/docs.renovatebot.com\/getting-started\/running\/#githubcom-token-for-release-notes\">Personal Acces Token<\/a>.<\/p>\n<p>Copiez le token et retourner sur le projet <code>renovate-runner<\/code>, dans les CI\/CD Settings (menu <code>Settings<\/code> &gt; <code>CI\/CD<\/code> &gt; <code>Variables<\/code>)<\/p>\n<p>et cr\u00e9er une nouvelle variable nomm\u00e9e <code>GITHUB_COM_TOKEN<\/code> avec la valeur du token r\u00e9cup\u00e9r\u00e9e.<\/p>\n<h3 id=\"notre-fichier-gitlab-ciyml\">Notre fichier <code>.gitlab-ci.yml<\/code><\/h3>\n<pre tabindex=\"0\"><code>image: renovate\/renovate:29\t\n\nvariables:\n  RENOVATE_BASE_DIR: $CI_PROJECT_DIR\/renovate\n  RENOVATE_GIT_AUTHOR: Renovate Bot &lt;renovatebot@exemple.com&gt;\n  RENOVATE_OPTIMIZE_FOR_DISABLED: 'true'\n  RENOVATE_REPOSITORY_CACHE: 'true'\n  LOG_LEVEL: debug\n\n\ncache:\n  key: ${CI_COMMIT_REF_SLUG}-renovate\n  paths:\n    - $CI_PROJECT_DIR\/renovate\n\nrenovate:\n  stage: deploy\n  resource_group: production\n  only:\n    - schedules\n  script:\n    - renovate $RENOVATE_EXTRA_FLAGS\n<\/code><\/pre><p>Renovate propose la plupart de ces configurations en variable d&rsquo;environnements.\nN\u00e9anmoins, pour pouvoir utiliser le bot dans d&rsquo;autres contextes que GitLab (un lancement manuel par exemple), je n&rsquo;ai gard\u00e9 que les param\u00e8tres sp\u00e9cifiques \u00e0 GitLab dans le fichier <code>.gitlab-ci.yml<\/code>.<\/p>\n<p>Le reste des configurations se trouvent donc dans le fichier <code>config.js<\/code> que nous allons regarder tout de suite. \u2b07\ufe0f<\/p>\n<h3 id=\"fichier-de-configuration-renovate\">Fichier de configuration Renovate<\/h3>\n<p>Renovate s&rsquo;appuie donc sur un fichier de configuration <code>config.js<\/code><\/p>\n<p>On y d\u00e9fini notamment la plateforme Git \u00e0 utiliser, <a href=\"https:\/\/docs.renovatebot.com\/self-hosted-configuration\/\">les options du configurations propres<\/a> au runner et les <a href=\"https:\/\/docs.renovatebot.com\/config-presets\/\">profils de configurations (Config Presets)<\/a> qui seront appliqu\u00e9s pour tous les projets <code>onboardingConfig<\/code>.<\/p>\n<pre tabindex=\"0\"><code>module.exports = {\n        endpoint: 'https:\/\/[url du gitlab]\/api\/v4\/',\n        platform: 'gitlab',\n        persistRepoData: true,\n        logLevel: 'debug',\n        onboardingConfig: {\n                &quot;extends&quot;: [\n                        &quot;local&gt;groupes\/sous-groupes\/renovate\/renovate-config&quot;\n                ],\n        },\n        autodiscover: true,\n};\n<\/code><\/pre><h3 id=\"t\u00e2che-r\u00e9currente\">T\u00e2che r\u00e9currente<\/h3>\n<ul>\n<li>Menu CI\/CD &gt; Schedules<\/li>\n<li>Cr\u00e9er une nouvelle t\u00e2che planifi\u00e9e avec la fr\u00e9quence de passage que vous souhaitez (j&rsquo;ai fait le choix, totalement arbitraire, de faire tourner l&rsquo;analyse 2 fois par jour) et \u00e0 d\u00e9clencher.<\/li>\n<li>Sauvegardez<\/li>\n<\/ul>\n<h2 id=\"configuration-des-configurations-par-d\u00e9faut\">Configuration des configurations par d\u00e9faut<\/h2>\n<p>Dans le projet <code>renovate-config<\/code> nous allons cr\u00e9er un simple fichier <code>renovate.json<\/code> qui sera celui recherchait par d\u00e9faut par le fichier <code>config.json<\/code> et notamment la ligne <code>&quot;local&gt;groupes\/sous-groupes\/renovate\/renovate-config&quot;<\/code>.<\/p>\n<p>Voici les choix que j&rsquo;ai fait :<\/p>\n<pre tabindex=\"0\"><code> {\n  &quot;$schema&quot;: &quot;https:\/\/docs.renovatebot.com\/renovate-schema.json&quot;,\n  &quot;packageRules&quot;: [\n    {\n      &quot;depTypeList&quot;: [&quot;devDependencies&quot;, &quot;require-dev&quot;],\n      &quot;updateTypes&quot;: [&quot;patch&quot;, &quot;minor&quot;, &quot;digest&quot;],\n      &quot;groupName&quot;: &quot;devDependencies (non-major)&quot;\n    }\n  ],\n  &quot;extends&quot;: [\n    &quot;config:base&quot;,\n    &quot;:preserveSemverRanges&quot;,\n    &quot;:dependencyDashboard&quot;,\n    &quot;:rebaseStalePrs&quot;,\n    &quot;:enableVulnerabilityAlertsWithLabel('security')&quot;,\n    &quot;group:recommended&quot;\n  ]\n}\n<\/code><\/pre><ul>\n<li>\n<p><code>packageRules<\/code> permet de cr\u00e9er des nouveaux regroupements. Dans notre cas, cela nous permet d&rsquo;avoir une seule Merge Request contenant toutes les d\u00e9pendances de d\u00e9veloppements du projet lorsqu&rsquo;elles sont non-majeures.<\/p>\n<\/li>\n<li>\n<p><code>extends<\/code> nous permet de d\u00e9finir les r\u00e8gles\/configs presets que l&rsquo;on souhaite activer. On active notamment le <a href=\"https:\/\/docs.renovatebot.com\/key-concepts\/dashboard\/\">Dependency Dashboard<\/a> qui permet de suivre dans un ticket l&rsquo;\u00e9tat de toutes les mises \u00e0 jour disponibles et\/ou des MR en cours et diff\u00e9rents  <a href=\"https:\/\/docs.renovatebot.com\/presets-group\/\">Group Presets<\/a>.<\/p>\n<\/li>\n<\/ul>\n<h2 id=\"activer-le-bot-pour-les-projets\">Activer le bot pour les projets<\/h2>\n<p>Avec ces configurations, chaque projet souhaite activer le bot n&rsquo;a que 4 \u00e9tapes \u00e0 suivre :<\/p>\n<ol>\n<li>Ajouter l&rsquo;utilisateur <code>renovatebot<\/code> sur le projet, avec au moins un droit de <code>contributeur<\/code><\/li>\n<li>Activer les <code>issues<\/code> pour le <code>dependency Dashboard<\/code><\/li>\n<li>Activer les <code>merge requests<\/code> pour tout d&rsquo;abord avec une 1\u00e8re Merge Request de cr\u00e9ation de la configuration renovate, puis les MR associ\u00e9es aux mises \u00e0 jours \u00e0 appliquer.<\/li>\n<li>Affiner les configurations de Renovate selon les sp\u00e9cificit\u00e9s de son projet<\/li>\n<\/ol>\n<p>Et voil\u00e0 \ud83c\udf7e<\/p>\n<p>Nous avons maintenant un bot Renovate qui tourne de fa\u00e7on r\u00e9guli\u00e8re et va updater l&rsquo;ensemble des repositories auxquels il a acc\u00e8s.<\/p>"},{"title":"Test de Gomplate","link":"https:\/\/gastaud.io\/article\/onedayonly\/gomplate\/","pubDate":"Sat, 20 Nov 2021 00:00:00 +0000","guid":"https:\/\/gastaud.io\/article\/onedayonly\/gomplate\/","description":"<p><a href=\"https:\/\/docs.gomplate.ca\/\">Gomplate<\/a> est un utilitaire en Go qui permet de faire un rendu dynamique.<\/p>\n<p>La possiblit\u00e9 d&rsquo;utiliser du templating Go, d&rsquo;utiliser les fonctions built-in de gotemplate et le support de datasources par Gomplate me paraissait int\u00e9ressant.<\/p>\n<h2 id=\"objectifs\">Objectifs<\/h2>\n<p>Ce test va probablement d\u00e9tourner un peu l&rsquo;objectif initial du produit mais je vais tenter de voir si Gomplate peut \u00eatre un candidat pour aider \u00e0 la g\u00e9n\u00e9ration de documentation dynamique.<\/p>\n<h2 id=\"le-projet\">Le projet<\/h2>\n<p>_Point de d\u00e9part\u00a0: <a href=\"https:\/\/docs.gomplate.ca\/\">Documentation de Gomplate<\/a><\/p>\n<h3 id=\"pr\u00e9paration\">Pr\u00e9paration<\/h3>\n<ul>\n<li>Installation de Gomplate<\/li>\n<\/ul>\n<p>Il existe de nombreuses m\u00e9thodes pour <a href=\"https:\/\/docs.gomplate.ca\/installing\/\">installer Gomplate<\/a>.<\/p>\n<p>De mon c\u00f4t\u00e9, j&rsquo;essaie en ce moment de privil\u00e9gier l&rsquo;utilisaton de <a href=\"\">asdf<\/a> pour g\u00e9rer mes utilitaires locaux et leurs versions.<\/p>\n<p>La proc\u00e9dure n&rsquo;\u00e9tant pas d\u00e9taill\u00e9e dans la documentation officielle Gomplate, la voici ci-dessous :<\/p>\n<ul>\n<li>Ajout du plugin<\/li>\n<\/ul>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-shell\" data-lang=\"shell\">asdf plugin add gomplate\n<\/code><\/pre><\/div><ul>\n<li>Installation de la version de gomplate souhait\u00e9e<\/li>\n<\/ul>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">asdf install gomplate v3.10.0\n<\/code><\/pre><\/div><ul>\n<li>Activation pour tout le syst\u00e8me<\/li>\n<\/ul>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">asdf global gomplate v3.10.0\n<\/code><\/pre><\/div><ul>\n<li>V\u00e9rification de bon fonctionnement<\/li>\n<\/ul>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">gomplate --version\ngomplate version 3.10.0\n<\/code><\/pre><\/div><p>Ca fonctionne ! \ud83d\udc4d<\/p>\n<h2 id=\"d\u00e9couverte-du-fonctionnement-de-loutil\">D\u00e9couverte du fonctionnement de l&rsquo;outil<\/h2>\n<h3 id=\"comment-d\u00e9clarer-un-template-\">Comment d\u00e9clarer un template ?<\/h3>\n<p>Pour notre besoin, nous allons avoir besoin de pouvoir cr\u00e9er un (1) ou plusieurs fichiers d&rsquo;entr\u00e9e qui contiendront des variables que l&rsquo;on souhaitera remplacer lors de la g\u00e9n\u00e9ration.<\/p>\n<p>La documentation de la CLI parle de l&rsquo;option <code>-t<\/code> pour un template mais cela ne semble pas \u00eatre r\u00e9ellement notre besoin.<\/p>\n<p>Par contre, la page de documentation sur la configuration mentionne des entr\u00e9es qui semblent prom\u00e9teuses <a href=\"https:\/\/docs.gomplate.ca\/config\/#inputfiles\">inputFiles<\/a> et <a href=\"https:\/\/docs.gomplate.ca\/config\/#inputdir\">inputDir<\/a>. \ud83c\udf7e<\/p>\n<p>Testons cela. \ud83d\ude80<\/p>\n<h3 id=\"test-avec-1-seul-template-en-entr\u00e9e\">Test avec 1 seul template en entr\u00e9e<\/h3>\n<p>Nous allons cr\u00e9er 2 fichiers pour ce test<\/p>\n<p><code>test.tpl<\/code> qui va simplement reprendre l&rsquo;exemple de base de gomplate<\/p>\n<pre tabindex=\"0\"><code>Hello {{ .Env.USER }}\n<\/code><\/pre><p>et le fichier <code>.gomplate.yaml<\/code><\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"color:#f92672\">inputFiles<\/span>:\n  - <span style=\"color:#ae81ff\">basic.tpl<\/span>\n<\/code><\/pre><\/div><p>V\u00e9rifions notre arborescence<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">tree -a\n.\n\u251c\u2500\u2500 basic.tpl\n\u2514\u2500\u2500 .gomplate.yaml\n\n<span style=\"color:#ae81ff\">0<\/span> directories, <span style=\"color:#ae81ff\">2<\/span> files\n<\/code><\/pre><\/div><p>Testons l&rsquo;ex\u00e9cution de gomplate<\/p>\n<pre tabindex=\"0\"><code>gomplate\nHello jyg\n<\/code><\/pre><h3 id=\"test-avec-plusieurs-fichiers-de-template\">Test avec plusieurs fichiers de template<\/h3>\n<p>Pour ce test, nous allons avoir une arborescence l\u00e9g\u00e8rement plus complexe avec l&rsquo;ajout d&rsquo;un dossier <code>in<\/code> dans lequel on va avoir 2 fois notre fichier pr\u00e9c\u00e9dent <code>basic.tpl<\/code> qui sera d\u00e9pos\u00e9 (et l\u00e9g\u00e8rement \u00e9dit\u00e9).<\/p>\n<pre tabindex=\"0\"><code>tree -a -L 2\n.\n\u251c\u2500\u2500 .gomplate.yaml\n\u251c\u2500\u2500 in\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 basic2.tpl\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 basic.tpl\n<\/code><\/pre><p>Nous faisons \u00e9voluer le fichier <code>.gomplate<\/code> comme ceci :<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"color:#f92672\">inputDir<\/span>: <span style=\"color:#ae81ff\">in\/<\/span>\n<\/code><\/pre><\/div><p>En ex\u00e9cutant <code>gomplate<\/code> on se rend compte qu&rsquo;il n&rsquo;y a pas d&rsquo;output dans le terminal.<\/p>\n<p>Par contre 2 fichiers on \u00e9tait cr\u00e9\u00e9 \u00e0 la racine de notre dossier :<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">tree -a -L <span style=\"color:#ae81ff\">2<\/span>\n.\n\u251c\u2500\u2500 basic2.tpl\n\u251c\u2500\u2500 basic.tpl\n\u251c\u2500\u2500 .gomplate.yaml\n\u251c\u2500\u2500 in\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 basic2.tpl\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 basic.tpl\n<\/code><\/pre><\/div><p>et  ces 2 fichiers contiennent bien la variable interpr\u00e9t\u00e9e.<\/p>\n<p>Il faut donc d\u00e9finir la valeur <code>outputDir<\/code> pour un mapping 1:1 ou <code>outputMap<\/code> si l&rsquo;on souhaite pouvoir agir sur le nom des fichiers de sorti.<\/p>\n<p>Exemple d&rsquo;utilisation d'<code>outputMap<\/code> pour modifier l&rsquo;extension du fichier de <code>tpl<\/code> \u00e0 <code>md<\/code> (markdown).<\/p>\n<pre tabindex=\"0\"><code>inputDir: in\/\noutputMap: |\n    out\/{{ .in | strings.ReplaceAll &quot;.tpl&quot; &quot;.md&quot; }}\n<\/code><\/pre><p>OK \u00e0 priori, avec ces 2 directives nous devrions \u00eatre en mesure de pouvoir avancer sur notre projet.<\/p>\n<h3 id=\"impl\u00e9mentation-du-projet\">Impl\u00e9mentation du projet<\/h3>\n<p>Pour notre test, nous allons reprendre la structure simplifier d&rsquo;un projet existant.<\/p>\n<p>Quelques \u00e9lements de contexte :<\/p>\n<ul>\n<li>le projet sert de documentation,<\/li>\n<li>chaque page de la documentation est un fichier markdown,<\/li>\n<li>\u00e0 la g\u00e9n\u00e9ration de la documentation, on souhaite \u00eatre capable de r\u00e9cup\u00e9rer ou injecter des variables, par exemple, la branche Git utilis\u00e9e pour la g\u00e9n\u00e9ration,<\/li>\n<li>on veut que les fichiers g\u00e9n\u00e9r\u00e9s respectent l&rsquo;arborescence initiale.<\/li>\n<\/ul>\n<h3 id=\"pr\u00e9paration-des-dossiers-et-fichiers\">Pr\u00e9paration des dossiers et fichiers<\/h3>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">tree -a -L <span style=\"color:#ae81ff\">4<\/span>\n.\n\u251c\u2500\u2500 .gomplate.yaml\n\u251c\u2500\u2500 in\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 en\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 docker-compose.md\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 security\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0     \u251c\u2500\u2500 cohesion.md\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0     \u2514\u2500\u2500 gitleaks.md\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 home.md\n<\/code><\/pre><\/div><p>Les d\u00e9tails int\u00e9ressants :<\/p>\n<ul>\n<li><code>.gomplate.yaml<\/code><\/li>\n<\/ul>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"color:#f92672\">inputDir<\/span>: <span style=\"color:#ae81ff\">in\/<\/span>\n<span style=\"color:#f92672\">outputDir<\/span>: <span style=\"color:#ae81ff\">out\/<\/span>\n<\/code><\/pre><\/div><ul>\n<li>dans le fichier <code>docker-compose.md<\/code> on va d\u00e9finir la r\u00e9cup\u00e9ration d&rsquo;une variable d&rsquo;environnement repr\u00e9sentant la branche Git.<\/li>\n<\/ul>\n<pre tabindex=\"0\"><code># Docker-compose\n\n## Docker-compose helper\n\n...\n\n```yaml\ninclude:\n  - project: '&lt;group&gt;\/&lt;project&gt;\/templates'\n    ref: {{ env.Getenv &quot;GITBRANCH&quot; }}\n    file: '\/docker-compose.gitlab-ci.yml'\n```\n\n...\n<\/code><\/pre><h4 id=\"ex\u00e9cution-et-v\u00e9rification\">Ex\u00e9cution et v\u00e9rification<\/h4>\n<p>On peut maintenant lancer la commande <code>GITBRANCH=test gomplate<\/code> et v\u00e9rifiier les r\u00e9sultats.<\/p>\n<ul>\n<li>\u2705 le dossier <code>out<\/code> contient bien la m\u00eame arborescence de fichiers ;<\/li>\n<\/ul>\n<pre tabindex=\"0\"><code>tree -a -L 4\n.\n\u251c\u2500\u2500 .gomplate.yaml\n\u251c\u2500\u2500 in\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 en\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 docker-compose.md\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 security\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0     \u251c\u2500\u2500 cohesion.md\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0     \u2514\u2500\u2500 gitleaks.md\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 home.md\n\u2514\u2500\u2500 out\n    \u251c\u2500\u2500 en\n    \u2502\u00a0\u00a0 \u251c\u2500\u2500 docker-compose.md\n    \u2502\u00a0\u00a0 \u2514\u2500\u2500 security\n    \u2502\u00a0\u00a0     \u251c\u2500\u2500 cohesion.md\n    \u2502\u00a0\u00a0     \u2514\u2500\u2500 gitleaks.md\n    \u2514\u2500\u2500 home.md\n<\/code><\/pre><ul>\n<li>\n<p>\u2705 la variable <code>{{ env.Getenv &quot;GITBRANCH&quot; }}<\/code>est bien remplac\u00e9e par la valeur attendue ;<\/p>\n<\/li>\n<li>\n<p>\u2705 il est possible de d\u00e9finir une valeur par d\u00e9faut <code>{{ env.Getenv &quot;GITBRANCH&quot; &quot;defaultbranch&quot; }}<\/code><\/p>\n<\/li>\n<\/ul>\n<pre tabindex=\"0\"><code># Docker-compose\n\n## Docker-compose helper\n\n...\n\n```yaml\ninclude:\n  - project: '&lt;group&gt;\/&lt;project&gt;\/templates'\n    ref: test\n    file: '\/docker-compose.gitlab-ci.yml'\n```\n\n...\n<\/code><\/pre><h2 id=\"conclusion\">Conclusion<\/h2>\n<p>Gomplate r\u00e9pond plut\u00f4t bien \u00e0 un besoin simple de g\u00e9n\u00e9ration \/ transformation de fichiers. En moins d'1 heure, en ne connaissant par l&rsquo;outil, nous avons r\u00e9ussi \u00e0 r\u00e9pondre \u00e0 notre besoin, sans partir dans des outils plus complexes \u00e0 installer et utiliser.<\/p>\n<p>Le fait que Gomplate soit disponible comme un simple binaire facilite son installation et est donc tr\u00e8s int\u00e9ressant \u00e0 utiliser aussi dans le cadre d&rsquo;une CI.<\/p>\n<h2 id=\"prochaines-\u00e9tapes\">Prochaines \u00e9tapes<\/h2>\n<ul>\n<li>Creuser les fonctionnalit\u00e9s\/fonctions natives de Gomplate, notamment l&rsquo;utilisation des datasources ;<\/li>\n<li>Voir l&rsquo;utilisation de Gomplate en remplacement de <code>envsubst<\/code> ;<\/li>\n<\/ul>\n"},{"title":"Git - Trier les tags d'un repo utilisant du semantic versioning","link":"https:\/\/gastaud.io\/article\/git-sort-tags-semantic-version\/","pubDate":"Mon, 20 Sep 2021 23:10:00 +0200","guid":"https:\/\/gastaud.io\/article\/git-sort-tags-semantic-version\/","description":"<p>Un rapide article aujourd&rsquo;hui pour vous parler d&rsquo;une d\u00e9couverte r\u00e9cente dans les fonctions de tri de Git et les tags bas\u00e9s sur le <a href=\"https:\/\/semver.org\/lang\/fr\/\">semantic versioning<\/a>.<\/p>\n<p>Par d\u00e9faut, si vous lancez la commande <code>git tag -l<\/code>, Git va faire un tri alphab\u00e9tique.<\/p>\n<p>Cependant, ce tri donne des r\u00e9sultats d\u00e9concertant lorsque les tags utilisent une notation bas\u00e9e sur le semantic versioning.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">git tag -l\nv2.6.1\nv2.6.10\nv2.6.11\nv2.6.12\nv2.6.2\nv2.6.3\nv2.6.4\nv2.6.5\nv2.6.6\nv2.6.7\nv2.6.8\nv2.6.9\nv2.7.0\n<\/code><\/pre><\/div><p>Not\u00e9 ici la s\u00e9quence <code>v2.6.10<\/code>, <code>v2.6.11<\/code> et <code>v2.6.12<\/code>  qui s&rsquo;intercale entre la version <code>v2.6.1<\/code> et <code>v2.6.2<\/code>.<\/p>\n<p>Afin de r\u00e9soudre ce souci, il est possible d&rsquo;utiliser la fonction de tri <code>--sort<\/code> avec l&rsquo;attribut <code>version<\/code> dans Git.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">git tag --sort<span style=\"color:#f92672\">=<\/span>version:refname\n<\/code><\/pre><\/div><p>Cela affichera les r\u00e9sultats tri\u00e9s de fa\u00e7on coh\u00e9rente :<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">git tag --sort<span style=\"color:#f92672\">=<\/span>version:refname\nv2.6.1\nv2.6.2\nv2.6.3\nv2.6.4\nv2.6.5\nv2.6.6\nv2.6.7\nv2.6.8\nv2.6.9\nv2.6.10\nv2.6.11\nv2.6.12\nv2.7.0\n<\/code><\/pre><\/div>"},{"title":"Mettre \u00e0 jour Buildx dans Docker CLI","link":"https:\/\/gastaud.io\/article\/docker-buildx-upgrade\/","pubDate":"Sat, 02 May 2020 14:29:42 +0200","guid":"https:\/\/gastaud.io\/article\/docker-buildx-upgrade\/","description":"<p>Docker CLI, depuis la version 19.03 inclut le plugin <a href=\"https:\/\/github.com\/docker\/buildx\">buildx<\/a> permettant d&rsquo;\u00e9tendre les fonctions de build de Docker en s&rsquo;appuyant sur <a href=\"https:\/\/github.com\/moby\/buildkit\">Buildkit<\/a>.<\/p>\n<p>Parmis les principaux points qu&rsquo;apportent Buildkit on notera les suivants :<\/p>\n<ul>\n<li>r\u00e9solution en parall\u00e8le des d\u00e9pendences<\/li>\n<li>meilleure gestion du cache (import\/export, r\u00e9solution)<\/li>\n<li>possibit\u00e9 de distribuer les charges de travail<\/li>\n<li>ex\u00e9cution sans droits root<\/li>\n<\/ul>\n<p>Et donc, comme indiqu\u00e9 plus haut, buildkit est maintenant inclut dans docker CLI.\nProbl\u00e8me, la version disponible et pr\u00e9-paquag\u00e9e n&rsquo;est pas \u00e0 jour des derni\u00e8res \u00e9volutions.<\/p>\n<p>Si vous souhaitez profiter des toutes derni\u00e8res \u00e9volutions,\nil vous sera n\u00e9cessaire de proc\u00e9der \u00e0 une mise \u00e0 jour.<\/p>\n\n<div class=\"shortcode-notice info\">\n  <div class=\"shortcode-notice-title info\">\n    \n      version de buildx\n    \n  <\/div>\n  <p>Ce tutoriel utilise la version <strong>0.4.1<\/strong> de buildx.<br>\nPensez \u00e0 v\u00e9rifier le num\u00e9ro de la derni\u00e8re version avant de copier\/coller les instructions.<\/p>\n\n<\/div>\n\n<h2 id=\"les-\u00e9tapes\">Les \u00e9tapes<\/h2>\n<p>V\u00e9rifier l&rsquo;existance du dossier <code>~\/.docker\/cli-plugins<\/code>.\nS&rsquo;il n&rsquo;existe pas, cr\u00e9ez le<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">mkdir ~\/.docker\/cli-plugins\n<\/code><\/pre><\/div><p>T\u00e9l\u00e9charger la derni\u00e8re version de buildx depuis la <a href=\"https:\/\/github.com\/docker\/buildx\/releases\">page de release Buildx sur Github<\/a> ou directement via votre terminal<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">wget -O ~\/.docker\/cli-plugins\/docker-buildx https:\/\/github.com\/docker\/buildx\/releases\/download\/v0.4.1\/buildx-v0.4.1.linux-amd64\n<\/code><\/pre><\/div><p>D\u00e9finir les droits d&rsquo;\u00e9x\u00e9cution sur le binaire : <code>chmod +x ~\/.docker\/cli-plugins\/docker-buildx<\/code><\/p>\n<p>et voil\u00e0 !<\/p>\n<p>Il ne nous reste plus qu&rsquo;\u00e0 v\u00e9rifier si la version de buildx est maintenant celle attendue<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">docker buildx version<span style=\"color:#e6db74\">`<\/span>\n<\/code><\/pre><\/div><p>Si vous voulez que buildx deviennent le builder par d\u00e9faut de Docker CLI<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">docker builx install\n<\/code><\/pre><\/div><h2 id=\"d\u00e9mo\">D\u00e9mo<\/h2>\n<p><a href=\"https:\/\/asciinema.org\/a\/aWtsg3uCTb2wbEeZHh79c2ntS\"><img src=\"https:\/\/asciinema.org\/a\/aWtsg3uCTb2wbEeZHh79c2ntS.svg\" alt=\"asciicast\"><\/a><\/p>\n"},{"title":"Azure-cli, activer l'auto-compl\u00e9tion sur Zsh","link":"https:\/\/gastaud.io\/article\/azure-cli-autocomplete\/","pubDate":"Tue, 26 Nov 2019 23:35:18 +0100","guid":"https:\/\/gastaud.io\/article\/azure-cli-autocomplete\/","description":"<p>Azure-cli n&rsquo;a pas d&rsquo;auto-compl\u00e9tion activ\u00e9e sur Zsh et le framework Oh-My-Zsh n&rsquo;a pas de plugin disponible en natif.<\/p>\n<p>Cependant, Azure-cli est install\u00e9 avec un script d&rsquo;auto-compl\u00e9tion pour bash qui devrait se trouver dans le r\u00e9pertoire <code>bash_completion.d<\/code>.<\/p>\n<p>Dans ma configuration, en utilisant Ubuntu 18.04, j&rsquo;ai trouv\u00e9 le script <code>azure-cli<\/code> dans un r\u00e9pertoire <code>\/etc\/bash_completion.d\/<\/code>.<\/p>\n<p>Maintenant nous avons juste besoin de charger le script autocomplete dans notre <code>~\/.zshrc<\/code> avec la ligne suivante :<\/p>\n<pre tabindex=\"0\"><code>source \/etc\/bash_completion.d\/azure-cli\n<\/code><\/pre><p>Sauvegardez et rechargez votre terminal. Fin.<\/p>\n<h4 id=\"notes-compl\u00e9mentaires\">Notes compl\u00e9mentaires<\/h4>\n<ul>\n<li>Si vous rencontrez une erreur au red\u00e9marrage, vous devrez peut-\u00eatre ajouter cette ligne avant le chargement du script pour assurer la compatibilit\u00e9 crois\u00e9e entre les scripts bash et zsh.<\/li>\n<\/ul>\n<pre tabindex=\"0\"><code>autoload -U +X bashcompinit &amp;&amp; bashcompinit\n<\/code><\/pre><ul>\n<li>\n<p>En regardant les diff\u00e9rents billets du blog, vous constaterez peut-\u00eatre que le script s&rsquo;appelle <code>az<\/code>, et non <code>azure-cli<\/code>.<\/p>\n<\/li>\n<li>\n<p>D&rsquo;apr\u00e8s le r\u00e9pertoire que je l&rsquo;ai trouv\u00e9, le script \u00e9tait charg\u00e9 nativement si j&rsquo;utilise le shell bash. Si vous utilisez bash et n&rsquo;avez pas l&rsquo;auto-compl\u00e9tion activ\u00e9e, vous devez probablement chercher le script <code>azure-cli<\/code> ou <code>az<\/code> puis le charger dans votre fichier <code>~\/.bashrc<\/code>.<\/p>\n<\/li>\n<\/ul>\n"},{"title":"Gitlab CI, After Script et Merge Request","link":"https:\/\/gastaud.io\/article\/gitlab-ci-after-script-merge-request\/","pubDate":"Wed, 23 Oct 2019 09:52:49 +0200","guid":"https:\/\/gastaud.io\/article\/gitlab-ci-after-script-merge-request\/","description":"<p>Gitlab CI propose de nombreuses int\u00e9grations lorsque vous lancez vos tests (junit par exemple).<\/p>\n<p>En compl\u00e9ment, ou parfois \u00e0 d\u00e9faut d&rsquo;avoir acc\u00e8s \u00e0 la version entreprise de Gitlab, il peut \u00eatre utile de voir le r\u00e9sultat d&rsquo;une commande ou d&rsquo;un export en commentaire de votre Merge Request.<\/p>\n<p>Pour r\u00e9aliser cela nous allons utiliser les options suivantes :<\/p>\n<ul>\n<li>les variables d&rsquo;environnement pr\u00e9d\u00e9finies dans Gitlab CI<\/li>\n<li>l&rsquo;option <code>only:merge_requests<\/code> de Gitlab CI<\/li>\n<li>la fonction <code>after_script<\/code> de Gitlab CI<\/li>\n<li>l&rsquo;API Gitlab pour les Merge Request<\/li>\n<li>un jeton (token) Gitlab \u00e0 g\u00e9n\u00e9rer en amont<\/li>\n<li>curl<\/li>\n<li><a href=\"https:\/\/stedolan.github.io\/jq\/manual\/\">JQ<\/a><\/li>\n<\/ul>\n<p>Avec ce process, nous pourrons ainsi avoir un retour dans notre Merge Request<\/p>\n\n\n\n\n\n\n\n\n\n\n  <img style=\"max-width: 100%; height: auto;\" src=\"https:\/\/gastaud.io\/article\/gitlab-ci-after-script-merge-request\/images\/gitlab-post-comment-on-merge-request_hu935644fc2fb52f1b6c28995c4714ec67_24687_1200x1200_fit_box_3.png\" width=\"1070\" height=\"160\">\n\n\n\n<h2 id=\"le-script-final\">Le script final<\/h2>\n<pre tabindex=\"0\"><code>post:comment:on:merge-request:\n  variables:\n    FILE_EXPORT_PATH: &quot;text-export.txt&quot;\n  before_script:\n    - apk --no-cache add curl jq\n  script:\n    - echo &quot;An awesome text to report&quot; &gt; text-export.txt\n  after_script:\n    - |\n      if [ -f &quot;text-export.txt&quot; ]\n      then\n        printf &quot;${CI_JOB_NAME} \\n&lt;pre&gt;$(cat text-export.txt)\\n&lt;\/pre&gt;\\n&quot; | \\\n        jq -R -c -s '{&quot;body&quot;:.}' - | \\\n        curl -X POST &quot;${CI_API_V4_URL}\/projects\/${CI_MERGE_REQUEST_PROJECT_ID}\/merge_requests\/${CI_MERGE_REQUEST_IID}\/notes&quot; \\\n          --header &quot;PRIVATE-TOKEN: ${GITLAB_PRIVATE_TOKEN}&quot; \\\n          --header &quot;Content-Type: application\/json&quot; \\\n          --data @-;\n      fi\n  only:\n    refs:\n      - merge_requests\n<\/code><\/pre><h2 id=\"details\">Details<\/h2>\n<h3 id=\"pr\u00e9paration\">Pr\u00e9paration<\/h3>\n<p>Tout d&rsquo;abord, nous devons nous assurer que notre image de base, une Alpine dans notre exemple, contient les outils <code>curl<\/code> et <code>jq<\/code>.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"color:#f92672\">before_script<\/span>:\n    - <span style=\"color:#ae81ff\">apk --no-cache add curl jq<\/span>\n<\/code><\/pre><\/div><h3 id=\"le-script\">Le script<\/h3>\n<p>Au niveau de notre script, nous allons exporter la sortie dans un fichier qui sera ensuite exploit\u00e9 dans l'<code>after_script<\/code><\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"color:#f92672\">script<\/span>:\n    - <span style=\"color:#ae81ff\">echo &#34;An awesome text to report&#34; &gt; ${FILE_EXPORT_PATH}<\/span>\n<\/code><\/pre><\/div><p>Nous allons nous assurer que le job n&rsquo;est ex\u00e9cut\u00e9 que lors d&rsquo;une Merge Request, ce qui nous garantira d&rsquo;avoir acc\u00e8s aux bonnes variables d&rsquo;environnement.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"color:#f92672\">only<\/span>:\n  <span style=\"color:#f92672\">refs<\/span>:\n    - <span style=\"color:#ae81ff\">merge_requests<\/span>\n<\/code><\/pre><\/div><h3 id=\"after-script\">After script<\/h3>\n<p>Enfin, notre <code>after_script<\/code>.<\/p>\n<p><code>printf<\/code> est utilis\u00e9 ici pour aider \u00e0 la mise en forme de notre texte, supprimer d&rsquo;\u00e9ventuelles contraintes de formatage&hellip;<\/p>\n<p>On utilise la variable <code>CI_JOB_NAME<\/code> pour pouvoir directement voir dans le commentaire quel job \u00e0 renvoyer l&rsquo;information.<\/p>\n<pre tabindex=\"0\"><code>printf &quot;${CI_JOB_NAME} \\n&lt;pre&gt;$(cat text-export.txt)\\n&lt;\/pre&gt;\\n&quot; | \\\n<\/code><\/pre><p><code>jq<\/code> arrive ensuite pour formater le contenu avant sa transmission \u00e0 l&rsquo;API Gitlab.<\/p>\n<pre tabindex=\"0\"><code>jq -R -c -s '{&quot;body&quot;:.}' - | \\\n<\/code><\/pre><p>Enfin, l&rsquo;envoi \u00e0 l&rsquo;API Gitlab en utilisant les variables suivantes <code>CI_API_V4_URL<\/code>, <code>CI_MERGE_REQUEST_PROJECT_ID<\/code>, <code>CI_MERGE_REQUEST_IID<\/code>, <code>GITLAB_PRIVATE_TOKEN<\/code>.<\/p>\n<p>La variable <code>GITLAB_PRIVATE_TOKEN<\/code> est la seule qui n&rsquo;est pas &ldquo;nativement&rdquo; disponible. Il vous faudra la g\u00e9n\u00e9rer avec l&rsquo;utilisateur qui postera le commentaire et la renseigner dans les variables CI\/CD de votre projet.<\/p>\n<pre tabindex=\"0\"><code>curl -X POST &quot;${CI_API_V4_URL}\/projects\/${CI_MERGE_REQUEST_PROJECT_ID}\/merge_requests\/${CI_MERGE_REQUEST_IID}\/notes&quot; \\\n    --header &quot;PRIVATE-TOKEN: ${GITLAB_PRIVATE_TOKEN}&quot; \\\n    --header &quot;Content-Type: application\/json&quot; \\\n    --data @-;\n<\/code><\/pre><h2 id=\"quelques-cas-dusage-possibles\">Quelques cas d&rsquo;usage possibles<\/h2>\n<ul>\n<li>Terraform plan<\/li>\n<li>Fuite de mots de passe \/ cl\u00e9s<\/li>\n<li>Retours tests d&rsquo;intrusions<\/li>\n<li>Retours de tests de performance<\/li>\n<li>&hellip;<\/li>\n<\/ul>\n"},{"title":"Test de Ansible Molecule","link":"https:\/\/gastaud.io\/article\/onedayonly\/molecule\/","pubDate":"Sat, 10 Aug 2019 00:00:00 +0000","guid":"https:\/\/gastaud.io\/article\/onedayonly\/molecule\/","description":"<p>Pour l&rsquo;un de nos clients chez <a href=\"https:\/\/www.clever-age.com\">Clever Age<\/a>, nous avons appuyer une grosse partie de notre provisioning de l&rsquo;ensemble de nos environnements avec Ansible et nous utilisons un grand nombre de r\u00f4les cr\u00e9\u00e9s par l&rsquo;excellant <a href=\"https:\/\/github.com\/geerlingguy\">Geerlingguy<\/a>.<\/p>\n<p>L&rsquo;un de ces r\u00f4les est le <a href=\"https:\/\/github.com\/geerlingguy\/ansible-role-php\">r\u00f4le Ansible PHP<\/a>.<\/p>\n<p>Le r\u00f4le permet de confier \u00e0 Ansible la gestion du fichier du pool php-fpm.<\/p>\n<p>Malheureusement, il n&rsquo;est pas possible de modifier certaines variables du fichier d&rsquo;origine ou d&rsquo;ajouter de nouvelles lignes simplement.<\/p>\n<h2 id=\"objectifs\">Objectifs<\/h2>\n<p>Nous voulons donc cr\u00e9er un nouveau r\u00f4le qui d\u00e9pendra du r\u00f4le <code>geerlingguy.php<\/code> et viendra g\u00e9rer de nouvelles variables, notamment\u00a0:<\/p>\n<ul>\n<li><code>pm<\/code> pour pouvoir passer de <code>dynamic<\/code> \u00e0 <code>static<\/code><\/li>\n<li><code>pm.max_requests<\/code><\/li>\n<\/ul>\n<h2 id=\"le-projet\">Le projet<\/h2>\n<p><em>Point de d\u00e9part\u00a0: <a href=\"https:\/\/molecule.readthedocs.io\/en\/stable\/\">Documentation de Molecule<\/a><\/em><\/p>\n<h3 id=\"pr\u00e9paration\">Pr\u00e9paration<\/h3>\n<ul>\n<li>Installation de molecule<\/li>\n<\/ul>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">pip install --user molecule\n<\/code><\/pre><\/div><h3 id=\"impl\u00e9mentation\">Impl\u00e9mentation<\/h3>\n<p>Molecule permet de g\u00e9n\u00e9rer directement un nouveau r\u00f4le Ansible avec une pr\u00e9configuration des tests.\nNous allons utiliser cette fonctionnalit\u00e9 pour cr\u00e9er notre r\u00f4le.<\/p>\n<h4 id=\"g\u00e9n\u00e9rer-un-nouveau-r\u00f4le-ansible-avec-molecule\">G\u00e9n\u00e9rer un nouveau r\u00f4le Ansible avec Molecule<\/h4>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">molecule init role --role-name newrole.php --verifier-name testinfra --driver-name docker\n<\/code><\/pre><\/div><p>On d\u00e9finit ici le nom du r\u00f4le, l&rsquo;outil de tests qui nous servira \u00e0 v\u00e9rifier le r\u00e9sultat du provisioning, <code>verifier<\/code>, et le pilote, <code>driver<\/code>, qui va ex\u00e9cuter recevoir l&rsquo;\u00e9x\u00e9cution des tests.<\/p>\n<p><em>A noter : on d\u00e9finit ici testinfra et docker pour l&rsquo;exemple, n\u00e9anmoins ce sont les valeurs par d\u00e9faut.<\/em><\/p>\n<p>Une fois le r\u00f4le g\u00e9n\u00e9r\u00e9, vous obtenez la structure d&rsquo;un r\u00f4le classique et un ensemble de dossiers et fichiers sp\u00e9cifique \u00e0 Molecule\u00a0:<\/p>\n<pre tabindex=\"0\"><code>.\n\u251c\u2500\u2500 defaults\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 main.yml\n\u251c\u2500\u2500 handlers\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 main.yml\n\u251c\u2500\u2500 meta\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 main.yml\n\u251c\u2500\u2500 molecule\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 default\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 Dockerfile.j2\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 INSTALL.rst\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 molecule.yml\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 playbook.yml\n\u2502\u00a0\u00a0     \u2514\u2500\u2500 tests\n\u2502\u00a0\u00a0         \u2514\u2500\u2500 test_default.py\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 tasks\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 main.yml\n\u2514\u2500\u2500 vars\n    \u2514\u2500\u2500 main.yml\n<\/code><\/pre><p>En regardant le fichier <code>Install.rst<\/code>, on se rend compte que pour faire fonctionner molecule avec Docker il faut ajouter un sous-module \u00e0 notre syst\u00e8me\u00a0:<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">pip install <span style=\"color:#e6db74\">&#39;molecule[docker]&#39;<\/span>\n<\/code><\/pre><\/div><p>Si vous suivez une approche TDD, il est maintenant temps d&rsquo;aller modifier les fichiers <code>molecule.yaml<\/code> et <code>playbook.yaml<\/code> pour y ajouter les \u00e9l\u00e9ments \u00e0 provisionner.<\/p>\n<h4 id=\"d\u00e9finir-son-playbook-de-tests\">D\u00e9finir son playbook de tests<\/h4>\n<p>L&rsquo;objectif du playbook est de tester le r\u00f4le courant et ses \u00e9ventuels d\u00e9pendances.<\/p>\n<p>On va donc d\u00e9finir un playbook, dans le fichier <code>playbook.yaml<\/code>, avec nos variables et les r\u00f4les \u00e0 appliquer<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-yaml\" data-lang=\"yaml\">---\n- <span style=\"color:#f92672\">name<\/span>: <span style=\"color:#ae81ff\">Converge<\/span>\n  <span style=\"color:#f92672\">hosts<\/span>: <span style=\"color:#ae81ff\">all<\/span>\n  <span style=\"color:#f92672\">become<\/span>: <span style=\"color:#66d9ef\">yes<\/span>\n\n  <span style=\"color:#f92672\">vars<\/span>:\n    <span style=\"color:#f92672\">php_enable_webserver<\/span>: <span style=\"color:#66d9ef\">false<\/span>\n    <span style=\"color:#f92672\">php_enable_php_fpm<\/span>: <span style=\"color:#66d9ef\">true<\/span>\n    <span style=\"color:#f92672\">php_install_recommends<\/span>: <span style=\"color:#66d9ef\">false<\/span>\n    <span style=\"color:#f92672\">php_fpm_pm_max_requests<\/span>: <span style=\"color:#ae81ff\">1000<\/span>\n    <span style=\"color:#f92672\">php_fpm_pm_type<\/span>: <span style=\"color:#e6db74\">&#34;static&#34;<\/span>\n\n  <span style=\"color:#f92672\">pre_tasks<\/span>:\n    - <span style=\"color:#f92672\">name<\/span>: <span style=\"color:#ae81ff\">Update apt cache.<\/span>\n      <span style=\"color:#f92672\">apt<\/span>: <span style=\"color:#ae81ff\">update_cache=true cache_valid_time=3600<\/span>\n      <span style=\"color:#f92672\">when<\/span>: <span style=\"color:#ae81ff\">ansible_os_family == &#39;Debian&#39;<\/span>\n      <span style=\"color:#f92672\">changed_when<\/span>: <span style=\"color:#66d9ef\">false<\/span>\n\n  <span style=\"color:#f92672\">roles<\/span>:\n    - <span style=\"color:#f92672\">role<\/span>: <span style=\"color:#ae81ff\">geerlingguy.php<\/span>\n    - <span style=\"color:#f92672\">role<\/span>: <span style=\"color:#ae81ff\">newrole.php<\/span>\n\n<\/code><\/pre><\/div><p>Dans notre cas, on va tester l&rsquo;installation du r\u00f4le <code>geerlingguy.php<\/code> suivi de notre nouveau role <code>newrole.php<\/code> et passer les 2 variables que l&rsquo;on souhaite v\u00e9rifier par la suite :<\/p>\n<ul>\n<li><code>php_fpm_pm_max_requests: 1000<\/code><\/li>\n<li><code>php_fpm_pm_type: &quot;static&quot;<\/code><\/li>\n<\/ul>\n<h4 id=\"\u00e9crire-des-tests-avec-testinfra\">\u00c9crire des tests avec testinfra<\/h4>\n<p>Avant de lancer les tests, on va \u00e9crire un petit test avec <code>testinfra<\/code>, un module Pyhton pour tester ses configurations.<\/p>\n<p>Dans le fichier <code>newrole.php\/molecule\/default\/tests\/test_default.py<\/code>, nous allons ajouter une nouvelle fonction<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-python\" data-lang=\"python\"><span style=\"color:#f92672\">import<\/span> os\n\n<span style=\"color:#f92672\">import<\/span> testinfra.utils.ansible_runner\n\ntestinfra_hosts <span style=\"color:#f92672\">=<\/span> testinfra<span style=\"color:#f92672\">.<\/span>utils<span style=\"color:#f92672\">.<\/span>ansible_runner<span style=\"color:#f92672\">.<\/span>AnsibleRunner(\n    os<span style=\"color:#f92672\">.<\/span>environ[<span style=\"color:#e6db74\">&#39;MOLECULE_INVENTORY_FILE&#39;<\/span>])<span style=\"color:#f92672\">.<\/span>get_hosts(<span style=\"color:#e6db74\">&#39;all&#39;<\/span>)\n\n<span style=\"color:#66d9ef\">def<\/span> <span style=\"color:#a6e22e\">test_php_pool_file<\/span>(host):\n    f <span style=\"color:#f92672\">=<\/span> host<span style=\"color:#f92672\">.<\/span>file(<span style=\"color:#e6db74\">&#39;\/etc\/php\/7.0\/fpm\/pool.d\/www.conf&#39;<\/span>)\n\n    <span style=\"color:#75715e\"># Check if www.conf file exists<\/span>\n    <span style=\"color:#66d9ef\">assert<\/span> f<span style=\"color:#f92672\">.<\/span>exists\n\n    c <span style=\"color:#f92672\">=<\/span> f<span style=\"color:#f92672\">.<\/span>content\n\n    <span style=\"color:#75715e\"># Check if our 2 variables exists with the right values<\/span>\n    <span style=\"color:#66d9ef\">assert<\/span> <span style=\"color:#e6db74\">b<\/span><span style=\"color:#e6db74\">&#39;pm = static&#39;<\/span> <span style=\"color:#f92672\">in<\/span> c\n    <span style=\"color:#66d9ef\">assert<\/span> <span style=\"color:#e6db74\">b<\/span><span style=\"color:#e6db74\">&#39;pm.max_requests = 1000&#39;<\/span> <span style=\"color:#f92672\">in<\/span> c\n\n<\/code><\/pre><\/div><h4 id=\"lancer-les-tests\">Lancer les tests<\/h4>\n<p>La commande <code>molecule test<\/code> va lancer l&rsquo;ensemble des \u00e9tapes de tests (cr\u00e9ation, lint, provision, tests&hellip;).<\/p>\n<p>Si l&rsquo;on veut uniquement le provisioning, il est possible de lancer uniquement la commande <code>molecule converge<\/code>.<\/p>\n<p>Si l&rsquo;on veut tester manuellement le r\u00e9sultat du provisioning, <code>molecule verify<\/code> sera \u00e0 lancer.<\/p>\n<h3 id=\"les-comandes-essentielles\">Les comandes essentielles<\/h3>\n<ul>\n<li><code>molecule test<\/code> permet de lancer toutes les \u00e9tapes<\/li>\n<li><code>molecule converge<\/code> permet de lancer le provisioning<\/li>\n<li><code>molecule verify<\/code> permet de lancer uniquement les tests<\/li>\n<li><code>molecule login<\/code> permet de se connecter dans le conteneur<\/li>\n<li><code>molecule destroy<\/code> pour supprimer le conteneur<\/li>\n<\/ul>\n<h2 id=\"r\u00e9sum\u00e9\">R\u00e9sum\u00e9<\/h2>\n<p>Les objectifs atteints :<\/p>\n<ul>\n<li><input checked=\"\" disabled=\"\" type=\"checkbox\"> Un nouveau r\u00f4le Ansible qui permet de surcharger les variables du fichier pool de fpm<\/li>\n<li><input checked=\"\" disabled=\"\" type=\"checkbox\"> Apprentissage des commandes principales de <code>molecule<\/code><\/li>\n<li><input checked=\"\" disabled=\"\" type=\"checkbox\"> D\u00e9couverte de <code>testinfra<\/code><\/li>\n<li><input checked=\"\" disabled=\"\" type=\"checkbox\"> Un test, minimalistes, pour v\u00e9rifier si les valeurs sont bien disponibles dans le fichier avec <code>testinfra<\/code><\/li>\n<\/ul>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>Molecule permet de tester simplement dans divers contextes ses r\u00f4les Ansible.\nSans garantir \u00e0 100% l&rsquo;int\u00e9grit\u00e9 du r\u00e9sultat lors de l&rsquo;application sur un autre environnement, il permet d&rsquo;avoir un niveau de confiance bien plus \u00e9lev\u00e9 et de simplifier les tests de non r\u00e9gressions.<\/p>\n<h2 id=\"prochaines-\u00e9tapes\">Prochaines \u00e9tapes<\/h2>\n<ul>\n<li>G\u00e9n\u00e9raliser l&rsquo;utilisation de molecule sur tous les nouveaux d\u00e9veloppements de r\u00f4le<\/li>\n<li>Ajouter des tests sur les r\u00f4les existants<\/li>\n<li>Creuser les options disponibles pour lancer des tests en mode matrice<\/li>\n<li>Tester le provisioning d&rsquo;un ensemble plus cons\u00e9quent de r\u00f4les avec Molecule<\/li>\n<li>Tester le framework de test <code>goss<\/code> en compl\u00e9ment \/ remplacement de <code>testinfra<\/code><\/li>\n<\/ul>\n"},{"title":"Git - Tips & Tricks","link":"https:\/\/gastaud.io\/article\/git-tips-tricks\/","pubDate":"Tue, 23 Apr 2019 14:44:54 +0200","guid":"https:\/\/gastaud.io\/article\/git-tips-tricks\/","description":"<p>Git est un outil tr\u00e8s puissant et il propose de nombreuses fonctionnalit\u00e9s qui ne sont pas toujours connues ou simple \u00e0 appr\u00e9hender.<\/p>\n<p>Vous trouverez ci-dessous quelques astuces pour vous aider au quotidien et ne plus avoir peur de faire une b\u00eatise !<\/p>\n<h2 id=\"visualiser-son-historique\">Visualiser son historique<\/h2>\n<h3 id=\"visualiser-lhistorique-des-logs-de-la-branche-courante\">Visualiser l&rsquo;historique des logs de la branche courante<\/h3>\n<pre tabindex=\"0\"><code>git log\n<\/code><\/pre><h3 id=\"am\u00e9liorer-laffichage-de-ses-logs\">Am\u00e9liorer l&rsquo;affichage de ses logs<\/h3>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">git log --pretty<span style=\"color:#f92672\">=<\/span>format:<span style=\"color:#e6db74\">&#34;%h %ad | %s%d [%an]&#34;<\/span> --graph --date<span style=\"color:#f92672\">=<\/span>short\n<\/code><\/pre><\/div><p>que vous pouvez alias-er par exemple en  <code>git hist<\/code> avec la commande suivante<\/p>\n<pre tabindex=\"0\"><code>git config --global alias.hist 'log --pretty=format:&quot;%h %ad | %s%d [%an]&quot; --graph --date'\n<\/code><\/pre><p>ou en modifiant votre fichier de configuration <code>~\/.gitconfig<\/code>, section <code>[alias]<\/code><\/p>\n<pre tabindex=\"0\"><code>[alias]\n  hist = log --pretty=format:\\&quot;%h %ad | %s%d [%an]\\&quot; --graph --date=short\n<\/code><\/pre><h3 id=\"comparer-2-branches\">Comparer 2 branches<\/h3>\n<pre tabindex=\"0\"><code>git log &lt;branch&gt;..&lt;branch&gt;\n<\/code><\/pre><p>pour plus de d\u00e9tails : <a href=\"https:\/\/gastaud.io\/article\/git-compare-branches\/\">Git - Comparer les commits de 2 branches Git<\/a><\/p>\n<h2 id=\"v\u00e9rifier--modifier-ce-que-lon-commit\">V\u00e9rifier \/ modifier ce que l&rsquo;on commit<\/h2>\n<h3 id=\"d\u00e9couper-ses-modifications-en-plusieurs-commits\">D\u00e9couper ses modifications en plusieurs commits<\/h3>\n<pre tabindex=\"0\"><code>git add -p\n<\/code><\/pre><p>Vous pourrez avec cette commande ne s\u00e9lectionner que les parties qui vous int\u00e9ressent.<\/p>\n<h3 id=\"faire-un-diff-sur-un-nouveau-fichier\">Faire un diff sur un nouveau fichier<\/h3>\n<pre tabindex=\"0\"><code>git diff new_file.txt # Ne renvoi rien\ngit add --intent-to-add new_file.txt # ou git add -N\ngit diff new_file.txt # Renvoi le contenu du fichier\n<\/code><\/pre><p>Voir le d\u00e9tail sur <a href=\"https:\/\/stackoverflow.com\/a\/24347875\">Stackoverflow<\/a><\/p>\n<h3 id=\"commiter-tous-les-fichiers-modifi\u00e9s\">Commiter tous les fichiers modifi\u00e9s<\/h3>\n<p>Si vous avez effectu\u00e9 un diff et que vous \u00eates certains que toutes les modifications sont \u00e0 commiter, vous pouvez utiliser<\/p>\n<pre tabindex=\"0\"><code>git add --update\n<\/code><\/pre><p>ou<\/p>\n<pre tabindex=\"0\"><code>git add -u\n<\/code><\/pre><h3 id=\"ajout-interactif\">Ajout interactif<\/h3>\n<p>La commande <code>git add -i<\/code> peut \u00e9galement \u00eatre bien pratique, pour combiner plusieurs actions. Par exemple l&rsquo;update complet d&rsquo;un fichier, r\u00e9cup\u00e9rer en mode patch certains \u00e9l\u00e9ments d&rsquo;un autre fichier ou encore ajouter des fichiers jamais commit\u00e9s.<\/p>\n<h4 id=\"ajout-interactif-de-fichiers-jamais-commit\u00e9s\">Ajout interactif de fichiers jamais commit\u00e9s<\/h4>\n<p>Lorsque vous avez des fichiers non track\u00e9s et que vous voulez les ajouter au repository, vous ne voulez pas forc\u00e9ment ajouter tout le r\u00e9pertoire.<\/p>\n<p>Exemple :<\/p>\n<pre tabindex=\"0\"><code>git status\nSur la branche preview\nModifications qui seront valid\u00e9es :\nFichiers non suivis:\n  (utilisez &quot;git add &lt;fichier&gt;...&quot; pour inclure dans ce qui sera valid\u00e9)\n\n\tcontent\/article\/openshift\/\n<\/code><\/pre><p>On voit ici que le dossier <code>content\/article\/openshift\/<\/code> n&rsquo;est pas pr\u00e9sent dans le repository et doit donc \u00eatre ajout\u00e9 via <code>git add<\/code>.<\/p>\n<p>Si nous faisons uniquement <code>git add content\/article\/openshift\/<\/code> alors tous les fichiers seront mis dans l&rsquo;index.<\/p>\n<p>Si le nombre de fichiers est limit\u00e9, il sera rapide d&rsquo;ajouter simplement le nom du fichier \u00e0 la suite de la commande <code>git add content\/article\/openshift\/&lt;my-file&gt;<\/code>.<\/p>\n<p>Par contre, si vous avez plusieurs fichiers, alors la commande <code>--interactive<\/code> va grandement vous aider.<\/p>\n<p>Etapes :<\/p>\n<ul>\n<li>Lancer <code>git add -i<\/code><\/li>\n<li>Presser 4 pour afficher les fichiers non track\u00e9s<\/li>\n<li>La liste affich\u00e9e est num\u00e9rot\u00e9.<\/li>\n<\/ul>\n<p>Vous avez maintenant les options suivantes :<\/p>\n<ol>\n<li>Soit saisir un num\u00e9ro et presser entr\u00e9e. Puis un autre num\u00e9ro etc&hellip;<\/li>\n<li>Si vous avez des plages de num\u00e9ros, vous pouvez les d\u00e9clarer via la syntaxe : <code>&lt;num1&gt;-&lt;num2&gt;<\/code><\/li>\n<li>Vous pouvez \u00e9galement utiliser le <code>;<\/code> pour s\u00e9parer 2 num\u00e9ros : <code>1;2;3;4;12<\/code><\/li>\n<\/ol>\n<p>Toutes ces options sont bien entendus combinables : <code>1-4;12<\/code><\/p>\n<h3 id=\"corriger-son-dernier-commit\">Corriger son dernier commit<\/h3>\n<ul>\n<li>Ajouter les modifications au commit pr\u00e9c\u00e9dent et vous proposer de modifier le message<\/li>\n<\/ul>\n<pre tabindex=\"0\"><code>git commit --amend\n<\/code><\/pre><ul>\n<li>Ajouter les modifications au commit pr\u00e9c\u00e9dent sans modification du message de commit<\/li>\n<\/ul>\n<pre tabindex=\"0\"><code>git commit --amend --no-edit\n<\/code><\/pre><h3 id=\"corriger-un-pr\u00e9c\u00e9dent-commit\">Corriger un pr\u00e9c\u00e9dent commit<\/h3>\n<h4 id=\"version-1\">Version 1<\/h4>\n<pre tabindex=\"0\"><code>git commit\ngit rebase -i &lt;head&gt;\n<\/code><\/pre><p>Puis d\u00e9placer le dernier commit au niveau du commit dans lequel l&rsquo;int\u00e9grer et d\u00e9finir le mot cl\u00e9 \u00e0 <code>fixup<\/code> ou juste la lettre <code>f<\/code>.<\/p>\n<h4 id=\"version-2\">Version 2<\/h4>\n<pre tabindex=\"0\"><code>git commit --fixup &lt;hash&gt;\n<\/code><\/pre><p>va automatiquement reprendre le nom du commit cibl\u00e9, pr\u00e9fix\u00e9 par fixup!<\/p>\n<p>Exemple :<\/p>\n<pre tabindex=\"0\"><code>* 61739a7 2019-04-25 | fixup! new commit (HEAD -&gt; master) [Jean-Yves Gastaud]\n* ccb6360 2019-04-23 | rename file [Jean-Yves Gastaud]\n* 0dfc522 2019-04-23 | new commit [Jean-Yves Gastaud]\n* 229fe83 2019-04-23 | new commit [Jean-Yves Gastaud]\n* c1420d3 2019-04-23 | new commit [Jean-Yves Gastaud]\n* cb893a3 2019-04-23 | new commit [Jean-Yves Gastaud]\n* b95163d 2019-04-23 | Good commit [Jean-Yves Gastaud]\n<\/code><\/pre><p>Il est ainsi possible de demander \u00e0 Git de rebase l&rsquo;historique dans le bon ordre automatiquement<\/p>\n<pre tabindex=\"0\"><code>git rebase -i --autosquash &lt;hash&gt;\n<\/code><\/pre><p>Va proposer automatiquement la fusion des commits marqu\u00e9s en <code>fixup<\/code> avec son commit de r\u00e9f\u00e9rence.<\/p>\n<pre tabindex=\"0\"><code>pick b95163d Good commit\npick cb893a3 new commit\nfixup 61739a7 fixup! new commit\npick c1420d3 new commit\npick 229fe83 new commit\npick 0dfc522 new commit\npick ccb6360 rename file\n<\/code><\/pre><p>!! Attention aux conflits quand m\u00eame !!<\/p>\n<h3 id=\"faire-un-rebase-en-incluant-le-tout-1er-commit\">Faire un rebase en incluant le tout 1er commit<\/h3>\n<pre tabindex=\"0\"><code>git rebase -i --root\n<\/code><\/pre><h2 id=\"squash-vs-fixup\">Squash VS Fixup<\/h2>\n<h3 id=\"fixup\">Fixup<\/h3>\n<p>Comme vu ci-dessus, fixup va inclure un commit dans le commit pr\u00e9c\u00e9dent.<\/p>\n<p>Le message du commit et la description du commit parent seront conserv\u00e9s.<\/p>\n<h3 id=\"squash\">Squash<\/h3>\n<p>Un squash est le regroupement d&rsquo;un ou plusieurs commits dans un parent, de la m\u00eame mani\u00e8re que fixup.<\/p>\n<p>C&rsquo;est une pratique couramment utilis\u00e9 dans les projets Open Source afin de simplifier le tracking entre une Merge Request et un commit.<\/p>\n<p>A la diff\u00e9rence de fixup, squash va afficher l&rsquo;\u00e9diteur de texte avec les messages et descriptions des n commits merg\u00e9s et vous proposer de modifier la description pour correspondre au regroupement de vos commis.<\/p>\n<p>Tout comme pour fixup, il est possible de d\u00e9finir directement un commit comme \u00e9tat &ldquo;\u00e0 squasher&rdquo; :<\/p>\n<pre tabindex=\"0\"><code>git commit --squash &lt;hash&gt;\n<\/code><\/pre><p>puis, comme pour fixup,<\/p>\n<pre tabindex=\"0\"><code>git rebase -i --autosquash &lt;hash&gt;\n<\/code><\/pre><h3 id=\"modifier-lhistorique-dune-branche\">Modifier l&rsquo;historique d&rsquo;une branche<\/h3>\n<pre tabindex=\"0\"><code>git rebase -i &lt;hash&gt;\n<\/code><\/pre><h3 id=\"annuler-un-rebase-en-cours\">Annuler un rebase en cours<\/h3>\n<p>Oula, qu&rsquo;est ce qu&rsquo;il se passe ici ?<\/p>\n<p>Je vais plut\u00f4t annuler \u00e7a !<\/p>\n<pre tabindex=\"0\"><code>git rebase --abort\n<\/code><\/pre><h3 id=\"valider-une-r\u00e9solution-de-conflit-dans-un-rebase\">Valider une r\u00e9solution de conflit dans un rebase<\/h3>\n<pre tabindex=\"0\"><code>git add &lt;file&gt;\ngit rebase --continue\n<\/code><\/pre><p>J&rsquo;ajoute mon correctif de conflit et je continue le rebase !<\/p>\n<p>Mon correctif sera inclu dans le commit qui vient d&rsquo;\u00eatre appliqu\u00e9. C&rsquo;est pour cela que je n&rsquo;ai pas besoin de commit\u00e9 \u00e0 nouveau.<\/p>\n<h3 id=\"pas-de-changement-lors-du-rebase-\">Pas de changement lors du rebase ?<\/h3>\n<p>Parfois, un message indique que l&rsquo;application du commit n&rsquo;apporte pas de changement.<\/p>\n<p>Le commit est donc d\u00e9j\u00e0 inclus dans une modification d\u00e9j\u00e0 appliqu\u00e9 au d\u00e9p\u00f4t. Il peut \u00eatre supprim\u00e9 sans craintes.<\/p>\n<pre tabindex=\"0\"><code>git rebase --skip\n<\/code><\/pre><h3 id=\"retrouver-les-changements-effectu\u00e9es-sur-une-fonction\">Retrouver les changements effectu\u00e9es sur une fonction<\/h3>\n<p>Avec <code>git grep<\/code> tu peux d\u00e9j\u00e0 avoir un certain niveau d\u2019information (commit de la fonction, fichier dans laquelle elle est).<\/p>\n<p>Si tu as un changement de nom par contre\u2026<\/p>\n<p>Exemple :<\/p>\n<p><code>git grep -p csv_import_get_ftp_params_provider_2 $(git rev-list --all)<\/code> renvoi<\/p>\n<pre tabindex=\"0\"><code>a79ed97f14eede3cdd98ae53ce3b990b58bbd351:sites\/all\/modules\/custom\/csv_import\/csv_import_query.inc:function csv_import_get_ftp_params_provider_2() {\n8b7ca76ca554accd397fcec1073776d60280521e:sites\/all\/modules\/custom\/csv_import\/csv_import_query.inc:function csv_import_get_ftp_params_provider_2() {\n118ffd0839985555dbaadd9edd9846ce9c18eb05:sites\/all\/modules\/custom\/csv_import\/csv_import.module:function csv_import_get_ftp_params_provider_2() {\n2b5196af0dddd28744acbf0ec015b6d59bddedee:sites\/all\/modules\/custom\/csv_import\/csv_import.module:function csv_import_get_ftp_params_provider_2() {\nd58b7662c6a8d47793fd79ec25030ee5c7cee4a6:sites\/all\/modules\/custom\/csv_import\/csv_import.module:function csv_import_get_ftp_params_provider_2() {\n<\/code><\/pre><h2 id=\"cr\u00e9er-partager-et-appliquer-un-patch\">Cr\u00e9er, partager et appliquer un patch<\/h2>\n<h3 id=\"cr\u00e9er-un-patch\">Cr\u00e9er un patch<\/h3>\n<h4 id=\"format-patch\"><code>format-patch<\/code><\/h4>\n<pre tabindex=\"0\"><code>git format-patch &lt;hash&gt;..HEAD\n<\/code><\/pre><p>va g\u00e9n\u00e9rer n fichiers (o\u00f9 n est \u00e9gale au nombre de commits entre le hash et HEAD)<\/p>\n<p>Si vous voulez regrouper les commits en 1 seul fichier<\/p>\n<pre tabindex=\"0\"><code>git format-patch -k --stdout &lt;hash&gt;..&lt;hash&gt; &gt; my.patch\n<\/code><\/pre><p>L&rsquo;option <code>-k<\/code> permet de garder sujet\/message du commit intact et ne pas avoir la mention <code>[PATCH]<\/code> ins\u00e9rer automatiquement.<\/p>\n<p>Exemple : <code>wk-patch<\/code> n&rsquo;a pas l&rsquo;option <code>-k<\/code><\/p>\n<pre tabindex=\"0\"><code>diff wk-patch.patch nok-patch.patch                \n4c4\n&lt; Subject: fixup! new commit\n---\n&gt; Subject: [PATCH 1\/2] fixup! new commit\n27c27\n&lt; Subject: change\n---\n&gt; Subject: [PATCH 2\/2] change\n<\/code><\/pre><h4 id=\"diff\"><code>diff<\/code><\/h4>\n<p>Avec l&rsquo;utilisation de la commande <code>git diff<\/code> vous pouvez \u00e9galement g\u00e9r\u00e9ner un patch.<\/p>\n<pre tabindex=\"0\"><code>git diff &lt;hash&gt;..&lt;hash&gt; &gt; my.patch\n<\/code><\/pre><p>La diff\u00e9rence avec <code>format-patch<\/code> est que l&rsquo;on n&rsquo;a pas de notions d&rsquo;historique des commits et donc que :<\/p>\n<ul>\n<li>toutes les modifications se retrouveront unfi\u00e9es dans un seul fichier<\/li>\n<li>on perd l&rsquo;information sur l&rsquo;auteur des commits<\/li>\n<\/ul>\n<h3 id=\"partager-un-patch\">Partager un patch<\/h3>\n<p>!! peu de chance que vous ayez \u00e0 l&rsquo;utiliser mais toujours marrant de savoir que cela existe !!<\/p>\n<pre tabindex=\"0\"><code>git send-email --to=&quot;jygastaud &lt;jygastaud@clever-age.com&gt;&quot; 0001-fixup-new-commit.patch\n<\/code><\/pre><p>Sur Ubuntu\/Debian, peut n\u00e9cessiter l&rsquo;installation d&rsquo;un paquet sp\u00e9cifique : <code>apt-get install git-email<\/code><\/p>\n<p>Il faut aussi un serveur smtp dispo, soit en passer un en ligne de commande<\/p>\n<h3 id=\"appliquer-un-patch\">Appliquer un patch<\/h3>\n<ul>\n<li>Si le patch a \u00e9t\u00e9 cr\u00e9\u00e9 avec <code>format-patch<\/code><\/li>\n<\/ul>\n<pre tabindex=\"0\"><code>git am &lt;my-patch-files&gt;\n<\/code><\/pre><ul>\n<li>Vous pouvez utiliser la commande <code>apply<\/code><\/li>\n<\/ul>\n<pre tabindex=\"0\"><code>git apply &lt;my-patch&gt;\n<\/code><\/pre><h3 id=\"supprimer-un-patch\">Supprimer un patch<\/h3>\n<pre tabindex=\"0\"><code>git apply -R &lt;my-patch&gt;\n<\/code><\/pre><h3 id=\"description-dune-branche\">Description d&rsquo;une branche<\/h3>\n<h4 id=\"ajouter-une-description\">Ajouter une description<\/h4>\n<pre tabindex=\"0\"><code>git branch --edit-description\n<\/code><\/pre><h4 id=\"voir-la-description\">Voir la description<\/h4>\n<pre tabindex=\"0\"><code>git config branch.master.description\n<\/code><\/pre><pre tabindex=\"0\"><code>git request-pull v1.0 origin master:v1.x -p\nThe following changes since commit 61739a74f55f6f08bfebbccfc00e5e2cbb9bda52:\n\n  fixup! new commit (2019-04-25 17:51:22 +0200)\n\nare available in the Git repository at:\n\n  git@github.com:jygastaud\/git-conferences.git v1.x\n\nfor you to fetch changes up to 967fbc53e959804b17acc90cec89bdafad2e63ce:\n\n  change (2019-04-25 18:29:25 +0200)\n\n----------------------------------------------------------------\n(from the branch description for master local branch)\n\nLa branche de prod\n\n----------------------------------------------------------------\nJean-Yves Gastaud (1):\n      change\n\n README2.md | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a\/README2.md b\/README2.md\nindex 504bc75..3a59441 100644\n--- a\/README2.md\n+++ b\/README2.md\n@@ -12,3 +12,5 @@ Encore 1 ligne\n \n Derniere ligne ?\n \n+A change\n+\n\n<\/code><\/pre><h2 id=\"g\u00e9rer-ses-repositories-distants\">G\u00e9rer ses repositories distants<\/h2>\n<h3 id=\"fetch--rebase-vs-pull---rebase\">fetch + rebase VS pull (&ndash;rebase)<\/h3>\n<h3 id=\"g\u00e9rer-plusieurs-remotes\">G\u00e9rer plusieurs remotes<\/h3>\n<h2 id=\"les-hooks\">Les hooks<\/h2>\n<h3 id=\"g\u00e9rer-et-partager-ses-hooks-simplement\">G\u00e9rer et partager ses hooks simplement<\/h3>\n<pre tabindex=\"0\"><code>tree gitconfig \ngitconfig\n|-- .gitconfig\n|-- hooks\n|   |-- pre-commit\n|   `-- prepare-commit-msg\n`-- templates\n    `-- .gitmessage\n<\/code><\/pre><p>puis <code>git config --local include.path ..\/gitconfig\/.gitconfig<\/code><\/p>\n<h2 id=\"debuger\">Debuger<\/h2>\n<h3 id=\"git-grep\">git grep<\/h3>\n<p><a href=\"#retrouver-les-changements-effectu\u00e9es-sur-une-fonction\">Voir la section pr\u00e9c\u00e9dente<\/a><\/p>\n<h3 id=\"savoir-ce-qui-est-d\u00e9j\u00e0-commit\u00e9--push\u00e9-sur-une-autre-branche\">Savoir ce qui est d\u00e9j\u00e0 commit\u00e9 \/ push\u00e9 sur une autre branche<\/h3>\n<h3 id=\"git-bisect\">git bisect<\/h3>\n<pre tabindex=\"0\"><code>git bisect start\ngit bisect good &lt;hash&gt;\ngit bisect bad\ngit bisect bad\ngit bisect reset\n<\/code><\/pre><p>Si vous avez des tests automatis\u00e9s et que vous avez une erreur sur ces tests, il est possible de lancer un git bisect pour ex\u00e9cuter automatiquement les tests<\/p>\n<pre tabindex=\"0\"><code>git bisect start\ngit bisect good &lt;hash&gt;\ngit bisect bad\ngit bisect run .\/test.sh\ngit bisect reset\n<\/code><\/pre><h3 id=\"git-reflog\">git reflog<\/h3>\n<p>L&rsquo;historique de toutes les actions que vous avez pu effectuer sur votre repository <strong>local<\/strong>.<\/p>\n<pre tabindex=\"0\"><code>cc7ccc0 (HEAD -&gt; squash, master) HEAD@{0}: rebase -i (finish): returning to refs\/heads\/squash\ncc7ccc0 (HEAD -&gt; squash, master) HEAD@{1}: rebase -i (start): checkout HEAD~5\ncc7ccc0 (HEAD -&gt; squash, master) HEAD@{2}: rebase -i (finish): returning to refs\/heads\/squash\ncc7ccc0 (HEAD -&gt; squash, master) HEAD@{3}: rebase -i (start): checkout HEAD~5\n72f188e HEAD@{4}: commit: squash! vide2\n74e13d7 HEAD@{5}: commit: vide3\n7eb0740 HEAD@{6}: commit: vide2\n2396775 HEAD@{7}: commit: vide\ncc7ccc0 (HEAD -&gt; squash, master) HEAD@{8}: checkout: moving from master to squash\ncc7ccc0 (HEAD -&gt; squash, master) HEAD@{9}: am: [PATCH 5\/5] change\n<\/code><\/pre><p>Comme vous pouvez le constater, chaque action contient un hash sp\u00e9cifique.<\/p>\n<p>Avec ce hash, il vous ai possible de revenir revert simplement une action si n\u00e9cessaire.<\/p>\n<h3 id=\"git-worktree\">git worktree<\/h3>\n<p><code>git worktree<\/code> permet de g\u00e9rer plusieurs espaces de travail pour un m\u00eame d\u00e9p\u00f4t.<\/p>\n<p>Un cas typique d&rsquo;utilisation :<\/p>\n<ul>\n<li>Je travaille sur une nouvelle fonctionnalit\u00e9 et j&rsquo;ai des modifications en cours<\/li>\n<li>Un ticket URGENT arrive et j&rsquo;ai besoin de changer de branche pour faire une correction<\/li>\n<li>Je commite mon travail en cours (au risque d&rsquo;ajouter des choses non voulues) et je change de branche<\/li>\n<\/ul>\n<p>STOP ! Utilisez maintenant <code>worktree<\/code> !<\/p>\n<pre tabindex=\"0\"><code>git worktree add &lt;path&gt; [&lt;branch&gt;]\n<\/code><\/pre><p>avec cette commande, je vais cr\u00e9er un nouvel espace de travail dans un dossier diff\u00e9rent et travailler dessus comme sur ma branche courante.<\/p>\n<p>Une fois fini, le travail commit\u00e9 et pouss\u00e9, il ne nous reste plus qu&rsquo;\u00e0 supprimer l&rsquo;espace de travail :<\/p>\n<pre tabindex=\"0\"><code>git worktree prune\n<\/code><\/pre><h2 id=\"configurer-votre-profil\">Configurer votre profil<\/h2>\n<h3 id=\"g\u00e9rer-plusieurs-profils\">G\u00e9rer plusieurs profils<\/h3>\n<p>Voir article <a href=\"https:\/\/gastaud.io\/article\/git-gerer-profils\/\">Git - G\u00e9rer des profils distincts dans Git<\/a><\/p>\n<h2 id=\"partager-un-repository\">Partager un repository<\/h2>\n<h3 id=\"git-bundle\">git bundle<\/h3>\n<p>Git bundle permet de partager une branche d&rsquo;un d\u00e9p\u00f4t avec tout son historique de commit, de commiter dessus et de r\u00e9int\u00e9grer les modifications dans le d\u00e9p\u00f4t d&rsquo;origine.<\/p>\n<p>Pratique lorsque vous devez bosser avec des \u00e9quipes distantes qui ne peuvent pas avoir un vrai acc\u00e8s au d\u00e9p\u00f4t initial.<\/p>\n<pre tabindex=\"0\"><code>git bundle create &lt;repo.bundle&gt; &lt;branch or hash or tag&gt;\n<\/code><\/pre><p>puis partager le fichier <code>repo.bundle<\/code>.<\/p>\n<p>Sur le poste de l&rsquo;autre d\u00e9veloppeur, il est maintenant possible de cloner le d\u00e9p\u00f4t<\/p>\n<pre tabindex=\"0\"><code>git clone -b &lt;branch_name&gt; &lt;path\/to\/repo.bundle&gt; &lt;folder&gt;\n<\/code><\/pre><p>Le dev peut g\u00e9n\u00e9rer un nouveau bundle de diff \u00e0 partager<\/p>\n<pre tabindex=\"0\"><code>git bundle create gitconf.bundle origin\/master..master\n<\/code><\/pre><p>puis vous pouvez r\u00e9int\u00e9grer le fichier dans le repo d&rsquo;origine avec les m\u00eames commandes que pour la mise \u00e0 jour via un d\u00e9p\u00f4t distant<\/p>\n<ul>\n<li>Avec un commit merge<\/li>\n<\/ul>\n<pre tabindex=\"0\"><code>git pull ..\/testconf\/gitconf.bundle master\n<\/code><\/pre><ul>\n<li>Avec pull &ndash;rebase<\/li>\n<\/ul>\n<pre tabindex=\"0\"><code>git pull --rebase ..\/testconf\/gitconf.bundle master\n<\/code><\/pre><p>Si des modifications ont \u00e9t\u00e9 faites sur le d\u00e9p\u00f4t d&rsquo;origine et qu&rsquo;elles doivent \u00eatre repartag\u00e9e au d\u00e9veloppeur, il suffira de g\u00e9n\u00e9rer un diff avec le bundle pr\u00e9c\u00e9dent. Le d\u00e9veloppeur n&rsquo;aura plus qu&rsquo;\u00e0 remplacer son fichier initial et faire un pull dessus.<\/p>\n<h2 id=\"tags\">Tags<\/h2>\n<h3 id=\"tri-des-tags-et-semantic-versioning\">Tri des tags et Semantic Versioning<\/h3>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">git tag --sort<span style=\"color:#f92672\">=<\/span>version:refname\n<\/code><\/pre><\/div><p>Pour plus de d\u00e9tails, voir l&rsquo;article <a href=\"https:\/\/gastaud.io\/article\/git-sort-tags-semantic-version\/\">Git - Trier les tags d&rsquo;un repo utilisant du semantic versioning<\/a><\/p>"},{"title":"GoHugo - G\u00e9n\u00e9rer des d\u00e9clinaisons d'images utilisables en page d'accueil","link":"https:\/\/gastaud.io\/article\/hugo-homepage-images-processing\/","pubDate":"Thu, 11 Apr 2019 16:51:22 +0200","guid":"https:\/\/gastaud.io\/article\/hugo-homepage-images-processing\/","description":"<p>GoHugo, depuis la version 0.32, est capable de g\u00e9rer des <a href=\"https:\/\/gohugo.io\/about\/new-in-032\/#page-resources\">ressources d\u00e9di\u00e9es \u00e0 une page<\/a>\nainsi que de g\u00e9n\u00e9rer des <a href=\"https:\/\/gohugo.io\/about\/new-in-032\/#image-processing\">d\u00e9clinaisons d&rsquo;images<\/a>.<\/p>\n<p>Cette fonctionnalit\u00e9 est notamment tr\u00e8s pratique pour g\u00e9rer automatiquement des d\u00e9clinaisons responsives de ces images et les utiliser dans un template ou shortcode.<\/p>\n<p>Cela fonctionne parfaitement lorsque l&rsquo;on est dans un contenu.<\/p>\n<h2 id=\"alors-cest-quoi-le-soucis-\">Alors c&rsquo;est quoi le soucis ?<\/h2>\n<p>Admettons que nous voulions utiliser des images responsives sur la page d&rsquo;accueil de notre site.<\/p>\n<ul>\n<li>La page d&rsquo;accueil ne fait pas parti d&rsquo;une section ou d&rsquo;un bundle et n&rsquo;a donc pas acc\u00e8s \u00e0 des ressources<\/li>\n<li>Les fichiers mis dans le r\u00e9pertoire <code>static<\/code> ne sont pas trait\u00e9s comme des ressources par Hugo<\/li>\n<\/ul>\n<p>Il sera donc n\u00e9cessaire de g\u00e9rer &ldquo;\u00e0 la main&rdquo;, la d\u00e9clinaison de ces images et les inclure manuellement dans notre template.<\/p>\n<p>Pour simplifier, nous avons donc l&rsquo;aborescence suivante :<\/p>\n<pre tabindex=\"0\"><code>.\n| content\n| | _index.md\n| layouts\n| | index.html\n<\/code><\/pre><h2 id=\"quoi--mais-jai-trop-dimages-\u00e0-d\u00e9cliner-et-le-contenu-change-souvent-\">Quoi ? Mais j&rsquo;ai trop d&rsquo;images \u00e0 d\u00e9cliner et le contenu change souvent ?!<\/h2>\n<p>Pour arriver \u00e0 automatiser le processus de g\u00e9n\u00e9ration, nous allons utiliser introduit en version 0.35 : les <a href=\"https:\/\/gohugo.io\/news\/0.35-relnotes\/\">Headless Bundles<\/a>.<\/p>\n<p>Ce nouveau type de bundle permet de g\u00e9rer du contenu qui ne produira pas une page \u00e0 part enti\u00e8re lors du rendu fait par Hugo.<\/p>\n<p>Il est donc pratique de s&rsquo;en servir pour g\u00e9rer des blocs, messages&hellip; qui sont ensuite injecter dans les pages.<\/p>\n<p>En s&rsquo;appuyant donc sur ce type de bundle et en le combinant avec la fonctionnalit\u00e9 de g\u00e9n\u00e9ration d&rsquo;images, nous allons pouvoir obtenir automatiquement nos diverses d\u00e9clinaisons d&rsquo;images.<\/p>\n<p>Nous allons suivre les quesques \u00e9tapes suivantes.<\/p>\n<h3 id=\"cr\u00e9ation-dun-nouveau-bundle-headless-img\">Cr\u00e9ation d&rsquo;un nouveau bundle <code>headless-img<\/code><\/h3>\n<p>On aura donc maintenant l&rsquo;arborescence de contenus suivante :<\/p>\n<pre tabindex=\"0\"><code>.\n| content\n| | _index.md\n| | headless-img\n| | | images\n| | | | mon_image.png\n| | | index.md\n<\/code><\/pre><p>Le fichier <code>headless-img\/index.md<\/code> contiendra<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-markdown\" data-lang=\"markdown\">+++\nheadless = true\ntitle = &#34;homepage images&#34;\n+++\n\n{{&lt; <span style=\"color:#f92672\">imghp<\/span> <span style=\"color:#a6e22e\">src<\/span><span style=\"color:#f92672\">=<\/span><span style=\"color:#e6db74\">&#34;images\/mon_image.png&#34;<\/span> <span style=\"color:#a6e22e\">alt<\/span><span style=\"color:#f92672\">=<\/span><span style=\"color:#e6db74\">&#34;A nice image for our homepage&#34;<\/span> &gt;}}\n<\/code><\/pre><\/div><p>Nous avons donc cr\u00e9er ici une ressource <code>headless<\/code> qui va appeler un shortcode hugo nomm\u00e9 <code>imghp<\/code> et qui prend en param\u00e8tres un chemin et un texte alternatif.<\/p>\n<h3 id=\"cr\u00e9ation-du-shortcode\">Cr\u00e9ation du shortcode<\/h3>\n<p>Dans l&rsquo;aborescence <code>layouts\/shortcodes<\/code> nous allons cr\u00e9er un nouveau fichier nomm\u00e9 <code>imghp.html<\/code> qui contiendra la logique de g\u00e9n\u00e9ration de nos images et leur rendu.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-golang\" data-lang=\"golang\">{{ <span style=\"color:#75715e\">\/* Le shortcode pour les images responsives de la HP *\/<\/span> }}\n\n{{<span style=\"color:#75715e\">\/* get file that matches the filename as specified as src=&#34;&#34; in shortcode *\/<\/span>}}\n{{ <span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">src<\/span> <span style=\"color:#f92672\">:=<\/span> (.<span style=\"color:#a6e22e\">Site<\/span>.<span style=\"color:#a6e22e\">GetPage<\/span> <span style=\"color:#e6db74\">&#34;\/headless-img&#34;<\/span>).<span style=\"color:#a6e22e\">Resources<\/span>.<span style=\"color:#a6e22e\">GetMatch<\/span> (<span style=\"color:#a6e22e\">printf<\/span> <span style=\"color:#e6db74\">&#34;*%s*&#34;<\/span> (.<span style=\"color:#a6e22e\">Get<\/span> <span style=\"color:#e6db74\">&#34;src&#34;<\/span>)) }}\n\n{{<span style=\"color:#75715e\">\/* set image sizes, these are hardcoded for now, x dictates that images are resized to this width *\/<\/span>}}\n\n{{ <span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">tinyw<\/span> <span style=\"color:#f92672\">:=<\/span> <span style=\"color:#66d9ef\">default<\/span> <span style=\"color:#e6db74\">&#34;500x&#34;<\/span> }}\n{{ <span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">smallw<\/span> <span style=\"color:#f92672\">:=<\/span> <span style=\"color:#66d9ef\">default<\/span> <span style=\"color:#e6db74\">&#34;800x&#34;<\/span> }}\n{{ <span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">mediumw<\/span> <span style=\"color:#f92672\">:=<\/span> <span style=\"color:#66d9ef\">default<\/span> <span style=\"color:#e6db74\">&#34;1200x&#34;<\/span> }}\n{{ <span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">largew<\/span> <span style=\"color:#f92672\">:=<\/span> <span style=\"color:#66d9ef\">default<\/span> <span style=\"color:#e6db74\">&#34;1500x&#34;<\/span> }}\n\n{{<span style=\"color:#75715e\">\/* resize the src image to the given sizes *\/<\/span>}}\n\n{{ .<span style=\"color:#a6e22e\">Scratch<\/span>.<span style=\"color:#a6e22e\">Set<\/span> <span style=\"color:#e6db74\">&#34;tiny&#34;<\/span> (<span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">src<\/span>.<span style=\"color:#a6e22e\">Resize<\/span> <span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">tinyw<\/span>) }}\n{{ .<span style=\"color:#a6e22e\">Scratch<\/span>.<span style=\"color:#a6e22e\">Set<\/span> <span style=\"color:#e6db74\">&#34;small&#34;<\/span> (<span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">src<\/span>.<span style=\"color:#a6e22e\">Resize<\/span> <span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">smallw<\/span>) }}\n{{ .<span style=\"color:#a6e22e\">Scratch<\/span>.<span style=\"color:#a6e22e\">Set<\/span> <span style=\"color:#e6db74\">&#34;medium&#34;<\/span> (<span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">src<\/span>.<span style=\"color:#a6e22e\">Resize<\/span> <span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">mediumw<\/span>) }}\n{{ .<span style=\"color:#a6e22e\">Scratch<\/span>.<span style=\"color:#a6e22e\">Set<\/span> <span style=\"color:#e6db74\">&#34;large&#34;<\/span> (<span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">src<\/span>.<span style=\"color:#a6e22e\">Resize<\/span> <span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">largew<\/span>) }}\n\n{{<span style=\"color:#75715e\">\/* add the processed images to the scratch *\/<\/span>}}\n\n{{ <span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">tiny<\/span> <span style=\"color:#f92672\">:=<\/span> .<span style=\"color:#a6e22e\">Scratch<\/span>.<span style=\"color:#a6e22e\">Get<\/span> <span style=\"color:#e6db74\">&#34;tiny&#34;<\/span> }}\n{{ <span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">small<\/span> <span style=\"color:#f92672\">:=<\/span> .<span style=\"color:#a6e22e\">Scratch<\/span>.<span style=\"color:#a6e22e\">Get<\/span> <span style=\"color:#e6db74\">&#34;small&#34;<\/span> }}\n{{ <span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">medium<\/span> <span style=\"color:#f92672\">:=<\/span> .<span style=\"color:#a6e22e\">Scratch<\/span>.<span style=\"color:#a6e22e\">Get<\/span> <span style=\"color:#e6db74\">&#34;medium&#34;<\/span> }}\n{{ <span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">large<\/span> <span style=\"color:#f92672\">:=<\/span> .<span style=\"color:#a6e22e\">Scratch<\/span>.<span style=\"color:#a6e22e\">Get<\/span> <span style=\"color:#e6db74\">&#34;large&#34;<\/span> }}\n\n{{<span style=\"color:#75715e\">\/* only use images smaller than or equal to the src (original) image size, as Hugo will upscale small images *\/<\/span>}}\n{{<span style=\"color:#75715e\">\/* set the sizes attribute to (min-width: 35em) 1200px, 100vw unless overridden in shortcode *\/<\/span>}}\n\n&lt;<span style=\"color:#a6e22e\">img<\/span> \n{{ <span style=\"color:#a6e22e\">with<\/span> .<span style=\"color:#a6e22e\">Get<\/span> <span style=\"color:#e6db74\">&#34;sizes&#34;<\/span> }}<span style=\"color:#a6e22e\">sizes<\/span>=<span style=\"color:#960050;background-color:#1e0010\">&#39;<\/span>{{.}}<span style=\"color:#960050;background-color:#1e0010\">&#39;<\/span>{{ <span style=\"color:#66d9ef\">else<\/span> }}<span style=\"color:#a6e22e\">sizes<\/span>=<span style=\"color:#e6db74\">&#34;(min-width: 35em) 1200px, 100vw&#34;<\/span>{{ <span style=\"color:#a6e22e\">end<\/span> }}\n<span style=\"color:#a6e22e\">srcset<\/span>=<span style=\"color:#960050;background-color:#1e0010\">&#39;<\/span>\n{{ <span style=\"color:#66d9ef\">if<\/span> <span style=\"color:#a6e22e\">ge<\/span> <span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">src<\/span>.<span style=\"color:#a6e22e\">Width<\/span> <span style=\"color:#e6db74\">&#34;500&#34;<\/span> }}\n    {{ <span style=\"color:#a6e22e\">with<\/span> <span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">tiny<\/span>.<span style=\"color:#a6e22e\">RelPermalink<\/span> }}{{.}} <span style=\"color:#ae81ff\">500<\/span><span style=\"color:#a6e22e\">w<\/span>{{ <span style=\"color:#a6e22e\">end<\/span> }}\n{{ <span style=\"color:#a6e22e\">end<\/span> }}\n{{ <span style=\"color:#66d9ef\">if<\/span> <span style=\"color:#a6e22e\">ge<\/span> <span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">src<\/span>.<span style=\"color:#a6e22e\">Width<\/span> <span style=\"color:#e6db74\">&#34;800&#34;<\/span> }}\n    {{ <span style=\"color:#a6e22e\">with<\/span> <span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">small<\/span>.<span style=\"color:#a6e22e\">RelPermalink<\/span> }}, {{.}} <span style=\"color:#ae81ff\">800<\/span><span style=\"color:#a6e22e\">w<\/span>{{ <span style=\"color:#a6e22e\">end<\/span> }}\n{{ <span style=\"color:#a6e22e\">end<\/span> }}\n{{ <span style=\"color:#66d9ef\">if<\/span> <span style=\"color:#a6e22e\">ge<\/span> <span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">src<\/span>.<span style=\"color:#a6e22e\">Width<\/span> <span style=\"color:#e6db74\">&#34;1200&#34;<\/span> }}\n    {{ <span style=\"color:#a6e22e\">with<\/span> <span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">medium<\/span>.<span style=\"color:#a6e22e\">RelPermalink<\/span> }}, {{.}} <span style=\"color:#ae81ff\">1200<\/span><span style=\"color:#a6e22e\">w<\/span>{{ <span style=\"color:#a6e22e\">end<\/span> }}\n{{ <span style=\"color:#a6e22e\">end<\/span> }}\n{{ <span style=\"color:#66d9ef\">if<\/span> <span style=\"color:#a6e22e\">ge<\/span> <span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">src<\/span>.<span style=\"color:#a6e22e\">Width<\/span> <span style=\"color:#e6db74\">&#34;1500&#34;<\/span> }}\n    {{ <span style=\"color:#a6e22e\">with<\/span> <span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">large<\/span>.<span style=\"color:#a6e22e\">RelPermalink<\/span> }}, {{.}} <span style=\"color:#ae81ff\">1500<\/span><span style=\"color:#a6e22e\">w<\/span> {{ <span style=\"color:#a6e22e\">end<\/span> }}\n{{ <span style=\"color:#a6e22e\">end<\/span> }}<span style=\"color:#960050;background-color:#1e0010\">&#39;<\/span>\n{{ <span style=\"color:#66d9ef\">if<\/span> .<span style=\"color:#a6e22e\">Get<\/span> <span style=\"color:#960050;background-color:#1e0010\">$<\/span><span style=\"color:#a6e22e\">medium<\/span> }}\n    <span style=\"color:#a6e22e\">src<\/span>=<span style=\"color:#e6db74\">&#34;{{ $medium.RelPermalink }}&#34;<\/span> \n{{ <span style=\"color:#66d9ef\">else<\/span> }}\n    <span style=\"color:#a6e22e\">src<\/span>=<span style=\"color:#e6db74\">&#34;{{ $src.RelPermalink }}&#34;<\/span> \n{{ <span style=\"color:#a6e22e\">end<\/span> }}\n{{ <span style=\"color:#a6e22e\">with<\/span> .<span style=\"color:#a6e22e\">Get<\/span> <span style=\"color:#e6db74\">&#34;alt&#34;<\/span> }}<span style=\"color:#a6e22e\">alt<\/span>=<span style=\"color:#960050;background-color:#1e0010\">&#39;<\/span>{{.}}<span style=\"color:#960050;background-color:#1e0010\">&#39;<\/span>{{ <span style=\"color:#a6e22e\">end<\/span> }}&gt;\n<\/code><\/pre><\/div><p>Ce template peut \u00eatre retrouv\u00e9 sur de nombreux sites comme exemple.<\/p>\n<p>La principale diff\u00e9rence avec les autres se situe dans cette ligne :<br>\n<code>{{ $src := (.Site.GetPage &quot;\/headless-img&quot;).Resources.GetMatch (printf &quot;*%s*&quot; (.Get &quot;src&quot;)) }}<\/code><br>\nqui nous permet de r\u00e9cup\u00e9rer les ressources du bundle <code>headless-img<\/code>.<\/p>\n<p>Lorsque ce shortcode est ex\u00e9cut\u00e9, il va donc chercher l&rsquo;image d\u00e9finie dans le fichier <code>headless-img\/index.md<\/code> et g\u00e9n\u00e8re automatiquement les d\u00e9clinaisons.<\/p>\n<h3 id=\"et-maintenant-jaffiche-\u00e7a-comment-sur-ma-page-daccueil-\">Et maintenant, j&rsquo;affiche \u00e7a comment sur ma page d&rsquo;accueil ?<\/h3>\n<p>Il ne nous reste plus qu&rsquo;\u00e0 appeler notre bundle dans notre template <code>index.html<\/code>.<\/p>\n<h4 id=\"si-vous-navez-quun-uniquement-1-seul-contenu-indexmd\">Si vous n&rsquo;avez qu&rsquo;un uniquement 1 seul contenu index.md<\/h4>\n<pre tabindex=\"0\"><code>{{ define &quot;content&quot; }}\n  {{ \/* Le rendu de la section headless. Assume *\/ }}\n  {{ $headless := .Site.GetPage &quot;\/headless-img&quot; }}\n  {{ $headless.Content }}\n{{ end }}\n<\/code><\/pre><h4 id=\"si-vous-devez-rendre-plusieurs-fichiers-md-en-se-basant-sur-un-nom-img1-img2-par-lexemple\">Si vous devez rendre plusieurs fichiers .md en se basant sur un nom. <code>img1<\/code>, <code>img2<\/code>&hellip; par l&rsquo;exemple<\/h4>\n<pre tabindex=\"0\"><code>{{ define &quot;content&quot; }}\n  {{ $headless := .Site.GetPage &quot;\/headless-img&quot; }}\n  {{ $reusablePages := $headless.Resources.Match &quot;img*&quot; }}\n  {{ range $reusablePages }}\n    {{ .Content }}\n  {{ end }}\n{{ end }}\n<\/code><\/pre><p>Et voil\u00e0, maintenant vos images sont bien rendues sur votre page d&rsquo;accueil et leurs versions responsives sont \u00e9galement disponibles.<\/p>\n"},{"title":"Drupal, Gitlab CI et Clever Cloud sont dans un bateau","link":"https:\/\/gastaud.io\/article\/drupal-clever-cloud\/","pubDate":"Wed, 10 Apr 2019 15:15:36 +0200","guid":"https:\/\/gastaud.io\/article\/drupal-clever-cloud\/","description":"<p>D\u00e9ployer un site Drupal 8 sur Clever Cloud avec Gitlab CI<\/p>\n<h2 id=\"quest-ce-que-clever-cloud\">Qu&rsquo;est-ce que Clever Cloud<\/h2>\n<p><a href=\"https:\/\/www.clever-cloud.com\/\">Clever Cloud<\/a> est un h\u00e9bergement PAAS (Platform As A Service).<\/p>\n<h3 id=\"grands-principes\">Grands principes<\/h3>\n<p>A chaque d\u00e9ploiement de l&rsquo;application, une nouvelle machine va \u00eatre cr\u00e9\u00e9e.<br>\nLe code disponible dans un repository Git fournit par Clever Cloud va \u00eatre d\u00e9ployer sur la machine et si aucune erreur n&rsquo;est d\u00e9tect\u00e9e, l&rsquo;ancienne machine et la nouvelle sont switch\u00e9es.<br>\nLe stockage de donn\u00e9es et le stockage permanent de fichiers sont assur\u00e9s respectivement par l&rsquo;add-on <code>mysql<\/code> et <code>bucketFS<\/code>.<\/p>\n<p>Toutes les machines et services poss\u00e8dent un identifiant unique et un alias.<\/p>\n<h2 id=\"serveurs-et-services\">Serveurs et services<\/h2>\n<p>2 environnements sont provisionn\u00e9es.<\/p>\n<h3 id=\"preprod\">PREPROD<\/h3>\n<p><strong>VM<\/strong><\/p>\n<ul>\n<li>type XS : 1GB de RAM, 1 CPU<\/li>\n<li>Pas d&rsquo;autoscalling<\/li>\n<li>1 seule instance<\/li>\n<\/ul>\n<p><strong>Services<\/strong><\/p>\n<ul>\n<li>MySQL (la base de donn\u00e9es)\n<ul>\n<li>10GB d&rsquo;espace<\/li>\n<\/ul>\n<\/li>\n<li>2 FS Buckets\n<ul>\n<li>1 pour le r\u00e9pertoire public de Drupal<\/li>\n<li>1 pour le repertoire priv\u00e9 de Drupal<\/li>\n<li>Assure la persistance des donn\u00e9es \/ fichiers upload\u00e9s<\/li>\n<li>Pas de taille min \/ max<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h3 id=\"prod\">PROD<\/h3>\n<p>Identique \u00e0 la PREPROD avec une machine un peu plus puissante : type S, 2GB de RAM, 2 CPUs<\/p>\n<h2 id=\"configurations-et-variables-denvironnement\">Configurations et variables d&rsquo;environnement<\/h2>\n<h3 id=\"variables-denvironnements\">Variables d&rsquo;environnements<\/h3>\n<p>Afin d&rsquo;acc\u00e9der aux diff\u00e9rents services associ\u00e9s depuis la VM, Clever Cloud fournit des variables d&rsquo;environnement.<\/p>\n<p>Les variables standards sur Clever Cloud et disponibles par environnement sont document\u00e9es au lien suivant :<\/p>\n<ul>\n<li><a href=\"https:\/\/www.clever-cloud.com\/doc\/admin-console\/environment-variables\/\">G\u00e9n\u00e9ral<\/a><\/li>\n<li><a href=\"https:\/\/www.clever-cloud.com\/doc\/php\/php-apps\/#environment-injection\">PHP<\/a><\/li>\n<\/ul>\n<h3 id=\"variables-par-instances\">Variables par instances<\/h3>\n<p>Les variables ci-dessous sont d\u00e9finies et modifi\u00e9es pour chacune des instances.<\/p>\n<h5 id=\"variables-custom--customisables\">Variables custom \/ customisables<\/h5>\n<pre tabindex=\"0\"><code>CC_FS_BUCKET=\/web\/sites\/default\/files:bucket-xxxx-xxxx-xxxx-xxxx-xxxx-fsbucket.services.clever-cloud.com\nCC_FS_BUCKET=\/private\/files:bucket-xxxx-xxxx-xxxx-xxxx-xxxx-fsbucket.services.clever-cloud.com\n<\/code><\/pre><p>permet de d\u00e9finir quels r\u00e9pertoires doivent \u00eatre stock\u00e9s de fa\u00e7on permanente dans le <code>FS BUCKETS<\/code>.<\/p>\n<pre tabindex=\"0\"><code>CC_PRE_RUN_HOOK=composer.phar run-script drupal-update-all\n<\/code><\/pre><p>Permet d&rsquo;ex\u00e9cuter \u00e0 la fin d&rsquo;un d\u00e9ploiement les actions de migration de base de donn\u00e9es \/ vidage de cache&hellip;\nPour simplifier le process, on joue un script disponible dans le <code>composer.json<\/code><\/p>\n<pre tabindex=\"0\"><code>CC_WEBROOT=\/web\n<\/code><\/pre><p>Permet de d\u00e9finir l&rsquo;emplacement du fichier index.php\/index.html pour l&rsquo;affichage du site.<\/p>\n<pre tabindex=\"0\"><code>DRUPAL_SALT=xxxxxxx\n<\/code><\/pre><p><strong>Le SALT Drupal est obligatoire<\/strong>. Il sera n\u00e9cessaire de le configurer \u00e9galement sur les instances de DEV\/LOCAL en cas de redescente de base.<\/p>\n<pre tabindex=\"0\"><code>PHP_VERSION=7.2\n<\/code><\/pre><p>D\u00e9finie la version de PHP \u00e0 utiliser<\/p>\n<pre tabindex=\"0\"><code>PORT=8080\n<\/code><\/pre><p><strong>Ne pas changer le port. Il est configur\u00e9 par d\u00e9faut sur 8080 pour le bon fonctionnement Clever Cloud.<\/strong><\/p>\n<pre tabindex=\"0\"><code>GIT_TAG=CURRENT_VERSION\n<\/code><\/pre><p>Cette variable est d\u00e9finie automatiquement par Gitlab CI.<\/p>\n<p>Elle sert uniquement de repaire pour savoir quel est le dernier tag d\u00e9ploy\u00e9<\/p>\n<h4 id=\"variables-li\u00e9es-aux-services\">Variables li\u00e9es aux services<\/h4>\n<pre tabindex=\"0\"><code>BUCKET_HOST\nMYSQL_ADDON_DB\nMYSQL_ADDON_HOST\nMYSQL_ADDON_PASSWORD\nMYSQL_ADDON_PORT\nMYSQL_ADDON_URI\nMYSQL_ADDON_USER\n<\/code><\/pre><h2 id=\"faire-communiquer-drupal-et-les-services-clever-cloud\">Faire communiquer Drupal et les services Clever Cloud<\/h2>\n<p>Afin de pouvoir utiliser pleinement les services de Clever Cloud, les adaptations suivantes ont \u00e9t\u00e9 faites sur Drupal.<\/p>\n<ul>\n<li>Ajout au fichier <code>default.settings.php<\/code> d&rsquo;une condition pour inclure un fichier de settings propre \u00e0 Clever Cloud<\/li>\n<\/ul>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#66d9ef\">if<\/span> (<span style=\"color:#a6e22e\">getenv<\/span>(<span style=\"color:#e6db74\">&#39;APP_ID&#39;<\/span>)) {\n  <span style=\"color:#66d9ef\">include<\/span> $app_root <span style=\"color:#f92672\">.<\/span> <span style=\"color:#e6db74\">&#39;\/&#39;<\/span> <span style=\"color:#f92672\">.<\/span> $site_path <span style=\"color:#f92672\">.<\/span> <span style=\"color:#e6db74\">&#39;\/settings.clevercloud.php&#39;<\/span>;\n}\n<\/code><\/pre><\/div><ul>\n<li>Commit du fichier settings.php (iso default.settings.php)<\/li>\n<li>Cr\u00e9ation et commit du fichier <code>settings.clevercloud.php<\/code> qui ne s&rsquo;inclue que si la variable <code>APP_ID<\/code> est disponible.<\/li>\n<\/ul>\n<p>Sur Clever Cloud : <code>APP_ID<\/code>: the ID of the application. Each application has a unique identifier used to identify it in our system. This ID is the same than the one you can find in the information section of your application.<\/p>\n<ul>\n<li>Modification de la partie <code>scripts<\/code> du fichier <code>composer.json<\/code> pour y ajouter des scripts d&rsquo;installation et de mise \u00e0 jour.<\/li>\n<\/ul>\n<p>Extract du fichier composer.json<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-json\" data-lang=\"json\"><span style=\"color:#960050;background-color:#1e0010\">...<\/span>\n    <span style=\"color:#e6db74\">&#34;scripts&#34;<\/span><span style=\"color:#960050;background-color:#1e0010\">:<\/span> {\n        <span style=\"color:#960050;background-color:#1e0010\">...<\/span>\n        <span style=\"color:#f92672\">&#34;drupal-config-update&#34;<\/span>: [\n            <span style=\"color:#e6db74\">&#34;cd web &amp;&amp; ..\/vendor\/bin\/drush cr&#34;<\/span>,\n            <span style=\"color:#e6db74\">&#34;cd web &amp;&amp; ..\/vendor\/bin\/drush cim -y&#34;<\/span>,\n            <span style=\"color:#e6db74\">&#34;cd web &amp;&amp; ..\/vendor\/bin\/drush csim -y&#34;<\/span>,\n            <span style=\"color:#e6db74\">&#34;cd web &amp;&amp; ..\/vendor\/bin\/drush csim -y&#34;<\/span>,\n            <span style=\"color:#e6db74\">&#34;cd web &amp;&amp; ..\/vendor\/bin\/drush locale:check&#34;<\/span>,\n            <span style=\"color:#e6db74\">&#34;cd web &amp;&amp; ..\/vendor\/bin\/drush locale:update&#34;<\/span>,\n            <span style=\"color:#e6db74\">&#34;cd web &amp;&amp; ..\/vendor\/bin\/drush cr&#34;<\/span>\n        ],\n        <span style=\"color:#f92672\">&#34;drupal-db-update&#34;<\/span>: [\n            <span style=\"color:#e6db74\">&#34;cd web &amp;&amp; ..\/vendor\/bin\/drush updb -y&#34;<\/span>,\n            <span style=\"color:#e6db74\">&#34;cd web &amp;&amp; ..\/vendor\/bin\/drush entup -y&#34;<\/span>\n        ],\n        <span style=\"color:#f92672\">&#34;drupal-monsite-default-install&#34;<\/span>: [\n            <span style=\"color:#e6db74\">&#34;cd web &amp;&amp; ..\/vendor\/bin\/drush si monsite_profile --locale=en --account-name=admin --account-pass=admin -y&#34;<\/span>,\n            <span style=\"color:#e6db74\">&#34;cd web &amp;&amp; ..\/vendor\/bin\/drush cset system.site uuid 28ece100-1664-4072-9a92-55a5a53192f3 -y&#34;<\/span>,\n            <span style=\"color:#e6db74\">&#34;@drupal-update-all&#34;<\/span>\n        ],\n        <span style=\"color:#f92672\">&#34;drupal-theme-build&#34;<\/span>: [\n            <span style=\"color:#e6db74\">&#34;cd web\/themes\/custom\/monsite &amp;&amp; npm install &amp;&amp; .\/node_modules\/.bin\/gulp build -r&#34;<\/span>\n        ],\n        <span style=\"color:#f92672\">&#34;drupal-update-all&#34;<\/span>: [\n            <span style=\"color:#e6db74\">&#34;@drupal-theme-build&#34;<\/span>,\n            <span style=\"color:#e6db74\">&#34;@drupal-db-update&#34;<\/span>,\n            <span style=\"color:#e6db74\">&#34;@drupal-config-update&#34;<\/span>\n        ]\n    }<span style=\"color:#960050;background-color:#1e0010\">,<\/span>\n<span style=\"color:#960050;background-color:#1e0010\">...<\/span>\n<\/code><\/pre><\/div><h2 id=\"utiliser-les-outils-php-et-drupal-avec-clever-cloud\">Utiliser les outils PHP et Drupal avec Clever Cloud<\/h2>\n<p>Si besoin de lancer des commandes directement sur le serveur, il vous faudra passer par l&rsquo;outil <code>Clever Tools<\/code> en ligne de commande.<\/p>\n<p><a href=\"https:\/\/www.clever-cloud.com\/doc\/clever-tools\/getting_started\/\">La documentation d&rsquo;installation<\/a><\/p>\n<h3 id=\"connexion-ssh\">Connexion SSH<\/h3>\n<p>Clever Tools dispose d&rsquo;une commande <code>ssh<\/code>.<\/p>\n<p>Comme nous avons 2 VMs d\u00e9finies dans le <code>.clever.json<\/code>, il est n\u00e9cessaire de pr\u00e9ciser \u00e0 cette commande l&rsquo;environnement cible.<\/p>\n<ul>\n<li>Connexion PREPROD : <code>clever ssh mon-site-preprod<\/code><\/li>\n<li>Connexion PROD : <code>clever ssh mon-site-prod<\/code><\/li>\n<\/ul>\n<h3 id=\"composer\">Composer<\/h3>\n<p>Composer est disponible directement et globalement sur les machines via <code>composer.phar<\/code>.<\/p>\n<p>Si vous avez besoin de lancer un <code>composer install<\/code> directement sur la machine, il vous faudra donc ex\u00e9cuter <code>composer.phar install<\/code>.<\/p>\n<h3 id=\"drush-drupal-console-et-autres-binaires-du-projet\">Drush, Drupal Console et autres binaires du projet<\/h3>\n<p>Drush et Drupal Console sont \u00e0 d\u00e9clarer en d\u00e9pendance composer du projet.<\/p>\n<p>Une fois le <code>composer install<\/code> ex\u00e9cut\u00e9, il est possible d&rsquo;aller utiliser les binaires dans le dossier <code>vendor\/bin<\/code>.<\/p>\n<p>Exemple pour Drush depuis la racine de l&rsquo;application : <code>.\/vendor\/bin\/drush MA_COMMANDE<\/code><\/p>\n<h2 id=\"gitlab--gitlab-ci\">Gitlab \/ Gitlab CI<\/h2>\n<p>Afin de simplifier la gestion et le d\u00e9ploiement de l&rsquo;application, les actions suivantes ont \u00e9t\u00e9 men\u00e9es sur l&rsquo;Int\u00e9gration Continue (via Gitlab-ci).<\/p>\n<ul>\n<li>Activation d&rsquo;un runner Gitlab CI sur l&rsquo;instance de QA<\/li>\n<li>Activation de Gitlab CI dans le projet Drupal<\/li>\n<li>Cr\u00e9ation d&rsquo;une t\u00e2che de d\u00e9ploiement pour la PREPROD\n<ul>\n<li>cette t\u00e2che se d\u00e9clenche automatiquement que si des changements se produisent sur la branche <code>release<\/code><\/li>\n<li>On force la mise \u00e0 jour du code dans le d\u00e9p\u00f4t Git de Clever Cloud \u00e0 chaque d\u00e9ploiement.<\/li>\n<\/ul>\n<\/li>\n<li>Cr\u00e9ation d&rsquo;une t\u00e2che de d\u00e9ploiement sur la PROD\n<ul>\n<li>Cette t\u00e2che doit \u00eatre d\u00e9clench\u00e9 manuellement et n&rsquo;est propos\u00e9e que lorsque d&rsquo;un tag est pouss\u00e9<\/li>\n<\/ul>\n<\/li>\n<li>Cr\u00e9ation de 2 jobs planifi\u00e9s dans Gitlab\n<ul>\n<li>Mise en sommeil de la VM de PREPROD \u00e0 19h<\/li>\n<li>Reveil de la VM de PREPROD \u00e0 8h<\/li>\n<\/ul>\n<\/li>\n<li>Commit du fichier <code>.clever.json<\/code> qui contient les infos pour acc\u00e9der aux 2 machines.<\/li>\n<\/ul>\n<p>L&rsquo;ensemble des jobs\/t\u00e2ches sont d\u00e9finies dans le fichier <code>.gitlab-ci.yml<\/code> \u00e0 la racine du repository Git.<\/p>\n<p>Des variables d&rsquo;environnement ont \u00e9t\u00e9 d\u00e9finies dans le projet Gitlab pour \u00eatre accessible par Gitlab CI :<\/p>\n<ul>\n<li>APP_ID_PREPROD<\/li>\n<li>APP_ID_PROD<\/li>\n<li>APP_ALIAS_PREPROD<\/li>\n<li>APP_ALIAS_PROD<\/li>\n<li>APP_NAME_PREPROD<\/li>\n<li>APP_NAME_PROD<\/li>\n<li>CLEVER_SECRET<\/li>\n<li>CLEVER_TOKEN<\/li>\n<\/ul>\n<h3 id=\"r\u00e9installation-des-environnements\">(R\u00e9)Installation des environnements<\/h3>\n<p>2 jobs, \u00e0 lancer manuellement sont disponibles dans Gitlab-ci.<\/p>\n<ul>\n<li><code>install_preprod<\/code><\/li>\n<li><code>install_prod<\/code><\/li>\n<\/ul>\n<p>Ces 2 jobs r\u00e9installent compl\u00e8tement le site (r\u00e9installation du profil d&rsquo;install)<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"color:#f92672\">install_preprod<\/span>:\n  <span style=\"color:#f92672\">stage<\/span>: <span style=\"color:#ae81ff\">oneshot<\/span>\n  <span style=\"color:#f92672\">environment<\/span>:\n    <span style=\"color:#f92672\">name<\/span>: <span style=\"color:#ae81ff\">preproduction<\/span>\n    <span style=\"color:#f92672\">url<\/span>:  <span style=\"color:#ae81ff\">https:\/\/$APP_NAME_PREPROD.cleverapps.io<\/span>\n  <span style=\"color:#f92672\">when<\/span>: <span style=\"color:#ae81ff\">manual<\/span>\n  <span style=\"color:#f92672\">before_script<\/span>:\n    - <span style=\"color:#ae81ff\">apt-get update -yqq<\/span>\n    - <span style=\"color:#ae81ff\">apt-get install git curl -y<\/span>\n  <span style=\"color:#f92672\">only<\/span>:\n    - <span style=\"color:#ae81ff\">\/release\/<\/span>\n  <span style=\"color:#f92672\">except<\/span>:\n    - <span style=\"color:#ae81ff\">schedules<\/span>\n  <span style=\"color:#f92672\">script<\/span>:\n    - <span style=\"color:#ae81ff\">curl -O https:\/\/clever-tools.cellar.services.clever-cloud.com\/releases\/latest\/clever-tools-latest_linux.tar.gz<\/span>\n    - <span style=\"color:#ae81ff\">tar -xvf clever-tools-latest_linux.tar.gz --strip-components=1<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever login --token $CLEVER_TOKEN --secret $CLEVER_SECRET<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever env set CC_PRE_RUN_HOOK &#34;composer.phar run-script drupal-monsite-default-install&#34; --alias $APP_ALIAS_PREPROD<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever deploy --alias $APP_ALIAS_PREPROD -f<\/span>\n\n<span style=\"color:#f92672\">install_prod<\/span>:\n  <span style=\"color:#f92672\">stage<\/span>: <span style=\"color:#ae81ff\">oneshot<\/span>\n  <span style=\"color:#f92672\">environment<\/span>:\n    <span style=\"color:#f92672\">name<\/span>: <span style=\"color:#ae81ff\">production<\/span>\n    <span style=\"color:#f92672\">url<\/span>:  <span style=\"color:#ae81ff\">https:\/\/$APP_NAME_PROD.cleverapps.io<\/span>\n  <span style=\"color:#f92672\">when<\/span>: <span style=\"color:#ae81ff\">manual<\/span>\n  <span style=\"color:#f92672\">before_script<\/span>:\n    - <span style=\"color:#ae81ff\">apt-get update -yqq<\/span>\n    - <span style=\"color:#ae81ff\">apt-get install git curl -y<\/span>\n  <span style=\"color:#f92672\">only<\/span>:\n    - <span style=\"color:#ae81ff\">tags<\/span>\n  <span style=\"color:#f92672\">script<\/span>:\n    - <span style=\"color:#ae81ff\">curl -O https:\/\/clever-tools.cellar.services.clever-cloud.com\/releases\/latest\/clever-tools-latest_linux.tar.gz<\/span>\n    - <span style=\"color:#ae81ff\">tar -xvf clever-tools-latest_linux.tar.gz --strip-components=1<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever login --token $CLEVER_TOKEN --secret $CLEVER_SECRET<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever env set CC_PRE_RUN_HOOK &#34;composer.phar run-script drupal-monsite-default-install&#34; --alias $APP_ALIAS_PROD<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever deploy --alias $APP_ALIAS_PROD -f<\/span>\n<\/code><\/pre><\/div><p>Le d\u00e9ploiement est g\u00e9r\u00e9 par 3 nouveaux stages.<br>\nLe d\u00e9ploiement en prod, s&rsquo;il est r\u00e9ussi, va d\u00e9clencher le job <code>update_info_prod<\/code> et modifier la variable d&rsquo;environnement GIT_TAG qui peut nous permettre de tracker simplement la derni\u00e8re version d\u00e9ploy\u00e9e.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"color:#f92672\">deploy_preprod<\/span>:\n  <span style=\"color:#f92672\">stage<\/span>: <span style=\"color:#ae81ff\">deploy_preprod<\/span>\n  <span style=\"color:#f92672\">environment<\/span>:\n    <span style=\"color:#f92672\">name<\/span>: <span style=\"color:#ae81ff\">preproduction<\/span>\n    <span style=\"color:#f92672\">url<\/span>:  <span style=\"color:#ae81ff\">https:\/\/$APP_NAME_PREPROD.cleverapps.io<\/span>\n  <span style=\"color:#f92672\">before_script<\/span>:\n    - <span style=\"color:#ae81ff\">apt-get update -yqq<\/span>\n    - <span style=\"color:#ae81ff\">apt-get install git curl -y<\/span>\n  <span style=\"color:#f92672\">only<\/span>:\n    - <span style=\"color:#ae81ff\">\/release\/<\/span>\n    - <span style=\"color:#ae81ff\">\/clevercloud\/<\/span>\n  <span style=\"color:#f92672\">except<\/span>:\n    - <span style=\"color:#ae81ff\">schedules<\/span>\n  <span style=\"color:#f92672\">script<\/span>:\n    - <span style=\"color:#ae81ff\">curl -O https:\/\/clever-tools.cellar.services.clever-cloud.com\/releases\/1.0.0\/clever-tools-1.0.0_linux.tar.gz<\/span>\n    - <span style=\"color:#ae81ff\">tar -xvf clever-tools-1.0.0_linux.tar.gz --strip-components=1<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever env set CC_PRE_RUN_HOOK &#34;composer.phar run-script drupal-update-all&#34; --alias $APP_ALIAS_PREPROD -v<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever deploy --alias $APP_ALIAS_PREPROD -f -v<\/span>\n\n<span style=\"color:#f92672\">deploy_prod<\/span>:\n  <span style=\"color:#f92672\">stage<\/span>: <span style=\"color:#ae81ff\">deploy_prod<\/span>\n  <span style=\"color:#f92672\">environment<\/span>:\n    <span style=\"color:#f92672\">name<\/span>: <span style=\"color:#ae81ff\">production<\/span>\n    <span style=\"color:#f92672\">url<\/span>:  <span style=\"color:#ae81ff\">https:\/\/$APP_NAME_PROD.cleverapps.io<\/span>\n  <span style=\"color:#f92672\">when<\/span>: <span style=\"color:#ae81ff\">manual<\/span>\n  <span style=\"color:#f92672\">before_script<\/span>:\n    - <span style=\"color:#ae81ff\">apt-get update -yqq<\/span>\n    - <span style=\"color:#ae81ff\">apt-get install git curl -y<\/span>\n  <span style=\"color:#f92672\">only<\/span>:\n    - <span style=\"color:#ae81ff\">tags<\/span>\n  <span style=\"color:#f92672\">script<\/span>:\n    - <span style=\"color:#ae81ff\">curl -O https:\/\/clever-tools.cellar.services.clever-cloud.com\/releases\/latest\/clever-tools-latest_linux.tar.gz<\/span>\n    - <span style=\"color:#ae81ff\">tar -xvf clever-tools-latest_linux.tar.gz --strip-components=1<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever login --token $CLEVER_TOKEN --secret $CLEVER_SECRET<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever env set CC_PRE_RUN_HOOK &#34;composer.phar run-script drupal-update-all&#34; --alias $APP_ALIAS_PROD<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever deploy --alias $APP_ALIAS_PROD -f<\/span>\n  <span style=\"color:#f92672\">allow_failure<\/span>: <span style=\"color:#66d9ef\">false<\/span>\n\n<span style=\"color:#f92672\">update_variable_prod<\/span>:\n  <span style=\"color:#f92672\">stage<\/span>: <span style=\"color:#ae81ff\">update_info_prod<\/span>\n  <span style=\"color:#f92672\">environment<\/span>:\n    <span style=\"color:#f92672\">name<\/span>: <span style=\"color:#ae81ff\">production<\/span>\n    <span style=\"color:#f92672\">url<\/span>:  <span style=\"color:#ae81ff\">https:\/\/$APP_NAME_PROD.cleverapps.io<\/span>\n  <span style=\"color:#f92672\">before_script<\/span>:\n    - <span style=\"color:#ae81ff\">apt-get update -yqq<\/span>\n    - <span style=\"color:#ae81ff\">apt-get install git curl -y<\/span>\n  <span style=\"color:#f92672\">only<\/span>:\n    - <span style=\"color:#ae81ff\">tags<\/span>\n  <span style=\"color:#f92672\">script<\/span>:\n    - <span style=\"color:#ae81ff\">curl -O https:\/\/clever-tools.cellar.services.clever-cloud.com\/releases\/latest\/clever-tools-latest_linux.tar.gz<\/span>\n    - <span style=\"color:#ae81ff\">tar -xvf clever-tools-latest_linux.tar.gz --strip-components=1<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever login --token $CLEVER_TOKEN --secret $CLEVER_SECRET<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever env set GIT_TAG $CI_COMMIT_TAG --alias $APP_ALIAS_PROD<\/span>\n  <span style=\"color:#f92672\">when<\/span>: <span style=\"color:#ae81ff\">on_success<\/span>\n<\/code><\/pre><\/div><p>Pour la PREPROD, nous avons \u00e9galement planifi\u00e9s des jobs automatiques qui vont arr\u00eater et d\u00e9marrer la machine.<br>\nCes 2 jobs sont d\u00e9finis via l&rsquo;interface Gitlab et contiennent des variables d\u00e9di\u00e9es (<code>SLEEPING<\/code> et <code>WAKEUP<\/code>)<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"color:#f92672\">good_night_preprod<\/span>:\n  <span style=\"color:#f92672\">stage<\/span>: <span style=\"color:#ae81ff\">money<\/span>\n  <span style=\"color:#f92672\">only<\/span>:\n    <span style=\"color:#f92672\">variables<\/span>:\n      - <span style=\"color:#ae81ff\">$SLEEPING == &#34;PREPROD&#34;<\/span>\n  <span style=\"color:#f92672\">before_script<\/span>:\n    - <span style=\"color:#ae81ff\">apt-get update -yqq<\/span>\n    - <span style=\"color:#ae81ff\">apt-get install git curl -y<\/span>\n  <span style=\"color:#f92672\">script<\/span>:\n    - <span style=\"color:#ae81ff\">curl -O https:\/\/clever-tools.cellar.services.clever-cloud.com\/releases\/latest\/clever-tools-latest_linux.tar.gz<\/span>\n    - <span style=\"color:#ae81ff\">tar -xvf clever-tools-latest_linux.tar.gz --strip-components=1<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever login --token $CLEVER_TOKEN --secret $CLEVER_SECRET<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever stop --alias $APP_ALIAS_PREPROD<\/span>\n\n<span style=\"color:#f92672\">wakeup_preprod<\/span>:\n  <span style=\"color:#f92672\">stage<\/span>: <span style=\"color:#ae81ff\">money<\/span>\n  <span style=\"color:#f92672\">only<\/span>:\n    <span style=\"color:#f92672\">variables<\/span>:\n      - <span style=\"color:#ae81ff\">$WAKEUP == &#34;PREPROD&#34;<\/span>\n  <span style=\"color:#f92672\">before_script<\/span>:\n    - <span style=\"color:#ae81ff\">apt-get update -yqq<\/span>\n    - <span style=\"color:#ae81ff\">apt-get install git curl -y<\/span>\n  <span style=\"color:#f92672\">script<\/span>:\n    - <span style=\"color:#ae81ff\">curl -O https:\/\/clever-tools.cellar.services.clever-cloud.com\/releases\/latest\/clever-tools-latest_linux.tar.gz<\/span>\n    - <span style=\"color:#ae81ff\">tar -xvf clever-tools-latest_linux.tar.gz --strip-components=1<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever login --token $CLEVER_TOKEN --secret $CLEVER_SECRET<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever env set CC_PRE_RUN_HOOK &#34;composer.phar run-script drupal-update-all&#34; --alias $APP_ALIAS_PREPROD<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever restart --alias $APP_ALIAS_PREPROD<\/span>\n<\/code><\/pre><\/div><p><strong>Le fichier gitlab-ci.yml complet<\/strong><\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"color:#f92672\">image<\/span>: <span style=\"color:#ae81ff\">debian:9-slim<\/span>\n\n<span style=\"color:#f92672\">variables<\/span>:\n  <span style=\"color:#f92672\">GIT_STRATEGY<\/span>: <span style=\"color:#ae81ff\">clone<\/span>\n\n<span style=\"color:#f92672\">stages<\/span>:\n  - <span style=\"color:#ae81ff\">oneshot<\/span>\n  - <span style=\"color:#ae81ff\">deploy_preprod<\/span>\n  - <span style=\"color:#ae81ff\">deploy_prod<\/span>\n  - <span style=\"color:#ae81ff\">update_info_prod<\/span>\n  - <span style=\"color:#ae81ff\">money<\/span>\n\n<span style=\"color:#f92672\">deploy_preprod<\/span>:\n  <span style=\"color:#f92672\">stage<\/span>: <span style=\"color:#ae81ff\">deploy_preprod<\/span>\n  <span style=\"color:#f92672\">environment<\/span>:\n    <span style=\"color:#f92672\">name<\/span>: <span style=\"color:#ae81ff\">preproduction<\/span>\n    <span style=\"color:#f92672\">url<\/span>:  <span style=\"color:#ae81ff\">https:\/\/$APP_NAME_PREPROD.cleverapps.io<\/span>\n  <span style=\"color:#f92672\">before_script<\/span>:\n    - <span style=\"color:#ae81ff\">apt-get update -yqq<\/span>\n    - <span style=\"color:#ae81ff\">apt-get install git curl -y<\/span>\n  <span style=\"color:#f92672\">only<\/span>:\n    - <span style=\"color:#ae81ff\">\/release\/<\/span>\n    - <span style=\"color:#ae81ff\">\/clevercloud\/<\/span>\n  <span style=\"color:#f92672\">except<\/span>:\n    - <span style=\"color:#ae81ff\">schedules<\/span>\n  <span style=\"color:#f92672\">script<\/span>:\n    - <span style=\"color:#ae81ff\">curl -O https:\/\/clever-tools.cellar.services.clever-cloud.com\/releases\/1.0.0\/clever-tools-1.0.0_linux.tar.gz<\/span>\n    - <span style=\"color:#ae81ff\">tar -xvf clever-tools-1.0.0_linux.tar.gz --strip-components=1<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever env set CC_PRE_RUN_HOOK &#34;composer.phar run-script drupal-update-all&#34; --alias $APP_ALIAS_PREPROD -v<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever deploy --alias $APP_ALIAS_PREPROD -f -v<\/span>\n\n<span style=\"color:#f92672\">deploy_prod<\/span>:\n  <span style=\"color:#f92672\">stage<\/span>: <span style=\"color:#ae81ff\">deploy_prod<\/span>\n  <span style=\"color:#f92672\">environment<\/span>:\n    <span style=\"color:#f92672\">name<\/span>: <span style=\"color:#ae81ff\">production<\/span>\n    <span style=\"color:#f92672\">url<\/span>:  <span style=\"color:#ae81ff\">https:\/\/$APP_NAME_PROD.cleverapps.io<\/span>\n  <span style=\"color:#f92672\">when<\/span>: <span style=\"color:#ae81ff\">manual<\/span>\n  <span style=\"color:#f92672\">before_script<\/span>:\n    - <span style=\"color:#ae81ff\">apt-get update -yqq<\/span>\n    - <span style=\"color:#ae81ff\">apt-get install git curl -y<\/span>\n  <span style=\"color:#f92672\">only<\/span>:\n    - <span style=\"color:#ae81ff\">tags<\/span>\n  <span style=\"color:#f92672\">script<\/span>:\n    - <span style=\"color:#ae81ff\">curl -O https:\/\/clever-tools.cellar.services.clever-cloud.com\/releases\/latest\/clever-tools-latest_linux.tar.gz<\/span>\n    - <span style=\"color:#ae81ff\">tar -xvf clever-tools-latest_linux.tar.gz --strip-components=1<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever login --token $CLEVER_TOKEN --secret $CLEVER_SECRET<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever env set CC_PRE_RUN_HOOK &#34;composer.phar run-script drupal-update-all&#34; --alias $APP_ALIAS_PROD<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever deploy --alias $APP_ALIAS_PROD -f<\/span>\n  <span style=\"color:#f92672\">allow_failure<\/span>: <span style=\"color:#66d9ef\">false<\/span>\n\n<span style=\"color:#f92672\">update_variable_prod<\/span>:\n  <span style=\"color:#f92672\">stage<\/span>: <span style=\"color:#ae81ff\">update_info_prod<\/span>\n  <span style=\"color:#f92672\">environment<\/span>:\n    <span style=\"color:#f92672\">name<\/span>: <span style=\"color:#ae81ff\">production<\/span>\n    <span style=\"color:#f92672\">url<\/span>:  <span style=\"color:#ae81ff\">https:\/\/$APP_NAME_PROD.cleverapps.io<\/span>\n  <span style=\"color:#f92672\">before_script<\/span>:\n    - <span style=\"color:#ae81ff\">apt-get update -yqq<\/span>\n    - <span style=\"color:#ae81ff\">apt-get install git curl -y<\/span>\n  <span style=\"color:#f92672\">only<\/span>:\n    - <span style=\"color:#ae81ff\">tags<\/span>\n  <span style=\"color:#f92672\">script<\/span>:\n    - <span style=\"color:#ae81ff\">curl -O https:\/\/clever-tools.cellar.services.clever-cloud.com\/releases\/latest\/clever-tools-latest_linux.tar.gz<\/span>\n    - <span style=\"color:#ae81ff\">tar -xvf clever-tools-latest_linux.tar.gz --strip-components=1<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever login --token $CLEVER_TOKEN --secret $CLEVER_SECRET<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever env set GIT_TAG $CI_COMMIT_TAG --alias $APP_ALIAS_PROD<\/span>\n  <span style=\"color:#f92672\">when<\/span>: <span style=\"color:#ae81ff\">on_success<\/span>\n\n<span style=\"color:#f92672\">good_night_preprod<\/span>:\n  <span style=\"color:#f92672\">stage<\/span>: <span style=\"color:#ae81ff\">money<\/span>\n  <span style=\"color:#f92672\">only<\/span>:\n    <span style=\"color:#f92672\">variables<\/span>:\n      - <span style=\"color:#ae81ff\">$SLEEPING == &#34;PREPROD&#34;<\/span>\n  <span style=\"color:#f92672\">before_script<\/span>:\n    - <span style=\"color:#ae81ff\">apt-get update -yqq<\/span>\n    - <span style=\"color:#ae81ff\">apt-get install git curl -y<\/span>\n  <span style=\"color:#f92672\">script<\/span>:\n    - <span style=\"color:#ae81ff\">curl -O https:\/\/clever-tools.cellar.services.clever-cloud.com\/releases\/latest\/clever-tools-latest_linux.tar.gz<\/span>\n    - <span style=\"color:#ae81ff\">tar -xvf clever-tools-latest_linux.tar.gz --strip-components=1<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever login --token $CLEVER_TOKEN --secret $CLEVER_SECRET<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever stop --alias $APP_ALIAS_PREPROD<\/span>\n\n<span style=\"color:#f92672\">wakeup_preprod<\/span>:\n  <span style=\"color:#f92672\">stage<\/span>: <span style=\"color:#ae81ff\">money<\/span>\n  <span style=\"color:#f92672\">only<\/span>:\n    <span style=\"color:#f92672\">variables<\/span>:\n      - <span style=\"color:#ae81ff\">$WAKEUP == &#34;PREPROD&#34;<\/span>\n  <span style=\"color:#f92672\">before_script<\/span>:\n    - <span style=\"color:#ae81ff\">apt-get update -yqq<\/span>\n    - <span style=\"color:#ae81ff\">apt-get install git curl -y<\/span>\n  <span style=\"color:#f92672\">script<\/span>:\n    - <span style=\"color:#ae81ff\">curl -O https:\/\/clever-tools.cellar.services.clever-cloud.com\/releases\/latest\/clever-tools-latest_linux.tar.gz<\/span>\n    - <span style=\"color:#ae81ff\">tar -xvf clever-tools-latest_linux.tar.gz --strip-components=1<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever login --token $CLEVER_TOKEN --secret $CLEVER_SECRET<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever env set CC_PRE_RUN_HOOK &#34;composer.phar run-script drupal-update-all&#34; --alias $APP_ALIAS_PREPROD<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever restart --alias $APP_ALIAS_PREPROD<\/span>\n\n<span style=\"color:#f92672\">install_preprod<\/span>:\n  <span style=\"color:#f92672\">stage<\/span>: <span style=\"color:#ae81ff\">oneshot<\/span>\n  <span style=\"color:#f92672\">environment<\/span>:\n    <span style=\"color:#f92672\">name<\/span>: <span style=\"color:#ae81ff\">preproduction<\/span>\n    <span style=\"color:#f92672\">url<\/span>:  <span style=\"color:#ae81ff\">https:\/\/$APP_NAME_PREPROD.cleverapps.io<\/span>\n  <span style=\"color:#f92672\">when<\/span>: <span style=\"color:#ae81ff\">manual<\/span>\n  <span style=\"color:#f92672\">before_script<\/span>:\n    - <span style=\"color:#ae81ff\">apt-get update -yqq<\/span>\n    - <span style=\"color:#ae81ff\">apt-get install git curl -y<\/span>\n  <span style=\"color:#f92672\">only<\/span>:\n    - <span style=\"color:#ae81ff\">\/release\/<\/span>\n  <span style=\"color:#f92672\">except<\/span>:\n    - <span style=\"color:#ae81ff\">schedules<\/span>\n  <span style=\"color:#f92672\">script<\/span>:\n    - <span style=\"color:#ae81ff\">curl -O https:\/\/clever-tools.cellar.services.clever-cloud.com\/releases\/latest\/clever-tools-latest_linux.tar.gz<\/span>\n    - <span style=\"color:#ae81ff\">tar -xvf clever-tools-latest_linux.tar.gz --strip-components=1<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever login --token $CLEVER_TOKEN --secret $CLEVER_SECRET<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever env set CC_PRE_RUN_HOOK &#34;composer.phar run-script drupal-monsite-default-install&#34; --alias $APP_ALIAS_PREPROD<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever deploy --alias $APP_ALIAS_PREPROD -f<\/span>\n\n<span style=\"color:#f92672\">install_prod<\/span>:\n  <span style=\"color:#f92672\">stage<\/span>: <span style=\"color:#ae81ff\">oneshot<\/span>\n  <span style=\"color:#f92672\">environment<\/span>:\n    <span style=\"color:#f92672\">name<\/span>: <span style=\"color:#ae81ff\">production<\/span>\n    <span style=\"color:#f92672\">url<\/span>:  <span style=\"color:#ae81ff\">https:\/\/$APP_NAME_PROD.cleverapps.io<\/span>\n  <span style=\"color:#f92672\">when<\/span>: <span style=\"color:#ae81ff\">manual<\/span>\n  <span style=\"color:#f92672\">before_script<\/span>:\n    - <span style=\"color:#ae81ff\">apt-get update -yqq<\/span>\n    - <span style=\"color:#ae81ff\">apt-get install git curl -y<\/span>\n  <span style=\"color:#f92672\">only<\/span>:\n    - <span style=\"color:#ae81ff\">tags<\/span>\n  <span style=\"color:#f92672\">script<\/span>:\n    - <span style=\"color:#ae81ff\">curl -O https:\/\/clever-tools.cellar.services.clever-cloud.com\/releases\/latest\/clever-tools-latest_linux.tar.gz<\/span>\n    - <span style=\"color:#ae81ff\">tar -xvf clever-tools-latest_linux.tar.gz --strip-components=1<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever login --token $CLEVER_TOKEN --secret $CLEVER_SECRET<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever env set CC_PRE_RUN_HOOK &#34;composer.phar run-script drupal-monsite-default-install&#34; --alias $APP_ALIAS_PROD<\/span>\n    - <span style=\"color:#ae81ff\">.\/clever deploy --alias $APP_ALIAS_PROD -f<\/span>\n<\/code><\/pre><\/div>"},{"title":"G\u00e9rer des profils distincts dans Git","link":"https:\/\/gastaud.io\/article\/git-gerer-profils\/","pubDate":"Thu, 24 Jan 2019 00:02:55 +0100","guid":"https:\/\/gastaud.io\/article\/git-gerer-profils\/","description":"<p>Vous utilisez votre ordinateur pour travailler et, plus que jamais, il vous arrive s\u00fbrement d&rsquo;utiliser Git (que feriez-vous sur cet article sinon ?).<br>\nPeut-\u00eatre vous arrive-t-il aussi de travailler sur un projet personnel sur ce m\u00eame ordinateur ?<br>\nOu de contribuer \u00e0 un projet Open-Source, toujours sur la m\u00eame machine.<\/p>\n<p>Vous avez probablement d\u00e9fini une identit\u00e9 (nom + mail) globale \u00e0 votre machine et vous la surcharg\u00e9e pour chaque projet en fonction du contexte.<\/p>\n<p>Parfait !<\/p>\n<p>Et pourtant malgr\u00e9 ces pr\u00e9cautions d&rsquo;usage, qui n&rsquo;a jamais commit\u00e9 dans un projet avec une mauvaise identit\u00e9 ?\nParfois peut \u00eatre vous en vous rendu compte tardivement et votre email personnel se retrouve maintenant <em>ad vitam \u00e6ternam<\/em> dans les logs d&rsquo;un projet.<\/p>\n<p>Alors comment essayer de limiter le risque d&rsquo;erreurs ?<\/p>\n<p>Pourrait-on automatiser la s\u00e9lection de la bonne identit\u00e9 pour nos projets ?<\/p>\n<p>Et bien, la bonne nouvelle est que <strong>la r\u00e9ponse est OUI<\/strong>  et que nous allons voir comment juste apr\u00e8s.<\/p>\n<h2 id=\"gitconfig-et-includes-conditionnels-gr\u00e2ce-\u00e0-includeif\">.gitconfig et includes conditionnels gr\u00e2ce \u00e0 IncludeIf<\/h2>\n<p>Prenons le fichier <code>.gitconfig<\/code>, g\u00e9n\u00e9ralement pr\u00e9sent dans votre <code>$HOME<\/code>.<\/p>\n<pre tabindex=\"0\"><code>[user]\n\tname = Jean-Yves Gastaud\n\temail = mon.super@mail.com\n\n[alias]\n  amend = commit --amend\n  st = status\n  co = checkout\n<\/code><\/pre><p>Ce fichier d\u00e9fini notre identit\u00e9 par d\u00e9faut et nos aliases globaux.<\/p>\n<p>Gr\u00e2ce \u00e0 utilisation de la directive <code>includeIf<\/code><sup id=\"fnref:1\"><a href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\">1<\/a><\/sup> dans notre fichier <code>.gitconfig<\/code>, nous pouvons maintenant ajouter des includes conditionnels li\u00e9s, notamment \u00e0 des r\u00e9pertoires.<\/p>\n<pre tabindex=\"0\"><code>[includeIf &quot;gitdir:\/workspace\/work\/&quot;]\n  path = ~\/.gitconfig-work\n\n[includeIf &quot;gitdir:\/workspace\/opensource\/&quot;]\n  path = ~\/.gitconfig-opensource\n\n[includeIf &quot;gitdir:\/workspace\/perso\/&quot;]\n  path = ~\/.gitconfig-perso\n<\/code><\/pre><p>Pour chaque <code>includeIf<\/code> d\u00e9fini, il ne nous reste plus qu&rsquo;\u00e0 cr\u00e9er le fichier correspondant au chemin que nous avons d\u00e9fini dans <code>path<\/code>.<\/p>\n<p>Dans le fichier <code>~\/.gitconfig-work<\/code>, nous n&rsquo;avons donc plus qu&rsquo;\u00e0 ajouter ou surcharger les configurations qui nous int\u00e9resse.<\/p>\n<p>Exemple :<\/p>\n<pre tabindex=\"0\"><code>[user]\n        email = my.work@mail.com\n\n[core]\n        editor = &quot;code -w&quot;\n<\/code><\/pre><p>Dans ce fichier, on surcharge donc notre email et on ajoute un \u00e9diteur de code par d\u00e9faut.<\/p>\n<p>Maintenant, tout dossier sous le r\u00e9pertoire <code>\/workspace\/work<\/code> b\u00e9n\u00e9ficiera automatiquement du chargement des configuration fichier <code>.gitconfig<\/code>, surcharger ou compl\u00e9ter par celles du fichier <code>.gitconfig-work<\/code>.<\/p>\n<p>Pour v\u00e9rifier votre configuration, allez dans n&rsquo;importe quel r\u00e9pertoire sous le r\u00e9pertoire cibl\u00e9, <code>\/workspace\/work<\/code> dans notre exemple et lancer la commande <code>git config -l<\/code><\/p>\n<p>Vous devriez voir la fusion de vos fichiers de configs.<\/p>\n<pre tabindex=\"0\"><code>user.name=Jean-Yves Gastaud\nemail = mon.super@mail.com\nalias.amend=commit --amend\nalias.st=status\nalias.co=checkout\nincludeif.gitdir:\/workspace\/work\/.path=~\/.gitconfig-work\nuser.email=my.work@mail.com\ncore.editor=code -w\nincludeif.gitdir:\/workspace\/opensource\/.path=~\/.gitconfig-opensource\nincludeif.gitdir:\/workspace\/perso\/.path=~\/.gitconfig-perso\ncore.repositoryformatversion=0\ncore.filemode=true\n\u2026\n<\/code><\/pre><p>Comme vous pouvez le constater, les 2 mails d\u00e9finis sont pr\u00e9sents dans le listing.<br>\nLe dernier &ldquo;parlant&rdquo; ayant raison, c&rsquo;est donc bien la valeur du fichier <code>.gitconfig-work<\/code> qui sera consid\u00e9r\u00e9 lors du commit.<\/p>\n<p>Vous remarquerez aussi que les 2 autres includes sont list\u00e9s mais n&rsquo;ajoutent pas de nouvelles variables \/ surcharges car leur condition d&rsquo;application n&rsquo;est pas remplie.<\/p>\n<p>Cela nous laisse toujours la possibilit\u00e9 de surcharger, localement au repository, toutes configurations souhait\u00e9es, comme avant.<\/p>\n<p><strong>En quelques lignes et une gestion simple de vos r\u00e9pertoires de travail, il vous est donc possible de ne plus risquer de vous tromper dans le changement de vos identit\u00e9s.<\/strong><\/p>\n<section class=\"footnotes\" role=\"doc-endnotes\">\n<hr>\n<ol>\n<li id=\"fn:1\" role=\"doc-endnote\">\n<p>Depuis la version 2.13 de Git (mai 2017). <a href=\"https:\/\/git-scm.com\/docs\/git-config#_includes\">Documentation sur les Includes dans Git<\/a>&#160;<a href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\">&#x21a9;&#xfe0e;<\/a><\/p>\n<\/li>\n<\/ol>\n<\/section>\n"},{"title":"Mon workflow Git","link":"https:\/\/gastaud.io\/article\/git-workflow\/","pubDate":"Tue, 22 Jan 2019 08:51:19 +0100","guid":"https:\/\/gastaud.io\/article\/git-workflow\/","description":"<p>R\u00e9sum\u00e9 du workflow Git, en ligne de commande, que j&rsquo;applique de fa\u00e7on quasi-syst\u00e9matique \u00e0 mes projets.<\/p>\n<p>Dans cet article, je n&rsquo;entrerai pas le d\u00e9tail de la gestion des Pull Request \/ Merge Request, des process de validation&hellip;<\/p>\n<p>Ces \u00e9l\u00e9ments peuvent, bien entendu, modifier l\u00e9g\u00e8rement le process en fonction des contraintes que peuvent avoir les outils utilis\u00e9s.<\/p>\n<h2 id=\"branches-principales\">Branches principales<\/h2>\n<p>Au d\u00e9marrage du projet, 3 branches principales et permanentes (qui seront toujours pr\u00e9sentes) sont cr\u00e9\u00e9es.<\/p>\n<ul>\n<li><code>master<\/code> : La branche de production<\/li>\n<li><code>stage<\/code> :  La branche de pr\u00e9production. Contient toutes les branches de story \/ ticket valid\u00e9es, pr\u00eates \u00e0 passer en production.<\/li>\n<li><code>dev<\/code> : La branche de d\u00e9veloppement. Contient les branches \u00e0 tester. Les branches sont merg\u00e9es dessus au fil de l&rsquo;eau.<\/li>\n<\/ul>\n<p>A l&rsquo;initialisation, toutes les branches auront donc le m\u00eame point de d\u00e9part.<\/p>\n\n<link rel=\"stylesheet\" href=\"https:\/\/gastaud.io\/css\/hugo-easy-gallery.css\" \/>\n<div class=\"box\">\n<figure  class=\"center\"  itemprop=\"associatedMedia\"\n  itemscope itemtype=\"http:\/\/schema.org\/ImageObject\" \n  style=\"max-width:400px\" >\n    <div class=\"img\">\n      <img itemprop=\"thumbnail\" src=\"images\/01-init.svg\" \/>\n    <\/div>\n    <a href=\"images\/01-init.svg\" itemprop=\"contentUrl\"><\/a>\n  <\/figure>\n<\/div>\n\n<p>Au fil des d\u00e9veloppements, ces 3 branches, en plus des branches de tickets, vont vivres \u00e0 des rythmes diff\u00e9rents.<\/p>\n<p>Pour chaque ticket ou story, une nouvelle branche sera cr\u00e9\u00e9e. Nous verrons plus loin l&rsquo;origine de cette branche.<\/p>\n<h2 id=\"cycle-de-vie-des-branches-principales\">Cycle de vie des branches principales<\/h2>\n<h3 id=\"dev\">dev<\/h3>\n<p>La branche de <code>dev<\/code> re\u00e7oit les <em>merges<\/em> des branches de ticket au fil de l&rsquo;eau.<\/p>\n<p>Les branches de ticket peuvent ne pas \u00eatre rebase au moment du merge dans <code>dev<\/code>.<\/p>\n<p>Il est possible de merger plusieurs fois une m\u00eame branche de ticket dans <code>dev<\/code>.<\/p>\n\n\n<div class=\"box\">\n<figure  class=\"center\"  itemprop=\"associatedMedia\"\n  itemscope itemtype=\"http:\/\/schema.org\/ImageObject\" \n  style=\"max-width:400px\" >\n    <div class=\"img\">\n      <img itemprop=\"thumbnail\" src=\"images\/02-dev-branch.svg\" \/>\n    <\/div>\n    <a href=\"images\/02-dev-branch.svg\" itemprop=\"contentUrl\"><\/a>\n  <\/figure>\n<\/div>\n\n<h3 id=\"stage\">stage<\/h3>\n<p>Tous les d\u00e9veloppements ou les cr\u00e9ations de nouvelles branches partent de la branche <code>stage<\/code>.<\/p>\n\n\n<div class=\"box\">\n<figure  class=\"center\"  itemprop=\"associatedMedia\"\n  itemscope itemtype=\"http:\/\/schema.org\/ImageObject\" \n  style=\"max-width:500px\" >\n    <div class=\"img\">\n      <img itemprop=\"thumbnail\" src=\"images\/03.1-stage-branch.svg\" \/>\n    <\/div>\n    <a href=\"images\/03.1-stage-branch.svg\" itemprop=\"contentUrl\"><\/a>\n      <figcaption><h4>merge de la branche t2 dans stage<\/h4>\n      <\/figcaption>\n  <\/figure>\n<\/div>\n\n<p>Les merges sur <code>stage<\/code> doivent \u00eatre <strong>obligatoirement<\/strong> fait en <code>fast-forward<\/code>.<\/p>\n<p>Cela implique que chaque branche est <code>rebase<\/code> avant d&rsquo;\u00eatre merg\u00e9e dans <code>stage<\/code>.<\/p>\n<p>On garde ainsi un historique propre et lin\u00e9aire.<\/p>\n\n\n<div class=\"box\">\n<figure  class=\"center\"  itemprop=\"associatedMedia\"\n  itemscope itemtype=\"http:\/\/schema.org\/ImageObject\" \n  style=\"max-width:400px\" >\n    <div class=\"img\">\n      <img itemprop=\"thumbnail\" src=\"images\/03.1-stage-ff.svg\" \/>\n    <\/div>\n    <a href=\"images\/03.1-stage-ff.svg\" itemprop=\"contentUrl\"><\/a>\n      <figcaption><h4>merge avec fast-forward<\/h4>\n      <\/figcaption>\n  <\/figure>\n<\/div>\n\n<blockquote>\n<p>En cas de merge de multiples branches, le travail peut \u00eatre un peu fastidieux car il implique de passer sur chaque branche 1 par 1 pour la rebase avant de la merger.<\/p>\n<\/blockquote>\n<h3 id=\"master\">master<\/h3>\n<p>On ne merge sur <code>master<\/code> que dans 2 cas :<\/p>\n<ol>\n<li>lors d&rsquo;un passage en production de fin de sprint ou de cycle de release. Le merge se fait uniquement via la branche <code>stage<\/code>.<\/li>\n<li>en cas de hotfix. Via une branche de ticket.<\/li>\n<\/ol>\n\n\n<div class=\"box\">\n<figure  class=\"center\"  itemprop=\"associatedMedia\"\n  itemscope itemtype=\"http:\/\/schema.org\/ImageObject\" \n  style=\"max-width:400px\" >\n    <div class=\"img\">\n      <img itemprop=\"thumbnail\" src=\"images\/04-master-branch.svg\" \/>\n    <\/div>\n    <a href=\"images\/04-master-branch.svg\" itemprop=\"contentUrl\"><\/a>\n      <figcaption><h4>merge de stage dans master<\/h4>\n      <\/figcaption>\n  <\/figure>\n<\/div>\n\n<p>La branche <code>master<\/code> ne doit <strong>jamais \u00eatre rebase ou &ldquo;push forc\u00e9e&rdquo;<\/strong>.<\/p>\n<blockquote>\n<p>Id\u00e9alement la branche <code>master<\/code> est prot\u00e9g\u00e9e pour \u00e9viter les erreurs.<\/p>\n<\/blockquote>\n<h3 id=\"r\u00e9initialisation-des-branches\">R\u00e9initialisation des branches<\/h3>\n<h4 id=\"cas-1--merge-dans-master\">Cas 1 : Merge dans master<\/h4>\n<p>A la suite d&rsquo;un merge dans <code>master<\/code>, il conviendra,<\/p>\n<ul>\n<li>de rebase <code>stage<\/code><\/li>\n<\/ul>\n\n\n<div class=\"box\">\n<figure  class=\"center\"  itemprop=\"associatedMedia\"\n  itemscope itemtype=\"http:\/\/schema.org\/ImageObject\" \n  style=\"max-width:500px\" >\n    <div class=\"img\">\n      <img itemprop=\"thumbnail\" src=\"images\/05-reinit-branches.svg\" \/>\n    <\/div>\n    <a href=\"images\/05-reinit-branches.svg\" itemprop=\"contentUrl\"><\/a>\n      <figcaption><h4>merge de stage dans master<\/h4>\n      <\/figcaption>\n  <\/figure>\n<\/div>\n\n<ul>\n<li>de r\u00e9initialiser <code>dev<\/code><\/li>\n<\/ul>\n\n\n<div class=\"box\">\n<figure  class=\"center\"  itemprop=\"associatedMedia\"\n  itemscope itemtype=\"http:\/\/schema.org\/ImageObject\" \n  style=\"max-width:500px\" >\n    <div class=\"img\">\n      <img itemprop=\"thumbnail\" src=\"images\/05.1-reinit-branches.svg\" \/>\n    <\/div>\n    <a href=\"images\/05.1-reinit-branches.svg\" itemprop=\"contentUrl\"><\/a>\n      <figcaption><h4>r\u00e9initialisation de dev<\/h4>\n      <\/figcaption>\n  <\/figure>\n<\/div>\n\n<h4 id=\"cas-2--divergences-des-branches\">Cas 2 : Divergences des branches<\/h4>\n<p>Avec le fait d&rsquo;accepter n&rsquo;importe quel merge sur <code>dev<\/code>, cette branche peut devenir rapidement tr\u00e8s diff\u00e9rentes, diverger, de la branche de production, <code>master<\/code>, voir de <code>stage<\/code> comme illustr\u00e9 ci-dessous :<\/p>\n\n\n<div class=\"box\">\n<figure  class=\"center\"  itemprop=\"associatedMedia\"\n  itemscope itemtype=\"http:\/\/schema.org\/ImageObject\" \n  style=\"max-width:300px\" >\n    <div class=\"img\">\n      <img itemprop=\"thumbnail\" src=\"images\/06-divergences-branches.svg\" \/>\n    <\/div>\n    <a href=\"images\/06-divergences-branches.svg\" itemprop=\"contentUrl\"><\/a>\n      <figcaption><h4>Divergences importantes entre stage et dev<\/h4>\n      <\/figcaption>\n  <\/figure>\n<\/div>\n\n<p>La branche <code>dev<\/code> sera donc r\u00e9guli\u00e8rement r\u00e9initialis\u00e9e \u00e0 partir de la branche <code>stage<\/code>, qui rappelons-le contient la derni\u00e8re version des tickets valid\u00e9s et pr\u00eats \u00e0 passer en production.<\/p>\n<blockquote>\n<p>Concr\u00e8tement, nous allons donc faire un <code>git reset --hard origin\/stage<\/code><\/p>\n<\/blockquote>\n<p>Une fois la r\u00e9initialisation faite, il conviendra, si cela est n\u00e9cessaire, de :<\/p>\n<ol>\n<li><code>rebase<\/code> les branches de d\u00e9veloppements \u00e0 partir de <code>stage<\/code><\/li>\n<li>(Re)merger dans <code>dev<\/code> les branches qui auraient pu \u00eatre en attente de tests.<\/li>\n<\/ol>\n<blockquote>\n<p>Dans notre exemple, les branches <code>t1-new-feat<\/code>, <code>t2-new-feat<\/code>, <code>t3-new-feat<\/code> n&rsquo;ont pas encore \u00e9t\u00e9 merg\u00e9es dans <code>stage<\/code> et seront donc <code>rebase<\/code> puis <code>merge<\/code> dans <code>dev<\/code>.<\/p>\n<\/blockquote>\n\n\n<div class=\"box\">\n<figure  class=\"center\"  itemprop=\"associatedMedia\"\n  itemscope itemtype=\"http:\/\/schema.org\/ImageObject\" \n  style=\"max-width:300px\" >\n    <div class=\"img\">\n      <img itemprop=\"thumbnail\" src=\"images\/06.1-divergences-branches.svg\" \/>\n    <\/div>\n    <a href=\"images\/06.1-divergences-branches.svg\" itemprop=\"contentUrl\"><\/a>\n      <figcaption><h4>Historique git propre<\/h4>\n      <\/figcaption>\n  <\/figure>\n<\/div>\n\n<!-- raw HTML omitted -->\n<p><strong>D\u00e9taillons maintenant le workflow de gestion des tickets via Git.<\/strong><\/p>\n<h2 id=\"workflow-ticket--story\">Workflow ticket \/ story<\/h2>\n<p>Pour traiter un nouveau ticket, nous commen\u00e7ons d&rsquo;abord par se mettre sur la branche <code>stage<\/code> et s&rsquo;assurer qu&rsquo;elle soit \u00e0 jour de notre <code>remote<\/code>.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">git checkout stage\ngit fetch\ngit reset --hard origin\/stage\n<\/code><\/pre><\/div><p>Nous avons ainsi une branche locale <code>stage<\/code> \u00e0 jour de notre <code>remote<\/code>.<\/p>\n<blockquote>\n<p>Si vous avez plusieurs remote, un <code>git fetch --all<\/code> est recommand\u00e9 et il conviendra d&rsquo;adapter le <code>reset --hard<\/code>.<\/p>\n<\/blockquote>\n<p>Cr\u00e9ons maintenant notre branche de ticket\/story.<\/p>\n<p>Pour cela, nous partons <strong>toujours<\/strong> de la branche <code>stage<\/code>.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">git checkout -b <span style=\"color:#f92672\">[<\/span>NUM-TICKET<span style=\"color:#f92672\">]<\/span>-<span style=\"color:#f92672\">[<\/span>short-description<span style=\"color:#f92672\">]<\/span>\n<\/code><\/pre><\/div><blockquote>\n<p>Si vous utilisez Gitlab pour vos tickets, il existe une fonction permettant de g\u00e9n\u00e9rer directement des branches \u00e0 partir d&rsquo;un ticket et ainsi d&rsquo;avoir un nommage de vos branches coh\u00e9rent.<br>\nGitlab se base sur le titre du ticket pour nommer la branche.<br>\nIl conviendra d&rsquo;avoir d\u00e9finie la branche <code>stage<\/code> comme branche par d\u00e9faut dans Gitlab pour que le process expliqu\u00e9 ci-dessus s&rsquo;applique automatiquement.<\/p>\n<\/blockquote>\n<p>Une fois le ticket termin\u00e9, ou dans un \u00e9tat suffisamment stable pour \u00eatre test\u00e9, nous pouvons merger la branche dans <code>dev<\/code>.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">git checkout dev\ngit merge <span style=\"color:#f92672\">[<\/span>NUM-TICKET<span style=\"color:#f92672\">]<\/span>-<span style=\"color:#f92672\">[<\/span>short-description<span style=\"color:#f92672\">]<\/span>\n<\/code><\/pre><\/div><p>Si le ticket n&rsquo;est pas valid\u00e9, vous pouvez merger \u00e0 nouveau la branche de ticket dans <code>dev<\/code>.<\/p>\n<p>Si le ticket est valid\u00e9 (tests automatis\u00e9s valid\u00e9s, recette OK&hellip;), il est donc consid\u00e9r\u00e9 comme pouvant potentiellement passer en production.<\/p>\n<p>Dans le cadre d&rsquo;un projet Agile avec des sprints ou d&rsquo;une TMA avec lotissement de correctifs \/ \u00e9volutions, le ticket ne passera potentiellement pas en production.<\/p>\n<p>Notre branche de ticket va donc \u00eatre merg\u00e9 dans la branche <code>stage<\/code> (notre pr\u00e9production).<\/p>\n<p>Avant le merge, assurons nous de nouveau que <code>stage<\/code> est (toujours) \u00e0 jour localement.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">git checkout stage\n<span style=\"color:#75715e\"># Mise \u00e0 jour de stage si n\u00e9cessaire; Voir plus haut<\/span>\ngit merge --ff-only <span style=\"color:#f92672\">[<\/span>NUM-TICKET<span style=\"color:#f92672\">]<\/span>-<span style=\"color:#f92672\">[<\/span>short-description<span style=\"color:#f92672\">]<\/span>\ngit push origin stage\n<\/code><\/pre><\/div><blockquote>\n<p>Si vous avez un message d&rsquo;erreur, c&rsquo;est probablement que la branche de ticket n&rsquo;est pas \u00e0 jour de <code>stage<\/code>.<br>\nPensez \u00e0 la <code>rebase<\/code>.<\/p>\n<\/blockquote>\n<p>Si une autre branche de ticket (\u00e9galement valid\u00e9e) doit \u00eatre merg\u00e9e sur <code>stage<\/code>, nous devons donc rebase cette branche avant de pouvoir la merger.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">git checkout <span style=\"color:#f92672\">[<\/span>NUM-TICKET<span style=\"color:#f92672\">]<\/span>-<span style=\"color:#f92672\">[<\/span>short-description<span style=\"color:#f92672\">]<\/span>\ngit rebase origin\/stage\n<\/code><\/pre><\/div><p>Et nous recommen\u00e7ons ensuite l&rsquo;\u00e9tape pr\u00e9c\u00e9dente.<\/p>\n<p>Le code de <code>stage<\/code> peut ainsi \u00eatre d\u00e9ploy\u00e9 en pr\u00e9production pour validation finale et globale.<\/p>\n<p>Une fois le sprint fini ou la release compl\u00e9t\u00e9e, nous allons finalement merger l&rsquo;ensemble de la branche <code>stage<\/code> sur <code>master<\/code>.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">git checkout master\n<span style=\"color:#75715e\"># Mise \u00e0 jour de la branche master locale si n\u00e9cessaire<\/span>\ngit merge origin\/stage\n* git push origin master\n<\/code><\/pre><\/div><blockquote>\n<p>Ici, nous ne mergons pas la branche stage en <code>fast-forward<\/code> afin d&rsquo;avoir une lecture simplifi\u00e9e \/ rapide, gr\u00e2ce au commit de merge, de la derni\u00e8re livraison.<br>\nJe vous laisse choisir si vous pr\u00e9f\u00e9rez cela ou non.<\/p>\n<\/blockquote>\n<p>Enfin il est possible de tagger la branche <code>master<\/code> et publier notre release.<\/p>\n<p>Une fois notre branche <code>stage<\/code> merg\u00e9e dans <code>master<\/code>, nous pouvons r\u00e9initialiser nos branches <code>stage<\/code> et <code>dev<\/code>.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\">git checkout stage <span style=\"color:#f92672\">&amp;&amp;<\/span> git reset --hard origin\/master\n<\/code><\/pre><\/div><h2 id=\"faq\">FAQ<\/h2>\n<p>Quelques questions \/ objections r\u00e9currentes reviennent r\u00e9guli\u00e8rement quand je pr\u00e9sente ce workflow.<\/p>\n<h3 id=\"pourquoi-avoir-une-branche-de-preproduction--stage-\">Pourquoi avoir une branche de preproduction \/ stage ?<\/h3>\n<p>C&rsquo;est certainement le point qui fait le plus d\u00e9bat.<\/p>\n<p>Il serait possible de se passer de la branche <code>stage<\/code> et merger directement nos tickets dans <code>master<\/code>.<br>\n<code>master<\/code> serait ainsi toujours la branche par d\u00e9faut, le point d&rsquo;origine de tous les nouveaux d\u00e9veloppements.<\/p>\n<p>J&rsquo;appr\u00e9cie d&rsquo;avoir une branche <code>stage<\/code> pour les raisons suivantes :<\/p>\n<ol>\n<li>\n<p><strong>En cas de bug sur ma branche <code>stage<\/code><\/strong> : parceque 2 d\u00e9veloppements ne sont finalement pas totalement compatibles entre eux ou qu&rsquo;ils entrainent une r\u00e9gression, il est possible simplement d&rsquo;ajouter un <code>hotfix<\/code> sur <code>stage<\/code> en s&rsquo;assurant qu&rsquo;il ira en prod,<\/p>\n<\/li>\n<li>\n<p><strong>En cas de non validation d&rsquo;un ticket au sein d&rsquo;une release<\/strong> :  il est autoris\u00e9 de r\u00e9-\u00e9crire l&rsquo;historique de <code>stage<\/code> (m\u00eame si cela doit \u00eatre exceptionnel) sans grosses contraintes de communication avec l&rsquo;ensembe de l&rsquo;\u00e9quipe.<br>\nAlors que la r\u00e9-\u00e9criture de l&rsquo;historique de <code>master<\/code> n&rsquo;est pas autoris\u00e9e.<\/p>\n<\/li>\n<\/ol>\n<p>Avoir une branche <code>stage<\/code> offre donc une plus grande souplesse dans le workflow.<\/p>\n<h3 id=\"rebase-chaque-branche-de-travail-avant-le-merge-sur-stage-peut-\u00eatre-long-et-conflictuel\">Rebase chaque branche de travail avant le merge sur <code>stage<\/code> peut \u00eatre long et conflictuel<\/h3>\n<p>Cela n&rsquo;est vrai que si vos branches de travail sont longues et trainent dans le temps.<br>\n\u00c9ventuellement si vous \u00eates un tr\u00e8s grand nombre de d\u00e9veloppeurs \u00e0 travailler sur de nouvelles branches simultan\u00e9ement.<\/p>\n<p>Il est donc n\u00e9cessaire que l&rsquo;\u00e9quipe applique un minimum de rigeur sur la gestion des points suivants :<\/p>\n<ol>\n<li>\n<p>Rebase r\u00e9gulier des branches de travail depuis <code>stage<\/code> : les principaux conflits sont ainsi r\u00e9solus directement par le d\u00e9veloppeur. Il peut s&rsquo;assurer que son travail n&rsquo;a pas \u00e9t\u00e9 impact\u00e9.<\/p>\n<\/li>\n<li>\n<p>Les branches de travail doivent avoir une dur\u00e9e de vie courte, quelques heures ou jours maximum.<br>\nAu plus les process de tests \/ non r\u00e9gressions sont automatis\u00e9s, au plus vous serez en mesure de r\u00e9duire ce d\u00e9lai.<\/p>\n<\/li>\n<\/ol>\n<h3 id=\"comment-faire-si-une-branche-de-travail-d\u00e9pend-dun-autre-d\u00e9veloppement-en-cours-et-non-disponible-sur-stage-\">Comment faire si une branche de travail d\u00e9pend d&rsquo;un autre d\u00e9veloppement en cours et non disponible sur <code>stage<\/code> ?<\/h3>\n<p>C&rsquo;est une question sans r\u00e9elle solution que l&rsquo;on retrouvera dans tous workflows.<\/p>\n<p>En fonction du niveau de d\u00e9pendance, 2 cas peuvent s&rsquo;appliquer :<\/p>\n<ol>\n<li>\n<p><strong>Je ne d\u00e9pend que d&rsquo;un commit pr\u00e9sent dans une autre branche<\/strong> : dans ce cas, je pr\u00e9conise de <code>cherry-pick<\/code> le commit dans la branche. On garde ainsi nos branches ind\u00e9pendantes.<\/p>\n<\/li>\n<li>\n<p><strong>Ma nouvelle feature vient compl\u00e9ter une autre branche<\/strong> : dans ce cas, ma suggestion est d&rsquo;avoir une branche commune qui regroupera les 2 d\u00e9veloppements au moment du merge dans <code>stage<\/code>.<\/p>\n<\/li>\n<\/ol>\n<p>Notre nouvelle branche pourra donc, selon un choix \u00e0 discuter avec les \u00e9quipes,<\/p>\n<ul>\n<li>soit partir de la branche du ticket dont je d\u00e9pend et \u00eatre rebase r\u00e9guli\u00e8rement avec les nouveaux d\u00e9veloppements de cette branche,<\/li>\n<li>soit travailler directement sur la m\u00eame branche.<\/li>\n<\/ul>\n<p><strong>Alors qu&rsquo;en pensez-vous ? A vous maintenant de r\u00e9agir et partager vos propres workflows<\/strong><sup id=\"fnref:1\"><a href=\"#fn:1\" class=\"footnote-ref\" role=\"doc-noteref\">1<\/a><\/sup>.<\/p>\n<section class=\"footnotes\" role=\"doc-endnotes\">\n<hr>\n<ol>\n<li id=\"fn:1\" role=\"doc-endnote\">\n<p>graph git g\u00e9n\u00e9r\u00e9 grace \u00e0 <a href=\"https:\/\/github.com\/deuill\/grawkit\">Grawkit<\/a>&#160;<a href=\"#fnref:1\" class=\"footnote-backref\" role=\"doc-backlink\">&#x21a9;&#xfe0e;<\/a><\/p>\n<\/li>\n<\/ol>\n<\/section>\n"},{"title":"Git - Comparer les commits de 2 branches Git","link":"https:\/\/gastaud.io\/article\/git-compare-branches\/","pubDate":"Fri, 18 Jan 2019 09:50:14 +0100","guid":"https:\/\/gastaud.io\/article\/git-compare-branches\/","description":"<p>Listing des diff\u00e9rentes mani\u00e8res de comparer des branches dans Git.<\/p>\n<h2 id=\"tldr\">TLDR;<\/h2>\n<p>Toute la magie r\u00e9side dans l&rsquo;utilisation des plages de r\u00e9vision et la syntaxe <code>BRANCH_1..BRANCH_2<\/code><\/p>\n<h2 id=\"connaitre-les-diff\u00e9rences-entre-2-branches\">Connaitre les diff\u00e9rences entre 2 branches<\/h2>\n<pre tabindex=\"0\"><code>git diff [BRANCH_1]..[BRANCH_2]\n<\/code><\/pre><p>retourne les diff\u00e9rences de code entre 2 branches.<\/p>\n<pre tabindex=\"0\"><code>diff --git a\/readme.txt b\/readme.txt\nindex ac28f91..ae362e4 100644\n--- a\/readme.txt\n+++ b\/readme.txt\n@@ -1 +1 @@\n-Coucou\n+Coucou2\n<\/code><\/pre><p>On constate ici que dans le fichier readme.txt de a branche <code>b<\/code>, le mot <code>Coucou<\/code> a \u00e9t\u00e9 remplac\u00e9 par <code>Coucou2<\/code>.<\/p>\n<h2 id=\"comparer-les-commits-de-2-branches\">Comparer les commits de 2 branches<\/h2>\n<p>Avec l&rsquo;utilisation de <code>diff<\/code>, nous n&rsquo;avons cependant pas la vision sur les commits qui ont cr\u00e9\u00e9 le delta.<\/p>\n<p>Pour ce type de comparaison, il est possible d&rsquo;utiliser <code>git log<\/code>.<\/p>\n<pre tabindex=\"0\"><code>git log [BRANCH_1]..[BRANCH_2]\n<\/code><\/pre><p>retourne uniquement les commits de <code>branch_2<\/code> non pr\u00e9sents dans <code>branch_1<\/code><\/p>\n<pre tabindex=\"0\"><code>commit fc372ddecf5d98a8ba0d000a3c1ccf77a4f1237b (b)\nAuthor: Jean-Yves Gastaud &lt;jeanyves@gastaud.io&gt;\nDate:   Thu Jan 10 16:53:07 2019 +0100\n\n    third commit\n<\/code><\/pre><p>Pour avoir plus de d\u00e9tails sur le contenu du changement, il est possible d&rsquo;utiliser toutes la panoplie d&rsquo;options de la commande <code>log<\/code>.<\/p>\n<p>L&rsquo;utilisation des options <code>--graph<\/code> et <code>--stat<\/code> est particuli\u00e8rement utile dans ce cas.<\/p>\n<pre tabindex=\"0\"><code>$ git log --oneline --decorate --graph --stat [BRANCH_1]..[BRANCH_2]\n<\/code><\/pre><p>retourne<\/p>\n<pre tabindex=\"0\"><code>* fc372dd (b) 3eme commit\n   readme.txt | 2 +-\n   1 file changed, 1 insertion(+), 1 deletion(-)\n<\/code><\/pre><p>Admettons maintenant que <code>branch_1<\/code> est \u00e9t\u00e9 rebase par rapport \u00e0 <code>master<\/code> mais pas la <code>branch_2<\/code>, est-ce que mon delta de commit sera toujours coh\u00e9rent ?<\/p>\n<p>Prenons l&rsquo;exemple suivant :<\/p>\n<p><code>master<\/code> contient<\/p>\n<pre tabindex=\"0\"><code>* 81faeea 2019-01-18 | a commit in master (master) [Jean-Yves Gastaud]\n* 3be0b31 2019-01-10 | 1er commit [Jean-Yves Gastaud]\n<\/code><\/pre><p><code>branch_1<\/code> a \u00e9t\u00e9 rebase avec les donn\u00e9es de <code>master<\/code> et contient 2 commits de plus.<\/p>\n<pre tabindex=\"0\"><code>* 5b3a95a 2019-01-18 | a commit (BRANCH_1) [Jean-Yves Gastaud]\n* 19b7763 2019-01-10 | 2nd commit [Jean-Yves Gastaud]\n* 81faeea 2019-01-18 | a commit in master (master) [Jean-Yves Gastaud]\n* 3be0b31 2019-01-10 | 1er commit [Jean-Yves Gastaud]\n<\/code><\/pre><p><code>branch_2<\/code> quant \u00e0 elle n&rsquo;est pas rebase et par toujours du 1er commit sur <code>master<\/code>.<\/p>\n<pre tabindex=\"0\"><code>* fc372dd 2019-01-10 | 3eme commit (BRANCH_2) [Jean-Yves Gastaud]\n* 3be0b31 2019-01-10 | 1er commit [Jean-Yves Gastaud]\n<\/code><\/pre><p>Retestons notre<br>\n<code>git log --oneline --decorate --graph --stat [BRANCH_1]..[BRANCH_2]<\/code><\/p>\n<pre tabindex=\"0\"><code>* fc372dd (BRANCH_2) 3eme commit\n   readme.txt | 2 +-\n   1 file changed, 1 insertion(+), 1 deletion(-)\n<\/code><\/pre><p>Super, nous voyons toujours notre commit non pr\u00e9sent dans la <code>branch_1<\/code> !<\/p>\n<p>Si besoin de savoir quelles sont les commits de la <code>branch_1<\/code> non pr\u00e9sents dans <code>branch_2<\/code>, il est bien entendu possible d&rsquo;inverser les arguments.<\/p>\n<p>Ainsi <code>git log --oneline --decorate --graph --stat [BRANCH_2]..[BRANCH_1]<\/code> nous renverra<\/p>\n<pre tabindex=\"0\"><code>* 5b3a95a (BRANCH_1) a commit\n|  readme.txt | 3 +--\n|  1 file changed, 1 insertion(+), 2 deletions(-)\n* 19b7763 2nd commit\n|  readme.txt | 3 ++-\n|  1 file changed, 2 insertions(+), 1 deletion(-)\n* 81faeea (master) a commit in master\n   readme.txt | 2 +-\n   1 file changed, 1 insertion(+), 1 deletion(-)\n<\/code><\/pre><p>Enfin, si notre <code>branch_1<\/code> a \u00e9t\u00e9 merg\u00e9e dans <code>master<\/code>, nous pouvons toujours avoir les m\u00eames r\u00e9sultats.<\/p>\n<p>Un <code>git log --oneline --decorate --graph --stat [BRANCH_2]..master<\/code> renverra bien l&rsquo;ensemble de l&rsquo;arbre de commits absents.<\/p>\n<pre tabindex=\"0\"><code>*   9eb6957 (HEAD -&gt; master) Merge branch 'a'\n|\\\n| * 5b3a95a (a) a commit\n| |  readme.txt | 3 +--\n| |  1 file changed, 1 insertion(+), 2 deletions(-)\n| * 19b7763 2nd commit\n|\/\n|    readme.txt | 3 ++-\n|    1 file changed, 2 insertions(+), 1 deletion(-)\n* 81faeea a commit in master\n   readme.txt | 2 +-\n   1 file changed, 1 insertion(+), 1 deletion(-)\n<\/code><\/pre>"},{"title":"Split JSON avec jq","link":"https:\/\/gastaud.io\/article\/split-json-jq\/","pubDate":"Wed, 16 Jan 2019 16:36:46 +0100","guid":"https:\/\/gastaud.io\/article\/split-json-jq\/","description":"<p>Vous \u00eates vous d\u00e9j\u00e0 demand\u00e9 comment d\u00e9couper un JSON en plusieurs &ldquo;sous-&ldquo;fichiers&rdquo; ?<\/p>\n<p>Si oui, cette ligne de commande est faite pour vous !<\/p>\n<p>La seule d\u00e9pendance ici est l&rsquo;utilisation de l&rsquo;outils <a href=\"https:\/\/stedolan.github.io\/jq\/\">jq<\/a>.<\/p>\n<p>Je vous encourage d&rsquo;ailleurs \u00e0 regarder cet outil tr\u00e8s puissant sur la manipulation de JSON.<\/p>\n<h2 id=\"tldr\">TL;DR<\/h2>\n<pre tabindex=\"0\"><code>jq -c -M '.[]' myfile.json | \\\nwhile read line; do echo $line &gt; $(echo $line | jq -r -c &quot;.id&quot;).json; done\n<\/code><\/pre><p>La commande va parser le fichier <code>myfile.json<\/code> et cr\u00e9er 1 fichier pour chaque <code>id<\/code> trouv\u00e9.<\/p>\n<p>Cela fonctionne \u00e9galement en r\u00e9cup\u00e9rant directement la sortie d&rsquo;un service \/ API&hellip;<\/p>\n<h3 id=\"exemple\">Exemple<\/h3>\n<pre tabindex=\"0\"><code>echo '[{ &quot;id&quot;: &quot;16&quot;, &quot;name&quot;: &quot;produit 1&quot; }, { &quot;id&quot;: &quot;17&quot;, &quot;name&quot;: &quot;produit 2&quot; }, { &quot;id&quot;: &quot;18&quot;, &quot;name&quot;: &quot;produit 3&quot; }, { &quot;id&quot;: &quot;19&quot;, &quot;name&quot;: &quot;produit 4&quot; }]' | \\\njq -c -M '.[]' | \\\nwhile read line; do \\\necho $line &gt; $(echo $line | jq -r -c &quot;.id&quot;).json; \\\ndone\n<\/code><\/pre><h2 id=\"d\u00e9tails\">D\u00e9tails<\/h2>\n<h3 id=\"parsing-du-fichier-json-initial\">Parsing du fichier JSON initial<\/h3>\n<pre tabindex=\"0\"><code>jq -c -M '.[]' myfile.json\n<\/code><\/pre><p>On va parser le fichier <code>myfile.json<\/code> et r\u00e9cup\u00e9r\u00e9r 1 ligne pour chaque entr\u00e9e de 1er niveau.<\/p>\n<h3 id=\"lire-chaque-ligne-et-cr\u00e9er-le-json-associ\u00e9\">Lire chaque ligne et cr\u00e9er le json associ\u00e9<\/h3>\n<p>Tant qu&rsquo;il y a des lignes, on boucle dessus.<\/p>\n<pre tabindex=\"0\"><code>while read line; do \n    ...\ndone\n<\/code><\/pre><p>On renvoi ensuite la ligne et on utilise de nouveau jq pour r\u00e9cup\u00e9rer l&rsquo;id afin de nommer le fichier<\/p>\n<pre tabindex=\"0\"><code>echo $line &gt; $(echo $line | jq -r -c &quot;.id&quot;).json;\n<\/code><\/pre><h3 id=\"fichier-myfilejson\">Fichier myfile.json<\/h3>\n<pre tabindex=\"0\"><code>[\n    {\n        &quot;id&quot;: &quot;16&quot;,\n        &quot;name&quot;: &quot;produit 1&quot;\n    },\n    {\n        &quot;id&quot;: &quot;17&quot;,\n        &quot;name&quot;: &quot;produit 2&quot;\n    },\n    {\n        &quot;id&quot;: &quot;18&quot;,\n        &quot;name&quot;: &quot;produit 3&quot;\n    },\n    {\n        &quot;id&quot;: &quot;19&quot;,\n        &quot;name&quot;: &quot;produit 4&quot;\n    }\n]\n<\/code><\/pre><h3 id=\"options-de-la-commande\">Options de la commande<\/h3>\n<ul>\n<li><code>-c             compact instead of pretty-printed output;<\/code> : nous permet d&rsquo;avoir 1 ligne affich\u00e9 par r\u00e9sultat<\/li>\n<li><code>-M             monochrome (don't colorize JSON);<\/code> : purement esth\u00e9tique pour ne pas risquer de polluer l&rsquo;extraction<\/li>\n<li><code>-r             output raw strings, not JSON texts;<\/code> : permet de supprimer le guillemet de sortir de notre ID.<\/li>\n<\/ul>\n<h2 id=\"bonus--ajouter-des-informations-\u00e0-un-fichier-json\">Bonus : Ajouter des informations \u00e0 un fichier JSON<\/h2>\n<pre tabindex=\"0\"><code>$ jq '. += { &quot;category&quot;: &quot;ma categoie&quot;, &quot;subcategory&quot;: &quot;ma sous categorie&quot; }' 19.json\n<\/code><\/pre><p>jq va ajouter les entr\u00e9es <code>category<\/code> et <code>subcategory<\/code> au fichier <code>19.json<\/code> lors de l&rsquo;affichage.<\/p>\n<p>Il vous ai bien entendu ensuite possible de passer ce nouveau JSON \u00e0 un nouveau fichier et l&rsquo;enregistrer.<\/p>\n"},{"title":"Makefile et parall\u00e9lisation des jobs","link":"https:\/\/gastaud.io\/article\/makefile-parallel\/","pubDate":"Wed, 16 Jan 2019 15:18:58 +0100","guid":"https:\/\/gastaud.io\/article\/makefile-parallel\/","description":"<p>PoC \/ Tests sur l&rsquo;utilisation des jobs parall\u00e8les via Make et r\u00e9sultats constat\u00e9s<\/p>\n<p><a href=\"https:\/\/www.gnu.org\/software\/make\/manual\/html_node\/Parallel.html\">https:\/\/www.gnu.org\/software\/make\/manual\/html_node\/Parallel.html<\/a><\/p>\n<h2 id=\"tests-basiques\">Tests basiques<\/h2>\n<h3 id=\"makefile\">Makefile<\/h3>\n<pre tabindex=\"0\"><code>all: test test2 test3 test4 test5 test6 test7\n\ntest:\n\t@sleep 2 &amp;&amp; \\\n\techo &quot;---&gt; 1&quot;\n\ntest2:\n\t@sleep 2 &amp;&amp; \\\n\techo &quot;---&gt; 2&quot;\n\ntest3: test test2 .FORCE\n\t@sleep 2 &amp;&amp; \\\n\techo &quot;---&gt; 3&quot;\n\ntest4:\n\t@sleep 2 &amp;&amp; \\\n\techo &quot;---&gt; 4&quot;\n\ntest5:\n\t@sleep 2 &amp;&amp; \\\n\techo &quot;---&gt; 5&quot;\n\ntest6:\n\t@sleep 2 &amp;&amp; \\\n\techo &quot;---&gt; 6&quot;\n\ntest7:\n\t@sleep 2 &amp;&amp; \\\n\techo &quot;---&gt; 7&quot;\n\n.FORCE:\n<\/code><\/pre><h2 id=\"tests-sans-parall\u00e9lisation\">Tests sans parall\u00e9lisation<\/h2>\n<p>On ex\u00e9cute 7 jobs \u00e0 la suite.<\/p>\n<p>Pour tester plus facilement nos r\u00e9sultats, chaque job dure environ 2 secondes.<\/p>\n<pre tabindex=\"0\"><code>$ date +'%Y%m%d%H%M%S' &amp;&amp; make &amp;&amp;  date +'%Y%m%d%H%M%S'\n20181213172307\n---&gt; 1\n---&gt; 2\n---&gt; 3\n---&gt; 4\n---&gt; 5\n---&gt; 6\n---&gt; 7\n20181213172321\n<\/code><\/pre><p>Comme pr\u00e9vu, nos jobs mettent 14 secondes \u00e0 s&rsquo;\u00e9x\u00e9cuter.<\/p>\n<h3 id=\"tests-avec-parall\u00e9lisation\">Tests avec parall\u00e9lisation<\/h3>\n<p>On lance maintenant la m\u00eame commande en parall\u00e9lisant les 7 jobs.<\/p>\n<pre tabindex=\"0\"><code>$ date  +'%Y%m%d%H%M%S' &amp;&amp; make -j7 &amp;&amp; date  +'%Y%m%d%H%M%S'\n20181213172242\n---&gt; 1\n---&gt; 5\n---&gt; 7\n---&gt; 2\n---&gt; 6\n---&gt; 4\n---&gt; 3\n20181213172246\n<\/code><\/pre><p>On a ici un temps d&rsquo;\u00e9x\u00e9cution de 4 secondes correspondant au temps d&rsquo;\u00e9x\u00e9cution du <code>test3<\/code> qui est oblig\u00e9 d&rsquo;attendre l&rsquo;\u00e9x\u00e9ution de <code>test1<\/code> et <code>test2<\/code>.<\/p>\n<blockquote>\n<p>Soucis : Les jobs s&rsquo;ex\u00e9cutent dans un ordre al\u00e9atoire.<\/p>\n<\/blockquote>\n<h2 id=\"job-parall\u00e8les-ind\u00e9pendants\">Job Parall\u00e8les ind\u00e9pendants<\/h2>\n<p>Maintenant que nous avons vu comment fonctionne les jobs parall\u00e8le avec Make, comment g\u00e9rer l&rsquo;\u00e9x\u00e9cution de plusieurs \u00e9tapes ayant des d\u00e9pendances de fa\u00e7on optimis\u00e9e ?<\/p>\n<p>Dans notre exemple, nous allons simuler 2 \u00e9tapes :<\/p>\n<ol>\n<li>Le clone de 2 repositories Git<\/li>\n<li>Le lancement d&rsquo;une commande <code>composer<\/code><\/li>\n<\/ol>\n<h3 id=\"makefile-1\">Makefile<\/h3>\n<pre tabindex=\"0\"><code>steps: ## les 2 \u00e9tapes, sans parall\u00e9lisation\n\t$(MAKE) step1\n\t$(MAKE) step2\n\nsteps_j: ## les 2 \u00e9tapes, avec parall\u00e9lisation\n\t$(MAKE) step1 -j2\n\t$(MAKE) step2 -j2\n\nstep1: clone_toto clone_tata\n\nclone_toto:\n\t@sleep 2 &amp;&amp; \\\n\techo &quot;git clone toto&quot;\n\nclone_tata:\n\t@sleep 2 &amp;&amp; \\\n\techo &quot;git clone tata&quot;\n\nstep2: composer_toto composer_tata\n\ncomposer_toto:\n\t@sleep 2 &amp;&amp; \\\n\techo &quot;composer toto&quot;\n\ncomposer_tata:\n\t@sleep 2 &amp;&amp; \\\n\techo &quot;composer tata&quot;\n<\/code><\/pre><h3 id=\"tests-sans-parall\u00e9lisation-1\">Tests sans parall\u00e9lisation<\/h3>\n<p>On lance notre test de r\u00e9f\u00e9rence<\/p>\n<pre tabindex=\"0\"><code>$ date +'%Y%m%d%H%M%S' &amp;&amp; make steps &amp;&amp;  date +'%Y%m%d%H%M%S'\n20181213173756\nmake step1\nmake[1] : on entre dans le r\u00e9pertoire \u00ab \/var\/www\/make-parallel \u00bb\ngit clone toto\ngit clone tata\nmake[1] : on quitte le r\u00e9pertoire \u00ab \/var\/www\/make-parallel \u00bb\nmake step2\nmake[1] : on entre dans le r\u00e9pertoire \u00ab \/var\/www\/make-parallel \u00bb\ncomposer toto\ncomposer tata\nmake[1] : on quitte le r\u00e9pertoire \u00ab \/var\/www\/make-parallel \u00bb\n20181213173804\n<\/code><\/pre><p>Ce test met 48 secondes \u00e0 s&rsquo;\u00e9x\u00e9cuter.<\/p>\n<blockquote>\n<p>Make semble particuli\u00e8rement lent dans les appels r\u00e9cursifs \u00e0 lui m\u00eame.<br>\nJe n&rsquo;ai pas creus\u00e9 pourquoi.<\/p>\n<\/blockquote>\n<p>Le m\u00eame test en adaptant <code>steps<\/code> comme suit nous fait revenir au timing initialement pr\u00e9vu (8 secondes)<\/p>\n<pre tabindex=\"0\"><code>steps2: step1 step2\n<\/code><\/pre><h3 id=\"tests-avec-parall\u00e9lisation-1\">Tests avec parall\u00e9lisation<\/h3>\n<p>En appliquant les capacit\u00e9s de paral\u00e9lisation de Make, on peut donc maintenant assurer un ordre d&rsquo;\u00e9x\u00e9cution sur les jobs et lancer en m\u00eame ceux ind\u00e9pendants.<\/p>\n<pre tabindex=\"0\"><code>$ date +'%Y%m%d%H%M%S' &amp;&amp; make steps_j &amp;&amp;  date +'%Y%m%d%H%M%S'\n20181213173803\nmake step1 -j2\nmake[1] : on entre dans le r\u00e9pertoire \u00ab \/var\/www\/make-parallel \u00bb\ngit clone toto\ngit clone tata\nmake[1] : on quitte le r\u00e9pertoire \u00ab \/var\/www\/make-parallel \u00bb\nmake step2 -j2\nmake[1] : on entre dans le r\u00e9pertoire \u00ab \/var\/www\/make-parallel \u00bb\ncomposer tata\ncomposer toto\nmake[1] : on quitte le r\u00e9pertoire \u00ab \/var\/www\/make-parallel \u00bb\n20181213173807\n<\/code><\/pre><p>On obtient une ex\u00e9cution de seulement 4 secondes !<\/p>\n<h2 id=\"notre-makefile-de-tests-complet\">Notre Makefile de tests complet<\/h2>\n<pre tabindex=\"0\"><code>all: test test2 test3 test4 test5 test6 test7\n\ntest:\n\t@sleep 2 &amp;&amp; \\\n\techo &quot;---&gt; 1&quot;\n\ntest2:\n\t@sleep 2 &amp;&amp; \\\n\techo &quot;---&gt; 2&quot;\n\ntest3: test test2 .FORCE\n\t@sleep 2 &amp;&amp; \\\n\techo &quot;---&gt; 3&quot;\n\ntest4:\n\t@sleep 2 &amp;&amp; \\\n\techo &quot;---&gt; 4&quot;\n\ntest5:\n\t@sleep 2 &amp;&amp; \\\n\techo &quot;---&gt; 5&quot;\n\ntest6:\n\t@sleep 2 &amp;&amp; \\\n\techo &quot;---&gt; 6&quot;\n\ntest7:\n\t@sleep 2 &amp;&amp; \\\n\techo &quot;---&gt; 7&quot;\n\n.FORCE:\n\nsteps:\n\t$(MAKE) step1\n\t$(MAKE) step2\n\nsteps_j:\n\t$(MAKE) step1 -j2\n\t$(MAKE) step2 -j2\n\nstep1: clone_toto clone_tata\n\nclone_toto:\n\t@sleep 2 &amp;&amp; \\\n\techo &quot;git clone toto&quot;\n\nclone_tata:\n\t@sleep 2 &amp;&amp; \\\n\techo &quot;git clone tata&quot;\n\nstep2: composer_toto composer_tata\n\ncomposer_toto:\n\t@sleep 2 &amp;&amp; \\\n\techo &quot;composer toto&quot;\n\ncomposer_tata:\n\t@sleep 2 &amp;&amp; \\\n\techo &quot;composer tata&quot;\n<\/code><\/pre>"},{"title":"Kubernetes a gagn\u00e9 la bataille des orchestrateurs","link":"https:\/\/gastaud.io\/article\/kubernetes-a-gagne\/","pubDate":"Thu, 05 Jul 2018 00:00:00 +0000","guid":"https:\/\/gastaud.io\/article\/kubernetes-a-gagne\/","description":"<p>L&rsquo;arriv\u00e9e il y a quelques ann\u00e9es de solutions permettant de simplifier la cr\u00e9ation et gestion de conteneurs, telles que <a href=\"https:\/\/www.docker.com\/\">Docker<\/a> ou <a href=\"https:\/\/coreos.com\/rkt\/\">rkt<\/a>, combin\u00e9es aux approches micro-services et leurs popularit\u00e9s croissantes ont entra\u00een\u00e9 rapidement le besoin de solutions dites d'<code>orchestration de conteneurs<\/code>.<\/p>\n<p>De nombreuses solutions d&rsquo;orchestration ont vu le jour et se sont retrouv\u00e9es disponibles en Open-Source.<br>\n<a href=\"https:\/\/mesosphere.github.io\/marathon\/\">Apache Mesos Marathon<\/a>, <a href=\"https:\/\/docs.docker.com\/swarm\/overview\/\">Docker Swarm<\/a>, <a href=\"https:\/\/kubernetes.io\/\">Kubernetes<\/a> ou encore <a href=\"https:\/\/docs.openstack.org\/magnum\/latest\/\">OpenStack Magnum<\/a> permettent toutes de traiter ces probl\u00e9matiques.<\/p>\n<p>Il y a encore 2 ou 3 ans, les communaut\u00e9s Mesos et Docker avait fait de gros efforts pour rendre Mesos capable d&rsquo;orchestrer des Docker.<\/p>\n<p>Pourtant, cela n&rsquo;emp\u00eacha pas Docker inc. de lancer sa propre solution, Docker Swarm, pour traiter ces aspects. Avec une volont\u00e9 affich\u00e9e d&rsquo;\u00eatre une solution d&rsquo;orchestration plus int\u00e9gr\u00e9e \u00e0 Docker, plus simple \u00e0 mettre en place tout en  assumant une gestion moines &ldquo;fines&rdquo; des ressources que Mesos.<\/p>\n<p>Chacun a sa place, pour son besoin. Pas de vraie comp\u00e9tition entre les 2.<\/p>\n<p>Mesos (via Marathon) semblait avoir pris de l&rsquo;avance sur ces concurrents. En milieu d&rsquo;ann\u00e9e 2017, une bascule a commenc\u00e9 \u00e0 se produire vers Kubernetes, utilis\u00e9 chez Google depuis plus de 10 ans.<\/p>\n<p>Alors pourquoi peut-on penser aujourd&rsquo;hui que Kubernetes a gagn\u00e9 cette bataille et devient, un peu au m\u00eame titre que Docker pour les conteneurs, le standard de l&rsquo;industrie ?<\/p>\n<h2 id=\"des-signes-qui-ne-trompent-pas\">Des signes qui ne trompent pas<\/h2>\n<h3 id=\"une-croissance-tr\u00e8s-rapide\">Une croissance tr\u00e8s rapide<\/h3>\n<ul>\n<li>Kubernetes est actuellement le projet le plus actif sur Github.<\/li>\n<li>Les plateformes comme OpenShift ou CoreOS int\u00e8grent Kubernetes.<\/li>\n<li>Le <a href=\"https:\/\/www.cncf.io\/\">CNCF (Cloud Native Computing Foundation)<\/a> en a fait le 1er projet <code>Graduated<\/code>, son rang le plus haut.<\/li>\n<li>L\u2019\u00e9cosyst\u00e8me autours de Kubernetes s&rsquo;\u00e9toffe tr\u00e8s vite pour plus d&rsquo;int\u00e9grations autours des syst\u00e8mes, du monitoring, la s\u00e9curit\u00e9, le d\u00e9ploiement continue&hellip;<\/li>\n<\/ul>\n\n\n\n\n\n\n\n\n\n\n  <a href=\"https:\/\/landscape.cncf.io\/images\/landscape.png\">\n    <img style=\"max-width: 100%; height: auto;\" src=\"https:\/\/gastaud.io\/article\/kubernetes-a-gagne\/images\/landscape_hu8160a2094572a4ea1ef3eec9a836f79c_4402107_1200x1200_fit_box_3.png\" width=\"1200\" height=\"724\">\n  <\/a>\n\n\n\n<h3 id=\"le-cloud-public-la-adopt\u00e9\">Le Cloud public l&rsquo;a adopt\u00e9<\/h3>\n<p>Toutes les principales plateformes Cloud proposent Kubernetes en tant services :<\/p>\n<ul>\n<li>Google Cloud Platform avec GKE (Google Kubernetes Engine)<\/li>\n<li>Amazon Web Services avec EKS (Amazon Elastic Container Service for Kubernetes)<\/li>\n<li>Azure avec AKS (Azure Kubernetes Service)<\/li>\n<li>Digital Ocean propose une beta priv\u00e9e<\/li>\n<\/ul>\n<h3 id=\"docker-for-mac-int\u00e8gre-nativement-kubernetes\">Docker (for mac) int\u00e8gre nativement Kubernetes<\/h3>\n<p>Docker a annonc\u00e9 l&rsquo;ann\u00e9e derni\u00e8re int\u00e9gr\u00e9e directement Kubernetes dans son application Docker for mac et propose maintenant le choix entre Swarm et Kubernetes.<\/p>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>Faut-il donc oublier vraiment toutes les autres solutions et ne faire plus que Kubernetes ?<\/p>\n<p>Pour des probl\u00e9matiques d&rsquo;orchestration combinant des applications non conteneuris\u00e9es et\/ou des conteneurs, alors une solution comme Mesos pourra vous \u00eatre d&rsquo;une grande aide.<\/p>\n<p>Pour des probl\u00e9matiques d&rsquo;orchestration d\u00e9di\u00e9s uniquement aux conteneurs, <strong>sur une petite volum\u00e9trie de serveurs et conteneurs<\/strong>, une solution comme Docker Swarm sera certainement un bon tremplin, plus abordable.<\/p>\n<p>Enfin, si vous avez besoin d&rsquo;une solution <strong>fiable, robuste, scalable et p\u00e9renne<\/strong>, alors la balance tend clairement vers Kubernetes.<\/p>\n"},{"title":"Drupal Behat afterFeature","link":"https:\/\/gastaud.io\/article\/behat-afterfeature\/","pubDate":"Fri, 02 Sep 2016 17:12:10 +0200","guid":"https:\/\/gastaud.io\/article\/behat-afterfeature\/","description":"<p>Snippet permettant d&rsquo;\u00e9x\u00e9cuter des actions \u00e0 la fin de l&rsquo;\u00e9x\u00e9cution d&rsquo;une feature Behat.<\/p>\n<pre tabindex=\"0\"><code>\/**\n * Defines application features from the specific context.\n *\/\nclass FeatureContext extends RawDrupalContext implements SnippetAcceptingContext {\n\n  \/**\n   * That function will be executed after the end of the feature.\n   *\n   * @AfterFeature\n   *\/\n  public static function cleanupSomethingAfterFeatureExcution() {\n    \/\/ Do something.\n  }\n}\n<\/code><\/pre><h1 id=\"ressources\">Ressources<\/h1>\n<ul>\n<li><a href=\"http:\/\/docs.behat.org\/en\/v2.5\/guides\/3.hooks.html\">http:\/\/docs.behat.org\/en\/v2.5\/guides\/3.hooks.html<\/a><\/li>\n<li><a href=\"https:\/\/www.webomelette.com\/content-fixtures-behat-testing-drupal-7\">https:\/\/www.webomelette.com\/content-fixtures-behat-testing-drupal-7<\/a><\/li>\n<li><a href=\"http:\/\/www.metaltoad.com\/blog\/what-i-learned-today-drupal-behat-scenario-cleanup\">http:\/\/www.metaltoad.com\/blog\/what-i-learned-today-drupal-behat-scenario-cleanup<\/a><\/li>\n<\/ul>\n"},{"title":"Obtenir l'url d'une page g\u00e9r\u00e9e via page_manager","link":"https:\/\/gastaud.io\/article\/panel-page-manager-obtenir-url\/","pubDate":"Tue, 09 Aug 2016 14:48:18 +0200","guid":"https:\/\/gastaud.io\/article\/panel-page-manager-obtenir-url\/","description":"<p>Ci-dessous une technique pour r\u00e9cup\u00e9rer l&rsquo;url d\u00e9finie dans une page g\u00e9r\u00e9e via le module page_manager (inclus dans Ctools).<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#75715e\">\/\/ Load ctools plugins.\n<\/span><span style=\"color:#75715e\"><\/span><span style=\"color:#a6e22e\">ctools_include<\/span>(<span style=\"color:#e6db74\">&#39;page&#39;<\/span>, <span style=\"color:#e6db74\">&#39;page_manager&#39;<\/span>, <span style=\"color:#e6db74\">&#39;plugins\/tasks&#39;<\/span>);\n\n<span style=\"color:#75715e\">\/\/ Load path &#34;my-page&#34; page.\n<\/span><span style=\"color:#75715e\"><\/span>$path <span style=\"color:#f92672\">=<\/span> <span style=\"color:#a6e22e\">page_manager_page_load<\/span>(<span style=\"color:#e6db74\">&#39;my-page&#39;<\/span>)<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">path<\/span>;\n<\/code><\/pre><\/div>"},{"title":"Drupal 8 - Suppression de configurations \u00e0 la d\u00e9sinstallation du module","link":"https:\/\/gastaud.io\/article\/drupal-8-suppression-config-desinstallation\/","pubDate":"Mon, 02 May 2016 15:21:25 +0200","guid":"https:\/\/gastaud.io\/article\/drupal-8-suppression-config-desinstallation\/","description":"<p>Lors de l&rsquo;impl\u00e9mentation d&rsquo;un module, il va vous arriver de cr\u00e9er des configurations lors de l&rsquo;installation.<\/p>\n<p>Il est n\u00e9cessaire de penser \u00e0 les supprimer \u00e0 la d\u00e9sinstallation.<\/p>\n<p>Pour cela, il vous suffit d&rsquo;utiliser le <code>hook_uninstall<\/code> dans le fichier <code>.install<\/code> de votre module, comme cela pouvait \u00eatre fait sur Drupal 7.<\/p>\n<p>Exemple avec une configuration du type <em>migration<\/em> nomm\u00e9e <em>communique<\/em>:<\/p>\n<pre tabindex=\"0\"><code>\/**\n * Implements hook_uninstall().\n *\/\nfunction migrate_communique_uninstall() {\n    \\Drupal::entityTypeManager()-&gt;getStorage('migration')-&gt;load('communique')-&gt;delete();\n}\n<\/code><\/pre>\n<div class=\"ui blue message\">\n  <div class=\"content\">\n    \n      <div class=\"header\">\n        A lire aussi\n      <\/div>\n    \n    \n    <p><ul>\n<li><a href=\"https:\/\/gastaud.io\/article\/drupal-8-migration-xml\/\">R\u00e9aliser un import de fichier XML sous Drupal 8<\/a><\/li>\n<li><a href=\"https:\/\/gastaud.io\/article\/drupal-8-migration-xml-surcharge-source\/\">Import de fichier XML sous Drupal 8 - Traitements sp\u00e9cifiques sur la source<\/a><\/li>\n<\/ul>\n<\/p>\n  <\/div>\n<\/div>\n\n"},{"title":"Import de fichier XML sous Drupal 8 - Traitements sp\u00e9cifiques sur la source","link":"https:\/\/gastaud.io\/article\/drupal-8-migration-xml-surcharge-source\/","pubDate":"Mon, 02 May 2016 10:13:54 +0200","guid":"https:\/\/gastaud.io\/article\/drupal-8-migration-xml-surcharge-source\/","description":"<p>Dans l&rsquo;article pr\u00e9c\u00e9dent, <a href=\"https:\/\/gastaud.io\/article\/drupal-8-migration-xml\/\">R\u00e9aliser un import de fichier XML sous Drupal 8<\/a>, nous avons vu comment importer un fichier XML via Migrate.\nLa configuration \u00e9tait simple et nous avons pu faire l&rsquo;ensemble du mapping sans manipuler les donn\u00e9es du fichier XML.<\/p>\n<p>Nous allons \u00e9tendre notre exemple pr\u00e9c\u00e9dent pour importer une date.<br>\nCependant cette date n&rsquo;a pas un format de date <em>standard<\/em> et n&rsquo;est pas valide pour \u00eatre import\u00e9e dans Drupal directement.<\/p>\n<h2 id=\"mise-\u00e0-jour-du-contexte\">Mise \u00e0 jour du contexte<\/h2>\n<h3 id=\"fichier-xml-\u00e0-importer\">Fichier XML \u00e0 importer<\/h3>\n<pre tabindex=\"0\"><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\n&lt;flux-communiques&gt;\n    &lt;communique&gt;\n        &lt;id-communique&gt;1&lt;\/id-communique&gt;\n        &lt;titre&gt;Titre de l'article 1&lt;\/titre&gt;\n        &lt;date-diffusion&gt;2016-02-05 16:07:00&lt;\/date-diffusion&gt;\n        &lt;corps&gt;&lt;![CDATA[&lt;div style=&quot;color: red&quot;&gt;Un texte bleu&lt;\/div&gt;]]&gt;&lt;\/corps&gt;\n        &lt;pj1&gt;http:\/\/example.com\/monfichier.pdf&lt;\/pj1&gt;\n    &lt;\/communique&gt;\n    &lt;communique&gt;\n        &lt;id-communique&gt;2&lt;\/id-communique&gt;\n        &lt;titre&gt;Titre de l'article 2&lt;\/titre&gt;\n        &lt;date-diffusion&gt;2016-01-14 16:24:00&lt;\/date-diffusion&gt;\n        &lt;corps&gt;&lt;![CDATA[&lt;div style=&quot;color: red&quot;&gt;Un texte rouge&lt;\/div&gt;]]&gt;\n        &lt;\/corps&gt;\n        &lt;pj1&gt;http:\/\/example.com\/monfichier2.pdf&lt;\/pj1&gt;\n    &lt;\/communique&gt;\n&lt;\/flux-communiques&gt;\n<\/code><\/pre><p>La champ <code>date-diffusion<\/code> n&rsquo;est pas valide et nous allons avoir besoin d&rsquo;ajouter un <strong>T<\/strong> entre la date et l&rsquo;heure pour obtenir le format suivant : <code>2016-02-05T16:07:00<\/code><\/p>\n<h3 id=\"type-de-contenus\">Type de contenus<\/h3>\n<p>Nous poss\u00e9dons un type de contenus, nomm\u00e9 <code>Communique<\/code> avec les champs suivants :<\/p>\n<table>\n<thead>\n<tr>\n<th>Label<\/th>\n<th>Nom machine<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Titre<\/td>\n<td>title<\/td>\n<\/tr>\n<tr>\n<td>Corps<\/td>\n<td>body<\/td>\n<\/tr>\n<tr>\n<td>Date de diffusion<\/td>\n<td>field_communique_date<\/td>\n<\/tr>\n<tr>\n<td>Pi\u00e8ce jointe<\/td>\n<td>field_communique_attachment<\/td>\n<\/tr>\n<tr>\n<td>Id du communiqu\u00e9<\/td>\n<td>field_communique_id<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 id=\"modifications-du-module-dimport\">Modifications du module d&rsquo;import<\/h2>\n<h3 id=\"nouveau-mapping-des-champs-source\">Nouveau mapping des champs source<\/h3>\n<p>Dans la partie <code>source<\/code>, nous allons ajouter une entr\u00e9e pour mapper la date de diffusion.<\/p>\n<pre tabindex=\"0\"><code>source:\n  #...\n  fields:\n    #...\n    -\n      name: date_diffusion\n      label: Date Diffusion\n      selector: date-diffusion\n  #...\n<\/code><\/pre><h3 id=\"nouveau-mapping-des-champs-drupal\">Nouveau mapping des champs Drupal<\/h3>\n<pre tabindex=\"0\"><code>process:\n  #...\n  field_press_release_date: date_diffusion\n  #...\n<\/code><\/pre><p>Si vous lancez un <code>drush migrate-fields-source<\/code> vous devriez voir le nouveau champ apparaitre<\/p>\n<pre tabindex=\"0\"><code>$drush mfs communique\nID              id_communique  \nTitre           titre          \nCorps           corps          \nDate Diffusion  date_diffusion\nLien            pj1\n<\/code><\/pre><h2 id=\"modification-de-la-class-source\">Modification de la class Source<\/h2>\n<p>Comme indiqu\u00e9 pr\u00e9c\u00e9demment, il va maintenant nous falloir alt\u00e9rer la valeur de la date de diffusion pour qu&rsquo;elle est un format interpr\u00e9table par Drupal et ainsi que l&rsquo;import de ce champ se r\u00e9alise correctement.<\/p>\n<p>Pour cela nous allons devoir modifier le comportement du plugin de la source (Url.php dans notre) et utiliser, comme sur Drupal 7, la m\u00e9thode prepareRow().<\/p>\n<p>Nous allons donc cr\u00e9er une nouvelle class nomm\u00e9e Communique.php dans <code>.\/src\/Plugin\/migrate\/source\/Communique.php<\/code><br>\nOn obtient l&rsquo;arborescence de module suivante :<\/p>\n<pre tabindex=\"0\"><code>.\n\u251c\u2500\u2500 config\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 install\n\u2502\u00a0\u00a0     \u2514\u2500\u2500 migrate_plus.migration.communique.yml\n\u251c\u2500\u2500 migrate_press_release.info.yml\n\u2514\u2500\u2500 src\n    \u2514\u2500\u2500 Plugin\n        \u251c\u2500\u2500 migrate\n        \u2502\u00a0\u00a0 \u2514\u2500\u2500 source\n        \u2502\u00a0\u00a0     \u2514\u2500\u2500 Communique.php\n<\/code><\/pre><p>Notre nouvelle class <code>Communique.php<\/code> :<\/p>\n<pre tabindex=\"0\"><code>&lt;?php\n\nnamespace Drupal\\migrate_communique\\Plugin\\migrate\\source;\n\nuse Drupal\\migrate\\Row;\nuse Drupal\\migrate_plus\\Plugin\\migrate\\source\\Url;\n\n\/**\n * Source plugin for communique content.\n *\n * @MigrateSource(\n *   id = &quot;communique&quot;\n * )\n *\/\n\nclass Communique extends Url {\n\n    \/**\n     * {@inheritdoc}\n     *\/\n    public function prepareRow(Row $row) {\n        \/\/ Alter XML provided date.\n        $row-&gt;setSourceProperty('date_diffusion', $this-&gt;processDateDiffusion($row-&gt;getSource()['date_diffusion']));\n        return parent::prepareRow($row);\n    }\n\n    \/**\n     * Format Date from XML to be compliant with DateTime format.\n     *\n     * @param $element\n     * @return string\n     *\/\n    protected function processDateDiffusion($element) {\n\n        $exploded = explode(' ', (string) $element);\n        $data = implode('T', $exploded);\n\n        return $data;\n    }\n}\n<\/code><\/pre><p>Nous avons donc ici une class qui \u00e9tant la class <code>Url<\/code> et pr\u00e9pare le format de la date obtenue dans le champ <em>date_diffusion<\/em> pour correspondre \u00e0 ce qu&rsquo;attend Drupal.<br>\nL&rsquo;utilisation de <code>$row-&gt;setSourceProperty()<\/code> permet de (re)d\u00e9finir  la valeur de la date de diffusion.<\/p>\n<p>Il ne nous reste plus qu&rsquo;\u00e0 pr\u00e9ciser \u00e0 notre migration de prendre en compte cette source en remplacement de la source <code>url<\/code> initialement d\u00e9finie.<\/p>\n<p>Pour cela, nous allons modifier notre fichier <code>migrate_plus.migration.communique.yml<\/code> pour lui renseigner l&rsquo;id de notre source : <em>communique<\/em><\/p>\n<pre tabindex=\"0\"><code>source:\n  # L'identifiant de notre source custom.\n  plugin: communique\n  #...\n<\/code><\/pre>\n<div class=\"ui red message\">\n  <div class=\"content\">\n    \n    \n    <p><p>Afin que les changements de configuration soient pris en compte, il vous faudra d\u00e9sinstaller le module, supprimer la config d\u00e9j\u00e0 enregistr\u00e9e et r\u00e9installer le module.\nVoir l&rsquo;article <a href=\"https:\/\/gastaud.io\/article\/drupal-8-suppression-config-desinstallation\/\">Drupal 8 - Suppression de configurations \u00e0 la d\u00e9sinstallation du module<\/a><\/p>\n<\/p>\n  <\/div>\n<\/div>\n\n<p>Une fois les modifications effectu\u00e9es, il ne vous reste plus qu&rsquo;\u00e0 relancer votre migration.<\/p>\n\n<div class=\"ui blue message\">\n  <div class=\"content\">\n    \n      <div class=\"header\">\n        A lire aussi\n      <\/div>\n    \n    \n    <p><ul>\n<li><a href=\"https:\/\/gastaud.io\/article\/drupal-8-migration-xml\/\">R\u00e9aliser un import de fichier XML sous Drupal 8<\/a><\/li>\n<li><a href=\"https:\/\/gastaud.io\/article\/drupal-8-suppression-config-desinstallation\/\">Drupal 8 - Suppression de configurations \u00e0 la d\u00e9sinstallation du module<\/a><\/li>\n<\/ul>\n<\/p>\n  <\/div>\n<\/div>\n\n"},{"title":"R\u00e9aliser un import de fichier XML sous Drupal 8","link":"https:\/\/gastaud.io\/article\/drupal-8-migration-xml\/","pubDate":"Mon, 02 May 2016 10:13:54 +0200","guid":"https:\/\/gastaud.io\/article\/drupal-8-migration-xml\/","description":"<p>Dans cette article nous allons voir comment r\u00e9aliser un import de contenu XML dans Drupal 8 \u00e0 l&rsquo;aide des modules migrate, <a href=\"https:\/\/drupal.org\/project\/migrate_plus\">migrate_plus<\/a> et <a href=\"https:\/\/drupal.org\/project\/migrate_source_xml\">migrate_source_xml<\/a>.<\/p>\n<h2 id=\"r\u00e9cup\u00e9ration-des-modules-et-installation\">R\u00e9cup\u00e9ration des modules et installation<\/h2>\n<ul>\n<li><code>migrate<\/code> est disponible dans le coeur de Drupal 8. Cependant il ne contient \u00e0 ce jour que la possibilit\u00e9 de migrer des contenus en provenance d&rsquo;une base SQL.<\/li>\n<li><code>migrate_plus<\/code> est un module contribu\u00e9 qui permet notamment d&rsquo;obtenir la possibilit\u00e9 de migrer des contenus \u00e0 partir d&rsquo;une URL.<\/li>\n<\/ul>\n<pre tabindex=\"0\"><code>drush dl migrate_plus --dev\n<\/code><\/pre><ul>\n<li><code>migrate_source_xml<\/code> permet d&rsquo;avoir une class de migration g\u00e9rant des fichiers XML.<\/li>\n<\/ul>\n<pre tabindex=\"0\"><code># depuis le dossier .\/modules\/[contrib]\ngit clone --branch refactor https:\/\/git.drupal.org\/project\/migrate_source_xml.git\n<\/code><\/pre><h2 id=\"contexte\">Contexte<\/h2>\n<h2 id=\"fichier-xml-\u00e0-importer\">Fichier XML \u00e0 importer<\/h2>\n<pre tabindex=\"0\"><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;\n&lt;flux-communiques&gt;\n    &lt;communique&gt;\n        &lt;id-communique&gt;1&lt;\/id-communique&gt;\n        &lt;titre&gt;Titre de l'article 1&lt;\/titre&gt;\n        &lt;corps&gt;&lt;![CDATA[&lt;div style=&quot;color: red&quot;&gt;Un texte bleu&lt;\/div&gt;]]&gt;&lt;\/corps&gt;\n        &lt;pj1&gt;http:\/\/example.com\/monfichier.pdf&lt;\/pj1&gt;\n    &lt;\/communique&gt;\n    &lt;communique&gt;\n        &lt;id-communique&gt;2&lt;\/id-communique&gt;\n        &lt;titre&gt;Titre de l'article 2&lt;\/titre&gt;\n        &lt;date-diffusion&gt;2016-01-14 16:24:00&lt;\/date-diffusion&gt;\n        &lt;corps&gt;&lt;![CDATA[&lt;div style=&quot;color: red&quot;&gt;Un texte rouge&lt;\/div&gt;]]&gt;\n        &lt;\/corps&gt;\n        &lt;pj1&gt;http:\/\/example.com\/monfichier2.pdf&lt;\/pj1&gt;\n    &lt;\/communique&gt;\n&lt;\/flux-communiques&gt;\n<\/code><\/pre><h3 id=\"type-de-contenus\">Type de contenus<\/h3>\n<p>Nous poss\u00e9dons un type de contenus, nomm\u00e9 <code>Communique<\/code> avec les champs suivants :<\/p>\n<table>\n<thead>\n<tr>\n<th>Label<\/th>\n<th>Nom machine<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>Titre<\/td>\n<td>title<\/td>\n<\/tr>\n<tr>\n<td>Corps<\/td>\n<td>body<\/td>\n<\/tr>\n<tr>\n<td>Pi\u00e8ce jointe<\/td>\n<td>field_communique_attachment<\/td>\n<\/tr>\n<tr>\n<td>Id du communiqu\u00e9<\/td>\n<td>field_communique_id<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 id=\"cr\u00e9ation-du-module-dimport\">Cr\u00e9ation du module d&rsquo;import<\/h2>\n<h3 id=\"cr\u00e9ation-du-module-migrate_communique\">Cr\u00e9ation du module migrate_communique<\/h3>\n<p>Nous allons nommer notre module <code>migrate_communique<\/code> et commencer par cr\u00e9er le dossier du m\u00eame nom dans le r\u00e9pertoire <code>modules<\/code> \u00e0 la racine de Drupal.<\/p>\n<pre tabindex=\"0\"><code>.\/modules\n\u251c\u2500\u2500 migrate_communique\n\u2514\u2500\u2500 README.txt\n<\/code><\/pre><p>et d\u00e9finir le fichier migrate_communique.info.yml<\/p>\n<pre tabindex=\"0\"><code>type: module\nname: Migrate Communiqu\u00e9\ndescription: 'Migration des communiqu\u00e9s.'\npackage: Migration\ncore: 8.x\ndependencies:\n  - migrate_plus\n  - migrate_source_xml\n<\/code><\/pre><p>On notera ici les d\u00e9pendances aux modules <code>migrate_plus<\/code> et <code>migrate_source_xml<\/code>.<\/p>\n<h3 id=\"d\u00e9finition-du-mapping-entre-le-xml-et-le-type-de-contenus\">D\u00e9finition du mapping entre le XML et le type de contenus<\/h3>\n<p>Nous allons cr\u00e9er un fichier de configuration nomm\u00e9 <code>migrate_plus.migration.communique.yml<\/code> dans le dossier <code>config\/install<\/code> \u00e0 partir de la racine du module.<\/p>\n<pre tabindex=\"0\"><code>.\n\u251c\u2500\u2500 config\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 install\n\u2502\u00a0\u00a0     \u2514\u2500\u2500 migrate_plus.migration.pressrelease.yml\n\u251c\u2500\u2500 migrate_communique.info.yml\n<\/code><\/pre><p>La configuration contenue dans ce fichier sera initialis\u00e9e \u00e0 l&rsquo;installation du module.<\/p>\n<h4 id=\"contenu-du-fichier-migrate_plusmigrationcommuniqueyml\">Contenu du fichier migrate_plus.migration.communique.yml<\/h4>\n<h4 id=\"contenu-int\u00e9gral-du-fichier\">Contenu int\u00e9gral du fichier<\/h4>\n<p>Nous d\u00e9taillerons juste apr\u00e8s le contenu de ce fichier<\/p>\n<pre tabindex=\"0\"><code># Configuration de la migration des communiqu\u00e9s.\nid: communique\nlabel: Communique\nmigration_group: Communique\nmigration_dependencies: {}\n\nsource:\n  plugin: url\n  data_fetcher_plugin: http\n  data_parser_plugin: xml\n  urls: http:\/\/exemple\/monfichier.xml\n  item_selector: \/flux-communiques\/communique\n  fields:\n    -\n      # Nom machine utilis\u00e9 pour le mapping lors du processing.\n      name: id_communique\n      # Label qui sera affich\u00e9 dans l'UI.\n      label: ID\n      # Nom du champ dans le XML.\n      selector: id-communique\n    -\n      name: titre\n      label: Titre\n      selector: titre\n    -\n      name: corps\n      label: Corps\n      selector: corps\n    -\n      name: pj1\n      label: Lien\n      selector: pj1\n\n  ids:\n    id_communique:\n      type: string\n\ndestination:\n  plugin: entity:node\n\nprocess:\n  type:\n    plugin: default_value\n    default_value: communique\n\n  title: titre\n  field_communique_id: id_communique\n  'field_communique_body\/value': corps\n  'field_communique_body\/format':\n      plugin: default_value\n      default_value: full_html\n  field_communique_attachment: pj1\n\n  sticky:\n    plugin: default_value\n    default_value: 0\n\n  uid:\n    plugin: default_value\n    default_value: 1\n<\/code><\/pre><h4 id=\"configuration-g\u00e9n\u00e9rale\">Configuration g\u00e9n\u00e9rale<\/h4>\n<pre tabindex=\"0\"><code>id: communique\nlabel: Communique\nmigration_group: Communique\nmigration_dependencies: {}\n<\/code><\/pre><h4 id=\"mapping-de-la-source\">Mapping de la source<\/h4>\n<pre tabindex=\"0\"><code>source:\n  plugin: url\n  data_fetcher_plugin: http\n  data_parser_plugin: xml\n<\/code><\/pre><ul>\n<li><strong>plugin<\/strong> permet de d\u00e9finir le nom du plugin qui va servir de base pour la parsing de la source.<br>\nIci le plugin <code>url<\/code> est fourni par le module <code>migrate_plus<\/code> dans la class <code>Url.php<\/code> (.\/modules\/contrib\/migrate_plus\/src\/Plugin\/migrate\/source\/Url.php)<\/li>\n<\/ul>\n<p>Dans cette class, on retrouve notamment l&rsquo;annotation suivante qui nous permet d&rsquo;obtenir l&rsquo;id du plugin :<\/p>\n<pre tabindex=\"0\"><code>\/**\n * Source plugin for retrieving data via URLs.\n *\n * @MigrateSource(\n *   id = &quot;url&quot;\n * )\n *\/\n<\/code><\/pre><ul>\n<li><strong>data_fetcher_plugin<\/strong> : plugin impl\u00e9ment\u00e9 dans <code>migrate_plus<\/code> pour permettre de r\u00e9cup\u00e9rer un fichier depuis une URL (.\/modules\/contrib\/migrate_plus\/src\/Plugin\/migrate_plus\/data_fetcher\/Http.php)<\/li>\n<\/ul>\n<pre tabindex=\"0\"><code>\/**\n * Retrieve data over an HTTP connection for migration.\n *\n * @DataFetcher(\n *   id = &quot;http&quot;,\n *   title = @Translation(&quot;HTTP&quot;)\n * )\n *\/\n<\/code><\/pre><ul>\n<li><strong>data_parser_plugin<\/strong> : plugin impl\u00e9ment\u00e9 par <code>migrate_source_xml<\/code> (.\/modules\/contrib\/migrate_source_xml\/src\/Plugin\/migrate_plus\/data_parser\/Xml.php)<\/li>\n<\/ul>\n<pre tabindex=\"0\"><code>\/**\n * Obtain XML data for migration.\n *\n * @DataParser(\n *   id = &quot;xml&quot;,\n *   title = @Translation(&quot;XML&quot;)\n * )\n *\/\n<\/code><\/pre><ul>\n<li><strong>urls<\/strong> : une liste d&rsquo;urls auxquelles r\u00e9cup\u00e9rer le fichier XML.<\/li>\n<\/ul>\n<pre tabindex=\"0\"><code>  urls: http:\/\/exemple\/monfichier.xml\n<\/code><\/pre><ul>\n<li><strong>item_selector<\/strong> : permet de d\u00e9finir l&rsquo;emplacement des items qui seront mapp\u00e9s et pars\u00e9s (au format XPath)<\/li>\n<\/ul>\n<pre tabindex=\"0\"><code>  item_selector: \/flux-communiques\/communique\n<\/code><\/pre><ul>\n<li><strong>fields<\/strong> : permet de d\u00e9finir le mapping entre les champs XML et la class de migration<\/li>\n<\/ul>\n<p>On va ici d\u00e9finir pour chaque champs :<\/p>\n<ul>\n<li>un nom machine - qui sera r\u00e9utiliser plus tard lors du processing du champ<\/li>\n<li>un label (optionnel)<\/li>\n<li>un s\u00e9lecteur - le nom du champ dans le XML<\/li>\n<\/ul>\n<pre tabindex=\"0\"><code>  fields:\n    -\n      # Nom machine utilis\u00e9 pour le mapping lors du processing.\n      name: id_communique\n      # Label qui sera affich\u00e9 dans l'UI.\n      label: ID\n      # Nom du champ dans le XML.\n      selector: id-communique\n    -\n      name: titre\n      label: Titre\n      selector: titre\n    -\n      name: corps\n      label: Corps\n      selector: corps\n    -\n      name: pj1\n      label: Lien\n      selector: pj1\n<\/code><\/pre><ul>\n<li><strong>ids<\/strong> : permet de d\u00e9finir l&rsquo;identifiant unique dans le flux pour g\u00e9rer les nouveaux imports et mises \u00e0 jour<\/li>\n<\/ul>\n<pre tabindex=\"0\"><code>  ids:\n    id_communique:\n      type: string\n<\/code><\/pre><h4 id=\"destination-de-la-migration\">Destination de la migration<\/h4>\n<pre tabindex=\"0\"><code>destination:\n  plugin: entity:node\n<\/code><\/pre><p>On d\u00e9finie ici une migration vers une entitit\u00e9 de type <code>node<\/code>.\nOn peut tout \u00e0 fait envisager \u00e9galement de migrer vers d&rsquo;autres entit\u00e9s comme les utilisateurs ou .<\/p>\n<pre tabindex=\"0\"><code># Exemple pour l'entit\u00e9 User.\ndestination:\n  plugin: entity:user\n<\/code><\/pre><h4 id=\"mapping-avec-les-champs-drupal\">Mapping avec les champs Drupal<\/h4>\n<p>La partie <code>process<\/code> du yaml permet de d\u00e9finir le type de contenus dans lequel importer les donn\u00e9es.<\/p>\n<pre tabindex=\"0\"><code>process:\n  type:\n    plugin: default_value\n    default_value: communique\n<\/code><\/pre><p>Le <code>type<\/code> est ici forc\u00e9 \u00e0 <code>Communique<\/code>. Tous les contenus cr\u00e9\u00e9s auront seront donc des communiqu\u00e9s.<br>\nLe plugin <code>default_value<\/code> permet de d\u00e9finir la valeur par d\u00e9faut que prendra un champ.<br>\nOn r\u00e9utilise ce plugin plus bas pour d\u00e9finir la valeur du champ Sticky et l&rsquo;identifiant de l&rsquo;utilisateur (uid)<\/p>\n<pre tabindex=\"0\"><code>sticky:\n  plugin: default_value\n  default_value: 0\n\nuid:\n  plugin: default_value\n  default_value: 1\n<\/code><\/pre><p>Nous retrouvons ensuite le mapping des champs \u00e0 proprement parler.<\/p>\n<pre tabindex=\"0\"><code>  title: titre\n  field_communique_id: id_communique\n<\/code><\/pre><p>Il s&rsquo;\u00e9crit sous la forme :<\/p>\n<pre tabindex=\"0\"><code>  nom_machine_champ_drupal: nom_machine_source_yaml\n<\/code><\/pre><p>Enfin on retrouvera une notation un peu particuli\u00e8re pour les champs <em>compos\u00e9s<\/em> tels que le champ <code>body<\/code> qui attend une valeur compl\u00e8te, un format et potentiellement un r\u00e9sum\u00e9.<\/p>\n<pre tabindex=\"0\"><code>  'field_communique_body\/value': corps\n  'field_communique_body\/format':\n      plugin: default_value\n      default_value: full_html\n<\/code><\/pre><p>Comme vous l&rsquo;avez s\u00fbrement remarqu\u00e9, ces champs s&rsquo;\u00e9crivent sous la forme <code>'nom_champ\/propri\u00e9t\u00e9'<\/code>.<br>\nIl est important de ne pas omettre les guillemets simples autour du nom du champ.<\/p>\n<h2 id=\"lancer-la-migration-avec-drush\">Lancer la migration avec Drush<\/h2>\n<p>Il est possible, comme sur Drupal 7 de lancer les migrations via Drush.\nPour cela, il va cependant nous falloir installer un module compl\u00e9mentaire : <a href=\"https:\/\/drupal.org\/project\/migrate_tools\">migrate_tools<\/a>.<\/p>\n<p>Un simple <code>drush dl migrate_tools --dev<\/code> et <code>drush en migrate_tools<\/code> devrait suffire.<\/p>\n<p>Ce module permet de faire le pont entre drush et migrate, qui rappelons-le est maintenant inclus directement dans le coeur de Drupal.<\/p>\n<p>Une fois le module install\u00e9, on va avoir aux habituelles commandes drush migrate<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-shell\" data-lang=\"shell\">drush --filter<span style=\"color:#f92672\">=<\/span>migrate_tools\nAll commands in migrate_tools: <span style=\"color:#f92672\">(<\/span>migrate_tools<span style=\"color:#f92672\">)<\/span>\n migrate-fields-sourc  List the fields available <span style=\"color:#66d9ef\">for<\/span> mapping in a source.\n e <span style=\"color:#f92672\">(<\/span>mfs<span style=\"color:#f92672\">)<\/span>                                                                  \n migrate-import <span style=\"color:#f92672\">(<\/span>mi<span style=\"color:#f92672\">)<\/span>   Perform one or more migration processes.           \n migrate-messages      View any messages associated with a migration.     \n <span style=\"color:#f92672\">(<\/span>mmsg<span style=\"color:#f92672\">)<\/span>                                                                   \n migrate-reset-status  Reset a active migration<span style=\"color:#960050;background-color:#1e0010\">&#39;<\/span>s status to idle.         \n <span style=\"color:#f92672\">(<\/span>mrs<span style=\"color:#f92672\">)<\/span>                                                                    \n migrate-rollback      Rollback one or more migrations.                   \n <span style=\"color:#f92672\">(<\/span>mr<span style=\"color:#f92672\">)<\/span>                                                                     \n migrate-status <span style=\"color:#f92672\">(<\/span>ms<span style=\"color:#f92672\">)<\/span>   List all migrations with current status.           \n migrate-stop <span style=\"color:#f92672\">(<\/span>mst<span style=\"color:#f92672\">)<\/span>    Stop an active migration operation.\n<\/code><\/pre><\/div><h3 id=\"v\u00e9rification-du-mapping-des-champs\">V\u00e9rification du mapping des champs<\/h3>\n<pre tabindex=\"0\"><code>drush mfs communique\nID              id_communique  \nTitre           titre          \nCorps           corps          \nLien            pj1\n<\/code><\/pre><p>On retrouve bien ici les champs mapp\u00e9s dans la partie <code>source<\/code>.<\/p>\n<h3 id=\"status-des-imports\">Status des imports<\/h3>\n<pre tabindex=\"0\"><code>drush ms communique\n Group: Communique  Status  Total  Imported  Unprocessed  Last imported       \n communique         Idle    2      0         0            \n<\/code><\/pre><h3 id=\"lancer-la-migration\">Lancer la migration<\/h3>\n<pre tabindex=\"0\"><code>drush mi communique --update\nProcessed 2 items (2 created, 0 updated, 0 failed, 0 ignored) - done with 'communique'          \n\ndrush mi communique --update\nProcessed 2 items (0 created, 2 updated, 0 failed, 0 ignored) - done with 'communique'          \n<\/code><\/pre><p>Voil\u00e0 ! Vos noeuds ont \u00e9t\u00e9 cr\u00e9\u00e9s et sont maintenant visibles dans le back-office Drupal.<\/p>\n\n<div class=\"ui blue message\">\n  <div class=\"content\">\n    \n      <div class=\"header\">\n        A lire aussi\n      <\/div>\n    \n    \n    <p><ul>\n<li><a href=\"https:\/\/gastaud.io\/article\/drupal-8-migration-xml-surcharge-source\/\">Import de fichier XML sous Drupal 8 - Traitements sp\u00e9cifiques sur la source<\/a><\/li>\n<li><a href=\"https:\/\/gastaud.io\/article\/drupal-8-suppression-config-desinstallation\/\">Drupal 8 - Suppression de configurations \u00e0 la d\u00e9sinstallation du module<\/a><\/li>\n<\/ul>\n<\/p>\n  <\/div>\n<\/div>\n\n"},{"title":"Utiliser Tmux et Tmuxinator pour g\u00e9rer vos confs projets","link":"https:\/\/gastaud.io\/article\/tmux-tmuxinator-confs-projets\/","pubDate":"Wed, 27 Apr 2016 19:00:00 +0200","guid":"https:\/\/gastaud.io\/article\/tmux-tmuxinator-confs-projets\/","description":"<h2 id=\"pr\u00e9-requis\">Pr\u00e9-requis<\/h2>\n<p>Avoir install\u00e9 sur vos machines<\/p>\n<ul>\n<li><a href=\"https:\/\/tmux.github.io\/\">Tmux<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/tmuxinator\/tmuxinator\">Tmuxinator<\/a><\/li>\n<\/ul>\n<h2 id=\"votre-nouveau-projet-en-3-\u00e9tapes\">Votre nouveau projet en 3 \u00e9tapes<\/h2>\n<ol>\n<li><code>tmuxinator new [project_name]<\/code> : Cette commande va g\u00e9n\u00e9rer un fichier YAML avec la configuration par d\u00e9faut<\/li>\n<li>Editer le fichier g\u00e9n\u00e9r\u00e9 : <code>~\/.tmuxinator\/[project_name].yml<\/code> (voir exemple ci-dessous)<\/li>\n<li>Lancer <code>mux [project_name]<\/code> dans votre terminal<\/li>\n<\/ol>\n<h2 id=\"exemple-de-configuration-tmuxinator-pour-le-projet-store-sous-drupal\">Exemple de configuration Tmuxinator pour le projet Store sous Drupal<\/h2>\n<!-- raw HTML omitted -->\n<!-- raw HTML omitted -->\n<p>Dans le terminal, lancer <code>mux store<\/code> permettra ainsi de lancer 5 fen\u00eatres tmux + phpstorm.<\/p>\n<h2 id=\"r\u00e9f\u00e9rences\">R\u00e9f\u00e9rences<\/h2>\n<ul>\n<li><a href=\"http:\/\/nils-blum-oeste.net\/getting-started-with-tmux\/\">Tame the command line: How to get started with tmux<\/a><\/li>\n<li><a href=\"http:\/\/www.mediacurrent.com\/blog\/efficient-drupal-development-tmux-and-tmuxinator\">Drupal: Efficient Drupal Development with Tmux and Tmuxinator - See more at: http:\/\/www.mediacurrent.com\/blog\/efficient-drupal-development-tmux-and-tmuxinator#sthash.QBmP4vJZ.dpuf<\/a><\/li>\n<li><a href=\"http:\/\/zdk.github.io\/manage-different-tmux-configurations-with-tmuxinator\/\">Manage different tmux configurations with tmuxinator<\/a><\/li>\n<\/ul>\n"},{"title":"Oubliez cURL et Wget gr\u00e2ce \u00e0 HTTPie","link":"https:\/\/gastaud.io\/article\/httpie\/","pubDate":"Thu, 14 Apr 2016 11:49:35 +0200","guid":"https:\/\/gastaud.io\/article\/httpie\/","description":"<p><a href=\"https:\/\/github.com\/jkbrzt\/httpie\">HTTPie<\/a> est un utilitaire en ligne de commande qui a vocation \u00e0 remplacer simplement l&rsquo;utilisation des commandes cURL et wget tout en ajoutant une petite touche bien agr\u00e9able.<\/p>\n<h2 id=\"installation\">Installation<\/h2>\n<p>Plusieurs modes d&rsquo;installation sont possibles. En effet HTTPie est disponible via les packages officiels (pas forcement la derni\u00e8re version) ou via <code>pip<\/code>.<\/p>\n<p>Vous trouverez toutes les informations n\u00e9cessaires dans le <a href=\"https:\/\/github.com\/jkbrzt\/httpie#installation\">Readme du projet<\/a>.<\/p>\n<h2 id=\"formatage-et-couleur\">Formatage et couleur<\/h2>\n<h3 id=\"formatage\">Formatage<\/h3>\n<p>Si vous avez d\u00e9j\u00e0 lanc\u00e9 des requ\u00eates \u00e0 une API via cURL, vous avez d\u00e9j\u00e0 du voir des r\u00e9sultats comme ci-dessous.<\/p>\n<pre tabindex=\"0\"><code>curl &quot;https:\/\/www.drupal.org\/api-d7\/user.json?name=jygastaud&quot;\n\n{&quot;self&quot;:&quot;https:\\\/\\\/www.drupal.org\\\/api-d7\\\/user?name=jygastaud&quot;,&quot;first&quot;:&quot;https:\\\/\\\/www.drupal.org\\\/api-d7\\\/user?name=jygastaud\\u0026page=0&quot;,&quot;last&quot;:&quot;https:\\\/\\\/www.drupal.org\\\/api-d7\\\/user?name=jygastaud\\u0026page=0&quot;,&quot;list&quot;:[{&quot;field_industries_worked_in&quot;:[],&quot;field_organizations&quot;:[{&quot;uri&quot;:&quot;https:\\\/\\\/www.drupal.org\\\/api-d7\\\/field_collection_item\\\/468341&quot;,&quot;id&quot;:&quot;468341&quot;,&quot;resource&quot;:&quot;field_collection_item&quot;},{&quot;uri&quot;:&quot;https:\\\/\\\/www.drupal.org\\\/api-d7\\\/field_collection_item\\\/724935&quot;,&quot;id&quot;:&quot;724935&quot;,&quot;resource&quot;:&quot;field_collection_item&quot;}],&quot;field_bio&quot;:[],&quot;field_irc_nick&quot;:&quot;jygastaud&quot;,&quot;field_websites&quot;:[{&quot;url&quot;:&quot;http:\\\/\\\/www.clever-age.com&quot;,&quot;attributes&quot;:[]}],&quot;field_country&quot;:&quot;France&quot;,&quot;field_gender&quot;:&quot;male&quot;,&quot;field_languages&quot;:[&quot;English&quot;,&quot;French&quot;],&quot;field_terms_of_service&quot;:true,&quot;field_areas_of_expertise&quot;:[],&quot;field_contributed&quot;:[&quot;patches&quot;,&quot;modules&quot;,&quot;issues&quot;,&quot;applications&quot;,&quot;forums&quot;,&quot;services&quot;,&quot;irc&quot;,&quot;mentor&quot;],&quot;field_events_attended&quot;:[],&quot;field_mentors&quot;:[],&quot;field_drupal_contributions&quot;:[],&quot;field_first_name&quot;:null,&quot;field_last_name&quot;:null,&quot;og_user_node&quot;:[],&quot;og_membership&quot;:[],&quot;og_membership__1&quot;:[],&quot;og_membership__2&quot;:[],&quot;og_membership__3&quot;:[],&quot;og_user_node__og_membership&quot;:[],&quot;og_user_node__og_membership__1&quot;:[],&quot;og_user_node__og_membership__2&quot;:[],&quot;og_user_node__og_membership__3&quot;:[],&quot;uid&quot;:&quot;660334&quot;,&quot;name&quot;:&quot;jygastaud&quot;,&quot;url&quot;:&quot;https:\\\/\\\/www.drupal.org\\\/u\\\/jygastaud&quot;,&quot;edit_url&quot;:&quot;https:\\\/\\\/www.drupal.org\\\/user\\\/660334\\\/edit&quot;,&quot;created&quot;:&quot;1259343731&quot;,&quot;language&quot;:&quot;en&quot;,&quot;flag_sid&quot;:0,&quot;flag_drupalorg_user_spam_user&quot;:[]}]}\n\n<\/code><\/pre><p>Pas tr\u00e8s lisible, vous en conviendrez ?<\/p>\n<p>Avec HTTPie, fini les requ\u00eates CURL \u00e0 une API qui renvoi un gros bloc illisible. Vous pouvez maintenant avoir un retour lisible dans votre terminal.<\/p>\n<pre tabindex=\"0\"><code>http &quot;https:\/\/www.drupal.org\/api-d7\/user.json?name=jygastaud&quot;\n\nHTTP\/1.1 200 OK\nAccept-Ranges: bytes\nAccess-Control-Allow-Credentials: false\nAccess-Control-Allow-Headers: Content-Type\nAccess-Control-Allow-Methods: GET, OPTIONS\nAccess-Control-Allow-Origin: *\nAge: 0\nCache-Control: no-cache\nConnection: keep-alive\nContent-Encoding: gzip\nContent-Length: 546\nContent-Security-Policy: frame-ancestors 'self'\nContent-Type: application\/json\nDate: Thu, 14 Apr 2016 10:16:55 GMT\nEtag: &quot;1460629015-1&quot;\nExpires: Sun, 19 Nov 1978 05:00:00 GMT\nFastly-Debug-Digest: 760fd3aa9c342b8e1bbdc30d5ebbb895ba37763ccd3ce2e0bdab520ac08df17e\nLast-Modified: Thu, 14 Apr 2016 10:16:55 GMT\nServer: Apache\nStrict-Transport-Security: max-age=10886400; includeSubDomains; preload\nVary: Cookie,Accept-Encoding\nVia: 1.1 varnish\nVia: 1.1 varnish\nX-Cache: MISS, MISS\nX-Cache-Hits: 0, 0\nX-Content-Type-Options: nosniff\nX-Drupal-Cache: MISS\nX-Frame-Options: SAMEORIGIN\nX-Served-By: cache-sea1924-SEA, cache-fra1227-FRA\nx-host: www.drupal.org\nx-url: \/api-d7\/user.json?name=jygastaud\n\n{\n    &quot;first&quot;: &quot;https:\/\/www.drupal.org\/api-d7\/user?name=jygastaud&amp;page=0&quot;,\n    &quot;last&quot;: &quot;https:\/\/www.drupal.org\/api-d7\/user?name=jygastaud&amp;page=0&quot;,\n    &quot;list&quot;: [\n        {\n            &quot;created&quot;: &quot;1259343731&quot;,\n            &quot;edit_url&quot;: &quot;https:\/\/www.drupal.org\/user\/660334\/edit&quot;,\n            &quot;field_areas_of_expertise&quot;: [],\n            &quot;field_bio&quot;: [],\n            &quot;field_contributed&quot;: [\n                &quot;patches&quot;,\n                &quot;modules&quot;,\n                &quot;issues&quot;,\n                &quot;applications&quot;,\n                &quot;forums&quot;,\n                &quot;services&quot;,\n                &quot;irc&quot;,\n                &quot;mentor&quot;\n            ],\n            &quot;field_country&quot;: &quot;France&quot;,\n            &quot;field_drupal_contributions&quot;: [],\n            &quot;field_events_attended&quot;: [],\n            &quot;field_first_name&quot;: null,\n            &quot;field_gender&quot;: &quot;male&quot;,\n            &quot;field_industries_worked_in&quot;: [],\n            &quot;field_irc_nick&quot;: &quot;jygastaud&quot;,\n            &quot;field_languages&quot;: [\n                &quot;English&quot;,\n                &quot;French&quot;\n            ],\n            &quot;field_last_name&quot;: null,\n            &quot;field_mentors&quot;: [],\n            &quot;field_organizations&quot;: [\n                {\n                    &quot;id&quot;: &quot;468341&quot;,\n                    &quot;resource&quot;: &quot;field_collection_item&quot;,\n                    &quot;uri&quot;: &quot;https:\/\/www.drupal.org\/api-d7\/field_collection_item\/468341&quot;\n                },\n                {\n                    &quot;id&quot;: &quot;724935&quot;,\n                    &quot;resource&quot;: &quot;field_collection_item&quot;,\n                    &quot;uri&quot;: &quot;https:\/\/www.drupal.org\/api-d7\/field_collection_item\/724935&quot;\n                }\n            ],\n            &quot;field_terms_of_service&quot;: true,\n            &quot;field_websites&quot;: [\n                {\n                    &quot;attributes&quot;: [],\n                    &quot;url&quot;: &quot;http:\/\/www.clever-age.com&quot;\n                }\n            ],\n            &quot;flag_drupalorg_user_spam_user&quot;: [],\n            &quot;flag_sid&quot;: 0,\n            &quot;language&quot;: &quot;en&quot;,\n            &quot;name&quot;: &quot;jygastaud&quot;,\n            &quot;og_membership&quot;: [],\n            &quot;og_membership__1&quot;: [],\n            &quot;og_membership__2&quot;: [],\n            &quot;og_membership__3&quot;: [],\n            &quot;og_user_node&quot;: [],\n            &quot;og_user_node__og_membership&quot;: [],\n            &quot;og_user_node__og_membership__1&quot;: [],\n            &quot;og_user_node__og_membership__2&quot;: [],\n            &quot;og_user_node__og_membership__3&quot;: [],\n            &quot;uid&quot;: &quot;660334&quot;,\n            &quot;url&quot;: &quot;https:\/\/www.drupal.org\/u\/jygastaud&quot;\n        }\n    ],\n    &quot;self&quot;: &quot;https:\/\/www.drupal.org\/api-d7\/user?name=jygastaud&quot;\n}\n\n<\/code><\/pre><h3 id=\"couleur\">couleur<\/h3>\n<p>Regarder l&rsquo;image en exemple sur le d\u00e9p\u00f4t github<\/p>\n<!-- raw HTML omitted -->\n<p>Comme vous pouvez le voir, HTTPie en plus d&rsquo;apporter un formatage de la sortie vous apportera aussi une colorisation.<\/p>\n<h2 id=\"uniformation\">Uniformation<\/h2>\n<p>Donc avant, vous aviez cURL pour requ\u00eater vos URLs et Wget pour t\u00e9l\u00e9charger des fichiers.<\/p>\n<p>Gr\u00e2ce \u00e0 HTTPie, vous pouvez maintenant ne retenir qu&rsquo;une seule syntaxe !<\/p>\n<p>Ainsi<\/p>\n<pre tabindex=\"0\"><code>wget https:\/\/ftp.drupal.org\/files\/projects\/devel-8.x-1.x-dev.tar.gz\n<\/code><\/pre><p>devient<\/p>\n<pre tabindex=\"0\"><code>http --download https:\/\/ftp.drupal.org\/files\/projects\/devel-8.x-1.x-dev.tar.gz\n<\/code><\/pre><p>Le fait d&rsquo;\u00eatre capable d&rsquo;utiliser une m\u00eame commande pour les requ\u00eates et pour le t\u00e9l\u00e9chargement va vite vous simplifier la vie. Une seule syntaxe \u00e0 retenir.<\/p>\n<p>Le fait de n&rsquo;avoir plus qu&rsquo;une commande \u00e0 retenir va nous simplifier la vie et cela est encore plus vrai si on doit utiliser des certificats SSL.<\/p>\n<p>Par exemple, nous avons une API que l&rsquo;on doit requ\u00eater et une route de r\u00e9cup\u00e9ration de fichier zip, le tout accessible uniquement via une cl\u00e9 et un certificat SSL.<\/p>\n<p>On passera donc des commandes suivantes<\/p>\n<pre tabindex=\"0\"><code>curl -i -v --key .\/certs\/mon-certicat.key --cert .\/certs\/mon-certicat.pem &quot;https:\/\/mon-api.com\/infos&quot;\n\nwget --private-key=&quot;.\/certs\/mon-certicat.key&quot; --certificate=&quot;.\/certs\/mon-certicat.pem&quot; &quot;https:\/\/mon-api.com\/infos\/images.zip&quot;\n\n<\/code><\/pre><p>\u00e0<\/p>\n<pre tabindex=\"0\"><code>http --cert=.\/certs\/mon-certicat.pem --cert-key=.\/certs\/mon-certicat.key https:\/\/mon-api.com\/infos\n\nhttp --download --cert=.\/certs\/mon-certicat.pem --cert-key=.\/certs\/mon-certicat.key https:\/\/mon-api.com\/infos\/images.zip\n<\/code><\/pre><p>C&rsquo;est quand m\u00eame plus simple \u00e0 retenir quand on a qu&rsquo;une seule syntaxe non ?<\/p>\n<h2 id=\"pour-aller-plus-loin\">Pour aller plus loin<\/h2>\n<p>Lisez la documentation disponible sur le <a href=\"https:\/\/github.com\/jkbrzt\/httpie\">d\u00e9p\u00f4t Github HTTPie<\/a> elle est vraiment tr\u00e8s compl\u00e8te.<\/p>\n"},{"title":"Tmux - Exemple de fichier de configuration","link":"https:\/\/gastaud.io\/article\/tmux-exemple-config\/","pubDate":"Thu, 07 Apr 2016 13:49:38 +0200","guid":"https:\/\/gastaud.io\/article\/tmux-exemple-config\/","description":"<p>Exemple de fichier .tmux.conf<\/p>\n<pre tabindex=\"0\"><code># Update default binding of `Enter` to also use copy-pipe\nunbind -t vi-copy Enter\nbind-key -t vi-copy Enter copy-pipe &quot;reattach-to-user-namespace pbcopy&quot;\n\n# Mouse scrolling and selection\nset -g mode-mouse on\nset -g default-terminal &quot;screen-256color&quot;\n\n# Allows for faster key repetition\nset -s escape-time 0\n\n# C-b is not acceptable -- Vim uses it; Set ctrl+a as a prefix\nset -g prefix C-a\nunbind-key C-b\nbind-key a send-prefix\n\nbind-key C-a last-window\n\n# Kill windows and panes faster, by rebinding the confirmation key\nbind-key &amp;amp; kill-window\nbind-key x kill-pane\nbind-key q kill-server\n\n# Renumber windows sequentially after closing any of them\nset -g renumber-windows on\n\n# set window and pane index to 1 (0 by default)\nset-option -g base-index 1\nsetw -g pane-base-index 1\n\n# increase scrollback lines\nset -g history-limit 10000\n\n# get notification when something happend on a tmux window\nsetw -g monitor-activity on\n\n# disable auto-rename from tmux\nset-window-option -g allow-rename off\n<\/code><\/pre>"},{"title":"[Vid\u00e9o] La gestion de produit Agile en 2 mots","link":"https:\/\/gastaud.io\/article\/gestion-produit-agile-2-mots\/","pubDate":"Thu, 07 Apr 2016 13:32:21 +0200","guid":"https:\/\/gastaud.io\/article\/gestion-produit-agile-2-mots\/","description":"<p>J&rsquo;ai (re)d\u00e9couvert cette vid\u00e9o il y a quelque temps.<\/p>\n<p>C&rsquo;est une pr\u00e9sentation synth\u00e9tique de 15 minutes permettant de comprendre tr\u00e8s simplement les grands concepts de l&rsquo;agilit\u00e9 d&rsquo;un point de vue m\u00e9tier dans le cadre de d\u00e9veloppement logiciel Agile.<br>\nLa vid\u00e9o a \u00e9t\u00e9 r\u00e9alis\u00e9e par de Henrik Kniberg, traduite par C\u00e9dric Cheval\u00e9rias et doubl\u00e9e par Florent Lothon.<\/p>\n<p>A diffuser sans mod\u00e9ration !<\/p>\n<!-- raw HTML omitted -->\n"},{"title":"HTTP 1.x ou HTTP\/2  - Lequel choisir ?","link":"https:\/\/gastaud.io\/article\/http1x-http2-lequel-choisir\/","pubDate":"Tue, 02 Feb 2016 13:45:17 +0200","guid":"https:\/\/gastaud.io\/article\/http1x-http2-lequel-choisir\/","description":"<p>Le support de HTTP\/2 est maintenant suffisamment stable et impl\u00e9ment\u00e9 dans les serveurs les plus couramment utilis\u00e9s alors qu&rsquo;elles sont les raisons qui pourraient nous pousser \u00e0 choisir une version ou l&rsquo;autre dans nos projets ?<\/p>\n<h2 id=\"http-1x\">HTTP 1.x<\/h2>\n<p>Vous serez obliger de continuer \u00e0 utiliser HTTP 1.x dans les cas suivants :<\/p>\n<ul>\n<li>site en HTTP non s\u00e9curis\u00e9<\/li>\n<\/ul>\n<p>Bien qu&rsquo;HTTP\/2 supporte officiellement le protocole HTTP non s\u00e9curis\u00e9, l&rsquo;ensemble des navigateurs ont pour le moment fait le choix de ne pas l&rsquo;impl\u00e9menter.\nDu coup, si vous n&rsquo;avez pas de certificat pour votre nom de domaine, il vous faudra rester sur HTTP 1.x<\/p>\n<ul>\n<li>Restrictions techniques par rapport au serveur web utilis\u00e9<\/li>\n<\/ul>\n<p>Les cas sont rares maintenant.\nVous pouvez v\u00e9rifier si le votre le supporte sur le <a href=\"https:\/\/github.com\/http2\/http2-spec\/wiki\/Implementations\">Github HTTP2<\/a><\/p>\n<ul>\n<li>mon h\u00e9bergeur ne veut pas le faire<\/li>\n<\/ul>\n<p>Changer d&rsquo;h\u00e9bergeur ! :)<\/p>\n<h2 id=\"http2\">HTTP\/2<\/h2>\n<p>Si vous avez la main sur l&rsquo;architecture serveur de votre application ou \u00eates \u00e0 m\u00eame d&rsquo;\u00eatre prescripteur sur les solutions \u00e0 impl\u00e9menter alors il est pr\u00e9conis\u00e9 d&rsquo;envisager de passer \u00e0 HTTP\/2 d\u00e8s maintenant.<\/p>\n<h2 id=\"les-contraintes\">Les contraintes<\/h2>\n<ul>\n<li>un serveur supportant HTTP\/2<\/li>\n<li>un certificat SSL<\/li>\n<\/ul>\n<p>Et vous alors \u00eates-vous d\u00e9j\u00e0 passer \u00e0 HTTP\/2 ?<\/p>\n"},{"title":"Bash - R\u00e9cup\u00e9rer le chemin absolu d'ex\u00e9cution du script","link":"https:\/\/gastaud.io\/article\/bash-chemin-absolu\/","pubDate":"Thu, 10 Dec 2015 13:50:52 +0200","guid":"https:\/\/gastaud.io\/article\/bash-chemin-absolu\/","description":"<p>Lors de l&rsquo;ex\u00e9cution d&rsquo;un fichier <em>bash<\/em>, il est parfois n\u00e9cessaire de r\u00e9cup\u00e9rer le chemin d&rsquo;ex\u00e9cution du script.<\/p>\n<h2 id=\"tldr\">TL;DR<\/h2>\n<pre tabindex=\"0\"><code>$(dirname $(readlink -f $0))\n<\/code><\/pre>\n<div class=\"ui negative message\">\n  <div class=\"content\">\n    \n    \n    <p><p><!-- raw HTML omitted -->Attention :<!-- raw HTML omitted --> Sur Mac OS X, readlink ne fonctionne pas de la m\u00eame fa\u00e7on que sur Linux.\nIl vous faudra chercher une alternative. 3 pistes :<\/p>\n<!-- raw HTML omitted -->\n<ul>\n<li>la plus universelle <!-- raw HTML omitted -->$( cd &ldquo;$( dirname &ldquo;${0}&rdquo; )&rdquo; &amp;&amp; pwd )<!-- raw HTML omitted --><!-- raw HTML omitted --><\/li>\n<li>utiliser <!-- raw HTML omitted -->realpath -s $0<!-- raw HTML omitted --><!-- raw HTML omitted --><\/li>\n<li>installer <!-- raw HTML omitted -->greadlink<!-- raw HTML omitted --><!-- raw HTML omitted --><\/li>\n<\/ul>\n<\/p>\n  <\/div>\n<\/div>\n\n<h2 id=\"la-commande-dirname\">La commande dirname<\/h2>\n<p>L&rsquo;un des moyens les plus courant est d&rsquo;utiliser la commande<\/p>\n<pre tabindex=\"0\"><code>`dirname &quot;$0&quot;`\n<\/code><\/pre><ul>\n<li>$0 renvoi le nom du script<\/li>\n<\/ul>\n<p>Le soucis ici c&rsquo;est que le retour de <code>dirname<\/code> ne sera pas identique en fonction de son lieu d&rsquo;ex\u00e9cution.<\/p>\n<p>Par exemple, les appels suivants auront tous un retour diff\u00e9rent :<\/p>\n<pre tabindex=\"0\"><code>vagrant@store-dev:~$ sh www\/store\/updates\/update_sample.sh\ndirname: www\/store\/updates\n\nvagrant@store-dev:~\/www\/store\/updates$ sh update_sample.sh\ndirname: .\n\nvagrant@store-dev:~\/www\/store$ sh .\/updates\/update_sample.sh\ndirname: .\/updates\n\nvagrant@store-dev:~\/www\/store\/docroot$ sh ..\/updates\/update_sample.sh\ndirname: ..\/updates\n<\/code><\/pre><h2 id=\"readlink-pour-nous-sauver\">Readlink pour nous sauver<\/h2>\n<p>Pour s&rsquo;assurer que <code>dirname<\/code> nous renvoi toujours un chemin consistant, il est possible de le combiner avec la commande <code>readlink<\/code><\/p>\n<p>La combinaison des 2 nous renverra ainsi un chemin absolu vers le script.\nOn aura donc maintenant un retour identique pour tous nos appels au script.<\/p>\n<pre tabindex=\"0\"><code>vagrant@store-dev:~$ sh www\/store\/updates\/update_sample.sh\ndirname: www\/store\/updates\ndirname\/readlink: \/home\/vagrant\/www\/store\/updates\n\nvagrant@store-dev:~\/www\/store\/updates$ sh update_sample.sh\ndirname: .\ndirname\/readlink: \/home\/vagrant\/www\/store\/updates\n\nvagrant@store-dev:~\/www\/store$ sh .\/updates\/update_sample.sh\ndirname: .\/updates\ndirname\/readlink: \/home\/vagrant\/www\/store\/updates\n\nvagrant@store-dev:~\/www\/store\/docroot$ sh ..\/updates\/update_sample.sh\ndirname: ..\/updates\ndirname\/readlink: \/home\/vagrant\/www\/store\/updates\n<\/code><\/pre><h2 id=\"v\u00e9rifier-ce-qui-marche-chez-vous\">V\u00e9rifier ce qui marche chez vous<\/h2>\n<p>A mettre dans un fichier test.sh<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"color:#75715e\">#!\/usr\/bin\/env bash\n<\/span><span style=\"color:#75715e\"><\/span>\necho <span style=\"color:#e6db74\">&#34;pwd: `pwd`&#34;<\/span>\necho <span style=\"color:#e6db74\">&#34;\\$0: <\/span>$0<span style=\"color:#e6db74\">&#34;<\/span>\necho <span style=\"color:#e6db74\">&#34;basename: `basename <\/span>$0<span style=\"color:#e6db74\">`&#34;<\/span>\necho <span style=\"color:#e6db74\">&#34;dirname: `dirname <\/span>$0<span style=\"color:#e6db74\">`&#34;<\/span>\necho <span style=\"color:#e6db74\">&#34;dirname\/readlink: <\/span><span style=\"color:#66d9ef\">$(<\/span>dirname <span style=\"color:#66d9ef\">$(<\/span>readlink -f $0<span style=\"color:#66d9ef\">))<\/span><span style=\"color:#e6db74\">&#34;<\/span>\necho <span style=\"color:#e6db74\">&#34;realpath: <\/span><span style=\"color:#66d9ef\">$(<\/span>dirname <span style=\"color:#66d9ef\">$(<\/span>realpath -s $0<span style=\"color:#66d9ef\">))<\/span><span style=\"color:#e6db74\">&#34;<\/span>\necho <span style=\"color:#e6db74\">&#34;cd\/pwd: <\/span><span style=\"color:#66d9ef\">$(<\/span> cd <span style=\"color:#e6db74\">&#34;<\/span><span style=\"color:#66d9ef\">$(<\/span> dirname <span style=\"color:#e6db74\">&#34;<\/span><span style=\"color:#e6db74\">${<\/span>0<span style=\"color:#e6db74\">}<\/span><span style=\"color:#e6db74\">&#34;<\/span> <span style=\"color:#66d9ef\">)<\/span><span style=\"color:#e6db74\">&#34;<\/span> &amp;amp;&amp;amp; pwd <span style=\"color:#66d9ef\">)<\/span><span style=\"color:#e6db74\">&#34;<\/span><\/code><\/pre><\/div>\n<h2 id=\"mise-en-application\">Mise en application<\/h2>\n<p>L&rsquo;exemple ci-dessous nous permet de d\u00e9finir, en fonction des param\u00e8tres d&rsquo;appel du script, l&rsquo;alias drush \u00e0 utiliser.\nCependant si le script est appel\u00e9 sans param\u00e8tre, nous souhaitons passer d\u00e9finir <code>--root<\/code> en tant qu&rsquo;alias.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-bash\" data-lang=\"bash\"><span style=\"color:#75715e\">#!\/usr\/bin\/env bash\n<\/span><span style=\"color:#75715e\"><\/span>\n<span style=\"color:#75715e\"># Si pas de param\u00e8tres.<\/span>\n<span style=\"color:#66d9ef\">if<\/span> <span style=\"color:#f92672\">[<\/span> -z <span style=\"color:#e6db74\">&#34;<\/span><span style=\"color:#e6db74\">${<\/span>1<span style=\"color:#e6db74\">}<\/span><span style=\"color:#e6db74\">&#34;<\/span> <span style=\"color:#f92672\">]<\/span>\n<span style=\"color:#66d9ef\">then<\/span>\n    DIR<span style=\"color:#f92672\">=<\/span><span style=\"color:#66d9ef\">$(<\/span>dirname <span style=\"color:#66d9ef\">$(<\/span>readlink -f $0<span style=\"color:#66d9ef\">))<\/span>\n\n    <span style=\"color:#75715e\"># On v\u00e9rifie que docroot existe.<\/span>\n    <span style=\"color:#66d9ef\">if<\/span> <span style=\"color:#f92672\">[<\/span> -d <span style=\"color:#e6db74\">${<\/span>DIR<span style=\"color:#e6db74\">}<\/span><span style=\"color:#e6db74\">&#34;\/..\/docroot&#34;<\/span> <span style=\"color:#f92672\">]<\/span>\n    <span style=\"color:#66d9ef\">then<\/span>\n        drush_alias<span style=\"color:#f92672\">=<\/span><span style=\"color:#e6db74\">&#34;--root=<\/span><span style=\"color:#e6db74\">${<\/span>DIR<span style=\"color:#e6db74\">}<\/span><span style=\"color:#e6db74\">\/..\/docroot&#34;<\/span>\n    <span style=\"color:#66d9ef\">else<\/span>\n        echo <span style=\"color:#e6db74\">&#34;Script was not run.&#34;<\/span>\n        exit <span style=\"color:#ae81ff\">1<\/span>\n    <span style=\"color:#66d9ef\">fi<\/span>\n<span style=\"color:#66d9ef\">else<\/span>\n    drush_alias<span style=\"color:#f92672\">=<\/span><span style=\"color:#e6db74\">&#34;@<\/span><span style=\"color:#e6db74\">${<\/span>1<span style=\"color:#e6db74\">}<\/span><span style=\"color:#e6db74\">&#34;<\/span>\n<span style=\"color:#66d9ef\">fi<\/span>\n\n<span style=\"color:#75715e\"># La suite du script.<\/span><\/code><\/pre><\/div>\n"},{"title":"Git : Supprimer toutes les branches merg\u00e9es sur le d\u00e9pot distant","link":"https:\/\/gastaud.io\/article\/git-supprimer-branches-mergees\/","pubDate":"Thu, 03 Dec 2015 13:51:38 +0200","guid":"https:\/\/gastaud.io\/article\/git-supprimer-branches-mergees\/","description":"<p>Supprimer toutes les branches merg\u00e9es sur votre d\u00e9pot distant en 1 ligne de commande c&rsquo;est possible !<\/p>\n<h2 id=\"tldr\">TL;DR<\/h2>\n<pre tabindex=\"0\"><code>git branch -r --merged master | grep -v master | sed 's\/origin\\\/\/\/' | xargs -n 1 git push --delete origin\n<\/code><\/pre>\n<div class=\"ui negative message\">\n  <div class=\"content\">\n    \n    \n    <p><p><!-- raw HTML omitted -->Attention :<!-- raw HTML omitted --> cette commande permet de v\u00e9ritablement supprimer des branches dans le d\u00e9p\u00f4t distant. Par exemple, si <!-- raw HTML omitted -->develop<!-- raw HTML omitted --> est merg\u00e9 avec <!-- raw HTML omitted -->master<!-- raw HTML omitted -->, cela supprimera <!-- raw HTML omitted -->develop<!-- raw HTML omitted -->. Mais peut-\u00eatre que les d\u00e9veloppeurs n&rsquo;ont pas le droit de pousser du code sur <!-- raw HTML omitted -->master<!-- raw HTML omitted --> directement\u2026 et se retrouveront, du coup, bloqu\u00e9s. Pensez-y\u00a0!<\/p>\n<\/p>\n  <\/div>\n<\/div>\n\n<h3 id=\"exemple-excluant-une-branche-nomm\u00e9e-develop\">Exemple excluant une branche nomm\u00e9e <code>develop<\/code><\/h3>\n<pre tabindex=\"0\"><code>git branch -r --merged master | grep -v master | grep -v develop | sed 's\/origin\\\/\/\/' | xargs -n 1 git push --delete origin\n<\/code><\/pre><h2 id=\"explications\">Explications<\/h2>\n<ul>\n<li><code>git branch -r --merged master<\/code> va r\u00e9cup\u00e9rer la liste des branches distantes merg\u00e9es (par rapport \u00e0 master)<\/li>\n<li><code>grep -v master<\/code> va exclure la branche master des r\u00e9sultats (vous pouvez ajouter d&rsquo;autres branches \u00e0 exclure si n\u00e9cessaire)<\/li>\n<\/ul>\n<p>Ces 2 premi\u00e8res commandes combin\u00e9es vont renvoy\u00e9s un r\u00e9sultat dans ce style :<\/p>\n<pre tabindex=\"0\"><code>  origin\/t-619\n  origin\/t-63\n  origin\/t-675\n  origin\/t-707\n  origin\/t-808\n  origin\/t-809\n  origin\/t-819\n<\/code><\/pre><ul>\n<li><code>sed 's\/origin\\\/\/\/'<\/code> permet de supprimer le <code>origin\/<\/code><\/li>\n<li><code>xargs -n 1 git push --delete origin<\/code> va prendre chaque ligne renvoy\u00e9e et lancer la commande <code>git push --delete origin BRANCHE<\/code><\/li>\n<\/ul>\n"},{"title":"Envoyer des mails HTML via Drupal et Rules","link":"https:\/\/gastaud.io\/article\/drupal-rules-mails-html\/","pubDate":"Tue, 03 Nov 2015 13:54:56 +0200","guid":"https:\/\/gastaud.io\/article\/drupal-rules-mails-html\/","description":"<h2 id=\"modules-de-la-contribution\">Modules de la contribution<\/h2>\n<ul>\n<li><a href=\"https:\/\/www.drupal.org\/project\/htmlmail\">HTML Mail<\/a> pour la gestion pure du mail au format HTML<\/li>\n<li><a href=\"https:\/\/www.drupal.org\/project\/mailsystem\">Mail System<\/a> pour d\u00e9finir quel type de mail on envoi en fonction des actions<\/li>\n<li><a href=\"https:\/\/www.drupal.org\/project\/variable_email\">Variable Email<\/a> vous permettra de stocker le mail dans une variable et l&rsquo;envoyer via Rules.<\/li>\n<\/ul>\n<h2 id=\"configuration-du-module-html-mail\">Configuration du module HTML Mail<\/h2>\n<p>D\u00e9finissez dans le module HTMLMail le th\u00e8me de votre site. Cela permettra de centraliser vos templates au sein de votre th\u00e8me.<\/p>\n<h2 id=\"configuration-du-module-mail-system\">Configuration du module Mail System<\/h2>\n<p>Configur\u00e9 le module pour envoyer des mails passant par HTMLMail dans les cas qui vous int\u00e9ressent, par exemple les mails syst\u00e8me (cr\u00e9ation de compte, demande de renouvellement de mots de passe) en param\u00e9trant la class <code>HTMLMailSystem<\/code><\/p>\n<p>Si vous voulez que les mails envoy\u00e9s via Rules passent \u00e9galement par HTML Mail, il est possible de cr\u00e9er un &ldquo;New settings&rdquo; en s\u00e9lectionnant le module Rules dans la liste.\nUne fois le nouveau param\u00e9trage cr\u00e9\u00e9, il est possible de lui associer la class <code>HTMLMailSystem<\/code><\/p>\n<h2 id=\"cr\u00e9er-une-nouvelle-variable-de-type-mail\">Cr\u00e9er une nouvelle variable de type mail<\/h2>\n<p>Dans le .info du module, ajouter une d\u00e9pendance \u00e0<\/p>\n<pre tabindex=\"0\"><code>dependencies[] = variable\n<\/code><\/pre><p>Cr\u00e9er un fichier my_module.variable.inc<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n<span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\"> * @file\n<\/span><span style=\"color:#e6db74\"> * my_module.variable.inc\n<\/span><span style=\"color:#e6db74\"> *\/<\/span>\n\n<span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\"> * Implements hook_variable_info().\n<\/span><span style=\"color:#e6db74\"> *\/<\/span>\n<span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">my_module_variable_info<\/span>($options) {\n\n  $variable[<span style=\"color:#e6db74\">&#39;mail_do_something_[mail_part]&#39;<\/span>] <span style=\"color:#f92672\">=<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n    <span style=\"color:#e6db74\">&#39;title&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;Send mail when something happend&#39;<\/span>),\n    <span style=\"color:#e6db74\">&#39;type&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;mail_text&#39;<\/span>,\n    <span style=\"color:#e6db74\">&#39;default&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n      <span style=\"color:#e6db74\">&#39;subject&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;My default subject&#39;<\/span>,\n      <span style=\"color:#e6db74\">&#39;body&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;My default body&#39;<\/span>,\n    ),\n    <span style=\"color:#e6db74\">&#39;required&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#66d9ef\">TRUE<\/span>,\n    <span style=\"color:#e6db74\">&#39;group&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;store_mails&#39;<\/span>,\n    <span style=\"color:#e6db74\">&#39;token&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#66d9ef\">TRUE<\/span>,\n  );\n\n  <span style=\"color:#66d9ef\">return<\/span> $variable;\n}\n<\/code><\/pre><\/div><h2 id=\"permettre-la-modification-et-traduction-de-la-variable\">Permettre la modification et traduction de la variable<\/h2>\n<ul>\n<li>Allez sur la page \/admin\/config\/regional\/i18n\/variable et cocher la nouvelle variable cr\u00e9\u00e9e.<\/li>\n<li>Enregistrer<\/li>\n<li>La variable devrait \u00eatre disponible en \u00e9dition pour les diff\u00e9rentes langues de votre site sur la page \/admin\/store\/mails<\/li>\n<li>Modifier les valeurs des diff\u00e9rentes langues pour y inclure le markup HTML du mail<\/li>\n<li>Enregistrer<\/li>\n<\/ul>\n<h2 id=\"packaging-du-mail\">Packaging du mail<\/h2>\n<p>Ajouter \u00e0 votre feature la variable \u00e0 packager\nAjouter \u00e0 votre feature la variable <code>variable_realm_list_language<\/code><\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n$strongarm <span style=\"color:#f92672\">=<\/span> <span style=\"color:#66d9ef\">new<\/span> <span style=\"color:#66d9ef\">stdClass<\/span>();\n$strongarm<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">disabled<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#66d9ef\">FALSE<\/span>; <span style=\"color:#75715e\">\/* Edit this to true to make a default strongarm disabled initially *\/<\/span>\n$strongarm<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">api_version<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#ae81ff\">1<\/span>;\n$strongarm<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">name<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#39;variable_realm_list_language&#39;<\/span>;\n$strongarm<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">value<\/span> <span style=\"color:#f92672\">=<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n  <span style=\"color:#ae81ff\">0<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;mail_do_something_[mail_part]&#39;<\/span>,\n);\n$export[<span style=\"color:#e6db74\">&#39;variable_realm_list_language&#39;<\/span>] <span style=\"color:#f92672\">=<\/span> $strongarm;<\/code><\/pre><\/div>\n<h2 id=\"utiliser-la-variable-dans-rules\">Utiliser la variable dans Rules<\/h2>\n<ul>\n<li>Ajouter une Action : Send mail with Variable<\/li>\n<li>S\u00e9lectionner la variable cr\u00e9\u00e9e<\/li>\n<li>Ajouter <code>node:author:language<\/code> dans la partie Langue, ce qui permettra d&rsquo;envoyer automatiquement la variable localis\u00e9e \u00e0 l&rsquo;utilisateur<\/li>\n<\/ul>\n<p>Lors du packaging de votre rules, une d\u00e9pendance \u00e0 variable_email devrait se cr\u00e9er.<\/p>\n<pre tabindex=\"0\"><code>dependencies[] = variable_email\n<\/code><\/pre><p>Dans l&rsquo;exemple, ci-dessous nous allons envoyer un mail \u00e0 l&rsquo;auteur du contenu \u00e0 sa cr\u00e9ation et on obtient l&rsquo;export suivant :<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n<span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\"> * Implements hook_default_rules_configuration().\n<\/span><span style=\"color:#e6db74\"> *\/<\/span>\n<span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">my_module_default_rules_configuration<\/span>() {\n  $items <span style=\"color:#f92672\">=<\/span> <span style=\"color:#66d9ef\">array<\/span>();\n  $items[<span style=\"color:#e6db74\">&#39;rules_mail_to_author&#39;<\/span>] <span style=\"color:#f92672\">=<\/span> <span style=\"color:#a6e22e\">entity_import<\/span>(<span style=\"color:#e6db74\">&#39;rules_config&#39;<\/span>, <span style=\"color:#e6db74\">&#39;{ &#34;rules_mail_to_author&#34; : {\n<\/span><span style=\"color:#e6db74\">      &#34;LABEL&#34; : &#34;Send mail to author&#34;,\n<\/span><span style=\"color:#e6db74\">      &#34;PLUGIN&#34; : &#34;reaction rule&#34;,\n<\/span><span style=\"color:#e6db74\">      &#34;OWNER&#34; : &#34;rules&#34;,\n<\/span><span style=\"color:#e6db74\">      &#34;REQUIRES&#34; : [ &#34;variable_email&#34;, &#34;rules&#34; ],\n<\/span><span style=\"color:#e6db74\">      &#34;ON&#34; : { &#34;node_insert--page&#34; : { &#34;bundle&#34; : &#34;page&#34; } },\n<\/span><span style=\"color:#e6db74\">      &#34;DO&#34; : [\n<\/span><span style=\"color:#e6db74\">        { &#34;variable_email_mail&#34; : {\n<\/span><span style=\"color:#e6db74\">            &#34;to&#34; : [ &#34;node:author:mail&#34; ],\n<\/span><span style=\"color:#e6db74\">            &#34;variable&#34; : &#34;mail_do_something_[mail_part]&#34;,\n<\/span><span style=\"color:#e6db74\">            &#34;language&#34; : [ &#34;node:author:language&#34; ]\n<\/span><span style=\"color:#e6db74\">          }\n<\/span><span style=\"color:#e6db74\">        },\n<\/span><span style=\"color:#e6db74\">      ]\n<\/span><span style=\"color:#e6db74\">    }\n<\/span><span style=\"color:#e6db74\">  }&#39;<\/span>);\n\n  <span style=\"color:#66d9ef\">return<\/span> $items;\n}<\/code><\/pre><\/div>\n<p>Et vous comment g\u00e9rez-vous les envois de mails HTML dans Drupal ?<\/p>\n"},{"title":"Tests unitaires avec Simpletest dans Drupal 7","link":"https:\/\/gastaud.io\/article\/tests-unitaires-simpletest-drupal-7\/","pubDate":"Thu, 22 Oct 2015 22:18:35 +0200","guid":"https:\/\/gastaud.io\/article\/tests-unitaires-simpletest-drupal-7\/","description":"\n<div class=\"ui positive message\">\n  <div class=\"content\">\n    \n    \n    <p><p>Si vous ne l&rsquo;avez pas encore lu, allez lire l&rsquo;article <a href=\"https:\/\/gastaud.io\/article\/tester-modules-drupal-7\/\">Tester ses modules Drupal 7<\/a>.<\/p>\n<\/p>\n  <\/div>\n<\/div>\n\n<p>Les tests fonctionnels via Simpletest sont g\u00e9r\u00e9s par la class <code>DrupalUnitTestCase<\/code> qu&rsquo;il faut ensuite \u00e9tendre dans son module.<\/p>\n<p>Drupal va ex\u00e9cuter chaque fonction commen\u00e7ant par <code>test<\/code> d\u00e9finie dans la class.<\/p>\n<p>Contrairement aux tests fonctionnels, Drupal ne va pas reconstruire d&rsquo;environnement pour lancer ce type de tests. Il va se contenter de charger les modules sp\u00e9cifi\u00e9s dans l&rsquo;initialisation et lancer les fonctions donner avec les bons param\u00e8tres.<\/p>\n<p>Les fonctions test\u00e9es ne doivent donc pas d\u00e9pendre de donn\u00e9es r\u00e9cup\u00e9r\u00e9es en base. C&rsquo;est le retour de la fonction qui sera ensuite v\u00e9rifi\u00e9.<\/p>\n<h1 id=\"mise-en-place-des-tests\">Mise en place des tests<\/h1>\n<p>Comme pour les <a href=\"https:\/\/gastaud.io\/article\/tests-fonctionnels-simpletest-drupal-7\/\">tests fonctionnels<\/a> il faut \u00e9tendre une class de base de Drupal : <code>DrupalUnitTestCase<\/code><\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n<span style=\"color:#66d9ef\">class<\/span> <span style=\"color:#a6e22e\">MyModuleUnitTestCase<\/span> <span style=\"color:#66d9ef\">extends<\/span> <span style=\"color:#a6e22e\">DrupalUnitTestCase<\/span> {}<\/code><\/pre><\/div>\n<p>On va \u00e9galement d\u00e9clarer la m\u00e9thode getInfo().<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n<span style=\"color:#66d9ef\">public<\/span> <span style=\"color:#66d9ef\">static<\/span> <span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">getInfo<\/span>() {\n  <span style=\"color:#75715e\">\/\/ Note: getInfo() strings should not be translated.\n<\/span><span style=\"color:#75715e\"><\/span>  <span style=\"color:#66d9ef\">return<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n    <span style=\"color:#e6db74\">&#39;name&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;My module unit tests&#39;<\/span>,\n    <span style=\"color:#e6db74\">&#39;description&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;Test that utilities functions used in my_module modules work fine.&#39;<\/span>,\n    <span style=\"color:#e6db74\">&#39;group&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;My module group&#39;<\/span>,\n  );\n\n}<\/code><\/pre><\/div>\n<p>D\u00e9claration les modules dont on va charger le code via la m\u00e9thode <code>setUp()<\/code><\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n<span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\"> * Set up the test environment.\n<\/span><span style=\"color:#e6db74\"> *\n<\/span><span style=\"color:#e6db74\"> * Note that we use drupal_load() instead of passing our module dependency\n<\/span><span style=\"color:#e6db74\"> * to parent::setUp(). That&#39;s because we&#39;re using DrupalUnitTestCase, and\n<\/span><span style=\"color:#e6db74\"> * thus we don&#39;t want to install the module, only load it&#39;s code.\n<\/span><span style=\"color:#e6db74\"> *\n<\/span><span style=\"color:#e6db74\"> * Also, DrupalUnitTestCase can&#39;t actually install modules. This is by\n<\/span><span style=\"color:#e6db74\"> * design.\n<\/span><span style=\"color:#e6db74\"> *\/<\/span>\n<span style=\"color:#66d9ef\">public<\/span> <span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">setUp<\/span>() {\n  <span style=\"color:#a6e22e\">drupal_load<\/span>(<span style=\"color:#e6db74\">&#39;module&#39;<\/span>, <span style=\"color:#e6db74\">&#39;my_module&#39;<\/span>);\n  <span style=\"color:#66d9ef\">parent<\/span><span style=\"color:#f92672\">::<\/span><span style=\"color:#a6e22e\">setUp<\/span>();\n}<\/code><\/pre><\/div>\n<p>et enfin d\u00e9clarer nos fonction de test qui doivent commencer par le mot cl\u00e9 <code>test<\/code>.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n<span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\"> * Test my_module_check_login().\n<\/span><span style=\"color:#e6db74\"> *\n<\/span><span style=\"color:#e6db74\"> * Note that no environment is provided; we&#39;re just testing the correct\n<\/span><span style=\"color:#e6db74\"> * behavior of a function when passed specific arguments.\n<\/span><span style=\"color:#e6db74\"> *\/<\/span>\n<span style=\"color:#66d9ef\">public<\/span> <span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">testMyModuleCheckLoginFunction<\/span>() {\n  <span style=\"color:#75715e\">\/\/ Your tests\n<\/span><span style=\"color:#75715e\"><\/span>}<\/code><\/pre><\/div>\n<h2 id=\"exemple\">Exemple<\/h2>\n<p>On souhaite pouvoir tester le retour de la fonction suivante :<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n<span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\"> * Check the user login match the regex.\n<\/span><span style=\"color:#e6db74\"> *\n<\/span><span style=\"color:#e6db74\"> * @return\n<\/span><span style=\"color:#e6db74\"> *\/<\/span>\n<span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">my_module_check_login<\/span>($login) {\n  <span style=\"color:#75715e\">\/\/ Extract all regex defined.\n<\/span><span style=\"color:#75715e\"><\/span>  $regex_list <span style=\"color:#f92672\">=<\/span> <span style=\"color:#a6e22e\">explode<\/span>(<span style=\"color:#e6db74\">&#39;;&#39;<\/span>, <span style=\"color:#e6db74\">&#39;\/^[0-9]{7}[A-Z]$\/i;\/^(P|A|Z|F|E|S)[A-Z]{3}[0-9]{5}$\/i;\/^.*domain\\.fr$\/i&#39;<\/span>);\n\n  <span style=\"color:#75715e\">\/\/ Check if the login verify at least one regex.\n<\/span><span style=\"color:#75715e\"><\/span>  <span style=\"color:#66d9ef\">foreach<\/span> ($regex_list <span style=\"color:#66d9ef\">as<\/span> $regex) {\n\n    <span style=\"color:#75715e\">\/\/ If there is one result.\n<\/span><span style=\"color:#75715e\"><\/span>    <span style=\"color:#66d9ef\">if<\/span> (<span style=\"color:#a6e22e\">preg_match<\/span>($regex, $login)) {\n      <span style=\"color:#66d9ef\">return<\/span> <span style=\"color:#66d9ef\">TRUE<\/span>;\n    }\n  }\n  <span style=\"color:#66d9ef\">return<\/span> <span style=\"color:#66d9ef\">FALSE<\/span>;\n}<\/code><\/pre><\/div>\n<p>ce qui pourrait donner un test comme ceci :<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n<span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\"> * Test my_module_check_login().\n<\/span><span style=\"color:#e6db74\"> *\n<\/span><span style=\"color:#e6db74\"> * Note that no environment is provided; we&#39;re just testing the correct\n<\/span><span style=\"color:#e6db74\"> * behavior of a function when passed specific arguments.\n<\/span><span style=\"color:#e6db74\"> *\/<\/span>\n<span style=\"color:#66d9ef\">public<\/span> <span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">testMyModuleCheckLoginFunction<\/span>() {\n  $result <span style=\"color:#f92672\">=<\/span> <span style=\"color:#a6e22e\">my_module_check_login<\/span>(<span style=\"color:#66d9ef\">NULL<\/span>);\n  <span style=\"color:#75715e\">\/\/ Note that test assertion messages should never be translated, so\n<\/span><span style=\"color:#75715e\"><\/span>  <span style=\"color:#75715e\">\/\/ this string is not wrapped in t().\n<\/span><span style=\"color:#75715e\"><\/span>  $message <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#39;A NULL value should return FALSE.&#39;<\/span>;\n  $this<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">assertFalse<\/span>($result, $message);\n\n  $result <span style=\"color:#f92672\">=<\/span> <span style=\"color:#a6e22e\">my_module_check_login<\/span>(<span style=\"color:#e6db74\">&#39;&#39;<\/span>);\n  $message <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#39;An empty string should return FALSE.&#39;<\/span>;\n  $this<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">assertFalse<\/span>($result, $message);\n\n  $result <span style=\"color:#f92672\">=<\/span> <span style=\"color:#a6e22e\">my_module_check_login<\/span>(<span style=\"color:#e6db74\">&#39;something@domain.fr&#39;<\/span>);\n  $message <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#39;An string with @domain.fr should return TRUE.&#39;<\/span>;\n  $this<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">assertTrue<\/span>($result, $message);\n\n  $result <span style=\"color:#f92672\">=<\/span> <span style=\"color:#a6e22e\">store_user_check_login<\/span>(<span style=\"color:#e6db74\">&#39;@domain.fr&#39;<\/span>);\n  $message <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#39;An string with @domain.fr should return TRUE.&#39;<\/span>;\n  $this<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">assertTrue<\/span>($result, $message);\n\n  $result <span style=\"color:#f92672\">=<\/span> <span style=\"color:#a6e22e\">my_module_check_login<\/span>(<span style=\"color:#e6db74\">&#39;AZER01234&#39;<\/span>);\n  $message <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#39;A string that looks like a CP code should return TRUE.&#39;<\/span>;\n  $this<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">assertTrue<\/span>($result, $message);\n\n}<\/code><\/pre><\/div>\n\n<div class=\"ui positive message\">\n  <div class=\"content\">\n    \n      <div class=\"header\">\n        Pour en savoir plus\n      <\/div>\n    \n    \n    <p><p>\u00a0<\/p>\n<ul>\n<li><a href=\"https:\/\/gastaud.io\/article\/tester-modules-drupal-7\/\">Tester ses modules Drupal 7<\/a><\/li>\n<li><a href=\"https:\/\/gastaud.io\/article\/tests-fonctionnels-simpletest-drupal-7\/\">Tests fonctionnels avec Drupal 7<\/a><\/li>\n<\/ul>\n<\/p>\n  <\/div>\n<\/div>\n\n"},{"title":"Tests fonctionnels avec Simpletest dans Drupal 7","link":"https:\/\/gastaud.io\/article\/tests-fonctionnels-simpletest-drupal-7\/","pubDate":"Thu, 22 Oct 2015 22:17:56 +0200","guid":"https:\/\/gastaud.io\/article\/tests-fonctionnels-simpletest-drupal-7\/","description":"\n<div class=\"ui positive message\">\n  <div class=\"content\">\n    \n    \n    <p><p>Si vous ne l&rsquo;avez pas encore lu, allez lire l&rsquo;article <a href=\"https:\/\/gastaud.io\/article\/tester-modules-drupal-7\/\">Tester ses modules Drupal 7<\/a>.<\/p>\n<\/p>\n  <\/div>\n<\/div>\n\n<p>Les tests fonctionnels via Simpletest sont g\u00e9r\u00e9s par la class <code>DrupalWebTestCase<\/code> qu&rsquo;il faut ensuite \u00e9tendre dans son module.<\/p>\n<p>Pour chaque fonction commen\u00e7ant par <code>test<\/code> d\u00e9finie dans la class, Simpletest va reconstruire enti\u00e8rement une instance de Drupal, activer les modules d\u00e9sir\u00e9s et ex\u00e9cuter les tests.<\/p>\n<p>L&rsquo;avantage de ce type de tests r\u00e9side dans le fait que Drupal a acc\u00e8s aux informations de la base de donn\u00e9es et est donc en capacit\u00e9 d\u2019ex\u00e9cuter la totalit\u00e9 des fonctions disponibles.<\/p>\n<p>Il est donc possible d&rsquo;utiliser dans ces tests des fonctions comme node_save(), variable_get() &hellip;<\/p>\n<h2 id=\"mise-en-place-des-tests\">Mise en place des tests<\/h2>\n<h3 id=\"d\u00e9clarer-ces-fichier-de-tests\">D\u00e9clarer ces fichier de tests<\/h3>\n<p>Drupal d\u00e9crit l&rsquo;ensemble de ces tests dans des fichiers portant l&rsquo;extension <strong>.test<\/strong>.<br>\nChaque fichier de test doit ensuite \u00eatre d\u00e9clar\u00e9 dans le <strong>.info<\/strong> de votre module via la d\u00e9claration <code>files[]<\/code> qui est notamment utilis\u00e9 par Drupal pour charger ces class.<\/p>\n<p>Ainsi si vous nommez le fichier contenant vos tests <em>mon_module.test<\/em>, il vous faudra ajouter la ligne suivante dans le .info : <code>files[] = mon_module.test<\/code><\/p>\n<h3 id=\"contenu-minimal-dun-fichier-de-tests\">Contenu minimal d&rsquo;un fichier de tests<\/h3>\n<h4 id=\"etendre-la-class-de-base-de-drupal\">Etendre la class de base de Drupal<\/h4>\n<p>Dans le cadre de tests fonctionnels, c&rsquo;est donc la class <code>DrupalWebTestCase<\/code> que l&rsquo;on veut \u00e9tendre.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n<span style=\"color:#66d9ef\">class<\/span> <span style=\"color:#a6e22e\">EditorListTests<\/span> <span style=\"color:#66d9ef\">extends<\/span> <span style=\"color:#a6e22e\">DrupalWebTestCase<\/span> {}<\/code><\/pre><\/div>\n<h3 id=\"d\u00e9claration-des-infos-de-notre-test\">D\u00e9claration des infos de notre test<\/h3>\n<p>C&rsquo;est la fonction static getInfo() qui se charge de recevoir ces informations.\nElle retourne un tableau contenant un nom, une description et un groupe.<\/p>\n<p>Le nom doit \u00eatre unique pour votre class.\nLe groupe peut \u00eatre commun \u00e0 plusieurs modules, ce qui est pratique pour pouvoir lancer par exemple l&rsquo;ensemble des tests de vos modules custom en 1 fois.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n<span style=\"color:#66d9ef\">public<\/span> <span style=\"color:#66d9ef\">static<\/span> <span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">getInfo<\/span>() {\n  <span style=\"color:#75715e\">\/\/ Note: getInfo() strings should not be translated.\n<\/span><span style=\"color:#75715e\"><\/span>  <span style=\"color:#66d9ef\">return<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n    <span style=\"color:#e6db74\">&#39;name&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;Editor List tests&#39;<\/span>,\n    <span style=\"color:#e6db74\">&#39;description&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;Test that editor_list module work fine.&#39;<\/span>,\n    <span style=\"color:#e6db74\">&#39;group&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;Editor List&#39;<\/span>,\n  );\n\n}<\/code><\/pre><\/div>\n<h4 id=\"d\u00e9claration-des-modules-\u00e0-activer\">D\u00e9claration des modules \u00e0 activer<\/h4>\n<p>2nd fonction obligatoire \u00e0 d\u00e9clarer dans vos fichiers de tests, il s&rsquo;agit de <code>setUp()<\/code>.\nCette fonction vous permet de d\u00e9finir, notamment, les modules \u00e0 charger lors de l&rsquo;initialisation des environnements de test.<\/p>\n<p>Dans l&rsquo;exemple ci-dessous, on d\u00e9clare ainsi que les environnements doivent avoir le module <em>editor_list<\/em> de charger.\nA l&rsquo;initialisation Drupal se charge de regarder les d\u00e9pendances des modules \u00e0 activer et les activer automatiquement.\nIl n&rsquo;est donc pas n\u00e9cessaire de tous les d\u00e9clarer explicitement.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n<span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\"> * {@inheritdoc}\n<\/span><span style=\"color:#e6db74\"> *\/<\/span>\n<span style=\"color:#66d9ef\">public<\/span> <span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">setUp<\/span>() {\n  <span style=\"color:#66d9ef\">parent<\/span><span style=\"color:#f92672\">::<\/span><span style=\"color:#a6e22e\">setUp<\/span>(<span style=\"color:#e6db74\">&#39;editor_list&#39;<\/span>);\n}<\/code><\/pre><\/div>\n<p>Il est \u00e9galement possible d&rsquo;utiliser cette m\u00e9thode <em>setUp()<\/em> pour d\u00e9finir des \u00e9l\u00e9ments n\u00e9cessaires \u00e0 l&rsquo;ensemble de vos tests.\nPar exemple, cr\u00e9ation d&rsquo;un compte admin, cr\u00e9ation de contenus de tests &hellip;\nCes \u00e9l\u00e9ments pourront ainsi \u00eatre exploiter directement dans vos tests sans avoir besoin de r\u00e9p\u00e9ter la phase de cr\u00e9ation \u00e0 chaque fois.<\/p>\n<h4 id=\"d\u00e9clarer-une-fonction-de-test\">D\u00e9clarer une fonction de test<\/h4>\n<p>Un fonction de test doit obligatoirement commencer par le mot cl\u00e9 <code>test<\/code>.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n<span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\"> * Test Administration page access and edition.\n<\/span><span style=\"color:#e6db74\"> *\/<\/span>\n<span style=\"color:#66d9ef\">public<\/span> <span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">testAdministrationPage<\/span>() {\n  <span style=\"color:#75715e\">\/\/ Your tests\n<\/span><span style=\"color:#75715e\"><\/span>}<\/code><\/pre><\/div>\n<p>L&rsquo;obligation d&rsquo;avoir le mot cl\u00e9 <em>test<\/em> au d\u00e9but du nom de votre fonction vous permet de cr\u00e9er des fonctions utilitaires dans la m\u00eame class en \u00e9tant certains qu&rsquo;elles ne seront pas ex\u00e9cuter par Simpletest si vous ne les appelez pas explicitement.<\/p>\n<h1 id=\"exemple-complet\">Exemple complet<\/h1>\n<p><a href=\"https:\/\/git.clever-age.net\/clever-age-expertise\/drupal-editor-list\/blob\/master\/editor_list.test\">Module editor_list<\/a><\/p>\n\n<div class=\"ui positive message\">\n  <div class=\"content\">\n    \n      <div class=\"header\">\n        Pour en savoir plus\n      <\/div>\n    \n    \n    <p><p>\u00a0<\/p>\n<ul>\n<li><a href=\"https:\/\/gastaud.io\/article\/tester-modules-drupal-7\/\">Tester ses modules Drupal 7<\/a><\/li>\n<li><a href=\"https:\/\/gastaud.io\/article\/tests-unitaires-simpletest-drupal-7\/\">Tests unitaires avec Drupal 7<\/a><\/li>\n<\/ul>\n<\/p>\n  <\/div>\n<\/div>\n\n"},{"title":"Tester ses modules Drupal","link":"https:\/\/gastaud.io\/article\/tester-modules-drupal-7\/","pubDate":"Thu, 22 Oct 2015 22:17:21 +0200","guid":"https:\/\/gastaud.io\/article\/tester-modules-drupal-7\/","description":"\n<div class=\"ui grey message\">\n  <div class=\"content\">\n    \n    \n    <p><p>Les tests fonctionnels et encore plus unitaires sous Drupal c&rsquo;est la gal\u00e8re.<\/p>\n<\/p>\n  <\/div>\n<\/div>\n\n<p>Voil\u00e0 une phrase qu&rsquo;il est fr\u00e9quent d&rsquo;entendre.<\/p>\n<p>Nous allons donc essayer via une s\u00e9rie d&rsquo;articles de voir quels types de tests s&rsquo;offrent \u00e0 nous, que ce soit via les outils fournis directement par Drupal ou via l&rsquo;utilisation d&rsquo;outils externes.<\/p>\n<p>D\u00e9marrons donc par une pr\u00e9sentation du framework de tests embarqu\u00e9 dans Drupal 7 : <strong>Simpletest<\/strong>.<\/p>\n<h2 id=\"types-de-tests-disponibles\">Types de tests disponibles<\/h2>\n<p>Simpletest met \u00e0 disposition 2 types de tests :<\/p>\n<ul>\n<li>des tests fonctionnels<\/li>\n<li>des tests unitaires<\/li>\n<\/ul>\n<h3 id=\"tests-fonctionnels\">Tests fonctionnels<\/h3>\n<p>Ces tests sont g\u00e9r\u00e9s par la class <code>DrupalWebTestCase<\/code> qu&rsquo;il faut ensuite \u00e9tendre dans son module.<\/p>\n<p>Pour chaque fonction commen\u00e7ant par <code>test<\/code> d\u00e9finie dans la class, Simpletest va reconstruire enti\u00e8rement une instance de Drupal, activer les modules d\u00e9sir\u00e9s et ex\u00e9cuter les tests.<\/p>\n<p>L&rsquo;avantage de ce type de tests r\u00e9side dans le fait que Drupal a acc\u00e8s aux informations de la base de donn\u00e9es et est donc en capacit\u00e9 d\u2019ex\u00e9cuter la totalit\u00e9 des fonctions disponibles.<\/p>\n<p>Il est donc possible d&rsquo;utiliser dans ces tests des fonctions comme node_save(), variable_get() &hellip;<\/p>\n\n<div class=\"ui negative message\">\n  <div class=\"content\">\n    \n    \n    <p><p>Si le nombre de tests lanc\u00e9 est cons\u00e9quent, il est possible que ces tests prennent beaucoup de temps.<\/p>\n<\/p>\n  <\/div>\n<\/div>\n\n\n<div class=\"ui blue message\">\n  <div class=\"content\">\n    \n      <div class=\"header\">\n        A lire aussi\n      <\/div>\n    \n    \n    <p><p>\u00a0<\/p>\n<ul>\n<li><a href=\"https:\/\/gastaud.io\/article\/tests-fonctionnels-simpletest-drupal-7\/\">Tests fonctionnels avec Simpletest sous Drupal 7<\/a><\/li>\n<\/ul>\n<\/p>\n  <\/div>\n<\/div>\n\n<h3 id=\"tests-unitaires\">Tests unitaires<\/h3>\n<p>Ces tests sont g\u00e9r\u00e9s par la class <code>DrupalUnitTestCase<\/code> qu&rsquo;il faut ensuite \u00e9tendre dans son module.<\/p>\n<p>Comme pour les tests fonctionnels, Drupal va ex\u00e9cuter chaque fonction commen\u00e7ant par <code>test<\/code> d\u00e9finie dans la class.<\/p>\n<p>Contrairement aux tests fonctionnels, Drupal ne va pas reconstruire d&rsquo;environnement pour lancer ce type de tests. Il va se contenter de charger les modules sp\u00e9cifi\u00e9s dans l&rsquo;initialisation et lancer les fonctions donner avec les bons param\u00e8tres.<\/p>\n<p>Les fonctions test\u00e9es ne doivent donc pas d\u00e9pendre de donn\u00e9es r\u00e9cup\u00e9r\u00e9es en base. C&rsquo;est le retour de la fonction qui sera ensuite v\u00e9rifi\u00e9.<\/p>\n\n<div class=\"ui blue message\">\n  <div class=\"content\">\n    \n      <div class=\"header\">\n        A lire aussi\n      <\/div>\n    \n    \n    <p><p>\u00a0<\/p>\n<ul>\n<li><a href=\"https:\/\/gastaud.io\/article\/tests-unitaires-simpletest-drupal-7\/\">Tests unitaires dans Drupal 7 avec Simpletest<\/a><\/li>\n<\/ul>\n<\/p>\n  <\/div>\n<\/div>\n\n<h2 id=\"commandes-de-base\">Commandes de base<\/h2>\n<p>Pour pouvoir lancer vos tests sur Drupal, il faut activer le module Simpletest.<br>\n<code>drush en simpletest -y<\/code><\/p>\n<ul>\n<li>Lancer les tests via le coeur de Drupal\n<ul>\n<li>pour un groupe de tests<br>\n<code>php scripts\/run-tests.sh --url http:\/\/site.dev &quot;Editor List&quot;<\/code><\/li>\n<li>pour une class \u00e0 tester<br>\n<code>php scripts\/run-tests.sh --url http:\/\/site.dev --class EditorListTests<\/code><\/li>\n<\/ul>\n<\/li>\n<li>Lancer les tests via Drush\n<ul>\n<li>pour un groupe de tests<br>\n<code>drush test-run --uri=http:\/\/site.dev &quot;Editor List&quot;<\/code><\/li>\n<li>pour une class \u00e0 tester<br>\n<code>drush test-run --uri=http:\/\/site.dev EditorListTests<\/code><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h3 id=\"stocker-les-r\u00e9sultats-des-tests\">Stocker les r\u00e9sultats des tests<\/h3>\n<p>Que ce soit via Drush ou le sh, vous pouvez sp\u00e9cifier l&rsquo;option &ndash;xml=chemin\/dossier\/resultats\/de\/tests qui recevra les r\u00e9capitulatifs des tester effectu\u00e9s.<\/p>\n<p><code>drush test-run --uri=http:\/\/site.dev --xml=$PWD\/tests MonModuleTests<\/code><\/p>\n\n<div class=\"ui positive message\">\n  <div class=\"content\">\n    \n      <div class=\"header\">\n        Pour en savoir plus\n      <\/div>\n    \n    \n    <p><p>\u00a0<\/p>\n<ul>\n<li><a href=\"https:\/\/gastaud.io\/article\/tests-fonctionnels-simpletest-drupal-7\/\">Tests fonctionnels avec Drupal 7<\/a><\/li>\n<li><a href=\"https:\/\/gastaud.io\/article\/tests-unitaires-simpletest-drupal-7\/\">Tests unitaires avec Drupal 7<\/a><\/li>\n<li><a href=\"https:\/\/www.drupal.org\/node\/265828\">Liste des assertions Simpletest disponibles<\/a><\/li>\n<\/ul>\n<\/p>\n  <\/div>\n<\/div>\n\n"},{"title":"R\u00e9initiliser tous les emails et mots de passe utilisateur avec Drush","link":"https:\/\/gastaud.io\/article\/reinitiliser-tous-les-emails-et-mots-de-passe-utilisateur-avec-drush\/","pubDate":"Mon, 05 Oct 2015 13:51:52 +0200","guid":"https:\/\/gastaud.io\/article\/reinitiliser-tous-les-emails-et-mots-de-passe-utilisateur-avec-drush\/","description":"<p>Si vous r\u00e9cup\u00e9rez une base de donn\u00e9es Drupal en local, il peut \u00eatre utile d&rsquo;assigner des valeurs par d\u00e9faut aux comptes utilisateurs.<\/p>\n<p>Cela permet entre autre :<\/p>\n<ul>\n<li>de se connecter simplement \u00e0 l&rsquo;ensemble des comptes<\/li>\n<li>d&rsquo;\u00e9viter que des mails de tests soient envoy\u00e9s aux contacts r\u00e9els<\/li>\n<\/ul>\n<p>Avec Drush, cela est possible en une ligne de commande :<\/p>\n<pre tabindex=\"0\"><code>drush sql-sanitize --sanitize-email=&quot;user+%uid@localhost&quot; --sanitize-password=&quot;password&quot;\n<\/code><\/pre>"},{"title":"Installer la derni\u00e8re version de Git sur Ubuntu","link":"https:\/\/gastaud.io\/article\/ubuntu-git-latest\/","pubDate":"Wed, 30 Sep 2015 10:44:14 +0200","guid":"https:\/\/gastaud.io\/article\/ubuntu-git-latest\/","description":"<p>Si vous utilisez r\u00e9guli\u00e8rement les syst\u00e8mes de paquets fournis avec vos distributions, vous avez surement remarqu\u00e9 que les versions fournies sont g\u00e9n\u00e9ralement anciennes.<\/p>\n<p>Sur Ubuntu, Git ne d\u00e9roge pas \u00e0 la r\u00e8gle.<\/p>\n<p>En effet, Ubuntu 14.04 LTS donne acc\u00e8s \u00e0 Git 1.9 et Ubuntu 15.05, Git 2.1. Au mieux une version sortie en fin 2014.<\/p>\n<p>Faites le test par vous m\u00eame en ouvrant votre terminal et en lan\u00e7ant la commande suivante :<\/p>\n<pre tabindex=\"0\"><code>$&gt; git --version\n<\/code><\/pre><h2 id=\"pourquoi-mettre-\u00e0-jour-\">Pourquoi mettre \u00e0 jour ?<\/h2>\n<p>Dans un 1er temps pour profiter des corrections et optimisations apport\u00e9es r\u00e9guli\u00e8rement au produit (am\u00e9lioration des capacit\u00e9s de merge par exemple qui permet d&rsquo;avoir une meilleure gestion des conflits), sans oublier les \u00e9volutions qui sont ajout\u00e9es r\u00e9guli\u00e8rement.<\/p>\n<p>On peux par exemple not\u00e9 que la version 2.5 de Git apporte la possibilit\u00e9 d&rsquo;avoir plusieurs <code>worktree<\/code> - nos espaces de travail - simultan\u00e9s via la commande <a href=\"https:\/\/git-scm.com\/docs\/git-worktree\"><code>git worktree<\/code><\/a> et depuis le 28 septembre 2015, la version 2.6.0 de Git est sortie.<\/p>\n<p>Alors comment mettre \u00e0 jour simplement notre machine pour avoir acc\u00e8s aux derni\u00e8res versions de Git.<\/p>\n<h2 id=\"comment-on-fait-\">Comment on fait ?<\/h2>\n<p>La solution en 3 commandes (\u00e0 lancer dans votre terminal)<\/p>\n<pre tabindex=\"0\"><code>sudo add-apt-repository ppa:git-core\/ppa\nsudo apt-get update\nsudo apt-get install git\n<\/code><\/pre><p>Et voil\u00e0 ! Vous devriez maintenant avoir acc\u00e8s \u00e0 la derni\u00e8re version de Git.<\/p>\n<p>Pour v\u00e9rifier lancer \u00e0 nouveau, dans votre terminal :<\/p>\n<pre tabindex=\"0\"><code>git --version\n<\/code><\/pre><p>Si la commande vous renvoie <code>git version 2.6.0<\/code> (ou un nombre sup\u00e9rieure), c&rsquo;est que tout c&rsquo;est bien d\u00e9roul\u00e9.<\/p>\n<p>Maintenant, votre version de Git continuera \u00e0 se mettre \u00e0 jour.<\/p>\n<p>Je vous invite donc \u00e0 suivre r\u00e9guli\u00e8rement les <a href=\"https:\/\/github.com\/git\/git\/tree\/master\/Documentation\/RelNotes\">Releases Notes  de Git<\/a>, toujours pleines de bonnes informations \u00e0 d\u00e9couvrir.<\/p>\n"},{"title":"Gestion et surcharge d'un field instance sous Drupal 8","link":"https:\/\/gastaud.io\/article\/entityconnect-d8\/conversion-form-instance\/","pubDate":"Mon, 31 Aug 2015 23:54:38 +0200","guid":"https:\/\/gastaud.io\/article\/entityconnect-d8\/conversion-form-instance\/","description":"<!-- raw HTML omitted -->\n<p>Retour sur le portage des options de configuration de champs pour le module <a href=\"https:\/\/drupal.org\/project\/entityconnect\">Entity Connect<\/a> sous Drupal 8.<\/p>\n<p>Entity Connect vient ajouter de nouvelles fonctionnalit\u00e9s aux champs de type Entity Reference. Le module propose de d\u00e9finir, pour chaque instance de champ, si les fonctionnalit\u00e9s propos\u00e9es doivent \u00eatre activ\u00e9es ou non.<\/p>\n<h2 id=\"surcharge-de-linstance-du-champ-entity-reference\">Surcharge de l&rsquo;instance du champ Entity Reference<\/h2>\n<p>Dans notre cas, nous avons besoin de surcharger la class <code>ConfigurableEntityReferenceItem<\/code>.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n<span style=\"color:#66d9ef\">namespace<\/span> <span style=\"color:#a6e22e\">Drupal\\entityconnect<\/span>;\n\n<span style=\"color:#66d9ef\">use<\/span> <span style=\"color:#a6e22e\">Drupal\\entity_reference\\ConfigurableEntityReferenceItem<\/span>;\n<span style=\"color:#66d9ef\">use<\/span> <span style=\"color:#a6e22e\">Drupal\\Core\\Form\\FormStateInterface<\/span>;\n\n<span style=\"color:#66d9ef\">class<\/span> <span style=\"color:#a6e22e\">ConfigurableEntityconnectItem<\/span> <span style=\"color:#66d9ef\">extends<\/span> <span style=\"color:#a6e22e\">ConfigurableEntityReferenceItem<\/span> {\n  <span style=\"color:#75715e\">\/\/ Do something.\n<\/span><span style=\"color:#75715e\"><\/span>}<\/code><\/pre><\/div>\n<h2 id=\"cr\u00e9ation-du-formulaire\">Cr\u00e9ation du formulaire<\/h2>\n<h3 id=\"ajout-des-param\u00e8tres-au-field-instance\">Ajout des param\u00e8tres au field instance.<\/h3>\n<h4 id=\"drupal-7\">Drupal 7<\/h4>\n<p>Sur Drupal 7, nous impl\u00e9mentions le <code>hook_field_instance_settings_form<\/code> que nous invoquions ensuite via un <code>hook_form_alter<\/code>.<br>\nC&rsquo;est l&rsquo;utilisation du hook_form_alter qui permettait de passer de nouvelles valeurs de configuration au champ.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n<span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\"> * Add settings to an instance field settings form.\n<\/span><span style=\"color:#e6db74\"> *\n<\/span><span style=\"color:#e6db74\"> * Invoked from field_ui_field_edit_form() to allow the module defining the\n<\/span><span style=\"color:#e6db74\"> * field to add settings for a field instance.\n<\/span><span style=\"color:#e6db74\"> *\n<\/span><span style=\"color:#e6db74\"> * @return array\n<\/span><span style=\"color:#e6db74\"> *   The form definition for the field instance settings.\n<\/span><span style=\"color:#e6db74\"> *\/<\/span>\n<span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">entityconnect_field_instance_settings_form<\/span>($field, $instance) {\n  $settings <span style=\"color:#f92672\">=<\/span> $instance;\n\n  <span style=\"color:#75715e\">\/\/ Add choice for user to not load entity connect &#34;add&#34; button\n<\/span><span style=\"color:#75715e\"><\/span>  <span style=\"color:#75715e\">\/\/ on the field.\n<\/span><span style=\"color:#75715e\"><\/span>  $form[<span style=\"color:#e6db74\">&#39;entityconnect_unload_add&#39;<\/span>] <span style=\"color:#f92672\">=<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n    <span style=\"color:#e6db74\">&#39;#type&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;radios&#39;<\/span>,\n    <span style=\"color:#e6db74\">&#39;#title&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;Display Entity Connect &#34;add&#34; button.&#39;<\/span>),\n    <span style=\"color:#e6db74\">&#39;#default_value&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#f92672\">!<\/span><span style=\"color:#a6e22e\">isset<\/span>($settings[<span style=\"color:#e6db74\">&#39;entityconnect_unload_add&#39;<\/span>]) <span style=\"color:#f92672\">?<\/span> <span style=\"color:#a6e22e\">variable_get<\/span>(<span style=\"color:#e6db74\">&#39;entityconnect_unload_add_default&#39;<\/span>, <span style=\"color:#ae81ff\">1<\/span>) <span style=\"color:#f92672\">:<\/span> $settings[<span style=\"color:#e6db74\">&#39;entityconnect_unload_add&#39;<\/span>],\n    <span style=\"color:#e6db74\">&#39;#description&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;Choose &#34;No&#34; if you want to unload &#34;add&#34; button for the field&#39;<\/span>),\n    <span style=\"color:#e6db74\">&#39;#options&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n      <span style=\"color:#e6db74\">&#39;0&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;Yes&#39;<\/span>),\n      <span style=\"color:#e6db74\">&#39;1&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;No&#39;<\/span>),\n    ),\n    <span style=\"color:#e6db74\">&#39;#weight&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#ae81ff\">1<\/span>,\n  );\n\n  <span style=\"color:#75715e\">\/\/ ...\n<\/span><span style=\"color:#75715e\"><\/span>}<\/code><\/pre><\/div>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n<span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\"> * Implements hook_FORM_ID_form_alter().\n<\/span><span style=\"color:#e6db74\"> *\n<\/span><span style=\"color:#e6db74\"> * @param $form\n<\/span><span style=\"color:#e6db74\"> * @param $form_state\n<\/span><span style=\"color:#e6db74\"> * @param $form_id\n<\/span><span style=\"color:#e6db74\"> *\/<\/span>\n<span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">entityconnect_form_field_ui_field_edit_form_alter<\/span>(<span style=\"color:#f92672\">&amp;<\/span>$form, <span style=\"color:#f92672\">&amp;<\/span>$form_state, $form_id) {\n\n  $field_types <span style=\"color:#f92672\">=<\/span> <span style=\"color:#a6e22e\">_entityconnect_get_references_field_type_list<\/span>();\n\n  <span style=\"color:#75715e\">\/\/ Use to add choice field.\n<\/span><span style=\"color:#75715e\"><\/span>  <span style=\"color:#66d9ef\">if<\/span> (<span style=\"color:#a6e22e\">in_array<\/span>($form[<span style=\"color:#e6db74\">&#39;#field&#39;<\/span>][<span style=\"color:#e6db74\">&#39;type&#39;<\/span>], $field_types)) {\n    $instance <span style=\"color:#f92672\">=<\/span> $form[<span style=\"color:#e6db74\">&#39;#instance&#39;<\/span>];\n    $field <span style=\"color:#f92672\">=<\/span> $form[<span style=\"color:#e6db74\">&#39;#field&#39;<\/span>];\n    $additions <span style=\"color:#f92672\">=<\/span> <span style=\"color:#a6e22e\">module_invoke<\/span>(<span style=\"color:#e6db74\">&#39;entityconnect&#39;<\/span>, <span style=\"color:#e6db74\">&#39;field_instance_settings_form&#39;<\/span>, $field, $instance);\n    <span style=\"color:#66d9ef\">if<\/span> (<span style=\"color:#a6e22e\">is_array<\/span>($additions) <span style=\"color:#f92672\">&amp;&amp;<\/span> <span style=\"color:#a6e22e\">isset<\/span>($form[<span style=\"color:#e6db74\">&#39;instance&#39;<\/span>])) {\n      $form[<span style=\"color:#e6db74\">&#39;instance&#39;<\/span>] <span style=\"color:#f92672\">+=<\/span> $additions;\n    }\n  }\n}<\/code><\/pre><\/div>\n<h3 id=\"drupal-8\">Drupal 8<\/h3>\n<p>Sur Drupal 8, la d\u00e9claration du formulaire va passer par la fonction <code>fieldSettingsForm()<\/code>.<\/p>\n<ul>\n<li>La r\u00e9cup\u00e9ration de la configuration va se faire via <code>$this-&gt;getSettings()<\/code>. Cette fonction renvoi un tableau.<\/li>\n<li>On va charger le formulaire parent (la configuration de notre champ Entity Reference) et ensuite ajouter nos champs. Sans cela, seuls nos nouveaux champs vont apparaitre.<\/li>\n<\/ul>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n  <span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\">   * {@inheritdoc}\n<\/span><span style=\"color:#e6db74\">   *\/<\/span>\n  <span style=\"color:#66d9ef\">public<\/span> <span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">fieldSettingsForm<\/span>(<span style=\"color:#66d9ef\">array<\/span> $form, <span style=\"color:#a6e22e\">FormStateInterface<\/span> $form_state) {\n    $settings <span style=\"color:#f92672\">=<\/span> $this<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">getSettings<\/span>();\n\n    $form <span style=\"color:#f92672\">=<\/span> <span style=\"color:#66d9ef\">parent<\/span><span style=\"color:#f92672\">::<\/span><span style=\"color:#a6e22e\">fieldSettingsForm<\/span>($form, $form_state);\n\n    <span style=\"color:#75715e\">\/\/ ...\n<\/span><span style=\"color:#75715e\"><\/span>\n    $form[<span style=\"color:#e6db74\">&#39;entityconnect&#39;<\/span>][<span style=\"color:#e6db74\">&#39;buttons&#39;<\/span>][<span style=\"color:#e6db74\">&#39;button_add&#39;<\/span>] <span style=\"color:#f92672\">=<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n      <span style=\"color:#e6db74\">&#39;#required&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;1&#39;<\/span>,\n      <span style=\"color:#e6db74\">&#39;#default_value&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> $settings[<span style=\"color:#e6db74\">&#39;entityconnect&#39;<\/span>][<span style=\"color:#e6db74\">&#39;buttons&#39;<\/span>][<span style=\"color:#e6db74\">&#39;button_add&#39;<\/span>],\n      <span style=\"color:#e6db74\">&#39;#description&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> $this<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;Default: &#34;off&#34;&lt;br \/&gt;\n<\/span><span style=\"color:#e6db74\">                            Choose &#34;on&#34; if you want the &#34;add&#34; buttons displayed by default.&lt;br \/&gt;\n<\/span><span style=\"color:#e6db74\">                            Each field can override this value.&#39;<\/span>),\n      <span style=\"color:#e6db74\">&#39;#weight&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;0&#39;<\/span>,\n      <span style=\"color:#e6db74\">&#39;#type&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;radios&#39;<\/span>,\n      <span style=\"color:#e6db74\">&#39;#options&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n        <span style=\"color:#e6db74\">&#39;0&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> $this<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;on&#39;<\/span>),\n        <span style=\"color:#e6db74\">&#39;1&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> $this<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;off&#39;<\/span>),\n      ),\n      <span style=\"color:#e6db74\">&#39;#title&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> $this<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;Default Entity Connect &#34;add&#34; button display&#39;<\/span>),\n    );\n\n\n    <span style=\"color:#75715e\">\/\/ ...\n<\/span><span style=\"color:#75715e\"><\/span>  }<\/code><\/pre><\/div>\n\n<div class=\"ui positive message\">\n  <div class=\"content\">\n    \n    \n    <p><p>Comme dans le formulaire d&rsquo;administration, nous n&rsquo;avons pas besoin de tester l&rsquo;existance ou non d&rsquo;une configuration.<br>\nOn se contente de r\u00e9cup\u00e9rer la valeur configur\u00e9e.<\/p>\n<\/p>\n  <\/div>\n<\/div>\n\n<h2 id=\"d\u00e9finir-les-valeurs-par-d\u00e9faut-de-notre-instance-de-champ\">D\u00e9finir les valeurs par d\u00e9faut de notre instance de champ<\/h2>\n<h3 id=\"utiliser-des-valeurs-d\u00e9finies-en-dures\">Utiliser des valeurs d\u00e9finies en dures<\/h3>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n  <span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\">   * {@inheritdoc}\n<\/span><span style=\"color:#e6db74\">   *\/<\/span>\n  <span style=\"color:#66d9ef\">public<\/span> <span style=\"color:#66d9ef\">static<\/span> <span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">defaultFieldSettings<\/span>() {\n    <span style=\"color:#66d9ef\">return<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n      <span style=\"color:#e6db74\">&#39;entityconnect&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n        <span style=\"color:#e6db74\">&#39;buttons&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n          <span style=\"color:#e6db74\">&#39;button_add&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#ae81ff\">1<\/span>,\n          <span style=\"color:#e6db74\">&#39;button_edit&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#ae81ff\">1<\/span>,\n        ),\n        <span style=\"color:#e6db74\">&#39;icons&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n          <span style=\"color:#e6db74\">&#39;icon_add&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#ae81ff\">0<\/span>,\n          <span style=\"color:#e6db74\">&#39;icon_edit&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#ae81ff\">0<\/span>,\n        ),\n      ),\n    ) <span style=\"color:#f92672\">+<\/span> <span style=\"color:#66d9ef\">parent<\/span><span style=\"color:#f92672\">::<\/span><span style=\"color:#a6e22e\">defaultFieldSettings<\/span>();\n  }<\/code><\/pre><\/div>\n\n<div class=\"ui red message\">\n  <div class=\"content\">\n    \n    \n    <p><p>Attention \u00e0 ne pas oublier l&rsquo;appel \u00e0 <code>+ parent::defaultFieldSettings();<\/code> sous peine de ne pas avoir l&rsquo;ensemble des settings disponibles.<\/p>\n<\/p>\n  <\/div>\n<\/div>\n\n<h3 id=\"utiliser-les-valeurs-par-d\u00e9faut-disponible-dans-la-configuration\">Utiliser les valeurs par d\u00e9faut disponible dans la configuration<\/h3>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n  <span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\">   * {@inheritdoc}\n<\/span><span style=\"color:#e6db74\">   *\/<\/span>\n  <span style=\"color:#66d9ef\">public<\/span> <span style=\"color:#66d9ef\">static<\/span> <span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">defaultFieldSettings<\/span>() {\n\n    $config <span style=\"color:#f92672\">=<\/span> <span style=\"color:#a6e22e\">\\Drupal<\/span><span style=\"color:#f92672\">::<\/span><span style=\"color:#a6e22e\">config<\/span>(<span style=\"color:#e6db74\">&#39;entityconnect.administration_config&#39;<\/span>);\n    $data <span style=\"color:#f92672\">=<\/span> $config<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">getRawData<\/span>();\n\n    <span style=\"color:#66d9ef\">return<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n      <span style=\"color:#e6db74\">&#39;entityconnect&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> $data\n    ) <span style=\"color:#f92672\">+<\/span> <span style=\"color:#66d9ef\">parent<\/span><span style=\"color:#f92672\">::<\/span><span style=\"color:#a6e22e\">defaultFieldSettings<\/span>();\n  }<\/code><\/pre><\/div>\n<ul>\n<li><code>\\Drupal::config('entityconnect.administration_config')<\/code> nous permet de charger les informations de configurations disponibles<\/li>\n<li><code>$config-&gt;getRawData()<\/code> nous permet de r\u00e9cup\u00e9rer un tableau des valeurs (ci-dessous) par d\u00e9faut (celles d\u00e9finies \u00e0 l&rsquo;installation ou configur\u00e9es via le Back-Office).<\/li>\n<\/ul>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-perl\" data-lang=\"perl\"><span style=\"color:#75715e\"># Le retour de l&#39;appel \u00e0 $config-&gt;getRawData();<\/span>\n<span style=\"color:#75715e\"># Dans cet exemple, on voit que les valeurs par d\u00e9faut<\/span>\n<span style=\"color:#75715e\">#   ont \u00e9t\u00e9 chang\u00e9es dans l&#39;interface d&#39;administration.<\/span>\narray (size<span style=\"color:#f92672\">=<\/span><span style=\"color:#ae81ff\">2<\/span>)\n  <span style=\"color:#e6db74\">&#39;buttons&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span>\n    array (size<span style=\"color:#f92672\">=<\/span><span style=\"color:#ae81ff\">2<\/span>)\n      <span style=\"color:#e6db74\">&#39;button_add&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> string <span style=\"color:#e6db74\">&#39;1&#39;<\/span> (length<span style=\"color:#f92672\">=<\/span><span style=\"color:#ae81ff\">1<\/span>)\n      <span style=\"color:#e6db74\">&#39;button_edit&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> string <span style=\"color:#e6db74\">&#39;0&#39;<\/span> (length<span style=\"color:#f92672\">=<\/span><span style=\"color:#ae81ff\">1<\/span>)\n  <span style=\"color:#e6db74\">&#39;icons&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span>\n    array (size<span style=\"color:#f92672\">=<\/span><span style=\"color:#ae81ff\">2<\/span>)\n      <span style=\"color:#e6db74\">&#39;icon_add&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> string <span style=\"color:#e6db74\">&#39;2&#39;<\/span> (length<span style=\"color:#f92672\">=<\/span><span style=\"color:#ae81ff\">1<\/span>)\n      <span style=\"color:#e6db74\">&#39;icon_edit&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> string <span style=\"color:#e6db74\">&#39;1&#39;<\/span> (length<span style=\"color:#f92672\">=<\/span><span style=\"color:#ae81ff\">1<\/span>)<\/code><\/pre><\/div>\n<p>Il ne nous reste plus qu&rsquo;\u00e0 remplacer nos valeurs en dures par nos valeurs dynamiques.<\/p>\n<h2 id=\"prise-en-compte-de-notre-nouvelle-class\">Prise en compte de notre nouvelle class<\/h2>\n<p>Le module Entity Reference d\u00e9finie sa class dans son fichier .module et utilise le <code>hook_field_info_alter<\/code><\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"> 1<\/span><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n<span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"> 2<\/span>\n<span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"> 3<\/span><span style=\"color:#e6db74\">\/**\n<\/span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"> 4<\/span><span style=\"color:#e6db74\"> * Implements hook_field_info_alter().\n<\/span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"> 5<\/span><span style=\"color:#e6db74\"> *\/<\/span>\n<span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"> 6<\/span><span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">entity_reference_field_info_alter<\/span>(<span style=\"color:#f92672\">&amp;<\/span>$info) {\n<span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"> 7<\/span>  <span style=\"color:#75715e\">\/\/ Make the entity reference field configurable.\n<\/span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"> 8<\/span><span style=\"color:#75715e\"><\/span>  $info[<span style=\"color:#e6db74\">&#39;entity_reference&#39;<\/span>][<span style=\"color:#e6db74\">&#39;no_ui&#39;<\/span>] <span style=\"color:#f92672\">=<\/span> <span style=\"color:#66d9ef\">FALSE<\/span>;\n<span style=\"display:block;width:100%;background-color:#3c3d38\"><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\"> 9<\/span>  $info[<span style=\"color:#e6db74\">&#39;entity_reference&#39;<\/span>][<span style=\"color:#e6db74\">&#39;class&#39;<\/span>] <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#39;\\Drupal\\entity_reference\\ConfigurableEntityReferenceItem&#39;<\/span>;\n<\/span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\">10<\/span>  $info[<span style=\"color:#e6db74\">&#39;entity_reference&#39;<\/span>][<span style=\"color:#e6db74\">&#39;list_class&#39;<\/span>] <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#39;\\Drupal\\Core\\Field\\EntityReferenceFieldItemList&#39;<\/span>;\n<span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\">11<\/span>  $info[<span style=\"color:#e6db74\">&#39;entity_reference&#39;<\/span>][<span style=\"color:#e6db74\">&#39;default_widget&#39;<\/span>] <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#39;entity_reference_autocomplete&#39;<\/span>;\n<span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\">12<\/span>  $info[<span style=\"color:#e6db74\">&#39;entity_reference&#39;<\/span>][<span style=\"color:#e6db74\">&#39;default_formatter&#39;<\/span>] <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#39;entity_reference_label&#39;<\/span>;\n<span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\">13<\/span>  $info[<span style=\"color:#e6db74\">&#39;entity_reference&#39;<\/span>][<span style=\"color:#e6db74\">&#39;provider&#39;<\/span>] <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#39;entity_reference&#39;<\/span>;\n<span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\">14<\/span>}<\/code><\/pre><\/div>\n<p>Afin que notre class soit prise en compte, nous allons nous aussi utiliser ce hook et remplacer la class ConfigurableEntityReferenceItem par la notre.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\">1<\/span><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n<span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\">2<\/span>\n<span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\">3<\/span><span style=\"color:#e6db74\">\/**\n<\/span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\">4<\/span><span style=\"color:#e6db74\"> * Implements hook_field_info_alter().\n<\/span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\">5<\/span><span style=\"color:#e6db74\"> *\/<\/span>\n<span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\">6<\/span><span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">entityconnect_field_info_alter<\/span>(<span style=\"color:#f92672\">&amp;<\/span>$info) {\n<span style=\"display:block;width:100%;background-color:#3c3d38\"><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\">7<\/span>  $info[<span style=\"color:#e6db74\">&#39;entity_reference&#39;<\/span>][<span style=\"color:#e6db74\">&#39;class&#39;<\/span>] <span style=\"color:#f92672\">=<\/span> <span style=\"color:#e6db74\">&#39;\\Drupal\\entityconnect\\ConfigurableEntityconnectItem&#39;<\/span>;\n<\/span><span style=\"margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f\">8<\/span>}<\/code><\/pre><\/div>\n"},{"title":"Cr\u00e9ation d'un formulaire d'administration sous Drupal 8","link":"https:\/\/gastaud.io\/article\/entityconnect-d8\/formulaire-administration\/","pubDate":"Thu, 27 Aug 2015 15:47:06 +0200","guid":"https:\/\/gastaud.io\/article\/entityconnect-d8\/formulaire-administration\/","description":"<p>Retour sur le portage de la partie d&rsquo;administration du module <a href=\"https:\/\/drupal.org\/project\/entityconnect\">Entity Connect<\/a> sous Drupal 8.<\/p>\n<h2 id=\"conversion-du-fichier-info\">Conversion du fichier .info<\/h2>\n<ul>\n<li>Le nom du fichier change l\u00e9g\u00e8rement. Il passe ainsi de <code>*.info<\/code> \u00e0 <code>*.info.yml<\/code><\/li>\n<li>Comme vous pouvez vous en douter en lisant le nom du nouveau fichier, le format utilis\u00e9 dans ce fichier est le <code>YAML<\/code>.<\/li>\n<\/ul>\n<h3 id=\"drupal-7\">Drupal 7<\/h3>\n<p><code>.info<\/code><\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"color:#ae81ff\">name = &#34;Entity Connect&#34;<\/span>\n<span style=\"color:#ae81ff\">description = &#34;Allows for referenced entity to be created and edited from the entity reference field&#34;<\/span>\n<span style=\"color:#ae81ff\">core = &#34;7.x&#34;<\/span>\n<span style=\"color:#ae81ff\">package = Entity Connect<\/span>\n<span style=\"color:#ae81ff\">configure = admin\/config\/content\/entityconnect<\/span><\/code><\/pre><\/div>\n<h3 id=\"drupal-8\">Drupal 8<\/h3>\n<p><code>.info.yml<\/code><\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"color:#f92672\">name<\/span>: <span style=\"color:#ae81ff\">Entity Connect<\/span>\n<span style=\"color:#f92672\">type<\/span>: <span style=\"color:#ae81ff\">module<\/span>\n<span style=\"color:#f92672\">description<\/span>: <span style=\"color:#ae81ff\">Allows for referenced entity to be created and edited from the entity reference field<\/span>\n<span style=\"color:#f92672\">core<\/span>: <span style=\"color:#ae81ff\">8.<\/span><span style=\"color:#ae81ff\">x<\/span>\n<span style=\"color:#f92672\">package<\/span>: <span style=\"color:#ae81ff\">Entity Connect<\/span>\n<span style=\"color:#f92672\">configure<\/span>: <span style=\"color:#ae81ff\">entityconnect.administration_form<\/span>\n<span style=\"color:#f92672\">dependencies<\/span>:\n  - <span style=\"color:#ae81ff\">entity_reference<\/span><\/code><\/pre><\/div>\n<ul>\n<li>La notation YAML fait que les <code>=<\/code> deviennet des <code>:<\/code><\/li>\n<li>Les guillemets sont toujours optionnels<\/li>\n<li>Il faut maintenant d\u00e9finir un type : <code>type: module<\/code><\/li>\n<li>Le lien <code>configure<\/code> permettant d&rsquo;afficher un lien direct vers l&rsquo;interface d&rsquo;administration ne contient plus directement un lien mais une r\u00e9f\u00e9rence \u00e0 une route d\u00e9finie dans le fichier <code>entityconnect.routing.yml<\/code> (voir ci-dessous).<\/li>\n<li>Le module <code>entityreference<\/code> (sous D7) fait maintenant partie int\u00e9grante du coeur et s&rsquo;appelle <code>entity_reference<\/code>.<\/li>\n<\/ul>\n<h2 id=\"d\u00e9finition-des-routes\">D\u00e9finition des routes<\/h2>\n<h3 id=\"drupal-7-1\">Drupal 7<\/h3>\n<p>la d\u00e9finition d&rsquo;un nouveau chemin d&rsquo;acc\u00e8s (route) passait par l&rsquo;utilisation du hook_menu.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n<span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\"> * Implements hook_menu().\n<\/span><span style=\"color:#e6db74\"> *\/<\/span>\n<span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">entityconnect_menu<\/span>() {\n  $items <span style=\"color:#f92672\">=<\/span> <span style=\"color:#66d9ef\">array<\/span>();\n\n  $items[<span style=\"color:#e6db74\">&#39;admin\/config\/content\/entityconnect&#39;<\/span>] <span style=\"color:#f92672\">=<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n    <span style=\"color:#e6db74\">&#39;title&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;Entity Connect&#39;<\/span>,\n    <span style=\"color:#e6db74\">&#39;description&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;Configure default values for Entity Reference fields using Entity Connect&#39;<\/span>,\n    <span style=\"color:#e6db74\">&#39;page callback&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;drupal_get_form&#39;<\/span>,\n    <span style=\"color:#e6db74\">&#39;page arguments&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#66d9ef\">array<\/span>(<span style=\"color:#e6db74\">&#39;_entityconnect_admin_form&#39;<\/span>),\n    <span style=\"color:#e6db74\">&#39;access arguments&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#66d9ef\">array<\/span>(<span style=\"color:#e6db74\">&#39;administer site configuration&#39;<\/span>),\n    <span style=\"color:#e6db74\">&#39;file&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;includes\/entityconnect.admin.inc&#39;<\/span>,\n  );\n\n  <span style=\"color:#66d9ef\">return<\/span> $items;\n}<\/code><\/pre><\/div>\n<h3 id=\"drupal-8-1\">Drupal 8<\/h3>\n<p>nous d\u00e9finissons maintenant ces \u00e9l\u00e9ments dans un fichier en <code>*.routing.yml<\/code><\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"color:#f92672\">entityconnect.administration_form<\/span>:\n  <span style=\"color:#f92672\">path<\/span>: <span style=\"color:#e6db74\">&#39;\/admin\/config\/content\/entityconnect&#39;<\/span>\n  <span style=\"color:#f92672\">defaults<\/span>:\n    <span style=\"color:#f92672\">_form<\/span>: <span style=\"color:#e6db74\">&#39;\\Drupal\\entityconnect\\Form\\AdministrationForm&#39;<\/span>\n    <span style=\"color:#f92672\">_title<\/span>: <span style=\"color:#e6db74\">&#39;Entity Connect Administration&#39;<\/span>\n  <span style=\"color:#f92672\">requirements<\/span>:\n    <span style=\"color:#f92672\">_permission<\/span>: <span style=\"color:#e6db74\">&#39;access administration pages&#39;<\/span><\/code><\/pre><\/div>\n<h2 id=\"conversion-du-formulaire\">Conversion du formulaire<\/h2>\n<h3 id=\"drupal-7-2\">Drupal 7<\/h3>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n<span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\"> * Defines the settings form.\n<\/span><span style=\"color:#e6db74\"> *\/<\/span>\n<span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">_entityconnect_admin_form<\/span>($form, <span style=\"color:#f92672\">&amp;<\/span>$form_state) {\n  $form <span style=\"color:#f92672\">=<\/span> <span style=\"color:#66d9ef\">array<\/span>();\n  $form[<span style=\"color:#e6db74\">&#39;entityconnect&#39;<\/span>] <span style=\"color:#f92672\">=<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n    <span style=\"color:#e6db74\">&#39;#type&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;fieldset&#39;<\/span>,\n    <span style=\"color:#e6db74\">&#39;#title&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;EntityConnect default Parameters&#39;<\/span>),\n  );\n  $form[<span style=\"color:#e6db74\">&#39;entityconnect&#39;<\/span>][<span style=\"color:#e6db74\">&#39;button&#39;<\/span>] <span style=\"color:#f92672\">=<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n    <span style=\"color:#e6db74\">&#39;#type&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;fieldset&#39;<\/span>,\n    <span style=\"color:#e6db74\">&#39;#title&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;Buttons display Parameters&#39;<\/span>),\n  );\n  $form[<span style=\"color:#e6db74\">&#39;entityconnect&#39;<\/span>][<span style=\"color:#e6db74\">&#39;button&#39;<\/span>][<span style=\"color:#e6db74\">&#39;button_add&#39;<\/span>] <span style=\"color:#f92672\">=<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n    <span style=\"color:#e6db74\">&#39;#required&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;1&#39;<\/span>,\n    <span style=\"color:#e6db74\">&#39;#key_type_toggled&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;1&#39;<\/span>,\n    <span style=\"color:#e6db74\">&#39;#default_value&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">variable_get<\/span>(<span style=\"color:#e6db74\">&#39;entityconnect_unload_add_default&#39;<\/span>, <span style=\"color:#ae81ff\">1<\/span>),\n    <span style=\"color:#e6db74\">&#39;#description&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;Default: &#34;off&#34;&lt;br \/&gt;\n<\/span><span style=\"color:#e6db74\">                          Choose &#34;on&#34; if you want the &#34;add&#34; buttons displayed by default.&lt;br \/&gt;\n<\/span><span style=\"color:#e6db74\">                          Each field can override this value.&#39;<\/span>),\n    <span style=\"color:#e6db74\">&#39;#weight&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;0&#39;<\/span>,\n    <span style=\"color:#e6db74\">&#39;#type&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;radios&#39;<\/span>,\n    <span style=\"color:#e6db74\">&#39;#options&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n      <span style=\"color:#e6db74\">&#39;0&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;on&#39;<\/span>),\n      <span style=\"color:#e6db74\">&#39;1&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;off&#39;<\/span>),\n    ),\n    <span style=\"color:#e6db74\">&#39;#title&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;Default Entity Connect &#34;add&#34; button display&#39;<\/span>),\n  );\n  $form[<span style=\"color:#e6db74\">&#39;entityconnect&#39;<\/span>][<span style=\"color:#e6db74\">&#39;button&#39;<\/span>][<span style=\"color:#e6db74\">&#39;button_edit&#39;<\/span>] <span style=\"color:#f92672\">=<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n    <span style=\"color:#e6db74\">&#39;#required&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;1&#39;<\/span>,\n    <span style=\"color:#e6db74\">&#39;#key_type_toggled&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;1&#39;<\/span>,\n    <span style=\"color:#e6db74\">&#39;#default_value&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">variable_get<\/span>(<span style=\"color:#e6db74\">&#39;entityconnect_unload_edit_default&#39;<\/span>, <span style=\"color:#ae81ff\">1<\/span>),\n    <span style=\"color:#e6db74\">&#39;#description&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;Default: &#34;off&#34;&lt;br \/&gt;\n<\/span><span style=\"color:#e6db74\">                          Choose &#34;on&#34; if you want the &#34;edit&#34; buttons displayed by default.&lt;br \/&gt;\n<\/span><span style=\"color:#e6db74\">                          Each field can override this value.&#39;<\/span>),\n    <span style=\"color:#e6db74\">&#39;#weight&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;1&#39;<\/span>,\n    <span style=\"color:#e6db74\">&#39;#type&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;radios&#39;<\/span>,\n    <span style=\"color:#e6db74\">&#39;#options&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n      <span style=\"color:#e6db74\">&#39;0&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;on&#39;<\/span>),\n      <span style=\"color:#e6db74\">&#39;1&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;off&#39;<\/span>),\n    ),\n    <span style=\"color:#e6db74\">&#39;#title&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;Default Entity Connect &#34;edit&#34; button display&#39;<\/span>),\n  );\n <span style=\"color:#f92672\">...<\/span>\n\n  $form[<span style=\"color:#e6db74\">&#39;submit&#39;<\/span>] <span style=\"color:#f92672\">=<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n    <span style=\"color:#e6db74\">&#39;#type&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;submit&#39;<\/span>,\n    <span style=\"color:#e6db74\">&#39;#value&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;Save&#39;<\/span>,\n    <span style=\"color:#e6db74\">&#39;#weight&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;2&#39;<\/span>,\n  );\n\n  <span style=\"color:#66d9ef\">return<\/span> $form;\n}<\/code><\/pre><\/div>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n<span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\"> * The settings form submit.\n<\/span><span style=\"color:#e6db74\"> *\/<\/span>\n<span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">_entityconnect_admin_form_submit<\/span>($form, <span style=\"color:#f92672\">&amp;<\/span>$form_state) {\n    <span style=\"color:#a6e22e\">variable_set<\/span>(<span style=\"color:#e6db74\">&#39;entityconnect_unload_add_default&#39;<\/span>, $form_state[<span style=\"color:#e6db74\">&#39;values&#39;<\/span>][<span style=\"color:#e6db74\">&#39;button_add&#39;<\/span>]);\n    <span style=\"color:#a6e22e\">variable_set<\/span>(<span style=\"color:#e6db74\">&#39;entityconnect_unload_edit_default&#39;<\/span>, $form_state[<span style=\"color:#e6db74\">&#39;values&#39;<\/span>][<span style=\"color:#e6db74\">&#39;button_edit&#39;<\/span>]);\n    <span style=\"color:#a6e22e\">drupal_set_message<\/span>(<span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;The settings were saved.&#39;<\/span>));\n}<\/code><\/pre><\/div>\n<h3 id=\"drupal-8-2\">Drupal 8<\/h3>\n<p>Nous allons devoir d\u00e9finir une class qui va \u00e9tendre la class <code>ConfigFormBase<\/code> de Drupal.<br>\nPour assurer l&rsquo;autoload des classes, Drupal suit les conventions PSR-4.<br>\nNous allons donc cr\u00e9er notre nouvelle class au sein de l&rsquo;arborescence suivante :<\/p>\n<pre tabindex=\"0\"><code>.\n\u251c\u2500\u2500 src\n\u2502   \u2514\u2500\u2500 Form\n\u2502       \u2514\u2500\u2500 AdministrationForm.php\n<\/code><\/pre><p>On d\u00e9fini une namespace \u00e0 notre class qui sera de la forme <code>Drupal\\nom_du_module\\Form\\MyForm<\/code><\/p>\n\n<div class=\"ui positive message\">\n  <div class=\"content\">\n    \n    \n    <p>Les modules avec un nom compos\u00e9 utilisent le caract\u00e8re underscore ( _ ) comme s\u00e9parateur.<\/p>\n  <\/div>\n<\/div>\n\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n<span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\"> * @file\n<\/span><span style=\"color:#e6db74\"> * Contains Drupal\\entityconnect\\Form\\AdministrationForm.\n<\/span><span style=\"color:#e6db74\"> *\/<\/span>\n\n<span style=\"color:#66d9ef\">namespace<\/span> <span style=\"color:#a6e22e\">Drupal\\entityconnect\\Form<\/span>;<\/code><\/pre><\/div>\n<p>On va d\u00e9finir les classes utilis\u00e9es dans notre Formulaire.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n<span style=\"color:#66d9ef\">use<\/span> <span style=\"color:#a6e22e\">Drupal\\Core\\Form\\ConfigFormBase<\/span>;\n<span style=\"color:#66d9ef\">use<\/span> <span style=\"color:#a6e22e\">Drupal\\Core\\Form\\FormStateInterface<\/span>;<\/code><\/pre><\/div>\n<p>On instancie notre class qui \u00e9tend <code>ConfigFormBase<\/code>.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n<span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\"> * Class DefaultForm.\n<\/span><span style=\"color:#e6db74\"> *\n<\/span><span style=\"color:#e6db74\"> * @package Drupal\\entityconnect\\Form\n<\/span><span style=\"color:#e6db74\"> *\/<\/span>\n<span style=\"color:#66d9ef\">class<\/span> <span style=\"color:#a6e22e\">AdministrationForm<\/span> <span style=\"color:#66d9ef\">extends<\/span> <span style=\"color:#a6e22e\">ConfigFormBase<\/span> {<\/code><\/pre><\/div>\n\n<div class=\"ui positive message\">\n  <div class=\"content\">\n    \n    \n    <p>ConfigFormBase nous permet ne pas avoir \u00e0 red\u00e9finir l&rsquo;action de sauvegarde, l&rsquo;instanciation du th\u00e8me et du message de confirmation d&rsquo;enregistrement.<\/p>\n  <\/div>\n<\/div>\n\n<p>Nous commen\u00e7ons par initialiser la fonction getEditableConfigNames() qui va nous permettre de d\u00e9finir un tableau contenant les noms des objets de configuration que notre formulaire va pouvoir \u00e9diter.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n  <span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\">   * {@inheritdoc}\n<\/span><span style=\"color:#e6db74\">   *\/<\/span>\n  <span style=\"color:#66d9ef\">protected<\/span> <span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">getEditableConfigNames<\/span>() {\n    <span style=\"color:#66d9ef\">return<\/span> [\n      <span style=\"color:#e6db74\">&#39;entityconnect.administration_config&#39;<\/span>\n    ];\n  }<\/code><\/pre><\/div>\n<p>La documentation Drupal fait g\u00e9n\u00e9ralement r\u00e9f\u00e9rence \u00e0 un nom de la forme \u00a0<code>mon_module.settings<\/code>. Cependant settings n&rsquo;est pas un nom obligatoire. Le format attendu \u00e9tant le suivant <code>&lt;module_name&gt;.&lt;config_object_name&gt;.&lt;optional_sub_key&gt;.yml<\/code>.<\/p>\n\n<div class=\"ui positive message\">\n  <div class=\"content\">\n    \n    \n    <p>Ce nom sera utilis\u00e9 \u00e0 chaque fois qu&rsquo;il est n\u00e9cessaire de r\u00e9cup\u00e9rer ou modifier cet \u00e9l\u00e9ment de configuration. Il sera \u00e9galement utilis\u00e9 pour instancier les valeurs par d\u00e9faut.<\/p>\n  <\/div>\n<\/div>\n\n<p>On donne ensuite un Id \u00e0 notre formulaire.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n  <span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\">   * {@inheritdoc}\n<\/span><span style=\"color:#e6db74\">   *\/<\/span>\n  <span style=\"color:#66d9ef\">public<\/span> <span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">getFormId<\/span>() {\n    <span style=\"color:#66d9ef\">return<\/span> <span style=\"color:#e6db74\">&#39;entityconnect_administration_form&#39;<\/span>;\n  }<\/code><\/pre><\/div>\n\n<div class=\"ui positive message\">\n  <div class=\"content\">\n    \n    \n    <p>Il est recommand\u00e9 de faire commencer le formId par le nom du module.<\/p>\n  <\/div>\n<\/div>\n\n<p>Et on construit notre formulaire via la fonction <code>buildFrom<\/code>.<br>\nComme nous avons besoin de r\u00e9cup\u00e9rer des \u00e9l\u00e9ments de configuration, nous allons charger la configuration via <code>$this-&gt;config('entityconnect.administration_config')<\/code> que l&rsquo;on stocke dans une variable nomm\u00e9e <code>$config<\/code>.<br>\nAinsi nous utiliserons la variable $config pour r\u00e9cup\u00e9rer la valeur souhait\u00e9e via l&rsquo;appel <code>$config-&gt;get('ma_variable'),<\/code><\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n  <span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\">   * {@inheritdoc}\n<\/span><span style=\"color:#e6db74\">   *\/<\/span>\n  <span style=\"color:#66d9ef\">public<\/span> <span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">buildForm<\/span>(<span style=\"color:#66d9ef\">array<\/span> $form, <span style=\"color:#a6e22e\">FormStateInterface<\/span> $form_state) {\n    $config <span style=\"color:#f92672\">=<\/span> $this<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">config<\/span>(<span style=\"color:#e6db74\">&#39;entityconnect.administration_config&#39;<\/span>);\n\n    $form[<span style=\"color:#e6db74\">&#39;entityconnect&#39;<\/span>] <span style=\"color:#f92672\">=<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n      <span style=\"color:#e6db74\">&#39;#type&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;fieldset&#39;<\/span>,\n      <span style=\"color:#e6db74\">&#39;#title&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> $this<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;EntityConnect default Parameters&#39;<\/span>),\n    );\n\n    <span style=\"color:#75715e\">\/\/ ...\n<\/span><span style=\"color:#75715e\"><\/span>\n    $form[<span style=\"color:#e6db74\">&#39;entityconnect&#39;<\/span>][<span style=\"color:#e6db74\">&#39;button&#39;<\/span>][<span style=\"color:#e6db74\">&#39;button_add&#39;<\/span>] <span style=\"color:#f92672\">=<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n      <span style=\"color:#e6db74\">&#39;#required&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;1&#39;<\/span>,\n      <span style=\"color:#e6db74\">&#39;#default_value&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> $config<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">get<\/span>(<span style=\"color:#e6db74\">&#39;button_add&#39;<\/span>),\n      <span style=\"color:#e6db74\">&#39;#description&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> $this<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;Default: &#34;off&#34;&lt;br \/&gt;\n<\/span><span style=\"color:#e6db74\">                            Choose &#34;on&#34; if you want the &#34;add&#34; buttons displayed by default.&lt;br \/&gt;\n<\/span><span style=\"color:#e6db74\">                            Each field can override this value.&#39;<\/span>),\n      <span style=\"color:#e6db74\">&#39;#weight&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;0&#39;<\/span>,\n      <span style=\"color:#e6db74\">&#39;#type&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#e6db74\">&#39;radios&#39;<\/span>,\n      <span style=\"color:#e6db74\">&#39;#options&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> <span style=\"color:#66d9ef\">array<\/span>(\n        <span style=\"color:#e6db74\">&#39;0&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> $this<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;on&#39;<\/span>),\n        <span style=\"color:#e6db74\">&#39;1&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> $this<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;off&#39;<\/span>),\n      ),\n      <span style=\"color:#e6db74\">&#39;#title&#39;<\/span> <span style=\"color:#f92672\">=&gt;<\/span> $this<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">t<\/span>(<span style=\"color:#e6db74\">&#39;Default Entity Connect &#34;add&#34; button display&#39;<\/span>),\n    );\n\n    <span style=\"color:#75715e\">\/\/ ...\n<\/span><\/code><\/pre><\/div>\n<p>Et on fini par retourner un appel \u00e0 la class parent avec les \u00e9l\u00e9ments de formulaire ajout\u00e9s.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n    <span style=\"color:#66d9ef\">return<\/span> <span style=\"color:#66d9ef\">parent<\/span><span style=\"color:#f92672\">::<\/span><span style=\"color:#a6e22e\">buildForm<\/span>($form, $form_state);\n  }<\/code><\/pre><\/div>\n<p>Enfin il est n\u00e9cessaire de pouvoir enregistrer les valeurs apr\u00e8s validation du formulaire.\nNous allons donc encore une fois utiliser l&rsquo;objet <code>$this-&gt;config('entityconnect.administration_config')<\/code> et d\u00e9finir la variable avec la valeur du formulaire.<\/p>\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n\n  <span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\">   * {@inheritdoc}\n<\/span><span style=\"color:#e6db74\">   *\/<\/span>\n  <span style=\"color:#66d9ef\">public<\/span> <span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">submitForm<\/span>(<span style=\"color:#66d9ef\">array<\/span> <span style=\"color:#f92672\">&amp;<\/span>$form, <span style=\"color:#a6e22e\">FormStateInterface<\/span> $form_state) {\n    <span style=\"color:#66d9ef\">parent<\/span><span style=\"color:#f92672\">::<\/span><span style=\"color:#a6e22e\">submitForm<\/span>($form, $form_state);\n\n    $this<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">config<\/span>(<span style=\"color:#e6db74\">&#39;entityconnect.administration_config&#39;<\/span>)\n      <span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">set<\/span>(<span style=\"color:#e6db74\">&#39;icon_add&#39;<\/span>, $form_state<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">getValue<\/span>(<span style=\"color:#e6db74\">&#39;icon_add&#39;<\/span>))\n      <span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">set<\/span>(<span style=\"color:#e6db74\">&#39;icon_edit&#39;<\/span>, $form_state<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">getValue<\/span>(<span style=\"color:#e6db74\">&#39;icon_edit&#39;<\/span>))\n      <span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">save<\/span>();\n  }\n\n}<\/code><\/pre><\/div>\n<p>On a maintenant un formulaire pr\u00eat \u00e0 fonctionner et enregistrer des \u00e9l\u00e9ments de configuration.<\/p>\n<h2 id=\"d\u00e9finition-des-valeurs-par-d\u00e9faut\">D\u00e9finition des valeurs par d\u00e9faut<\/h2>\n<p>Vous aurez peut \u00eatre remarqu\u00e9 qu&rsquo;\u00e0 la diff\u00e9rence de la fonction variable_get() dans Drupal 7, nous n&rsquo;avons pas d\u00e9fini de valeurs par d\u00e9faut \u00e0 nos <!-- raw HTML omitted -->variables<!-- raw HTML omitted --> configuration.<\/p>\n<p>Il n&rsquo;est donc plus possible (et n\u00e9cessaire) de red\u00e9finir \u00e0 chaque appel la valeur par d\u00e9faut associ\u00e9e \u00e0 une variable comme dans cet exemple <code>variable_get('entityconnect_unload_add_default', 1)<\/code> o\u00f9 1 \u00e9tait la valeur par d\u00e9faut.<\/p>\n<p>Dans Drupal 8, les configurations sont maintenant stock\u00e9es dans des fichiers.<br>\nPour d\u00e9finir une valeur par d\u00e9faut \u00e0 nos \u00e9l\u00e9ments de configuration, il est donc n\u00e9cessaire de d\u00e9finir cela \u00e0 l&rsquo;installation du module.<\/p>\n<p>2 voies sont possibles :<\/p>\n<ul>\n<li>\n<p>soit via le hook_install, si les valeurs a renseign\u00e9e sont dynamiques\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-php\" data-lang=\"php\"><span style=\"color:#f92672\">&lt;?<\/span><span style=\"color:#a6e22e\">php<\/span>\n<span style=\"color:#e6db74\">\/**\n<\/span><span style=\"color:#e6db74\"> * Implements hook_install() in Drupal 8.\n<\/span><span style=\"color:#e6db74\"> *\/<\/span>\n<span style=\"color:#66d9ef\">function<\/span> <span style=\"color:#a6e22e\">modulename_install<\/span>() {\n  <span style=\"color:#75715e\">\/\/ Set default values for config which require dynamic values.\n<\/span><span style=\"color:#75715e\"><\/span>  <span style=\"color:#a6e22e\">\\Drupal<\/span><span style=\"color:#f92672\">::<\/span><span style=\"color:#a6e22e\">configFactory<\/span>()<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">getEditable<\/span>(<span style=\"color:#e6db74\">&#39;modulename.settings&#39;<\/span>)\n    <span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">set<\/span>(<span style=\"color:#e6db74\">&#39;default_from_address&#39;<\/span>, <span style=\"color:#a6e22e\">\\Drupal<\/span><span style=\"color:#f92672\">::<\/span><span style=\"color:#a6e22e\">config<\/span>(<span style=\"color:#e6db74\">&#39;system.site&#39;<\/span>)<span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">get<\/span>(<span style=\"color:#e6db74\">&#39;mail&#39;<\/span>))\n    <span style=\"color:#f92672\">-&gt;<\/span><span style=\"color:#a6e22e\">save<\/span>();\n}<\/code><\/pre><\/div><\/p>\n<\/li>\n<li>\n<p>soit via l&rsquo;utilisation d&rsquo;un fichier YAML qui contiendra les configurations par d\u00e9faut (si les valeurs sont statiques).\n<div class=\"highlight\"><pre tabindex=\"0\" style=\"color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4\"><code class=\"language-yaml\" data-lang=\"yaml\"><span style=\"color:#75715e\"># Contenu du fichier entityconnect.administration_config.yml<\/span>\n<span style=\"color:#f92672\">button_add<\/span>: <span style=\"color:#ae81ff\">1<\/span>\n<span style=\"color:#f92672\">button_edit<\/span>: <span style=\"color:#ae81ff\">1<\/span>\n<span style=\"color:#f92672\">icon_add<\/span>: <span style=\"color:#ae81ff\">0<\/span>\n<span style=\"color:#f92672\">icon_edit<\/span>: <span style=\"color:#ae81ff\">0<\/span><\/code><\/pre><\/div><\/p>\n<\/li>\n<\/ul>\n\n<div class=\"ui positive message\">\n  <div class=\"content\">\n    \n    \n    <p>Ce fichier doit se nommer avec le m\u00eame nom que l&rsquo;objet de configuration que nous appelons dans notre formulaire.<\/p>\n  <\/div>\n<\/div>\n\n<p>Le fichier YAML cr\u00e9\u00e9 doit \u00eatre plac\u00e9 dans l&rsquo;arborescence suivante :<\/p>\n<pre tabindex=\"0\"><code>.\n\u251c\u2500\u2500 config\n\u2502   \u2514\u2500\u2500 install\n\u2502       \u2514\u2500\u2500 entityconnect.administration_config.yml\n<\/code><\/pre>\n<div class=\"ui positive message\">\n  <div class=\"content\">\n    \n      <div class=\"header\">\n        Pour en savoir plus\n      <\/div>\n    \n    \n    <p><p>\u00a0<\/p>\n<ul>\n<li><a href=\"https:\/\/www.drupal.org\/node\/1667896\">Upgrading Drupal 7 Variables to Drupal 8 Configuration<\/a><\/li>\n<li><a href=\"https:\/\/www.drupal.org\/node\/2120571\">Configuration Storage in Drupal 8<\/a><\/li>\n<\/ul>\n<\/p>\n  <\/div>\n<\/div>\n\n<h2 id=\"organisation-des-fichiers-r\u00e9capitulatif\">Organisation des fichiers (r\u00e9capitulatif)<\/h2>\n<p>Drupal 7<\/p>\n<pre tabindex=\"0\"><code>.\n\u251c\u2500\u2500 entityconnect.info\n\u251c\u2500\u2500 entityconnect.module\n\u251c\u2500\u2500 includes\n\u2502   \u251c\u2500\u2500 entityconnect.admin.inc\n<\/code><\/pre><p>Drupal 8<\/p>\n<pre tabindex=\"0\"><code>.\n\u251c\u2500\u2500 config\n\u2502   \u2514\u2500\u2500 install\n\u2502       \u2514\u2500\u2500 entityconnect.administration_config.yml\n\u251c\u2500\u2500 entityconnect.info.yml\n\u251c\u2500\u2500 entityconnect.module\n\u251c\u2500\u2500 entityconnect.routing.yml\n\u251c\u2500\u2500 src\n\u2502   \u2514\u2500\u2500 Form\n\u2502       \u2514\u2500\u2500 AdministrationForm.php\n<\/code><\/pre>"},{"title":"Docker - Comprendre le fonctionnement du tag latest","link":"https:\/\/gastaud.io\/article\/docker-comprendre-tag-latest\/","pubDate":"Thu, 09 Apr 2015 00:00:00 +0000","guid":"https:\/\/gastaud.io\/article\/docker-comprendre-tag-latest\/","description":"<p>Cet article est une synth\u00e8se d&rsquo;une <a href=\"https:\/\/medium.com\/@mccode\/the-misunderstood-docker-tag-latest-af3babfd6375\">publication parue sur Medium<\/a> qui explique de mani\u00e8re pr\u00e9cise le fonctionnement du tag <strong>latest<\/strong> disponible sur les images Docker.<\/p>\n<p>Comme vous le savez surement, il est possible de tagger ces images Docker pour les d\u00e9poser sur un registry.<\/p>\n<p>Cependant si vous avez essay\u00e9 d&rsquo;utiliser le tag <strong>lastest<\/strong> disponible pour certaines images, notamment sur le <a href=\"https:\/\/hub.docker.com\/\">Hub Docker<\/a>, vous avez pu vous rendre compte que ce tag n&rsquo;a pas toujours le comportement esp\u00e9r\u00e9.<\/p>\n<h2 id=\"un-secret-enfin-r\u00e9v\u00e9l\u00e9\">Un secret enfin r\u00e9v\u00e9l\u00e9<\/h2>\n<p>Ce que l&rsquo;on apprend dans l&rsquo;article, c&rsquo;est que ce tag &ldquo;latest&rdquo; n&rsquo;est pas vraiment un tag.<\/p>\n<p>Il n&rsquo;est en r\u00e9alit\u00e9 pr\u00e9sent que pour d\u00e9signer la derni\u00e8re version commit\u00e9e <strong>sans tag<\/strong> et non pas la derni\u00e8re version tagg\u00e9e\/commit\u00e9e.<\/p>\n<p>Il est ainsi probable que beaucoup de versions &ldquo;latest&rdquo; sur le Hub Docker ne soit en r\u00e9alit\u00e9 pas les versions les plus \u00e0 jour.\nCela implique en effet une forte rigeur (et connaissance du fonctionnement que l&rsquo;on vient de d\u00e9crire) de la part des auteurs des images disponibles car pour maintenir le tag &ldquo;latest&rdquo; \u00e0 jour, il est obligatoire de toujours pr\u00e9voir un double taggage de son image.<\/p>\n<ol>\n<li>une premi\u00e8re fois sans num\u00e9ro de tag<\/li>\n<li>une seconde fois avec une num\u00e9ro de tag<\/li>\n<\/ol>\n<h2 id=\"le-conseil-de-lauteur\">Le conseil de l&rsquo;auteur<\/h2>\n<ul>\n<li><strong>Oublier que ce tag latest existe<\/strong> et ne jamais l&rsquo;utiliser.<\/li>\n<li><strong>Toujours<\/strong> penser \u00e0 tagger vos images<\/li>\n<\/ul>\n<p>Je vous invite \u00e0 <a href=\"https:\/\/medium.com\/@mccode\/the-misunderstood-docker-tag-latest-af3babfd6375\">lire l&rsquo;int\u00e9gralit\u00e9 de la d\u00e9monstration<\/a> pour plus de d\u00e9tails.<\/p>\n"},{"title":"Docker - Erreurs de d\u00e9butant","link":"https:\/\/gastaud.io\/article\/docker-erreurs-debutant\/","pubDate":"Tue, 12 Aug 2014 00:00:00 +0000","guid":"https:\/\/gastaud.io\/article\/docker-erreurs-debutant\/","description":"<p>Dans l&rsquo;article pr\u00e9c\u00e9dent, je parlais du projet [Wintersmith Docker]((<a href=\"https:\/\/github.com\/jygastaud\/wintersmith_docker\">https:\/\/github.com\/jygastaud\/wintersmith_docker<\/a>) et mentionnais le fait que j&rsquo;avais fait des erreurs dans la conception initiale, d\u00fbes \u00e0 un manque de connaissance de Docker.<\/p>\n<h2 id=\"le-dockerfile\">Le Dockerfile<\/h2>\n<p>L&rsquo;une des premi\u00e8res erreurs que j&rsquo;ai fait a \u00e9t\u00e9 de vouloir tout mettre dans le Dockerfile en pensant que tout doit passer par l\u00e0 pour automatiser les choses.<\/p>\n<p>C&rsquo;est en partie vrai mais il est parfois pr\u00e9f\u00e9rable de s\u00e9parer la partie stack logiciel, data et build.<\/p>\n<p>Mon 1er Dockerfile ressemblait \u00e0 cela<\/p>\n<pre tabindex=\"0\"><code># DOCKER-VERSION 0.9.1\nFROM  ubuntu\n\nRUN     sudo apt-get update -qq\nRUN     sudo apt-get install python-software-properties -y -qq\nRUN     sudo apt-get install nodejs npm -y -qq\nRUN     sudo apt-get install nodejs-legacy -y -qq\n\n# Bundle app source\nADD .\/www \/www\n\n# Install app dependencies\nRUN sudo npm install wintersmith -g\n\n# Create new site\nWORKDIR \/www\nRUN wintersmith new site\n\n# Launch Wintersmith preview\nWORKDIR \/www\/site\nCMD [&quot;wintersmith&quot;, &quot;preview&quot;]\n\nEXPOSE  8080\n<\/code><\/pre><p>Rentrons un peu dans le d\u00e9tail du fichier pour voir les diff\u00e9rentes probl\u00e9matiques:<\/p>\n<ul>\n<li><code>FROM  ubuntu<\/code>\nnous ne sp\u00e9cifions aucune version ce qui a pour cons\u00e9quence de provoquer le t\u00e9l\u00e9chargement de tous les layers Ubuntu et non pas ceux seulement d\u00e9di\u00e9s \u00e0 la version souhait\u00e9e.<\/li>\n<\/ul>\n<p>Il est donc pr\u00e9f\u00e9rable d&rsquo;\u00e9crire l&rsquo;instruction sous la forme suivante:\n<code>FROM ubuntu:&lt;VERSION&gt;<\/code><\/p>\n<ul>\n<li><code>RUN wintersmith new site<\/code>\n\u00e9tait cens\u00e9 cr\u00e9\u00e9 l&rsquo;arborescence du site par d\u00e9faut tel que pr\u00e9sent\u00e9e ci-dessous<\/li>\n<\/ul>\n<pre tabindex=\"0\"><code>www\n\u2514\u2500\u2500 site\n    \u251c\u2500\u2500 contents\n    \u2502\u00a0\u00a0 \u251c\u2500\u2500 articles\n    \u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 XXXXXXXXXXX\n    \u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 YYYYYYYYYYY\n    \u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 ZZZZZZZZZZZ\n    \u2502\u00a0\u00a0 \u251c\u2500\u2500 authors\n    \u2502\u00a0\u00a0 \u2514\u2500\u2500 css\n    \u251c\u2500\u2500 plugins\n    \u2514\u2500\u2500 templates\n<\/code><\/pre><p>La commande s&rsquo;ex\u00e9cutait bien mais le dossier cr\u00e9\u00e9 n&rsquo;est pas r\u00e9cup\u00e9rable.<\/p>\n<p>Avec quelques recherches, il s&rsquo;av\u00e8re que la commande <code>docker cp<\/code> semblait \u00eatre faite pour cela mais l&rsquo;export de fonctionne pas car cp n&rsquo;est, \u00e0 ce jour, capable de ne copier qu&rsquo;un fichier \u00e0 la fois (nous y reviendrons dans un prochain article).<\/p>\n<ul>\n<li><code>CMD [&quot;wintersmith&quot;, &quot;preview&quot;]<\/code>\nbien que cette commande ne soit pas fausse en soit, il est recommand\u00e9 par par Docker l&rsquo;utilisation de ENTRYPOINT[] et de garder au niveau du CMD[] uniquement les param\u00e8tres de la commande.<\/li>\n<\/ul>\n<p>J&rsquo;apprenais d&rsquo;ailleurs par la suite, en discutant sur le channel IRC de Docker, que ENTRYPOINT ne servait pas que au niveau des commandes du Dockerfile mais aussi du run d&rsquo;un conteneur en d\u00e9finissant la commande par d\u00e9faut ex\u00e9cut\u00e9e.<\/p>\n<p>En effet, lors du run, il est possible de sp\u00e9cifier des param\u00e8tres d&rsquo;ex\u00e9cution \u00e0 l&rsquo;image d&rsquo;origine. Il est ainsi possible d&rsquo;\u00e9x\u00e9cuter diff\u00e9rents types de conteneur:<\/p>\n<ul>\n<li><code>docker run -d jygastaud\/wintersmith preview<\/code> permettra de lancer le mode preview<\/li>\n<li><code>docker run -d jygastaud\/wintersmith new nouveau_site<\/code> lancera la commande de g\u00e9n\u00e9ration de site de Wintersmith.<\/li>\n<\/ul>\n<p>Finalement, apr\u00e8s qelques ajustements et changements de m\u00e9thodes, mon Dockerfile ressemble \u00e0 cela maintetenant:<\/p>\n<pre tabindex=\"0\"><code>FROM ubuntu:14.04\nMAINTAINER jygastaud\n\n# Some stuff\n# Here\n\nENTRYPOINT [&quot;wintersmith&quot;]\n\n# Clean up APT when done.\nRUN apt-get clean &amp;&amp; rm -rf \/var\/lib\/apt\/lists\/* \/tmp\/* \/var\/tmp\/*\n<\/code><\/pre><p>Cette configuration nous donne un panel d&rsquo;utilisation globale de Wintersmith en ne stockant qu&rsquo;un minimum d&rsquo;information.<\/p>\n"},{"title":"Wintersmith Docker - Introduction","link":"https:\/\/gastaud.io\/article\/projet-wintersmith-docker-introduction\/","pubDate":"Fri, 08 Aug 2014 00:00:00 +0000","guid":"https:\/\/gastaud.io\/article\/projet-wintersmith-docker-introduction\/","description":"<p>Apr\u00e8s plusieurs lectures et essais rapides de Docker, j&rsquo;ai d\u00e9cid\u00e9 d&rsquo;y consacrer une peu plus de temps de mani\u00e8re concr\u00eate.<\/p>\n<p>Etant en pleine r\u00e9flexions sur l&rsquo;outil que j&rsquo;allais utilis\u00e9 pour la r\u00e9alisation de ce blog, j&rsquo;ai donc d\u00e9cid\u00e9 de lancer le projet <a href=\"https:\/\/github.com\/jygastaud\/wintersmith_docker\">Wintersmith Docker<\/a> afin de pouvoir tester <a href=\"http:\/\/wintersmith.io\">Wintersmith<\/a>, un g\u00e9n\u00e9rateur de site statique bas\u00e9 sur <a href=\"http:\/\/nodejs.org\">Nodejs<\/a>, dans un conteneur <a href=\"http:\/\/docker.com\">Docker<\/a>.<\/p>\n<h2 id=\"ca-sert-\u00e0-quoi-ce-projet\">Ca sert \u00e0 quoi ce projet?<\/h2>\n<p>Le projet donne acc\u00e8s \u00e0 2 types d&rsquo;utilisation:<\/p>\n<ul>\n<li><strong>Pour les utilisateurs de Vagrant<\/strong>, un Vagrantfile est disponible en plus du Dockerfile afin d&rsquo;avoir l&rsquo;installation de Docker, la cr\u00e9ation de l&rsquo;image et le lancement du conteneur Docker r\u00e9alis\u00e9 de mani\u00e8re automatis\u00e9.<\/li>\n<li><strong>Pour les utilisateurs de Docker<\/strong>, le Dockerfile permet de cr\u00e9er une image contenant Nodejs et Wintersmith pr\u00e9-install\u00e9s qui peut \u00eatre ensuite appeler pour la cr\u00e9ation d&rsquo;un ou plusieurs containers.<\/li>\n<\/ul>\n<p>La mise en place de ce projet a \u00e9t\u00e9 extremement formatrice sur Docker.\nJe vous invite \u00e0 lire les <a href=\"http:\/\/jygastaud.github.io\/blog\/articles\/notes-docker\/\">quelques notes en vracs<\/a> que j&rsquo;ai post\u00e9 dans mon billet pr\u00e9c\u00e9dent.<\/p>\n<h2 id=\"un-peu-dhistorique\">Un peu d&rsquo;historique<\/h2>\n<p>L&rsquo;id\u00e9e initiale du projet \u00e9tait simple et se d\u00e9composait comme ceci:<\/p>\n<ol>\n<li>Cr\u00e9er une image via Docker contenant Wintersmith et Nodejs pour pouvoir \u00e0 volont\u00e9:<\/li>\n<\/ol>\n<ul>\n<li>Cr\u00e9er un nouveau site Wintersmith: <code>wintersmith new &lt;site name&gt;<\/code><\/li>\n<li>Partager un site existant<\/li>\n<\/ul>\n<ol start=\"2\">\n<li>Cr\u00e9er un conteneur Docker qui<\/li>\n<\/ol>\n<ul>\n<li>Lancerait Wintermith en mode preview: <code>wintersmith preview<\/code><\/li>\n<li>Permettrait de voir en live les modifications \/ ajouts faits dans le dossier de contenus.<\/li>\n<\/ul>\n<p><strong>Le tout sans avoir besoin d&rsquo;installer Wintersmith et Nodejs en local<\/strong> (comme avec une VM Vagrant en fait mais en plus l\u00e9ger).<\/p>\n<h2 id=\"un-peu-dhonn\u00eatet\u00e9\">Un peu d&rsquo;honn\u00eatet\u00e9<\/h2>\n<p>Soyons franc,tout n&rsquo;a pas \u00e9t\u00e9 tout beau et tout rose.<\/p>\n<p>Docker n&rsquo;offre pas la m\u00eame souplesse qu&rsquo;un machine virtuelle compl\u00e8te et est notamment restreint par:<\/p>\n<ul>\n<li>le fait de ne devoir g\u00e9rer qu&rsquo;un seul processus par conteneur (tout du moins si l&rsquo;on ne passe pas par des solutions de contournement telles que Supervisor)<\/li>\n<li>le fait de ne pas pouvoir ex\u00e9cuter plusieurs commandes lors du <code>run<\/code> d&rsquo;un conteneur<\/li>\n<li>le fait de ne pas avoir d&rsquo;acc\u00e8s direct (via ssh par exemple) au conteneur lanc\u00e9<\/li>\n<li><em>etc<\/em><\/li>\n<\/ul>\n<p>Ces quelques exemples montrent bien les diff\u00e9rences, potentiellement mal per\u00e7ues\/comprises, entre un Docker et une VM.<\/p>\n<p>Les usages que l&rsquo;on peut trouver aux 2\nsyst\u00e8mes ne sont pas forcement concurrents. Pour preuves, Vagrant propose de faire son provisioning avec Docker.<\/p>\n"},{"title":"Mes notes sur Docker","link":"https:\/\/gastaud.io\/article\/notes-docker\/","pubDate":"Mon, 04 Aug 2014 00:00:00 +0000","guid":"https:\/\/gastaud.io\/article\/notes-docker\/","description":"<ul>\n<li>\n<p>Le fichier <code>Dockerfile<\/code> produit une Image<\/p>\n<\/li>\n<li>\n<p>Dans le Dockerfile il n&rsquo;est possible de mettre qu&rsquo;une seule instruction CMD.<\/p>\n<ul>\n<li>Si 2 intructions (ou plus) sont dans le m\u00eame fichier, seule la derni\u00e8re sera ex\u00e9cut\u00e9e.<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>Il est possible de commiter, pusher ou r\u00e9cup\u00e9rer une image (\u00e0 la fa\u00e7on de GIT)<\/p>\n<\/li>\n<li>\n<p><code>docker run ...<\/code> produit une instance de l&rsquo;image<\/p>\n<\/li>\n<li>\n<p>Les fichiers ajout\u00e9s via l&rsquo;image (et le Dockerfile) sont fixes \u00e0 chaque lancement de run<\/p>\n<\/li>\n<li>\n<p>Si la commande run contient l&rsquo;option <code>-v<\/code> il est possible de partager un dossier local (alors dynamique dans le conteneur)<\/p>\n<ul>\n<li>Si un <code>ADD<\/code> dans le dockerfile contient le m\u00eame chemin que celui que l&rsquo;on veut rendre dynamique, cela ne marche pas.<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>Il est possible de partager des volumes entre les conteneurs lors du run via l&rsquo;option &ndash;volumes-from<\/p>\n<\/li>\n<li>\n<p>On peut nommer un conteneur (&ndash;name) ce qui permet d&rsquo;effectuer les op\u00e9rations via son nom et non son ID (ne marche pas pour toutes les commandes)<\/p>\n<\/li>\n<\/ul>\n"},{"title":"Configurer Nginx pour g\u00e9rer automatiquement vos sous-domaines locaux","link":"https:\/\/gastaud.io\/article\/nginx-sousdomaines-auto\/","pubDate":"Tue, 07 Jan 2014 00:00:00 +0000","guid":"https:\/\/gastaud.io\/article\/nginx-sousdomaines-auto\/","description":"<p>Lors des d\u00e9veloppements locaux, nous avons pris l&rsquo;habitude de toujours d\u00e9finir un nom de domaine local (mon_site.local dans notre exemple) afin de coller au plus proche de la r\u00e9alit\u00e9 du site final.<\/p>\n<h2 id=\"le-processus-classique-\u00e9tait-le-suivant\">Le processus classique \u00e9tait le suivant<\/h2>\n<ul>\n<li>Ajout d&rsquo;un nouveau dossier allant contenir notre site dans notre r\u00e9pertoire de d\u00e9veloppement (\/var\/www dans notre exemple). On obtient ainsi l&rsquo;arborescence suivante:<\/li>\n<\/ul>\n<pre tabindex=\"0\"><code>\/var\/www\/\n\u251c\u2500\u2500 mon_site1\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 index.html\n\u251c\u2500\u2500 mon_site2\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 index.html\n<\/code><\/pre><ul>\n<li>Ajout d&rsquo;un nouvel host local dans le fichier <code>\/etc\/hosts<\/code><\/li>\n<\/ul>\n<pre tabindex=\"0\"><code>127.0.0.1 mon_site.local\n<\/code><\/pre><hr>\n<h2 id=\"fonctionnement-d\u00e9sir\u00e9\">Fonctionnement d\u00e9sir\u00e9<\/h2>\n<p>Le but principal de notre impl\u00e9mentation est de pouvoir traiter un ensemble de sous-sites de mani\u00e8re g\u00e9n\u00e9rique pour ne pas avoir \u00e0 recr\u00e9er de nouveaux vhosts et aller modifier le fichier hosts local \u00e0 chaque nouvelle impl\u00e9mentation.<\/p>\n<p>Ainsi avec l&rsquo;arborescence ci-dessous,<\/p>\n<pre tabindex=\"0\"><code>\/var\/www\/\n\u251c\u2500\u2500 local.dev\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 www\n\u2502\u00a0\u00a0     \u251c\u2500\u2500 test1\n\u2502\u00a0\u00a0     \u2502\u00a0\u00a0 \u2514\u2500\u2500 index.html\n\u2502\u00a0\u00a0     \u2514\u2500\u2500 test2\n\u2502\u00a0\u00a0         \u2514\u2500\u2500 index.html\n<\/code><\/pre><p>il nous sera imm\u00e9diatement possible d&rsquo;acc\u00e9der aux sous-domaines suivants et de charger le bon fichier d&rsquo;index:<\/p>\n<pre tabindex=\"0\"><code>test1.local.dev\ntest2.local.dev\n<\/code><\/pre><hr>\n<h2 id=\"configuration-de-nginx\">Configuration de Nginx<\/h2>\n<p>Nous partons du pr\u00e9requis que vous avez d\u00e9j\u00e0 Nginx install\u00e9 sur votre machine.\nSi ce n&rsquo;est pas le cas, une bref recherche vous permettra de trouver de nombreux tutoriaux traitant de ce sujet.<\/p>\n<p>Dans notre exemple, la version de nginx utilis\u00e9e est la suivante:\nnginx version: nginx\/1.4.1 (Ubuntu)<\/p>\n<h3 id=\"cr\u00e9ation-dun-nouveau-site-nginx\">Cr\u00e9ation d&rsquo;un nouveau site nginx<\/h3>\n<p>Configuration de notre site dans le dossier <code>\/etc\/nginx\/sites-available<\/code><\/p>\n<pre tabindex=\"0\"><code>server {\n    # On \u00e9coute le port 80.\n    listen 80;\n\n    # On va utiliser une expression r\u00e9guli\u00e8re\n    # pour r\u00e9cup\u00e9rer le sous-domaine dans une variable nomm\u00e9e &quot;sub&quot;.\n    server_name  ~^(?P&lt;sub&gt;.+)\\.local\\.dev$;\n\n    location \/ {\n         # On d\u00e9finie le chemin local\n         # en utilisant la variable &quot;sub&quot; r\u00e9cup\u00e9r\u00e9e pr\u00e9c\u00e9demment.\n         root \/var\/www\/local.dev\/www\/$sub;\n    }\n}\n<\/code><\/pre><p>On active notre site via un lien symbolique dans le dossier <code>\/etc\/nginx\/sites-enable<\/code><\/p>\n<pre tabindex=\"0\"><code>ln -s \/etc\/nginx\/sites-available\/local.dev \/etc\/nginx\/sites-enable\/local.dev\n<\/code><\/pre><p>et on red\u00e9marre le serveur via la commande:<\/p>\n<pre tabindex=\"0\"><code>service nginx restart\n<\/code><\/pre><hr>\n<h2 id=\"configuration-dun-hote-g\u00e9n\u00e9rique\">Configuration d&rsquo;un hote g\u00e9n\u00e9rique<\/h2>\n<h3 id=\"1\u00e8res-tentatives\">1\u00e8res tentatives<\/h3>\n<p>Notre 1\u00e8re logique a donc \u00e9tait d&rsquo;essayer de d\u00e9finir un domaine g\u00e9n\u00e9rique au sein de notre fichier <code>\/etc\/hosts<\/code> comme nous pouvions le faire avec notre domaine classique.<\/p>\n<p>Nous avons donc tenter l&rsquo;ajout de plusieurs lignes avec ou sans wildcard (*) telles que:<\/p>\n<pre tabindex=\"0\"><code>127.0.0.1 local.dev\n127.0.0.1 .local.dev\n127.0.0.1 *.local.dev\n<\/code><\/pre><p>mais rien n&rsquo;y fait, ces solutions ne marchent pas et il nous fallait toujours ajouter nos sous-domaines &ldquo;\u00e0 la main&rdquo; dans le fichier hosts.<\/p>\n<h3 id=\"le-probl\u00e8me\">Le probl\u00e8me<\/h3>\n<p>Le fichier hosts ne permet pas de d\u00e9finir de wildcards pour un domaine.<\/p>\n<p>Il est donc normal que nos tentitves pr\u00e9c\u00e9dentes ne fonctionnent pas.<\/p>\n<h3 id=\"la-solution\">La solution<\/h3>\n<p>Nos diverses recherches nous ont emmen\u00e9 \u00e0 d\u00e9couvrir que la seule solution possible pour obtenir le r\u00e9sultat escompt\u00e9 \u00e9tait de passer par un serveur de DNS install\u00e9 en local et qui se chargerait de faire l&rsquo;interpr\u00e9tation et la traduction du nom de domaine.<\/p>\n<p>Plusieurs solutions existes pour cela et dont la plus connue est <strong>Bind<\/strong>.\nToutefois, Bind nous paraissait trop lourd \u00e0 impl\u00e9menter pour notre besoin et nous nous sommes finalement rabattus sur l&rsquo;utilisation de <strong>DNSMasq<\/strong> qui permet une impl\u00e9mentation rapide et une configuration tr\u00e8s simple.<\/p>\n<p>###DNSmasq<\/p>\n<p>Dans le fichier \/etc\/dnsmasq.conf (le cr\u00e9er s&rsquo;il n&rsquo;existe pas), il vous suffit d&rsquo;ajouter la ligne suivante:<\/p>\n<pre tabindex=\"0\"><code>address=\/mon_nom_de_domaine.extension\/127.0.0.1\n<\/code><\/pre><p>et c&rsquo;est tout!<\/p>\n<p>Dans notre exemple, nous avons donc ajouter:<\/p>\n<pre tabindex=\"0\"><code>address=\/local.dev\/127.0.0.1\n<\/code><\/pre><p>On red\u00e9marre dnsmasq (<code>service dnsmasq restart<\/code> sur Ubuntu) et on peut constater que nos domaines sont accessibles.<\/p>\n"},{"title":"Drupal Export des noeuds en Markdown","link":"https:\/\/gastaud.io\/article\/drupal-export-noeud-markdown\/","pubDate":"Wed, 01 Jan 2014 00:00:00 +0000","guid":"https:\/\/gastaud.io\/article\/drupal-export-noeud-markdown\/","description":"<p>Ressources utiles:<\/p>\n<pre><code>* https:\/\/github.com\/lukaswhite\/Drupal-Markdownify\n* https:\/\/github.com\/lukaswhite\/Drupal-Jekyll-Export\n* http:\/\/www.gizra.com\/content\/dekyll-drupal-on-jekyll\/\n<\/code><\/pre>\n"},{"title":"DrupalCamp Paris2013 - Behat et Drupal","link":"https:\/\/gastaud.io\/article\/drupalcamp\/paris2013\/behat\/","pubDate":"Sat, 22 Jun 2013 15:00:28 +0100","guid":"https:\/\/gastaud.io\/article\/drupalcamp\/paris2013\/behat\/","description":"<h1 id=\"behat-et-drupal\">Behat et Drupal<\/h1>\n<h3 id=\"selenium-api-et-ide\">Selenium API et IDE<\/h3>\n<ul>\n<li>Comment automatiser les tests?\n<ul>\n<li>Selenium Server<\/li>\n<li>PHPUnit<\/li>\n<li>Selenium IDE<\/li>\n<li>&hellip;<\/li>\n<\/ul>\n<\/li>\n<li>Comment tester l&rsquo;ajax?\n<ul>\n<li>Selenium \u00e0 installer<\/li>\n<\/ul>\n<\/li>\n<li>Comment tester le multi-domaine<\/li>\n<\/ul>\n<h3 id=\"behaviour-driven-development-bdd\">Behaviour Driven Development (BDD)<\/h3>\n<p>CF: Ryan Weaver\nportland 2013<\/p>\n<h2 id=\"gherkin\">Gherkin<\/h2>\n<p>Langage qui permet de d\u00e9finir des tests<\/p>\n<ul>\n<li>Feature\n<ul>\n<li>Scenario\n<ul>\n<li>Steps\n<ul>\n<li>Context: Given\n<ul>\n<li>And<\/li>\n<\/ul>\n<\/li>\n<li>When\n<ul>\n<li>And<\/li>\n<\/ul>\n<\/li>\n<li>Result: Then<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h3 id=\"cucumber\">Cucumber<\/h3>\n<ul>\n<li>Gherkin + Ruby<\/li>\n<\/ul>\n<h2 id=\"behat--mink\">Behat + Mink<\/h2>\n<h3 id=\"behat\">Behat<\/h3>\n<ul>\n<li>Gherkin + PHP =&gt; Behat<\/li>\n<\/ul>\n<h3 id=\"mink\">Mink<\/h3>\n<ul>\n<li>\n<p>Drivers<\/p>\n<ul>\n<li>ZombieDriver<\/li>\n<li>Sahi<\/li>\n<li>Goutte (si pas besoin de js)<\/li>\n<li>Selenium (avec js)<\/li>\n<\/ul>\n<\/li>\n<li>\n<p>MinkExtension sert \u00e0 lier Behat et Mink<\/p>\n<\/li>\n<li>\n<p>Behat est ind\u00e9pendant de l&rsquo;application web<\/p>\n<\/li>\n<li>\n<p>Behat s&rsquo;installe via Composer<\/p>\n<\/li>\n<li>\n<p>Ex\u00e9cutable dans dossier bin\/<\/p>\n<\/li>\n<\/ul>\n<p>FeatureContext.php doit \u00eatre modifi\u00e9 pour \u00e9tendre MinkContext\nIl est possible de cr\u00e9er des subcontexts<\/p>\n<p>Attention le mouseover ne fonctionne que sur les \u00e9l\u00e9ments javascript, pas css<\/p>\n<h2 id=\"projet-drupal\">Projet Drupal<\/h2>\n<p>DrupalExtension =&gt; project\/drupalextension<\/p>\n"},{"title":"DrupalCamp Paris2013 - GED","link":"https:\/\/gastaud.io\/article\/drupalcamp\/paris2013\/ged\/","pubDate":"Fri, 21 Jun 2013 15:00:28 +0100","guid":"https:\/\/gastaud.io\/article\/drupalcamp\/paris2013\/ged\/","description":"<h1 id=\"ged\">GED<\/h1>\n<h2 id=\"acquisition\">Acquisition<\/h2>\n<ul>\n<li>Imports: Feed, custom<\/li>\n<li>Services, CMIS API<\/li>\n<li>Solr<\/li>\n<\/ul>\n<h2 id=\"stockage\">Stockage<\/h2>\n<p>Versionning + diff\nArchivage: Taxonomie\nWorkflow: Maestro<\/p>\n<h2 id=\"classement\">Classement<\/h2>\n<ul>\n<li>Gestion des permissions<\/li>\n<li>FacetAPI<\/li>\n<\/ul>\n<h3 id=\"principales\">Principales<\/h3>\n<p>Alfresco\nKnowledge Tree<\/p>\n<h3 id=\"annexes\">Annexes<\/h3>\n<ul>\n<li>ExoPatform<\/li>\n<li>Quotero<\/li>\n<\/ul>\n<h2 id=\"comparaison\">Comparaison<\/h2>\n<p>Faiblesses de Drupal:<\/p>\n<ul>\n<li>Int\u00e9gration avec MS et Google Docs<\/li>\n<li>WebDav<\/li>\n<\/ul>\n<p>Avantages de Drupal :<\/p>\n<ul>\n<li>Garantie de support et d&rsquo;\u00e9volutivit\u00e9<\/li>\n<li>Confiance<\/li>\n<li>431 modules uniquement pour la gestion de fichiers<\/li>\n<li>Couverture fonctionnelle &ldquo;sans limite&rdquo;<\/li>\n<\/ul>\n<h2 id=\"cas-clients\">Cas clients<\/h2>\n<h3 id=\"intranet-collaboratif--m\u00e9decin-sans-fronti\u00e8re\">Intranet collaboratif : M\u00e9decin Sans Fronti\u00e8re<\/h3>\n<p>Probl\u00e8mes:<\/p>\n<ul>\n<li>Documents non structur\u00e9s<\/li>\n<li>Pas toujours \u00e0 jour<\/li>\n<\/ul>\n<p>Solutions:<\/p>\n<ul>\n<li>FacetApi<\/li>\n<li>LDAP<\/li>\n<\/ul>\n<h3 id=\"constructeur-automobile\">Constructeur automobile<\/h3>\n<p>Probl\u00e8mes:<\/p>\n<ul>\n<li>M\u00e9diath\u00e8que d\u00e9j\u00e0 en place<\/li>\n<li>Pas de partages<\/li>\n<li>Pas de visualisation<\/li>\n<\/ul>\n<p>Solutions:<\/p>\n<ul>\n<li>Feed + Feeds Xpath Parser pour les imports<\/li>\n<\/ul>\n<h3 id=\"pr\u00eat-\u00e0-porter\">Pr\u00eat \u00e0 porter<\/h3>\n<p>Probl\u00e8mes:<\/p>\n<ul>\n<li>Gestion manuelle des PDF<\/li>\n<li>Synchronisation journali\u00e8re de l&rsquo;ensemble des docs<\/li>\n<li>Uniquement accessible sur le r\u00e9seau d&rsquo;entreprise<\/li>\n<\/ul>\n<p>Solutions:<\/p>\n<ul>\n<li>Classement par taxo<\/li>\n<li>Module custom pour import automatique 1 fois \/ jour<\/li>\n<li>Versionning Drupal<\/li>\n<li>Apache Solr\n<ul>\n<li>6000 pdf \/ jour \u00e0 importer en 2 heures<\/li>\n<\/ul>\n<\/li>\n<li>Permissions g\u00e9r\u00e9es de fa\u00e7on hierarchique\n<ul>\n<li>Modules custom d&rsquo;import du LDAP avec la hierachie associ\u00e9<\/li>\n<\/ul>\n<\/li>\n<li>D\u00e9l\u00e9gations des permissions\n<ul>\n<li>Le salari\u00e9 C peut d\u00e9l\u00e9guer au salari\u00e9 B<\/li>\n<li>Module Custom<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h2 id=\"drupal-est-il-toujours-pertinant\">Drupal est-il toujours pertinant?<\/h2>\n<p>Toujours faire le pendant entre le niveau de custom \u00e0 faire vs autres solutions du march\u00e9<\/p>\n<p>Dans ce cas, penser \u00e0 faire collaborer Alfresco et Drupal via module CMIS<\/p>\n<p>On peut donc toujours utiliser Drupal en pensant bien les besoins \u00e0 l&rsquo;avance.<\/p>\n<h2 id=\"qa\">Q&amp;A<\/h2>\n<p>Niveau indexation: Les optimisations sont plus simples \u00e0 mettre en place cot\u00e9 Drupal que Alfresco<\/p>\n<p>Pas de modules contribu\u00e9s<\/p>\n"},{"title":"DrupalCamp Paris2013 - Livraison continue avec Drupal 7","link":"https:\/\/gastaud.io\/article\/drupalcamp\/paris2013\/livraison-continue\/","pubDate":"Fri, 21 Jun 2013 15:00:28 +0100","guid":"https:\/\/gastaud.io\/article\/drupalcamp\/paris2013\/livraison-continue\/","description":"<p>Prise de note lors de la conf\u00e9rence du DrupalCamp Paris 2013.<\/p>\n<h2 id=\"comment-faire-un-site-en-15-mois-sans-le-faire-\u00e0-larrache\">Comment faire un site en 1,5 mois sans le faire \u00e0 l&rsquo;arrache?<\/h2>\n<p>Faut le faire en agile!<\/p>\n<ul>\n<li>On s&rsquo;engage sur une date ou sur un p\u00e9rim\u00e8tre mais pas les 2<\/li>\n<li>D\u00e9finition du &ldquo;Minimum Viable Product&rdquo;<\/li>\n<\/ul>\n<p>Comment livrer le plus souvent possible:<\/p>\n<ul>\n<li>Tout automatis\u00e9<\/li>\n<\/ul>\n<h3 id=\"construction-dun-build-pipeline\">Construction d&rsquo;un build pipeline<\/h3>\n<p>Flux automatis\u00e9 jusqu&rsquo;\u00e0 la mise en production<\/p>\n<p>commit -&gt; Int\u00e9 continue -&gt; recette d\u00e9veloppeur -&gt; d\u00e9ploiement en int\u00e9gration -&gt; recette m\u00e9tier -&gt; d\u00e9ploiement en pr\u00e9 prod -&gt; validation finale -&gt; d\u00e9ploiement en production<\/p>\n<p>Tout a \u00e9t\u00e9 orchestr\u00e9 avec Jenkinks<\/p>\n<h4 id=\"quelle-base-de-donn\u00e9es-fait-foi\">Quelle base de donn\u00e9es fait foi?<\/h4>\n<p>La base de donn\u00e9es de developpement doit \u00eatre la base de r\u00e9f\u00e9rence<\/p>\n<h4 id=\"comment-transposer-la-configuration-dun-environnement-\u00e0-lautre\">Comment transposer la configuration d&rsquo;un environnement \u00e0 l&rsquo;autre?<\/h4>\n<ul>\n<li>Features<\/li>\n<li>hook_update<\/li>\n<\/ul>\n<p>1 feature par grand domaine, pas par r\u00e9elles fonctionnalit\u00e9s<\/p>\n<p>Toujours valider le d\u00e9ploiement (smoke tests)<\/p>\n<p>Lancement des tests d&rsquo;int\u00e9gration automatis\u00e9s -&gt; Dans le cadre du projet, seul le front a \u00e9t\u00e9 test\u00e9 automatiquement<\/p>\n<h4 id=\"et-la-qualit\u00e9-du-code\">Et la qualit\u00e9 du code?<\/h4>\n<ul>\n<li>Coder<\/li>\n<li>Lint<\/li>\n<li>phpmd<\/li>\n<li>phpcd<\/li>\n<\/ul>\n<h4 id=\"tests-de-performances\">Tests de performances<\/h4>\n<ul>\n<li>JMeter<\/li>\n<li>Gatling? en Scala<\/li>\n<\/ul>\n<h3 id=\"outils-utilis\u00e9s\">Outils utilis\u00e9s<\/h3>\n<ul>\n<li>D\u00e9ploiement en environnement de d\u00e9v -&gt; Capistrano<\/li>\n<li>Validation -&gt; PHPUnit + cURL<\/li>\n<\/ul>\n<h2 id=\"capistrano\">Capistrano<\/h2>\n<p>Permet de faire de d\u00e9ploiement parall\u00e9lis\u00e9 via un domaine sp\u00e9cifique\nIl permet de d\u00e9ployer les fichiers sp\u00e9cifiques aux devs et ne pas se pr\u00e9occuper des fichiers sp\u00e9cifiques \u00e0 l&rsquo;environnement.<\/p>\n<p>Permet de sauvegarder l&rsquo;ancienne version du code, partage des fichiers de config<\/p>\n<p>Quasi plus besoin d&rsquo;actions manuelles sur le serveur au final.<\/p>\n<h2 id=\"smoke-tests\">Smoke Tests<\/h2>\n<p>Permet de d\u00e9tecter les &ldquo;fuites&rdquo; cot\u00e9 Front<\/p>\n<ul>\n<li>Succession de curl\n<ul>\n<li>home en HTTP 200<\/li>\n<li>Pas de blocs d&rsquo;erreurs<\/li>\n<li>Pas de 404 sur les assets<\/li>\n<li>Blocs importants bien pr\u00e9sents<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<h2 id=\"comment-on-monitore\">Comment on monitore?<\/h2>\n<ul>\n<li>Buildwall Jenkins<\/li>\n<\/ul>\n<h2 id=\"et-les-tests-unitaires-dans-tout-\u00e7a\">Et les tests unitaires dans tout \u00e7a?<\/h2>\n<ul>\n<li>Uniquement sur le front dans le cadre de ce projet<\/li>\n<li>SimpleTest trop lent<\/li>\n<\/ul>\n<p>D\u00e9coupage pour la mise en production avec 2 taches<\/p>\n<ul>\n<li>1 pour le merge de DEV vers PROD only<\/li>\n<li>1 pour la mise en prod de la branch &ldquo;merg\u00e9e&rdquo;<\/li>\n<\/ul>\n<h2 id=\"coordonn\u00e9es-contact\">Coordonn\u00e9es contact<\/h2>\n<p>twitter: @arnaudhuon\nmail: <a href=\"mailto:ahuon@octo.com\">ahuon@octo.com<\/a><\/p>\n"},{"title":"DrupalCamp Paris2013 - Magnifiez votre backend","link":"https:\/\/gastaud.io\/article\/drupalcamp\/paris2013\/magnifiez-backend\/","pubDate":"Fri, 21 Jun 2013 01:14:28 +0100","guid":"https:\/\/gastaud.io\/article\/drupalcamp\/paris2013\/magnifiez-backend\/","description":"<h2 id=\"les-formulaires-peuvent-\u00eatre-tr\u00e8s-long\">Les formulaires peuvent \u00eatre tr\u00e8s long<\/h2>\n<p>Cr\u00e9ation d&rsquo;un concept =&gt; FormMode bas\u00e9 sur le concept des ViewsMode : <a href=\"https:\/\/drupal.org\/sandbox\/Artusamak\/1796634\">https:\/\/drupal.org\/sandbox\/Artusamak\/1796634<\/a><\/p>\n<p>Field Extra Widget : <a href=\"https:\/\/drupal.org\/project\/field_extrawidgets\">https:\/\/drupal.org\/project\/field_extrawidgets<\/a><\/p>\n<p>Views Megarow =&gt; Edition en ligne dans le backend : <a href=\"https:\/\/drupal.org\/project\/views_megarow\">https:\/\/drupal.org\/project\/views_megarow<\/a><\/p>\n<p>D\u00e9mo du Back Cartier<\/p>\n<p>Bundle Switcher : <a href=\"https:\/\/drupal.org\/project\/bundleswitcher\">https:\/\/drupal.org\/project\/bundleswitcher<\/a><\/p>\n<h2 id=\"cr\u00e9er-vos-propres-plugins-pour-lapi-de-views\">Cr\u00e9er vos propres plugins pour l&rsquo;api de views<\/h2>\n<p>Views Matrix<\/p>\n<h2 id=\"g\u00e9rer-les-transitions-de-workflow\">G\u00e9rer les transitions de workflow<\/h2>\n<p>Une 12ene de type d&rsquo;entit\u00e9\nProbl\u00e9matique avec les modules contrib qui ne g\u00e8rent que les nodes<\/p>\n<p>Statefield: <a href=\"https:\/\/drupal.org\/sandbox\/damz\/1550106\">https:\/\/drupal.org\/sandbox\/damz\/1550106<\/a><\/p>\n<p>(hors session: <a href=\"https:\/\/drupal.org\/sandbox\/andypost\/1114392\">https:\/\/drupal.org\/sandbox\/andypost\/1114392<\/a>)<\/p>\n<h2 id=\"cr\u00e9er-des-variantes-de-certaines-parties-du-contenus\">Cr\u00e9er des variantes de certaines parties du contenus<\/h2>\n<p>avec propri\u00e9t\u00e9s et champs surchargeables<\/p>\n<p>Objectif: Pas de duplication de nodes pour ce besoin<\/p>\n<p>Entity override : <a href=\"https:\/\/drupal.org\/project\/entity_override\">https:\/\/drupal.org\/project\/entity_override<\/a><\/p>\n<p>Utiliser aussi pour la traduction!!\nExportable dans des features\nEst capable de cr\u00e9er l&rsquo;entit\u00e9 cible.<\/p>\n<p>Module Tree + TreeField<\/p>\n<p>Device Management<\/p>\n<p>EntityBundlePlugin : <a href=\"https:\/\/drupal.org\/project\/entity_bundle_plugin\">https:\/\/drupal.org\/project\/entity_bundle_plugin<\/a> ?<\/p>\n<p>Synchronisation des contenus via Migration et XMLRPC<\/p>\n<p>Le backend a \u00e9t\u00e9 impl\u00e9ment\u00e9 comme un sous-projet \u00e0 part en tiers\n18 mois du 1er jour \u00e0 la mise en ligne 1 =&gt; Par contre les nouveaux sites sont simples \u00e0 mettre en ligne<\/p>\n<p>1 master pour piloter tous les sites, choisir si le site est Ecommerce ou non.\nLes frontaux sont totalement passifs.<\/p>\n"}]}}