diff --git a/Dockerfile b/Dockerfile index 723584d7..d0dcf058 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,21 +1,23 @@ -FROM python:3.12-slim-trixie +FROM ubuntu:20.04 -# The installer requires curl (and certificates) to download the release archive -RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates git dos2unix vim +ENV DEBIAN_FRONTEND=noninteractive -# Download the latest installer -ADD https://astral.sh/uv/install.sh /uv-installer.sh +RUN apt-get update && apt-get install -y --no-install-recommends \ + git curl build-essential dos2unix vim \ + libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev \ + wget llvm libncursesw5-dev xz-utils tk-dev libxml2-dev \ + libxmlsec1-dev libffi-dev liblzma-dev ca-certificates \ + && rm -rf /var/lib/apt/lists/* -# Run the installer then remove it -RUN sh /uv-installer.sh && rm /uv-installer.sh +ENV PYENV_ROOT=/root/.pyenv +ENV PATH="$PYENV_ROOT/bin:$PYENV_ROOT/shims:/home/bugsinpy/framework/bin:$PATH" -# Ensure the installed binary is on the `PATH` -ENV PATH="/root/.local/bin/:$PATH" -ENV BUGSINPY_HOME="/home/bugsinpy/" -ENV PATH="$BUGSINPY_HOME/framework/bin:$PATH" +RUN curl -fsSL https://pyenv.run | bash && \ + mkdir -p "$PYENV_ROOT/versions" "$PYENV_ROOT/cache" && \ + pyenv rehash + +ENV BUGSINPY_HOME=/home/bugsinpy -# Set working directory WORKDIR /home -# Default command -CMD ["bash"] \ No newline at end of file +CMD ["bash"] diff --git a/README.md b/README.md index 23352002..e14e13c6 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,9 @@ docker build -t bugsinpy . docker run -dt \ -v ./framework:/home/bugsinpy/framework \ -v ./projects:/home/bugsinpy/projects \ - -v ./workspace:/home/workspace + -v ./workspace:/home/workspace \ + -v bugsinpy-pyenv-versions:/root/.pyenv/versions \ + -v bugsinpy-pyenv-cache:/root/.pyenv/cache \ --name {your_container_name} bugsinpy docker exec -it {your_container_name} bash ``` @@ -35,6 +37,7 @@ Command | Description info | Get the information of a specific project or a specific bug checkout | Checkout buggy or fixed version project from dataset compile | Compile sources from project that have been checkout +safe-compile | Compile sources using the Python version declared by the checked-out project's `bugsinpy_bug.info` test | Run test case that relevant with bug, single-test case from input user, or all test cases from project coverage | Run code coverage analysis from test case that relevant with bug, single-test case from input user, or all test cases mutation | Run mutation analysis from input user or test case that relevant with bug @@ -46,6 +49,5 @@ fuzz | Run a test input generation from specific bug - Checkout a buggy source code version (youtube-dl, bug 2, buggy version): - `bugsinpy-checkout -p youtube-dl -v 0 -i 2 -w /temp/projects` - Compile sources and tests, and run tests from current directory: - - `bugsinpy-compile` + - `bugsinpy-safe-compile` - `bugsinpy-test` - diff --git a/framework/bin/bugsinpy-safe-compile b/framework/bin/bugsinpy-safe-compile new file mode 100755 index 00000000..c12b4e45 --- /dev/null +++ b/framework/bin/bugsinpy-safe-compile @@ -0,0 +1,111 @@ +#!/usr/bin/env bash + +set -euo pipefail + +usage="-w work_dir + The checked-out project directory to compile. Default will be the current directory. + --no-editable-install + Skip the post-compile editable install step. + --require-editable-install + Treat a failed editable install as a compile failure. By default it is best-effort. +" + +print_usage() { + cat <<____HALP +Usage: ${0##*/} [ --help ] [ -w work_dir ] [ --no-editable-install ] [ --require-editable-install ] +$usage +____HALP +} + +die() { + echo "bugsinpy-safe-compile: $*" >&2 + exit 1 +} + +work_dir="" +run_editable_install="1" +require_editable_install="0" + +while [[ $# -gt 0 ]]; do + case "$1" in + -h|--help) + print_usage + exit 0 + ;; + -w) + shift + [[ $# -gt 0 ]] || die "-w requires a work directory" + work_dir="$1" + ;; + --no-editable-install) + run_editable_install="0" + ;; + --require-editable-install) + require_editable_install="1" + ;; + *) + die "unknown argument: $1" + ;; + esac + shift +done + +if [[ -z "$work_dir" ]]; then + work_dir=$(pwd) +fi + +cd "$work_dir" || die "cannot enter work directory: $work_dir" +work_dir=$(pwd) + +[[ -e "$work_dir/bugsinpy_bug.info" ]] || die "this is not a checkout project folder: missing bugsinpy_bug.info" +[[ -e "$work_dir/bugsinpy_requirements.txt" ]] || die "this is not a checkout project folder: missing bugsinpy_requirements.txt" +[[ -e "$work_dir/bugsinpy_run_test.sh" ]] || die "this is not a checkout project folder: missing bugsinpy_run_test.sh" + +PYVER=$(awk -F'"' '/^python_version=/{ print $2; exit }' "$work_dir/bugsinpy_bug.info") +[[ -n "$PYVER" ]] || die "could not read python_version from bugsinpy_bug.info" + +command -v pyenv >/dev/null 2>&1 || die "pyenv is required but was not found on PATH" + +echo "Using Python $PYVER for $work_dir" +pyenv install -s "$PYVER" +printf '%s\n' "$PYVER" > "$work_dir/.python-version" +export PYENV_VERSION="$PYVER" +pyenv rehash >/dev/null 2>&1 || true + +actual_pyver=$(python3 -c 'import sys; print("{}.{}.{}".format(*sys.version_info[:3]))') +if [[ "$actual_pyver" != "$PYVER" ]]; then + die "python3 resolved to $actual_pyver, expected $PYVER" +fi + +echo "Running bugsinpy-compile with Python $actual_pyver" +script_dir=$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) +"$script_dir/bugsinpy-compile" + +if [[ "$run_editable_install" == "1" ]]; then + if [[ -f "$work_dir/setup.py" || -f "$work_dir/pyproject.toml" ]]; then + echo "Running post-compile editable install" + set +e + if [[ -d "$work_dir/env/Scripts" ]]; then + # shellcheck disable=SC1091 + source "$work_dir/env/Scripts/activate" + else + # shellcheck disable=SC1091 + source "$work_dir/env/bin/activate" + fi + python -m pip install -e . --no-deps + editable_status=$? + deactivate >/dev/null 2>&1 || true + set -e + + if [[ "$editable_status" -ne 0 ]]; then + if [[ "$require_editable_install" == "1" ]]; then + die "editable install failed" + fi + echo "Warning: editable install failed; continuing because it is best-effort by default" >&2 + fi + else + echo "Skipping editable install: no setup.py or pyproject.toml found" + fi +fi + +echo "Safe compile finished for $work_dir"