diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 0832275ef763510366bad2720baf3753dbd90496..a9b381ec4420ebbf2856daa8f9777f6843de8e8c 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -2,8 +2,6 @@ default:
   image:
     name: $CI_REGISTRY_IMAGE/ci-build-runner:$CI_COMMIT_REF_SLUG
     pull_policy: always
-  before_script:
-    - python --version # For debugging
   cache:
     paths:
       - .cache/pip
@@ -33,41 +31,37 @@ trigger_prepare:
     strategy: depend
     include: .prepare.gitlab-ci.yml
 
+.python_before:
+  before_script:
+    - python --version # For debugging
+
 run_black:
   stage: lint
+  extends: .python_before
   script:
     - tox -e black
   allow_failure: true
 
 run_flake8:
   stage: lint
+  extends: .python_before
   script:
     - tox -e pep8
   allow_failure: true
 
 run_pylint:
   stage: lint
+  extends: .python_before
   script:
     - tox -e pylint
   allow_failure: true
 
-
 sast:
   variables:
     SAST_EXCLUDED_ANALYZERS: brakeman, flawfinder, kubesec, nodejs-scan, phpcs-security-audit,
       pmd-apex, security-code-scan, sobelow, spotbugs
   stage: test
 
-dependency_scanning:
-  # override default before_script, job won't have Python available
-  before_script:
-    - uname
-
-secret_detection:
-  # override default before_script, job won't have Python available
-  before_script:
-    - uname
-
 # Basic setup for all Python versions for which we don't have a base image
 .run_unit_test_version_base:
   before_script:
@@ -89,6 +83,7 @@ run_unit_tests:
 # Run code coverage on the base image thus also performing unit tests
 run_unit_tests_coverage:
   stage: test
+  extends: .python_before
   script:
    - tox -e coverage
   coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
@@ -102,6 +97,7 @@ run_unit_tests_coverage:
 
 package_files:
   stage: package
+  extends: .python_before
   artifacts:
     expire_in: 1w
     paths:
@@ -111,6 +107,7 @@ package_files:
 
 package_docs:
   stage: package
+  extends: .python_before
   artifacts:
     expire_in: 1w
     paths:
@@ -118,9 +115,20 @@ package_docs:
   script:
     - tox -e docs
 
-
-
-
+publish_on_pypi:
+  stage: publish
+  environment: pypi
+  needs:
+    - package_files
+  when: manual
+  rules:
+    - if: '$CI_COMMIT_TAG && $CI_COMMIT_REF_PROTECTED == "true"'
+  script:
+    - echo "run twine for pypi"
+    - |
+      TWINE_PASSWORD="${PYPI_TOKEN}" \
+      TWINE_USERNAME="__token__" \
+      python -m twine upload dist/*
 
 publish_on_gitlab:
   stage: publish
@@ -137,7 +145,6 @@ publish_on_gitlab:
       python -m twine upload \
       --repository-url ${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/pypi dist/*
 
-
 publish_to_readthedocs:
   stage: publish
   allow_failure: true
diff --git a/setup.cfg b/setup.cfg
index 370d875fa76ee4de682056deb8966bffc69e556c..f5336dc3c062a04b632a912fa17bff10281451f2 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -7,7 +7,7 @@ url = https://git.astron.nl/lofar2.0/sid
 license = Apache License 2.0
 classifiers =
     Development Status :: 3 - Alpha
-    Environment :: Web Environment
+    Environment :: Plugins
     Intended Audience :: Developers
     Intended Audience :: Science/Research
     License :: OSI Approved :: Apache Software License
@@ -20,10 +20,10 @@ classifiers =
     Programming Language :: Python :: 3.10
     Programming Language :: Python :: 3.11
     Programming Language :: Python :: 3.12
-    Topic :: Internet :: WWW/HTTP
-    Topic :: Internet :: WWW/HTTP :: Dynamic Content
+    Topic :: Software Development :: Libraries
     Topic :: Scientific/Engineering
     Topic :: Scientific/Engineering :: Astronomy
+    Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator
 
 [options]
 include_package_data = true