diff --git a/.bumpversion.toml b/.bumpversion.toml
new file mode 100644
index 0000000000000000000000000000000000000000..fa69c63cdb5c8dbe369ff78750b946f6c7402621
--- /dev/null
+++ b/.bumpversion.toml
@@ -0,0 +1,19 @@
+[tool.bumpversion]
+commit = true
+current_version = "1.0.0"
+parse = "(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)"
+replace = "{new_version}"
+search = "{current_version}"
+serialize = [
+    "{major}.{minor}.{patch}",
+]
+tag = false
+
+[[tool.bumpversion.files]]
+filename = "CHANGELOG.md"
+replace = """## [Unreleased]
+
+## [{new_version}] - {now:%Y-%m-%d}
+"""
+search = """## [Unreleased]
+"""
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6d2a3d146fb70cac84c0b0d738b54ea8421d6fa8
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,176 @@
+include:
+    -   remote: 'https://gitlab.com/swepy/cicd-templates/docker-build/-/raw/0.2.0/templates/docker-build.yml'
+    -   remote: 'https://gitlab.com/swepy/cicd-templates/release-by-changelog/-/raw/0.4.2/templates/release-by-changelog.yml'
+
+release_by_changelog:
+    rules:
+        -   if: '$CI_PIPELINE_SOURCE != "pipeline" && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'
+
+next_version:
+    stage: build
+    image: alpine:latest
+    variables:
+        PACKAGE_NAME: ruff
+        IMAGE_NAME: swepy/ruff
+    script:
+        #!/bin/bash
+        - apk update
+        - apk add --no-cache curl jq
+
+        # get docker tags
+        - echo "Image name=$IMAGE_NAME"
+        - docker_url="https://registry.hub.docker.com/v2/repositories/$IMAGE_NAME/tags"
+        - tags=""
+
+        # get all tags
+        - while [ -n "$docker_url" ]; do
+        -     docker_response=$(curl -s "$docker_url")
+        -     if [ "$(echo "$docker_response" | jq -r '.results | length')" -eq 0 ]; then
+        -         break
+        -     fi
+        -     tags="$tags $(echo "$docker_response" | jq -r '.results[].name')"
+        -     docker_url=$(echo "$docker_response" | jq -r '.next')
+        -     if [ "$docker_url" = "null" ]; then
+        -         docker_url=""
+        -     fi
+        - done
+        - echo "tags on docker=$tags"
+
+        # pypi
+        - pypi_url="https://pypi.org/pypi/$PACKAGE_NAME/json"
+        - pypi_response=$(curl -s "$pypi_url")
+        - releases=$(echo "$pypi_response" | jq -r '.releases | keys[]')
+
+        - echo "releases on pypi:"
+        - printf "%s " $releases
+
+        # find missing releases
+        - missing_releases=""
+        - eval 'for release in $releases; do found=0; for tag in $tags; do if [ "$release" = "$tag" ]; then found=1; break; fi; done; if [ $found -eq 0 ]; then missing_releases="$missing_releases $release"; fi; done'
+        - missing_releases=$(echo $missing_releases)
+        - echo "missing tags=$missing_releases"
+
+        # regex pattern for versioning (adjust if needed)
+        #regex='^[0-9]+(\.[0-9]+)*$'
+        - regex='^((0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*))(-((0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*)(\.(0|[1-9][0-9]*|[0-9]*[a-zA-Z-][0-9a-zA-Z-]*))*))?(\+([0-9a-zA-Z-]+(\.[0-9a-zA-Z-]+)*))?$'
+        - sorted_releases=$(printf "%s\n" $missing_releases | grep -E "$regex" | sort -t. -k1,1n -k2,2n -k3,3n)
+
+        # print the sorted versions
+        - echo "Sorted releases:"
+        - printf "%s " $sorted_releases
+
+        - version_to_publish=$(echo $sorted_releases | awk '{print $1}')
+        - 'echo "Version to publish: $version_to_publish"'
+
+        - echo "$version_to_publish" > version.txt
+        - cat version.txt
+    artifacts:
+        name: "version"
+        paths:
+            - version.txt
+        expire_in: 1 day
+    rules:
+        -   if: $CI_PIPELINE_SOURCE == "pipeline"
+
+docker_build:
+    variables:
+        TAG: $CI_COMMIT_SHA
+        REPOSITORY: ruff
+        PACKAGE_NAME: ruff
+        IMAGE_NAME: $DOCKER_USER/$REPOSITORY
+        TARGET_IMAGE: $CI_REGISTRY_IMAGE:$TAG
+        PROJECT_PATH: .
+    before_script:
+        - cat version.txt
+        - KANIKO_OPTIONS="--build-arg VERSION=$(cat version.txt)"
+        - echo "Kaniko options=$KANIKO_OPTIONS"
+    needs:
+        -   job: next_version
+            artifacts: true
+    rules:
+        -   if: $CI_PIPELINE_SOURCE == "pipeline"
+
+test_ruff_image:
+    image: $SRC_IMAGE
+    stage: test
+    variables:
+        SRC_IMAGE: $CI_REGISTRY_IMAGE:$TAG
+        TAG: $CI_COMMIT_SHA
+    tags:
+        - docker
+    script:
+        - ruff --help
+    rules:
+        -   if: $CI_PIPELINE_SOURCE == "pipeline"
+
+trivy:
+    stage: test
+    image: docker:stable
+    variables:
+        SRC_IMAGE: $CI_REGISTRY_IMAGE:$TAG
+        TAG: $CI_COMMIT_SHA
+        TRIVY_VERSION: "latest"
+    services:
+        -   name: docker:dind
+            entrypoint: ["env", "-u", "DOCKER_HOST"]
+            command: ["dockerd-entrypoint.sh"]
+    tags:
+        - docker
+    before_script:
+        - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin lab.frogg.it:5050
+        - docker info
+        - docker pull aquasec/trivy:$TRIVY_VERSION
+        - wget --no-verbose https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/gitlab.tpl -P /usr/local/bin/
+        - docker pull $SRC_IMAGE
+    allow_failure: true
+    script:
+        # Build report using Trivy Docker image
+        - docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v $CI_PROJECT_DIR:/root aquasec/trivy:$TRIVY_VERSION image --no-progress --exit-code 0 --format template --template "/usr/local/bin/gitlab.tpl" -o /root/gl-container-scanning-report.json $SRC_IMAGE
+        # Print report
+        - docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy:$TRIVY_VERSION image --no-progress --exit-code 0 --severity HIGH $SRC_IMAGE
+        # Fail on severe vulnerabilities
+        - docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/trivy:$TRIVY_VERSION image --no-progress --exit-code 1 --severity CRITICAL $SRC_IMAGE
+    cache:
+        paths:
+            - .trivycache/
+    rules:
+        -   if: $CI_PIPELINE_SOURCE == "pipeline"
+
+docker_push:
+    image: docker:latest
+    services:
+        - docker:dind
+    variables:
+        DOCKER_REPO: ruff
+        TARGET_IMAGE: swepy/$DOCKER_REPO
+        SRC_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
+    stage: deploy
+    tags:
+        - docker
+    before_script:
+        - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin lab.frogg.it:5050
+        - VERSION=$(cat version.txt)
+    script:
+        # Pull the image
+        - docker pull $SRC_IMAGE
+
+        # Get the Ruff version by running the container
+        - RUFF_VERSION=${VERSION}
+        - RUFF_MINOR=${RUFF_VERSION%.*}
+        - RUFF_MAJOR=${RUFF_VERSION%%.*}
+        - echo $RUFF_VERSION
+
+        # Tag the image
+        - docker tag $SRC_IMAGE $TARGET_IMAGE:$RUFF_VERSION
+        - docker tag $SRC_IMAGE $TARGET_IMAGE:$RUFF_MINOR
+        - docker tag $SRC_IMAGE $TARGET_IMAGE:$RUFF_MAJOR
+        - docker tag $SRC_IMAGE $TARGET_IMAGE:latest
+
+        # Login to Docker Hub using DOCKER_AUTH_CONFIG
+        - docker login -u $DOCKER_USER -p $DOCKER_PASSWORD
+        - docker push $TARGET_IMAGE:$RUFF_VERSION
+        - docker push $TARGET_IMAGE:$RUFF_MINOR
+        - docker push $TARGET_IMAGE:$RUFF_MAJOR
+        - docker push $TARGET_IMAGE:latest
+    rules:
+        -   if: $CI_PIPELINE_SOURCE == "pipeline"
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..12e582062c9206231a412713e3d60d40b979b601
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,12 @@
+# Changelog
+
+All notable changes to this job will be documented in this file.
+
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+
+## [Unreleased]
+
+## [1.0.0] - 2024-06-24
+
+* Initial version
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..6a7e012169c58f7ea840e82e9f1282a4c30d49b3
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,79 @@
+# This dockerfile can generate a docker image with ruff since ruff firsts version,
+# with a few changes in configuration. Those changes are required since early versions
+# of ruff compilation stage rely on "open64", wich is not available in recent alpine
+# linux.
+
+
+ARG APP_ROOT=/app
+ARG APP_USER=app
+ARG APP_UID=1000
+ARG APP_GID=1000
+
+# Default environment with a non priviledged user.
+# For ruff < 0.0.20, uncomment following line
+FROM python:3.12.0-alpine3.18 as base
+# For ruff < 0.0.20, comment following line
+#FROM python:alpine as base
+
+ARG APP_ROOT
+ARG APP_USER
+ARG APP_UID
+ARG APP_GID
+
+RUN mkdir -p $APP_ROOT/.local/bin && \
+    adduser $APP_USER -h $APP_ROOT -u $APP_UID -g $APP_GID -DH && \
+    chown -R $APP_UID:$APP_GID $APP_ROOT  # force the user to be the owner of the app directory
+
+FROM base as builder
+
+ARG VERSION=""
+ARG APP_UID
+ARG APP_ROOT
+
+ENV VERSION=${VERSION}
+ENV PATH=$APP_ROOT/venv/bin:$PATH
+
+# For ruff < 0.0.20, uncomment following line
+RUN  apk add --no-cache maturin cargo
+
+# Never build with root because we don't need priviledged access to
+# build, preventing external source code from running as root.
+USER $APP_UID
+
+WORKDIR $APP_ROOT
+
+RUN python3 -m venv --upgrade-deps $APP_ROOT/venv
+
+# disable pip cache to reduce the image size
+RUN pip install --prefer-binary --no-cache-dir ruff==${VERSION}
+
+
+FROM base
+
+ARG APP_ROOT
+ARG APP_USER
+ARG APP_UID
+ARG APP_GID
+ARG VERSION=""
+
+# For ruff < 0.0.20, uncomment following line
+RUN apk add libgcc
+
+USER $APP_UID
+WORKDIR $APP_ROOT
+
+ENV PATH=$APP_ROOT/venv/bin:$PATH
+ENV VERSION=${VERSION}
+COPY --from=builder --chown=$APP_UID:$APP_GID $APP_ROOT/venv ./venv
+
+LABEL maintainers="Dorian Turba <contact.docker.cblm4@simplelogin.com>, osoc@devkontrol.slmail.me"
+LABEL description="Lightweight image to run Ruff, the extremely fast Python linter and code formatter, written in Rust."
+LABEL version="${VERSION}"
+LABEL license="MIT"
+LABEL org.opencontainers.image.source="https://lab.frogg.it/swepy/containers/ruff"
+LABEL org.opencontainers.image.source.gitlab_com="https://gitlab.com/swepy/containers/ruff"
+LABEL org.opencontainers.image.issues="https://lab.frogg.it/swepy/containers/ruff/-/issues"
+LABEL org.opencontainers.image.created="$(date -u +'%Y-%m-%dT%H:%M:%SZ')"
+LABEL org.opencontainers.image.title="Ruff"
+LABEL org.opencontainers.image.url="https://astral.sh/ruff"
+LABEL org.opencontainers.image.revision="0.1.0"
diff --git a/LICENCE.md b/LICENCE.md
new file mode 100644
index 0000000000000000000000000000000000000000..dabc166f600f5f6461e330abb57270c42da81c18
--- /dev/null
+++ b/LICENCE.md
@@ -0,0 +1,18 @@
+Copyright 2024 dorian turba
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this
+software and associated documentation files (the “Software”), to deal in the Software
+without restriction, including without limitation the rights to use, copy, modify,
+merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or
+substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT
+OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
index 597f3c4a93d46c98c2a1467b00922fabae700893..e26e41e4a3a94514c4ccf5df1b1e75ee58b85fb3 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,3 @@
 # Python Ruff
 
-Docker image for python with Ruff installed
\ No newline at end of file
+Docker image for python with Ruff installed