Initial commit

This commit is contained in:
thecookingsenpai 2023-12-25 13:28:06 +01:00
commit b262b2e0dc
17 changed files with 459 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
alternative.json
localenv
altenv
.cache

53
README.md Normal file
View File

@ -0,0 +1,53 @@
# NixVenv
## A pythonesque venv like approach to system virtual environments
### What is this?
Just as Python's venv allows developers to specify virtual environments for their python projects, NixVenv goal is to provide developers with the possibility to create virtual environments for their whole system.
Have you ever met a dependency that is required but would break your code elsewhere in your system? With NixVenv you can create a virtual environment for that specific application and run it with customized files, environment variables and configurations. Once you have finished, just deactivate it and you will be back in your usual shell environment.
### Features
• Customizable environment variables
• Easy to set up configuration
• Local files support
• Local binaries support and priority
### Installation
To install NixVenv, execute in a terminal:
git clone https://github.com/thecookingsenpai/nixvenv
cd nixvenv
pip install -r requirements.txt
Alternatively, you can install NixVenv using pip:
pip install nixvenv
Or by building the pip package yourself:
git clone https://github.com/thecookingsenpai/nixvenv
cd nixvenv
python -m pip install -e pip_package
In both these cases, you will have to provide (or copy from here) your own config.json file
### Usage
python nvenv.py operation=<operation> config_file=<configuration_json>
### Command Line Arguments
#### operation=
• run: activate the nvenv environment specified by the configuration
• activate: same as above
• new: create and activate the nvenv environment specified by the configuration if not already present
• remove: remove the nvenv environment specified by the configuration
#### config_file=
Any valid json file containing a nvenv configuration (relative path).

20
config.json Normal file
View File

@ -0,0 +1,20 @@
{
"nvenv_name": "localenv",
"working_dir": "./localenv",
"local_bin": "bin",
"load_file": "load.sh",
"init_cmds": {
"nvenv_folder": "mkdir test",
"local_bin_folder": "echo '#!/bin/bash \necho It is working!' > nvenv_test && chmod +x nvenv_test",
"here": "mkdir .cache"
},
"overwrites" : {
"IS_VENV": "true"
},
"files": [
["./test", ".cache/test"]
],
"additions" : {
"PATH": "/usr/local/sbin:/home/tcsenpai/bin"
}
}

159
nvenv.py Normal file
View File

