Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-extra-platforms for 
openSUSE:Factory checked in at 2026-08-05 17:48:17
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-extra-platforms (Old)
 and      /work/SRC/openSUSE:Factory/.python-extra-platforms.new.16738 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-extra-platforms"

Wed Aug  5 17:48:17 2026 rev:31 rq:1369498 version:13.6.0

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/python-extra-platforms/python-extra-platforms.changes
    2026-08-03 18:02:09.353068596 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-extra-platforms.new.16738/python-extra-platforms.changes
 2026-08-05 17:49:05.047273680 +0200
@@ -1,0 +2,13 @@
+Tue Aug  4 07:44:06 UTC 2026 - Johannes Kastl 
<[email protected]>
+
+- update to 13.6.0:
+  * Add AlmaLinux platform detection: ALMALINUX / is_almalinux()
+    (via ID=almalinux in os-release).
+  * Add EndeavourOS platform detection: ENDEAVOUROS /
+    is_endeavouros() (via ID=endeavouros in os-release). Closes
+    #658.
+  * Unrecognized shell, terminal, CI and agent warnings now report
+    the environment variables (SHELL, TERM, CI, LLM, ...) each is
+    detected from, not just the architecture/platform primitives.
+
+-------------------------------------------------------------------

Old:
----
  extra_platforms-13.5.3.tar.gz

New:
----
  extra_platforms-13.6.0.tar.gz

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

Other differences:
------------------
++++++ python-extra-platforms.spec ++++++
--- /var/tmp/diff_new_pack.nPIlbf/_old  2026-08-05 17:49:05.887303109 +0200
+++ /var/tmp/diff_new_pack.nPIlbf/_new  2026-08-05 17:49:05.891303249 +0200
@@ -23,7 +23,7 @@
 %bcond_with libalternatives
 %endif
 Name:           python-extra-platforms
-Version:        13.5.3
+Version:        13.6.0
 Release:        0
 Summary:        Detect platforms and group them by family
 License:        Apache-2.0

++++++ extra_platforms-13.5.3.tar.gz -> extra_platforms-13.6.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/extra-platforms-13.5.3/changelog.md 
new/extra-platforms-13.6.0/changelog.md
--- old/extra-platforms-13.5.3/changelog.md     2026-08-02 09:29:03.000000000 
+0200
+++ new/extra-platforms-13.6.0/changelog.md     2026-08-03 09:24:41.000000000 
+0200
@@ -1,7 +1,16 @@
 # Changelog
 
