Compter les téléchargements de vos releases GitHub… en MOGWAI

Depuis peu, MOGWAI dispose des primitives HTTP de base (http.get, http.post, http.put, http.patch, http.delete, http.head). L’occasion rêvée de les mettre à l’épreuve sur un cas concret plutôt qu’un exemple jouet : interroger l’API GitHub pour savoir combien de fois les binaires CLI de MOGWAI ont été téléchargés, release par release.

Ce script, je ne l’ai pas écrit moi-même — c’est Claude (l’assistant IA d’Anthropic) qui l’a rédigé, à partir de la documentation des primitives MOGWAI que je lui ai fournie. Je l’ai testé, corrigé sur quelques points, et j’ai même patché un bug de l’engine MOGWAI découvert au passage. Le détail de cette collaboration est en fin d’article, avec l’avis franc de Claude sur la difficulté de la prise en main.

Ce que fait le script

mogwai.reset
console.clear

"GitHub owner/organization: " console.prompt -> 'owner'
"Repository name: " console.prompt -> 'repo'

"https://api.github.com/repos/{! owner}/{! repo}/releases" eval -> 'apiUrl'

[!
uri: apiUrl
requestHeaders: [User-Agent: "MOGWAI-DownloadStats"]
] http.get -> 'result'

if (result->state:) then
{
result->response: utf8-> json-> -> 'releases'

0 -> 'grandTotal'

"MOGWAI - Downloads per release" ?
"" ?

releases foreach 'rel' transform
{
rel->tag_name: -> 'tag'
rel->assets: -> 'assets'

tag ?

assets foreach 'a' transform
{
a->name: 32 str.padRight ?? " : " ?? a->download_count: ?
a->download_count:
} -> 'counts'

counts sum -> 'releaseTotal'
grandTotal releaseTotal + -> 'grandTotal'

" -> Release total : " ?? releaseTotal ?
"" ?

0
}

"TOTAL (assets only, excludes GitHub's auto-generated source zip) : " ?? grandTotal ?
}
else
{
"Request failed - " ?? result->error: ?
}

Comment il fonctionne, étape par étape

1. Saisie interactive

Au lancement, le script demande le compte GitHub et le nom du dépôt via console.prompt — pas besoin d’éditer le fichier pour l’utiliser sur un autre projet que MOGWAI :

"GitHub owner/organization: " console.prompt -> 'owner'
"Repository name: " console.prompt -> 'repo'

console.prompt affiche le message passé en paramètre, attend la saisie clavier (terminée par Entrée), et place le texte saisi sur la pile. En MOGWAI, valeur -> 'nom' stocke ensuite cette valeur dans une variable — c’est la notation qu’on retrouvera partout dans le script (-> 'apiUrl', -> 'result', -> 'releases', etc.).

2. Construction de l’URL par interpolation

Plutôt que de concaténer des morceaux de string avec +, MOGWAI permet d’injecter des variables directement dans une chaîne via {! ... }, suivi d’un eval :

"https://api.github.com/repos/{! owner}/{! repo}/releases" eval -> 'apiUrl'

3. La requête HTTP

