Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
12 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
187 changes: 187 additions & 0 deletions .github/workflows/theme-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
name: Publication d'un thème

on:
workflow_call:
inputs:
tools-ref:
description: Version de release-tools utilisée pour les scripts (doit correspondre à la version du workflow).
type: string
default: v1.1

permissions:
contents: read

jobs:
build:
name: Contrôles et paquet
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-24.04
timeout-minutes: 15
permissions:
contents: read
outputs:
slug: ${{ steps.meta.outputs.slug }}
version: ${{ steps.meta.outputs.version }}
steps:
- name: Code du thème
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
path: theme
persist-credentials: false

- name: Outils de publication
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: wearewp/release-tools
ref: ${{ inputs.tools-ref }}
path: tools
persist-credentials: false

# Un relèvement de la version minimale sur main s'applique même aux appels épinglés sur un ancien tag.
- name: Version minimale de wawp-theme-updater exigée sur main
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: wearewp/release-tools
ref: main
path: tools-main
sparse-checkout: config
persist-credentials: false

- name: Slug et version
id: meta
run: |
echo "slug=${GITHUB_REPOSITORY#*/}" >> "$GITHUB_OUTPUT"
echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"

- name: Contrôles avant publication
env:
SLUG: ${{ steps.meta.outputs.slug }}
run: |
ref_min="$(cat tools/config/min-theme-updater-version.txt)"
main_min="$(cat tools-main/config/min-theme-updater-version.txt)"
min="$(printf '%s\n%s\n' "$ref_min" "$main_min" | sort -V | tail -n 1)"
php tools/scripts/check-headers.php --type=theme theme "$SLUG" "$GITHUB_REF_NAME" "$min"

- name: Syntaxe PHP
run: find theme -name '*.php' -not -path '*/node_modules/*' -print0 | xargs -0 -n1 php -l > /dev/null

- name: Dépendances Composer
if: hashFiles('theme/composer.json') != ''
working-directory: theme
run: composer install --no-dev --prefer-dist --no-interaction --no-progress --optimize-autoloader

- name: Assets
if: hashFiles('theme/package.json') != ''
working-directory: theme
run: npm ci && npm run build

- name: Paquet
env:
SLUG: ${{ steps.meta.outputs.slug }}
VERSION: ${{ steps.meta.outputs.version }}
run: |
mkdir -p "build/$SLUG" dist
rsync -a --exclude-from=theme/.distignore --exclude=.git --exclude=.github theme/ "build/$SLUG/"
php tools/scripts/check-package.php --type=theme "build/$SLUG" "$SLUG" "$VERSION"
(cd build && zip -rqX "../dist/$SLUG-$VERSION.zip" "$SLUG")

- name: Conservation du paquet
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: paquet
path: dist/
retention-days: 1
if-no-files-found: error
overwrite: true

sign-upload:
name: Signature et envoi
needs: build
runs-on: ubuntu-24.04
timeout-minutes: 10
permissions:
contents: read
concurrency:
group: release-upload-${{ github.repository }}
cancel-in-progress: false
steps:
- name: Outils de publication
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
repository: wearewp/release-tools
ref: ${{ inputs.tools-ref }}
path: tools
persist-credentials: false

- name: Récupération du paquet
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: paquet
path: dist

- name: Installation de lftp
run: |
sudo apt-get update -qq
sudo apt-get install -y -qq lftp > /dev/null

- name: Signature
env:
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
ZIP: dist/${{ needs.build.outputs.slug }}-${{ needs.build.outputs.version }}.zip
run: php tools/scripts/sign.php "$ZIP"

- name: Envoi FTPS vers le serveur de mises à jour
env:
FTP_HOST: ${{ secrets.FTP_HOST }}
FTP_USER: ${{ secrets.FTP_USER }}
LFTP_PASSWORD: ${{ secrets.FTP_PASSWORD }}
ZIP: dist/${{ needs.build.outputs.slug }}-${{ needs.build.outputs.version }}.zip
run: bash tools/scripts/upload-ftps.sh "$ZIP"

