ybox 0.9.9__py3-none-any.whl → 0.9.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ybox/__init__.py +1 -1
- ybox/cmd.py +17 -1
- ybox/conf/completions/ybox.fish +2 -0
- ybox/conf/distros/arch/init-user.sh +2 -2
- ybox/conf/distros/arch/init.sh +1 -0
- ybox/conf/distros/arch/pkgdeps.py +2 -0
- ybox/conf/distros/deb-generic/pkgdeps.py +2 -1
- ybox/conf/profiles/basic.ini +29 -16
- ybox/conf/profiles/dev.ini +0 -6
- ybox/conf/resources/entrypoint-root.sh +1 -0
- ybox/conf/resources/entrypoint-user.sh +5 -3
- ybox/conf/resources/entrypoint.sh +22 -13
- ybox/conf/resources/prime-run +0 -2
- ybox/conf/resources/run-in-dir +4 -0
- ybox/conf/resources/run-user-bash-cmd +17 -1
- ybox/conf/resources/ybox-systemd.template +22 -0
- ybox/config.py +9 -1
- ybox/pkg/clean.py +1 -7
- ybox/pkg/info.py +1 -7
- ybox/pkg/inst.py +20 -14
- ybox/pkg/list.py +1 -6
- ybox/pkg/repair.py +4 -0
- ybox/pkg/search.py +1 -7
- ybox/run/cmd.py +2 -1
- ybox/run/control.py +91 -24
- ybox/run/create.py +186 -48
- ybox/run/destroy.py +67 -4
- ybox/run/logs.py +2 -1
- ybox/run/ls.py +2 -1
- ybox/run/pkg.py +46 -4
- ybox/state.py +22 -3
- ybox/util.py +5 -5
- {ybox-0.9.9.dist-info → ybox-0.9.10.dist-info}/METADATA +35 -12
- {ybox-0.9.9.dist-info → ybox-0.9.10.dist-info}/RECORD +38 -37
- {ybox-0.9.9.dist-info → ybox-0.9.10.dist-info}/WHEEL +1 -1
- {ybox-0.9.9.dist-info → ybox-0.9.10.dist-info}/LICENSE +0 -0
- {ybox-0.9.9.dist-info → ybox-0.9.10.dist-info}/entry_points.txt +0 -0
- {ybox-0.9.9.dist-info → ybox-0.9.10.dist-info}/top_level.txt +0 -0
ybox/__init__.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
"""`ybox` is a tool to easily manage linux distributions in containers"""
|
2
|
-
__version__ = "0.9.
|
2
|
+
__version__ = "0.9.10"
|
ybox/cmd.py
CHANGED
@@ -10,6 +10,7 @@ import sys
|
|
10
10
|
from enum import Enum
|
11
11
|
from typing import Callable, Iterable, Optional, Union
|
12
12
|
|
13
|
+
from ybox import __version__ as product_version
|
13
14
|
from ybox.config import Consts
|
14
15
|
|
15
16
|
from .print import print_error, print_info, print_notice, print_warn
|
@@ -207,7 +208,7 @@ def run_command(cmd: Union[str, list[str]], capture_output: bool = False,
|
|
207
208
|
sys.exit(result.returncode)
|
208
209
|
else:
|
209
210
|
return result.returncode
|
210
|
-
if capture_output and result.stderr:
|
211
|
+
if capture_output and result.stderr and error_msg != "SKIP":
|
211
212
|
print_warn(result.stderr.decode("utf-8"), file=sys.stderr)
|
212
213
|
return result.stdout.decode("utf-8") if capture_output else result.returncode
|
213
214
|
|
@@ -221,6 +222,21 @@ def _print_subprocess_output(result: subprocess.CompletedProcess[bytes]) -> None
|
|
221
222
|
print_warn(result.stderr.decode("utf-8"), file=sys.stderr)
|
222
223
|
|
223
224
|
|
225
|
+
def parser_version_check(parser: argparse.ArgumentParser, argv: list[str]) -> None:
|
226
|
+
"""
|
227
|
+
Update command-line parser to add `--version` option to existing ones that will output the
|
228
|
+
ybox product version and exit if specified in the given list of arguments.
|
229
|
+
|
230
|
+
:param parser: instance of :class:`argparse.ArgumentParser` having the command-line parser
|
231
|
+
:param argv: the list of arguments to be parsed
|
232
|
+
"""
|
233
|
+
parser.add_argument("--version", action="store_true", help="output ybox version")
|
234
|
+
# argv may have required positional arguments, hence check for --version separately
|
235
|
+
if "--version" in argv:
|
236
|
+
print(product_version)
|
237
|
+
sys.exit(0)
|
238
|
+
|
239
|
+
|
224
240
|
def parse_opt_deps_args(argv: list[str]) -> argparse.Namespace:
|
225
241
|
"""
|
226
242
|
Common command-line parser for `opt_deps` utilities (see [pkgmgr] section of distro.ini)
|
ybox/conf/completions/ybox.fish
CHANGED
@@ -29,6 +29,8 @@ end
|
|
29
29
|
complete -f -c ybox-create -s h -l help -d "show help"
|
30
30
|
complete -c ybox-create -s n -l name -d "name of the ybox container" -r
|
31
31
|
complete -f -c ybox-create -s F -l force-own-orphans -d "force ownership of orphans on shared root"
|
32
|
+
complete -f -c ybox-create -s C -l distribution-config -d "path to custom distribution configuration file"
|
33
|
+
complete -f -c ybox-create -l distribution-image -d "custom container image"
|
32
34
|
complete -f -c ybox-create -s q -l quiet -d "skip interactive questions"
|
33
35
|
complete -f -c ybox-create -n "not __fish_seen_subcommand_from (__fish_ybox_complete_distributions)" -a "(__fish_ybox_complete_distributions)"
|
34
36
|
|
@@ -10,8 +10,8 @@ current_user="$(id -un)"
|
|
10
10
|
# install binaries for paru from paru-bin (paru takes too long to compile)
|
11
11
|
PARU="paru --noconfirm"
|
12
12
|
echo_color "$fg_cyan" "Installing AUR helper 'paru'" >> $status_file
|
13
|
-
export HOME
|
14
|
-
cd
|
13
|
+
export HOME=$(getent passwd "$current_user" | cut -d: -f6)
|
14
|
+
cd "$HOME"
|
15
15
|
rm -rf paru-bin
|
16
16
|
git clone https://aur.archlinux.org/paru-bin.git
|
17
17
|
cd paru-bin
|
ybox/conf/distros/arch/init.sh
CHANGED
@@ -11,7 +11,7 @@ where:
|
|
11
11
|
and so on; resolution of level > 2 is not required since caller currently ignores those
|
12
12
|
* <order>: this is a simple counter assigned to the dependencies where the value itself is of no
|
13
13
|
significance but if multiple dependencies have the same value then it means that they
|
14
|
-
are ORed dependencies and only one of them
|
14
|
+
are ORed dependencies and only one of them need to be selected for installation
|
15
15
|
* <installed>: true if the dependency already installed and false otherwise
|
16
16
|
* <description>: detailed description of the dependency; it can contain literal \n to indicate
|
17
17
|
newlines in the description
|
@@ -69,6 +69,7 @@ class PkgDetail(Enum):
|
|
69
69
|
OPTIONAL_DEP = 5
|
70
70
|
|
71
71
|
|
72
|
+
# noinspection PyUnusedLocal
|
72
73
|
def process_next_item(line: str, parse_line: Callable[[str], tuple[PkgDetail, str]],
|
73
74
|
parse_dep: Callable[[str], Iterable[tuple[str, str, Optional[str]]]],
|
74
75
|
installed: Callable[[str], bool], max_level: int,
|
ybox/conf/profiles/basic.ini
CHANGED
@@ -20,7 +20,8 @@
|
|
20
20
|
# - YBOX_SYS_CONF_DIR: path to system configuration directory where configuration directory
|
21
21
|
# shipped with ybox is installed (or the string form of
|
22
22
|
# the directory if it is not on filesystem like an egg or similar)
|
23
|
-
# - TARGET_HOME: set to the home directory of the container user
|
23
|
+
# - TARGET_HOME: set to the home directory of the container user as it exists on the host
|
24
|
+
# (i.e. the expanded value of the "home" key in the [base] section)
|
24
25
|
# Additionally a special notation can be used for current date+time with this notation:
|
25
26
|
# ${NOW:<fmt>}. The <fmt> uses the format supported by python strftime
|
26
27
|
# (https://docs.python.org/3/library/datetime.html#datetime.datetime.strftime)
|
@@ -115,7 +116,7 @@ nvidia = off
|
|
115
116
|
# works well on most Linux distros (https://wiki.archlinux.org/title/docker or
|
116
117
|
# https://wiki.archlinux.org/title/podman), and the official NVIDIA docs:
|
117
118
|
# https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html
|
118
|
-
# For example on ubuntu with podman, configure the apt repository from the previous link
|
119
|
+
# For example, on ubuntu with podman, configure the apt repository from the previous link
|
119
120
|
# and install the package as noted there, then run
|
120
121
|
# `sudo nvidia-ctk cdi generate --output=/etc/cdi/nvidia.yaml`.
|
121
122
|
# This will need to be repeated if nvidia driver version is upgraded.
|
@@ -200,29 +201,40 @@ documents = $HOME/Documents:$TARGET_HOME/Documents:ro
|
|
200
201
|
# section) to a directory that is mounted read-only.
|
201
202
|
#
|
202
203
|
# The value has two parts separated by "->" with the LHS of this being the source that
|
203
|
-
# is to be copied while the RHS is the required relative path in the
|
204
|
-
#
|
204
|
+
# is to be copied while the RHS is the required relative path in the container's home directory.
|
205
|
+
# Any spaces around "->" are excluded from the LHS and RHS names.
|
205
206
|
# Source is skipped if it does not exist or not readable with a message on standard output.
|
206
207
|
#
|
207
208
|
# Typically this will contain shell, vim and other common configuration files.
|
208
209
|
# These can be either files or directories and are skipped if they do not exist.
|
209
|
-
# The keys here
|
210
|
-
# unique and can be used to override in later files that include this one.
|
210
|
+
# The keys here should be unique and can be used to override in later files that include this one.
|
211
211
|
#
|
212
|
-
#
|
213
|
-
#
|
214
|
-
#
|
212
|
+
# Two special suffixes can be used in the key names:
|
213
|
+
# 1) ":dir" : this suffix indicates that the source is a directory and entire directory structure
|
214
|
+
# should be replicated in the target with symlinks only for the individual files;
|
215
|
+
# this helps in cases where user needs to make a separate copy of a file inside
|
216
|
+
# the directory (using either ":copy" suffix or manually)
|
217
|
+
# 2) ":copy": this suffix indicates that the source should be copied to the target; if a
|
218
|
+
# file/directory to be copied lies inside another directory that is being linked,
|
219
|
+
# then it should be mentioned before this and marked with ":dir" so that directory
|
220
|
+
# structure is replicated in the target (see example of fish shell config below)
|
221
|
+
#
|
222
|
+
# Note: The files from the host are mounted read-only by default in the target container, so if
|
223
|
+
# you need to change a file within a container then you can use the ":copy" suffix in the key name
|
224
|
+
# or manually remove the symlink and make a copy of the file. This will remove the
|
215
225
|
# direct sharing between the two which has to be done manually thereon if required.
|
216
226
|
# The sharing behavior also depends on "config_hardlinks" as described in its comment above
|
217
227
|
# in the [base] section.
|
218
228
|
#
|
219
|
-
# Note: The HOME
|
220
|
-
#
|
221
|
-
#
|
229
|
+
# Note: The LHS should typically have a path having $HOME while RHS will be relative to the
|
230
|
+
# target's home inside the container. Do not use $TARGET_HOME on RHS (or LHS for that matter)
|
231
|
+
# which is the target's home as on the host and not the one inside the container.
|
222
232
|
[configs]
|
223
233
|
bashrc = $HOME/.bashrc -> .bashrc
|
224
234
|
starship = $HOME/.config/starship.toml -> .config/starship.toml
|
225
|
-
|
235
|
+
# replicate fish configuration directory with copy of fish_variables but symlinks for the rest
|
236
|
+
fish_conf:dir = $HOME/.config/fish -> .config/fish
|
237
|
+
fish_vars:copy = $HOME/.config/fish/fish_variables -> .config/fish/fish_variables
|
226
238
|
omf = $HOME/.config/omf -> .config/omf
|
227
239
|
omf_data = $HOME/.local/share/omf -> .local/share/omf
|
228
240
|
zshrc = $HOME/.zshrc -> .zshrc
|
@@ -261,10 +273,11 @@ speechconf = $HOME/.config/speech-dispatcher -> .config/speech-dispatcher
|
|
261
273
|
[env]
|
262
274
|
TERM
|
263
275
|
TERMINFO_DIRS = /usr/share/terminfo:/var/lib/terminfo
|
264
|
-
|
265
|
-
|
276
|
+
# always pretend desktop to be GNOME since KDE/*DE apps required by xdg-* are not installed
|
277
|
+
XDG_CURRENT_DESKTOP = GNOME
|
278
|
+
XDG_SESSION_DESKTOP = GNOME
|
279
|
+
DESKTOP_SESSION = gnome
|
266
280
|
XDG_SESSION_TYPE
|
267
|
-
DESKTOP_SESSION
|
268
281
|
GTK_IM_MODULE
|
269
282
|
QT_IM_MODULE
|
270
283
|
SDL_IM_MODULE
|
ybox/conf/profiles/dev.ini
CHANGED
@@ -12,12 +12,6 @@ caps_add = SYS_PTRACE
|
|
12
12
|
#projects = $HOME/projects:$TARGET_HOME/projects
|
13
13
|
#pyenv = $HOME/.pyenv:$TARGET_HOME/.pyenv:ro
|
14
14
|
|
15
|
-
[env]
|
16
|
-
# always pretend desktop to be GNOME since KDE apps required by xdg-* are not installed
|
17
|
-
XDG_CURRENT_DESKTOP = GNOME
|
18
|
-
XDG_SESSION_DESKTOP = GNOME
|
19
|
-
DESKTOP_SESSION = gnome
|
20
|
-
|
21
15
|
[apps]
|
22
16
|
# some packages for Arch Linux - uncomment and update for your distribution as required
|
23
17
|
#ides = intellij-idea-community-edition-jre,visual-studio-code-bin,zed
|
@@ -8,6 +8,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
8
8
|
|
9
9
|
source "$SCRIPT_DIR/entrypoint-common.sh"
|
10
10
|
|
11
|
+
export HOME=/root
|
11
12
|
echo_color "$fg_cyan" "Copying prime-run, run-in-dir and run-user-bash-cmd" >> $status_file
|
12
13
|
cp -a "$SCRIPT_DIR/prime-run" /usr/local/bin/prime-run
|
13
14
|
cp -a "$SCRIPT_DIR/run-in-dir" /usr/local/bin/run-in-dir
|
@@ -13,9 +13,11 @@ mkdir -p "$user_home/.gnupg" && chmod 0700 "$user_home/.gnupg"
|
|
13
13
|
echo "keyserver $DEFAULT_GPG_KEY_SERVER" > "$user_home/.gnupg/dirmngr.conf"
|
14
14
|
rm -f "$user_home"/.gnupg/*/*.lock
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
if [ ! -e "$user_home/.config/pip/pip.conf" ]; then
|
17
|
+
echo_color "$fg_cyan" "Enabling python pip installation for $current_user" >> $status_file
|
18
|
+
mkdir -p "$user_home/.config/pip"
|
19
|
+
cat > "$user_home/.config/pip/pip.conf" << EOF
|
19
20
|
[global]
|
20
21
|
break-system-packages = true
|
21
22
|
EOF
|
23
|
+
fi
|
@@ -22,7 +22,7 @@ echo -n > $status_file
|
|
22
22
|
# the current user as well as the group of the normal user
|
23
23
|
uid="$(id -u)"
|
24
24
|
gid="$(id -g)"
|
25
|
-
chown $uid:$YBOX_HOST_GID $status_file
|
25
|
+
chown $uid:${YBOX_HOST_GID:-$gid} $status_file
|
26
26
|
chmod 0660 $status_file
|
27
27
|
if [ "$uid" -eq 0 ]; then
|
28
28
|
SUDO=""
|
@@ -47,16 +47,14 @@ function show_usage() {
|
|
47
47
|
echo " -h show this help message and exit"
|
48
48
|
}
|
49
49
|
|
50
|
-
# link the configuration files in HOME to the target directory having the required files
|
51
|
-
function
|
50
|
+
# copy/link the configuration files in HOME to the target directory having the required files
|
51
|
+
function replicate_config_files() {
|
52
52
|
# line is of the form <src> -> <dest>; pattern below matches this while trimming spaces
|
53
53
|
echo_color "$fg_orange" "Linking configuration files from $config_dir to user's home" >> $status_file
|
54
|
-
pattern='(
|
54
|
+
pattern='(COPY|LINK_DIR|LINK):(.*)'
|
55
55
|
while read -r config; do
|
56
56
|
if [[ "$config" =~ $pattern ]]; then
|
57
|
-
home_file="${BASH_REMATCH[
|
58
|
-
# expand env variables
|
59
|
-
eval home_file="$home_file"
|
57
|
+
home_file="$HOME/${BASH_REMATCH[2]}"
|
60
58
|
dest_file="$config_dir/${BASH_REMATCH[2]}"
|
61
59
|
# only replace the file if it is already a link (assuming the link target may
|
62
60
|
# have changed in the config_list file), or a directory containing links
|
@@ -80,7 +78,15 @@ function link_config_files() {
|
|
80
78
|
mkdir -p "$home_filedir"
|
81
79
|
fi
|
82
80
|
if [ ! -e "$home_file" ]; then
|
83
|
-
|
81
|
+
if [ "${BASH_REMATCH[1]}" = "COPY" ]; then
|
82
|
+
cp -r "$dest_file" "$home_file"
|
83
|
+
elif [ "${BASH_REMATCH[1]}" = "LINK_DIR" ]; then
|
84
|
+
# if dest_file is a directory, then replicate the directory structure in home and
|
85
|
+
# symlink individual files which is accomplished with "cp -sr"
|
86
|
+
cp -sr "$dest_file" "$home_file"
|
87
|
+
else # "${BASH_REMATCH[1]}" = "LINK"
|
88
|
+
ln -s "$dest_file" "$home_file"
|
89
|
+
fi
|
84
90
|
fi
|
85
91
|
fi
|
86
92
|
else
|
@@ -180,7 +186,7 @@ run_dir=${XDG_RUNTIME_DIR:-/run/user/$uid}
|
|
180
186
|
if [ -d $run_dir ]; then
|
181
187
|
$SUDO chown $uid:$gid $run_dir 2>/dev/null || true
|
182
188
|
fi
|
183
|
-
if
|
189
|
+
if compgen -G "$run_dir/*" >/dev/null; then
|
184
190
|
$SUDO chown $uid:$gid $run_dir/* 2>/dev/null || true
|
185
191
|
fi
|
186
192
|
|
@@ -212,7 +218,7 @@ fi
|
|
212
218
|
|
213
219
|
# process config files, application installs and invoke startup apps
|
214
220
|
if [ -n "$config_list" ]; then
|
215
|
-
|
221
|
+
replicate_config_files
|
216
222
|
fi
|
217
223
|
if [ -n "$app_list" ]; then
|
218
224
|
install_apps
|
@@ -231,14 +237,17 @@ function cleanup() {
|
|
231
237
|
# clear status file first just in case other operations do not finish before SIGKILL comes
|
232
238
|
echo -n > $status_file
|
233
239
|
# first send SIGTERM to all "docker exec" processes that will have parent PID as 0 or 1
|
240
|
+
kill_sent=0
|
234
241
|
exec_pids="$(ps -e -o ppid=,pid= | \
|
235
242
|
awk '{ if (($1 == 0 || $1 == 1) && $2 != 1 && $2 != '$childPID') print $2 }')"
|
236
243
|
for pid in $exec_pids; do
|
237
|
-
|
238
|
-
|
244
|
+
if kill -TERM $pid 2>/dev/null; then
|
245
|
+
kill_sent=1
|
246
|
+
echo "Sent SIGTERM to $pid"
|
247
|
+
fi
|
239
248
|
done
|
240
249
|
# sleep a bit for $exec_pids to finish
|
241
|
-
[ -
|
250
|
+
[ $kill_sent -eq 1 ] && sleep 3
|
242
251
|
# lastly kill the infinite tail process
|
243
252
|
kill -TERM $childPID
|
244
253
|
}
|
ybox/conf/resources/prime-run
CHANGED
ybox/conf/resources/run-in-dir
CHANGED
@@ -49,6 +49,7 @@ if [ -e "$nvidia_setup" ]; then
|
|
49
49
|
flock -x -w 60 $lock_fd || /bin/true
|
50
50
|
trap "flock -u $lock_fd || /bin/true" 0 1 2 3 4 5 6 7 8 10 11 12 13 14 15
|
51
51
|
if ! is_nvidia_valid; then
|
52
|
+
# set umask for root execution to ensure that other users have read/execute permissions
|
52
53
|
umask 022
|
53
54
|
sudo /bin/bash "$nvidia_setup" || /bin/true
|
54
55
|
fi
|
@@ -57,4 +58,7 @@ if [ -e "$nvidia_setup" ]; then
|
|
57
58
|
fi
|
58
59
|
fi
|
59
60
|
|
61
|
+
# reset to more conservative umask setting
|
62
|
+
umask 027
|
63
|
+
|
60
64
|
exec "$@"
|
@@ -1,5 +1,11 @@
|
|
1
1
|
#!/bin/bash
|
2
2
|
|
3
|
+
# This script either runs a given command using bash directly, or if YBOX_HOST_UID environment
|
4
|
+
# variable is set to something other than the UID of the current user, then uses sudo to run
|
5
|
+
# the command as that UID (plus YBOX_HOST_GID as the GID). This latter case happens for rootless
|
6
|
+
# docker where the ybox container runs as the root user (due to absence of --userns=keep-id like
|
7
|
+
# in podman) but some commands need to be run as a normal user like Arch's paru/yay AUR helpers.
|
8
|
+
|
3
9
|
set -e
|
4
10
|
|
5
11
|
if [ "$#" -ne 1 ]; then
|
@@ -8,7 +14,17 @@ if [ "$#" -ne 1 ]; then
|
|
8
14
|
fi
|
9
15
|
|
10
16
|
if [ "$(id -u)" -eq 0 -a -n "$YBOX_HOST_UID" ] && getent passwd $YBOX_HOST_UID > /dev/null; then
|
11
|
-
|
17
|
+
sudo -u "#$YBOX_HOST_UID" -g "#$YBOX_HOST_GID" /bin/bash -c "$1"
|
18
|
+
status=$?
|
19
|
+
if [ $status -ne 0 ]; then
|
20
|
+
echo "FAILED (exit code = $status) in sudo execution of: $1"
|
21
|
+
exit $status
|
22
|
+
fi
|
12
23
|
else
|
13
24
|
eval "$1"
|
25
|
+
status=$?
|
26
|
+
if [ $status -ne 0 ]; then
|
27
|
+
echo "FAILED (exit code = $status) in execution of: $1"
|
28
|
+
exit $status
|
29
|
+
fi
|
14
30
|
fi
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# ybox-{name}.service
|
2
|
+
#
|
3
|
+
# Autogenerated by ybox-create {version} on {date}
|
4
|
+
|
5
|
+
[Unit]
|
6
|
+
Description={manager_name} ybox-{name}.service
|
7
|
+
Wants=network-online.target
|
8
|
+
After=network-online.target
|
9
|
+
{docker_requires}
|
10
|
+
[Service]
|
11
|
+
EnvironmentFile={env_file}
|
12
|
+
Type=forking
|
13
|
+
Restart=on-failure
|
14
|
+
TimeoutStopSec=70
|
15
|
+
# sleep to allow for initialization of the user's login/graphical environment
|
16
|
+
ExecStartPre=/usr/bin/sleep $SLEEP_SECS
|
17
|
+
ExecStart=/bin/sh -c 'ybox-control start {name}'
|
18
|
+
ExecStop=/bin/sh -c 'ybox-control stop -t 20 --ignore-stopped {name}'
|
19
|
+
ExecStopPost=/bin/sh -c 'ybox-control stop -t 20 --ignore-stopped {name}'
|
20
|
+
{pid_file}
|
21
|
+
[Install]
|
22
|
+
WantedBy=default.target
|
ybox/config.py
CHANGED
@@ -142,6 +142,9 @@ class Consts:
|
|
142
142
|
Defines fixed file/path and other names used by ybox that are not configurable.
|
143
143
|
"""
|
144
144
|
|
145
|
+
# standard system executable paths
|
146
|
+
_SYS_BIN_DIRS = ("/usr/bin", "/bin", "/usr/sbin", "/sbin", "/usr/local/bin", "/usr/local/sbin")
|
147
|
+
# regex pattern to match all manual page directories
|
145
148
|
_MAN_DIRS_PATTERN = re.compile(r"/usr(/local)?(/share)?/man(/[^/]*)?/man[0-9][a-zA-Z_]*")
|
146
149
|
|
147
150
|
@staticmethod
|
@@ -216,13 +219,18 @@ class Consts:
|
|
216
219
|
@staticmethod
|
217
220
|
def container_bin_dirs() -> Iterable[str]:
|
218
221
|
"""directories on the container that has executables that may need to be wrapped"""
|
219
|
-
return
|
222
|
+
return Consts._SYS_BIN_DIRS
|
220
223
|
|
221
224
|
@staticmethod
|
222
225
|
def container_man_dir_pattern() -> re.Pattern[str]:
|
223
226
|
"""directory regex pattern on the container having man-pages that may need to be linked"""
|
224
227
|
return Consts._MAN_DIRS_PATTERN
|
225
228
|
|
229
|
+
@staticmethod
|
230
|
+
def sys_bin_dirs() -> Iterable[str]:
|
231
|
+
"""standard directories to search for system installed executables"""
|
232
|
+
return Consts._SYS_BIN_DIRS
|
233
|
+
|
226
234
|
@staticmethod
|
227
235
|
def nvidia_target_base_dir() -> str:
|
228
236
|
"""base directory path where NVIDIA libs/data are linked in the container"""
|
ybox/pkg/clean.py
CHANGED
@@ -8,14 +8,10 @@ from configparser import SectionProxy
|
|
8
8
|
from ybox.cmd import PkgMgr, build_shell_command, run_command
|
9
9
|
from ybox.config import StaticConfiguration
|
10
10
|
from ybox.print import print_info
|
11
|
-
from ybox.state import RuntimeConfiguration, YboxStateManagement
|
12
11
|
|
13
12
|
|
14
|
-
# noinspection PyUnusedLocal
|
15
13
|
def clean_cache(args: argparse.Namespace, pkgmgr: SectionProxy, docker_cmd: str,
|
16
|
-
conf: StaticConfiguration
|
17
|
-
state: YboxStateManagement) -> int:
|
18
|
-
# pylint: disable=unused-argument
|
14
|
+
conf: StaticConfiguration) -> int:
|
19
15
|
"""
|
20
16
|
Clean package cache and related intermediate files.
|
21
17
|
|
@@ -23,8 +19,6 @@ def clean_cache(args: argparse.Namespace, pkgmgr: SectionProxy, docker_cmd: str,
|
|
23
19
|
:param pkgmgr: the `[pkgmgr]` section from `distro.ini` configuration file of the distribution
|
24
20
|
:param docker_cmd: the podman/docker executable to use
|
25
21
|
:param conf: the :class:`StaticConfiguration` for the container
|
26
|
-
:param runtime_conf: the `RuntimeConfiguration` of the container
|
27
|
-
:param state: instance of `YboxStateManagement` having the state of all ybox containers
|
28
22
|
:return: integer exit status of clean command where 0 represents success
|
29
23
|
"""
|
30
24
|
print_info(f"Cleaning package cache in container '{conf.box_name}'")
|
ybox/pkg/info.py
CHANGED
@@ -8,14 +8,10 @@ from configparser import SectionProxy
|
|
8
8
|
|
9
9
|
from ybox.cmd import PkgMgr, page_command
|
10
10
|
from ybox.config import StaticConfiguration
|
11
|
-
from ybox.state import RuntimeConfiguration, YboxStateManagement
|
12
11
|
|
13
12
|
|
14
|
-
# noinspection PyUnusedLocal
|
15
13
|
def info_packages(args: argparse.Namespace, pkgmgr: SectionProxy, docker_cmd: str,
|
16
|
-
conf: StaticConfiguration
|
17
|
-
state: YboxStateManagement) -> int:
|
18
|
-
# pylint: disable=unused-argument
|
14
|
+
conf: StaticConfiguration) -> int:
|
19
15
|
"""
|
20
16
|
Show detailed information of an installed or repository package(s).
|
21
17
|
|
@@ -23,8 +19,6 @@ def info_packages(args: argparse.Namespace, pkgmgr: SectionProxy, docker_cmd: st
|
|
23
19
|
:param pkgmgr: the `[pkgmgr]` section from `distro.ini` configuration file of the distribution
|
24
20
|
:param docker_cmd: the podman/docker executable to use
|
25
21
|
:param conf: the :class:`StaticConfiguration` for the container
|
26
|
-
:param runtime_conf: the `RuntimeConfiguration` of the container
|
27
|
-
:param state: instance of `YboxStateManagement` having the state of all ybox containers
|
28
22
|
:return: integer exit status of info command where 0 represents success
|
29
23
|
"""
|
30
24
|
quiet_flag = pkgmgr[PkgMgr.QUIET_DETAILS_FLAG.value] if args.quiet else ""
|
ybox/pkg/inst.py
CHANGED
@@ -27,7 +27,10 @@ from ybox.util import check_package, ini_file_reader, select_item_from_menu
|
|
27
27
|
_EXEC_RE = re.compile(r"^(\s*(Try)?Exec\s*=\s*)(\S+)\s*(.*?)\s*$")
|
28
28
|
# match !p and !a to replace executable program (third group above) and arguments respectively
|
29
29
|
_FLAGS_RE = re.compile("![ap]")
|
30
|
-
|
30
|
+
# environment variables passed through from host environment to podman/docker executable
|
31
|
+
_PASSTHROUGH_ENVVARS = ("XAUTHORITY", "DISPLAY", "WAYLAND_DISPLAY", "FREETYPE_PROPERTIES",
|
32
|
+
"__NV_PRIME_RENDER_OFFLOAD", "__GLX_VENDOR_LIBRARY_NAME",
|
33
|
+
"__VK_LAYER_NV_optimus", "VK_ICD_FILES", "VK_ICD_FILENAMES")
|
31
34
|
|
32
35
|
|
33
36
|
def install_package(args: argparse.Namespace, pkgmgr: SectionProxy, docker_cmd: str,
|
@@ -153,7 +156,9 @@ def _install_package(package: str, args: argparse.Namespace, install_cmd: str, l
|
|
153
156
|
if not skip_desktop_files:
|
154
157
|
copy_type |= CopyType.DESKTOP
|
155
158
|
if not skip_executables:
|
156
|
-
|
159
|
+
resp = input("Create application executable(s)? (Y/n) ") if quiet == 0 else "Y"
|
160
|
+
if resp.strip().lower() != "n":
|
161
|
+
copy_type |= CopyType.EXECUTABLE
|
157
162
|
# TODO: wrappers for newly installed required dependencies should also be created;
|
158
163
|
# handle DependencyType.SUGGESTION if supported by underlying package manager
|
159
164
|
app_flags: dict[str, str] = {}
|
@@ -223,13 +228,14 @@ def get_optional_deps(package: str, docker_cmd: str, container_name: str,
|
|
223
228
|
# 2) redirect PKG: lines somewhere else like a common file: this can be done but will
|
224
229
|
# likely be more messy than the code below (e.g. handle concurrent executions),
|
225
230
|
# but still can be considered in future
|
231
|
+
# (reduced bufsize to show download progress better)
|
226
232
|
with subprocess.Popen(build_shell_command(
|
227
233
|
docker_cmd, container_name, f"{opt_deps_cmd} {package}"),
|
228
|
-
stdout=subprocess.PIPE) as deps_result:
|
234
|
+
bufsize=256, stdout=subprocess.PIPE) as deps_result:
|
229
235
|
line = bytearray()
|
230
236
|
# possible end of lines
|
231
|
-
eol1 = b"\r"
|
232
|
-
eol2 = b"\n"
|
237
|
+
eol1 = ord(b"\r")
|
238
|
+
eol2 = ord(b"\n")
|
233
239
|
buffered = 0
|
234
240
|
assert deps_result.stdout is not None
|
235
241
|
# readline does not work for in-place updates like from aria2
|
@@ -245,7 +251,7 @@ def get_optional_deps(package: str, docker_cmd: str, container_name: str,
|
|
245
251
|
break
|
246
252
|
else:
|
247
253
|
line.append(char[0])
|
248
|
-
if buffered >=
|
254
|
+
if buffered >= 8: # flush frequently to show download progress, for example
|
249
255
|
sys.stdout.flush()
|
250
256
|
buffered = 0
|
251
257
|
sys.stdout.flush()
|
@@ -419,7 +425,7 @@ def docker_cp_action(docker_cmd: str, box_name: str, src: str,
|
|
419
425
|
with tempfile.TemporaryDirectory() as temp_dir:
|
420
426
|
# use shell pipe and tar instead of python Popen and tarfile which will require much more
|
421
427
|
# code unncessarily and may not be able to use `run_command`
|
422
|
-
shell_cmd = (f"'{docker_cmd}' exec '{box_name}' tar -C '{src_dir}' -
|
428
|
+
shell_cmd = (f"'{docker_cmd}' exec '{box_name}' tar -C '{src_dir}' -chpf - '{src_file}' | "
|
423
429
|
f"tar -C '{temp_dir}' -xpf -")
|
424
430
|
if (code := int(run_command(["/bin/sh", "-c", shell_cmd], exit_on_error=False,
|
425
431
|
error_msg=f"copying of file from '{box_name}:{src}'"))) == 0:
|
@@ -457,9 +463,9 @@ def _wrap_desktop_file(filename: str, file: str, docker_cmd: str, conf: StaticCo
|
|
457
463
|
else:
|
458
464
|
full_cmd = program
|
459
465
|
# pseudo-tty cannot be allocated with rootless docker outside of a terminal app
|
460
|
-
|
461
|
-
|
462
|
-
f'"" {full_cmd}\n')
|
466
|
+
env_vars = " -e=".join(_PASSTHROUGH_ENVVARS)
|
467
|
+
return (f'{match.group(1)}{docker_cmd} exec -e={env_vars} {conf.box_name} '
|
468
|
+
f'/usr/local/bin/run-in-dir "" {full_cmd}\n')
|
463
469
|
|
464
470
|
# the destination will be $HOME/.local/share/applications
|
465
471
|
os.makedirs(conf.env.user_applications_dir, mode=Consts.default_directory_mode(),
|
@@ -568,9 +574,9 @@ def _can_wrap_executable(filename: str, file: str, conf: StaticConfiguration, qu
|
|
568
574
|
print_warn(f"Skipping local wrapper for {file}")
|
569
575
|
return False
|
570
576
|
# also check if creating user executable will override system executable
|
571
|
-
for bin_dir in
|
577
|
+
for bin_dir in Consts.sys_bin_dirs():
|
572
578
|
sys_exec = f"{bin_dir}/{filename}"
|
573
|
-
if os.
|
579
|
+
if os.access(sys_exec, os.X_OK):
|
574
580
|
resp = input(f"Target file {wrapper_exec} will override system installed "
|
575
581
|
f"{sys_exec}. Continue? (y/N) ") if quiet < 2 else "N"
|
576
582
|
if resp.strip().lower() != "y":
|
@@ -604,9 +610,9 @@ def _wrap_executable(filename: str, file: str, docker_cmd: str, conf: StaticConf
|
|
604
610
|
lambda f_match: _replace_flags(f_match, flags, f'"{file}"', '"$@"'), flags)
|
605
611
|
else:
|
606
612
|
full_cmd = f'/usr/local/bin/run-in-dir "`pwd`" "{file}" "$@"'
|
613
|
+
env_vars = " -e=".join(_PASSTHROUGH_ENVVARS)
|
607
614
|
exec_content = ("#!/bin/sh\n",
|
608
|
-
f"exec {docker_cmd} exec -it -e=
|
609
|
-
f"-e=FREETYPE_PROPERTIES {conf.box_name} ", full_cmd)
|
615
|
+
f"exec {docker_cmd} exec -it -e={env_vars} {conf.box_name} ", full_cmd)
|
610
616
|
with open(wrapper_exec, "w", encoding="utf-8") as wrapper_fd:
|
611
617
|
wrapper_fd.writelines(exec_content)
|
612
618
|
os.chmod(wrapper_exec, mode=0o755, follow_symlinks=True)
|
ybox/pkg/list.py
CHANGED
@@ -164,11 +164,8 @@ def _format_long_line(line: str, separator: str, dep_of_width: int,
|
|
164
164
|
return name, version, dep_of, description
|
165
165
|
|
166
166
|
|
167
|
-
# noinspection PyUnusedLocal
|
168
167
|
def list_files(args: argparse.Namespace, pkgmgr: SectionProxy, docker_cmd: str,
|
169
|
-
conf: StaticConfiguration
|
170
|
-
state: YboxStateManagement) -> int:
|
171
|
-
# pylint: disable=unused-argument
|
168
|
+
conf: StaticConfiguration) -> int:
|
172
169
|
"""
|
173
170
|
List the files of a package installed in a container including those not managed by `ybox-pkg`.
|
174
171
|
|
@@ -176,8 +173,6 @@ def list_files(args: argparse.Namespace, pkgmgr: SectionProxy, docker_cmd: str,
|
|
176
173
|
:param pkgmgr: the `[pkgmgr]` section from `distro.ini` configuration file of the distribution
|
177
174
|
:param docker_cmd: the podman/docker executable to use
|
178
175
|
:param conf: the :class:`StaticConfiguration` for the container
|
179
|
-
:param runtime_conf: the `RuntimeConfiguration` of the container
|
180
|
-
:param state: instance of `YboxStateManagement` having the state of all ybox containers
|
181
176
|
:return: integer exit status of list package files command where 0 represents success
|
182
177
|
"""
|
183
178
|
package: str = args.package
|
ybox/pkg/repair.py
CHANGED
@@ -14,6 +14,10 @@ from ybox.print import (fgcolor, print_color, print_error, print_info,
|
|
14
14
|
print_warn)
|
15
15
|
from ybox.state import RuntimeConfiguration, YboxStateManagement
|
16
16
|
|
17
|
+
# TODO: SW: repair should work even if no containers are active (just remove the locks)
|
18
|
+
# Also, just checking lock file existing lock file existence is not enough (e.g. for dpkg/apt)
|
19
|
+
# and may need to check if they are locked by any process after kill so define this in distro.ini
|
20
|
+
|
17
21
|
|
18
22
|
def repair_package_state(args: argparse.Namespace, pkgmgr: SectionProxy, docker_cmd: str,
|
19
23
|
conf: StaticConfiguration, runtime_conf: RuntimeConfiguration,
|
ybox/pkg/search.py
CHANGED
@@ -8,14 +8,10 @@ from configparser import SectionProxy
|
|
8
8
|
|
9
9
|
from ybox.cmd import PkgMgr, page_command
|
10
10
|
from ybox.config import StaticConfiguration
|
11
|
-
from ybox.state import RuntimeConfiguration, YboxStateManagement
|
12
11
|
|
13
12
|
|
14
|
-
# noinspection PyUnusedLocal
|
15
13
|
def search_packages(args: argparse.Namespace, pkgmgr: SectionProxy, docker_cmd: str,
|
16
|
-
conf: StaticConfiguration
|
17
|
-
state: YboxStateManagement) -> int:
|
18
|
-
# pylint: disable=unused-argument
|
14
|
+
conf: StaticConfiguration) -> int:
|
19
15
|
"""
|
20
16
|
Uninstall package specified by `args.package` on a ybox container with given podman/docker
|
21
17
|
command. Additional flags honored are `args.quiet` to bypass user confirmation during
|
@@ -27,8 +23,6 @@ def search_packages(args: argparse.Namespace, pkgmgr: SectionProxy, docker_cmd:
|
|
27
23
|
:param pkgmgr: the `[pkgmgr]` section from `distro.ini` configuration file of the distribution
|
28
24
|
:param docker_cmd: the podman/docker executable to use
|
29
25
|
:param conf: the :class:`StaticConfiguration` for the container
|
30
|
-
:param runtime_conf: the `RuntimeConfiguration` of the container
|
31
|
-
:param state: instance of `YboxStateManagement` having the state of all ybox containers
|
32
26
|
:return: integer exit status of search command where 0 represents success
|
33
27
|
"""
|
34
28
|
quiet_flag = pkgmgr[PkgMgr.QUIET_DETAILS_FLAG.value] if args.quiet else ""
|