fmtr.tools 1.1.1__py3-none-any.whl → 1.4.37__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.
Files changed (97) hide show
  1. fmtr/tools/__init__.py +86 -52
  2. fmtr/tools/ai_tools/__init__.py +2 -2
  3. fmtr/tools/ai_tools/agentic_tools.py +151 -32
  4. fmtr/tools/ai_tools/inference_tools.py +2 -1
  5. fmtr/tools/api_tools.py +73 -12
  6. fmtr/tools/async_tools.py +4 -0
  7. fmtr/tools/av_tools.py +7 -0
  8. fmtr/tools/caching_tools.py +101 -3
  9. fmtr/tools/constants.py +41 -0
  10. fmtr/tools/context_tools.py +23 -0
  11. fmtr/tools/data_modelling_tools.py +227 -14
  12. fmtr/tools/database_tools/__init__.py +6 -0
  13. fmtr/tools/database_tools/document.py +51 -0
  14. fmtr/tools/datatype_tools.py +22 -2
  15. fmtr/tools/datetime_tools.py +12 -0
  16. fmtr/tools/debugging_tools.py +60 -1
  17. fmtr/tools/dns_tools/__init__.py +7 -0
  18. fmtr/tools/dns_tools/client.py +97 -0
  19. fmtr/tools/dns_tools/dm.py +257 -0
  20. fmtr/tools/dns_tools/proxy.py +66 -0
  21. fmtr/tools/dns_tools/server.py +138 -0
  22. fmtr/tools/docker_tools/__init__.py +6 -0
  23. fmtr/tools/entrypoints/__init__.py +0 -0
  24. fmtr/tools/entrypoints/cache_hfh.py +3 -0
  25. fmtr/tools/entrypoints/ep_test.py +2 -0
  26. fmtr/tools/entrypoints/install_yamlscript.py +8 -0
  27. fmtr/tools/{console_script_tools.py → entrypoints/remote_debug_test.py} +1 -6
  28. fmtr/tools/entrypoints/shell_debug.py +8 -0
  29. fmtr/tools/environment_tools.py +3 -2
  30. fmtr/tools/function_tools.py +77 -1
  31. fmtr/tools/google_api_tools.py +15 -4
  32. fmtr/tools/ha_tools/__init__.py +8 -0
  33. fmtr/tools/ha_tools/constants.py +9 -0
  34. fmtr/tools/ha_tools/core.py +16 -0
  35. fmtr/tools/ha_tools/supervisor.py +16 -0
  36. fmtr/tools/ha_tools/utils.py +46 -0
  37. fmtr/tools/http_tools.py +52 -0
  38. fmtr/tools/inherit_tools.py +27 -0
  39. fmtr/tools/interface_tools/__init__.py +8 -0
  40. fmtr/tools/interface_tools/context.py +13 -0
  41. fmtr/tools/interface_tools/controls.py +354 -0
  42. fmtr/tools/interface_tools/interface_tools.py +189 -0
  43. fmtr/tools/iterator_tools.py +122 -1
  44. fmtr/tools/logging_tools.py +99 -18
  45. fmtr/tools/mqtt_tools.py +89 -0
  46. fmtr/tools/networking_tools.py +73 -0
  47. fmtr/tools/packaging_tools.py +14 -0
  48. fmtr/tools/path_tools/__init__.py +12 -0
  49. fmtr/tools/path_tools/app_path_tools.py +40 -0
  50. fmtr/tools/{path_tools.py → path_tools/path_tools.py} +217 -14
  51. fmtr/tools/path_tools/type_path_tools.py +3 -0
  52. fmtr/tools/pattern_tools.py +277 -0
  53. fmtr/tools/pdf_tools.py +39 -1
  54. fmtr/tools/settings_tools.py +27 -6
  55. fmtr/tools/setup_tools/__init__.py +8 -0
  56. fmtr/tools/setup_tools/setup_tools.py +481 -0
  57. fmtr/tools/string_tools.py +92 -13
  58. fmtr/tools/tabular_tools.py +61 -0
  59. fmtr/tools/tools.py +27 -2
  60. fmtr/tools/version +1 -1
  61. fmtr/tools/version_tools/__init__.py +12 -0
  62. fmtr/tools/version_tools/version_tools.py +51 -0
  63. fmtr/tools/webhook_tools.py +17 -0
  64. fmtr/tools/yaml_tools.py +64 -5
  65. fmtr/tools/youtube_tools.py +128 -0
  66. fmtr_tools-1.4.37.data/scripts/add-service +14 -0
  67. fmtr_tools-1.4.37.data/scripts/add-user-path +8 -0
  68. fmtr_tools-1.4.37.data/scripts/apt-headless +23 -0
  69. fmtr_tools-1.4.37.data/scripts/compose-update +10 -0
  70. fmtr_tools-1.4.37.data/scripts/docker-sandbox +43 -0
  71. fmtr_tools-1.4.37.data/scripts/docker-sandbox-init +23 -0
  72. fmtr_tools-1.4.37.data/scripts/docs-deploy +6 -0
  73. fmtr_tools-1.4.37.data/scripts/docs-serve +5 -0
  74. fmtr_tools-1.4.37.data/scripts/download +9 -0
  75. fmtr_tools-1.4.37.data/scripts/fmtr-test-script +3 -0
  76. fmtr_tools-1.4.37.data/scripts/ftu +3 -0
  77. fmtr_tools-1.4.37.data/scripts/ha-addon-launch +16 -0
  78. fmtr_tools-1.4.37.data/scripts/install-browser +8 -0
  79. fmtr_tools-1.4.37.data/scripts/parse-args +43 -0
  80. fmtr_tools-1.4.37.data/scripts/set-password +5 -0
  81. fmtr_tools-1.4.37.data/scripts/snips-install +14 -0
  82. fmtr_tools-1.4.37.data/scripts/ssh-auth +28 -0
  83. fmtr_tools-1.4.37.data/scripts/ssh-serve +15 -0
  84. fmtr_tools-1.4.37.data/scripts/vlc-tn +10 -0
  85. fmtr_tools-1.4.37.data/scripts/vm-launch +17 -0
  86. {fmtr_tools-1.1.1.dist-info → fmtr_tools-1.4.37.dist-info}/METADATA +178 -54
  87. fmtr_tools-1.4.37.dist-info/RECORD +122 -0
  88. {fmtr_tools-1.1.1.dist-info → fmtr_tools-1.4.37.dist-info}/WHEEL +1 -1
  89. fmtr_tools-1.4.37.dist-info/entry_points.txt +6 -0
  90. fmtr_tools-1.4.37.dist-info/top_level.txt +1 -0
  91. fmtr/tools/docker_tools.py +0 -30
  92. fmtr/tools/interface_tools.py +0 -64
  93. fmtr/tools/version_tools.py +0 -62
  94. fmtr_tools-1.1.1.dist-info/RECORD +0 -65
  95. fmtr_tools-1.1.1.dist-info/entry_points.txt +0 -3
  96. fmtr_tools-1.1.1.dist-info/top_level.txt +0 -2
  97. {fmtr_tools-1.1.1.dist-info → fmtr_tools-1.4.37.dist-info}/licenses/LICENSE +0 -0
