Jenkins, sers-moi donc un Chtijug !

Nicolas Géraud était venu nous parler des pipelines il y déja deux ans.Est-ce que cette présentation allait être mieux ? En tout cas, elle est bien différente.

Petit point sponsoring de l’IUT sur la taxe d’apprentissage : les entreprises peuvent déclarer des organismes de formation bénéficiaires de leur taxe d’apprentissage. Ca peut avoir une influence sur le budget de ces organismes.

Et c’est parti !

Avec d’abord un petit sondage :

  • qui connait Jenkins ? A main levée, tout le monde dans la salle.
  • qui a déja migré vers Jenkins 2 ? Encore à main levée, pas grand monde.

Jenkins 2

Donc Jenkins 2 … est totalement compatible avec Jenkins 1. Et c’est bien, parce que la montée de version va être simple. Dans l’ensemble, les plugins sont compatibles.

Avec Jenkins 2, il y a aussi de nouveaux sites

  • http://jenkins.io qui est bien plus grand public
  • le project Voltron sert de plugin store avec une chouette présentation. Limite comme le google play store, en fait.

Il y a maintenant au premier démarrage un mot de passe à aller chercher dans les logs de la machine. Ca va permettre de sécuriser le serveur Jenkins avec un admin par défaut. Evidement, on peut toujours avoir un Jenkins non sécurisé, mais il faut le vouloir. Ce qui change pas mal de Jenkins 1 qui, par défaut, n’était pas sécurisé. En bonus, une liste de plugins par défaut est disponible, pour faire une première personnalisation de Jenkins.

Et ensuite, on arrive aux fameux pipeline-as-code.

Pipeline as code

Un job est défini par un Jenkinsfile, écrit dans un DSL Groovy, qu’on peut stocker dans son SCM. L’avantage, c’est aussi de permettre d’utiliser les structures de contrôle de Groovy pour personnaliser le job.

Et ensuite

Il y a tout un tas de nouveaux trucs qui ne vont pas tarder à arriver

BlueOcean (déja disponible en pas beta, mais pas release non plus)

La fameuse nouvelle interface graphique pour Jenkins. Les captures sont très chouettes !

Franchement, l’affichage des pipelines est très chouette. Si j’avais eu ça chez Perigee, j’aurais pu montrer un vraiment très beau pipeline. Le plus incroyable, c’est quand même de pouvoir faire « pousser » un pipeline graphiquement. Il faut voir l’écran pour le croire, je trouve.

Pipeline déclaratif

En bonus, ça permet à des non-développeurs de modifier le build, ce qui n’est pas une si mauvaise idée, puisque le code exécuté est toujours en SCM. En fait, le pipeline généré n’est pas exactement identique à celui qu’un développeur écrirait : c’est un pipeline déclaratif.

D’une façon amusante, le speaker décrit la syntaxe d’un DSL Groovy comme « à mi-chemin entre YAML et Groovy ». Guillaume Laforge et tous ses amis pleurent des larmes de sang en entendant ça. Parce que quand on regarde un DSL Groovy, comme par exemple le MarkupBuilder, on est exactement dans la syntaxe visible à l’écran. Bon, je ne vais pas en vouloir au speaker : il connaît sans doute mieux Jenkins que je connais Groovy.

Back-end storage

Manifestement, l’objectif est de découpler le fonctionnement de Jenkins et le stockage des configurations d’exécution et des résultats. C’est une chouette idée, mais pas vraiment assistée par les capacités des plugins à écrire eux-mêmes des fichiers.

Donc Jenkins 2, c’est ?

Jenkins 2, c’est mieux, et orienté sur le continuous delivery. Et pas le continuous deployment. Autrement dit, l’équipe de cloudbees ne vise pas trop à utiliser Jenkins pour le déploiement en prod.

Quelques questions

Si les pipelines sont déclaratifs, comme maven ou gradle, pourquoi ne pas réutiliser leur outillage ?

Jenkins est utilisé pour faire le delivery, ce que ne font pas ces outils. En un sens, je comprend cette réponse : le scope fonctionnel est différent, et ce serait assez curieux de dire « on peut faire du gradle pour le déploiement ». Et puis, Cloudbees et Gradle inc ne vont pas fusionner demain.

