diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 3d2cc4d3f782e1f098a251126b4e3d9b2f1a3991..e64ddbe152296fcd9cfc59d9fcf4defeaf406e29 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -20,3 +20,30 @@ deploy_devcontainer:
     expire_in: 1 year
   tags:
     - docker
+
+.simple_ros_test:
+  variables:
+    TEMPLATES_PIPELINE_REF: $CI_COMMIT_REF_NAME
+  trigger:
+    project: tecnalia_robotics/ci_test_repos/simple_ros_test
+    strategy: depend
+
+simple_ros_melodic_test:
+  extends: .simple_ros_test
+  variables:
+    TEMPLATE_TO_USE: ci-templates/auto-rules/melodic-default.yml
+
+simple_ros_noetic_test:
+  extends: .simple_ros_test
+  variables:
+    TEMPLATE_TO_USE: ci-templates/auto-rules/noetic-default.yml
+
+simple_ros_core_with_noetic_test:
+  extends: .simple_ros_test
+  variables:
+    TEMPLATE_TO_USE: test/core_with_noetic.yml
+
+simple_ros_auto_rules_melodic_noetic_test:
+  extends: .simple_ros_test
+  variables:
+    TEMPLATE_TO_USE: test/auto_rules_melodic_noetic.yml
diff --git a/ci-templates/auto-rules/no-default.yml b/ci-templates/auto-rules/no-default.yml
index 9f8b4326558b859ea7f21527229ee768266e9616..4921c978f900fd4012d63f9ec282777de455f08d 100644
--- a/ci-templates/auto-rules/no-default.yml
+++ b/ci-templates/auto-rules/no-default.yml
@@ -49,6 +49,53 @@ industrial_ci_humble:
     - if: $DEFAULT_DISTRO == "humble"
     - if: $BUILD_HUMBLE
 
+.py3_rules:
+  rules:
+    - if: $DEFAULT_DISTRO == "noetic"
+      when: always
+    - if: $BUILD_NOETIC
+      when: always
+    - if: $DEFAULT_DISTRO == "humble"
+      when: always
+    - if: $BUILD_HUMBLE
+      when: always
+
+py3-flake8:
+  extends:
+    - .py3_rules
+    - .py3-flake8
+
+py3-flake8_extended:
+  extends:
+    - .py3_rules
+    - .py3-flake8_extended
+
+py3-security:
+  extends:
+    - .py3_rules
+    - .py3-security
+
+.py2_rules:
+  rules:
+    - if: $DEFAULT_DISTRO == "kinetic"
+      when: always
+    - if: $BUILD_KINETIC
+      when: always
+    - if: $DEFAULT_DISTRO == "melodic"
+      when: always
+    - if: $BUILD_MELODIC
+      when: always
+
+py2-flake8:
+  extends:
+    - .py2_rules
+    - .py2-flake8
+
+py2-flake8_extended:
+  extends:
+    - .py2_rules
+    - .py2-flake8_extended
+
 ddeploy:
   extends: .ddeploy
   rules:
diff --git a/ci-templates/core.yml b/ci-templates/core.yml
index 790f9bd07daec3f6b034da7fce93c2d71fa2b795..e29f46b4ed543a8e7ebd2757bf0c474cd19f7978 100644
--- a/ci-templates/core.yml
+++ b/ci-templates/core.yml
@@ -17,6 +17,7 @@ stages:
 
 include:
   - ci-templates/syntax-check.yml
+  - ci-templates/python.yml
   - ci-templates/industrial-ci-templates.yml
 
 default:
diff --git a/ci-templates/python.yml b/ci-templates/python.yml
new file mode 100644
index 0000000000000000000000000000000000000000..61d476aadbad4820c1325ea65af993b783e55768
--- /dev/null
+++ b/ci-templates/python.yml
@@ -0,0 +1,60 @@
+
+.flake8_template:
+  stage: build
+  before_script: ${PYTHON_VERSION} -m pip -q install flake8
+  script: ${PYTHON_VERSION} -m flake8 --max-line-length 120 --statistics --show-source .
+
+
+.flake8_extended_template:
+  stage: .post
+  before_script:
+    - ${PYTHON_VERSION} -m pip install flake8 flake8-isort flake8-builtins
+    # The following flake8 extensions only work on python3
+    - ${PYTHON_VERSION} -m pip install flake8-eradicate flake8-functions-names flake8-return flake8-functions || true
+  script: ${PYTHON_VERSION} -m flake8 --max-line-length 120 --statistics --show-source .
+  allow_failure: true
+
+.security_template:
+  stage: .post
+  before_script:
+    - ${PYTHON_VERSION} -m pip install bandit safety
+  script:
+    - bandit -r . || RET=1
+    - safety check || RET=1
+    - exit $RET
+  allow_failure: true
+
+.py3_template:
+  image: python:3-alpine
+  variables:
+    PYTHON_VERSION: python3
+
+.py2_template:
+  image: python:2-alpine
+  variables:
+    PYTHON_VERSION: python2
+
+.py3-flake8:
+  extends:
+    - .flake8_template
+    - .py3_template
+
+.py2-flake8:
+  extends:
+    - .flake8_template
+    - .py2_template
+
+.py3-flake8_extended:
+  extends:
+    - .flake8_extended_template
+    - .py3_template
+
+.py2-flake8_extended:
+  extends:
+    - .flake8_extended_template
+    - .py2_template
+
+.py3-security:
+  extends:
+    - .security_template
+    - .py3_template
diff --git a/ci-templates/syntax-check.yml b/ci-templates/syntax-check.yml
index 292faf6ab07f6e9aeff793c0e5c2ba6389079ce6..b0608b02bc0e367a7c3c20c55f2bf8a84f006586 100644
--- a/ci-templates/syntax-check.yml
+++ b/ci-templates/syntax-check.yml
@@ -20,18 +20,6 @@ clang_format:
     CLANG_FORMAT_VERSION: "3.8"
   when: always
 
-##########################################################
-## Simple check for basic Python syntax errors; this is much less thorough than
-## other options like pylint, but much faster and less strict
-##########################################################
-python_syntax:
-  image: alpine:3.15 # python2 is no longer available in alpine:3.16
-  before_script:
-    - apk add --update python2
-  stage: build
-  script: python -m compileall -q .
-  when: always
-
 ##########################################################
 ## Check for errors in bash/sh scripts
 ## Uses https://github.com/koalaman/shellcheck
diff --git a/test/auto_rules_melodic_noetic.yml b/test/auto_rules_melodic_noetic.yml
new file mode 100644
index 0000000000000000000000000000000000000000..26e5143d807ccf67eef87c99cac615e3e5beb39e
--- /dev/null
+++ b/test/auto_rules_melodic_noetic.yml
@@ -0,0 +1,5 @@
+include: ci-templates/auto-rules/no-default.yml
+
+variables:
+  BUILD_MELODIC: 1
+  BUILD_NOETIC: 1
diff --git a/test/core_with_noetic.yml b/test/core_with_noetic.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ce063ed55bc62ed643bddd27a45f9359ac3af735
--- /dev/null
+++ b/test/core_with_noetic.yml
@@ -0,0 +1,11 @@
+include: ci-templates/core.yml
+
+industrial_ci_melodic:
+  extends: .industrial_ci
+  variables:
+    ROS_DISTRO: melodic
+
+industrial_ci_noetic:
+  extends: .industrial_ci
+  variables:
+    ROS_DISTRO: noetic