diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 93659703a0c2039ceefb0f06008028816a3da170..97a8064fe9d042d51180def786b3ee340fd8ee2f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -3,15 +3,91 @@ variables:
     GIT_SUBMODULE_FORCE_HTTPS: "true"
     SECURE_FILES_DOWNLOAD_PATH: './'
 
+    VERSION_DEV_SUFFIX: '-dev'
+
+    GITLAB_API_PROJECT_URL: ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}
+
+    PROJECT_FOLDER: ExpressAPI
+
+
+.get_version:
+    script:
+        - IS_DEV=$([[ $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH ]] && echo false || echo true)
+        - VERSION=$(jq -r .version $PROJECT_FOLDER/package.json)$([[ $IS_DEV == true ]] && echo $VERSION_DEV_SUFFIX || echo '')
+
+.clean_release:
+    script:
+        # Delete release if it already exists
+        - 'curl --request DELETE --header "JOB-TOKEN: $CI_JOB_TOKEN" "${GITLAB_API_PROJECT_URL}/releases/${VERSION}"'
+        # Delete tag if it already exists (use private-token because job-token don't have permission to delete tags)
+        - 'curl --request DELETE --header "PRIVATE-TOKEN: $GITLAB_PROJECT_ACCESS_TOKEN" "${GITLAB_API_PROJECT_URL}/repository/tags/${VERSION}"'
+
+
 stages:
     - test
+    - clean
+    - release
 
-build:test:
-    image: node:latest
+
+test:build:
     stage: test
+    image: node:latest
     tags:
         - build
     script:
         - cd ExpressAPI
         - npm install
-        - npm run build
\ No newline at end of file
+        - npm run build
+    rules:
+        - if: '$CI_COMMIT_TAG =~ "/^$/"'
+
+clean:release:
+    stage: clean
+    tags:
+        - gitlab_clean
+    image: registry.gitlab.com/gitlab-ci-utils/curl-jq:latest
+    script:
+        - !reference [.get_version, script]
+        - !reference [.clean_release, script]
+    rules:
+        - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /^v[0-9]+(\.[0-9]+)*$/'
+
+clean:dev:release:
+    stage: clean
+    tags:
+        - gitlab_clean
+    image: registry.gitlab.com/gitlab-ci-utils/curl-jq:latest
+    script:
+        - !reference [.get_version, script]
+        - VERSION="${VERSION}${VERSION_DEV_SUFFIX}"
+        - !reference [.clean_release, script]
+    rules:
+        - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
+
+release:gitlab:
+    stage: release
+    tags:
+        - release
+    image: registry.gitlab.com/gitlab-ci-utils/curl-jq:latest
+    script:
+        - !reference [.get_version, script]
+        - echo 'Running release_job'
+        # Extract description from CHANGELOG.md
+        - CHANGELOG_LINE_START=`awk '/##\ [0-9]+\.[0-9]+\.[0-9]+/{print NR; exit;}' CHANGELOG.md`
+        - CHANGELOG_LINE_END=`awk '/##\ [0-9]+\.[0-9]+\.[0-9]+/{ count++; if(count>1) {print NR; exit;}}' CHANGELOG.md`
+        - DESCRIPTION=`awk 'NR > '$CHANGELOG_LINE_START' && NR < '$CHANGELOG_LINE_END'' CHANGELOG.md`
+        # Create Release (can't be done by release_step of gitlab image because it don't have access to env var defined in script_step)
+        - >
+            RELEASE_DATA=$(jq --null-input --arg version "$VERSION" --arg description "# Changelog (version $VERSION) $DESCRIPTION" --arg tag_name "$VERSION" --arg ref "$CI_COMMIT_SHORT_SHA" '{
+                "name": $version,
+                "description": $description,
+                "tag_name": $tag_name,
+                "ref": $ref
+            }')
+        - >
+            curl --data "${RELEASE_DATA}" \
+              --header "Content-Type: application/json" \
+              --header "JOB-TOKEN: $CI_JOB_TOKEN" \
+              --request POST "${GITLAB_API_PROJECT_URL}/releases"
+    rules:
+        - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH || $CI_COMMIT_BRANCH =~ /^v[0-9]+(\.[0-9]+)*$/'
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..8fda360c76688893c3f1b2cd6eb0ccdd60dd874b
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,35 @@
+# Changelog
+
+<!--
+### ✨ Feature
+### 🤏 Minor change
+### 🎨 Interface
+### 🐛 Bugfix
+### 🔒 Security
+### 🚀‍️ CI / CD
+### 🔨 Internal / Developers
+### 📚 Documentation
+
+**💥 Breaking:**
+**⚠️ Deprecation:**
+-->
+
+## 2.0.0 (2023-09-15)
+
+### ✨ Feature
+- Added license: AGPLv3
+
+### 🎨 Interface
+- **💥 Breaking:** Renamed `enonce` to `assignment`
+- **💥 Breaking:** Renamed `exercice` to `exercise`
+
+### 🔨 Internal / Developers
+- Auto release by pipeline
+    - For vX.Y.Z branch create release and tag with `-dev` suffix
+    - For main branch create definitive release and remove `-dev` releases
+
+
+## 1.0.1 (2023-08-12)
+
+### ✨ Feature
+- 🎉 Initial release of the project
\ No newline at end of file