Mais quand même, en transformant les jobs en bouts de code, je trouve que Cloudbees fait rentrer Jenkins dans une espace de continuum de build qui va de mon poste jusqu’aux artefacts déployables en prod.

Comment se compare l’API de pipeline-as-code et de job-dsl ?

L’inspiration initiale de pipeline-as-code était buildflow, mais ça a vite divergé.

Et la démo

  • Premier truc cool : les plugins installés sont toujours installés à la dernière version.
  • Deuxième truc cool : BlueOcean est déjà disponible comme plugin, et donc installable.
  • Troisième truc cool : le travail d’administrateur Jenkins s’arrête, pour les jobs, à créer les jobs et les connecter à des SCM. Tout le reste vient du Jenkinsfile.

Et donc, on passe à la création du Jenkinsfile. Et là, la structure des DSL Groovy réapparaît vraiment, et c’est bien.

  • Quatrième truc cool : les commandes checkout de SCM utilisent la révision du Jenkinsfilecomme référence. Par conséquent, l’enchaînement d’étapes se passera beaucoup mieux que dans Jenkins 1, puisqu’il n’y aura pas de révision à passer. Par contre, curieusement, le DSL ne semble pas contenir de commande maven, puisque notre speaker exécute les commandes maven via un sh "mvn clean package". Arf … apparement, le plugin exécutant maven est « mauvais ».
  • Cinquième truc cool : l’interface de Jenkins inclut un générateurs de snippets façon vieux wizards qui permet d’apprendre très rapidement le DSL des pipelines.
  • Sixième truc cool : l’affichage des pipelines en cours est vraiment très sympa, avec des logs par noeud du pipeline affichés en temps réel.
  • Septième truc cool : le DSL inclut une notion de stashes, valable uniquement pour la durée du pipeline, qui permet donc de ne pas déployer dans Nexus les artefacts temporaires.
  • Premier truc pas cool : le pipeline est toujours exécuté sur le master. Du coup, la montée en charge pourrait mal se passer.
  • Huitième truc cool : pour les pipelines déclaratifs, il est possible de demander à Jenkins d’analyser la syntaxe.
  • Deuxième truc pas cool : on peut relancer un pipeline à partir d’une étape uniquement dans la version commerciale de Jenkins. C’est moins bien … mais je comprend tout à fait.
  • Neuvième truc cool : il est possible de créer des morceaux de code partagés entre pipelines.
  • Dixième truc cool : les milestones permettent d’éviter de lancer plusieurs builds concurrents sur une ressource critique.

Et si vous voulez voir le Jenkinsfile utilisé pour ce live-coding, il est disponible sur github.

D’autres questions

Comment tester un pipeline ?

Eh bien, on ne peut pas : il faut le lancer dans une instance de Jenkins. Autrement dit, c’est le genre de truc où il faut une instance de test de Jenkins. Et mon collègue me souffle à l’oreille qu’on peut très bien utiliser l’annotation @StaticCompile de Groovy pour vérifier la syntaxe autant que possible (parce que @StaticCompile, sur un DSL, il doit bien s’amuser, tiens).

Conclusion

J’ai quand même hâte de voir les projets passer au moins aux pipelines as code, histoire d’avoir dans mon SCM, à côté de mon pom.xml, le Jenkinsfile qui va bien. Et histoire aussi d’ajouter un peu de Groovy dans tout ça !

Publicités

J’ai appris le Python … et un truc sur Groovy

Ces derniers temps, j’ai dû bosser sur un outil de déploiement écrit en Python. Et donc j’ai fait du Python. Pendant longtemps, j’ai cru que c’était un langage difficilement compréhensible. Cela dit, pendant ce temps-là, j’ai aussi ajouté au Java le Javascript (et le Flash, mais c’était aussi déplaisant que … disons … se faire arracher les dents par Davy Jones – si vous ne voyez pas qui ça peut être, pensez au mec à la tête de poulpe dans Pirates des Caraïbes). Et comme j’avais également fait un peu de Ruby pour rire … Disons que le Python ne m’a finalement pas paru si difficilement appréhendable.

En fait, en pratique, j’ai trouvé le passage au Python assez facile, à la nuance notable du système d’imports qui engendre le même genre d’incompréhension que le CLASSPATH pouvait en engendrer chez moi il y a plus d’une décennie. A part ces imports, ces modules et ces packages, Python est somme toute très agréable. Et même cette histoire de définition des blocs par l’indentation (et donc sans aucune accolade) ne me gêne en fait pas du tout.