+## [`13.6.0` 
(2026-08-03)](https://github.com/kdeldycke/extra-platforms/compare/v13.5.3...v13.6.0)
+
+- Add AlmaLinux platform detection: `ALMALINUX` / `is_almalinux()` (via 
`ID=almalinux` in `os-release`).
+- Add EndeavourOS platform detection: `ENDEAVOUROS` / `is_endeavouros()` (via 
`ID=endeavouros` in `os-release`). Closes 
[#658](https://github.com/kdeldycke/extra-platforms/issues/658).
+- Unrecognized shell, terminal, CI and agent warnings now report the 
environment variables (`SHELL`, `TERM`, `CI`, `LLM`, ...) each is detected 
from, not just the architecture/platform primitives.
+
 ## [`13.5.3` 
(2026-08-02)](https://github.com/kdeldycke/extra-platforms/compare/v13.5.2...v13.5.3)
 
+> [!NOTE]
+> `13.5.3` is available on [🐍 
PyPI](https://pypi.org/project/extra-platforms/13.5.3/) and [πŸ™ 
GitHub](https://github.com/kdeldycke/extra-platforms/releases/tag/v13.5.3).
+
 - Sync CI tooling, workflow pins and dependency floors with the latest 
repomatic release; no functional changes to the package.
 
 ## [`13.5.2` 
(2026-08-01)](https://github.com/kdeldycke/extra-platforms/compare/v13.5.1...v13.5.2)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/extra-platforms-13.5.3/citation.cff 
new/extra-platforms-13.6.0/citation.cff
--- old/extra-platforms-13.5.3/citation.cff     2026-08-02 09:29:03.000000000 
+0200
+++ new/extra-platforms-13.6.0/citation.cff     2026-08-03 09:24:41.000000000 
+0200
@@ -8,6 +8,6 @@
     email: [email protected]
     orcid: "https://orcid.org/0000-0001-9748-9014";
 doi: 10.5281/zenodo.13341712
-version: 13.5.3
-date-released: 2026-08-02
+version: 13.6.0
+date-released: 2026-08-03
 url: "https://github.com/kdeldycke/extra-platforms";
\ No newline at end of file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/extra-platforms-13.5.3/docs/cli.md 
new/extra-platforms-13.6.0/docs/cli.md
--- old/extra-platforms-13.5.3/docs/cli.md      2026-08-02 09:29:03.000000000 
+0200
+++ new/extra-platforms-13.6.0/docs/cli.md      2026-08-03 09:24:41.000000000 
+0200
@@ -57,7 +57,7 @@
 
 ```{code-block} shell-session
 $ extra-platforms
-extra-platforms 13.5.2
+extra-platforms 13.5.3
 
 ── Architecture ── πŸ“± ARM64 (AArch64) ──[AARCH64]────────────
             id: aarch64
@@ -87,7 +87,7 @@
 
 ```{code-block} json
 {
-  "version": "13.5.2",
+  "version": "13.5.3",
   "architecture": {
     "id": "aarch64",
     "name": "ARM64 (AArch64)",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/extra-platforms-13.5.3/docs/detection.md 
new/extra-platforms-13.6.0/docs/detection.md
--- old/extra-platforms-13.5.3/docs/detection.md        2026-08-02 
09:29:03.000000000 +0200
+++ new/extra-platforms-13.6.0/docs/detection.md        2026-08-03 
09:24:41.000000000 +0200
@@ -27,6 +27,7 @@
 | {func}`~is_aarch64`              |  πŸ“±  | {data}`~AARCH64`              |
 | {func}`~is_aix`                  |  ➿  | {data}`~AIX`                  |
 | {func}`~is_alacritty`            |  πŸ”³  | {data}`~ALACRITTY`            |
+| {func}`~is_almalinux`            |     | {data}`~ALMALINUX`            |
 | {func}`~is_alpine`               |  πŸ”οΈ  | {data}`~ALPINE`               |
 | {func}`~is_altlinux`             |  Ξ”   | {data}`~ALTLINUX`             |
 | {func}`~is_amzn`                 |  β€»   | {data}`~AMZN`                 |
@@ -80,6 +81,7 @@
 | {func}`~is_dash`                 |  πŸ’¨  | {data}`~DASH`                 |
 | {func}`~is_debian`               |  πŸŒ€  | {data}`~DEBIAN`               |
 | {func}`~is_dragonfly_bsd`        |  πŸͺ°  | {data}`~DRAGONFLY_BSD`        |
+| {func}`~is_endeavouros`          |  πŸš€  | {data}`~ENDEAVOUROS`          |
 | {func}`~is_exherbo`              |  🐽  | {data}`~EXHERBO`              |
 | {func}`~is_fedora`               |  🎩  | {data}`~FEDORA`               |
 | {func}`~is_fish`                 |  🐟  | {data}`~FISH`                 |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/extra-platforms-13.5.3/docs/install.md 
new/extra-platforms-13.6.0/docs/install.md
--- old/extra-platforms-13.5.3/docs/install.md  2026-08-02 09:29:03.000000000 
+0200
+++ new/extra-platforms-13.6.0/docs/install.md  2026-08-03 09:24:41.000000000 
+0200
@@ -31,7 +31,7 @@
 
 ````{tab-item} Specific version
 ```shell-session
-$ uvx [email protected]
+$ uvx [email protected]
 ```
 ````
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/extra-platforms-13.5.3/docs/platforms.md 
new/extra-platforms-13.6.0/docs/platforms.md
--- old/extra-platforms-13.5.3/docs/platforms.md        2026-08-02 
09:29:03.000000000 +0200
+++ new/extra-platforms-13.6.0/docs/platforms.md        2026-08-03 
09:24:41.000000000 +0200
@@ -70,6 +70,7 @@
 | Icon | Symbol                 | Name                           | Detection 
function        |
 | :--: | :--------------------- | :----------------------------- | 
:------------------------ |
 |  ➿  | {data}`~AIX`           | IBM AIX                        | 
{func}`~is_aix`           |
+|     | {data}`~ALMALINUX`     | AlmaLinux                      | 
{func}`~is_almalinux`     |
 |  πŸ”οΈ  | {data}`~ALPINE`        | Alpine Linux                   | 
{func}`~is_alpine`        |
 |  Ξ”   | {data}`~ALTLINUX`      | ALT Linux                      | 
{func}`~is_altlinux`      |
 |  β€»   | {data}`~AMZN`          | Amazon Linux                   | 
{func}`~is_amzn`          |
@@ -84,6 +85,7 @@
 |  ΟΎ   | {data}`~CYGWIN`        | Cygwin                         | 
{func}`~is_cygwin`        |
 |  πŸŒ€  | {data}`~DEBIAN`        | Debian                         | 
{func}`~is_debian`        |
 |  πŸͺ°  | {data}`~DRAGONFLY_BSD` | DragonFly BSD                  | 
{func}`~is_dragonfly_bsd` |
+|  πŸš€  | {data}`~ENDEAVOUROS`   | EndeavourOS                    | 
{func}`~is_endeavouros`   |
 |  🐽  | {data}`~EXHERBO`       | Exherbo Linux                  | 
{func}`~is_exherbo`       |
 |  🎩  | {data}`~FEDORA`        | Fedora                         | 
{func}`~is_fedora`        |
 |  😈  | {data}`~FREEBSD`       | FreeBSD                        | 
{func}`~is_freebsd`       |
@@ -202,13 +204,14 @@
 ---
 sankey-beta
 
-ALL_PLATFORMS,LINUX,45
+ALL_PLATFORMS,LINUX,47
 ALL_PLATFORMS,BSD,7
 ALL_PLATFORMS,SYSTEM_V,3
 ALL_PLATFORMS,UNIX_LAYERS,2
 ALL_PLATFORMS,OTHER_POSIX,2
 ALL_PLATFORMS,LINUX_LAYERS,2
 ALL_PLATFORMS,ALL_WINDOWS,1
+LINUX,ALMALINUX,1
 LINUX,ALPINE,1
 LINUX,ALTLINUX,1
 LINUX,AMZN,1
@@ -221,6 +224,7 @@
 LINUX,CLEARLINUX,1
 LINUX,CLOUDLINUX,1
 LINUX,DEBIAN,1
+LINUX,ENDEAVOUROS,1
 LINUX,EXHERBO,1
 LINUX,FEDORA,1
 LINUX,GENERIC_LINUX,1
@@ -307,6 +311,7 @@
             (⊞ WSL1)
             (⊞ WSL2)
         )🐧 LINUX(
+            ( ALMALINUX)
             (πŸ”οΈ ALPINE)
             (Ξ” ALTLINUX)
             (β€» AMZN)
@@ -319,6 +324,7 @@
             (✳️ CLEARLINUX)
             (꩜ CLOUDLINUX)
             (πŸŒ€ DEBIAN)
+            (πŸš€ ENDEAVOUROS)
             (🐽 EXHERBO)
             (🎩 FEDORA)
             (πŸ₯š GENERIC_LINUX)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/extra-platforms-13.5.3/docs/pytest.md 
new/extra-platforms-13.6.0/docs/pytest.md
--- old/extra-platforms-13.5.3/docs/pytest.md   2026-08-02 09:29:03.000000000 
+0200
+++ new/extra-platforms-13.6.0/docs/pytest.md   2026-08-03 09:24:41.000000000 
+0200
@@ -115,6 +115,7 @@
 | {deco}`~pytest.skip_all_terminals`        | 
{deco}`~pytest.unless_any_terminal`         |  πŸ’»  | {data}`~ALL_TERMINALS`      
  |
 | {deco}`~pytest.skip_all_traits`           | {deco}`~pytest.unless_any_trait` 
           |  ⁕   | {data}`~ALL_TRAITS`           |
 | {deco}`~pytest.skip_all_windows`          | 
{deco}`~pytest.unless_any_windows`          |  πŸͺŸ  | {data}`~ALL_WINDOWS`        
  |
+| {deco}`~pytest.skip_almalinux`            | {deco}`~pytest.unless_almalinux` 
           |     | {data}`~ALMALINUX`            |
 | {deco}`~pytest.skip_alpine`               | {deco}`~pytest.unless_alpine`    
           |  πŸ”οΈ  | {data}`~ALPINE`               |
 | {deco}`~pytest.skip_altlinux`             | {deco}`~pytest.unless_altlinux`  
           |  Ξ”   | {data}`~ALTLINUX`             |
 | {deco}`~pytest.skip_amzn`                 | {deco}`~pytest.unless_amzn`      
           |  β€»   | {data}`~AMZN`                 |
@@ -157,6 +158,7 @@
 | {deco}`~pytest.skip_dash`                 | {deco}`~pytest.unless_dash`      
           |  πŸ’¨  | {data}`~DASH`                 |
 | {deco}`~pytest.skip_debian`               | {deco}`~pytest.unless_debian`    
           |  πŸŒ€  | {data}`~DEBIAN`               |
 | {deco}`~pytest.skip_dragonfly_bsd`        | 
{deco}`~pytest.unless_dragonfly_bsd`        |  πŸͺ°  | {data}`~DRAGONFLY_BSD`      
  |
+| {deco}`~pytest.skip_endeavouros`          | 
{deco}`~pytest.unless_endeavouros`          |  πŸš€  | {data}`~ENDEAVOUROS`        
  |
 | {deco}`~pytest.skip_exherbo`              | {deco}`~pytest.unless_exherbo`   
           |  🐽  | {data}`~EXHERBO`              |
 | {deco}`~pytest.skip_fedora`               | {deco}`~pytest.unless_fedora`    
           |  🎩  | {data}`~FEDORA`               |
 | {deco}`~pytest.skip_fish`                 | {deco}`~pytest.unless_fish`      
           |  🐟  | {data}`~FISH`                 |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/extra-platforms-13.5.3/docs/trait.md 
new/extra-platforms-13.6.0/docs/trait.md
--- old/extra-platforms-13.5.3/docs/trait.md    2026-08-02 09:29:03.000000000 
+0200
+++ new/extra-platforms-13.6.0/docs/trait.md    2026-08-03 09:24:41.000000000 
+0200
@@ -145,6 +145,7 @@
 |  πŸ“±  | {data}`~AARCH64`              | ARM64 (AArch64)                | 
{func}`~is_aarch64`              | Architecture |
 |  ➿  | {data}`~AIX`                  | IBM AIX                        | 
{func}`~is_aix`                  | Platform     |
 |  πŸ”³  | {data}`~ALACRITTY`            | Alacritty                      | 
{func}`~is_alacritty`            | Terminal     |
+|     | {data}`~ALMALINUX`            | AlmaLinux                      | 
{func}`~is_almalinux`            | Platform     |
 |  πŸ”οΈ  | {data}`~ALPINE`               | Alpine Linux                   | 
{func}`~is_alpine`               | Platform     |
 |  Ξ”   | {data}`~ALTLINUX`             | ALT Linux                      | 
{func}`~is_altlinux`             | Platform     |
 |  β€»   | {data}`~AMZN`                 | Amazon Linux                   | 
{func}`~is_amzn`                 | Platform     |
@@ -180,6 +181,7 @@
 |  πŸ’¨  | {data}`~DASH`                 | Dash                           | 
{func}`~is_dash`                 | Shell        |
 |  πŸŒ€  | {data}`~DEBIAN`               | Debian                         | 
{func}`~is_debian`               | Platform     |
 |  πŸͺ°  | {data}`~DRAGONFLY_BSD`        | DragonFly BSD                  | 
{func}`~is_dragonfly_bsd`        | Platform     |
+|  πŸš€  | {data}`~ENDEAVOUROS`          | EndeavourOS                    | 
{func}`~is_endeavouros`          | Platform     |
 |  🐽  | {data}`~EXHERBO`              | Exherbo Linux                  | 
{func}`~is_exherbo`              | Platform     |
 |  🎩  | {data}`~FEDORA`               | Fedora                         | 
{func}`~is_fedora`               | Platform     |
 |  🐟  | {data}`~FISH`                 | Fish                           | 
{func}`~is_fish`                 | Shell        |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/extra-platforms-13.5.3/extra_platforms/__init__.py 
new/extra-platforms-13.6.0/extra_platforms/__init__.py
--- old/extra-platforms-13.5.3/extra_platforms/__init__.py      2026-08-02 
09:29:03.000000000 +0200
+++ new/extra-platforms-13.6.0/extra_platforms/__init__.py      2026-08-03 
09:24:41.000000000 +0200
@@ -82,6 +82,7 @@
     is_aarch64,
     is_aix,
     is_alacritty,
+    is_almalinux,
     is_alpine,
     is_altlinux,
     is_amzn,
@@ -117,6 +118,7 @@
     is_dash,
     is_debian,
     is_dragonfly_bsd,
+    is_endeavouros,
     is_exherbo,
     is_fedora,
     is_fish,
@@ -284,6 +286,7 @@
 )
 from .platform_data import (
     AIX,
+    ALMALINUX,
     ALPINE,
     ALTLINUX,
     AMZN,
@@ -298,6 +301,7 @@
     CYGWIN,
     DEBIAN,
     DRAGONFLY_BSD,
+    ENDEAVOUROS,
     EXHERBO,
     FEDORA,
     FREEBSD,
@@ -410,7 +414,7 @@
 """
 
 
-__version__ = "13.5.3"
+__version__ = "13.6.0"
 
 
 def _initialize_group_detection_functions() -> None:
@@ -561,6 +565,7 @@
     "ALL_TRAITS",
     "ALL_TRAIT_IDS",
     "ALL_WINDOWS",
+    "ALMALINUX",
     "ALPINE",
     "ALTLINUX",
     "AMZN",
@@ -605,6 +610,7 @@
     "DASH",
     "DEBIAN",
     "DRAGONFLY_BSD",
+    "ENDEAVOUROS",
     "EXHERBO",
     "FEDORA",
     "FISH",
@@ -752,6 +758,7 @@
     "is_aarch64",
     "is_aix",
     "is_alacritty",
+    "is_almalinux",
     "is_alpine",
     "is_altlinux",
     "is_amzn",
@@ -805,6 +812,7 @@
     "is_dash",
     "is_debian",
     "is_dragonfly_bsd",
+    "is_endeavouros",
     "is_exherbo",
     "is_fedora",
     "is_fish",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/extra-platforms-13.5.3/extra_platforms/detection.py 
new/extra-platforms-13.6.0/extra_platforms/detection.py
--- old/extra-platforms-13.5.3/extra_platforms/detection.py     2026-08-02 
09:29:03.000000000 +0200
+++ new/extra-platforms-13.6.0/extra_platforms/detection.py     2026-08-03 
09:24:41.000000000 +0200
@@ -105,17 +105,82 @@
 """
 
 
-def _unrecognized_message(report: bool = True) -> str:
+_CATEGORY_ENV_SIGNALS: dict[str, tuple[str, ...]] = {
+    "shell": (
+        "SHELL",
+        "BASH_VERSION",
+        "FISH_VERSION",
+        "KSH_VERSION",
+        "NU_VERSION",
+        "XONSH_VERSION",
+        "ZSH_VERSION",
+        "PSModulePath",
+    ),
+    "terminal": (
+        "TERM",
+        "TERM_PROGRAM",
+        "TERM_PROGRAM_VERSION",
+        "COLORTERM",
+        "TERMINAL_NAME",
+    ),
+    "CI": ("CI",),
+    "agent": ("LLM",),
+}
+"""Environment variables to surface in an unrecognized-trait report, per 
category.
+
+Architectures and platforms are diagnosed from the ``sys.platform`` /
+``platform.*`` / ``os_release_id()`` primitives the report already prints. 
Shells,
+terminals, CI systems and agents are instead identified by environment 
variables,
+which those primitives never expose. This maps each such category (keyed by the
+label {func}`_single_match` passes) to the variables its ``is_*()`` heuristics 
read.
+
+The first entry is the category's primary signal, always shown (even when 
unset, so
+a reader sees why an unrecognized trait logged at ``INFO`` rather than 
``WARNING``).
+The rest are shown only when set: an unrecognized trait leaves every 
presence-only
+marker unset, so the value-bearing variables (a ``SHELL`` path, a 
``TERM_PROGRAM``
+name) are what carry the smoking gun.
+
+.. caution::
+    Only gating and value-bearing variables belong here, never a blanket dump 
of
+    :data:`os.environ`: the environment routinely holds credentials and tokens.
+
+.. note::
+    The ``shell`` entry must list every
+    {attr}`~extra_platforms.Shell.version_env_var` in the registry;
+    ``test_shell_env_signals_cover_version_vars`` enforces it.
+"""
+
+
+def _env_signal_lines(var_names: tuple[str, ...]) -> str:
+    """Render environment variables as aligned ``name: value`` report lines.
+
+    The first variable is always shown (rendering ``<not set>`` when absent); 
the
+    rest only when present. See {data}`_CATEGORY_ENV_SIGNALS`.
+    """
+    primary, *rest = var_names
+    shown = [primary, *(name for name in rest if name in environ)]
+    width = max(len(name) for name in shown) + 1  # +1 for the trailing colon.
+    rows: list[str] = []
+    for name in shown:
+        value = repr(environ[name]) if name in environ else "<not set>"
+        rows.append(f"  {name + ':':<{width}} {value}")
+    return "\n".join(rows)
+
+
+def _unrecognized_message(report: bool = True, category: str | None = None) -> 
str:
     """Generate a message for unrecognized environments.
 
-    ```{important}
-    This message must contain all the primitives used in the `detection` 
module so
-    maintainers can debug heuristics from user reports.
-    ```
+    The report opens with the ``sys.platform`` / ``platform.*`` / 
``os_release_id()``
+    primitives that identify architectures and platforms. When ``category`` 
names an
+    environment-variable-driven trait type, the variables that category's 
heuristics
+    read are appended, since the primitives above never expose them (see
+    {data}`_CATEGORY_ENV_SIGNALS`).
 
     :param report: If `True`, append a request to report the issue on GitHub.
         Set to `False` for environments where the trait is legitimately absent
-        (e.g., no terminal in CI, no CI locally).
+        (like no terminal in CI, or no CI locally).
+    :param category: Trait category label (as passed to {func}`_single_match`) 
whose
+        environment-variable signals to append, or `None` for the primitives 
alone.
     """
     msg = (
         "Environment:\n"
@@ -128,6 +193,9 @@
         f"  platform.architecture: {platform.architecture()!r}\n"
         f"  os_release_id:         {os_release_id()!r}"
     )
+    env_signals = _CATEGORY_ENV_SIGNALS.get(category) if category else None
+    if env_signals:
+        msg += f"\n{_env_signal_lines(env_signals)}"
     if report:
         msg += (
             "\n\nPlease report this at "
@@ -142,17 +210,23 @@
     *,
     strict: bool,
     expected: bool = True,
+    category: str | None = None,
 ) -> None:
     """Log or raise on unrecognized trait detection.
 
-    :param trait_name: Human-readable name of the trait type (e.g., 
`"architecture"`).
+    :param trait_name: Human-readable name of the trait type (like 
`"architecture"`).
     :param strict: If `True`, raise {exc}`SystemError` instead of logging.
     :param expected: If `True`, the trait is always expected to be detected
         (architecture, platform, shell), so an unrecognized result logs a 
`WARNING`
         and asks users to report the issue. If `False` (terminal, CI), the 
trait may
         legitimately be absent, so only `INFO` is logged without a report 
request.
+    :param category: Trait category label whose environment-variable signals to
+        include in the message (see {data}`_CATEGORY_ENV_SIGNALS`).
     """
-    msg = f"Unrecognized {trait_name}: 
{_unrecognized_message(report=expected)}"
+    msg = (
+        f"Unrecognized {trait_name}: "
+        f"{_unrecognized_message(report=expected, category=category)}"
+    )
     if strict:
         raise SystemError(msg)
     # Defer logging import to keep cold module load fast: stdlib logging pulls
@@ -413,6 +487,12 @@
 
 
 @cache
+def is_almalinux() -> bool:
+    """Return {data}`True` if current platform is 
{data}`~extra_platforms.ALMALINUX`."""
+    return os_release_id() == "almalinux"
+
+
+@cache
 def is_alpine() -> bool:
     """Return {data}`True` if current platform is 
{data}`~extra_platforms.ALPINE`."""
     return os_release_id() == "alpine"
@@ -542,6 +622,14 @@
 
 
 @cache
+def is_endeavouros() -> bool:
+    """Return {data}`True` if current platform is
+    {data}`~extra_platforms.ENDEAVOUROS`.
+    """
+    return os_release_id() == "endeavouros"
+
+
+@cache
 def is_exherbo() -> bool:
     """Return {data}`True` if current platform is 
{data}`~extra_platforms.EXHERBO`."""
     return os_release_id() == "exherbo"
@@ -2125,10 +2213,10 @@
     if matching:
         raise RuntimeError(
             f"Multiple {plural or label + 's'} match: {matching!r}. "
-            f"{_unrecognized_message()}"
+            f"{_unrecognized_message(category=label)}"
         )
 
-    _report_unrecognized(label, strict=strict, expected=expected)
+    _report_unrecognized(label, strict=strict, expected=expected, 
category=label)
     return unknown
 
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/extra-platforms-13.5.3/extra_platforms/group_data.py 
new/extra-platforms-13.6.0/extra_platforms/group_data.py
--- old/extra-platforms-13.5.3/extra_platforms/group_data.py    2026-08-02 
09:29:03.000000000 +0200
+++ new/extra-platforms-13.6.0/extra_platforms/group_data.py    2026-08-03 
09:24:41.000000000 +0200
@@ -71,6 +71,7 @@
 from .group import Group
 from .platform_data import (
     AIX,
+    ALMALINUX,
     ALPINE,
     ALTLINUX,
     AMZN,
@@ -85,6 +86,7 @@
     CYGWIN,
     DEBIAN,
     DRAGONFLY_BSD,
+    ENDEAVOUROS,
     EXHERBO,
     FEDORA,
     FREEBSD,
@@ -399,6 +401,7 @@
     "βš™οΈ",
     (
         AIX,
+        ALMALINUX,
         ALPINE,
         ALTLINUX,
         AMZN,
@@ -413,6 +416,7 @@
         CYGWIN,
         DEBIAN,
         DRAGONFLY_BSD,
+        ENDEAVOUROS,
         EXHERBO,
         FEDORA,
         FREEBSD,
@@ -537,6 +541,7 @@
     "Linux distributions",
     "🐧",
     (
+        ALMALINUX,
         ALPINE,
         ALTLINUX,
         AMZN,
@@ -549,6 +554,7 @@
         CLEARLINUX,
         CLOUDLINUX,
         DEBIAN,
+        ENDEAVOUROS,
         EXHERBO,
         FEDORA,
         GENERIC_LINUX,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/extra-platforms-13.5.3/extra_platforms/platform_data.py 
new/extra-platforms-13.6.0/extra_platforms/platform_data.py
--- old/extra-platforms-13.5.3/extra_platforms/platform_data.py 2026-08-02 
09:29:03.000000000 +0200
+++ new/extra-platforms-13.6.0/extra_platforms/platform_data.py 2026-08-03 
09:24:41.000000000 +0200
@@ -44,6 +44,21 @@
 
 AIX = Platform("aix", "IBM AIX", "➿", "https://ibm.com/products/aix/";)
 
+ALMALINUX = Platform("almalinux", "AlmaLinux", "", "https://almalinux.org";)
+"""
+```{note}
+AlmaLinux has no representative Unicode emoji, so like
+{data}`~extra_platforms.NOBARA` it uses a [NerdFont](https://www.nerdfonts.com)
+icon instead:
+[`` (i.e. `nf-linux-almalinux` /
+`f31d`)](https://www.nerdfonts.com/cheat-sheet?glyphSearch=nf-linux-almalinux).
+
+The side-effect of using a NerdFont character is it will only display correctly
+when using a supported font. Otherwise, it will appear as an unknown or 
invisible
+character depending on the fonts.
+```
+"""
+
 ALPINE = Platform("alpine", "Alpine Linux", "πŸ”οΈ", "https://alpinelinux.org";)
 
 ALTLINUX = Platform("altlinux", "ALT Linux", "Ξ”", "https://altlinux.org";)
@@ -74,6 +89,8 @@
     "dragonfly_bsd", "DragonFly BSD", "πŸͺ°", "https://www.dragonflybsd.org";
 )
 
+ENDEAVOUROS = Platform("endeavouros", "EndeavourOS", "πŸš€", 
"https://endeavouros.com";)
+
 EXHERBO = Platform("exherbo", "Exherbo Linux", "🐽", "https://exherbolinux.org";)
 
 FEDORA = Platform("fedora", "Fedora", "🎩", "https://fedoraproject.org";)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/extra-platforms-13.5.3/extra_platforms/pytest.py 
new/extra-platforms-13.6.0/extra_platforms/pytest.py
--- old/extra-platforms-13.5.3/extra_platforms/pytest.py        2026-08-02 
09:29:03.000000000 +0200
+++ new/extra-platforms-13.6.0/extra_platforms/pytest.py        2026-08-03 
09:24:41.000000000 +0200
@@ -223,6 +223,7 @@
     skip_all_terminals: MarkDecorator
     skip_all_traits: MarkDecorator
     skip_all_windows: MarkDecorator
+    skip_almalinux: MarkDecorator
     skip_alpine: MarkDecorator
     skip_altlinux: MarkDecorator
     skip_amzn: MarkDecorator
@@ -265,6 +266,7 @@
     skip_dash: MarkDecorator
     skip_debian: MarkDecorator
     skip_dragonfly_bsd: MarkDecorator
+    skip_endeavouros: MarkDecorator
     skip_exherbo: MarkDecorator
     skip_fedora: MarkDecorator
     skip_fish: MarkDecorator
@@ -393,6 +395,7 @@
     unless_aarch64: MarkDecorator
     unless_aix: MarkDecorator
     unless_alacritty: MarkDecorator
+    unless_almalinux: MarkDecorator
     unless_alpine: MarkDecorator
     unless_altlinux: MarkDecorator
     unless_amzn: MarkDecorator
@@ -446,6 +449,7 @@
     unless_dash: MarkDecorator
     unless_debian: MarkDecorator
     unless_dragonfly_bsd: MarkDecorator
+    unless_endeavouros: MarkDecorator
     unless_exherbo: MarkDecorator
     unless_fedora: MarkDecorator
     unless_fish: MarkDecorator
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/extra-platforms-13.5.3/pyproject.toml 
new/extra-platforms-13.6.0/pyproject.toml
--- old/extra-platforms-13.5.3/pyproject.toml   2026-08-02 09:29:03.000000000 
+0200
+++ new/extra-platforms-13.6.0/pyproject.toml   2026-08-03 09:24:41.000000000 
+0200
@@ -5,12 +5,13 @@
 [project]
 # Docs: https://packaging.python.org/en/latest/guides/writing-pyproject-toml/
 name = "extra-platforms"
-version = "13.5.3"
+version = "13.6.0"
 description = "πŸ”Ž Detect architectures, platforms, shells, terminals, CI 
systems and agents, grouped by family"
 readme = "readme.md"
 keywords = [
   "Agent detection",
   "Alacritty",
+  "AlmaLinux",
   "Almquist Shell",
   "Alpine Linux",
   "ALT Linux",
@@ -53,6 +54,7 @@
   "Debian",
   "Distro detection",
   "DragonFly BSD",
+  "EndeavourOS",
   "Exherbo Linux",
   "Fedora",
   "Fish",
@@ -255,7 +257,7 @@
 ]
 urls.Changelog = 
"https://github.com/kdeldycke/extra-platforms/blob/main/changelog.md";
 urls.Documentation = "https://kdeldycke.github.io/extra-platforms";
-urls.Download = 
"https://github.com/kdeldycke/extra-platforms/releases/tag/v13.5.3";
+urls.Download = 
"https://github.com/kdeldycke/extra-platforms/releases/tag/v13.6.0";
 urls.Funding = "https://github.com/sponsors/kdeldycke";
 urls.Homepage = "https://github.com/kdeldycke/extra-platforms";
 urls.Issues = "https://github.com/kdeldycke/extra-platforms/issues";
@@ -452,7 +454,7 @@
 report.precision = 2
 
 [tool.bumpversion]
-current_version = "13.5.3"
+current_version = "13.6.0"
 # Parse versions with an optional .devN suffix (PEP 440).
 parse = 
"(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)(\\.dev(?P<dev>\\d+))?"
 serialize = [
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/extra-platforms-13.5.3/readme.md 
new/extra-platforms-13.6.0/readme.md
--- old/extra-platforms-13.5.3/readme.md        2026-08-02 09:29:03.000000000 
+0200
+++ new/extra-platforms-13.6.0/readme.md        2026-08-03 09:24:41.000000000 
+0200
@@ -29,7 +29,7 @@
 
 ```shell-session
 $ uvx extra-platforms
-extra-platforms 13.5.2
+extra-platforms 13.5.3
 
 ── Architecture ── πŸ“± ARM64 (AArch64) ──[AARCH64]────────────
             id: aarch64
@@ -72,7 +72,7 @@
 ```pycon
 >>> import extra_platforms
 >>> extra_platforms.__version__
-'13.5.2'
+'13.5.3'
 ```
 
 ## Examples
@@ -145,6 +145,7 @@
 >>> from extra_platforms import LINUX
 >>> LINUX.members
 mappingproxy({
+    'almalinux': Platform(id='almalinux', name='AlmaLinux'),
     'alpine': Platform(id='alpine', name='Alpine Linux'),
     'altlinux': Platform(id='altlinux', name='ALT Linux'),
     'amzn': Platform(id='amzn', name='Amazon Linux'),
@@ -157,6 +158,7 @@
     'clearlinux': Platform(id='clearlinux', name='Clear Linux OS'),
     'cloudlinux': Platform(id='cloudlinux', name='CloudLinux OS'),
     'debian': Platform(id='debian', name='Debian'),
+    'endeavouros': Platform(id='endeavouros', name='EndeavourOS'),
     'exherbo': Platform(id='exherbo', name='Exherbo Linux'),
     'fedora': Platform(id='fedora', name='Fedora'),
     'generic_linux': Platform(id='generic_linux', name='Generic Linux'),
@@ -192,8 +194,9 @@
     'xenserver': Platform(id='xenserver', name='XenServer'),
 })
 >>> LINUX.member_ids
-frozenset({'rocky', 'kvmibm', 'pidora', 'pikaos', 'sourcemage', 'sles', 
'tuxedo', 'rhel', 'opensuse', 'android', 'ultramarine', 'fedora', 'ubuntu', 
'nixos', 'generic_linux', 'oracle', 'buildroot', 'gentoo', 'void', 'chromeos', 
'alpine', 'slackware', 'arch', 'centos', 'amzn', 'xenserver', 'nobara', 
'cachyos', 'cloudlinux', 'ibm_powerkvm', 'kali', 'exherbo', 'mandriva', 
'openwrt', 'raspbian', 'debian', 'altlinux', 'guix', 'slitaz', 'linuxmint', 
'mageia', 'scientific', 'manjaro', 'parallels', 'clearlinux'})
+frozenset({'rocky', 'kvmibm', 'pidora', 'pikaos', 'sourcemage', 'sles', 
'tuxedo', 'rhel', 'opensuse', 'android', 'ultramarine', 'fedora', 'ubuntu', 
'nixos', 'generic_linux', 'oracle', 'buildroot', 'gentoo', 'void', 'chromeos', 
'almalinux', 'alpine', 'slackware', 'arch', 'centos', 'amzn', 'xenserver', 
'nobara', 'cachyos', 'cloudlinux', 'ibm_powerkvm', 'kali', 'exherbo', 
'mandriva', 'openwrt', 'raspbian', 'debian', 'endeavouros', 'altlinux', 'guix', 
'slitaz', 'linuxmint', 'mageia', 'scientific', 'manjaro', 'parallels', 
'clearlinux'})
 >>> print("\n".join([p.name for p in LINUX]))
+AlmaLinux
 Alpine Linux
 ALT Linux
 Amazon Linux
@@ -206,6 +209,7 @@
 Clear Linux OS
 CloudLinux OS
 Debian
+EndeavourOS
 Exherbo Linux
 Fedora
 Generic Linux
@@ -341,6 +345,7 @@
             (⊞ WSL1)
             (⊞ WSL2)
         )🐧 LINUX(
+            ( ALMALINUX)
             (πŸ”οΈ ALPINE)
             (Ξ” ALTLINUX)
             (β€» AMZN)
@@ -353,6 +358,7 @@
             (✳️ CLEARLINUX)
             (꩜ CLOUDLINUX)
             (πŸŒ€ DEBIAN)
+            (πŸš€ ENDEAVOUROS)
             (🐽 EXHERBO)
             (🎩 FEDORA)
             (πŸ₯š GENERIC_LINUX)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/extra-platforms-13.5.3/tests/test_platform_data.py 
new/extra-platforms-13.6.0/tests/test_platform_data.py
--- old/extra-platforms-13.5.3/tests/test_platform_data.py      2026-08-02 
09:29:03.000000000 +0200
+++ new/extra-platforms-13.6.0/tests/test_platform_data.py      2026-08-03 
09:24:41.000000000 +0200
@@ -15,6 +15,9 @@
 
 from __future__ import annotations
 
+import ast
+from pathlib import Path
+
 from extra_platforms import (
     ALL_PLATFORM_GROUPS,
     ALL_PLATFORMS,
@@ -108,3 +111,76 @@
     assert BSD_WITHOUT_MACOS.issubset(BSD)
     assert UNIX.issuperset(BSD_WITHOUT_MACOS)
     assert BSD.issuperset(BSD_WITHOUT_MACOS)
+
+
+def _in_private_use_area(char: str) -> bool:
+    """Return ``True`` for a Unicode Private Use Area codepoint.
+
+    NerdFont glyphs live in the PUA. Ordinary emoji sit above ``U+E000`` as 
well, so
+    a bare ``>= U+E000`` test misclassifies them; match the three PUA ranges 
instead.
+    """
+    codepoint = ord(char)
+    return (
+        0xE000 <= codepoint <= 0xF8FF
+        or 0xF0000 <= codepoint <= 0xFFFFD
+        or 0x100000 <= codepoint <= 0x10FFFD
+    )
+
+
+def test_nerdfont_icons_documented_in_source():
+    """Every platform with a NerdFont icon embeds that glyph in its docstring.
+
+    NerdFont glyphs live in the Unicode Private Use Area and render as an
+    invisible or placeholder character without a patched font, so
+    each such platform carries an attribute docstring warning about the font
+    requirement, whose icon link repeats the glyph. Because the glyph is 
invisible
+    in an editor, it is easy to drop from that link by accident, which then 
renders
+    as broken plaintext instead of a hyperlink. Lock the invariant by parsing 
the
+    source: every NerdFont-icon platform must have an attribute docstring, and 
that
+    docstring must contain the glyph itself.
+    """
+    source = (
+        Path(__file__).parent.parent / "extra_platforms" / "platform_data.py"
+    ).read_text(encoding="utf-8")
+    body = ast.parse(source).body
+
+    checked = []
+    for definition, following in zip(body, body[1:]):
+        # Match a module-level "NAME = Platform(id, name, icon, url)" 
assignment.
+        if not (
+            isinstance(definition, ast.Assign)
+            and len(definition.targets) == 1
+            and isinstance(definition.targets[0], ast.Name)
+            and isinstance(definition.value, ast.Call)
+            and isinstance(definition.value.func, ast.Name)
+            and definition.value.func.id == "Platform"
+            and len(definition.value.args) >= 3
+            and isinstance(definition.value.args[2], ast.Constant)
+            and isinstance(definition.value.args[2].value, str)
+        ):
+            continue
+
+        icon = definition.value.args[2].value
+        # Only NerdFont icons (Private Use Area codepoints) are concerned.
+        if not any(_in_private_use_area(char) for char in icon):
+            continue
+
+        name = definition.targets[0].id
+        checked.append(name)
+
+        # The assignment must be immediately followed by an attribute 
docstring.
+        assert (
+            isinstance(following, ast.Expr)
+            and isinstance(following.value, ast.Constant)
+            and isinstance(following.value.value, str)
+        ), (
+            f"Platform {name} uses NerdFont icon {icon!r} but has no attribute 
"
+            f"docstring documenting the font requirement."
+        )
+        # The docstring must embed the glyph, or the icon link renders broken.
+        assert icon in following.value.value, (
+            f"Platform {name}'s NerdFont icon {icon!r} is missing from its "
+            f"attribute docstring; the icon link renders as broken plaintext."
+        )
+
+    assert checked, "expected NerdFont-icon platforms such as ALMALINUX and 
NOBARA"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/extra-platforms-13.5.3/tests/test_root.py 
new/extra-platforms-13.6.0/tests/test_root.py
--- old/extra-platforms-13.5.3/tests/test_root.py       2026-08-02 
09:29:03.000000000 +0200
+++ new/extra-platforms-13.6.0/tests/test_root.py       2026-08-03 
09:24:41.000000000 +0200
@@ -85,7 +85,7 @@
     trait as trait_module,
 )
 from extra_platforms._deprecated import DEPRECATED_ALIASES, REMOVAL_VERSION
-from extra_platforms.detection import _unrecognized_message
+from extra_platforms.detection import _CATEGORY_ENV_SIGNALS, 
_unrecognized_message
 from extra_platforms.pytest import skip_hermetic_build
 
 from .conftest import github_runner_os
@@ -623,6 +623,54 @@
         assert "os_release_id:" in message
 
 
[email protected](
+    ("category", "gating_var"),
+    (
+        ("terminal", "TERM"),
+        ("CI", "CI"),
+        ("agent", "LLM"),
+    ),
+)
+def test_category_env_signals_gating_var(category, gating_var):
+    """The primary signal of each env-gated category is its ``expected=`` gate 
var."""
+    assert _CATEGORY_ENV_SIGNALS[category][0] == gating_var
+
+
+def test_shell_env_signals_cover_version_vars():
+    """Every shell startup variable in the registry is reported on a shell 
miss."""
+    version_vars = {
+        env_var
+        for shell in ALL_SHELLS
+        if (env_var := getattr(shell, "version_env_var", None))
+    }
+    assert version_vars <= set(_CATEGORY_ENV_SIGNALS["shell"])
+
+
[email protected]("category", ("shell", "terminal", "CI", "agent"))
+def test_unrecognized_message_reports_set_category_vars(monkeypatch, category):
+    """A set category variable, and its value, appear in that category's 
report."""
+    primary_var = _CATEGORY_ENV_SIGNALS[category][0]
+    monkeypatch.setenv(primary_var, "sentinel-value")
+    message = _unrecognized_message(category=category)
+    assert f"{primary_var}:" in message
+    assert "sentinel-value" in message
+
+
+def test_unrecognized_message_hides_unset_secondary_vars(monkeypatch):
+    """Unset non-primary category variables are omitted, keeping the report 
concise."""
+    monkeypatch.delenv("TERM_PROGRAM", raising=False)
+    message = _unrecognized_message(category="terminal")
+    assert "TERM_PROGRAM:" not in message
+
+
+def test_unrecognized_message_without_category_omits_env_block(monkeypatch):
+    """Architecture and platform reports carry only the system primitives."""
+    monkeypatch.setenv("TERM", "xterm-256color")
+    message = _unrecognized_message(category="platform")
+    assert "TERM:" not in message
+    assert "os_release_id:" in message
+
+
 @pytest.mark.skipif(
     sys.version_info < (3, 14), reason="platform.invalidate_caches added in 
Python 3.14"
 )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/extra-platforms-13.5.3/tests/test_sphinx_crossrefs.py 
new/extra-platforms-13.6.0/tests/test_sphinx_crossrefs.py
--- old/extra-platforms-13.5.3/tests/test_sphinx_crossrefs.py   2026-08-02 
09:29:03.000000000 +0200
+++ new/extra-platforms-13.6.0/tests/test_sphinx_crossrefs.py   2026-08-03 
09:24:41.000000000 +0200
@@ -865,7 +865,8 @@
 def _get_all_public_symbols() -> Iterator[str]:
     """Collect all public symbols from extra_platforms and pytest modules.
 
-    Returns a list of (symbol_name, is_pytest_decorator) tuples for 
parametrization.
+    Yields each symbol name as a string; ``pytest`` submodule symbols carry a
+    ``pytest.`` prefix.
     """
 
     # Add all symbols exposed in extra_platforms root.
@@ -878,7 +879,7 @@
             yield f"pytest.{symbol_name}"
 
 
[email protected]("symbol_name", _get_all_public_symbols())
[email protected]("symbol_name", list(_get_all_public_symbols()))
 def test_get_expected_page_for_symbol_handles_public_api(symbol_name):
     """Test that get_expected_page_for_symbol correctly handles each public 
symbol.
 
@@ -1025,12 +1026,18 @@
         "extend",
         "insert",
     }
+    # Standard-library members (like ``os.environ``) resolve via intersphinx 
to the
+    # Python docs, so they have no anchor on a local extra_platforms page to 
check.
+    stdlib_symbols = {
+        "os.environ",
+    }
     if (
         symbol in python_builtins
         or symbol.split(".")[-1] in python_builtins
         or (role == "meth" and symbol in python_builtin_methods)
+        or symbol in stdlib_symbols
     ):
-        pytest.skip("Skipping builtin symbol")
+        pytest.skip("Skipping builtin or standard-library symbol")
 
     if (
         symbol.startswith((
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/extra-platforms-13.5.3/uv.lock 
new/extra-platforms-13.6.0/uv.lock
--- old/extra-platforms-13.5.3/uv.lock  2026-08-02 09:29:03.000000000 +0200
+++ new/extra-platforms-13.6.0/uv.lock  2026-08-03 09:24:41.000000000 +0200
@@ -373,7 +373,7 @@
 
 [[package]]
 name = "extra-platforms"
-version = "13.5.3.dev0"
+version = "13.6.0.dev0"
 source = { editable = "." }
 
 [package.optional-dependencies]

Reply via email to