Cet article a pour but de couvrir tout le processus d'édition d'une gem. Il sera découpé en deux grandes parties. Tout d'abord ce qu'il faut savoir à minima pour créer sa propre gem (honnêtement, pas grand-chose) pour que vous puissiez créer la vôtre sans souci. Je vais ensuite donner mes recommandations (subjectives) quant aux outils et bonnes pratiques concernant l'écriture et le maintien de cette gem.
Savoir minimal
Tout d'abord on va voir ce qu'il faut savoir à minima. Ce "chapitre" va essayer de couvrir tout ce qui rend différent une gem d'un script ruby qu'on écrit dans un coin.
Création d'une nouvelle gem
Tout d'abord, comment on "crée" une gem ? La bonne question est, qu'est-ce qu'est le "minimal" pour avoir une gem ?
- Un dossier duquel on va pouvoir require des fichiers ruby
- Un fichier en
.gemspec
Donc par rapport à un script normal, tout ce qui change c'est le fichier.gemspec
. On va détailler ses différentes sections plus bas dans cet article.
Cependant on peut utiliser un utilitaire qui va nous créer un squelette un peu moins "vide". Bundler que vous avez tous déjà utilisé permet de créer un bon premier squelette.
La commande que j'utilise la plupart du temps est :
bundle gem nom_de_ma_gem --coc --mit --test=rspec
--coc
va créer un fichier de code of conduct.--mit
va ajouter le fichier de license de [MIT].--test=rspec
va instancier les quelques fichiers par défaut derspec
.
Dans la section suivante on va détailler les différentes parties de cette arborescence.
Arborescence par défaut
Voilà l'arborescence crée par bundler :
CODE_OF_CONDUCT.md
contient un coc minimaliste.LICENSE.txt
va contenir la license MIT pour votre gem.Rakefile
va contenir les taches de déploiement d'une gem.lib/ma_belle_gem.rb
va contenir le fichier vide qui contiendra plus tard le code d'initialisation de votre gem. Ce fichier sera automatiquement require quand quelqu'un ajoutera votre gem à son Gemfile.lib/ma_belle_gem/version.rb
va définir la constanteMaBelleGem::VERSION
qui contiendra le numéro de version courant de votre gem.- Le dossier
spec
va contenir les quelques fichiers classiques de tests en rspec. - Le dossier
bin
va contenir les scripts utiles au développement.setup
va servir à setup l'environment de développement (en général justebundle install
).console
va lancer une console ruby avec votre gem préchargée.
Le fichier .gemspec
Le fichier .gemspec
est particulier et possède de nombreuses sections plus-ou-moins optionnelles. On va décortiquer celui qui est généré par bundler
, mais vous pouvez aller voir ici pour la documentation complète.
# Charge votre fichier qui défini la version de votre gem dans la constante MaBelleGem::VERSION.
require_relative 'lib/ma_belle_gem/version'
Gem::Specification.new do |spec|
# Le nom de votre gem (Obligatoire). Ce nom sera utilisé pour:
# * Le nom de votre gem dans rubygems;
# * Le nom du fihcier qui sera chargé par défaut (lib/ma_belle_gem.rb).
spec.name = "ma_belle_gem"
# Votre numéro de version. Sa valeur sera utilisée dans rubygems. (Obligatoire)
spec.version = MaBelleGem::VERSION
# La liste des auteurs de la gem (Obligatoire d'avoir au moins un auteur)
spec.authors = ["Denis <Zaratan> Pasin"]
# La liste des emails des auteurs de la gem.
# Attention cette information est publique sur rubygems. (Obligatoire d'avoir au moins un email)
spec.email = ["zaratan@hey.com"]
# summary doit contenir un rapide résumé de ce que fait votre gem.
# Je ne suis pas sur de où cette information est utilisée. (Obligatoire)
spec.summary = %q{TODO: Write a short summary, because RubyGems requires one.}
# description doit contenir une "longue" description de ce à quoi sert votre gem.
# Cette information sera affichée tel quelle sur rubygems. (Optionel (mais pas vraiment en pratique))
spec.description = %q{TODO: Write a longer description or delete this line.}
# En général la homepage sera votre repo github a moins que vous ne créiez un site dédié à votre gem.
spec.homepage = "TODO: Put your gem's website or public repo URL here."
# Le shortname de la license
spec.license = "MIT"
# La version de Ruby minimale.
# Je vous conseille de set cette version à la dernière version de Ruby qui n'est pas en EOL (End Of Life).
# Voir: https://www.ruby-lang.org/en/downloads/
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
# Section optionnelle. Si ce n'est pas scécifié c'est rubygems.org (en général je supprime cette section).
spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
spec.metadata["homepage_uri"] = spec.homepage
# L'url de l'emplacement de votre code
spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
# L'url qui permet d'acceder à votre changelog. Cette section est optionnelle. Si vous n'avez pas de changelog supprimez la.
spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
# La section files va permettre de
# supprimer des fichier qui ne doivent pas être inclus dans la release de votre gem (genre les tests).
# En général cette section reste inchangée.
# Specify which files should be added to the gem when it is released.
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
end
# Le dossier dans lequel les executable seront mis
# (par exemple `bundle` pour la gem de bundler et `rails` pour la gem de rails)
# Ce dossier peut ne pas exister.
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
# Quels dossier seront "require" automatiquement au démarrage de votre gem.
# Pareil en général, pas de changement ici.
spec.require_paths = ["lib"]
end
Les deux types de commandes qui seront très souvent présentes dans un gemspec sont :
spec.add_development_dependency
Va ajouter une gem qui ne sera installée que dans le contexte du développement de la gem (rspec
par exemple). La syntaxe est la même que dans un Gemfile :
spec.add_development_dependency "rubocop", "> 0.58"
spec.add_dependency
Va ajouter une dépendance directe de votre gem. Celle-ci sera installée par tous les projets qui utilisent votre gem.
Pareil, même syntaxe que dans un Gemfile :
spec.add_dependency "activesupport", "> 2.0"
Le déploiement
Une fois que votre gemspec est défini on peut déployer sur rubygems.org et rentre notre gem disponible au monde.
Il faut absolument avoir un compte créé sur rubygems.org.
Ensuite, si vous avez utilisé bundler
pour créer votre gem. Il suffit de faire : rake release
dans un terminal et Rake s'occupera pour vous de la publication.
À savoir :
- Créer un package avec le code de votre gem.
- Créer un tag git avec votre numéro de version.
- Faire un
git push
avec vos commits et vos tags. - Publier votre gem sur rubygems (Il est possible/probable que vous deviez entrer votre username/password ici).
Voilà. Après quelques minutes, n'importe qui pourra utiliser votre gem.
Des outils pratiques
Dans la partie précédente on a pu voir ce qu’il fallait pour avoir une gem minimale. On va maintenant passer en revue quelques outils (gems) que j’inclus dans quasiment toutes les gems que j’utilise. Notez que quasiment toutes ces recommandations s’appliqueraient aussi à un projet Rails. Ces outils ne sont effectivement pas du tout liés au processus de création d’une gem.
Rubocop
Commençons par Rubocop que vous devez tous plus ou moins connaitre. Pour moi, c’est plus ou moins obligatoire de l’utiliser dans un projet (Ou un “équivalent” genre reek).
Utiliser rubocop permet plusieurs choses :
- C’est une façon de normaliser le style du code à travers un projet. Ça permet d’ouvrir n’importe quel fichier et d’avoir un “style” consistant.
- Ça règle toute discussion futile autour du style dans une PR. Si le linter ne force pas un style, alors cette façon de coder est valide.
- Ça permet de s’assurer que certaines règles de performance sont respectées, que certaines syntaxes dangereuses ne sont pas utilisées ou encore que certaine erreurs évitables le sont.
Pour ajouter Rubocop à notre gem on ajoute dans le .gemspec
:
spec.add_development_dependency "rubocop"
spec.add_development_dependency "rubocop-performance"
Voici un exemple de configuration que j’utilise dans presque toutes mes gems :
.rubocop.yml# Utiliser une version (peu) édulcorée des règles de base de rubocop
inherit_from:
- http://relaxed.ruby.style/rubocop.yml
# Ajoute des règles de performance aux règles de base de Rubocop
require:
- rubocop-performance
AllCops:
# Chaque version de rubocop ajoute de nouvelles règles.
# Ceci les active par défaut.
NewCops: enable
DisplayStyleGuide: true
DisplayCopNames: true
Exclude:
- "bin/*"
- "vendor/**/*"
# Certains fichiers sont de gigantesques block.
# Ne pas les compter.
Metrics/BlockLength:
Exclude:
- "spec/**/*.rb"
- "Guardfile"
- "vendor/bundle"
- "*.gemspec"
# Les règles qui vont suivre sont des règles de style
# permettant de split les lignes.
Layout/DotPosition:
Enabled: true
EnforcedStyle: trailing
Style/TrailingCommaInArrayLiteral:
Enabled: true
EnforcedStyleForMultiline: comma
Style/TrailingCommaInHashLiteral:
Enabled: true
EnforcedStyleForMultiline: comma
Layout/MultilineArrayLineBreaks:
Enabled: true
Layout/MultilineHashKeyLineBreaks:
Enabled: true
Layout/MultilineMethodArgumentLineBreaks:
Enabled: true
Layout/FirstArrayElementLineBreak:
Enabled: true
Layout/FirstHashElementLineBreak:
Enabled: true
Layout/FirstMethodArgumentLineBreak:
Enabled: true
Layout/MultilineAssignmentLayout:
Enabled: true
# Ajoute une limite maximum à la longueur d'une ligne
Layout/LineLength:
Enabled: true
Max: 120
# Cette option fait en sorte que Rubocop essaye d'ajouter
# des retours à la ligne là où il faut.
AutoCorrect: true
Exclude:
- Gemfile
- Guardfile
Bonus : Autocorrect rapide
Il arrive souvent qu’on veuille lancer Rubocop sur l’ensemble du projet et corriger chaque fichier qui a un problème. Rubocop à deux options mutuellement exclusives :
-P
permet de vérifier les fichiers en parallèle et donc de profiter des nombreux cœurs de nos CPU modernes-A
permet de corriger les fichiers automatiquement.
Comme on ne peut pas utiliser les deux options en même temps on va définir un alias qui utilise automatiquement le résultat de du mode rapide (-P
) comme paramètre du mode (-A
) et ne corriger (lentement) que les fichiers qui ont des problèmes.
alias fastcop="rubocop -P -f fi | xargs rubocop -A"
Solargraph
Solargraph est une gem qui permet d’ajouter un “Language Server” à votre projet Ruby. Qu’est-ce qu’un language server me direz-vous ? C’est un utilitaire qui va tourner en tache de fond et permettre a votre IDE de lui poser des questions telles que : “Quelle est la documentation de cette méthode ?” ou “Quelles sont les méthodes disponibles sur cet classe/objet ?” ou encore “Quels sont les arguments de cette méthode ?”. Ça permet ensuite à notre IDE de fournir une bien meilleure autocomplétion.
On l’ajoute à notre projet de la manière suivante :
spec.add_development_dependency "solargraph"
On peut ensuite demander à solargraph de télécharger les documentations disponibles.
# Télécharge la documentation de tel ruby
solargraph download-core 2.7.2
# Compile la documentation de toutes les gems
# utilisées dans le projet
solargraph bundle
Chaque IDE a sa propre façon de se connecter à solargraph. Le comment lier son IDE a solargraph sera laissé en exercice au lecteur.
Bonus VS Code configuration
Je vais quand même fournir quelques options de configuration que j’utilise pour solargraph à l’intérieur de VS Code.
"solargraph.diagnostics": true,
"solargraph.formatting": true,
"solargraph.completion": true,
"solargraph.folding": true,
"solargraph.hover": true,
Je laisse à solargraph le travail d’appeler Rubocop avec formatting
et diagnostics
. Ou de savoir comment “replier” le code avec folding
. L’autocomplétion s’active avec completion
. Et hover
permet d’afficher les informations des méthodes en cas de hover.
Bundle audit et Bundle outdated
Des fois on veut pouvoir vérifier directement en console l’état de nos dépendances. Pour ce faire, on va utiliser deux outils.
bundle outdated
Cette commande vient de base avec bundler et lui permet de lister toutes les gems de notre Gemfile.lock
qui ne sont pas à jour. De plus ça nous indique pour chaque gem
quelle est la version à jour vers laquelle update. Il ne reste alors plus qu’à faire bundle update
bundle audit
Bundle audit est une extension de bundler qui permet de vérifier si une de nos gems à une faille de sécurité connue (CVE). On utilise la gem de cette manière :
On l’ajoute au .gemspec
:
spec.add_development_dependency "bundle-audit"
Puis :
# Met à jour ta liste des CVEs
bundle audit update
# Vérifie si une de mes gems à une CVE connue
bundle audit
Overcommit
On a déjà plein d’outils de qualité de code qu’on aimerait lancer automatiquement pour éviter de les oublier. Pour cela on va utiliser la gem overcommit. Elle permet de facilement définir une liste de taches à lancer avant chaque commit
ou push
git.
Comme d’habitude on ajoute la gem à notre .gemspec
spec.add_development_dependency "overcommit"
On va ensuite “installer” les git hooks :
bundle exec overcommit -i
Enfin, on va configurer overcommit pour lui indiquer quelles actions lancer :
.overcommit.yml# Avant chaque commit
PreCommit:
# Lance rubocop sur les fichiers ruby qui ont changé.
# Si il y a un problème, empêche le commit.
RuboCop:
enabled: true
# Utilise le mode rapide de rubocop
command: ["rubocop", "-P"]
quiet: false
# Vérifie si il y a des gems outdated et affiche un warning sinon
BundleOutdated:
enabled: true
# Vérifie si il y a des CVEs et bloque le commit si oui.
BundleAudit:
enabled: true
# Avant chaque push
PrePush:
# Lance rspec et empêche le push si il y a un problème.
RSpec:
enabled: true
command: ["rspec", "-f", "p"]
quiet: false
C'est tout.
On ne lance pas RSpec à chaque commit car ceux-ci peuvent être très longs et on va vouloir encourager de petits commits. Si chaque commit prend 20 minutes, on ne va jamais commit.
Note : À chaque fois que le fichier .overcommit.yml
change pour des raisons de sécurité on va devoir faire bundle exec overcommit --sign
Bonus : lancer rubocop (rapide) sur l’ensemble du projet avant le push
À l’heure où cet article est écrit, il n’existe pas de façon pré-écrite de lancer Rubocop sur tous les fichiers avant chaque push
. Voilà une recette pour remédier au problème :
On ajoute le fichier :
.git-hooks/pre_push/rubocop.rb# frozen_string_literal: true
module Overcommit
module Hook
module PrePush
# Runs `rubocop` on every files.
class Rubocop < Base
def run
result = execute(['rubocop', '-P'])
return :pass if result.success?
output = result.stdout + result.stderr
[:fail, output]
end
end
end
end
end
Ensuite on modifie notre .overcommit.yml
# Avant chaque push
PrePush:
# […]
Rubocop:
enabled: true
Voilà !
Yard
Il est souvent bon d’écrire une documentation des différentes méthodes à même notre code et ce pour plusieurs raisons :
- Ça permet aux autres personnes qui voudraient utiliser notre gem de savoir ce à quoi servent les différentes classes et méthodes
- Ça permet à solargraph de faire de l’autocomplétion
Pour ce faire on va utiliser une gem classique : Yard.
Comme d’habitude on l’ajoute au .gemspec
spec.add_development_dependency "yard"
Ensuite on va documenter chaque méthode de la manière suivante :
# Use a context and inject its content at this place in the code
#
# @param [String, Symbol] context_name The context namespace
# @param [*Any] args Any arg to be passed down to the injected context
# @param [String, Symbol] namespace namespace name where to look for the context
# @param [String, Symbol] ns Alias for :namespace
# @param block Content that will be re-injected (see #execute_tests)
def in_context(context_name, *args, namespace: nil, ns: nil, &block)
Je vous laisse vous référer à la documentation de Yard pour plus d’informations.
Zeitwerk
Le dernier outil que j’utilise par défaut dans quasiment toutes les gems est Zeitwerk. Dès que notre gem contient un peu plus de 3 fichiers, faire les require à la main de chacun de ceux-ci en haut du premier fichier de notre gem peut vite devenir un cauchemar (car certains fichiers vont dépendre sur d’autres et vont donc devoir être require avant, etc.).
Zeitwerk permet de simplifier ce processus en faisant un autoload à la Rails sur nos fichiers. Il suffira ensuite que chaque fichier définisse la constante correspondant au path et nom du fichier pour que Zeitwerk s’occupe de faire des require automatique.
Sa configuration est assez simple.
On l’ajoute au .gemspec
:
spec.add_dependency 'zeitwerk', '~> 2'
Ensuite, suivant la documentation, dans notre fichier principal presque tout en haut on fait :
lib/active_shotgun.rbrequire "zeitwerk"
loader = Zeitwerk::Loader.for_gem
loader.setup # ready!
C'est tout. Les différents fichiers seront require correctement.
Des bonnes pratiques
Je ne vais pas m’étendre ici sur le pourquoi écrire des tests est crucial pour une gem qui va ensuite être utilisé par plein de gens. Écrivez. Des. Tests.
On va voir quelques points qu’il est, je pense, important de traiter.
Écrire un changelog
Si vous comptez sur le fait que votre gem soit utilisée par d’autres personnes, il est important de leur fournir un moyen de savoir ce qu’y change entre chaque version de votre gem. Pour cela, le plus simple est de maintenir un fichier CHANGELOG.md
à la racine de votre gem.
De plus, n’oubliez pas d’ajouter l’url menant à celui-ci dans votre .gemspec
.
spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
Pour ce qui est du format de changelog, je conseille de suivre le très bon (et simple) format de keepachangelog. Honnêtement n’importe quel format fera l’affaire.
Écrire de la documentation
On a déjà parlé de documentation à l’intérieur du code avec Yard. Pensez aussi à compléter votre Readme avec des cas d’usage classiques (voir toute la documentation si la gem est simple) avec des liens vers d’autres fichiers en .md
ou des pages web si nécéssaire.
Rappelez-vous : Si y a pas de doc ou si elle est nulle, il faut VRAIMENT que les gens n’aient pas le choix pour utiliser votre outil.
Automatiser son déploiement
Les gens qui me connaissent un peu savent que j’oublie toujours tout et que j’automatise au maximum mes différentes taches pour éviter toute erreur humaine.
On a vu précédemment que le déploiement se faisait par une simple commande rake
. Cependant, il ne faut pas oublier de la lancer, ni d’augmenter le numéro de version avant de le faire.
On va donc voir ici comment le faire via des actions Github.
On va avoir 3 actions que je vais décortiquer plus bas. Je pars du principe que toute PR qui est merge dans master doit créer une nouvelle version (éventuellement mineure). J’ai trop vu des gems qui avaient des correctifs dans master
/main
pendant des mois sans qu’un déploiement se fasse sur rubygems. Les utilisateurs se retrouvent alors à référencer le projet Github directement dans leur Gemfile ce qui en fait quelque chose de pas très stable.
La branche principale s’appelle main
. On ajoutera une protection dessus pour empêcher les commit directs.
Partant de ce constat, les 3 actions seront :
- Sur chaque commit lié a une branche qui n’est pas
main
, on lance les tests dans toutes les versions de ruby que supporte la gem. Ça permet de s’assurer que si jamais on change quelque chose, ça ne va pas casser dans une version de ruby en particulier. En tant qu’utilisateur je trouve ça rassurant. - Sur chaque PR à destination de
main
on vérifie que le numéro de version a bien changé et que ce numéro de version est bien supérieur au précédent. - Sur chaque commit dans
main
, on commence par lancer les tests dans toutes les versions de ruby. Si ces tests passent et que la version a été changée (ce qui devrait toujours être le cas), on va déployer notre nouvelle version de gem.
Vérifier la version sur chaque PR vers main
Pour ajouter des actions Github, rien de plus simple. On crée dans le dossier .github/workflows/
des fichiers correspondant à nos actions.
# Le nom qui sera affiché lors de l'exécution de notre action
name: Verify version change
# A quel moment l'action doit être lancée
on:
pull_request:
branches:
- main
# Quoi faire
jobs:
version_change:
runs-on: ubuntu-latest
# Les étapes de l'action
steps:
# on récupère le code
- uses: actions/checkout@v2
# On fetch la branche principale
# (pour pouvoir faire des comparaison avec la branche courante).
- name: Fetch main branch
run: git fetch origin main:main
# On fait un diff avec main et on vérifie
# qu'il y a bien un changement dans le fichier de version
- name: Verify if there's a change in version
run: "git diff main -- lib/rspec_in_context/version.rb | grep VERSION"
# Pour éventuellement débug
- name: Print new version
run: 'git diff main -- lib/rspec_in_context/version.rb | grep -E "^\+.*VERSION" | grep -E -o "[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?"'
# Magie noire en bash pour vérifier que
# le numéro de version est bien plus grand.
- name: Verify if higher version
run: '[[ $(git diff main -- lib/rspec_in_context/version.rb | grep -E "^\+.*VERSION" | grep -E -o "[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?") > $(git diff main -- lib/rspec_in_context/version.rb | grep -E "^-.*VERSION" | grep -E -o "[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?") ]]'
Lancer les tests a chaque commit sauf sur main
Ici pour lancer les tests sur toutes les versions de ruby on va utiliser une matrice :
.github/workflows/test_only.ymlname: Tests
on:
push:
branches-ignore:
- main
jobs:
tests:
# On défini ici la matrice. Comme on a qu'une seule variable,
# les "combinaisons" sont faciles. C'est un simple tableau de 4 éléments
strategy:
matrix:
ruby: [2.5, 2.6, 2.7, 3.0]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
# Cette action va setup le bon ruby et installer les gems.
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
# On va chercher la version de ruby dans la matrice
ruby-version: ${{ matrix.ruby }}
# On cache l'instalation des gems
bundler-cache: true
# On lance rubocop
- name: Run linter
run: bundle exec rubocop
# On lance les tests
- name: Run tests
run: bundle exec rspec
Lancer les tests et déployer au besoin sur les push dans la branche main
Cette fois-ci on va utiliser encore un nouvel outil des actions Github. On va utiliser la notion de dépendances entre jobs. De plus on va utiliser les secrets pour stocker notre clé API de rubygems.
.github/workflows/verify_version_change.ymlame: Test and Release
on:
push:
branches:
- main
jobs:
# Ce job est identique à celui de test_only
tests:
strategy:
matrix:
ruby: [2.5, 2.6, 2.7, 3.0]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- name: Run linter
run: bundle exec rubocop
- name: Run tests
run: bundle exec rspec
# On a un deuxième job de release
release:
# On indique qu'il a besoin que le job de tests soit fini et positif
needs: [tests]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: 3.0
bundler-cache: true
# On prépare le fichier ~/.gem/credentials
# qui va permettre de s'authentifier sur rubygems
- name: Prepare credentials
env:
# On lit dans les secrets du repo github
# pour le mettre en variable d'environment
RUBYGEM_KEY: ${{ secrets.RUBYGEM_KEY }}
run: "mkdir -p ~/.gem && echo -e \"---\\r\\n:rubygems_api_key: $RUBYGEM_KEY\" > ~/.gem/credentials && chmod 0600 ~/.gem/credentials"
# Comme on va créer un tag git on a besoin d'avoir choisi un email/username
- name: Setup username/email
run: 'git config --global user.email zaratan@hey.com && git config --global user.name "Denis <Zaratan> Pasin"'
# On récupère tous les tags existants
- name: Fetch tags from remote
run: "git fetch -t"
# On verifie si le fichier de version.rb à changé depuis le dernier tag
# Si oui, on lance rake release
- name: Publish if version change
run: 'git diff `git tag | tail -1` -- lib/rspec_in_context/version.rb | grep -E "^\+.*VERSION" && rake release || echo "No release for now"'
Conclusion
Voilà, avec tout ça vous devriez être capable de créer vous-même vos propres gems. Dans la seconde partie j’y ai mis plein de suggestions et de bonnes pratiques mais honnêtement, aucune n’est obligatoire. Si ça vous semble trop intimidant, commencez petit et ajoutez les choses petit à petit. N’oubliez pas que ce que vous lisez ici correspond à un grand nombre d’itérations de ma part ; au début je n’utilisais rien de tout ça.
Pour me poser plus de questions ou réagir à cet article vous pouvez aller directement vers la PR correspondante : #10. Et si l'article vous a plus vous pouvez toujours m'offrir un thé ici : Ko-fi.
Pour aller plus loin :
- Ma gem de rspec_in_context qui utilise toutes ces techniques (sauf Zeitwerk, car il n’y a que 2-3 fichiers).