Mieux, comme je fais du Python dans Eclipse avec PyDev, j’ai un truc qui me manque en Groovy : un débuggeur.Parce que l’un des plus mauvais côtés de la prise de contrôle de Groovy par Spring a été l’intégration complète, totale et irrémédiable du plugin Groovy pour eclipse dans STS. Du coup, pour débugger du Groovy, il faut STS … Et je trouve ça un peu lourd. Enfin, tout ça, c’est ce que j’ai compris.

Cela dit, le plus gros inconvénient de tout ça, c’est que quand je commence un script Groovy, il grossit, grossit, et à un moment, je tombe sur un bug que je n’arrive pas à corriger avec un ou deux assert ou des ajouts de log. Et c’est le moment où je réécris tout en Java, perdant du temps et du flux.

Du coup, si je trouvais un bon plugin me permettant de faire du debug en groovy … eh bien je pourrais maintenir de bien plus gros bouts de code Groovy.

Les wikis ont changé, et c’est bien !

Depuis des années, dans les projets auxquels je participais, on utilise des wikis « simples », comme typiquement mediawiki. C’était pratique, mais dès qu’on sortait des pages de texte, on était embêté.
Il fallait alors installer, si on en avait le courage, un logiciel de forum, ou de blog interne, ou tant d’autres choses.Honnêtement, c’était assez peu pratique. Suffisamment, d’ailleurs, pour donner aux utilisateurs peu intéressés comme moi l’impression d’un monde déjà fermé.Et puis j’ai du jeter un oeil, pour différentes raisons, à XWiki (la plus importante étant sans doute l’interview donnée par les patrons de l’entreprise éponyme aux castcodeurs). Et là, le choc :

  • Joli
  • Avec un éditeur WYSIWYG
  • Extensible par une multitude d’applications (forum, gestionnaire de tâches, outil de microblogging, …)
  • Scriptable en Velocity et en Groovy !
  • Installable facilement dans tous les environnements

Bref, une révolution.

Il y a certes deux ou trois choses gênantes, comme le manque de certains imports/exports, mais rien de dramatique à mon sens.

Du coup, forcément, je vais tester plus de choses avec cet outil aussi puissant que bien fichu.

Groovy it, dude !

Je vais faire un peu de live-blogging d’un problème pénible.

Ce matin, je devais faire un merge.

Et curieusement, ce merge foirait, à cause d’une erreur … difficilement compréhensible


Working copy and merge source not ready for reintegration
svn: Reintegrate can only be used if revisions 9043 through 9581 were previously merged from http://achille.perigee.fr/svn16/autocat/autocat-java/branches/2.1 to the reintegrate source, but this is not the case:
autocat-java/branches/2.0-item-196-cc-html5

J’avais trouvé (via Stackoverflow évidement) une méthode manuelle pour corriger le problème. Et j’étais en train de me préparer à faire tout ça à la main en quelques heures.

Et puis je me suis dit que c’est quand même con de corriger une cinquantaine de fichiers à la main quand je peux scripter ça.

Et aussitôt, j’ai lancé ma meilleure Groovy Console pour y écrire ce script, qui fait exactement ce que mentionne la solution : faire un svn propdel sur chaque fichier mentionné.

Et dix minutes plus tard …

Le plus compliqué pour moi a été de me décider : est-ce que j’utilise svnant, ou est-ce que je fais directement de l’exécution shell de « svn » ? Eh bien ce qui m’a poussé vers la deuxième solution,c ‘est que svnant n’est disponible que par téléchargement direct, et que je n’avais pas le courage de voir comment Groovy Grapes allait devoir être configuré pour ça …

En tout cas, ça confirme encore une fois mon opinion sur le fait que Groovy est définitvement LE langage de la JVM pour scripter dans tous les environements.

Extraire le majorVersion/minorVersion d’un projet maven

