Дата и время публикации:
Проблема и решение
1. Суть проблемы
Реализация локального сервера, замещающего репозитарий testPyPI, которые является публичным индексным репозитарием пакетов Python (Python Package Index, PyPI) и устраняющий угрозу разрушения реального индекса, исходя от pypi.org
Что также делает приватной разработку проектов Python, который может содержать один или несколько пакетов.
pypiserver предоставляет минимальную функциональность совместимую с pip или easy_install (легкая установка и развертывание).[3.1] Он реализован на базе проекта bottle [3.2] и работает с пакетами из регулярной директории. Поддерживает форматы пакетов wheel, eggs [3.3], и так называемой, конечной и зачастую бинарной сборки bdists [3.4], упакованной в бинарном формате одним из выбранных архиватором или упаковщика менеджера пакета Linux формата. Может использоваться вместе с PGP-сигнатурами и загружен либо с помощью утилитами pip, setuptools, twine, pypi-uploader или просто скопирован с scp [3.1].
2. Решение
2.1 Конфигурация, установка и запуск минимального сервера PyPI
Предоставляется пакетом pypiserver, который нужно установить и создать директорию локального репозитария, как показано в дампе 2.1.1
Дамп 2.1.1
$ pip install pypiserver $ mkdir /home/$USER/yocto-poky/pypi-packages ... WARNING: The script pypi-server is installed in '/home/user/.local/bin' which is not on PATH. Consider adding this directory to PATH or, if you prefer to suppress this warning, use —no-warn-script-location. ...
В данном случае, директорией локального репозитария /home/$USER/yocto-poky/pypi-packages находится на внешнем носителе информации, смонтированным в точке монтирования /home/$USER/yocto-poky
Далее, в ручную разбиваем директорию на поддиректории, где каждая из них соответствует названию проекта, дерево которого показано в дампе 2.1.2
Дамп 2.1.2
├── bar
│ └── bar-0.1.tar.gz
└── foo
├── Foo-1.0.tar.gz
└── Foo-2.0.tar.gz
В котором показано дерево репозитария, состоящего из поддиректорий с нормализованными именами проекта, внутри которой находятся загружаемые файлы архивов. При этом, в случае наличия нескольких версий, имена файлов архивов должны иметь версии, подобно тому, какие имеют проект bar (одну версию — 0.1), а Foo (c версиями 1.0 и 2.0)
Каждая поддиректория должна иметь нормализованное имя проекта, в соответствии с требованиями PEP 503[3.7], что выражеается в том, что имя поддиректория проекта Foo должна быть приведена к foo, a Bar — bar [3.6]. PEP 503[3.7] так же требует наличие текстовой (индексной) страницы в HTML5, как показано в листинге 2.1.3 . При этом, это требование является обязательно, например, если вместо pypiserver будет использоваться apache2.
Листинг 2.1.4
<!DOCTYPE html>
<html>
<body>
<a href="/foo/">foo
<a href="/bar/">bar
</body>
</html>
В показанной структуре индексной страницы должны быть указаны линки от корня репозитария "/" с привязкой тэга к тексту и имеющий атрибут href, который ссылается на соответствующий проект согласно PEP 503 [3.7].
Поэтому теперь можем попробовать запустить pypiserver, например на сетевом порту 8080, т.к. на моей машине на порту 80 крутится Apache2 c собственном локальном хранилище Git-репозитариев [3.8] . Пробный запуск pypiserver показан в дампе 2.1.5
Дамп 2.1.5
/home/user/.local/bin/pypi-server -p 8080 /home/$USER/yocto-poky/pypi-packages 127.0.0.1 - - [24/Feb/2022 17:35:13] "GET / HTTP/1.1" 200 1007 127.0.0.1 - - [24/Feb/2022 17:35:13] "GET /favicon.ico HTTP/1.1" 404 711 127.0.0.1 - - [24/Feb/2022 17:35:37] "GET /packages/ HTTP/1.1" 200 333 127.0.0.1 - - [24/Feb/2022 17:35:43] "GET /simple/ HTTP/1.1" 200 241 127.0.0.1 - - [24/Feb/2022 17:35:47] "GET /packages/ HTTP/1.1" 200 333 127.0.0.1 - - [24/Feb/2022 17:36:43] "GET /packages/ HTTP/1.1" 200 333 127.0.0.1 - - [24/Feb/2022 17:36:43] "GET /favicon.ico HTTP/1.1" 404 711 ...
Где показан удачный результат загрузки индексной страницы (/packages/) с кодом 200 и факт отказа при загрузки favicon.ico с кодом 404, отсутствие которого не влияет на функционирования pypi-packages, что и подтверждает нам динамически созданная страница для http://localhost:8080
Рисунок 2.1.6
2.2 Запуск в фоновом режиме под менеджера сеанса Systemd
Так как, в моем случае все было установлено в домашней директории, поэтому воспользовался функцию в systemd, реализующую долговременную службу (user_lingering) и позволяющей запускать службу до входа пользователя в систему [3.10]. Для чего, нужно сначало активировать эту функцию, как показано в дампе 2.2.1
Дамп 2.2.1
user@home2:~$ loginctl enable-linger user
При этом, если выполнить loginctl через sudo, то все остальные попытки запуска закончаться ошибкой, которая показан в дампе 2.2.2
Дамп 2.2.2
user@home2:~$ systemctl --user enable pypi-server.service Failed to connect to bus: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined (consider using --machine=@.host --user to connect to bus of other user)
Потому что у самого пользователя не была активирована функция user_lingering в systemd [3.12]
После активации этой функции у пользователя, для запуска pypi-server нужно создать директорию ~/.config/systemd/user/ , если она не создана и поместить свой unit-файл с расширением .service
Пользовательский процесс systemd запускается сразу после первого входа пользователя в систему, и будет убит после завершения последнего сеанса пользователя. Иногда, может быть полезно запустить службу сразу после загрузки, и поддерживать процесс systemd запущенным даже после завершения последнего сеанса пользователя, например, чтобы некоторый пользовательский процесс работал без какой-либо открытой сессии. Для этой цели используются долговременные службы, которые запускаются из директории ~/.config/systemd/user/ и в unit-файле, как показано в дампе 2.2.3
Дамп 2.2.3
# саt ~/.config/systemd/user/pypi-server.service [Unit] Description=pypiserver daemon [Service] ExecStart=/home/user/.local/bin/pypi-server -p 8080 --log-file /home/user/.local/log/pypi-server.log /home/user/yocto-poky/pypi-packages ExecStop=/bin/kill -TERM $MAINPID ExecReload=/bin/kill -HUP $MAINPID Restart=always WorkingDirectory=/home/user/yocto-poky/pypi-packages [Install] WantedBy=default.target
Теперь нужно, активировать функцию и попробовать запустить сервис, как показано в дампе 2.2.4
Дамп 2.2.4
$ systemctl --user enable pypi-server.service
Created symlink /home/user/.config/systemd/user/default.target.wants/pypi-server.service → /home/user/.config/systemd/user/pypi-server.service.
...
user@home2:~$ systemctl --user daemon-reload
...
user@home2:~$ systemctl --user start pypi-server
user@home2:~$ systemctl --user status pypi-server
● pypi-server.service - pypiserver daemon
Loaded: loaded (/home/user/.config/systemd/user/pypi-server.service; enabled; vendor preset: enabled)
Active: active (running) since Fri 2022-02-25 10:19:21 MSK; 5s ago
Main PID: 6741 (pypi-server)
Tasks: 1 (limit: 16615)
Memory: 17.6M
CPU: 201ms
CGroup: /user.slice/user-1000.slice/user@1000.service/app.slice/pypi-server.service
└─6741 /usr/bin/python3 /home/user/.local/bin/pypi-server -p 8080 --log-file /home/user/.local/log/pypi-server.log /home/user/yocto-poky/pypi-packages
фев 25 10:19:21 home2 systemd[946]: Started pypiserver daemon.
В случае внесения изменения в файл pypi-server может потребоваться выполнить systemctl с --user daemon-reload, как показано выше в дампе 2.2.4
Для отключения функции поддержки долговременной службы выполните команду из дампа 2.2.5
Дамп 2.2.5
$ systemctl --user stop pypi-server.service $ systemctl --user disable pypi-server.service $ loginctl disable-linger
Соответственно, перед этим мне нужно было остановить службу pypi-server и деактивировать. А уже потом отключить поддежку запуска долговременных служб у пользователя.
2.3 Поддержка процедуры аутентификации
Для ее включения, мне понадобилось установить модуль passlib, который поддерживает разбор, требуемый также при запуске приложения wsgi в Apaches, файла аутентификации формата htpasswd, как показано в дампе 2.3.1
Дамп 2.3.1
$ pip install passlib
Соответственно, после чего добавил пользователя pypisrv, который не зарегистрирован в системе, как показано в дампе 2.3.2
Дамп 2.3.2
$ htpasswd -sc /home/$USER/yocto-poky/pypi-packages/.htpasswd pypisrv
После чего, мне оставалось лишь сделать правки в файл ~/.config/systemd/user/pypi-server.service, как показано в дампе 2.3.3
Дамп 2.3.3
# саt ~/.config/systemd/user/pypi-server.service [Unit] Description=pypiserver daemon [Service] ExecStart=/home/user/.local/bin/pypi-server -p 8080 -a update,download -P /home/$USER/yocto-poky/pypi-packages/.htpasswd --log-file /home/user/.local/log/pypi-server.log /home/user/yocto-poky/pypi-packages ExecStop=/bin/kill -TERM $MAINPID ExecReload=/bin/kill -HUP $MAINPID Restart=always WorkingDirectory=/home/user/yocto-poky/pypi-packages [Install] WantedBy=default.target
В результате, мной были добавлена опция -a (--authenticate) c установленными действиями для аутентификации, такие как updatе или download, и вместе с ней опция -P , где искать файл с паролями.
Далее, мне осталось лишь перезапустить сервиc pypi-sever, как показано в дампе 2.3.4
Дамп 2.3.4
~$ systemctl --user daemon-reload
~$ systemctl --user restart pypi-server.service
~$ systemctl --user status pypi-server.service
● pypi-server.service - pypiserver daemon
Loaded: loaded (/home/user/.config/systemd/user/pypi-server.service; enabled; vendor preset: enabled)
Active: active (running) since Sun 2022-02-27 21:58:08 MSK; 4s ago
Main PID: 20479 (pypi-server)
Tasks: 1 (limit: 16615)
Memory: 19.1M
CPU: 261ms
CGroup: /user.slice/user-1000.slice/user@1000.service/app.slice/pypi-server.service
└─20479 /usr/bin/python3 /home/user/.local/bin/pypi-server -p 8080 -a update,download -P /home/user/yocto-poky/pypi-packages/.htpasswd --log-file /home/user/.local/log/pypi-server.log /home/user/yo>
После чего, установил разработанный мной пакет shellenv-package-rjaan, как показано в дампе 2.3.5
Дамп 2.3.5
~$ pip install --index-url http://localhost:8080/simple/ shellenv-package-rjaan Defaulting to user installation because normal site-packages is not writeable Looking in indexes: http://localhost:8080/simple/ warning: missing-index-doctype × The package index page being used does not have a proper HTML doctype declaration. ╰─> Problematic URL: http://localhost:8080/simple/shellenv-package-rjaan/ note: This is an issue with the page at the URL mentioned above. hint: You might need to reach out to the owner of that package index, to get this fixed. See https://github.com/pypa/pip/issues/10825 for context. Collecting shellenv-package-rjaan User for localhost:8080: pypisrv Password: Downloading http://localhost:8080/packages/myprog/shellenv-package-rjaan-0.1.tar.gz (2.8 kB) Installing build dependencies ... done Getting requirements to build wheel ... done Preparing metadata (pyproject.toml) ... done Building wheels for collected packages: shellenv-package-rjaan Building wheel for shellenv-package-rjaan (pyproject.toml) ... done Created wheel for shellenv-package-rjaan: filename=shellenv_package_rjaan-0.1-py3-none-any.whl size=3539 sha256=7441388ce993b19cd95b0246c7c0b0d53a83517f38ac4b63fdc6e83dc191a28e Stored in directory: /home/user/.cache/pip/wheels/a7/40/d7/e11b7c490a75e5e7decde841885bd8e03e85b615e4d551eeb4 Successfully built shellenv-package-rjaan Installing collected packages: shellenv-package-rjaan Successfully installed shellenv-package-rjaan-0.1
При этом, для загрузки пакета мне понадобилось ввести пару значений аутентификационных данных (выделено жирным), состоящие из имени пользователя – pypisrv и его пароля, установленные ранее в дампе 2.3.2
2.4 Запуск в apache2
В моем случае, речь шла об начальной интеграции хранилища Git-репозитариев, настроенного ранее [3.10], и индексируемого репозитария пакетов Python (Python package index) на уровне разграничения полномочий, потому что все процессы с сайтами в моей конфигурации порождаются от имени пользователя git, а это значит все ранее устанавливаемые которого пораждаются apache2
Поэтому мне понадобилось установить те же самые пакеты Python у пользователя git, что устанавливались ранее во время разворачивания pypi-server, как это показано в дампе 2.4.1
Дамп 2.4.1
$ sudo -u git pip install pypiserver
[sudo] пароль для user:
Collecting pypiserver
Downloading pypiserver-1.4.2-py2.py3-none-any.whl (77 kB)
|████████████████████████████████| 77 kB 711 kB/s
Installing collected packages: pypiserver
WARNING: The script pypi-server is installed in '/home/git/.local/bin' which is not on PATH.
Consider adding this directory to PATH or, if you prefer to suppress this warning, use --no-warn-script-location.
Successfully installed pypiserver-1.4.2
$ sudo -u git pip install passlib
Collecting passlib
Downloading passlib-1.7.4-py2.py3-none-any.whl (525 kB)
|████████████████████████████████| 525 kB 2.4 MB/s
Installing collected packages: passlib
Successfully installed passlib-1.7.4
$ sudo -u git mkdir -p /home/git/pypisrv/wsgi
Для использования Apache2 в связке с pypiserver предпочтительно использовать mod_wsgi [3.1], как предлагается в проекте Buttle [3.13]. Для чего, нужно удостверится, что установлен соответсвующий пакет. Так для Debian-11 таким пакетом является libapache2-mod-wsgi-py3, проверка наличия и установка которого показана в дампе 2.4.2
Дамп 2.4.2
$ apt search libapache2-mod-wsgi-py3 Сортировка… Готово Полнотекстовый поиск… Готово libapache2-mod-wsgi-py3/stable 4.7.1-3+b1 i386 Python 3 WSGI adapter module for Apache $ sudo apt install libapache2-mod-wsgi-py3 [sudo] пароль для user: Чтение списков пакетов… Готово Построение дерева зависимостей… Готово Чтение информации о состоянии… Готово Следующие НОВЫЕ пакеты будут установлены: libapache2-mod-wsgi-py3 ... apache2_invoke: Enable module wsgi
Далее, в директории /etc/apache2, как показано в дампе 2.4.3, настраиваем альтернативные порты 8443 и 8080
Дамп 2.4.3
...
Listen 80
Listen 8080
<IfModule ssl_module>
Listen 443
Listen 8443
</IfModule>
<IfModule mod_gnutls.c>
Listen 443
Listen 8443
</IfModule>
...
На основании уже имеющегося файла 000-default.conf в директории /etc/apache2/sites-available создал 001-default.conf под порт 8080 и привел, как показано в дампе 2.4.4
Дамп 2.4.4
sudoedit /etc/apache2/sites-available/001-default.conf
...
<VirtualHost *:8080>
# The ServerName directive sets the request scheme, hostname and port that
# the server uses to identify itself. This is used when creating
# redirection URLs. In the context of virtual hosts, the ServerName
# specifies what hostname must appear in the request's Host: header to
# match this virtual host. For the default virtual host (this file) this
# value is not decisive as it is used as a last resort host regardless.
# However, you must set it for any further virtual host explicitly.
#ServerName www.example.com
ServerAdmin root@localhost
DocumentRoot /home/git/pypisrv
<Directory /home/git/pypisrv>
Require all granted
</Directory>
# WSGI Settings
WSGIScriptAlias / /home/git/pypisrv/wsgi/pypiserver.wsgi
WSGIDaemonProcess pypisrv user=git group=git umask=0007 \
processes=1 threads=5 maximum-requests=500 \
display-name=wsgi-pypisrv inactivity-timeout=300
WSGIProcessGroup pypisrv
# Required for authentication (https://github.com/pypiserver/pypiserver/issues/288)
WSGIPassAuthorization On
<Directory /home/git/pypisrv/wsgi>
<Files pypiserver.wsgi>
Require all granted
</Files>
</Directory>
# Available loglevels: trace8, ..., trace1, debug, info, notice, warn,
# error, crit, alert, emerg.
# It is also possible to configure the loglevel for particular
# modules, e.g.
#LogLevel info ssl:warn
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
# For most configuration files from conf-available/, which are
# enabled or disabled at a global level, it is possible to
# include a line for only one particular virtual host. For example the
# following line enables the CGI configuration for this host only
# after it has been globally disabled with "a2disconf".
#Include conf-available/serve-cgi-bin.conf
</VirtualHost>
Как показано в листинге 2.4.5, конструктором pypiserver:app() запускается pypiserver в bottle-приложении со следующей ключевые значения конфигурации:
- root= путь к корневой директории индексного репозитария пакетов Python;
- redirect_to_fallback= установлен в False, запрет переадресации, в случае установки в True должен быть установлен fallback_url=, например fallback_url="http://pypi.python.org/simple";
- password_file= путь к файлу .htpasswd;
- overwrite= в значении True устанавливает перезапись существующих пакетов.
Перезапускаем сервер, как показано в дампе 2.4.6
Дамп 2.4.6
$ sudo service apache2 reload
$ sudo service apache2 restart
$ sudo service apache2 status
● apache2.service - The Apache HTTP Server
Loaded: loaded (/lib/systemd/system/apache2.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2022-02-28 17:25:59 MSK; 3min 0s ago
Docs: https://httpd.apache.org/docs/2.4/
Process: 21212 ExecStart=/usr/sbin/apachectl start (code=exited, status=0/SUCCESS)
Main PID: 21216 (apache2)
Tasks: 64 (limit: 16615)
Memory: 53.3M
CPU: 699ms
CGroup: /system.slice/apache2.service
├─21216 /usr/sbin/apache2 -k start
├─21217 /usr/sbin/apache2 -k start
├─21218 wsgi-pypisrv -k start
├─21219 /usr/sbin/apache2 -k start
└─21220 /usr/sbin/apache2 -k start
фев 28 17:25:59 home2 systemd[1]: Starting The Apache HTTP Server...
фев 28 17:25:59 home2 systemd[1]: Started The Apache HTTP Server.
После чего,, мне осталось лишь настроить доступ Apach-пользователя pypisrv к только что запущенному виртуальном узлу, как это делалось ранее и показано в дампе 2.4.7
Дамп 2.4.7
$ htpasswd -sc /home/git/pypi-packages/.htpasswd pypisrv
Теперь, мне лишь осталось обеспечить доступ по HTTPS и альтернативному порту 8443, т.к. родной порт 443 планирую использовать в дальнейшем с виртуальным узлом, выделенный для обслуживания хранилища Git-репозитариев.
Поэтому переименовал файл 001-default.conf в 002-default.conf в директории /etc/apache2/sites-available и привел к виду, как показано в листинге 2.4.8
Дамп 2.4.8
<VirtualHost *:8443>
# The ServerName directive sets the request scheme, hostname and port that
# the server uses to identify itself. This is used when creating
# redirection URLs. In the context of virtual hosts, the ServerName
# specifies what hostname must appear in the request's Host: header to
# match this virtual host. For the default virtual host (this file) this
# value is not decisive as it is used as a last resort host regardless.
# However, you must set it for any further virtual host explicitly.
#ServerName www.example.com
ServerAdmin root@localhost
DocumentRoot /home/git/pypisrv
<Directory /home/git/pypisrv>
Require all granted
</Directory>
# WSGI Settings
WSGIScriptAlias / /home/git/pypisrv/wsgi/pypiserver.wsgi
#WSGIDaemonProcess pypisrv user=git group=git umask=0007 \
# processes=1 threads=5 maximum-requests=500 \
# display-name=wsgi-pypisrv inactivity-timeout=300
WSGIProcessGroup pypisrv
# Required for authentication (https://github.com/pypiserver/pypiserver/issues/288)
WSGIPassAuthorization On
<Directory /home/git/pypisrv/wsgi>
<Files pypiserver.wsgi>
Require all granted
</Files>
</Directory>
# SSL Engine Switch:
# Enable/Disable SSL for this virtual host.
SSLEngine on
# A self-signed (snakeoil) certificate can be created by installing
# the ssl-cert package. See
# /usr/share/doc/apache2/README.Debian.gz for more info.
# If both key and certificate are stored in the same file, only the
# SSLCertificateFile directive is needed.
SSLCertificateFile /etc/ssl/certs/ssl-cert-snakeoil.pem
SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
# SSL Protocol Adjustments:
# The safe and default but still SSL/TLS standard compliant shutdown
# approach is that mod_ssl sends the close notify alert but doesn't wait for
# the close notify alert from client. When you need a different shutdown
# approach you can use one of the following variables:
# o ssl-unclean-shutdown:
# This forces an unclean shutdown when the connection is closed, i.e. no
# SSL close notify alert is send or allowed to received. This violates
# the SSL/TLS standard but is needed for some brain-dead browsers. Use
# this when you receive I/O errors because of the standard approach where
# mod_ssl sends the close notify alert.
# o ssl-accurate-shutdown:
# This forces an accurate shutdown when the connection is closed, i.e. a
# SSL close notify alert is send and mod_ssl waits for the close notify
# alert of the client. This is 100% SSL/TLS standard compliant, but in
# practice often causes hanging connections with brain-dead browsers. Use
# this only for browsers where you know that their SSL implementation
# works correctly.
# Notice: Most problems of broken clients are also related to the HTTP
# keep-alive facility, so you usually additionally want to disable
# keep-alive for those clients, too. Use variable "nokeepalive" for this.
# Similarly, one has to force some clients to use HTTP/1.0 to workaround
# their broken HTTP/1.1 implementation. Use variables "downgrade-1.0" and
# "force-response-1.0" for this.
BrowserMatch "MSIE [2-6]" \
nokeepalive ssl-unclean-shutdown \
downgrade-1.0 force-response-1.0
# MSIE 7 and newer should be able to use keepalive
BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown
# allow user who has a certificate to access a particular URL if user presented it.
# <Location />
# SSLRequireSSL On
# SSLVerifyClient optional
# SSLVerifyDepth 1
# SSLOptions +StdEnvVars +StrictRequire
# </Location>
# SSL Engine Options:
# Set various options for the SSL engine.
# o FakeBasicAuth:
# Translate the client X.509 into a Basic Authorisation. This means that
# the standard Auth/DBMAuth methods can be used for access control. The
# user name is the `one line' version of the client's X.509 certificate.
# Note that no password is obtained from the user. Every entry in the user
# file needs this password: `xxj31ZMTZzkVA'.
# o ExportCertData:
# This exports two additional environment variables: SSL_CLIENT_CERT and
# SSL_SERVER_CERT. These contain the PEM-encoded certificates of the
# server (always existing) and the client (only existing when client
# authentication is used). This can be used to import the certificates
# into CGI scripts.
# o StdEnvVars:
# This exports the standard SSL/TLS related `SSL_*' environment variables.
# Per default this exportation is switched off for performance reasons,
# because the extraction step is an expensive operation and is usually
# useless for serving static content. So one usually enables the
# exportation for CGI and SSI requests only.
# o OptRenegotiate:
# This enables optimized SSL connection renegotiation handling when SSL
# directives are used in per-directory context.
SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire
</VirtualHost>
При этом, строчки для WSGIDaemonProcess были закоментированы, т.к. является достяжимой к WSGIDaemonProcess из конфигурации виртуального узла для порта 8080
После чего, нужно задействовать готовый профиль default-ssl, как показано в дампе 2.4.9
Дамп 2.4.9
sudo a2ensite 002-default.conf sudo systemctl restart apache2 sudo systemctl reload apache2
В результате получил картинку, которая показана на рисунке 2.4.10
Рисунок 2.4.10
2.5 Установка пакетов
После чего, смог сделать разработку более безопасной своего первого и маленького проекта shellenv, реализующий функциональность SHELL-команды env(1), которая относится к архаичной системе (legacy system) и выполняемой интерпретатором команд dash, sh, bash, ksh или более свежим их аналогом zsh
Далее, полученные файлы закладываем в локальный индексный репозитарий пакетов Python, который находится в директории /home/git/pypisrv/, как показано в дампе 2.5.1
Дамп 2.5.1
~ /home/git/pypisrv$ exa --tree . ├── shellenv/ └── shellenv-package-rjaan-0.1.tar.gz
После чего, можно выполнить установку пакета shellenv, как показано в дампе 2.5.2
Дамп 2.5.2
pip install --upgrade -U shellenv-package-rjaan --extra-index-url http://localhost:8080/simple
При этом, если использовать --index-url, то возникает ошибка разрешения зависимостей.
Для включения загрузки и установки пакетов с использованием механизма аутентификации в файле /home/git/pypisrv/wsgi/pypiserver.wsgi нужно добавить соответствующую строчку (выделенную жирным) в листинге 2.5.3
Дамп 2.5.3
#!/usr/bin/env python3
# An example pypiserver-wsgi.py for use with apache2 and mod_wsgi, edit as necessary.
#
# apache virtualhost configuration for mod_wsgi daemon mode:
# Alias /robots.txt /srv/yoursite/htdocs/robots.txt
# WSGIPassAuthorization On
# WSGIScriptAlias / /srv/yoursite/cfg/pypiserver.wsgi
# WSGIDaemonProcess pypisrv user=pypisrv group=pypisrv processes=1 threads=5 maximum-requests=500 umask=0007 display-name=wsgi-pypisrv inactivity-timeout=300
# WSGIProcessGroup pypisrv
USER="git"
PACKAGES = "/home/"+USER+"/pypisrv/"
HTPASSWD = PACKAGES+".htpasswd"
import pypiserver
conf = pypiserver.default_config (
root= PACKAGES,
redirect_to_fallback=False,
password_file=HTPASSWD,
auther=pam.authenticate,
overwrite=True )
application = pypiserver.app(**conf)
Добавленная мной строчка auther=pam.authenticate, нужна для реализации поддержки аутентификации провойдеров узлов децентрализованной беспроводной сети (ad hoc authentification providers)[3.19], которая основывается на файловой базы данных учетных записей в среде Linux и подобных Unix-систем.
При этом, нужно доставить пакет pam для пользователя git, от имени которого запускается apache2 и соответственно, как показано в дампе 2.5.4
Дамп 2.5.4
$ sudo -u git pip install --upgrade pam Requirement already satisfied: pam in /home/git/.local/lib/python3.9/site-packages (0.2.0) Collecting pam Using cached pam-0.2.0-py3-none-any.whl (976 bytes) Downloading pam-0.1.4.tar.gz (2.6 kB) Requirement already satisfied: python-pam in /home/git/.local/lib/python3.9/site-packages (from pam) (1.8.4)
После чего, можно попробовать установить пакет shellenv, как показано ниже.
2.6 Совместное использование Git и индексного репозитраия пакетов Python
Выполнить клонирование пакета shellenv из локального git-репозитария, как показано в дампе 2.6.1
Дамп 2.6.1
$ LANG=C git clone git://localhost/python-shellenv.git Cloning into 'python-shellenv'... remote: Enumerating objects: 28, done. remote: Counting objects: 100% (28/28), done. remote: Compressing objects: 100% (22/22), done. Receiving objects: 100% (28/28), 4.73 KiB | 4.73 MiB/s, done. remote: Total 28 (delta 5), reused 0 (delta 0), pack-reused 0 Resolving deltas: 100% (5/5), done.
Перейти в python-shellenv и выполнить сборку пакета, после чего пакет поместить, как показано в дампе 2.5.1
3. Библиография
3.1 pypiserver - minimal PyPI server for use with pip/easy_install
3.2 Bottle: Python Web Framework
3.4 Python. Creating Built Distributions
3.5 Монтирования партиций (носителей информации) по ее метке.
3.6 PyPA. Hosting your own simple repository
3.7 Python. PEP 503 -- Simple Repository API
3.8 Как разворачивать свой собственное (локальное) хранилище Git репозитариев
3.9 provide systemd service template for pypiserver #137
3.10 ArchLinux. Systemd (Русский)/User (Русский)
3.12 0.0.35: $DBUS_SESSION_BUS_ADDRESS and $XDG_RUNTIME_DIR not defined
3.13 bottle’s documentation on the Apache’s module MOD_WSGI
3.14 Список файлов пакета libapache2-mod-wsgi-py3 в bullseye для архитектуры amd64
3.15 wolfg, wolfg1969 оn gist.github.com
3.16 pypiserver on GitHub
3.17 How to setup apache fo multi user