kitowall 7.7.0 → 7.9.0
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.
- package/dist/core/setup.js +11 -2
- package/package.json +3 -1
- package/scripts/bootstrap-host.sh +552 -0
- package/scripts/bootstrap-system.sh +187 -0
package/dist/core/setup.js
CHANGED
|
@@ -140,6 +140,11 @@ function bootstrapHostPath() {
|
|
|
140
140
|
function bootstrapSystemPath() {
|
|
141
141
|
return (0, node_path_1.join)(ROOT_DIR, 'scripts', 'bootstrap-system.sh');
|
|
142
142
|
}
|
|
143
|
+
async function assertBootstrapScriptExists(targetPath, label) {
|
|
144
|
+
if (await fileExists(targetPath))
|
|
145
|
+
return;
|
|
146
|
+
throw new Error(`${label} not found at ${targetPath}. Reinstala el CLI con: npm i -g --prefix ~/.local kitowall@latest`);
|
|
147
|
+
}
|
|
143
148
|
async function bootstrapVersionsPath() {
|
|
144
149
|
return (0, node_path_1.join)(homeDir(), '.local', 'share', 'kitowall', 'bootstrap-versions.json');
|
|
145
150
|
}
|
|
@@ -365,7 +370,9 @@ async function listSetupVersions() {
|
|
|
365
370
|
return { ok: true, items };
|
|
366
371
|
}
|
|
367
372
|
async function runBootstrapHostMode(mode) {
|
|
368
|
-
const
|
|
373
|
+
const scriptPath = bootstrapHostPath();
|
|
374
|
+
await assertBootstrapScriptExists(scriptPath, 'bootstrap-host.sh');
|
|
375
|
+
const out = await (0, exec_1.run)('bash', [scriptPath], {
|
|
369
376
|
env: {
|
|
370
377
|
...process.env,
|
|
371
378
|
HOME: homeDir(),
|
|
@@ -377,7 +384,9 @@ async function runBootstrapHostMode(mode) {
|
|
|
377
384
|
return { ok: true, code: out.code, logs: `${out.stdout}${out.stderr}` };
|
|
378
385
|
}
|
|
379
386
|
async function runBootstrapSystemItems(ids) {
|
|
380
|
-
const
|
|
387
|
+
const scriptPath = bootstrapSystemPath();
|
|
388
|
+
await assertBootstrapScriptExists(scriptPath, 'bootstrap-system.sh');
|
|
389
|
+
const out = await (0, exec_1.run)('pkexec', ['bash', scriptPath, ...ids], {
|
|
381
390
|
env: {
|
|
382
391
|
...process.env,
|
|
383
392
|
HOME: homeDir(),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kitowall",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.9.0",
|
|
4
4
|
"description": "CLI/daemon for Hyprland wallpapers using swww with pack-based rotation.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
"type": "commonjs",
|
|
11
11
|
"files": [
|
|
12
12
|
"dist",
|
|
13
|
+
"scripts/bootstrap-host.sh",
|
|
14
|
+
"scripts/bootstrap-system.sh",
|
|
13
15
|
"README.md",
|
|
14
16
|
"LICENSE.md",
|
|
15
17
|
"NOTICE.md",
|
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Host bootstrap for AppImage/non-Flatpak installations.
|
|
5
|
+
# Installs system dependencies plus kitowall/kitsune/kitsune-rendercore binaries.
|
|
6
|
+
|
|
7
|
+
KITSUNE_REPO="${KITSUNE_REPO:-https://github.com/KitotsuMolina/Kitsune.git}"
|
|
8
|
+
KITSUNE_RENDERCORE_REPO="${KITSUNE_RENDERCORE_REPO:-https://github.com/KitotsuMolina/Kitsune-RenderCore.git}"
|
|
9
|
+
KITSUNE_TAG="${KITSUNE_TAG:-}"
|
|
10
|
+
KITSUNE_RENDERCORE_TAG="${KITSUNE_RENDERCORE_TAG:-}"
|
|
11
|
+
KITOWALL_BOOTSTRAP_MODE="${KITOWALL_BOOTSTRAP_MODE:-full}"
|
|
12
|
+
KITOWALL_SKIP_SYSTEM_DEPS="${KITOWALL_SKIP_SYSTEM_DEPS:-0}"
|
|
13
|
+
|
|
14
|
+
BOOTSTRAP_KITSUNE_VERSION=""
|
|
15
|
+
BOOTSTRAP_RENDERCORE_VERSION=""
|
|
16
|
+
|
|
17
|
+
need_cmd() {
|
|
18
|
+
command -v "$1" >/dev/null 2>&1
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
prepend_user_path() {
|
|
22
|
+
local home_dir="${HOME:?HOME is required}"
|
|
23
|
+
export PATH="$home_dir/.local/bin:$home_dir/.cargo/bin:$PATH"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
run_sudo() {
|
|
27
|
+
if need_cmd sudo; then
|
|
28
|
+
sudo "$@"
|
|
29
|
+
else
|
|
30
|
+
"$@"
|
|
31
|
+
fi
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
wait_pacman_lock() {
|
|
35
|
+
local lock_file="/var/lib/pacman/db.lck"
|
|
36
|
+
local tries=0
|
|
37
|
+
local max_tries=40
|
|
38
|
+
|
|
39
|
+
while [[ -e "$lock_file" ]]; do
|
|
40
|
+
# If a package manager is still running, just wait.
|
|
41
|
+
if pgrep -x pacman >/dev/null 2>&1 || \
|
|
42
|
+
pgrep -x yay >/dev/null 2>&1 || \
|
|
43
|
+
pgrep -x paru >/dev/null 2>&1 || \
|
|
44
|
+
pgrep -x makepkg >/dev/null 2>&1; then
|
|
45
|
+
tries=$((tries + 1))
|
|
46
|
+
if ((tries > max_tries)); then
|
|
47
|
+
echo "[bootstrap] pacman lock is busy for too long: $lock_file" >&2
|
|
48
|
+
return 1
|
|
49
|
+
fi
|
|
50
|
+
echo "[bootstrap] waiting for pacman lock (${tries}/${max_tries})..."
|
|
51
|
+
sleep 3
|
|
52
|
+
continue
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# No package process alive: stale lock, safe to remove.
|
|
56
|
+
echo "[bootstrap] removing stale pacman lock: $lock_file"
|
|
57
|
+
run_sudo rm -f "$lock_file"
|
|
58
|
+
break
|
|
59
|
+
done
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
ensure_user_bin_dirs() {
|
|
63
|
+
local home_dir
|
|
64
|
+
home_dir="${HOME:?HOME is required}"
|
|
65
|
+
mkdir -p "$home_dir/.local/bin" "$home_dir/.cargo/bin"
|
|
66
|
+
prepend_user_path
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
install_arch_deps() {
|
|
70
|
+
local repo_pkgs=(
|
|
71
|
+
nodejs npm
|
|
72
|
+
hyprland
|
|
73
|
+
swww
|
|
74
|
+
cava
|
|
75
|
+
pkgconf
|
|
76
|
+
gtk4
|
|
77
|
+
gtk4-layer-shell
|
|
78
|
+
jq
|
|
79
|
+
git
|
|
80
|
+
base-devel
|
|
81
|
+
)
|
|
82
|
+
# Avoid rust/rustup conflicts on Arch.
|
|
83
|
+
if need_cmd rustup; then
|
|
84
|
+
repo_pkgs+=(rustup)
|
|
85
|
+
elif need_cmd rustc && need_cmd cargo; then
|
|
86
|
+
:
|
|
87
|
+
else
|
|
88
|
+
repo_pkgs+=(rustup)
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
echo "[bootstrap] installing Arch repo packages: ${repo_pkgs[*]}"
|
|
92
|
+
wait_pacman_lock
|
|
93
|
+
run_sudo pacman -S --needed --noconfirm "${repo_pkgs[@]}"
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
install_ubuntu_deps() {
|
|
97
|
+
local pkgs=(
|
|
98
|
+
nodejs npm
|
|
99
|
+
rustc cargo
|
|
100
|
+
pkg-config
|
|
101
|
+
libgtk-4-dev
|
|
102
|
+
jq curl git
|
|
103
|
+
cava
|
|
104
|
+
)
|
|
105
|
+
echo "[bootstrap] installing Debian/Ubuntu packages: ${pkgs[*]}"
|
|
106
|
+
run_sudo apt-get update
|
|
107
|
+
run_sudo apt-get install -y "${pkgs[@]}"
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
install_system_deps() {
|
|
111
|
+
if need_cmd pacman; then
|
|
112
|
+
install_arch_deps
|
|
113
|
+
return
|
|
114
|
+
fi
|
|
115
|
+
if need_cmd apt-get; then
|
|
116
|
+
install_ubuntu_deps
|
|
117
|
+
return
|
|
118
|
+
fi
|
|
119
|
+
echo "[bootstrap] unsupported distro package manager. Install manually: nodejs npm rust cargo pkg-config gtk4 gtk4-layer-shell swww hyprland cava" >&2
|
|
120
|
+
exit 1
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
install_kitowall_cli() {
|
|
124
|
+
if ! need_cmd npm; then
|
|
125
|
+
echo "[bootstrap] npm is not available after dependency install" >&2
|
|
126
|
+
exit 1
|
|
127
|
+
fi
|
|
128
|
+
local home_dir
|
|
129
|
+
home_dir="${HOME:?HOME is required}"
|
|
130
|
+
echo "[bootstrap] installing/updating kitowall CLI in user prefix ($home_dir/.local)"
|
|
131
|
+
npm i -g --prefix "$home_dir/.local" kitowall
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
install_github_release_bin() {
|
|
135
|
+
local repo="$1" # owner/name
|
|
136
|
+
local asset_name="$2" # exact asset name
|
|
137
|
+
local out_bin="$3" # absolute output path
|
|
138
|
+
|
|
139
|
+
if ! need_cmd curl; then
|
|
140
|
+
echo "[bootstrap] missing curl for GitHub release install" >&2
|
|
141
|
+
return 1
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
local asset_url="https://github.com/${repo}/releases/latest/download/${asset_name}"
|
|
145
|
+
local release_tag=""
|
|
146
|
+
local out_dir tmp_bin
|
|
147
|
+
out_dir="$(dirname "$out_bin")"
|
|
148
|
+
mkdir -p "$out_dir"
|
|
149
|
+
tmp_bin="$(mktemp "${out_dir}/.${asset_name}.XXXXXX.tmp")"
|
|
150
|
+
|
|
151
|
+
echo "[bootstrap] downloading ${repo} asset: ${asset_name}"
|
|
152
|
+
if ! curl -fL \
|
|
153
|
+
--retry 3 \
|
|
154
|
+
--retry-delay 2 \
|
|
155
|
+
-H "User-Agent: Kitowall-Bootstrap" \
|
|
156
|
+
-H "Accept: application/octet-stream" \
|
|
157
|
+
"$asset_url" -o "$tmp_bin"; then
|
|
158
|
+
rm -f "$tmp_bin"
|
|
159
|
+
release_tag="$(latest_release_tag "$repo" || true)"
|
|
160
|
+
if [[ -n "${release_tag:-}" ]]; then
|
|
161
|
+
asset_url="https://github.com/${repo}/releases/download/${release_tag}/${asset_name}"
|
|
162
|
+
echo "[bootstrap] retrying ${repo} asset with explicit tag: ${release_tag}"
|
|
163
|
+
tmp_bin="$(mktemp "${out_dir}/.${asset_name}.XXXXXX.tmp")"
|
|
164
|
+
if ! curl -fL \
|
|
165
|
+
--retry 3 \
|
|
166
|
+
--retry-delay 2 \
|
|
167
|
+
-H "User-Agent: Kitowall-Bootstrap" \
|
|
168
|
+
-H "Accept: application/octet-stream" \
|
|
169
|
+
"$asset_url" -o "$tmp_bin"; then
|
|
170
|
+
rm -f "$tmp_bin"
|
|
171
|
+
return 1
|
|
172
|
+
fi
|
|
173
|
+
else
|
|
174
|
+
return 1
|
|
175
|
+
fi
|
|
176
|
+
fi
|
|
177
|
+
chmod 755 "$tmp_bin"
|
|
178
|
+
mv -f "$tmp_bin" "$out_bin"
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
latest_release_tag() {
|
|
182
|
+
local repo="$1"
|
|
183
|
+
|
|
184
|
+
if ! need_cmd curl || ! need_cmd jq; then
|
|
185
|
+
return 1
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
curl -fsSL "https://api.github.com/repos/${repo}/releases/latest" | jq -r '.tag_name // empty'
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
write_kitsune_wrapper() {
|
|
192
|
+
local home_dir="${HOME:?HOME is required}"
|
|
193
|
+
local share_dir="$home_dir/.local/share/kitsune"
|
|
194
|
+
local config_dir="$home_dir/.config/kitsune"
|
|
195
|
+
local state_dir="$home_dir/.local/state/kitsune"
|
|
196
|
+
|
|
197
|
+
mkdir -p "$home_dir/.local/bin" "$config_dir" "$state_dir/run"
|
|
198
|
+
rm -f "$home_dir/.local/bin/kitsune"
|
|
199
|
+
cat > "$home_dir/.local/bin/kitsune" <<EOF
|
|
200
|
+
#!/usr/bin/env bash
|
|
201
|
+
set -euo pipefail
|
|
202
|
+
export KITSUNE_HOME="$share_dir"
|
|
203
|
+
export KITSUNE_BIN_DIR="$share_dir/bin"
|
|
204
|
+
export KITSUNE_CFG="$config_dir/base.conf"
|
|
205
|
+
export KITSUNE_CAVA_CFG="$config_dir/cava.conf"
|
|
206
|
+
export KITSUNE_RUN_PREFIX="$state_dir/run"
|
|
207
|
+
mkdir -p "\$KITSUNE_RUN_PREFIX"
|
|
208
|
+
exec "$share_dir/scripts/kitsune.sh" "\$@"
|
|
209
|
+
EOF
|
|
210
|
+
chmod 755 "$home_dir/.local/bin/kitsune"
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
is_kitsune_bundle_corrupted() {
|
|
214
|
+
local home_dir="${HOME:?HOME is required}"
|
|
215
|
+
local script_path="$home_dir/.local/share/kitsune/scripts/kitsune.sh"
|
|
216
|
+
|
|
217
|
+
[[ ! -f "$script_path" ]] && return 0
|
|
218
|
+
grep -Fq 'exec "/home/' "$script_path" && grep -Fq '/scripts/kitsune.sh" "$@"' "$script_path"
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
is_kitsune_binary_corrupted() {
|
|
222
|
+
local home_dir="${HOME:?HOME is required}"
|
|
223
|
+
local bin_path="$home_dir/.local/share/kitsune/bin/kitsune"
|
|
224
|
+
|
|
225
|
+
[[ ! -f "$bin_path" ]] && return 0
|
|
226
|
+
file "$bin_path" 2>/dev/null | grep -Fq 'ELF' || return 0
|
|
227
|
+
return 1
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
repair_kitsune_host_layout() {
|
|
231
|
+
local home_dir="${HOME:?HOME is required}"
|
|
232
|
+
local share_dir="$home_dir/.local/share/kitsune"
|
|
233
|
+
local config_dir="$home_dir/.config/kitsune"
|
|
234
|
+
local state_dir="$home_dir/.local/state/kitsune"
|
|
235
|
+
|
|
236
|
+
if [[ ! -d "$share_dir/scripts" || ! -d "$share_dir/bin" ]]; then
|
|
237
|
+
echo "[bootstrap] kitsune share layout missing in $share_dir; run full bootstrap first" >&2
|
|
238
|
+
return 1
|
|
239
|
+
fi
|
|
240
|
+
|
|
241
|
+
if is_kitsune_binary_corrupted; then
|
|
242
|
+
echo "[bootstrap] kitsune binary is corrupted; restoring binaries from latest release"
|
|
243
|
+
install_kitsune_bins
|
|
244
|
+
return 0
|
|
245
|
+
fi
|
|
246
|
+
|
|
247
|
+
if is_kitsune_bundle_corrupted; then
|
|
248
|
+
echo "[bootstrap] kitsune bundle is corrupted; restoring bundle from latest release"
|
|
249
|
+
install_kitsune_bundle
|
|
250
|
+
return 0
|
|
251
|
+
fi
|
|
252
|
+
|
|
253
|
+
mkdir -p "$config_dir" "$state_dir/run"
|
|
254
|
+
if [[ -f "$share_dir/config/base.conf" && ! -f "$config_dir/base.conf" ]]; then
|
|
255
|
+
cp "$share_dir/config/base.conf" "$config_dir/base.conf"
|
|
256
|
+
fi
|
|
257
|
+
if [[ -f "$share_dir/config/cava.conf" && ! -f "$config_dir/cava.conf" ]]; then
|
|
258
|
+
cp "$share_dir/config/cava.conf" "$config_dir/cava.conf"
|
|
259
|
+
fi
|
|
260
|
+
|
|
261
|
+
write_kitsune_wrapper
|
|
262
|
+
echo "[bootstrap] repaired kitsune host layout"
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
install_kitsune_bundle() {
|
|
266
|
+
local home_dir="${HOME:?HOME is required}"
|
|
267
|
+
local share_dir="$home_dir/.local/share/kitsune"
|
|
268
|
+
local config_dir="$home_dir/.config/kitsune"
|
|
269
|
+
local state_dir="$home_dir/.local/state/kitsune"
|
|
270
|
+
local tmp_dir
|
|
271
|
+
tmp_dir="$(mktemp -d)"
|
|
272
|
+
|
|
273
|
+
if ! need_cmd curl || ! need_cmd jq || ! need_cmd tar; then
|
|
274
|
+
echo "[bootstrap] missing curl/jq/tar for kitsune bundle install" >&2
|
|
275
|
+
rm -rf "$tmp_dir"
|
|
276
|
+
return 1
|
|
277
|
+
fi
|
|
278
|
+
|
|
279
|
+
local api_url="https://api.github.com/repos/KitotsuMolina/Kitsune/releases/latest"
|
|
280
|
+
local tar_url
|
|
281
|
+
tar_url="$(curl -fsSL "$api_url" | jq -r '.tarball_url // empty')"
|
|
282
|
+
if [[ -z "${tar_url:-}" || "$tar_url" == "null" ]]; then
|
|
283
|
+
echo "[bootstrap] kitsune release tarball URL not found" >&2
|
|
284
|
+
rm -rf "$tmp_dir"
|
|
285
|
+
return 1
|
|
286
|
+
fi
|
|
287
|
+
|
|
288
|
+
echo "[bootstrap] downloading kitsune bundle sources"
|
|
289
|
+
curl -fL --retry 3 --retry-delay 2 "$tar_url" -o "$tmp_dir/kitsune.tar.gz"
|
|
290
|
+
tar -xzf "$tmp_dir/kitsune.tar.gz" -C "$tmp_dir"
|
|
291
|
+
local src_dir
|
|
292
|
+
src_dir="$(find "$tmp_dir" -mindepth 1 -maxdepth 1 -type d | head -n1)"
|
|
293
|
+
if [[ -z "${src_dir:-}" || ! -d "$src_dir/scripts" || ! -d "$src_dir/config" ]]; then
|
|
294
|
+
echo "[bootstrap] invalid kitsune source layout in tarball" >&2
|
|
295
|
+
rm -rf "$tmp_dir"
|
|
296
|
+
return 1
|
|
297
|
+
fi
|
|
298
|
+
|
|
299
|
+
mkdir -p "$share_dir" "$config_dir" "$state_dir"
|
|
300
|
+
rm -rf "$share_dir/scripts" "$share_dir/config" "$share_dir/completions"
|
|
301
|
+
cp -a "$src_dir/scripts" "$share_dir/"
|
|
302
|
+
cp -a "$src_dir/config" "$share_dir/"
|
|
303
|
+
[[ -d "$src_dir/completions" ]] && cp -a "$src_dir/completions" "$share_dir/" || true
|
|
304
|
+
mkdir -p "$share_dir/bin"
|
|
305
|
+
|
|
306
|
+
if [[ ! -f "$config_dir/base.conf" ]]; then
|
|
307
|
+
cp "$share_dir/config/base.conf" "$config_dir/base.conf"
|
|
308
|
+
fi
|
|
309
|
+
if [[ ! -f "$config_dir/cava.conf" ]]; then
|
|
310
|
+
cp "$share_dir/config/cava.conf" "$config_dir/cava.conf"
|
|
311
|
+
fi
|
|
312
|
+
|
|
313
|
+
# Ensure scripts point to prebuilt binaries instead of local cargo build paths.
|
|
314
|
+
sed -i \
|
|
315
|
+
-e '/^echo "\[i\] Building Rust renderer\.\.\."$/d' \
|
|
316
|
+
-e '/^cargo build --release --locked --bins$/d' \
|
|
317
|
+
-e '/^cargo build --release --bins$/d' \
|
|
318
|
+
-e '/^echo "\[i\] Building GTK overlay frontend\.\.\."$/d' \
|
|
319
|
+
-e '/^cargo build --release --bin kitsune-overlay$/d' \
|
|
320
|
+
-e 's|\./target/release/kitsune-overlay|"${KITSUNE_BIN_DIR:-./bin}"/kitsune-overlay|g' \
|
|
321
|
+
-e 's|\./target/release/kitsune|"${KITSUNE_BIN_DIR:-./bin}"/kitsune|g' \
|
|
322
|
+
-e 's|"${KITSUNE_BIN_DIR:-./bin}"/kitsune --config|"${KITSUNE_BIN_DIR:-./bin}"/kitsune run --config|g' \
|
|
323
|
+
"$share_dir/scripts/start.sh" \
|
|
324
|
+
"$share_dir/scripts/kitsune.sh"
|
|
325
|
+
sed -i -e '/^cargo build --release$/d' "$share_dir/scripts/install.sh" || true
|
|
326
|
+
|
|
327
|
+
write_kitsune_wrapper
|
|
328
|
+
|
|
329
|
+
rm -rf "$tmp_dir"
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
cargo_install_git_bin() {
|
|
333
|
+
local repo="$1"
|
|
334
|
+
local bin="$2"
|
|
335
|
+
local extra_args="$3"
|
|
336
|
+
local tag="${4:-}"
|
|
337
|
+
|
|
338
|
+
local args=(install --git "$repo" --locked --force "$bin")
|
|
339
|
+
if [[ -n "$tag" ]]; then
|
|
340
|
+
args+=(--tag "$tag")
|
|
341
|
+
fi
|
|
342
|
+
if [[ -n "$extra_args" ]]; then
|
|
343
|
+
# shellcheck disable=SC2206
|
|
344
|
+
local split_extra=($extra_args)
|
|
345
|
+
args+=("${split_extra[@]}")
|
|
346
|
+
fi
|
|
347
|
+
|
|
348
|
+
echo "[bootstrap] cargo ${args[*]}"
|
|
349
|
+
cargo "${args[@]}"
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
install_kitsune_bins() {
|
|
353
|
+
local home_dir="${HOME:?HOME is required}"
|
|
354
|
+
local bin_dir="$home_dir/.local/bin"
|
|
355
|
+
local share_bin_dir="$home_dir/.local/share/kitsune/bin"
|
|
356
|
+
local release_ok=0
|
|
357
|
+
mkdir -p "$share_bin_dir"
|
|
358
|
+
|
|
359
|
+
# Prefer prebuilt binaries from GitHub Releases to avoid local toolchain/submodule issues.
|
|
360
|
+
if install_github_release_bin "KitotsuMolina/Kitsune" "kitsune-linux-x86_64" "$share_bin_dir/kitsune" && \
|
|
361
|
+
install_github_release_bin "KitotsuMolina/Kitsune" "kitsune-overlay-linux-x86_64" "$share_bin_dir/kitsune-overlay" && \
|
|
362
|
+
install_github_release_bin "KitotsuMolina/Kitsune" "kitsune-color-resolve-linux-x86_64" "$share_bin_dir/kitsune-color-resolve" && \
|
|
363
|
+
install_github_release_bin "KitotsuMolina/Kitsune-RenderCore" "kitsune-rendercore-linux-x86_64" "$bin_dir/kitsune-rendercore"; then
|
|
364
|
+
ln -sf "$share_bin_dir/kitsune-overlay" "$bin_dir/kitsune-overlay"
|
|
365
|
+
ln -sf "$share_bin_dir/kitsune-color-resolve" "$bin_dir/kitsune-color-resolve"
|
|
366
|
+
BOOTSTRAP_KITSUNE_VERSION="$(latest_release_tag "KitotsuMolina/Kitsune" | sed 's/^v//')"
|
|
367
|
+
BOOTSTRAP_RENDERCORE_VERSION="$(latest_release_tag "KitotsuMolina/Kitsune-RenderCore" | sed 's/^v//')"
|
|
368
|
+
release_ok=1
|
|
369
|
+
fi
|
|
370
|
+
|
|
371
|
+
if [[ "$release_ok" -eq 1 ]]; then
|
|
372
|
+
install_kitsune_bundle || echo "[bootstrap] warning: failed to install kitsune script bundle; falling back to raw binary command" >&2
|
|
373
|
+
return
|
|
374
|
+
fi
|
|
375
|
+
|
|
376
|
+
echo "[bootstrap] warning: release binary install failed, falling back to cargo install" >&2
|
|
377
|
+
if ! need_cmd cargo; then
|
|
378
|
+
echo "[bootstrap] cargo is not available after dependency install" >&2
|
|
379
|
+
exit 1
|
|
380
|
+
fi
|
|
381
|
+
cargo_install_git_bin "$KITSUNE_REPO" kitsune "" "$KITSUNE_TAG"
|
|
382
|
+
cargo_install_git_bin "$KITSUNE_RENDERCORE_REPO" kitsune-rendercore "--features wayland-layer" "$KITSUNE_RENDERCORE_TAG"
|
|
383
|
+
BOOTSTRAP_KITSUNE_VERSION="${KITSUNE_TAG#v}"
|
|
384
|
+
BOOTSTRAP_RENDERCORE_VERSION="${KITSUNE_RENDERCORE_TAG#v}"
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
ensure_rendercore_service() {
|
|
388
|
+
if ! need_cmd kitsune-rendercore; then
|
|
389
|
+
echo "[bootstrap] warning: kitsune-rendercore binary not found; skipping service install" >&2
|
|
390
|
+
return
|
|
391
|
+
fi
|
|
392
|
+
|
|
393
|
+
local home_dir="${HOME:?HOME is required}"
|
|
394
|
+
local user_systemd_dir="$home_dir/.config/systemd/user"
|
|
395
|
+
local app_config_dir="$home_dir/.config/kitsune-rendercore"
|
|
396
|
+
local env_dst="$app_config_dir/env"
|
|
397
|
+
local map_dst="$app_config_dir/video-map.conf"
|
|
398
|
+
local bin_path
|
|
399
|
+
bin_path="$(command -v kitsune-rendercore || true)"
|
|
400
|
+
if [[ -z "$bin_path" ]]; then
|
|
401
|
+
echo "[bootstrap] warning: kitsune-rendercore binary path not found; skipping service install" >&2
|
|
402
|
+
return
|
|
403
|
+
fi
|
|
404
|
+
|
|
405
|
+
mkdir -p "$user_systemd_dir" "$app_config_dir"
|
|
406
|
+
if [[ ! -f "$env_dst" ]]; then
|
|
407
|
+
cat > "$env_dst" <<EOF
|
|
408
|
+
# Kitsune RenderCore user env
|
|
409
|
+
KRC_VIDEO_MAP_FILE=$map_dst
|
|
410
|
+
KRC_VIDEO_FPS=30
|
|
411
|
+
KRC_VIDEO_SPEED=1.0
|
|
412
|
+
KRC_QUALITY=high
|
|
413
|
+
KRC_PAUSE_ON_STEAM_GAME=true
|
|
414
|
+
KRC_STEAM_POLL_MS=1000
|
|
415
|
+
EOF
|
|
416
|
+
fi
|
|
417
|
+
if [[ ! -f "$map_dst" ]]; then
|
|
418
|
+
: > "$map_dst"
|
|
419
|
+
fi
|
|
420
|
+
|
|
421
|
+
cat > "$user_systemd_dir/kitsune-rendercore.service" <<EOF
|
|
422
|
+
[Unit]
|
|
423
|
+
Description=Kitsune RenderCore Live Wallpaper
|
|
424
|
+
After=graphical-session.target
|
|
425
|
+
Wants=graphical-session.target
|
|
426
|
+
|
|
427
|
+
[Service]
|
|
428
|
+
Type=simple
|
|
429
|
+
Environment=PATH=$home_dir/.local/bin:$home_dir/.cargo/bin:/usr/local/bin:/usr/bin:/bin
|
|
430
|
+
EnvironmentFile=-$env_dst
|
|
431
|
+
ExecStart=$bin_path
|
|
432
|
+
Restart=on-failure
|
|
433
|
+
RestartSec=1
|
|
434
|
+
|
|
435
|
+
[Install]
|
|
436
|
+
WantedBy=default.target
|
|
437
|
+
EOF
|
|
438
|
+
|
|
439
|
+
systemctl --user daemon-reload || true
|
|
440
|
+
systemctl --user enable kitsune-rendercore.service || true
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
verify_bins() {
|
|
444
|
+
local required_bins=()
|
|
445
|
+
if [[ "$KITOWALL_BOOTSTRAP_MODE" == "kitsune-only" || "$KITOWALL_BOOTSTRAP_MODE" == "kitsune-repair" ]]; then
|
|
446
|
+
required_bins=(kitsune kitsune-rendercore)
|
|
447
|
+
elif [[ "$KITOWALL_BOOTSTRAP_MODE" == "kitowall-only" ]]; then
|
|
448
|
+
required_bins=(kitowall)
|
|
449
|
+
else
|
|
450
|
+
required_bins=(kitowall kitsune kitsune-rendercore swww swww-daemon cava)
|
|
451
|
+
fi
|
|
452
|
+
local missing=()
|
|
453
|
+
for b in "${required_bins[@]}"; do
|
|
454
|
+
if ! need_cmd "$b"; then
|
|
455
|
+
missing+=("$b")
|
|
456
|
+
fi
|
|
457
|
+
done
|
|
458
|
+
|
|
459
|
+
if ((${#missing[@]} > 0)); then
|
|
460
|
+
echo "[bootstrap] missing binaries after bootstrap: ${missing[*]}" >&2
|
|
461
|
+
|
|
462
|
+
local needs_kitsune_recovery=0
|
|
463
|
+
for b in "${missing[@]}"; do
|
|
464
|
+
if [[ "$b" == "kitsune" || "$b" == "kitsune-rendercore" ]]; then
|
|
465
|
+
needs_kitsune_recovery=1
|
|
466
|
+
break
|
|
467
|
+
fi
|
|
468
|
+
done
|
|
469
|
+
|
|
470
|
+
if [[ "$needs_kitsune_recovery" -eq 1 ]]; then
|
|
471
|
+
echo "[bootstrap] attempting release-based recovery for Kitsune binaries" >&2
|
|
472
|
+
install_kitsune_bins
|
|
473
|
+
missing=()
|
|
474
|
+
for b in "${required_bins[@]}"; do
|
|
475
|
+
if ! need_cmd "$b"; then
|
|
476
|
+
missing+=("$b")
|
|
477
|
+
fi
|
|
478
|
+
done
|
|
479
|
+
fi
|
|
480
|
+
|
|
481
|
+
if ((${#missing[@]} > 0)); then
|
|
482
|
+
echo "[bootstrap] missing binaries after recovery: ${missing[*]}" >&2
|
|
483
|
+
exit 2
|
|
484
|
+
fi
|
|
485
|
+
fi
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
write_bootstrap_versions() {
|
|
489
|
+
local home_dir="${HOME:?HOME is required}"
|
|
490
|
+
local state_dir="$home_dir/.local/share/kitowall"
|
|
491
|
+
local state_file="$state_dir/bootstrap-versions.json"
|
|
492
|
+
|
|
493
|
+
mkdir -p "$state_dir"
|
|
494
|
+
cat > "$state_file" <<EOF
|
|
495
|
+
{
|
|
496
|
+
"kitsune": {
|
|
497
|
+
"version": "${BOOTSTRAP_KITSUNE_VERSION}",
|
|
498
|
+
"source": "${KITOWALL_BOOTSTRAP_MODE}"
|
|
499
|
+
},
|
|
500
|
+
"kitsune-rendercore": {
|
|
501
|
+
"version": "${BOOTSTRAP_RENDERCORE_VERSION}",
|
|
502
|
+
"source": "${KITOWALL_BOOTSTRAP_MODE}"
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
EOF
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
main() {
|
|
509
|
+
# Some launchers provide a stale/non-existent CWD; recover to HOME.
|
|
510
|
+
cd "${HOME:?HOME is required}" || true
|
|
511
|
+
# Clean host Python env that can break AUR build tools (meson/python).
|
|
512
|
+
unset PYTHONHOME || true
|
|
513
|
+
unset PYTHONPATH || true
|
|
514
|
+
|
|
515
|
+
ensure_user_bin_dirs
|
|
516
|
+
if [[ "$KITOWALL_BOOTSTRAP_MODE" == "kitsune-repair" ]]; then
|
|
517
|
+
repair_kitsune_host_layout
|
|
518
|
+
verify_bins
|
|
519
|
+
echo "[ok] kitsune host repair complete"
|
|
520
|
+
exit 0
|
|
521
|
+
fi
|
|
522
|
+
if [[ "$KITOWALL_BOOTSTRAP_MODE" == "kitowall-only" ]]; then
|
|
523
|
+
if [[ "$KITOWALL_SKIP_SYSTEM_DEPS" != "1" ]]; then
|
|
524
|
+
install_system_deps
|
|
525
|
+
fi
|
|
526
|
+
install_kitowall_cli
|
|
527
|
+
verify_bins
|
|
528
|
+
echo "[ok] kitowall bootstrap complete"
|
|
529
|
+
echo "[paths] HOME=$HOME"
|
|
530
|
+
echo "[paths] kitowall=$(command -v kitowall || echo '<missing>')"
|
|
531
|
+
echo "[info] bootstrap finished; manual init is only needed on a fresh host without config"
|
|
532
|
+
exit 0
|
|
533
|
+
fi
|
|
534
|
+
if [[ "$KITOWALL_BOOTSTRAP_MODE" != "kitsune-only" && "$KITOWALL_SKIP_SYSTEM_DEPS" != "1" ]]; then
|
|
535
|
+
install_system_deps
|
|
536
|
+
install_kitowall_cli
|
|
537
|
+
elif [[ "$KITOWALL_BOOTSTRAP_MODE" != "kitsune-only" ]]; then
|
|
538
|
+
install_kitowall_cli
|
|
539
|
+
fi
|
|
540
|
+
install_kitsune_bins
|
|
541
|
+
ensure_rendercore_service
|
|
542
|
+
write_bootstrap_versions
|
|
543
|
+
verify_bins
|
|
544
|
+
echo "[ok] host bootstrap complete"
|
|
545
|
+
echo "[paths] HOME=$HOME"
|
|
546
|
+
echo "[paths] kitowall=$(command -v kitowall || echo '<missing>')"
|
|
547
|
+
echo "[paths] kitsune=$(command -v kitsune || echo '<missing>')"
|
|
548
|
+
echo "[paths] kitsune-rendercore=$(command -v kitsune-rendercore || echo '<missing>')"
|
|
549
|
+
echo "[info] bootstrap finished; manual init is only needed on a fresh host without config"
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
main "$@"
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Root-only helper for installing host system packages.
|
|
5
|
+
# Intended to be invoked via pkexec from Kitowall.
|
|
6
|
+
|
|
7
|
+
need_cmd() {
|
|
8
|
+
command -v "$1" >/dev/null 2>&1
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
require_root() {
|
|
12
|
+
if [[ "${EUID:-$(id -u)}" -ne 0 ]]; then
|
|
13
|
+
echo "[bootstrap-system] this helper must run as root" >&2
|
|
14
|
+
exit 1
|
|
15
|
+
fi
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
wait_pacman_lock() {
|
|
19
|
+
local lock_file="/var/lib/pacman/db.lck"
|
|
20
|
+
local tries=0
|
|
21
|
+
local max_tries=40
|
|
22
|
+
|
|
23
|
+
while [[ -e "$lock_file" ]]; do
|
|
24
|
+
if pgrep -x pacman >/dev/null 2>&1 || \
|
|
25
|
+
pgrep -x yay >/dev/null 2>&1 || \
|
|
26
|
+
pgrep -x paru >/dev/null 2>&1 || \
|
|
27
|
+
pgrep -x makepkg >/dev/null 2>&1; then
|
|
28
|
+
tries=$((tries + 1))
|
|
29
|
+
if ((tries > max_tries)); then
|
|
30
|
+
echo "[bootstrap-system] pacman lock busy for too long: $lock_file" >&2
|
|
31
|
+
return 1
|
|
32
|
+
fi
|
|
33
|
+
echo "[bootstrap-system] waiting for pacman lock (${tries}/${max_tries})..."
|
|
34
|
+
sleep 3
|
|
35
|
+
continue
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
echo "[bootstrap-system] removing stale pacman lock: $lock_file"
|
|
39
|
+
rm -f "$lock_file"
|
|
40
|
+
break
|
|
41
|
+
done
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
install_arch_deps() {
|
|
45
|
+
local repo_pkgs=(
|
|
46
|
+
nodejs npm
|
|
47
|
+
hyprland
|
|
48
|
+
swww
|
|
49
|
+
cava
|
|
50
|
+
pkgconf
|
|
51
|
+
gtk4
|
|
52
|
+
gtk4-layer-shell
|
|
53
|
+
jq
|
|
54
|
+
git
|
|
55
|
+
base-devel
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
if need_cmd rustup; then
|
|
59
|
+
repo_pkgs+=(rustup)
|
|
60
|
+
elif need_cmd rustc && need_cmd cargo; then
|
|
61
|
+
:
|
|
62
|
+
else
|
|
63
|
+
repo_pkgs+=(rustup)
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
echo "[bootstrap-system] installing Arch packages: ${repo_pkgs[*]}"
|
|
67
|
+
wait_pacman_lock
|
|
68
|
+
pacman -S --needed --noconfirm "${repo_pkgs[@]}"
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
arch_packages_for_ids() {
|
|
72
|
+
local id
|
|
73
|
+
local pkgs=()
|
|
74
|
+
for id in "$@"; do
|
|
75
|
+
case "$id" in
|
|
76
|
+
nodejs) pkgs+=(nodejs) ;;
|
|
77
|
+
npm) pkgs+=(npm) ;;
|
|
78
|
+
hyprctl) pkgs+=(hyprland) ;;
|
|
79
|
+
swww|swww-daemon) pkgs+=(swww) ;;
|
|
80
|
+
cava) pkgs+=(cava) ;;
|
|
81
|
+
pkgconf) pkgs+=(pkgconf) ;;
|
|
82
|
+
gtk4) pkgs+=(gtk4) ;;
|
|
83
|
+
gtk4-layer-shell) pkgs+=(gtk4-layer-shell) ;;
|
|
84
|
+
jq) pkgs+=(jq) ;;
|
|
85
|
+
git) pkgs+=(git) ;;
|
|
86
|
+
base-devel) pkgs+=(base-devel) ;;
|
|
87
|
+
rustup|cargo|rustc) pkgs+=(rustup) ;;
|
|
88
|
+
esac
|
|
89
|
+
done
|
|
90
|
+
printf '%s\n' "${pkgs[@]}" | awk 'NF && !seen[$0]++'
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
install_arch_selected() {
|
|
94
|
+
local requested=("$@")
|
|
95
|
+
local repo_pkgs=()
|
|
96
|
+
mapfile -t repo_pkgs < <(arch_packages_for_ids "${requested[@]}")
|
|
97
|
+
if ((${#repo_pkgs[@]} == 0)); then
|
|
98
|
+
echo "[bootstrap-system] no supported Arch packages mapped from: ${requested[*]}" >&2
|
|
99
|
+
return 1
|
|
100
|
+
fi
|
|
101
|
+
echo "[bootstrap-system] installing selected Arch packages: ${repo_pkgs[*]}"
|
|
102
|
+
wait_pacman_lock
|
|
103
|
+
pacman -S --needed --noconfirm "${repo_pkgs[@]}"
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
install_ubuntu_deps() {
|
|
107
|
+
local pkgs=(
|
|
108
|
+
nodejs npm
|
|
109
|
+
rustc cargo
|
|
110
|
+
pkg-config
|
|
111
|
+
libgtk-4-dev
|
|
112
|
+
jq curl git
|
|
113
|
+
cava
|
|
114
|
+
)
|
|
115
|
+
echo "[bootstrap-system] installing Debian/Ubuntu packages: ${pkgs[*]}"
|
|
116
|
+
apt-get update
|
|
117
|
+
apt-get install -y "${pkgs[@]}"
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
ubuntu_packages_for_ids() {
|
|
121
|
+
local id
|
|
122
|
+
local pkgs=()
|
|
123
|
+
for id in "$@"; do
|
|
124
|
+
case "$id" in
|
|
125
|
+
nodejs) pkgs+=(nodejs) ;;
|
|
126
|
+
npm) pkgs+=(npm) ;;
|
|
127
|
+
cava) pkgs+=(cava) ;;
|
|
128
|
+
jq) pkgs+=(jq) ;;
|
|
129
|
+
git) pkgs+=(git) ;;
|
|
130
|
+
rustup|cargo|rustc) pkgs+=(rustc cargo) ;;
|
|
131
|
+
pkgconf) pkgs+=(pkg-config) ;;
|
|
132
|
+
gtk4) pkgs+=(libgtk-4-dev) ;;
|
|
133
|
+
esac
|
|
134
|
+
done
|
|
135
|
+
printf '%s\n' "${pkgs[@]}" | awk 'NF && !seen[$0]++'
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
install_ubuntu_selected() {
|
|
139
|
+
local requested=("$@")
|
|
140
|
+
local pkgs=()
|
|
141
|
+
mapfile -t pkgs < <(ubuntu_packages_for_ids "${requested[@]}")
|
|
142
|
+
if ((${#pkgs[@]} == 0)); then
|
|
143
|
+
echo "[bootstrap-system] no supported Debian/Ubuntu packages mapped from: ${requested[*]}" >&2
|
|
144
|
+
return 1
|
|
145
|
+
fi
|
|
146
|
+
echo "[bootstrap-system] installing selected Debian/Ubuntu packages: ${pkgs[*]}"
|
|
147
|
+
apt-get update
|
|
148
|
+
apt-get install -y "${pkgs[@]}"
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
main() {
|
|
152
|
+
require_root
|
|
153
|
+
|
|
154
|
+
if (($# > 0)); then
|
|
155
|
+
if need_cmd pacman; then
|
|
156
|
+
install_arch_selected "$@"
|
|
157
|
+
echo "[ok] selected system dependencies installed"
|
|
158
|
+
exit 0
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
if need_cmd apt-get; then
|
|
162
|
+
install_ubuntu_selected "$@"
|
|
163
|
+
echo "[ok] selected system dependencies installed"
|
|
164
|
+
exit 0
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
echo "[bootstrap-system] unsupported distro package manager for selected install: $*" >&2
|
|
168
|
+
exit 1
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
if need_cmd pacman; then
|
|
172
|
+
install_arch_deps
|
|
173
|
+
echo "[ok] system dependencies installed"
|
|
174
|
+
exit 0
|
|
175
|
+
fi
|
|
176
|
+
|
|
177
|
+
if need_cmd apt-get; then
|
|
178
|
+
install_ubuntu_deps
|
|
179
|
+
echo "[ok] system dependencies installed"
|
|
180
|
+
exit 0
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
echo "[bootstrap-system] unsupported distro package manager. Install manually: nodejs npm rust cargo pkg-config gtk4 gtk4-layer-shell swww hyprland cava" >&2
|
|
184
|
+
exit 1
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
main "$@"
|