Pour un projet sur lequel je bossais, j’avais besoin de récupérer les composants du numéro de version de mon projet maven sous forme numérique (et pas chaîne de caractère). J’ai bien essayé d’utiliser le properties-maven-plugin, mais ça marchait pas (j’ai même demandé à StackOverflow). Mais aucune solution ne correspondait à mon problème. J’ai donc dégainé le gmaven-plugin et … voilà !

			<plugin>
				<groupId>org.codehaus.gmaven</groupId>
				<artifactId>gmaven-plugin</artifactId>
				<executions>
					<execution>
						<id>Compute portbase and admin port for Jenkins domain</id>
						<phase>validate</phase>
						<goals>
							<goal>execute</goal>
						</goals>
						<configuration>
							<classpath>
								<element>
									<groupId>org.apache.maven</groupId>
									<artifactId>maven-artifact</artifactId>
									<version>3.0.3</version>
								</element>
							</classpath>
							<providerSelection>${gmaven.provider.version}</providerSelection>
							<!-- small script used to update some properties according to version -->
							<source>
								<![CDATA[
import org.apache.maven.artifact.versioning.*

/* et voila, c'est tout ! */
def parsed = new DefaultArtifactVersion(project.version)

										]]>
							</source>
						</configuration>
					</execution>
				</executions>
			</plugin>

Oui, bon, j’aurais préféré mettre ça dans un gist, mais apparement WordPress.com ne fournit pas ça.

Confession intime