fmtr/tools/tools.py CHANGED
@@ -27,7 +27,15 @@ def identity(x: Any) -> Any:
27
27
  return x
28
28
 
29
29
 
30
- class Empty:
30
+ class Special:
31
+ """
32
+
33
+ Classes to differentiate special arguments from primitive arguments.
34
+
35
+ """
36
+
37
+
38
+ class Empty(Special):
31
39
  """
32
40
 
33
41
  Class to denote an unspecified object (e.g. argument) when `None` cannot be used.
@@ -35,7 +43,7 @@ class Empty:
35
43
  """
36
44
 
37
45
 
38
- class Raise:
46
+ class Raise(Special):
39
47
  """
40
48
 
41
49
  Class to denote when a function should raise instead of e.g. returning a default.
@@ -43,4 +51,21 @@ class Raise:
43
51
  """
44
52
 
45
53
 
54
+ class Auto(Special):
55
+ """
56
+
57
+ Class to denote when an argument should be inferred.
58
+
59
+ """
60
+
61
+
62
+ class Required(Special):
63
+ """
64
+
65
+ Class to denote when an argument is required.
66
+
67
+ """
68
+
69
+
70
+
46
71
  EMPTY = Empty()
fmtr/tools/version CHANGED
@@ -1 +1 @@
1
- 1.1.1
1
+ 1.4.37
@@ -0,0 +1,12 @@
1
+ from fmtr.tools.import_tools import MissingExtraMockModule
2
+
3
+ from fmtr.tools.version_tools.version_tools import read, read_path, get
4
+
5
+ try:
6
+ import semver
7
+
8
+ semver = semver
9
+ parse = semver.VersionInfo.parse
10
+
11
+ except ModuleNotFoundError as exception:
12
+ semver = parse = MissingExtraMockModule('version.dev', exception)
@@ -0,0 +1,51 @@
1
+ from fmtr.tools import environment_tools as env
2
+ from fmtr.tools.constants import Constants
3
+ from fmtr.tools.inspection_tools import get_call_path
4
+
5
+
6
+ def read() -> str:
7
+ """
8
+
9
+ Read a generic version file from the calling package path.
10
+
11
+ """
12
+
13
+ path = get_call_path(offset=2).parent / Constants.FILENAME_VERSION
14
+ return read_path(path)
15
+
16
+
17
+ def read_path(path) -> str:
18
+ """
19
+
20
+ Read in version from specified path
21
+
22
+ """
23
+ from fmtr.tools.tools import Constants
24
+ text = path.read_text(encoding=Constants.ENCODING).strip()
25
+
26
+ text = get(text)
27
+ return text
28
+
29
+
30
+ def get(text) -> str:
31
+ """
32
+
33
+ Optionally add dev build info to raw version string.
34
+
35
+ """
36
+
37
+ if not env.IS_DEV:
38
+ return text
39
+
40
+ import datetime
41
+ from fmtr.tools.tools import Constants
42
+ from fmtr.tools.version_tools import parse
43
+
44
+ timestamp = datetime.datetime.now(datetime.timezone.utc).strftime(Constants.DATETIME_SEMVER_BUILD_FORMAT)
45
+
46
+ version = parse(text)
47
+ version = version.bump_patch()
48
+ version = version.replace(prerelease=Constants.DEVELOPMENT, build=timestamp)
49
+ text = str(version)
50
+
51
+ return text
@@ -0,0 +1,17 @@
1
+ from fmtr.tools import environment_tools, Constants
2
+ from fmtr.tools.http_tools import client
3
+
4
+
5
+ def notify(title, body, url=None):
6
+ """
7
+
8
+ Send simple debug notification
9
+
10
+ """
11
+ url = url or environment_tools.get(Constants.WEBHOOK_URL_NOTIFY_KEY)
12
+ client.post(url, json=dict(title=title, body=body))
13
+
14
+
15
+ if __name__ == '__main__':
16
+ notify('Title', 'Body')
17
+ notify
fmtr/tools/yaml_tools.py CHANGED
@@ -1,12 +1,63 @@
1
+ from functools import lru_cache
1
2
  from typing import Any
