Aller au contenu

Publier une version

Le CLI est distribué depuis un index public que nous hébergeons, conforme à la PEP 503. Rien qu'un site statique : ni serveur, ni base, ni authentification.

https://pkg.prod.aim.dyneco.io

Installer, côté utilisateur

uv tool install https://pkg.prod.aim.dyneco.io/packages/dyneco_aim-0.3.0-py3-none-any.whl

Une seule URL, rien à configurer. Les dépendances se résolvent depuis PyPI.

uv tool install --index https://pkg.prod.aim.dyneco.io/simple/ dyneco-aim
pipx install --pip-args="--extra-index-url https://pkg.prod.aim.dyneco.io/simple/" dyneco-aim

La commande installée s'appelle aim dans tous les cas.

Pourquoi dyneco-aim et pas aim ?

aim est déjà pris sur PyPI par un outil de suivi d'expériences ML. Deux index proposant le même nom feraient silencieusement gagner la version la plus haute — la sienne, en 3.x, contre la nôtre en 0.x. C'est le schéma exact d'une dependency confusion.

Le nom de distribution et le nom de la commande sont deux décisions séparées : seul le premier change. Personne ne tape jamais le nom de distribution après l'installation.

Si tu utilises aussi l'outil ML, installe avec un suffixe : pipx install dyneco-aim --suffix=-ctx donne la commande aim-ctx.

Publier, côté mainteneur

  1. Incrémente version dans pyproject.toml. C'est la seule déclaration : le code lit la version depuis les métadonnées du paquet installé.
  2. Commite et pousse.
  3. Lance le pipeline publier-paquet dans Azure DevOps.

Le déclenchement est manuel, jamais automatique sur commit : publier une version est une décision, et une version publiée est immuable.

Ce que fait le pipeline

flowchart LR
    A[Tests] --> B[Construire la roue]
    B --> C[Récupérer l'index publié]
    C --> D[Fusionner]
    D --> E[Déployer]
    E --> F[Installer et vérifier]

L'étape de fusion est celle qui compte. Un déploiement statique remplace l'intégralité du site : régénérer l'index depuis le seul dist/ local effacerait toutes les versions antérieures et casserait les installations épinglées. Le script lit donc l'index en ligne, rapatrie les roues existantes, et republie l'ensemble.

Il refuse par ailleurs de republier une version existante sous une empreinte différente — un fichier dont le contenu change sous le même nom est précisément ce qu'un index ne doit jamais faire.

La dernière étape installe réellement le paquet depuis l'index publié et vérifie que aim version renvoie la version attendue. Sans elle, un index cassé passerait pour un succès.

Répétition générale

Le pipeline accepte un paramètre « Construire et vérifier sans publier » : il va jusqu'à la construction de l'index et affiche ce qui aurait été déployé.

En local

uv build --wheel
uv run python scripts/build_index.py \
  --dist dist --out site-pkg \
  --base-url https://pkg.prod.aim.dyneco.io
python3 -m http.server 8765 --directory site-pkg

Ce que le pipeline ne fait pas

Il ne pose pas d'étiquette git. À faire à la main si tu veux retrouver le code d'une version publiée :

git tag -a v0.3.0 -m "version 0.3.0" && git push origin v0.3.0

Il ne publie pas sur PyPI public. Cela n'aura de sens que le jour où le serveur sera auto-hébergeable par un tiers : aujourd'hui, un pip install depuis PyPI donnerait une commande sans plateforme à contacter.

Il ne publie que la roue. Le paquet est du Python pur ; l'archive source n'apporterait rien et exposerait publiquement l'infrastructure et les scripts de déploiement.

Limite assumée

L'index publié fait office de stockage des versions antérieures. Si le site était effacé, l'historique des roues le serait aussi — elles restent reconstructibles depuis les étiquettes git, mais pas au bit près.

Un compte de stockage servant de source de vérité corrigerait cela ; ce n'est pas justifié pour un paquet de 80 kio.

Coût

0 €. Le palier gratuit de Static Web Apps inclut 100 Go de bande passante par mois, les domaines personnalisés et les certificats. Le pipeline consomme le quota gratuit d'agents hébergés (1 800 minutes par mois) ; une publication en prend environ trois.