Compare commits

..
1 Commits
Author SHA1 Message Date
Murphy 8c0577ad14 Create codeql-analysis.yml 2022-03-15 11:22:46 +01:00
24 changed files with 381 additions and 1223 deletions

No files matched your search

BIN
View File
Binary file not shown.
+1 -12
View File
@@ -36,17 +36,6 @@ jobs:
run: pip install -r requirements/test.txt
- name: Run
run: tox -e py38-codecheck-flake8
unit-tests:
name: linux-unit-tests
runs-on: ubuntu-20.04
continue-on-error: true
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install dependencies
run: pip install -r requirements/test.txt
- name: Run
run: tox -e py38-tests
integration-tests:
name: linux-integration-tests
runs-on: ubuntu-20.04
@@ -74,7 +63,7 @@ jobs:
release-job:
name: Publish package
runs-on: ubuntu-20.04
needs: [integration-tests, unit-tests, flake8, pylint]
needs: [integration-tests, flake8, pylint]
if: github.event_name == 'release' && github.event.action == 'published'
steps:
- name: Checkout
-1
View File
@@ -1,4 +1,3 @@
.coverage
.idea
**/__pycache__/
venv
-2
View File
@@ -4,8 +4,6 @@ disable=
C0116, # Missing function or method docstring (missing-function-docstring)
C0115, # Missing class docstring (missing-class-docstring)
R0913, # Too many arguments (7/5) (too-many-arguments)
W0511, # TODO (fixme)
C0103, # invalid-name
[FORMAT]
max-line-length=120
+3 -4
View File
@@ -1,8 +1,7 @@
PYTHON?=python3
PIP?=pip3
all:
$(PIP) install .
pip install .
pub:
rm -rf dist
@@ -10,9 +9,9 @@ pub:
twine upload --repository=pypi --verbose dist/*
install:
$(PIP) install .
pip install .
uninstall:
$(PIP) uninstall r2env
pip uninstall r2env
.PHONY: all clean pub install uninstall
-1
View File
@@ -64,7 +64,6 @@ versions - list installed packages
list - list all packages available to r2env
shell - enter a new shell with PATH env var set
purge - remove ~/.r2env
update - update list of available profiles
Environment
+92
View File
@@ -0,0 +1,92 @@
{
"r2frida": {
"source": "https://github.com/nowsecure/r2frida",
"versions": [
{
"id": "git",
"packages": {}
}
]
},
"r2dec": {
"source": "https://github.com/wargio/r2dec-js",
"versions": [
{
"id": "git",
"packages": {}
}
]
},
"r2ghidra": {
"source": "https://github.com/radareorg/r2ghidra",
"versions": [
{
"id": "git",
"packages": {}
}
]
},
"r0": {
"source": "https://github.com/radareorg/r0",
"versions": [
{
"id": "git",
"packages": {}
}
]
},
"radare2": {
"source": "https://github.com/radareorg/radare2",
"versions": [
{
"id": "5.3.0",
"packages": {
"w64": "radare2-5.3.0-w64.zip",
"deb_x64": "radare2_5.3.0_amd64.deb"
}
},
{
"id": "5.3.1",
"packages": {
"w64": "radare2-5.3.1-w64.zip",
"mac_x64": "radare2-5.3.1.pkg",
"deb_x64": "radare2_5.3.1_amd64.deb"
}
},
{
"id": "5.4.0",
"packages": {
"w64": "radare2-5.4.0-w64.zip",
"mac_x64": "radare2-5.4.0.pkg",
"deb_x64": "radare2_5.4.0_amd64.deb"
}
},
{
"id": "5.4.2",
"packages": {
"w32": "radare2-5.4.2-w32.zip",
"w64": "radare2-5.4.2-w64.zip",
"mac_x64": "radare2-5.4.2-amd64.pkg",
"mac_arm64": "radare2-5.4.2-m1.pkg",
"deb_i386": "radare2_5.4.2_i386.deb",
"deb_x64": "radare2_5.4.2_amd64.deb"
}
},
{
"id": "5.5.0",
"packages": {
"w32": "radare2-5.5.0-w32.zip",
"w64": "radare2-5.5.0-w64.zip",
"mac_x64": "radare2-5.5.0-amd64.pkg",
"mac_arm64": "radare2-5.5.0-m1.pkg",
"deb_i386": "radare2_5.5.0_i386.deb",
"deb_x64": "radare2_5.5.0_amd64.deb"
}
},
{
"id": "git",
"packages": {}
}
]
}
}
+55 -71
View File
@@ -4,14 +4,11 @@ import os
import shutil
import json
import sys
from pathlib import Path
import re
from dploy import stow, unstow
from r2env.exceptions import R2EnvException
from r2env.package_manager import PackageManager
from r2env.tools import print_console, load_json_file, WARNING, ERROR, host_platform
from r2env.tools import print_console, load_json_file, WARNING, ERROR
class R2Env:
@@ -26,16 +23,16 @@ class R2Env:
def init(self):
if os.path.isdir(self._r2env_path):
print_console(f"[x] r2env already initialized. Path {self._r2env_path} already exists.", ERROR)
print_console("[x] r2env already initialized. Path {} already exists.".format(self._r2env_path), ERROR)
return
os.mkdir(self._r2env_path)
print_console(f"[*] r2env initialized at {self._r2env_path}")
print_console("[*] r2env initialized at {}".format(self._r2env_path))
def get_r2_path(self):
self.check_if_r2env_initialized()
self.exit_if_r2env_not_initialized()
r2_version = self._get_current_version()
r2_path = self._package_manager.get_package_path(r2_version) if r2_version else "Radare package not configured"
print_console(f" [*] {r2_version} ({r2_path})")
print_console(" [*] {0} ({1})".format(r2_version, r2_path))
def show_config(self):
print_console("[*] Current r2env config:")
@@ -49,100 +46,88 @@ class R2Env:
print_console("- Available")
packages = self._package_manager.list_available_packages()
for profile in packages:
print_console(f" - {profile}:", WARNING)
print_console(" - {}:".format(profile), WARNING)
for version in packages[profile]['versions']:
dists = ", ".join(version['packages'].keys())
print_console(f" - {profile}@{version['id']} - {dists}", formatter=1)
print_console(" - {}@{} - {}".format(profile, version['id'], dists), formatter=1)
def list_installed_packages(self):
self.check_if_r2env_initialized()
self.exit_if_r2env_not_initialized()
print_console("- Installed")
for pkg in self._package_manager.list_installed_packages():
if pkg == self._get_current_version():
print_console(f" - {pkg} (in use)")
print_console(" - {} (in use)".format(pkg))
else:
print_console(f" - {pkg}")
print_console(" - {}".format(pkg))
def install(self, package, **kwargs):
self.check_if_r2env_initialized()
use_meson = kwargs["use_meson"] if "use_meson" in kwargs else False
use_dist = kwargs["use_dist"] if "use_dist" in kwargs else False
if "@" not in package:
package = f"{package}@git"
def install(self, package, use_meson=False, use_dist=False):
self.exit_if_r2env_not_initialized()
if not self._check_package_format(package):
raise R2EnvException("[x] Invalid package format.")
print_console("[x] Invalid package format.", level=ERROR)
return
try:
profile, version = package.split('@')
except ValueError:
except:
profile = package
version = "git"
self._package_manager.install_package(profile, version, use_meson=use_meson, use_dist=use_dist)
print_console("[*] Add $HOME/.r2env/bin to your PATH or use 'r2env shell'.")
def uninstall(self, package, **kwargs):
self.check_if_r2env_initialized()
use_dist = kwargs["use_dist"] if "use_dist" in kwargs else False
def uninstall(self, package, use_meson, use_dist):
self.exit_if_r2env_not_initialized()
if not self._check_package_format(package):
raise R2EnvException("[x] Invalid package format.")
print_console("[x] Invalid package format.", level=ERROR)
return
self._package_manager.uninstall_package(package, use_dist)
def purge(self):
print_console(f"[*] Removing {self._r2env_path}")
print("Removing " + self._r2env_path)
shutil.rmtree(self._r2env_path)
def use(self, package=None, **_kwargs):
self.check_if_r2env_initialized()
def use(self, package=None):
self.exit_if_r2env_not_initialized()
if not package:
print_console("[x] Package not defined.", ERROR)
self.list_installed_packages()
raise R2EnvException("Package not defined.")
if package not in self._package_manager.list_installed_packages():
raise R2EnvException(f"Package {package} not installed.")
return
cur_ver = self._get_current_version()
new_dst_dir = self._package_manager.get_package_path(package)
if cur_ver:
unstow([self._package_manager.get_package_path(cur_ver)], self._r2env_path)
stow([new_dst_dir], self._r2env_path)
self._set_current_version(package)
print_console(f"[*] Using {package} package")
print_console("[*] Using {} package".format(package))
@staticmethod
def version():
thispath = os.path.dirname(os.path.realpath(__file__))
with open(f"{thispath}/version.txt", "r", encoding="utf-8") as version:
print_console(version.read())
with open(thispath + "/version.txt", "r") as version:
return version.read()
def shell(self, cmd=None, **_kwargs):
bin_path = os.path.join(self._r2env_path, "bin")
if host_platform() == "windows":
os.system(f"set PATH={bin_path};%PATH% && cmd")
def shell(self, cmd=""):
if os.name== "nt":
os.system("set PATH="+self._r2env_path+"\\bin;%PATH% && cmd")
return True
pkgconfig_path = os.path.join(self._r2env_path, "lib", "pkgconfig")
bin_path = os.path.join(self._r2env_path, "bin")
export_ps1 = f"export PS1=\"r2env ({self._get_current_version()})\\$ \";"
export_pkg_config = f"export PKG_CONFIG_PATH=\"{pkgconfig_path}\";"
export_path = f"export PATH=\"{bin_path}:$PATH\";"
cmd_shell = "$SHELL -f"
full_cmd = export_ps1 + export_pkg_config + export_path + cmd_shell
if host_platform() == "android": # hack for pre-dtag builds of r2
library_path = os.path.join(self._r2env_path, "lib")
full_cmd = f"export LD_LIBRARY_PATH=\"{library_path}\";" + full_cmd
if host_platform() == "osx":
library_path = os.path.join(self._r2env_path, "lib")
# XXX this doesnt work on sip-enabled macs, that's why we use install_name_tool after install
full_cmd = f"export DYLD_LIBRARY_PATH=\"{library_path}\";" + full_cmd
if cmd is None:
return os.system(full_cmd) == 0
return os.system(f"{full_cmd} -c '{cmd}'") == 0
line = "export PS1=\"r2env\\$ \";export PKG_CONFIG_PATH=\""
line = line + self._r2env_path+"/lib/pkgconfig\";export PATH=\""
line = line + self._r2env_path + "/bin:$PATH\"; $SHELL -f"
if os.path.isfile("/default.prop"): # hack for pre-dtag builds of r2
line = "export LD_LIBRARY_PATH=\"" + self._r2env_path + "/lib\";" + line
if os.uname().sysname == "Darwin":
line = "export DYLD_LIBRARY_PATH=\"" + self._r2env_path + "/lib\";" + line
if cmd.strip() == "":
return os.system(line)
return os.system(line + " -c '" + cmd + "'")
@staticmethod
def _load_config():
filename = os.path.join(os.path.dirname(__file__), 'config', 'config.json')
config = load_json_file(filename)
environment_path = os.getenv("R2ENV_PATH")
if environment_path is not None:
config["r2env_path"] = environment_path
ep = os.getenv("R2ENV_PATH")
if ep is not None:
config["r2env_path"] = ep
if not config:
config = {}
sys.exit(-1)
return config
@staticmethod
@@ -152,27 +137,26 @@ class R2Env:
@staticmethod
def _check_package_format(package):
regexp = r"[a-z0-9]+@(\d\.\d\.\d|git)"
if not package:
return False
return re.search(regexp, package) is not None
if package == "":
return True
return True
# regexp = re.compile(r"\w+\d@(?:\d\.\d\.\d|git)")
# return regexp.match(package)
def _get_current_version(self):
version_file = os.path.join(self._r2env_path, self.VERSION_FILE)
if not os.path.isfile(version_file):
return ''
with open(version_file, 'r', encoding="utf-8") as file_desc:
with open(version_file, 'r') as file_desc:
version = file_desc.read()
return version
def _set_current_version(self, package):
version_file = os.path.join(self._r2env_path, self.VERSION_FILE)
with open(version_file, 'w', encoding="utf-8") as file_desc:
with open(version_file, 'w') as file_desc:
file_desc.write(package)
def update_version_profiles(self):
PackageManager.update_radare2_profiles(self._r2env_path)
def check_if_r2env_initialized(self):
def exit_if_r2env_not_initialized(self):
if not os.path.isdir(self._r2env_path):
raise R2EnvException("Not r2env initialized. Execute 'r2env init' first.")
print_console("Not r2env initialized. Execute 'r2env init' first.", ERROR)
sys.exit(-1)
-10
View File
@@ -1,10 +0,0 @@
class R2EnvException(Exception):
pass
class ActionException(R2EnvException):
pass
class PackageManagerException(R2EnvException):
pass
+101 -216
View File
@@ -2,26 +2,22 @@
import os
import shutil
import time
from urllib.error import URLError
import zipfile
import json
import wget
import requests
import sys
import time
from zipfile import ZipFile
from r2env.exceptions import PackageManagerException
from r2env.tools import load_json_file, git_fetch, git_clean, print_console, ERROR, exit_if_not_exists, host_distname
class PackageManager:
LOG_DIR = "log"
PACKAGES_DIR = "versions"
SOURCE_DIR = "src"
DOWNLOAD_URL = "https://github.com/radareorg/radare2/releases/download/"
def __init__(self, r2env_path):
self._packages = self._load_profiles(r2env_path)
self._packages = self._load_profiles()
self._r2env_path = r2env_path
def get_package_path(self, package_name):
@@ -40,112 +36,18 @@ class PackageManager:
return res
@staticmethod
def _load_profiles(r2env_path):
filename = os.path.join(r2env_path, 'profiles.json')
# if profiles.json doesn't exist, generate it now
if not os.path.exists(filename):
print_console(f"NOTE: {filename} does not exist. Generating it now.")
PackageManager.update_radare2_profiles(r2env_path)
def _load_profiles():
filename = os.path.join(os.path.dirname(__file__), 'config', 'profiles.json')
profiles = load_json_file(filename)
if not profiles:
raise PackageManagerException("No profiles configured.")
sys.exit(-1)
return profiles
@staticmethod
def update_radare2_profiles(r2env_path):
gh_release_info_req = requests.get(
"https://api.github.com/repos/radareorg/radare2/releases",
{"headers": ["X-GitHub-Api-Version: 2022-11-28", "Accept: application/vnd.github+json"]},
timeout=10 # 10-second timeout for API request
)
gh_release_info_req.raise_for_status() # raise an error if the request got a bad return code
gh_release_info = gh_release_info_req.json()
radare2_versions = [{'id': 'git', 'packages': {}}] # include the git version by default
for release in gh_release_info:
version = {'id': release['tag_name'], 'packages': {}}
# generate a list of downloadable assets for this release that r2env can use
for asset in release['assets']:
# note: you're about to see 100 lines of hackery to deal with inconsistent naming schemes
# the goal is to include as many valid assets as is reasonable without including any that are not valid
# separate file extension
asset_name, asset_ext = os.path.splitext(asset['name'])
# normalize 'seperators' before splitting into components
asset_ref = asset_name.replace('_', '-').split('-')
# filter out things like r2ios_sdk and r2blob
if asset_ref[0] != 'radare2':
continue
# Some assets don't have the version number in the name, which breaks the following processing.
# None of them are of interest anyway, so just skip them.
if release['tag_name'] not in asset_ref:
continue
# remove the beginning 'radare2'
asset_ref.remove('radare2')
# we don't need to care about the release id when checking, so remove it
asset_ref.remove(release['tag_name'])
# once "radare2" and the version number have been removed from asset_ref,
# if all that's left matches a filter, save it.
_VERSION_FILTERS = {
'.zip': { # used for Windows builds among other things
'w32': [['w32']],
'w64': [['w64']]
},
'.deb': {
'deb_i386': [['i386']],
'deb_x64': [['amd64']]
},
'.pkg': { # macOS bundle
'mac_arm64': [['m1']],
'mac_x64': [[], ['amd64'], ['x64'], ['macos']]
}
}
if asset_ext in _VERSION_FILTERS:
for platform, refs in _VERSION_FILTERS[asset_ext].items():
if asset_ref in refs:
version['packages'][platform] = asset['name']
break
radare2_versions.append(version)
profiles = {
'r2frida': {
'source': 'https://github.com/nowsecure/r2frida',
'versions': [{'id': 'git', 'packages': {}}]
},
'r2dec': {
'source': 'https://github.com/wargio/r2dec-js',
'versions': [{'id': 'git', 'packages': {}}]
},
'r2ghidra': {
'source': 'https://github.com/radareorg/r2ghidra',
'versions': [{'id': 'git', 'packages': {}}]
},
'r0': {
'source': 'https://github.com/radareorg/r0',
'versions': [{'id': 'git', 'packages': {}}]
},
'radare2': {
'source': 'https://github.com/radareorg/radare2',
'versions': radare2_versions[::-1]
}
}
if not os.path.isdir(r2env_path):
os.makedirs(r2env_path)
with open(os.path.join(r2env_path, 'profiles.json'), 'w', encoding='utf-8') as f:
json.dump(profiles, f)
def install_package(self, profile, version, use_meson, use_dist):
log_path = os.path.join(self._r2env_path, self.LOG_DIR)
timestamp = round(time.monotonic() * 1000)
logfile = os.path.join(log_path, f"{profile}_{version}_{timestamp}_build.txt")
logfile = os.path.join(log_path, "{}_{}_{}_build.txt".format(profile, version, round(time.monotonic() * 1000)))
source_path = os.path.join(os.path.join(self._r2env_path, self.SOURCE_DIR, profile))
dst_dir = os.path.join(os.path.join(self._r2env_path, self.PACKAGES_DIR), f"{profile}@{version}")
dst_dir = os.path.join(os.path.join(self._r2env_path, self.PACKAGES_DIR), "{}@{}".format(profile, version))
if not os.path.isdir(log_path):
os.makedirs(log_path)
if not os.path.isdir(source_path):
@@ -154,18 +56,17 @@ class PackageManager:
os.makedirs(dst_dir)
self._exit_if_package_not_available(profile, version)
if use_dist:
if self._install_from_dist(profile, version):
print_console(f"[*] Binary package {profile}@{version} installed successfully")
if self._install_from_dist(profile, version, source_path, dst_dir, logfile, use_meson=use_meson):
print_console("[*] Binary package {}@{} installed successfully".format(profile, version))
else:
raise PackageManagerException(f"[x] Failed to install {profile}@{version}.")
print_console("Error.")
elif self._build_from_source(profile, version, source_path, dst_dir, logfile, use_meson=use_meson):
print_console("[*] Package {}@{} installed successfully".format(profile, version))
else:
if self._build_from_source(profile, version, source_path, dst_dir, logfile, use_meson=use_meson):
print_console(f"[*] Package {profile}@{version} installed successfully")
else:
if os.path.isdir(dst_dir):
shutil.rmtree(dst_dir)
raise PackageManagerException(f"[x] Failed to build {profile}@{version}. "
f"Check {logfile} for more information.")
print_console("[x] Something wrong happened during the build process. Check {} for more information.".format
(logfile), level=ERROR)
if os.path.isdir(dst_dir):
shutil.rmtree(dst_dir)
def uninstall_package(self, package_name, use_dist):
pkg_found = False
@@ -173,19 +74,18 @@ class PackageManager:
if package_name == package:
pkg_found = True
if use_dist:
profile, version = package.split("@")
self._uninstall_from_dist(profile, version)
kv = package.split("@")
self._uninstall_from_dist(kv[0], kv[1])
else:
pkg_dir = os.path.join(self._r2env_path, self.PACKAGES_DIR, package)
pkg_dir = os.path.join(os.path.join(self._r2env_path, self.PACKAGES_DIR), package_name)
try:
shutil.rmtree(pkg_dir)
print_console(f"[-] Removed package {package_name}")
print_console("Removed package {}".format(package_name))
except OSError as err:
raise PackageManagerException(f"[x] Unable to remove package {package_name}. Error: {err}") \
from err
print_console("[x] Unable to remove package {0}. Error: {1}".format(package_name, err), ERROR)
break
if not pkg_found:
raise PackageManagerException(f"[x] Unable to find installed package {package_name}")
print_console("[x] Unable to find installed package {0}".format(package_name), ERROR)
def _get_pkgname(self, profile, version, cos):
for pkgv in self._packages[profile]["versions"]:
@@ -196,85 +96,82 @@ class PackageManager:
return None
def _get_disturl(self, profile, version, cos):
pkgname = self._get_pkgname(profile, version, cos)
if pkgname is not None:
return "/".join([self.DOWNLOAD_URL, version, pkgname])
gh = "https://github.com/radareorg/radare2/releases/download/"
f = self._get_pkgname(profile, version, cos)
if f is not None:
return "/".join([gh, version, f])
return None
def _install_from_dist(self, profile, version):
print_console(f"[*] Installing {profile}@{version} package from binary dist")
dist_name = host_distname()
disturl = self._get_disturl(profile, version, dist_name)
pkgname = self._get_pkgname(profile, version, dist_name)
pkgfile = "/".join([self._r2env_path, "src", pkgname])
def _install_from_dist(self, profile, version, source_path, dst_path, logfile, use_meson=False):
print_console("[*] Installing {}@{} package from binary dist".format(profile, version))
dn = host_distname()
disturl = self._get_disturl(profile, version, dn)
pkgname = self._get_pkgname(profile, version, dn)
ofile = "/".join([self._r2env_path, "src", pkgname])
# os.system("wget -O " + ofile + " -qc " + disturl)
wget.download(disturl, ofile);
try:
wget.download(disturl, pkgfile)
except (URLError, ConnectionResetError) as err:
raise PackageManagerException(f"Failed to download {profile} package. Error: {err}") from err
# TODO: check package checksum
if dist_name in ("mac_x64", "mac_arm64"):
return os.system(f"sudo installer -pkg {pkgfile} -target /") == 0
if dist_name in "w64":
return self._install_windows_package(pkgfile)
if dist_name in ("deb_x64", "deb_i386"):
return os.system(f"sudo dpkg -i {pkgfile}") == 0
if dist_name in "android": # termux
return os.system("apt install radare2") == 0
raise PackageManagerException(f"Operative System not supported: {os.uname()}")
def _install_windows_package(self, pkgfile):
try:
with zipfile.ZipFile(pkgfile, 'r') as zip_file:
zip_file.extractall()
dst_folder = os.path.sep.join([self._r2env_path, "bin"])
if not os.path.isdir(dst_folder):
os.makedirs(dst_folder, 493) # 0755)
for file_path in zip_file.namelist():
if file_path.endswith(".exe") or file_path.endswith(".dll") and file_path.find("bin") != -1:
file_name = os.path.basename(file_path)
dst_filepath = os.path.sep.join([dst_folder, file_name])
shutil.copyfile(file_path, dst_filepath)
print_console(f"[-] Copying {file_path}")
radare2_bin_file_path = os.path.sep.join([self._r2env_path, "bin", "radare2.exe"])
r2_bin_file_path = os.path.sep.join([self._r2env_path, "bin", "r2.exe"])
shutil.copyfile(radare2_bin_file_path, r2_bin_file_path)
sysname = os.uname().sysname
except:
sysname = "Windows"
# TODO: checksum
if sysname == "Darwin":
if os.system("sudo installer -pkg " + ofile + " -target /") == 0:
return True
except (FileNotFoundError, IOError) as err:
print_console(f"[x] An error occurred when installing {pkgfile}. Error: {err}", level=ERROR)
self._uninstall_from_dist("radare2", "latest")
raise PackageManagerException(f"Unable to install Windows package {pkgfile}.") from err
if sysname == "Windows":
with ZipFile(ofile, 'r') as z:
z.extractall()
dst = os.path.sep.join([self._r2env_path, "bin"])
for f in z.namelist():
if f.endswith(".exe") or f.endswith(".dll") and f.find("bin") != -1:
fname = os.path.basename(f)
d = os.path.sep.join([dst, fname])
dname = os.path.dirname(d)
if not os.path.isdir(dname):
os.makedirs(dname, 493) # 0755)
shutil.copyfile(f, d)
print(f)
f = os.path.sep.join([self._r2env_path, "bin", "radare2.exe"])
d = os.path.sep.join([self._r2env_path, "bin", "r2.exe"])
shutil.copyfile(f, d)
if sysname == "Linux" and os.path.exists("/usr/bin/dpkg"):
if os.system("sudo dpkg -i " + ofile) == 0:
return True
if os.path.isfile("/default.prop"): # termux
if os.system("apt install radare2") == 0:
return True
return False
return False
def _uninstall_from_dist(self, profile, version):
print_console(f"[*] Cleaning {profile}@{version} package from binary dist")
dist_name = host_distname()
if dist_name in "w64":
dst = os.path.sep.join([self._r2env_path, "bin"])
print_console(f"[*] Removing {dst}")
try:
shutil.rmtree(dst)
return True
except OSError as err:
raise PackageManagerException(f"An issue occurred while removing {dst}. Error: {err}") from err
if dist_name in ("mac_x64", "mac_arm64"):
result_code = 0
print_console("[*] Uninstalling {}@{} package from binary dist".format(profile, version))
try:
sysname = os.uname().sysname
except:
sysname = "Windows"
if sysname == "Darwin":
pkgname = "org.radare.radare2"
result_code += os.system("pkgutil --pkg-info " + pkgname)
result_code += os.system(f"cd / && pkgutil --only-files --files {pkgname} | "
f"tr '\\n' '\\0' | xargs -n 1 -0 sudo rm -f")
result_code += os.system(f"cd / && pkgutil --only-dirs --files {pkgname} | "
f"tail -r | tr '\\n' '\\0' | xargs -n 1 -0 sudo rmdir 2>/dev/null")
result_code += os.system(f"sudo pkgutil --forget {pkgname}")
return result_code == 0
if dist_name in ("deb_x64", "deb_i386"):
return os.system("sudo dpkg -r " + profile) == 0
if dist_name in "android": # termux
return os.system("sudo dpkg -r radare2") == 0
raise PackageManagerException(f"Operative System not supported: {os.uname()}")
os.system("pkgutil --pkg-info " + pkgname)
os.system("cd / && pkgutil --only-files --files "+pkgname+" | tr '\\n' '\\0' | xargs -n 1 -0 sudo rm -f")
os.system("cd / && pkgutil --only-dirs --files "+pkgname+" | tail -r | tr '\\n' '\\0' | xargs -n 1 -0 sudo rmdir 2>/dev/null")
os.system("sudo pkgutil --forget " + pkgname)
if sysname == "Linux" and os.path.exists("/usr/bin/dpkg"):
try:
pkgname = profile.split("@")[0]
except:
pkgname = profile
if os.system("sudo dpkg -r " + pkgname) == 0:
return True
if os.path.isfile("/default.prop"): # termux
if os.system("sudo dpkg -r radare2") == 0:
return True
return False
return True
def _build_from_source(self, profile, version, source_path, dst_path, logfile, use_meson=False):
exit_if_not_exists(['git'])
print_console(f"[*] Installing {profile}@{version} package from source")
print_console(f"[-] Cloning {version} version")
print_console("[*] Installing {}@{} package from source".format(profile, version))
print_console("[-] Cloning {} version".format(version))
git_fetch(self._packages[profile]["source"], version, source_path)
print_console("[-] Cleaning Repo")
git_clean(source_path)
@@ -285,36 +182,23 @@ class PackageManager:
@staticmethod
def _build_using_acr(source_path, dst_path, logfile, r2env_path):
"""Only works in Unix systems"""
if host_distname() is not None and host_distname() in "w64":
raise PackageManagerException("acr is not supported on Windows platform. Use meson instead.")
exit_if_not_exists(['make'])
print_console("[-] Building package using acr...")
extra_flags = ""
if os.path.isfile("/default.prop"):
extra_flags = " --with-compiler=termux"
config_path = r2env_path + "/lib/pkgconfig"
cmd = f"(export PKG_CONFIG_PATH=\"{config_path}\";" \
f"cd {source_path} && " \
f"rm -rf shlr/capstone && " \
f"./configure {extra_flags} --with-rpath --prefix={dst_path} 2>&1 && " \
f"make -j4 2>&1 && " \
f"make install) > {logfile}"
print_console(f"Executing {cmd}")
cmd = "(export PKG_CONFIG_PATH=\"{4}\";cd {0} && rm -rf shlr/capstone && ./configure {1}" \
" --with-rpath --prefix={2} 2>&1 && make -j4 2>&1" \
"&& make install) > {3}".format(source_path, extra_flags, dst_path, logfile, r2env_path + "/lib/pkgconfig")
print(cmd)
return os.system(cmd) == 0
@staticmethod
def _build_using_meson(source_path, dst_path, logfile, r2env_path):
exit_if_not_exists(['meson', 'ninja'])
print_console("[-] Building package using meson ...")
config_path = r2env_path + "/lib/pkgconfig"
# TODO: ensure source_path logfile or dst_path have no spaces
cmd = f"(export PKG_CONFIG_PATH=\"{config_path}\";" \
f"cd \"{source_path}\" && rm -rf build && " \
f"meson build --buildtype=release --prefix={dst_path} -Dlocal=true 2>&1 && " \
f"ninja -C build && " \
f"ninja -C build install) > {logfile} && " \
f"for a in {dst_path}/bin/* ; do install_name_tool -add_rpath {dst_path}/lib $a ; done"
print_console(f"Executing {cmd}")
cmd = "(export PKG_CONFIG_PATH=\"{4}\";cd {0} && rm -rf build && meson . build --buildtype=release --prefix={1} -Dlocal=true 2>&1" \
"&& ninja -C build && ninja -C build install) > {2}".format(source_path, dst_path, logfile,r2env_path + "/lib/pkgconfig")
return os.system(cmd) == 0
def _exit_if_package_not_available(self, profile, version):
@@ -324,5 +208,6 @@ class PackageManager:
pkg_found = True
break
if not pkg_found:
raise PackageManagerException(
f"{profile}@{version} package not available. Use 'list' command to see available packages.")
print_console("{}@{} package not available. Use 'list' command to see available packages."
.format(profile, version), level=ERROR)
sys.exit(-1)
+89 -96
View File
@@ -3,126 +3,119 @@
import argparse
import sys
from r2env.exceptions import ActionException, R2EnvException, PackageManagerException
from r2env.tools import print_console, ERROR
from r2env.core import R2Env
class REPL:
HELP_MESSAGE = """
Usage: r2env [-flags] [action] [args...]
HELP_MESSAGE = """
Usage: r2env [-flags] [action] [args...]
Flags:
Flags:
-h, --help - show this help.
-v, --version - display r2env version.
-m, --meson - use meson instead of acr.
-p, --package - install the dist package instead of building
-l, --list - list available and installed packages
-h, --help - show this help.
-v, --version - display r2env version.
-m, --meson - use meson instead of acr.
-p, --package - install the dist package instead of building
-l, --list - list available and installed packages
Actions:
Actions:
init - create ~/.r2env directory.
config - display current .r2env settings.
add [pkg] - build and install given package. See -p and -m
use [pkg] - stow a specific version to be the default.
rm [pkg] - remove package from ~/.r2env
path - show path of current r2 in use.
list - list all packages available to r2env.
shell - enter a new shell with PATH env var set.
purge - remove ~/.r2env
update - update list of available profiles
init - create ~/.r2env directory.
config - display current .r2env settings.
add [pkg] - build and install given package. See -p and -m
use [pkg] - stow a specific version to be the default.
rm [pkg] - remove package from ~/.r2env
path - show path of current r2 in use.
list - list all packages available to r2env.
shell - enter a new shell with PATH env var set.
purge - remove ~/.r2env
Environment
Environment
R2ENV_PATH - specify different path other than ~/.r2env
R2ENV_PATH - specify different path other than ~/.r2env
"""
"""
def __init__(self):
self.actions_with_arguments = ["add", "install", "rm", "uninstall", "use"]
self._core = R2Env()
self.actions = {
"init": self._core.init,
"v": self._core.version,
"version": self._core.version,
"path": self._core.get_r2_path,
"sh": self._core.shell,
"shell": self._core.shell,
"config": self._core.show_config,
"ls": self._core.list_packages,
"list": self._core.list_packages,
"add": self._core.install,
"install": self._core.install,
"installed": self._core.list_installed_packages,
"uninstall": self._core.uninstall,
"update": self._core.update_version_profiles,
"rm": self._core.uninstall,
"use": self._core.use,
"purge": self._core.purge,
"h": self.show_help,
"help": self.show_help
}
def show_help(self):
print_console(self.HELP_MESSAGE)
def show_help():
print_console(HELP_MESSAGE)
def run_action(self, action_args): # noqa: C901
args = []
if not action_args:
self.show_help()
raise ActionException("No action selected")
if action_args.version:
self.actions["version"]()
return
if action_args.list:
list_method = self.actions["list"]
list_method()
if len(action_args.args) == 0:
self.show_help()
raise ActionException("No action selected")
if len(action_args.args) > 1:
args = action_args.args[1:]
action = action_args.args[0]
if action not in self.actions:
raise ActionException("Invalid action selected")
if len(args) < 1 and action in self.actions_with_arguments:
if action in ["add", "install"]:
self._core.list_packages()
if action in ["use", "rm", "uninstall"]:
self._core.list_installed_packages()
raise ActionException(f"Action {action} requires a package as an argument.")
if action in self.actions_with_arguments:
self.actions[action](args[0], use_meson=action_args.meson, use_dist=action_args.package)
elif action in "shell" and len(args) > 0:
self.actions[action](args[0])
def show_version():
print_console(R2Env().version())
actions_with_argument = ["add", "install", "rm", "uninstall"]
actions_with_arguments = ["sh", "shell", "use"]
actions = {
"init": R2Env().init,
"v": show_version,
"version": show_version,
"path": R2Env().get_r2_path,
"sh": R2Env().shell,
"shell": R2Env().shell,
"config": R2Env().show_config,
"ls": R2Env().list_packages,
"list": R2Env().list_packages,
"add": R2Env().install,
"install": R2Env().install,
"installed": R2Env().list_installed_packages,
"uninstall": R2Env().uninstall,
"rm": R2Env().uninstall,
"use": R2Env().use,
"purge": R2Env().purge,
"h": show_help,
"help": show_help
}
def run_action(argp):
action = ""
args = []
if len(argp.args) > 0:
action = argp.args[0]
if len(argp.args) > 1:
args = argp.args[1:]
if argp.version:
print_console(R2Env().version())
elif argp.list:
actions["list"]()
elif action == "":
show_help()
elif action not in actions:
print_console("Invalid action", ERROR)
elif action in actions_with_arguments:
actions[action](" ".join(args))
elif action in actions_with_argument:
exit_if_not_argument_is_set(args, action)
actions[action](args[0], use_meson=argp.meson, use_dist=argp.package)
else:
actions[action]()
def exit_if_not_argument_is_set(args, action):
if len(args) < 1:
if action in ["use", "rm", "uninstall"]:
print_console("[x] Package not defined.", ERROR)
R2Env().list_installed_packages()
else:
self.actions[action]()
print_console("[x] Missing package argument.", ERROR)
R2Env().list_available_packages()
sys.exit(-1)
def main():
parser = argparse.ArgumentParser()
parser.add_argument("args", help="run specified action. (Run r2env help for more information)",
action="store", nargs="*", default=[])
# parser.add_argument('args', metavar='args', nargs='+', type=str, help='Specified arguments')
#parser.add_argument('args', metavar='args', nargs='+', type=str, help='Specified arguments')
parser.add_argument("-v", "--version", dest="version", help="Show r2env version", action="store_true")
parser.add_argument("-m", "--meson", dest="meson", help="Use meson instead of acr to compile", action="store_true")
parser.add_argument("-p", "--package", dest="package", help="Use binary package for target system if available",
action="store_true")
parser.add_argument("-p", "--package", dest="package", help="Use binary package for target system if available", action="store_true")
parser.add_argument("-l", "--list", dest="list", help="List available and installed packages", action="store_true")
parser.print_help = REPL().show_help
action_args = parser.parse_args()
try:
REPL().run_action(action_args)
except ActionException as err:
print_console(f"[x] Parameter error. Details: {err}", level=ERROR)
sys.exit(-1)
except PackageManagerException as err:
print_console(f"[x] Package Error: {err}", level=ERROR)
sys.exit(-1)
except R2EnvException as err:
print_console(f"[x] r2Env Error: {err}", level=ERROR)
sys.exit(-1)
parser.print_help = show_help
argp = parser.parse_args()
run_action(argp)
if __name__ == "__main__":
-297
View File
@@ -1,297 +0,0 @@
from doublex import assert_that
from hamcrest import equal_to
from mock import patch
import unittest
from r2env.core import R2Env
from r2env.exceptions import R2EnvException
class TestCore(unittest.TestCase):
def setUp(self):
self.packages_dir = ["5.3.0"]
self.profiles = {
"radare2": {
"source": "https://github.com/radareorg/radare2",
"versions": [
{
"id": "5.3.0",
"packages": {
"w64": "radare2-5.3.0-w64.zip",
"deb_x64": "radare2_5.3.0_amd64.deb"
}
}
]
}
}
def tearDown(self):
patch.stopall()
@patch("r2env.core.R2Env._get_default_path")
def test_default_config(self, mock_default_path):
_ = R2Env()
mock_default_path.assert_called()
@patch("r2env.core.load_json_file")
def test_custom_config_using_json_file(self, mock_load_json_file):
custom_r2_path = "custom_r2_path"
mock_load_json_file.return_value = {"r2env_path": "custom_r2_path"}
r2env = R2Env()
assert_that(r2env._r2env_path, equal_to(custom_r2_path))
@patch("os.getenv")
def test_custom_config_using_envvar(self, mock_getenv):
custom_r2_path = "custom_r2_path"
mock_getenv.return_value = custom_r2_path
r2env = R2Env()
assert_that(r2env._r2env_path, equal_to(custom_r2_path))
@patch("os.mkdir")
@patch("os.path.isdir")
def test_init_failed(self, mock_isdir, mock_mkdir):
mock_isdir.return_value = True
R2Env().init()
mock_mkdir.assert_not_called()
@patch("os.mkdir")
@patch("os.path.isdir")
def test_init_successfully(self, mock_isdir, mock_mkdir):
mock_isdir.return_value = False
r2env= R2Env()
r2env.init()
mock_mkdir.assert_called_with(r2env._r2env_path)
@patch("os.path.isdir")
def test_check_if_r2env_not_initialized(self, mock_isdir):
mock_isdir.return_value = False
with self.assertRaises(R2EnvException):
R2Env().check_if_r2env_initialized()
@patch("r2env.core.print_console")
@patch("r2env.core.PackageManager.list_available_packages")
def test_list_available_pkg_successfully(self, mock_list_pkg, mock_print_console):
mock_list_pkg.return_value = self.profiles
R2Env().list_available_packages()
mock_print_console.assert_called_with(" - [email protected] - w64, deb_x64", formatter=1)
@patch("r2env.core.R2Env._get_current_version")
@patch("r2env.core.print_console")
@patch("os.path.isdir")
@patch("r2env.core.PackageManager.list_installed_packages")
def test_list_installed_pkg_successfully(self, mock_list_pkg, mock_isdir, mock_print, mock_current):
mock_list_pkg.return_value = self.packages_dir
mock_isdir.return_value = True
R2Env().list_installed_packages()
mock_print.assert_called_with(" - 5.3.0")
mock_current.return_value = "5.3.0"
R2Env().list_installed_packages()
mock_print.assert_called_with(" - 5.3.0 (in use)")
def test_check_package_format(self):
r2env = R2Env()
assert_that(r2env._check_package_format("radare2@git"), equal_to(True))
assert_that(r2env._check_package_format("[email protected]"), equal_to(True))
assert_that(r2env._check_package_format("[email protected]"), equal_to(False))
assert_that(r2env._check_package_format("[email protected]"), equal_to(False))
assert_that(r2env._check_package_format("radare2git"), equal_to(False))
@patch("r2env.core.R2Env._check_package_format")
@patch("r2env.core.R2Env.check_if_r2env_initialized")
@patch("r2env.core.PackageManager.install_package")
def test_install_successfully_default(self, mock_pkg_install, mock_initialized, mock_valid_format):
mock_initialized.return_value = True
mock_valid_format.return_value = True
package = "radare2"
version = "5.3.0"
R2Env().install(f"{package}@{version}")
mock_pkg_install.assert_called_with(package, version, use_meson=False, use_dist=False)
@patch("r2env.core.R2Env._check_package_format")
@patch("r2env.core.R2Env.check_if_r2env_initialized")
@patch("r2env.core.PackageManager.install_package")
def test_install_successfully_meson(self, mock_pkg_install, mock_initialized, mock_valid_format):
mock_initialized.return_value = True
mock_valid_format.return_value = True
package = "radare2"
version = "5.3.0"
R2Env().install(f"{package}@{version}", use_meson=True)
mock_pkg_install.assert_called_with(package, version, use_meson=True, use_dist=False)
@patch("r2env.core.R2Env._check_package_format")
@patch("r2env.core.R2Env.check_if_r2env_initialized")
@patch("r2env.core.PackageManager.install_package")
def test_install_successfully_dist(self, mock_pkg_install, mock_initialized, mock_valid_format):
mock_initialized.return_value = True
mock_valid_format.return_value = True
package = "radare2"
version = "5.3.0"
R2Env().install(f"{package}@{version}", use_dist=True)
mock_pkg_install.assert_called_with(package, version, use_meson=False, use_dist=True)
@patch("r2env.core.R2Env.check_if_r2env_initialized")
def test_install_successfully_failed(self, mock_initialized):
mock_initialized.return_value = True
package = "radare2@"
version = ""
with self.assertRaises(R2EnvException):
R2Env().install(f"{package}{version}", use_dist=True)
@patch("r2env.core.R2Env._check_package_format")
@patch("r2env.core.R2Env.check_if_r2env_initialized")
@patch("r2env.core.PackageManager.install_package")
def test_install_successfully_failed_with_default(self, mock_pkg_install, mock_initialized, mock_valid_format):
mock_initialized.return_value = True
mock_valid_format.return_value = True
package = "radare2"
R2Env().install(package)
mock_pkg_install.assert_called_with(package, "git", use_meson=False, use_dist=False)
@patch("r2env.core.R2Env._check_package_format")
@patch("r2env.core.R2Env.check_if_r2env_initialized")
@patch("r2env.core.PackageManager.uninstall_package")
def test_uninstall_successfully_default(self, mock_pkg_uninstall, mock_initialized, mock_valid_format):
mock_initialized.return_value = True
mock_valid_format.return_value = True
package = "[email protected]"
R2Env().uninstall(package)
mock_pkg_uninstall.assert_called_with(package, False)
@patch("r2env.core.R2Env._check_package_format")
@patch("r2env.core.R2Env.check_if_r2env_initialized")
@patch("r2env.core.PackageManager.uninstall_package")
def test_uninstall_successfully_dist(self, mock_pkg_uninstall, mock_initialized, mock_valid_format):
mock_initialized.return_value = True
mock_valid_format.return_value = True
package = "[email protected]"
R2Env().uninstall(package, use_dist=True)
mock_pkg_uninstall.assert_called_with(package, True)
@patch("r2env.core.R2Env._check_package_format")
@patch("r2env.core.R2Env.check_if_r2env_initialized")
def test_uninstall_failed(self, mock_initialized, mock_valid_format):
mock_initialized.return_value = True
mock_valid_format.return_value = False
package = "[email protected]"
with self.assertRaises(R2EnvException):
R2Env().uninstall(package, use_dist=True)
@patch("shutil.rmtree")
def test_purge(self, mock_rmtree):
r2env = R2Env()
r2env.purge()
mock_rmtree.assert_called_with(r2env._r2env_path)
@patch("r2env.core.PackageManager.list_installed_packages")
@patch("r2env.core.R2Env.check_if_r2env_initialized")
def test_use_package_not_installed(self, mock_initialized, mock_list_pkg):
package = "radare2@not_existing"
mock_list_pkg.return_value = ["[email protected]"]
mock_initialized.return_value = True
r2env = R2Env()
with self.assertRaises(R2EnvException):
r2env.use(package)
@patch("r2env.core.PackageManager.list_installed_packages")
@patch("r2env.core.PackageManager.get_package_path")
@patch("r2env.core.unstow")
@patch("r2env.core.stow")
@patch("r2env.core.R2Env._set_current_version")
@patch("r2env.core.R2Env._get_current_version")
@patch("r2env.core.R2Env.check_if_r2env_initialized")
def test_use_successfully(self, mock_initialized, mock_get_cur_ver, mock_set_cur_ver, mock_stow, mock_unstow,
mock_pkg_path, mock_list_pkg):
package = "[email protected]"
mock_initialized.return_value = True
mock_get_cur_ver.return_value = "5.3.0"
mock_list_pkg.return_value = ["[email protected]"]
new_path = f"new_path/{package}"
curr_path = f"new_path/old_package"
mock_pkg_path.side_effect = [new_path, curr_path]
r2env = R2Env()
r2env.use(package)
mock_set_cur_ver.assert_called_once_with(package)
mock_stow.assert_called_once_with([new_path], r2env._r2env_path)
mock_unstow.assert_called_once_with([curr_path], r2env._r2env_path)
@patch("r2env.core.PackageManager.list_installed_packages")
@patch("r2env.core.R2Env.check_if_r2env_initialized")
def test_use_with_no_none_package(self, mock_initialized, mock_list_pkg):
mock_initialized.return_value = True
r2env = R2Env()
with self.assertRaises(R2EnvException):
r2env.use(None)
mock_list_pkg.assert_called_once()
@patch("r2env.core.PackageManager.list_installed_packages")
@patch("r2env.core.R2Env.check_if_r2env_initialized")
def test_use_with_no_package(self, mock_initialized, mock_list_pkg):
mock_initialized.return_value = True
r2env = R2Env()
with self.assertRaises(R2EnvException):
r2env.use()
mock_list_pkg.assert_called_once()
@patch("r2env.core.PackageManager")
@patch("r2env.core.os.path.join")
@patch("r2env.core.host_platform")
@patch("r2env.core.os.system")
@patch("r2env.core.load_json_file")
def test_shell_windows(self, mock_load_json_file, mock_system, mock_host_platform, mock_join, _):
def side_effect(path1, path2, *args):
return f"{path1}\\{path2}"
r2env_path = "c:\\windows\\temp\\r2env\\"
mock_join.side_effect = side_effect
mock_host_platform.return_value = "windows"
mock_load_json_file.return_value = {"r2env_path": r2env_path}
cmd = "r2 -v"
R2Env().shell(cmd)
mock_system.assert_called_with(f"set PATH={r2env_path}\\bin;%PATH% && cmd")
@patch("r2env.core.host_platform")
@patch("r2env.core.os.system")
@patch("r2env.core.load_json_file")
def test_shell_android(self, mock_load_json_file, mock_system, mock_host_platform):
mock_host_platform.return_value = "android"
r2env_path = "/tmp/r2env"
mock_load_json_file.return_value = {"r2env_path": r2env_path}
cmd = "r2 -v"
R2Env().shell(cmd)
mock_system.assert_called_with(f"export LD_LIBRARY_PATH=\"{r2env_path}/lib\";"
f"export PS1=\"r2env ()\\$ \";"
f"export PKG_CONFIG_PATH=\"{r2env_path}/lib/pkgconfig\";"
f"export PATH=\"{r2env_path}/bin:$PATH\";"
f"$SHELL -f -c \'{cmd}\'")
@patch("r2env.core.host_platform")
@patch("r2env.core.os.system")
@patch("r2env.core.load_json_file")
def test_shell_osx(self, mock_load_json_file, mock_system, mock_host_platform):
mock_host_platform.return_value = "osx"
r2env_path = "/tmp/r2env"
mock_load_json_file.return_value = {"r2env_path": r2env_path}
cmd = "r2 -v"
R2Env().shell(cmd)
mock_system.assert_called_with(f"export DYLD_LIBRARY_PATH=\"{r2env_path}/lib\";"
f"export PS1=\"r2env ()\\$ \";"
f"export PKG_CONFIG_PATH=\"{r2env_path}/lib/pkgconfig\";"
f"export PATH=\"{r2env_path}/bin:$PATH\";"
f"$SHELL -f -c \'{cmd}\'")
@patch("r2env.core.host_platform")
@patch("r2env.core.os.system")
@patch("r2env.core.load_json_file")
def test_shell_linux(self, mock_load_json_file, mock_system, mock_host_platform):
mock_host_platform.return_value = "linux"
r2env_path = "/tmp/r2env"
mock_load_json_file.return_value = {"r2env_path": r2env_path}
cmd = "r2 -v"
R2Env().shell(cmd)
mock_system.assert_called_with(f"export PS1=\"r2env ()\\$ \";"
f"export PKG_CONFIG_PATH=\"{r2env_path}/lib/pkgconfig\";"
f"export PATH=\"{r2env_path}/bin:$PATH\";"
f"$SHELL -f -c \'{cmd}\'")
View File
Whitespace-only changes.
-403
View File
@@ -1,403 +0,0 @@
import os
from unittest.mock import MagicMock
from urllib.error import URLError
from doublex import assert_that
from hamcrest import equal_to
from mock import patch
import unittest
from r2env.exceptions import PackageManagerException
from r2env.package_manager import PackageManager
class TestPackageManager(unittest.TestCase):
def setUp(self):
self.profile = "radare2"
self.version = "5.3.0"
self.profiles = {
self.profile: {
"source": "https://github.com/radareorg/radare2",
"versions": [
{
"id": self.version,
"packages": {
"w64": "radare2-5.3.0-w64.zip",
"deb_x64": "radare2_5.3.0_amd64.deb"
}
}
]
}
}
self.load_json_mock = patch("r2env.package_manager.load_json_file").start()
self.load_json_mock.return_value = self.profiles
self.default_r2env_path = "/tmp/r2env"
self.package_manager = PackageManager(self.default_r2env_path)
def tearDown(self):
patch.stopall()
def test_constructor(self):
assert_that(self.package_manager._packages, equal_to(self.profiles))
assert_that(self.package_manager._r2env_path, equal_to(self.default_r2env_path))
def test_constructor_with_no_profiles(self):
self.load_json_mock.return_value = []
with self.assertRaises(PackageManagerException):
PackageManager(self.default_r2env_path)
def test_get_package_path(self):
package_name = "test"
package_path = self.package_manager.get_package_path(package_name)
assert_that(package_path, equal_to(os.path.join(self.default_r2env_path, "versions", package_name)))
def test_list_available_packages(self):
assert_that(self.package_manager.list_available_packages(), equal_to(self.profiles))
@patch("r2env.package_manager.os")
def test_list_installed_packages(self, os_mock):
packages = ["PKG1", "PKG2", "PKG3"]
os_mock.path.isdir.return_value = True
os_mock.listdir.return_value = packages
assert_that(self.package_manager.list_installed_packages(), equal_to(packages))
@patch("r2env.package_manager.os.makedirs")
@patch("r2env.package_manager.os.path.isdir")
def test_install_package_with_no_log_path(self, mock_isdir, mock_makedirs):
patch("r2env.package_manager.PackageManager._build_from_source").start()
patch("r2env.package_manager.PackageManager._install_from_dist").start()
mock_isdir.side_effect = [False, True, True]
self.package_manager.install_package(self.profile, self.version, True, True)
mock_makedirs.assert_called_once()
@patch("r2env.package_manager.os.makedirs")
@patch("r2env.package_manager.os.path.isdir")
def test_install_package_with_no_source_path(self, mock_isdir, mock_makedirs):
patch("r2env.package_manager.PackageManager._build_from_source").start()
patch("r2env.package_manager.PackageManager._install_from_dist").start()
mock_isdir.side_effect = [True, False, True]
self.package_manager.install_package(self.profile, self.version, True, True)
source_path = os.path.join(os.path.join(self.package_manager._r2env_path,
self.package_manager.SOURCE_DIR, self.profile))
mock_makedirs.assert_called_once_with(source_path)
@patch("r2env.package_manager.os.makedirs")
@patch("r2env.package_manager.os.path.isdir")
def test_install_package_with_no_dstdir(self, mock_isdir, mock_makedirs):
patch("r2env.package_manager.PackageManager._build_from_source").start()
patch("r2env.package_manager.PackageManager._install_from_dist").start()
mock_isdir.side_effect = [True, True, False]
self.package_manager.install_package(self.profile, self.version, True, True)
dst_dir = os.path.join(self.package_manager._r2env_path,
self.package_manager.PACKAGES_DIR,
f"{self.profile}@{self.version}")
mock_makedirs.assert_called_once_with(dst_dir)
@patch("r2env.package_manager.PackageManager._install_from_dist")
@patch("r2env.package_manager.os.path.isdir")
def test_install_package_from_dist_success(self, mock_isdir,
mock_dist):
mock_isdir.return_value = True
mock_dist.return_value = True
self.package_manager.install_package(self.profile, self.version, True, True)
mock_dist.assert_called_once_with(self.profile, self.version)
@patch("r2env.package_manager.PackageManager._install_from_dist")
@patch("r2env.package_manager.os.path.isdir")
def test_install_package_from_dist_failed(self, mock_isdir,
mock_dist):
mock_isdir.return_value = True
mock_dist.return_value = False
with self.assertRaises(PackageManagerException):
self.package_manager.install_package(self.profile, self.version, True, True)
mock_dist.assert_called_once_with(self.profile, self.version)
@patch("r2env.package_manager.PackageManager._build_from_source")
@patch("r2env.package_manager.os.path.isdir")
def test_install_package_from_build_success(self, mock_isdir,
mock_build):
mock_isdir.return_value = True
mock_build.return_value = True
self.package_manager.install_package(self.profile, self.version, True, False)
mock_build.assert_called_once()
@patch("r2env.package_manager.shutil.rmtree")
@patch("r2env.package_manager.PackageManager._build_from_source")
@patch("r2env.package_manager.os.path.isdir")
def test_install_package_from_build_failed(self, mock_isdir,
mock_build, mock_rmtree):
mock_isdir.return_value = True
mock_build.return_value = False
with self.assertRaises(PackageManagerException):
self.package_manager.install_package(self.profile, self.version, True, False)
mock_build.assert_called_once()
mock_rmtree.assert_called_once()
@patch("r2env.package_manager.PackageManager.list_installed_packages")
@patch("r2env.package_manager.PackageManager._uninstall_from_dist")
def test_uninstall_package_from_dist_successfully(self, mock_uninstall, mock_list_pkg):
mock_list_pkg.return_value = ["[email protected]", "[email protected]"]
profile = "radare2"
version = "2.0.0"
package = f"{profile}@{version}"
self.package_manager.uninstall_package(package, True)
mock_uninstall.assert_called_once_with(profile, version)
@patch("r2env.package_manager.PackageManager.list_installed_packages")
@patch("r2env.package_manager.shutil.rmtree")
def test_uninstall_package_from_build_successfully(self, mock_rmtree, mock_list_pkg):
mock_list_pkg.return_value = ["[email protected]", "[email protected]"]
package = "[email protected]"
self.package_manager.uninstall_package(package, False)
pkg_dir = os.path.join(self.package_manager._r2env_path,
self.package_manager.PACKAGES_DIR,
package)
mock_rmtree.assert_called_once_with(pkg_dir)
@patch("r2env.package_manager.PackageManager.list_installed_packages")
@patch("r2env.package_manager.shutil.rmtree")
def test_uninstall_package_from_build_failed(self, mock_rmtree, mock_list_pkg):
mock_list_pkg.return_value = ["[email protected]", "[email protected]"]
package = "[email protected]"
mock_rmtree.side_effect = OSError("fail")
with self.assertRaises(PackageManagerException):
self.package_manager.uninstall_package(package, False)
@patch("r2env.package_manager.PackageManager.list_installed_packages")
def test_uninstall_package_not_found(self, mock_list_pkg):
mock_list_pkg.return_value = ["[email protected]"]
package = "[email protected]"
with self.assertRaises(PackageManagerException):
self.package_manager.uninstall_package(package, False)
def test_get_pkgname_success(self):
target_pkg = "radare2-5.3.1-w64.zip"
target_os = "w64"
target_version = "5.3.1"
packages_mock = {"radare2": {
"versions": [
{
"id": "5.3.0",
"packages": {
"w64": "radare2-5.3.0-w64.zip",
}
},
{
"id": target_version,
"packages": {
target_os: target_pkg,
"mac_x64": "radare2-5.3.1.pkg",
}
}]
}}
self.package_manager._packages = packages_mock
assert_that(self.package_manager._get_pkgname("radare2", target_version, target_os), equal_to(target_pkg))
def test_get_pkgname_failed(self):
target_os = "w64"
target_version = "5.3.1"
packages_mock = {"radare2": {
"versions": [
{
"id": "5.3.0",
"packages": {
"w64": "radare2-5.3.0-w64.zip",
}
},
]
}}
self.package_manager._packages = packages_mock
assert_that(self.package_manager._get_pkgname("radare2", target_version, target_os), equal_to(None))
@patch("r2env.package_manager.PackageManager._get_pkgname")
def test_get_disturl_success(self, mock_getpkg):
target_pkg = "radare2-5.3.1-w64.zip"
version = "5.2.0"
mock_getpkg.return_value = target_pkg
dist_url = self.package_manager._get_disturl("mock_profile", version, "mock_cos")
expected_url = "/".join([self.package_manager.DOWNLOAD_URL, version, target_pkg])
assert_that(dist_url, equal_to(expected_url))
@patch("r2env.package_manager.PackageManager._get_pkgname")
def test_get_disturl_failed(self, mock_getpkg):
target_pkg = None
mock_getpkg.return_value = target_pkg
dist_url = self.package_manager._get_disturl("mock_profile", "mock_version", "mock_cos")
assert_that(dist_url, equal_to(None))
@patch("r2env.package_manager.wget")
@patch("r2env.package_manager.PackageManager._get_pkgname")
@patch("r2env.package_manager.PackageManager._get_disturl")
@patch("r2env.package_manager.host_distname")
def test_install_from_dist_wget_is_called(self, mock_host_distname,
mock__get_disturl,
mock__get_pkgname,
mock_wget):
patch("r2env.package_manager.os.system").start()
pkgname = "radare2-5.3.1-w64.zip"
disturl = "http://example.org"
pkgfile = "/".join([self.package_manager._r2env_path, "src", pkgname])
mock__get_pkgname.return_value = pkgname
mock__get_disturl.return_value = disturl
mock_host_distname.return_value = "mac_arm64"
profile = "radare2"
version = "5.6.0"
self.package_manager._install_from_dist(profile, version)
mock_wget.download.assert_called_once_with(disturl, pkgfile)
@patch("r2env.package_manager.wget")
def test_install_from_dist_wget_error_url(self, mock_wget):
pkgname_mock = patch("r2env.package_manager.PackageManager._get_pkgname").start()
get_disturl_mock = patch("r2env.package_manager.PackageManager._get_disturl").start()
pkgname_mock.return_value = "radare2-5.3.1-w64.zip"
get_disturl_mock.return_value = "http://example.org"
mock_wget.download.side_effect = URLError("error")
with self.assertRaises(PackageManagerException):
self.package_manager._install_from_dist("profile", "version")
@patch("r2env.package_manager.wget")
def test_install_from_dist_wget_error_connection(self, mock_wget):
pkgname_mock = patch("r2env.package_manager.PackageManager._get_pkgname").start()
get_disturl_mock = patch("r2env.package_manager.PackageManager._get_disturl").start()
pkgname_mock.return_value = "radare2-5.3.1-w64.zip"
get_disturl_mock.return_value = "http://example.org"
mock_wget.download.side_effect = ConnectionResetError("error")
with self.assertRaises(PackageManagerException):
self.package_manager._install_from_dist("profile", "version")
@patch("r2env.package_manager.wget")
def test_install_from_dist_wget_error_connection(self, mock_wget):
pkgname_mock = patch("r2env.package_manager.PackageManager._get_pkgname").start()
get_disturl_mock = patch("r2env.package_manager.PackageManager._get_disturl").start()
pkgname_mock.return_value = "radare2-5.3.1-w64.zip"
get_disturl_mock.return_value = "http://example.org"
mock_wget.download.side_effect = ConnectionResetError("error")
with self.assertRaises(PackageManagerException):
self.package_manager._install_from_dist("profile", "version")
@patch("r2env.package_manager.os.system")
@patch("r2env.package_manager.PackageManager._get_pkgname")
@patch("r2env.package_manager.PackageManager._get_disturl")
@patch("r2env.package_manager.host_distname")
def test_install_from_dist_mac(self, mock_host_distname,
mock__get_disturl,
mock__get_pkgname,
mock_system):
pkgname = "radare2-5.3.1-mac.pkg"
pkgfile = "/".join([self.package_manager._r2env_path, "src", pkgname])
mock__get_pkgname.return_value = pkgname
mock__get_disturl.return_value = "http://example.org"
mock_host_distname.return_value = "mac_arm64"
profile = "radare2"
version = "5.6.0"
patch("r2env.package_manager.wget.download").start()
mock_system.return_value = 0
assert_that(self.package_manager._install_from_dist(profile, version), equal_to(True))
mock_system.assert_called_once_with(f"sudo installer -pkg {pkgfile} -target /")
@patch("r2env.package_manager.os.system")
@patch("r2env.package_manager.PackageManager._get_pkgname")
@patch("r2env.package_manager.PackageManager._get_disturl")
@patch("r2env.package_manager.host_distname")
def test_install_from_dist_linux_deb(self, mock_host_distname,
mock__get_disturl,
mock__get_pkgname,
mock_system):
pkgname = "radare2-5.3.1-linux.deb"
pkgfile = "/".join([self.package_manager._r2env_path, "src", pkgname])
mock__get_pkgname.return_value = pkgname
mock__get_disturl.return_value = "http://example.org"
mock_host_distname.return_value = "deb_x64"
profile = "radare2"
version = "5.6.0"
patch("r2env.package_manager.wget.download").start()
mock_system.return_value = 0
assert_that(self.package_manager._install_from_dist(profile, version), equal_to(True))
mock_system.assert_called_once_with(f"sudo dpkg -i {pkgfile}")
@patch("r2env.package_manager.os.system")
@patch("r2env.package_manager.PackageManager._get_pkgname")
@patch("r2env.package_manager.PackageManager._get_disturl")
@patch("r2env.package_manager.host_distname")
def test_install_from_dist_mac_os_not_supported(self, mock_host_distname,
mock__get_disturl,
mock__get_pkgname,
mock_system):
mock__get_pkgname.return_value = "radare2-5.3.1-mac.zip"
mock__get_disturl.return_value = "http://example.org"
mock_host_distname.return_value = "not_existing_so"
patch("r2env.package_manager.wget.download").start()
with self.assertRaises(PackageManagerException):
self.package_manager._install_from_dist("radare2", "5.6.0")
mock_system.assert_not_called()
@patch("r2env.package_manager.PackageManager._uninstall_from_dist")
@patch("r2env.package_manager.zipfile.ZipFile")
def test_install_windows_package_file_not_found(self, zipfile_mock, uninstall_mock):
zipfile_mock.extractall.side_effect = FileNotFoundError()
with self.assertRaises(PackageManagerException):
self.package_manager._install_windows_package("radare2")
uninstall_mock.assert_called_once()
@patch("r2env.package_manager.os.path.isdir")
@patch("r2env.package_manager.os.makedirs")
def test_install_windows_package_create_dst_folder(self, makedirs_mock, isdir_mock):
patch("r2env.package_manager.shutil.copyfile").start()
patch("r2env.package_manager.zipfile").start()
isdir_mock.return_value = False
patch("r2env.package_manager.shutil.copyfile").start()
self.package_manager._install_windows_package("radare2")
makedirs_mock.assert_called_once_with(os.path.join(self.package_manager._r2env_path, "bin"), 493)
@patch("r2env.package_manager.os.path.isdir")
def test_install_windows_package_successfully(self, isdir_mock):
with patch("r2env.package_manager.zipfile.ZipFile") as mock_ZipFile:
mock_zip = MagicMock()
mock_zip.return_value = ["1", "2", "3"]
mock_ZipFile.return_value = mock_zip
mock_copy = patch("r2env.package_manager.shutil.copyfile").start()
isdir_mock.return_value = True
assert_that(self.package_manager._install_windows_package("radare2"), equal_to(True))
mock_copy.assert_called_once()
@patch("r2env.package_manager.host_distname")
def test_uninstall_from_dist_windows(self, mock_distname):
mock_distname.return_value = "w64"
mock_rmtree = patch("r2env.package_manager.shutil.rmtree").start()
dst_folder = os.path.join(self.package_manager._r2env_path, "bin")
assert_that(self.package_manager._uninstall_from_dist("radare2", "1.0.0"), equal_to(True))
mock_rmtree.assert_called_once_with(dst_folder)
@patch("r2env.package_manager.host_distname")
def test_uninstall_from_dist_mac(self, mock_distname):
mock_distname.return_value = "mac_arm64"
mock_system = patch("r2env.package_manager.os.system").start()
mock_system.side_effect = [0,0,0,0]
assert_that(self.package_manager._uninstall_from_dist("radare2", "1.0.0"), equal_to(True))
@patch("r2env.package_manager.host_distname")
def test_uninstall_from_dist_mac_failed(self, mock_distname):
mock_distname.return_value = "mac_arm64"
mock_system = patch("r2env.package_manager.os.system").start()
mock_system.side_effect = [0,1,0,0]
assert_that(self.package_manager._uninstall_from_dist("radare2", "1.0.0"), equal_to(False))
@patch("r2env.package_manager.host_distname")
def test_uninstall_from_dist_linux(self, mock_distname):
mock_distname.return_value = "deb_x64"
mock_system = patch("r2env.package_manager.os.system").start()
mock_system.return_value = 0
assert_that(self.package_manager._uninstall_from_dist("radare2", "1.0.0"), equal_to(True))
mock_system.assert_called_once_with("sudo dpkg -r radare2")
@patch("r2env.package_manager.host_distname")
def test_uninstall_from_dist_os_not_supported(self, mock_distname):
mock_distname.return_value = "non_existent"
with self.assertRaises(PackageManagerException):
self.package_manager._uninstall_from_dist("radare2", "1.0.0")
@patch("r2env.package_manager.host_distname")
def test_acr_not_supported_on_windows(self, mock_distname):
mock_distname.return_value = "w64"
with self.assertRaises(PackageManagerException):
self.package_manager._build_using_acr("source", "dest", "logfile", "r2env")
View File
Whitespace-only changes.
-55
View File
@@ -1,55 +0,0 @@
from mock import patch
import unittest
from r2env.repl import REPL
from r2env.exceptions import ActionException
class MockActions:
def __init__(self):
self.args = []
self.version = False
self.list = False
class TestRepl(unittest.TestCase):
def setUp(self):
self.repl = REPL()
def tearDown(self):
patch.stopall()
def test_invalid_run_with_no_action(self):
with self.assertRaises(ActionException):
self.repl.run_action(None)
no_action = MockActions()
with self.assertRaises(ActionException):
self.repl.run_action(no_action)
@patch("r2env.repl.R2Env.list_packages")
@patch("r2env.repl.R2Env.list_installed_packages")
def test_invalid_run_action_with_no_arguments(self,mock_list_installed_packages, mock_list_packages):
use_action = MockActions()
use_action.args.append("use")
with self.assertRaises(ActionException):
self.repl.run_action(use_action)
mock_list_installed_packages.assert_called()
install_action = MockActions()
install_action.args.append("install")
with self.assertRaises(ActionException):
self.repl.run_action(install_action)
mock_list_packages.assert_called()
@patch("r2env.repl.REPL.show_help")
def test_print_help(self, mock_help):
help_action = MockActions()
with self.assertRaises(ActionException):
self.repl.run_action(help_action)
mock_help.assert_called()
def test_invalid_action(self):
invalid_action = MockActions()
invalid_action.args.append("invalid_action")
with self.assertRaises(ActionException):
self.repl.run_action(invalid_action)
+31 -40
View File
@@ -2,16 +2,14 @@
import json
import os
import sys
from shutil import which
import git
from colorama import Fore, Style
from git import Repo
from r2env.exceptions import PackageManagerException
# Global Vars
INFO = 0
WARNING = 1
ERROR = 2
@@ -33,53 +31,45 @@ def print_console(msg, level=INFO, formatter=0):
def host_platform():
if os.name == "nt":
return "windows"
return "w64"
if os.path.isfile("/default.prop"):
return "android"
if os.uname().sysname == "Darwin":
return "osx"
return "unix"
# TODO :improve to support all archs
def host_distname(): # noqa: C901
dist_name = None
def host_distname():
try:
sysname = os.uname().sysname
machine = os.uname().machine
except AttributeError:
sysname = "Windows"
except:
sysname = "w64"
machine = "amd64"
if sysname in "Windows" and os.name in "nt":
dist_name = "w64"
elif sysname in "Darwin":
if machine in "x86_64":
dist_name = "mac_x64"
if machine in "arm64":
dist_name = "mac_arm64"
elif sysname == "Linux":
if os.path.exists("/usr/bin/dpkg"):
if machine == "x86_64":
dist_name = "deb_x64"
else:
dist_name = "deb_i386"
if sysname == "Darwin":
if machine == "x86_64":
return "mac_x64"
if machine == "arm64":
return "mac_arm64"
return None
if sysname == "Linux":
if not os.path.exists("/usr/bin/dpkg"):
return None
if machine == "x86_64":
return "deb_x64"
return "deb_i386"
if os.name == "nt":
return "w64"
if os.path.isfile("/default.prop"):
dist_name = "android"
return dist_name
return "android"
return None
def git_fetch(url, version, source_path):
try:
if os.path.isdir(os.path.join(source_path, ".git")):
repo = Repo(source_path)
repo.git.checkout("master")
repo.remotes.origin.pull("master")
else:
repo = Repo.clone_from(url, source_path)
except git.GitCommandError as err:
raise PackageManagerException("An error occured clonning the repo. "
f"Verify the source path is not dirty. Error: {err}") \
from err
if os.path.isdir(os.path.join(source_path, ".git")):
repo = Repo(source_path)
repo.remotes.origin.pull("master")
else:
repo = Repo.clone_from(url, source_path)
if version != "git":
repo.git.checkout(version)
sms = repo.submodules
@@ -100,13 +90,14 @@ def exists(tool):
def exit_if_not_exists(tools):
for tool in tools:
if not exists(tool):
raise PackageManagerException(f"[x] {tool} is required. Please install it first")
print_console("[x] {} is required. Please install it first", level=ERROR)
sys.exit(-1)
def load_json_file(filepath):
try:
with open(filepath, encoding="utf-8") as json_file:
with open(filepath) as json_file:
return json.load(json_file)
except OSError as err:
print_console(f"File {filepath} not found. Msg: {err}", ERROR)
print_console("File {} not found. Msg: {}".format(filepath, err), ERROR)
return None
+1 -1
View File
@@ -1 +1 @@
0.5.6
0.4.2
-1
View File
@@ -1,5 +1,4 @@
dploy>=0.1.2
colorama>=0.4.4
wget
requests
GitPython
+1 -1
View File
@@ -7,4 +7,4 @@ mock
pytest
coverage
doublex
tox
tox
Executable → Regular
+1 -1
View File
@@ -6,7 +6,7 @@ readlink=$(which greadlink 2>/dev/null) || readlink=$(which readlink)
base_dir=$(cd $(dirname $(${readlink} -f "$0")); pwd)
echo "Cleaning *.pyc files ..."
find . -name \*.pyc | xargs rm 2>/dev/null
find $base_dir -name \*.pyc | xargs rm 2>/dev/null
python -m coverage run -m pytest -v -rsx -k "test_" --ignore=bin/
ecode=$?
-1
View File
@@ -35,7 +35,6 @@ install_requires=
dploy >= 0.1.2
colorama >= 0.4.4
GitPython
requests
setup_requires =
setuptools_scm >= 1.15
include_package_data = True
+1 -4
View File
@@ -5,8 +5,5 @@ from setuptools import setup
setup(
setup_requires=['pbr'],
pbr=True,
package_files=[
('config', ['r2env/config/config.json']),
('', ['version.txt'])
]
package_files=[('config', ['r2env/config/config.json', 'r2env/config/profiles.json'])]
)
+5 -6
View File
@@ -1,16 +1,15 @@
[tox]
envlist = py{38,39,310}-{tests},{py38,py39,310}-codecheck-{pylint,flake8,bandit}
envlist = py{38,39}-{tests},{py38,py39}-codecheck-{pylint,flake8,bandit}
skipsdist = True
[testenv]
allowlist_externals = bash
deps = -r {toxinidir}/requirements/test.txt
passenv = *
commands =
py{38,39,310}-tests: bash {toxinidir}/run_tests.sh unit
py{38,39,310}-codecheck-pylint: pylint r2env
py{38,39,310}-codecheck-flake8: flake8 r2env
py{38,39,310}-codecheck-bandit: bandit -c .bandit.yml -r r2env
py{38,39}-tests: {toxinidir}/run_tests.sh unit
py{38,39}-codecheck-pylint: pylint r2env
py{38,39}-codecheck-flake8: flake8 r2env
py{38,39}-codecheck-bandit: bandit -c .bandit.yml -r r2env
[flake8]
# to ignore complete files, add to the file: # flake8: noqa