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 CHANGED
@@ -1,2 +1,2 @@
1
1
  """`ybox` is a tool to easily manage linux distributions in containers"""
2
- __version__ = "0.9.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)
@@ -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="$(eval echo "~$current_user")"
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
@@ -6,6 +6,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
6
 
7
7
  source "$SCRIPT_DIR/entrypoint-common.sh"
8
8
 
9
+ export HOME=/root
9
10
  # pacman configuration
10
11
  PAC="pacman --noconfirm"
11
12
  echo_color "$fg_cyan" "Configuring pacman" >> $status_file
@@ -17,6 +17,8 @@ where:
17
17
  newlines in the description
18
18
  """
19
19
 
20
+ # TODO: SW: this is returning back installed packages too which should be skipped
21
+
20
22
  import gzip
21
23
  import os
22
24
  import re
@@ -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 should normlly be selected for installation
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,
@@ -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 target directory.
204
- # On the target container the same is used to symlink from the target on RHS to source on LHS.
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 have no special significance other than the fact that they should be
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
- # Note: The files are symlinks in the container user area and are mounted on a read-only
213
- # mount by default, so if you need to change a file within a container then you will
214
- # need to first remove the symlink and make a copy of the file. This will remove the
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 environment variable here will be evaluated both in the host
220
- # session (for the source to be transferred) and inside the container session
221
- # (for the target). Do not use TARGET_HOME here which can be incorrect for the host session.
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
- fishrc = $HOME/.config/fish -> .config/fish
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
- XDG_CURRENT_DESKTOP
265
- XDG_SESSION_DESKTOP
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
@@ -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
- echo_color "$fg_cyan" "Enabling python pip installation for $current_user" >> $status_file
17
- mkdir -p "$user_home/.config/pip"
18
- cat > "$user_home/.config/pip/pip.conf" << EOF
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 link_config_files() {
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='(.*[^[:space:]]+)[[:space:]]*->[[:space:]]*(.*)'
54
+ pattern='(COPY|LINK_DIR|LINK):(.*)'
55
55
  while read -r config; do
56
56
  if [[ "$config" =~ $pattern ]]; then
57
- home_file="${BASH_REMATCH[1]}"
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
- ln -s "$dest_file" "$home_file"
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 [ -n "$(ls $run_dir 2>/dev/null)" ]; then
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
- link_config_files
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
- echo "Sending SIGTERM to $pid"
238
- kill -TERM $pid
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
- [ -n "$exec_pids" ] && sleep 3
250
+ [ $kill_sent -eq 1 ] && sleep 3
242
251
  # lastly kill the infinite tail process
243
252
  kill -TERM $childPID
244
253
  }
@@ -1,7 +1,5 @@
1
1
  #!/bin/sh
2
2
 
3
- set -e
4
-
5
3
  __NV_PRIME_RENDER_OFFLOAD=1
6
4
  __GLX_VENDOR_LIBRARY_NAME=nvidia
7
5
  __VK_LAYER_NV_optimus=NVIDIA_only
@@ -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
- exec sudo -u "#$YBOX_HOST_UID" -g "#$YBOX_HOST_GID" /bin/bash -c "$1"
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 ("/usr/bin", "/usr/sbin", "/bin", "/sbin", "/usr/local/bin", "/usr/local/sbin")
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, runtime_conf: RuntimeConfiguration,
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, runtime_conf: RuntimeConfiguration,
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
- _LOCAL_BIN_DIRS = ["/usr/bin", "/bin", "/usr/sbin", "/sbin", "/usr/local/bin", "/usr/local/sbin"]
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
- copy_type |= CopyType.EXECUTABLE
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"[0]
232
- eol2 = b"\n"[0]
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 >= 4: # flush frequently to show download progress, for example
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}' -cpf - '{src_file}' | "
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
- return (f'{match.group(1)}{docker_cmd} exec -e=XAUTHORITY -e=DISPLAY '
461
- f'-e=FREETYPE_PROPERTIES {conf.box_name} /usr/local/bin/run-in-dir '
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 _LOCAL_BIN_DIRS:
577
+ for bin_dir in Consts.sys_bin_dirs():
572
578
  sys_exec = f"{bin_dir}/{filename}"
573
- if os.path.exists(sys_exec):
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=XAUTHORITY -e=DISPLAY "
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, runtime_conf: RuntimeConfiguration,
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, runtime_conf: RuntimeConfiguration,
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 ""