stupeflix
stupeflix
Projet de démo d'un environnement de dev automatisé
L'idée
On va faire un portail de diffusion de vidéo
Environnement de dev
Le projet est en crystal-lang mais ça importe peu. Le reste est agnostique du langage.
initialisation
crystal init app stupeflix
cd stupeflix
rm .travis.yml
Dans le fichiers shards.yml
, j'ajoute kemal
:
dependencies:
kemal:
github: kemalcr/kemal
Je lance la récupération de la librairie
shards install
Puis j'écris le code Hello World
pour démarrer
require "kemal"
get "/" do
"Hello World!"
end
Kemal.run
Je lance le serveur pour vérifier que ça fonctionne
crystal src/stupeflix.cr
[development] Kemal is ready to lead at http://0.0.0.0:3000
Ajoutons maintenant les lib de test
Dans le shards.yml
dependencies:
kemal:
github: kemalcr/kemal
development_dependencies:
ameba:
github: crystal-ameba/ameba
version: ~> 0.13.0
spec-kemal:
github: kemalcr/spec-kemal
crytic:
github: hanneskaeufler/crytic
version: ~> 7
spec-kemal
va nous apporter le langage spec avec des macros lié àKemal
.ameba
est la librairie de lint/best-practices.crytic
est une librairie de mutation testing.
Écrivons notre premier test
On modifie le spec_helper.cr
pour intégré spec-kemal
require "spec"
require "spec-kemal"
require "../src/stupeflix"
puis on rédige le premier test dans stupeflix_spec.cr
require "./spec_helper"
describe "Stupeflix" do
it "renders /" do
get "/"
response.body.should eq "Hello World!"
end
end"
Maintenant, je peux lancer mon premier test
KEMAL_ENV=test crystal spec
.
Finished in 363 microseconds
1 examples, 0 failures, 0 errors, 0 pending
Ma première analyse ameba
./bin/ameba
Inspecting 4 files.
....
Finished in 1.24 milliseconds
4 inspected, 0 failures.
Et mon premier mutation testing
KEMAL_ENV=test ./bin/crytic test
✅ Original test suite passed.
Running 2 mutations.
✅ StringLiteralChange
in ./src/stupeflix.cr:3:5
✅ StringLiteralChange
in ./src/stupeflix.cr:4:3
Finished in 14.41 seconds:
2 mutations, 2 covered, 0 uncovered, 0 errored, 0 timeout. Mutation Score Indicator (MSI): 100.0%
Tout est ok mais on commence à voir les différentes actions que je dois mener pour le dev.
Déclenchement automatique
Maintenant que j'ai mes outils, j'aurais besoin que certaines actions soit automatiques
Pour cela, j'utilise guardian.
la commande est guardian init
La version généré est simpliste mais elle permet de comprendre le format.
files: ./**/*.cr
run: crystal build ./src/stupeflix.cr
---
files: ./shard.yml
run: shards install
Si un evenement se produit sur un fichier qui réponds à l'argument files
alors l'argument run
est lancé.
Dans le fichier initialisé, dès qu'un fichier .cr
est touché, ça lance un build. Dès que le fichier decripteur du projet shard.yml
est touché, on lance l'installation des dépendances.
C'est un bonne base. Mais je vais enlever le build
qui ne m'intéresse pas pour le moment. Et ajouter nos tests.
Ajout du Linter
Le linter ameba
analyse la syntaxe crystal
, nous avons besoin qui se déclenche à chaque modification de fichier .cr
On reprends la base du build
mais avec la commande ameba
files: ./**/*.cr
run: ./bin/ameba
---
files: ./shard.yml
run: shards install
Argh, celà analyse aussi les fichier .cr
qui sont dans les lib
.
Je vais faire une correction temporaire en spécifiant les répertoires que je veux analyser. J'avoue, j'anticipe sur la suite.
files: ./spec/**/*.cr
run: ./bin/ameba
---
files: ./src/**/*.cr
run: ./bin/ameba
---
files: ./shard.yml
run: shards install
Je lance guardian
et quand je modifie un fichier :
guardian
💂 Guardian is on duty!
± ./spec/stupeflix_spec.cr
└ 1 insertion(+), 1 deletion(-)
$ ./bin/ameba
Inspecting 4 files.
....
Finished in 13.02 milliseconds
4 inspected, 0 failures.
Ajout des tests
Même combat pour les tests, j'ai besoin qu'il se lance à chaque modification de fichier crystal.
files: ./spec/**/*.cr
run: ./bin/ameba
---
files: ./src/**/*.cr
run: ./bin/ameba
---
files: ./spec/**/*.cr
run: KEMAL_ENV=test crystal spec
---
files: ./src/**/*.cr
run: KEMAL_ENV=test crystal spec
---
files: ./shard.yml
run: shards install
Ajout des mutations testing
les mutations testing ne vont m'interessé qu'à la modification des fichiers de test.
files: ./spec/**/*.cr
run: ./bin/ameba
---
files: ./src/**/*.cr
run: ./bin/ameba
---
files: ./spec/**/*.cr
run: KEMAL_ENV=test crystal spec
---
files: ./src/**/*.cr
run: KEMAL_ENV=test crystal spec
---
files: ./spec/**/*.cr
run: KEMAL_ENV=test ./bin/crytic test
---
files: ./shard.yml
run: shards install
Testons
Je relance guardian
et voilà ce qui ce produit.
💂 Guardian is on duty!
± ./spec/stupeflix_spec.cr
└ 1 insertion(+), 1 deletion(-)
$ ./bin/ameba
Inspecting 4 files.
....
Finished in 1.73 milliseconds
4 inspected, 0 failures.
$ KEMAL_ENV=test crystal spec
.
Finished in 313 microseconds
1 examples, 0 failures, 0 errors, 0 pending
$ KEMAL_ENV=test ./bin/crytic test
✅ Original test suite passed.
Running 2 mutations.
✅ StringLiteralChange
in ./src/stupeflix.cr:3:5
✅ StringLiteralChange
in ./src/stupeflix.cr:4:3
Finished in 14.35 seconds:
2 mutations, 2 covered, 0 uncovered, 0 errored, 0 timeout. Mutation Score Indicator (MSI): 100.0%
Les trois commandes prévues pour se lancer a la modification d'un fichier de test, se sont exécutées dans l'ordre du fichier guardian.
Les processus de développement
Commençons maintenant à lister les processus dont nous avons besoin.
En l'état actuel, j'ai trois processus qui tourne sur ma machine pour le projet
guardian
pour l'execution automatiquecrystal srr/stupeflix.cr
l'application ce qui me permet de l'explorer
Le Procfile
Ecrivons notre première version de Procfile
avec ces 2 process.
web: crystal src/stupeflix.cr
fsevent: guardian
Le format est assez simple <process type>: <command>
Tant que vous êtes en local, les process type reste le nom que vous voulez donner au processus.
Par contre, si vous comptez deployer sur Heroku ou Pivotal Cloud Foundry certains process type sont reservés comme web
, worker
, release
, ...
Le lanceur.
Le cli d'Heroku peut faire process manager mais quand vous deployer sur Heroku, certaines commandes peuvent preter à confusion sur l'endroit où elles s'executent.
Du coup, je lui préfère overmind. Il a tmux
en pré-requis mais rien d'insurmontable.
Pour lancer nos processus, on utilise la commande overmind start
overmind start
system | Tmux socket name: overmind-stupeflix-7BuOIz8444130fHluw-1C_
system | Tmux session ID: stupeflix
system | Listening at ./.overmind.sock
web | Started with pid 60114...
fsevent | Started with pid 60115...
fsevent | 💂 Guardian is on duty!
web | [development] Kemal is ready to lead at http://0.0.0.0:3000
A partir de maintenant, la sortie standard de chaque process apparait dans le terminal avec le process type en préfixe.
Pour la suite, ce que l'on va vouloir c'est rebooter le process web
si les tests sont OK. Pour ce faire, la commande est overmind restart web
dans un nouveau terminal.
La CI locale.
nous avons notre liste de processus locaux et l'execution automatique. Mais je ne peux pas paralleliser les jobs avec guardian
ni définir des actions en cas de réussite ou d'echec.
C'est là que va intervenir werk.
Initialisation
Werk
fournit une commande d'initialisation werk init
qui génère un fichier werk.yml
simple mais facilement compréhensible.
---
version: "1"
description: Lorem ipsum dolor sic amet ...
variables: {}
jobs:
main:
description: Default job
variables: {}
commands:
- echo "Hello world!"
needs: []
can_fail: false
silent: false
A minima, Werk
a besoin d'un job main
. Apprenons le reste par la pratique.
Pipeline pour les fichiers src/**/*.cr
Si nous reprenons notre fichier guardian
, nous pouvons voir que nous lançons 2 actions pour les fichiers src :
- ./bin/ameba
- KEMAL_ENV=test crystal spec
On va y ajouter le redemarrage du serveur avec overmind
.
Les tests et le linter peuvent se faire en parallèle. Par contre, je ne rebooterais le serveur que si les tests passent. Commençons à formaliser ça dans werk
D'abords le restart du serveur web.
web_server:
description: Restart web server
commands:
- overmind restart web
Voyons ce que donne le werk plan web_server
┌─────────────────────────────────────────────┐
│ Stage 0 │
├────────────┬────────────────────┬───────────┤
│ Name │ Description │ Can fail? │
├────────────┼────────────────────┼───────────┤
│ web_server │ Restart web server │ No │
└────────────┴────────────────────┴───────────┘
maintenant si je lance werk run web_server
, overmind
relance bien le serveur.
Ajoutons les tests et mettons les en pré-requis du web_server
web_server:
description: Restart web server
commands:
- overmind restart web
needs:
- spec
spec:
description: Launch spec test
commands:
- KEMAL_ENV=test crystal spec
Voyons ce que donne le werk plan web_server
┌─────────────────────────────────────────────┐
│ Stage 0 │
├────────────┬────────────────────┬───────────┤
│ Name │ Description │ Can fail? │
├────────────┼────────────────────┼───────────┤
│ spec │ Launch spec test │ No │
├────────────┴────────────────────┴───────────┤
│ Stage 1 │
├────────────┬────────────────────┬───────────┤
│ Name │ Description │ Can fail? │
├────────────┼────────────────────┼───────────┤
│ web_server │ Restart web server │ No │
└────────────┴────────────────────┴───────────┘
Lançons le job werk run web_server
et là, gros FAIL. Les tests essayent de lancer un serveur web sur le même port que celui de mon serveur d'exploration. Il faut donc modifier la commande de lancement dans le Procfile en ajoutant l'argument -p 3001
web: crystal src/stupeflix.cr -p 3001
fsevent: guardian
Et si je relance overmind
puis werk
tout se passe bien. Les tests sont lancé, sont passant et le serveur redémarre.
Ajoutons le linter mais en non bloquant cette fois.
web_server:
description: Restart web server
commands:
- overmind restart web
needs:
- spec
- linter
spec:
description: Launch spec test
commands:
- KEMAL_ENV=test crystal spec
linter:
description: Linter with ameba
commands:
- ./bin/ameba
can_fail: true
Voyons ce que donne le werk plan web_server
┌─────────────────────────────────────────────┐
│ Stage 0 │
├────────────┬────────────────────┬───────────┤
│ Name │ Description │ Can fail? │
├────────────┼────────────────────┼───────────┤
│ linter │ Linter with ameba │ Yes │
├────────────┼────────────────────┼───────────┤
│ spec │ Launch spec test │ No │
├────────────┴────────────────────┴───────────┤
│ Stage 1 │
├────────────┬────────────────────┬───────────┤
│ Name │ Description │ Can fail? │
├────────────┼────────────────────┼───────────┤
│ web_server │ Restart web server │ No │
└────────────┴────────────────────┴───────────┘
On voit bien que les deux jobs sont placés sur le même stage et donc seront executés en même temps.
On peut même en profiter pour ajouter l'utilisation d'un outil natif crystal sur le format.
web_server:
description: Restart web server
commands:
- overmind restart web
needs:
- spec
- linter
- format
spec:
description: Launch spec test
commands:
- KEMAL_ENV=test crystal spec
linter:
description: Linter with ameba
commands:
- ./bin/ameba
can_fail: true
format:
description: Native crystal tool format
commands:
- crystal tool format --check
can_fail: true
maintenant, dans guardian
je modifie pour ne lancer que werk
files: ./src/**/*.cr
run: werk run web_server
Attaquons nous aux tests.
Pour les tests, nous n'avons pas besoin de relancer le serveur web, par contre, nous allons lancer les tests de mutation. Et ces tests ont besoin que les autres tests soient passant. Et l'on peut y appliquer les linter/format/etc...
Je peux donc factoriser avec ce qui a été fait avant.
mutation_test:
description: Launch mutation test after others test
commands:
- KEMAL_ENV=test ./bin/crytic test
needs:
- spec
- linter
- format
On voit ainsi rapidement l'avantage de werk
pour l'organisation et la factorisation des actions automatiques. Reste à mettre à jour guardian
files: ./src/**/*.cr
run: werk run web_server
---
files: ./spec/**/*.cr
run: werk run mutation_test
---
files: ./shard.yml
run: shards install
Voici qui clot le premier chapitre.
Pour la suite :
- Ajout d'autres intégration
- Ajout des tests dynamiques
- Ajout de la gestion de la documentation
Par rapport à crystal-lang, ajout de la configuration de Visual Studio Code.
stupeflix
- 0
- 0
- 0
- 0
- 4
- almost 4 years ago
- December 7, 2020
MIT License
Fri, 22 Nov 2024 08:31:36 GMT