diff --git a/templates/Release/Semantic-release.yaml b/templates/Release/Semantic-release.yaml
index 240958d6e64f3a719db766949be7a36f9d6e7824..632cd775251dab3a2404c0624698a8b822a505f1 100644
--- a/templates/Release/Semantic-release.yaml
+++ b/templates/Release/Semantic-release.yaml
@@ -46,14 +46,13 @@
 #
 # We can't merge rules with `!reference` until we switch to Gitlab >= 14.3
 # https://gitlab.com/gitlab-org/gitlab/-/issues/322992
+# Use a `.rules-map` as a workaround
 
 .on-stable-with-semantic-release-config:
   rules:
-    - if: '$CI_PIPELINE_SOURCE == "schedule"'
-      when: never
-    # Exclude semantic-release commits on $STABLE_BRANCH
-    - if: $CI_COMMIT_BRANCH == $STABLE_BRANCH && $CI_COMMIT_MESSAGE =~ /^chore\(release\):/
-      when: never
+    - !reference [.rules-map, not-on-schedule]
+    - !reference [.rules-map, not-on-draft]
+    - !reference [.rules-map, not-on-semantic-release-commit]
     - if: $CI_COMMIT_BRANCH == $STABLE_BRANCH
       exists:
         - release.config.js
diff --git a/templates/Rules.yaml b/templates/Rules.yaml
index 966fed44b447d1824b13d3693e93025274afab1f..d9907aa7857de75d5af59168ef613fb4b90c7459 100644
--- a/templates/Rules.yaml
+++ b/templates/Rules.yaml
@@ -37,6 +37,7 @@
 #     - `wip foo`
 #     - `foo (draft)`
 #     - `foo [WIP]`
+#   - message generated by `semantic-release`
 ---
 variables:
   STABLE_BRANCH: stable
@@ -47,97 +48,110 @@ variables:
   BUGFIX_PREFIX: bugfix/
   FEATURE_PREFIX: feature/
 
-
 # This rules template should be used as the default rules.
 # It select all branches except the stable one.
-# We always exclude schedules.
 .not-on-stable:
   rules:
-    - if: '$CI_PIPELINE_SOURCE == "schedule"'
-      when: never
-    - if: $CI_COMMIT_BRANCH == $STABLE_BRANCH
-      when: never
-    # Exclude `Draft` or `WIP` messages
-    - if: $CI_COMMIT_MESSAGE =~ /(?:draft|wip):|\[(?:draft|wip)\]|\((?:draft|wip)\)/i
-      when: never
-    - if: $CI_COMMIT_BRANCH
-      when: on_success
+    - !reference [.rules-map, not-on-schedule]
+    - !reference [.rules-map, not-on-stable]
+    - !reference [.rules-map, not-on-draft]
+    - !reference [.rules-map, not-on-semantic-release-commit]
+    - !reference [.rules-map, on-branch]
 
 # Select only branches that are not stable, testing or development.
-# We always exclude schedules.
 .on-branches:
   rules:
-    - if: '$CI_PIPELINE_SOURCE == "schedule"'
-      when: never
-    - if: $CI_COMMIT_TAG
-      when: never
-    - if: $CI_COMMIT_BRANCH == $STABLE_BRANCH
-      when: never
-    - if: $CI_COMMIT_BRANCH == $TESTING_BRANCH
-      when: never
-    - if: $CI_COMMIT_BRANCH == $DEV_BRANCH
-      when: never
-    # Exclude `Draft` or `WIP` messages
-    - if: $CI_COMMIT_MESSAGE =~ /(?:draft|wip):|\[(?:draft|wip)\]|\((?:draft|wip)\)/i
-      when: never
-    - if: $CI_COMMIT_BRANCH
-      when: on_success
+    - !reference [.rules-map, not-on-schedule]
+    - !reference [.rules-map, not-on-tag]
+    - !reference [.rules-map, not-on-stable]
+    - !reference [.rules-map, not-on-testing]
+    - !reference [.rules-map, not-on-dev]
+    - !reference [.rules-map, not-on-draft]
+    - !reference [.rules-map, not-on-semantic-release-commit]
+    - !reference [.rules-map, on-branch]
 
-# Select the developpment branch except for commits comming from `semantic-release`
-# We always exclude schedules.
+# Select the developpment branch
 .on-dev:
   rules:
-    - if: '$CI_PIPELINE_SOURCE == "schedule"'
-      when: never
-    # Exclude `Draft` or `WIP` messages
-    - if: $CI_COMMIT_MESSAGE =~ /(?:draft|wip):|\[(?:draft|wip)\]|\((?:draft|wip)\)/i
-      when: never
-    # Exclude $CI_DEFAULT_BRANCH of semantic-release commits
-    - if: $CI_COMMIT_BRANCH == $DEV_BRANCH && $CI_COMMIT_MESSAGE =~ /^chore\(release\):/
-      when: never
-    - if: $CI_COMMIT_BRANCH == $DEV_BRANCH
-      when: on_success
+    - !reference [.rules-map, not-on-schedule]
+    - !reference [.rules-map, not-on-draft]
+    - !reference [.rules-map, not-on-semantic-release-commit]
+    - !reference [.rules-map, on-dev]
 
