Skip to content

Commit

Permalink
Showing 3 changed files with 199 additions and 0 deletions.
115 changes: 115 additions & 0 deletions python-setup/auto_install_packages.py
@@ -0,0 +1,115 @@
#!/usr/bin/env python3

import sys
import os
import subprocess
from tempfile import mkdtemp

import extractor_version


def _check_call(command):
print('+ {}'.format(' '.join(command)), flush=True)
subprocess.check_call(command, stdin=subprocess.DEVNULL)


def _check_output(command):
print('+ {}'.format(' '.join(command)), flush=True)
out = subprocess.check_output(command, stdin=subprocess.DEVNULL)
print(out, flush=True)
sys.stderr.flush()
return out


def install_packages_with_poetry():
try:
_check_call(['poetry', 'install', '--no-root'])
except subprocess.CalledProcessError:
sys.exit('package installation with poetry failed, see error above')

# poetry is super annoying with `poetry run`, since it will put lots of output on
# STDOUT if the current global python interpreter is not matching the one in the
# virtualenv for the package, which was the case for using poetry for Python 2 when
# default system interpreter was Python 3 :/

poetry_out = _check_output(['poetry', 'run', 'which', 'python'])
python_executable_path = poetry_out.decode('utf-8').splitlines()[-1]

return python_executable_path


def install_packages_with_pipenv():
try:
_check_call(['pipenv', 'install', '--keep-outdated', '--ignore-pipfile'])
except subprocess.CalledProcessError:
sys.exit('package installation with pipenv failed, see error above')

pipenv_out = _check_output(['pipenv', 'run', 'which', 'python'])
python_executable_path = pipenv_out.decode('utf-8').splitlines()[-1]

return python_executable_path


def install_requirements_txt_packages(version: int, requirements_txt_path: str):
# create temporary directory ... that just lives "forever"
venv_path = mkdtemp(prefix='codeql-action-python-autoinstall-')

# virtualenv is a bit nicer for setting up virtual environment, since it will provide
# up-to-date versions of pip/setuptools/wheel which basic `python3 -m venv venv` won't

if version == 2:
_check_call(['python2', '-m', 'virtualenv', venv_path])
elif version == 3:
_check_call(['python3', '-m', 'virtualenv', venv_path])

venv_pip = os.path.join(venv_path, 'bin', 'pip')
try:
_check_call([venv_pip, 'install', '-r', requirements_txt_path])
except subprocess.CalledProcessError:
sys.exit('package installation with pip failed, see error above')

venv_python = os.path.join(venv_path, 'bin', 'python')

return venv_python


def install_packages() -> str:
if os.path.exists('poetry.lock'):
print('Found poetry.lock, will install packages with poetry', flush=True)
return install_packages_with_poetry()

if os.path.exists('Pipfile') or os.path.exists('Pipfile.lock'):
if os.path.exists('Pipfile.lock'):
print('Found Pipfile.lock, will install packages with Pipenv', flush=True)
else:
print('Found Pipfile, will install packages with Pipenv', flush=True)
return install_packages_with_pipenv()

version = extractor_version.get_extractor_version(sys.argv[1], quiet=False)

if os.path.exists('requirements.txt'):
print('Found requirements.txt, will install packages with pip', flush=True)
return install_requirements_txt_packages(version, 'requirements.txt')

print("was not able to install packages automatically", flush=True)


if __name__ == "__main__":
if len(sys.argv) != 2:
sys.exit('Must provide base directory for codeql tool as only argument')

# The binaries for packages installed with `pip install --user` are not available on
# PATH by default, so we need to manually add them.
os.environ['PATH'] = os.path.expanduser('~/.local/bin') + os.pathsep + os.environ['PATH']

python_executable_path = install_packages()

if python_executable_path is not None:
print("Setting CODEQL_PYTHON={}".format(python_executable_path))
print("::set-env name=CODEQL_PYTHON::{}".format(python_executable_path))

# TODO:
# - no packages
# - poetry without version
# - pipenv without version
# - pipenv without lockfile
53 changes: 53 additions & 0 deletions python-setup/extractor_version.py
@@ -0,0 +1,53 @@
#!/usr/bin/env python

# A quick hack to get package installation for Code Scanning to work,
# since it needs to know which version we're going to analyze the project as.

# This file needs to be placed next to `python_tracer.py`, so in
# `<codeql-path>/python/tools/`

from __future__ import print_function, division

import os
import sys
from contextlib import contextmanager


@contextmanager
def suppress_stdout_stderr():
# taken from
# https://thesmithfam.org/blog/2012/10/25/temporarily-suppress-console-output-in-python/
with open(os.devnull, "w") as devnull:
old_stdout = sys.stdout
old_stderr = sys.stderr
sys.stdout = devnull
sys.stderr = devnull
try:
yield
finally:
sys.stdout = old_stdout
sys.stderr = old_stderr


def get_extractor_version(codeql_base_dir: str, quiet: bool = True) -> int:

extractor_dir = os.path.join(codeql_base_dir, 'python', 'tools')
sys.path = [extractor_dir] + sys.path

from python_tracer import getzipfilename

zippath = os.path.join(extractor_dir, getzipfilename())
sys.path = [zippath] + sys.path
import buildtools.discover

if quiet:
with suppress_stdout_stderr():
return buildtools.discover.get_version()
else:
return buildtools.discover.get_version()


if __name__ == "__main__":
codeql_base_dir = sys.argv[1]
version = get_extractor_version(codeql_base_dir)
print('{!r}'.format(version))
31 changes: 31 additions & 0 deletions python-setup/install_tools.sh
@@ -0,0 +1,31 @@
#!/bin/sh
set -x
set -e

# The binaries for packages installed with `pip install --user` are not available on PATH
# by default, so we fix up PATH to suppress warnings by pip. This also needs to be done by
# any script that needs to access poetry/pipenv.
#
# Using `::add-path::` from the actions toolkit is not enough, since that only affects
# subsequent actions in the current job, and not the current action.
export PATH="$HOME/.local/bin:$PATH"

python2 -m pip install --user --upgrade pip setuptools wheel
python3 -m pip install --user --upgrade pip setuptools wheel

# virtualenv is a bit nicer for setting up virtual environment, since it will provide up-to-date versions of
# pip/setuptools/wheel which basic `python3 -m venv venv` won't
python2 -m pip install --user virtualenv
python3 -m pip install --user virtualenv

# venv is required for installation of poetry or pipenv (I forgot which)
sudo apt-get install -y python3-venv

# We're install poetry with pip instead of the recommended way, since the recommended way
# caused some problem since `poetry run` gives output like:
#
# /root/.poetry/lib/poetry/_vendor/py2.7/subprocess32.py:149: RuntimeWarning: The _posixsubprocess module is not being used. Child process reliability may suffer if your program uses threads.
# "program uses threads.", RuntimeWarning)
# LGTM_PYTHON_SETUP_VERSION=The currently activated Python version 2.7.18 is not supported by the project (^3.5). Trying to find and use a compatible version. Using python3 (3.8.2) 3

python3 -m pip install --user poetry pipenv

0 comments on commit 5701037

Please sign in to comment.