Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package gcil for openSUSE:Factory checked in 
at 2026-11-09 16:07:38
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/gcil (Old)
 and      /work/SRC/openSUSE:Factory/.gcil.new.3759290 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "gcil"

Mon Nov  9 16:07:38 2026 rev:5 rq:1396283 version:13.3.0

Changes:
--------
--- /work/SRC/openSUSE:Factory/gcil/gcil.changes        2026-09-21 
12:24:12.822785388 +0200
+++ /work/SRC/openSUSE:Factory/.gcil.new.3759290/gcil.changes   2026-11-09 
16:08:44.514115784 +0100
@@ -1,0 +2,34 @@
+Sun Nov  8 09:45:06 UTC 2026 - Johannes Kastl 
<[email protected]>
+
+- update to 13.3.0:
+  * Features
+    - cli, menus: implement '--presets' to preselect jobs in menu
+      (e2928df)
+    - mise: avoid '--path' if already the default 'mise use'
+      configuration (3e152f0)
+    - updates: show 'mise use' update command if running from
+      'mise' (117b2ab)
+    - updates: show 'mise use' update in the 'mise' configuration
+      of the tool (255cf86)
+  * Bug Fixes
+    - themes: resolve yellow color support on light theme UI
+      (4e0d170)
+  * Documentation
+    - mkdocs: exclude generated pages from
+      'git-revision-date-localized' (6dd18c8)
+    - readme, prepare: link 'pipeline' badges to GitLab CI
+      pipelines page (330fe4a)
+  * Cleanups
+    - cliff: migrate 'cliff.toml' to 'conf/git-cliff/' folder
+      (60c051e)
+    - mise: create 'menu:gcil' task for 'gcil' use cases (45dfb35)
+    - mise: update 'changelog' task to generate with 'git-cliff'
+      (56c9af6)
+    - mise: remove 'gcil' task in favor of 'menu:gcil' (c0c0a91)
+    - pre-commit: migrate to 'pre-commit-crocodile' 8.4.0 (43fa2e7)
+    - pre-commit: migrate to 'pre-commit-crocodile' 9.0.0 (e5e136b)
+  * Build
+    - pages: install 'git-cliff' 2.14.1 through 'mise' from
+      'extrepo' (7918741)
+
+-------------------------------------------------------------------

Old:
----
  gitlabci_local-13.2.0.tar.gz

New:
----
  gitlabci_local-13.3.0.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ gcil.spec ++++++
--- /var/tmp/diff_new_pack.JbDJDh/_old  2026-11-09 16:08:45.349150762 +0100
+++ /var/tmp/diff_new_pack.JbDJDh/_new  2026-11-09 16:08:45.351150846 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           gcil
-Version:        13.2.0
+Version:        13.3.0
 Release:        0
 Summary:        Launch .gitlab-ci.yml jobs locally
 License:        Apache-2.0

++++++ gitlabci_local-13.2.0.tar.gz -> gitlabci_local-13.3.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/.gitlab-ci.yml 
new/gitlabci_local-13.3.0/.gitlab-ci.yml
--- old/gitlabci_local-13.2.0/.gitlab-ci.yml    2026-09-19 19:10:29.335961300 
+0200
+++ new/gitlabci_local-13.3.0/.gitlab-ci.yml    2026-11-08 01:01:01.307126500 
+0100
@@ -1,5 +1,5 @@
 include:
-  - component: 
gitlab.com/RadianDevCore/tools/pre-commit-crocodile/[email protected]
+  - component: 
gitlab.com/RadianDevCore/tools/pre-commit-crocodile/[email protected]
     inputs:
       stage: prepare
   - project: RadianDevCore/gitlab-ci/components
@@ -100,7 +100,7 @@
       stage: deploy
       image: registry.gitlab.com/radiandevcore/tools/gcil/gcil:pages
       pdf: gcil
-      watch: ./config/* ./docs/.* ./docs/* ./mkdocs.yml ./README.md
+      watch: ./conf/git-cliff/* ./docs/.* ./docs/* ./mkdocs.yml ./README.md
 
 stages:
   - development
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/.mise/tasks/gcil 
new/gitlabci_local-13.3.0/.mise/tasks/gcil
--- old/gitlabci_local-13.2.0/.mise/tasks/gcil  2026-09-19 19:10:29.335961300 
+0200
+++ new/gitlabci_local-13.3.0/.mise/tasks/gcil  1970-01-01 01:00:00.000000000 
+0100
@@ -1,8 +0,0 @@
-#!/usr/bin/env bash
-
-# Mise
-#MISE alias=["interactive"]
-#MISE description="Interactive menu with gcil"
-
-# Action
-gcil "${@}"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/.mise/tasks/menu/gcil 
new/gitlabci_local-13.3.0/.mise/tasks/menu/gcil
--- old/gitlabci_local-13.2.0/.mise/tasks/menu/gcil     1970-01-01 
01:00:00.000000000 +0100
+++ new/gitlabci_local-13.3.0/.mise/tasks/menu/gcil     2026-11-08 
01:01:01.307126500 +0100
@@ -0,0 +1,8 @@
+#!/usr/bin/env bash
+
+# Mise
+#MISE alias=["interactive", "gcil"]
+#MISE description="Interactive menu with gcil"
+
+# Action
+gcil "${@}"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/.mise/tasks/sources/changelog 
new/gitlabci_local-13.3.0/.mise/tasks/sources/changelog
--- old/gitlabci_local-13.2.0/.mise/tasks/sources/changelog     2026-09-19 
19:10:29.335961300 +0200
+++ new/gitlabci_local-13.3.0/.mise/tasks/sources/changelog     2026-11-08 
01:01:01.307126500 +0100
@@ -15,18 +15,40 @@
 if [ ! -z "${release_tag:-}" ]; then
   tag="${release_tag}"
 else
-  read -p ' > Release tag : ' -r tag
+  read -p ' > Release tag [ENTER to skip tag]: ' -r tag
 fi
 echo ' '
 
+# Prepare release tag
+if [ ! -z "${tag}" ]; then
+  git tag -f -m "${tag}" "${tag}"
+fi
+
+# Detect configuration
+config='./conf/git-cliff/cliff.toml'
+for path in ./conf/git-cliff/cliff.toml ./config/cliff.toml ./conf/cliff.toml; 
do
+  if [ -e "${path}" ]; then
+    config="${path}"
+    break
+  fi
+done
+
 # Generate changelog
-git tag -f -m "${tag}" "${tag}"
-git-cliff --no-exec --config ./config/cliff.toml --output ./CHANGELOG.md
-sed -i "s#raw/[^/]*/docs#raw/${tag}/docs#g" ./README.md
+git-cliff --no-exec --config "${config}" --output ./CHANGELOG.md
+if [ ! -z "${tag}" ]; then
+  sed -i "s#raw/[^/]*/docs#raw/${tag}/docs#g" ./README.md
+fi
 git add -v ./CHANGELOG.md ./README.md
 echo ' '
-git commit -m "docs(changelog): release version ${tag} and refresh changes 
history" -s
+if [ ! -z "${tag}" ]; then
+  git commit -m "docs(changelog): release version ${tag} and refresh changes 
history" -s
+else
+  version=$(git describe --abbrev=7 --always --long --tags)
+  git commit -m "docs(changelog): update to ${version} and refresh changes 
history" -s
+fi
 echo ' '
 
 # Create release tag
-git tag -f -m "${tag}" "${tag}"
+if [ ! -z "${tag}" ]; then
+  git tag -f -m "${tag}" "${tag}"
+fi
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/.pre-commit-config.yaml 
new/gitlabci_local-13.3.0/.pre-commit-config.yaml
--- old/gitlabci_local-13.2.0/.pre-commit-config.yaml   2026-09-19 
19:10:29.335961300 +0200
+++ new/gitlabci_local-13.3.0/.pre-commit-config.yaml   2026-11-08 
01:01:01.307126500 +0100
@@ -9,8 +9,8 @@
   - pre-commit
   - pre-push
   - prepare-commit-msg
-minimum_pre_commit_version: 3.8.0
-minimum_prek_version: 0.2.1
+minimum_pre_commit_version: 4.6.2
+minimum_prek_version: 0.5.2
 
 # pre-commit exclusions
 exclude: >
@@ -45,6 +45,12 @@
     hooks:
       - id: check-added-large-files
         args: ['--maxkb=500']