J'ai une confession à vous faire.
J'ai écrit il y a quelques temps déja des petits scripts en Groovy pour exporter mes données de Posterous et Goodreads. Au début, ça
me paraissait facile. Et puis j'ai voulu rajouter des fonctionnalités : exporter plus de trucs, exporter dans plus de formats … Et ce qui devait m'arriver m'est arrivé. un jour (je crois que c'était l'année dernière au mois d'octobre) j'en ai eu marre.
J'en ai eu marre de la limitation débile que je m'imposais de tout mettre dans le même fichier.
J'en ai eu marre également de ne pas pouvoir débugger, d'être coincé dans une version spécifique de http-builder, et de ne pas vraiment pouvoir faire ce que je voulais.
Et puis je commencais à me perdre dans mes closures.
J'ai donc longuement réfléchi au problème, et pris la décision qui s'imposait.
J'arrête ces scripts en groovy. Ou plutôt, en m'appuyant sur cette expérience, je fais un reboot et je redémarre les projets dans mon langage de prédilection : Java.
Je ne crois pas que Groovy soit en cause, en fait. Mais, commeles développeurs de free.fr qui sont passés de JSF à PHP en deux jours, j'ai ma zone de confort. Et il semble que cette zone de confort soit bien définie (autrement dit je commence à me scléroser) : Java, Maven, et Eclipse de préférence (mais j'y reviens plus bas, pas de panique). Donc, je retourne dans mes pantoufles, et je m'y remets.
Avec toutefois une nuance. je me suis dit que tant qu'à faire un aspirateur de site web, autant avoir une prétention, même minime, à l'universalité. Je développe donc maintenant une espèce de "truc" à laquelle je donne un fichier de configuration (avec les différents login/password/clés d'accès aux APIs), et qui, pour chaque site "connu", va aspirer les éléments intéressants. Un truc somme toute basique.
Tellement basique, d'ailleurs, que le truc avec lequel j'ai eu pour l'instant le plus de diffculités à comprendre est le mécanisme d'authentification basique avec http-client. Pour le reste, laissez-moi juste vous dire que Jackson est sacrément balaize pour le mapping JSON/objet. En revanche, une chose me chagrine : il semble que personne n'ait pensé à utiliser les pouvoirs "magiques" des proxies Java pour écrire un accès à une API web sous forme d'une interface .. quelque chose comme ça :

Avec ça, j'écris vite fait les parties utiles de l'API du site, et après mon code devient hyper-facile, vous ne trouvez pas ?
Bon, je crois que je vais le faire tellement c'est simple.
En revanche, le truc qui em déçoit beaucoup, c'est GitHub. je croyais que c'était facile d'y créer un dépôt pour y placer mon code, mais en fait non : il faut à chaque fois passer par cette saleté du millénaire précédent de ligne de commande pour créer la branche originale avec msysgit. Parce qu'évidement (ou tout au moins d'après ma compréhension du truc) EGit n'arrive pas à faire le premier push sur GitHub. Et ça, franchement, ça me donne envie d'abandonner ce "truc" hyper-hype pour revenir à un simple Subversion (chez origo ou google code). Notez bien que je n'ai pas encore essayé avec NetBeans.
Parce que oui, en ce moment, j'utilise un peu NetBeans. Je ne comprend pas tout, et l'absence des pespectives d'Eclipse me manque beaucoup, mais l'un dans l'autre il y a de bonnes idées dans cet IDE (par exemple dans les refactorings comme le pull up qui permet de créer automatiquement les déclarations de méthode abstraite dans la super-classes).
Bon, ben avec tout ça, normalement, je devrais avoir vite codé l'export, non ? NON ? non.
Parce qu'il y a quand même du boulot : il faut récupérer plein de données en accédant à plein d'APIs, faire en sorte que les URLs soient toutes réécrites pour ne pas retourner sur les sites initiaux, télécharger les pièces jointes, et finallement produire du contenu (mais pour ça, je crois bien que ej vais passer par un quelconque (lire StringTemplate) moteur de templates Java. Mais, l'un dans l'autre, je suis confiant, même si on verra ce que l'avenir me réserve à ce sujet.

Transformer un projet ant en artefact maven

Grâce à mon collègue Alexandre, une petite astuce qui pourra un jour vous sauver la vie (moi, en tout cas, elle aurait pu me la sauver).
Si vous avez déjà dû récupérer un JAR dans la nature pour le mettre dans un projet maven, vous savez que ça peut être galère.
Toutefois, si ce projet est compilé avec ant, tout n'est pas perdu. Eric Hauser a écrit ant2maven qui, à partir du build.xml et d'un peu de configuration, va extraire les dépendances et construire un pom à peu près correct.
Et, pour vérifier que le POM est bien minimal (c'est-à-dire exprime des dépendances canoniques), maintenant que m2eclipse n'intègre plus de dependency hierarchy, vous pouvez générer un graphe des dépendances avec depgraph (parce que bon, dependency:tree, c'est pas toujours très lisible).

Utiliser gmaven pour Groovy 1.8

Juste histoire d’être sûr de s’en souvenir plus tard, voici le pété à écrire dans son pom.xml pour pouvoir utiliser du Groovy 1.8 avec GMaven :
Sans le exclude, vous aurez l’impression que ça marche, jusqu’à ce qu’il vous dise à peu près
java.lang.NoClassDefFoundError: org/apache/tools/ant/BuildException
at org.codehaus.gmaven.runtime.v1_7.ScriptExecutorFeature$ScriptExecutorImpl.createAntBuilder(ScriptExecutorFeature.java:87)
at org.codehaus.gmaven.runtime.v1_7.ScriptExecutorFeature$ScriptExecutorImpl.createMagicAttribute(ScriptExecutorFeature.java:105)
at org.codehaus.gmaven.runtime.support.ScriptExecutorSupport.applyContext(ScriptExecutorSupport.java:108)
at org.codehaus.gmaven.runtime.support.ScriptExecutorSupport.execute(ScriptExecutorSupport.java:69)
at org.codehaus.gmaven.plugin.execute.ExecuteMojo.process(ExecuteMojo.java:239)
at org.codehaus.gmaven.plugin.ComponentMojoSupport.doExecute(ComponentMojoSupport.java:60)
at org.codehaus.gmaven.plugin.MojoSupport.execute(MojoSupport.java:69)
at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:101)
Ce qui, il faut le reconnaître, n’est pas vraiment une façon sympathique de vous dire qu’il vous manque la bonne version de groovy-all.

Transformer un CLASSPATH en liste de dépendances maven ?

Aujourd’hui, j’ai eu un petit truc pénible à faire.
J’avais un jar venu … de l’extérieur … que je souhaitais mettre dans mon repository maven d’entreprise. Bien sûr, les choses auraient été bien plus simple si un artefact avait été disponible, mais hélas, ça n’était pas le cas (apparemment pour des raisons commerciales).
Donc, j’avais ce jar , dans un dossier lib, accompagné d’une palanquée d’autres jars de provenances diverses (jetty, log4j, jUnit, Jena, …).
Coup de bol, comme il s’agit d’un jar exécutable (ou à peu prés), celui-ci disposait d’un MANIFEST.MF complet. En particulier, il disposait d’une entrée Class-Path, listant donc les dépendances runtime de ce jar. Il me fallait donc les extraire pour les transformer en dépendances maven. Bon, comme, en fait, je devais faire ça pour un paquet de jars, j’ai écrit un petit script Groovy pour automatiser tout ça :

 

 

En bonus, pour les cas où on sait à quoi correspond un jar (par exemple, si je sais que jena/jena.jar correspond à l’artefact com.hp.hpl.jena:jena:2.6.3, on peut ajouter ça dans un fichier de propriétés Java (mais en XML, parce que les caractères « : » sont interprétés – curieusement – comme des séparateurs clé/valeur). Un fichier qui aura donc cette tête-là :

 

 

Vous allez bien sûr me demander si il ya des feintes dans ce paquet épais de code Groovy.
Pas trop, mis à part une bon sang d’expression régulière. Oui, je sais, maintenant, j’ai deux problèmes. Cela dit, l’avantage avec ce genre de truc, c’est que le temps de développement de l’expression régulière (grâce à un testeur en ligne) est en fait bien plus long que son temps de mise en oeuvre dans le code.

Posterous backup script v2

As a follow-up to Posterous official blog message concerning most wanted feature, here is great (or maybe not) news for you, users of posterous backup script.
I’ve just released v2.1 of Posterous backup script.
Why such a tremendous version jump ?
Because many things changed in that little script.

New and noteworthy
  • First, the script now uses Posterous API, thanks to the greatness of Groovy HTTP Builder.
  • What’s more, I now export not only posts, but also tags and pages, leading to an updated organization : each of your posterous site now have, under its very own folder, the following architecture
  +— pages
  |      +— list of your user pages
  +— tags
  |      +— list of your tags, for each the generated page contains all associated posts
  +— posts
  |      +— list of your all your posts
  +— images
  |      +— list of all images used in both your posts and page. For each, both thumbnail and full size image are downloaded
  +— audio_files
  |      +— list of all audio used in both your posts and page.
  +— videos
  |      +— list of all videos used in both your posts and page.
  +— posts.html           # a page listing all your posts
  +— pages.html           # a page listing all your pages
  +— tags.html            # a page listing all your tags

Actually, the links are not exported. Do you want me to export them ? If so, how ?
  • Concerning page content, even if the layout is far simpler than elegant layouts found on our beloved site, I tried to respect, as far as possible, microformats recomendations. As a consequence, posts are written using hatom hentry, users infos (in posts and comments) are written using hcard, which theorically makes CSS skinning easy (even if I’ve not yet thought about it, I must confess)
The mandatory how-to

Concerning the usage guide of that script, nothing has changed (more or less) since initial release of v1 : you must still install both Java and Groovy. Since I’ve kept v1 of that script, you now download it at http://dl.dropbox.com/u/2753331/posterous_2.groovy and then run it using Groovy using either standard command line :

groovy posterous_2.groovy

which will show you the available options :

This is posterous export script v 2.1
2.1 is mainly due to the use of posterous api, instead of old-style http queries
You like that script ? You already use flattr ? Please go to http://flattr.com/thing/54243/Posterous-backup-script to sh
ow your appreciation
error: Missing required options: u, p, o
usage: groovy posterous.groovy
-f,–forceRewrite When present, all sites are regenerated. Existing
data on disk is totally wiped out
-h,–help provides full help and usage information
-o,–output <arg> An eventually existing output folder, where all
data will be output. Beware, if some data exists in
that folder, it may be overwritten.
-p,–password <arg> Unfortunatly one have to give its password to this
little script
-s,–separeMedia Separates media from posts directory. When set, all
medias are copied in subfolders of output folders
named as posts they’re associated with. This option
is by request of Eli Weinberg, with my best wishes.
-u,–username <arg> Sets posterous mail address here

Besides, you may have notice some options changes : the -downloadThumbnails options has been removed, and -forceRewrite option to ensure previous files were removed has been added.
As for version 1, you can still be sure to always launch latest script version but be sure to change used URL, otherwise result won’t necessary meet your expectations.

Troubleshooting guide ?

Naaah, there does not seems to be that much issues for that over-simple script written on the shoulders of giants.
Anway, if you really want to ask, this script is slow, and it’s a known problem (but there is nothing I can do) (except re-writing it in Java with great usage of multi-threading for parallelizing posts and medias downloads … well, Ok, I’ve thought about that, but I won’t implement it before … v2.2).