diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c90b23b70625a2c7a904c305eccba4c95b2c49a0..7edc015fbcd77dbbf7b43c46fe987b12f6ef2b47 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,25 +1,29 @@
 stages:
-  - jalhyd
   - install
   - test
   - build
-  - release-linux
-  - release-windows
-  - release-mac
-  - release-android
+  - deploy
+  - deploy-stable
+  - releases-nightly
+  - releases-version
 
 variables:
   LC_ALL: C.UTF-8
   ANDROID_HOME: "/opt/android"
   ANDROID_SDK_ROOT: "/opt/android"
+  DEPLOY_HOST_LOGIN: "nghyd@aubes"
+  DEPLOY_URL: "/var/www/html/cassiopee"
+  DEPLOY_STABLE_URL: "/var/www/cassiopee-production"
+  RELEASES_URL: "$DEPLOY_STABLE_URL/cassiopee-releases"
 
 cache:
   paths:
     - node_modules/
 
 jalhyd:
-  stage: jalhyd
+  stage: install
   only:
+    - pushes
     - tags
     - schedules
     - web
@@ -34,9 +38,10 @@ jalhyd:
     - npm install
     - npm run build
 
-install:
+npm-install:
   stage: install
   only:
+    - pushes
     - tags
     - schedules
     - web
@@ -44,15 +49,6 @@ install:
     - rm -rf node_modules
     - npm install
 
-build:
-  stage: build
-  only:
-    - tags
-    - schedules
-    - web
-  script:
-    - npm run build
-
 test:
   stage: test
   only:
@@ -62,42 +58,54 @@ test:
   script:
     - npm run e2e
 
-release-linux:
-  stage: release-linux
+build:
+  stage: build
   only:
+    - pushes
     - tags
     - schedules
     - web
   script:
-    - npm run release-linux
-    - find release -name "fr.irstea.cassiopee_*.deb" -exec scp "{}" nghyd@aubes:/var/www/cassiopee-production/cassiopee-releases/linux-nightly.deb \;
+    - npm run build -- --base-href=/cassiopee/$CI_COMMIT_REF_NAME
 
-release-windows:
-  stage: release-windows
+deploy:
+  stage: deploy
   only:
+    - pushes
     - tags
-    - schedules
     - web
   script:
-    - npm run release-windows
-    - find release -name "Cassio*Setup*.exe" -exec scp "{}" nghyd@aubes:/var/www/cassiopee-production/cassiopee-releases/windows-nightly.exe \;
+    # Copie de la branche / du tag
+    - rsync -a dist/ $DEPLOY_HOST_LOGIN:$DEPLOY_STABLE_URL/
 
-release-mac:
-  stage: release-mac
+deploy-stable:
+  stage: deploy-stable
   only:
     - tags
+    variables:
+      - $CI_COMMIT_REF_NAME == "stable"
+  script:
+    # Copie de la branche production
+    - rsync -a dist/ $DEPLOY_HOST_LOGIN:$DEPLOY_STABLE_URL/
+    # Modification du dossier base href
+    - ssh $DEPLOY_HOST_LOGIN "sed -i 's:/cassiopee/stable/:/:g' $DEPLOY_STABLE_URL/index.html"
+
+releases-nightly:
+  stage: releases-nightly
+  only:
     - schedules
-    - web
   script:
-    - npm run release-mac
-    - find release -name "Cassio*-mac.zip" -exec scp "{}" nghyd@aubes:/var/www/cassiopee-production/cassiopee-releases/macos-nightly.zip \;
+    - npm run release-all
+    - find release -name "fr.irstea.cassiopee_*.deb" -exec scp "{}" $DEPLOY_HOST_LOGIN:$RELEASES_URL/linux-nightly.deb \;
+    - find release -name "Cassio*Setup*.exe" -exec scp "{}" $DEPLOY_HOST_LOGIN:$RELEASES_URL/windows-nightly.exe \;
+    - find release -name "Cassio*-mac.zip" -exec scp "{}" $DEPLOY_HOST_LOGIN:$RELEASES_URL/macos-nightly.zip \;
+    - find release -name "cassiopee-*.apk" -exec scp "{}" $DEPLOY_HOST_LOGIN:$RELEASES_URL/android-nightly.apk \;
 
