Lors de la première version de cet atelier, github-actions n’existait pas. L’automatisation se faisait alors depuis les outils d’intégration continue disponibles sur github: travis, appveyor, circle-ci, ... Si ces outils sont toujours disponibles, github-actions change grandement la façon de faire son intégration continue. Il est plus flexible grâce aux différentes actions que l’on peut trouver dans son marketplace. Il permet d’exécuter facilement des actions pour tous les processus gérés par github: ouverture d’issue, pull request, release, ... Sortie fin 2019, il est rapidement arrivé numéro un dans les usages détrônant ainsi travis.
pre-commit¶
L’outil pre-commit va nous permettre d’automatiser l’utilisation du linter et du formatage à chaque commit pour vérifier que ce que l’on met dans le code suit certaines règles. Il permet de créer des hooks git de manière très simple et il est aisément extensible. Vous pouvez aller voir la liste des hooks et trouver votre bonheur.
Commençons par son installation. Nous allons rester dans l’univers de pixi et donc faire
pixi add pre-commit
Vous pouvez à présent initier pre-commit à l’aide de la ligne de commande suivante
pre-commit install
Il nous faut à présent définir ce que nous voulons tester à chaque commit en écrivant un fichier de configuration appelé .pre-commit-config.yaml
. C’est bien évidemment à vous de mesurer l’impact de l’utilisation de cet outil. Si vous êtes trop contraignant et que le pre-commit prend une minute, vous n’aurez probablement pas l’adhésion des contributeurs. Il vous faudra donc trouver une solution acceptable qui profite à tous.
Dans le cadre de cet atelier, nous souhaitons vérifier la liste suivante à chaque commit
- ne pas mettre des fichiers trop gros (
check-added-large-files
) - ne pas donner des noms de fichiers ou de répertoires qui ne sont pas compatibles avec tous les OS (
check-case-conflict
) - vérifier que les fichiers
json
etyaml
sont bien construits (check-json
,check-yaml
) - faire en sorte que tous les fichiers finissent par une ligne vide (
end-of-file-fixer
) - enlever les espaces qui ne servent à rien (
trailing-whitespace
) - vérifier qu’il n’y a pas de fichiers qui sont en conflit de fusion (
check-merge-conflict
) - vérifie que l’on n’essaie pas de pousser dans la branche principale (
no-commit-to-branch
) - vérifier les caractères de fin de lignes et les rendre homogènes (
mixed-line-ending
) - remplacer les tabulations par des espaces (https://
GitHub .com /Lucas -C /pre -commit -hooks) - vérifier que les changements sont bien compatibles avec ruff (https://
GitHub .com /pre -commit /mirrors -clang -format)
Voici à quoi ressemble le fichier .pre-commit-config.yaml
repos:
- repo: https://GitHub.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: check-added-large-files
- id: check-case-conflict
- id: end-of-file-fixer
- id: trailing-whitespace
- id: check-merge-conflict
- id: check-yaml
exclude: conda/recipe/meta.yaml
- id: check-json
- id: no-commit-to-branch
- id: mixed-line-ending
args: ["--fix=lf"]
description: Forces to replace line ending by the UNIX 'lf' character.
- repo: https://GitHub.com/Lucas-C/pre-commit-hooks
rev: v1.5.1
hooks:
- id: forbid-tabs
- id: remove-tabs
args: [--whitespaces-count, "4"]
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.5.0
hooks:
# Run the linter.
- id: ruff
types_or: [python, pyi, jupyter]
args: [--fix]
# Run the formatter.
- id: ruff-format
types_or: [python, pyi, jupyter]
À partir de maintenant, à chaque fois que vous ferez un commit, l’ensemble de ces règles seront vérifiées.
Github¶
Si vous n’avez pas encore de compte sur github, créez en un. Vous devez ensuite créer un nouveau projet que vous pouvez par exemple nommer splinart
.
L’idée étant de mettre dans ce dépôt le final_step
de la partie sur pytest
, copiez ce répertoire dans un répertoire à part. Lorsque vous créez un dépôt sur github, celui-ci vous donne plusieurs recettes pour l’initialiser. Nous choisirons la première.
Mais avant cela, nous allons ajouter un fichier readme.md
et un .gitignore
.
Vous pouvez à présent initialiser votre projet en utilisant la commande suivante
git init
Nous allons dans un premier temps ajouter les deux fichiers que nous avons créés précédemment.
git add readme.md .gitignore
git commit -m "initial commit"
Puis tous les autres fichiers en faisant
git add splinart doc demos ...
Vérifiez avant de faire le commit que tous les fichiers sont les bons et qu’il n’en manque pas.
git commit -m "add splinart"
Initialisez le remote
en suivant ce qui est écrit sur votre github. J’avais pour cette exemple
git remote add origin https://github.com/gouarin/splinart.git
Vous pouvez maintenant faire un push de vos trois commits en faisant
git push --set-upstream origin main
Vous pouvez à présent vérifier que tous vos fichiers sont présents sur votre github.
Maintenant que votre dépôt a bien été créé, nous allons nous intéresser (dans l’ordre) aux étapes suivantes
- Mise en place d’un premièr workflow github-actions pour l’intégration continue.
- Validation des push en utilisant pytest.
- Déploiement de
splinart
sur PyPi et conda.
github-actions¶
Les actions sont déclenchées à partir de ce qu’il y a dans le répertoire .github/workflows
. Nous allons créer une branche qui va initier la CI.
git checkout -b init-ci
Il faut maintenant créer le premier worflow en ajoutant le fichier .github/worflows/ci.yml
dont le contenut est
name: ci
on:
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
Il vous faut ajouter le fichier
git add .github/worflows/ci.yml
puis de pousser la branche sur github
git push --set-upstream origin init-ci
Vous n’avez plus qu’à retourner sur github et demander à ouvrir une pull request. Vous verrez alors la CI se mettre en place.
Nous allons à présent ajouter l’installation de pytest, l’installation de notre application et enfin vérifier que tous les tests passent bien avec pytest.
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Test with pytest
run: |
pytest
Vous pouvez rester dans le même branche et commiter les changements au fur et à mesure. La pull request sera mise à jour automatiquement.
Nous allons à présent ajouter différentes versions de Python dans la CI ainsi que plusieurs OS.
Il suffit de changer la première partie de build
runs-on: ubuntu-latest
par
runs-on: ${{ matrix.os }}
strategy:
matrix:
python-version: [3.8, 3.9, "3.10", 3.11, 3.12]
os:
- ubuntu-latest
- macos-latest
- windows-latest
Nous avons créé ici une matrice qui a deux paramètres. La CI va réaliser toutes les combinaisons possibles.
ReadTheDocs¶
Nous allons à présent nous intéresser à générer la documentation automatiquement sur ReadTheDocs. Pour cela, nous allons ajouter un fichier environment.yml
dans le répertoire doc
indiquant tout ce dont nous avons besoin pour sphinx et ses dépendances. Et un fichier .readthedocs.yml
à la racine du projet indiquant comment installer notre projet.
Voici à quoi ressemblent ces deux fichiers
name: splinart
channels:
- conda-forge
dependencies:
- notebook
- sphinx
- nbsphinx
- pydata-sphinx-theme
- numpydoc
- nbsphinx-link
- myst-parser
version: 2
build:
os: "ubuntu-20.04"
tools:
python: "mambaforge-22.9"
python:
install:
- method: pip
path: .
conda:
environment: doc/environment.yml
Vous devez à présent vous créer un compte sur https://
Vous pouvez également faire en sorte que la génération de la documentation sur readthedocs fasse partie intégrante de la CI. Pour cela, il vous suffit d’aller dans votre projet sur readthedocs et aller dans Admin/advanced parameters
et de cocher la case sur les pull request
.
Mise en place d’une nouvelle release¶
Nous allons changer un peu la recette conda pour faire le package de l’application. Nous avions vu dans la partie 3 comment faire une recette conda à partir d’un dossier local. Nous allons à présent le faire directement à partir d’une release du dépôt github. Il suffit de changer le fichier comme suit avec les bonnes dépendances
{% set version = "0.2.0" %}
package:
name: splinart
version: {{ version }}
source:
git_url: https://github.com/gouarin/splinart.git
git_rev: v{{ version }}
build:
number: 0
script: "{{PYTHON}} -m pip install . --no-deps -vv"
noarch: python
requirements:
build:
- python>=3.8
- setuptools
run:
- python>=3.8
- numpy
- matplotlib
test:
imports:
- splinart
about:
home: http://github.com/gouarin/splinart
license: BSD
license_family: BSD
summary: 'spline art generator'
description: 'spline art generator'
extra:
recipe-maintainers: 'loic.gouarin@gmail.com'
Publication¶
Nous avons maintenant une CI qui teste notre application. Nous allons en créer une autre qui va être déclanchée au moment d’une release et qui va déployer une nouvelle version de l’application sur PyPi et sur conda.
Voici l’action correspondante
name: publish
on:
release:
types: [published]
jobs:
make_sdist:
name: Make SDist and weel
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install build
run: pip install build
- name: Build SDist
run: python -m build --sdist
- name: Build wheel
run: python -m build --wheel
- uses: actions/upload-artifact@v3
with:
path: dist/*
upload_on_pypi:
needs: [make_sdist]
environment: pypi
permissions:
id-token: write
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v3
with:
name: artifact
path: dist
- uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: https://test.pypi.org/legacy/
upload_on_conda:
needs: [make_sdist]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: mamba-org/setup-micromamba@v1
with:
environment-name: build-env
create-args: >-
python=3.10
conda-build
anaconda-client
- name: Build the recipe
shell: bash -l {0}
run: conda build recipes
- name: upload on conda
shell: bash -l {0}
run: anaconda -t ${{ secrets.ANACONDA_TOKEN }} upload --force /home/runner/micromamba/envs/build-env/conda-bld/*/splinart-*.tar.bz2
Nous allons commencer par créer un token sur anaconda. Il faut aller sur votre compte anaconda dans la partie settings->access
et créer un token. Celui-ci devra avoir les droits suivants
api:read
(Allow read access to the API site)api:write
(Allow write access to the API site)
Vous pouvez à présent l’ajouter dans les secrets de guthub en allant, au niveau de votre dépôt, dans settings/secrets and variables/actions
et ajouter votre token dans la partie repository secrets
.
Pour la partie PyPi, vous pouvez faire de même (ajouter un token) en allant sur Api token ou dire que votre dépôt github est un dépôt de confiance (trusted publisher). Vous devrez pour cela activer la double authentification.
Une fois que vous avec mis tout ça en place et que l’action est mise dans la branche main
du dépôt, vous pouvez tester celle-ci en créant une nouvelle release.****