Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package python-lastversion for 
openSUSE:Factory checked in at 2026-07-06 12:34:44
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-lastversion (Old)
 and      /work/SRC/openSUSE:Factory/.python-lastversion.new.1982 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "python-lastversion"

Mon Jul  6 12:34:44 2026 rev:2 rq:1363964 version:3.6.12

Changes:
--------
--- /work/SRC/openSUSE:Factory/python-lastversion/python-lastversion.changes    
2026-03-31 16:21:23.164511636 +0200
+++ 
/work/SRC/openSUSE:Factory/.python-lastversion.new.1982/python-lastversion.changes
  2026-07-06 12:37:27.162081978 +0200
@@ -1,0 +2,20 @@
+Mon Jul  6 06:26:38 UTC 2026 - Dirk Müller <[email protected]>
+
+- update to 3.6.12:
+  * Fixed Docker image publish on release
+  * Moved the `NOSONAR` suppression for the missing `USER`
+    directive off the `FROM` line; BuildKit's frontend rejects
+    trailing inline comments on `FROM`, breaking the `Publish
+    Docker image` workflow in 3.6.11
+  * Fixed `Version` object JSON serialization error when caching
+    release data with dict output format
+  * The `version` field is now serialized to string at the cache
+    layer for both file and Redis backends
+  * This fixes the "Object of type 'Version' is not JSON
+    serializable" warning when running `lastversion --changelog
+    *.spec`
+  * Companion to the 3.6.8 datetime serialization fix; the dict-
+    format path kept `version` as a `Version` instance and
+    tripped the same cache encoder
+
+-------------------------------------------------------------------

Old:
----
  lastversion-3.6.10.tar.gz

New:
----
  lastversion-3.6.12.tar.gz

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

Other differences:
------------------
++++++ python-lastversion.spec ++++++
--- /var/tmp/diff_new_pack.AYNJ7m/_old  2026-07-06 12:37:28.478127706 +0200
+++ /var/tmp/diff_new_pack.AYNJ7m/_new  2026-07-06 12:37:28.486127984 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           python-lastversion
-Version:        3.6.10
+Version:        3.6.12
 Release:        0
 Summary:        Find the latest stable release version of an arbitrary project
 License:        BSD-2-Clause

++++++ lastversion-3.6.10.tar.gz -> lastversion-3.6.12.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/lastversion-3.6.10/.github/workflows/dockerpublish.yml 
new/lastversion-3.6.12/.github/workflows/dockerpublish.yml
--- old/lastversion-3.6.10/.github/workflows/dockerpublish.yml  2026-03-24 
18:47:40.000000000 +0100
+++ new/lastversion-3.6.12/.github/workflows/dockerpublish.yml  2026-05-15 
15:42:18.000000000 +0200
@@ -25,13 +25,13 @@
         uses: actions/checkout@v3
 
       - name: Log in to Docker Hub
-        uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
+        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2
         with:
           username: ${{ secrets.DOCKER_USERNAME }}
           password: ${{ secrets.DOCKER_TOKEN }}
 
       - name: Log in to the Container registry
-        uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9
+        uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2
         with:
           registry: ghcr.io
           username: ${{ github.actor }}
@@ -39,14 +39,14 @@
 
       - name: Extract metadata (tags, labels) for Docker
         id: meta
-        uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
+        uses: docker/metadata-action@030e881283bb7a6894de51c315a6bfe6a94e05cf
         with:
           images: |
             ${{ github.repository }}
             ghcr.io/${{ github.repository }}
 
       - name: Build and push Docker images
-        uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8
+        uses: docker/build-push-action@bcafcacb16a39f128d818304e6c9c0c18556b85f
         with:
           context: .
           push: true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/lastversion-3.6.10/.github/workflows/pythonpackage.yml 
new/lastversion-3.6.12/.github/workflows/pythonpackage.yml
--- old/lastversion-3.6.10/.github/workflows/pythonpackage.yml  2026-03-24 
18:47:40.000000000 +0100
+++ new/lastversion-3.6.12/.github/workflows/pythonpackage.yml  2026-05-15 
15:42:18.000000000 +0200
@@ -36,7 +36,7 @@
           echo "%packager Dick Solomon <[email protected]>" >> ~/.rpmmacros
           cd tests
           lastversion brotli.spec
-          lastversion nginx-module-immutable.spec
+          lastversion nginx-module-security-headers.spec
           if lastversion libmozjpeg.spec; then
             exit 1
           fi
@@ -45,15 +45,15 @@
           # Use custom token if available, otherwise fall back to GITHUB_TOKEN 
(for Dependabot PRs)
           LASTVERSION_GITHUB_API_TOKEN: ${{ 
secrets.LASTVERSION_GITHUB_API_TOKEN || github.token }}
       - name: 'Upload brotli.spec artifact'
-        uses: actions/upload-artifact@v4
+        uses: actions/upload-artifact@v7
         with:
           name: brotli.spec
           path: tests/brotli.spec
-      - name: 'Upload nginx-module-immutable.spec artifact'
-        uses: actions/upload-artifact@v4
+      - name: 'Upload nginx-module-security-headers.spec artifact'
+        uses: actions/upload-artifact@v7
         with:
-          name: nginx-module-immutable.spec
-          path: tests/nginx-module-immutable.spec
+          name: nginx-module-security-headers.spec
+          path: tests/nginx-module-security-headers.spec
   build:
 
     runs-on: ubuntu-latest
@@ -98,7 +98,7 @@
         LASTVERSION_GITHUB_API_TOKEN: ${{ secrets.LASTVERSION_GITHUB_API_TOKEN 
|| github.token }}
 
     - name: Upload pytest test results
-      uses: actions/upload-artifact@v4
+      uses: actions/upload-artifact@v7
       with:
         name: pytest-results-${{ matrix.python-version }}
         path: junit/test-results-${{ matrix.python-version }}.xml
@@ -118,7 +118,7 @@
     container: getpagespeed/rpmbuilder:centos-8
     steps:
     - name: Download brotli.spec artifact
-      uses: actions/download-artifact@v4
+      uses: actions/download-artifact@v8
       with:
         name: brotli.spec
     - name: Build updated spec
@@ -126,15 +126,15 @@
         mkdir -p /github/home/rpmbuild/{SOURCES,SPECS}/
         mv *.spec /sources/
         build
-  build-nginx-module-immutable-rpm:
+  build-nginx-module-security-headers-rpm:
     needs: py36
     runs-on: ubuntu-latest
     container: getpagespeed/rpmbuilder:centos-8
     steps:
-    - name: Download nginx-module-immutable.spec artifact
-      uses: actions/download-artifact@v4
+    - name: Download nginx-module-security-headers.spec artifact
+      uses: actions/download-artifact@v8
       with:
-        name: nginx-module-immutable.spec
+        name: nginx-module-security-headers.spec
     - name: Build updated spec
       run: |
         mkdir -p /github/home/rpmbuild/{SOURCES,SPECS}/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lastversion-3.6.10/.github/workflows/rpmbuild.yml 
new/lastversion-3.6.12/.github/workflows/rpmbuild.yml
--- old/lastversion-3.6.10/.github/workflows/rpmbuild.yml       2026-03-24 
18:47:40.000000000 +0100
+++ new/lastversion-3.6.12/.github/workflows/rpmbuild.yml       2026-05-15 
15:42:18.000000000 +0200
@@ -9,9 +9,11 @@
     runs-on: ubuntu-latest
     steps:
     - name: Install SSH key
-      uses: shimataro/ssh-key-action@v2
+      uses: shimataro/ssh-key-action@87a8f067114a8ce263df83e9ed5c849953548bc3  
# v2.8.1
       with:
         key: ${{ secrets.BUILDER_SSH_KEY }}
         known_hosts: ${{ secrets.BUILDER_SSH_KNOWN_HOSTS }}
     - name: Trigger RPM build
-      run: ${{ secrets.RPM_BUILD_CMD }}
+      run: eval "$RPM_BUILD_CMD"
+      env:
+        RPM_BUILD_CMD: ${{ secrets.RPM_BUILD_CMD }}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lastversion-3.6.10/CHANGELOG.md 
new/lastversion-3.6.12/CHANGELOG.md
--- old/lastversion-3.6.10/CHANGELOG.md 2026-03-24 18:47:40.000000000 +0100
+++ new/lastversion-3.6.12/CHANGELOG.md 2026-05-15 15:42:18.000000000 +0200
@@ -1,6 +1,18 @@
 # Changelog
 All notable changes to this project will be documented in this file.
 
+## [3.6.12] - 2026-05-15
+### Fixed
+* Fixed Docker image publish on release
+  - Moved the `NOSONAR` suppression for the missing `USER` directive off the 
`FROM` line; BuildKit's frontend rejects trailing inline comments on `FROM`, 
breaking the `Publish Docker image` workflow in 3.6.11
+
+## [3.6.11] - 2026-05-15
+### Fixed
+* Fixed `Version` object JSON serialization error when caching release data 
with dict output format
+  - The `version` field is now serialized to string at the cache layer for 
both file and Redis backends
+  - This fixes the "Object of type 'Version' is not JSON serializable" warning 
when running `lastversion --changelog *.spec`
+  - Companion to the 3.6.8 datetime serialization fix; the dict-format path 
kept `version` as a `Version` instance and tripped the same cache encoder
+
 ## [3.6.9] - 2026-03-16
 ### Fixed
 * Fixed `lastversion wordpress` returning wrong version (6.3.8 instead of 
latest)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lastversion-3.6.10/CLAUDE.md 
new/lastversion-3.6.12/CLAUDE.md
--- old/lastversion-3.6.10/CLAUDE.md    1970-01-01 01:00:00.000000000 +0100
+++ new/lastversion-3.6.12/CLAUDE.md    2026-05-15 15:42:18.000000000 +0200
@@ -0,0 +1,90 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with 
code in this repository.
+
+## Project
+
+**lastversion** — CLI tool and Python library that finds the latest stable 
release version of software projects across multiple hosting platforms (GitHub, 
GitLab, PyPI, Bitbucket, SourceForge, WordPress, etc.). Used heavily in RPM 
build automation and CI/CD pipelines.
+
+## Development Setup
+
+- **Virtualenv:** `~/.virtualenvs/lastversion/`
+- **Secrets:** Source `~/.secrets` before running tests (contains 
`GITHUB_API_TOKEN` and other API tokens)
+
+## Common Commands
+
+```bash
+# Run all tests (sources secrets, activates venv, parallel execution with 
10min timeout)
+make test
+
+# Run a single test
+source ~/.secrets && ~/.virtualenvs/lastversion/bin/python -m pytest 
tests/test_github.py::test_function_name -v
+
+# Lint
+~/.virtualenvs/lastversion/bin/python -m flake8 src tests
+~/.virtualenvs/lastversion/bin/python -m pylint src tests
+
+# Security scan
+~/.virtualenvs/lastversion/bin/python -m bandit -c pyproject.toml -r 
src/lastversion
+
+# Release (CI auto-publishes to PyPI on GitHub release creation)
+# 1. Bump __version__ in src/lastversion/__about__.py
+# 2. Commit: git commit -m "chore(release): X.Y.Z"
+# 3. Tag: git tag -s vX.Y.Z -m "vX.Y.Z"
+# 4. Push: git push origin master && git push origin vX.Y.Z
+# 5. Create release: gh release create vX.Y.Z --title "vX.Y.Z" --notes "..."
+# CI workflow pythonpublish.yml handles PyPI upload automatically.
+# Do NOT use `make publish` or manual `twine upload`.
+make publish  # backup only, prefer CI
+
+# Build single binary
+make one-file
+```
+
+## Architecture
+
+### Repository Holder Pattern
+
+Core abstraction: `BaseProjectHolder` (in 
`src/lastversion/repo_holders/base.py`) is the abstract base class for all 
hosting platform adapters. Each adapter lives in 
`src/lastversion/repo_holders/` and implements release discovery for its 
platform.
+
+`HolderFactory` (`holder_factory.py`) maintains an ordered registry of 
adapters and selects the appropriate one based on the input URL/name. Detection 
order matters — adapters are tried in registration order.
+
+### Key Modules
+
+- **`lastversion.py`** — Core logic: `latest()`, `has_update()`, 
`check_version()` entry points
+- **`cli.py`** — CLI argument parsing, all subcommands (get, download, 
extract, install, update-spec, etc.)
+- **`version.py`** — `Version` class extending `packaging.Version` with 
normalization for messy real-world version strings (dashed versions, 
Java-style, pre-release variants)
+- **`cache.py`** — Dual-level caching: HTTP-level (CacheControl/ETag) + 
release-data-level (File or Redis backend) with PID-based file locking
+- **`config.py`** — Platform-aware config singleton 
(`~/.config/lastversion/lastversion.yml` on Linux, `~/Library/Application 
Support/lastversion/lastversion.yml` on macOS)
+
+### GitHub Adapter (`repo_holders/github.py`)
+
+Most complex adapter. Uses GraphQL API with REST fallback. Handles rate 
limiting, API tokens (`GITHUB_API_TOKEN`/`GITHUB_TOKEN` env vars), commit-based 
version extraction, and release note parsing.
+
+### RPM Spec Integration
+
+`update-spec` command parses `.spec` files for `%{upstream_github}`, `%global 
lastversion_repo`, and `%global lastversion_*` directives to determine repo and 
version constraints.
+
+## Testing
+
+- **Framework:** pytest with pytest-xdist (parallel: `-n auto`)
+- **CI matrix:** Python 3.6, 3.9, 3.13
+- Tests include live API calls — require valid API tokens
+- Test files mirror the module structure: `test_github.py`, `test_gitlab.py`, 
`test_pypi.py`, etc.
+- Helper utilities in `tests/helpers.py`
+
+## Linting
+
+- **flake8:** max-line-length=120, select C,E,F,W,B,B950, ignore 
E203/E501/E704 (configured in `setup.cfg`)
+- **bandit:** security scanning configured in `pyproject.toml`
+- **pre-commit:** ruff, flake8, black, isort, bandit — all at line-length 120
+
+## Code Style
+
+- **Docstrings:** Google-style on all new/modified functions. First line: 
one-sentence imperative summary ending with a period. Include Args, Returns, 
Raises sections with type info even if type hints exist. Document coroutine 
behavior where relevant.
+- **Interpreter:** Always use `~/.virtualenvs/lastversion/bin/python` — never 
bare `python` or `pytest`.
+- **PRs:** Must include docstrings for new/modified functions. Must update 
`.cursor/rules/` if adding new modules or architectural changes.
+
+## Python Compatibility
+
+Supports Python 3.6+. Conditional dependencies exist for `cachecontrol` and 
`urllib3` based on Python version (see `setup.py`).
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lastversion-3.6.10/Dockerfile 
new/lastversion-3.6.12/Dockerfile
--- old/lastversion-3.6.10/Dockerfile   2026-03-24 18:47:40.000000000 +0100
+++ new/lastversion-3.6.12/Dockerfile   2026-05-15 15:42:18.000000000 +0200
@@ -1,4 +1,6 @@
 # Use a lightweight base image with Python and pip installed
+# Root required for GitHub Actions workspace compatibility.
+# NOSONAR
 FROM python:3.13-alpine
 
 # Using "lastversion" user as provided by some linter was a mistake and causes 
issues with GitHub actions being ran as "runner"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lastversion-3.6.10/Makefile 
new/lastversion-3.6.12/Makefile
--- old/lastversion-3.6.10/Makefile     2026-03-24 18:47:40.000000000 +0100
+++ new/lastversion-3.6.12/Makefile     2026-05-15 15:42:18.000000000 +0200
@@ -14,7 +14,7 @@
 
 publish: clean
        python setup.py sdist bdist_wheel
-       twine upload -s dist/*
+       twine upload dist/*
 
 clean:
        rm -rf *.egg-info *.egg dist build .pytest_cache
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lastversion-3.6.10/docs/requirements.txt 
new/lastversion-3.6.12/docs/requirements.txt
--- old/lastversion-3.6.10/docs/requirements.txt        2026-03-24 
18:47:40.000000000 +0100
+++ new/lastversion-3.6.12/docs/requirements.txt        2026-05-15 
15:42:18.000000000 +0200
@@ -4,3 +4,4 @@
 mkdocstrings[crystal,python]
 markdown-include
 pymdown-extensions
+markdown>=3.8.1 # not directly required, pinned by Snyk to avoid a 
vulnerability
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lastversion-3.6.10/scripts/check_docs_frontmatter.py 
new/lastversion-3.6.12/scripts/check_docs_frontmatter.py
--- old/lastversion-3.6.10/scripts/check_docs_frontmatter.py    2026-03-24 
18:47:40.000000000 +0100
+++ new/lastversion-3.6.12/scripts/check_docs_frontmatter.py    2026-05-15 
15:42:18.000000000 +0200
@@ -27,7 +27,7 @@
 REQUIRED_FIELDS = ["title", "description"]
 
 # Regex to extract YAML front-matter
-FRONTMATTER_REGEX = re.compile(r"^---\s*\n(.*?)\n---\s*\n", re.DOTALL)
+FRONTMATTER_REGEX = re.compile(r"^---[ \t]*\n(.*?)\n---[ \t]*\n", re.DOTALL)
 
 
 def is_excluded(filepath: Path) -> bool:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lastversion-3.6.10/src/lastversion/__about__.py 
new/lastversion-3.6.12/src/lastversion/__about__.py
--- old/lastversion-3.6.10/src/lastversion/__about__.py 2026-03-24 
18:47:40.000000000 +0100
+++ new/lastversion-3.6.12/src/lastversion/__about__.py 2026-05-15 
15:42:18.000000000 +0200
@@ -1,4 +1,4 @@
 """Package metadata"""
 
-__version__ = "3.6.10"
+__version__ = "3.6.12"
 __self__ = "dvershinin/lastversion"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lastversion-3.6.10/src/lastversion/cache.py 
new/lastversion-3.6.12/src/lastversion/cache.py
--- old/lastversion-3.6.10/src/lastversion/cache.py     2026-03-24 
18:47:40.000000000 +0100
+++ new/lastversion-3.6.12/src/lastversion/cache.py     2026-05-15 
15:42:18.000000000 +0200
@@ -13,10 +13,30 @@
 from typing import Any, Dict, Optional
 
 from lastversion.config import get_config
+from lastversion.version import Version
 
 log = logging.getLogger(__name__)
 
 
+def _json_default(obj: Any) -> Any:
+    """Serialize types that the stdlib JSON encoder does not handle natively.
+
+    Args:
+        obj: Object to serialize. Currently handles 
:class:`lastversion.version.Version`.
+
+    Returns:
+        A JSON-serializable representation of ``obj``.
+
+    Raises:
+        TypeError: If ``obj`` is of a type this handler does not know about.
+            Unknown types are surfaced explicitly rather than silently coerced
+            via ``str()`` — a leaked unexpected type is a bug, not a cache 
entry.
+    """
+    if isinstance(obj, Version):
+        return str(obj)
+    raise TypeError(f"Object of type {type(obj).__name__} is not JSON 
serializable")
+
+
 class CacheBackend(ABC):
     """Abstract base class for cache backends."""
 
@@ -228,7 +248,7 @@
 
         try:
             with open(cache_path, "w", encoding="utf-8") as f:
-                json.dump(data, f)
+                json.dump(data, f, default=_json_default)
             log.debug("Cached value for key: %s (TTL: %d)", key, ttl)
         except (IOError, TypeError) as e:
             log.warning("Error writing cache for key %s: %s", key, e)
@@ -493,7 +513,7 @@
             ttl = self.default_ttl
 
         try:
-            data = json.dumps(value)
+            data = json.dumps(value, default=_json_default)
             if ttl > 0:
                 self._client.setex(redis_key, ttl, data)
             else:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lastversion-3.6.10/src/lastversion/cli.py 
new/lastversion-3.6.12/src/lastversion/cli.py
--- old/lastversion-3.6.10/src/lastversion/cli.py       2026-03-24 
18:47:40.000000000 +0100
+++ new/lastversion-3.6.12/src/lastversion/cli.py       2026-05-15 
15:42:18.000000000 +0200
@@ -494,8 +494,8 @@
         args.repo = __self__
 
     # "expand" repo:1.2 as repo --branch 1.2
-    # noinspection HttpUrlsUsage
-    if ":" in args.repo and not (args.repo.startswith(("https://";, "http://";)) 
and args.repo.count(":") == 1):
+    _url_prefixes = ("https://";, "http://";)  # NOSONAR - URL parsing, not HTTP 
request
+    if ":" in args.repo and not (args.repo.startswith(_url_prefixes) and 
args.repo.count(":") == 1):
         # right split ':' once only to preserve it in protocol of URLs
         # https://github.com/repo/owner:2.1
         repo_args = args.repo.rsplit(":", 1)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lastversion-3.6.10/src/lastversion/lastversion.py 
new/lastversion-3.6.12/src/lastversion/lastversion.py
--- old/lastversion-3.6.10/src/lastversion/lastversion.py       2026-03-24 
18:47:40.000000000 +0100
+++ new/lastversion-3.6.12/src/lastversion/lastversion.py       2026-05-15 
15:42:18.000000000 +0200
@@ -99,7 +99,7 @@
             elif line.startswith("Source0:"):
                 source0 = line.split("Source0:")[1].strip()
                 # noinspection HttpUrlsUsage
-                if source0.startswith("https://";) or 
source0.startswith("http://";):
+                if source0.startswith("https://";) or 
source0.startswith("http://";):  # NOSONAR
                     spec_urls.append(source0)
             elif line.startswith("%global upstream_version "):
                 current_version = shlex.split(line)[2].strip()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/lastversion-3.6.10/src/lastversion/repo_holders/alpine.py 
new/lastversion-3.6.12/src/lastversion/repo_holders/alpine.py
--- old/lastversion-3.6.10/src/lastversion/repo_holders/alpine.py       
2026-03-24 18:47:40.000000000 +0100
+++ new/lastversion-3.6.12/src/lastversion/repo_holders/alpine.py       
2026-05-15 15:42:18.000000000 +0200
@@ -101,7 +101,7 @@
 
             # APKINDEX is a tar.gz containing an APKINDEX file
             tar_bytes = io.BytesIO(response.content)
-            with tarfile.open(fileobj=tar_bytes, mode="r:gz") as tar:
+            with tarfile.open(fileobj=tar_bytes, mode="r:gz") as tar:  # 
NOSONAR
                 for member in tar.getmembers():
                     if member.name == "APKINDEX":
                         file_obj = tar.extractfile(member)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/lastversion-3.6.10/src/lastversion/repo_holders/base.py 
new/lastversion-3.6.12/src/lastversion/repo_holders/base.py
--- old/lastversion-3.6.10/src/lastversion/repo_holders/base.py 2026-03-24 
18:47:40.000000000 +0100
+++ new/lastversion-3.6.12/src/lastversion/repo_holders/base.py 2026-05-15 
15:42:18.000000000 +0200
@@ -68,7 +68,7 @@
         return False
     try:
         # On Unix, signal 0 doesn't kill but checks if process exists
-        os.kill(pid, 0)
+        os.kill(pid, 0)  # NOSONAR
         return True
     except OSError as err:
         # ESRCH = No such process, EPERM = Permission denied (process exists)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lastversion-3.6.10/src/lastversion/utils.py 
new/lastversion-3.6.12/src/lastversion/utils.py
--- old/lastversion-3.6.10/src/lastversion/utils.py     2026-03-24 
18:47:40.000000000 +0100
+++ new/lastversion-3.6.12/src/lastversion/utils.py     2026-05-15 
15:42:18.000000000 +0200
@@ -363,7 +363,7 @@
     """
 
     archive_file: tarfile.TarFile
-    with tarfile.open(fileobj=buffer, mode="r") as archive_file:
+    with tarfile.open(fileobj=buffer, mode="r") as archive_file:  # NOSONAR
         if not check_if_tar_safe(archive_file):
             raise TarPathTraversalException("Attempted Path Traversal in Tar 
File")
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/lastversion-3.6.10/tests/nginx-module-security-headers.spec 
new/lastversion-3.6.12/tests/nginx-module-security-headers.spec
--- old/lastversion-3.6.10/tests/nginx-module-security-headers.spec     
1970-01-01 01:00:00.000000000 +0100
+++ new/lastversion-3.6.12/tests/nginx-module-security-headers.spec     
2026-05-15 15:42:18.000000000 +0200
@@ -0,0 +1,138 @@
+#############################################
+%global upstream_github GetPageSpeed
+%global upstream_name ngx_security_headers
+#############################################
+%global lastversion_tag x
+%global lastversion_dir x
+%global upstream_version x
+############################################
+%if 0%{?plesk}
+%define name_prefix sw-
+%define nginx_modules_dir %{_datadir}/nginx/modules
+%define extradist .pl%{plesk}
+%define nginx_load_module load_module nginx/modules
+%else
+%define name_prefix %{nil}
+%define nginx_modules_dir %{_libdir}/nginx/modules
+%define extradist %{nil}
+%define nginx_load_module load_module modules
+%endif
+
+#
+%define nginx_user nginx
+%define nginx_group nginx
+
+%global main_version %{nginx_version}
+%define main_release 1%{?dist}.ngx
+
+%if 0%{?rhel} || 0%{?amzn}
+%define _group System Environment/Daemons
+BuildRequires: openssl-devel
+%endif
+
+%if 0%{?suse_version} == 1315
+%define _group Productivity/Networking/Web/Servers
+BuildRequires: libopenssl-devel
+%define _debugsource_template %{nil}
+%endif
+
+%if 0%{?rhel} == 7
+%define epoch 1
+Epoch: %{epoch}
+%define dist .el7
+BuildRequires: gcc-c++
+BuildRequires: binutils
+BuildRequires: make
+%define _group System Environment/Daemons
+%endif
+
+%if 0%{?rhel} == 8
+%define epoch 1
+Epoch: %{epoch}
+%define _debugsource_template %{nil}
+%define dist .el8
+BuildRequires: gcc-c++
+BuildRequires: binutils
+BuildRequires: make
+%define _group System Environment/Daemons
+%endif
+
+%define bdir %{_builddir}/%{name}-%{main_version}
+
+Summary: NGINX module for adding security headers
+Name: %{name_prefix}nginx-module-security-headers
+Version: %{main_version}+%{upstream_version}
+Release: 2%{?dist}%{extradist}.gps
+Vendor: GetPageSpeed, Inc.
+URL: https://github.com/GetPageSpeed/ngx_security_headers
+Group: %{_group}
+
+Source0: http://nginx.org/download/nginx-%{main_version}.tar.gz
+Source100: 
%{url}/archive/%{lastversion_tag}/%{upstream_name}-%{lastversion_tag}.tar.gz
+
+License: BSD
+
+BuildRoot: %{_tmppath}/%{name}-%{main_version}-%{main_release}-root
+
+BuildRequires: zlib-devel
+BuildRequires: pcre-devel
+Requires: %{name_prefix}nginx-r%{main_version}
+
+Provides: %{name}-r%{main_version}
+
+%description
+This NGINX module adds security headers such as X-Content-Type-Options,
+X-Frame-Options, and other important security-related HTTP headers.
+
+%define WITH_CC_OPT $(echo %{optflags} $(pcre-config --cflags))
+%define WITH_LD_OPT -Wl,-z,relro -Wl,-z,now
+
+%define BASE_CONFIGURE_ARGS $(echo "--prefix=%{_sysconfdir}/nginx 
--sbin-path=%{_sbindir}/nginx --modules-path=%{_libdir}/nginx/modules 
--conf-path=%{_sysconfdir}/nginx/nginx.conf 
--error-log-path=%{_localstatedir}/log/nginx/error.log 
--http-log-path=%{_localstatedir}/log/nginx/access.log 
--pid-path=%{_localstatedir}/run/nginx.pid 
--lock-path=%{_localstatedir}/run/nginx.lock 
--http-client-body-temp-path=%{_localstatedir}/cache/nginx/client_temp 
--http-proxy-temp-path=%{_localstatedir}/cache/nginx/proxy_temp 
--http-fastcgi-temp-path=%{_localstatedir}/cache/nginx/fastcgi_temp 
--http-uwsgi-temp-path=%{_localstatedir}/cache/nginx/uwsgi_temp 
--http-scgi-temp-path=%{_localstatedir}/cache/nginx/scgi_temp 
--user=%{nginx_user} --group=%{nginx_group} --with-compat --with-file-aio 
--with-threads --with-http_addition_module --with-http_auth_request_module 
--with-http_dav_module --with-http_flv_module --with-http_gunzip_module 
--with-http_gzip_static_module --with-http_mp4_module --with-http_ran
 dom_index_module --with-http_realip_module --with-http_secure_link_module 
--with-http_slice_module --with-http_ssl_module --with-http_stub_status_module 
--with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module 
--with-stream --with-stream_realip_module --with-stream_ssl_module 
--with-stream_ssl_preread_module")
+%define MODULE_CONFIGURE_ARGS $(echo "--add-dynamic-module=%{lastversion_dir}")
+
+%prep
+%setup -qcTn %{name}-%{main_version}
+tar --strip-components=1 -zxf %{SOURCE0}
+tar xzf %SOURCE100
+
+%build
+cd %{bdir}
+./configure %{BASE_CONFIGURE_ARGS} %{MODULE_CONFIGURE_ARGS} \
+       --with-cc-opt="%{WITH_CC_OPT}" \
+       --with-ld-opt="%{WITH_LD_OPT}"
+make %{?_smp_mflags} modules
+
+%install
+cd %{bdir}
+%{__rm} -rf $RPM_BUILD_ROOT
+%{__mkdir} -p $RPM_BUILD_ROOT%{_datadir}/doc/%{name}
+%{__install} -m 644 -p %{lastversion_dir}/LICENSE \
+    $RPM_BUILD_ROOT%{_datadir}/doc/%{name}/
+%{__install} -m 644 -p %{lastversion_dir}/README.md \
+    $RPM_BUILD_ROOT%{_datadir}/doc/%{name}/
+%{__mkdir} -p $RPM_BUILD_ROOT%{nginx_modules_dir}
+for so in `find %{bdir}/objs/ -maxdepth 1 -type f -name "*.so"`; do
+%{__install} -m755 $so \
+   $RPM_BUILD_ROOT%{nginx_modules_dir}
+done
+
+%check
+%{__rm} -rf $RPM_BUILD_ROOT/usr/src
+cd %{bdir}
+grep -v 'usr/src' debugfiles.list > debugfiles.list.new && mv 
debugfiles.list.new debugfiles.list
+cat /dev/null > debugsources.list
+%if 0%{?suse_version} >= 1500
+cat /dev/null > debugsourcefiles.list
+%endif
+
+%clean
+%{__rm} -rf $RPM_BUILD_ROOT
+
+%files
+%defattr(-,root,root)
+%{nginx_modules_dir}/*
+%dir %{_datadir}/doc/%{name}
+%{_datadir}/doc/%{name}/*
+
+%changelog
+# no changelog
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lastversion-3.6.10/tests/test_cache.py 
new/lastversion-3.6.12/tests/test_cache.py
--- old/lastversion-3.6.10/tests/test_cache.py  2026-03-24 18:47:40.000000000 
+0100
+++ new/lastversion-3.6.12/tests/test_cache.py  2026-05-15 15:42:18.000000000 
+0200
@@ -326,6 +326,30 @@
         result = cache.get("test/repo")
         assert result == data
 
+    def test_release_cache_version_roundtrip(self, caplog):
+        """Regression: ReleaseDataCache must accept a dict with a Version 
field.
+
+        Mirrors the production flow at ``lastversion.lastversion:410`` where 
the
+        release dict for ``output_format == "dict"`` still holds a
+        :class:`Version` instance.
+        """
+        from lastversion.version import Version
+
+        backend = FileCacheBackend(cache_dir=self.temp_dir)
+        cache = ReleaseDataCache(backend=backend, enabled=True, ttl=3600)
+
+        data = {"version": Version("0.2.42"), "tag_name": "v0.2.42"}
+
+        with caplog.at_level("WARNING", logger="lastversion.cache"):
+            cache.set("dvershinin/gixy", data, pre_ok=False, even=False, 
formal=False)
+
+        assert "not JSON serializable" not in caplog.text
+
+        result = cache.get("dvershinin/gixy", pre_ok=False, even=False, 
formal=False)
+        assert result is not None
+        assert result["version"] == "0.2.42"
+        assert result["tag_name"] == "v0.2.42"
+
     def test_cache_key_params(self):
         """Test that different parameters create different cache keys."""
         backend = FileCacheBackend(cache_dir=self.temp_dir)
@@ -393,6 +417,52 @@
         assert result == data
         assert result["tag_date"] == "2024-01-15 10:30:00"
 
+    def test_cache_version_serialization(self, caplog):
+        """Regression: Version objects in cache data must serialize cleanly.
+
+        Reproduces the production warning seen with `lastversion --changelog 
*.spec`:
+        ``Object of type 'Version' is not JSON serializable``.
+        """
+        from lastversion.version import Version
+
+        cache = FileCacheBackend(cache_dir=self.temp_dir, default_ttl=3600)
+
+        data = {
+            "version": Version("1.2.3"),
+            "tag_name": "v1.2.3",
+        }
+
+        with caplog.at_level("WARNING", logger="lastversion.cache"):
+            cache.set("test-repo-version", data)
+
+        assert "not JSON serializable" not in caplog.text
+
+        result = cache.get("test-repo-version")
+        assert result is not None
+        # Cache returns the version as a string; callers 
(lastversion._return_from_cache)
+        # re-wrap with Version() for dict output.
+        assert result["version"] == "1.2.3"
+        assert result["tag_name"] == "v1.2.3"
+
+    def test_cache_rejects_unknown_type(self, caplog):
+        """Unknown non-serializable types must still surface as a warning.
+
+        Guards against a regression where someone swaps the explicit
+        ``_json_default`` for a blanket ``default=str``, which would silently
+        cache garbage instead of flagging the bug.
+        """
+
+        class Unserializable:
+            pass
+
+        cache = FileCacheBackend(cache_dir=self.temp_dir, default_ttl=3600)
+
+        with caplog.at_level("WARNING", logger="lastversion.cache"):
+            cache.set("test-repo-unknown", {"weird": Unserializable()})
+
+        assert "not JSON serializable" in caplog.text
+        assert cache.get("test-repo-unknown") is None
+
 
 class TestCacheFactory:
     """Tests for cache factory function."""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/lastversion-3.6.10/tests/test_github.py 
new/lastversion-3.6.12/tests/test_github.py
--- old/lastversion-3.6.10/tests/test_github.py 2026-03-24 18:47:40.000000000 
+0100
+++ new/lastversion-3.6.12/tests/test_github.py 2026-05-15 15:42:18.000000000 
+0200
@@ -143,7 +143,7 @@
     # 3.5.4 is more specific than 3.5
     assert GitHubRepoSession.is_version_more_specific(Version("3.5.4"), 
Version("3.5"))
     # 3.5.4.1 is more specific than 3.5.4
-    assert GitHubRepoSession.is_version_more_specific(Version("3.5.4.1"), 
Version("3.5.4"))
+    assert GitHubRepoSession.is_version_more_specific(Version("3.5.4.1"), 
Version("3.5.4"))  # NOSONAR
     # 3.5.4 is more specific than 3
     assert GitHubRepoSession.is_version_more_specific(Version("3.5.4"), 
Version("3"))
     # 3.5 is NOT more specific than 3.5.4

Reply via email to