@ -0,0 +1,159 @@
import os
import subprocess
import json
from pprint import pprint
import sys
import shutil
environment = os.environ.copy()
config = {}
nvenv_name = ""
local_bin_folder = ""
load_file = ""
hostname = ""
username = ""
# INFO Entry point
def main(config_file="./config.json", new=False, delete=False):
print("[ Welcome to NixVenv, the *nix compatible virtual shell environment manager ]")
global environment, config, cmds, local_bin_folder, load_file, nvenv_name, shell, hostname, username
p = subprocess.run("whoami", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
username = p.stdout.decode('utf-8').strip()
p = subprocess.run("hostname", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
hostname = p.stdout.decode('utf-8').strip()
p = subprocess.run("echo $SHELL", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
shell = p.stdout.decode('utf-8').strip()
print(shell)
cmds = 'echo "[ Setting environment variables ]"\n'
config = open(config_file, "r")
config = json.loads(config.read())
print("[*] Config file loaded: " + config_file)
nvenv_name = config["nvenv_name"]
# Halt if we have to delete the environment
if delete:
if not os.path.exists(nvenv_name):
print("[*] Environment does not exist: " + nvenv_name)
else:
shutil.rmtree(nvenv_name, ignore_errors=True)
print("[*] Deleted environment: " + nvenv_name)
os._exit(0)
# Else we use the environment
print("[*] Reading from environment: " + nvenv_name)
local_bin_folder = os.getcwd() + "/" + nvenv_name + "/" + config["local_bin"]
print("[*] Local bin folder: " + local_bin_folder)
load_file = os.getcwd() + "/" + nvenv_name + "/" + config["load_file"]
new_environment(new)
load_config()
load_environment()
# INFO Load a configuration file
def load_config():
global cmds, local_bin_folder, load_file, hostname
# Overwrites replace environment variables fully or create a new one
overwrites = config['overwrites']
for key in overwrites:
cmds += "export " + key + "=" + overwrites[key] + "\n"
print("[*] Overwriting enviromental variable: " + key)
# Additions add the value to the environment variable preserving the original value
additions = config["additions"]
for key in additions:
cmds += "export " + key + "=${" + key + "}:" + additions[key] + "\n"
print("[*] Adding to enviromental variable: " + key)
# Setting our environment variables
print("[*] Setting our environment variables")
cmds += "export PATH=" + local_bin_folder + ":" + os.environ["PATH"]
# Loading the static environment variables defined in the config file
print("[*] Setting the environment name as " + config["nvenv_name"])
cmds += "export NVENV_NAME=" + config["nvenv_name"] + "\n"
print("[*] Changing to: " + config["working_dir"])
cmds += "cd " + config["working_dir"] + "\n"
cmds += 'echo "nvenv environment loaded: $NVENV_NAME"\n'
# Setting up customizations
cmds += 'export PS1="' + username + ' [' + config["nvenv_name"] + '@' + hostname+ '] :> "\n'
# Finally, writing to the load file
print("[*] Writing to the load file: " + load_file)
with open(load_file, "w") as f:
f.write(cmds)
# INFO Loading the local environment fully
def load_environment():
print("[*] Loading the environment")
global environment, load_file, shell
# Multiple shells are supported
if "zsh" in shell:
cmd = "source " + load_file + " && " + shell + " -f -d"
else:
cmd = shell + " --rcfile " + load_file
# Finally, loading the environment by sourcing the load file
print("[+] Executing: " + cmd)
print("\nNOTE: Digit 'exit' and press ENTER to return to the previous environment\n")
os.system(cmd)
# INFO Environemnt creation
def new_environment(new_flag=False):
global cmds, local_bin_folder
if os.path.exists(os.getcwd() + "/" + nvenv_name):
if not new_flag:
raise Exception("[x] Environment already exists: " + nvenv_name)
print("[+] Environment already exists: " + nvenv_name)
return
# Creating the local_bin_folder if it doesn't exist
if not os.path.exists(local_bin_folder):
os.makedirs(local_bin_folder)
# Reading the init commands
init_cmds_list = config["init_cmds"]
for cmd in init_cmds_list:
# Replacing folders if needed
if (cmd == "here"):
running_dir = os.getcwd()
elif (cmd == "nvenv_folder"):
running_dir = os.getcwd() + "/" + nvenv_name
elif (cmd == "local_bin_folder"):
running_dir = local_bin_folder
else:
# Support for custom directories
# REVIEW Security note: limit it to the current working directory
if not os.path.exists(running_dir):
raise Exception("Running directory does not exist: " + running_dir)
# Compiling the command to be executed before the environment is activated
single_cmd = init_cmds_list[cmd]
actual_cmd = "cd '" + running_dir + "' && " + single_cmd
print("[*] Executing: " + actual_cmd)
os.system(actual_cmd)
# Support files
files = config["files"]
for file in files:
origin = file[0]
destination = file[1]
if not os.path.exists(origin):
raise Exception("[x] Origin file does not exist: " + origin)
if os.path.exists(destination):
raise Exception("[x] Destination file already exists: " + destination)
print("[*] Copying file: " + origin + " to: " + destination)
shutil.copy(origin, destination)
if __name__ == '__main__':
# Argument parsing
args_len = len(sys.argv)
print(sys.argv)
filename = sys.argv[0]
params = {
"operation": "run",
"config_file": "config.json",
}
# Getting parameters
for param in sys.argv[1:]:
if "operation=" in param:
params["operation"] = param.split("=")[1]
if "config_file=" in param:
params["config_file"] = param.split("=")[1]
# Sanity checks
if not os.path.exists(params["config_file"]):
raise Exception("[x] Config file does not exist: " + params["config_file"])
# Runs based on operation
if params["operation"] == "activate" or params["operation"] == "run":
main(config_file=params["config_file"])
elif params["operation"] == "new":
main(config_file=params["config_file"], new=True)
elif params["operation"] == "remove":
main(config_file=params["config_file"], delete=True)

0
pip_package/LICENSE Normal file
View File

0
pip_package/MANIFEST.in Normal file
View File

47
pip_package/README.md Normal file
View File

@ -0,0 +1,47 @@
# NixVenv
## A pythonesque venv like approach to system virtual environments
### What is this?
Just as Python's venv allows developers to specify virtual environments for their python projects, NixVenv goal is to provide developers with the possibility to create virtual environments for their whole system.
Have you ever met a dependency that is required but would break your code elsewhere in your system? With NixVenv you can create a virtual environment for that specific application and run it with customized files, environment variables and configurations. Once you have finished, just deactivate it and you will be back in your usual shell environment.
### Features
• Customizable environment variables
• Easy to set up configuration
• Local files support
• Local binaries support and priority
### Installation
To install NixVenv, execute in a terminal:
git clone https://github.com/thecookingsenpai/nixvenv
cd nixvenv
pip install -r requirements.txt
Alternatively, you can install NixVenv using pip:
pip install nixvenv
In this case, you will have to provide (or copy from here) your own config.json file
### Usage
python nvenv.py operation=<operation> config_file=<configuration_json>
### Command Line Arguments
#### operation=
• run: activate the nvenv environment specified by the configuration
• activate: same as above
• new: create and activate the nvenv environment specified by the configuration if not already present
• remove: remove the nvenv environment specified by the configuration
#### config_file=
Any valid json file containing a nvenv configuration (relative path).

View File

View File

@ -0,0 +1,4 @@
Metadata-Version: 2.1
Name: nixvenv
Version: 0.0.0
License-File: LICENSE

View File

@ -0,0 +1,10 @@
LICENSE
MANIFEST.in
README.md
pyproject.toml
src/nixvenv/__init__.py
src/nixvenv/__main__.py
src/nixvenv.egg-info/PKG-INFO
src/nixvenv.egg-info/SOURCES.txt
src/nixvenv.egg-info/dependency_links.txt
src/nixvenv.egg-info/top_level.txt

View File

@ -0,0 +1 @@

View File

@ -0,0 +1 @@
nixvenv

View File

@ -0,0 +1 @@
__version__ = "0.0.1"

View File

@ -0,0 +1,159 @@
import os
import subprocess
import json
from pprint import pprint
import sys
import shutil
environment = os.environ.copy()
config = {}
nvenv_name = ""
local_bin_folder = ""
load_file = ""
hostname = ""
username = ""
# INFO Entry point
def main(config_file="./config.json", new=False, delete=False):
print("[ Welcome to NixVenv, the *nix compatible virtual shell environment manager ]")
global environment, config, cmds, local_bin_folder, load_file, nvenv_name, shell, hostname, username
p = subprocess.run("whoami", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
username = p.stdout.decode('utf-8').strip()
p = subprocess.run("hostname", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
hostname = p.stdout.decode('utf-8').strip()
p = subprocess.run("echo $SHELL", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
shell = p.stdout.decode('utf-8').strip()
print(shell)
cmds = 'echo "[ Setting environment variables ]"\n'
config = open(config_file, "r")
config = json.loads(config.read())
print("[*] Config file loaded: " + config_file)
nvenv_name = config["nvenv_name"]
# Halt if we have to delete the environment
if delete:
if not os.path.exists(nvenv_name):
print("[*] Environment does not exist: " + nvenv_name)
else:
shutil.rmtree(nvenv_name, ignore_errors=True)
print("[*] Deleted environment: " + nvenv_name)
os._exit(0)
# Else we use the environment
print("[*] Reading from environment: " + nvenv_name)
local_bin_folder = os.getcwd() + "/" + nvenv_name + "/" + config["local_bin"]
print("[*] Local bin folder: " + local_bin_folder)
load_file = os.getcwd() + "/" + nvenv_name + "/" + config["load_file"]
new_environment(new)
load_config()
load_environment()
# INFO Load a configuration file
def load_config():
global cmds, local_bin_folder, load_file, hostname
# Overwrites replace environment variables fully or create a new one
overwrites = config['overwrites']
for key in overwrites:
cmds += "export " + key + "=" + overwrites[key] + "\n"
print("[*] Overwriting enviromental variable: " + key)
# Additions add the value to the environment variable preserving the original value
additions = config["additions"]
for key in additions:
cmds += "export " + key + "=${" + key + "}:" + additions[key] + "\n"
print("[*] Adding to enviromental variable: " + key)
# Setting our environment variables
print("[*] Setting our environment variables")
cmds += "export PATH=" + local_bin_folder + ":" + os.environ["PATH"]
# Loading the static environment variables defined in the config file
print("[*] Setting the environment name as " + config["nvenv_name"])
cmds += "export NVENV_NAME=" + config["nvenv_name"] + "\n"
print("[*] Changing to: " + config["working_dir"])
cmds += "cd " + config["working_dir"] + "\n"
cmds += 'echo "nvenv environment loaded: $NVENV_NAME"\n'
# Setting up customizations
cmds += 'export PS1="' + username + ' [' + config["nvenv_name"] + '@' + hostname+ '] :> "\n'
# Finally, writing to the load file
print("[*] Writing to the load file: " + load_file)
with open(load_file, "w") as f:
f.write(cmds)
# INFO Loading the local environment fully
def load_environment():
print("[*] Loading the environment")
global environment, load_file, shell
# Multiple shells are supported
if "zsh" in shell:
cmd = "source " + load_file + " && " + shell + " -f -d"
else:
cmd = shell + " --rcfile " + load_file
# Finally, loading the environment by sourcing the load file
print("[+] Executing: " + cmd)
print("\nNOTE: Digit 'exit' and press ENTER to return to the previous environment\n")
os.system(cmd)
# INFO Environemnt creation
def new_environment(new_flag=False):
global cmds, local_bin_folder
if os.path.exists(os.getcwd() + "/" + nvenv_name):
if not new_flag:
raise Exception("[x] Environment already exists: " + nvenv_name)
print("[+] Environment already exists: " + nvenv_name)
return
# Creating the local_bin_folder if it doesn't exist
if not os.path.exists(local_bin_folder):
os.makedirs(local_bin_folder)
# Reading the init commands
init_cmds_list = config["init_cmds"]
for cmd in init_cmds_list:
# Replacing folders if needed
if (cmd == "here"):
running_dir = os.getcwd()
elif (cmd == "nvenv_folder"):
running_dir = os.getcwd() + "/" + nvenv_name
elif (cmd == "local_bin_folder"):
running_dir = local_bin_folder
else:
# Support for custom directories
# REVIEW Security note: limit it to the current working directory
if not os.path.exists(running_dir):
raise Exception("Running directory does not exist: " + running_dir)
# Compiling the command to be executed before the environment is activated
single_cmd = init_cmds_list[cmd]
actual_cmd = "cd '" + running_dir + "' && " + single_cmd
print("[*] Executing: " + actual_cmd)
os.system(actual_cmd)
# Support files
files = config["files"]
for file in files:
origin = file[0]
destination = file[1]
if not os.path.exists(origin):
raise Exception("[x] Origin file does not exist: " + origin)
if os.path.exists(destination):
raise Exception("[x] Destination file already exists: " + destination)
print("[*] Copying file: " + origin + " to: " + destination)
shutil.copy(origin, destination)
if __name__ == '__main__':
# Argument parsing
args_len = len(sys.argv)
print(sys.argv)
filename = sys.argv[0]
params = {
"operation": "run",
"config_file": "config.json",
}
# Getting parameters
for param in sys.argv[1:]:
if "operation=" in param:
params["operation"] = param.split("=")[1]
if "config_file=" in param:
params["config_file"] = param.split("=")[1]
# Sanity checks
if not os.path.exists(params["config_file"]):
raise Exception("[x] Config file does not exist: " + params["config_file"])
# Runs based on operation
if params["operation"] == "activate" or params["operation"] == "run":
main(config_file=params["config_file"])
elif params["operation"] == "new":
main(config_file=params["config_file"], new=True)
elif params["operation"] == "remove":
main(config_file=params["config_file"], delete=True)

0
test Normal file
View File