-# Select the pre-release testing branch except for commits comming from `semantic-release`
-# We always exclude schedules.
+# Select the pre-release testing branch
 .on-testing:
   rules:
-    - if: '$CI_PIPELINE_SOURCE == "schedule"'
-      when: never
-    # Exclude `Draft` or `WIP` messages
-    - if: $CI_COMMIT_MESSAGE =~ /(?:draft|wip):|\[(?:draft|wip)\]|\((?:draft|wip)\)/i
-      when: never
-    # Exclude $CI_DEFAULT_BRANCH of semantic-release commits
-    - if: $CI_COMMIT_BRANCH == $TESTING_BRANCH && $CI_COMMIT_MESSAGE =~ /^chore\(release\):/
-      when: never
-    - if: $CI_COMMIT_BRANCH == $TESTING_BRANCH
-      when: on_success
+    - !reference [.rules-map, not-on-schedule]
+    - !reference [.rules-map, not-on-draft]
+    - !reference [.rules-map, not-on-semantic-release-commit]
+    - !reference [.rules-map, on-testing]
 
-# Select the stable branch except for commits comming from `semantic-release`
-# We always exclude schedules.
+# Select the stable branch
 .on-stable:
   rules:
-    - if: '$CI_PIPELINE_SOURCE == "schedule"'
-      when: never
-    # Exclude `Draft` or `WIP` messages
-    - if: $CI_COMMIT_MESSAGE =~ /(?:draft|wip):|\[(?:draft|wip)\]|\((?:draft|wip)\)/i
-      when: never
-    # Exclude $CI_DEFAULT_BRANCH of semantic-release commits
-    - if: $CI_COMMIT_BRANCH == $STABLE_BRANCH && $CI_COMMIT_MESSAGE =~ /^chore\(release\):/
-      when: never
-    - if: $CI_COMMIT_BRANCH == $STABLE_BRANCH
-      when: on_success
+    - !reference [.rules-map, not-on-schedule]
+    - !reference [.rules-map, not-on-draft]
+    - !reference [.rules-map, not-on-semantic-release-commit]
+    - !reference [.rules-map, on-stable]
 
 # Select the protected release tags
-# We always exclude schedules.
 .on-release-tag:
   rules:
-    - if: '$CI_PIPELINE_SOURCE == "schedule"'
-      when: never
+    - !reference [.rules-map, not-on-schedule]
+    - !reference [.rules-map, not-on-draft]
+    - !reference [.rules-map, on-release-tag]
+
+# We use a single rules hash to be referenced as array elements by
+# individual rules templates above.
+# This avoid the duplication of rules conditions
+#
+# SEE ALSO
+# ========
+#
+# - https://forum.gitlab.com/t/add-conditions-to-rules-array-merged-with-reference/62600
+# - https://gitlab.com/gitlab-org/gitlab/-/issues/322992
+# - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67922
+# - https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67922
+.rules-map:
+  not-on-schedule:
+    if: '$CI_PIPELINE_SOURCE == "schedule"'
+    when: never
+  not-on-tag:
+    if: $CI_COMMIT_TAG
+    when: never
+  not-on-stable:
+    if: $CI_COMMIT_BRANCH == $STABLE_BRANCH
+    when: never
+  not-on-testing:
+    if: $CI_COMMIT_BRANCH == $TESTING_BRANCH
+    when: never
+  not-on-dev:
+    if: $CI_COMMIT_BRANCH == $DEV_BRANCH
+    when: never
+  not-on-draft:
     # Exclude `Draft` or `WIP` messages
-    - if: $CI_COMMIT_MESSAGE =~ /(?:draft|wip):|\[(?:draft|wip)\]|\((?:draft|wip)\)/i
-      when: never
-    # Only for protected release tags
-    - if: $CI_COMMIT_TAG =~ /^release\// && $CI_COMMIT_REF_PROTECTED
-      when: on_success
+    if: $CI_COMMIT_MESSAGE =~ /(?:draft|wip):|\[(?:draft|wip)\]|\((?:draft|wip)\)/i
+    when: never
+  not-on-semantic-release-commit:
+    # Exclude $CI_DEFAULT_BRANCH of semantic-release commits
+    if: $CI_COMMIT_BRANCH && $CI_COMMIT_MESSAGE =~ /^chore\(release\):/
+    when: never
+  on-branch:
+    if: $CI_COMMIT_BRANCH
+    when: on_success
+  on-stable:
+    if: $CI_COMMIT_BRANCH == $STABLE_BRANCH
+    when: on_success
+  on-testing:
+    if: $CI_COMMIT_BRANCH == $TESTING_BRANCH
+    when: on_success
+  on-dev:
+    if: $CI_COMMIT_BRANCH == $DEV_BRANCH
+    when: on_success
+  on-tag:
+    if: $CI_COMMIT_TAG
+    when: on_success
+  on-release-tag:
+    if: $CI_COMMIT_TAG =~ /^release\// && $CI_COMMIT_REF_PROTECTED
+    when: on_success
 ...