2
- from yaml import CLoader as Loader, CDumper as Dumper
3
- from yaml import load, dump
3
+ from yaml import CDumper as Dumper
4
+ from yaml import dump
4
5
 
6
+ try:
7
+ import yamlscript
8
+ except ImportError:
9
+ raise # Raise if even the package isn't installed, to trigger the regular missing extra exception.
10
+ except Exception as exception:
11
+ pass # Allow missing binary, so we can install on-demand
12
+
13
+
14
+ def install():
15
+ """
16
+
17
+ Installs the YAML Script runtime binary of the specified version.
18
+
19
+ """
20
+ import subprocess
21
+ from fmtr.tools import logger, packaging
22
+
23
+ version = packaging.get_version('yamlscript')
24
+ logger.warning(f"Installing YAML Script runtime binary version {version}...")
25
+ result = subprocess.run(f"curl https://yamlscript.org/install | VERSION={version} LIB=1 bash", shell=True, check=True)
26
+ return result
27
+
28
+
29
+ @lru_cache
30
+ def get_module(is_auto=True):
31
+ """
32
+
33
+ Get the YAML Script runtime module, installing the runtime if specified
5
34
 
6
- def to_yaml(obj: Any) -> str:
7
35
  """
36
+ try:
37
+ import yamlscript
38
+ except Exception as exception:
39
+ if not is_auto:
40
+ raise ImportError(f'YAML Script runtime missing and {is_auto=}. Set to {True} to install.') from exception
41
+ install()
42
+ import yamlscript
43
+ return yamlscript
8
44
 
9
45
 