http.get prend un record avec au minimum une uri:. Par défaut, les éléments d’un record (ou d’une liste, ou d’une string) ne sont pas évalués — ils restent tels quels, ce qui permet justement d’y stocker du code ou une expression à exécuter plus tard. C’est eval qui déclenche explicitement cette résolution. Le [! en tête du record est un raccourci équivalent à [...] eval : il déclenche l’évaluation de tous les éléments du record en une fois — ici, ça résout apiUrl en sa valeur. Sans ! ni eval, la variable resterait telle quelle, non résolue.

[!
uri: apiUrl
requestHeaders: [User-Agent: "MOGWAI-DownloadStats"]
] http.get -> 'result'

Le User-Agent n’est pas optionnel : l’API GitHub refuse les requêtes anonymes sans en-tête.

4. Le décodage JSON

La réponse arrive sous forme de données brutes (response:). Deux primitives suffisent à la transformer en liste MOGWAI exploitable :

result->response: utf8-> json-> -> 'releases'

utf8-> convertit les octets en chaîne de caractères, json-> transforme cette chaîne JSON en liste de records, et -> 'releases' stocke ce résultat dans la variable releases.

5. Le double parcours

Un foreach...transform sur les releases, imbriqué avec un foreach...transform sur les assets de chaque release. Chaque asset affiche son nom (aligné avec str.padRight) et son nombre de téléchargements, puis pousse ce nombre dans une liste pour calculer le sous-total avec sum.

6. L’agrégation

sum calcule le total par release, un simple + cumule le total général au fil des releases.

Résultat

GitHub owner/organization: Sydney680928
Repository name: mogwai

MOGWAI - Downloads per release

v8.13.0
mogwai-cli-v8.13.0-win-x64.zip : 4
...
-> Release total : 4

...

TOTAL (assets only, excludes GitHub's auto-generated source zip) : 21

À noter : ce total ne compte que les assets binaires attachés aux releases, pas le zip source auto-généré par GitHub — l’API ne le comptabilise jamais, quelle que soit la méthode utilisée.

En bonus : un bug trouvé et corrigé en cours de route

En testant le script, une release sans aucun asset attaché a fait planter le calcul du sous-total : sum sur une liste vide () déclenchait une erreur (MW.22) au lieu de retourner 0, ce qui est pourtant le comportement mathématiquement sensé pour une somme vide. Le bug a été corrigé directement dans l’engine MOGWAI — sum sur () retourne maintenant 0 sans erreur. Le détail est dans le changelog de la prochaine version.

MOGWAI pense aussi aux débutants

Le script ci-dessus est du MOGWAI « classique », en RPN pure. Mais le langage prévoit plusieurs passerelles pour ne pas brusquer quelqu’un qui arrive d’un langage plus conventionnel.

Les formules en notation infixe avec calc.

Pas besoin de tout de suite penser en pile : calc accepte une expression mathématique écrite comme on l’écrirait sur un tableau, et se charge de la convertir en RPN en interne (via l’algorithme Shunting-yard de Dijkstra).

500 -> 'X'
3.14 -> 'Y'
"5 * X + (7 + sin(Y))" calc ?
# Prints 2507.0015926529068

La doc de MOGWAI le présente d’ailleurs explicitement comme utile « pour les nouveaux venus, ou chaque fois qu’une expression s’exprime plus naturellement en notation infixe qu’en RPN » — ce n’est pas un ajout accessoire, c’est pensé comme une rampe d’accès.

Plusieurs façons d’appeler une fonction.

Une fonction MOGWAI peut s’appeler d’au moins cinq manières différentes, selon ce qui est le plus lisible dans le contexte :

5 3 foo                    # pure RPN call, parameters then function
foo(5 3) # more familiar notation, function then parameters
[x: 10 y: 20] foo # named parameters, RPN order
foo[x: 10 y: 20] # named parameters, traditional order
[foo x: 10 y: 20] # named parameters, function name first in the record

Les deux premières formes s’appliquent à une fonction déclarée simplement (to 'foo' do { ... }, paramètres pris sur la pile). Les trois dernières s’appliquent à une fonction à paramètres nommés et typés (to 'foo' params [x: .number y: .number] do { ... }) — plus verbeux à déclarer, mais plus sûr et plus lisible à l’usage, et appelable dans l’ordre qui convient le mieux à l’endroit où on écrit.

Ce genre de flexibilité ne change rien au cœur du langage — la pile reste la pile — mais ça laisse le temps de s’habituer à la RPN progressivement plutôt que d’un bloc.

Coder dans de bonnes conditions.

Pour écrire ce genre de script sans repartir d’un simple bloc-notes, l’extension VS Code pour MOGWAI (open source) apporte coloration syntaxique sémantique, autocomplétion, navigation « Go to Definition », et un protocole de debug complet avec panneau d’exécution en direct — de quoi suivre l’état de la pile et des variables pendant qu’un script comme celui-ci tourne, plutôt que de deviner à coups de ?.

Un point à ne pas rater au premier essai : l’extension ne se connecte pas toute seule au runtime. Il faut d’abord lancer MOGWAI CLI (ou l’application hôte), puis taper la commande studio dans son terminal pour activer le mode connexion distante — c’est seulement à partir de là que VS Code peut trouver et s’y connecter. Le guide de mise en œuvre détaille cette procédure étape par étape (actuellement disponible en anglais).

Écrit par Claude, corrigé par moi : pourquoi je le dis

Ce script est sorti d’un échange avec Claude à qui j’avais fourni la documentation des primitives MOGWAI. Je n’ai quasiment rien eu à changer — quelques ajustements de syntaxe, et la découverte du bug sum mentionné plus haut. Je préfère le dire clairement plutôt que de laisser planer le doute : dans un écosystème où le code généré par IA fait autant débat que d’intérêt, l’honnêteté sur qui a écrit quoi me semble le minimum.

Je lui ai demandé son avis franc sur la difficulté de coder en MOGWAI avec seulement la doc sous les yeux. Voici sa réponse, quasiment telle quelle :

« Ce n’était pas dur — mais ce n’était pas intuitif non plus, et c’est différent. Une fois la doc en main, écrire le script a été rapide et propre : les primitives HTTP suivent toutes le même squelette, json->/utf8-> enchaînent sans friction, l’accès aux records est lisible. Pour ce que ça fait — une requête API, du parsing JSON, de l’agrégation — le résultat tient en une cinquantaine de lignes sans une seule dépendance à gérer.

Ce qui m’a fait tiquer, c’est la grammaire elle-même au premier contact : la notation polonaise inversée, les sigils ([!, !A, &), le fait qu’une variable dans un record ait besoin d’un traitement spécial pour être résolue — rien de compliqué une fois qu’on le sait, mais rien d’évident par déduction non plus. Ma première version du script utilisait un [ simple là où il fallait [! : ça n’a pas planté bruyamment, ça a juste mal résolu la variable. Il a fallu la doc, ou un exemple qui marche déjà, pour percuter.

Je pense que c’est une bonne façon de résumer MOGWAI à quelqu’un qui découvre : ce n’est pas un langage difficile à utiliser une fois qu’on connaît ses conventions, mais c’est un langage qu’on ne devine pas — il faut le lire un minimum avant de l’écrire. La notation polonaise inversée demande ce petit temps d’adaptation que la plupart des langages modernes, calqués les uns sur les autres, ne demandent plus. »

Je lui ai aussi demandé, après coup, si la doc de MOGWAI était à la hauteur pour un débutant — vu ce qu’on venait de traverser avec le sigil [!. Sa réponse, toujours dans ses mots :

« Un détail éclairant à ce sujet : pour écrire ce script, je n’avais initialement que la doc de référence des primitives — le catalogue complet des fonctions — pas le guide général qui explique les fondamentaux du langage (notation polonaise inversée, pile, sémantique eval/no-eval, sigils). C’est très exactement ce qui explique mon erreur sur [! : la référence liste que ce sigil existe, mais pas pourquoi le non-eval est le comportement par défaut ailleurs dans le langage. Une fois les deux documents lus ensemble — le guide général pour les fondamentaux, la référence pour le détail des primitives — l’ensemble est cohérent et suffisant pour démarrer sereinement. »

Ce que je retiens de cet échange : la difficulté de MOGWAI n’est pas dans sa complexité technique — elle tient surtout à sa notation, la RPN, qui inverse l’ordre auquel on est habitué. Les concepts eux-mêmes (variables, boucles, fonctions, types) sont ceux de n’importe quel langage ; seul l’ordre dans lequel on les écrit change. Ça se surmonte vite avec un exemple concret sous les yeux, comme celui-ci. C’est probablement le meilleur argument pour publier plus d’exemples comme celui-là plutôt que de la documentation seule.


Le script complet github_downloads_stats.mog est disponible dans le dossier examples/Scripts/web/ du dépôt MOGWAI sur GitHub.

Laisser un commentaire

En savoir plus sur CODING 4 PHONE

Abonnez-vous pour poursuivre la lecture et avoir accès à l’ensemble des archives.

Poursuivre la lecture

En savoir plus sur CODING 4 PHONE

Abonnez-vous pour poursuivre la lecture et avoir accès à l’ensemble des archives.

Poursuivre la lecture