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

- Auteur : Clément Hadrot
- Publié le : 2023-12-13
- Mis à jour le : 2023-12-13
- Catégorie : Outils &amp; workflow
- URL : https://wpmoderne.dev.wordpress-developpement.fr/outils/packager-zip-release-extension-dist-archive-command/

## L’essentiel

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