-release-android:
-  stage: release-android
+releases-version:
+  stage: releases-version
   only:
     - tags
-    - schedules
-    - web
+    variables:
+      - $CI_COMMIT_REF_NAME =~ /^[0-9]+\.[0-9]+\.[0-9]+$/ # version tag
   script:
-    - npm run release-android
-    - find release -name "cassiopee-*.apk" -exec scp "{}" nghyd@aubes:/var/www/cassiopee-production/cassiopee-releases/android-nightly.apk \;
+    - nodejs scripts/release-version.sh $CI_COMMIT_REF_NAME $DEPLOY_HOST_LOGIN $RELEASES_URL
diff --git a/scripts/release-version.sh b/scripts/release-version.sh
new file mode 100755
index 0000000000000000000000000000000000000000..128616f6f8d64d600977b099bb84f9c726a356a2
--- /dev/null
+++ b/scripts/release-version.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+# Fabrique les exécutables electron/cordova pour une version de Cassiopée $1, les
+# distribue sur le serveur $2 dans le dossier $3, et met à jour le fichier releases.json
+
+if [ "$#" -lt 3 ]; then
+    echo "usage: $0 X.Y.Z login@server /var/www/releases_dir"
+    exit 1
+fi
+
+VERSION="$1"
+HOST_LOGIN="$2"
+RELEASES_DIR="$3"
+# RELEASES_DIR="/tmp/cassiopee-releases" # debug
+RELEASES_FILE="$RELEASES_DIR/releases.json"
+TMP_RELEASES_FILE="tmp-releases.json"
+
+echo "deploy-version.sh: building release for version $VERSION, deploying in $HOST_LOGIN:$RELEASES_DIR"
+
+# build releases
+npm run release-all
+
+# update existing releases file
+
+# fetch current releases file
+scp "$HOST_LOGIN:$RELEASES_FILE" "./$TMP_RELEASES_FILE"
+
+grep -P "\"latest\": \"$VERSION\"" "$TMP_RELEASES_FILE"
+
+if [[ $? == 0 ]]
+then
+    echo "$VERSION est déjà la version la plus récente, pas de mise à jour du fichier releases.json"
+else
+    sed -i -E "s/\"latest\": .+,/\"latest\": \"$VERSION\",/" "$TMP_RELEASES_FILE"
+    echo -e "\t\"$VERSION\": {
+\t\t\"darwin\": \"Cassiopée-${VERSION}-mac.zip\",
+\t\t\"linux\": \"fr.irstea.cassiopee_${VERSION}_amd64.deb\",
+\t\t\"win32\": \"Cassiopée Setup $VERSION.exe\",
+\t\t\"android\": \"cassiopee-$VERSION.apk\"
+\t}," > releases_patch.tmp
+
+    sed -i "/\"latest\": \"$VERSION\",/r releases_patch.tmp" "$TMP_RELEASES_FILE"
+    rm releases_patch.tmp
+fi
+
+# send updated file, remove local copy
+scp "./$TMP_RELEASES_FILE" "$HOST_LOGIN:$RELEASES_FILE"
+rm "./$TMP_RELEASES_FILE"
+
+# copy releases to public web directory
+scp "release/Cassiopée Setup $VERSION.exe" "release/fr.irstea.cassiopee_${VERSION}_amd64.deb" "release/cassiopee-$VERSION.apk" "release/Cassiopée-${VERSION}-mac.zip" "$HOST_LOGIN:$RELEASES_DIR/"
+
+# symlink "latest" version for each platform
+ssh $HOST_LOGIN /bin/bash << "EOF"
+    cd "$RELEASES_DIR"
+    ln -sf "Cassiopée Setup $VERSION.exe" "windows-latest.exe"
+    ln -sf "fr.irstea.cassiopee_${VERSION}_amd64.deb" "linux-latest.deb"
+    ln -sf "Cassiopée-${VERSION}-mac.zip" "macos-latest.zip"
+    ln -sf "cassiopee-$VERSION.apk" "android-latest.apk"
+EOF