added ability to indicate user
This commit is contained in:
parent
7a1616d553
commit
6b2dd13adf
3 changed files with 552 additions and 0 deletions
359
aactivator.py
Executable file
359
aactivator.py
Executable file
|
|
@ -0,0 +1,359 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: UTF-8 -*-
|
||||
"""\
|
||||
Usage: eval "$(aactivator init)"
|
||||
|
||||
aactivator is a script for automatically sourcing environments in an interactive shell.
|
||||
The interface for using this is two files:
|
||||
|
||||
- .activate.sh: when sourced, this file activates your environment
|
||||
- .deactivate.sh: when sourced, this file deactivates your environment
|
||||
|
||||
A typical setup in a python project:
|
||||
|
||||
$ ln -vs venv/bin/activate .activate.sh
|
||||
$ echo deactivate > .deactivate.sh
|
||||
|
||||
If an environment is already active it will not be re-activated.
|
||||
If a different project is activated, the previous project will be deactivated beforehand.
|
||||
|
||||
aactivator will ask before automatically sourcing environments, and optionally
|
||||
remember your answer. You can later adjust your per-project preferences in the
|
||||
~/.cache/aactivator/ directory.
|
||||
|
||||
see also: https://github.com/Yelp/aactivator
|
||||
"""
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
|
||||
import io
|
||||
import os.path
|
||||
import stat
|
||||
import sys
|
||||
from os.path import relpath
|
||||
from shlex import quote
|
||||
|
||||
ENVIRONMENT_VARIABLE = "AACTIVATOR_ACTIVE"
|
||||
ACTIVATE = ".activate.sh"
|
||||
DEACTIVATE = ".deactivate.sh"
|
||||
|
||||
__version__ = "2.0.0"
|
||||
|
||||
|
||||
def init(arg0):
|
||||
arg0 = os.path.realpath(arg0)
|
||||
cmd = 'if [ -x {exe} ]; then eval "`{exe}`"; fi'.format(exe=arg0)
|
||||
return """\
|
||||
export AACTIVATOR_VERSION={version}
|
||||
alias aactivator={arg0}
|
||||
unset {varname}
|
||||
if [ "$ZSH_VERSION" ]; then
|
||||
precmd_aactivator() {{ {cmd}; }}
|
||||
if ! [ "${{precmd_functions[(r)precmd_aactivator]}}" ]; then
|
||||
precmd_functions=(precmd_aactivator $precmd_functions)
|
||||
fi
|
||||
else
|
||||
if ! ( echo "$PROMPT_COMMAND" | grep -Fq '{cmd}' ); then
|
||||
PROMPT_COMMAND='{cmd}; '"$PROMPT_COMMAND"
|
||||
fi
|
||||
fi""".format(
|
||||
version=__version__, arg0=arg0, cmd=cmd, varname=ENVIRONMENT_VARIABLE
|
||||
)
|
||||
|
||||
|
||||
def get_filesystem_id(path):
|
||||
try:
|
||||
return os.stat(path).st_dev
|
||||
except OSError as error:
|
||||
if error.errno == 2: # no such file
|
||||
return None
|
||||
else:
|
||||
raise
|
||||
|
||||
|
||||
def insecure_inode(path):
|
||||
"""This particular inode can be altered by someone other than the owner"""
|
||||
pathstat = os.stat(path).st_mode
|
||||
# Directories with a sticky bit are always acceptable.
|
||||
if os.path.isdir(path) and pathstat & stat.S_ISVTX:
|
||||
return False
|
||||
# The path is writable by someone who is not us.
|
||||
elif pathstat & (stat.S_IWGRP | stat.S_IWOTH):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def first(iterable, predicate):
|
||||
for x in iterable:
|
||||
if predicate(x):
|
||||
return x
|
||||
|
||||
|
||||
def insecure(path):
|
||||
"""Find an insecure path, at or above this one"""
|
||||
return first(search_parent_paths(path), insecure_inode)
|
||||
|
||||
|
||||
def search_parent_paths(path):
|
||||
path = os.path.abspath(path)
|
||||
original_fs_id = fs_id = get_filesystem_id(path)
|
||||
previous_path = None
|
||||
while original_fs_id == fs_id and path != previous_path:
|
||||
yield path
|
||||
previous_path = path
|
||||
path = os.path.dirname(path)
|
||||
fs_id = get_filesystem_id(path)
|
||||
|
||||
|
||||
def error_command(message):
|
||||
return "echo %s >&2" % quote("aactivator: " + message)
|
||||
|
||||
|
||||
def mkdirp(path):
|
||||
try:
|
||||
os.makedirs(path)
|
||||
except OSError:
|
||||
if os.path.isdir(path):
|
||||
return
|
||||
else:
|
||||
raise
|
||||
|
||||
|
||||
def _get_lines_if_there(path):
|
||||
if os.path.exists(path):
|
||||
return io.open(path).read().splitlines()
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
class ConfigFile(object):
|
||||
def __init__(self, directory, name):
|
||||
self.path = os.path.join(directory, name)
|
||||
self.lines = frozenset(_get_lines_if_there(self.path))
|
||||
|
||||
def write(self, mode, value):
|
||||
mkdirp(os.path.dirname(self.path))
|
||||
with io.open(self.path, mode) as file_obj:
|
||||
file_obj.write(value)
|
||||
|
||||
def append(self, value):
|
||||
self.write("a", value + "\n")
|
||||
|
||||
|
||||
def path_is_under(path, under):
|
||||
relpath = os.path.relpath(path, under).split("/")
|
||||
return not relpath[:1] == [".."]
|
||||
|
||||
|
||||
def user_cache_dir(env):
|
||||
# stolen from pip.utils.appdirs.user_cache_dir
|
||||
# expanduser doesn't take an env argument -.-
|
||||
from os.path import expanduser
|
||||
|
||||
orig, os.environ = os.environ, env
|
||||
try:
|
||||
return expanduser(env.get("XDG_CACHE_HOME", "~/.cache"))
|
||||
finally:
|
||||
os.environ = orig
|
||||
|
||||
|
||||
class ActivateConfig(object):
|
||||
def __init__(self, env, get_input):
|
||||
self.env = env
|
||||
self.get_input = get_input
|
||||
self.path = os.path.join(user_cache_dir(self.env), "aactivator")
|
||||
self.allowed = ConfigFile(self.path, "allowed")
|
||||
self.not_now = ConfigFile(self.path, "not-now")
|
||||
self.disallowed = ConfigFile(self.path, "disallowed")
|
||||
|
||||
def refresh_not_now(self, pwd):
|
||||
result = []
|
||||
for path in self.not_now.lines:
|
||||
dirname = os.path.dirname(path)
|
||||
if path_is_under(pwd, dirname):
|
||||
result.append(path)
|
||||
self.not_now.write("w", "\n".join(result))
|
||||
|
||||
def _prompt_user(self, path):
|
||||
print(
|
||||
"aactivator will source {0} and {1} at {2}.".format(
|
||||
ACTIVATE,
|
||||
DEACTIVATE,
|
||||
path,
|
||||
),
|
||||
file=sys.stderr,
|
||||
)
|
||||
while True:
|
||||
print("Acceptable? (y)es (n)o (N)ever: ", file=sys.stderr, end="")
|
||||
sys.stderr.flush()
|
||||
try:
|
||||
response = self.get_input()
|
||||
# Allow ^D to be "no"
|
||||
except EOFError:
|
||||
response = "n"
|
||||
|
||||
if response.startswith("N"):
|
||||
self.disallowed.append(path)
|
||||
print(
|
||||
"aactivator will remember this: " "~/.cache/aactivator/disallowed",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return False
|
||||
|
||||
response = response.lower()
|
||||
if response.startswith("n"):
|
||||
self.not_now.append(path)
|
||||
return False
|
||||
elif response.startswith("y"):
|
||||
self.allowed.append(path)
|
||||
print(
|
||||
"aactivator will remember this: " "~/.cache/aactivator/allowed",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return True
|
||||
else:
|
||||
print("I didn't understand your response.", file=sys.stderr)
|
||||
print(file=sys.stderr)
|
||||
|
||||
def find_allowed(self, path):
|
||||
self.refresh_not_now(path)
|
||||
return first(search_parent_paths(path), self.is_allowed)
|
||||
|
||||
def is_allowed(self, path, _getuid=os.getuid):
|
||||
activate = os.path.join(path, ACTIVATE)
|
||||
if not os.path.exists(activate):
|
||||
return False
|
||||
elif os.stat(activate).st_uid != _getuid():
|
||||
# If we do not own this path, short circuit on activating
|
||||
return False
|
||||
elif path in self.disallowed.lines or path in self.not_now.lines:
|
||||
return False
|
||||
elif path in self.allowed.lines:
|
||||
return True
|
||||
else:
|
||||
return self._prompt_user(path)
|
||||
|
||||
|
||||
def security_check(path):
|
||||
if not os.path.exists(path):
|
||||
return "aactivator: File does not exist: " + path
|
||||
insecure_path = insecure(path)
|
||||
if insecure_path is not None:
|
||||
return "aactivator: Cowardly refusing to source {0} because writeable by others: {1}".format(
|
||||
relpath(path), relpath(insecure_path)
|
||||
)
|
||||
|
||||
|
||||
def command_for_path(cmd, path, pwd):
|
||||
if path == pwd:
|
||||
return cmd
|
||||
else:
|
||||
return " &&\n".join(
|
||||
(
|
||||
'OLDPWD_bak="$OLDPWD"',
|
||||
"cd " + quote(path),
|
||||
cmd,
|
||||
'cd "$OLDPWD_bak"',
|
||||
"cd " + quote(pwd),
|
||||
"unset OLDPWD_bak",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def aactivate(path, pwd):
|
||||
return command_for_path(
|
||||
" &&\n".join(
|
||||
(
|
||||
"aactivator security-check " + ACTIVATE,
|
||||
"source ./" + ACTIVATE,
|
||||
"export %s=%s" % (ENVIRONMENT_VARIABLE, quote(path)),
|
||||
)
|
||||
),
|
||||
path,
|
||||
pwd,
|
||||
)
|
||||
|
||||
|
||||
def deaactivate(path, pwd):
|
||||
unset = "unset " + ENVIRONMENT_VARIABLE
|
||||
deactivate_path = os.path.join(path, DEACTIVATE)
|
||||
|
||||
if os.path.exists(deactivate_path):
|
||||
return command_for_path(
|
||||
" &&\n".join(
|
||||
(
|
||||
"aactivator security-check " + DEACTIVATE,
|
||||
"source ./" + DEACTIVATE,
|
||||
)
|
||||
)
|
||||
+ "\n"
|
||||
+ unset,
|
||||
path,
|
||||
pwd,
|
||||
)
|
||||
else:
|
||||
return " &&\n".join(
|
||||
(
|
||||
unset,
|
||||
error_command(
|
||||
"Cannot deactivate. File missing: {0}".format(deactivate_path)
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def get_output(
|
||||
environ, pwd=".", get_input=sys.stdin.readline, arg0="/path/to/aactivator"
|
||||
):
|
||||
try:
|
||||
pwd = os.path.realpath(pwd)
|
||||
except OSError as error:
|
||||
if error.errno == 2: # no such file
|
||||
return ""
|
||||
else:
|
||||
raise
|
||||
config = ActivateConfig(environ, get_input)
|
||||
activate_path = config.find_allowed(pwd)
|
||||
result = []
|
||||
|
||||
if environ.get("AACTIVATOR_VERSION") == __version__:
|
||||
activated_env = environ.get(ENVIRONMENT_VARIABLE)
|
||||
else:
|
||||
result.append(init(arg0))
|
||||
activated_env = None
|
||||
|
||||
if (
|
||||
activated_env != activate_path
|
||||
): # did we already activate the current environment?
|
||||
if activated_env: # deactivate it
|
||||
result.append(deaactivate(activated_env, pwd))
|
||||
if activate_path:
|
||||
result.append(aactivate(activate_path, pwd))
|
||||
return " &&\n".join(result)
|
||||
|
||||
|
||||
def aactivator(args, env):
|
||||
if len(args) == 1:
|
||||
return get_output(env, arg0=args[0])
|
||||
elif len(args) == 2 and args[1] == "init":
|
||||
return init(args[0])
|
||||
elif len(args) == 3 and args[1] == "security-check":
|
||||
exit(security_check(args[2]))
|
||||
else:
|
||||
return __doc__ + "\nVersion: " + __version__
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
print(aactivator(tuple(sys.argv), os.environ.copy()))
|
||||
except KeyboardInterrupt: # pragma: no cover
|
||||
# Silence ^C
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
sys.exit(main())
|
||||
except KeyboardInterrupt:
|
||||
# Silence ^C
|
||||
pass
|
||||
129
setup_config
Executable file
129
setup_config
Executable file
|
|
@ -0,0 +1,129 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Function to check if a command is available
|
||||
command_exists() {
|
||||
command -v "$1" &> /dev/null
|
||||
}
|
||||
|
||||
# Function to install dependencies based on the package manager
|
||||
install_dependencies() {
|
||||
case $PACKAGE_MANAGER in
|
||||
"brew") $PACKAGE_MANAGER install "$@" ;;
|
||||
"apt-get") sudo $PACKAGE_MANAGER install -y "$@" ;;
|
||||
"yum") sudo $PACKAGE_MANAGER install -y "$@" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Check if running on macOS or Linux
|
||||
if [ "$(uname)" == "Darwin" ]; then
|
||||
# Check if Homebrew is not installed
|
||||
if ! command_exists brew; then
|
||||
echo "Homebrew is not installed. Installing Homebrew..."
|
||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
||||
PACKAGE_MANAGER="brew"
|
||||
fi
|
||||
elif [ "$(uname)" == "Linux" ]; then
|
||||
# Check if on Linux and use the appropriate package manager
|
||||
if command_exists apt-get; then
|
||||
PACKAGE_MANAGER="apt-get"
|
||||
elif command_exists yum; then
|
||||
PACKAGE_MANAGER="yum"
|
||||
else
|
||||
echo "Unsupported package manager. Exiting."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Clone or initialize dotfiles repository
|
||||
DOTFILES_DIR="$HOME/.dotfiles"
|
||||
if [ -d "$DOTFILES_DIR" ]; then
|
||||
if [ -d "$DOTFILES_DIR/.git" ]; then
|
||||
echo "Updating existing dotfiles repository..."
|
||||
(cd "$DOTFILES_DIR" && git pull origin master)
|
||||
else
|
||||
git -C "$DOTFILES_DIR" init
|
||||
git -C "$DOTFILES_DIR" clone https://github.com/jfraeys/.dotfiles.git "$DOTFILES_DIR"
|
||||
fi
|
||||
else
|
||||
echo "Creating dotfiles directory..."
|
||||
mkdir -p "$DOTFILES_DIR"
|
||||
git -C "$DOTFILES_DIR" init
|
||||
git -C "$DOTFILES_DIR" clone https://github.com/jfraeys/.dotfiles.git "$DOTFILES_DIR"
|
||||
fi
|
||||
|
||||
# Check and install git and other tools
|
||||
if ! command_exists git; then
|
||||
install_dependencies git fd ripgrep fzf tree
|
||||
fi
|
||||
|
||||
# Check and install zsh
|
||||
if ! command_exists zsh; then
|
||||
install_dependencies zsh
|
||||
# Clone Oh-My-Zsh if not already present
|
||||
if [ ! -d "$HOME/.oh-my-zsh" ]; then
|
||||
git clone https://github.com/ohmyzsh/ohmyzsh.git "$HOME/.oh-my-zsh"
|
||||
fi
|
||||
# Set Zsh as the default shell
|
||||
chsh -s "$(command -v zsh)"
|
||||
fi
|
||||
|
||||
# Check and install tmux
|
||||
if ! command_exists tmux; then
|
||||
install_dependencies tmux
|
||||
fi
|
||||
|
||||
# Install Go
|
||||
case $PACKAGE_MANAGER in
|
||||
"brew")
|
||||
$PACKAGE_MANAGER install go
|
||||
;;
|
||||
"apt-get")
|
||||
sudo $PACKAGE_MANAGER install -y golang
|
||||
;;
|
||||
"yum")
|
||||
sudo $PACKAGE_MANAGER install -y golang
|
||||
;;
|
||||
esac
|
||||
|
||||
# Install Python and pip
|
||||
case $PACKAGE_MANAGER in
|
||||
"brew")
|
||||
$PACKAGE_MANAGER install python
|
||||
;;
|
||||
"apt-get")
|
||||
sudo $PACKAGE_MANAGER install -y python3 python3-pip
|
||||
;;
|
||||
"yum")
|
||||
sudo $PACKAGE_MANAGER install -y python3 python3-pip
|
||||
;;
|
||||
esac
|
||||
|
||||
# Install some common linters (uncomment if needed)
|
||||
# case $PACKAGE_MANAGER in
|
||||
# "brew")
|
||||
# $PACKAGE_MANAGER install shellcheck
|
||||
# ;;
|
||||
# "apt-get")
|
||||
# sudo $PACKAGE_MANAGER install -y shellcheck
|
||||
# ;;
|
||||
# "yum")
|
||||
# sudo $PACKAGE_MANAGER install -y shellcheck
|
||||
# ;;
|
||||
# esac
|
||||
|
||||
# Create symbolic links for dotfiles
|
||||
ln -s "$DOTFILES_DIR/zsh/.zshrc" ~/.zshrc
|
||||
ln -s "$DOTFILES_DIR/tmux/.tmux.conf" ~/.tmux.conf
|
||||
ln -s "$DOTFILES_DIR/nvim" ~/.config/nvim
|
||||
|
||||
# Source configurations for the current session
|
||||
source ~/.zshrc
|
||||
tmux source ~/.tmux.conf
|
||||
|
||||
# Inform the user and wait for input
|
||||
echo "Zsh, Tmux, Neovim, Go, and development tools configurations applied. Press Enter to start Zsh and Tmux."
|
||||
read -r
|
||||
|
||||
# Start Zsh and Tmux
|
||||
zsh
|
||||
|
||||
64
stow_setup
Executable file
64
stow_setup
Executable file
|
|
@ -0,0 +1,64 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
# Check for Git
|
||||
if ! command -v git &> /dev/null; then
|
||||
echo "Git is not installed. Aborting." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# make sure we have pulled in and updated any submodules
|
||||
git submodule init
|
||||
git submodule update
|
||||
|
||||
# what directories should be installable by all users including the root user
|
||||
base=(
|
||||
zsh
|
||||
nvim
|
||||
tmux
|
||||
)
|
||||
|
||||
# folders that should, or only need to be installed for a local user
|
||||
useronly=(
|
||||
git
|
||||
)
|
||||
|
||||
# files to ignore during stow
|
||||
ignore_files=(
|
||||
".DS_Store"
|
||||
"__setup"
|
||||
"bin"
|
||||
"git"
|
||||
"setup.sh"
|
||||
)
|
||||
|
||||
# run the stow command for the passed-in directory ($2) in location $1
|
||||
stowit() {
|
||||
usr=$1
|
||||
app=$2
|
||||
# -v verbose
|
||||
# -R recursive
|
||||
# -t target
|
||||
# --ignore files to ignore
|
||||
stow -v -R -t "${usr}" --ignore "$(IFS="|"; echo "${ignore_files[*]}")" "${app}"
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo "Stowing apps for user: ${USER}"
|
||||
|
||||
# install apps available to local users and root
|
||||
for app in "${base[@]}"; do
|
||||
stowit "${HOME}" "${app}"
|
||||
done
|
||||
|
||||
# install only user space folders
|
||||
for app in "${useronly[@]}"; do
|
||||
if [[ ! "$(whoami)" = "root" ]]; then
|
||||
stowit "${HOME}" "${app}"
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "##### ALL DONE"
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in a new issue