nullforge 0.1.0__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.
- nullforge/__init__.py +1 -0
- nullforge/foundry/README.md +0 -0
- nullforge/foundry/__init__.py +7 -0
- nullforge/foundry/full_cast.py +22 -0
- nullforge/inventories/README.md +0 -0
- nullforge/inventories/example.py +87 -0
- nullforge/models/__init__.py +1 -0
- nullforge/models/containers.py +60 -0
- nullforge/models/dns.py +222 -0
- nullforge/models/monitoring/__init__.py +29 -0
- nullforge/models/monitoring/base.py +19 -0
- nullforge/models/monitoring/nezha.py +13 -0
- nullforge/models/netsec.py +43 -0
- nullforge/models/profiles.py +33 -0
- nullforge/models/system.py +15 -0
- nullforge/models/users.py +9 -0
- nullforge/models/warp.py +65 -0
- nullforge/models/zerotrust.py +9 -0
- nullforge/molds/__init__.py +38 -0
- nullforge/molds/base_mold.py +59 -0
- nullforge/molds/containers.py +26 -0
- nullforge/molds/defaults.py +10 -0
- nullforge/molds/dns.py +70 -0
- nullforge/molds/features.py +75 -0
- nullforge/molds/haproxy.py +23 -0
- nullforge/molds/monitoring/__init__.py +45 -0
- nullforge/molds/monitoring/nezha.py +118 -0
- nullforge/molds/netsec.py +237 -0
- nullforge/molds/profiles.py +30 -0
- nullforge/molds/system.py +140 -0
- nullforge/molds/telemt.py +140 -0
- nullforge/molds/tor.py +28 -0
- nullforge/molds/user.py +77 -0
- nullforge/molds/utils.py +115 -0
- nullforge/molds/warp.py +54 -0
- nullforge/molds/xray.py +16 -0
- nullforge/molds/zerotrust.py +59 -0
- nullforge/runes/__init__.py +46 -0
- nullforge/runes/base.py +327 -0
- nullforge/runes/containers.py +212 -0
- nullforge/runes/dns.py +256 -0
- nullforge/runes/haproxy.py +135 -0
- nullforge/runes/monitoring.py +21 -0
- nullforge/runes/netsec.py +547 -0
- nullforge/runes/prepare.py +30 -0
- nullforge/runes/profiles.py +583 -0
- nullforge/runes/telemt.py +252 -0
- nullforge/runes/tor.py +57 -0
- nullforge/runes/users.py +159 -0
- nullforge/runes/warp.py +273 -0
- nullforge/runes/xray.py +81 -0
- nullforge/runes/zerotrust.py +91 -0
- nullforge/smithy/__init__.py +1 -0
- nullforge/smithy/admin.py +40 -0
- nullforge/smithy/arch.py +36 -0
- nullforge/smithy/blocky.py +37 -0
- nullforge/smithy/cloudflare.py +32 -0
- nullforge/smithy/github.py +256 -0
- nullforge/smithy/http.py +85 -0
- nullforge/smithy/install.py +149 -0
- nullforge/smithy/monitoring/__init__.py +1 -0
- nullforge/smithy/monitoring/nezha/__init__.py +0 -0
- nullforge/smithy/monitoring/nezha/agent.py +54 -0
- nullforge/smithy/monitoring/nezha/dashboard.py +137 -0
- nullforge/smithy/monitoring/nezha/deploy.py +81 -0
- nullforge/smithy/network.py +95 -0
- nullforge/smithy/packages.py +176 -0
- nullforge/smithy/service.py +37 -0
- nullforge/smithy/sni.py +358 -0
- nullforge/smithy/swap.py +132 -0
- nullforge/smithy/system.py +86 -0
- nullforge/smithy/versions.py +282 -0
- nullforge/templates/__init__.py +32 -0
- nullforge/templates/cloudflared/tunnel.yml.j2 +6 -0
- nullforge/templates/dns/blocky.yaml.j2 +11 -0
- nullforge/templates/dns/dns.yaml.j2 +6 -0
- nullforge/templates/dns/resolv.conf.j2 +1 -0
- nullforge/templates/dns/resolved.conf.j2 +13 -0
- nullforge/templates/etc/default/zramswap.j2 +13 -0
- nullforge/templates/nvim/nvim_patch.lua.j2 +5 -0
- nullforge/templates/profiles/direnv.toml +2 -0
- nullforge/templates/profiles/starship.toml +186 -0
- nullforge/templates/profiles/tmux.conf +32 -0
- nullforge/templates/profiles/zshrc.j2 +46 -0
- nullforge/templates/scripts/telemt-synfix.sh +89 -0
- nullforge/templates/scripts/teleproxy-warp.sh +127 -0
- nullforge/templates/scripts/warp-v6-policy.sh +118 -0
- nullforge/templates/scripts/zt-tunnel-warp.sh +28 -0
- nullforge/templates/systemd/blocky.service.j2 +19 -0
- nullforge/templates/systemd/cloudflare-tunnel.service.j2 +27 -0
- nullforge/templates/systemd/cloudflare-warp.service.j2 +21 -0
- nullforge/templates/systemd/dns-internal.service.j2 +15 -0
- nullforge/templates/systemd/telemt.service.j2 +36 -0
- nullforge/templates/telemt/telemt.toml.j2 +49 -0
- nullforge/templates/tor/torrc.j2 +5 -0
- nullforge-0.1.0.dist-info/METADATA +39 -0
- nullforge-0.1.0.dist-info/RECORD +99 -0
- nullforge-0.1.0.dist-info/WHEEL +4 -0
- nullforge-0.1.0.dist-info/licenses/LICENSE +7 -0
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
"""Shell profiles and tools deployment module."""
|
|
2
|
+
|
|
3
|
+
import shlex
|
|
4
|
+
|
|
5
|
+
from pyinfra.context import host
|
|
6
|
+
from pyinfra.facts.files import Block, Directory, File
|
|
7
|
+
from pyinfra.operations import files, git, server
|
|
8
|
+
|
|
9
|
+
from nullforge.models.profiles import NerdFont
|
|
10
|
+
from nullforge.molds import FeaturesMold, ProfilesMold, UserMold
|
|
11
|
+
from nullforge.smithy.github import sha256_for_download_url
|
|
12
|
+
from nullforge.smithy.http import CURL_ARGS
|
|
13
|
+
from nullforge.smithy.install import install_release_binary
|
|
14
|
+
from nullforge.smithy.packages import get_pm
|
|
15
|
+
from nullforge.smithy.versions import STATIC_URLS, ZSH_PLUGINS, Versions, get_versions, is_pinned_version_installed
|
|
16
|
+
from nullforge.templates import get_template_path, render_template
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
ZSHRC_MARKER = "# {mark} NULLFORGE MANAGED BLOCK"
|
|
20
|
+
"""Marker around the generated part of ``.zshrc``; anything outside it survives a re-deploy."""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def deploy_shell_profiles() -> None:
|
|
24
|
+
"""Deploy shell profiles and tools configuration."""
|
|
25
|
+
|
|
26
|
+
features: FeaturesMold = host.data.features
|
|
27
|
+
profiles_opts: ProfilesMold = features.profiles
|
|
28
|
+
reinstall = profiles_opts.reinstall
|
|
29
|
+
|
|
30
|
+
_install_eza(reinstall)
|
|
31
|
+
|
|
32
|
+
_install_tmux(reinstall)
|
|
33
|
+
|
|
34
|
+
_install_nvim(reinstall)
|
|
35
|
+
|
|
36
|
+
_install_zoxide(reinstall)
|
|
37
|
+
|
|
38
|
+
_install_direnv(reinstall)
|
|
39
|
+
|
|
40
|
+
_install_starship(reinstall)
|
|
41
|
+
|
|
42
|
+
for user, home_dir in host.loop(_get_profile_targets(features)):
|
|
43
|
+
_install_user_profiles(user, home_dir, reinstall, profiles_opts.font)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _get_profile_targets(features: FeaturesMold) -> list[tuple[str, str]]:
|
|
47
|
+
"""Get list of users and home directories for profile installation."""
|
|
48
|
+
|
|
49
|
+
user_opts: UserMold = features.users
|
|
50
|
+
profiles_opts: ProfilesMold = features.profiles
|
|
51
|
+
targets = []
|
|
52
|
+
if profiles_opts.for_root:
|
|
53
|
+
targets.append(("root", "/root"))
|
|
54
|
+
|
|
55
|
+
if profiles_opts.for_user:
|
|
56
|
+
user = user_opts.name
|
|
57
|
+
targets.append((user, f"/home/{user}"))
|
|
58
|
+
|
|
59
|
+
return targets
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _install_user_profiles(user: str, home_dir: str, reinstall: bool, font: NerdFont | None) -> None:
|
|
63
|
+
"""Configure user profile."""
|
|
64
|
+
|
|
65
|
+
_configure_user_oh_my_zsh(user, home_dir, reinstall)
|
|
66
|
+
_configure_user_shell_profiles(user, home_dir)
|
|
67
|
+
if font:
|
|
68
|
+
_install_nerd_font(user, home_dir, font, reinstall)
|
|
69
|
+
_install_user_tmux(user, home_dir, reinstall)
|
|
70
|
+
_install_user_nvim(user, home_dir, reinstall)
|
|
71
|
+
_install_atuin(user, home_dir, reinstall)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _configure_user_oh_my_zsh(user: str, home_dir: str, reinstall: bool) -> None:
|
|
75
|
+
"""Install oh-my-zsh and its plugins for a specific user."""
|
|
76
|
+
|
|
77
|
+
oh_my_zsh_dir = f"{home_dir}/.oh-my-zsh"
|
|
78
|
+
plugins_dir = f"{oh_my_zsh_dir}/custom/plugins"
|
|
79
|
+
fact_kwargs = {"_sudo": True, "_sudo_user": user}
|
|
80
|
+
|
|
81
|
+
if reinstall:
|
|
82
|
+
files.directory(
|
|
83
|
+
name=f"Remove existing oh-my-zsh for {user}",
|
|
84
|
+
path=oh_my_zsh_dir,
|
|
85
|
+
present=False,
|
|
86
|
+
_sudo=True,
|
|
87
|
+
_sudo_user=user,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
if not host.get_fact(Directory, oh_my_zsh_dir, **fact_kwargs):
|
|
91
|
+
git.repo(
|
|
92
|
+
name=f"Install oh-my-zsh for {user}",
|
|
93
|
+
src="https://github.com/ohmyzsh/ohmyzsh",
|
|
94
|
+
dest=oh_my_zsh_dir,
|
|
95
|
+
_sudo=True,
|
|
96
|
+
_sudo_user=user,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
for plugin_name, plugin_src in host.loop(ZSH_PLUGINS.items()):
|
|
100
|
+
plugin_dir = f"{plugins_dir}/{plugin_name}"
|
|
101
|
+
if reinstall:
|
|
102
|
+
files.directory(
|
|
103
|
+
name=f"Remove existing {plugin_name} plugin for {user}",
|
|
104
|
+
path=plugin_dir,
|
|
105
|
+
present=False,
|
|
106
|
+
_sudo=True,
|
|
107
|
+
_sudo_user=user,
|
|
108
|
+
)
|
|
109
|
+
if not host.get_fact(Directory, plugin_dir, **fact_kwargs):
|
|
110
|
+
git.repo(
|
|
111
|
+
name=f"Install {plugin_name} plugin for {user}",
|
|
112
|
+
src=plugin_src,
|
|
113
|
+
dest=plugin_dir,
|
|
114
|
+
_sudo=True,
|
|
115
|
+
_sudo_user=user,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _configure_user_shell_profiles(user: str, home_dir: str) -> None:
|
|
120
|
+
"""Configure shell profiles (.zshrc, starship, direnv) for a specific user."""
|
|
121
|
+
|
|
122
|
+
_configure_user_zshrc(user, home_dir)
|
|
123
|
+
|
|
124
|
+
files.put(
|
|
125
|
+
name=f"Configure starship prompt for {user}",
|
|
126
|
+
src=get_template_path("profiles/starship.toml"),
|
|
127
|
+
dest=f"{home_dir}/.config/starship.toml",
|
|
128
|
+
mode="0644",
|
|
129
|
+
_sudo=True,
|
|
130
|
+
_sudo_user=user,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
files.put(
|
|
134
|
+
name=f"Configure direnv for {user}",
|
|
135
|
+
src=get_template_path("profiles/direnv.toml"),
|
|
136
|
+
dest=f"{home_dir}/.config/direnv/direnv.toml",
|
|
137
|
+
mode="0644",
|
|
138
|
+
create_remote_dir=True,
|
|
139
|
+
_sudo=True,
|
|
140
|
+
_sudo_user=user,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _configure_user_zshrc(user: str, home_dir: str) -> None:
|
|
145
|
+
"""Write the generated part of ``.zshrc`` into a marked block, leaving host-specific edits untouched."""
|
|
146
|
+
|
|
147
|
+
zshrc_path = f"{home_dir}/.zshrc"
|
|
148
|
+
fact_kwargs = {"_sudo": True, "_sudo_user": user}
|
|
149
|
+
|
|
150
|
+
# an empty list means the file exists without markers: a pre-marker deploy templated the whole
|
|
151
|
+
# file, so drop every line once - otherwise the block gets prepended to a stale copy of itself
|
|
152
|
+
if host.get_fact(Block, path=zshrc_path, marker=ZSHRC_MARKER, **fact_kwargs) == []:
|
|
153
|
+
files.line(
|
|
154
|
+
name=f"Clear unmarked .zshrc for {user}",
|
|
155
|
+
path=zshrc_path,
|
|
156
|
+
line=".*",
|
|
157
|
+
present=False,
|
|
158
|
+
backup=True,
|
|
159
|
+
_sudo=True,
|
|
160
|
+
_sudo_user=user,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
files.block(
|
|
164
|
+
name=f"Configure .zshrc for {user}",
|
|
165
|
+
path=zshrc_path,
|
|
166
|
+
content=render_template("profiles/zshrc.j2", home=home_dir).rstrip("\n"),
|
|
167
|
+
marker=ZSHRC_MARKER,
|
|
168
|
+
before=True,
|
|
169
|
+
after=True,
|
|
170
|
+
_sudo=True,
|
|
171
|
+
_sudo_user=user,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# files.block writes through a mktemp file, so a freshly created .zshrc would keep its 0600
|
|
175
|
+
files.file(
|
|
176
|
+
name=f"Set .zshrc permissions for {user}",
|
|
177
|
+
path=zshrc_path,
|
|
178
|
+
mode="0644",
|
|
179
|
+
_sudo=True,
|
|
180
|
+
_sudo_user=user,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _install_nerd_font(user: str, home_dir: str, family: NerdFont, reinstall: bool = False) -> None:
|
|
185
|
+
"""Install a Nerd Font family for a user straight from its release archive."""
|
|
186
|
+
|
|
187
|
+
versions = get_versions()
|
|
188
|
+
version = versions.versions["nerd_fonts"]
|
|
189
|
+
fonts_root = f"{home_dir}/.local/share/fonts"
|
|
190
|
+
# the version is part of the directory name, so bumping the pin re-triggers the install
|
|
191
|
+
font_dir = f"{fonts_root}/{family}-{version}"
|
|
192
|
+
|
|
193
|
+
if not reinstall and host.get_fact(Directory, font_dir, _sudo=True, _sudo_user=user):
|
|
194
|
+
return
|
|
195
|
+
|
|
196
|
+
url = versions.nerd_font_tar(family)
|
|
197
|
+
# kept in the user's own cache so the two profile targets never fight over one /tmp path
|
|
198
|
+
cache_dir = f"{home_dir}/.cache/nullforge"
|
|
199
|
+
archive_path = f"{cache_dir}/{family}-{version}.tar.xz"
|
|
200
|
+
|
|
201
|
+
files.directory(
|
|
202
|
+
name=f"Create nullforge cache directory for {user}",
|
|
203
|
+
path=cache_dir,
|
|
204
|
+
mode="0755",
|
|
205
|
+
_sudo=True,
|
|
206
|
+
_sudo_user=user,
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
files.download(
|
|
210
|
+
name=f"Download {family} Nerd Font for {user}",
|
|
211
|
+
src=url,
|
|
212
|
+
dest=archive_path,
|
|
213
|
+
force=True,
|
|
214
|
+
sha256sum=sha256_for_download_url(url),
|
|
215
|
+
extra_curl_args=Versions.release_curl_args(),
|
|
216
|
+
_sudo=True,
|
|
217
|
+
_sudo_user=user,
|
|
218
|
+
_retries=3,
|
|
219
|
+
_retry_delay=10,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
server.shell(
|
|
223
|
+
name=f"Install {family} Nerd Font for {user}",
|
|
224
|
+
commands=[
|
|
225
|
+
f"rm -rf {shlex.quote(f'{fonts_root}/{family}')}-*",
|
|
226
|
+
f"mkdir -p {shlex.quote(font_dir)}",
|
|
227
|
+
f"tar -xJf {shlex.quote(archive_path)} -C {shlex.quote(font_dir)}",
|
|
228
|
+
f"fc-cache -f {shlex.quote(fonts_root)}",
|
|
229
|
+
f"rm -f {shlex.quote(archive_path)}",
|
|
230
|
+
],
|
|
231
|
+
_sudo=True,
|
|
232
|
+
_sudo_user=user,
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def _install_user_tmux(user: str, home_dir: str, reinstall: bool = False) -> None:
|
|
237
|
+
"""Install and configure tmux for a specific user."""
|
|
238
|
+
|
|
239
|
+
tpm_dir = f"{home_dir}/.tmux/plugins/tpm"
|
|
240
|
+
config_dir = f"{home_dir}/.config/tmux"
|
|
241
|
+
fact_kwargs = {"_sudo": True, "_sudo_user": user}
|
|
242
|
+
|
|
243
|
+
if reinstall or not host.get_fact(Directory, tpm_dir, **fact_kwargs):
|
|
244
|
+
files.directory(
|
|
245
|
+
name=f"Remove existing tmux plugins dir for {user}",
|
|
246
|
+
path=f"{home_dir}/.tmux",
|
|
247
|
+
present=False,
|
|
248
|
+
_sudo=True,
|
|
249
|
+
_sudo_user=user,
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
files.directory(
|
|
253
|
+
name=f"Remove existing tmux config dir for {user}",
|
|
254
|
+
path=config_dir,
|
|
255
|
+
present=False,
|
|
256
|
+
_sudo=True,
|
|
257
|
+
_sudo_user=user,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
git.repo(
|
|
261
|
+
name=f"Install TPM (Tmux Plugin Manager) for {user}",
|
|
262
|
+
src="https://github.com/tmux-plugins/tpm",
|
|
263
|
+
dest=tpm_dir,
|
|
264
|
+
_sudo=True,
|
|
265
|
+
_sudo_user=user,
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
files.directory(
|
|
269
|
+
name=f"Create tmux config directory for {user}",
|
|
270
|
+
path=config_dir,
|
|
271
|
+
mode="0755",
|
|
272
|
+
_sudo=True,
|
|
273
|
+
_sudo_user=user,
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
files.put(
|
|
277
|
+
name=f"Setup tmux config for {user}",
|
|
278
|
+
src=get_template_path("profiles/tmux.conf"),
|
|
279
|
+
dest=f"{config_dir}/tmux.conf",
|
|
280
|
+
mode="0644",
|
|
281
|
+
_sudo=True,
|
|
282
|
+
_sudo_user=user,
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def _install_user_nvim(user: str, home_dir: str, reinstall: bool = False) -> None:
|
|
287
|
+
"""Install and configure nvim/NvChad for a specific user."""
|
|
288
|
+
|
|
289
|
+
fact_kwargs = {"_sudo": True, "_sudo_user": user}
|
|
290
|
+
if not reinstall and host.get_fact(File, f"{home_dir}/.config/nvim/lua/chadrc.lua", **fact_kwargs):
|
|
291
|
+
return
|
|
292
|
+
|
|
293
|
+
for nvim_path in host.loop(
|
|
294
|
+
(
|
|
295
|
+
f"{home_dir}/.config/nvim",
|
|
296
|
+
f"{home_dir}/.local/state/nvim",
|
|
297
|
+
f"{home_dir}/.local/share/nvim",
|
|
298
|
+
)
|
|
299
|
+
):
|
|
300
|
+
files.directory(
|
|
301
|
+
name=f"Remove existing nvim path {nvim_path} for {user}",
|
|
302
|
+
path=nvim_path,
|
|
303
|
+
present=False,
|
|
304
|
+
_sudo=True,
|
|
305
|
+
_sudo_user=user,
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
git.repo(
|
|
309
|
+
name=f"Install NvChad for {user}",
|
|
310
|
+
src="https://github.com/NvChad/starter",
|
|
311
|
+
dest=f"{home_dir}/.config/nvim",
|
|
312
|
+
_sudo=True,
|
|
313
|
+
_sudo_user=user,
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
with open(get_template_path("nvim/nvim_patch.lua.j2"), encoding="utf-8") as f:
|
|
317
|
+
nvim_patch = f.read()
|
|
318
|
+
|
|
319
|
+
files.block(
|
|
320
|
+
name=f"Apply nvim patch to init.lua for {user}",
|
|
321
|
+
path=f"{home_dir}/.config/nvim/init.lua",
|
|
322
|
+
content=nvim_patch,
|
|
323
|
+
marker="-- {mark} Change cursor to original after exiting vim",
|
|
324
|
+
_sudo=True,
|
|
325
|
+
_sudo_user=user,
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
files.replace(
|
|
329
|
+
name=f"Change theme to tokyo-night for {user}",
|
|
330
|
+
path=f"{home_dir}/.config/nvim/lua/chadrc.lua",
|
|
331
|
+
text="onedark",
|
|
332
|
+
replace="tokyonight",
|
|
333
|
+
_sudo=True,
|
|
334
|
+
_sudo_user=user,
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def _install_starship(reinstall: bool = False) -> None:
|
|
339
|
+
"""Install starship prompt."""
|
|
340
|
+
|
|
341
|
+
pm = get_pm()
|
|
342
|
+
|
|
343
|
+
if pm.is_debian_based and pm.distro_major in [13, 25]:
|
|
344
|
+
if reinstall or not host.get_fact(File, "/usr/bin/starship"):
|
|
345
|
+
_install_starship_from_repo()
|
|
346
|
+
else:
|
|
347
|
+
if reinstall or not host.get_fact(File, "/usr/local/bin/starship"):
|
|
348
|
+
_install_starship_from_script()
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def _install_starship_from_repo() -> None:
|
|
352
|
+
"""Install starship prompt from repo."""
|
|
353
|
+
|
|
354
|
+
pm = get_pm()
|
|
355
|
+
pm.install(
|
|
356
|
+
name="Install starship from repo",
|
|
357
|
+
packages=["starship"],
|
|
358
|
+
_sudo=True,
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def _install_starship_from_script() -> None:
|
|
363
|
+
"""Install starship prompt from script."""
|
|
364
|
+
|
|
365
|
+
starship_install_path = "/tmp/starship.sh"
|
|
366
|
+
files.download(
|
|
367
|
+
name="Download starship installation script",
|
|
368
|
+
src=STATIC_URLS["starship_install"],
|
|
369
|
+
dest=starship_install_path,
|
|
370
|
+
mode="0755",
|
|
371
|
+
extra_curl_args=CURL_ARGS,
|
|
372
|
+
_sudo=True,
|
|
373
|
+
_retries=3,
|
|
374
|
+
_retry_delay=10,
|
|
375
|
+
)
|
|
376
|
+
|
|
377
|
+
server.shell(
|
|
378
|
+
name="Install starship prompt",
|
|
379
|
+
commands=[f"sh {starship_install_path} --yes"],
|
|
380
|
+
_sudo=True,
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def _install_atuin(user: str, home_dir: str, reinstall: bool = False) -> None:
|
|
385
|
+
"""Install atuin for better shell history."""
|
|
386
|
+
|
|
387
|
+
fact_kwargs = {"_sudo": True, "_sudo_user": user}
|
|
388
|
+
if not reinstall and host.get_fact(File, f"{home_dir}/.atuin/bin/atuin", **fact_kwargs):
|
|
389
|
+
return
|
|
390
|
+
|
|
391
|
+
atuin_install_path = "/tmp/atuin.sh"
|
|
392
|
+
files.download(
|
|
393
|
+
name=f"Download atuin installation script for {user}",
|
|
394
|
+
src=STATIC_URLS["atuin_install"],
|
|
395
|
+
dest=atuin_install_path,
|
|
396
|
+
mode="0755",
|
|
397
|
+
extra_curl_args=CURL_ARGS,
|
|
398
|
+
_sudo=True,
|
|
399
|
+
_retries=3,
|
|
400
|
+
_retry_delay=10,
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
server.shell(
|
|
404
|
+
name=f"Install atuin installation script for {user}",
|
|
405
|
+
commands=[f"sh {atuin_install_path} -- --non-interactive"],
|
|
406
|
+
_sudo=True,
|
|
407
|
+
_sudo_user=user,
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def _install_zoxide(reinstall: bool = False) -> None:
|
|
412
|
+
"""Install zoxide for better navigation."""
|
|
413
|
+
|
|
414
|
+
if not reinstall and host.get_fact(File, "/usr/local/bin/zoxide"):
|
|
415
|
+
return
|
|
416
|
+
|
|
417
|
+
zoxide_install_path = "/tmp/zoxide.sh"
|
|
418
|
+
files.download(
|
|
419
|
+
name="Download zoxide installation script",
|
|
420
|
+
src=STATIC_URLS["zoxide_install"],
|
|
421
|
+
dest=zoxide_install_path,
|
|
422
|
+
mode="0755",
|
|
423
|
+
extra_curl_args=CURL_ARGS,
|
|
424
|
+
_sudo=True,
|
|
425
|
+
_retries=3,
|
|
426
|
+
_retry_delay=10,
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
server.shell(
|
|
430
|
+
name="Install zoxide",
|
|
431
|
+
commands=[f"sh {zoxide_install_path} --bin-dir /usr/local/bin"],
|
|
432
|
+
_sudo=True,
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
def _install_direnv(reinstall: bool = False) -> None:
|
|
437
|
+
"""Install direnv for better env management."""
|
|
438
|
+
|
|
439
|
+
direnv_exec_path = "/usr/local/bin/direnv"
|
|
440
|
+
if not reinstall and is_pinned_version_installed("direnv", direnv_exec_path):
|
|
441
|
+
return
|
|
442
|
+
|
|
443
|
+
install_release_binary(
|
|
444
|
+
name="Install direnv binary",
|
|
445
|
+
url=get_versions().direnv_url(),
|
|
446
|
+
dest=direnv_exec_path,
|
|
447
|
+
)
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def _install_eza(reinstall: bool = False) -> None:
|
|
451
|
+
"""Install eza binary for enhanced ls functionality."""
|
|
452
|
+
|
|
453
|
+
eza_exec_path = "/usr/local/bin/eza"
|
|
454
|
+
if not reinstall and is_pinned_version_installed("eza", eza_exec_path):
|
|
455
|
+
return
|
|
456
|
+
|
|
457
|
+
install_release_binary(
|
|
458
|
+
name="Extract eza and install eza binary",
|
|
459
|
+
url=get_versions().eza_tar(),
|
|
460
|
+
dest=eza_exec_path,
|
|
461
|
+
binary_name="eza",
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
|
|
465
|
+
def _install_tmux(reinstall: bool = False) -> None:
|
|
466
|
+
"""Install tmux package."""
|
|
467
|
+
|
|
468
|
+
if not reinstall and is_pinned_version_installed("tmux", "/usr/local/bin/tmux"):
|
|
469
|
+
return
|
|
470
|
+
|
|
471
|
+
versions = get_versions()
|
|
472
|
+
tmux_tar_path = "/tmp/tmux.tar.gz"
|
|
473
|
+
tmux_src_dir = f"/tmp/tmux-{versions.versions['tmux']}"
|
|
474
|
+
|
|
475
|
+
files.download(
|
|
476
|
+
name="Download tmux source",
|
|
477
|
+
src=versions.tmux_tar(),
|
|
478
|
+
dest=tmux_tar_path,
|
|
479
|
+
force=True,
|
|
480
|
+
extra_curl_args=get_versions().release_curl_args(),
|
|
481
|
+
_retries=3,
|
|
482
|
+
_retry_delay=10,
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
server.shell(
|
|
486
|
+
name="Extract and build tmux",
|
|
487
|
+
commands=[
|
|
488
|
+
"rm -rf /tmp/tmux-*/",
|
|
489
|
+
f"tar -zxf {tmux_tar_path} -C /tmp/ && rm -f {tmux_tar_path}",
|
|
490
|
+
f"cd {tmux_src_dir} && ./configure",
|
|
491
|
+
f"cd {tmux_src_dir} && make -j$(nproc) && make install",
|
|
492
|
+
f"rm -rf {tmux_src_dir}",
|
|
493
|
+
],
|
|
494
|
+
_sudo=True,
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
pm = get_pm()
|
|
498
|
+
pm.install(
|
|
499
|
+
name="Remove existing tmux package",
|
|
500
|
+
packages=["tmux"],
|
|
501
|
+
present=False,
|
|
502
|
+
_sudo=True,
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
def _install_nvim(reinstall: bool = False) -> None:
|
|
507
|
+
"""Install nvim package."""
|
|
508
|
+
|
|
509
|
+
if not reinstall and is_pinned_version_installed("nvim", "/usr/bin/nvim-source/AppRun"):
|
|
510
|
+
return
|
|
511
|
+
|
|
512
|
+
# clean up any previous install so old and new extracted files never mix
|
|
513
|
+
files.directory(
|
|
514
|
+
name="Remove existing nvim-source directory",
|
|
515
|
+
path="/usr/bin/nvim-source",
|
|
516
|
+
present=False,
|
|
517
|
+
_sudo=True,
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
files.file(
|
|
521
|
+
name="Remove existing nvim binary",
|
|
522
|
+
path="/usr/bin/nvim",
|
|
523
|
+
present=False,
|
|
524
|
+
force=True,
|
|
525
|
+
force_backup=False,
|
|
526
|
+
_sudo=True,
|
|
527
|
+
)
|
|
528
|
+
|
|
529
|
+
nvim_appimage_path = "/tmp/nvim.appimage"
|
|
530
|
+
files.download(
|
|
531
|
+
name="Download nvim appimage",
|
|
532
|
+
src=get_versions().nvim_appimage(),
|
|
533
|
+
dest=nvim_appimage_path,
|
|
534
|
+
mode="0755",
|
|
535
|
+
force=True,
|
|
536
|
+
extra_curl_args=get_versions().release_curl_args(),
|
|
537
|
+
_sudo=True,
|
|
538
|
+
_retries=3,
|
|
539
|
+
_retry_delay=10,
|
|
540
|
+
)
|
|
541
|
+
|
|
542
|
+
files.directory(
|
|
543
|
+
name="Create nvim source directory",
|
|
544
|
+
path="/usr/bin/nvim-source",
|
|
545
|
+
mode="0755",
|
|
546
|
+
_sudo=True,
|
|
547
|
+
)
|
|
548
|
+
|
|
549
|
+
server.shell(
|
|
550
|
+
name="Extract nvim appimage to target directory",
|
|
551
|
+
commands=[f"cd /usr/bin && {nvim_appimage_path} --appimage-extract"],
|
|
552
|
+
_sudo=True,
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
server.shell(
|
|
556
|
+
name="Move extracted contents to nvim-source",
|
|
557
|
+
commands=["mv /usr/bin/squashfs-root/* /usr/bin/nvim-source/"],
|
|
558
|
+
_sudo=True,
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
files.directory(
|
|
562
|
+
name="Remove empty squashfs-root directory",
|
|
563
|
+
path="/usr/bin/squashfs-root",
|
|
564
|
+
present=False,
|
|
565
|
+
_sudo=True,
|
|
566
|
+
)
|
|
567
|
+
|
|
568
|
+
files.link(
|
|
569
|
+
name="Create symlink to nvim",
|
|
570
|
+
path="/usr/bin/nvim",
|
|
571
|
+
target="/usr/bin/nvim-source/AppRun",
|
|
572
|
+
_sudo=True,
|
|
573
|
+
)
|
|
574
|
+
|
|
575
|
+
files.file(
|
|
576
|
+
name="Remove nvim appimage file",
|
|
577
|
+
path=nvim_appimage_path,
|
|
578
|
+
present=False,
|
|
579
|
+
_sudo=True,
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
|
|
583
|
+
deploy_shell_profiles()
|