ybox 0.9.8__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 +2 -0
- ybox/cmd.py +307 -0
- ybox/conf/completions/ybox.fish +93 -0
- ybox/conf/distros/arch/add-gpg-key.sh +29 -0
- ybox/conf/distros/arch/distro.ini +192 -0
- ybox/conf/distros/arch/init-base.sh +10 -0
- ybox/conf/distros/arch/init-user.sh +35 -0
- ybox/conf/distros/arch/init.sh +82 -0
- ybox/conf/distros/arch/list_fmt_long.py +76 -0
- ybox/conf/distros/arch/pkgdeps.py +276 -0
- ybox/conf/distros/deb-generic/check-package.sh +77 -0
- ybox/conf/distros/deb-generic/distro.ini +190 -0
- ybox/conf/distros/deb-generic/fetch-gpg-key-id.sh +30 -0
- ybox/conf/distros/deb-generic/init-base.sh +11 -0
- ybox/conf/distros/deb-generic/init-user.sh +3 -0
- ybox/conf/distros/deb-generic/init.sh +136 -0
- ybox/conf/distros/deb-generic/list_fmt_long.py +114 -0
- ybox/conf/distros/deb-generic/pkgdeps.py +208 -0
- ybox/conf/distros/deb-oldstable/distro.ini +21 -0
- ybox/conf/distros/deb-stable/distro.ini +21 -0
- ybox/conf/distros/supported.list +5 -0
- ybox/conf/distros/ubuntu2204/distro.ini +21 -0
- ybox/conf/distros/ubuntu2404/distro.ini +21 -0
- ybox/conf/profiles/apps.ini +26 -0
- ybox/conf/profiles/basic.ini +310 -0
- ybox/conf/profiles/dev.ini +25 -0
- ybox/conf/profiles/games.ini +39 -0
- ybox/conf/resources/entrypoint-base.sh +170 -0
- ybox/conf/resources/entrypoint-common.sh +23 -0
- ybox/conf/resources/entrypoint-cp.sh +32 -0
- ybox/conf/resources/entrypoint-root.sh +20 -0
- ybox/conf/resources/entrypoint-user.sh +21 -0
- ybox/conf/resources/entrypoint.sh +249 -0
- ybox/conf/resources/prime-run +13 -0
- ybox/conf/resources/run-in-dir +60 -0
- ybox/conf/resources/run-user-bash-cmd +14 -0
- ybox/config.py +255 -0
- ybox/env.py +205 -0
- ybox/filelock.py +77 -0
- ybox/migrate/0.9.0-0.9.7:0.9.8.py +33 -0
- ybox/pkg/__init__.py +0 -0
- ybox/pkg/clean.py +33 -0
- ybox/pkg/info.py +40 -0
- ybox/pkg/inst.py +638 -0
- ybox/pkg/list.py +191 -0
- ybox/pkg/mark.py +68 -0
- ybox/pkg/repair.py +150 -0
- ybox/pkg/repo.py +251 -0
- ybox/pkg/search.py +52 -0
- ybox/pkg/uninst.py +92 -0
- ybox/pkg/update.py +56 -0
- ybox/print.py +121 -0
- ybox/run/__init__.py +0 -0
- ybox/run/cmd.py +54 -0
- ybox/run/control.py +102 -0
- ybox/run/create.py +1116 -0
- ybox/run/destroy.py +64 -0
- ybox/run/graphics.py +367 -0
- ybox/run/logs.py +57 -0
- ybox/run/ls.py +64 -0
- ybox/run/pkg.py +445 -0
- ybox/schema/0.9.1-added.sql +27 -0
- ybox/schema/0.9.6-added.sql +18 -0
- ybox/schema/init.sql +39 -0
- ybox/schema/migrate/0.9.0:0.9.1.sql +42 -0
- ybox/schema/migrate/0.9.1:0.9.2.sql +8 -0
- ybox/schema/migrate/0.9.2:0.9.3.sql +2 -0
- ybox/schema/migrate/0.9.5:0.9.6.sql +2 -0
- ybox/state.py +914 -0
- ybox/util.py +351 -0
- ybox-0.9.8.dist-info/LICENSE +19 -0
- ybox-0.9.8.dist-info/METADATA +533 -0
- ybox-0.9.8.dist-info/RECORD +76 -0
- ybox-0.9.8.dist-info/WHEEL +5 -0
- ybox-0.9.8.dist-info/entry_points.txt +8 -0
- ybox-0.9.8.dist-info/top_level.txt +1 -0
ybox/__init__.py
ADDED
ybox/cmd.py
ADDED
@@ -0,0 +1,307 @@
|
|
1
|
+
"""
|
2
|
+
Utilities related to command execution like running a command, get podman/docker executable etc.
|
3
|
+
"""
|
4
|
+
|
5
|
+
import argparse
|
6
|
+
import errno
|
7
|
+
import shlex
|
8
|
+
import subprocess
|
9
|
+
import sys
|
10
|
+
from enum import Enum
|
11
|
+
from typing import Callable, Iterable, Optional, Union
|
12
|
+
|
13
|
+
from ybox.config import Consts
|
14
|
+
|
15
|
+
from .print import print_error, print_info, print_notice, print_warn
|
16
|
+
|
17
|
+
|
18
|
+
class PkgMgr(str, Enum):
|
19
|
+
"""
|
20
|
+
Package manager actions that are defined for each Linux distribution in its distro.ini file.
|
21
|
+
"""
|
22
|
+
INSTALL = "install"
|
23
|
+
CHECK_AVAIL = "check_avail"
|
24
|
+
CHECK_INSTALL = "check_install"
|
25
|
+
QUIET_FLAG = "quiet_flag"
|
26
|
+
QUIET_DETAILS_FLAG = "quiet_details_flag"
|
27
|
+
OPT_DEPS = "opt_deps"
|
28
|
+
OPT_DEP_FLAG = "opt_dep_flag"
|
29
|
+
UNINSTALL = "uninstall"
|
30
|
+
PURGE_FLAG = "purge_flag"
|
31
|
+
REMOVE_DEPS_FLAG = "remove_deps_flag"
|
32
|
+
ORPHANS = "orphans"
|
33
|
+
UPDATE_META = "update_meta"
|
34
|
+
UPDATE = "update"
|
35
|
+
UPDATE_ALL = "update_all"
|
36
|
+
CLEAN = "clean"
|
37
|
+
CLEAN_QUIET = "clean_quiet"
|
38
|
+
MARK_EXPLICIT = "mark_explicit"
|
39
|
+
INFO = "info"
|
40
|
+
INFO_ALL = "info_all"
|
41
|
+
LIST = "list"
|
42
|
+
LIST_ALL = "list_all"
|
43
|
+
LIST_LONG = "list_long"
|
44
|
+
LIST_ALL_LONG = "list_all_long"
|
45
|
+
LIST_FILES = "list_files"
|
46
|
+
SEARCH = "search"
|
47
|
+
SEARCH_ALL = "search_all"
|
48
|
+
SEARCH_OFFICIAL_FLAG = "search_official_flag"
|
49
|
+
SEARCH_WORD_START_FLAG = "search_word_start_flag"
|
50
|
+
SEARCH_WORD_END_FLAG = "search_word_end_flag"
|
51
|
+
PROCESSES_PATTERN = "processes_pattern"
|
52
|
+
LOCKS_PATTERN = "locks_pattern"
|
53
|
+
REPAIR = "repair"
|
54
|
+
REPAIR_ALL = "repair_all"
|
55
|
+
|
56
|
+
|
57
|
+
class RepoCmd(str, Enum):
|
58
|
+
"""
|
59
|
+
Repository management actions defined for each Linux distribution in its distro.ini file.
|
60
|
+
"""
|
61
|
+
EXISTS = "exists"
|
62
|
+
DEFAULT_GPG_KEY_SERVER = "default_gpg_key_server"
|
63
|
+
ADD_KEY = "add_key"
|
64
|
+
ADD_KEY_ID = "add_key_id"
|
65
|
+
ADD = "add"
|
66
|
+
ADD_SOURCE = "add_source"
|
67
|
+
REMOVE_KEY = "remove_key"
|
68
|
+
REMOVE = "remove"
|
69
|
+
|
70
|
+
|
71
|
+
class YboxLabel(str, Enum):
|
72
|
+
"""
|
73
|
+
Labels for ybox created objects.
|
74
|
+
"""
|
75
|
+
CONTAINER_LABEL_GROUP = "io.ybox.container"
|
76
|
+
CONTAINER_TYPE = f"{CONTAINER_LABEL_GROUP}.type"
|
77
|
+
CONTAINER_DISTRIBUTION = f"{CONTAINER_LABEL_GROUP}.distribution"
|
78
|
+
|
79
|
+
# ybox container types (first two are temporary ones)
|
80
|
+
CONTAINER_BASE = f"{CONTAINER_TYPE}=base"
|
81
|
+
CONTAINER_COPY = f"{CONTAINER_TYPE}=copy"
|
82
|
+
CONTAINER_PRIMARY = f"{CONTAINER_TYPE}=primary"
|
83
|
+
|
84
|
+
|
85
|
+
def get_ybox_state(docker_cmd: str, box_name: str, expected_states: Iterable[str],
|
86
|
+
exit_on_error: bool = False, state_msg: str = "") -> tuple[str, str]:
|
87
|
+
"""
|
88
|
+
Check if the given ybox container exists and is in one of the given states, or get the state
|
89
|
+
if the given `expected_states` is empty.
|
90
|
+
|
91
|
+
:param docker_cmd: the podman/docker executable to use
|
92
|
+
:param box_name: name of the ybox container
|
93
|
+
:param expected_states: Iterable of one or more expected states like 'running', 'exited';
|
94
|
+
empty value means any state is permissible which is returned
|
95
|
+
:param exit_on_error: whether to exit using `sys.exit` if the check fails
|
96
|
+
:param state_msg: string to be inserted in the error message "No...ybox container ..."
|
97
|
+
when `exit_on_error` is false, so this should be a display name for the
|
98
|
+
`expected_states` with a space at the start e.g. ' active', ' stopped'
|
99
|
+
:return: if `exit_on_error` is False, then return a tuple of (state, distribution) of the
|
100
|
+
container if it was in the set of `expected_states` or if `expected_states` was empty
|
101
|
+
"""
|
102
|
+
check_result = subprocess.run(
|
103
|
+
[docker_cmd, "inspect", "--type=container", '--format={{index .Config.Labels "' +
|
104
|
+
YboxLabel.CONTAINER_TYPE.value + '"}} {{index .Config.Labels "' +
|
105
|
+
YboxLabel.CONTAINER_DISTRIBUTION.value + '"}} {{.State.Status}}', box_name],
|
106
|
+
check=False, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL)
|
107
|
+
if check_result.returncode != 0:
|
108
|
+
if exit_on_error:
|
109
|
+
print_error(f"No{state_msg} ybox container '{box_name}' found")
|
110
|
+
sys.exit(check_result.returncode)
|
111
|
+
return tuple[str, str]()
|
112
|
+
result = check_result.stdout.decode("utf-8").split()
|
113
|
+
if len(result) == 3 and result[0] == "primary":
|
114
|
+
distribution = result[1]
|
115
|
+
state = result[2]
|
116
|
+
if not expected_states or state in expected_states:
|
117
|
+
return (state, distribution)
|
118
|
+
if exit_on_error:
|
119
|
+
print_error(f"Unexpected state for ybox container '{box_name}': {state}")
|
120
|
+
sys.exit(1)
|
121
|
+
return tuple[str, str]()
|
122
|
+
if exit_on_error:
|
123
|
+
print_error(f"Container '{box_name}' not a ybox container!")
|
124
|
+
sys.exit(1)
|
125
|
+
return tuple[str, str]()
|
126
|
+
|
127
|
+
|
128
|
+
def check_active_ybox(docker_cmd: str, box_name: str, exit_on_error: bool = False) -> bool:
|
129
|
+
"""
|
130
|
+
Check if the given ybox container is up and running.
|
131
|
+
|
132
|
+
:param docker_cmd: the podman/docker executable to use
|
133
|
+
:param box_name: name of the ybox container
|
134
|
+
:param exit_on_error: whether to exit using `sys.exit` if the check fails
|
135
|
+
:return: if `exit_on_error` is False, then return the result of verification as True or False
|
136
|
+
"""
|
137
|
+
return bool(get_ybox_state(docker_cmd, box_name, expected_states=("running",),
|
138
|
+
exit_on_error=exit_on_error, state_msg=" active"))
|
139
|
+
|
140
|
+
|
141
|
+
def check_ybox_exists(docker_cmd: str, box_name: str, exit_on_error: bool = False) -> bool:
|
142
|
+
"""
|
143
|
+
Check if the given ybox container exists in either active or inactive state.
|
144
|
+
|
145
|
+
:param docker_cmd: the podman/docker executable to use
|
146
|
+
:param box_name: name of the ybox container
|
147
|
+
:param exit_on_error: whether to exit using `sys.exit` if the check fails
|
148
|
+
:return: if `exit_on_error` is False, then return the result of verification as True or False
|
149
|
+
"""
|
150
|
+
return bool(get_ybox_state(docker_cmd, box_name, expected_states=(),
|
151
|
+
exit_on_error=exit_on_error))
|
152
|
+
|
153
|
+
|
154
|
+
def build_shell_command(docker_cmd: str, box_name: str, cmd: str,
|
155
|
+
enable_pty: bool = True) -> list[str]:
|
156
|
+
"""
|
157
|
+
Build a podman/docker command (as a list) to be run using `run-user-bash-cmd` in the given
|
158
|
+
ybox container (that in turn runs the command as non-root user using `sudo` with `/bin/bash`).
|
159
|
+
|
160
|
+
:param docker_cmd: the podman/docker executable to use
|
161
|
+
:param box_name: name of the ybox container
|
162
|
+
:param cmd: the command to be run in the container
|
163
|
+
:param enable_pty: if True then enable pseudo-pty allocation for the `podman/docker exec`
|
164
|
+
command and set interactive mode else no pty is allocated, defaults to True
|
165
|
+
:return: command to be executed (e.g. in `subprocess`) as a list of strings
|
166
|
+
"""
|
167
|
+
shell = f"/usr/local/bin/{Consts.run_user_bash_cmd()}"
|
168
|
+
if enable_pty:
|
169
|
+
return [docker_cmd, "exec", "-it", box_name, shell, cmd]
|
170
|
+
return [docker_cmd, "exec", box_name, shell, cmd]
|
171
|
+
|
172
|
+
|
173
|
+
def run_command(cmd: Union[str, list[str]], capture_output: bool = False,
|
174
|
+
exit_on_error: bool = True, error_msg: Optional[str] = None) -> Union[str, int]:
|
175
|
+
"""
|
176
|
+
Helper wrapper around `subprocess.run` to display failure message (in red foreground color)
|
177
|
+
for the case of failure, exit on failure and capturing and returning output if required.
|
178
|
+
|
179
|
+
:param cmd: the command to be run which can be either a list of strings, or a single string
|
180
|
+
which will be split like done by unix shell using `shlex.split`
|
181
|
+
:param capture_output: if True then capture stdout and return it but stderr is still displayed
|
182
|
+
on screen using `print_warn` method in purple foreground color
|
183
|
+
:param exit_on_error: whether to exit using `sys.exit` if command fails
|
184
|
+
:param error_msg: string to be inserted in error message "FAILURE in ..." so should be a
|
185
|
+
user-friendly name of the action that the command was supposed to do;
|
186
|
+
if not specified then the entire command string is displayed;
|
187
|
+
the special value 'SKIP' can be used to skip printing any error message
|
188
|
+
:return: the captured standard output if `capture_output` is true and command is successful
|
189
|
+
encoded as UTF-8 string else the return code of the command as an integer
|
190
|
+
(if `exit_on_error` is False)
|
191
|
+
"""
|
192
|
+
args = shlex.split(cmd) if isinstance(cmd, str) else cmd
|
193
|
+
try:
|
194
|
+
result = subprocess.run(args, capture_output=capture_output, check=False)
|
195
|
+
except OSError as err:
|
196
|
+
if exit_on_error:
|
197
|
+
raise # an unexpected internal issue, so keep the full stack trace
|
198
|
+
print_error(f"FAILURE invoking '{cmd}': {err}")
|
199
|
+
return err.errno or errno.ENOENT
|
200
|
+
if result.returncode != 0:
|
201
|
+
_print_subprocess_output(result)
|
202
|
+
if not error_msg:
|
203
|
+
error_msg = f"'{cmd}'"
|
204
|
+
if error_msg != "SKIP":
|
205
|
+
print_error(f"FAILURE in {error_msg} -- see the output above for details")
|
206
|
+
if exit_on_error:
|
207
|
+
sys.exit(result.returncode)
|
208
|
+
else:
|
209
|
+
return result.returncode
|
210
|
+
if capture_output and result.stderr:
|
211
|
+
print_warn(result.stderr.decode("utf-8"), file=sys.stderr)
|
212
|
+
return result.stdout.decode("utf-8") if capture_output else result.returncode
|
213
|
+
|
214
|
+
|
215
|
+
def _print_subprocess_output(result: subprocess.CompletedProcess[bytes]) -> None:
|
216
|
+
"""print completed subprocess output in color (orange for standard output and purple
|
217
|
+
for standard error)"""
|
218
|
+
if result.stdout:
|
219
|
+
print_notice(result.stdout.decode("utf-8"))
|
220
|
+
if result.stderr:
|
221
|
+
print_warn(result.stderr.decode("utf-8"), file=sys.stderr)
|
222
|
+
|
223
|
+
|
224
|
+
def parse_opt_deps_args(argv: list[str]) -> argparse.Namespace:
|
225
|
+
"""
|
226
|
+
Common command-line parser for `opt_deps` utilities (see [pkgmgr] section of distro.ini)
|
227
|
+
that parses given arguments for the program and returns the result :class:`argparse.Namespace`.
|
228
|
+
|
229
|
+
:param argv: the list of arguments to be parsed
|
230
|
+
:return: the result of parsing using the `argparse` library as a :class:`argparse.Namespace`
|
231
|
+
"""
|
232
|
+
parser = argparse.ArgumentParser(
|
233
|
+
description="Recursively find optional dependencies of a package")
|
234
|
+
# default separator is something that does not appear in descriptions (at least so far)
|
235
|
+
parser.add_argument("-s", "--separator", type=str, default="::::",
|
236
|
+
help="separator to use between the columns")
|
237
|
+
parser.add_argument("-p", "--prefix", type=str, default="",
|
238
|
+
help="prefix string before each line of result")
|
239
|
+
parser.add_argument("-H", "--header", type=str, default="",
|
240
|
+
help="header line to print before the results (without trailing newline)")
|
241
|
+
parser.add_argument("-l", "--level", type=int, default=2,
|
242
|
+
help="maximum level to search for optional dependencies")
|
243
|
+
parser.add_argument("package", type=str, help="name of the package")
|
244
|
+
return parser.parse_args(argv)
|
245
|
+
|
246
|
+
|
247
|
+
def page_output(output: Iterable[bytes], pager: str) -> int:
|
248
|
+
"""
|
249
|
+
Display an `Iterable` of bytes on the terminal one screenful at a time using the given `pager`.
|
250
|
+
|
251
|
+
:param output: the `Iterable` of `bytes` to be displayed (e.g. file/stream object, `list` etc)
|
252
|
+
:param pager: the command to be executed for pagination as a separate process with any
|
253
|
+
arguments that are required; the arguments are split using :func:`shlex.split`
|
254
|
+
so you can use shell-like quoting/escaping if they have spaces
|
255
|
+
:return: the exit code of the `pager` command
|
256
|
+
"""
|
257
|
+
if not pager:
|
258
|
+
for out in output:
|
259
|
+
sys.stdout.write(out.decode("utf-8"))
|
260
|
+
sys.stdout.flush()
|
261
|
+
return 0
|
262
|
+
try:
|
263
|
+
with subprocess.Popen(shlex.split(pager), stdin=subprocess.PIPE) as page_in:
|
264
|
+
assert page_in.stdin is not None
|
265
|
+
for out in output:
|
266
|
+
page_in.stdin.write(out)
|
267
|
+
page_in.communicate()
|
268
|
+
return page_in.returncode
|
269
|
+
except BrokenPipeError:
|
270
|
+
return 0 # this can happen if pager ends (e.g. using 'q' in less)
|
271
|
+
except OSError as err:
|
272
|
+
print_error(f"FAILURE invoking pager '{pager}': {err}")
|
273
|
+
return err.errno or errno.ENOENT
|
274
|
+
except KeyboardInterrupt:
|
275
|
+
# fail cleanly for user interrupt in the pager
|
276
|
+
print()
|
277
|
+
print_info("Interrupt")
|
278
|
+
return 130 # see https://tldp.org/LDP/abs/html/exitcodes.html
|
279
|
+
|
280
|
+
|
281
|
+
def page_command(cmd: Union[str, list[str]], pager: str, error_msg: Optional[str] = None,
|
282
|
+
transform: Optional[Callable[[str], str]] = None, header: str = "") -> int:
|
283
|
+
"""
|
284
|
+
Execute a given command using `subprocess.run` and show its output one screenful at a time
|
285
|
+
as UTF-8 string using the given `pager` command. In case of failure of either the command
|
286
|
+
or the `pager`, a failure message is shown and the exit code of the failed process is returned.
|
287
|
+
|
288
|
+
:param cmd: the command to be run which can be either a list of strings, or a single string
|
289
|
+
which will be split like done by unix shell using `shlex.split`
|
290
|
+
:param pager: the command to be executed for pagination as a separate process,
|
291
|
+
or empty to skip pagination
|
292
|
+
:param error_msg: string to be inserted in error message "FAILURE in ..." so should be a
|
293
|
+
user-friendly name of the action that the command was supposed to do;
|
294
|
+
if not specified then the entire command string is displayed;
|
295
|
+
the special value 'SKIP' can be used to skip printing any error message
|
296
|
+
:param transform: optional function to apply to the output of command before pagination
|
297
|
+
:param header: header to be inserted at the start of output, default is empty string
|
298
|
+
:return: the exit code of `cmd` if it failed else the exit code of the `pager` command
|
299
|
+
"""
|
300
|
+
result = run_command(cmd, capture_output=True, exit_on_error=False, error_msg=error_msg)
|
301
|
+
if isinstance(result, int):
|
302
|
+
return result
|
303
|
+
if not result:
|
304
|
+
return 0
|
305
|
+
result_bytes = transform(result).encode("utf-8") if transform else result.encode("utf-8")
|
306
|
+
output = (header.encode("utf-8"), result_bytes) if header else (result_bytes,)
|
307
|
+
return page_output(output, pager)
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# Completions for ybox commands, a "Manage containers hosting Linux distributions and apps"
|
2
|
+
|
3
|
+
function __fish_ybox_complete_containers
|
4
|
+
/usr/bin/python3 (type -p ybox-ls) --format="{{ .Names }}"
|
5
|
+
end
|
6
|
+
|
7
|
+
function __fish_ybox_complete_all_containers
|
8
|
+
/usr/bin/python3 (type -p ybox-ls) --all --format="{{ .Names }}"
|
9
|
+
end
|
10
|
+
|
11
|
+
function __fish_ybox_complete_stopped_containers
|
12
|
+
/usr/bin/python3 (type -p ybox-ls) --filter="status=exited" --format="{{ .Names }}"
|
13
|
+
end
|
14
|
+
|
15
|
+
function __fish_ybox_complete_distributions
|
16
|
+
set user_supported ~/.config/ybox/distros/supported.list
|
17
|
+
set sys_supported ~/.local/lib/python3*/site-packages/ybox/conf/distros/supported.list
|
18
|
+
set local_supported src/ybox/conf/distros/supported.list
|
19
|
+
if test -r "$user_supported"
|
20
|
+
/usr/bin/cat $user_supported
|
21
|
+
else if test -r "$sys_supported" 2>/dev/null
|
22
|
+
/usr/bin/cat $sys_supported
|
23
|
+
else if test -r "$local_supported" 2>/dev/null
|
24
|
+
/usr/bin/cat $local_supported
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
complete -f -c ybox-create -s h -l help -d "show help"
|
30
|
+
complete -c ybox-create -s n -l name -d "name of the ybox container" -r
|
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 q -l quiet -d "skip interactive questions"
|
33
|
+
complete -f -c ybox-create -n "not __fish_seen_subcommand_from (__fish_ybox_complete_distributions)" -a "(__fish_ybox_complete_distributions)"
|
34
|
+
|
35
|
+
|
36
|
+
complete -f -c ybox-destroy -s h -l help -d "show help"
|
37
|
+
complete -f -c ybox-destroy -s f -l force -d "force destroy the container using SIGKILL if required"
|
38
|
+
complete -f -c ybox-destroy -n "not __fish_seen_subcommand_from (__fish_ybox_complete_all_containers)" -a "(__fish_ybox_complete_all_containers)"
|
39
|
+
|
40
|
+
complete -f -c ybox-logs -s h -l help -d "show help"
|
41
|
+
complete -f -c ybox-logs -s f -l follow -d "follow log output like 'tail -f'"
|
42
|
+
complete -f -c ybox-logs -n "not __fish_seen_subcommand_from (__fish_ybox_complete_all_containers)" -a "(__fish_ybox_complete_all_containers)"
|
43
|
+
|
44
|
+
|
45
|
+
complete -f -c ybox-ls -s h -l help -d "show help"
|
46
|
+
complete -f -c ybox-ls -s a -l all -d "show all containers including stopped"
|
47
|
+
complete -f -c ybox-ls -s f -l filter -d "filter in <key>=<value> format" -r
|
48
|
+
complete -f -c ybox-ls -s s -l format -d "format output using a JSON/Go template string" -r
|
49
|
+
complete -f -c ybox-ls -s l -l long-format -d "show more extended information"
|
50
|
+
|
51
|
+
|
52
|
+
complete -c ybox-cmd -s h -l help -d "show help"
|
53
|
+
complete -c ybox-cmd -n "not __fish_seen_subcommand_from (__fish_ybox_complete_containers)" -a "(__fish_ybox_complete_containers)" -f
|
54
|
+
|
55
|
+
|
56
|
+
# define subcommands for ybox-control
|
57
|
+
set -l control_commands start stop restart status
|
58
|
+
complete -c ybox-control -f
|
59
|
+
# common options for all ybox-control subcommands
|
60
|
+
complete -c ybox-control -s h -l help -d "show help"
|
61
|
+
# available subcommands for ybox-control with descriptions
|
62
|
+
complete -c ybox-control -n "not __fish_seen_subcommand_from $control_commands" -a start -d "start an inactive ybox container"
|
63
|
+
complete -c ybox-control -n "not __fish_seen_subcommand_from $control_commands" -a stop -d "stop an active ybox container"
|
64
|
+
complete -c ybox-control -n "not __fish_seen_subcommand_from $control_commands" -a restart -d "restart a ybox container"
|
65
|
+
complete -c ybox-control -n "not __fish_seen_subcommand_from $control_commands" -a status -d "show status of a ybox container"
|
66
|
+
# define arguments for individual subcommands using the "-n" option for the subcommand
|
67
|
+
complete -c ybox-control -n "__fish_seen_subcommand_from start" -a "(__fish_ybox_complete_stopped_containers)"
|
68
|
+
complete -c ybox-control -n "__fish_seen_subcommand_from stop" -a "(__fish_ybox_complete_containers)"
|
69
|
+
complete -c ybox-control -n "__fish_seen_subcommand_from restart" -a "(__fish_ybox_complete_all_containers)"
|
70
|
+
complete -c ybox-control -n "__fish_seen_subcommand_from status" -a "(__fish_ybox_complete_all_containers)"
|
71
|
+
|
72
|
+
|
73
|
+
# define subcommands for ybox-pkg
|
74
|
+
set -l pkg_commands install uninstall update list list-files info search mark clean repair
|
75
|
+
complete -c ybox-pkg -f
|
76
|
+
# common options for all ybox-pkg subcommands
|
77
|
+
complete -c ybox-pkg -s h -l help -d "show help"
|
78
|
+
complete -c ybox-pkg -s z -l ybox -d "ybox container to use for package operation" -rfa "(__fish_ybox_complete_containers)"
|
79
|
+
complete -c ybox-pkg -s C -l distribution-config -d "path to distribution configuration file" -rF
|
80
|
+
complete -c ybox-pkg -s q -l quiet -d "proceed without asking any questions using defaults"
|
81
|
+
# available subcommands for ybox-pkg with descriptions
|
82
|
+
complete -c ybox-pkg -n "not __fish_seen_subcommand_from $pkg_commands" -a install -d "install a package with dependencies"
|
83
|
+
complete -c ybox-pkg -n "not __fish_seen_subcommand_from $pkg_commands" -a uninstall -d "uninstall a package and optionally its dependencies"
|
84
|
+
complete -c ybox-pkg -n "not __fish_seen_subcommand_from $pkg_commands" -a update -d "update some or all packages"
|
85
|
+
complete -c ybox-pkg -n "not __fish_seen_subcommand_from $pkg_commands" -a list -d "list installed packages"
|
86
|
+
complete -c ybox-pkg -n "not __fish_seen_subcommand_from $pkg_commands" -a list-files -d "list files of an installed package"
|
87
|
+
complete -c ybox-pkg -n "not __fish_seen_subcommand_from $pkg_commands" -a info -d "show detailed information of package(s)"
|
88
|
+
complete -c ybox-pkg -n "not __fish_seen_subcommand_from $pkg_commands" -a search -d "search repositories"
|
89
|
+
complete -c ybox-pkg -n "not __fish_seen_subcommand_from $pkg_commands" -a mark -d "mark package as dependency or explicitly installed"
|
90
|
+
complete -c ybox-pkg -n "not __fish_seen_subcommand_from $pkg_commands" -a clean -d "clean package cache"
|
91
|
+
complete -c ybox-pkg -n "not __fish_seen_subcommand_from $pkg_commands" -a repair -d "try to repair package state"
|
92
|
+
# define arguments for individual subcommands using the "-n" option for the subcommand
|
93
|
+
complete -c ybox-pkg -n "__fish_seen_subcommand_from install" -s o -l skip-opt-deps -d "skip optional dependencies"
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# this script fetches a GPG/PGP key file from a URL and adds the key to pacman
|
4
|
+
|
5
|
+
set -e
|
6
|
+
|
7
|
+
# Check if all arguments are provided
|
8
|
+
if [ $# -ne 1 ]; then
|
9
|
+
echo "Usage: $0 <url>"
|
10
|
+
exit 1
|
11
|
+
fi
|
12
|
+
|
13
|
+
# ensure that only system paths are searched for all the system utilities
|
14
|
+
export PATH="/usr/sbin:/usr/bin:/sbin:/bin"
|
15
|
+
|
16
|
+
file="$(mktemp /tmp/gpg-key-XXXXXXXXXX)"
|
17
|
+
|
18
|
+
trap "rm -f $file" 0 1 2 3 15
|
19
|
+
|
20
|
+
curl -sSL "$1" -o "$file"
|
21
|
+
KEYIDS="$(gpg --show-keys --with-colons "$file" | sed -n 's/^fpr:*\([^:]*\).*/\1/p' | tr '\n' ' ')"
|
22
|
+
sudo pacman-key --add "$file"
|
23
|
+
|
24
|
+
for keyid in $KEYIDS; do
|
25
|
+
sudo pacman-key --lsign-key $keyid
|
26
|
+
done
|
27
|
+
echo "KEYID=$KEYIDS"
|
28
|
+
|
29
|
+
exit 0
|
@@ -0,0 +1,192 @@
|
|
1
|
+
# Configuration specific to each distribution (INI style file)
|
2
|
+
|
3
|
+
# The following environment variables are set when running the init.sh and init-user.sh scripts.
|
4
|
+
# The scripts are required to honor the meaning of the corresponding variables as described in
|
5
|
+
# their comments.
|
6
|
+
# CONFIGURE_FASTEST_MIRRORS: empty or non-empty corresponding to configure_fastest_mirrors below
|
7
|
+
# REQUIRED_PKGS: packages specified in `packages.required` below
|
8
|
+
# REQUIRED_DEPS: packages specified in `packages.required_deps` below
|
9
|
+
# RECOMMENDED_PKGS: packages specified in `packages.recommended` below
|
10
|
+
# RECOMMENDED_DEPS: packages specified in `packages.recommended_deps` below
|
11
|
+
# SUGGESTED_PKGS: packages specified in `packages.suggested` below
|
12
|
+
# SUGGESTED_DEPS: packages specified in `packages.suggested_deps` below
|
13
|
+
# EXTRA_PKGS: packages specified in `packages.extra` below
|
14
|
+
|
15
|
+
|
16
|
+
# Base configuration for the distribution
|
17
|
+
[base]
|
18
|
+
# name is required
|
19
|
+
name = Arch Linux
|
20
|
+
# Comma separated files to include before applying these settings.
|
21
|
+
# Paths can be absolute or relative to the location of this file.
|
22
|
+
includes =
|
23
|
+
# points to the daily updated image to minimize upgrade size
|
24
|
+
image = quay.io/archlinux/archlinux
|
25
|
+
# directories which are shared between the containers of a distribution when
|
26
|
+
# `shared_root` is provided in the container configuration
|
27
|
+
shared_root_dirs = /etc,/opt,/usr,/var
|
28
|
+
# the secondary groups of the container user; it requires to include at least the equivalent of
|
29
|
+
# nobody/nogroup to work correctly (the last field in /etc/subgid)
|
30
|
+
secondary_groups = nobody,wheel,video,input,lp,mail
|
31
|
+
# whether to search for and configure fastest available mirrors for packages
|
32
|
+
configure_fastest_mirrors = true
|
33
|
+
# distribution scripts that need to be copied to the container in $YBOX_TARGET_SCRIPTS_DIR
|
34
|
+
# (should include init.sh, init-base.sh and init-user.sh scripts that are are normally required
|
35
|
+
# for all distributions)
|
36
|
+
scripts = init-base.sh,init.sh,init-user.sh,pkgdeps.py,list_fmt_long.py,add-gpg-key.sh
|
37
|
+
|
38
|
+
|
39
|
+
# Initial set of packages to be installed in the distribution image
|
40
|
+
[packages]
|
41
|
+
# packages required for a functional ybox container
|
42
|
+
required = base-devel python-ijson expac lesspipe
|
43
|
+
# dependencies of the `required` packages
|
44
|
+
required_deps = git ed unzip fastjar
|
45
|
+
# recommended packages required for many GUI/CLI apps to work properly
|
46
|
+
recommended = aria2 bash-completion bc man-db man-pages pulseaudio-alsa zip wget shared-mime-info
|
47
|
+
libva-utils mesa-utils vulkan-tools ttf-liberation
|
48
|
+
# dependencies of the `recommended` packages
|
49
|
+
recommended_deps = intel-media-driver libva-mesa-driver vulkan-intel vulkan-mesa-layers python-pip
|
50
|
+
# optional packages for enhanced experience in shell and GUI apps
|
51
|
+
# (for some reason TERMINFO_DIRS does not work for root user, so explicitly installing terminfo
|
52
|
+
# packages for other terminal emulators available in arch which occupy only a tiny space)
|
53
|
+
suggested = cantarell-fonts ttf-fira-code noto-fonts neovim eza ncdu fd bat
|
54
|
+
kitty-terminfo rxvt-unicode-terminfo realtime-privileges tree starship
|
55
|
+
# dependencies of the `suggested` packages
|
56
|
+
suggested_deps = xsel
|
57
|
+
# additional packages that are in AUR and installed by paru in init-user.sh
|
58
|
+
extra = neovim-symlinks tinyxxd autojump
|
59
|
+
|
60
|
+
|
61
|
+
# The commands here will be run as normal userns mapped user, so use sudo if the command
|
62
|
+
# needs to run as root inside the container
|
63
|
+
[pkgmgr]
|
64
|
+
# the variables here are all required ones unless noted otherwise
|
65
|
+
|
66
|
+
# install command does not have a placeholder for {package} since it is also used by
|
67
|
+
# entrypoint scripts to install packages, so this assumes that this command accepts a list of one
|
68
|
+
# or more space-separated packages at the end
|
69
|
+
install = /usr/bin/paru -S --needed --removemake --cleanafter {quiet} {opt_dep}
|
70
|
+
# Show the packages that satisfy the given {package} where latter can be a virtual package
|
71
|
+
# (i.e. "Provides") or an actual package or a combination of both (e.g. some packages provide and
|
72
|
+
# replace another actual package).
|
73
|
+
# Not required for Arch since install command itself shows the selection if required.
|
74
|
+
check_avail
|
75
|
+
# check an installed actual or virtual (i.e. "Provides") package and list them in reverse
|
76
|
+
# install/upgrade time order (i.e. most recently installed/upgraded first)
|
77
|
+
check_install = /usr/bin/expac -Qs --timefmt=%%s '%%l\t%%n' '^{package}$' | \
|
78
|
+
/usr/bin/sort -nr | /usr/bin/cut -f2
|
79
|
+
# proceed quietly without asking questions
|
80
|
+
quiet_flag = --noconfirm
|
81
|
+
# this is substituted for `{quiet}` placeholder in `info`, `info_all`, `search` and `search_all`
|
82
|
+
quiet_details_flag = -q
|
83
|
+
# --asdeps works correctly only if an optional dependency is actually marked to be so in
|
84
|
+
# the arch package, but will cause trouble for cases where user wants to mark a package as
|
85
|
+
# an optional dependency of another even otherwise (e.g. qt5ct as optional dependency of qt5-base)
|
86
|
+
# opt_dep_flag = --asdeps
|
87
|
+
opt_dep_flag =
|
88
|
+
# Expected output of the `opt_deps` command is:
|
89
|
+
# {header}
|
90
|
+
# {prefix}<name>{separator}<level>{separator}<installed>{separator}<description>
|
91
|
+
# where
|
92
|
+
# <name>: name of the optional dependency
|
93
|
+
# <level>: level of the dependency i.e. 1 for direct dependency, 2 for dependency of dependency and
|
94
|
+
# so on; resolution of level > 2 is not required since caller currently ignores those
|
95
|
+
# <installed>: true if the dependency already installed and false otherwise
|
96
|
+
opt_deps = /usr/bin/python3 $YBOX_TARGET_SCRIPTS_DIR/pkgdeps.py \
|
97
|
+
-s '{separator}' -p '{prefix}' -H '{header}'
|
98
|
+
uninstall = /usr/bin/paru -R {quiet} {purge} {remove_deps} {package}
|
99
|
+
purge_flag = --nosave
|
100
|
+
remove_deps_flag = --recursive
|
101
|
+
orphans = /usr/bin/pacman -Qdtq
|
102
|
+
update_meta = /usr/bin/paru -Sy
|
103
|
+
update = /usr/bin/paru -S --needed --removemake --cleanafter {quiet} {packages}
|
104
|
+
update_all = /usr/bin/paru -Syu --removemake --cleanafter {quiet}
|
105
|
+
clean = /usr/bin/paru -Sccd
|
106
|
+
clean_quiet = /bin/yes | %(clean)s
|
107
|
+
mark_explicit = /usr/bin/paru -D --asexplicit {package}
|
108
|
+
|
109
|
+
info = /usr/bin/pacman -Qi {quiet} {packages}
|
110
|
+
info_all = /usr/bin/paru -Si {quiet} {packages}
|
111
|
+
|
112
|
+
# next two variables are not required ones rather are used for expansion in list variables
|
113
|
+
# to avoid repetition
|
114
|
+
list_fmt = /usr/bin/sed 's/^\([^[:space:]]*\)[[:space:]]\+/\1{separator}/'
|
115
|
+
list_fmt_long = /usr/bin/python3 $YBOX_TARGET_SCRIPTS_DIR/list_fmt_long.py '{separator}'
|
116
|
+
|
117
|
+
# list and list_all show package name and version separated by {separator}; the list command only
|
118
|
+
# shows explicitly installed packages while the "_all" variant also shows dependents
|
119
|
+
list = /usr/bin/pacman -Qe {packages} | %(list_fmt)s
|
120
|
+
list_all = /usr/bin/pacman -Q {packages} | %(list_fmt)s
|
121
|
+
# list_long and list_all_long show package name, version, dependency-of and description separated
|
122
|
+
# by {separator}; the dependency-of column gives the required and optional dependencies in the
|
123
|
+
# format: req(<pkg> <pkg> ...),opt(<pkg> <pkg> ...); like above the "_all" variant shows all
|
124
|
+
# packages including dependents while other one shows only explicitly installed ones
|
125
|
+
list_long = /usr/bin/pacman -Qie {packages} | %(list_fmt_long)s
|
126
|
+
list_all_long = /usr/bin/pacman -Qi {packages} | %(list_fmt_long)s
|
127
|
+
|
128
|
+
list_files = /usr/bin/pacman -Qlq {package}
|
129
|
+
|
130
|
+
# next five variables are not required ones rather are used for expansion in search variables
|
131
|
+
|
132
|
+
# --searchby=name only applies to the AUR packages, so filter out packages where only the package
|
133
|
+
# names match; the weird "set ..." subcommand converts space-separated quoted arguments into a
|
134
|
+
# regex i.e. ' one ' 'two' ' three' to " one |two | three" which is accomplished by declaring
|
135
|
+
# those arguments as positional arguments $1, $2 etc, then echo all arguments with IFS as "|"
|
136
|
+
args_re = "$(set -- {search}; IFS="|"; echo "$*")"
|
137
|
+
# word matching works correctly after removing color codes, but keep those in the final output
|
138
|
+
search_filter_prefix = /usr/bin/gawk 'BEGIN {{ IGNORECASE=1 }} /^[^[:space:]].*/ {{
|
139
|
+
pkg = $0; gsub(/\x1B\[[0-9;]+m/, ""); pkg_nc = $0; getline; '
|
140
|
+
search_filter_suffix = 'if (pkg_nc ~ /{word_start}('%(args_re)s'){word_end}/) print pkg"\n"$0 }}'
|
141
|
+
name_filter = %(search_filter_prefix)s%(search_filter_suffix)s
|
142
|
+
all_filter = %(search_filter_prefix)s'pkg_nc = pkg_nc $0; '%(search_filter_suffix)s
|
143
|
+
|
144
|
+
# search in package names in the repositories
|
145
|
+
search = /usr/bin/paru -Ss {quiet} {official} --searchby=name --sortby=popularity --color=always \
|
146
|
+
{search} | %(name_filter)s
|
147
|
+
# search in package names and descriptions in the repositories
|
148
|
+
search_all = /usr/bin/paru -Ss {quiet} {official} --sortby=popularity --color=always \
|
149
|
+
{search} | %(all_filter)s
|
150
|
+
# this is substituted for `{official}` placeholder in `search` and `search_all`
|
151
|
+
search_official_flag = --repo
|
152
|
+
# this is substituted for `{word_start}` placeholder in `search` and `search_all`
|
153
|
+
search_word_start_flag = \<
|
154
|
+
# this is substituted for `{word_end}` placeholder in `search` and `search_all`
|
155
|
+
search_word_end_flag = \>
|
156
|
+
|
157
|
+
# regex pattern matching processes that may be invoked by package manager directly or indirectly
|
158
|
+
# that may need to be terminated for cleanup
|
159
|
+
processes_pattern = \b(pacman|paru|keyboxd|dirmngr)\b
|
160
|
+
# comma separated globs for package manager related lock files (no spaces in names or within)
|
161
|
+
locks_pattern = /var/lib/pacman/db.lck,$HOME/.gnupg/public-keys.d/*.lock
|
162
|
+
repair = /usr/bin/paru -Syyuu {quiet}
|
163
|
+
# reinstall all packages
|
164
|
+
repair_all = %(repair)s && (/usr/bin/pacman -Qqn | /usr/bin/paru -S {quiet} -) && \
|
165
|
+
(/usr/bin/pacman -Qqm | /usr/bin/paru -S {quiet} -) && %(clean_quiet)s
|
166
|
+
|
167
|
+
|
168
|
+
# Commands related to repository management
|
169
|
+
[repo]
|
170
|
+
conf_file = /etc/pacman.conf
|
171
|
+
exists = /usr/bin/grep -q '^[[:space:]]*\[{name}\][[:space:]]*$' %(conf_file)s
|
172
|
+
# Ubuntu's keyserver is more reliable than the standard gnupg.net/pgp.net ones
|
173
|
+
default_gpg_key_server = hkps://keyserver.ubuntu.com
|
174
|
+
# output of the `add_key` command should have a line of the form "KEYID=..." that provides the
|
175
|
+
# registered keys that can be passed to `remove_key`
|
176
|
+
add_key = /bin/bash $YBOX_TARGET_SCRIPTS_DIR/add-gpg-key.sh '{url}'
|
177
|
+
add_key_id = /usr/bin/sudo /usr/bin/pacman-key --keyserver '{server}' --recv-keys '{key}' && \
|
178
|
+
/usr/bin/sudo /usr/bin/pacman-key --lsign-key '{key}'
|
179
|
+
# additional options if supported should be mentioned using {options} below
|
180
|
+
add = /bin/echo '[{name}],{urls}' | /usr/bin/sed 's/,/\nServer = /g' | \
|
181
|
+
/usr/bin/sudo /usr/bin/tee -a %(conf_file)s
|
182
|
+
# if source repository is supported, then it should be provided in `add_source` like for `add`
|
183
|
+
add_source
|
184
|
+
remove_key = /usr/bin/sudo /usr/bin/pacman-key --delete {key}
|
185
|
+
# The sed pattern below removes the [{name}] section from pacman.conf
|
186
|
+
# (S_PAT matches start of the section while E_PAT matches next section which has to be retained).
|
187
|
+
# If source repositories are supported in `add_source` command above, then {remove_source} boolean
|
188
|
+
# should be checked to remove source repository too.
|
189
|
+
rm_s_pat = ^[[:space:]]*\[{name}\][[:space:]]*$
|
190
|
+
rm_e_pat = ^[[:space:]]*\[.*
|
191
|
+
remove = /usr/bin/sudo /usr/bin/sed -i \
|
192
|
+
'/%(rm_s_pat)s/,/%(rm_e_pat)s/ {{/%(rm_s_pat)s/d;/%(rm_e_pat)s/!d;}}' %(conf_file)s
|
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -e
|
4
|
+
|
5
|
+
# disable the sandbox in the newer pacman versions that does not work in container
|
6
|
+
sed -i 's/^#[ ]*DisableSandbox/DisableSandbox/' /etc/pacman.conf
|
7
|
+
# sudo is not present in the upstream archlinux image
|
8
|
+
pacman -Sy
|
9
|
+
pacman -S --noconfirm --needed sudo
|
10
|
+
yes | pacman -Scc 2>/dev/null >/dev/null
|