Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-pypiserver for openSUSE:Factory checked in at 2023-09-12 21:03:05 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-pypiserver (Old) and /work/SRC/openSUSE:Factory/.python-pypiserver.new.1766 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-pypiserver" Tue Sep 12 21:03:05 2023 rev:8 rq:1110445 version:1.5.2 Changes: -------- --- /work/SRC/openSUSE:Factory/python-pypiserver/python-pypiserver.changes 2023-05-10 16:18:37.947013025 +0200 +++ /work/SRC/openSUSE:Factory/.python-pypiserver.new.1766/python-pypiserver.changes 2023-09-12 21:03:51.866275218 +0200 @@ -1,0 +2,23 @@ +Mon Sep 11 14:23:34 UTC 2023 - Torsten Gruner <simmpho...@opensuse.org> + +- update to 1.5.2 + * Upgrade GitHub Actions #447 + * Support current versions of CPython #453 + * fix: force setuptools update + no duplicate runs in GH Actions #445 + * from importlib import reload for Python 3 #448 + * fix: correct 1.5.1 tag date in CHANGES #457 + * feat: 𩺠allow customized health check endpoint #442 + * fix: explicit optional types in config.py #472 + * chore: disable tests on Python3.6 #471 + * Upgrade to psf/black stable style 2023 #474 + * Update README to reflect run/update commands #451 + * chore: add help output for run and update to README #478 + * Update README.rst and config.py #470 + * chore: update docs folder #479 + * Bump waitress from 1.4.4 to 2.1.2 in /docker #454 + * Health endpoint usage is missing. #481 + * Feat/dependabot #493 + * fix: Add missing pip dependency #500 + * chore(auto-release-candidate-07-30-2023) #505 + +------------------------------------------------------------------- Old: ---- pypiserver-1.5.1.tar.gz New: ---- _multibuild pypiserver-1.5.2.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-pypiserver.spec ++++++ --- /var/tmp/diff_new_pack.F4xqD0/_old 2023-09-12 21:03:53.402330007 +0200 +++ /var/tmp/diff_new_pack.F4xqD0/_new 2023-09-12 21:03:53.406330150 +0200 @@ -15,25 +15,26 @@ # Please submit bugfixes or comments via https://bugs.opensuse.org/ # - +%global flavor @BUILD_FLAVOR@%{nil} +%if "%{flavor}" == "test" +%define psuffix -test +%bcond_without test +%else +%define psuffix %{nil} +%bcond_with test +%endif %{?sle15_python_module_pythons} -%bcond_without python2 -Name: python-pypiserver -Version: 1.5.1 +Name: python-pypiserver%{psuffix} +Version: 1.5.2 Release: 0 Summary: Minimal PyPI server for uploading & downloading packages with pip/easy_install License: MIT URL: https://github.com/pypiserver Source: https://github.com/pypiserver/pypiserver/archive/v%{version}.tar.gz#/pypiserver-%{version}.tar.gz -BuildRequires: %{python_module WebTest} -BuildRequires: %{python_module passlib >= 1.6} BuildRequires: %{python_module pip >= 7} -BuildRequires: %{python_module pytest >= 3.5} BuildRequires: %{python_module setuptools-git >= 0.3} BuildRequires: %{python_module setuptools_scm >= 1.15.0} BuildRequires: %{python_module setuptools} -BuildRequires: %{python_module tox} -BuildRequires: %{python_module twine} BuildRequires: %{python_module wheel >= 0.25.0} BuildRequires: fdupes BuildRequires: python-rpm-macros @@ -42,9 +43,16 @@ Requires(post): update-alternatives Requires(postun):update-alternatives BuildArch: noarch -%if %{with python2} -BuildRequires: python-mock +# SECTION test requirements +%if %{with test} +BuildRequires: %{python_module passlib >= 1.6} +BuildRequires: %{python_module pypiserver = %{version}} +BuildRequires: %{python_module pytest >= 3.5} +BuildRequires: %{python_module tox} +BuildRequires: %{python_module twine} +BuildRequires: %{python_module WebTest} %endif +# /SECTION %python_subpackages %description @@ -57,18 +65,22 @@ rm -f pytest.ini %build -%python_build +%pyproject_wheel %install -%python_install +%if !%{with test} +%pyproject_install %python_clone -a %{buildroot}%{_bindir}/pypi-server %python_expand %fdupes %{buildroot}%{$python_sitelib} +%endif +%if %{with test} %check # test_hash_algos: # ERROR: No matching distribution found for a==1.0 (from centodeps) # (see centodeps-setup.py) %pytest tests -k "not (test_pipInstall_openOk or test_pipInstall_authedOk or test_hash_algos or test_pip_install_open_succeeds or test_pip_install_authed_succeeds)" +%endif %post %python_install_alternative pypi-server @@ -76,10 +88,12 @@ %postun %python_uninstall_alternative pypi-server +%if !%{with test} %files %{python_files} %doc README.rst %license LICENSE.txt %{python_sitelib}/pypiserver %{python_sitelib}/pypiserver-%{version}*-info %python_alternative %{_bindir}/pypi-server +%endif ++++++ _multibuild ++++++ <multibuild> <package>test</package> </multibuild> ++++++ pypiserver-1.5.1.tar.gz -> pypiserver-1.5.2.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pypiserver-1.5.1/.github/dependabot.yml new/pypiserver-1.5.2/.github/dependabot.yml --- old/pypiserver-1.5.1/.github/dependabot.yml 1970-01-01 01:00:00.000000000 +0100 +++ new/pypiserver-1.5.2/.github/dependabot.yml 2023-07-30 23:18:51.000000000 +0200 @@ -0,0 +1,13 @@ +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates + +version: 2 +updates: + - package-ecosystem: "pip" + directory: "/docker" + schedule: + interval: "monthly" + - package-ecosystem: "pip" + directory: "/requirements" + schedule: + interval: "monthly" + diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pypiserver-1.5.1/.github/workflows/ci.yml new/pypiserver-1.5.2/.github/workflows/ci.yml --- old/pypiserver-1.5.1/.github/workflows/ci.yml 2022-10-18 16:06:16.000000000 +0200 +++ new/pypiserver-1.5.2/.github/workflows/ci.yml 2023-07-30 23:18:51.000000000 +0200 @@ -5,67 +5,45 @@ on: # This will run when any branch or tag is pushed push: - # standalone is an old branch containing a fully functional pypiserver - # executable, from back in the day before docker & a better pip. - branches-ignore: - - standalone + branches: + - "master" tags: - "v**" - # Allowing to run on fork pull requests + # Allowing to run on fork and other pull requests pull_request: jobs: - test-cpython: + test-python: runs-on: ubuntu-latest strategy: + fail-fast: false matrix: - # Commented out as tests with tox on python3.9 fail with updated dependencies. - # See https://github.com/pypiserver/pypiserver/issues/406. - # uncomment when the issue #406 is resolved. - # python-version: ["3.6", "3.7", "3.8", "3.9"] - python-version: ["3.6", "3.7", "3.8"] + python-version: ["3.7", "3.8", "3.9", "3.10", "pypy3.9", "3.11"] # "3.12-dev" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies - run: pip install tox - - name: Run tests - # Create a tox env specification by stripping the dot out of the version - # specification and appending it to "py" run: | - tox -e "py$(echo ${{ matrix.python-version }} | tr -d .)" - - test-pypy: - # Run a a separate job so we don't need to mess with conditionally - # splitting the python version from the build matrix. Also the pypy - # tests take freaking forever. - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: pypy3 - - name: Install dependencies - run: pip install tox + pip install --upgrade setuptools + pip install tox==3.27.* - name: Run tests - run: tox -e pypy3 + run: tox -e py check: - # These checks only need to be done once, not for every python version we s - # upport + # These checks only need to be done once, not for every python version we + # support runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: - # Pretty much any python version will do - python-version: "3.9" + # Use the current version of Python + python-version: "3.x" - name: Install dependencies run: | pip install -r "requirements/dev.pip" @@ -92,12 +70,12 @@ test-docker: runs-on: "ubuntu-latest" steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: - # Pretty much any python version will do - python-version: "3.9" + # Use the current version of Python + python-version: "3.x" - name: Install test dependencies run: pip install -r "requirements/test.pip" - name: Install package @@ -110,8 +88,7 @@ needs: - "check" - "test-docker" - - "test-cpython" - - "test-pypy" + - "test-python" steps: - name: "Everything is good!" run: "echo true" @@ -128,10 +105,10 @@ if: startsWith(github.event.ref, 'refs/tags/v') steps: - uses: actions/checkout@master - - name: Set up Python 3.8 - uses: actions/setup-python@v1 + - name: Set up Python + uses: actions/setup-python@v4 with: - python-version: 3.8 + python-version: 3.x - name: Build distribution _wheel_. run: | @@ -151,11 +128,11 @@ needs: - "tests" steps: - - uses: "actions/checkout@v2" + - uses: "actions/checkout@v3" - - uses: "actions/setup-python@v2" + - uses: "actions/setup-python@v4" with: - python-version: "3.9" + python-version: "3.x" # This script prints a JSON array of needed docker tags, depending on the # ref. That array is then used to construct the matrix of the @@ -188,10 +165,10 @@ matrix: tag: "${{ fromJson(needs.docker-determine-tags.outputs.tags) }}" steps: - - uses: "actions/checkout@v2" + - uses: "actions/checkout@v3" - name: "Cache Docker layers" - uses: "actions/cache@v2" + uses: "actions/cache@v3" with: path: "/tmp/.buildx-cache" key: "${{ runner.os }}-buildx-${{ github.sha }}" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pypiserver-1.5.1/CHANGES.rst new/pypiserver-1.5.2/CHANGES.rst --- old/pypiserver-1.5.1/CHANGES.rst 2022-10-18 16:06:16.000000000 +0200 +++ new/pypiserver-1.5.2/CHANGES.rst 2023-07-30 23:18:51.000000000 +0200 @@ -4,7 +4,28 @@ 2.0.0 (tbd) ----------- -1.5.1rc10-01-2022 (__rc__) +1.5.2 (2023-07-30) +-------------------------- + +- 3f520cd FIX: Add missing pip dependency (#500) +- 85e065e MAINT: Feat/dependabot (#493) +- 73dbe15 FIX: Health endpoint usage is missing. (#481) +- e0c9723 MAINT: Bump waitress from 1.4.4 to 2.1.2 in /docker (#454) +- a95f456 MAINT: update docs folder (#479) +- 8cc8e80 MAINT: Update README.rst and config.py (#470) +- 754b0f4 MAINT: add help output for `run` and `update` to README (#478) +- 5fd6400 MAINT: Update README to reflect run/update commands (#451) +- abc4bfb MAINT: Upgrade to psf/black stable style 2023 (#474) +- 383c936 MAINT: disable tests on Python3.6 (#471) +- d716d0f FIX: explicit optional types in `config.py` (#472) +- ae3dcf2 ENH: :stethoscope: allow customized health check endpoint (#442) +- 2f3b997 FIX: correct 1.5.1 tag date in CHANGES (#457) +- 4a0c6fb MAINT: from importlib import reload for Python 3 (#448) +- 0ba44b5 FIX: force setuptools update + no duplicate runs in GH Actions (#445) +- 530852b MAINT: Support current versions of CPython (#453) +- 6ea316c MAINT: Upgrade GitHub Actions (#447) + +1.5.1 (2022-10-18) -------------------------- - 61e4487 ENH: add extremely basic /health endpoint (#396) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pypiserver-1.5.1/README.rst new/pypiserver-1.5.2/README.rst --- old/pypiserver-1.5.1/README.rst 2022-10-18 16:06:16.000000000 +0200 +++ new/pypiserver-1.5.2/README.rst 2023-07-30 23:18:51.000000000 +0200 @@ -1,6 +1,6 @@ .. -*- mode: rst; coding: utf-8 -*- -.. image:: pypiserver_logo.png +.. image:: docs/__resources__/pypiserver_logo.png :width: 300 px :align: center @@ -9,8 +9,8 @@ ============================================================================== |pypi-ver| |test-status| |dependencies| |python-ver| |proj-license| -:Version: 1.5.1 -:Date: 2022-10-18 +:Version: 1.5.2 +:Date: 2023-07-30 :Source: https://github.com/pypiserver/pypiserver :PyPI: https://pypi.org/project/pypiserver/ :Tests: https://github.com/pypiserver/pypiserver/actions @@ -73,7 +73,7 @@ 2. Copy some packages into your ``~/packages`` folder and then get your ``pypiserver`` up and running:: - pypi-server -p 8080 ~/packages & # Will listen to all IPs. + pypi-server run -p 8080 ~/packages & # Will listen to all IPs. 3. From the client computer, type this:: @@ -92,124 +92,145 @@ 4. Enter ``pypi-server -h`` in the cmd-line to print a detailed usage message:: - pypi-server [OPTIONS] [PACKAGES_DIRECTORY...] - start PyPI compatible package server serving packages from - PACKAGES_DIRECTORY. If PACKAGES_DIRECTORY is not given on the - command line, it uses the default ~/packages. pypiserver scans this - directory recursively for packages. It skips packages and - directories starting with a dot. Multiple package directories can be - specified. - - pypi-server understands the following options: - - -p, --port PORT - Listen on port PORT (default: 8080). - - -i, --interface INTERFACE - Listen on interface INTERFACE (default: 0.0.0.0, any interface). - - -a, --authenticate (update|download|list), ... - Comma-separated list of (case-insensitive) actions to authenticate. - Requires to have set the password (-P option). - To password-protect package downloads (in addition to uploads) while - leaving listings public, use: - -P foo/htpasswd.txt -a update,download - To allow unauthorized access, use: - -P . -a . - Note that when uploads are not protected, the `register` command - is not necessary, but `~/.pypirc` still need username and password fields, - even if bogus. - By default, only 'update' is password-protected. - - -P, --passwords PASSWORD_FILE - Use apache htpasswd file PASSWORD_FILE to set usernames & passwords when - authenticating certain actions (see -a option). - To allow unauthorized access, use: - -P . -a . - - --disable-fallback - Disable redirect to real PyPI index for packages not found in the - local index. - - --fallback-url FALLBACK_URL - For packages not found in the local index, this URL will be used to - redirect to (default: https://pypi.org/simple/). - - --server METHOD - Use METHOD to run the server. Valid values include paste, - cherrypy, twisted, gunicorn, gevent, wsgiref, auto. The - default is to use "auto" which chooses one of paste, cherrypy, - twisted or wsgiref. - - -r, --root PACKAGES_DIRECTORY - [deprecated] Serve packages from PACKAGES_DIRECTORY. - - -o, --overwrite - Allow overwriting existing package files. - - --hash-algo ALGO - Any `hashlib` available algo used as fragments on package links. - Set one of (0, no, off, false) to disabled it (default: md5). - - --welcome HTML_FILE - Uses the ASCII contents of HTML_FILE as welcome message response. - - -v - Enable verbose logging; repeat for more verbosity. - - --log-conf <FILE> - Read logging configuration from FILE. - By default, configuration is read from `log.conf` if found in server's dir. - - --log-file <FILE> - Write logging info into this FILE. - - --log-frmt <FILE> - The logging format-string (see `logging.LogRecord` class from standard python library). - [Default: %(asctime)s|%(name)s|%(levelname)s|%(thread)d|%(message)s] - - --log-req-frmt FORMAT - A format-string selecting Http-Request properties to log; set to '%s' to see them all. - [Default: %(bottle.request)s] - - --log-res-frmt FORMAT - A format-string selecting Http-Response properties to log; set to '%s' to see them all. - [Default: %(status)s] - - --log-err-frmt FORMAT - A format-string selecting Http-Error properties to log; set to '%s' to see them all. - [Default: %(body)s: %(exception)s \n%(traceback)s] - - --cache-control AGE - Add "Cache-Control: max-age=AGE, public" header to package downloads. - Pip 6+ needs this for caching. - - pypi-server -h, --help - Show this help message. - - pypi-server --version - Show pypi-server's version. - - pypi-server -U [OPTIONS] [PACKAGES_DIRECTORY...] - Update packages in PACKAGES_DIRECTORY. This command searches - pypi.org for updates and shows a pip command line which - updates the package. - - The following additional options can be specified with -U: - - -x - Execute the pip commands instead of only showing them. - - -d DOWNLOAD_DIRECTORY - Download package updates to this directory. The default is to use - the directory which contains the latest version of the package to - be updated. - - -u - Allow updating to unstable version (alpha, beta, rc, dev versions). - - Visit https://github.com/pypiserver/pypiserver for more information. + start PyPI compatible package server serving packages from PACKAGES_DIRECTORY. If PACKAGES_DIRECTORY is not given on the command line, it uses the default ~/packages. pypiserver scans this directory recursively for packages. It skips packages and directories starting with a dot. Multiple package directories may be specified. + positional arguments: + {run,update} + run Run pypiserver, serving packages from + PACKAGES_DIRECTORY + update Handle updates of packages managed by pypiserver. By + default, a pip command to update the packages is + printed to stdout for introspection or pipelining. See + the `-x` option for updating packages directly. + + options: + -h, --help show this help message and exit + -v, --verbose Enable verbose logging; repeat for more verbosity. + --log-file FILE Write logging info into this FILE, as well as to + stdout or stderr, if configured. + --log-stream STREAM Log messages to the specified STREAM. Valid values are + stdout, stderr, and none + --log-frmt FORMAT The logging format-string. (see `logging.LogRecord` + class from standard python library) + --hash-algo HASH_ALGO + Any `hashlib` available algorithm to use for + generating fragments on package links. Can be disabled + with one of (0, no, off, false). + --backend {auto,simple-dir,cached-dir} + A backend implementation. Keep the default 'auto' to + automatically determine whether to activate caching or + not + --version show program's version number and exit + + Visit https://github.com/pypiserver/pypiserver for more information + +More details about ``pypi-server run`` +-------------------------------------- + +Enter ``pypi-server run -h`` in the cmd-line to print a detailed usage +message *about starting the server*:: + + usage: pypi-server run [-h] [-v] [--log-file FILE] [--log-stream STREAM] [--log-frmt FORMAT] [--hash-algo HASH_ALGO] + [--backend {auto,simple-dir,cached-dir}] [--version] [-p PORT] [-i HOST] [-a AUTHENTICATE] [-P PASSWORD_FILE] [--disable-fallback] + [--fallback-url FALLBACK_URL] [--health-endpoint HEALTH_ENDPOINT] [--server METHOD] [-o] [--welcome HTML_FILE] + [--cache-control AGE] [--log-req-frmt FORMAT] [--log-res-frmt FORMAT] [--log-err-frmt FORMAT] + [package_directory [package_directory ...]] + + positional arguments: + package_directory The directory from which to serve packages. + + optional arguments: + -h, --help show this help message and exit + -v, --verbose Enable verbose logging; repeat for more verbosity. + --log-file FILE Write logging info into this FILE, as well as to stdout or stderr, if configured. + --log-stream STREAM Log messages to the specified STREAM. Valid values are stdout, stderr, and none + --log-frmt FORMAT The logging format-string. (see `logging.LogRecord` class from standard python library) + --hash-algo HASH_ALGO + Any `hashlib` available algorithm to use for generating fragments on package links. Can be disabled with one of (0, no, off, + false). + --backend {auto,simple-dir,cached-dir} + A backend implementation. Keep the default 'auto' to automatically determine whether to activate caching or not + --version show program's version number and exit + -p PORT, --port PORT Listen on port PORT (default: 8080) + -i HOST, -H HOST, --interface HOST, --host HOST + Listen on interface INTERFACE (default: 0.0.0.0) + -a AUTHENTICATE, --authenticate AUTHENTICATE + Comma-separated list of (case-insensitive) actions to authenticate (options: download, list, update; default: update). + + Any actions not specified are not authenticated, so to authenticate downloads and updates, but allow unauthenticated viewing of + the package list, you would use: + + pypi-server -a 'download, update' -P ./my_passwords.htaccess + + To disable authentication, use: + + pypi-server -a . -P . + + See the `-P` option for configuring users and passwords. + + Note that when uploads are not protected, the `register` command is not necessary, but `~/.pypirc` still needs username and + password fields, even if bogus. + -P PASSWORD_FILE, --passwords PASSWORD_FILE + Use an apache htpasswd file PASSWORD_FILE to set usernames and passwords for authentication. + + To allow unauthorized access, use: + + pypi-server -a . -P . + + --disable-fallback Disable the default redirect to PyPI for packages not found in the local index. + --fallback-url FALLBACK_URL + Redirect to FALLBACK_URL for packages not found in the local index. + --health-endpoint HEALTH_ENDPOINT + Configure a custom liveness endpoint. + It always returns 200 Ok if the service is up. + Otherwise, it means that the service is not responsive. + --server METHOD Use METHOD to run the server. Valid values include paste, cherrypy, twisted, gunicorn, gevent, wsgiref, and auto. The default is to + use "auto", which chooses one of paste, cherrypy, twisted, or wsgiref. + -o, --overwrite Allow overwriting existing package files during upload. + --welcome HTML_FILE Use the contents of HTML_FILE as a custom welcome message on the home page. + --cache-control AGE Add "Cache-Control: max-age=AGE" header to package downloads. Pip 6+ requires this for caching. AGE is specified in seconds. + --log-req-frmt FORMAT + A format-string selecting Http-Request properties to log; set to '%s' to see them all. + --log-res-frmt FORMAT + A format-string selecting Http-Response properties to log; set to '%s' to see them all. + --log-err-frmt FORMAT + A format-string selecting Http-Error properties to log; set to '%s' to see them all. + +More details about ``pypi-server update`` +----------------------------------------- + +Enter ``pypi-server update -h`` in the cmd-line to print a detailed usage +message *about updating the managed packages*:: + + usage: pypi-server update [-h] [-v] [--log-file FILE] [--log-stream STREAM] [--log-frmt FORMAT] [--hash-algo HASH_ALGO] + [--backend {auto,simple-dir,cached-dir}] [--version] [-x] [-d DOWNLOAD_DIRECTORY] [-u] [--blacklist-file IGNORELIST_FILE] + [package_directory [package_directory ...]] + + positional arguments: + package_directory The directory from which to serve packages. + + optional arguments: + -h, --help show this help message and exit + -v, --verbose Enable verbose logging; repeat for more verbosity. + --log-file FILE Write logging info into this FILE, as well as to stdout or stderr, if configured. + --log-stream STREAM Log messages to the specified STREAM. Valid values are stdout, stderr, and none + --log-frmt FORMAT The logging format-string. (see `logging.LogRecord` class from standard python library) + --hash-algo HASH_ALGO + Any `hashlib` available algorithm to use for generating fragments on package links. Can be disabled with one of (0, no, off, + false). + --backend {auto,simple-dir,cached-dir} + A backend implementation. Keep the default 'auto' to automatically determine whether to activate caching or not + --version show program's version number and exit + -x, --execute Execute the pip commands rather than printing to stdout + -d DOWNLOAD_DIRECTORY, --download-directory DOWNLOAD_DIRECTORY + Specify a directory where packages updates will be downloaded. The default behavior is to use the directory which contains the + package being updated. + -u, --allow-unstable Allow updating to unstable versions (alpha, beta, rc, dev, etc.) + --blacklist-file IGNORELIST_FILE, --ignorelist-file IGNORELIST_FILE + Don't update packages listed in this file (one package name per line, without versions, '#' comments honored). This can be useful + if you upload private packages into pypiserver, but also keep a mirror of public packages that you regularly update. Attempting to + pull an update of a private package from `pypi.org` might pose a security risk - e.g. a malicious user might publish a higher + version of the private package, containing arbitrary code. Client-Side Configurations ========================== @@ -301,7 +322,7 @@ #. You need to restart the server with the ``-P`` option only once (but user/password pairs can later be added or updated on the fly):: - ./pypi-server -p 8080 -P htpasswd.txt ~/packages & + ./pypi-server run -p 8080 -P htpasswd.txt ~/packages & Upload with ``setuptools`` -------------------------- @@ -359,7 +380,7 @@ To run the most recent release of ``pypiserver`` with Docker, simply:: - docker run pypiserver/pypiserver:latest + docker run pypiserver/pypiserver:latest run This starts ``pypiserver`` serving packages from the ``/data/packages`` directory inside the container, listening on the container port 8080. @@ -371,17 +392,17 @@ Of course, just running a container isn't that interesting. To map port 80 on the host to port 8080 on the container:: - docker run -p 80:8080 pypiserver/pypiserver:latest + docker run -p 80:8080 pypiserver/pypiserver:latest run You can now access your ``pypiserver`` at ``localhost:80`` in a web browser. To serve packages from a directory on the host, e.g. ``~/packages``:: - docker run -p 80:8080 -v ~/packages:/data/packages pypiserver/pypiserver:latest + docker run -p 80:8080 -v ~/packages:/data/packages pypiserver/pypiserver:latest run To authenticate against a local ``.htpasswd`` file:: - docker run -p 80:8080 -v ~/.htpasswd:/data/.htpasswd pypiserver/pypiserver:latest -P .htpasswd packages + docker run -p 80:8080 -v ~/.htpasswd:/data/.htpasswd pypiserver/pypiserver:latest run -P .htpasswd packages You can also specify ``pypiserver`` to run as a Docker service using a composefile. An example composefile is `provided <docker-compose.yml>`_. @@ -416,13 +437,6 @@ pip install git+git://github.com/pypiserver/pypiserver.git -Running on Heroku/Dotcloud --------------------------- - -https://github.com/dexterous/pypiserver-on-the-cloud contains -instructions on how to run ``pypiserver`` on one of the supported cloud -service providers. - Recipes ======= @@ -430,14 +444,14 @@ Managing the Package Directory ------------------------------ -The ``pypi-server`` command has the ``-U`` option that searches for updates of +The ``pypi-server`` command has the ``update`` command that searches for updates of available packages. It scans the package directory for available packages and searches on pypi.org for updates. Without further -options ``pypi-server -U`` will just print a list of commands which must +options ``pypi-server update`` will just print a list of commands which must be run in order to get the latest version of each package. Output looks like:: - $ ./pypi-server -U + $ ./pypi-server update checking 106 packages for newer version .........u.e...........e..u............. @@ -457,7 +471,7 @@ means the package can be updated and ``'e'`` means the list of releases on pypi is empty. After that it shows a *pip* command line which can be used to update a one package. Either copy and paste that or run -``pypi-server -Ux`` in order to really execute those commands. You need +``pypi-server update -x`` in order to really execute those commands. You need to have *pip* installed for that to work however. Specifying an additional ``-u`` option will also allow alpha, beta and @@ -529,7 +543,7 @@ User=www-data Group=www-data - ExecStart=/usr/local/bin/pypi-server -p 8080 -a update,download --log-file /var/log/pypiserver.log -P /etc/nginx/.htpasswd /var/www/pypi + ExecStart=/usr/local/bin/pypi-server run -p 8080 -a update,download --log-file /var/log/pypiserver.log -P /etc/nginx/.htpasswd /var/www/pypi ExecStop=/bin/kill -TERM $MAINPID ExecReload=/bin/kill -HUP $MAINPID Restart=always @@ -557,7 +571,7 @@ management. An example configuration file for ``supervisor`` is given below:: [program:pypi] - command=/home/pypi/pypi-venv/bin/pypi-server -p 7001 -P /home/pypi/.htpasswd /home/pypi/packages + command=/home/pypi/pypi-venv/bin/pypi-server run -p 7001 -P /home/pypi/.htpasswd /home/pypi/packages directory=/home/pypi user=pypi autostart=true @@ -575,7 +589,7 @@ Create a start_pypiserver.bat:: - pypi-server -p 8080 C:\Path\To\Packages & + pypi-server run -p 8080 C:\Path\To\Packages & Test the batch file by running it first before creating the service. Make sure you can access the server remotely, and install packages. If you can, proceed, if not troubleshoot until you can. @@ -913,6 +927,36 @@ Further information on micropython-packaging can be found here: https://docs.micropython.org/en/latest/reference/packages.html +Custom Health Check Endpoint +---------------------------- + +``pypiserver`` provides a default health endpoint at ``/health``. It always returns +``200 Ok`` if the service is up. Otherwise, it means that the service is not responsive. + +In addition, ``pypiserver`` allows users to customize the health endpoint. +Alphanumeric characters, hyphens, forward slashes and underscores are allowed +and the endpoint should not overlap with any existing routes. +Valid examples: ``/healthz``, ``/health/live-1``, ``/api_health``, ``/action/health`` + +Configure a custom health endpoint by CLI arguments +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Run pypiserver with ``--health-endpoint`` argument:: + + pypi-server run --health-endpoint /action/health + +Configure a custom health endpoint by script +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. code-block:: python + + import pypiserver + from pypiserver import bottle + app = pypiserver.app(root="./packages", health_endpoint="/action/health") + bottle.run(app=app, host="0.0.0.0", port=8080, server="auto") + +Try ``curl http://localhost:8080/action/health`` + Sources ======= @@ -1012,10 +1056,10 @@ .. _twine: https://pypi.org/project/twine/ .. _pypi-uploader: https://pypi.org/project/pypi-uploader/ .. _python-pam: https://pypi.org/project/python-pam/ -.. |test-status| image:: https://github.com/pypiserver/pypiserver/workflows/Test/badge.svg +.. |test-status| image:: https://github.com/pypiserver/pypiserver/actions/workflows/ci.yml/badge.svg :alt: test status :scale: 100% - :target: https://github.com/pypiserver/pypiserver/actions?query=workflow%3ATest + :target: https://github.com/pypiserver/pypiserver/actions/workflows/ci.yml .. |pypi-ver| image:: https://img.shields.io/pypi/v/pypiserver.svg :target: https://pypi.org/project/pypiserver/ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pypiserver-1.5.1/bootstrap.py new/pypiserver-1.5.2/bootstrap.py --- old/pypiserver-1.5.1/bootstrap.py 2022-10-18 16:06:16.000000000 +0200 +++ new/pypiserver-1.5.2/bootstrap.py 2023-07-30 23:18:51.000000000 +0200 @@ -88,6 +88,8 @@ ez["use_setuptools"](**setup_args) if to_reload: + from importlib import reload + reload(pkg_resources) import pkg_resources diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pypiserver-1.5.1/contributor_docs/README.md new/pypiserver-1.5.2/contributor_docs/README.md --- old/pypiserver-1.5.1/contributor_docs/README.md 2022-10-18 16:06:16.000000000 +0200 +++ new/pypiserver-1.5.2/contributor_docs/README.md 1970-01-01 01:00:00.000000000 +0100 @@ -1,25 +0,0 @@ -# Hello - -Welcome to the contributor's documentation section of `pypiserver`. -Here you can find information about the inner working of the system. -Contributions and support in maintaining (both the project and these documents) -is very welcome :v: - -> This documentation is still a work in progress and would benefit from - your help! -> Some areas to describe are highlighted in - [this issue](https://github.com/pypiserver/pypiserver/issues/368), - but feel free to add if you see something missing there. -> All the help will be very appreciated! :) - -## Find more information - -If something is missing in the documentation here, try checking in the -`README.rst` and if it's still not there, don't hesitate to contribute. - -## Documentation organization - -This folder contains various documents describing the `pypiserver` that might -be of interest to someone studying it in more details. -The documentation files are written in `.md` and it will be great if new -files are added in the same format. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pypiserver-1.5.1/contributor_docs/architecture_overview.md new/pypiserver-1.5.2/contributor_docs/architecture_overview.md --- old/pypiserver-1.5.1/contributor_docs/architecture_overview.md 2022-10-18 16:06:16.000000000 +0200 +++ new/pypiserver-1.5.2/contributor_docs/architecture_overview.md 1970-01-01 01:00:00.000000000 +0100 @@ -1,3 +0,0 @@ -# Architecture Overview - -To be described... diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pypiserver-1.5.1/docker/docker-requirements.txt new/pypiserver-1.5.2/docker/docker-requirements.txt --- old/pypiserver-1.5.1/docker/docker-requirements.txt 2022-10-18 16:06:16.000000000 +0200 +++ new/pypiserver-1.5.2/docker/docker-requirements.txt 2023-07-30 23:18:51.000000000 +0200 @@ -7,5 +7,5 @@ # If a user overrides args but does not override the server arg, we fall back to # whatever bottle chooses as a default. Since the wsgiref server is not # production-ready, install waitress as a fallback for these cases. -waitress==1.4.4 +waitress==2.1.2 watchdog==1.0.2 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pypiserver-1.5.1/docs/README.md new/pypiserver-1.5.2/docs/README.md --- old/pypiserver-1.5.1/docs/README.md 1970-01-01 01:00:00.000000000 +0100 +++ new/pypiserver-1.5.2/docs/README.md 2023-07-30 23:18:51.000000000 +0200 @@ -0,0 +1,33 @@ +# pypiserver docs + +Welcome to the documentation section of `pypiserver`. + +> :bulb: This documentation is still a work in progress +> and would benefit from your help. + +## More information + +If something is missing in the documentation here, maybe it is covered in +[`README.rst`](../README.rst). + +## Documentation setup + +> Some areas to describe are highlighted in + [this issue](https://github.com/pypiserver/pypiserver/issues/368) + ([#368](https://github.com/pypiserver/pypiserver/issues/368)), + but feel free to add if you see something missing there. + +The [`./contents`](./contents/) directory is designed to + all the documentation files. + +### How to contribute + +Everyone is very welcome to extend `pypiserver` documentation. + +1. :thought_balloon: If you think of a topic you would like to describe, this is great. +2. :deciduous_tree: Fork this project and clone it locally to start your contribution. +3. :page_facing_up: Create a new Markdown (`<doc title>.md`) file/subdirectory+file inside the [`./contents`](./contents/): + - :file_folder: Use subdirectories to organize related documentation into topics. +4. :octocat: Push your changes to your fork and open a PR to this repository. +5. :bell: Tag someone from the maintainers if you'd like, but in any case we will try to review the PR as soon as possible. +6. :dizzy: Once reviewed and accepted, your documentation will be merged into this repository. Binary files old/pypiserver-1.5.1/docs/__resources__/pypiserver_logo.png and new/pypiserver-1.5.2/docs/__resources__/pypiserver_logo.png differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pypiserver-1.5.1/pypiserver/__init__.py new/pypiserver-1.5.2/pypiserver/__init__.py --- old/pypiserver-1.5.1/pypiserver/__init__.py 2022-10-18 16:06:16.000000000 +0200 +++ new/pypiserver-1.5.2/pypiserver/__init__.py 2023-07-30 23:18:51.000000000 +0200 @@ -7,9 +7,9 @@ from pypiserver.bottle import Bottle from pypiserver.config import Config, RunConfig, strtobool -version = __version__ = "1.5.1" +version = __version__ = "1.5.2" __version_info__ = tuple(_re.split("[.-]", __version__)) -__updated__ = "2022-10-18 16:06:16" +__updated__ = "2023-07-30 23:18:50" __title__ = "pypiserver" __summary__ = "A minimal PyPI server for use with pip/easy_install." @@ -121,7 +121,7 @@ (or its base), defined in `pypiserver.config`, may be overridden. """ config = Config.default_with_overrides(**backwards_compat_kwargs(kwargs)) - return app_from_config(config) + return setup_routes_from_config(app_from_config(config), config) def app_from_config(config: RunConfig) -> Bottle: @@ -141,6 +141,20 @@ return _app.app +def setup_routes_from_config(app: Bottle, config: RunConfig) -> Bottle: + """Set up additional routes supplied from the config.""" + + def _setup_health_endpoint(app, config): + if config.health_endpoint in [route.rule for route in app.routes]: + raise RuntimeError( + "Provided health endpoint overlaps with existing routes" + ) + app.route(config.health_endpoint, "GET", lambda: "Ok") + + _setup_health_endpoint(app, config) + return app + + T = t.TypeVar("T") diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pypiserver-1.5.1/pypiserver/__main__.py new/pypiserver-1.5.2/pypiserver/__main__.py --- old/pypiserver-1.5.1/pypiserver/__main__.py 2022-10-18 16:06:16.000000000 +0200 +++ new/pypiserver-1.5.2/pypiserver/__main__.py 2023-07-30 23:18:51.000000000 +0200 @@ -169,6 +169,7 @@ # Here `app` is a Bottle instance, which we pass to bottle.run() to run # the server app = pypiserver.app_from_config(config) + app = pypiserver.setup_routes_from_config(app, config) if config.server_method == "gunicorn": # When bottle runs gunicorn, gunicorn tries to pull its arguments from diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pypiserver-1.5.1/pypiserver/_app.py new/pypiserver-1.5.2/pypiserver/_app.py --- old/pypiserver-1.5.1/pypiserver/_app.py 2022-10-18 16:06:16.000000000 +0200 +++ new/pypiserver-1.5.2/pypiserver/_app.py 2023-07-30 23:18:51.000000000 +0200 @@ -82,11 +82,6 @@ log.info(config.log_err_frmt, vars(http_error)) -@app.route("/health") -def health(): - return "Ok" - - @app.route("/favicon.ico") def favicon(): return HTTPError(404) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pypiserver-1.5.1/pypiserver/cache.py new/pypiserver-1.5.2/pypiserver/cache.py --- old/pypiserver-1.5.1/pypiserver/cache.py 2022-10-18 16:06:16.000000000 +0200 +++ new/pypiserver-1.5.2/pypiserver/cache.py 2023-07-30 23:18:51.000000000 +0200 @@ -14,7 +14,6 @@ ENABLE_CACHING = True except ImportError: - Observer = None ENABLE_CACHING = False diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pypiserver-1.5.1/pypiserver/config.py new/pypiserver-1.5.2/pypiserver/config.py --- old/pypiserver-1.5.1/pypiserver/config.py 2022-10-18 16:06:16.000000000 +0200 +++ new/pypiserver-1.5.2/pypiserver/config.py 2023-07-30 23:18:51.000000000 +0200 @@ -76,6 +76,7 @@ AUTHENTICATE = ["update"] FALLBACK_URL = "https://pypi.org/simple/" + HEALTH_ENDPOINT = "/health" HASH_ALGO = "md5" INTERFACE = "0.0.0.0" LOG_FRMT = "%(asctime)s|%(name)s|%(levelname)s|%(thread)d|%(message)s" @@ -134,6 +135,19 @@ ) +def health_endpoint_arg(arg: str) -> str: + """Verify the health_endpoint and raises ValueError if invalid.""" + rule_regex = r"^/[a-z0-9/_-]+$" + if re.fullmatch(rule_regex, arg, re.I) is not None: + return arg + + raise argparse.ArgumentTypeError( + "Invalid path for the health endpoint. Make sure that it contains only " + "alphanumeric characters, hyphens, forward slashes and underscores. " + f"In other words, make sure to match the following regex: {rule_regex}" + ) + + def html_file_arg(arg: t.Optional[str]) -> str: """Parse the provided HTML file and return its contents.""" if arg is None or arg == "pypiserver/welcome.html": @@ -383,6 +397,15 @@ ), ) run_parser.add_argument( + "--health-endpoint", + default=DEFAULTS.HEALTH_ENDPOINT, + type=health_endpoint_arg, + help=( + "Configure a custom liveness endpoint. It always returns 200 Ok if " + "the service is up. Otherwise, it means that the service is not responsive." + ), + ) + run_parser.add_argument( "--server", metavar="METHOD", default=DEFAULTS.SERVER_METHOD, @@ -430,6 +453,7 @@ help=( 'Add "Cache-Control: max-age=AGE" header to package downloads. ' "Pip 6+ requires this for caching." + "AGE is specified in seconds." ), ) run_parser.add_argument( @@ -598,7 +622,6 @@ return levels.get(self.verbosity, logging.NOTSET) def get_backend(self, arg: str) -> IBackend: - available_backends: t.Dict[str, BackendFactory] = { "auto": get_file_backend, "simple-dir": SimpleFileBackend, @@ -658,6 +681,7 @@ password_file: t.Optional[str], disable_fallback: bool, fallback_url: str, + health_endpoint: str, server_method: str, overwrite: bool, welcome_msg: str, @@ -665,7 +689,7 @@ log_req_frmt: str, log_res_frmt: str, log_err_frmt: str, - auther: t.Callable[[str, str], bool] = None, + auther: t.Optional[t.Callable[[str, str], bool]] = None, **kwargs: t.Any, ) -> None: """Construct a RuntimeConfig.""" @@ -676,6 +700,7 @@ self.password_file = password_file self.disable_fallback = disable_fallback self.fallback_url = fallback_url + self.health_endpoint = health_endpoint self.server_method = server_method self.overwrite = overwrite self.welcome_msg = welcome_msg @@ -700,6 +725,7 @@ "password_file": namespace.passwords, "disable_fallback": namespace.disable_fallback, "fallback_url": namespace.fallback_url, + "health_endpoint": namespace.health_endpoint, "server_method": namespace.server, "overwrite": namespace.overwrite, "welcome_msg": namespace.welcome, @@ -802,7 +828,9 @@ return default_config.with_updates(**overrides) @classmethod - def from_args(cls, args: t.Sequence[str] = None) -> Configuration: + def from_args( + cls, args: t.Optional[t.Sequence[str]] = None + ) -> Configuration: """Construct a Config from the passed args or sys.argv.""" # If pulling args from sys.argv (commandline arguments), argv[0] will # be the program name, (i.e. pypi-server), so we don't need to Binary files old/pypiserver-1.5.1/pypiserver_logo.png and new/pypiserver-1.5.2/pypiserver_logo.png differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pypiserver-1.5.1/requirements/test-requirements.txt new/pypiserver-1.5.2/requirements/test-requirements.txt --- old/pypiserver-1.5.1/requirements/test-requirements.txt 1970-01-01 01:00:00.000000000 +0100 +++ new/pypiserver-1.5.2/requirements/test-requirements.txt 2023-07-30 23:18:51.000000000 +0200 @@ -0,0 +1,11 @@ +gevent>=1.1b4; python_version >= '3' +httpx +pip +passlib>=1.6 +pytest>=6.2.2 +pytest-cov +setuptools>=40.0,<62.0.0 +tox +twine +webtest +wheel>=0.25.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pypiserver-1.5.1/requirements/test.pip new/pypiserver-1.5.2/requirements/test.pip --- old/pypiserver-1.5.1/requirements/test.pip 2022-10-18 16:06:16.000000000 +0200 +++ new/pypiserver-1.5.2/requirements/test.pip 2023-07-30 23:18:51.000000000 +0200 @@ -1,14 +1,3 @@ # Just the absolutely necessary extra requirements for # running tests - -gevent>=1.1b4; python_version >= '3' -httpx -pip -passlib>=1.6 -pytest>=6.2.2 -pytest-cov -setuptools>=40.0,<62.0.0 -tox -twine -webtest -wheel>=0.25.0 +-r test-requirements.txt diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pypiserver-1.5.1/setup.py new/pypiserver-1.5.2/setup.py --- old/pypiserver-1.5.1/setup.py 2022-10-18 16:06:16.000000000 +0200 +++ new/pypiserver-1.5.2/setup.py 2023-07-30 23:18:51.000000000 +0200 @@ -9,12 +9,12 @@ "pytest>=2.3", "tox", "twine", - "pip>=7", "passlib>=1.6", "webtest", ] setup_requires = ["setuptools", "setuptools-git >= 0.3", "wheel >= 0.25.0"] +install_requires = ["pip>=7"] def read_file(rel_path: str): @@ -45,6 +45,7 @@ packages=["pypiserver"], package_data={"pypiserver": ["welcome.html"]}, python_requires=">=3.6", + install_requires=install_requires, setup_requires=setup_requires, extras_require={"passlib": ["passlib>=1.6"], "cache": ["watchdog"]}, tests_require=tests_require, @@ -69,6 +70,8 @@ "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", "Topic :: Software Development :: Build Tools", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pypiserver-1.5.1/tests/test_app.py new/pypiserver-1.5.2/tests/test_app.py --- old/pypiserver-1.5.1/tests/test_app.py 2022-10-18 16:06:16.000000000 +0200 +++ new/pypiserver-1.5.2/tests/test_app.py 2023-07-30 23:18:51.000000000 +0200 @@ -188,12 +188,29 @@ assert len(resp.html("a")) == 0 -def test_health(testapp): +def test_health_default_endpoint(testapp): resp = testapp.get("/health") assert resp.status_int == 200 assert "Ok" in resp +def test_health_customized_endpoint(root): + from pypiserver import app + + _app = app(root=root.strpath, health_endpoint="/healthz") + testapp = webtest.TestApp(_app) + resp = testapp.get("/healthz") + assert resp.status_int == 200 + assert "Ok" in resp + + +def test_health_invalid_customized_endpoint(root): + from pypiserver import app + + with pytest.raises(RuntimeError, match="overlaps with existing routes"): + app(root=root.strpath, health_endpoint="/simple") + + def test_favicon(testapp): testapp.get("/favicon.ico", status=404) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pypiserver-1.5.1/tests/test_config.py new/pypiserver-1.5.2/tests/test_config.py --- old/pypiserver-1.5.1/tests/test_config.py 2022-10-18 16:06:16.000000000 +0200 +++ new/pypiserver-1.5.2/tests/test_config.py 2023-07-30 23:18:51.000000000 +0200 @@ -369,6 +369,21 @@ exp_config_type=RunConfig, exp_config_values={"fallback_url": "foobar.com"}, ), + # health-endpoint + ConfigTestCase( + case="Run: health-endpoint unspecified", + args=["run"], + legacy_args=[], + exp_config_type=RunConfig, + exp_config_values={"health_endpoint": DEFAULTS.HEALTH_ENDPOINT}, + ), + ConfigTestCase( + case="Run: health-endpoint specified", + args=["run", "--health-endpoint", "/healthz"], + legacy_args=["--health-endpoint", "/healthz"], + exp_config_type=RunConfig, + exp_config_values={"health_endpoint": "/healthz"}, + ), # server method ConfigTestCase( case="Run: server method unspecified", @@ -683,6 +698,14 @@ ) for val in ("true", "foo", "1", "md6") ), + *( + ConfigErrorCase( + case=f"Invalid health endpoint: {val}", + args=["run", "--health-endpoint", val], + exp_txt="Invalid path for the health endpoint", + ) + for val in ("/", "health", "/health!", "/:health", "/health?check=True") + ), ) # pylint: disable=unsubscriptable-object CONFIG_ERROR_PARAMS = (i[1:] for i in _CONFIG_ERROR_CASES) @@ -725,7 +748,7 @@ @pytest.mark.parametrize( "args, exp_txt", CONFIG_ERROR_PARAMS, - ids=CONFIG_TEST_IDS, + ids=CONFIG_ERROR_IDS, ) def test_config_error( args: t.List[str], diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pypiserver-1.5.1/tests/test_init.py new/pypiserver-1.5.2/tests/test_init.py --- old/pypiserver-1.5.1/tests/test_init.py 2022-10-18 16:06:16.000000000 +0200 +++ new/pypiserver-1.5.2/tests/test_init.py 2023-07-30 23:18:51.000000000 +0200 @@ -12,6 +12,7 @@ # Local imports import pypiserver +from pypiserver.config import Config logger = logging.getLogger(__name__) @@ -103,3 +104,19 @@ with pytest.raises(ValueError) as err: pypiserver.backwards_compat_kwargs(kwargs) assert "('redirect_to_fallback', 'disable_fallback')" in str(err.value) + + +def test_setup_routes_from_config_customized_endpoint() -> None: + _app = pypiserver.setup_routes_from_config( + pypiserver.app(), + Config.default_with_overrides(health_endpoint="/healthz"), + ) + assert "/healthz" in (route.rule for route in _app.routes) + + +def test_setup_routes_from_config_invalid_customized_endpoint() -> None: + with pytest.raises(RuntimeError, match="overlaps with existing routes"): + pypiserver.setup_routes_from_config( + pypiserver.app(), + Config.default_with_overrides(health_endpoint="/simple"), + ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pypiserver-1.5.1/tests/test_main.py new/pypiserver-1.5.2/tests/test_main.py --- old/pypiserver-1.5.1/tests/test_main.py 2022-10-18 16:06:16.000000000 +0200 +++ new/pypiserver-1.5.2/tests/test_main.py 2023-07-30 23:18:51.000000000 +0200 @@ -39,7 +39,6 @@ @pytest.fixture() def main(monkeypatch): - main = main_wrapper() def run(**kwargs): @@ -294,3 +293,20 @@ us.name.lower() in them for us, them in zip(our_check_order, bottle_adapters) ) + + +def test_health_endpoint_default(main): + main([]) + assert main.app._pypiserver_config.health_endpoint == "/health" + assert "/health" in (route.rule for route in main.app.routes) + + +def test_health_endpoint_customized(main): + main(["--health-endpoint", "/healthz"]) + assert main.app._pypiserver_config.health_endpoint == "/healthz" + assert "/healthz" in (route.rule for route in main.app.routes) + + +def test_health_endpoint_invalid_customized(main): + with pytest.raises(SystemExit): + main(["--health-endpoint", "/health!"]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/pypiserver-1.5.1/tox.ini new/pypiserver-1.5.2/tox.ini --- old/pypiserver-1.5.1/tox.ini 2022-10-18 16:06:16.000000000 +0200 +++ new/pypiserver-1.5.2/tox.ini 2023-07-30 23:18:51.000000000 +0200 @@ -1,5 +1,5 @@ [tox] -envlist = py36, py37, py38, py39, pypy3 +envlist = py36, py37, py38, py39, py310, py311, pypy3 [testenv] deps=-r{toxinidir}/requirements/test.pip