vendredi 25 septembre 2026

À propos

Contact

Outils & workflow

Packager le zip de release d’une extension avec dist-archive-command

À chaque tag Git, générer automatiquement un zip propre de l'extension, sans fichiers de développement, prêt pour WordPress.org ou un client.

Par Clément Hadrot • 13 décembre 2023 • 5 min de lecture • Aucun commentaire
Packager le zip de release d'une extension avec dist-archive-command

Un client nous a écrit un jour parce que son extension premium livrait, dans le zip qu’il recevait, un dossier node_modules complet et un fichier .env de développement. Rien de grave en soi, mais ce genre de détail sème le doute sur le sérieux d’une livraison. Depuis, avant chaque tag, une seule commande construit l’archive de release, sans oubli et sans surplus.

Cette commande s’appelle dist-archive-command, un petit utilitaire distribué sous forme de script shell et repris par plusieurs générateurs de squelettes d’extensions WordPress. Il ne fait qu’une chose : lire un fichier de règles d’exclusion et produire un zip contenant exactement ce qui doit être livré, rien de plus.

Le problème du zip artisanal

Avant d’adopter cet outil, l’équipe utilisait un script maison bâti autour de zip -r et d’une longue liste d’exclusions passées en ligne de commande. Le script fonctionnait, jusqu’au jour où quelqu’un ajoutait un nouveau dossier de tests sans penser à l’exclure. Le zip livré au client contenait alors la suite de tests Pest complète, avec ses fixtures.

Le souci de fond n’est pas la maladresse d’un développeur isolé : c’est qu’un script d’exclusion manuel n’a pas de source de vérité partagée. Chaque développeur qui le modifie ajoute sa propre exception, et personne ne sait plus vraiment ce qui est censé sortir du zip.

Un fichier .distignore comme unique référence

dist-archive-command reprend un principe simple, calqué sur .gitignore : un fichier .distignore à la racine du dépôt liste les chemins à exclure de l’archive finale. Contrairement à .gitignore, qui décide ce qui entre dans le dépôt Git, .distignore décide ce qui sort du zip de distribution. Les deux listes ne se recoupent pas forcément : un fichier de configuration de build doit rester versionné dans Git, mais n’a rien à faire dans le zip livré.

L'essentiel à retenir : Un fichier .distignore trie ce qui part dans le zip ; La commande tourne dans un hook Git ou une Action ; Le zip généré est identique à celui testé en local

Un exemple de .distignore pour une extension gérée avec Composer et un build front :

.git
.github
.distignore
.gitattributes
node_modules
src
tests
phpunit.xml.dist
composer.lock
webpack.config.js
*.map
.env
.env.example

Notez que src ne désigne pas ici le code PHP principal, mais un dossier de sources JavaScript non transpilées : seul le résultat du build, déposé dans assets/build, doit voyager dans le zip.

Générer l’archive

Une fois le fichier en place, la commande se résume à ceci, lancée depuis la racine du dépôt :

dist-archive-command . dist/mon-extension.zip

L’outil lit .distignore, copie l’arborescence du dépôt dans un répertoire temporaire en excluant les chemins listés, puis compresse ce répertoire dans le fichier cible. Le nom du dossier racine à l’intérieur du zip correspond au slug de l’extension, comme l’exige WordPress.org pour un dépôt SVN, ce qui évite la manipulation manuelle habituelle consistant à renommer le dossier avant de zipper.

Intégration dans un tag Git

La commande devient utile quand elle se déclenche automatiquement. Dans une Action GitHub, un job dédié se lance sur chaque tag au format v* :

on:
  push:
    tags:
      - "v*"
jobs:
  package:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Installer les dépendances de production
        run: composer install --no-dev --optimize-autoloader
      - name: Construire les assets
        run: npm ci && npm run build
      - name: Générer l'archive
        run: dist-archive-command . dist/${{ github.ref_name }}.zip
      - name: Publier l'archive en artefact
        uses: actions/upload-artifact@v4
        with:
          name: release-zip
          path: dist/*.zip

Le point important, souvent oublié dans un script maison : l’installation Composer se fait avec --no-dev avant l’archivage, pour que le zip contienne les dépendances de production réelles et non les outils de test ou de lint.

Vérifier le contenu avant de livrer

Un zip généré automatiquement mérite tout de même un contrôle. Deux vérifications rapides suffisent en général :

  • Lister le contenu de l’archive avec unzip -l dist/mon-extension.zip pour repérer un fichier oublié.
  • Installer le zip sur un WordPress vierge via l’écran Extensions > Ajouter > Téléverser une extension, pour s’assurer qu’elle s’active sans erreur de chemin.

Sur un projet suivi par l’agence, cette dernière étape a un jour révélé qu’un chemin absolu s’était glissé dans un fichier de configuration généré par le build, cassant l’activation sur un serveur dont l’arborescence différait de celle de la machine de développement. Le contrôle manuel post-génération reste donc utile, même avec un outil fiable.

Cas particulier : plusieurs cibles de livraison

Certaines extensions livrées à la fois sur WordPress.org et à un client premium ont besoin de deux zips différents, l’un incluant un module payant, l’autre non. La solution la plus propre consiste à maintenir deux fichiers d’exclusion, par exemple .distignore et .distignore-premium, et à appeler la commande deux fois avec l’option correspondante :

dist-archive-command . dist/gratuite.zip --ignore-file .distignore
dist-archive-command . dist/premium.zip --ignore-file .distignore-premium

Le fichier d’exclusion mérite une revue de code au même titre que le code lui-même : c’est lui qui décide ce que le client final reçoit, pas un script bricolé la veille d’une deadline.

Notre verdict

Adopter dist-archive-command ne change rien à la façon d’écrire l’extension, seulement à la façon de la livrer. Le gain n’est pas spectaculaire au quotidien, mais il élimine une catégorie entière d’incidents bêtes : le fichier de config oublié, le dossier de tests livré par erreur, le zip qui ne correspond pas à ce qui a été testé. Sur un dépôt qui vit plusieurs années, avec plusieurs développeurs qui se succèdent, ce filet de sécurité vaut largement les quelques minutes passées à écrire le .distignore initial.

Partager :

À propos de l'auteur

Clément Hadrot

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi