Déploiement continu avec CircleCI - Partie 2
La première partie permettait de découvrir les concepts de base de CircleCI et comment les mettre en application. Dans cette partie on va voir comment aller plus loin en utilisant les différents moyens permettant de partager de l’information entre les jobs.
État des lieux
version: 2.1
jobs:
build:
docker:
- image: circleci/node:11.10.1
steps:
- checkout
- run: yarn install
- run: yarn build
lint:
docker:
- image: circleci/node:11.10.1
steps:
- checkout
- run: yarn install
- run: yarn lint
test:
docker:
- image: circleci/node:11.10.1
steps:
- checkout
- run: yarn install
- run: yarn test:unit
deploy:
docker:
- image: circleci/node:11.10.1
steps:
- checkout
- run: yarn install
- run: yarn build
- run: yarn firebase deploy --token "$FIREBASE_TOKEN" --only hosting
workflows:
version: 2
integration:
jobs:
- build
- lint
- test
- deploy:
requires:
- build
- lint
- test
filters:
branches:
only: master
Notre configuration fonctionne mais on voit que l’on se répète. Chacun des jobs contient ces lignes :
docker:
- image: circleci/node:11.10.1
steps:
- checkout
- run: yarn install
Les deux premières lignes, celles de la configuration de l’environnement d’exécution, sont juste une répétition au sein du fichier de configuration, elles n’ont pas d’impact sur les performances. Mais par soucis de maintenabilité on utilisera les executors et les commands pour factoriser ces fragments.
Deuxième problème, chaque job est obligé de réinstaller les dépendances car ils exécutent leurs instructions dans un environnement isolé. Pour pallier ce problème on va installer les dépendances, les stocker en cache pour qu’elles puissent être partagées par les jobs d’un workflow. Les dépendances mises en cache pourront aussi être utilisées par les jobs futurs.
Le dernier problème est la nécessité de devoir re-construire l’application de production dans le job de déploiement alors que cette étape a été réalisée par le job de build. Encore une fois le problème vient du fait que les jobs sont isolés. Par défaut le job de déploiement ne peut pas lire ce qui a été généré par le job de build. On utilisera les workspaces pour résoudre ce souci.
Cacher les dépendances
Pour utiliser le cache on va définir un nouveau job chargé d’installer nos dépendances :
install:
docker:
- image: circleci/node:11.10.1
steps:
- checkout
- restore_cache:
key: v1-dependencies-{{ checksum "yarn.lock" }}
- run: yarn install
- save_cache:
key: v1-dependencies-{{ checksum "yarn.lock" }}
paths:
- ./node_modules
restore_cache
: restore le cache pour une clé donnéekey
: clé de restauration du cache
save_cache
: sauvegarde des fichiers en cachekey
: clé de stockage du cachepaths
: les élements à stocker, ici les node_modules
Cette étape tente de restaurer le cache contenant les node_modules
permettant ainsi de passer l’étape d’installation des dépendances.
NB : la clé de cache contient un hash de notre fichier yarn.lock
, généré par la fonction checksum. Donc si le fichier change, le hash change et le cache est re-généré.
On peut mettre à jour les autres jobs de notre configuration pour qu’ils utilisent le cache :
version: 2
jobs:
install:
docker:
- image: circleci/node:11.10.1
steps:
- checkout
- restore_cache:
key: v1-dependencies-{{ checksum "yarn.lock" }}
- run: yarn install
- save_cache:
key: v1-dependencies-{{ checksum "yarn.lock" }}
paths:
- ./node_modules
build:
docker:
- image: circleci/node:11.10.1
steps:
- checkout
- restore_cache:
key: v1-dependencies-{{ checksum "yarn.lock" }}
- run: yarn build
# autres jobs...
workflows:
version: 2
integration:
jobs:
- install
- build:
requires:
- install
- lint:
requires:
- install
- test:
requires:
- install
- deploy:
requires:
- build
- lint
- test
filters:
branches:
only: master
Désormais on installe une seule fois les dépendances que l’on partage entre les différents jobs. On n’oublie pas de préciser que chacun des jobs requiert que le job install ait été complété.
Avec du cache on gagne environ 20s sur le processus de déploiement.
Les workspaces
Un workspace est un espace qui permet à un job de partager des données avec un job lui succédant.
Dans notre cas on va construire l’application dans le job de build
, stocker les fichiers statiques dans le workspace pour que le job deploy
puisse les récupérer si nécessaire.
build:
docker:
- image: circleci/node:11.10.1
steps:
- checkout
- restore_cache:
key: v1-dependencies-{{ checksum "yarn.lock" }}
- run: yarn build
- persist_to_workspace:
root: ~/project
paths:
- ./dist
persist_to_workspace
: indique que l’on veut écrire dans le workspaceroot
: le dossier dans lequel on doit aller chercher l’information, par défaut~/project
paths
: liste des chemins des fichiers/dossiers à écrire dans le workspace
deploy:
docker:
- image: circleci/node:11.10.1
steps:
- checkout
- restore_cache:
key: v1-dependencies-{{ checksum "yarn.lock" }}
- attach_workspace:
at: ~/project
- run: yarn firebase deploy --token "$FIREBASE_TOKEN" --only hosting
En utilisant les workspaces en complément du cache on est parvenu à gagner une trentaine de secondes par rapport à la configuration de la première partie 🎉.
🍒 sur le 🍰 : les executors et les commandes
Ici on peaufine notre configuration afin d’éviter un maximum les répétitions dans notre fichier de configuration.
Les commands
permettent de factoriser des étapes de nos jobs.
Les executors
permettent de factoriser les environnements dans lesquels seront exécutés nos scripts.
commands:
checkout_and_restore_cache:
steps:
- checkout
- restore_cache:
key: v1-dependencies-{{ checksum "yarn.lock" }}
executors:
node:
docker:
- image: circleci/node:11.10.1
On peut mettre à jour nos jobs :
install:
executor: node
steps:
- checkout_and_restore_cache
- run: yarn install
- save_cache:
key: v1-dependencies-{{ checksum "yarn.lock" }}
paths:
- ./node_modules
La configuration finale
version: 2.1
commands:
checkout_and_restore_cache:
steps:
- checkout
- restore_cache:
key: v1-dependencies-{{ checksum "yarn.lock" }}
executors:
node:
docker:
- image: circleci/node:11.10.1
jobs:
install:
executor: node
steps:
- checkout_and_restore_cache
- run: yarn install
- save_cache:
key: v1-dependencies-{{ checksum "yarn.lock" }}
paths:
- ./node_modules
build:
executor: node
steps:
- checkout_and_restore_cache
- run: yarn build
- persist_to_workspace:
root: ~/project
paths:
- dist/*
lint:
executor: node
steps:
- checkout_and_restore_cache
- run: yarn lint
test:
executor: node
steps:
- checkout_and_restore_cache
- run: yarn test:unit
deploy:
executor: node
steps:
- checkout_and_restore_cache
- attach_workspace:
at: ~/project
- run: yarn firebase deploy --token "$FIREBASE_TOKEN" --only hosting
workflows:
version: 2
integration:
jobs:
- install
- build:
requires:
- install
- lint:
requires:
- install
- test:
requires:
- install
- deploy:
requires:
- build
- lint
- test
filters:
branches:
only: master
Conclusion
On a enfin une configuration de bogass 😎 Performante grâce à l’utilisation du cache et des workspaces, maintenable grâce aux commandes et aux executors, sympathique grâce aux pommes.
Merci de m’avoir lu.