From 0166bd43891f4bab8934e4379e8c34f399d681f5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?C=C3=A9dric=20OLIVIER?= <cedric3.olivier@orange.com>
Date: Fri, 1 Dec 2023 08:36:12 +0000
Subject: [PATCH] feat: support CI/CD component design

See: https://docs.gitlab.com/ee/ci/components/
---
 README.md                            | 130 ++++++++++--------
 bumpversion.sh                       |   2 +-
 kicker.json                          |   6 +
 templates/gitlab-ci-python-vault.yml |  14 +-
 templates/gitlab-ci-python.yml       | 198 +++++++++++++++++++++++++--
 5 files changed, 278 insertions(+), 72 deletions(-)

diff --git a/README.md b/README.md
index 55dbfc3..8b25f40 100644
--- a/README.md
+++ b/README.md
@@ -4,30 +4,55 @@ This project implements a GitLab CI/CD template to build, test and analyse your
 
 ## Usage
 
-In order to include this template in your project, add the following to your `gitlab-ci.yml`:
+This template can be used both as a [CI/CD component](https://docs.gitlab.com/ee/ci/components/#use-a-component-in-a-cicd-configuration) 
+or using the legacy [`include:project`](https://docs.gitlab.com/ee/ci/yaml/index.html#includeproject) syntax.
+
+### Use as a CI/CD component
+
+Add the following to your `gitlab-ci.yml`:
+
+```yaml
+include:
+  # 1: include the component
+  - component: gitlab.com/to-be-continuous/python/gitlab-ci-python@6.4.1
+    # 2: set/override component inputs
+    inputs:
+      image: registry.hub.docker.com/library/python:3.10
+      pytest-enabled: true
+```
+
+### Use as a CI/CD template (legacy)
+
+Add the following to your `gitlab-ci.yml`:
 
 ```yaml
 include:
+  # 1: include the template
   - project: 'to-be-continuous/python'
     ref: '6.4.1'
     file: '/templates/gitlab-ci-python.yml'
+
+variables:
+  # 2: set/override template variables
+  PYTHON_IMAGE: registry.hub.docker.com/library/python:3.10
+  PYTEST_ENABLED: "true"
 ```
 
 ## Global configuration
 
 The Python template uses some global configuration used throughout all jobs.
 
-| Name                 | description                                                                           | default value      |
+| Input / Variable     | Description                                                                           | Default value      |
 | -------------------- | ------------------------------------------------------------------------------------- | ------------------ |
-| `PYTHON_IMAGE`       | The Docker image used to run Python <br/>:warning: **set the version required by your project** | `registry.hub.docker.com/library/python:3` |
-| `PYTHON_PROJECT_DIR` | Python project root directory                                                         | `.`                |
-| `PYTHON_BUILD_SYSTEM`| Python build-system to use to install dependencies, build and package the project (see below) | _none_ (auto-detect) |
+| `image` / `PYTHON_IMAGE` | The Docker image used to run Python <br/>:warning: **set the version required by your project** | `registry.hub.docker.com/library/python:3` |
+| `project-dir` / `PYTHON_PROJECT_DIR` | Python project root directory                                                         | `.`                |
+| `build-system` / `PYTHON_BUILD_SYSTEM`| Python build-system to use to install dependencies, build and package the project (see below) | _none_ (auto-detect) |
 | `PIP_INDEX_URL`      | Python repository url                                                                 | _none_             |
 | `PIP_EXTRA_INDEX_URL` | Exra Python repository url                                                           | _none_             |
-| `PIP_OPTS`           | pip [extra options](https://pip.pypa.io/en/stable/cli/pip/#general-options)     | _none_             |
-| `PYTHON_EXTRA_DEPS`  | Python extra sets of dependencies to install<br/>For [Setuptools](https://setuptools.pypa.io/en/latest/userguide/dependency_management.html?highlight=extras#optional-dependencies) or [Poetry](https://python-poetry.org/docs/pyproject/#extras) only | _none_ |
-| `PYTHON_REQS_FILE`   | Main requirements file _(relative to `$PYTHON_PROJECT_DIR`)_<br/>For [Requirements Files](https://pip.pypa.io/en/stable/user_guide/#requirements-files) build-system only | `requirements.txt` |
-| `PYTHON_EXTRA_REQS_FILES` | Extra dev requirements file(s) to install _(relative to `$PYTHON_PROJECT_DIR`)_ | `requirements-dev.txt` |
+| `pip-opts` / `PIP_OPTS` | pip [extra options](https://pip.pypa.io/en/stable/cli/pip/#general-options)     | _none_             |
+| `extra-deps` / `PYTHON_EXTRA_DEPS` | Python extra sets of dependencies to install<br/>For [Setuptools](https://setuptools.pypa.io/en/latest/userguide/dependency_management.html?highlight=extras#optional-dependencies) or [Poetry](https://python-poetry.org/docs/pyproject/#extras) only | _none_ |
+| `reqs-file` / `PYTHON_REQS_FILE` | Main requirements file _(relative to `$PYTHON_PROJECT_DIR`)_<br/>For [Requirements Files](https://pip.pypa.io/en/stable/user_guide/#requirements-files) build-system only | `requirements.txt` |
+| `extra-reqs-files` / `PYTHON_EXTRA_REQS_FILES` | Extra dev requirements file(s) to install _(relative to `$PYTHON_PROJECT_DIR`)_ | `requirements-dev.txt` |
 
 The cache policy also makes the necessary to manage pip cache (not to download Python dependencies over and over again).
 
@@ -66,10 +91,10 @@ It is activated by setting `$PYLINT_ENABLED` to `true`.
 
 It is bound to the `build` stage, and uses the following variables:
 
-| Name                     | description                        | default value     |
+| Input / Variable         | Description                        | Default value     |
 | ------------------------ | ---------------------------------- | ----------------- |
-| `PYLINT_ARGS`            | Additional [pylint CLI options](http://pylint.pycqa.org/en/latest/user_guide/run.html#command-line-options) |  _none_           |
-| `PYLINT_FILES`           | Files or directories to analyse   | _none_ (by default analyses all found python source files) |
+| `pylint-args` / `PYLINT_ARGS` | Additional [pylint CLI options](http://pylint.pycqa.org/en/latest/user_guide/run.html#command-line-options) |  _none_           |
+| `pylint-files` / `PYLINT_FILES` | Files or directories to analyse   | _none_ (by default analyses all found python source files) |
 
 In addition to a textual report in the console, this job produces the following reports, kept for one day:
 
@@ -96,9 +121,9 @@ In order to produce JUnit test reports, the tests are executed with the [xmlrunn
 
 It is bound to the `build` stage, and uses the following variables:
 
-| Name                     | description                                                          | default value           |
+| Input / Variable         | Description                                                          | Default value           |
 | ------------------------ | -------------------------------------------------------------------- | ----------------------- |
-| `UNITTEST_ARGS`          | Additional xmlrunner/unittest CLI options                            | _none_                  |
+| `unittest-args` / `UNITTEST_ARGS` | Additional xmlrunner/unittest CLI options                            | _none_                  |
 
 :information_source: use a `.coveragerc` file at the root of your Python project to control the coverage settings.
 
@@ -128,9 +153,9 @@ It is activated by setting `$PYTEST_ENABLED` to `true`.
 
 It is bound to the `build` stage, and uses the following variables:
 
-| Name                     | description                                                                                                                                   | default value           |
+| Input / Variable         | Description                                                                                                                                   | Default value           |
 | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- |
-| `PYTEST_ARGS`            | Additional [pytest](https://docs.pytest.org/en/stable/usage.html) or [pytest-cov](https://github.com/pytest-dev/pytest-cov#usage) CLI options | _none_                  |
+| `pytest-args` / `PYTEST_ARGS` | Additional [pytest](https://docs.pytest.org/en/stable/usage.html) or [pytest-cov](https://github.com/pytest-dev/pytest-cov#usage) CLI options | _none_                  |
 
 :information_source: use a `.coveragerc` file at the root of your Python project to control the coverage settings.
 
@@ -160,9 +185,9 @@ It is activated by setting `$NOSETESTS_ENABLED` to `true`.
 
 It is bound to the `build` stage, and uses the following variables:
 
-| Name                     | description                                                                             | default value           |
+| Input / Variable         | Description                                                                             | Default value           |
 | ------------------------ | --------------------------------------------------------------------------------------- | ----------------------- |
-| `NOSETESTS_ARGS`         | Additional [nose CLI options](https://nose.readthedocs.io/en/latest/usage.html#options) | _none_                  |
+| `nosetests-args` / `NOSETESTS_ARGS` | Additional [nose CLI options](https://nose.readthedocs.io/en/latest/usage.html#options) | _none_                  |
 
 By default coverage will be run on all the project directories. You can restrict it to your packages by setting the `$NOSE_COVER_PACKAGE` variable.
 More [info](https://nose.readthedocs.io/en/latest/plugins/cover.html)
@@ -183,9 +208,9 @@ are not set), and performs a [`compileall`](https://docs.python.org/3/library/co
 
 It is bound to the `build` stage, and uses the following variables:
 
-| Name                  | description                                                                   | default value |
+| Input / Variable      | Description                                                                   | Default value |
 | --------------------- | ----------------------------------------------------------------------------- | ------------- |
-| `PYTHON_COMPILE_ARGS` | [`compileall` CLI options](https://docs.python.org/3/library/compileall.html) | `*`           |
+| `compile-args` / `PYTHON_COMPILE_ARGS` | [`compileall` CLI options](https://docs.python.org/3/library/compileall.html) | `*`           |
 
 ### `py-bandit` job (SAST)
 
@@ -193,10 +218,10 @@ This job is **disabled by default** and performs a [Bandit](https://pypi.org/pro
 
 It is bound to the `test` stage, and uses the following variables:
 
-| Name             | description                                                            | default value     |
+| Input / Variable | Description                                                            | Default value     |
 | ---------------- | ---------------------------------------------------------------------- | ----------------- |
-| `BANDIT_ENABLED` | Set to `true` to enable Bandit analysis                                | _none_ (disabled) |
-| `BANDIT_ARGS`    | Additional [Bandit CLI options](https://github.com/PyCQA/bandit#usage) | `--recursive .`   |
+| `bandit-enabled` / `BANDIT_ENABLED` | Set to `true` to enable Bandit analysis                                | _none_ (disabled) |
+| `bandit-args` / `BANDIT_ARGS`    | Additional [Bandit CLI options](https://github.com/PyCQA/bandit#usage) | `--recursive .`   |
 
 In addition to a textual report in the console, this job produces the following reports, kept for one day:
 
@@ -211,10 +236,10 @@ This job is **disabled by default** and performs a dependency check analysis usi
 
 It is bound to the `test` stage, and uses the following variables:
 
-| Name             | description                                                             | default value     |
+| Input / Variable | Description                                                             | Default value     |
 | ---------------- | ----------------------------------------------------------------------- | ----------------- |
-| `SAFETY_ENABLED` | Set to `true` to enable Safety job                                      | _none_ (disabled) |
-| `SAFETY_ARGS`    | Additional [Safety CLI options](https://github.com/pyupio/safety#usage) | `--full-report`   |
+| `safety-enabled` / `SAFETY_ENABLED` | Set to `true` to enable Safety job                                      | _none_ (disabled) |
+| `safety-args` / `SAFETY_ARGS`    | Additional [Safety CLI options](https://github.com/pyupio/safety#usage) | `--full-report`   |
 
 ### `py-trivy` job (dependency check)
 
@@ -222,10 +247,10 @@ This job is **disabled by default** and performs a dependency check analysis usi
 
 It is bound to the `test` stage, and uses the following variables:
 
-| Name             | description                                                             | default value     |
+| Input / Variable | Description                                                             | Default value     |
 | ---------------- | ----------------------------------------------------------------------- | ----------------- |
-| `PYTHON_TRIVY_ENABLED` | Set to `true` to enable Trivy job                                 | _none_ (disabled) |
-| `PYTHON_TRIVY_ARGS`    | Additional [Trivy CLI options](https://aquasecurity.github.io/trivy/v0.21.1/getting-started/cli/fs/) | `--vuln-type library`   |
+| `trivy-enabled` / `PYTHON_TRIVY_ENABLED` | Set to `true` to enable Trivy job                                 | _none_ (disabled) |
+| `trivy-args` / `PYTHON_TRIVY_ARGS`       | Additional [Trivy CLI options](https://aquasecurity.github.io/trivy/v0.21.1/getting-started/cli/fs/) | `--vuln-type library`   |
 
 In addition to a textual report in the console, this job produces the following reports, kept for one day:
 
@@ -239,12 +264,12 @@ This job generates a [SBOM](https://cyclonedx.org/) file listing all dependencie
 
 It is bound to the `test` stage, and uses the following variables:
 
-| Name                  | description                            | default value     |
+| Input / Variable      | Description                            | Default value     |
 | --------------------- | -------------------------------------- | ----------------- |
-| `PYTHON_SBOM_DISABLED` | Set to `true` to disable this job | _none_ |
-| `PYTHON_SBOM_SYFT_URL` | Url to the `tar.gz` package for `linux_amd64` of Syft to use (ex: `https://github.com/anchore/syft/releases/download/v0.62.3/syft_0.62.3_linux_amd64.tar.gz`)<br/>_When unset, the latest version will be used_ | _none_ |
-| `PYTHON_SBOM_NAME` | Component name of the emitted SBOM | `$CI_PROJECT_PATH/$PYTHON_PROJECT_DIR` |
-| `PYTHON_SBOM_OPTS` | Options for syft used for SBOM analysis | `--catalogers python-index-cataloger` |
+| `sbom-disabled` / `PYTHON_SBOM_DISABLED` | Set to `true` to disable this job | _none_ |
+| `sbom-syft-url` / `PYTHON_SBOM_SYFT_URL` | Url to the `tar.gz` package for `linux_amd64` of Syft to use (ex: `https://github.com/anchore/syft/releases/download/v0.62.3/syft_0.62.3_linux_amd64.tar.gz`)<br/>_When unset, the latest version will be used_ | _none_ |
+| `sbom-name` / `PYTHON_SBOM_NAME` | Component name of the emitted SBOM | `$CI_PROJECT_PATH/$PYTHON_PROJECT_DIR` |
+| `sbom-opts` / `PYTHON_SBOM_OPTS` | Options for syft used for SBOM analysis | `--catalogers python-index-cataloger` |
 
 In addition to logs in the console, this job produces the following reports, kept for one week:
 
@@ -299,16 +324,16 @@ The Python template supports two packaging systems:
 
 The release job is bound to the `publish` stage, appears only on production and integration branches and uses the following variables:
 
-| Name                    | description                                                             | default value     |
+| Input / Variable        | Description                                                             | Default value     |
 | ----------------------- | ----------------------------------------------------------------------- | ----------------- |
-| `PYTHON_RELEASE_ENABLED`| Set to `true` to enable the release job                                 | _none_ (disabled) |
-| `PYTHON_RELEASE_NEXT`   | The part of the version to increase (one of: `major`, `minor`, `patch`) | `minor`           |
-| `PYTHON_SEMREL_RELEASE_DISABLED`| Set to `true` to disable [semantic-release integration](#semantic-release-integration)   | _none_ (disabled) |
+| `release-enabled` / `PYTHON_RELEASE_ENABLED`| Set to `true` to enable the release job                                 | _none_ (disabled) |
+| `release-next` / `PYTHON_RELEASE_NEXT`   | The part of the version to increase (one of: `major`, `minor`, `patch`) | `minor`           |
+| `semrel-release-disabled` / `PYTHON_SEMREL_RELEASE_DISABLED`| Set to `true` to disable [semantic-release integration](#semantic-release-integration)   | _none_ (disabled) |
 | `GIT_USERNAME`          | Git username for Git push operations (see below)                        | _none_            |
 | :lock: `GIT_PASSWORD`   | Git password for Git push operations (see below)                        | _none_            |
 | :lock: `GIT_PRIVATE_KEY`| SSH key for Git push operations (see below)                             | _none_            |
-| `PYTHON_RELEASE_COMMIT_MESSAGE`| The Git commit message to use on the release commit. This is templated using the [Python Format String Syntax](http://docs.python.org/2/library/string.html#format-string-syntax). Available in the template context are current_version and new_version. | `chore(python-release): {current_version} → {new_version}` |
-| `PYTHON_REPOSITORY_URL`| Target PyPI repository to publish packages                              | _[GitLab project's PyPI packages repository](https://docs.gitlab.com/ee/user/packages/pypi_repository/)_ |
+| `release-commit-message` / `PYTHON_RELEASE_COMMIT_MESSAGE`| The Git commit message to use on the release commit. This is templated using the [Python Format String Syntax](http://docs.python.org/2/library/string.html#format-string-syntax). Available in the template context are current_version and new_version. | `chore(python-release): {current_version} → {new_version}` |
+| `repository-url` / `PYTHON_REPOSITORY_URL`| Target PyPI repository to publish packages                              | _[GitLab project's PyPI packages repository](https://docs.gitlab.com/ee/user/packages/pypi_repository/)_ |
 | `PYTHON_REPOSITORY_USERNAME`| Target PyPI repository username credential                              | `gitlab-ci-token` |
 | :lock: `PYTHON_REPOSITORY_PASSWORD`| Target PyPI repository password credential                              | `$CI_JOB_TOKEN` |
 
@@ -359,7 +384,7 @@ You can either use a SSH key or user/password credentials.
 
 We recommend you to use a [project deploy key](https://docs.gitlab.com/ee/user/project/deploy_keys/#project-deploy-keys) with write access to your project.
 
-The key should not have a passphrase (see [how to generate a new SSH key pair](https://docs.gitlab.com/ce/ssh/README.html#generating-a-new-ssh-key-pair)).
+The key should not have a passphrase (see [how to generate a new SSH key pair](https://docs.gitlab.com/ee/user/ssh.html#generate-an-ssh-key-pair)).
 
 Specify :lock: `$GIT_PRIVATE_KEY` as secret project variable with the private part of the deploy key.
 
@@ -412,11 +437,11 @@ This variant allows delegating your secrets management to a [Vault](https://www.
 
 In order to be able to communicate with the Vault server, the variant requires the additional configuration parameters:
 
-| Name              | Description                            | Default value     |
+| Input / Variable  | Description                            | Default value     |
 | ----------------- | -------------------------------------- | ----------------- |
 | `TBC_VAULT_IMAGE` | The [Vault Secrets Provider](https://gitlab.com/to-be-continuous/tools/vault-secrets-provider) image to use (can be overridden) | `$CI_REGISTRY/to-be-continuous/tools/vault-secrets-provider:master` |
-| `VAULT_BASE_URL`  | The Vault server base API url          | _none_ |
-| `VAULT_OIDC_AUD`  | The `aud` claim for the JWT | `$CI_SERVER_URL` |
+| `vault-base-url` / `VAULT_BASE_URL`  | The Vault server base API url          | _none_ |
+| `vault-oidc-aud` / `VAULT_OIDC_AUD`  | The `aud` claim for the JWT | `$CI_SERVER_URL` |
 | :lock: `VAULT_ROLE_ID`   | The [AppRole](https://www.vaultproject.io/docs/auth/approle) RoleID | **must be defined** |
 | :lock: `VAULT_SECRET_ID` | The [AppRole](https://www.vaultproject.io/docs/auth/approle) SecretID | **must be defined** |
 
@@ -439,22 +464,19 @@ With:
 
 ```yaml
 include:
-  # main template
-  - project: 'to-be-continuous/python'
-    ref: '6.4.1'
-    file: '/templates/gitlab-ci-python.yml'
+  # main component
+  - component: gitlab.com/to-be-continuous/python/gitlab-ci-python@6.4.1
   # Vault variant
-  - project: 'to-be-continuous/python'
-    ref: '6.4.1'
-    file: '/templates/gitlab-ci-python-vault.yml'
+  - component: gitlab.com/to-be-continuous/python/gitlab-ci-python-vault@6.4.1
+    inputs:
+      vault-base-url: "https://vault.acme.host/v1"
+      # audience claim for JWT
+      vault-oidc-aud: "https://vault.acme.host"
 
 variables:
-    # audience claim for JWT
-    VAULT_OIDC_AUD: "https://vault.acme.host"
     # Secrets managed by Vault
     GIT_PASSWORD: "@url@http://vault-secrets-provider/api/secrets/b7ecb6ebabc231/git/semantic-release?field=group-access-token"
     GIT_PRIVATE_KEY: "@url@http://vault-secrets-provider/api/secrets/b7ecb6ebabc231/git/semantic-release?field=private-key"
     PYTHON_REPOSITORY_PASSWORD: "@url@http://vault-secrets-provider/api/secrets/b7ecb6ebabc231/pip-repo/repository?field=password"
-    VAULT_BASE_URL: "https://vault.acme.host/v1"
     # $VAULT_ROLE_ID and $VAULT_SECRET_ID defined as a secret CI/CD variable
 ```
diff --git a/bumpversion.sh b/bumpversion.sh
index f06829a..924f849 100755
--- a/bumpversion.sh
+++ b/bumpversion.sh
@@ -27,7 +27,7 @@ if [[ "$curVer" ]]; then
   log_info "Bump version from \\e[33;1m${curVer}\\e[0m to \\e[33;1m${nextVer}\\e[0m (release type: $relType)..."
 
   # replace in README
-  sed -e "s/ref: '$curVer'/ref: '$nextVer'/" README.md > README.md.next
+  sed -e "s/ref: '$curVer'/ref: '$nextVer'/" -e "s/@$curVer/@$nextVer/" README.md > README.md.next
   mv -f README.md.next README.md
 
   # replace in template and variants
diff --git a/kicker.json b/kicker.json
index e1a4f7f..0430580 100644
--- a/kicker.json
+++ b/kicker.json
@@ -148,6 +148,12 @@
       "description": "Detect security vulnerabilities with [Trivy](https://github.com/aquasecurity/trivy/) (dependencies analysis)",
       "enable_with": "PYTHON_TRIVY_ENABLED",
       "variables": [
+        {
+          "name": "PYTHON_TRIVY_IMAGE",
+          "description": "The Docker image used to run Trivy",
+          "default": "registry.hub.docker.com/aquasec/trivy:latest",
+          "advanced": true
+        },
         {
           "name": "PYTHON_TRIVY_ARGS",
           "description": "Additional [Trivy CLI options](https://aquasecurity.github.io/trivy/v0.21.1/getting-started/cli/fs/)",
diff --git a/templates/gitlab-ci-python-vault.yml b/templates/gitlab-ci-python-vault.yml
index 6dbe492..f444b1f 100644
--- a/templates/gitlab-ci-python-vault.yml
+++ b/templates/gitlab-ci-python-vault.yml
@@ -1,13 +1,23 @@
 # =====================================================================================================================
 # === Vault template variant
 # =====================================================================================================================
+spec:
+  inputs:
+    vault-base-url:
+      description: The Vault server base API url
+      default: '' # null
+    vault-oidc-aud:
+      description: The `aud` claim for the JWT
+      default: $CI_SERVER_URL
+---
 variables:
   # variabilized vault-secrets-provider image
-  TBC_VAULT_IMAGE: "$CI_REGISTRY/to-be-continuous/tools/vault-secrets-provider:master"
+  TBC_VAULT_IMAGE: $CI_REGISTRY/to-be-continuous/tools/vault-secrets-provider:master
   # variables have to be explicitly declared in the YAML to be exported to the service
   VAULT_ROLE_ID: "$VAULT_ROLE_ID"
   VAULT_SECRET_ID: "$VAULT_SECRET_ID"
-  VAULT_OIDC_AUD: "$CI_SERVER_URL"
+  VAULT_OIDC_AUD: $[[ inputs.vault-oidc-aud ]]
+  VAULT_BASE_URL: $[[ inputs.vault-base-url ]]
 
 .python-base:
   services:
diff --git a/templates/gitlab-ci-python.yml b/templates/gitlab-ci-python.yml
index 7650ba1..e70df2e 100644
--- a/templates/gitlab-ci-python.yml
+++ b/templates/gitlab-ci-python.yml
@@ -13,6 +13,148 @@
 # program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 # Floor, Boston, MA  02110-1301, USA.
 # =========================================================================================
+spec:
+  inputs:
+    image:
+      description: The Docker image used to run Python - **set the version required by your project**
+      default: registry.hub.docker.com/library/python:3
+    project-dir:
+      description: Python project root directory
+      default: .
+    build-system:
+      description: Python build-system to use to install dependencies, build and package the project
+      options:
+      - auto
+      - setuptools
+      - poetry
+      - pipenv
+      - reqfile
+      default: auto
+    reqs-file:
+      description: |-
+        Main requirements file _(relative to `$PYTHON_PROJECT_DIR`)_
+
+        For [Requirements Files](https://pip.pypa.io/en/stable/user_guide/#requirements-files) build-system only
+      default: requirements.txt
+    extra-reqs-files:
+      description: |-
+        Extra dev requirements file(s) to install _(relative to `$PYTHON_PROJECT_DIR`)_
+
+        For [Requirements Files](https://pip.pypa.io/en/stable/user_guide/#requirements-files) build-system only
+      default: requirements-dev.txt
+    compile-args:
+      description: '[`compileall` CLI options](https://docs.python.org/3/library/compileall.html)'
+      default: '*'
+    pip-opts:
+      description: pip extra [options](https://pip.pypa.io/en/stable/cli/pip/#general-options)
+      default: '' # null
+    extra-deps:
+      description: |-
+        Extra sets of dependencies to install
+
+        For [Setuptools](https://setuptools.pypa.io/en/latest/userguide/dependency_management.html?highlight=extras#optional-dependencies) or [Poetry](https://python-poetry.org/docs/pyproject/#extras) only
+      default: '' # null
+    package-enabled:
+      description: Enable package
+      type: boolean
+      default: false
+    pylint-enabled:
+      description: Enable pylint
+      type: boolean
+      default: false
+    pylint-args:
+      description: Additional [pylint CLI options](http://pylint.pycqa.org/en/latest/user_guide/run.html#command-line-options)
+      default: '' # null
+    pylint-files:
+      description: Files or directories to analyse
+      default: '' # null
+    unittest-enabled:
+      description: Enable unittest
+      type: boolean
+      default: false
+    unittest-args:
+      description: Additional xmlrunner/unittest CLI options
+      default: '' # null
+    pytest-enabled:
+      description: Enable pytest
+      type: boolean
+      default: false
+    pytest-args:
+      description: Additional [pytest](https://docs.pytest.org/en/stable/usage.html) or [pytest-cov](https://github.com/pytest-dev/pytest-cov#usage) CLI options
+      default: '' # null
+    nosetests-enabled:
+      description: Enable nosetest
+      type: boolean
+      default: false
+    nosetests-args:
+      description: Additional [nose CLI options](https://nose.readthedocs.io/en/latest/usage.html#options)
+      default: '' # null
+    bandit-enabled:
+      description: Enable Bandit
+      type: boolean
+      default: false
+    bandit-args:
+      description: Additional [Bandit CLI options](https://github.com/PyCQA/bandit#usage)
+      default: --recursive .
+    safety-enabled:
+      description: Enable Safety
+      type: boolean
+      default: false
+    safety-args:
+      description: Additional [Safety CLI options](https://github.com/pyupio/safety#usage)
+      default: --full-report
+    trivy-enabled:
+      description: Enable Trivy
+      type: boolean
+      default: false
+    trivy-image:
+      description: The Docker image used to run Trivy
+      default: registry.hub.docker.com/aquasec/trivy:latest
+    trivy-args:
+      description: Additional [Trivy CLI options](https://aquasecurity.github.io/trivy/v0.21.1/getting-started/cli/fs/)
+      default: --vuln-type library
+    sbom-disabled:
+      description: Disable Software Bill of Materials
+      type: boolean
+      default: false
+    sbom-syft-url:
+      description: |-
+        Url to the `tar.gz` package for `linux_amd64` of Syft to use
+
+        _When unset, the latest version will be used_
+      default: '' # null
+    sbom-name:
+      description: Component name of the emitted SBOM
+      default: $CI_PROJECT_PATH/$PYTHON_PROJECT_DIR
+    sbom-opts:
+      description: Options for syft used for SBOM analysis
+      default: --catalogers python-index-cataloger
+    release-enabled:
+      description: Enable Release
+      type: boolean
+      default: false
+    release-next:
+      description: 'The part of the version to increase (one of: `major`, `minor`, `patch`)'
+      options:
+      - ''
+      - major
+      - minor
+      - patch
+      default: minor
+    semrel-release-disabled:
+      description: Disable semantic-release integration
+      type: boolean
+      default: false
+    release-commit-message:
+      description: The Git commit message to use on the release commit. This is templated using the [Python Format String Syntax](http://docs.python.org/2/library/string.html#format-string-syntax). Available in the template context are current_version and new_version.
+      default: "chore(python-release): {current_version} \u2192 {new_version}"
+    repository-url:
+      description: |-
+        Target PyPI repository to publish packages.
+
+        _defaults to [GitLab project's packages repository](https://docs.gitlab.com/ee/user/packages/pypi_repository/)_
+      default: ${CI_SERVER_URL}/api/v4/projects/${CI_PROJECT_ID}/packages/pypi
+---
 # default workflow rules: Merge Request pipelines
 workflow:
   rules:
@@ -66,12 +208,13 @@ variables:
   POETRY_VIRTUALENVS_IN_PROJECT: "false"
   PIPENV_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pipenv"
 
-  PYTHON_IMAGE: "registry.hub.docker.com/library/python:3"
+  # PYTHON_IMAGE: "registry.hub.docker.com/library/python:3"
+  PYTHON_IMAGE: $[[ inputs.image ]]
   # Default Python project root directory
-  PYTHON_PROJECT_DIR: .
+  PYTHON_PROJECT_DIR: $[[ inputs.project-dir ]]
 
-  PYTHON_REQS_FILE: requirements.txt
-  PYTHON_EXTRA_REQS_FILES: "requirements-dev.txt"
+  PYTHON_REQS_FILE: $[[ inputs.reqs-file ]]
+  PYTHON_EXTRA_REQS_FILES: $[[ inputs.extra-reqs-files ]]
 
   # default production ref name (pattern)
   PROD_REF: '/^(master|main)$/'
@@ -81,29 +224,54 @@ variables:
   RELEASE_REF: '/^v?[0-9]+\.[0-9]+\.[0-9]+$/'
 
   # compileall
-  PYTHON_COMPILE_ARGS: "*"
+  PYTHON_COMPILE_ARGS: $[[ inputs.compile-args ]]
 
-  BANDIT_ARGS: "--recursive ."
+  BANDIT_ARGS: $[[ inputs.bandit-args ]]
 
   # Safety tool
-  SAFETY_ARGS: "--full-report"
+  SAFETY_ARGS: $[[ inputs.safety-args ]]
 
   # Trivy tool
-  PYTHON_TRIVY_IMAGE: "registry.hub.docker.com/aquasec/trivy:latest"
-  PYTHON_TRIVY_ARGS: "--vuln-type library"
+  PYTHON_TRIVY_ENABLED: $[[ inputs.trivy-enabled ]]
+  PYTHON_TRIVY_IMAGE: $[[ inputs.trivy-image ]]
+  PYTHON_TRIVY_ARGS: $[[ inputs.trivy-args ]]
 
-  PYTHON_SBOM_NAME: "$CI_PROJECT_PATH/$PYTHON_PROJECT_DIR"
-  PYTHON_SBOM_OPTS: "--catalogers python-index-cataloger"
+  PYTHON_SBOM_NAME: $[[ inputs.sbom-name ]]
+  PYTHON_SBOM_OPTS: $[[ inputs.sbom-opts ]]
 
-  PYTHON_RELEASE_NEXT: "minor"
-  PYTHON_RELEASE_COMMIT_MESSAGE: "chore(python-release): {current_version} → {new_version}"
+  PYTHON_RELEASE_NEXT: $[[ inputs.release-next ]]
+  PYTHON_RELEASE_COMMIT_MESSAGE: $[[ inputs.release-commit-message ]]
 
   # By default, publish on the Packages registry of the project
   # https://docs.gitlab.com/ee/user/packages/pypi_repository/#authenticate-with-a-ci-job-token
-  PYTHON_REPOSITORY_URL: ${CI_SERVER_URL}/api/v4/projects/${CI_PROJECT_ID}/packages/pypi
-  PYTHON_REPOSITORY_USERNAME: 'gitlab-ci-token'
+  PYTHON_REPOSITORY_URL: $[[ inputs.repository-url ]]
+  PYTHON_REPOSITORY_USERNAME: gitlab-ci-token
   PYTHON_REPOSITORY_PASSWORD: $CI_JOB_TOKEN
 
+  PYTHON_BUILD_SYSTEM: $[[ inputs.build-system ]]
+  PIP_OPTS: $[[ inputs.pip-opts ]]
+  PYTHON_EXTRA_DEPS: $[[ inputs.extra-deps ]]
+  PYTHON_PACKAGE_ENABLED: $[[ inputs.package-enabled ]]
+  PYLINT_ENABLED: $[[ inputs.pylint-enabled ]]
+  PYLINT_ARGS: $[[ inputs.pylint-args ]]
+  PYLINT_FILES: $[[ inputs.pylint-files ]]
+  UNITTEST_ENABLED: $[[ inputs.unittest-enabled ]]
+  UNITTEST_ARGS: $[[ inputs.unittest-args ]]
+  PYTEST_ENABLED: $[[ inputs.pytest-enabled ]]
+  PYTEST_ARGS: $[[ inputs.pytest-args ]]
+  NOSETESTS_ARGS: $[[ inputs.nosetests-args ]]
+
+  PYTHON_SBOM_SYFT_URL: $[[ inputs.sbom-syft-url ]]
+
+  PYTHON_SEMREL_RELEASE_DISABLED: $[[ inputs.semrel-release-disabled ]]
+
+  NOSETESTS_ENABLED: $[[ inputs.nosetests-enabled ]]
+  BANDIT_ENABLED: $[[ inputs.bandit-enabled ]]
+  SAFETY_ENABLED: $[[ inputs.safety-enabled ]]
+  PYTHON_SBOM_DISABLED: $[[ inputs.sbom-disabled ]]
+  PYTHON_RELEASE_ENABLED: $[[ inputs.release-enabled ]]
+
+
 .python-scripts: &python-scripts |
   # BEGSCRIPT
   set -e
-- 
GitLab