Sachant que les contributeurs actuels sont tous francophones, ce fichier sera écrit en français.
Un git clone
du dépôt est suffisant, mais il est conseillé d'y ajouter un
paramètre supplémentaire, pour distinguer l'ancien dépôt du nouveau:
git clone [email protected]:Inist-CNRS/web-services.git github-web-services
Ainsi, votre répertoire se nommera github-web-services
, et sera facilement
distingué du répertoire web-services
correspondant au dépôt tdm/web-services
sur le GitBucket de l'Inist.
Les scripts utilisés par ce dépôt sont pour la plupart écrits en node.
Pour profiter du système des workspaces, il faut npm 7+.
Il faut donc s'assurer d'avoir node 22+ (voir .nvmrc).
Il est conseillé d'installer node via nvm, et
de se conformer à la version inscrite dans le fichier .nvmrc.
Pour cela: nvm install
.
Pour plus d'information, voir la documentation de
nvm. Il existe même un moyen de passer
automatiquement à la version demandée, en arrivant à la racine du répertoire:
nvm / Deeper Shell
integration.
Pour VSCode, il est recommandé d'accepter l'installation des extensions:
La branche principale (main
) du dépôt est protégée.
Ça signifie que pour contribuer au dépôt, il faut passer par le mécanisme des
pull requests.
Et pour créer une pull request (ou contribution), il faut d'abord créer une branche.
Son nom est important, car il permettra aux GitHub Actions automatiques d'obtenir des informations sur la partie du dépôt qui est travaillée.
Les noms des branches auront 3 parties:
services
pour indiquer qu'on travaille dans le répertoire des services- le nom du service (ou de l'image de base) concerné(e) (en deux parties
séparées par un tiret, suivant la convention de nommage des containers dans
ezmaster), correspondant au nom du
répertoire (donc sans
ws-
) - le détail de l'opération. C'est un commentaire (où il faut séparer les mots par des tirets)
Chacune de ces parties sera écrite en minuscules, sans accent, sans espace, et
elles seront séparées par le caractère /
.
Par exemple, pour améliorer le service base-line
, et lui ajouter une route
v1/lowercase
, on pourrait créer une branche nommée
services/base-line/add-route-lowercase
.
Ainsi, c'est le service base-line
qui sera concerné par les actions
automatiques.
D'autres exemples de noms de branche:
services/base-line-python/make-python-script-executable
services/base-line/change-required-input-for-no-accent
services/terms-teeft/add-teeft-with-number
docs/contributing/add-new-branch
Remarque : seules branches commençant par
services/
et contenant deux/
déclencheront l'action de test du service.
Remarque : comme nous construisons des programmes open source, tâchons de garder tout ce qui est technique (ça peut exclure la documentation elle-même) en anglais.
Avant toute chose, il faut s'assurer qu'un service qui pourrait accueillir votre nouvelle route n'existe pas déjà. Cela évitera de créer un nouveau service.
À noter: les sous-sections suivantes expliquent la structure du répertoire à
créer pour un service, mais le script
generate:service
se charge maintenant
d'initialiser le répertoire pour vous. Voir Script d'initialisation d'un
nouveau service
📘 Ceci est maintenant automatique quand on utilise le script
generate:service
. Voir Script d'initialisation d'un nouveau service
Tous les services sont dans le répertoire services
.
Chacun dans son propre répertoire.
Son nom suit la convention de nommage des instances ezmaster: au moins deux
parties composées de lettres minuscules (et éventuellement de chiffres, mais ce
n'est pas conseillé, à cause de la confusion avec le numéro de version de
l'instance). Par exemple :base-line
, astro-ner
, ...
Pour profiter du système de workspaces de npm, il faut déclarer le répertoire
du nouveau service dans le package.json
situé à la racine du dépôt.
Par exemple, voici les services base-line
et base-line-python
déclarés dans
le package.json
:
{
"workspaces": [
"services/base-line",
"services/base-line-python"
]
}
Ainsi, vous serez capable de lancer des scripts d'un service (par exemple
base-line
) depuis la racine du dépôt (à condition de disposer de npm 7+):
npm -w services/base-line run start:dev
npm -w services/base-line run stop:dev
📘 Ceci est maintenant automatique quand on utilise le script
generate:service
.
Chaque répertoire de service contient :
- un répertoire
v1
(ouv2
, ...) contenant son code source (contenant les.ini
, dans un arbre plus ou moins profond qui détermine les futures routes du service). - un fichier
Dockerfile
qui part d'une imageezs-python-server
- un fichier
.dockerignore
(le même que celui deezs-python-server
, mais dans lequel on ajoute les fichiers sources) - le cas échéant, un fichier
config.json
contenant la configuration par défaut de l'image (quand le service a besoin d'une configuration particulière). - un fichier
package.json
, sur le modèle de celui deezs-python-server
, oùezs-python-server
est remplacé par le nom du service (celui du répertoire, précédé dews-
; exemple:ws-base-line
), et où on réinitialise la version à0.0.0
. - un fichier
swagger.json
dans lequel on modifie le title (devant commencer par le nom du service, par exemplebase-line -
, c'est ce qui déterminera le tri d'affichage des services dans l'OpenAPI). - un fichier
README.md
expliquant en quoi consiste le service. - un fichier
examples.http
avec un exemple de requête pour chaque route - un fichier
tests.hurl
généré à partir des exemples, pour éviter les régressions du service
Le fichier examples.http
se situe à la racine d'une instance (et donc de son
répertoire).
📘 Ce fichier est initialisé automatiquement par le script
generate:service
.
Il reste nécessaire d'écrire les requêtes pour chaque route créée.
Il contient des exemples de requêtes HTTP, et constitue donc une partie de la
documentation du service.
Il sert de base à la génération de métadonnées d'exemple en notation pointée
qu'on peut généralement ajouter sans modification dans le .ini
(via le script
generate:example-metadata
).
De plus, il sert aussi à générer les tests (via le script
generate:example-tests
), il est donc
doublement important de bien le renseigner.
Le début du fichier examples.http
(attention, ce nom est utilisé dans
plusieurs scripts, veillez à bien l'orthographier) contient une commentaire
explicatif, et une variable permettant de changer le serveur cible des requêtes:
# These examples can be used directly in VSCode, using REST Client extension (humao.rest-client)
# Décommenter/commenter les lignes voulues pour tester localement
@host=http://localhost:31976
# @host=https://base-line.services.istex.fr
Ensuite viennent les requêtes elles-mêmes.
Le début d'une requête est signalé par une ligne contenant uniquement ###
.
Puis, on assigne un identifiant (un name
) à la requête. Cet identifiant doit
être unique et facile à reconstituer, il est donc conseillé de le construire à
partir de la route de la requête.
Par exemple, la route /v1/true/json
donnera lieu à un name
valant
v1TrueJson
:
###
# @name v1TrueJson
# On met ici un commentaire décrivant ce que fait la route appelée
Après ces commentaires viennent les lignes décrivant la requête:
POST {{host}}/v1/true/json HTTP/1.1
Content-Type: application/json
[
{ "value": "à l'école" },
{ "value": "où" }
]
En général on utilise la méthode HTTP POST
, et le Content-Type: application/json
(c'est le type du body envoyé), puis le tableau JSON envoyé
(et en général, il contient un ou plusieurs objets avec un champ value
).
Remarque: comme ces exemples serviront aussi aux tests, il est utile d'y mettre aussi des exemples dont on veut vérifier le comportement.
Le fichier services/<instance>/tests.hurl
est la plupart du temps généré (sauf
pour les enchaînements de services qu'on a dans les data-*
).
Pour ça, il faut d'abord lancer le serveur en local dans un terminal:
npm -w services/<instance> run start:dev
ou bien
npx ezs -v -m -d services/<instance>/
puis lancer la génération des tests (depuis la racine du dépôt) dans un autre terminal:
npm run generate:example-tests services/<instance>
Remarque: le fichier
services/<instance>/examples.http
doit exister et contenir au moins un exemple.
Voir examples.http
Ce fichier servira lors d'un push sur GitHub à tester toutes les routes du
service en question, pour s'assurer de leur non-régression.
Pour que ce soit utile, toutes les routes doivent être testées.
On peut aussi tester le serveur local.
📘 On peut aussi écrire ce fichier à la main, voir hurl.
Pour faciliter la création d'un nouveau service, un script npm est disponible:
generate:service
.
Il prend en paramètre le nom du service (tout en minuscules, en deux parties
séparées par un tiret).
Il demande le titre du service (short description), sa description (long
description), le nom de l'auteur et mail.
Il crée le répertoire services/service-name
, l'ajoute dans les workspaces du
dépôt, et dans la liste des services à la fin du README.
Exemple:
npm run generate:service service-name
⚠ Ne pas mettre de caractère
&
dans les réponses, ça provoque un remplacement bizarre.
Pour avoir une documentation OpenAPI complète, on peut écrire la description
d'un service en Markdown.
On peut se contenter d'écrire cette description dans la métadonnée
post.description
directement, en mettant les lignes bout-à-bout, séparées par
^M
.
Mais il est plus simple d'utiliser le script ./bin/insert-description.sh
, qui
prend en paramètres un ou plusieurs chemins de fichiers Markdown (.md
).
Pour chaque fichier .md
, il insère le contenu dans le fichier dont le chemin
correspond au nom du .md
(en remplaçant les _
par des /
).
Exemples:
./bin/insert-description.sh services/terms-extraction/v1*.md
./bin/insert-description.sh services/terms-extraction/v1_teeft_fr.md
Alternative: utiliser le script npm insert:description
:
$ npm run insert:description services/terms-extraction/v*.md
> [email protected] insert:description
> ./bin/insert-description.sh services/terms-extraction/v1_teeft_en.md services/terms-extraction/v1_teeft_fr.md services/terms-extraction/v1_teeft_with-numbers_en.md services/terms-extraction/v1_teeft_with-numbers_fr.md
- services/terms-extraction/v1/teeft/en.ini ✓
- services/terms-extraction/v1/teeft/fr.ini ✓
- services/terms-extraction/v1/teeft/with-numbers/en.ini ✓
- services/terms-extraction/v1/teeft/with-numbers/fr.ini ✓
Note: si vous voulez bénéficier de l'auto-complétion des chemins de fichiers, utilisez plutôt
./bin/insert-description.sh
.
DVC est un outil de versionnage de données. Lorsqu'on crée un service qui nécessite un modèle ou une table, il est nécessaire de l'utiliser pour ne pas avoir de gros fichiers sur git. En plus du reste, il faut suivre ces étapes lorsqu'on utilise DVC :
-
S'assurer d'avoir déposé les données sur le webdav du service TDM en ayant préalablement utilisé DVC (pour cela : )
- mettre son fichier nommé
DOSSIER_OU_FICHIER_A_PUSH
dans un autre dossier. - Initier un dépôt DVC en faisant
dvc init
(nécessite d'être dans un dépôt git). - se connecter au webdav du service (à ne faire que la première fois), pour cela :
- spécifier l'url du webdav (en utilisant le protocole webdavs):
dvc remote add -d webdav-remote webdavs://YOUR_WEBDAV_URL.fr
- entrer le login :
dvc remote modify --local webdav-remote login YOUR_LOGIN
- entrer le mot de passe :
dvc remote modify --local webdav-remote password YOUR_PASSWORD
- spécifier l'url du webdav (en utilisant le protocole webdavs):
- push le fichier sur le webdav :
dvc add DOSSIER_OU_FICHIER_A_PUSH
puisdvc push
, sans se soucier du nom - Le fichier
DOSSIER_OU_FICHIER_A_PUSH.dvc
est créé et devra être copié à l'endroit où le modèle doit être dans le code - remarque : il est possible de faire tout ça dans le dépôt git directement, cela ajoutera simplement un
.gitignore
qu'il ne faudra pas déplacer ou supprimer. Aussi, les dossiers.dvc
et.dvcignore
ne seront ni à ignorer par git, ni a push sur le dépôt
- mettre son fichier nommé
-
Créer un fichier
.env
à la racine du service (./services/\<service-name\>/.env
) qui ressemblera à ça :export WEBDAV_URL=webdavs://YOUR_WEBDAV_URL.fr export WEBDAV_LOGIN=YOUR_LOGIN export WEBDAV_PASSWORD=YOUR_PASSWORD
-
modifier les scripts
build:dev
etbuild
du fichierpackage.json
du service-
pour
build:dev
:". ./.env 2> /dev/null; DOCKER_BUILDKIT=1 docker build -t cnrsinist/${npm_package_name}:latest --secret id=webdav_login,env=WEBDAV_LOGIN --secret id=webdav_password,env=WEBDAV_PASSWORD --secret id=webdav_url,env=WEBDAV_URL ."
-
pour
build
:". ./.env 2> /dev/null; DOCKER_BUILDKIT=1 docker build -t cnrsinist/${npm_package_name}:${npm_package_version} --secret id=webdav_login,env=WEBDAV_LOGIN --secret id=webdav_password,env=WEBDAV_PASSWORD --secret id=webdav_url,env=WEBDAV_URL ."
-
-
modifier le
Dockerfile
en conséquence, en s'inspirant du Dockerfile deaffiliation-rnsr
-
arrêter le conteneur.
-
reconstruire l'image :
npm -w services/service-name run build:dev
-
relancer le conteneur
Pour lancer le serveur ezs en dehors de docker:
- se placer à la racine du dépôt
- lancer
npx ezs -v -m -d services/nom-du-service/
Évidemment, il faut avoir au préalable configuré la bonne version de node (celle
qui correspond aux images de base) et lancé npm install
depuis la racine du
dépôt.
Il est conseillé d'installer node via nvm, et
de se conformer à la version inscrite dans le fichier .nvmrc.
Pour cela: nvm install
.
Pour plus d'information, voir la documentation de
nvm. Il existe même un moyen de passer
automatiquement à la version demandée, en arrivant à la racine du répertoire:
nvm / Deeper Shell
integration.
Dans le cas d'un service écrit en python, ne pas oublier d'activer l'environnement virtuel où sont installées les dépendances (à créer à la racine du service).
cd services/<service-name>
# Création de l'environnement virtuel
python3 -m venv .venv
# Activation de l'environnement virtuel
source .venv/bin/activate
Pour construire l'image avec le tag latest
:
npm -w services/service-name run build:dev
Pour lancer l'image:
npm -w services/service-name run start:dev
Pour arrêter le serveur:
npm -w services/service-name run stop:dev
ou bien:
docker stop dev
Pour tester un service lancé localement, utiliser:
npm run test:local service-name
Pour tester un service en production, taper:
npm run test:remote service-name
Pour tester tous les services en production qui ont un fichier
tests.hurl
:
npm run test:remotes services/*
Pour tester uniquement certains services en production (à condition qu'ils aient
un fichier tests.hurl
):
npm run test:remotes service-name service2-name
📘 Pour éviter qu'un service soit testé lorsqu'il est en production, on peut positionner la propriété
avoid-testing
dupackage.json
du service àtrue
.Exemple de cas où c'est utile:
ark-tools
, où on crée des identifiants censés être uniques. Afin de ne pas épuiser les possibilités, on évite de le tester trop souvent.
Pour tester un service qui est sur une machine de production, mais pas encore
publié (sans URL externe), en se basant sur l'URL présente dans swagger.json
(et en remplaçant le nom de la machine par son IP interne, si on la connaît):
./bin/test-ip-services.sh services/service-name/
Une fois que le nouveau service est créé, il faut l'ajouter à la liste du README de la racine du dépôt.
📘 Ceci est automatique quand on utilise le script
generate:service
.
⚠ Cette partie ne concerne pas directement l'écriture des services, mais plus le mainteneur des images de base.
Le répertoire bases
contient les images de base, c'est-à-dire celles qui
simplifient l'écriture de plusieurs services web.
Quand on met à jour les paquets npm de l'image racine ezs-python-server
, il ne
faut pas oublier de changer les versions des paquets du package.json
situé à
la racine du dépôt (pour que les serveurs lancés localement utilisent les mêmes
versions que les serveurs sous Docker).
De même, il faut mettre à jour tous les web services qui utilisent ces images de base! Pour lister les services concernés par une image de base:
grep ezs-python-server services/*/Dockerfile template/Dockerfile bases/*/Dockerfile
Il faut changer le FROM
de tous les Dockerfile
trouvés par la commande, et
ne pas oublier de:
-
créer une nouvelle version de l'image de base modifiée:
cd bases/image-a-modifier npm version patch|minor|major
-
pour chaque
service-name
modifié, lancer:npm -w services/service-name version patch
Il y a plusieurs images de base:
python-node
: image avec python et node, base des serveurs ezsezs-python-server
: serveur ezs vide, acceptant les scripts ezs et pythonezs-python-saxon-server
: serveur ezs vide, acceptant les scripts ezs et python, embarquant saxon, sous la forme de la commandexslt
.
Note: il existe maintenant un script qui se charge de la mise à jour des images qui dépendent directement d'une image de base:
npm run update:images <image-name>
. Assurez-vous que l'image a été créée (version, build, push) avant de lancer le script.
Pour créer une version, on peut se servir de npm et du workspace associé au service en question.
Exemple: npm -w services/service-nme version patch
.
L'argument de npm version
est major
, minor
ou patch
suivant qu'il y a un
changement majeur, un ajout de fonctionnalité ou une correction.
Cela va créer un tag, modifier le numéro de version dans le README, et pousser le tout sur GitHub, déclenchant une action de Github qui poussera automatiquement l'image sur Docker Hub.
Remarque: on peut aussi créer la version manuellement. Pour ça il faut se déplacer dans le répertoire du
Dockerfile
et lancernpm version
en utilisant l'argumentmajor
,minor
oupatch
suivant qu'il y a un changement majeur, un ajout de fonctionnalité ou une correction.
Pour la mise en production d'un service, il faut modifier son fichier
swagger.json
.
Il faut transformer cette partie:
"servers": [
{
"x-comment": "Will be automatically completed by the ezs server."
},
{
"url": "http://vptdmservices.intra.inist.fr:49233/",
"description": "Latest version for production",
"#DISABLED#x-profil": "Standard"
}
],
en
"servers": [
{
"x-comment": "Will be automatically completed by the ezs server."
},
{
"url": "http://vptdmservices.intra.inist.fr:49245/",
"description": "Latest version for production",
"x-profil": "Standard"
}
],
Où:
- on enlève
#DISABLED#
devantx-profil
, en vérifiant que la valeur de ce champ est bienStandard
, - on ajuste le champ
url
du même objet pour pointer sur l'URL interne du container sur la machine de production.
Puis, on lance ./bin/publish.sh
, qui demande les login et mot de passe de la
machine du reverse proxy.
Warning
La procédure durant la phase de transition de
GitBucket à GitHub était plus
complexe, et permettait la publication (via make publish
) avant d'avoir
fusionné la Pull Request.
La nouvelle manière de faire implique que la PR soit fusionnée dans la branche
principale, afin de ne pas configurer le reverse proxy avec des
swagger.json
obsolètes.