From d7142d523911198ee9f140e7758d2d36273c27d4 Mon Sep 17 00:00:00 2001 From: Danila Vershinin Date: Thu, 4 Jan 2024 00:45:35 +0700 Subject: [PATCH] Add certifi installation support Implemented a function to ensure certifi is installed in the virtual environment during package installation. This feature includes a script to install or update a set of Root Certificates for the SSL module, utilizing certificates provided by the certifi package. --- src/pip_safe/install_certifi.py | 56 +++++++++++++++++++++++++++++++++ src/pip_safe/main.py | 15 +++++++++ 2 files changed, 71 insertions(+) create mode 100644 src/pip_safe/install_certifi.py diff --git a/src/pip_safe/install_certifi.py b/src/pip_safe/install_certifi.py new file mode 100644 index 0000000..a59f3df --- /dev/null +++ b/src/pip_safe/install_certifi.py @@ -0,0 +1,56 @@ +"""Install the Python SSL Certificates""" +# install_certifi.py +# +# sample script to install or update a set of default Root Certificates +# for the ssl module. Uses the certificates provided by the certifi package: +# https://pypi.org/project/certifi/ + +import os +import os.path +import ssl +import stat +import subprocess +import sys + +STAT_0o775 = ( + stat.S_IRUSR + | stat.S_IWUSR + | stat.S_IXUSR + | stat.S_IRGRP + | stat.S_IWGRP + | stat.S_IXGRP + | stat.S_IROTH + | stat.S_IXOTH +) + + +def main(): + """Creates a CA bundle file for the ssl module from a set of PEM files.""" + openssl_dir, openssl_cafile = os.path.split( + ssl.get_default_verify_paths().openssl_cafile + ) + + print(" -- pip install --upgrade certifi") + subprocess.check_call( + [sys.executable, "-E", "-s", "-m", "pip", "install", "--upgrade", "certifi"] + ) + + import certifi + + # change working directory to the default SSL directory + os.chdir(openssl_dir) + relpath_to_certifi_cafile = os.path.relpath(certifi.where()) + print(" -- removing any existing file or link") + try: + os.remove(openssl_cafile) + except FileNotFoundError: + pass + print(" -- creating symlink to certifi certificate bundle") + os.symlink(relpath_to_certifi_cafile, openssl_cafile) + print(" -- setting permissions") + os.chmod(openssl_cafile, STAT_0o775) + print(" -- update complete") + + +if __name__ == "__main__": + main() diff --git a/src/pip_safe/main.py b/src/pip_safe/main.py index dd934c9..5c6b4ec 100644 --- a/src/pip_safe/main.py +++ b/src/pip_safe/main.py @@ -4,6 +4,7 @@ import logging import os import shutil +import sys import six from tabulate import tabulate @@ -174,6 +175,17 @@ def ensure_latest_pip(venv_pip): call_subprocess(args, extra_env={"PIP_DISABLE_PIP_VERSION_CHECK": "1"}) +def ensure_certifi(venv_python): + """Ensure certifi is installed in the virtualenv""" + # Only do this on Darwin (OSX) + if not sys.platform.startswith("darwin"): + return + args = [venv_python, os.path.join(os.path.dirname(__file__), "install_certifi.py")] + log.info("Ensuring certifi in the virtualenv") + log.debug(" ".join(args)) + call_subprocess(args) + + def install_package(name, system_wide=False, upgrade=False): """Install the given package""" # for system-wide install, we must ensure virtualenv and pip create world-readable files, @@ -190,6 +202,7 @@ def install_package(name, system_wide=False, upgrade=False): install_for = "system-wide" if system_wide else "for current user" create = True venv_pip = venv_dir + "/bin/pip" + venv_python = venv_dir + "/bin/python" if upgrade: log.info("Upgrading %s %s ...", name, install_for) # if pip is there, do not recreate virtualenv @@ -204,6 +217,8 @@ def install_package(name, system_wide=False, upgrade=False): # before invoking pip, ensure it is the latest by upgrading it ensure_latest_pip(venv_pip) + ensure_certifi(venv_python) + log.debug("Running pip install in the virtualenv %s", name) # call_subprocess here is used for convenience: since we already import # this, why not :)