Skip to content

Commit

Permalink
Merge pull request #1550 from crytic/dev-doctor-paths
Browse files Browse the repository at this point in the history
slither-doctor: PATH checks
  • Loading branch information
montyly authored Jan 9, 2023
2 parents a6ca165 + 407d35c commit 0fd24c0
Show file tree
Hide file tree
Showing 6 changed files with 186 additions and 4 deletions.
87 changes: 87 additions & 0 deletions .github/workflows/doctor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
name: CI

defaults:
run:
shell: bash

on:
workflow_dispatch:
pull_request:
paths:
- 'slither/tools/doctor/**'
- '.github/workflows/doctor.yml'

jobs:
slither-doctor:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: ["ubuntu-latest", "windows-2022"]
python: ["3.8", "3.9", "3.10", "3.11"]
exclude:
# strange failure
- os: windows-2022
python: 3.8
steps:
- uses: actions/checkout@v3

- name: Set up Python ${{ matrix.python }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python }}

- name: Try system-wide Slither
run: |
echo "::group::Install slither"
pip3 install .
echo "::endgroup::"
# escape cwd so python doesn't pick up local module
cd /
echo "::group::Via module"
python3 -m slither.tools.doctor .
echo "::endgroup::"
echo "::group::Via binary"
slither-doctor .
echo "::endgroup::"
- name: Try user Slither
run: |
echo "::group::Install slither"
pip3 install --user .
echo "::endgroup::"
# escape cwd so python doesn't pick up local module
cd /
echo "::group::Via module"
python3 -m slither.tools.doctor .
echo "::endgroup::"
echo "::group::Via binary"
slither-doctor .
echo "::endgroup::"
- name: Try venv Slither
run: |
echo "::group::Install slither"
python3 -m venv venv
source venv/bin/activate || source venv/Scripts/activate
hash -r
pip3 install .
echo "::endgroup::"
# escape cwd so python doesn't pick up local module
cd /
echo "::group::Via module"
python3 -m slither.tools.doctor .
echo "::endgroup::"
echo "::group::Via binary"
slither-doctor .
echo "::endgroup::"
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
packages=find_packages(),
python_requires=">=3.8",
install_requires=[
"packaging",
"prettytable>=0.7.2",
"pycryptodome>=3.4.6",
# "crytic-compile>=0.2.4",
Expand Down
5 changes: 5 additions & 0 deletions slither/tools/doctor/__main__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import argparse
import logging
import sys

from crytic_compile import cryticparser

Expand All @@ -25,6 +27,9 @@ def parse_args() -> argparse.Namespace:


def main():
# log on stdout to keep output in order
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, force=True)

args = parse_args()
kwargs = vars(args)

Expand Down
2 changes: 2 additions & 0 deletions slither/tools/doctor/checks/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Callable, List
from dataclasses import dataclass

from slither.tools.doctor.checks.paths import check_slither_path
from slither.tools.doctor.checks.platform import compile_project, detect_platform
from slither.tools.doctor.checks.versions import show_versions

Expand All @@ -12,6 +13,7 @@ class Check:


ALL_CHECKS: List[Check] = [
Check("PATH configuration", check_slither_path),
Check("Software versions", show_versions),
Check("Project platform", detect_platform),
Check("Project compilation", compile_project),
Expand Down
85 changes: 85 additions & 0 deletions slither/tools/doctor/checks/paths.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
from pathlib import Path
from typing import List, Optional, Tuple
import shutil
import sys
import sysconfig

from slither.utils.colors import yellow, green, red


def path_is_relative_to(path: Path, relative_to: Path) -> bool:
"""
Check if a path is relative to another one.
Compatibility wrapper for Path.is_relative_to
"""
if sys.version_info >= (3, 9, 0):
return path.is_relative_to(relative_to)

path_parts = path.resolve().parts
relative_to_parts = relative_to.resolve().parts

if len(path_parts) < len(relative_to_parts):
return False

for (a, b) in zip(path_parts, relative_to_parts):
if a != b:
return False

return True


def check_path_config(name: str) -> Tuple[bool, Optional[Path], List[Path]]:
"""
Check if a given Python binary/script is in PATH.
:return: Returns if the binary on PATH corresponds to this installation,
its path (if present), and a list of possible paths where this
binary might be found.
"""
binary_path = shutil.which(name)
possible_paths = []

for scheme in sysconfig.get_scheme_names():
script_path = Path(sysconfig.get_path("scripts", scheme))
purelib_path = Path(sysconfig.get_path("purelib", scheme))
script_binary_path = shutil.which(name, path=script_path)
if script_binary_path is not None:
possible_paths.append((script_path, purelib_path))

binary_here = False
if binary_path is not None:
binary_path = Path(binary_path)
this_code = Path(__file__)
this_binary = list(filter(lambda x: path_is_relative_to(this_code, x[1]), possible_paths))
binary_here = len(this_binary) > 0 and all(
path_is_relative_to(binary_path, script) for script, _ in this_binary
)

return binary_here, binary_path, list(set(script for script, _ in possible_paths))


def check_slither_path(**_kwargs) -> None:
binary_here, binary_path, possible_paths = check_path_config("slither")
show_paths = False

if binary_path:
print(green(f"`slither` found in PATH at `{binary_path}`."))
if binary_here:
print(green("Its location matches this slither-doctor installation."))
else:
print(
yellow(
"This path does not correspond to this slither-doctor installation.\n"
+ "Double-check the order of directories in PATH if you have several Slither installations."
)
)
show_paths = True
else:
print(red("`slither` was not found in PATH."))
show_paths = True

if show_paths:
print()
print("Consider adding one of the following directories to PATH:")
for path in possible_paths:
print(f" * {path}")
10 changes: 6 additions & 4 deletions slither/tools/doctor/checks/versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,19 @@
from typing import Optional
import urllib

from packaging.version import parse, LegacyVersion, Version
from packaging.version import parse, Version

from slither.utils.colors import yellow, green


def get_installed_version(name: str) -> Optional[LegacyVersion | Version]:
def get_installed_version(name: str) -> Optional[Version]:
try:
return parse(metadata.version(name))
except metadata.PackageNotFoundError:
return None


def get_github_version(name: str) -> Optional[LegacyVersion | Version]:
def get_github_version(name: str) -> Optional[Version]:
try:
with urllib.request.urlopen(
f"https://api.github.com/repos/crytic/{name}/releases/latest"
Expand Down Expand Up @@ -45,7 +45,9 @@ def show_versions(**_kwargs) -> None:

for name, (installed, latest) in versions.items():
color = yellow if name in outdated else green
print(f"{name + ':':<16}{color(installed or 'N/A'):<16} (latest is {latest or 'Unknown'})")
print(
f"{name + ':':<16}{color(str(installed) or 'N/A'):<16} (latest is {str(latest) or 'Unknown'})"
)

if len(outdated) > 0:
print()
Expand Down

0 comments on commit 0fd24c0

Please sign in to comment.