diff --git a/.gitlab/releaser.py b/.gitlab/releaser.py
new file mode 100644
index 0000000000000000000000000000000000000000..97dd7e9489035552176950b5178c8ee1b67447d4
--- /dev/null
+++ b/.gitlab/releaser.py
@@ -0,0 +1,154 @@
+#!/usr/bin/python3
+'''
+This module is meant to overload the release note in gitlab for the current project.
+Expects to find in environment following variables:
+  - CI_PROJECT_URL - Automatically set by gitlab-ci
+  - CI_COMMIT_TAG - Automatically set by gitlab-ci
+  - CI_PROJECT_ID - Automatically set by gitlab-ci
+  - CI_COMMIT_TAG - Automatically set by gitlab-ci
+  - RELEASER_TOKEN - Token used by technical user
+  - JOB_ARTIFACTS - String containing job name containing all artifacts, to set manually
+  - EXPECTED_ARTIFACTS - List containing all artifacts generated to set manually
+'''
+
+import math
+import urllib.request
+import urllib.error
+import json
+import os
+import jinja2
+
+def convert_size(size_bytes):
+    '''Print proper size'''
+    if size_bytes == 0:
+        return '0B'
+    size_name = ('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB')
+    i = int(math.floor(math.log(size_bytes, 1024)))
+    power = math.pow(1024, i)
+    size = round(size_bytes / power, 2)
+    return '%s %s' % (size, size_name[i])
+
+def get_current_message():
+    '''Get current release message'''
+    releaser_token = os.environ['RELEASER_TOKEN']
+    ci_project_id = os.environ['CI_PROJECT_ID']
+    ci_commit_tag = os.environ['CI_COMMIT_TAG']
+    tag_url = 'https://git.duniter.org/api/v4/projects/'
+    tag_url += ci_project_id
+    tag_url += '/repository/tags/'
+    tag_url += ci_commit_tag
+    request = urllib.request.Request(tag_url)
+    request.add_header('Private-Token', releaser_token)
+    response = urllib.request.urlopen(request)
+    data = json.load(response)
+    if data['release'] is None:
+        return False, ''
+    else:
+        return True, data['release']['description'].split('# Downloads')[0]
+
+def build_artifact_url(artifact, source):
+    '''Given an artifact name, builds the url to download it'''
+    job_artifacts = os.environ['JOB_ARTIFACTS']
+    ci_project_url = os.environ['CI_PROJECT_URL']
+    ci_commit_tag = os.environ['CI_COMMIT_TAG']
+    if source:
+        source_url = ci_project_url
+        source_url += '/repository/'
+        source_url += ci_commit_tag
+        source_url += '/archive.'
+        source_url += artifact
+        return source_url
+    else:
+        artifact_url = ci_project_url
+        artifact_url += '/-/jobs/artifacts/'
+        artifact_url += ci_commit_tag
+        artifact_url += '/raw/'
+        artifact_url += artifact
+        artifact_url += '?job='
+        artifact_url += job_artifacts
+        return artifact_url
+
+def get_artifact_weight(location):
+    '''Retrieve size of artifacts'''
+    size = os.path.getsize(location)
+    return convert_size(int(size))
+
+
+def build_compiled_message(current_message):
+    '''Create a new release message using the release template'''
+
+    expected_artifacts = os.environ['EXPECTED_ARTIFACTS']
+    try:
+        expected_artifacts = json.loads(expected_artifacts)
+    except json.decoder.JSONDecodeError:
+        print('CRITICAL EXPECTED_ARTIFACTS environment variable JSON probably malformed')
+        print('CRITICAL Correct : \'["test_linux.txt","test_windows.txt"]\' ')
+        print('CRITICAL Not Correct: "[\'test_linux.txt\',\'test_windows.txt\']" ')
+        exit(1)
+    artifacts_list = []
+    for artifact in expected_artifacts:
+        artifact_dict = {
+            'name': artifact,
+            'url': build_artifact_url(artifact, False),
+            'size': get_artifact_weight(artifact),
+            'icon': ':package:'
+        }
+        artifacts_list.append(artifact_dict)
+    expected_sources = ['tar.gz', 'zip']
+    for source in expected_sources:
+        source_url = build_artifact_url(source, True)
+        artifact_dict = {
+            'name': 'Source code ' + source,
+            'url': source_url,
+            'size': get_artifact_weight(source_url),
+            'icon': ':compression:'
+        }
+        artifacts_list.append(artifact_dict)
+
+    j2_env = jinja2.Environment(
+        loader=jinja2.FileSystemLoader(
+            os.path.dirname(os.path.abspath(__file__))
+            ),
+        trim_blocks=True
+        )
+    # pylint: disable=maybe-no-member
+    template = j2_env.get_template('release_template.md')
+    return template.render(
+        current_message=current_message,
+        artifacts=artifacts_list
+    )
+
+
+def send_compiled_message(exists_release, compiled_message):
+    '''Send to gitlab new message'''
+    releaser_token = os.environ['RELEASER_TOKEN']
+    ci_project_id = os.environ['CI_PROJECT_ID']
+    ci_commit_tag = os.environ['CI_COMMIT_TAG']
+    release_url = 'https://git.duniter.org/api/v4/projects/'
+    release_url += ci_project_id
+    release_url += '/repository/tags/'
+    release_url += ci_commit_tag
+    release_url += '/release'
+    if exists_release:
+        # We need to send a PUT request
+        method = 'PUT'
+    else:
+        # We need to send a POST request
+        method = 'POST'
+    send_data = {
+        'tag_name':ci_commit_tag,
+        'description':compiled_message
+        }
+    send_data_serialized = json.dumps(send_data).encode('utf-8')
+    request = urllib.request.Request(release_url, data=send_data_serialized, method=method)
+    request.add_header('Private-Token', releaser_token)
+    request.add_header('Content-Type', 'application/json')
+    urllib.request.urlopen(request)
+
+def main():
+    '''Execute main scenario'''
+    exists_release, current_message = get_current_message()
+    compiled_message = build_compiled_message(current_message)
+    send_compiled_message(exists_release, compiled_message)
+    print('Artifacts uploaded successfully')
+main()