diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 74fe994651a31a7aa9a0962c22ea6300a295fca9..36b38274b44ff62a0310559591fdab3424c44cc3 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -15,8 +15,9 @@ cache:
 stages:
   - quality
   - tests
+  - publish
 
-# Jobs templates ------------------
+# Jobs templates
 
 .install-deps-template: &install-deps
   before_script:
@@ -36,14 +37,14 @@ stages:
   stage: tests
   script: make test
 
-# Quality jobs ----------------------
+# Quality jobs
 
 run-pre-commit:
   <<: *quality
   script: make check
 
 
-# Tests jobs ------------------------
+# Tests jobs
 
 python3.9:
   <<: *test
@@ -56,3 +57,14 @@ python3.10:
 python3.11:
   <<: *test
   image: python:3.11
+
+# Publish jobs
+
+publish:
+  <<: *install-deps
+  stage: publish
+  image: python:3.10
+  only: /^v(\d+\.)?(\d+\.)?(\*|\d+)$/
+  script:
+    - make build
+    - poetry publish -username=__token__ --password=$PYPI_TOKEN
diff --git a/Makefile b/Makefile
index e91084e5f3c752ee7cdc0a3efacd3364fd6bf54a..7f16b4d01e44346eda8d9332631247145f1109b6 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,6 @@
-PHONY: clean lint format test
+.DEFAULT_GOAL := build
+
+PHONY: clean check test build
 
 clean:
 	@rm -rf \
@@ -11,4 +13,5 @@ check:
 test:
 	@poetry run pytest
 
-validate: lint format test
+build:
+	@poetry build
diff --git a/tests/test_urscript_commands.py b/tests/test_urscript_commands.py
index fd56c54314cabf08f03e5b4b88a4697bd1212d65..127af0751cd6080f3202650fa1501c0e9287eb8c 100644
--- a/tests/test_urscript_commands.py
+++ b/tests/test_urscript_commands.py
@@ -53,7 +53,7 @@ def test_get_func():
 
 
 def test__get_conf(conf_list, conf_str):
-    assert _get_conf(*conf_list) == conf_str
+    assert _get_conf(conf_list) == conf_str
 
 
 def test__get_pose(pose_list, pose_str):