46
+ @lru_cache
47
+ def get_interpreter():
48
+ """
49
+
50
+ Fetches and returns a cached instance of the YAMLScript interpreter.
51
+
52
+ """
53
+ module = get_module()
54
+ interpreter = module.YAMLScript()
55
+ return interpreter
56
+
57
+ def to_yaml(obj: Any) -> str:
58
+ """
59
+
60
+ Serialize to YAML
10
61
 
11
62
  """
12
63
  yaml_str = dump(obj, allow_unicode=True, Dumper=Dumper)
@@ -16,8 +67,16 @@ def to_yaml(obj: Any) -> str:
16
67
  def from_yaml(yaml_str: str) -> Any:
17
68
  """
18
69
 
19
-
70
+ Deserialize from YAML
20
71
 
21
72
  """
22
- obj = load(yaml_str, Loader=Loader)
73
+ obj = get_interpreter().load(yaml_str)
23
74
  return obj
75
+
76
+
77
+ if __name__ == '__main__':
78
+ from fmtr.tools import Path
79
+
80
+ py = Path('hw.yml')
81
+ data = py.read_yaml()
82
+ data
@@ -0,0 +1,128 @@
1
+ from dataclasses import dataclass
2
+ from functools import cached_property
3
+ from pytubefix import YouTube, Stream, extract, request
4
+ from pytubefix.exceptions import RegexMatchError
5
+ from typing import AsyncIterator, Iterator
6
+ from urllib.error import HTTPError
7
+
8
+ from fmtr.tools.path_tools.path_tools import Path
9
+
10
+ Stream = Stream
11
+
12
+
13
+ class Video(YouTube):
14
+ """
15
+
16
+ Video stub
17
+
18
+ """
19
+
20
+
21
+ class AudioStreamDownloadError(Exception):
22
+ """
23
+
24
+ Error downloading audio stream
25
+
26
+ """
27
+ pass
28
+
29
+
30
+ @dataclass
31
+ class AudioStreamData:
32
+ """
33
+
34
+ Audio stream download data and progress information
35
+
36
+ """
37
+ message: str | None = None
38
+ chunk: bytes | None = None
39
+ percentage: int | None = None
40
+
41
+
42
+ class AudioStreamDownloader:
43
+ """
44
+
45
+ Download the highest-bitrate audio stream and write to temp directory.
46
+
47
+ """
48
+
49
+ def __init__(self, url_or_id: str):
50
+ """
51
+
52
+ Initialise with URL or video ID
53
+
54
+ """
55
+
56
+ try:
57
+ self.id = extract.video_id(url_or_id)
58
+ except RegexMatchError:
59
+ self.id = url_or_id
60
+
61
+ self.path = None
62
+
63
+ @cached_property
64
+ def url(self) -> str:
65
+ """
66
+
67
+ Get URL from ID
68
+
69
+ """
70
+ return f'https://youtube.com/watch?v={self.id}'
71
+
72
+ async def download(self) -> AsyncIterator[AudioStreamData]:
73
+ """
74
+
75
+ Download the audio stream and yield chunks and progress information
76
+
77
+ """
78
+
79
+ yield AudioStreamData(message='Fetching video metadata...')
80
+ video = Video(self.url)
81
+
82
+ yield AudioStreamData('Finding audio streams...')
83
+
84
+ audio_streams = video.streams.filter(only_audio=True).order_by('bitrate')
85
+ if not audio_streams:
86
+ raise AudioStreamDownloadError(f'Error downloading: no audio streams found in "{video.title}"')
87
+
88
+ stream = audio_streams.last()
89
+ yield AudioStreamData(f'Found highest-bitrate audio stream: {stream.audio_codec}/{stream.subtype}@{stream.abr}')
90
+
91
+ self.path = Path.temp() / stream.default_filename
92
+ if self.path.exists():
93
+ self.path.unlink()
94
+
95
+ if stream.filesize == 0:
96
+ raise AudioStreamDownloadError(f'Error downloading: empty audio stream found in "{video.title}"')
97
+
98
+ yield AudioStreamData('Downloading...')
99
+
100
+ with self.path.open('wb') as out_file:
101
+ for data in self.iter_data(stream):
102
+ out_file.write(data.chunk)
103
+ yield data
104
+
105
+ def iter_data(self, stream: Stream, chunk_size: int | None = None) -> Iterator[AudioStreamData]:
106
+ """
107
+
108
+ Iterate over chunks of the specified size
109
+
110
+ """
111
+ bytes_total = bytes_remaining = stream.filesize
112
+
113
+ if chunk_size:
114
+ request.default_range_size = chunk_size
115
+
116
+ try:
117
+ stream = request.stream(stream.url)
118
+ except HTTPError as e:
119
+ if e.code != 404:
120
+ raise
121
+ stream = request.seq_stream(stream.url)
122
+
123
+ for chunk in stream:
124
+ bytes_remaining -= len(chunk)
125
+ percentage = round(((bytes_total - bytes_remaining) / bytes_total) * 100)
126
+
127
+ data = AudioStreamData(chunk=chunk, percentage=percentage)
128
+ yield data
@@ -0,0 +1,14 @@
1
+ #!/bin/bash
2
+
3
+ . parse-args "$@"
4
+
5
+ loginctl enable-linger $USER
6
+
7
+ NAME=$(basename "$FILE")
8
+
9
+ mkdir -p ~/.config/systemd/user
10
+ cp "$FILE" ~/.config/systemd/user/
11
+ systemctl --user daemon-reload
12
+ systemctl --user enable "$NAME"
13
+ systemctl --user start "$NAME"
14
+ systemctl --user status -l -n 50 "$NAME"
@@ -0,0 +1,8 @@
1
+ #!/bin/bash
2
+
3
+ SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
4
+ source $SCRIPT_DIR/parse-args "$@"
5
+
6
+ NEW=${NEW:-/home/${USERNAME}/.local/bin/}
7
+
8
+ PATH="${NEW}:${PATH}"
@@ -0,0 +1,23 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
6
+ source $SCRIPT_DIR/parse-args "$@"
7
+
8
+ export DEBIAN_FRONTEND=noninteractive
9
+ apt update --yes
10
+
11
+ # if --full was passed, run full-upgrade
12
+ if [ "${FULL}" = "1" ]; then
13
+ apt --yes full-upgrade
14
+ fi
15
+
16
+ # if ARGS is not empty, install the packages
17
+ if [ ${#ARGS[@]} -gt 0 ]; then
18
+ apt install --yes --no-install-recommends "${ARGS[@]}"
19
+ fi
20
+
21
+ apt autoremove --yes
22
+ apt clean
23
+ rm -rf /var/lib/apt/lists/*
@@ -0,0 +1,10 @@
1
+ #!/bin/bash
2
+
3
+ . parse-args "$@"
4
+
5
+ PROJECT_DIR=/opt/dev/repo/infrastructure/${CONTEXT-gex}/docker/${SERVICE}
6
+ CONTEXT=${CONTEXT-gex}
7
+
8
+ docker --context "$CONTEXT" compose --project-directory "$PROJECT_DIR" pull
9
+ docker --context "$CONTEXT" compose --project-directory "$PROJECT_DIR" up --detach
10
+ docker --context "$CONTEXT" compose --project-directory "$PROJECT_DIR" logs --follow
@@ -0,0 +1,43 @@
1
+ #!/bin/bash
2
+
3
+ . parse-args "$@"
4
+
5
+ #Spin up the container in the background with sleep infinity
6
+ CONTAINER_ID=$(docker run -d \
7
+ --hostname=sandbox \
8
+ --user=user \
9
+ --volume=/opt/dev/:/opt/dev/ \
10
+ --publish=${PORT-9000}:${PORT-9000} \
11
+ ${IMAGE-fmtr/python} \
12
+ bash -c "sleep infinity")
13
+
14
+
15
+ # Capture Docker's default name
16
+ DEFAULT_NAME=$(docker inspect --format '{{.Name}}' $CONTAINER_ID | cut -c2-)
17
+
18
+ # Modify it dynamically (prefix or suffix)
19
+ MODIFIED_NAME="sandbox_${DEFAULT_NAME}"
20
+ docker rename $CONTAINER_ID $MODIFIED_NAME
21
+
22
+ echo "Started container $MODIFIED_NAME."
23
+
24
+ #Run the sandbox-init script as root (interactive)
25
+ docker exec \
26
+ --interactive=true \
27
+ --tty=true \
28
+ --user=root \
29
+ $CONTAINER_ID \
30
+ /opt/dev/repo/fmtr.tools/scripts/docker-sandbox-init --tools
31
+
32
+ #Start an interactive bash session as user
33
+ docker exec \
34
+ --interactive=true \
35
+ --tty=true \
36
+ --user=user \
37
+ --env USER=user \
38
+ $CONTAINER_ID \
39
+ bash
40
+
41
+ # Cleanup
42
+ docker rm -f $MODIFIED_NAME
43
+ echo "Container $MODIFIED_NAME removed."
@@ -0,0 +1,23 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ SECURE_PATH=$PATH
6
+
7
+ SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
8
+ source $SCRIPT_DIR/add-user-path --username=user --new=$SCRIPT_DIR
9
+
10
+ . parse-args "$@"
11
+
12
+ echo "Setting up sandbox..."
13
+
14
+ set-password
15
+ set-password --username=user
16
+
17
+ echo "user ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/sandbox
18
+ echo "Defaults secure_path=\"${SECURE_PATH}\"" >> /etc/sudoers.d/sandbox
19
+ chmod 440 /etc/sudoers.d/sandbox
20
+
21
+ if [ "$TOOLS" == "1" ]; then
22
+ sudo -u user -H pip install --user fmtr.tools --upgrade
23
+ fi
@@ -0,0 +1,6 @@
1
+ #!/bin/bash
2
+
3
+ . parse-args "$@"
4
+
5
+ mkdocs gh-deploy --remote-branch docs --force
6
+
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+
3
+ . parse-args "$@"
4
+
5
+ mkdocs serve --dev-addr=${HOST-0.0.0.0}:${PORT-8180} --livereload
@@ -0,0 +1,9 @@
1
+ #!/bin/bash
2
+
3
+ . parse-args "$@"
4
+
5
+ OUT_OPT=${FILE:+-o "$FILE"}
6
+ [ -z "$FILE" ] && OUT_OPT="-O"
7
+
8
+ curl -L -C - --retry 5 --retry-delay 5 --retry-connrefused --progress-bar $OUT_OPT "$URL"
9
+
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ echo "Test Script Ran"
@@ -0,0 +1,3 @@
1
+ #!/bin/bash
2
+
3
+ pip install fmtr.tools --upgrade
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/with-contenv bashio
2
+
3
+ . parse-args "$@"
4
+
5
+ bashio::log.info "${NAME} container started."
6
+
7
+ export FMTR_DEV="$(bashio::config 'fmtr_dev')"
8
+
9
+ if bashio::var.true "${FMTR_DEV}"; then
10
+ bashio::log.info "Starting ${NAME} SSH development server"
11
+ printenv > /addon.env
12
+ echo "root:password" | chpasswd
13
+ /usr/sbin/sshd -D -o Port=22 -o PermitRootLogin=yes -o PasswordAuthentication=yes -o AllowTcpForwarding=yes -o LogLevel=VERBOSE
14
+ else
15
+ ${NAME}
16
+ fi
@@ -0,0 +1,8 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+
6
+ sudo -u user /opt/dev/venv/fmtr.tools/bin/python -m pip install fmtr.tools[browsers] --upgrade
7
+ /opt/dev/venv/fmtr.tools/bin/python -m playwright install-deps
8
+ sudo -u user /opt/dev/venv/fmtr.tools/bin/python -m playwright install chromium
@@ -0,0 +1,43 @@
1
+ #!/bin/bash
2
+
3
+ ARGS=() # positional arguments
4
+
5
+ while [ $# -gt 0 ]; do
6
+ case "$1" in
7
+ # --key=value or -k=value
8
+ --*=* | -*=*)
9
+ key=${1%%=*}
10
+ key=${key#--}
11
+ key=${key#-}
12
+ key=$(printf '%s' "$key" | tr 'a-z-' 'A-Z_')
13
+
14
+ val=${1#*=}
15
+ export "$key=$val"
16
+
17
+ unset key val # remove temporary variables
18
+ shift
19
+ ;;
20
+ # --key value or -k value, or boolean flag
21
+ --* | -*)
22
+ key=${1#--}
23
+ key=${key#-}
24
+ key=$(printf '%s' "$key" | tr 'a-z-' 'A-Z_')
25
+
26
+ if [ -n "$2" ] && [[ ! "$2" =~ ^- ]]; then
27
+ val="$2"
28
+ export "$key=$val"
29
+ shift 2
30
+ else
31
+ export "$key=1"
32
+ shift
33
+ fi
34
+
35
+ unset key val # remove temporary variables
36
+ ;;
37
+ # positional arguments
38
+ *)
39
+ ARGS+=("$1")
40
+ shift
41
+ ;;
42
+ esac
43
+ done
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+
3
+ . parse-args "$@"
4
+
5
+ echo "${USERNAME-root}:${PASSWORD-password}" | chpasswd
@@ -0,0 +1,14 @@
1
+ #!/bin/bash
2
+
3
+ . parse-args "$@"
4
+
5
+ DIR="/home/$(whoami)/.local/bin/"
6
+ REPO="snips"
7
+ BRANCH="main"
8
+ URL="https://github.com/${ORG}/${REPO}/archive/refs/heads/${BRANCH}.zip"
9
+
10
+ TMPDIR=$(mktemp -d)
11
+ download --url="$URL" --file=$TMPDIR/snips.zip
12
+ unzip $TMPDIR/snips.zip -d $TMPDIR
13
+ mv "$TMPDIR/${REPO}-${BRANCH}/"* "$DIR"
14
+ rm -rf "$TMPDIR"
@@ -0,0 +1,28 @@
1
+ #!/bin/bash
2
+
3
+ . parse-args "$@"
4
+
5
+ set -e
6
+
7
+ if [ -z "${USERNAME}" ]; then
8
+ echo "Error: --username argument is not set" >&2
9
+ exit 1
10
+ fi
11
+
12
+ SSH_DIR="$HOME/.ssh"
13
+ AUTHORIZED_KEYS="$SSH_DIR/authorized_keys"
14
+
15
+ mkdir -p "$SSH_DIR"
16
+ chmod 700 "$SSH_DIR"
17
+
18
+ TMP_KEYS=$(mktemp)
19
+ curl -fsSL "https://github.com/${USERNAME}.keys" > "$TMP_KEYS"
20
+
21
+ touch "$AUTHORIZED_KEYS"
22
+ chmod 600 "$AUTHORIZED_KEYS"
23
+
24
+ grep -Fvx -f "$AUTHORIZED_KEYS" "$TMP_KEYS" >> "$AUTHORIZED_KEYS"
25
+
26
+ rm "$TMP_KEYS"
27
+
28
+ echo "SSH keys from '${USERNAME}' added to '${AUTHORIZED_KEYS}'"
@@ -0,0 +1,15 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ FMTR_DEV_SSH_PORT=${FMTR_DEV_SSH_PORT:-22}
6
+
7
+ [ -f /home/user/.ssh/ssh_host ] || ssh-keygen -f /home/user/.ssh/ssh_host -N '' -t rsa
8
+
9
+ exec /usr/sbin/sshd -D -e -p "$FMTR_DEV_SSH_PORT" \
10
+ -h /home/user/.ssh/ssh_host \
11
+ -o PasswordAuthentication=no \
12
+ -o PermitRootLogin=prohibit-password \
13
+ -o PubkeyAuthentication=yes \
14
+ -o AuthorizedKeysFile=/home/user/.ssh/authorized_keys \
15
+ -o LogLevel=VERBOSE
@@ -0,0 +1,10 @@
1
+ #!/bin/bash
2
+
3
+ . parse-args "$@"
4
+
5
+ if ! command -v vlc >/dev/null 2>&1; then
6
+ echo "VLC not found. Installing dependencies..."
7
+ sudo apt-headless vlc-bin vlc-plugin-base
8
+ fi
9
+
10
+ vlc -I telnet --telnet-password ${PASSWORD-password} --telnet-host ${HOST-0.0.0.0}:${PORT-4212}
@@ -0,0 +1,17 @@
1
+ #!/bin/bash
2
+
3
+ . parse-args "$@"
4
+
5
+ sudo qemu-system-x86_64 \
6
+ -enable-kvm \
7
+ -m ${GB-12}G \
8
+ -smp ${CPUS-4} \
9
+ -cpu host \
10
+ -machine q35 \
11
+ -drive if=pflash,format=raw,readonly=on,file=/usr/share/OVMF/OVMF_CODE_4M.fd \
12
+ -drive if=pflash,format=raw,file=/usr/share/OVMF/OVMF_VARS_4M.fd \
13
+ -drive file=/opt/dev/vm/${NAME-default}/disk.qcow2,format=qcow2,if=virtio,cache=writeback,aio=threads \
14
+ -device virtio-net-pci,netdev=net0 \
15
+ -netdev bridge,id=net0,br=${BR-br0} \
16
+ -display ${DISPLAY_VM-gtk} \
17
+ -serial mon:stdio