Déploiement continu avec CircleCI - Partie 2
Tags: circleci, ci, cd
Published: 2019-03-18 14:17:01

---
title: Déploiement continu avec CircleCI - Partie 2
published: true
description: Optimisation d'une configuration CircleCI
cover_image: https://thepracticaldev.s3.amazonaws.com/i/hqxwdv09iz9y9pqxfz04.jpg
canonical_url: https://maximeblanc.fr/blog/deploiement-continu-avec-circleci-partie-2
tags: #circleci, #ci, #cd
---

La [première partie](/deploiement-continu-avec-circleci-partie-1) 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

```yml
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 :

```yml
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 :

```yml
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ée
  - `key` : clé de restauration du cache
- `save_cache` : sauvegarde des fichiers en cache
  - `key` : clé de stockage du cache
  - `paths` : les elements à 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 :

```yml
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.

![circleci-with-cache](https://thepracticaldev.s3.amazonaws.com/i/v4xljhbhu526uq29anpv.png)

## 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.

```yml
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 workspace
  - `root` : 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

```yml
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 🎉.

![circle-ci-final-build](https://thepracticaldev.s3.amazonaws.com/i/k0qbfmp2ov6fbyfezgee.png)

## 🍒 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.

```yml
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 peux mettre à jour nos jobs :

```yml
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

```yml
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.

## Liens utiles

- [Document de référence du fichier de configuration](https://circleci.com/docs/2.0/configuration-reference)
- [Configuration finale](https://github.com/jean-smaug/condorcet/blob/4046a6723fc2c4b6470c79b309683d9f7af9844a/.circleci/config.yml)