diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..640587f42b0897b2bcba2c47208cd69104f562e7
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,58 @@
+image: python:3.9-slim
+
+
+# Change pip's cache directory to be inside the project directory since we can
+# only cache local items.
+variables:
+  PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip"
+
+# Pip's cache doesn't store the python packages
+# https://pip.pypa.io/en/stable/reference/pip_install/#caching
+#
+# If you want to also cache the installed packages, you have to install
+# them in a virtualenv and cache it as well.
+cache:
+  paths:
+    - .cache/pip
+    - venv/
+
+stages:
+  - test
+  - build
+  - publish
+
+
+before_script:
+  - python -V  # Print out python version for debugging
+  - pip install virtualenv
+  - virtualenv venv
+  - source venv/bin/activate
+
+test_package:
+  stage: test
+  script:
+    - pip install -r requirements.txt
+    - pytest --cov=src --cov-report=xml
+  artifacts:
+    reports:
+      cobertura: coverage.xml
+
+
+build_package:
+  stage: build
+  script:
+    - pip install -r requirements.txt
+    - python -m build
+  artifacts:
+    paths:
+      - dist/*.whl
+      - dist/*.tar.gz
+
+publish_package:
+  only:
+    variables:
+      - $CI_COMMIT_TAG =~ /release/
+  stage: publish
+  script:
+    - pip install twine
+    - TWINE_PASSWORD=${CI_JOB_TOKEN} TWINE_USERNAME=gitlab-ci-token python -m twine upload --repository-url ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi dist/*