- name: Conservation de la signature
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: signature
path: dist/*.sig
retention-days: 1
if-no-files-found: error
overwrite: true

publish-release:
name: Archive dans la Release GitHub
needs: [build, sign-upload]
runs-on: ubuntu-24.04
timeout-minutes: 5
permissions:
contents: write
steps:
- name: Récupération du paquet
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: paquet
path: dist

- name: Récupération de la signature
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
name: signature
path: dist

- name: Release
env:
GH_TOKEN: ${{ github.token }}
run: |
if gh release view "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" > /dev/null 2>&1; then
assets="$(gh release view "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" --json assets --jq '.assets[].name')"
for file in dist/*; do
while IFS= read -r asset; do
if [ "$asset" = "$(basename "$file")" ]; then
echo "La Release $GITHUB_REF_NAME contient déjà $asset : aucun remplacement automatique. Vérifier l'asset existant et le supprimer à la main s'il doit être remplacé, puis relancer le job." >&2
exit 1
fi
done <<< "$assets"
done
gh release upload "$GITHUB_REF_NAME" dist/* --repo "$GITHUB_REPOSITORY"
else
gh release create "$GITHUB_REF_NAME" dist/* --repo "$GITHUB_REPOSITORY" --title "$GITHUB_REF_NAME" --notes "Paquet signé publié sur updates.wearewp.pro."
fi
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# release-tools

Outils de publication des plugins WeAre[WP] et client de mise à jour `wawp-updater`.
Outils de publication des plugins et des thèmes WeAre[WP], clients de mise à jour `wawp-updater` (plugins) et `wawp-theme-updater` (thèmes).

- `wawp-updater/` : bibliothèque embarquée dans chaque plugin (PHP 7.4+, WordPress 5.8+). Vérifie les mises à jour sur `updates.wearewp.pro` avec la clé `WAWP_LICENSE_KEY` et refuse tout paquet dont la signature Ed25519 est invalide.
- `.github/workflows/plugin-release.yml` : workflow réutilisable, déclenché par un tag `vX.Y.Z` dans le dépôt d'un plugin.
- `scripts/`, `bin/` : contrôles, signature, envoi FTPS, génération des clés, préparation d'un dépôt.
- `wawp-updater/` : bibliothèque embarquée dans chaque plugin (PHP 7.4+, WordPress 5.8+). Vérifie les mises à jour sur `updates.wearewp.pro` avec la clé `WAWP_LICENSE_KEY` et refuse tout paquet dont la signature Ed25519 est invalide. Figée en version 1.0.0.
- `wawp-theme-updater/` : bibliothèque embarquée dans le thème FSE autonome d'un client (PHP 7.4+, WordPress 6.1+), mêmes garanties, chargée par `functions.php`.
- `.github/workflows/plugin-release.yml` (tag `v1`) et `.github/workflows/theme-release.yml` (tag `v1.1`) : workflows réutilisables, déclenchés par un tag `vX.Y.Z` dans le dépôt d'un plugin ou d'un thème.
- `scripts/`, `bin/` : contrôles (`--type=theme` pour un thème), signature, envoi FTPS, génération des clés, préparation d'un dépôt.

Ce dépôt ne contient aucun secret. Mise en place : `docs/mise-en-place.md`.

Expand Down
42 changes: 31 additions & 11 deletions bin/setup-repo.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# Prépare un dépôt de plugin pour la publication automatique. Ne manipule jamais la valeur d'un secret.
# Usage : bash bin/setup-repo.sh wearewp/<slug> <chemin-local-du-plugin>
# Prépare un dépôt de plugin ou de thème pour la publication automatique. Ne manipule jamais la valeur d'un secret.
# Usage : bash bin/setup-repo.sh wearewp/<slug> <chemin-local-du-plugin-ou-du-thème>
set -euo pipefail

repo="${1:-}"
Expand All @@ -17,6 +17,15 @@ if [ -z "$path" ] || [ ! -d "$path" ]; then
fi
slug="${repo#wearewp/}"

# Thème : style.css avec Theme Name et pas de <slug>.php (même règle que l'ingestion du serveur).
kind=plugin
template="release.yml"
if [ -f "$path/style.css" ] && grep -q 'Theme Name:' "$path/style.css" && [ ! -f "$path/$slug.php" ]; then
kind=theme
template="theme-release.yml"
fi
echo "Type détecté : $kind."

visibility="$(gh api "repos/$repo" --jq '.visibility')"
if [ "$visibility" != "private" ]; then
echo "Le dépôt $repo doit être privé (visibilité actuelle : $visibility)." >&2
Expand Down Expand Up @@ -49,21 +58,32 @@ mkdir -p "$path/.github/workflows"
if [ -f "$path/.github/workflows/release.yml" ]; then
echo "release.yml déjà présent : non modifié."
else
cp "$tools/templates/release.yml" "$path/.github/workflows/release.yml"
echo "release.yml créé : à commiter."
cp "$tools/templates/$template" "$path/.github/workflows/release.yml"
echo "release.yml créé à partir de templates/$template : à commiter."
fi

status=0
if [ ! -f "$path/.distignore" ]; then
echo "À faire : créer $path/.distignore (fichiers exclus du paquet)." >&2
status=1
fi
if ! grep -qE '^[[:space:]*#@/]*Update URI:[[:space:]]*https://updates\.wearewp\.pro[[:space:]]*$' "$path/$slug.php" 2>/dev/null; then
echo "À faire : ajouter l'en-tête « Update URI: https://updates.wearewp.pro » dans $slug.php." >&2
status=1
fi
if [ ! -f "$path/wawp-updater/load.php" ]; then
echo "À faire : copier wawp-updater/ dans le plugin et ajouter le chargement (voir docs/mise-en-place.md)." >&2
status=1
if [ "$kind" = theme ]; then
if ! grep -qE '^[[:space:]*#@/]*Update URI:[[:space:]]*https://updates\.wearewp\.pro[[:space:]]*$' "$path/style.css" 2>/dev/null; then
echo "À faire : ajouter l'en-tête « Update URI: https://updates.wearewp.pro » dans style.css." >&2
status=1
fi
if [ ! -f "$path/wawp-theme-updater/load.php" ]; then
echo "À faire : copier wawp-theme-updater/ dans le thème et ajouter le chargement dans functions.php (voir docs/mise-en-place.md)." >&2
status=1
fi
else
if ! grep -qE '^[[:space:]*#@/]*Update URI:[[:space:]]*https://updates\.wearewp\.pro[[:space:]]*$' "$path/$slug.php" 2>/dev/null; then
echo "À faire : ajouter l'en-tête « Update URI: https://updates.wearewp.pro » dans $slug.php." >&2
status=1
fi
if [ ! -f "$path/wawp-updater/load.php" ]; then
echo "À faire : copier wawp-updater/ dans le plugin et ajouter le chargement (voir docs/mise-en-place.md)." >&2
status=1
fi
fi
exit "$status"
1 change: 1 addition & 0 deletions config/min-theme-updater-version.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.0.0
35 changes: 34 additions & 1 deletion docs/mise-en-place.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Mise en place de la publication des plugins WeAre[WP]
# Mise en place de la publication des plugins et des thèmes WeAre[WP]

Procédure unique, à suivre dans l'ordre. Les étapes marquées 🔒 manipulent des secrets : Thierry les exécute lui-même, dans son propre terminal, jamais dans une session automatisée.

Expand Down Expand Up @@ -98,6 +98,39 @@ Suivre la section 11 de la spec (`docs/superpowers/specs/2026-10-12-serveur-mise

La version de rotation de `wawp-updater` incrémente `$wawp_updater_version` dans `load.php` et `Config::VERSION` dans `src/class-config.php`, toujours égales (par exemple `1.0.0` vers `1.1.0`) : c'est la copie la plus récente qui est chargée sur un site. Une fois cette version taguée, relever `config/min-updater-version.txt` sur la branche `main` de release-tools à cette même version : chaque publication lit aussi ce fichier sur `main` et retient la plus haute des deux valeurs, ce qui bloque la publication d'un plugin qui embarque encore l'ancien client, même si son workflow est épinglé sur un ancien tag.

## 9. Tag `v1.1` de release-tools (une seule fois)

Les thèmes utilisent `theme-release.yml@v1.1` ; les plugins restent sur `@v1`. Après fusion sur `main` de la branche qui ajoute `wawp-theme-updater/` et `theme-release.yml`, depuis un clone de release-tools, dans Git Bash (`gh` authentifié comme à l'étape 5) :

```
git checkout main && git pull
git tag v1.1
git push origin v1.1
```

Le ruleset de l'étape 5 protège aussi ce tag (motif `refs/tags/v*`) : il ne pourra être ni déplacé ni supprimé.

## 10. Préparer un dépôt de thème

Le thème est un thème FSE autonome (pas de thème parent, pas d'en-tête `Template`), actif sur un seul site. Le dépôt appartient à l'organisation `wearewp`, est privé et porte le nom du slug du thème, qui est aussi le nom de son dossier et son `Text Domain`.

1. Copier `wawp-theme-updater/` (version taguée `v1.1`) à la racine du thème : `git -C <release-tools> archive v1.1 wawp-theme-updater | tar -x -C <thème>`.
2. Dans `functions.php`, après le contrôle `ABSPATH` :
```php
require __DIR__ . '/wawp-theme-updater/load.php';
\WAWP\ThemeUpdater\register( __DIR__ );
```
3. Dans `style.css` : `Update URI: https://updates.wearewp.pro`, `Requires at least: 6.1` (ou plus), `Requires PHP`, `Text Domain: <slug>`, `Version` à la prochaine version mineure. `Theme URI` est facultatif, mais s'il est vide le lien « Voir les détails de la version » de WordPress ne mène nulle part d'utile : renseigner `Theme URI: https://www.wearewp.pro/` (recommandé).
4. Créer `.distignore` avec des motifs racine ancrés par `/` (`/.git`, `/.github`, `/node_modules`, `/src`…).
5. Contrôler localement : `php <release-tools>/scripts/check-headers.php --type=theme <thème> <slug> v<version> 1.0.0` → `Contrôles réussis`.
6. Lancer, dans Git Bash, depuis le dossier de release-tools : `bash bin/setup-repo.sh wearewp/<slug> <chemin-local-du-thème>` (`Type détecté : theme.`), puis commiter `.github/workflows/release.yml`.
7. Ajouter le slug à la liste `themes` de la licence du site (`bin/new-key.php --theme=<slug>` pour une nouvelle licence, ou édition du registre puis `bin/check-licences.php`).
8. Publier comme un plugin (section 7, sans `readme.txt`), puis, après sauvegarde du site, installer une fois le zip publié à la main (Apparence › Thèmes › Ajouter › Téléverser, remplacer le thème actif). Les versions suivantes passent par les mises à jour de WordPress.

Le client des thèmes ne place jamais l'adresse de téléchargement dans le cache de WordPress, mais un marqueur `https://updates.wearewp.pro/v1/theme-update/<slug>` : `wawp-updater` 1.0.0, embarqué dans les plugins, refuse toute adresse `/v1/download/` hors d'une mise à jour de plugin. Une rotation de clé de signature (section 8) concerne aussi `wawp-theme-updater/src/class-config.php` (même `PUBLIC_KEYS`) et `config/min-theme-updater-version.txt`.

## Limite connue

WordPress n'appelle les filtres `Update URI` qu'après une réponse réussie d'`api.wordpress.org`. Si ce service est injoignable depuis un site, les mises à jour WeAre[WP] n'y apparaissent pas tant que la situation dure ; elles réapparaissent dès le contrôle suivant.

Il en va de même pour les thèmes (filtre `update_themes_updates.wearewp.pro`). WordPress envoie aussi à api.wordpress.org la liste des thèmes installés, dont le slug du thème client : si un thème public de wordpress.org portait le même slug, sa mise à jour serait proposée à la place. Choisir des slugs de thèmes propres au client.
6 changes: 5 additions & 1 deletion phpcs.xml.dist
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<?xml version="1.0"?>
<ruleset name="WAWP Updater">
<description>Standard WordPress et compatibilité PHP 7.4 pour la bibliothèque embarquée.</description>
<description>Standard WordPress et compatibilité PHP 7.4 pour les bibliothèques embarquées.</description>
<file>wawp-updater</file>
<file>wawp-theme-updater</file>
<arg name="extensions" value="php"/>
<arg value="sp"/>
<config name="testVersion" value="7.4-"/>
Expand All @@ -12,6 +13,7 @@
<properties>
<property name="text_domain" type="array">
<element value="wawp-updater"/>
<element value="wawp-theme-updater"/>
</property>
</properties>
</rule>
Expand All @@ -20,6 +22,8 @@
<property name="prefixes" type="array">
<element value="WAWP\Updater"/>
<element value="wawp_updater"/>
<element value="WAWP\ThemeUpdater"/>
<element value="wawp_theme_updater"/>
</property>
</properties>
</rule>
Expand Down
1 change: 1 addition & 0 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ parameters:
phpVersion: 70400
paths:
- wawp-updater
- wawp-theme-updater
- scripts
- bin
bootstrapFiles:
Expand Down
Loading
Loading