Compare commits

...
109 Commits
Author SHA1 Message Date
pancake daeba4ebcf Release 0.5.9 2024-08-31 00:03:48 +02:00
Eli Array Minkoff 5578bee417 Attempt to pull new tags if fetching a tag fails 2024-05-08 00:48:38 +02:00
pancake ab6cce7008 Release 0.5.8 2024-04-30 19:13:06 +02:00
pancake d501129500 Add r2yara for the profiles 2024-04-30 19:12:27 +02:00
pancake 51c1b2ca2c Do not register as installed packages that doesn't exist 2024-04-30 18:59:41 +02:00
pancake 743812b653 More checks 2024-04-30 17:18:34 +02:00
pancake ac3b695012 Add python -m r2env 2024-04-30 17:08:29 +02:00
pancake 00a3db509c Check for a valid package name 2024-04-30 17:07:48 +02:00
pancake ccd7231065 Use updated twine pub oneliner 2024-04-30 16:56:45 +02:00
semgrep.dev on behalf of @trufae 3b8716ea1d Add Semgrep CI 2023-03-29 10:36:34 +00:00
Eli Array Minkoff d6b40cf4b8 Fix pylint complaints 2023-03-03 21:02:07 +01:00
Eli Array Minkoff a4d678511e refactor PackageManager/update_radare2_profiles 2023-03-03 21:02:07 +01:00
Eli Array Minkoff 658ede86d9 Reverse order of radare2 provile versions
This ensures that the order remains in ascending order
2023-03-03 21:02:07 +01:00
Eli Array Minkoff d8fc2f8b71 Remove bundled profiles.json file
No longer used
2023-03-03 21:02:07 +01:00
Eli Array Minkoff 57398126f3 Update help text in README 2023-03-03 21:02:07 +01:00
Eli Array Minkoff f7e211b014 Use GitHub API to generate profiles.json 2023-03-03 21:02:07 +01:00
Murphy 3873eb48a7 Update tox.ini py-test job 2023-03-03 20:57:10 +01:00
Murphy c5ba226b21 Update tox.ini 2023-03-03 20:57:10 +01:00
Lazula 88e68daef5 Fix None in string error from host_distname() on unsupported systems 2023-03-02 18:48:47 +01:00
Murphy 2468813a3c Update version 2022-12-29 17:16:17 +01:00
pancake 223af1e85d Add 5.8.0 to the profile 2022-12-29 13:12:59 +01:00
Murphy 0ef5ff2d72 Bump version 2022-09-20 20:33:44 +02:00
pancake 2f937f67f1 Add profiles for 576 and 578 2022-09-20 16:46:35 +02:00
pancake 4380456e1d Make pip path configurable 2022-09-20 16:46:29 +02:00
pancake 59c9445d10 Add 5.7.4 packages 2022-07-07 10:53:07 +02:00
Murphy 713c18feb4 Add r2 5.7.2 profile 2022-07-01 12:24:37 +02:00
Murphy 670de20b01 Add r2 5.7.0 profile 2022-06-07 12:35:59 +02:00
Murphy 019e941dfa Update to v0.5.2 2022-04-28 18:25:51 +02:00
pancake 26925a29d6 Update for 5.6.8 2022-04-19 17:09:31 +02:00
Murphy 1a1f3fcde5 Fixing tests 2022-03-25 21:33:56 +01:00
Murphy 0c838e7d08 Fixing lints 2022-03-25 21:33:56 +01:00
Murphy b3852a92d3 Adding 5.6.6 profile 2022-03-25 21:33:56 +01:00
Murphy 6f81459f70 Raising an error using Windows with ACR 2022-03-25 21:33:56 +01:00
Murphy 3f16036689 Use git if version not defined 2022-03-25 21:33:56 +01:00
Murphy 00782ae62f Fix shell command using arguments 2022-03-25 21:33:56 +01:00
Murphy 93bd2b0e1e Skip C901 to run_action 2022-03-25 20:45:13 +01:00
Murphy 100dc1a9d0 Minor fix 2022-03-25 20:45:13 +01:00
Murphy e5677a03ae Fix version cmd 2022-03-25 20:45:13 +01:00
Murphy 35d769e73b Fix shell cmd with parameters 2022-03-25 20:45:13 +01:00
Murphy 587ce7ef96 Improve use and shell cmds 2022-03-25 20:45:13 +01:00
pancake edfe363401 Fix missing libs issue in meson builds on macos-sip targets 2022-03-25 20:39:59 +01:00
pancake 9d521d7f0e fix 2022-03-25 20:38:48 +01:00
pancake 2c0f8d48c9 Minor tweaks in the meson build function 2022-03-25 20:38:48 +01:00
Murphy d8a948c61b Fix tests 2022-03-15 11:36:37 +01:00
Murphy 374e93bf2b Raising exception when using pacakge not installed in use cmd 2022-03-15 11:36:37 +01:00
Murphy 9459ab1f72 checkout master when clonning a repo 2022-03-15 11:36:37 +01:00
Murphy 61b9bafdcb Fix some pylint lints 2022-03-15 11:36:37 +01:00
Murphy e7d1a24683 Fix minor issues 2022-03-15 11:36:37 +01:00
Murphy ac6a072246 Update profiles.json 2022-03-15 11:36:37 +01:00
Murphy ffa0f87d9c Add tests to package manager 2022-03-15 11:36:37 +01:00
Murphy bf35b36959 Refactor package manager 2022-03-15 11:36:37 +01:00
Murphy e6bcd7c4d4 Refactoring package manager 2022-03-15 11:36:37 +01:00
Murphy 25d26bb0f9 Add dependency to release-job 2022-03-15 11:36:37 +01:00
Murphy db54437130 Add unit tests to CI 2022-03-15 11:36:37 +01:00
Murphy 4ffe814c42 Fix some lints 2022-03-15 11:36:37 +01:00
Murphy 89272008ca Refactor package manager and add tests 2022-03-15 11:36:37 +01:00
Murphy a1b6363f26 Refactor r2env core and add tests 2022-03-15 11:36:37 +01:00
Murphy 47eeb599b6 Refactor r2env core and add tests 2022-03-15 11:36:37 +01:00
Murphy c89615d0fb Refactor repl and add tests 2022-03-15 11:36:37 +01:00
asoler e449e41971 Refactoring Repl 2022-03-15 11:36:37 +01:00
murphy 56426fd1f2 Fix some codechecks 2022-03-15 11:36:37 +01:00
murphy e27ce149b3 Update gitignore 2022-03-15 11:36:37 +01:00
murphy 5cb3f75f35 Add 5.5.2 version 2022-03-15 11:36:37 +01:00
Murphy 2200ddcb50 Create codeql-analysis.yml 2022-03-15 11:34:47 +01:00
Sergi Àlvarez i Capilla 50f444dab0 Release 0.4.2 2021-11-15 17:53:09 +01:00
pancake db4e174ecf Add r2-5.5.0 profile 2021-11-15 00:54:46 +01:00
pancake 9ada5d4322 Fetch submodules if any 2021-11-08 12:48:03 +01:00
pancake 47c5b9feab Initial Termux support 2021-09-27 16:44:02 +02:00
pancake e94f9c07af Release 0.4.1 2021-09-27 00:36:56 +02:00
pancake ef703be9fe Windows installation goes directly into the bin/ 2021-09-27 00:35:23 +02:00
pancake b0c2e38ece Initial support for Windows packages 2021-09-26 23:56:35 +02:00
pancake aa4a5170cd A couple more guards for os.uname 2021-09-26 23:24:40 +02:00
pancake 01e2b5efaa Update README 2021-09-26 23:23:01 +02:00
pancake d58b97085c os.uname() doesnt exist on windows 2021-09-26 23:20:57 +02:00
pancake e15c4249a3 Do not use ansi colors on Windows 2021-09-26 23:18:19 +02:00
pancake 2a7f1893f1 Dont polute cwd 2021-09-26 23:04:06 +02:00
pancake 378adc688a Improve help message 2021-09-26 23:00:37 +02:00
pancake 092eda37ea Add support for Debian -p add,rm 2021-09-26 22:58:33 +02:00
pancake 068f103a89 Fix some more lint hints 2021-09-26 22:36:11 +02:00
pancake 6c0c26aa0b Release 0.4.0 2021-09-26 22:27:08 +02:00
pancake 29fa15b555 Add help in the readme 2021-09-26 22:26:18 +02:00
pancake fdd5602970 Fixes for the CI 2021-09-26 21:38:16 +02:00
pancake 684d6d5211 Add more packages, export PKG_CONFIG_PATH and handle R2ENV_PATH 2021-09-26 21:36:12 +02:00
pancake bf6ff7d2db Fix README 2021-09-26 21:36:05 +02:00
pancake 30e319f3fb Defaults to git when no @version is specified 2021-09-26 21:03:59 +02:00
pancake 7c948fab24 Use DYLD_LIBRARY_PATH on macOS 2021-09-26 20:56:51 +02:00
pancake b9089e9b2d Argument handling issues fixed 2021-09-26 20:49:15 +02:00
pancake 0fa2cf4a87 Add r2frida package 2021-09-26 20:43:11 +02:00
pancake 35f1c4152e latest -> git 2021-09-26 20:35:09 +02:00
pancake 7ece62f905 Add more actions and unify the arguments for handling -p rm 2021-09-26 20:34:22 +02:00
pancake 4323295829 Improve output logs when installing packages 2021-09-26 20:11:45 +02:00
pancake 444974cf0a Add purge, honor -p install, latest -> git and more 2021-09-26 20:06:47 +02:00
pancake 9b60d72a55 Fix binary package removal for macOS 2021-09-26 19:48:18 +02:00
pancake c9e7123fad Improve commandline handling 2021-09-26 19:44:35 +02:00
pancake 48ca2997e9 Initial support for binary package installation on macOS 2021-09-26 18:57:35 +02:00
pancake 57db2d1f7a Add 5.4.2 packages 2021-09-23 18:26:29 +02:00
pancake 4d03c87bf1 Add 5.4.0 2021-08-22 02:25:39 +02:00
pancake eb7dfa643a There's nothing termux-specific in here 2021-07-26 17:40:42 +02:00
murphy f8fbc50505 Update CI 2021-06-21 18:27:33 +02:00
murphy 8606f42ceb Update versioning system 2021-06-21 18:09:06 +02:00
murphy a37787570a Update ci.yml 2021-06-21 18:02:20 +02:00
murphy 58a2e071ab Update ci.yml 2021-06-21 18:01:20 +02:00
murphy 96fd4aab0a Update ci.yml 2021-06-21 17:50:35 +02:00
murphy ff9fd6fa3d Update release process 2021-06-21 17:48:45 +02:00
murphy be5154450d Update release process 2021-06-21 17:36:56 +02:00
murphy 24bfde5f9b Update gitignore 2021-06-21 15:54:29 +02:00
murphy 129e96b506 Update release files 2021-06-21 15:54:20 +02:00
pancake 26919c415a More pypi massaging 2021-06-17 19:25:40 +02:00
pancake e9e08436cb Fix make pub and sync version (#13) 2021-06-17 19:08:14 +02:00
28 changed files with 1518 additions and 273 deletions

No files matched your search

BIN
View File
Binary file not shown.
+44 -4
View File
@@ -1,10 +1,14 @@
name: CI
on:
release:
types: [published]
push:
branches:
- master
pull_request:
branches:
- master
# schedule:
# - cron: '0 0 * * 1'
@@ -13,6 +17,7 @@ jobs:
pylint:
name: codecheck-pylint
runs-on: ubuntu-20.04
continue-on-error: true
steps:
- name: Checkout
uses: actions/checkout@v2
@@ -23,6 +28,7 @@ jobs:
flake8:
name: codecheck-flake8
runs-on: ubuntu-20.04
continue-on-error: true
steps:
- name: Checkout
uses: actions/checkout@v2
@@ -30,6 +36,17 @@ 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
@@ -38,15 +55,38 @@ jobs:
uses: actions/checkout@v2
- name: Install
run: sudo pip install . --force-reinstall
- name: Initializing
run: r2env init
- name: Execute r2env
run: r2env list
- name: Install r2 using r2env
run: |
r2env init
r2env install radare2@latest
- name: Execute latest r2 version
[ "`r2env | wc -l`" -gt 10 ] # measure help message
[ "`r2env -l | wc -l`" -gt 6 ] # measure pkgs
r2env install radare2@git
- name: Execute git version of r2
run: |
r2env use radare2@latest
r2env use radare2@git
$HOME/.r2env/bin/r2 -v
r2env shell "r2 -v"
- name: Uninstalling
run: sudo pip uninstall -y r2env && rm -rf $HOME/.r2env
release-job:
name: Publish package
runs-on: ubuntu-20.04
needs: [integration-tests, unit-tests, flake8, pylint]
if: github.event_name == 'release' && github.event.action == 'published'
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install Dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements/dist.txt
- name: Build
run: python setup.py sdist
- name: Publish
uses: pypa/gh-action-pypi-publish@release/v1
with:
user: __token__
password: ${{ secrets.TWINE_PASSWORD }}
+70
View File
@@ -0,0 +1,70 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: [ master ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
schedule:
- cron: '34 6 * * 0'
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: [ 'python' ]
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
# Learn more about CodeQL language support at https://git.io/codeql-language-support
steps:
- name: Checkout repository
uses: actions/checkout@v2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# queries: ./path/to/local/query, your-org/your-repo/queries@main
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
# ℹ️ Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
# and modify them (or add more) to build your code if your project
# uses a compiled language
#- run: |
# make bootstrap
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
+23
View File
@@ -0,0 +1,23 @@
on:
pull_request: {}
push:
branches:
- main
- master
paths:
- .github/workflows/semgrep.yml
schedule:
# random HH:MM to avoid a load spike on GitHub Actions at 00:00
- cron: 34 10 * * *
name: Semgrep
jobs:
semgrep:
name: Scan
runs-on: ubuntu-20.04
env:
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
container:
image: returntocorp/semgrep
steps:
- uses: actions/checkout@v3
- run: semgrep ci
+6
View File
@@ -1,3 +1,9 @@
.coverage
.idea
**/__pycache__/
venv
.DS_Store
AUTHORS
ChangeLog
dist/*
r2env.egg-info
+2
View File
@@ -4,6 +4,8 @@ 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
+9 -4
View File
@@ -1,15 +1,20 @@
PYTHON?=python3
PIP?=pip3
all:
pip install .
$(PIP) install .
pub:
twine upload --repository-url https://upload.pypi.org/legacy/ --verbose dist/*
rm -rf dist
$(PYTHON) setup.py sdist
twine check dist/*
twine upload -u __token__ --repository-url https://upload.pypi.org/legacy/ --verbose dist/*
#twine upload --repository=pypi --verbose dist/*
install:
pip install .
$(PIP) install .
uninstall:
pip uninstall r2env
$(PIP) uninstall r2env
.PHONY: all clean pub install uninstall
+57 -42
View File
@@ -1,53 +1,29 @@
r2env
=====
# r2env
[![CI](https://github.com/radareorg/r2env/actions/workflows/ci.yml/badge.svg)](https://github.com/radareorg/r2env/actions/workflows/ci.yml)
This repository contains the tool available via pip to install
and manage multiple versions of radare2 and its plugins.
Create self-contained environments with multiple versions of radare2
and plugins, make it easy to switch between them.
r2-tools doesn't conflict with r2pm in the sense that it's not
going to support all the packages and plugins and it's
* Build from source or install the system package if available.
r2env supports source and binary packages.
## Installation
Dependencies
------------
r2env is available via PIP.
* r2env should be self contained
* `pip install -U -f r2env`
Some tools may be used if installed in the system, making some
packages available, but we may try to use python-version if
possible, so it don't depend on system packages.
If you want to try it directly from source.
Package description
-------------------
INI, JSON or python class, loaded at runtime, but it is k=v stuff
* `pip install .`
* name: iaito
* platform: windows
* version: 5.2.1
* source: https://path/to/zip
## Usage
Actions
-------
* install / uninstall
* link / unlink
* update - pip install -U r2env
* upgrade - upgrade r2 and all the deps in sync
First we need to initialize the working directory:
How to install
--------------
r2env is available via PIP. (`pip install r2env`)
To build + install from source just run: `make`
Usage
-----
First of all you may want to initialize the `.r2env` directory somewhere with `cd .. ; r2env init`.
```
r2env init
```
Listing available packages is done via `r2env list`.
@@ -56,18 +32,57 @@ Source packages are managed with the `add`, `rm`, `use` and `unused`.
For example: Installing radare2 (assumes @git version)
```
cd /tmp
r2env init
r2env add radare2
r2env use radare2@git
r2env shell r2 -v
r2env shell "r2 -v"
```
r2pm vs r2env
-------------
## Help
```
Usage: r2env [-flags] [action] [args...]
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
Actions:
init - create ~/.r2env directory
config - display current .r2env settings
add [pkg] - build and install given package. See -p and -m
use [pkg] - use r2 package defined. pkg should be a release version or git.
rm [pkg] - remove package from ~/.r2env
path - show path of current r2 in use
version - show version of r2env
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
R2ENV_PATH - specify different path other than ~/.r2env
```
## r2pm vs r2env
r2env aims to provide a packaging for r2 with support for binary packages and for all major platforms.
r2pm focus on providing more packages, it's written in shellscript, so it doesnt run on windows and requires r2 to work.
Therefor r2env is kind of `nvm` from nodejs or `pyenv` from python. A way to run multiple different versions of r2 in the system.
This repository contains the tool available via pip to install
and manage multiple versions of radare2 and its plugins.
r2-tools doesn't conflict with r2pm in the sense that it's not
going to support all the packages and plugins and it's
+3
View File
@@ -0,0 +1,3 @@
from r2env.repl import main
main()
-33
View File
@@ -1,33 +0,0 @@
{
"radare0": {
"source": "https://github.com/radareorg/r0",
"versions": [
{
"id": "latest",
"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:": {
}
},
{
"id": "latest",
"packages:": {}
}
]
}
}
+91 -49
View File
@@ -1,14 +1,17 @@
# -*- coding: utf-8 -*-
import os
import re
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
from r2env.tools import print_console, load_json_file, WARNING, ERROR, host_platform
class R2Env:
@@ -23,114 +26,153 @@ class R2Env:
def init(self):
if os.path.isdir(self._r2env_path):
print_console("[x] r2env already initialized. Path {} already exists.".format(self._r2env_path), ERROR)
print_console(f"[x] r2env already initialized. Path {self._r2env_path} already exists.", ERROR)
return
os.mkdir(self._r2env_path)
print_console("[*] r2env initialized at {}".format(self._r2env_path))
print_console(f"[*] r2env initialized at {self._r2env_path}")
def get_r2_path(self):
self.exit_if_r2env_not_initialized()
self.check_if_r2env_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(" [*] {0} ({1})".format(r2_version, r2_path))
print_console(f" [*] {r2_version} ({r2_path})")
def show_config(self):
print_console("[*] Current r2env config:")
print_console(json.dumps(self._config))
def list_packages(self):
self.list_available_packages()
self.list_installed_packages()
def list_available_packages(self):
print_console("[*] List of available Radare packages")
print_console("- Available")
packages = self._package_manager.list_available_packages()
for profile in packages:
print_console(" - {} packages:".format(profile), WARNING)
print_console(f" - {profile}:", WARNING)
for version in packages[profile]['versions']:
print_console(" {}@{}".format(profile, version['id']), formatter=1)
dists = ", ".join(version['packages'].keys())
print_console(f" - {profile}@{version['id']} - {dists}", formatter=1)
def list_installed_packages(self):
self.exit_if_r2env_not_initialized()
print_console("[*] Installed Radare packages")
self.check_if_r2env_initialized()
print_console("- Installed")
for pkg in self._package_manager.list_installed_packages():
if pkg == self._get_current_version():
print_console(" [*] {}".format(pkg))
print_console(f" - {pkg} (in use)")
else:
print_console(" - {}".format(pkg))
print_console(f" - {pkg}")
def install(self, package, use_meson=False):
self.exit_if_r2env_not_initialized()
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"
if not self._check_package_format(package):
print_console("[x] Invalid Package format.", level=ERROR)
return
profile, version = package.split('@')
self._package_manager.install_package(profile, version, use_meson=use_meson)
print_console("[*] Magic Done! Remember to add the $HOME/.r2env/bin folder to your PATH.")
raise R2EnvException("[x] Invalid package format.")
try:
profile, version = package.split('@')
except ValueError:
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):
self.exit_if_r2env_not_initialized()
def uninstall(self, package, **kwargs):
self.check_if_r2env_initialized()
use_dist = kwargs["use_dist"] if "use_dist" in kwargs else False
if not self._check_package_format(package):
print_console("[x] Invalid Package format.", level=ERROR)
return
self._package_manager.uninstall_package(package)
raise R2EnvException("[x] Invalid package format.")
self._package_manager.uninstall_package(package, use_dist)
def use(self, package=None):
self.exit_if_r2env_not_initialized()
def purge(self):
print_console(f"[*] Removing {self._r2env_path}")
shutil.rmtree(self._r2env_path)
def use(self, package=None, **_kwargs):
self.check_if_r2env_initialized()
if not package:
print_console("[x] Package not defined. Please use 'r2env use' with one installed package. ", ERROR)
self.list_installed_packages()
return
raise R2EnvException("Package not defined.")
if package not in self._package_manager.list_installed_packages():
raise R2EnvException(f"Package {package} not installed.")
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("[*] Using {} package".format(package))
print_console(f"[*] Using {package} package")
@staticmethod
def version():
thispath = os.path.dirname(os.path.realpath(__file__))
with open(thispath + "/version.txt", "r") as version:
return version.read()
with open(f"{thispath}/version.txt", "r", encoding="utf-8") as version:
print_console(version.read())
def shell(self, cmd=""):
line = "export PS1=\"r2env\\$ \";export PATH=\"" + 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 cmd.strip() == "":
return os.system(line)
print(os.system(line + " -c '" + cmd + "'"))
return os.system(line + " -c '" + cmd + "'")
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")
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
@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
if not config:
sys.exit(-1)
config = {}
return config
@staticmethod
def _get_default_path():
# TODO: try to find an .r2env starting in current directory
return os.path.join(Path.home(), ".r2env")
@staticmethod
def _check_package_format(package):
regexp = re.compile(r"\w+\d@(?:\d\.\d\.\d|latest)")
return regexp.match(package)
regexp = r"[a-z0-9]+@(\d\.\d\.\d|git)"
if not package:
return False
return re.search(regexp, package) is not None
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') as file_desc:
with open(version_file, 'r', encoding="utf-8") 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') as file_desc:
with open(version_file, 'w', encoding="utf-8") as file_desc:
file_desc.write(package)
def exit_if_r2env_not_initialized(self):
def update_version_profiles(self):
PackageManager.update_radare2_profiles(self._r2env_path)
def check_if_r2env_initialized(self):
if not os.path.isdir(self._r2env_path):
print_console("Not r2env initialized. Execute 'r2env init' first.", ERROR)
sys.exit(-1)
raise R2EnvException("Not r2env initialized. Execute 'r2env init' first.")
+10
View File
@@ -0,0 +1,10 @@
class R2EnvException(Exception):
pass
class ActionException(R2EnvException):
pass
class PackageManagerException(R2EnvException):
pass
+261 -48
View File
@@ -2,20 +2,26 @@
import os
import shutil
import sys
import time
from urllib.error import URLError
import zipfile
import json
from r2env.tools import load_json_file, git_fetch, git_clean, print_console, ERROR, exit_if_not_exists
import wget
import requests
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()
self._packages = self._load_profiles(r2env_path)
self._r2env_path = r2env_path
def get_package_path(self, package_name):
@@ -34,90 +40,297 @@ class PackageManager:
return res
@staticmethod
def _load_profiles():
filename = os.path.join(os.path.dirname(__file__), 'config', 'profiles.json')
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)
profiles = load_json_file(filename)
if not profiles:
sys.exit(-1)
raise PackageManagerException("No profiles configured.")
return profiles
def install_package(self, profile, version, use_meson):
@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': {}}]
},
'r2yara': {
'source': 'https://github.com/radareorg/r2yara',
'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)
logfile = os.path.join(log_path, "{}_{}_{}_build.txt".format(profile, version, round(time.monotonic() * 1000)))
timestamp = round(time.monotonic() * 1000)
logfile = os.path.join(log_path, f"{profile}_{version}_{timestamp}_build.txt")
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), "{}@{}".format(profile, version))
dst_dir = os.path.join(os.path.join(self._r2env_path, self.PACKAGES_DIR), f"{profile}@{version}")
if not os.path.isdir(log_path):
os.makedirs(log_path)
if not os.path.isdir(source_path):
os.makedirs(source_path)
if not os.path.isdir(dst_dir):
os.makedirs(dst_dir)
self._exit_if_package_not_available(profile, version)
if self._build_from_source(profile, version, source_path, dst_dir, logfile, use_meson=use_meson):
print_console("[*] Package {}@{} installed successfully".format(profile, version))
else:
print_console("[x] Something wrong happened during the build process. Check {} for more information.".format
(logfile), level=ERROR)
if self._if_package_not_available(profile, version):
if os.path.isdir(dst_dir):
shutil.rmtree(dst_dir)
raise PackageManagerException(
f"{profile}@{version} package not available. Use 'list' command to see available packages.")
if use_dist:
if self._install_from_dist(profile, version):
print_console(f"[*] Binary package {profile}@{version} installed successfully")
else:
if os.path.isdir(dst_dir):
shutil.rmtree(dst_dir)
raise PackageManagerException(f"[x] Failed to install {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.")
def uninstall_package(self, package_name):
def uninstall_package(self, package_name, use_dist):
pkg_found = False
for package in self.list_installed_packages():
if package_name == package:
pkg_found = True
pkg_dir = os.path.join(os.path.join(self._r2env_path, self.PACKAGES_DIR), package_name)
try:
shutil.rmtree(pkg_dir)
print_console("Removed package {}".format(package_name))
except OSError as err:
print_console("[x] Unable to remove package {0}. Error: {1}".format(package_name, err), ERROR)
if use_dist:
profile, version = package.split("@")
self._uninstall_from_dist(profile, version)
else:
pkg_dir = os.path.join(self._r2env_path, self.PACKAGES_DIR, package)
try:
shutil.rmtree(pkg_dir)
print_console(f"[-] Removed package {package_name}")
except OSError as err:
raise PackageManagerException(f"[x] Unable to remove package {package_name}. Error: {err}") \
from err
break
if not pkg_found:
print_console("[x] Unable to find installed package {0}".format(package_name), ERROR)
raise PackageManagerException(f"[x] Unable to find installed package {package_name}")
def _build_from_package(self):
pass
def _get_pkgname(self, profile, version, cos):
if profile in self._packages:
for pkgv in self._packages[profile]["versions"]:
if pkgv["id"] == version:
for pkgd in pkgv["packages"].keys():
if pkgd == cos:
return pkgv["packages"][pkgd]
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])
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])
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)
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
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
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()}")
def _build_from_source(self, profile, version, source_path, dst_path, logfile, use_meson=False):
exit_if_not_exists(['git'])
print_console("[*] Installing {}@{} package from source".format(profile, version))
print_console("[-] Cloning {} version".format(version))
print_console(f"[*] Installing {profile}@{version} package from source")
print_console(f"[-] Cloning {version} version")
git_fetch(self._packages[profile]["source"], version, source_path)
print_console("[-] Cleaning Repo")
git_clean(source_path)
if use_meson:
return self._build_using_meson(source_path, dst_path, logfile)
return self._build_using_acr(source_path, dst_path, logfile)
return self._build_using_meson(source_path, dst_path, logfile, self._r2env_path)
return self._build_using_acr(source_path, dst_path, logfile, self._r2env_path)
@staticmethod
def _build_using_acr(source_path, dst_path, logfile):
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 for Termux...")
print_console("[-] Building package using acr...")
extra_flags = ""
if os.path.isfile("/default.prop"):
extra_flags = " --with-compiler=termux"
cmd = "(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)
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}")
return os.system(cmd) == 0
@staticmethod
def _build_using_meson(source_path, dst_path, logfile):
def _build_using_meson(source_path, dst_path, logfile, r2env_path):
exit_if_not_exists(['meson', 'ninja'])
print_console("[-] Building package using meson ...")
cmd = "(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)
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}")
return os.system(cmd) == 0
def _exit_if_package_not_available(self, profile, version):
pkg_found = False
for available_version in self._packages[profile]["versions"]:
if available_version['id'] == version:
pkg_found = True
break
if not pkg_found:
print_console("{}@{} package not available. Use 'list' command to see available packages."
.format(profile, version), level=ERROR)
sys.exit(-1)
def _if_package_not_available(self, profile, version):
if profile in self._packages:
for available_version in self._packages[profile]["versions"]:
if available_version['id'] == version:
return False
return True
+100 -67
View File
@@ -3,93 +3,126 @@
import argparse
import sys
from r2env.exceptions import ActionException, R2EnvException, PackageManagerException
from r2env.tools import print_console, ERROR
from r2env.core import R2Env
HELP_MESSAGE = """
Usage: r2env [action] [args...]
Actions:
class REPL:
HELP_MESSAGE = """
Usage: r2env [-flags] [action] [args...]
init - create .r2env in current directory
config - display current .r2env settings
install [pkg] - build and install given package. Use --meson to use it as the build system.
uninstall [pkg] - remove selected package
use [pkg] - use r2 package defined. pkg should be a release version or latest.
path - show path of current r2 in use
version - show version of r2env
versions - List all Radare versions installed
list - list all Radare packages available to r2env
shell - open a new shell with PATH env var set
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
Actions:
def show_help():
print_console(HELP_MESSAGE)
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
Environment
def show_version():
print_console(R2Env().version())
R2ENV_PATH - specify different path other than ~/.r2env
"""
actions_with_argument = ["install", "uninstall", "use"]
actions_with_arguments = ["shell"]
actions = {
"init": R2Env().init,
"version": show_version,
"path": R2Env().get_r2_path,
"config": R2Env().show_config,
"list": R2Env().list_available_packages,
"install": R2Env().install,
"shell": R2Env().shell,
"uninstall": R2Env().uninstall,
"use": R2Env().use,
"versions": R2Env().list_installed_packages,
"help": show_help
}
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 run_action(action, args, use_meson):
if action == "":
show_help()
elif action not in actions:
print_console("[X] Action not found", 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)
if action == "install":
actions[action](args[0], use_meson=use_meson)
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])
else:
actions[action](args[0])
else:
actions[action]()
def exit_if_not_argument_is_set(args, action):
if len(args) < 1:
if action == "use":
print_console("[x] Package not defined. Please use 'r2env use' with one installed package. ", ERROR)
actions["versions"]()
else:
print_console("[x] Missing package argument. ( as for example: radare2@latest)", ERROR)
print_console("[x] Missing package argument. ( as for example: radare2@latest)", ERROR)
sys.exit(-1)
self.actions[action]()
def main():
parser = argparse.ArgumentParser()
parser.add_argument("action", help="run specified action. (Run r2env help for more information)",
action="store", default=["help"])
parser.add_argument('args', metavar='args', nargs='*', type=str, help='Specified arguments')
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("-v", "--version", dest="version", help="Show r2env version", action="store_true")
parser.add_argument("-m", "--meson", dest="meson", help="Use meson as your build system.", action="store_true")
args = parser.parse_args()
if args.version:
print_console(R2Env().version())
return
run_action(args.action, args.args, args.meson)
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("-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)
if __name__ == "__main__":
+297
View File
@@ -0,0 +1,297 @@
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
@@ -0,0 +1,403 @@
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
@@ -0,0 +1,55 @@
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)
+62 -14
View File
@@ -2,14 +2,16 @@
import json
import os
import sys
from shutil import which
import git
from colorama import Fore, Style
from git import Repo
# Global Vars
from r2env.exceptions import PackageManagerException
INFO = 0
WARNING = 1
ERROR = 2
@@ -19,6 +21,9 @@ def print_console(msg, level=INFO, formatter=0):
tabs, color = ["", ""]
for _ in range(formatter):
tabs += " "
if os.name == 'nt':
print(tabs + msg)
return
if level == ERROR:
color = Fore.RED
elif level == WARNING:
@@ -28,20 +33,64 @@ def print_console(msg, level=INFO, formatter=0):
def host_platform():
if os.name == "nt":
return "w64"
return "windows"
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
try:
sysname = os.uname().sysname
machine = os.uname().machine
except AttributeError:
sysname = "Windows"
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 os.path.isfile("/default.prop"):
dist_name = "android"
return dist_name
def git_fetch(url, version, source_path):
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 != "latest":
repo.git.checkout(version)
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 version != "git":
try:
repo.git.checkout(version)
except git.GitCommandError:
# try again after pulling new tags from origin
repo.git.fetch("origin", "--tags")
repo.git.checkout(version)
sms = repo.submodules
for sm in sms:
if not sm.module_exists():
sm.update()
def git_clean(source_path):
@@ -56,14 +105,13 @@ def exists(tool):
def exit_if_not_exists(tools):
for tool in tools:
if not exists(tool):
print_console("[x] {} is required. Please install it first", level=ERROR)
sys.exit(-1)
raise PackageManagerException(f"[x] {tool} is required. Please install it first")
def load_json_file(filepath):
try:
with open(filepath) as json_file:
with open(filepath, encoding="utf-8") as json_file:
return json.load(json_file)
except OSError as err:
print_console("File {} not found. Msg: {}".format(filepath, err), ERROR)
print_console(f"File {filepath} not found. Msg: {err}", ERROR)
return None
+1 -1
View File
@@ -1 +1 @@
0.3.0
0.5.9
+2
View File
@@ -1,3 +1,5 @@
dploy>=0.1.2
colorama>=0.4.4
wget
requests
GitPython
+5
View File
@@ -0,0 +1,5 @@
-r build.txt
pip
pbr
setuptools
twine
+1 -1
View File
@@ -7,4 +7,4 @@ mock
pytest
coverage
doublex
tox
tox
Regular → Executable
+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 $base_dir -name \*.pyc | xargs rm 2>/dev/null
find . -name \*.pyc | xargs rm 2>/dev/null
python -m coverage run -m pytest -v -rsx -k "test_" --ignore=bin/
ecode=$?
+5 -3
View File
@@ -1,11 +1,12 @@
[metadata]
license_file = LICENSE
version = 0.2.2
name = r2env
author = 'pancake, murphy'
author_email = '[email protected], [email protected]'
author = pancake
# author_email = '[email protected], [email protected]'
author_email = [email protected]
description = radare2 multiple version and environment installation tool
long_description = file:README.md
long_description_content_type = text/markdown
url = https://www.radare.org/
project_urls =
Source = https://github.com/radareorg/r2env
@@ -34,6 +35,7 @@ install_requires=
dploy >= 0.1.2
colorama >= 0.4.4
GitPython
requests
setup_requires =
setuptools_scm >= 1.15
include_package_data = True
+4 -1
View File
@@ -5,5 +5,8 @@ from setuptools import setup
setup(
setup_requires=['pbr'],
pbr=True,
package_files=[('config', ['r2env/config/config.json', 'r2env/config/profiles.json'])]
package_files=[
('config', ['r2env/config/config.json']),
('', ['version.txt'])
]
)
+6 -5
View File
@@ -1,15 +1,16 @@
[tox]
envlist = py{38,39}-{tests},{py38,py39}-codecheck-{pylint,flake8,bandit}
envlist = py{38,39,310}-{tests},{py38,py39,310}-codecheck-{pylint,flake8,bandit}
skipsdist = True
[testenv]
allowlist_externals = bash
deps = -r {toxinidir}/requirements/test.txt
passenv = *
commands =
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
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
[flake8]
# to ignore complete files, add to the file: # flake8: noqa