+        # exclude: >
+        #   (?x)(
+        #     package-lock\.json$|
+        #     pnpm-lock\.yaml$|
+        #     yarn\.lock$
+        #   )
       - id: check-case-conflict
         # exclude: >
         #   (?x)(
@@ -79,7 +85,7 @@
 
   # Repository: commitizen
   - repo: https://github.com/commitizen-tools/commitizen
-    rev: v4.9.1
+    rev: v4.19.3
     hooks:
       - id: commitizen
         stages:
@@ -92,7 +98,7 @@
 
   # Repository: pre-commit-crocodile
   - repo: https://gitlab.com/RadianDevCore/tools/pre-commit-crocodile
-    rev: 8.2.0
+    rev: 9.0.0
     hooks:
       - id: check-yaml-ruamel-pure
       - id: prepare-commit-message
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/CHANGELOG.md 
new/gitlabci_local-13.3.0/CHANGELOG.md
--- old/gitlabci_local-13.2.0/CHANGELOG.md      2026-09-19 19:10:29.336961300 
+0200
+++ new/gitlabci_local-13.3.0/CHANGELOG.md      2026-11-08 01:01:01.309126400 
+0100
@@ -1,5 +1,38 @@
 # Changelog
 
+<a name="13.3.0"></a>
+## 
[13.3.0](https://gitlab.com/RadianDevCore/tools/gcil/compare/13.2.0...13.3.0) 
(2026-11-07)
+
+### โœจ Features
+
+- **cli, menus:** implement '--presets' to preselect jobs in menu 
([e2928df](https://gitlab.com/RadianDevCore/tools/gcil/commit/e2928dfb3cb21f008659e8c4a218bcf505c27667))
+- **mise:** avoid '--path' if already the default 'mise use' configuration 
([3e152f0](https://gitlab.com/RadianDevCore/tools/gcil/commit/3e152f08c72a05165c9b0cefc7e1d5a40ce25222))
+- **updates:** show 'mise use' update command if running from 'mise' 
([117b2ab](https://gitlab.com/RadianDevCore/tools/gcil/commit/117b2ab8c849a5fddc7b298cd02af8280559d92b))
+- **updates:** show 'mise use' update in the 'mise' configuration of the tool 
([255cf86](https://gitlab.com/RadianDevCore/tools/gcil/commit/255cf86efbbe30a57de77200ad27e992d3d0318b))
+
+### ๐Ÿ› Bug Fixes
+
+- **themes:** resolve yellow color support on light theme UI 
([4e0d170](https://gitlab.com/RadianDevCore/tools/gcil/commit/4e0d1702307609965b362707aacf829cdc31ea27))
+
+### ๐Ÿ“š Documentation
+
+- **mkdocs:** exclude generated pages from 'git-revision-date-localized' 
([6dd18c8](https://gitlab.com/RadianDevCore/tools/gcil/commit/6dd18c8d859450552ebfe7664cbf5a41293ed12d))
+- **readme, prepare:** link 'pipeline' badges to GitLab CI pipelines page 
([330fe4a](https://gitlab.com/RadianDevCore/tools/gcil/commit/330fe4afcf9a5afe39ba01acb91bd65ece4928d1))
+
+### โš™๏ธ Cleanups
+
+- **cliff:** migrate 'cliff.toml' to 'conf/git-cliff/' folder 
([60c051e](https://gitlab.com/RadianDevCore/tools/gcil/commit/60c051e2ce61c55e399be4739c129de398a93abd))
+- **mise:** create 'menu:gcil' task for 'gcil' use cases 
([45dfb35](https://gitlab.com/RadianDevCore/tools/gcil/commit/45dfb3596163e84985882bc6ca4a55199199ea4b))
+- **mise:** update 'changelog' task to generate with 'git-cliff' 
([56c9af6](https://gitlab.com/RadianDevCore/tools/gcil/commit/56c9af6bfb1416da2cb0d106605ef8b1b612daa5))
+- **mise:** remove 'gcil' task in favor of 'menu:gcil' 
([c0c0a91](https://gitlab.com/RadianDevCore/tools/gcil/commit/c0c0a91cad477c2cbb5eddc0a9bc468f467bcccb))
+- **pre-commit:** migrate to 'pre-commit-crocodile' 8.4.0 
([43fa2e7](https://gitlab.com/RadianDevCore/tools/gcil/commit/43fa2e7c32c3c9da94579d863d71c40947055dcb))
+- **pre-commit:** migrate to 'pre-commit-crocodile' 9.0.0 
([e5e136b](https://gitlab.com/RadianDevCore/tools/gcil/commit/e5e136be51b0ed66ef7f83bc6c54a2a916e28b41))
+
+### ๐Ÿ“ฆ Build
+
+- **pages:** install 'git-cliff' 2.14.1 through 'mise' from 'extrepo' 
([7918741](https://gitlab.com/RadianDevCore/tools/gcil/commit/7918741ad411616b0916de5e67c5945f354d56d5))
+
+
 <a name="13.2.0"></a>
 ## 
[13.2.0](https://gitlab.com/RadianDevCore/tools/gcil/compare/13.1.0...13.2.0) 
(2026-09-19)
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/PKG-INFO 
new/gitlabci_local-13.3.0/PKG-INFO
--- old/gitlabci_local-13.2.0/PKG-INFO  2026-09-19 19:10:32.325950100 +0200
+++ new/gitlabci_local-13.3.0/PKG-INFO  2026-11-08 01:01:05.387138000 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: gitlabci-local
-Version: 13.2.0
+Version: 13.3.0
 Summary: Launch .gitlab-ci.yml jobs locally
 Home-page: https://gitlab.com/RadianDevCore/tools/gcil
 Author: Adrian DC
@@ -61,7 +61,7 @@
 
[![Downloads](https://img.shields.io/pypi/dm/gitlabci-local?color=blue)](https://pypi.org/project/gitlabci-local)
 
[![License](https://img.shields.io/gitlab/license/RadianDevCore/tools/gcil?color=blue)](https://gitlab.com/RadianDevCore/tools/gcil/-/blob/main/LICENSE)
 <br />
-[![Build](https://gitlab.com/RadianDevCore/tools/gcil/badges/main/pipeline.svg)](https://gitlab.com/RadianDevCore/tools/gcil/-/commits/main/)
+[![Build](https://gitlab.com/RadianDevCore/tools/gcil/badges/main/pipeline.svg)](https://gitlab.com/RadianDevCore/tools/gcil/-/pipelines?ref=main)
 
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=RadianDevCore_gcil&metric=bugs)](https://sonarcloud.io/dashboard?id=RadianDevCore_gcil)
 [![Code 
Smells](https://sonarcloud.io/api/project_badges/measure?project=RadianDevCore_gcil&metric=code_smells)](https://sonarcloud.io/dashboard?id=RadianDevCore_gcil)
 
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=RadianDevCore_gcil&metric=coverage)](https://sonarcloud.io/dashboard?id=RadianDevCore_gcil)
@@ -97,7 +97,7 @@
 
 ## Preview
 
-![preview.svg](https://gitlab.com/RadianDevCore/tools/gcil/raw/13.2.0/docs/preview.svg)
+![preview.svg](https://gitlab.com/RadianDevCore/tools/gcil/raw/13.3.0/docs/preview.svg)
 
 ---
 
@@ -111,6 +111,7 @@
 gcil                   # Launch the jobs choices interactive menu
 gcil -p                # Launch the jobs pipeline automatically
 gcil -l                # Launch the job selection interactive menu
+gcil -P 'Job 1'        # Launch the jobs choices menu with 'Job 1' preselected
 gcil 'Dev'             # Launch jobs where the name contains a given string
 gcil --debug 'Job 1'   # Hold a finishing specific job for debugging
 gcil --bash 'Job 1'    # Prepare a bash environment for a specific job
@@ -132,7 +133,7 @@
             [--privileged [BOOL]] [--random-paths] [-r] [-S] [--ssh 
[SSH_USER]] [-v VOLUME] [-w WORKDIR]
             [--bash | --debug] [--display] [--shell SHELL] [--all] 
[--configure] [--input KEY=VAL] [-f] [-i] [-m]
             [--no-console] [--no-git-safeties] [--no-script-fail] [-R] 
[--no-verbose] [--scripts] [-t TAGS]
-            [-d | -s | -l | --pull | --rmi] [--]
+            [-d | -s | -P | -l | --pull | --rmi] [--]
             [names ...]
 
 gcil: Launch .gitlab-ci.yml jobs locally (aliases: gitlabci-local)
@@ -199,6 +200,7 @@
 features arguments:
   -d, --dump           # Dump parsed .gitlab-ci.yml configuration
   -s, --select         # Force jobs selection from enumerated names
+  -P, --presets        # Select jobs with preset names
   -l, --list           # Select one job to run (implies --manual)
   --pull               # Pull container images from jobs
   --rmi                # Delete container images from jobs
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/README.md 
new/gitlabci_local-13.3.0/README.md
--- old/gitlabci_local-13.2.0/README.md 2026-09-19 19:10:29.336961300 +0200
+++ new/gitlabci_local-13.3.0/README.md 2026-11-08 01:01:01.309126400 +0100
@@ -7,7 +7,7 @@
 
[![Downloads](https://img.shields.io/pypi/dm/gitlabci-local?color=blue)](https://pypi.org/project/gitlabci-local)
 
[![License](https://img.shields.io/gitlab/license/RadianDevCore/tools/gcil?color=blue)](https://gitlab.com/RadianDevCore/tools/gcil/-/blob/main/LICENSE)
 <br />
-[![Build](https://gitlab.com/RadianDevCore/tools/gcil/badges/main/pipeline.svg)](https://gitlab.com/RadianDevCore/tools/gcil/-/commits/main/)
+[![Build](https://gitlab.com/RadianDevCore/tools/gcil/badges/main/pipeline.svg)](https://gitlab.com/RadianDevCore/tools/gcil/-/pipelines?ref=main)
 
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=RadianDevCore_gcil&metric=bugs)](https://sonarcloud.io/dashboard?id=RadianDevCore_gcil)
 [![Code 
Smells](https://sonarcloud.io/api/project_badges/measure?project=RadianDevCore_gcil&metric=code_smells)](https://sonarcloud.io/dashboard?id=RadianDevCore_gcil)
 
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=RadianDevCore_gcil&metric=coverage)](https://sonarcloud.io/dashboard?id=RadianDevCore_gcil)
@@ -43,7 +43,7 @@
 
 ## Preview
 
-![preview.svg](https://gitlab.com/RadianDevCore/tools/gcil/raw/13.2.0/docs/preview.svg)
+![preview.svg](https://gitlab.com/RadianDevCore/tools/gcil/raw/13.3.0/docs/preview.svg)
 
 ---
 
@@ -57,6 +57,7 @@
 gcil                   # Launch the jobs choices interactive menu
 gcil -p                # Launch the jobs pipeline automatically
 gcil -l                # Launch the job selection interactive menu
+gcil -P 'Job 1'        # Launch the jobs choices menu with 'Job 1' preselected
 gcil 'Dev'             # Launch jobs where the name contains a given string
 gcil --debug 'Job 1'   # Hold a finishing specific job for debugging
 gcil --bash 'Job 1'    # Prepare a bash environment for a specific job
@@ -78,7 +79,7 @@
             [--privileged [BOOL]] [--random-paths] [-r] [-S] [--ssh 
[SSH_USER]] [-v VOLUME] [-w WORKDIR]
             [--bash | --debug] [--display] [--shell SHELL] [--all] 
[--configure] [--input KEY=VAL] [-f] [-i] [-m]
             [--no-console] [--no-git-safeties] [--no-script-fail] [-R] 
[--no-verbose] [--scripts] [-t TAGS]
-            [-d | -s | -l | --pull | --rmi] [--]
+            [-d | -s | -P | -l | --pull | --rmi] [--]
             [names ...]
 
 gcil: Launch .gitlab-ci.yml jobs locally (aliases: gitlabci-local)
@@ -145,6 +146,7 @@
 features arguments:
   -d, --dump           # Dump parsed .gitlab-ci.yml configuration
   -s, --select         # Force jobs selection from enumerated names
+  -P, --presets        # Select jobs with preset names
   -l, --list           # Select one job to run (implies --manual)
   --pull               # Pull container images from jobs
   --rmi                # Delete container images from jobs
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/conf/git-cliff/cliff.toml 
new/gitlabci_local-13.3.0/conf/git-cliff/cliff.toml
--- old/gitlabci_local-13.2.0/conf/git-cliff/cliff.toml 1970-01-01 
01:00:00.000000000 +0100
+++ new/gitlabci_local-13.3.0/conf/git-cliff/cliff.toml 2026-11-08 
01:01:01.309126400 +0100
@@ -0,0 +1,100 @@
+# git-cliff ~ default configuration file
+# https://git-cliff.org/docs/configuration
+
+[changelog]
+# template for the changelog footer
+header = """# Changelog"""
+# template for the changelog body
+# https://keats.github.io/tera/docs/#introduction
+body = """
+{% raw %}\n{% endraw %}\
+{% if version %}\
+    <a name="{{version}}"></a>
+    {% if previous.version %}\
+        ## [{{ version }}](<REPO>/compare/{{ previous.version }}...{{ version 
}}) ({{ timestamp | date(format="%Y-%m-%d") }})
+    {% else %}\
+        ## [{{ version }}](<REPO>/commits/{{ version }}) ({{ timestamp | 
date(format="%Y-%m-%d") }})
+    {% endif %}\
+{% else %}\
+    ## [Development](<REPO>/commits/main) ({{ commits | last | 
get(key='committer') | get(key='timestamp')  | date(format="%Y-%m-%d") }})
+{% endif %}\
+{% for group, commits in commits | group_by(attribute="group") %}
+    ### {{ group | striptags | trim | upper_first }}
+    {% for commit in commits
+    | filter(attribute="scope")
+    | sort(attribute="scope") %}
+        - {% if commit.breaking %}**๐Ÿšจ BREAKING CHANGE ๐Ÿšจ |** {% endif %}\
+            **{{commit.scope}}:** \
+            {{ commit.message }} \
+            ([{{ commit.id | truncate(length=7, end="") }}](<REPO>/commit/{{ 
commit.id }}))
+    {%- endfor -%}
+    {% raw %}\n{% endraw %}\
+    {%- for commit in commits %}
+        {%- if commit.scope -%}
+        {% else -%}
+            - {% if commit.breaking %}**๐Ÿšจ BREAKING CHANGE ๐Ÿšจ |** {% endif %}\
+                {{ commit.message }} \
+                ([{{ commit.id | truncate(length=7, end="") 
}}](<REPO>/commit/{{ commit.id }}))
+        {% endif -%}
+    {% endfor -%}
+{% endfor -%}
+{% raw %}\n{% endraw %}\
+"""
+# template for the changelog footer
+footer = """"""
+# remove the leading and trailing s
+trim = true
+# postprocessors
+postprocessors = [
+  { pattern = '<REPO>', replace = 
"https://gitlab.com/RadianDevCore/tools/gcil"; }, # replace repository URL
+]
+
+[git]
+# parse the commits based on https://www.conventionalcommits.org
+conventional_commits = true
+# filter out the commits that are not conventional
+filter_unconventional = true
+# process each line of a commit as an individual commit
+split_commits = false
+# regex for preprocessing the commit messages
+commit_preprocessors = [
+  # Replace issue numbers
+  #{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = 
"([#${2}](<REPO>/issues/${2}))"},
+  # Check spelling of the commit with https://github.com/crate-ci/typos
+  # If the spelling is incorrect, it will be automatically fixed.
+  #{ pattern = '.*', replace_command = 'typos --write-changes -' },
+]
+# regex for parsing and grouping commits
+commit_parsers = [
+  { message = "^security", group = "<!-- 01 -->๐Ÿ›ก๏ธ Security" },
+  { body = ".*security",   group = "<!-- 01 -->๐Ÿ›ก๏ธ Security" },
+  { message = "^feat",     group = "<!-- 02 -->โœจ Features" },
+  { message = "^fix",      group = "<!-- 03 -->๐Ÿ› Bug Fixes" },
+  { message = "^refactor", group = "<!-- 04 -->๐Ÿšœ Code Refactoring" },
+  { message = "^docs\\(changelog\\)", skip = true },
+  { message = "^doc",      group = "<!-- 05 -->๐Ÿ“š Documentation" },
+  { message = "^perf",     group = "<!-- 06 -->โšก Performance Improvements" },
+  { message = "^style",    group = "<!-- 07 -->๐ŸŽจ Styling" },
+  { message = "^test",     group = "<!-- 08 -->๐Ÿงช Test" },
+  { message = "^chore",    group = "<!-- 09 -->โš™๏ธ Cleanups" },
+  { message = "^ci",       group = "<!-- 10 -->๐Ÿš€ CI" },
+  { message = "^build",    group = "<!-- 11 -->๐Ÿ“ฆ Build" },
+  { message = "^revert",   group = "<!-- 12 -->โ—€๏ธ Revert" },
+  { message = "^wip",      group = "<!-- 13 -->๐Ÿšง WIP" },
+]
+# protect breaking changes from being skipped due to matching a skipping 
commit_parser
+protect_breaking_commits = false
+# filter out the commits that are not matched by commit parsers
+filter_commits = false
+# regex for matching git tags
+# tag_pattern = "[0-9].*"
+# regex for skipping tags
+# skip_tags = ""
+# regex for ignoring tags
+ignore_tags = ""
+# sort the tags topologically
+topo_order = false
+# sort the commits inside sections by oldest/newest order
+sort_commits = "oldest"
+# limit the number of commits included in the changelog.
+# limit_commits = 42
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/config/cliff.toml 
new/gitlabci_local-13.3.0/config/cliff.toml
--- old/gitlabci_local-13.2.0/config/cliff.toml 2026-09-19 19:10:29.336961300 
+0200
+++ new/gitlabci_local-13.3.0/config/cliff.toml 1970-01-01 01:00:00.000000000 
+0100
@@ -1,100 +0,0 @@
-# git-cliff ~ default configuration file
-# https://git-cliff.org/docs/configuration
-
-[changelog]
-# template for the changelog footer
-header = """# Changelog"""
-# template for the changelog body
-# https://keats.github.io/tera/docs/#introduction
-body = """
-{% raw %}\n{% endraw %}\
-{% if version %}\
-    <a name="{{version}}"></a>
-    {% if previous.version %}\
-        ## [{{ version }}](<REPO>/compare/{{ previous.version }}...{{ version 
}}) ({{ timestamp | date(format="%Y-%m-%d") }})
-    {% else %}\
-        ## [{{ version }}](<REPO>/commits/{{ version }}) ({{ timestamp | 
date(format="%Y-%m-%d") }})
-    {% endif %}\
-{% else %}\
-    ## [Development](<REPO>/commits/main) ({{ commits | last | 
get(key='committer') | get(key='timestamp')  | date(format="%Y-%m-%d") }})
-{% endif %}\
-{% for group, commits in commits | group_by(attribute="group") %}
-    ### {{ group | striptags | trim | upper_first }}
-    {% for commit in commits
-    | filter(attribute="scope")
-    | sort(attribute="scope") %}
-        - {% if commit.breaking %}**๐Ÿšจ BREAKING CHANGE ๐Ÿšจ |** {% endif %}\
-            **{{commit.scope}}:** \
-            {{ commit.message }} \
-            ([{{ commit.id | truncate(length=7, end="") }}](<REPO>/commit/{{ 
commit.id }}))
-    {%- endfor -%}
-    {% raw %}\n{% endraw %}\
-    {%- for commit in commits %}
-        {%- if commit.scope -%}
-        {% else -%}
-            - {% if commit.breaking %}**๐Ÿšจ BREAKING CHANGE ๐Ÿšจ |** {% endif %}\
-                {{ commit.message }} \
-                ([{{ commit.id | truncate(length=7, end="") 
}}](<REPO>/commit/{{ commit.id }}))
-        {% endif -%}
-    {% endfor -%}
-{% endfor -%}
-{% raw %}\n{% endraw %}\
-"""
-# template for the changelog footer
-footer = """"""
-# remove the leading and trailing s
-trim = true
-# postprocessors
-postprocessors = [
-  { pattern = '<REPO>', replace = 
"https://gitlab.com/RadianDevCore/tools/gcil"; }, # replace repository URL
-]
-
-[git]
-# parse the commits based on https://www.conventionalcommits.org
-conventional_commits = true
-# filter out the commits that are not conventional
-filter_unconventional = true
-# process each line of a commit as an individual commit
-split_commits = false
-# regex for preprocessing the commit messages
-commit_preprocessors = [
-  # Replace issue numbers
-  #{ pattern = '\((\w+\s)?#([0-9]+)\)', replace = 
"([#${2}](<REPO>/issues/${2}))"},
-  # Check spelling of the commit with https://github.com/crate-ci/typos
-  # If the spelling is incorrect, it will be automatically fixed.
-  #{ pattern = '.*', replace_command = 'typos --write-changes -' },
-]
-# regex for parsing and grouping commits
-commit_parsers = [
-  { message = "^security", group = "<!-- 01 -->๐Ÿ›ก๏ธ Security" },
-  { body = ".*security",   group = "<!-- 01 -->๐Ÿ›ก๏ธ Security" },
-  { message = "^feat",     group = "<!-- 02 -->โœจ Features" },
-  { message = "^fix",      group = "<!-- 03 -->๐Ÿ› Bug Fixes" },
-  { message = "^refactor", group = "<!-- 04 -->๐Ÿšœ Code Refactoring" },
-  { message = "^docs\\(changelog\\)", skip = true },
-  { message = "^doc",      group = "<!-- 05 -->๐Ÿ“š Documentation" },
-  { message = "^perf",     group = "<!-- 06 -->โšก Performance Improvements" },
-  { message = "^style",    group = "<!-- 07 -->๐ŸŽจ Styling" },
-  { message = "^test",     group = "<!-- 08 -->๐Ÿงช Test" },
-  { message = "^chore",    group = "<!-- 09 -->โš™๏ธ Cleanups" },
-  { message = "^ci",       group = "<!-- 10 -->๐Ÿš€ CI" },
-  { message = "^build",    group = "<!-- 11 -->๐Ÿ“ฆ Build" },
-  { message = "^revert",   group = "<!-- 12 -->โ—€๏ธ Revert" },
-  { message = "^wip",      group = "<!-- 13 -->๐Ÿšง WIP" },
-]
-# protect breaking changes from being skipped due to matching a skipping 
commit_parser
-protect_breaking_commits = false
-# filter out the commits that are not matched by commit parsers
-filter_commits = false
-# regex for matching git tags
-# tag_pattern = "[0-9].*"
-# regex for skipping tags
-# skip_tags = ""
-# regex for ignoring tags
-ignore_tags = ""
-# sort the tags topologically
-topo_order = false
-# sort the commits inside sections by oldest/newest order
-sort_commits = "oldest"
-# limit the number of commits included in the changelog.
-# limit_commits = 42
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/containers/pages/Dockerfile 
new/gitlabci_local-13.3.0/containers/pages/Dockerfile
--- old/gitlabci_local-13.2.0/containers/pages/Dockerfile       2026-09-19 
19:10:29.336961300 +0200
+++ new/gitlabci_local-13.3.0/containers/pages/Dockerfile       2026-11-08 
01:01:01.309126400 +0100
@@ -4,12 +4,17 @@
 # Install dependencies
 RUN apt update \
  && apt install -y --no-install-recommends \
+      extrepo \
+ && extrepo enable mise \
+ && apt update \
+ && apt install -y --no-install-recommends \
       entr \
       git \
+      mise \
       wget \
- && wget -O ./git-cliff.deb -q 
https://github.com/orhun/git-cliff/releases/download/v2.5.0/git-cliff-2.5.0.deb 
\
- && apt install -y --no-install-recommends ./git-cliff.deb \
- && rm -f ./git-cliff.deb \
+ && export MISE_DATA_DIR=/opt/mise \
+ && mise use --global [email protected] \
+ && ln -s "$(mise which git-cliff)" /usr/local/bin/git-cliff \
  && rm -rf /var/lib/apt/lists/*
 
 # Install requirements
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/docs/deprecations.md 
new/gitlabci_local-13.3.0/docs/deprecations.md
--- old/gitlabci_local-13.2.0/docs/deprecations.md      2026-09-19 
19:10:29.337961200 +0200
+++ new/gitlabci_local-13.3.0/docs/deprecations.md      2026-11-08 
01:01:01.310126500 +0100
@@ -12,7 +12,7 @@
 
[![Downloads](https://img.shields.io/pypi/dm/gitlabci-local?color=blue)](https://pypi.org/project/gitlabci-local)
 
[![License](https://img.shields.io/gitlab/license/RadianDevCore/tools/gcil?color=blue)](https://gitlab.com/RadianDevCore/tools/gcil/-/blob/main/LICENSE)
 <br />
-[![Build](https://gitlab.com/RadianDevCore/tools/gcil/badges/main/pipeline.svg)](https://gitlab.com/RadianDevCore/tools/gcil/-/commits/main/)
+[![Build](https://gitlab.com/RadianDevCore/tools/gcil/badges/main/pipeline.svg)](https://gitlab.com/RadianDevCore/tools/gcil/-/pipelines?ref=main)
 
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=RadianDevCore_gcil&metric=bugs)](https://sonarcloud.io/dashboard?id=RadianDevCore_gcil)
 [![Code 
Smells](https://sonarcloud.io/api/project_badges/measure?project=RadianDevCore_gcil&metric=code_smells)](https://sonarcloud.io/dashboard?id=RadianDevCore_gcil)
 
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=RadianDevCore_gcil&metric=coverage)](https://sonarcloud.io/dashboard?id=RadianDevCore_gcil)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/docs/gitlab-ci.md 
new/gitlabci_local-13.3.0/docs/gitlab-ci.md
--- old/gitlabci_local-13.2.0/docs/gitlab-ci.md 2026-09-19 19:10:29.337961200 
+0200
+++ new/gitlabci_local-13.3.0/docs/gitlab-ci.md 2026-11-08 01:01:01.310126500 
+0100
@@ -7,7 +7,7 @@
 
[![Downloads](https://img.shields.io/pypi/dm/gitlabci-local?color=blue)](https://pypi.org/project/gitlabci-local)
 
[![License](https://img.shields.io/gitlab/license/RadianDevCore/tools/gcil?color=blue)](https://gitlab.com/RadianDevCore/tools/gcil/-/blob/main/LICENSE)
 <br />
-[![Build](https://gitlab.com/RadianDevCore/tools/gcil/badges/main/pipeline.svg)](https://gitlab.com/RadianDevCore/tools/gcil/-/commits/main/)
+[![Build](https://gitlab.com/RadianDevCore/tools/gcil/badges/main/pipeline.svg)](https://gitlab.com/RadianDevCore/tools/gcil/-/pipelines?ref=main)
 
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=RadianDevCore_gcil&metric=bugs)](https://sonarcloud.io/dashboard?id=RadianDevCore_gcil)
 [![Code 
Smells](https://sonarcloud.io/api/project_badges/measure?project=RadianDevCore_gcil&metric=code_smells)](https://sonarcloud.io/dashboard?id=RadianDevCore_gcil)
 
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=RadianDevCore_gcil&metric=coverage)](https://sonarcloud.io/dashboard?id=RadianDevCore_gcil)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/docs/hooks.md 
new/gitlabci_local-13.3.0/docs/hooks.md
--- old/gitlabci_local-13.2.0/docs/hooks.md     2026-09-19 19:10:29.337961200 
+0200
+++ new/gitlabci_local-13.3.0/docs/hooks.md     2026-11-08 01:01:01.310126500 
+0100
@@ -7,7 +7,7 @@
 
[![Downloads](https://img.shields.io/pypi/dm/gitlabci-local?color=blue)](https://pypi.org/project/gitlabci-local)
 
[![License](https://img.shields.io/gitlab/license/RadianDevCore/tools/gcil?color=blue)](https://gitlab.com/RadianDevCore/tools/gcil/-/blob/main/LICENSE)
 <br />
-[![Build](https://gitlab.com/RadianDevCore/tools/gcil/badges/main/pipeline.svg)](https://gitlab.com/RadianDevCore/tools/gcil/-/commits/main/)
+[![Build](https://gitlab.com/RadianDevCore/tools/gcil/badges/main/pipeline.svg)](https://gitlab.com/RadianDevCore/tools/gcil/-/pipelines?ref=main)
 
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=RadianDevCore_gcil&metric=bugs)](https://sonarcloud.io/dashboard?id=RadianDevCore_gcil)
 [![Code 
Smells](https://sonarcloud.io/api/project_badges/measure?project=RadianDevCore_gcil&metric=code_smells)](https://sonarcloud.io/dashboard?id=RadianDevCore_gcil)
 
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=RadianDevCore_gcil&metric=coverage)](https://sonarcloud.io/dashboard?id=RadianDevCore_gcil)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/docs/prepare.sh 
new/gitlabci_local-13.3.0/docs/prepare.sh
--- old/gitlabci_local-13.2.0/docs/prepare.sh   2026-09-19 19:10:29.337961200 
+0200
+++ new/gitlabci_local-13.3.0/docs/prepare.sh   2026-11-08 01:01:01.310126500 
+0100
@@ -94,7 +94,7 @@
   echo 'pdf: false'
   echo '---'
   echo ''
-  git-cliff --no-exec --config "${root_path}/config/cliff.toml" \
+  git-cliff --no-exec --config "${root_path}/conf/git-cliff/cliff.toml" \
     | sed '/^- /{ s/</\&lt;/g; s/>/\&gt;/g; }'
 } >"${docs_path}/about/changelog.md"
 diff -u "${root_path}/CHANGELOG.md" "${docs_path}/about/changelog.md" || true
@@ -189,7 +189,7 @@
     echo '        <b>GitLab</b>'
     echo '      </td>'
     echo '      <td>'
-    echo "        <a href=\"${projectUrl}/-/commits/${branch}/\">"
+    echo "        <a href=\"${projectUrl}/-/pipelines?ref=${branch}\">"
     echo "          <img src=\"${projectUrl}/badges/${branch}/pipeline.svg\" 
alt=\"Build\" style=\"max-width: 100%;\">"
     echo '        </a>'
     echo "        <a href=\"${projectUrl}/-/network/${branch}/\">"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/gitlabci_local-13.2.0/gitlabci_local.egg-info/PKG-INFO 
new/gitlabci_local-13.3.0/gitlabci_local.egg-info/PKG-INFO
--- old/gitlabci_local-13.2.0/gitlabci_local.egg-info/PKG-INFO  2026-09-19 
19:10:32.258950500 +0200
+++ new/gitlabci_local-13.3.0/gitlabci_local.egg-info/PKG-INFO  2026-11-08 
01:01:05.263137600 +0100
@@ -1,6 +1,6 @@
 Metadata-Version: 2.4
 Name: gitlabci-local
-Version: 13.2.0
+Version: 13.3.0
 Summary: Launch .gitlab-ci.yml jobs locally
 Home-page: https://gitlab.com/RadianDevCore/tools/gcil
 Author: Adrian DC
@@ -61,7 +61,7 @@
 
[![Downloads](https://img.shields.io/pypi/dm/gitlabci-local?color=blue)](https://pypi.org/project/gitlabci-local)
 
[![License](https://img.shields.io/gitlab/license/RadianDevCore/tools/gcil?color=blue)](https://gitlab.com/RadianDevCore/tools/gcil/-/blob/main/LICENSE)
 <br />
-[![Build](https://gitlab.com/RadianDevCore/tools/gcil/badges/main/pipeline.svg)](https://gitlab.com/RadianDevCore/tools/gcil/-/commits/main/)
+[![Build](https://gitlab.com/RadianDevCore/tools/gcil/badges/main/pipeline.svg)](https://gitlab.com/RadianDevCore/tools/gcil/-/pipelines?ref=main)
 
[![Bugs](https://sonarcloud.io/api/project_badges/measure?project=RadianDevCore_gcil&metric=bugs)](https://sonarcloud.io/dashboard?id=RadianDevCore_gcil)
 [![Code 
Smells](https://sonarcloud.io/api/project_badges/measure?project=RadianDevCore_gcil&metric=code_smells)](https://sonarcloud.io/dashboard?id=RadianDevCore_gcil)
 
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=RadianDevCore_gcil&metric=coverage)](https://sonarcloud.io/dashboard?id=RadianDevCore_gcil)
@@ -97,7 +97,7 @@
 
 ## Preview
 
-![preview.svg](https://gitlab.com/RadianDevCore/tools/gcil/raw/13.2.0/docs/preview.svg)
+![preview.svg](https://gitlab.com/RadianDevCore/tools/gcil/raw/13.3.0/docs/preview.svg)
 
 ---
 
@@ -111,6 +111,7 @@
 gcil                   # Launch the jobs choices interactive menu
 gcil -p                # Launch the jobs pipeline automatically
 gcil -l                # Launch the job selection interactive menu
+gcil -P 'Job 1'        # Launch the jobs choices menu with 'Job 1' preselected
 gcil 'Dev'             # Launch jobs where the name contains a given string
 gcil --debug 'Job 1'   # Hold a finishing specific job for debugging
 gcil --bash 'Job 1'    # Prepare a bash environment for a specific job
@@ -132,7 +133,7 @@
             [--privileged [BOOL]] [--random-paths] [-r] [-S] [--ssh 
[SSH_USER]] [-v VOLUME] [-w WORKDIR]
             [--bash | --debug] [--display] [--shell SHELL] [--all] 
[--configure] [--input KEY=VAL] [-f] [-i] [-m]
             [--no-console] [--no-git-safeties] [--no-script-fail] [-R] 
[--no-verbose] [--scripts] [-t TAGS]
-            [-d | -s | -l | --pull | --rmi] [--]
+            [-d | -s | -P | -l | --pull | --rmi] [--]
             [names ...]
 
 gcil: Launch .gitlab-ci.yml jobs locally (aliases: gitlabci-local)
@@ -199,6 +200,7 @@
 features arguments:
   -d, --dump           # Dump parsed .gitlab-ci.yml configuration
   -s, --select         # Force jobs selection from enumerated names
+  -P, --presets        # Select jobs with preset names
   -l, --list           # Select one job to run (implies --manual)
   --pull               # Pull container images from jobs
   --rmi                # Delete container images from jobs
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/gitlabci_local-13.2.0/gitlabci_local.egg-info/SOURCES.txt 
new/gitlabci_local-13.3.0/gitlabci_local.egg-info/SOURCES.txt
--- old/gitlabci_local-13.2.0/gitlabci_local.egg-info/SOURCES.txt       
2026-09-19 19:10:32.280950300 +0200
+++ new/gitlabci_local-13.3.0/gitlabci_local.egg-info/SOURCES.txt       
2026-11-08 01:01:05.297137700 +0100
@@ -18,12 +18,12 @@
 run.sh
 setup.py
 sonar-project.properties
-.mise/tasks/gcil
+.mise/tasks/menu/gcil
 .mise/tasks/sources/changelog
 .mise/tasks/sources/pull
 .vscode/extensions.json
 .vscode/settings.json
-config/cliff.toml
+conf/git-cliff/cliff.toml
 containers/build/Dockerfile
 containers/codestyle/Dockerfile
 containers/deploy/Dockerfile
@@ -82,6 +82,7 @@
 src/features/configurations.py
 src/features/images.py
 src/features/menus.py
+src/features/mise.py
 src/features/pipelines.py
 src/jobs/__init__.py
 src/jobs/outputs.py
@@ -113,6 +114,7 @@
 src/prints/histories.py
 src/prints/themes.py
 src/system/__init__.py
+src/system/commands.py
 src/system/git.py
 src/system/notify.py
 src/system/platform.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/gitlabci_local-13.2.0/gitlabci_local.egg-info/scm_file_list.json 
new/gitlabci_local-13.3.0/gitlabci_local.egg-info/scm_file_list.json
--- old/gitlabci_local-13.2.0/gitlabci_local.egg-info/scm_file_list.json        
2026-09-19 19:10:32.257950300 +0200
+++ new/gitlabci_local-13.3.0/gitlabci_local.egg-info/scm_file_list.json        
2026-11-08 01:01:05.261137700 +0100
@@ -7,7 +7,7 @@
     ".gitlab-ci.yml",
     ".gitmodules",
     ".markdownlint.json",
-    ".mise/tasks/gcil",
+    ".mise/tasks/menu/gcil",
     ".mise/tasks/sources/changelog",
     ".mise/tasks/sources/pull",
     ".pre-commit-config.yaml",
@@ -19,7 +19,7 @@
     "LICENSE",
     "README.md",
     "TEST.md",
-    "config/cliff.toml",
+    "conf/git-cliff/cliff.toml",
     "containers/build/Dockerfile",
     "containers/codestyle/Dockerfile",
     "containers/deploy/Dockerfile",
@@ -76,6 +76,7 @@
     "src/features/configurations.py",
     "src/features/images.py",
     "src/features/menus.py",
+    "src/features/mise.py",
     "src/features/pipelines.py",
     "src/jobs/__init__.py",
     "src/jobs/outputs.py",
@@ -107,6 +108,7 @@
     "src/prints/histories.py",
     "src/prints/themes.py",
     "src/system/__init__.py",
+    "src/system/commands.py",
     "src/system/git.py",
     "src/system/notify.py",
     "src/system/platform.py",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/gitlabci_local-13.2.0/gitlabci_local.egg-info/scm_version.json 
new/gitlabci_local-13.3.0/gitlabci_local.egg-info/scm_version.json
--- old/gitlabci_local-13.2.0/gitlabci_local.egg-info/scm_version.json  
2026-09-19 19:10:32.237950600 +0200
+++ new/gitlabci_local-13.3.0/gitlabci_local.egg-info/scm_version.json  
2026-11-08 01:01:05.234137500 +0100
@@ -1,8 +1,8 @@
 {
-  "tag": "13.2.0",
+  "tag": "13.3.0",
   "distance": 0,
-  "node": "g139222ce3c119b44d2a3ad34df2b017f569deb7a",
+  "node": "gf2db60f756ea3205325799db93794026c9c6a37c",
   "dirty": false,
   "branch": "HEAD",
-  "node_date": "2026-09-19"
+  "node_date": "2026-11-07"
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/mkdocs.yml 
new/gitlabci_local-13.3.0/mkdocs.yml
--- old/gitlabci_local-13.2.0/mkdocs.yml        2026-09-19 19:10:29.337961200 
+0200
+++ new/gitlabci_local-13.3.0/mkdocs.yml        2026-11-08 01:01:01.311126500 
+0100
@@ -82,6 +82,9 @@
       html_report_dir: coverage-reports/html
   - git-revision-date-localized:
       enable_creation_date: true
+      exclude:
+        - about/*
+        - index.md
       fallback_to_build_date: true
       strict: false
       type: timeago
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/src/cli/entrypoint.py 
new/gitlabci_local-13.3.0/src/cli/entrypoint.py
--- old/gitlabci_local-13.2.0/src/cli/entrypoint.py     2026-09-19 
19:10:29.338961400 +0200
+++ new/gitlabci_local-13.3.0/src/cli/entrypoint.py     2026-11-08 
01:01:01.312126400 +0100
@@ -147,6 +147,13 @@
                 options=options,
             ).select()
 
+        # Select jobs with presets
+        elif options.presets and interactive:
+            result = MenusFeature(
+                pipeline=pipeline,
+                options=options,
+            ).select()
+
         # Launch pipeline or jobs
         elif options.pipeline or options.names:
             result = PipelinesFeature(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/src/cli/main.py 
new/gitlabci_local-13.3.0/src/cli/main.py
--- old/gitlabci_local-13.2.0/src/cli/main.py   2026-09-19 19:10:29.338961400 
+0200
+++ new/gitlabci_local-13.3.0/src/cli/main.py   2026-11-08 01:01:01.312126400 
+0100
@@ -353,6 +353,13 @@
         help='Force jobs selection from enumerated names',
     )
     subgroup.add_argument(
+        '-P',
+        '--presets',
+        dest='presets',
+        action='store_true',
+        help='Select jobs with preset names',
+    )
+    subgroup.add_argument(
         '-l',
         '--list',
         dest='list',
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/src/features/menus.py 
new/gitlabci_local-13.3.0/src/features/menus.py
--- old/gitlabci_local-13.2.0/src/features/menus.py     2026-09-19 
19:10:29.338961400 +0200
+++ new/gitlabci_local-13.3.0/src/features/menus.py     2026-11-08 
01:01:01.312126400 +0100
@@ -213,8 +213,20 @@
         assert self.__pipeline is not None
         for job in self.__pipeline.jobs:
 
+            # Preset names
+            job_preset: bool = False
+            if self.__options.presets:
+                if self.__options.names and Lists.match(
+                        self.__options.names,
+                        self.__pipeline.jobs[job].stage
+                        if self.__options.pipeline else job,
+                        ignore_case=self.__options.ignore_case,
+                        no_regex=self.__options.no_regex,
+                ):
+                    job_preset = True
+
             # Filter names
-            if self.__options.names:
+            elif self.__options.names:
 
                 # Filter jobs list, pylint: disable=duplicate-code
                 if not self.__options.pipeline and not Lists.match(
@@ -283,7 +295,7 @@
                     
title=f'{self.__pipeline.jobs[job].name}{tags}{job_details_string}',
                     value=job,
                     disabled=disabled,
-                    checked=default_check,
+                    checked=default_check or (job_preset and not disabled),
                     shortcut_key=True,
                 )
             ]
@@ -337,6 +349,9 @@
         # Drop pipeline mode for jobs
         self.__options.pipeline = False
 
+        # Launch selected jobs only
+        self.__options.no_regex = True
+
         # Footer
         print(' ')
         print(' ')
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/src/features/mise.py 
new/gitlabci_local-13.3.0/src/features/mise.py
--- old/gitlabci_local-13.2.0/src/features/mise.py      1970-01-01 
01:00:00.000000000 +0100
+++ new/gitlabci_local-13.3.0/src/features/mise.py      2026-11-08 
01:01:01.312126400 +0100
@@ -0,0 +1,244 @@
+#!/usr/bin/env python3
+
+# Standard libraries
+from json import loads
+from os import environ
+from pathlib import Path
+from re import MULTILINE, search, sub
+from typing import Any, Dict, List, Optional, Tuple
+
+# Components
+from ..package.version import Version
+from ..system.commands import Commands
+
+# Mise class
+class Mise:
+
+    # Constants
+    BACKEND_DEFAULT: str = 'pipx'
+    BACKENDS_PYTHON: List[str] = ['pipx', 'pypi']
+    BINARY: str = 'mise'
+    CONFIG_FILE: str = 'config.toml'
+    ENV_CONFIG_DIR: str = 'MISE_CONFIG_DIR'
+    ENV_DATA_DIR: str = 'MISE_DATA_DIR'
+    ENV_GLOBAL_CONFIG_FILE: str = 'MISE_GLOBAL_CONFIG_FILE'
+    ENV_XDG_CONFIG_HOME: str = 'XDG_CONFIG_HOME'
+    INSTALLS: str = 'installs'
+    VERSION_LATEST: str = 'latest'
+
+    # Arguments
+    @staticmethod
+    def arguments(config: str, specification: str = '') -> List[str]:
+
+        # Use default configuration
+        if config and specification \
+                and Path(config).expanduser().resolve() == 
Mise.target(specification):
+            return []
+
+        # Use global configuration
+        if config and Path(config).expanduser().resolve() == 
Mise.config_global():
+            return [
+                '--global',
+            ]
+
+        # Use configuration file
+        if config:
+            return [
+                '--path',
+                config,
+            ]
+
+        # Use local configuration
+        return []
+
+    # Dependencies
+    @staticmethod
+    def dependencies(
+        binary: str,
+        package: str,
+        version: str = VERSION_LATEST,
+    ) -> Optional[bool]:
+
+        # Variables
+        config: str
+        provided: str
+        tool: Optional[Tuple[str, str]]
+
+        # Ignore missing mise
+        if not Mise.exists():
+            return None
+
+        # Detect packages
+        provided = Commands.output(Mise.BINARY, [
+            'which',
+            binary,
+        ], quiet=True)
+        tool = Mise.tool(provided) if provided else None # pragma: no cover
+
+        # Upgrade packages
+        if provided and version == Mise.VERSION_LATEST: # pragma: no cover
+            return Commands.mise([
+                'upgrade',
+                tool[0] if tool else package,
+            ])
+
+        # Detect configuration file
+        if Mise.managed(): # pragma: no cover
+            config = Mise.config()
+        elif provided: # pragma: no cover
+            config = tool[1] if tool else ''
+        else: # pragma: no cover
+            return None
+
+        # Install packages in the configuration file
+        specification: str = f'{package}@{version}' # pragma: no cover
+        return Commands.mise( # pragma: no cover
+            ['use'] + Mise.arguments(config, specification) + [specification])
+
+    # Configuration
+    @staticmethod
+    def config() -> str:
+
+        # Detect configuration file
+        tool: Optional[Tuple[str, str]] = Mise.tool(Version.path())
+        if tool: # pragma: no cover
+            return tool[1]
+
+        # Fallback
+        return ''
+
+    # Configuration global
+    @staticmethod
+    def config_global() -> Path:
+
+        # Detect custom global configuration file
+        config_file: str = environ.get(Mise.ENV_GLOBAL_CONFIG_FILE, '')
+        if config_file:
+            return Path(config_file).expanduser().resolve()
+
+        # Detect global configuration folder
+        config_dir: str = environ.get(Mise.ENV_CONFIG_DIR, '')
+        if not config_dir:
+            config_dir = str(
+                Path(environ.get(Mise.ENV_XDG_CONFIG_HOME, '~/.config')) / 
Mise.BINARY)
+
+        # Result
+        return (Path(config_dir) / Mise.CONFIG_FILE).expanduser().resolve()
+
+    # Exists
+    @staticmethod
+    def exists() -> bool:
+
+        # Check if binary exists
+        return Commands.exists(Mise.BINARY)
+
+    # Installation
+    @staticmethod
+    def installation() -> str:
+
+        # Variables
+        parts = Path(Version.path()).parts
+
+        # Detect custom mise installs
+        data_dir: str = environ.get(Mise.ENV_DATA_DIR, '')
+        if data_dir:
+            installs = Path(data_dir).expanduser().parts + (Mise.INSTALLS, )
+            if parts[0:len(installs)] == installs and len(parts) > 
len(installs):
+                return parts[len(installs)] # pragma: no cover
+
+        # Detect default mise installs
+        for index in range(len(parts) - 2):
+            if parts[index] == Mise.BINARY and parts[index + 1] == 
Mise.INSTALLS:
+                return parts[index + 2] # pragma: no cover
+
+        # Fallback
+        return ''
+
+    # Managed
+    @staticmethod
+    def managed() -> bool:
+
+        # Check if running from mise
+        return bool(Mise.installation())
+
+    # Target
+    @staticmethod
+    def target(specification: str) -> Optional[Path]:
+
+        # Detect default configuration file
+        output: str = Commands.output(Mise.BINARY, [
+            'use',
+            '--dry-run',
+            specification,
+        ], errors=True)
+        match = search(
+            r'^mise would \w+ (.+) \(',
+            sub(r'\x1b\[[0-9;]*m', '', output),
+            MULTILINE,
+        )
+        if match: # pragma: no cover
+            return Path(match.group(1)).expanduser().resolve()
+
+        # Fallback
+        return None
+
+    # Tool
+    @staticmethod
+    def tool(path: str) -> Optional[Tuple[str, str]]:
+
+        # Variables
+        resolved: Path = Path(path).resolve()
+
+        # Find mise tool
+        for name, entries in Mise.tools().items(): # pragma: no cover
+            for entry in entries:
+                install: str = entry.get('install_path', '')
+                if not install:
+                    continue
+                install_path: Path = Path(install).resolve()
+                if resolved == install_path or install_path in 
resolved.parents:
+                    return name, str(entry.get('source', {}).get('path', ''))
+
+        # Fallback
+        return None
+
+    # Tools
+    @staticmethod
+    def tools() -> Dict[str, List[Dict[str, Any]]]:
+
+        # Ignore missing mise
+        if not Mise.exists():
+            return {}
+
+        # Get current mise tools
+        try:
+            tools = loads(
+                Commands.output(Mise.BINARY, [
+                    'ls',
+                    '--current',
+                    '--json',
+                ], quiet=True))
+            if isinstance(tools, dict): # pragma: no cover
+                return tools
+        except ValueError: # pragma: no cover
+            pass
+
+        # Fallback
+        return {} # pragma: no cover
+
+    # Package
+    @staticmethod
+    def package(name: str) -> str:
+
+        # Detect mise tool
+        tool: Optional[Tuple[str, str]] = Mise.tool(Version.path())
+        if tool: # pragma: no cover
+            return tool[0]
+
+        # Detect mise backend
+        backend: str = Mise.installation().split('-', maxsplit=1)[0]
+        if backend not in Mise.BACKENDS_PYTHON:
+            backend = Mise.BACKEND_DEFAULT
+
+        # Result
+        return f'{backend}:{name}'
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/src/package/updates.py 
new/gitlabci_local-13.3.0/src/package/updates.py
--- old/gitlabci_local-13.2.0/src/package/updates.py    2026-09-19 
19:10:29.339961300 +0200
+++ new/gitlabci_local-13.3.0/src/package/updates.py    2026-11-08 
01:01:01.313126600 +0100
@@ -3,13 +3,15 @@
 # Standard libraries
 from datetime import datetime
 from os import access, environ, W_OK
+from shlex import quote
 from time import localtime, strftime, time
-from typing import Optional
+from typing import Optional, Tuple
 
 # Modules libraries
 from packaging.version import Version as PackageVersion
 
 # Components
+from ..features.mise import Mise
 from ..prints.boxes import Boxes
 from ..prints.colors import Colors
 from ..system.platform import Platform
@@ -145,6 +147,42 @@
     def enabled(self) -> bool:
         return self.__enabled
 
+    # Installer
+    @staticmethod
+    def installer(name: str, available: Optional[str]) -> Tuple[str, str]:
+
+        # Detect package installer
+        package_install: str = ''
+        if Mise.managed():
+            package_install = ' '.join(['mise', 'use'] + [ # pragma: no cover
+                quote(argument) for argument in Mise.arguments(
+                    Mise.config(),
+                    f'{Mise.package(name)}@latest',
+                )
+            ])
+        elif Platform.PATH_SEPARATOR + 'pipx' + Platform.PATH_SEPARATOR in 
Version.path():
+            package_install = 'pipx upgrade' # pragma: no cover
+        else:
+            package_install = 'pip3 install -U' # pragma: no cover
+
+        # Detect package ownership
+        writable = access(__file__, W_OK)
+        if not package_install.startswith('mise') and (Platform.IS_USER_SUDO
+                                                       or not writable):
+            package_install = 'sudo ' + package_install # pragma: no cover
+
+        # Prepare package specification
+        package_specification: str = ''
+        if package_install.startswith('mise'):
+            package_specification = f'{Mise.package(name)}@latest' # pragma: 
no cover
+        elif 'pipx' in package_install:
+            package_specification = f'{name}' # pragma: no cover
+        else:
+            package_specification = f'{name}>={available}' # pragma: no cover
+
+        # Result
+        return package_install, package_specification
+
     # Message, pylint: disable=too-many-arguments,too-many-positional-arguments
     @staticmethod
     def message(
@@ -166,23 +204,10 @@
         version = Version.get()
 
         # Detect package installer
-        package_install: str = ''
-        if Platform.PATH_SEPARATOR + 'pipx' + Platform.PATH_SEPARATOR in 
Version.path():
-            package_install = 'pipx upgrade' # pragma: no cover
-        else:
-            package_install = 'pip3 install -U' # pragma: no cover
-
-        # Detect package ownership
-        writable = access(__file__, W_OK)
-        if Platform.IS_USER_SUDO or not writable:
-            package_install = 'sudo ' + package_install # pragma: no cover
-
-        # Prepare package specification
-        package_specification: str = ''
-        if 'pipx' in package_install:
-            package_specification = f'{name}' # pragma: no cover
-        else:
-            package_specification = f'{name}>={available}' # pragma: no cover
+        package_install, package_specification = Updates.installer(
+            name,
+            available,
+        )
 
         # Evaluate same version
         same = available and available == version
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/src/prints/themes.py 
new/gitlabci_local-13.3.0/src/prints/themes.py
--- old/gitlabci_local-13.2.0/src/prints/themes.py      2026-09-19 
19:10:29.339961300 +0200
+++ new/gitlabci_local-13.3.0/src/prints/themes.py      2026-11-08 
01:01:01.314126500 +0100
@@ -30,11 +30,11 @@
         # Colors enabled
         if Colors.enabled():
             Themes.__bold = 'bold'
-            Themes.__cyan = '#00FFFF bold'
+            Themes.__cyan = 'ansicyan bold'
             Themes.__disabled = 'italic'
-            Themes.__green = '#00FF00 bold noreverse'
+            Themes.__green = 'ansigreen bold noreverse'
             Themes.__selected = 'bold noreverse'
-            Themes.__yellow = '#FFFF00 bold'
+            Themes.__yellow = 'ansiyellow bold'
 
         # Colors disabled
         else:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/src/system/commands.py 
new/gitlabci_local-13.3.0/src/system/commands.py
--- old/gitlabci_local-13.2.0/src/system/commands.py    1970-01-01 
01:00:00.000000000 +0100
+++ new/gitlabci_local-13.3.0/src/system/commands.py    2026-11-08 
01:01:01.314126500 +0100
@@ -0,0 +1,140 @@
+#!/usr/bin/env python3
+
+# Standard libraries
+from pathlib import Path
+from shutil import which
+import subprocess
+from typing import List, Optional
+
+# Commands
+class Commands:
+
+    # Exec
+    @staticmethod
+    def exec(binary: str, arguments: List[str]) -> int:
+
+        # Run system command
+        try:
+            process = subprocess.run(
+                args=[binary] + arguments,
+                cwd=None,
+                check=False,
+                shell=False,
+            )
+            return process.returncode
+        except subprocess.CalledProcessError as e:
+            print(f"Error executing command: {e}")
+            return 1
+
+    # Exists
+    @staticmethod
+    def exists(binary: str) -> bool:
+
+        # Check system binary exists
+        return which(binary) is not None
+
+    # Grep
+    @staticmethod
+    def grep(file: Path, string: str) -> bool:
+
+        # Ignore missing file
+        if not file.exists():
+            return False
+
+        # Check file contains string
+        with open(
+                file,
+                encoding='utf8',
+                mode='r',
+        ) as f:
+            for line in f.readlines():
+
+                # Find string in line
+                if string in line:
+                    return True
+
+        # Fallback
+        return False # pragma: no cover
+
+    # Mise
+    @staticmethod
+    def mise(arguments: List[str], check: bool = True) -> bool:
+
+        # Run mise
+        return Commands.run(
+            'mise',
+            arguments,
+            check,
+        )
+
+    # Output
+    @staticmethod
+    def output(
+        binary: str,
+        arguments: List[str],
+        quiet: bool = False,
+        errors: bool = False,
+    ) -> str:
+
+        # Variables
+        stderr: Optional[int] = None
+
+        # Redirect errors
+        if errors:
+            stderr = subprocess.STDOUT
+        elif quiet:
+            stderr = subprocess.DEVNULL
+
+        # Get system output
+        try:
+            return subprocess.check_output(
+                args=[binary] + arguments,
+                cwd=None,
+                shell=False,
+                stderr=stderr,
+            ).strip().decode()
+        except subprocess.CalledProcessError as err:
+            return str(err.output.decode())
+        except FileNotFoundError:
+            return ''
+
+    # Pip
+    @staticmethod
+    def pip(arguments: List[str]) -> bool:
+
+        # Run with pipx
+        if Commands.exists('pipx'):
+            return Commands.run(
+                'pipx',
+                arguments,
+            )
+
+        # Run with pip # pragma: no cover
+        return Commands.run(
+            'sudo',
+            ['pip'] + arguments,
+        )
+
+    # Run
+    @staticmethod
+    def run(binary: str, arguments: List[str], check: bool = True) -> bool:
+
+        # Variables
+        stderr: Optional[int] = None if check else subprocess.DEVNULL
+        stdout: Optional[int] = None if check else subprocess.DEVNULL
+
+        # Run system command
+        try:
+            process = subprocess.run(
+                args=[binary] + arguments,
+                cwd=None,
+                check=check,
+                shell=False,
+                stderr=stderr,
+                stdout=stdout,
+            )
+            return process.returncode == 0
+        except subprocess.CalledProcessError as e:
+            if check:
+                print(f"Error executing command: {e}")
+            return False
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/gitlabci_local-13.2.0/tests/menus/test.sh 
new/gitlabci_local-13.3.0/tests/menus/test.sh
--- old/gitlabci_local-13.2.0/tests/menus/test.sh       2026-09-19 
19:10:29.343961200 +0200
+++ new/gitlabci_local-13.3.0/tests/menus/test.sh       2026-11-08 
01:01:01.320126500 +0100
@@ -16,6 +16,12 @@
 pexpect-executor --press a --enter gcil
 pexpect-executor --press a --enter -- gcil -s 'Job 1'
 pexpect-executor --down --down --space --enter -- gcil -m
+pexpect-executor --enter -- gcil -P 'Job 1'
+pexpect-executor --enter -- gcil -m --presets 'Job 1' 'Job 3'
+pexpect-executor --enter -- gcil -m -P 'Job.*[45]'
+pexpect-executor --down --space --enter -- gcil -P -R 'Job 1' 'Job 2'
+pexpect-executor --enter -- gcil -p -P menus-2
+pexpect-executor --space --enter -- gcil -P
 pexpect-executor --space --enter -- gcil -p -s
 pexpect-executor --space --enter -- gcil -p -s menus-1
 pexpect-executor -- gcil -p -s menus-0

Reply via email to