%\documentclass[handout,aspectratio=169]{beamer}
\documentclass[aspectratio=169]{beamer}

\usepackage[T1]{fontenc}
\usepackage[utf8]{inputenc}
\usepackage{lmodern}
\usepackage[french]{babel}
\usepackage{graphicx}
\usepackage{ulem}
\usepackage{listings}
\lstset{
    inputencoding=utf8,
    mathescape,
    basicstyle=\tiny\ttfamily,
    numberstyle=\scriptsize\ttfamily\color{gray},
    showstringspaces=false,
    tabsize=2,
    numbers=left,
    xleftmargin=8mm,
    frame=lines,
    framexleftmargin=8mm,
    framerule=1pt,
    rulecolor=\color{green!50!black},
    backgroundcolor=\color{green!5},
    commentstyle=\color{gray},
    morecomment=[l][commentstyle]{//},
    classoffset=0,
    keywords={Pour, allant, Si, alors, Sinon, Fin},
    keywordstyle=\color{blue!75},
    classoffset=1,
    morekeywords={Afficher},
    keywordstyle=\color{green!50!black},
    classoffset=0,
    morestring=[b]",
    stringstyle=\color{red!75},
    literate=
  {é}{{\'e}}{1}%
  {è}{{\`e}}{1}%
  {à}{{\`a}}{1}%
  {â}{{\^a}}{1}%%%
  {ç}{{\c{c}}}{1}%
  {œ}{{\oe}}{1}%
  {ù}{{\`u}}{1}%
  {É}{{\'E}}{1}%
  {È}{{\`E}}{1}%
  {À}{{\`A}}{1}%
  {Ç}{{\c{C}}}{1}%
  {Œ}{{\OE}}{1}%
  {Ê}{{\^E}}{1}%
  {ê}{{\^e}}{1}%
  {î}{{\^i}}{1}%
  {ï}{{\"i}}{1}%%%
  {ô}{{\^o}}{1}%
  {û}{{\^u}}{1}%
}

\usetheme[sectiontitle]{crans}

\title{Automatisez vos tâches avec une intégration continue}
\subtitle{Séminaire CRANS}
\author[]{\textsc{Yohann D'ANELLO}}
\date{11 février 2021}


\begin{document}
\begin{frame}
   \maketitle
\end{frame}

\section{Introduction}

\subsection{Discussion avec un chatbot}

\begin{frame}[containsverbatim]
  \begin{exampleblock}{Introduction avec un petit chatbot}
    \begin{lstlisting}[language=bash]
git clone https://gitlab.crans.org/ynerant/seminaire-ci.git
cd seminaire-ci
git checkout v1
python3 main.py
    \end{lstlisting}
  \end{exampleblock}
\end{frame}

\subsection{Des erreurs}

\begin{frame}[containsverbatim]
  \begin{block}{Le script ne semble pas fonctionner}
    \begin{lstlisting}[language=Python]
  File "/tmp/seminaire-ci/main.py", line 7
    print "Bienvenue dans le chatbot du séminaire de l'intégration continue !"
          ^
SyntaxError: Missing parentheses in call to 'print'.
Did you mean print("Bienvenue dans le chatbot du séminaire de l'intégration continue !")?
    \end{lstlisting}
  \end{block}
\end{frame}

\begin{frame}
  \includegraphics[width=180px]{img/re2o-500.png} \includegraphics[width=180px]{img/note-500.png}
  \includegraphics[width=180px]{img/youtube-500.jpg}
\end{frame}

\begin{frame}
  \begin{block}{Trois solutions}
    Il existe différents types de développeurs
    \begin{itemize}
      \item Celui/celle qui fait confiance à son code
      \pause
      \item Celui/celle qui ne fait pas confiance à son code et qui teste tout
        en permance
      \pause
      \item Celui/celle qui ne fait pas confiance à son code et qui embauche un
        esclave pour tout tester en permanence
    \end{itemize}
  \end{block}

  \pause

  \og{} Le code prouve que ça marche \fg{}

  \og{} De toute façon ce que j'ai écrit peut rien péter \fg{}
\end{frame}

\subsection{Comment éviter les erreurs ?}

\begin{frame}
  \begin{itemize}
    \item Relire 100 fois son code
    \pause
    \item Réfléchir avant d'écrire, prendre du recul
    \pause
    \item Tester son code après chaque modification
    \pause
    \item Utiliser un analyseur syntaxique
    \pause
    \item Tester automatiquement et régulièrement le code
  \end{itemize}
\end{frame}

\section{Correction des erreurs}

\subsection{Analyseur syntaxique}

\begin{frame}
  \begin{block}{Rôle d'un analyseur syntaxique}
    \begin{itemize}
      \item Lit le code sans l'exécuter ni le compiler
      \item Vérifie que la syntaxe est cohérente
      \item Repère les erreurs les plus courantes et suggère des modifications
      \item S'assure de l'uniformité du code et du respect de certaines
        conventions
      \item Existe dans la quasi-totalité des langages
      \item En Python : Flake8 et Pylint
    \end{itemize}
  \end{block}
\end{frame}

\begin{frame}[containsverbatim]
  \begin{exampleblock}{Installation des analyseurs}
    \begin{lstlisting}[language=bash]
(sudo apt install python3-virtualenv / sudo pacman -S python-virtualenv)
(python3 -m venv venv)
(source venv/bin/activate)
pip3 install pylint flake8
    \end{lstlisting}
  \end{exampleblock}

  \begin{exampleblock}{Analyse du code}
    En lancant \texttt{pylint main.py} :
    \begin{lstlisting}[language=Python]
************* Module main
main.py:7:11: E0001: Missing parentheses in call to 'print'.
Did you mean print("Bienvenue dans le chatbot du séminaire de l'intégration continue !")?
(<unknown>, line 7) (syntax-error)
    \end{lstlisting}
  \end{exampleblock}

  $\implies$ On n'a pas eu besoin d'exécuter le code
\end{frame}

\subsection{Intégration continue}

\begin{frame}
  \begin{itemize}
    \item Appeler ce script à la main $\to$ laborieux
    \item Solution : utiliser \sout{un esclave} de l'intégration continue
  \end{itemize}
\end{frame}

\begin{frame}
  \begin{block}{Qu'est-ce qu'une intégration continue ?}
    \begin{itemize}
      \item Script appelé après commit envoyé au serveur distant
      \item Utile pour analyser et tester le code
      \item Mais également pour déployer le projet automatiquement
    \end{itemize}
  \end{block}
\end{frame}

\begin{frame}[containsverbatim]
  \begin{exampleblock}{Analyse syntaxique automatique}
    \texttt{git checkout v2}

    Dans le fichier \texttt{.gitlab-ci.yml} :
    \begin{lstlisting}[]
flake8:
  image: python:3-alpine
  before_script:
    - pip install flake8 --no-cache-dir
  script: flake8 main.py

pylint:
  image: python:3-alpine
  before_script:
    - pip install pylint --no-cache-dir
  script: pylint main.py
    \end{lstlisting}

  Résultat : \url{https://gitlab.crans.org/ynerant/seminaire-ci/-/pipelines/5595}
  \end{exampleblock}
\end{frame}

\subsection{Nettoyer son code}

\begin{frame}
  \begin{block}{Utilité d'un analyseur syntaxique}
    \begin{itemize}
      \item \texttt{git checkout v2.5}
      \item Le script peut être lancé, mais le code n'est pas propre
      \item Un code bien écrit est un code qui peut être lu et maintenu
      \item Quelques règles standards :
        \begin{itemize}
          \item Lignes ne faisant pas plus de 79 caractères
          \item Code bien indenté
          \item Pas de variable inutile
          \item Code bien aéré (mais pas trop)
          \item Code documenté et commenté
          \item Imports triés
          \item Beaucoup d'options ...
        \end{itemize}
    \end{itemize}
  \end{block}
\end{frame}

\begin{frame}[containsverbatim]
  \begin{exampleblock}{Analyse du code}
    En lancant \texttt{pylint main.py} sur le tag \texttt{v2.5} :
    \begin{lstlisting}[language=Python]
************* Module main
main.py:23:0: C0301: Line too long (104/100) (line-too-long)
main.py:32:0: C0301: Line too long (469/100) (line-too-long)
main.py:39:0: C0301: Line too long (192/100) (line-too-long)
main.py:16:0: W0622: Redefining built-in 'help' (redefined-builtin)
main.py:91:0: W0622: Redefining built-in 'quit' (redefined-builtin)
main.py:1:0: C0114: Missing module docstring (missing-module-docstring)
main.py:6:0: C0116: Missing function or method docstring (missing-function-docstring)
main.py:42:0: C0103: Argument name "a" does not conform to snake_case naming style
    (invalid-name)
main.py:42:0: C0103: Argument name "b" does not conform to snake_case naming style
    (invalid-name)
main.py:42:0: C0103: Argument name "c" does not conform to snake_case naming style
    (invalid-name)
main.py:42:0: R0912: Too many branches (17/12) (too-many-branches)
main.py:96:4: R1722: Consider using sys.exit() (consider-using-sys-exit)

------------------------------------------------------------------
Your code has been rated at 8.03/10 (previous run: 8.03/10, +0.00)
    \end{lstlisting}
  \end{exampleblock}
\end{frame}

\subsection{Faire marcher son code}

\begin{frame}[containsverbatim]
  \begin{block}{Un code bien écrit n'est pas un code qui marche}
    \texttt{git checkout v5}

    En lançant \texttt{python3 main.py} :
    \begin{lstlisting}
Bienvenue dans le chatbot du séminaire de l'intégration continue !
Veuillez taper une commande. Tapez "aide" pour afficher l'aide.
> calcul 1 2 3
Traceback (most recent call last):
  File "/tmp/seminaire-ci/main.py", line 110, in <module>
    main()
  File "/tmp/seminaire-ci/main.py", line 27, in main
    print(globals()[args[0]](*args[1:]))
  File "/tmp/seminaire-ci/main.py", line 90, in calcul
    result = left % right
TypeError: not all arguments converted during string formatting
    \end{lstlisting}

    $\implies$ le code est bien écrit, mais ne fait pas ce qu'on veut
  \end{block}
\end{frame}

\begin{frame}[containsverbatim]
  \begin{block}{Utilisation de l'intégration continue}
    \texttt{git checkout v6}

    On ajoute au fichier \texttt{.gitlab-ci.yml} :
    \begin{lstlisting}
test:
  stage: test
  image: python:3-alpine
  script: python main.py
    \end{lstlisting}

    Le code est bien exécuté :
    \url{https://gitlab.crans.org/ynerant/seminaire-ci/-/jobs/13865}
  \end{block}
\end{frame}

\subsection{Environnement de tests}

\begin{frame}
  \begin{block}{Utiliser un environnement de test}
    \begin{itemize}
      \item Impossible d'exécuter le script pas d'entrée à fournir
      \item Impossible de choisir quoi tester et de vérifier la bonne
        exécution du programme
      \pause
      \item Solution : utiliser un environnement adapté et des tests unitaires
    \end{itemize}
  \end{block}
\end{frame}

\begin{frame}[containsverbatim]
  \begin{exampleblock}{Avec Pytest}
    \texttt{git checkout v8} \\
    \texttt{pip3 install pytest} \\
    Dans le fichier \texttt{main\_test.py} :
    \begin{lstlisting}[language=Python]
import unittest

import main


def test_aide():
    res = main.commande("aide")
    lines = res.split("\n")
    assert len(lines) == 6
    assert lines[0].startswith("aide")
    assert lines[1].startswith("seminaire")
    assert lines[2].startswith("blague")
    assert lines[3].startswith("calcul")
    assert lines[4].startswith("tri")
    assert lines[5].startswith("stop")
    \end{lstlisting}
    On a ajouté une fonction \texttt{commande}
  \end{exampleblock}
\end{frame}

\begin{frame}[containsverbatim]
  \begin{exampleblock}{Avec Pytest}
    \texttt{pytest .}
    \begin{lstlisting}[language=Python]
============================= test session starts =============================
platform linux -- Python 3.9.1, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /tmp/seminaire-ci
collected 1 item                                                              
main_test.py F                                                          [100%]
================================== FAILURES ===================================
__________________________________ test_aide __________________________________

    def test_aide():
        res = main.commande("aide")
>       lines = res.split("\n")
E       AttributeError: 'NoneType' object has no attribute 'split'

main_test.py:17: AttributeError
---------------------------- Captured stdout call -----------------------------
aide		Affiche l aide
[...]
=========================== short test summary info ===========================
FAILED main_test.py::test_aide - AttributeError: 'NoneType' object has no at...
============================== 1 failed in 0.03s ==============================
    \end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[containsverbatim]
  \begin{exampleblock}{Avec Pytest, après réparation de l'erreur}
    \texttt{git checkout v9} \\
    \texttt{pytest .}
    \begin{lstlisting}[language=Python]
============================= test session starts =============================
platform linux -- Python 3.9.1, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /tmp/seminaire-ci
collected 1 item                                                              

main_test.py .                                                          [100%]

============================== 1 passed in 0.01s ==============================
    \end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}[containsverbatim]
  \begin{exampleblock}{Un meilleur environnement de tests}
    \texttt{git checkout v10} \\
    On utilise une classe pour mieux construire son environnement
    \begin{lstlisting}[language=Python]
import unittest

import main


class TestMain(unittest.TestCase):
    def test_aide(self):
        res = main.commande("aide")
        lines = res.split("\n")
        self.assertEqual(len(lines), 6)
        self.assertTrue(lines[0].startswith("aide"))
        self.assertTrue(lines[1].startswith("seminaire"))
        self.assertTrue(lines[2].startswith("blague"))
        self.assertTrue(lines[3].startswith("calcul"))
        self.assertTrue(lines[4].startswith("tri"))
        self.assertTrue(lines[5].startswith("stop"))
    \end{lstlisting}
  \end{exampleblock}
\end{frame}

\begin{frame}
  \begin{block}{Qu'est-ce qu'un bon test unitaire ?}
    \begin{itemize}
      \item \og{} Unitaire \fg{} : on ne teste qu'une seule fonctionnalité à la
        fois
      \item Clair
      \item Pertinent
      \item Non-trivial
    \end{itemize}
  \end{block}
\end{frame}

\subsection{Couverture}

\begin{frame}[containsverbatim]
  \begin{block}{Couverture}
    \begin{itemize}
      \item Les tests unitaires exécutent une partie du code
      \item Il est possible de mesurer quelle partie du code est exécutée et
        testée
      \item On parle de \textit{couverture} du code (\textit{coverage} en anglais)
    \end{itemize}
  \end{block}

  \begin{exampleblock}{Calcul de la couverture du code}
    \begin{itemize}
      \item \texttt{git checkout v11}
      \item \texttt{pip install pytest-cov}
      \item \texttt{pytest --showlocals --cov=main --cov=main\_test --cov-report=term-missing .}
    \end{itemize}
  \end{exampleblock}
\end{frame}

\begin{frame}[containsverbatim]
  \begin{exampleblock}{Calcul de couverture}
    \texttt{pytest --showlocals --cov=main --cov=main\_test --cov-report=term-missing .}
    \begin{lstlisting}
============================= test session starts =============================
platform linux -- Python 3.9.1, pytest-6.2.2, py-1.10.0, pluggy-0.13.1
rootdir: /tmp/seminaire-ci
plugins: cov-2.11.1
collected 1 item                                                              
main_test.py .                                                          [100%]
----------- coverage: platform linux, python 3.9.1-final-0 -----------
Name           Stmts   Miss  Cover   Missing
--------------------------------------------
main.py           41     27    34%   21-27, 54, 70, 82-98, 105, 112-113, 117
main_test.py      14      0   100%
--------------------------------------------
TOTAL             55     27    51%

============================== 1 passed in 0.05s ==============================
    \end{lstlisting}
    Exercice pour le plaisir : couvrir et tester correctement la totalité du code
  \end{exampleblock}
\end{frame}

\subsection{Lutter contre le nazisme}

\begin{frame}
  \begin{block}{Configuration de l'analyse et des tests}
    \begin{itemize}
      \item \og{} Ohh vraiment je dois faire ça \fg{}, \og{} 79 caractères c'est
        pas assez \fg{}, \og{} Je ne veux/peux pas couvrir cette ligne \fg{}
      \item Couvrir et tester sont des très bonnes pratiques en règle générale
      \item Vous pouvez adapter la configuration selon vos propres critères,
        plus ou moins souples
    \end{itemize}
  \end{block}
\end{frame}

\section{Déploiement}

\subsection{Artefacts}

\begin{frame}
  \begin{block}{Rôle d'une intégration continue}
    Une intégration continue permet :
    \begin{itemize}
      \item d'analyser automatiquement la structure du code
      \item de tester automatiquement le projet
      \item \textit{a fortiori} de donner un indicateur aux autres personnes de
        la qualité du projet via des badges
      \pause
      \item De construire et de déployer automatiquement le projet
    \end{itemize}
  \end{block}
\end{frame}

\begin{frame}[containsverbatim]
  \begin{block}{Construction du projet}
    \texttt{git checkout v16} \\
    Un script d'intégration ne se limite pas aux tests : il peut compiler des
    projets

    Exemple avec la compilation automatique \LaTeX{} de ces slides :
    \begin{lstlisting}
slides:
  image: aergus/latex
  script:
    - latexmk -cd -pdf slides/seminaire-ci.tex
    \end{lstlisting}
    L'intégration continue génère un fichier \texttt{seminaire-ci.pdf} dans son
    script
  \end{block}
\end{frame}

\begin{frame}[containsverbatim]
  \begin{block}{Construction du projet}
    \texttt{git checkout v16} \\
    Il est possible d'exporter certains fichiers compilés :
    \begin{lstlisting}
slides:
  image: aergus/latex
  script:
    - latexmk -cd -pdf slides/seminaire-ci.tex
  artifacts:
    paths:
      - slides/seminaire-ci.pdf
    expire_in: 1 mo
    \end{lstlisting}
    Ici, les slides sont compilées et le fichier PDF généré est exporté et
    stocké pendant 1 mois :
    \url{https://gitlab.crans.org/ynerant/seminaire-ci/-/jobs/13826/artifacts/browse}
    (à des fins d'exemple, la limitation d'un mois à été retirée)

    Particulièrement utile pour générer des binaires à partir du code source
  \end{block}
\end{frame}

\subsection{Déploiement automatisé}

\begin{frame}
  \begin{block}{Déployer son projet}
    \begin{itemize}
      \item Une fois le projet dans une version stable et bien testé, le projet
        peut être installé
      \item Si on a suffisamment confiance en ses tests, possibilité de le faire
        automatiquement
      \item Deux solutions principales :
        \begin{itemize}
          \item Envoyer un signal (\textit{webhook}) aux services concernés
          \item Directement déployer le serveur ce qu'il faut $\to$ nécessite
            d'être simple à déployer et d'être peu distribué (projet développé
            dans un but interne : site, latex, ...)
        \end{itemize}
      \item Pour des gros projets, on privilégie la première option
      \item Attention aux droits donnés à Gitlab
    \end{itemize}
  \end{block}
\end{frame}

\begin{frame}[containsverbatim]
  \begin{block}{Déployer son projet}
    Les slides de ce séminaire sont automatiquement déployées sur
    \url{https://ynerant.fr/gitlab/seminaire-ci.pdf} :
    \begin{lstlisting}
slides:
  stage: compile_slides
  image: aergus/latex
  before_script:
    - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
    - eval $$(ssh-agent -s)
    - echo "$$SSH_KEY" | tr -d '\r' | ssh-add - > /dev/null
    - mkdir -p ~/.ssh
    - chmod 0700 ~/.ssh
    - ssh-keyscan ynerant.fr >> ~/.ssh/known_hosts
    - chmod 0644 ~/.ssh/known_hosts
  script:
    - latexmk -cd -pdf slides/seminaire-ci.tex
    - scp slides/seminaire-ci.pdf gitlab-ci@ynerant.fr:gitlab-ftp/seminaire-ci.pdf
    \end{lstlisting}
  \end{block}

  Autre exemple : \url{https://gitlab.crans.org/nounous/homepage} déploie
  automatiquement \url{https://crans.org/}
\end{frame}

\subsection{Webhooks}

\begin{frame}
  \begin{block}{Webhooks}
    Un \textit{webhook} est un événement envoyé à un service externe pour
    signaler un changement d'état.

    Exemples de webhooks :

    \begin{itemize}
      \item Message sur une plateforme de communication (IRC, Matrix, Discord,
        \ldots)
      \item Compilation automatique de documentation
      \item Traduction
      \item Compilation et déploiement du projet sur un service externe (Docker,
        PyPI ou autre gestionnaire de paquets, \ldots)
    \end{itemize}
  \end{block}
\end{frame}

\section{Conclusion}

\begin{frame}
  \begin{exampleblock}{En résumé}
    \begin{itemize}
      \item Analyser statiquement son code permet d'assurer qu'il est
        syntaxiquement correct et qu'il est surtout lisible
      \item Tester efficacement son projet assure son bon fonctionnement et
        évite des erreurs imprévues
      \item Ces tâches peuvent se faire automatiquement après chaque commit par
        le biais d'une intégration continue
      \item Il est enfin possible de construire et déployer son projet
        automatiquement
      \item Une intégration continue efficace est souvent signe d'un projet bien
        géré
    \end{itemize}
  \end{exampleblock}

  \pause

  \begin{block}{}
    \textit{Gérer} un projet n'est pas uniquement \textit{coder} un projet.
  \end{block}
\end{frame}

\end{document}
