mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 16:38:40 +01:00
Re-point paths for the ~ reorganisation (home-tidy)
Also bumps the 16 meta/requirements pins the freshness gate flagged (anthropic 1.5.0, numpy 2.5.3, pytest-randomly 5, flake8-bugbear 26.9.9, ruff 0.16.7, pyright 1.1.414, selenium 4.49.0, hypothesis 6.168.0, lxml 6.1.3, pipdeptree 4.2.5, the two types-* stubs) and Flutter 3.47.3 in meta/.fvmrc. One test still expected the pre-move ~/mcp-servers path. The pre-commit hook fed .zsh files to shfmt and shellcheck, neither of which parses zsh (SC2148/SC1071), so no zsh file could be committed since the hook was tightened on 2026-08-22; those now get `zsh -n` instead. The hook's own shfmt pass also staged its reformat of 8 shell files. CI_GREEN_SKIP because main is red on exactly the dependency-freshness finding this commit fixes, which is the documented use of that flag. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AwtowrowDXx6ccmWVPkCaF
This commit is contained in:
parent
0cea82cab5
commit
f15f00f3fe
2
.github/workflows/pre-commit.yml
vendored
2
.github/workflows/pre-commit.yml
vendored
@ -15,7 +15,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
# The md-naming / file-length hooks are thin shims that exec the shared
|
||||
# gate out of the utils repo. A runner has no ~/utils, so without this
|
||||
# gate out of the utils repo. A runner has no ~/src/utils, so without this
|
||||
# checkout they fail with "shared gate not found" -- which is exactly
|
||||
# how this workflow went red on 2026-08-24.
|
||||
UTILS_ROOT: ${{ github.workspace }}/.utils
|
||||
|
||||
@ -585,7 +585,7 @@ repos:
|
||||
|
||||
# Four markdown namespaces (README / CLAUDE* / DOCS* / TODO*), plus the
|
||||
# rule that a TODO carries "REMOVE ME AFTER FINISH" and nothing else may.
|
||||
# Shared gate in ~/utils owns the namespaces and the exemption list, so
|
||||
# Shared gate in ~/src/utils owns the namespaces and the exemption list, so
|
||||
# one repo's idea of a valid name cannot drift from every other repo's.
|
||||
- id: md-naming
|
||||
name: markdown naming convention
|
||||
|
||||
@ -32,19 +32,19 @@ Extracted to their own repos: [`steam-backlog-enforcer`](https://github.com/kuhy
|
||||
[`dufs-cloud`](https://github.com/kuhyx/dufs-cloud),
|
||||
[`build-your-x`](https://github.com/kuhyx/build-your-x) (lives at `~/build_your_x`),
|
||||
[`reverse-survivors`](https://github.com/kuhyx/reverse-survivors),
|
||||
[`kcd2-dice-solver`](https://github.com/kuhyx/kcd2-dice-solver) (lives at `~/kcd2-dice-solver`),
|
||||
[`kcd2-dice-solver`](https://github.com/kuhyx/kcd2-dice-solver) (lives at `~/src/kcd2-dice-solver`),
|
||||
[`focus-owner`](https://github.com/kuhyx/focus-owner),
|
||||
[`mtk-root`](https://github.com/kuhyx/mtk-root),
|
||||
[`system-maintenance`](https://github.com/kuhyx/system-maintenance) (lives at
|
||||
`~/system-maintenance`),
|
||||
`~/src/system-maintenance`),
|
||||
[`android-guardian`](https://github.com/kuhyx/android-guardian) (lives at
|
||||
`~/android-guardian`),
|
||||
[`phone-focus-mode`](https://github.com/kuhyx/phone-focus-mode) (with
|
||||
`python_pkg/focus_policy`),
|
||||
[`hosts-blocker`](https://github.com/kuhyx/hosts-blocker) (lives at
|
||||
`~/hosts-blocker`),
|
||||
`~/src/hosts-blocker`),
|
||||
[`digital-wellbeing`](https://github.com/kuhyx/digital-wellbeing) (lives at
|
||||
`~/digital-wellbeing`; carries the six suites that used to sit in
|
||||
`~/src/digital-wellbeing`; carries the six suites that used to sit in
|
||||
`linux_configuration/tests/`). Monorepo callers resolve both via
|
||||
`linux_configuration/lib/extracted_repos.sh`, which reads the invoking user's
|
||||
home from `SUDO_USER` — never `$HOME`, which is `/root` under sudo and under
|
||||
|
||||
@ -4,10 +4,10 @@ Paste everything below into a **fresh** Claude session (`/clear` first).
|
||||
|
||||
---
|
||||
|
||||
`~/testsAndMisc/linux_configuration/install_core_system.sh` is the documented
|
||||
`~/src/testsAndMisc/linux_configuration/install_core_system.sh` is the documented
|
||||
way to set this machine up from scratch. It does not work. Three defects were
|
||||
found on 2026-08-22 by running the real installer in a disposable Arch VM
|
||||
(`~/utils/vmbox`) rather than by reading it — 2 of its 7 modules cannot install
|
||||
(`~/src/utils/vmbox`) rather than by reading it — 2 of its 7 modules cannot install
|
||||
at all, and a third installs something that silently does nothing.
|
||||
|
||||
**Do not trust this file's line numbers without re-checking them.** They were
|
||||
@ -37,14 +37,14 @@ machine already exists in the repos, but the documented install path never runs
|
||||
|
||||
Both packages were extracted into their own repos. Verified on disk 2026-08-22:
|
||||
|
||||
- `~/screen-locker/install_systemd.sh` (5637 b, executable)
|
||||
- `~/steam-backlog-enforcer/install.sh` (3248 b, executable)
|
||||
- `~/src/screen-locker/install_systemd.sh` (5637 b, executable)
|
||||
- `~/src/steam-backlog-enforcer/install.sh` (3248 b, executable)
|
||||
|
||||
"Workout screen locker" is a **CORE** module, so a fresh machine fails on the
|
||||
first thing the installer does.
|
||||
|
||||
Note this makes the installer **cross-repo**: `~/testsAndMisc` no longer owns
|
||||
that code. That is design question (b) below — do not just hardcode `~/screen-locker`
|
||||
Note this makes the installer **cross-repo**: `~/src/testsAndMisc` no longer owns
|
||||
that code. That is design question (b) below — do not just hardcode `~/src/screen-locker`
|
||||
and call it done without putting the question to kuhy.
|
||||
|
||||
## Defect 2 — guard-lib is required but never installed
|
||||
@ -52,14 +52,14 @@ and call it done without putting the question to kuhy.
|
||||
`scripts/periodic_background/digital_wellbeing/setup_midnight_shutdown.sh`
|
||||
depends on `guardctl` and dies with `guardctl not found on PATH`. Nothing in
|
||||
`install_core_system.sh` installs guard-lib, which lives at
|
||||
`~/utils/guard-lib/install.sh` — a **third** sibling repo.
|
||||
`~/src/utils/guard-lib/install.sh` — a **third** sibling repo.
|
||||
|
||||
So the CORE module "Midnight shutdown timer" always fails on a fresh machine.
|
||||
|
||||
## Defect 3 — the hosts file-guards are installed only by a one-shot script
|
||||
|
||||
`install.sh` in the extracted `hosts-blocker` repo
|
||||
(github.com/kuhyx/hosts-blocker, checked out at `~/hosts-blocker`; it was
|
||||
(github.com/kuhyx/hosts-blocker, checked out at `~/src/hosts-blocker`; it was
|
||||
`scripts/periodic_background/hosts/install.sh` when this was written)
|
||||
never installs the
|
||||
hosts/nsswitch/resolved file-guards. Its own comment block around **line 109**
|
||||
@ -90,10 +90,10 @@ diff; the second stops `single_use/fixes/` from being load-bearing, which is
|
||||
arguably what "single use" is supposed to mean.
|
||||
|
||||
**(b) How should a testsAndMisc installer reach code in sibling repos?**
|
||||
`~/screen-locker`, `~/steam-backlog-enforcer` and `~/utils/guard-lib` are
|
||||
`~/src/screen-locker`, `~/src/steam-backlog-enforcer` and `~/src/utils/guard-lib` are
|
||||
separate repos now. Options, none obviously right:
|
||||
|
||||
1. **Expect-adjacent** — check for `~/screen-locker` etc., fail with a clear
|
||||
1. **Expect-adjacent** — check for `~/src/screen-locker` etc., fail with a clear
|
||||
message naming the repo to clone. Simple; the installer stops being
|
||||
self-contained.
|
||||
2. **Clone if missing** — the installer clones the siblings it needs. Works on
|
||||
@ -112,7 +112,7 @@ an actionable message rather than silently skipping a module.
|
||||
The fix is done when, in a **fresh** vmbox guest:
|
||||
|
||||
```bash
|
||||
vm share ~/testsAndMisc && vm share ~/utils
|
||||
vm share ~/src/testsAndMisc && vm share ~/src/utils
|
||||
vm new inst
|
||||
vm run inst 'git clone --no-hardlinks -q /mnt/hostrepo/testsAndMisc ~/tam'
|
||||
# plus whatever cloning question (b)'s answer implies for the sibling repos
|
||||
@ -134,16 +134,16 @@ vm run inst 'cd ~/tam && echo y | bash linux_configuration/install_core_system.s
|
||||
|
||||
- **Sandbox first, always.** This is a root installer that writes `/etc`,
|
||||
systemd units, pacman hooks and `chattr +i`. Never run it on the host to
|
||||
test it. `~/utils/vmbox`: `vm new` / `vm run` / `vm reset`. A sandbox pass
|
||||
test it. `~/src/utils/vmbox`: `vm new` / `vm run` / `vm reset`. A sandbox pass
|
||||
does **not** prove the host is fine — report it that way.
|
||||
- Installers prompt; `vm run` closes stdin, so pipe `echo y |`.
|
||||
- vmbox is fast: `vm run` ~14s cold / ~3s warm, `vm reset` ~2s. Reuse one
|
||||
sandbox, do not rebuild per command.
|
||||
- `~/utils/vmbox/SESSION_RESULTS.md` has the full 2026-08-22 measurements.
|
||||
- The 250-line file cap applies (`~/utils/file_length`). `install.sh` is at
|
||||
- `~/src/utils/vmbox/SESSION_RESULTS.md` has the full 2026-08-22 measurements.
|
||||
- The 250-line file cap applies (`~/src/utils/file_length`). `install.sh` is at
|
||||
130 lines and the migration at 169 — merging them wholesale will breach it,
|
||||
which is worth weighing in design question (a).
|
||||
- `~/testsAndMisc` requires a `docs/superpowers/evidence/*.json` artifact for
|
||||
- `~/src/testsAndMisc` requires a `docs/superpowers/evidence/*.json` artifact for
|
||||
any code change (pre-commit gate `ai-evidence-contract`); copy
|
||||
`docs/superpowers/evidence/template.json`.
|
||||
|
||||
|
||||
@ -29,10 +29,10 @@ Verify any of it:
|
||||
## What landed this session
|
||||
|
||||
**`hosts-blocker`** — [github.com/kuhyx/hosts-blocker](https://github.com/kuhyx/hosts-blocker),
|
||||
cloned at `~/hosts-blocker`. 52 commits.
|
||||
cloned at `~/src/hosts-blocker`. 52 commits.
|
||||
|
||||
**`digital-wellbeing`** — [github.com/kuhyx/digital-wellbeing](https://github.com/kuhyx/digital-wellbeing),
|
||||
cloned at `~/digital-wellbeing`. 89 commits, and it carries the six suites that
|
||||
cloned at `~/src/digital-wellbeing`. 89 commits, and it carries the six suites that
|
||||
used to sit in `linux_configuration/tests/`.
|
||||
|
||||
**The cap enforces.** `--warn` is gone from the `directory-depth-cap` hook, and
|
||||
@ -76,7 +76,7 @@ copies were the only ones on the machine.
|
||||
- **`syncyomi-guard` is red** since 00:00 on 2026-08-23, working as designed:
|
||||
the library collapsed from 2185 manga to 8. See the
|
||||
`syncyomi-restore-silent-partial` memory; do NOT checkpoint the WAL before
|
||||
recovering. Newest snapshot: `~/syncyomi/snapshots/`, 2026-08-15.
|
||||
recovering. Newest snapshot: `~/services/syncyomi/snapshots/`, 2026-08-15.
|
||||
|
||||
## Traps worth keeping
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ not measured from a rendered page.
|
||||
|
||||
---
|
||||
|
||||
## kcd2_dice_solver (`~/testsAndMisc/kcd2_dice_solver`, Vite/TS)
|
||||
## kcd2_dice_solver (`~/src/testsAndMisc/kcd2_dice_solver`, Vite/TS)
|
||||
|
||||
Design tokens live in `src/index.css` (422 lines), a single `:root` block of
|
||||
CSS custom properties consumed by all 5 component files. This is the correct
|
||||
@ -60,7 +60,7 @@ system (see Rule 4).
|
||||
|
||||
---
|
||||
|
||||
## bucket_catch frontend (`~/testsAndMisc/bucket_catch/packages/frontend`, Vite/TS)
|
||||
## bucket_catch frontend (`~/src/testsAndMisc/bucket_catch/packages/frontend`, Vite/TS)
|
||||
|
||||
**Confirmed: no shared design tokens exist.** `src/index.css` is a 12-line
|
||||
reset only (box-sizing + html/body/#root margin/font/background/color) — no
|
||||
|
||||
@ -27,7 +27,7 @@ Backed by repeated, concrete evidence across multiple sessions/repos.
|
||||
### MCP Inspector (mcp, typescript, 10.6k★)
|
||||
|
||||
You author and maintain **7+ MCP servers** (`i3wm-mcp`, `yay-mcp`, `aseprite-mcp`, `reaper-mcp`, `opengameart-mcp`,
|
||||
`freesound-mcp`, plus 23 more under `~/mcp-servers/servers/`) per `project-mcp-server-fleet.md` and the deployment
|
||||
`freesound-mcp`, plus 23 more under `~/src/mcp-servers/servers/`) per `project-mcp-server-fleet.md` and the deployment
|
||||
skill `project-mcp-per-repo-pattern.md`. Nothing in your memory or tooling survey shows a dedicated tool/resource/prompt
|
||||
debugger for the servers you _build_ — you've been testing them indirectly through Claude Code sessions themselves.
|
||||
MCP Inspector is a GUI built exactly for this gap: exercising tools/resources/prompts in isolation before wiring a new
|
||||
@ -44,7 +44,7 @@ which your own hook (`track_reads_pretool.sh`, since disabled) was already tryin
|
||||
|
||||
### Docker MCP Gateway (mcp, sandbox, cli, 1.5k★)
|
||||
|
||||
You run 23+ Dockerfiles for self-hosted MCP server containers (`~/mcp-servers/servers/*`) per the repo survey, on top
|
||||
You run 23+ Dockerfiles for self-hosted MCP server containers (`~/src/mcp-servers/servers/*`) per the repo survey, on top
|
||||
of an already-Docker-heavy self-hosted stack (Gitea, SearXNG, Syncyomi, Joplin Server, all `docker-compose.yml`-based).
|
||||
A container-aware MCP gateway is a direct fit for consolidating discovery/auth across a fleet this size, rather than
|
||||
each server being wired in ad hoc. Speculative on the _specific_ tool, high-confidence on the _need_ — you already
|
||||
@ -153,7 +153,7 @@ demonstrated preference for deterministic scripts you can read, diff, and put un
|
||||
### browser-use, Stagehand, Steel (standalone browser-agent harnesses)
|
||||
|
||||
Rejected as _redundant_, not irrelevant — you already have `claude-in-chrome` MCP tools wired in and actively used this
|
||||
session, plus `servo-fetch` (Servo-engine fetch/crawl/screenshot MCP) under `~/mcp-servers/servers/`. Both solve the
|
||||
session, plus `servo-fetch` (Servo-engine fetch/crawl/screenshot MCP) under `~/src/mcp-servers/servers/`. Both solve the
|
||||
"drive a real browser from an agent" problem you'd otherwise reach for these tools to solve. No session shows friction
|
||||
with the browser tooling you already have that a swap would fix.
|
||||
|
||||
|
||||
@ -75,7 +75,7 @@ touching anything.
|
||||
one.** This is the highest-value artifact on the phone.
|
||||
```bash
|
||||
adb -s 23181JEGR08034 shell 'find /sdcard -iname "*.kdbx" 2>/dev/null'
|
||||
adb -s 23181JEGR08034 pull <path> ~/phone-backup/
|
||||
adb -s 23181JEGR08034 pull <path> ~/data/phone-backup/
|
||||
```
|
||||
3. **Write down on paper**: mBank customer ID + password, Revolut passcode,
|
||||
Signal PIN, KeePassDX master password, both Google passwords.
|
||||
@ -87,13 +87,13 @@ touching anything.
|
||||
5. **Signal**: Settings → Backups → On-device backups. **Record the 30-digit
|
||||
passphrase on paper** — it is not your Signal PIN.
|
||||
```bash
|
||||
adb -s 23181JEGR08034 pull /sdcard/Signal/Backups/ ~/phone-backup/signal/
|
||||
adb -s 23181JEGR08034 pull /sdcard/Signal/Backups/ ~/data/phone-backup/signal/
|
||||
```
|
||||
Registration Lock: forgetting the PIN locks you out for up to 7 days.
|
||||
6. **Photos and downloads**:
|
||||
```bash
|
||||
adb -s 23181JEGR08034 pull /sdcard/DCIM/ ~/phone-backup/DCIM/
|
||||
adb -s 23181JEGR08034 pull /sdcard/Download/ ~/phone-backup/Download/
|
||||
adb -s 23181JEGR08034 pull /sdcard/DCIM/ ~/data/phone-backup/DCIM/
|
||||
adb -s 23181JEGR08034 pull /sdcard/Download/ ~/data/phone-backup/Download/
|
||||
```
|
||||
7. **mBank**: in the transactional service, note how to remove a paired device.
|
||||
The old phone stays listed as active after a reset and must be detached.
|
||||
|
||||
@ -36,7 +36,7 @@ To remove it entirely:
|
||||
```bash
|
||||
sudo systemctl disable --now stunnel-dot.service dot-cert-sync.timer
|
||||
sudo rm -f /etc/stunnel/dot-resolver.conf /etc/systemd/system/stunnel-dot.service
|
||||
rm -f ~/gitea/sites/dns.caddy && docker exec gitea-caddy caddy reload \
|
||||
rm -f ~/services/gitea/sites/dns.caddy && docker exec gitea-caddy caddy reload \
|
||||
--config /etc/caddy/Caddyfile --adapter caddyfile
|
||||
```
|
||||
|
||||
|
||||
@ -17,56 +17,56 @@ its timer next fires.
|
||||
```
|
||||
## systemd units
|
||||
/etc/systemd/system/dns-blocklist-refresh.service
|
||||
7:ExecStart=/home/kuhy/testsAndMisc/linux_configuration/features/setup_dns_blocker.sh refresh
|
||||
7:ExecStart=/home/kuhy/src/testsAndMisc/linux_configuration/features/setup_dns_blocker.sh refresh
|
||||
/etc/systemd/system/media-organizer.service
|
||||
10:ExecStart=/home/kuhy/testsAndMisc/linux_configuration/utils/organize_downloads.sh
|
||||
10:ExecStart=/home/kuhy/src/testsAndMisc/linux_configuration/utils/organize_downloads.sh
|
||||
/home/kuhy/.config/systemd/user/endurain-import.service
|
||||
3:Documentation=file:///home/kuhy/testsAndMisc/endurain/README.md
|
||||
11:WorkingDirectory=/home/kuhy/testsAndMisc
|
||||
21:ExecStart=/usr/bin/bash -c 'ENDURAIN_API_KEY="$(cat /home/kuhy/testsAndMisc/endurain/.api_key)" /home/kuhy/testsAndMisc/.venv/bin/python -m python_pkg.endurain_import'
|
||||
3:Documentation=file:///home/kuhy/src/testsAndMisc/endurain/README.md
|
||||
11:WorkingDirectory=/home/kuhy/src/testsAndMisc
|
||||
21:ExecStart=/usr/bin/bash -c 'ENDURAIN_API_KEY="$(cat /home/kuhy/src/testsAndMisc/endurain/.api_key)" /home/kuhy/src/testsAndMisc/.venv/bin/python -m python_pkg.endurain_import'
|
||||
/home/kuhy/.config/systemd/user/morning-routine.service
|
||||
13:Environment=PYTHONPATH=%h/testsAndMisc
|
||||
16:WorkingDirectory=%h/testsAndMisc
|
||||
13:Environment=PYTHONPATH=%h/src/testsAndMisc
|
||||
16:WorkingDirectory=%h/src/testsAndMisc
|
||||
/home/kuhy/.config/systemd/user/syncyomi-guard.service
|
||||
8:WorkingDirectory=/home/kuhy/testsAndMisc
|
||||
9:Environment=PYTHONPATH=/home/kuhy/testsAndMisc
|
||||
8:WorkingDirectory=/home/kuhy/src/testsAndMisc
|
||||
9:Environment=PYTHONPATH=/home/kuhy/src/testsAndMisc
|
||||
|
||||
## /etc references
|
||||
/etc/guard-lib/targets/nsswitch.conf
|
||||
4:PLUGIN="/home/kuhy/testsAndMisc/linux_configuration/periodic_background/hosts/guard/plugins/nsswitch-plugin.sh"
|
||||
4:PLUGIN="/home/kuhy/src/testsAndMisc/linux_configuration/periodic_background/hosts/guard/plugins/nsswitch-plugin.sh"
|
||||
/etc/guard-lib/targets/resolved.conf
|
||||
4:PLUGIN="/home/kuhy/testsAndMisc/linux_configuration/periodic_background/hosts/guard/plugins/resolved-plugin.sh"
|
||||
4:PLUGIN="/home/kuhy/src/testsAndMisc/linux_configuration/periodic_background/hosts/guard/plugins/resolved-plugin.sh"
|
||||
|
||||
## installed scripts referencing the repo
|
||||
/usr/local/bin/hosts-file-monitor.sh
|
||||
10:HOSTS_INSTALL_SCRIPT="/home/kuhy/testsAndMisc/linux_configuration/periodic_background/hosts/install.sh"
|
||||
10:HOSTS_INSTALL_SCRIPT="/home/kuhy/src/testsAndMisc/linux_configuration/periodic_background/hosts/install.sh"
|
||||
/usr/local/bin/periodic-system-maintenance.sh
|
||||
26:PACMAN_WRAPPER_INSTALL="/home/kuhy/testsAndMisc/linux_configuration/periodic_background/digital_wellbeing/pacman/install_pacman_wrapper.sh"
|
||||
27:MAKEPKG_WRAPPER_INSTALL="/home/kuhy/testsAndMisc/linux_configuration/periodic_background/digital_wellbeing/pacman/install_makepkg_wrapper.sh"
|
||||
28:HOSTS_INSTALL_SCRIPT="/home/kuhy/testsAndMisc/linux_configuration/periodic_background/hosts/install.sh"
|
||||
26:PACMAN_WRAPPER_INSTALL="/home/kuhy/src/testsAndMisc/linux_configuration/periodic_background/digital_wellbeing/pacman/install_pacman_wrapper.sh"
|
||||
27:MAKEPKG_WRAPPER_INSTALL="/home/kuhy/src/testsAndMisc/linux_configuration/periodic_background/digital_wellbeing/pacman/install_makepkg_wrapper.sh"
|
||||
28:HOSTS_INSTALL_SCRIPT="/home/kuhy/src/testsAndMisc/linux_configuration/periodic_background/hosts/install.sh"
|
||||
/usr/local/bin/wsg-grabber
|
||||
4:export PYTHONPATH="/home/kuhy/testsAndMisc${PYTHONPATH:+:$PYTHONPATH}"
|
||||
|
||||
## symlinks into the repo
|
||||
/home/kuhy/.config/i3blocks/claude_usage.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/claude_usage.sh
|
||||
/home/kuhy/.config/i3blocks/config -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/config
|
||||
/home/kuhy/.config/i3blocks/cpu_monitor.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/cpu_monitor.sh
|
||||
/home/kuhy/.config/i3blocks/disk.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/disk.sh
|
||||
/home/kuhy/.config/i3blocks/ethernet.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/ethernet.sh
|
||||
/home/kuhy/.config/i3blocks/gpu_monitor.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/gpu_monitor.sh
|
||||
/home/kuhy/.config/i3blocks/memory.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/memory.sh
|
||||
/home/kuhy/.config/i3blocks/motherboard_temp.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/motherboard_temp.sh
|
||||
/home/kuhy/.config/i3blocks/pc_startup_status.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/pc_startup_status.sh
|
||||
/home/kuhy/.config/i3blocks/persist_common.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/persist_common.sh
|
||||
/home/kuhy/.config/i3blocks/shutdown_countdown.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/shutdown_countdown.sh
|
||||
/home/kuhy/.config/i3blocks/time.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/time.sh
|
||||
/home/kuhy/.config/i3blocks/volume.sh -> /home/kuhy/testsAndMisc/linux_configuration/i3blocks/volume.sh
|
||||
/home/kuhy/.local/bin/keepass-open -> /home/kuhy/testsAndMisc/linux_configuration/features/keepass-open.sh
|
||||
/home/kuhy/.local/bin/line-speed-probe.sh -> /home/kuhy/testsAndMisc/linux_configuration/diagnostics/line-speed-probe.sh
|
||||
/home/kuhy/.local/bin/steam-download-duty.sh -> /home/kuhy/testsAndMisc/linux_configuration/diagnostics/steam-download-duty.sh
|
||||
/home/kuhy/.oh-my-zsh/custom/calc-live.zsh -> /home/kuhy/testsAndMisc/linux_configuration/zsh/calc-live.zsh
|
||||
/home/kuhy/.oh-my-zsh/custom/claude-tty-fix.zsh -> /home/kuhy/testsAndMisc/linux_configuration/zsh/claude-tty-fix.zsh
|
||||
/home/kuhy/.oh-my-zsh/custom/token-savers.zsh -> /home/kuhy/testsAndMisc/linux_configuration/zsh/token-savers.zsh
|
||||
/home/kuhy/.config/i3blocks/claude_usage.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/claude_usage.sh
|
||||
/home/kuhy/.config/i3blocks/config -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/config
|
||||
/home/kuhy/.config/i3blocks/cpu_monitor.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/cpu_monitor.sh
|
||||
/home/kuhy/.config/i3blocks/disk.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/disk.sh
|
||||
/home/kuhy/.config/i3blocks/ethernet.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/ethernet.sh
|
||||
/home/kuhy/.config/i3blocks/gpu_monitor.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/gpu_monitor.sh
|
||||
/home/kuhy/.config/i3blocks/memory.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/memory.sh
|
||||
/home/kuhy/.config/i3blocks/motherboard_temp.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/motherboard_temp.sh
|
||||
/home/kuhy/.config/i3blocks/pc_startup_status.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/pc_startup_status.sh
|
||||
/home/kuhy/.config/i3blocks/persist_common.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/persist_common.sh
|
||||
/home/kuhy/.config/i3blocks/shutdown_countdown.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/shutdown_countdown.sh
|
||||
/home/kuhy/.config/i3blocks/time.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/time.sh
|
||||
/home/kuhy/.config/i3blocks/volume.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/i3blocks/volume.sh
|
||||
/home/kuhy/.local/bin/keepass-open -> /home/kuhy/src/testsAndMisc/linux_configuration/features/keepass-open.sh
|
||||
/home/kuhy/.local/bin/line-speed-probe.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/diagnostics/line-speed-probe.sh
|
||||
/home/kuhy/.local/bin/steam-download-duty.sh -> /home/kuhy/src/testsAndMisc/linux_configuration/diagnostics/steam-download-duty.sh
|
||||
/home/kuhy/.oh-my-zsh/custom/calc-live.zsh -> /home/kuhy/src/testsAndMisc/linux_configuration/zsh/calc-live.zsh
|
||||
/home/kuhy/.oh-my-zsh/custom/claude-tty-fix.zsh -> /home/kuhy/src/testsAndMisc/linux_configuration/zsh/claude-tty-fix.zsh
|
||||
/home/kuhy/.oh-my-zsh/custom/token-savers.zsh -> /home/kuhy/src/testsAndMisc/linux_configuration/zsh/token-savers.zsh
|
||||
```
|
||||
|
||||
## Depth-cap progress
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
# Working safely in a shared `~/testsAndMisc` checkout
|
||||
# Working safely in a shared `~/src/testsAndMisc` checkout
|
||||
|
||||
Split out of `refactor_claude_todo_resume.md` (which hit the 250-line cap it
|
||||
exists to enforce). Nothing here is specific to the file-length refactor -- it
|
||||
|
||||
@ -25,7 +25,7 @@ Upstream, open and unfixed: [#1634] and [#1638].
|
||||
## Fixed on this phone (2026-08-28)
|
||||
|
||||
The phone no longer runs the upstream APK. It runs
|
||||
[kuhyx/TachiyomiSY](https://github.com/kuhyx/TachiyomiSY) (`~/tachiyomisy`,
|
||||
[kuhyx/TachiyomiSY](https://github.com/kuhyx/TachiyomiSY) (`~/vendor/tachiyomisy`,
|
||||
commit "Serialize backup restore and refuse to sync a collapsed library"),
|
||||
which carries three changes upstream does not have:
|
||||
|
||||
@ -52,8 +52,8 @@ the official APK rather than sitting beside it, and is signed with
|
||||
|
||||
```bash
|
||||
JAVA_HOME=/usr/lib/jvm/java-21-openjdk \
|
||||
~/tachiyomisy/gradlew -p ~/tachiyomisy assembleFoss -PsyReplaceUpstream
|
||||
adb install -r ~/tachiyomisy/app/build/outputs/apk/foss/app-arm64-v8a-foss.apk
|
||||
~/vendor/tachiyomisy/gradlew -p ~/vendor/tachiyomisy assembleFoss -PsyReplaceUpstream
|
||||
adb install -r ~/vendor/tachiyomisy/app/build/outputs/apk/foss/app-arm64-v8a-foss.apk
|
||||
```
|
||||
|
||||
Never install an official TachiyomiSY APK over it: the signatures differ, so
|
||||
@ -137,13 +137,13 @@ systemctl --user list-timers syncyomi-guard.timer
|
||||
journalctl --user -u syncyomi-guard.service -n 20
|
||||
```
|
||||
|
||||
Snapshots live in `~/syncyomi/snapshots/` and are ordinary `.tachibk` files —
|
||||
Snapshots live in `~/services/syncyomi/snapshots/` and are ordinary `.tachibk` files —
|
||||
`adb push` one to the phone and restore it directly.
|
||||
|
||||
**Retention: 14 snapshots at 30-minute intervals is about 7 hours of history.**
|
||||
That covers a fast incident — the 2026-08-09 window was 51 seconds — but not a
|
||||
slow drift noticed days later. For that, the reference copy is
|
||||
`~/syncyomi/recovery/`, which is never pruned.
|
||||
`~/services/syncyomi/recovery/`, which is never pruned.
|
||||
|
||||
After a **deliberate** library purge the guard will report a collapse; accept
|
||||
the new state with `--accept` to re-baseline.
|
||||
@ -167,7 +167,7 @@ and 1 won. The phone was not losing a merge — it was honestly reporting what i
|
||||
had. The restore writes manga and chapters but silently drops the category
|
||||
tables, which is the narrow form of the same 1.13.2 bug.
|
||||
|
||||
The categories still exist in `~/syncyomi/recovery/syncyomi_recovered_2026-08-09.tachibk`
|
||||
The categories still exist in `~/services/syncyomi/recovery/syncyomi_recovered_2026-08-09.tachibk`
|
||||
(18 definitions, 1724 memberships). Getting them into the app needs the
|
||||
downgrade path — 1.12.0 → restore → verify → upgrade — which was not attempted.
|
||||
If a future SY release fixes #1634, retry the restore before the downgrade.
|
||||
@ -181,7 +181,7 @@ payload may still exist as stale pages in `syncyomi.db-wal`.
|
||||
the WAL and the only remaining copy with it. Snapshot all three files first:
|
||||
|
||||
```bash
|
||||
sudo cp -a ~/syncyomi/config/syncyomi.db{,-shm,-wal} /some/safe/dir/
|
||||
sudo cp -a ~/services/syncyomi/config/syncyomi.db{,-shm,-wal} /some/safe/dir/
|
||||
```
|
||||
|
||||
Then rebuild by applying every WAL frame over the base database, last writer
|
||||
@ -189,7 +189,7 @@ wins, and extract `sync_data.data`. The 2026-08-09 reconstruction and its
|
||||
verification are recorded in
|
||||
`docs/superpowers/evidence/syncyomi-library-recovery-2026-08-09.json`.
|
||||
|
||||
**`~/syncyomi/recovery/recovered.db` is a forensic artifact, not a database.**
|
||||
**`~/services/syncyomi/recovery/recovered.db` is a forensic artifact, not a database.**
|
||||
It was assembled by splicing pages from several checkpoint generations, so it is
|
||||
not any single committed SQLite state — `quick_check` passes and the payload
|
||||
blob decodes, but do **not** copy it over `config/syncyomi.db`. The deliverable
|
||||
|
||||
@ -139,7 +139,7 @@
|
||||
<h3>The two things this change adds a home for</h3>
|
||||
<ul>
|
||||
<li><b>A file store</b> reachable from a phone on mobile data — the “Google Drive alternative”.</li>
|
||||
<li><b>The KeePassXC vault</b>: a single encrypted file, <code class="inl">~/Keepass/Passwords.kdbx</code>. KeePass has no built-in cloud sync by design — you choose where the file lives.</li>
|
||||
<li><b>The KeePassXC vault</b>: a single encrypted file, <code class="inl">~/data/Keepass/Passwords.kdbx</code>. KeePass has no built-in cloud sync by design — you choose where the file lives.</li>
|
||||
</ul>
|
||||
|
||||
<div class="note"><b>dufs</b> is the chosen tool: a single ~2.5 MB Rust binary that serves a <i>real filesystem folder</i> over HTTP with a web UI <b>and</b> built-in WebDAV. Because it serves the folder directly, an <code class="inl">rsync</code> (or any local write) into that folder shows up instantly — no database, no re-scan step. Contrast Seafile, which stores files in an internal block database that <code class="inl">rsync</code> cannot write into.</p></div>
|
||||
@ -171,7 +171,7 @@
|
||||
<div class="arrow">→</div>
|
||||
<div class="node"><b>rsync</b><span>only the .kdbx</span></div>
|
||||
<div class="arrow">→</div>
|
||||
<div class="node"><b>~/cloud/Keepass</b><span>phone pulls via WebDAV</span></div>
|
||||
<div class="node"><b>~/data/cloud/Keepass</b><span>phone pulls via WebDAV</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<p>On the phone, <b>KeePassDX</b> opens the database straight from the WebDAV URL — so the “mobile app” requirement is met with zero custom code.</p>
|
||||
@ -252,14 +252,14 @@ bad="$(printf '%s\n' "$staged" | grep -E '(^|/)\.dufs_cloud\.conf$' || true)"
|
||||
<h2 id="playground">4 · Playground — the serve-dir guard</h2>
|
||||
<p>Type a candidate <code class="inl">CLOUD_ROOT</code> and see the exact verdict <code class="inl">assert_cloud_root_safe</code> would give. (Assumes <code class="inl">$HOME=/home/kuhy</code>, repo <code class="inl">/home/kuhy/testsAndMisc</code>. Paths are resolved like <code class="inl">readlink -m</code>, so <code class="inl">..</code> collapses.)</p>
|
||||
<div class="mw">
|
||||
<input id="mwIn" value="/home/kuhy/cloud" spellcheck="false" oninput="mwEval()">
|
||||
<input id="mwIn" value="/home/kuhy/data/cloud" spellcheck="false" oninput="mwEval()">
|
||||
<div id="mwOut" class="verdict"></div>
|
||||
<div class="chips">
|
||||
<span class="chip" onclick="mwSet('/home/kuhy/cloud')">~/cloud</span>
|
||||
<span class="chip" onclick="mwSet('/home/kuhy/data/cloud')">~/cloud</span>
|
||||
<span class="chip" onclick="mwSet('/home/kuhy')">~ (home)</span>
|
||||
<span class="chip" onclick="mwSet('/home/kuhy/.ssh')">~/.ssh</span>
|
||||
<span class="chip" onclick="mwSet('/home/kuhy/.config/dufs')">~/.config/dufs</span>
|
||||
<span class="chip" onclick="mwSet('/home/kuhy/testsAndMisc/x')">inside the repo</span>
|
||||
<span class="chip" onclick="mwSet('/home/kuhy/src/testsAndMisc/x')">inside the repo</span>
|
||||
<span class="chip" onclick="mwSet('/srv/cloud/../../home/kuhy/.gnupg')">traversal → .gnupg</span>
|
||||
<span class="chip" onclick="mwSet('/srv/cloud')">/srv/cloud</span>
|
||||
</div>
|
||||
@ -288,9 +288,9 @@ bad="$(printf '%s\n' "$staged" | grep -E '(^|/)\.dufs_cloud\.conf$' || true)"
|
||||
}
|
||||
function guard(cloudRoot){
|
||||
// Mirrors assert_cloud_root_safe: HOME and / are blocked ONLY as an exact
|
||||
// match (subdirs of HOME like ~/cloud are fine); .ssh/.config/.gnupg and
|
||||
// match (subdirs of HOME like ~/data/cloud are fine); .ssh/.config/.gnupg and
|
||||
// the repo are blocked as exact-or-prefix.
|
||||
var HOME='/home/kuhy', REPO='/home/kuhy/testsAndMisc';
|
||||
var HOME='/home/kuhy', REPO='/home/kuhy/src/testsAndMisc';
|
||||
var r = resolve(cloudRoot);
|
||||
if (r===HOME) return {ok:false, why:'Resolves to '+r+' — that is $HOME itself; serving it would expose everything.'};
|
||||
if (r==='/') return {ok:false, why:'Resolves to / — the whole filesystem. Refused.'};
|
||||
@ -363,7 +363,7 @@ bad="$(printf '%s\n' "$staged" | grep -E '(^|/)\.dufs_cloud\.conf$' || true)"
|
||||
rat: "assert_config_gitignored refuses to persist the secret unless git confirms it's ignored — a runtime, fail-closed check."
|
||||
},
|
||||
{
|
||||
q: "Why does the sync copy only Passwords.kdbx instead of the whole ~/Keepass directory?",
|
||||
q: "Why does the sync copy only Passwords.kdbx instead of the whole ~/data/Keepass directory?",
|
||||
opts: [
|
||||
"Because rsync cannot recurse into directories that contain hidden dot-folders.",
|
||||
"Because copying the directory would exceed dufs's per-file upload size limit.",
|
||||
|
||||
@ -186,7 +186,7 @@ kuhy-sync.duckdns.org { reverse_proxy 127.0.0.1:8282 } # SyncYomi</pre></div>
|
||||
<div class="code"><pre>preflight # deps, docker up, gitea-caddy present
|
||||
migrate_caddy_snippets # split TODAY's Caddyfile → sites/*.caddy (keep dufs, sync)
|
||||
relocate_gitea # re-run setup_gitea.sh (now writes import + gitea.caddy)
|
||||
build_website # pnpm build → ~/personal-website/dist
|
||||
build_website # pnpm build → ~/src/personal-website/dist
|
||||
write_website_stack # loopback caddy compose + inner Caddyfile
|
||||
write_website_snippet# sites/website.caddy → kuhy.duckdns.org
|
||||
start_website # container up on 127.0.0.1:8088
|
||||
|
||||
@ -184,7 +184,7 @@
|
||||
</div>
|
||||
|
||||
<h3>Try it: how the desk routes a request</h3>
|
||||
<p>Pick a hostname and watch Caddy decide where it goes. This mirrors the real site blocks in <code>~/gitea/Caddyfile</code>.</p>
|
||||
<p>Pick a hostname and watch Caddy decide where it goes. This mirrors the real site blocks in <code>~/services/gitea/Caddyfile</code>.</p>
|
||||
|
||||
<div class="router">
|
||||
<label for="host">Incoming request hostname</label>
|
||||
|
||||
@ -503,7 +503,7 @@ worth explaining why:</p>
|
||||
# The live app: ~/dufs_client is a stale, remote-less checkout that
|
||||
# was superseded when dufs-cloud absorbed it (see testsAndMisc
|
||||
# CLAUDE.md). Same package name, so it is easy to icon the wrong one.
|
||||
repo=_HOME / "dufs-cloud" / "app",</code></pre>
|
||||
repo=_HOME / "src/dufs-cloud" / "app",</code></pre>
|
||||
|
||||
<p>There were two checkouts with the same <code>name:</code>, the same
|
||||
<code>applicationId</code>, and the same directory layout. Only one had a git remote and
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
{
|
||||
"title": "Endurain viewer fed by RunnerUp WebDAV exports",
|
||||
"objective": "Stand up self-hosted Endurain behind the existing Caddy edge and import RunnerUp activities into it, primarily over WebDAV from the phone with an adb pull as fallback. Endurain is a VIEWER only: screen-locker's workout gate keeps reading the phone over adb and is not modified, because a WebDAV drop folder is spoofable by anything that can write to ~/cloud and must never become a screen-unlock authority. Success means real activities from the phone are visible in Endurain, importing twice never creates a duplicate, and screen-locker still passes its full suite.",
|
||||
"objective": "Stand up self-hosted Endurain behind the existing Caddy edge and import RunnerUp activities into it, primarily over WebDAV from the phone with an adb pull as fallback. Endurain is a VIEWER only: screen-locker's workout gate keeps reading the phone over adb and is not modified, because a WebDAV drop folder is spoofable by anything that can write to ~/data/cloud and must never become a screen-unlock authority. Success means real activities from the phone are visible in Endurain, importing twice never creates a duplicate, and screen-locker still passes its full suite.",
|
||||
"acceptance_criteria": [
|
||||
"Endurain reachable at https://endurain.kuhy.duckdns.org with a valid Let's Encrypt certificate",
|
||||
"At least 4 real RunnerUp activities imported and visible, with correct distance and duration",
|
||||
"Re-running the importer over already-imported files creates ZERO new activities",
|
||||
"The .gpx and .tcx of a single run import as ONE activity, not two",
|
||||
"The seeded admin/admin login is rejected before the service is ever published",
|
||||
"The phone-facing WebDAV credential cannot read ~/cloud/Keepass or gdrive_backups",
|
||||
"The phone-facing WebDAV credential cannot read ~/data/cloud/Keepass or gdrive_backups",
|
||||
"screen-locker's full test suite still passes at 100% coverage"
|
||||
],
|
||||
"out_of_scope": [
|
||||
|
||||
@ -12,7 +12,7 @@
|
||||
"pre-commit passes without --no-verify."
|
||||
],
|
||||
"out_of_scope": [
|
||||
"Moving heavy_job_lock.sh to ~/utils: the measurement does not support it, and every consumer but the installer already reads the installed copy.",
|
||||
"Moving heavy_job_lock.sh to ~/src/utils: the measurement does not support it, and every consumer but the installer already reads the installed copy.",
|
||||
"The adb push half of update_android_hosts.sh, which needs a device in a writable state.",
|
||||
"digital_wellbeing itself, which is the next and last extraction."
|
||||
],
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
"check_directory_depth.sh --all exits 0 with --warn removed from the hook entry.",
|
||||
"The extracted repo's history spans all three paths the unit has lived at (63 commits), plus the 24 from the separately-filtered test suites.",
|
||||
"All 80 tracked files are byte-identical to the monorepo copies; node_modules and __pycache__ are excluded.",
|
||||
"All six moved suites plus every lib/tests/run_all.sh pass from ~/digital-wellbeing, with no root, pacman or Arch userland -- verified under env -i.",
|
||||
"All six moved suites plus every lib/tests/run_all.sh pass from ~/src/digital-wellbeing, with no root, pacman or Arch userland -- verified under env -i.",
|
||||
"Every moved test name is deleted from BOTH hardcoded arrays in .github/workflows/shell-tests.yml; every name still listed there resolves to a file that exists.",
|
||||
"check_and_enable_services.sh runs to completion and reports ok for pacman_wrapper, makepkg_wrapper, midnight_shutdown, startup_monitor, periodic_systems and hosts.",
|
||||
"The pacman-wrapper drift manifest verifies with sha256sum -c after regeneration.",
|
||||
@ -18,5 +18,5 @@
|
||||
"The Nextcloud dedup, measured and recommended against in the handoff.",
|
||||
"Reformatting vendor/common.sh, which is a verbatim copy."
|
||||
],
|
||||
"verifier": "./meta/scripts/check_directory_depth.sh --all exits 0; ~/digital-wellbeing/tests/run_all.sh passes all seven suites; grep for the six moved test names in shell-tests.yml returns 0; sudo check_and_enable_services.sh completes with no unbound-variable error and no missing-script error; sudo sha256sum -c --status /var/lib/pacman-wrapper/source.sha256 exits 0; report_live_state.sh --check greps 0 for linux_configuration/scripts/; shellcheck clean on all nine changed shell files."
|
||||
"verifier": "./meta/scripts/check_directory_depth.sh --all exits 0; ~/src/digital-wellbeing/tests/run_all.sh passes all seven suites; grep for the six moved test names in shell-tests.yml returns 0; sudo check_and_enable_services.sh completes with no unbound-variable error and no missing-script error; sudo sha256sum -c --status /var/lib/pacman-wrapper/source.sha256 exits 0; report_live_state.sh --check greps 0 for linux_configuration/scripts/; shellcheck clean on all nine changed shell files."
|
||||
}
|
||||
|
||||
@ -4,8 +4,8 @@
|
||||
"acceptance_criteria": [
|
||||
"The extracted repo's history spans all four paths the unit has lived at, not just the current one — 52 commits, reaching the original 'block websites uising etc hosts'.",
|
||||
"Every extracted source file is byte-identical to its monorepo copy (diff -r reports no differences).",
|
||||
"lib/tests/run_all.sh passes from ~/hosts-blocker, the real clone location, with 25 assertions.",
|
||||
"All six monorepo callers resolve to /home/kuhy/hosts-blocker, including under sudo, where $HOME would be /root.",
|
||||
"lib/tests/run_all.sh passes from ~/src/hosts-blocker, the real clone location, with 25 assertions.",
|
||||
"All six monorepo callers resolve to /home/kuhy/src/hosts-blocker, including under sudo, where $HOME would be /root.",
|
||||
"A missing checkout fails loudly with a clone instruction and a non-zero return — never a silent skip.",
|
||||
"check_directory_depth.sh --all reports 41 violations, all of them digital_wellbeing.",
|
||||
"report_live_state.sh --check output is byte-identical before and after: no new stale references.",
|
||||
@ -20,5 +20,5 @@
|
||||
"Fixing the 8 pre-existing stale live-state references — a separate commit after both extractions.",
|
||||
"Deleting .hippo/ or the untracked extraction residue — separate commits."
|
||||
],
|
||||
"verifier": "diff -r --exclude=.git between the monorepo unit and the extracted repo reports IDENTICAL; ~/hosts-blocker/lib/tests/run_all.sh exits 0 with 25 passed; pre-commit run --all-files --hook-stage pre-push in the extracted repo passes with no file modified; shellcheck is clean on the helper and all six callers; sudo -n resolution prints /home/kuhy/hosts-blocker; HOSTS_BLOCKER_DIR=/nonexistent require_extracted_repo returns 1 with a clone instruction; check_directory_depth.sh --all reports 41; diff of report_live_state.sh --check before/after is empty."
|
||||
"verifier": "diff -r --exclude=.git between the monorepo unit and the extracted repo reports IDENTICAL; ~/src/hosts-blocker/lib/tests/run_all.sh exits 0 with 25 passed; pre-commit run --all-files --hook-stage pre-push in the extracted repo passes with no file modified; shellcheck is clean on the helper and all six callers; sudo -n resolution prints /home/kuhy/src/hosts-blocker; HOSTS_BLOCKER_DIR=/nonexistent require_extracted_repo returns 1 with a clone instruction; check_directory_depth.sh --all reports 41; diff of report_live_state.sh --check before/after is empty."
|
||||
}
|
||||
|
||||
@ -13,5 +13,5 @@
|
||||
"Notifications: a failed unit is the gate; notify-send from a root system unit is not.",
|
||||
"Section 1 of the handoff (signal-bot boot resilience), which shipped separately in kuhyx/signal-bot."
|
||||
],
|
||||
"verifier": "linux_configuration/features/lib/tests/test_wg_verify_units.sh under meta/scripts/shell_coverage_jail.sh; plus two real reboots in ~/utils/vmbox (drifted -> failed unit, clean -> active unit) and a live run on the host."
|
||||
"verifier": "linux_configuration/features/lib/tests/test_wg_verify_units.sh under meta/scripts/shell_coverage_jail.sh; plus two real reboots in ~/src/utils/vmbox (drifted -> failed unit, clean -> active unit) and a live run on the host."
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"intent": "Give the new ~/restaurant-rater app a launcher icon from the shared family, so it stops shipping the stock Flutter beach icon.",
|
||||
"intent": "Give the new ~/src/restaurant-rater app a launcher icon from the shared family, so it stops shipping the stock Flutter beach icon.",
|
||||
"scope": [
|
||||
"python_pkg/app_icons/_glyph_art_more.py - the new SVG fragment",
|
||||
"python_pkg/app_icons/glyphs.py - register the glyph in GLYPHS",
|
||||
@ -14,14 +14,14 @@
|
||||
],
|
||||
"verification": [
|
||||
{
|
||||
"command": "PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons list",
|
||||
"command": "PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.app_icons list",
|
||||
"result": "pass",
|
||||
"evidence": "bowl-star appears under Glyphs; 'restaurant_rater #B8862E bowl-star /home/kuhy/restaurant-rater' appears under Apps."
|
||||
"evidence": "bowl-star appears under Glyphs; 'restaurant_rater #B8862E bowl-star /home/kuhy/src/restaurant-rater' appears under Apps."
|
||||
},
|
||||
{
|
||||
"command": "PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons generate --app restaurant_rater",
|
||||
"command": "PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.app_icons generate --app restaurant_rater",
|
||||
"result": "pass",
|
||||
"evidence": "wrote 6 asset files to /home/kuhy/restaurant-rater/assets/icon (icon/foreground/monochrome, svg+png each). The generator's safe-box and negative-space checks did not fire."
|
||||
"evidence": "wrote 6 asset files to /home/kuhy/src/restaurant-rater/assets/icon (icon/foreground/monochrome, svg+png each). The generator's safe-box and negative-space checks did not fire."
|
||||
},
|
||||
{
|
||||
"command": "visual review of the rendered 640px icon.png",
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
"changes": [
|
||||
"Exact-pinned 95 previously range-specified dependencies to their newest stable release, via ~/.claude/scripts/apply_dep_pins.py driven by the gate's own --json findings.",
|
||||
"Moved the gatelock git-tag dependency from gatelock-v0.1.0 to gatelock-v0.7.2 -- six minor versions of drift that a PEP 508 direct reference hid from every previous check.",
|
||||
"Installed the shared gate's three pieces (delegate, pre-commit hook, CI workflow) from ~/utils/dep_freshness/templates."
|
||||
"Installed the shared gate's three pieces (delegate, pre-commit hook, CI workflow) from ~/src/utils/dep_freshness/templates."
|
||||
],
|
||||
"verification": [
|
||||
{
|
||||
@ -17,7 +17,7 @@
|
||||
"evidence": "exit 0; clean venv rebuilt from the repinned meta/requirements.txt, pre-commit --all-files and the full suite green inside it"
|
||||
},
|
||||
{
|
||||
"command": "~/utils/scripts/check_dependency_freshness.sh --all",
|
||||
"command": "~/src/utils/scripts/check_dependency_freshness.sh --all",
|
||||
"result": "pass",
|
||||
"evidence": "exit 0 -- 97 findings before, 0 after"
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"intent": "Extract the last over-cap unit into github.com/kuhyx/digital-wellbeing (cloned at ~/digital-wellbeing), then delete --warn from the directory-depth-cap hook so the cap enforces for real. This is the closing step of the restructure campaign.",
|
||||
"intent": "Extract the last over-cap unit into github.com/kuhyx/digital-wellbeing (cloned at ~/src/digital-wellbeing), then delete --warn from the directory-depth-cap hook so the cap enforces for real. This is the closing step of the restructure campaign.",
|
||||
"scope": [
|
||||
"REMOVED: linux_configuration/periodic_background/digital_wellbeing/ (80 tracked files)",
|
||||
"REMOVED: 6 test suites + 2 test libs from linux_configuration/tests/",
|
||||
@ -33,7 +33,7 @@
|
||||
"evidence": "ALL 80 TRACKED FILES IDENTICAL. node_modules (340 files) and __pycache__ correctly excluded -- the repo has 80 tracked files, not the 423 on disk."
|
||||
},
|
||||
{
|
||||
"command": "~/digital-wellbeing/tests/run_all.sh",
|
||||
"command": "~/src/digital-wellbeing/tests/run_all.sh",
|
||||
"result": "pass",
|
||||
"evidence": "All six regression suites plus the discovered lib/tests/run_all.sh pass: 'All digital-wellbeing suites passed.'"
|
||||
},
|
||||
@ -55,7 +55,7 @@
|
||||
{
|
||||
"command": "sudo sha256sum -c --status /var/lib/pacman-wrapper/source.sha256",
|
||||
"result": "pass",
|
||||
"evidence": "Exit 0. The manifest embeds ABSOLUTE paths and had named deleted scripts/ paths since the flatten, so the drift check reported a permanent false 'stale or tampered'. It now names ~/digital-wellbeing and verifies -- with unchanged hashes, confirming the extracted bytes are identical."
|
||||
"evidence": "Exit 0. The manifest embeds ABSOLUTE paths and had named deleted scripts/ paths since the flatten, so the drift check reported a permanent false 'stale or tampered'. It now names ~/src/digital-wellbeing and verifies -- with unchanged hashes, confirming the extracted bytes are identical."
|
||||
},
|
||||
{
|
||||
"command": "report_live_state.sh --check | grep -c linux_configuration/scripts/",
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
{
|
||||
"intent": "Extract linux_configuration/periodic_background/hosts/ into its own public repo (github.com/kuhyx/hosts-blocker, cloned at ~/hosts-blocker), clearing 5 of the 46 remaining directory-depth violations without disturbing the live hosts blocker or its guards.",
|
||||
"intent": "Extract linux_configuration/periodic_background/hosts/ into its own public repo (github.com/kuhyx/hosts-blocker, cloned at ~/src/hosts-blocker), clearing 5 of the 46 remaining directory-depth violations without disturbing the live hosts blocker or its guards.",
|
||||
"scope": [
|
||||
"REMOVED: linux_configuration/periodic_background/hosts/ (15 files)",
|
||||
"ADDED: linux_configuration/lib/extracted_repos.sh (shared resolver)",
|
||||
@ -14,7 +14,7 @@
|
||||
"No source file was modified during extraction: all 15 files are byte-identical to the monorepo copies (verified with diff -r). install.sh already resolved its lib/ via BASH_SOURCE and needed no rewrite.",
|
||||
"Added linux_configuration/lib/extracted_repos.sh: resolves an extracted repo's checkout via SUDO_USER's passwd entry (NOT $HOME, which is /root under sudo), supports a <NAME>_DIR override, and fails loudly with a clone instruction when absent.",
|
||||
"Reused the existing REAL_HOME resolution already present in check_and_enable_services.sh for screen-locker rather than sourcing a second resolver into it.",
|
||||
"fresh-install/main.sh uses \"$HOME/hosts-blocker/install.sh\" because it runs from ~/linux-configuration (a different repo) and is not sudo-re-exec'd at that point.",
|
||||
"fresh-install/main.sh uses \"$HOME/src/hosts-blocker/install.sh\" because it runs from ~/src/testsAndMisc/linux_configuration (a different repo) and is not sudo-re-exec'd at that point.",
|
||||
"The new repo ships NO shfmt hook, deliberately: guard/plugins/*.sh are space-indented and unformatted in the monorepo too (its git hook shfmt's only STAGED files), and their installed copies under /usr/local/share/guard-lib-plugins/ are byte-identical by md5. An shfmt hook would rewrite both on first commit and break that identity."
|
||||
],
|
||||
"verification": [
|
||||
@ -29,7 +29,7 @@
|
||||
"evidence": "IDENTICAL — no source file altered by the extraction."
|
||||
},
|
||||
{
|
||||
"command": "./lib/tests/run_all.sh (in ~/hosts-blocker, the real clone location)",
|
||||
"command": "./lib/tests/run_all.sh (in ~/src/hosts-blocker, the real clone location)",
|
||||
"result": "pass",
|
||||
"evidence": "test_hosts_protect.sh: 25 passed, 0 failed. All hosts install test files passed."
|
||||
},
|
||||
@ -41,7 +41,7 @@
|
||||
{
|
||||
"command": "sudo -n bash -c 'source lib/extracted_repos.sh; extracted_repo_dir hosts-blocker'",
|
||||
"result": "pass",
|
||||
"evidence": "HOME=/home/kuhy resolved=/home/kuhy/hosts-blocker — resolves the invoking user, not /root/hosts-blocker."
|
||||
"evidence": "HOME=/home/kuhy resolved=/home/kuhy/src/hosts-blocker — resolves the invoking user, not /root/hosts-blocker."
|
||||
},
|
||||
{
|
||||
"command": "HOSTS_BLOCKER_DIR=/nonexistent require_extracted_repo hosts-blocker",
|
||||
@ -75,7 +75,7 @@
|
||||
}
|
||||
],
|
||||
"risks": [
|
||||
"A machine without ~/hosts-blocker cloned now fails the hosts install step. That is deliberate and loud (clone instruction printed); the previous silent-skip mode is what caused five months of dead self-repair after the 2026-05-15 reorg.",
|
||||
"A machine without ~/src/hosts-blocker cloned now fails the hosts install step. That is deliberate and loud (clone instruction printed); the previous silent-skip mode is what caused five months of dead self-repair after the 2026-05-15 reorg.",
|
||||
"install_core_system.sh on a genuinely fresh machine will hard-stop at the hosts module until the repo is cloned. INSTALLER_FIX_TASK.md already tracks fresh-machine installer defects and has been updated to name the new location.",
|
||||
"The guard plugins remain unformatted. If an shfmt hook is ever added to hosts-blocker it will rewrite them, and the installed copies must be re-synced in the same change.",
|
||||
"The live hosts blocker was NOT re-installed as part of this change; /etc/hosts and its guards are untouched and still enforced from their installed copies."
|
||||
|
||||
@ -6,13 +6,13 @@
|
||||
"Non-goal: changing any gate's rules, or restyling the existing icon family"
|
||||
],
|
||||
"changes": [
|
||||
"pre-commit.yml now checks out kuhyx/utils into .utils/ and points UTILS_ROOT at it. The md-naming and file-length hooks are thin shims that exec the real gate out of that repo; a runner has no ~/utils, so both failed with 'shared gate not found at /home/runner/utils/...' and the workflow has been red since 2026-08-24. The dedicated md-naming.yml already did this checkout — this copies the same approach rather than inventing one.",
|
||||
"pre-commit.yml now checks out kuhyx/utils into .utils/ and points UTILS_ROOT at it. The md-naming and file-length hooks are thin shims that exec the real gate out of that repo; a runner has no ~/src/utils, so both failed with 'shared gate not found at /home/runner/utils/...' and the workflow has been red since 2026-08-24. The dedicated md-naming.yml already did this checkout — this copies the same approach rather than inventing one.",
|
||||
"Registered app_icons entry 'punchme' (shared family accent #B8862E, glyph punch-card, linux=False since the app is Android-only).",
|
||||
"Added the _PUNCH_CARD glyph: a time card with a clipped corner, a column of four punched holes and three ruled lines. The first draft put two holes above two wide slots and read unmistakably as a face (eyes over teeth) at every size, which is why the holes are now a vertical column."
|
||||
],
|
||||
"verification": [
|
||||
{
|
||||
"command": "UTILS_ROOT=/home/kuhy/utils bash scripts/check_md_naming.sh README.md",
|
||||
"command": "UTILS_ROOT=/home/kuhy/src/utils bash scripts/check_md_naming.sh README.md",
|
||||
"result": "pass",
|
||||
"evidence": "Exit 0. With UTILS_ROOT=/nonexistent the same shim fails with 'shared gate not found at /nonexistent/scripts/check_md_naming.sh', so the override is what the fix relies on and it is proven in both directions."
|
||||
},
|
||||
@ -32,7 +32,7 @@
|
||||
"evidence": "All hooks passed, including pylint, bandit and 'pytest with coverage enforcement'."
|
||||
},
|
||||
{
|
||||
"command": "PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons generate --app punchme --android",
|
||||
"command": "PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.app_icons generate --app punchme --android",
|
||||
"result": "pass",
|
||||
"evidence": "Wrote 6 asset files and regenerated the Android mipmaps with no safe-box warning. Rendered at full size, circle-masked and 48dp and reviewed each; legible at all three."
|
||||
}
|
||||
|
||||
@ -6,10 +6,10 @@
|
||||
"python_pkg/app_icons/_glyph_art_more.py - the glyph's SVG fragment",
|
||||
"Non-goal: no new accent. The family accent #B8862E is shared; apps are distinguished by glyph shape only.",
|
||||
"Non-goal: no change to the 13 existing glyphs or to any other app's entry.",
|
||||
"The consuming repo (~/phone-focus-mode) is committed separately; it blocks binaries, so the icon lands there in vector form."
|
||||
"The consuming repo (~/src/phone-focus-mode) is committed separately; it blocks binaries, so the icon lands there in vector form."
|
||||
],
|
||||
"changes": [
|
||||
"Added AppIcon(key='focus_owner', repo=~/phone-focus-mode/focus-owner, glyph='padlock-closed', accent='#B8862E', linux=False). The repo path points into the monorepo because focus-owner was absorbed as a subtree on 2026-08-24 and both the standalone clone and its GitHub repo are gone.",
|
||||
"Added AppIcon(key='focus_owner', repo=~/src/phone-focus-mode/focus-owner, glyph='padlock-closed', accent='#B8862E', linux=False). The repo path points into the monorepo because focus-owner was absorbed as a subtree on 2026-08-24 and both the standalone clone and its GitHub repo are gone.",
|
||||
"Added the _PADLOCK_CLOSED glyph fragment: shackle stroked (inheriting the wrapping group's round caps), body filled in the accent, keyhole punched back out in the field colour via fill-rule='evenodd'. Splitting stroke and fill is deliberate - a fully stroked padlock loses its keyhole at 48dp, a fully filled one loses the shackle's opening.",
|
||||
"Geometry computed against style.py rather than eyeballed, per the lesson recorded on _NOTE_ASCENDER: body 292..732 x 520..792; shackle centreline r=132 about (512,520) giving an outer top of 352, inside the 232 safe edge, with legs at x=380/644 inside the body; keyhole leaves 60px of body above and 76px below the stem, both clear of MIN_NEGATIVE_SPACE=36."
|
||||
],
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
"hosts-blocker: stop_hosts_guard/restart_hosts_guard now handle the guard-lib instances (guard-file@hosts.*, guard-bind-mount@hosts.service) as well as the legacy per-file units, and refresh the canonical copy with 'guardctl file-guard sync hosts' -- without this a SECOND install run fought a live read-only bind mount and the install was not idempotent.",
|
||||
"screen-locker: install_systemd.sh installs its own runtime dependencies (tk for the tkinter shared library, plus the pinned gatelock and crdt-sync git dependencies). It previously installed none, so the post-install workout check crashed and the installer exited 1 on any fresh machine.",
|
||||
"steam-backlog-enforcer: install.sh no longer aborts under `set -euo pipefail` when a bare `read` finds no stdin; it defaults to not installing the systemd service when stdin is not a TTY, with STEAM_ENFORCER_INSTALL_SERVICE=y to opt in.",
|
||||
"tests/lib/security_apps.sh: the screen_locker section resolves the extracted repo via extracted_repo_dir instead of a hardcoded $HOME/testsAndMisc/python_pkg path, and its assertions were rewritten against the current implementation (SPORT_CHOICES, SPORT_TABLE_TENNIS, _validate_table_tennis, sets_won, MANUAL_WORKOUT_REFLECTION_MIN_CHARS) because the four symbols it used to grep for no longer exist anywhere in the codebase."
|
||||
"tests/lib/security_apps.sh: the screen_locker section resolves the extracted repo via extracted_repo_dir instead of a hardcoded $HOME/src/testsAndMisc/python_pkg path, and its assertions were rewritten against the current implementation (SPORT_CHOICES, SPORT_TABLE_TENNIS, _validate_table_tennis, sets_won, MANUAL_WORKOUT_REFLECTION_MIN_CHARS) because the four symbols it used to grep for no longer exist anywhere in the codebase."
|
||||
],
|
||||
"verification": [
|
||||
{
|
||||
@ -25,7 +25,7 @@
|
||||
{
|
||||
"command": "bash linux_configuration/tests/test_security_hardening.sh (host, after the path+assertion fix)",
|
||||
"result": "23 passed / 0 skipped",
|
||||
"evidence": "The five screen_locker checks now execute against ~/screen-locker and pass. Note this is a changed measuring instrument as well as a changed measurement: the suite is 24 checks today, so the task file's '18 passed / 6 skipped out of 17' target was stale and could not be matched literally."
|
||||
"evidence": "The five screen_locker checks now execute against ~/src/screen-locker and pass. Note this is a changed measuring instrument as well as a changed measurement: the suite is 24 checks today, so the task file's '18 passed / 6 skipped out of 17' target was stale and could not be matched literally."
|
||||
},
|
||||
{
|
||||
"command": "vm run <guest> 'bash guest_verify.sh' -- pristine vmbox guest, installer clones its own siblings",
|
||||
|
||||
@ -31,14 +31,14 @@
|
||||
"evidence": "exit 0 -- the basis for deleting both refactor todos"
|
||||
},
|
||||
{
|
||||
"command": "python3 -m pytest md_naming/tests/ (in ~/utils)",
|
||||
"command": "python3 -m pytest md_naming/tests/ (in ~/src/utils)",
|
||||
"result": "pass",
|
||||
"evidence": "71 passed; 100% branch coverage on the gate"
|
||||
}
|
||||
],
|
||||
"risks": [
|
||||
"A rename breaks an inbound link not caught by the reference sweep. Mitigated: the sweep covered ~/.claude/CLAUDE.md, rules/, skills/, memory/ and every tracked .md; a bare-basename match was deliberately narrowed after a dry run showed it would corrupt vendored node_modules files.",
|
||||
"A doc classified TASK is actually finished, or vice versa. Mitigated: verdicts came from content audits cross-checked against repo code, and are recorded in ~/utils/md_naming/migrate/classify.py.",
|
||||
"A doc classified TASK is actually finished, or vice versa. Mitigated: verdicts came from content audits cross-checked against repo code, and are recorded in ~/src/utils/md_naming/migrate/classify.py.",
|
||||
"The gate depends on kuhyx/utils being present; CI checks it out, and the shim fails loudly with install instructions if it is missing."
|
||||
],
|
||||
"rollback": [
|
||||
|
||||
@ -5,19 +5,19 @@
|
||||
"Non-goal: no change to the generator, the glyph set, or any other app's icon"
|
||||
],
|
||||
"changes": [
|
||||
"Registered betting_sim: repo ~/betting-sim/app, accent #B8862E, glyph track-bars (rising bars with a plotted trend point, which reads as odds movement).",
|
||||
"Registered betting_sim: repo ~/src/betting-sim/app, accent #B8862E, glyph track-bars (rising bars with a plotted trend point, which reads as odds movement).",
|
||||
"Set linux=false: betting-sim's desktop target is a Chrome-wrapped web build rather than the GTK embedder, so the hicolor PNG set would go unread."
|
||||
],
|
||||
"verification": [
|
||||
{
|
||||
"command": "PYTHONPATH=. python3 -m python_pkg.app_icons list",
|
||||
"result": "pass",
|
||||
"evidence": "betting_sim #B8862E track-bars /home/kuhy/betting-sim/app"
|
||||
"evidence": "betting_sim #B8862E track-bars /home/kuhy/src/betting-sim/app"
|
||||
},
|
||||
{
|
||||
"command": "PYTHONPATH=. python3 -m python_pkg.app_icons generate --app betting_sim --android",
|
||||
"result": "pass",
|
||||
"evidence": "wrote 6 asset files to /home/kuhy/betting-sim/app/assets/icon; mipmaps generated after adding flutter_launcher_icons to the app pubspec"
|
||||
"evidence": "wrote 6 asset files to /home/kuhy/src/betting-sim/app/assets/icon; mipmaps generated after adding flutter_launcher_icons to the app pubspec"
|
||||
},
|
||||
{
|
||||
"command": "visual check of assets/icon/icon.png",
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
{
|
||||
"command": "resolver across all four invocation modes",
|
||||
"result": "pass",
|
||||
"evidence": "as user -> /home/kuhy/hosts-blocker; via sudo -> same; as root with SUDO_USER/USER unset -> same; HOSTS_BLOCKER_DIR=/opt/hb -> /opt/hb (override still wins)."
|
||||
"evidence": "as user -> /home/kuhy/src/hosts-blocker; via sudo -> same; as root with SUDO_USER/USER unset -> same; HOSTS_BLOCKER_DIR=/opt/hb -> /opt/hb (override still wins)."
|
||||
},
|
||||
{
|
||||
"command": "sudo visudo -c",
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
{
|
||||
"command": "./meta/scripts/report_live_state.sh --check; echo $?",
|
||||
"result": "pass",
|
||||
"evidence": "Exit code 1 with 'live references into /home/kuhy/testsAndMisc remain', which is correct at baseline since nothing has moved yet."
|
||||
"evidence": "Exit code 1 with 'live references into /home/kuhy/src/testsAndMisc remain', which is correct at baseline since nothing has moved yet."
|
||||
},
|
||||
{
|
||||
"command": "14 hand-built path cases through check_directory_depth.sh",
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
],
|
||||
"changes": [
|
||||
"SYNCYOMI_IMAGE defaults to the local build syncyomi-kuhy:local, overridable back to a registry tag; adds SYNCYOMI_SRC and the fork URL.",
|
||||
"ensure_local_image() builds that image from ~/syncyomi-src (cloning the fork if absent) before the container starts, under DOCKER_BUILDKIT=1 because the Dockerfile reads $BUILDPLATFORM; SYNCYOMI_REBUILD=1 forces a rebuild.",
|
||||
"ensure_local_image() builds that image from ~/vendor/syncyomi-src (cloning the fork if absent) before the container starts, under DOCKER_BUILDKIT=1 because the Dockerfile reads $BUILDPLATFORM; SYNCYOMI_REBUILD=1 forces a rebuild.",
|
||||
"configure_caddy() now owns gitea/sites/<subdomain>.caddy instead of appending to the shared Caddyfile, deletes a legacy inline block, and validates the config before reloading.",
|
||||
"Preflight installs git and docker-buildx alongside the existing packages."
|
||||
],
|
||||
@ -36,12 +36,12 @@
|
||||
],
|
||||
"risks": [
|
||||
"The deployed build is develop (v1.1.14 + dependency bumps), not the v1.1.11 image it replaced; schema stays at version 8 and internal/database is unchanged between those points, so no migration runs.",
|
||||
"A machine without ~/syncyomi-src now clones the fork on first setup, which needs network and a Docker build (~4 min) instead of an image pull.",
|
||||
"A machine without ~/vendor/syncyomi-src now clones the fork on first setup, which needs network and a Docker build (~4 min) instead of an image pull.",
|
||||
"The Caddy snippet is only rewritten when its content differs, so hand edits to that file are overwritten by design."
|
||||
],
|
||||
"rollback": [
|
||||
"SYNCYOMI_IMAGE=ghcr.io/syncyomi/syncyomi:v1.1.11 ./linux_configuration/features/setup_syncyomi.sh setup",
|
||||
"Cold database copy: ~/syncyomi_safe_2026-08-28/pre-local-build/cold/syncyomi.db (schema version 8).",
|
||||
"Cold database copy: ~/archive/2026-08-28-syncyomi_safe/pre-local-build/cold/syncyomi.db (schema version 8).",
|
||||
"After rollback, re-run the guard and confirm 2163 manga, then a phone sync round-trip."
|
||||
]
|
||||
}
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
"evidence": "No findings against the generated unit; the only output was pre-existing warnings about unrelated units on this host."
|
||||
},
|
||||
{
|
||||
"command": "systemd-run --user --wait -p ProtectSystem=strict -p ProtectHome=read-only -p ReadWritePaths=/home/kuhy/personal-website ... running the three real build steps",
|
||||
"command": "systemd-run --user --wait -p ProtectSystem=strict -p ProtectHome=read-only -p ReadWritePaths=/home/kuhy/src/personal-website ... running the three real build steps",
|
||||
"result": "pass",
|
||||
"evidence": "vite build ok / ssr build ok / prerender ok, and `touch $HOME/.pw-sandbox-should-fail` was refused with 'Read-only file system'. The sandbox permits exactly the checkout."
|
||||
}
|
||||
|
||||
@ -336,7 +336,7 @@ printf '\nResults: %d passed, %d failed\n' "${PASS}" "${FAIL}"
|
||||
- [ ] **Step 1.3: Run the test to verify it passes**
|
||||
|
||||
```bash
|
||||
cd /home/kuhy/testsAndMisc
|
||||
cd /home/kuhy/src/testsAndMisc
|
||||
bash phone_focus_mode/lib/tests/test_adb_common.sh
|
||||
```
|
||||
|
||||
@ -1762,7 +1762,7 @@ After=network.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=%h/testsAndMisc/scripts/run_all/run_phone.sh auto
|
||||
ExecStart=%h/src/testsAndMisc/scripts/run_all/run_phone.sh auto
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
```
|
||||
|
||||
@ -5,7 +5,7 @@ fed by RunnerUp exports from the phone.
|
||||
|
||||
**Endurain is a viewer.** `screen-locker`'s workout gate still reads the phone
|
||||
over adb and is untouched by anything here — a WebDAV drop folder is trivially
|
||||
spoofable by anything that can write to `~/cloud`, so it must never become an
|
||||
spoofable by anything that can write to `~/data/cloud`, so it must never become an
|
||||
unlock authority.
|
||||
|
||||
## Paths
|
||||
@ -14,7 +14,7 @@ unlock authority.
|
||||
| ------------- | --------------------------------------------------------------------------- |
|
||||
| Public URL | `https://endurain.kuhy.duckdns.org` |
|
||||
| Local URL | `http://127.0.0.1:8085` (loopback only) |
|
||||
| WebDAV inbox | `https://kuhy-cloud.duckdns.org/RunnerUp` → `~/cloud/RunnerUp/` |
|
||||
| WebDAV inbox | `https://kuhy-cloud.duckdns.org/RunnerUp` → `~/data/cloud/RunnerUp/` |
|
||||
| Runtime data | `/var/opt/endurain/` |
|
||||
| Ledger | `~/.local/state/endurain-import/ledger.json` |
|
||||
| Importer code | `../python_pkg/endurain_import/` (repo requires Python under `python_pkg/`) |
|
||||
@ -23,9 +23,9 @@ unlock authority.
|
||||
## Flow
|
||||
|
||||
```
|
||||
phone --(WebDAV, manual tap)--> dufs ~/cloud/RunnerUp/ --\
|
||||
phone --(WebDAV, manual tap)--> dufs ~/data/cloud/RunnerUp/ --\
|
||||
>-- importer --> Endurain
|
||||
phone --(adb pull, fallback)--> ~/cloud/RunnerUp/ -------/
|
||||
phone --(adb pull, fallback)--> ~/data/cloud/RunnerUp/ -------/
|
||||
```
|
||||
|
||||
A systemd user timer (`endurain-import.timer`) runs every 15 min.
|
||||
@ -48,7 +48,7 @@ cd .. && .venv/bin/python -m pytest python_pkg/endurain_import/tests/ -q # 52 t
|
||||
- **The WebDAV URL must not end in a slash.** RunnerUp builds the target as
|
||||
`url + fileBase + fileExt` where `fileBase` already starts with `/`.
|
||||
- **`MKCOL` is not supported by dufs.** RunnerUp `PROPFIND`s the directory and
|
||||
only issues `MKCOL` on 404, so `~/cloud/RunnerUp/` must exist on disk or
|
||||
only issues `MKCOL` on 404, so `~/data/cloud/RunnerUp/` must exist on disk or
|
||||
adding the account fails with an opaque generic error (upstream #1147/#1172).
|
||||
- **HTTPS is mandatory.** RunnerUp targets `targetSdk 36` with no
|
||||
`usesCleartextTraffic`, so Android blocks plain HTTP. Self-signed certs are
|
||||
|
||||
@ -20,7 +20,7 @@ SCRIPT_NAME="$(basename "$0")"
|
||||
readonly SCRIPT_NAME
|
||||
readonly APP_URL="http://127.0.0.1:8085"
|
||||
readonly PUBLIC_HOST="endurain.kuhy.duckdns.org"
|
||||
readonly EDGE_DIR="${HOME}/gitea"
|
||||
readonly EDGE_DIR="${HOME}/services/gitea"
|
||||
readonly SITE_FILE="${EDGE_DIR}/sites/endurain.caddy"
|
||||
readonly EDGE_CONTAINER="gitea-caddy"
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
[Unit]
|
||||
Description=Import RunnerUp activities into Endurain
|
||||
Documentation=file:///home/kuhy/testsAndMisc/endurain/README.md
|
||||
Documentation=file:///home/kuhy/src/testsAndMisc/endurain/README.md
|
||||
# Endurain runs in Docker; without this the timer fires during boot before the
|
||||
# stack is listening and logs a spurious "not reachable" failure.
|
||||
After=network-online.target docker.service
|
||||
@ -8,9 +8,9 @@ Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
WorkingDirectory=/home/kuhy/testsAndMisc
|
||||
WorkingDirectory=/home/kuhy/src/testsAndMisc
|
||||
Environment=ENDURAIN_URL=http://127.0.0.1:8085
|
||||
Environment=ENDURAIN_INBOX=/home/kuhy/cloud/RunnerUp
|
||||
Environment=ENDURAIN_INBOX=/home/kuhy/data/cloud/RunnerUp
|
||||
Environment=ENDURAIN_STATE=/home/kuhy/.local/state/endurain-import
|
||||
Environment=ENDURAIN_BULK_DIR=/var/opt/endurain/backend/data/activity_files/bulk_import
|
||||
# Two adb devices are usually attached (USB phone + a network target), and the
|
||||
@ -18,7 +18,7 @@ Environment=ENDURAIN_BULK_DIR=/var/opt/endurain/backend/data/activity_files/bulk
|
||||
Environment=ENDURAIN_ADB_SERIAL=23181JEGR08034
|
||||
# The API key is read from a mode-600 file rather than embedded here: systemd
|
||||
# unit files are world-readable.
|
||||
ExecStart=/usr/bin/bash -c 'ENDURAIN_API_KEY="$(cat /home/kuhy/testsAndMisc/endurain/.api_key)" /home/kuhy/testsAndMisc/.venv/bin/python -m python_pkg.endurain_import'
|
||||
ExecStart=/usr/bin/bash -c 'ENDURAIN_API_KEY="$(cat /home/kuhy/src/testsAndMisc/endurain/.api_key)" /home/kuhy/src/testsAndMisc/.venv/bin/python -m python_pkg.endurain_import'
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
|
||||
@ -15,8 +15,9 @@ printf ' ✓ toolchain complete\n'
|
||||
|
||||
printf 'Running auto-fixers and shell_check before committing...\n'
|
||||
|
||||
# Get list of staged shell files
|
||||
mapfile -t staged_files < <(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(sh|bash|zsh)$' || true)
|
||||
# Get list of staged shell files. zsh is deliberately excluded: neither shfmt
|
||||
# nor shellcheck parses zsh (SC1071), so .zsh files get `zsh -n` further down.
|
||||
mapfile -t staged_files < <(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(sh|bash)$' || true)
|
||||
|
||||
if [[ ${#staged_files[@]} -gt 0 ]]; then
|
||||
printf 'Auto-fixing %d shell file(s)...\n' "${#staged_files[@]}"
|
||||
@ -44,7 +45,7 @@ printf 'Running shell_check validation...\n'
|
||||
# Run shell_check to validate all checks
|
||||
# Note: We only validate the staged files since we just auto-formatted them
|
||||
# Validate only shellcheck, not shfmt (we already applied formatting)
|
||||
mapfile -d '' -t staged_shell_files < <(git diff --cached --name-only --diff-filter=ACM -z | grep -zE '\.(sh|bash|zsh)$' || true)
|
||||
mapfile -d '' -t staged_shell_files < <(git diff --cached --name-only --diff-filter=ACM -z | grep -zE '\.(sh|bash)$' || true)
|
||||
|
||||
if [[ ${#staged_shell_files[@]} -gt 0 ]]; then
|
||||
# Run shellcheck on staged files (guaranteed present by verify_toolchain)
|
||||
@ -57,6 +58,15 @@ if [[ ${#staged_shell_files[@]} -gt 0 ]]; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# zsh files: syntax check only, with the shell that will actually run them.
|
||||
mapfile -d '' -t staged_zsh_files < <(git diff --cached --name-only --diff-filter=ACM -z | grep -zE '\.zsh$' || true)
|
||||
for file in "${staged_zsh_files[@]}"; do
|
||||
if ! zsh -n "$file"; then
|
||||
printf '\nCommit aborted: zsh -n found a syntax error in %s.\n' "$file" >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
printf 'shell_check passed.\n'
|
||||
|
||||
# Run duplicate code detection
|
||||
|
||||
@ -24,7 +24,7 @@ This repo automates Linux desktop bootstrap, hardening, and i3 setup. It’s pri
|
||||
## Core workflows (what to run)
|
||||
|
||||
- Fresh machine: run from repo root
|
||||
- `fresh-install/main.sh` (bootstraps configs, GPU, hosts, i3, pacman wrapper, services). It assumes the repo is at `~/linux-configuration` in some steps.
|
||||
- `fresh-install/main.sh` (bootstraps configs, GPU, hosts, i3, pacman wrapper, services). It assumes the repo is at `~/src/testsAndMisc/linux_configuration` in some steps.
|
||||
- Periodic services: `sudo periodic_background/setup_periodic_system.sh` (installs timer, startup service, hosts monitor, and browser pre-exec wrapper; then performs an initial run).
|
||||
- Pacman wrapper only: `sudo periodic_background/digital_wellbeing/pacman/install_pacman_wrapper.sh` (backs up pacman and wires the wrapper). The wrapper auto-runs hosts-guard pre/post hooks and can self-setup periodic services when missing.
|
||||
- Hosts guard:
|
||||
@ -66,4 +66,4 @@ For in-depth understanding of specific components, see these dedicated guides:
|
||||
| Midnight Shutdown | Auto-shutdown at night | `periodic_background/digital_wellbeing/setup_midnight_shutdown.sh` |
|
||||
| Compulsive Block | Limit app launches | `periodic_background/digital_wellbeing/block_compulsive_opening.sh` |
|
||||
| Music Wrapper | Block music during focus | `periodic_background/digital_wellbeing/youtube-music-wrapper.sh` |
|
||||
| Screen Locker | Require workout to unlock | External: `~/testsAndMisc/python_pkg/screen_locker/` |
|
||||
| Screen Locker | Require workout to unlock | External: `~/src/testsAndMisc/python_pkg/screen_locker/` |
|
||||
|
||||
@ -57,7 +57,7 @@
|
||||
|
||||
### 3. Screen Locker (Python - External Repo)
|
||||
|
||||
**File:** `/home/kuhy/testsAndMisc/python_pkg/screen_locker/screen_lock.py`
|
||||
**File:** `/home/kuhy/src/testsAndMisc/python_pkg/screen_locker/screen_lock.py`
|
||||
|
||||
**Current Workout Types:**
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
|
||||
````
|
||||
I need to implement comprehensive security hardening for a Linux digital wellbeing system.
|
||||
The codebase is at ~/linux-configuration/ with these components needing changes:
|
||||
The codebase is at ~/src/testsAndMisc/linux_configuration/ with these components needing changes:
|
||||
|
||||
## 1. HOSTS PROTECTION - nsswitch.conf Guard
|
||||
|
||||
@ -42,7 +42,7 @@ Changes needed:
|
||||
|
||||
## 3. SCREEN LOCKER - External Repo
|
||||
|
||||
Location: ~/testsAndMisc/python_pkg/screen_locker/screen_lock.py
|
||||
Location: ~/src/testsAndMisc/python_pkg/screen_locker/screen_lock.py
|
||||
|
||||
Changes needed:
|
||||
- REMOVE the "Running" workout option entirely (too easy to fake)
|
||||
@ -173,7 +173,7 @@ Modified files:
|
||||
|
||||
External repo (separate changes):
|
||||
|
||||
- ~/testsAndMisc/python_pkg/screen_locker/screen_lock.py (remove running, harden table tennis)
|
||||
- ~/src/testsAndMisc/python_pkg/screen_locker/screen_lock.py (remove running, harden table tennis)
|
||||
|
||||
```
|
||||
|
||||
|
||||
@ -167,7 +167,7 @@ LIMITATION (needs fix):
|
||||
|
||||
You are an expert on the screen_lock.py workout locker. You understand:
|
||||
|
||||
FILE LOCATION: ~/testsAndMisc/python_pkg/screen_locker/screen_lock.py (1261 lines)
|
||||
FILE LOCATION: ~/src/testsAndMisc/python_pkg/screen_locker/screen_lock.py (1261 lines)
|
||||
|
||||
PURPOSE:
|
||||
|
||||
|
||||
@ -39,8 +39,8 @@ systemctl status hosts-guard.path hosts-bind-mount.service
|
||||
sudo /usr/local/sbin/unlock-hosts
|
||||
|
||||
# Reinstall/repair
|
||||
sudo ~/linux-configuration/hosts/install.sh
|
||||
sudo ~/linux-configuration/hosts/guard/setup_hosts_guard.sh
|
||||
sudo ~/src/hosts-blocker/install.sh
|
||||
sudo ~/src/hosts-blocker/guard/setup_hosts_guard.sh
|
||||
````
|
||||
|
||||
## DO NOT
|
||||
|
||||
@ -51,7 +51,7 @@ else
|
||||
fi
|
||||
|
||||
# Test 5: google-chrome is blocked
|
||||
if grep -qi "google-chrome" ~/linux-configuration/scripts/periodic_background/digital_wellbeing/pacman/pacman_blocked_keywords.txt; then
|
||||
if grep -qi "google-chrome" ~/src/testsAndMisc/linux_configuration/scripts/periodic_background/digital_wellbeing/pacman/pacman_blocked_keywords.txt; then
|
||||
test_result "google-chrome in blocked list" "pass"
|
||||
else
|
||||
test_result "google-chrome in blocked list" "fail"
|
||||
|
||||
@ -20,7 +20,7 @@ command -v simplenote >/dev/null 2>&1 && simplenote &
|
||||
[ -x "$HOME/.fehbg" ] && "$HOME/.fehbg" &
|
||||
|
||||
# repo helpers (real current paths under single_use/utils); guarded in case moved
|
||||
idle="$HOME/testsAndMisc/linux_configuration/utils/turn_off_auto_idle_screen_shutdown.sh"
|
||||
idle="$HOME/src/testsAndMisc/linux_configuration/utils/turn_off_auto_idle_screen_shutdown.sh"
|
||||
[ -x "$idle" ] && bash "$idle" --watch-controller &
|
||||
|
||||
# lightweight status bar (root window name)
|
||||
|
||||
@ -201,13 +201,13 @@ static const Key keys[] = {
|
||||
* backend as the media keys, with a notification and NO pacman side-effects.
|
||||
* (toggle_mic.sh is deliberately avoided here: it runs `sudo pacman -S dunst`
|
||||
* if dunst is missing, which deadlocks pacman when spawned without a tty.) */
|
||||
{ MODKEY, XK_m, spawn, SHCMD("$HOME/testsAndMisc/linux_configuration/utils/volume_control.sh micmute") },
|
||||
{ MODKEY, XK_m, spawn, SHCMD("$HOME/src/testsAndMisc/linux_configuration/utils/volume_control.sh micmute") },
|
||||
|
||||
/* media keys — routed through volume_control.sh, same as the (now-fixed) i3 config */
|
||||
{ 0, XF86XK_AudioRaiseVolume, spawn, SHCMD("$HOME/testsAndMisc/linux_configuration/utils/volume_control.sh up") },
|
||||
{ 0, XF86XK_AudioLowerVolume, spawn, SHCMD("$HOME/testsAndMisc/linux_configuration/utils/volume_control.sh down") },
|
||||
{ 0, XF86XK_AudioMute, spawn, SHCMD("$HOME/testsAndMisc/linux_configuration/utils/volume_control.sh mute") },
|
||||
{ 0, XF86XK_AudioMicMute, spawn, SHCMD("$HOME/testsAndMisc/linux_configuration/utils/volume_control.sh micmute") },
|
||||
{ 0, XF86XK_AudioRaiseVolume, spawn, SHCMD("$HOME/src/testsAndMisc/linux_configuration/utils/volume_control.sh up") },
|
||||
{ 0, XF86XK_AudioLowerVolume, spawn, SHCMD("$HOME/src/testsAndMisc/linux_configuration/utils/volume_control.sh down") },
|
||||
{ 0, XF86XK_AudioMute, spawn, SHCMD("$HOME/src/testsAndMisc/linux_configuration/utils/volume_control.sh mute") },
|
||||
{ 0, XF86XK_AudioMicMute, spawn, SHCMD("$HOME/src/testsAndMisc/linux_configuration/utils/volume_control.sh micmute") },
|
||||
|
||||
/* tags 1-10 (i3 workspaces 1-10) */
|
||||
TAGKEYS( XK_1, 0)
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
CANONICAL="${KP_CANONICAL:-$HOME/cloud/Keepass/Passwords.kdbx}"
|
||||
CANONICAL="${KP_CANONICAL:-$HOME/data/cloud/Keepass/Passwords.kdbx}"
|
||||
SEARCH_ROOT="${KP_SEARCH_ROOT:-$HOME}"
|
||||
DRY_RUN=0
|
||||
STATE_DIR="${KP_STATE_DIR:-$HOME/.config/keepass-sync}"
|
||||
@ -25,16 +25,34 @@ STATE_DIR="${KP_STATE_DIR:-$HOME/.config/keepass-sync}"
|
||||
EXCLUDE_ARGS=()
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--canonical) CANONICAL="$2"; shift 2 ;;
|
||||
--root) SEARCH_ROOT="$2"; shift 2 ;;
|
||||
--exclude) EXCLUDE_ARGS+=("$2"); shift 2 ;;
|
||||
--dry-run|-n) DRY_RUN=1; shift ;;
|
||||
*) echo "unknown arg: $1" >&2; exit 2 ;;
|
||||
--canonical)
|
||||
CANONICAL="$2"
|
||||
shift 2
|
||||
;;
|
||||
--root)
|
||||
SEARCH_ROOT="$2"
|
||||
shift 2
|
||||
;;
|
||||
--exclude)
|
||||
EXCLUDE_ARGS+=("$2")
|
||||
shift 2
|
||||
;;
|
||||
--dry-run | -n)
|
||||
DRY_RUN=1
|
||||
shift
|
||||
;;
|
||||
*)
|
||||
echo "unknown arg: $1" >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
log() { printf '[keepass-consolidate] %s\n' "$*" >&2; }
|
||||
die() { printf '[keepass-consolidate] ERROR: %s\n' "$*" >&2; exit 1; }
|
||||
die() {
|
||||
printf '[keepass-consolidate] ERROR: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
MASTER_PW="${KP_MASTER_PW:-}"
|
||||
[[ -n "$MASTER_PW" ]] || die "KP_MASTER_PW not set (needed to open/merge vaults)"
|
||||
@ -47,18 +65,23 @@ FD="$(command -v fd || command -v fdfind || true)"
|
||||
opens() { printf '%s\n' "$MASTER_PW" | keepassxc-cli ls "$1" >/dev/null 2>&1; }
|
||||
opens "$CANONICAL" || die "master password does not open the canonical vault — aborting"
|
||||
|
||||
backup() { # backup <file> — copy into a timestamped sibling .backup_ dir
|
||||
backup() { # backup <file> — copy into a timestamped sibling .backup_ dir
|
||||
local f="$1" bdir
|
||||
bdir="$(dirname "$f")/.backup_$(date +%Y%m%d_%H%M%S)_consolidate"
|
||||
mkdir -p "$bdir"; cp -f "$f" "$bdir/"
|
||||
mkdir -p "$bdir"
|
||||
cp -f "$f" "$bdir/"
|
||||
}
|
||||
|
||||
# --- exclude prefixes: dirs whose vaults are NOT strays -----------------------
|
||||
# Critically the dufs serve-path: on the machine running dufs that directory IS
|
||||
# the remote canonical store (e.g. ~/cloud/Keepass) — it must never be treated
|
||||
# the remote canonical store (e.g. ~/data/cloud/Keepass) — it must never be treated
|
||||
# as a stray and removed. Plus any --exclude args and KP_EXCLUDE (colon-list).
|
||||
EXCLUDES=()
|
||||
_add_exclude() { local p; p="$(readlink -f "$1" 2>/dev/null || echo "$1")"; [[ -n "$p" ]] && EXCLUDES+=("$p"); }
|
||||
_add_exclude() {
|
||||
local p
|
||||
p="$(readlink -f "$1" 2>/dev/null || echo "$1")"
|
||||
[[ -n "$p" ]] && EXCLUDES+=("$p")
|
||||
}
|
||||
_dufs_cfg="$HOME/.config/dufs/dufs.yaml"
|
||||
if [[ -f "$_dufs_cfg" ]]; then
|
||||
_sp="$(sed -nE 's/^serve-path:[[:space:]]*//p' "$_dufs_cfg" | head -1)"
|
||||
@ -67,7 +90,7 @@ fi
|
||||
IFS=':' read -r -a _kp_excl <<<"${KP_EXCLUDE:-}"
|
||||
for e in "${_kp_excl[@]}" "${EXCLUDE_ARGS[@]}"; do [[ -n "$e" ]] && _add_exclude "$e"; done
|
||||
|
||||
is_excluded() { # is_excluded <abs-path> — under any excluded prefix?
|
||||
is_excluded() { # is_excluded <abs-path> — under any excluded prefix?
|
||||
local p="$1" ex
|
||||
for ex in "${EXCLUDES[@]}"; do
|
||||
[[ "$p" == "$ex" || "$p" == "$ex"/* ]] && return 0
|
||||
@ -90,8 +113,8 @@ STRAYS=()
|
||||
for f in "${CANDIDATES[@]}"; do
|
||||
[[ -f "$f" ]] || continue
|
||||
local_abs="$(readlink -f "$f")"
|
||||
[[ "$local_abs" == "$CANON_ABS" ]] && continue # the canonical itself
|
||||
is_excluded "$local_abs" && continue # dufs store / excluded dirs
|
||||
[[ "$local_abs" == "$CANON_ABS" ]] && continue # the canonical itself
|
||||
is_excluded "$local_abs" && continue # dufs store / excluded dirs
|
||||
# Skip our own timestamped backups defensively (in case fd glob missed).
|
||||
[[ "$f" == *"/.backup_"* ]] && continue
|
||||
STRAYS+=("$f")
|
||||
|
||||
@ -22,7 +22,7 @@ trap _t_teardown EXIT
|
||||
# shellcheck source=../wg_verify_units.sh
|
||||
. "$SCRIPT_DIR/../wg_verify_units.sh"
|
||||
|
||||
readonly ENTRY="/home/kuhy/testsAndMisc/linux_configuration/features/setup_wireguard_ssh.sh"
|
||||
readonly ENTRY="/home/kuhy/src/testsAndMisc/linux_configuration/features/setup_wireguard_ssh.sh"
|
||||
UNIT_DIR="$TEST_TMPDIR/units"
|
||||
mkdir -p "$UNIT_DIR"
|
||||
|
||||
|
||||
@ -27,8 +27,8 @@ source "$SCRIPT_DIR/../lib/common.sh"
|
||||
readonly GITEA_DOMAIN="kuhy.duckdns.org"
|
||||
readonly GITEA_OWNER="kuhyx"
|
||||
readonly GITHUB_OWNER="kuhyx"
|
||||
readonly GITEA_TOKEN_FILE="${HOME}/gitea/.admin_token"
|
||||
readonly GITHUB_MIRROR_TOKEN_FILE="${HOME}/gitea/.github_mirror_token"
|
||||
readonly GITEA_TOKEN_FILE="${HOME}/services/gitea/.admin_token"
|
||||
readonly GITHUB_MIRROR_TOKEN_FILE="${HOME}/services/gitea/.github_mirror_token"
|
||||
|
||||
die() {
|
||||
log_error "$1"
|
||||
|
||||
@ -34,7 +34,7 @@ source "$SCRIPT_DIR/../lib/common.sh"
|
||||
readonly GITEA_DOMAIN="gitea.kuhy.duckdns.org"
|
||||
readonly GITEA_ADMIN_USER="kuhyx"
|
||||
readonly GITEA_ADMIN_EMAIL="krzysztofrudnicki0@gmail.com"
|
||||
readonly GITEA_DATA_DIR="${HOME}/gitea"
|
||||
readonly GITEA_DATA_DIR="${HOME}/services/gitea"
|
||||
readonly COMPOSE_FILE="${GITEA_DATA_DIR}/docker-compose.yml"
|
||||
readonly CADDYFILE="${GITEA_DATA_DIR}/Caddyfile"
|
||||
# Per-service Caddy site blocks live here as individual *.caddy snippets so
|
||||
|
||||
@ -42,14 +42,14 @@ source "${SCRIPT_DIR}/../lib/common.sh"
|
||||
readonly DICE_DOMAIN="dice.kuhy.duckdns.org"
|
||||
readonly DICE_PORT="8089"
|
||||
# The solver was extracted to its own repo; this script only deploys it.
|
||||
readonly DICE_SRC="${HOME}/kcd2-dice-solver"
|
||||
readonly DICE_SRC="${HOME}/src/kcd2-dice-solver"
|
||||
readonly REPO_ROOT="${DICE_SRC}"
|
||||
readonly DICE_BUILD="${DICE_SRC}/deploy_build.sh"
|
||||
readonly DICE_DATA_DIR="${HOME}/kcd2-dice-serve"
|
||||
readonly DICE_DATA_DIR="${HOME}/services/kcd2-dice-serve"
|
||||
readonly DICE_COMPOSE="${DICE_DATA_DIR}/docker-compose.yml"
|
||||
readonly DICE_INNER_CADDY="${DICE_DATA_DIR}/Caddyfile"
|
||||
# Shared edge owned by setup_gitea.sh.
|
||||
readonly GITEA_DATA_DIR="${HOME}/gitea"
|
||||
readonly GITEA_DATA_DIR="${HOME}/services/gitea"
|
||||
readonly CADDYFILE="${GITEA_DATA_DIR}/Caddyfile"
|
||||
readonly SITES_DIR="${GITEA_DATA_DIR}/sites"
|
||||
readonly DICE_SNIPPET="${SITES_DIR}/dice.caddy"
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
# setup_keepass_sync.sh — set up "one KeePass vault, synced everywhere".
|
||||
#
|
||||
# IDENTICAL on every Linux device (PC and laptop alike): each keeps one local
|
||||
# working vault at ~/Keepass/Passwords.kdbx and syncs it to the canonical copy
|
||||
# working vault at ~/data/Keepass/Passwords.kdbx and syncs it to the canonical copy
|
||||
# on the dufs server over WebDAV, at the moment you open KeePass. You open the
|
||||
# vault with a single command — `keepass-open` — on every machine. The phone
|
||||
# uses KeePassDX (same idea: enter password → Synchronize over WebDAV → use).
|
||||
@ -22,13 +22,19 @@ readonly CONFIG_DIR="$HOME/.config/keepass-sync"
|
||||
readonly CONFIG="$CONFIG_DIR/config.env"
|
||||
readonly BIN_DIR="$HOME/.local/bin"
|
||||
readonly KEYRING_SERVICE="${KP_KEYRING_SERVICE:-keepass-sync}"
|
||||
readonly LOCAL_DB="$HOME/Keepass/Passwords.kdbx"
|
||||
readonly LOCAL_DB="$HOME/data/Keepass/Passwords.kdbx"
|
||||
|
||||
C() { printf '\033[1;34m[keepass-setup]\033[0m %s\n' "$*"; }
|
||||
OK() { printf '\033[1;32m ✓\033[0m %s\n' "$*"; }
|
||||
WARN() { printf '\033[1;33m !\033[0m %s\n' "$*"; }
|
||||
die() { printf '\033[1;31m[keepass-setup] ERROR:\033[0m %s\n' "$*" >&2; exit 1; }
|
||||
usage() { grep -E '^#( |$)' "$0" | sed -E 's/^# ?//'; exit 0; }
|
||||
die() {
|
||||
printf '\033[1;31m[keepass-setup] ERROR:\033[0m %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
usage() {
|
||||
grep -E '^#( |$)' "$0" | sed -E 's/^# ?//'
|
||||
exit 0
|
||||
}
|
||||
|
||||
install_deps() {
|
||||
C "Installing dependencies"
|
||||
@ -55,7 +61,8 @@ main() {
|
||||
read -r -p "dufs vault URL [https://kuhy-cloud.duckdns.org/Keepass/Passwords.kdbx]: " url
|
||||
url="${url:-https://kuhy-cloud.duckdns.org/Keepass/Passwords.kdbx}"
|
||||
read -r -p "dufs web username: " user
|
||||
read -r -s -p "dufs web password (server credential — cached in keyring): " dpass; echo
|
||||
read -r -s -p "dufs web password (server credential — cached in keyring): " dpass
|
||||
echo
|
||||
[[ -n "$user" && -n "$dpass" ]] || die "dufs username/password required"
|
||||
printf '%s' "$dpass" | secret-tool store --label='dufs server password (keepass-sync)' \
|
||||
service "$KEYRING_SERVICE" key dufs
|
||||
@ -86,8 +93,8 @@ main() {
|
||||
# --- retire the old one-way mirror unit (dufs host only) ----------------
|
||||
# is-enabled/is-active are pipe-free and work as non-root (unlike
|
||||
# `list-unit-files | grep -q`, which misfires under set -o pipefail).
|
||||
if systemctl is-enabled keepass-cloud-sync.path >/dev/null 2>&1 \
|
||||
|| systemctl is-active keepass-cloud-sync.path >/dev/null 2>&1; then
|
||||
if systemctl is-enabled keepass-cloud-sync.path >/dev/null 2>&1 ||
|
||||
systemctl is-active keepass-cloud-sync.path >/dev/null 2>&1; then
|
||||
C "Retiring the old one-way keepass-cloud-sync units"
|
||||
sudo systemctl disable --now keepass-cloud-sync.path keepass-cloud-sync.service 2>/dev/null || true
|
||||
OK "old one-way sync retired"
|
||||
|
||||
@ -43,14 +43,14 @@ readonly WEBSITE_DOMAIN="kuhy.duckdns.org"
|
||||
readonly GITEA_DOMAIN="gitea.kuhy.duckdns.org"
|
||||
readonly WEBSITE_PORT="8088"
|
||||
# React/TS source lives in its own standalone repo (github.com/kuhyx/personal-website),
|
||||
# cloned at ~/personal-website; the runtime data dir holds the generated compose +
|
||||
# cloned at ~/src/personal-website; the runtime data dir holds the generated compose +
|
||||
# inner Caddyfile.
|
||||
readonly WEBSITE_SRC="${HOME}/personal-website"
|
||||
readonly WEBSITE_DATA_DIR="${HOME}/personal-website-serve"
|
||||
readonly WEBSITE_SRC="${HOME}/src/personal-website"
|
||||
readonly WEBSITE_DATA_DIR="${HOME}/services/personal-website-serve"
|
||||
readonly WEBSITE_COMPOSE="${WEBSITE_DATA_DIR}/docker-compose.yml"
|
||||
readonly WEBSITE_INNER_CADDY="${WEBSITE_DATA_DIR}/Caddyfile"
|
||||
# Shared edge owned by setup_gitea.sh.
|
||||
readonly GITEA_DATA_DIR="${HOME}/gitea"
|
||||
readonly GITEA_DATA_DIR="${HOME}/services/gitea"
|
||||
readonly CADDYFILE="${GITEA_DATA_DIR}/Caddyfile"
|
||||
readonly SITES_DIR="${GITEA_DATA_DIR}/sites"
|
||||
readonly WEBSITE_SNIPPET="${SITES_DIR}/website.caddy"
|
||||
@ -60,8 +60,8 @@ readonly GITEA_SCRIPT="${SCRIPT_DIR}/setup_gitea.sh"
|
||||
readonly WG_SCRIPT="${SCRIPT_DIR}/setup_wireguard_ssh.sh"
|
||||
# Canonical CV data (rendered by the CV repo's build_cv.py); copied into the
|
||||
# website build. A committed copy in the repo keeps the build standalone.
|
||||
readonly CV_JSON_SRC="${HOME}/CV/generic/cv.json"
|
||||
readonly CV_PDF_SRC="${HOME}/CV/generic/cv-en.pdf"
|
||||
readonly CV_JSON_SRC="${HOME}/src/CV/generic/cv.json"
|
||||
readonly CV_PDF_SRC="${HOME}/src/CV/generic/cv-en.pdf"
|
||||
|
||||
die() {
|
||||
log_error "$1"
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
#
|
||||
# What it does (idempotent — safe to re-run):
|
||||
# - Installs runtime deps.
|
||||
# - Writes ~/searxng/{docker-compose.yml,core-config/settings.yml}.
|
||||
# - Writes ~/services/searxng/{docker-compose.yml,core-config/settings.yml}.
|
||||
# - Runs valkey (limiter backend) on 127.0.0.1:6379.
|
||||
# - Runs searxng on 127.0.0.1:8090, both with network_mode: host.
|
||||
# - Adds a reverse-proxy + security-header snippet to the shared edge.
|
||||
@ -53,13 +53,13 @@ readonly VALKEY_PORT="6379"
|
||||
# searxng/searxng:latest as of 2026-08-09 == release 2026.8.4. See the comment
|
||||
# on the image: line in the compose heredoc for why this is pinned.
|
||||
readonly SEARX_IMAGE="searxng/searxng@sha256:f4c8e59de166ed71f6380c0847c312ca51f0d41996e31d0559163b6b09ecde52"
|
||||
readonly SEARX_DATA_DIR="${HOME}/searxng"
|
||||
readonly SEARX_DATA_DIR="${HOME}/services/searxng"
|
||||
readonly SEARX_COMPOSE="${SEARX_DATA_DIR}/docker-compose.yml"
|
||||
readonly SEARX_CONFIG_DIR="${SEARX_DATA_DIR}/core-config"
|
||||
readonly SEARX_SETTINGS="${SEARX_CONFIG_DIR}/settings.yml"
|
||||
readonly SEARX_LIMITER="${SEARX_CONFIG_DIR}/limiter.toml"
|
||||
# Shared edge owned by setup_gitea.sh.
|
||||
readonly GITEA_DATA_DIR="${HOME}/gitea"
|
||||
readonly GITEA_DATA_DIR="${HOME}/services/gitea"
|
||||
readonly SITES_DIR="${GITEA_DATA_DIR}/sites"
|
||||
readonly SEARX_SNIPPET="${SITES_DIR}/searx.caddy"
|
||||
readonly CADDY_CONTAINER="gitea-caddy"
|
||||
@ -161,7 +161,6 @@ source "$SCRIPT_DIR/lib/searxng_expose.sh"
|
||||
# shellcheck source=lib/searxng_status.sh
|
||||
source "$SCRIPT_DIR/lib/searxng_status.sh"
|
||||
|
||||
|
||||
main() {
|
||||
local cmd="${1:-setup}"
|
||||
case "$cmd" in
|
||||
|
||||
@ -29,13 +29,13 @@ set -euo pipefail
|
||||
# SYNCYOMI_IMAGE=ghcr.io/syncyomi/syncyomi:v1.1.11 ./setup_syncyomi.sh
|
||||
# Any value containing a "/" is treated as a registry image and pulled as-is.
|
||||
readonly SYNCYOMI_IMAGE="${SYNCYOMI_IMAGE:-syncyomi-kuhy:local}"
|
||||
readonly SYNCYOMI_SRC="${SYNCYOMI_SRC:-${HOME}/syncyomi-src}"
|
||||
readonly SYNCYOMI_SRC="${SYNCYOMI_SRC:-${HOME}/vendor/syncyomi-src}"
|
||||
readonly SYNCYOMI_FORK_URL="https://github.com/kuhyx/syncyomi.git"
|
||||
readonly SYNCYOMI_PORT=8282
|
||||
readonly SYNCYOMI_LOCAL="http://127.0.0.1:${SYNCYOMI_PORT}"
|
||||
|
||||
# --- Data dir + secrets (outside the git repo, like ~/gitea) -----------------
|
||||
readonly DATA_DIR="${HOME}/syncyomi"
|
||||
# --- Data dir + secrets (outside the git repo, like ~/services/gitea) -----------------
|
||||
readonly DATA_DIR="${HOME}/services/syncyomi"
|
||||
readonly COMPOSE_FILE="${DATA_DIR}/docker-compose.yml"
|
||||
readonly CONFIG_TOML="${DATA_DIR}/config/config.toml"
|
||||
readonly CONF_FILE="${DATA_DIR}/.syncyomi.conf"
|
||||
@ -43,8 +43,8 @@ readonly TOKEN_FILE="${DATA_DIR}/.api_token"
|
||||
|
||||
# --- Existing host infrastructure (from setup_gitea.sh / install_joplin.sh) --
|
||||
readonly CADDY_CONTAINER="gitea-caddy"
|
||||
readonly CADDYFILE="${HOME}/gitea/Caddyfile"
|
||||
readonly CADDY_SITES_DIR="${HOME}/gitea/sites"
|
||||
readonly CADDYFILE="${HOME}/services/gitea/Caddyfile"
|
||||
readonly CADDY_SITES_DIR="${HOME}/services/gitea/sites"
|
||||
readonly CADDYFILE_IN_CONTAINER="/etc/caddy/Caddyfile"
|
||||
readonly DUCKDNS_UPDATER="${HOME}/.joplin-server/duckdns-update.sh"
|
||||
|
||||
@ -121,7 +121,7 @@ load_config() {
|
||||
|
||||
save_config() {
|
||||
# 0600 — holds the plaintext SyncYomi password so re-runs stay
|
||||
# non-interactive. Lives under ~/syncyomi (outside the git repo).
|
||||
# non-interactive. Lives under ~/services/syncyomi (outside the git repo).
|
||||
umask 077
|
||||
{
|
||||
printf 'SYNCYOMI_USER=%q\n' "${SYNCYOMI_USER}"
|
||||
|
||||
@ -21,9 +21,9 @@ readonly REPO_ROOT
|
||||
readonly UNIT_DIR="${HOME}/.config/systemd/user"
|
||||
readonly SERVICE_NAME="syncyomi-guard.service"
|
||||
readonly TIMER_NAME="syncyomi-guard.timer"
|
||||
readonly SNAPSHOT_DIR="${HOME}/syncyomi/snapshots"
|
||||
readonly SNAPSHOT_DIR="${HOME}/services/syncyomi/snapshots"
|
||||
readonly STATE_DIR="${HOME}/.local/share/syncyomi_guard"
|
||||
readonly DB_PATH="${HOME}/syncyomi/config/syncyomi.db"
|
||||
readonly DB_PATH="${HOME}/services/syncyomi/config/syncyomi.db"
|
||||
|
||||
log_info() { echo "[*] $*"; }
|
||||
log_ok() { echo "[+] $*"; }
|
||||
|
||||
@ -8,6 +8,6 @@ Description=Mirror KeePass vault into dufs cloud folder
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=__USER__
|
||||
# Only the vault file — not the whole ~/Keepass dir (keeps local .backup_* vaults
|
||||
# Only the vault file — not the whole ~/data/Keepass dir (keeps local .backup_* vaults
|
||||
# off the public server). No --delete: the cloud folder may hold other files.
|
||||
ExecStart=/usr/bin/rsync -a __HOME__/Keepass/Passwords.kdbx __CLOUD_ROOT__/Keepass/
|
||||
|
||||
@ -11,8 +11,8 @@ set -euo pipefail
|
||||
SERVICE_NAME="media-organizer"
|
||||
SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
|
||||
|
||||
DEFAULT_ORGANIZE_SCRIPT="/home/kuhy/testsAndMisc/linux_configuration/utils/organize_downloads.sh"
|
||||
LEGACY_ORGANIZE_SCRIPT="/home/kuhy/linux-configuration/utils/organize_downloads.sh"
|
||||
DEFAULT_ORGANIZE_SCRIPT="/home/kuhy/src/testsAndMisc/linux_configuration/utils/organize_downloads.sh"
|
||||
LEGACY_ORGANIZE_SCRIPT="/home/kuhy/src/testsAndMisc/linux_configuration/utils/organize_downloads.sh"
|
||||
if [[ -f $DEFAULT_ORGANIZE_SCRIPT ]]; then
|
||||
ORGANIZE_SCRIPT="$DEFAULT_ORGANIZE_SCRIPT"
|
||||
elif [[ -f $LEGACY_ORGANIZE_SCRIPT ]]; then
|
||||
|
||||
@ -65,8 +65,8 @@ fix_media_organizer() {
|
||||
# Find the organize_downloads.sh script
|
||||
local script_path=""
|
||||
local candidates=(
|
||||
"${ORGANIZE_SCRIPT_CANDIDATES:-/home/kuhy/testsAndMisc/linux_configuration/utils/organize_downloads.sh}"
|
||||
"/home/kuhy/linux-configuration/utils/organize_downloads.sh"
|
||||
"${ORGANIZE_SCRIPT_CANDIDATES:-/home/kuhy/src/testsAndMisc/linux_configuration/utils/organize_downloads.sh}"
|
||||
"/home/kuhy/src/testsAndMisc/linux_configuration/utils/organize_downloads.sh"
|
||||
)
|
||||
for candidate in "${candidates[@]}"; do
|
||||
if [[ -f $candidate ]]; then
|
||||
|
||||
@ -71,7 +71,7 @@ _t_lacks "$(_t_calls)" "systemctl disable" \
|
||||
# candidate paths are absolute; the first is overridden into the tmpdir and
|
||||
# the second is a real home path that does not exist on this machine.
|
||||
arch_reset
|
||||
if [[ -f /home/kuhy/linux-configuration/utils/organize_downloads.sh ]]; then
|
||||
if [[ -f /home/kuhy/src/testsAndMisc/linux_configuration/utils/organize_downloads.sh ]]; then
|
||||
_t_pass "fix_media_organizer: SKIP not-found case (the second candidate exists here)"
|
||||
else
|
||||
out="$(fix_media_organizer 2>&1)"
|
||||
|
||||
@ -13,8 +13,8 @@
|
||||
set -e
|
||||
|
||||
[ "${GPU_VENDOR}" = "amd" ] || {
|
||||
echo "AMD installer invoked but GPU_VENDOR=${GPU_VENDOR}"
|
||||
exit 0
|
||||
echo "AMD installer invoked but GPU_VENDOR=${GPU_VENDOR}"
|
||||
exit 0
|
||||
}
|
||||
|
||||
AMD_INSTALL_XF86=${AMD_INSTALL_XF86:-0}
|
||||
@ -32,9 +32,9 @@ warn() { echo "[amd][warn] $*" >&2; }
|
||||
|
||||
# Detect multilib enabled
|
||||
if grep -q '^\[multilib\]' /etc/pacman.conf; then
|
||||
MULTILIB_ENABLED=1
|
||||
MULTILIB_ENABLED=1
|
||||
else
|
||||
MULTILIB_ENABLED=0
|
||||
MULTILIB_ENABLED=0
|
||||
fi
|
||||
|
||||
# Basic packages
|
||||
@ -58,49 +58,49 @@ LIB32_AMDVLK_PKG="lib32-amdvlk"
|
||||
|
||||
# Simple AUR builder (reused from NVIDIA script style)
|
||||
_build_aur_pkg() {
|
||||
local pkg="$1"
|
||||
local url="https://aur.archlinux.org/${pkg}.git"
|
||||
mkdir -p "$HOME/aur"
|
||||
cd "$HOME/aur"
|
||||
if [ ! -d "$pkg" ]; then git clone "$url"; else (cd "$pkg" && git fetch -q --all && git reset -q --hard origin/HEAD || git pull --ff-only || true); fi
|
||||
cd "$pkg"
|
||||
rm -f -- *.pkg.tar.* 2> /dev/null || true
|
||||
yes | makepkg -s -c -C --noconfirm --needed
|
||||
local built=(*.pkg.tar.zst)
|
||||
yes | sudo pacman -U --noconfirm "${built[@]}"
|
||||
local pkg="$1"
|
||||
local url="https://aur.archlinux.org/${pkg}.git"
|
||||
mkdir -p "$HOME/sdk/aur"
|
||||
cd "$HOME/sdk/aur"
|
||||
if [ ! -d "$pkg" ]; then git clone "$url"; else (cd "$pkg" && git fetch -q --all && git reset -q --hard origin/HEAD || git pull --ff-only || true); fi
|
||||
cd "$pkg"
|
||||
rm -f -- *.pkg.tar.* 2>/dev/null || true
|
||||
yes | makepkg -s -c -C --noconfirm --needed
|
||||
local built=(*.pkg.tar.zst)
|
||||
yes | sudo pacman -U --noconfirm "${built[@]}"
|
||||
}
|
||||
|
||||
_install_repo_or_aur() {
|
||||
local pkg="$1"
|
||||
if pacman -Si "$pkg" > /dev/null 2>&1; then
|
||||
if pacman -Qi "$pkg" > /dev/null 2>&1; then
|
||||
vlog "$pkg already installed"
|
||||
else
|
||||
yes | sudo pacman -Sy --noconfirm "$pkg"
|
||||
fi
|
||||
else
|
||||
info "Building AUR package: $pkg"
|
||||
_build_aur_pkg "$pkg"
|
||||
fi
|
||||
local pkg="$1"
|
||||
if pacman -Si "$pkg" >/dev/null 2>&1; then
|
||||
if pacman -Qi "$pkg" >/dev/null 2>&1; then
|
||||
vlog "$pkg already installed"
|
||||
else
|
||||
yes | sudo pacman -Sy --noconfirm "$pkg"
|
||||
fi
|
||||
else
|
||||
info "Building AUR package: $pkg"
|
||||
_build_aur_pkg "$pkg"
|
||||
fi
|
||||
}
|
||||
|
||||
info "Installing AMD GPU stack"
|
||||
for p in "${BASE_PKGS[@]}" "$VULKAN_PKG"; do _install_repo_or_aur "$p"; done
|
||||
|
||||
if [ "$AMD_INSTALL_XF86" = 1 ]; then
|
||||
_install_repo_or_aur "$XF86_PKG"
|
||||
_install_repo_or_aur "$XF86_PKG"
|
||||
fi
|
||||
|
||||
# AMDVLK optional (install after vulkan-radeon if requested)
|
||||
if [ "$AMD_INSTALL_AMDVLK" = 1 ]; then
|
||||
_install_repo_or_aur "$AMDVLK_PKG"
|
||||
_install_repo_or_aur "$AMDVLK_PKG"
|
||||
fi
|
||||
|
||||
if [ $MULTILIB_ENABLED = 1 ] || [ "$AMD_INSTALL_LIB32" = 1 ]; then
|
||||
for p in "${LIB32_BASE[@]}" "$LIB32_VULKAN_PKG"; do _install_repo_or_aur "$p"; done
|
||||
if [ "$AMD_INSTALL_AMDVLK" = 1 ]; then _install_repo_or_aur "$LIB32_AMDVLK_PKG"; fi
|
||||
for p in "${LIB32_BASE[@]}" "$LIB32_VULKAN_PKG"; do _install_repo_or_aur "$p"; done
|
||||
if [ "$AMD_INSTALL_AMDVLK" = 1 ]; then _install_repo_or_aur "$LIB32_AMDVLK_PKG"; fi
|
||||
else
|
||||
vlog "Skipping 32-bit packages (multilib disabled)"
|
||||
vlog "Skipping 32-bit packages (multilib disabled)"
|
||||
fi
|
||||
|
||||
# Detect SI / CIK codename presence for optional amdgpu enablement
|
||||
@ -113,41 +113,41 @@ for n in "${SI_NAMES[@]}"; do echo "$GPU_LINES" | grep -q "$n" && IS_SI=1 && bre
|
||||
for n in "${CIK_NAMES[@]}"; do echo "$GPU_LINES" | grep -q "$n" && IS_CIK=1 && break; done
|
||||
|
||||
if [ "$AMD_ENABLE_SI_CIK" = "1" ] || { [ "$AMD_ENABLE_SI_CIK" = "auto" ] && { [ $IS_SI = 1 ] || [ $IS_CIK = 1 ]; }; }; then
|
||||
info "Configuring amdgpu for SI/CIK (IS_SI=$IS_SI IS_CIK=$IS_CIK)"
|
||||
TMP_CONF=$(mktemp)
|
||||
printf 'options amdgpu si_support=1\noptions amdgpu cik_support=1\n' > "$TMP_CONF"
|
||||
printf 'options radeon si_support=0\noptions radeon cik_support=0\n' >> "$TMP_CONF"
|
||||
sudo mkdir -p /etc/modprobe.d
|
||||
sudo cp "$TMP_CONF" /etc/modprobe.d/10-amdgpu-si-cik.conf
|
||||
rm -f "$TMP_CONF"
|
||||
# Ensure amdgpu early in MODULES
|
||||
if [ -f /etc/mkinitcpio.conf ]; then
|
||||
if ! grep -q '^MODULES=.*amdgpu' /etc/mkinitcpio.conf; then
|
||||
sudo sed -i 's/^MODULES=\(.*\)/MODULES=(amdgpu radeon)/' /etc/mkinitcpio.conf || true
|
||||
fi
|
||||
if ! grep -q 'modconf' /etc/mkinitcpio.conf; then
|
||||
warn "modconf hook not found in mkinitcpio.conf (needed for module options)"
|
||||
fi
|
||||
if [ "$AMD_SKIP_INITRAMFS" != 1 ]; then
|
||||
info "Regenerating initramfs (mkinitcpio -P)"
|
||||
sudo mkinitcpio -P || warn "mkinitcpio failed; review manually"
|
||||
else
|
||||
info "Skipping initramfs regeneration per AMD_SKIP_INITRAMFS=1"
|
||||
fi
|
||||
else
|
||||
warn "/etc/mkinitcpio.conf not found; skipping MODULES update"
|
||||
fi
|
||||
info "Configuring amdgpu for SI/CIK (IS_SI=$IS_SI IS_CIK=$IS_CIK)"
|
||||
TMP_CONF=$(mktemp)
|
||||
printf 'options amdgpu si_support=1\noptions amdgpu cik_support=1\n' >"$TMP_CONF"
|
||||
printf 'options radeon si_support=0\noptions radeon cik_support=0\n' >>"$TMP_CONF"
|
||||
sudo mkdir -p /etc/modprobe.d
|
||||
sudo cp "$TMP_CONF" /etc/modprobe.d/10-amdgpu-si-cik.conf
|
||||
rm -f "$TMP_CONF"
|
||||
# Ensure amdgpu early in MODULES
|
||||
if [ -f /etc/mkinitcpio.conf ]; then
|
||||
if ! grep -q '^MODULES=.*amdgpu' /etc/mkinitcpio.conf; then
|
||||
sudo sed -i 's/^MODULES=\(.*\)/MODULES=(amdgpu radeon)/' /etc/mkinitcpio.conf || true
|
||||
fi
|
||||
if ! grep -q 'modconf' /etc/mkinitcpio.conf; then
|
||||
warn "modconf hook not found in mkinitcpio.conf (needed for module options)"
|
||||
fi
|
||||
if [ "$AMD_SKIP_INITRAMFS" != 1 ]; then
|
||||
info "Regenerating initramfs (mkinitcpio -P)"
|
||||
sudo mkinitcpio -P || warn "mkinitcpio failed; review manually"
|
||||
else
|
||||
info "Skipping initramfs regeneration per AMD_SKIP_INITRAMFS=1"
|
||||
fi
|
||||
else
|
||||
warn "/etc/mkinitcpio.conf not found; skipping MODULES update"
|
||||
fi
|
||||
else
|
||||
vlog "SI/CIK enablement not required (AMD_ENABLE_SI_CIK=$AMD_ENABLE_SI_CIK IS_SI=$IS_SI IS_CIK=$IS_CIK)"
|
||||
vlog "SI/CIK enablement not required (AMD_ENABLE_SI_CIK=$AMD_ENABLE_SI_CIK IS_SI=$IS_SI IS_CIK=$IS_CIK)"
|
||||
fi
|
||||
|
||||
# Check active kernel driver
|
||||
KDRV=$(lspci -k -d ::0300 2> /dev/null | awk '/Kernel driver in use:/ {print $5; exit}')
|
||||
KDRV=$(lspci -k -d ::0300 2>/dev/null | awk '/Kernel driver in use:/ {print $5; exit}')
|
||||
[ -z "$KDRV" ] && KDRV=$(lsmod | grep -E 'amdgpu|radeon' | head -n1 | awk '{print $1}')
|
||||
info "Kernel driver in use: ${KDRV:-unknown}"
|
||||
|
||||
if [ "$KDRV" = "radeon" ] && { [ $IS_SI = 1 ] || [ $IS_CIK = 1 ]; }; then
|
||||
warn "radeon driver still active for SI/CIK; reboot may be required to switch to amdgpu"
|
||||
warn "radeon driver still active for SI/CIK; reboot may be required to switch to amdgpu"
|
||||
fi
|
||||
|
||||
export AMD_STACK_DONE=1
|
||||
|
||||
@ -5,96 +5,96 @@
|
||||
set -e
|
||||
|
||||
[ "$GPU_VENDOR" = "nvidia" ] || {
|
||||
echo "NVIDIA installer invoked but GPU_VENDOR=$GPU_VENDOR"
|
||||
exit 0
|
||||
echo "NVIDIA installer invoked but GPU_VENDOR=$GPU_VENDOR"
|
||||
exit 0
|
||||
}
|
||||
|
||||
_build_aur_pkg() {
|
||||
local pkg="$1"
|
||||
local repo_url="https://aur.archlinux.org/${pkg}.git"
|
||||
mkdir -p "$HOME/aur"
|
||||
cd "$HOME/aur"
|
||||
if [ ! -d "$pkg" ]; then git clone "$repo_url"; else (cd "$pkg" && git fetch -q --all && git reset -q --hard origin/HEAD || git pull --ff-only || true); fi
|
||||
cd "$pkg"
|
||||
rm -f -- *.pkg.tar.* 2> /dev/null || true
|
||||
yes | makepkg -s -c -C --noconfirm --needed || return 1
|
||||
local built=(*.pkg.tar.zst)
|
||||
yes | sudo pacman -U --noconfirm "${built[@]}"
|
||||
local pkg="$1"
|
||||
local repo_url="https://aur.archlinux.org/${pkg}.git"
|
||||
mkdir -p "$HOME/sdk/aur"
|
||||
cd "$HOME/sdk/aur"
|
||||
if [ ! -d "$pkg" ]; then git clone "$repo_url"; else (cd "$pkg" && git fetch -q --all && git reset -q --hard origin/HEAD || git pull --ff-only || true); fi
|
||||
cd "$pkg"
|
||||
rm -f -- *.pkg.tar.* 2>/dev/null || true
|
||||
yes | makepkg -s -c -C --noconfirm --needed || return 1
|
||||
local built=(*.pkg.tar.zst)
|
||||
yes | sudo pacman -U --noconfirm "${built[@]}"
|
||||
}
|
||||
|
||||
_choose_nvidia_pkg() {
|
||||
local have_linux have_linux_lts multiple_kernels driver_pkg prefer_open detect_out legacy_detected=0
|
||||
prefer_open=${NVIDIA_PREFER_OPEN:-1}
|
||||
pacman -Qq | grep -qx linux && have_linux=1 || have_linux=0
|
||||
pacman -Qq | grep -qx linux-lts && have_linux_lts=1 || have_linux_lts=0
|
||||
if [ $((have_linux + have_linux_lts)) -gt 1 ]; then multiple_kernels=1; else multiple_kernels=0; fi
|
||||
local have_linux have_linux_lts multiple_kernels driver_pkg prefer_open detect_out legacy_detected=0
|
||||
prefer_open=${NVIDIA_PREFER_OPEN:-1}
|
||||
pacman -Qq | grep -qx linux && have_linux=1 || have_linux=0
|
||||
pacman -Qq | grep -qx linux-lts && have_linux_lts=1 || have_linux_lts=0
|
||||
if [ $((have_linux + have_linux_lts)) -gt 1 ]; then multiple_kernels=1; else multiple_kernels=0; fi
|
||||
|
||||
# Optionally skip attempting to install nvidia-detect (some minimal repo setups don't have it yet)
|
||||
if [ -z "${NVIDIA_SKIP_DETECT:-}" ] && ! command -v nvidia-detect > /dev/null 2>&1; then
|
||||
if pacman -Si nvidia-detect > /dev/null 2>&1; then
|
||||
echo "Attempting to install helper utility: nvidia-detect" >&2
|
||||
# Use --needed to avoid forcing refresh (& avoid partial upgrade semantics with -Sy)
|
||||
yes | sudo pacman -S --needed --noconfirm nvidia-detect || echo "nvidia-detect install failed (continuing with heuristic)" >&2
|
||||
else
|
||||
echo "nvidia-detect not present in enabled repos; using heuristic selection." >&2
|
||||
fi
|
||||
fi
|
||||
# Optionally skip attempting to install nvidia-detect (some minimal repo setups don't have it yet)
|
||||
if [ -z "${NVIDIA_SKIP_DETECT:-}" ] && ! command -v nvidia-detect >/dev/null 2>&1; then
|
||||
if pacman -Si nvidia-detect >/dev/null 2>&1; then
|
||||
echo "Attempting to install helper utility: nvidia-detect" >&2
|
||||
# Use --needed to avoid forcing refresh (& avoid partial upgrade semantics with -Sy)
|
||||
yes | sudo pacman -S --needed --noconfirm nvidia-detect || echo "nvidia-detect install failed (continuing with heuristic)" >&2
|
||||
else
|
||||
echo "nvidia-detect not present in enabled repos; using heuristic selection." >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
if command -v nvidia-detect > /dev/null 2>&1; then
|
||||
detect_out="$(nvidia-detect 2> /dev/null || true)"
|
||||
fi
|
||||
if command -v nvidia-detect >/dev/null 2>&1; then
|
||||
detect_out="$(nvidia-detect 2>/dev/null || true)"
|
||||
fi
|
||||
|
||||
if [ -n "$detect_out" ]; then
|
||||
if echo "$detect_out" | grep -q '470'; then
|
||||
driver_pkg='nvidia-470xx-dkms'
|
||||
legacy_detected=1
|
||||
fi
|
||||
if echo "$detect_out" | grep -q '390'; then
|
||||
driver_pkg='nvidia-390xx-dkms'
|
||||
legacy_detected=1
|
||||
fi
|
||||
if echo "$detect_out" | grep -q '340'; then
|
||||
driver_pkg='nvidia-340xx-dkms'
|
||||
legacy_detected=1
|
||||
fi
|
||||
fi
|
||||
if [ -n "$detect_out" ]; then
|
||||
if echo "$detect_out" | grep -q '470'; then
|
||||
driver_pkg='nvidia-470xx-dkms'
|
||||
legacy_detected=1
|
||||
fi
|
||||
if echo "$detect_out" | grep -q '390'; then
|
||||
driver_pkg='nvidia-390xx-dkms'
|
||||
legacy_detected=1
|
||||
fi
|
||||
if echo "$detect_out" | grep -q '340'; then
|
||||
driver_pkg='nvidia-340xx-dkms'
|
||||
legacy_detected=1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$legacy_detected" = 0 ]; then
|
||||
# Heuristic modern driver selection
|
||||
if [ "$multiple_kernels" = 1 ]; then
|
||||
if [ "$prefer_open" = 1 ] && pacman -Si nvidia-open-dkms > /dev/null 2>&1; then driver_pkg='nvidia-open-dkms'; else driver_pkg='nvidia-dkms'; fi
|
||||
else
|
||||
if [ "$have_linux_lts" = 1 ] && [ "$have_linux" = 0 ]; then
|
||||
if [ "$prefer_open" = 1 ] && pacman -Si nvidia-open-lts > /dev/null 2>&1; then driver_pkg='nvidia-open-lts'; else driver_pkg='nvidia-lts'; fi
|
||||
else
|
||||
if [ "$prefer_open" = 1 ] && pacman -Si nvidia-open > /dev/null 2>&1; then driver_pkg='nvidia-open'; else driver_pkg='nvidia'; fi
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "Legacy NVIDIA generation detected via nvidia-detect output; choosing $driver_pkg" >&2
|
||||
fi
|
||||
if [ "$legacy_detected" = 0 ]; then
|
||||
# Heuristic modern driver selection
|
||||
if [ "$multiple_kernels" = 1 ]; then
|
||||
if [ "$prefer_open" = 1 ] && pacman -Si nvidia-open-dkms >/dev/null 2>&1; then driver_pkg='nvidia-open-dkms'; else driver_pkg='nvidia-dkms'; fi
|
||||
else
|
||||
if [ "$have_linux_lts" = 1 ] && [ "$have_linux" = 0 ]; then
|
||||
if [ "$prefer_open" = 1 ] && pacman -Si nvidia-open-lts >/dev/null 2>&1; then driver_pkg='nvidia-open-lts'; else driver_pkg='nvidia-lts'; fi
|
||||
else
|
||||
if [ "$prefer_open" = 1 ] && pacman -Si nvidia-open >/dev/null 2>&1; then driver_pkg='nvidia-open'; else driver_pkg='nvidia'; fi
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo "Legacy NVIDIA generation detected via nvidia-detect output; choosing $driver_pkg" >&2
|
||||
fi
|
||||
|
||||
echo "$driver_pkg"
|
||||
echo "$driver_pkg"
|
||||
}
|
||||
|
||||
_remove_conflicting_nvidia_pkgs() {
|
||||
local keep="$1"
|
||||
local candidates=(nvidia nvidia-lts nvidia-dkms nvidia-open nvidia-open-lts nvidia-open-dkms nvidia-470xx-dkms nvidia-390xx-dkms nvidia-340xx-dkms)
|
||||
local to_remove=()
|
||||
for p in "${candidates[@]}"; do
|
||||
if pacman -Qi "$p" > /dev/null 2>&1 && [ "$p" != "$keep" ]; then to_remove+=("$p"); fi
|
||||
done
|
||||
if [ ${#to_remove[@]} -gt 0 ]; then yes | sudo pacman -Rns --noconfirm "${to_remove[@]}" || true; fi
|
||||
local keep="$1"
|
||||
local candidates=(nvidia nvidia-lts nvidia-dkms nvidia-open nvidia-open-lts nvidia-open-dkms nvidia-470xx-dkms nvidia-390xx-dkms nvidia-340xx-dkms)
|
||||
local to_remove=()
|
||||
for p in "${candidates[@]}"; do
|
||||
if pacman -Qi "$p" >/dev/null 2>&1 && [ "$p" != "$keep" ]; then to_remove+=("$p"); fi
|
||||
done
|
||||
if [ ${#to_remove[@]} -gt 0 ]; then yes | sudo pacman -Rns --noconfirm "${to_remove[@]}" || true; fi
|
||||
}
|
||||
|
||||
_install_nvidia_stack() {
|
||||
local driver_pkg="$1"
|
||||
if [[ $driver_pkg == nvidia-*xx-dkms ]]; then _build_aur_pkg "$driver_pkg"; else yes | sudo pacman -Sy --noconfirm "$driver_pkg"; fi
|
||||
local utils_pkg="nvidia-utils" utils32_pkg="lib32-nvidia-utils"
|
||||
if ! pacman -Qi "$utils_pkg" > /dev/null 2>&1; then yes | sudo pacman -Sy --noconfirm "$utils_pkg"; fi
|
||||
if grep -q '^\[multilib\]' /etc/pacman.conf; then
|
||||
if ! pacman -Qi "$utils32_pkg" > /dev/null 2>&1; then yes | sudo pacman -Sy --noconfirm "$utils32_pkg" || true; fi
|
||||
fi
|
||||
local driver_pkg="$1"
|
||||
if [[ $driver_pkg == nvidia-*xx-dkms ]]; then _build_aur_pkg "$driver_pkg"; else yes | sudo pacman -Sy --noconfirm "$driver_pkg"; fi
|
||||
local utils_pkg="nvidia-utils" utils32_pkg="lib32-nvidia-utils"
|
||||
if ! pacman -Qi "$utils_pkg" >/dev/null 2>&1; then yes | sudo pacman -Sy --noconfirm "$utils_pkg"; fi
|
||||
if grep -q '^\[multilib\]' /etc/pacman.conf; then
|
||||
if ! pacman -Qi "$utils32_pkg" >/dev/null 2>&1; then yes | sudo pacman -Sy --noconfirm "$utils32_pkg" || true; fi
|
||||
fi
|
||||
}
|
||||
|
||||
echo "Detected NVIDIA GPU. Selecting driver..."
|
||||
|
||||
@ -6,116 +6,116 @@
|
||||
|
||||
# Function to play a sound on error
|
||||
play_error_sound() {
|
||||
#pactl set-sink-volume @DEFAULT_SINK@ +50%
|
||||
for _ in 1 2 3; do
|
||||
paplay /usr/share/sounds/freedesktop/stereo/dialog-error.oga
|
||||
done
|
||||
#pactl set-sink-volume @DEFAULT_SINK@ -50%
|
||||
#pactl set-sink-volume @DEFAULT_SINK@ +50%
|
||||
for _ in 1 2 3; do
|
||||
paplay /usr/share/sounds/freedesktop/stereo/dialog-error.oga
|
||||
done
|
||||
#pactl set-sink-volume @DEFAULT_SINK@ -50%
|
||||
}
|
||||
|
||||
install_from_aur() {
|
||||
local repo_url pkg_name repo_dir
|
||||
repo_url="$1"
|
||||
pkg_name="$2"
|
||||
local repo_url pkg_name repo_dir
|
||||
repo_url="$1"
|
||||
pkg_name="$2"
|
||||
|
||||
mkdir -p "$HOME/aur"
|
||||
cd "$HOME/aur" || return 1
|
||||
repo_dir="$(basename "$repo_url" .git)"
|
||||
mkdir -p "$HOME/sdk/aur"
|
||||
cd "$HOME/sdk/aur" || return 1
|
||||
repo_dir="$(basename "$repo_url" .git)"
|
||||
|
||||
if [ ! -d "$repo_dir" ]; then
|
||||
git clone "$repo_url"
|
||||
else
|
||||
echo "Repository $repo_dir already cloned; updating"
|
||||
(cd "$repo_dir" && git fetch --all -q && git reset --hard origin/HEAD -q || git pull --ff-only || true)
|
||||
fi
|
||||
cd "$repo_dir" || return 1
|
||||
if [ ! -d "$repo_dir" ]; then
|
||||
git clone "$repo_url"
|
||||
else
|
||||
echo "Repository $repo_dir already cloned; updating"
|
||||
(cd "$repo_dir" && git fetch --all -q && git reset --hard origin/HEAD -q || git pull --ff-only || true)
|
||||
fi
|
||||
cd "$repo_dir" || return 1
|
||||
|
||||
if pacman -Qi "$pkg_name" > /dev/null 2>&1; then
|
||||
echo "$pkg_name is already installed"
|
||||
return 0
|
||||
fi
|
||||
if pacman -Qi "$pkg_name" >/dev/null 2>&1; then
|
||||
echo "$pkg_name is already installed"
|
||||
return 0
|
||||
fi
|
||||
|
||||
echo "Cleaning old package artifacts to avoid duplicate -U targets"
|
||||
find . -maxdepth 1 -type f -name '*.pkg.tar.*' -delete 2> /dev/null || true
|
||||
echo "Cleaning old package artifacts to avoid duplicate -U targets"
|
||||
find . -maxdepth 1 -type f -name '*.pkg.tar.*' -delete 2>/dev/null || true
|
||||
|
||||
echo "Building $pkg_name (clean build)"
|
||||
# -c (clean up work dirs after) -C (clean build - remove src/ and pkg/ first)
|
||||
if ! yes | makepkg -s -c -C --noconfirm --nocheck --skipchecksums --skipinteg --skippgpcheck --needed; then
|
||||
echo "Build failed for $pkg_name" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "Building $pkg_name (clean build)"
|
||||
# -c (clean up work dirs after) -C (clean build - remove src/ and pkg/ first)
|
||||
if ! yes | makepkg -s -c -C --noconfirm --nocheck --skipchecksums --skipinteg --skippgpcheck --needed; then
|
||||
echo "Build failed for $pkg_name" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Collect only the freshly built packages (should now be only current version)
|
||||
mapfile -t built_pkgs < <(find . -maxdepth 1 -type f -name '*.pkg.tar.zst' -printf './%f\n')
|
||||
if [ ${#built_pkgs[@]} -eq 0 ]; then
|
||||
echo "No package files produced for $pkg_name" >&2
|
||||
return 1
|
||||
fi
|
||||
# Collect only the freshly built packages (should now be only current version)
|
||||
mapfile -t built_pkgs < <(find . -maxdepth 1 -type f -name '*.pkg.tar.zst' -printf './%f\n')
|
||||
if [ ${#built_pkgs[@]} -eq 0 ]; then
|
||||
echo "No package files produced for $pkg_name" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo "Installing built package(s): ${built_pkgs[*]}"
|
||||
if ! yes | sudo pacman -U --noconfirm "${built_pkgs[@]}"; then
|
||||
echo "Installation failed for $pkg_name" >&2
|
||||
return 1
|
||||
fi
|
||||
echo "Installing built package(s): ${built_pkgs[*]}"
|
||||
if ! yes | sudo pacman -U --noconfirm "${built_pkgs[@]}"; then
|
||||
echo "Installation failed for $pkg_name" >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Helper: try to install from AUR and log result to done.txt/failed.txt
|
||||
try_aur_install() {
|
||||
local repo_url="$1"
|
||||
local pkg_name="$2"
|
||||
if install_from_aur "$repo_url" "$pkg_name"; then
|
||||
echo "$pkg_name" >> done.txt
|
||||
else
|
||||
echo "$pkg_name" >> failed.txt
|
||||
fi
|
||||
local repo_url="$1"
|
||||
local pkg_name="$2"
|
||||
if install_from_aur "$repo_url" "$pkg_name"; then
|
||||
echo "$pkg_name" >>done.txt
|
||||
else
|
||||
echo "$pkg_name" >>failed.txt
|
||||
fi
|
||||
}
|
||||
|
||||
process_packages() {
|
||||
local file_path
|
||||
file_path="$1"
|
||||
: > failed.txt
|
||||
: > done.txt
|
||||
local file_path
|
||||
file_path="$1"
|
||||
: >failed.txt
|
||||
: >done.txt
|
||||
|
||||
while IFS= read -r pkg_name; do
|
||||
if [ -z "$pkg_name" ]; then
|
||||
continue
|
||||
fi
|
||||
while IFS= read -r pkg_name; do
|
||||
if [ -z "$pkg_name" ]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
local repo_url repo_dir
|
||||
repo_url="https://aur.archlinux.org/${pkg_name}-git.git"
|
||||
repo_dir="${pkg_name}-git"
|
||||
local repo_url repo_dir
|
||||
repo_url="https://aur.archlinux.org/${pkg_name}-git.git"
|
||||
repo_dir="${pkg_name}-git"
|
||||
|
||||
git clone "$repo_url"
|
||||
if [ -d "$repo_dir" ] && [ -z "$(ls -A "$repo_dir")" ]; then
|
||||
echo "Repository $repo_dir is empty, trying without -git suffix"
|
||||
repo_url="https://aur.archlinux.org/${pkg_name}.git"
|
||||
repo_dir="${pkg_name}"
|
||||
git clone "$repo_url"
|
||||
if [ -d "$repo_dir" ] && [ -z "$(ls -A "$repo_dir")" ]; then
|
||||
echo "Repository $repo_dir is empty, trying without -git suffix"
|
||||
repo_url="https://aur.archlinux.org/${pkg_name}.git"
|
||||
repo_dir="${pkg_name}"
|
||||
|
||||
git clone "$repo_url"
|
||||
if [ -d "$repo_dir" ] && [ -z "$(ls -A "$repo_dir")" ]; then
|
||||
echo "Repository $repo_dir is empty, trying to install with pacman"
|
||||
if sudo pacman -Sy --noconfirm "$pkg_name"; then
|
||||
echo "$pkg_name" >> done.txt
|
||||
else
|
||||
echo "$pkg_name" >> failed.txt
|
||||
fi
|
||||
else
|
||||
try_aur_install "$repo_url" "$pkg_name"
|
||||
fi
|
||||
else
|
||||
try_aur_install "$repo_url" "$pkg_name"
|
||||
fi
|
||||
done < "$file_path"
|
||||
git clone "$repo_url"
|
||||
if [ -d "$repo_dir" ] && [ -z "$(ls -A "$repo_dir")" ]; then
|
||||
echo "Repository $repo_dir is empty, trying to install with pacman"
|
||||
if sudo pacman -Sy --noconfirm "$pkg_name"; then
|
||||
echo "$pkg_name" >>done.txt
|
||||
else
|
||||
echo "$pkg_name" >>failed.txt
|
||||
fi
|
||||
else
|
||||
try_aur_install "$repo_url" "$pkg_name"
|
||||
fi
|
||||
else
|
||||
try_aur_install "$repo_url" "$pkg_name"
|
||||
fi
|
||||
done <"$file_path"
|
||||
}
|
||||
|
||||
# Helper: Check if all subpackages are installed
|
||||
# Returns 0 if ALL subpackages are installed, 1 otherwise
|
||||
all_subpackages_installed() {
|
||||
local -n sub_pkgs_ref=$1
|
||||
for subpkg in "${sub_pkgs_ref[@]}"; do
|
||||
if ! pacman -Qi "$subpkg" &> /dev/null; then
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
return 0
|
||||
local -n sub_pkgs_ref=$1
|
||||
for subpkg in "${sub_pkgs_ref[@]}"; do
|
||||
if ! pacman -Qi "$subpkg" &>/dev/null; then
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
@ -164,7 +164,7 @@ for entry in "${aur_packages[@]}"; do
|
||||
install_from_aur "$repo_url" "$pkg_name"
|
||||
done
|
||||
|
||||
cd ~/linux-configuration/fresh-install
|
||||
cd ~/src/testsAndMisc/linux_configuration/fresh-install
|
||||
if [ ! -d "$HOME/.config/mpv" ]; then
|
||||
mkdir -p "$HOME/.config/mpv"
|
||||
fi
|
||||
@ -176,10 +176,10 @@ else
|
||||
echo "Oh My Zsh is already installed"
|
||||
fi
|
||||
|
||||
cd ~/linux-configuration
|
||||
sudo "$HOME/hosts-blocker/install.sh"
|
||||
cd ~/src/testsAndMisc/linux_configuration
|
||||
sudo "$HOME/src/hosts-blocker/install.sh"
|
||||
i3/install.sh
|
||||
"$HOME/digital-wellbeing/pacman/install_pacman_wrapper.sh"
|
||||
"$HOME/src/digital-wellbeing/pacman/install_pacman_wrapper.sh"
|
||||
fixes/nvidia_troubleshoot.sh
|
||||
sudo features/setup_activitywatch.sh
|
||||
sudo utils/setup_media_organizer.sh
|
||||
|
||||
@ -28,7 +28,7 @@ sudo chmod 440 /etc/sudoers.d/player2-steam
|
||||
**4. Symlink the script into PATH:**
|
||||
|
||||
```bash
|
||||
sudo ln -sf ~/testsAndMisc/linux_configuration/gaming/start-player2.sh /usr/local/bin/start-player2
|
||||
sudo ln -sf ~/src/testsAndMisc/linux_configuration/gaming/start-player2.sh /usr/local/bin/start-player2
|
||||
```
|
||||
|
||||
**5. Start the getty on tty2** (needed if LightDM autologin is configured, otherwise tty2 has no login prompt):
|
||||
|
||||
@ -35,7 +35,7 @@ exec --no-startup-id xss-lock --transfer-sleep-lock -- i3lock --nofork
|
||||
exec --no-startup-id nm-applet
|
||||
|
||||
# Keep screen awake and unlocked; also treat controller input as activity
|
||||
exec --no-startup-id bash /home/kuhy/testsAndMisc/linux_configuration/utils/turn_off_auto_idle_screen_shutdown.sh --watch-controller
|
||||
exec --no-startup-id bash /home/kuhy/src/testsAndMisc/linux_configuration/utils/turn_off_auto_idle_screen_shutdown.sh --watch-controller
|
||||
|
||||
# Use pactl to adjust volume in PulseAudio.
|
||||
set $refresh_i3status killall -SIGUSR1 i3status
|
||||
@ -59,7 +59,7 @@ bindsym $mod+Return exec terminator
|
||||
|
||||
# scratchpad calculator: floating popup running the live-calc widget (type
|
||||
# math, results appear as you type; Ctrl-D to close)
|
||||
bindsym $mod+c exec --no-startup-id /home/kuhy/testsAndMisc/linux_configuration/zsh/calc-popup.sh
|
||||
bindsym $mod+c exec --no-startup-id /home/kuhy/src/testsAndMisc/linux_configuration/zsh/calc-popup.sh
|
||||
for_window [window_role="calc"] floating enable, border pixel 3, resize set 820 px 220 px, move position center
|
||||
|
||||
# kill focused window
|
||||
|
||||
@ -6,14 +6,14 @@
|
||||
# a clone that fails is a loud module failure, never a silent skip.
|
||||
#
|
||||
# CORE modules (always installed):
|
||||
# 0. Guard library (guardctl) – github.com/kuhyx/utils (~/utils/guard-lib)
|
||||
# 1. Workout screen locker – github.com/kuhyx/screen-locker (~/screen-locker)
|
||||
# 2. Hosts blocking setup – github.com/kuhyx/hosts-blocker (~/hosts-blocker)
|
||||
# 0. Guard library (guardctl) – github.com/kuhyx/utils (~/src/utils/guard-lib)
|
||||
# 1. Workout screen locker – github.com/kuhyx/screen-locker (~/src/screen-locker)
|
||||
# 2. Hosts blocking setup – github.com/kuhyx/hosts-blocker (~/src/hosts-blocker)
|
||||
# 3. Midnight shutdown timer – github.com/kuhyx/digital-wellbeing
|
||||
#
|
||||
# SECONDARY modules (prompted unless --all / --none given):
|
||||
# 4. Steam backlog enforcer – github.com/kuhyx/steam-backlog-enforcer
|
||||
# 5. Pacman wrapper – github.com/kuhyx/digital-wellbeing (~/digital-wellbeing)
|
||||
# 5. Pacman wrapper – github.com/kuhyx/digital-wellbeing (~/src/digital-wellbeing)
|
||||
# 6. i3 configuration – i3/
|
||||
# 7. Compulsive opening block – block_compulsive_opening.sh
|
||||
#
|
||||
|
||||
@ -59,7 +59,7 @@ extracted_repo_dir() {
|
||||
# yourself" is not actionable -- there is nothing to clone it with yet.
|
||||
#
|
||||
# The clone runs as the HUMAN user, never as root. Installers re-exec themselves
|
||||
# via sudo, and a root-owned ~/screen-locker silently breaks every later git
|
||||
# via sudo, and a root-owned ~/src/screen-locker silently breaks every later git
|
||||
# operation the user makes in it. A sandbox never catches this (the guest runs
|
||||
# as root throughout), so the ownership is forced here rather than assumed.
|
||||
#
|
||||
|
||||
@ -84,7 +84,7 @@ LEECHBLOCK_SCRIPT="$DW_REPO/install_leechblock.sh"
|
||||
REMOVE_GUEST_MODE_SCRIPT="$DW_REPO/remove_guest_mode.sh"
|
||||
VBOX_HOSTS_SCRIPT="$DW_REPO/virtualbox/enforce_vbox_hosts.sh"
|
||||
# screen-locker was EXTRACTED out of this monorepo into its own repo
|
||||
# (github.com/kuhyx/screen-locker, checked out at ~/screen-locker), so these
|
||||
# (github.com/kuhyx/screen-locker, checked out at ~/src/screen-locker), so these
|
||||
# paths deliberately live outside testsAndMisc. They used to point at
|
||||
# python_pkg/screen_locker/, which stopped existing at extraction time — the
|
||||
# result was check_workout_locker reporting a red "error" for a service that was
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
Security Hardening Test Suite
|
||||
==========================================
|
||||
|
||||
Testing components in: /home/kuhy/linux-configuration
|
||||
Testing components in: /home/kuhy/src/testsAndMisc/linux_configuration
|
||||
|
||||
--- HOSTS GUARD ---
|
||||
[0;32m✅ PASS[0m: /etc/hosts is immutable
|
||||
|
||||
@ -24,6 +24,7 @@ _REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||
_SCRIPT_DIRS = (
|
||||
_REPO_ROOT / "meta" / "scripts", # validate_evidence, validate_contract
|
||||
_REPO_ROOT / "linux_configuration" / "utils", # fast_count
|
||||
_REPO_ROOT / "linux_configuration" / "fixes", # g29_shifter_capture
|
||||
_REPO_ROOT
|
||||
/ "linux_configuration"
|
||||
/ "misc"
|
||||
|
||||
@ -73,7 +73,7 @@ sec_tests_screen_locker() {
|
||||
echo "--- SCREEN LOCKER ---"
|
||||
|
||||
# screen_locker was EXTRACTED to github.com/kuhyx/screen-locker. This used
|
||||
# to hardcode $HOME/testsAndMisc/python_pkg/screen_locker/, which stopped
|
||||
# to hardcode $HOME/src/testsAndMisc/python_pkg/screen_locker/, which stopped
|
||||
# existing -- so all five checks below silently SKIPPED, reporting a clean
|
||||
# run for tests that never executed. $HOME is also wrong under sudo; use the
|
||||
# shared resolver, which handles the sudo/systemd cases.
|
||||
|
||||
@ -153,7 +153,7 @@ class TestSessionEndHookLaunchesIngest:
|
||||
assert "ingest" in argv
|
||||
assert str(transcript) in argv
|
||||
assert "--quiet" in argv
|
||||
assert f"PYTHONPATH={home}/mcp-servers/session-autopsy" in argv
|
||||
assert f"PYTHONPATH={home}/src/mcp-servers/session-autopsy" in argv
|
||||
assert "timeout 120" in argv
|
||||
|
||||
def test_creates_the_autopsy_home(self, tmp_path: Path) -> None:
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# Check Brother laser printer consumable/maintenance status.
|
||||
# Thin wrapper that ensures dependencies are present, then runs the
|
||||
# Python implementation in ~/testsAndMisc/python_pkg/brother_printer.
|
||||
# Python implementation in ~/src/testsAndMisc/python_pkg/brother_printer.
|
||||
#
|
||||
# Usage:
|
||||
# ./check_brother_printer.sh # auto-detect USB or network
|
||||
@ -9,7 +9,7 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PYTHON_PKG_DIR="${HOME}/testsAndMisc/python_pkg"
|
||||
PYTHON_PKG_DIR="${HOME}/src/testsAndMisc/python_pkg"
|
||||
BROTHER_MODULE="brother_printer"
|
||||
|
||||
# ── Ensure dependencies ─────────────────────────────────────────────
|
||||
|
||||
@ -3,28 +3,28 @@
|
||||
# Uses 'fd' for fast searching - install with: sudo pacman -S fd
|
||||
#
|
||||
# Usage: ./find_keepassxc.sh [destination_directory]
|
||||
# Default destination: ~/Keepass
|
||||
# Default destination: ~/data/Keepass
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Source common library if available
|
||||
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
|
||||
if [[ -f "$SCRIPT_DIR/../lib/common.sh" ]]; then
|
||||
# shellcheck source=../lib/common.sh
|
||||
source "$SCRIPT_DIR/../lib/common.sh"
|
||||
# shellcheck source=../lib/common.sh
|
||||
source "$SCRIPT_DIR/../lib/common.sh"
|
||||
else
|
||||
log() { printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"; }
|
||||
log() { printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"; }
|
||||
fi
|
||||
|
||||
# Configuration
|
||||
DEST_DIR="${1:-$HOME/Keepass}"
|
||||
DEST_DIR="${1:-$HOME/data/Keepass}"
|
||||
SEARCH_ROOT="/"
|
||||
TIMEOUT_SECONDS=30
|
||||
|
||||
# Ensure fd is installed
|
||||
if ! command -v fd &> /dev/null; then
|
||||
log "ERROR: 'fd' is not installed. Install with: sudo pacman -S fd"
|
||||
exit 1
|
||||
if ! command -v fd &>/dev/null; then
|
||||
log "ERROR: 'fd' is not installed. Install with: sudo pacman -S fd"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Create destination directory if it doesn't exist
|
||||
@ -41,31 +41,31 @@ log "Searching for .kdbx files across the system (timeout: ${TIMEOUT_SECONDS}s).
|
||||
# Use timeout to ensure the search doesn't take too long
|
||||
# Exclude /proc, /sys, /dev, /run, /tmp, /var/cache, /var/tmp for speed
|
||||
FOUND_FILES=$(timeout "$TIMEOUT_SECONDS" fd \
|
||||
-e kdbx \
|
||||
-u \
|
||||
-a \
|
||||
--exclude '/proc' \
|
||||
--exclude '/sys' \
|
||||
--exclude '/dev' \
|
||||
--exclude '/run' \
|
||||
--exclude '/tmp' \
|
||||
--exclude '/var/cache' \
|
||||
--exclude '/var/tmp' \
|
||||
--exclude '/snap' \
|
||||
--exclude '/.snapshots' \
|
||||
--exclude '/lost+found' \
|
||||
. "$SEARCH_ROOT" 2> /dev/null || true)
|
||||
-e kdbx \
|
||||
-u \
|
||||
-a \
|
||||
--exclude '/proc' \
|
||||
--exclude '/sys' \
|
||||
--exclude '/dev' \
|
||||
--exclude '/run' \
|
||||
--exclude '/tmp' \
|
||||
--exclude '/var/cache' \
|
||||
--exclude '/var/tmp' \
|
||||
--exclude '/snap' \
|
||||
--exclude '/.snapshots' \
|
||||
--exclude '/lost+found' \
|
||||
. "$SEARCH_ROOT" 2>/dev/null || true)
|
||||
|
||||
if [[ -z $FOUND_FILES ]]; then
|
||||
log "No .kdbx files found."
|
||||
exit 0
|
||||
log "No .kdbx files found."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Count and display found files
|
||||
FILE_COUNT=$(echo "$FOUND_FILES" | wc -l)
|
||||
log "Found $FILE_COUNT .kdbx file(s):"
|
||||
echo "$FOUND_FILES" | while read -r file; do
|
||||
echo " - $file"
|
||||
echo " - $file"
|
||||
done
|
||||
|
||||
# Move files to destination
|
||||
@ -74,51 +74,51 @@ MOVED_COUNT=0
|
||||
SKIPPED_COUNT=0
|
||||
|
||||
while IFS= read -r src_file; do
|
||||
[[ -z $src_file ]] && continue
|
||||
[[ -z $src_file ]] && continue
|
||||
|
||||
# Skip if file is already in destination
|
||||
if [[ "$(dirname "$src_file")" == "$DEST_DIR" ]]; then
|
||||
log "Skipping (already in destination): $src_file"
|
||||
((SKIPPED_COUNT++)) || true
|
||||
continue
|
||||
fi
|
||||
# Skip if file is already in destination
|
||||
if [[ "$(dirname "$src_file")" == "$DEST_DIR" ]]; then
|
||||
log "Skipping (already in destination): $src_file"
|
||||
((SKIPPED_COUNT++)) || true
|
||||
continue
|
||||
fi
|
||||
|
||||
# Get the base filename
|
||||
base_name=$(basename "$src_file")
|
||||
dest_file="$DEST_DIR/$base_name"
|
||||
# Get the base filename
|
||||
base_name=$(basename "$src_file")
|
||||
dest_file="$DEST_DIR/$base_name"
|
||||
|
||||
# Handle filename conflicts by adding a number suffix
|
||||
if [[ -f $dest_file ]]; then
|
||||
# Check if it's the exact same file (by content)
|
||||
if cmp -s "$src_file" "$dest_file"; then
|
||||
log "Skipping (identical file exists): $src_file"
|
||||
# Remove the duplicate source file
|
||||
rm -v "$src_file"
|
||||
((SKIPPED_COUNT++)) || true
|
||||
continue
|
||||
fi
|
||||
# Handle filename conflicts by adding a number suffix
|
||||
if [[ -f $dest_file ]]; then
|
||||
# Check if it's the exact same file (by content)
|
||||
if cmp -s "$src_file" "$dest_file"; then
|
||||
log "Skipping (identical file exists): $src_file"
|
||||
# Remove the duplicate source file
|
||||
rm -v "$src_file"
|
||||
((SKIPPED_COUNT++)) || true
|
||||
continue
|
||||
fi
|
||||
|
||||
# Different file with same name - add suffix
|
||||
counter=1
|
||||
name_without_ext="${base_name%.kdbx}"
|
||||
while [[ -f $dest_file ]]; do
|
||||
dest_file="$DEST_DIR/${name_without_ext} ($counter).kdbx"
|
||||
((counter++))
|
||||
done
|
||||
log "Renaming to avoid conflict: $base_name -> $(basename "$dest_file")"
|
||||
fi
|
||||
# Different file with same name - add suffix
|
||||
counter=1
|
||||
name_without_ext="${base_name%.kdbx}"
|
||||
while [[ -f $dest_file ]]; do
|
||||
dest_file="$DEST_DIR/${name_without_ext} ($counter).kdbx"
|
||||
((counter++))
|
||||
done
|
||||
log "Renaming to avoid conflict: $base_name -> $(basename "$dest_file")"
|
||||
fi
|
||||
|
||||
# Move the file
|
||||
if mv -v "$src_file" "$dest_file"; then
|
||||
((MOVED_COUNT++)) || true
|
||||
else
|
||||
log "ERROR: Failed to move $src_file"
|
||||
fi
|
||||
done <<< "$FOUND_FILES"
|
||||
# Move the file
|
||||
if mv -v "$src_file" "$dest_file"; then
|
||||
((MOVED_COUNT++)) || true
|
||||
else
|
||||
log "ERROR: Failed to move $src_file"
|
||||
fi
|
||||
done <<<"$FOUND_FILES"
|
||||
|
||||
log "Done! Moved $MOVED_COUNT file(s), skipped $SKIPPED_COUNT file(s)."
|
||||
log "All KeePassXC databases are now in: $DEST_DIR"
|
||||
|
||||
# List final contents
|
||||
log "Contents of $DEST_DIR:"
|
||||
ls -la "$DEST_DIR"/*.kdbx 2> /dev/null || log "No .kdbx files in destination"
|
||||
ls -la "$DEST_DIR"/*.kdbx 2>/dev/null || log "No .kdbx files in destination"
|
||||
|
||||
@ -7,52 +7,52 @@
|
||||
# Make sure all databases use the same master password, or know each password.
|
||||
#
|
||||
# Usage: ./sync_keepassxc.sh [directory]
|
||||
# Default directory: ~/Keepass
|
||||
# Default directory: ~/data/Keepass
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Source common library if available
|
||||
SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
|
||||
if [[ -f "$SCRIPT_DIR/../lib/common.sh" ]]; then
|
||||
# shellcheck source=../lib/common.sh
|
||||
source "$SCRIPT_DIR/../lib/common.sh"
|
||||
# shellcheck source=../lib/common.sh
|
||||
source "$SCRIPT_DIR/../lib/common.sh"
|
||||
else
|
||||
log() { printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"; }
|
||||
log() { printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"; }
|
||||
fi
|
||||
|
||||
# Configuration
|
||||
KEEPASS_DIR="${1:-$HOME/Keepass}"
|
||||
KEEPASS_DIR="${1:-$HOME/data/Keepass}"
|
||||
BACKUP_DIR="$KEEPASS_DIR/.backup_$(date +%Y%m%d_%H%M%S)"
|
||||
|
||||
# Ensure keepassxc-cli is installed
|
||||
if ! command -v keepassxc-cli &> /dev/null; then
|
||||
log "ERROR: 'keepassxc-cli' is not installed. Install with: sudo pacman -S keepassxc"
|
||||
exit 1
|
||||
if ! command -v keepassxc-cli &>/dev/null; then
|
||||
log "ERROR: 'keepassxc-cli' is not installed. Install with: sudo pacman -S keepassxc"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check if directory exists
|
||||
if [[ ! -d $KEEPASS_DIR ]]; then
|
||||
log "ERROR: Directory does not exist: $KEEPASS_DIR"
|
||||
exit 1
|
||||
log "ERROR: Directory does not exist: $KEEPASS_DIR"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Find all .kdbx files
|
||||
mapfile -t KDBX_FILES < <(find "$KEEPASS_DIR" -maxdepth 1 -name "*.kdbx" -type f | sort)
|
||||
|
||||
if [[ ${#KDBX_FILES[@]} -eq 0 ]]; then
|
||||
log "No .kdbx files found in $KEEPASS_DIR"
|
||||
exit 0
|
||||
log "No .kdbx files found in $KEEPASS_DIR"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ ${#KDBX_FILES[@]} -eq 1 ]]; then
|
||||
log "Only one .kdbx file found. Nothing to merge."
|
||||
log "File: ${KDBX_FILES[0]}"
|
||||
exit 0
|
||||
log "Only one .kdbx file found. Nothing to merge."
|
||||
log "File: ${KDBX_FILES[0]}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
log "Found ${#KDBX_FILES[@]} .kdbx files in $KEEPASS_DIR:"
|
||||
for f in "${KDBX_FILES[@]}"; do
|
||||
echo " - $(basename "$f")"
|
||||
echo " - $(basename "$f")"
|
||||
done
|
||||
|
||||
# Create backup directory
|
||||
@ -61,7 +61,7 @@ log "Creating backups in: $BACKUP_DIR"
|
||||
|
||||
# Backup all files before any operation
|
||||
for f in "${KDBX_FILES[@]}"; do
|
||||
cp -v "$f" "$BACKUP_DIR/"
|
||||
cp -v "$f" "$BACKUP_DIR/"
|
||||
done
|
||||
log "All files backed up successfully."
|
||||
|
||||
@ -75,8 +75,8 @@ echo "=============================================="
|
||||
echo ""
|
||||
read -rp "Do you want to continue? (yes/no): " CONFIRM
|
||||
if [[ $CONFIRM != "yes" ]]; then
|
||||
log "Aborted by user."
|
||||
exit 1
|
||||
log "Aborted by user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Select the target database (the one to merge INTO)
|
||||
@ -93,9 +93,9 @@ read -rsp "Enter master password for TARGET database ($(basename "$TARGET_DB")):
|
||||
echo ""
|
||||
|
||||
# Verify target password works
|
||||
if ! echo "$TARGET_PASSWORD" | keepassxc-cli ls "$TARGET_DB" &> /dev/null; then
|
||||
log "ERROR: Failed to open target database. Wrong password?"
|
||||
exit 1
|
||||
if ! echo "$TARGET_PASSWORD" | keepassxc-cli ls "$TARGET_DB" &>/dev/null; then
|
||||
log "ERROR: Failed to open target database. Wrong password?"
|
||||
exit 1
|
||||
fi
|
||||
log "Target database password verified."
|
||||
|
||||
@ -107,41 +107,41 @@ SAME_PASSWORD="${SAME_PASSWORD,,}" # lowercase
|
||||
# Merge each source database into the target
|
||||
MERGE_COUNT=0
|
||||
for ((i = 1; i < ${#KDBX_FILES[@]}; i++)); do
|
||||
SOURCE_DB="${KDBX_FILES[$i]}"
|
||||
log ""
|
||||
log "Merging $(basename "$SOURCE_DB") into $(basename "$TARGET_DB")..."
|
||||
SOURCE_DB="${KDBX_FILES[$i]}"
|
||||
log ""
|
||||
log "Merging $(basename "$SOURCE_DB") into $(basename "$TARGET_DB")..."
|
||||
|
||||
# Reuse target password if user confirmed all are the same
|
||||
if [[ $SAME_PASSWORD == "y" || $SAME_PASSWORD == "yes" ]]; then
|
||||
SOURCE_PASSWORD="$TARGET_PASSWORD"
|
||||
else
|
||||
# Ask for source password (might be different)
|
||||
echo ""
|
||||
read -rsp "Enter master password for SOURCE database ($(basename "$SOURCE_DB")): " SOURCE_PASSWORD
|
||||
echo ""
|
||||
fi
|
||||
# Reuse target password if user confirmed all are the same
|
||||
if [[ $SAME_PASSWORD == "y" || $SAME_PASSWORD == "yes" ]]; then
|
||||
SOURCE_PASSWORD="$TARGET_PASSWORD"
|
||||
else
|
||||
# Ask for source password (might be different)
|
||||
echo ""
|
||||
read -rsp "Enter master password for SOURCE database ($(basename "$SOURCE_DB")): " SOURCE_PASSWORD
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Verify source password
|
||||
if ! echo "$SOURCE_PASSWORD" | keepassxc-cli ls "$SOURCE_DB" &> /dev/null; then
|
||||
log "ERROR: Failed to open source database $(basename "$SOURCE_DB"). Wrong password?"
|
||||
log "Skipping this database. You can try again later."
|
||||
continue
|
||||
fi
|
||||
# Verify source password
|
||||
if ! echo "$SOURCE_PASSWORD" | keepassxc-cli ls "$SOURCE_DB" &>/dev/null; then
|
||||
log "ERROR: Failed to open source database $(basename "$SOURCE_DB"). Wrong password?"
|
||||
log "Skipping this database. You can try again later."
|
||||
continue
|
||||
fi
|
||||
|
||||
# Perform the merge
|
||||
# keepassxc-cli merge requires: target_db source_db
|
||||
# It will prompt for passwords
|
||||
if echo -e "${TARGET_PASSWORD}\n${SOURCE_PASSWORD}" | keepassxc-cli merge "$TARGET_DB" "$SOURCE_DB"; then
|
||||
log "Successfully merged $(basename "$SOURCE_DB")"
|
||||
# Perform the merge
|
||||
# keepassxc-cli merge requires: target_db source_db
|
||||
# It will prompt for passwords
|
||||
if echo -e "${TARGET_PASSWORD}\n${SOURCE_PASSWORD}" | keepassxc-cli merge "$TARGET_DB" "$SOURCE_DB"; then
|
||||
log "Successfully merged $(basename "$SOURCE_DB")"
|
||||
|
||||
# Delete the source database after successful merge
|
||||
log "Deleting source database: $(basename "$SOURCE_DB")"
|
||||
rm -v "$SOURCE_DB"
|
||||
((MERGE_COUNT++)) || true
|
||||
else
|
||||
log "ERROR: Failed to merge $(basename "$SOURCE_DB")"
|
||||
log "Source database NOT deleted. Check the backup and try manually."
|
||||
fi
|
||||
# Delete the source database after successful merge
|
||||
log "Deleting source database: $(basename "$SOURCE_DB")"
|
||||
rm -v "$SOURCE_DB"
|
||||
((MERGE_COUNT++)) || true
|
||||
else
|
||||
log "ERROR: Failed to merge $(basename "$SOURCE_DB")"
|
||||
log "Source database NOT deleted. Check the backup and try manually."
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
@ -159,15 +159,15 @@ find "$KEEPASS_DIR" -maxdepth 1 -name "*.kdbx" -type f -exec basename {} \;
|
||||
# Rename to clean name if desired
|
||||
FINAL_COUNT=$(find "$KEEPASS_DIR" -maxdepth 1 -name "*.kdbx" -type f | wc -l)
|
||||
if [[ $FINAL_COUNT -eq 1 ]]; then
|
||||
log ""
|
||||
FINAL_NAME="$KEEPASS_DIR/Passwords.kdbx"
|
||||
if [[ $TARGET_DB != "$FINAL_NAME" ]]; then
|
||||
read -rp "Rename final database to 'Passwords.kdbx'? (y/n): " RENAME_CONFIRM
|
||||
if [[ $RENAME_CONFIRM == "y" ]]; then
|
||||
mv -v "$TARGET_DB" "$FINAL_NAME"
|
||||
log "Final database: $FINAL_NAME"
|
||||
fi
|
||||
fi
|
||||
log ""
|
||||
log "SUCCESS: You now have exactly ONE KeePassXC database!"
|
||||
log ""
|
||||
FINAL_NAME="$KEEPASS_DIR/Passwords.kdbx"
|
||||
if [[ $TARGET_DB != "$FINAL_NAME" ]]; then
|
||||
read -rp "Rename final database to 'Passwords.kdbx'? (y/n): " RENAME_CONFIRM
|
||||
if [[ $RENAME_CONFIRM == "y" ]]; then
|
||||
mv -v "$TARGET_DB" "$FINAL_NAME"
|
||||
log "Final database: $FINAL_NAME"
|
||||
fi
|
||||
fi
|
||||
log ""
|
||||
log "SUCCESS: You now have exactly ONE KeePassXC database!"
|
||||
fi
|
||||
|
||||
@ -15,7 +15,7 @@ phone-deploy() { bash "$HOME/.claude/scripts/phone_deploy.sh" "${1:-.}" "${@:2}"
|
||||
|
||||
# autopsy [subcommand ...] — the transcript analyzer; defaults to `report`.
|
||||
# Try: autopsy candidates | autopsy measure | autopsy report --mark-reviewed
|
||||
autopsy() { PYTHONPATH="$HOME/testsAndMisc" python3 -m python_pkg.session_autopsy "${1:-report}" "${@:2}"; }
|
||||
autopsy() { PYTHONPATH="$HOME/src/testsAndMisc" python3 -m python_pkg.session_autopsy "${1:-report}" "${@:2}"; }
|
||||
|
||||
# yay-clean [--dry-run] <pkg>...|--all — remove yay cache dirs plain rm chokes
|
||||
# on (read-only fakeroot/Bazel trees). The cache was 314GB on 2026-07-24.
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
{
|
||||
"flutter": "3.47.2"
|
||||
"flutter": "3.47.3"
|
||||
}
|
||||
|
||||
@ -173,7 +173,7 @@ disallow_untyped_decorators = false
|
||||
# bare name (the same dirs linux_configuration/tests/conftest.py adds to
|
||||
# sys.path at runtime) resolve under static analysis instead of raising E0401.
|
||||
# Paths are relative to the repo root, which is pre-commit's working directory.
|
||||
init-hook = "import sys; sys.path[:0] = ['meta/scripts', 'python_pkg', 'linux_configuration/utils', 'linux_configuration/misc/tools']"
|
||||
init-hook = "import sys; sys.path[:0] = ['meta/scripts', 'python_pkg', 'linux_configuration/utils', 'linux_configuration/fixes', 'linux_configuration/misc/tools']"
|
||||
# Analyse import fallback blocks
|
||||
analyse-fallback-blocks = true
|
||||
# Pickle collected data for later comparisons
|
||||
|
||||
@ -21,16 +21,16 @@
|
||||
# suite itself fails loudly here on a missing import.
|
||||
# ==============================================================================
|
||||
|
||||
anthropic==1.2.0
|
||||
anthropic==1.5.0
|
||||
beautifulsoup4==4.15.0
|
||||
coverage==7.16.0
|
||||
defusedxml==0.7.1
|
||||
markdownify==1.2.3
|
||||
numpy==2.5.2
|
||||
numpy==2.5.3
|
||||
pillow==12.3.0
|
||||
pytest==9.1.1
|
||||
pytest-cov==7.1.0
|
||||
pytest-randomly==4.1.0
|
||||
pytest-randomly==5.0.0
|
||||
pytest-sugar==1.1.1
|
||||
pytest-timeout==2.4.0
|
||||
pytest-xdist==3.8.0
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
add-trailing-comma==4.0.0
|
||||
aiohttp==3.14.3
|
||||
anthropic==1.2.0
|
||||
anthropic==1.5.0
|
||||
autoflake==2.4.0
|
||||
autopep8==2.3.2
|
||||
bandit==1.9.4
|
||||
@ -24,7 +24,7 @@ fixit==2.2.1
|
||||
flake8==7.3.0
|
||||
flake8-annotations==3.2.0
|
||||
flake8-bandit==4.1.1
|
||||
flake8-bugbear==25.11.29
|
||||
flake8-bugbear==26.9.9
|
||||
flake8-comprehensions==3.17.0
|
||||
flake8-docstrings==1.7.0
|
||||
flake8-eradicate==1.5.0
|
||||
@ -38,23 +38,23 @@ gatelock @ git+https://github.com/kuhyx/utils@gatelock-v0.7.3#subdirectory=gatel
|
||||
genanki==0.13.1
|
||||
geopandas==1.1.4
|
||||
howlongtobeatpy==1.0.23
|
||||
hypothesis==6.167.0
|
||||
hypothesis==6.168.0
|
||||
importlib-metadata==9.0.1
|
||||
interrogate==1.7.0
|
||||
# isort is deliberately not pinned: nothing here imports it (ruff's isort
|
||||
# rules replaced it) and pylint constrains it (`isort!=5.13,<10,>=5` as of
|
||||
# 4.0.8), so pinning it independently risks an unsolvable environment for a
|
||||
# package no code here imports. pylint brings a compatible one.
|
||||
lxml==6.1.2
|
||||
lxml==6.1.3
|
||||
markdownify==1.2.3
|
||||
matplotlib==3.11.1
|
||||
mccabe==0.7.0
|
||||
mypy==2.3.1
|
||||
numpy==2.5.2
|
||||
numpy==2.5.3
|
||||
opencv-python==5.0.0.93
|
||||
pillow==12.3.0
|
||||
pip-audit==2.10.1
|
||||
pipdeptree==4.2.2
|
||||
pipdeptree==4.2.5
|
||||
pre-commit==4.6.2
|
||||
prospector==1.19.1
|
||||
pycodestyle==2.14.0
|
||||
@ -62,10 +62,10 @@ pydocstyle==6.3.0
|
||||
pyflakes==3.4.0
|
||||
pygame==2.6.1
|
||||
pylint==4.0.8
|
||||
pyright==1.1.411
|
||||
pyright==1.1.414
|
||||
pytest==9.1.1
|
||||
pytest-cov==7.1.0
|
||||
pytest-randomly==4.1.0
|
||||
pytest-randomly==5.0.0
|
||||
pytest-sugar==1.1.1
|
||||
pytest-timeout==2.4.0
|
||||
pytest-xdist==3.8.0
|
||||
@ -76,15 +76,15 @@ radon==6.0.1
|
||||
reorder-python-imports==3.17.0
|
||||
requests==2.34.2
|
||||
rich==15.0.0
|
||||
ruff==0.16.5
|
||||
ruff==0.16.7
|
||||
# safety is deliberately not pinned: its only reference in this repo is a
|
||||
# commented-out pre-commit hook, and safety 3.8.1 declares `typer<0.26`,
|
||||
# which would hold the typer this repo actually imports below latest.
|
||||
selenium==4.48.0
|
||||
selenium==4.49.0
|
||||
typer==0.27.2
|
||||
types-python-dateutil==2.9.0.20260807
|
||||
types-PyYAML==6.0.12.20260815
|
||||
types-requests==2.33.0.20260712
|
||||
types-PyYAML==6.0.12.20260906
|
||||
types-requests==2.33.0.20260906
|
||||
types-setuptools==84.0.0.20260812
|
||||
typing_extensions==4.16.0 # Self annotation on py310 target (session_autopsy store lock)
|
||||
websockets==17.1
|
||||
|
||||
@ -206,7 +206,7 @@ if ((journal_kb > 204800)); then
|
||||
fi
|
||||
|
||||
# 19. AUR source dirs — old archives (>30 days)
|
||||
aur_dir="$HOME/aur"
|
||||
aur_dir="$HOME/sdk/aur"
|
||||
if [[ -d "$aur_dir" ]]; then
|
||||
old_archives_kb=$(find "$aur_dir" \
|
||||
\( -name "*.pkg.tar.zst" -o -name "*.tar.gz" -o -name "*.tar.xz" \
|
||||
@ -215,7 +215,7 @@ if [[ -d "$aur_dir" ]]; then
|
||||
awk '{t+=$1} END{print t+0}')
|
||||
if ((old_archives_kb > 0)); then
|
||||
report "AUR old archives (>30d)" "$old_archives_kb" \
|
||||
"find ~/aur '*.pkg.tar.zst' -mtime +30 -delete"
|
||||
"find ~/sdk/aur '*.pkg.tar.zst' -mtime +30 -delete"
|
||||
if confirm "Delete old AUR archives (>30 days)?"; then
|
||||
find "$aur_dir" \
|
||||
\( -name "*.pkg.tar.zst" -o -name "*.tar.gz" -o -name "*.tar.xz" \
|
||||
|
||||
@ -83,7 +83,7 @@ print_report_only() {
|
||||
for manual_entry in \
|
||||
"$HOME/Downloads/too_big:~/Downloads/too_big" \
|
||||
"$HOME/Downloads:~/Downloads total" \
|
||||
"$HOME/inne:~/inne" \
|
||||
"$HOME/media/inne:~/media/inne" \
|
||||
"/Games:/Games — still playing?"; do
|
||||
dir="${manual_entry%%:*}"
|
||||
label="${manual_entry#*:}"
|
||||
|
||||
@ -19,7 +19,7 @@ set -euo pipefail
|
||||
|
||||
SCRIPT_NAME="$(basename "$0")"
|
||||
readonly SCRIPT_NAME
|
||||
readonly REPO="${REPO:-$HOME/testsAndMisc}"
|
||||
readonly REPO="${REPO:-$HOME/src/testsAndMisc}"
|
||||
|
||||
CHECK_ONLY=0
|
||||
|
||||
@ -40,8 +40,8 @@ report_systemd() {
|
||||
[[ -n "$unit" ]] || continue
|
||||
found=1
|
||||
printf ' %s\n' "$unit"
|
||||
grep -n "$REPO\|%h/testsAndMisc" "$unit" | sed 's/^/ /'
|
||||
done < <(grep -rl "$REPO\|%h/testsAndMisc" \
|
||||
grep -n "$REPO\|%h/src/testsAndMisc" "$unit" | sed 's/^/ /'
|
||||
done < <(grep -rl "$REPO\|%h/src/testsAndMisc" \
|
||||
/etc/systemd/system "$HOME/.config/systemd/user" 2>/dev/null | sort)
|
||||
((found)) || echo " (none)"
|
||||
return "$found"
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
# android_ui — drive Android apps by element, never by coordinates
|
||||
|
||||
Shared across every repo with an Android surface (`~/screen-locker`'s
|
||||
workout_app, `~/todo`, `~/dufs-cloud/app`). Works against a plain release APK
|
||||
Shared across every repo with an Android surface (`~/src/screen-locker`'s
|
||||
workout_app, `~/src/todo`, `~/src/dufs-cloud/app`). Works against a plain release APK
|
||||
on a physical device over `adb` — no root, no emulator, no Flutter debug
|
||||
connection, no change to app source.
|
||||
|
||||
@ -31,7 +31,7 @@ false results before they were understood:
|
||||
## Use
|
||||
|
||||
```bash
|
||||
export PYTHONPATH=~/testsAndMisc
|
||||
export PYTHONPATH=~/src/testsAndMisc
|
||||
python3 -m python_pkg.android_ui dump
|
||||
python3 -m python_pkg.android_ui find "Connect Firebase"
|
||||
python3 -m python_pkg.android_ui --exact tap "Back"
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
"""Module entry point for the Android UI driver.
|
||||
|
||||
Usage:
|
||||
PYTHONPATH=~/testsAndMisc python3 -m python_pkg.android_ui --help
|
||||
PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.android_ui --help
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@ -8,17 +8,17 @@ drawn at one stroke weight and optically centred so a row of them lines up.
|
||||
|
||||
```bash
|
||||
# what is registered
|
||||
PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons list
|
||||
PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.app_icons list
|
||||
|
||||
# contact sheet for visual review: full size / circle-masked / 48dp
|
||||
PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons preview -o /tmp/sheet.png
|
||||
PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.app_icons preview -o /tmp/sheet.png
|
||||
|
||||
# write assets into every app repo and rebuild the Android mipmaps
|
||||
PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons generate --android
|
||||
PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.app_icons generate --android
|
||||
|
||||
# one app, plus the hicolor PNGs for a Linux desktop target
|
||||
PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons \
|
||||
generate --app todo --android --linux-out ~/todo/linux/icons
|
||||
PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.app_icons \
|
||||
generate --app todo --android --linux-out ~/src/todo/linux/icons
|
||||
```
|
||||
|
||||
## What gets written
|
||||
@ -58,7 +58,7 @@ review the contact sheet, then verify on the phone.
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
cd ~/testsAndMisc
|
||||
cd ~/src/testsAndMisc
|
||||
python3 -m pytest python_pkg/app_icons --cov=python_pkg.app_icons \
|
||||
--cov-report=term-missing
|
||||
```
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
"""Module entry point for the shared app-icon generator.
|
||||
|
||||
Usage:
|
||||
PYTHONPATH=~/testsAndMisc python3 -m python_pkg.app_icons --help
|
||||
PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.app_icons --help
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@ -130,3 +130,47 @@ _BOWL_STAR = """\
|
||||
L 430 493 L 459 397 L 379 337 L 479 335 Z" fill="{{ACCENT}}" stroke="none"/>
|
||||
<path d="M 268 566 L 756 566"/>
|
||||
<path d="M 348 566 C 348 700 420 752 512 752 C 604 752 676 700 676 566"/>"""
|
||||
|
||||
|
||||
# A handset under a broadcast arc: the bot's control panel, reached from
|
||||
# somewhere else. The arc is what makes it "remote" rather than "settings",
|
||||
# and it is detached from the body on purpose -- an arc that *meets* the body
|
||||
# is a padlock shackle, and padlock-closed is already in this family.
|
||||
#
|
||||
# Filled body with punched-out buttons, not a stroked outline, for the reason
|
||||
# the nib and the anvil are filled: a 200-wide stroked body leaves a 56px
|
||||
# interior at STROKE_WIDTH=72, which fills in solid at 48dp.
|
||||
#
|
||||
# Geometry, all inside SAFE_BOX (232..792) and clear of MIN_NEGATIVE_SPACE=36:
|
||||
# body x=412..612, y=440..792, corner radius 56
|
||||
# buttons r=40 at (512,540) and (512,680). Edge gap between them 60; to the
|
||||
# body's top edge 60, to its bottom edge 72, to each side wall 60
|
||||
# arc r=132 about the body's top centre (512,440), drawn between its
|
||||
# 135 and 45 degree points (419,347)-(605,347)
|
||||
#
|
||||
# The arc's *lowest* ink is its endpoints at y=347 plus half the stroke = 383,
|
||||
# which is 57 clear of the body at 440 -- not the arc's centre, which is
|
||||
# inside the body. Total ink y=272..792 and x=383..641, both within the box.
|
||||
_REMOTE_WAVE = """\
|
||||
<path d="M 419 347 A 132 132 0 0 1 605 347"/>
|
||||
<path fill="{{ACCENT}}" fill-rule="evenodd" stroke="none" d="\
|
||||
M 468 440 L 556 440 A 56 56 0 0 1 612 496 L 612 736 A 56 56 0 0 1 556 792 \
|
||||
L 468 792 A 56 56 0 0 1 412 736 L 412 496 A 56 56 0 0 1 468 440 Z \
|
||||
M 512 500 A 40 40 0 0 0 512 580 A 40 40 0 0 0 512 500 Z \
|
||||
M 512 640 A 40 40 0 0 0 512 720 A 40 40 0 0 0 512 640 Z"/>"""
|
||||
|
||||
|
||||
# House with a tick inside: the home, cleared. A stroked house outline rather
|
||||
# than a broom or sparkle -- a broom reads as a paintbrush at 48dp, and the
|
||||
# family already leans on "check" for done-ness (see checklist).
|
||||
#
|
||||
# Geometry, all inside SAFE_BOX (232..792) and clear of MIN_NEGATIVE_SPACE=36:
|
||||
# house apex (512,280), eaves y=480 at x=272/752, walls down to y=752.
|
||||
# Apex ink with the round join reaches ~y=244, inside the box.
|
||||
# tick (400,560)-(480,640)-(630,500). Bottom ink 676 vs floor ink 716
|
||||
# -> 40 gap; right ink 666 vs wall ink 716 -> 50; left ink 364 vs
|
||||
# wall ink 308 -> 56; top ink 464 vs the roof line at x=630
|
||||
# (y=363, ink to 399) -> 65.
|
||||
_HOUSE_TICK = """\
|
||||
<path d="M 272 752 L 272 480 L 512 280 L 752 480 L 752 752 Z"/>
|
||||
<path d="M 400 560 L 480 640 L 630 500"/>"""
|
||||
|
||||
@ -51,7 +51,7 @@ APPS: Final[dict[str, AppIcon]] = {
|
||||
# The live app: ~/dufs_client is a stale, remote-less checkout that
|
||||
# was superseded when dufs-cloud absorbed it (see testsAndMisc
|
||||
# CLAUDE.md). Same package name, so it is easy to icon the wrong one.
|
||||
repo=_HOME / "dufs-cloud" / "app",
|
||||
repo=_HOME / "src/dufs-cloud" / "app",
|
||||
accent="#B8862E",
|
||||
glyph="cloud-down",
|
||||
icon_name="dufs-client",
|
||||
@ -61,7 +61,7 @@ APPS: Final[dict[str, AppIcon]] = {
|
||||
key="focus_owner",
|
||||
# Absorbed into the phone-focus-mode monorepo on 2026-08-24; the
|
||||
# standalone ~/focus-owner clone is gone, as is its GitHub repo.
|
||||
repo=_HOME / "phone-focus-mode" / "focus-owner",
|
||||
repo=_HOME / "src/phone-focus-mode" / "focus-owner",
|
||||
accent="#B8862E",
|
||||
glyph="padlock-closed",
|
||||
icon_name="focus-owner",
|
||||
@ -69,7 +69,7 @@ APPS: Final[dict[str, AppIcon]] = {
|
||||
),
|
||||
AppIcon(
|
||||
key="workout_app",
|
||||
repo=_HOME / "screen-locker" / "stronglift_replacement" / "workout_app",
|
||||
repo=_HOME / "src/screen-locker" / "stronglift_replacement" / "workout_app",
|
||||
accent="#B8862E",
|
||||
glyph="barbell",
|
||||
icon_name="workout-app",
|
||||
@ -77,7 +77,7 @@ APPS: Final[dict[str, AppIcon]] = {
|
||||
),
|
||||
AppIcon(
|
||||
key="wake_alarm_sync",
|
||||
repo=_HOME / "wake-alarm" / "phone_app",
|
||||
repo=_HOME / "src/wake-alarm" / "phone_app",
|
||||
accent="#B8862E",
|
||||
glyph="clock",
|
||||
icon_name="wake-alarm-sync",
|
||||
@ -85,7 +85,7 @@ APPS: Final[dict[str, AppIcon]] = {
|
||||
),
|
||||
AppIcon(
|
||||
key="diet_guard_app",
|
||||
repo=_HOME / "diet-guard" / "app",
|
||||
repo=_HOME / "src/diet-guard" / "app",
|
||||
accent="#B8862E",
|
||||
glyph="shield-cutlery",
|
||||
icon_name="diet-guard-app",
|
||||
@ -93,7 +93,7 @@ APPS: Final[dict[str, AppIcon]] = {
|
||||
),
|
||||
AppIcon(
|
||||
key="todo",
|
||||
repo=_HOME / "todo",
|
||||
repo=_HOME / "src/todo",
|
||||
accent="#B8862E",
|
||||
glyph="checklist",
|
||||
icon_name="todo",
|
||||
@ -101,7 +101,7 @@ APPS: Final[dict[str, AppIcon]] = {
|
||||
),
|
||||
AppIcon(
|
||||
key="untools",
|
||||
repo=_HOME / "untools",
|
||||
repo=_HOME / "src/untools",
|
||||
accent="#B8862E",
|
||||
glyph="decision-tree",
|
||||
icon_name="untools",
|
||||
@ -111,7 +111,7 @@ APPS: Final[dict[str, AppIcon]] = {
|
||||
),
|
||||
AppIcon(
|
||||
key="punchme",
|
||||
repo=_HOME / "punchme",
|
||||
repo=_HOME / "src/punchme",
|
||||
accent="#B8862E",
|
||||
glyph="punch-card",
|
||||
icon_name="punchme",
|
||||
@ -120,7 +120,7 @@ APPS: Final[dict[str, AppIcon]] = {
|
||||
),
|
||||
AppIcon(
|
||||
key="restaurant_rater",
|
||||
repo=_HOME / "restaurant-rater",
|
||||
repo=_HOME / "src/restaurant-rater",
|
||||
accent="#B8862E",
|
||||
glyph="bowl-star",
|
||||
icon_name="restaurant-rater",
|
||||
@ -129,7 +129,7 @@ APPS: Final[dict[str, AppIcon]] = {
|
||||
),
|
||||
AppIcon(
|
||||
key="habit_stack",
|
||||
repo=_HOME / "habit_stack",
|
||||
repo=_HOME / "src/habit_stack",
|
||||
accent="#B8862E",
|
||||
glyph="chain-link",
|
||||
icon_name="habit-stack",
|
||||
@ -137,7 +137,7 @@ APPS: Final[dict[str, AppIcon]] = {
|
||||
),
|
||||
AppIcon(
|
||||
key="home_inventory",
|
||||
repo=_HOME / "home_inventory",
|
||||
repo=_HOME / "src/home_inventory",
|
||||
accent="#B8862E",
|
||||
glyph="storage-box",
|
||||
icon_name="home-inventory",
|
||||
@ -145,7 +145,7 @@ APPS: Final[dict[str, AppIcon]] = {
|
||||
),
|
||||
AppIcon(
|
||||
key="epopeja_karta",
|
||||
repo=_HOME / "epopeja_karta",
|
||||
repo=_HOME / "src/epopeja_karta",
|
||||
accent="#B8862E",
|
||||
glyph="quill-nib",
|
||||
icon_name="epopeja-karta",
|
||||
@ -158,7 +158,7 @@ APPS: Final[dict[str, AppIcon]] = {
|
||||
key="octoforge",
|
||||
# The Flutter app is a subdirectory of the repo: the repo root
|
||||
# holds the pure-Dart core and the tooling alongside it.
|
||||
repo=_HOME / "octoforge" / "octoforge" / "app",
|
||||
repo=_HOME / "src/octoforge" / "app",
|
||||
accent="#B8862E",
|
||||
glyph="anvil",
|
||||
icon_name="octoforge",
|
||||
@ -168,7 +168,7 @@ APPS: Final[dict[str, AppIcon]] = {
|
||||
),
|
||||
AppIcon(
|
||||
key="kuhylog",
|
||||
repo=_HOME / "kuhylog" / "kuhylog",
|
||||
repo=_HOME / "src/kuhylog",
|
||||
accent="#B8862E",
|
||||
glyph="track-bars",
|
||||
icon_name="kuhylog",
|
||||
@ -179,7 +179,7 @@ APPS: Final[dict[str, AppIcon]] = {
|
||||
),
|
||||
AppIcon(
|
||||
key="lyricanki",
|
||||
repo=_HOME / "lyricanki",
|
||||
repo=_HOME / "src/lyricanki",
|
||||
accent="#B8862E",
|
||||
glyph="note-ascender",
|
||||
icon_name="lyricanki",
|
||||
@ -189,7 +189,7 @@ APPS: Final[dict[str, AppIcon]] = {
|
||||
),
|
||||
AppIcon(
|
||||
key="betting_sim",
|
||||
repo=_HOME / "betting-sim" / "app",
|
||||
repo=_HOME / "src/betting-sim" / "app",
|
||||
accent="#B8862E",
|
||||
glyph="track-bars",
|
||||
icon_name="betting_sim",
|
||||
@ -198,6 +198,27 @@ APPS: Final[dict[str, AppIcon]] = {
|
||||
# would go unread.
|
||||
linux=False,
|
||||
),
|
||||
AppIcon(
|
||||
key="home_guard_app",
|
||||
repo=_HOME / "src/home-guard" / "app",
|
||||
accent="#B8862E",
|
||||
glyph="house-tick",
|
||||
icon_name="home-guard",
|
||||
linux=False,
|
||||
),
|
||||
AppIcon(
|
||||
key="signalbot_remote",
|
||||
# Inside the bot's own repository, not beside it: the app is a
|
||||
# client of that repo's management API, and kuhy's call was that a
|
||||
# route change and the client change land in one commit.
|
||||
repo=_HOME / "src/signal-bot" / "app",
|
||||
accent="#B8862E",
|
||||
glyph="remote-wave",
|
||||
icon_name="signalbot_remote",
|
||||
# Android only. The bot's desktop control is the TypeScript page
|
||||
# it already has, served on loopback.
|
||||
linux=False,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
"""Command-line entry point for the shared app-icon generator.
|
||||
|
||||
Usage:
|
||||
PYTHONPATH=~/testsAndMisc python -m python_pkg.app_icons list
|
||||
PYTHONPATH=~/testsAndMisc python -m python_pkg.app_icons preview -o sheet.png
|
||||
PYTHONPATH=~/testsAndMisc python -m python_pkg.app_icons generate --app todo
|
||||
PYTHONPATH=~/src/testsAndMisc python -m python_pkg.app_icons list
|
||||
PYTHONPATH=~/src/testsAndMisc python -m python_pkg.app_icons preview -o sheet.png
|
||||
PYTHONPATH=~/src/testsAndMisc python -m python_pkg.app_icons generate --app todo
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@ -31,9 +31,11 @@ from python_pkg.app_icons._glyph_art import (
|
||||
)
|
||||
from python_pkg.app_icons._glyph_art_more import (
|
||||
_BOWL_STAR,
|
||||
_HOUSE_TICK,
|
||||
_NOTE_ASCENDER,
|
||||
_PADLOCK_CLOSED,
|
||||
_PUNCH_CARD,
|
||||
_REMOTE_WAVE,
|
||||
_TRACK_BARS,
|
||||
)
|
||||
|
||||
@ -87,6 +89,12 @@ GLYPHS: Final[dict[str, Glyph]] = {
|
||||
_PADLOCK_CLOSED,
|
||||
),
|
||||
Glyph("bowl-star", "Filled star above an open bowl", _BOWL_STAR),
|
||||
Glyph("house-tick", "House outline with a tick inside", _HOUSE_TICK),
|
||||
Glyph(
|
||||
"remote-wave",
|
||||
"Handset with punched buttons under a broadcast arc",
|
||||
_REMOTE_WAVE,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -22,7 +22,7 @@ search.
|
||||
./install.sh
|
||||
|
||||
# Manual refresh.
|
||||
PYTHONPATH=~/testsAndMisc python3 -m python_pkg.archwiki_rag sync --reindex
|
||||
PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.archwiki_rag sync --reindex
|
||||
|
||||
# Query it.
|
||||
claude-archwiki
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
"""Module entry point for the offline Arch Wiki RAG corpus builder.
|
||||
|
||||
Usage:
|
||||
PYTHONPATH=~/testsAndMisc python3 -m python_pkg.archwiki_rag --help
|
||||
PYTHONPATH=~/src/testsAndMisc python3 -m python_pkg.archwiki_rag --help
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
"""Command-line entry point for the offline Arch Wiki RAG corpus.
|
||||
|
||||
Usage:
|
||||
PYTHONPATH=~/testsAndMisc python -m python_pkg.archwiki_rag sync
|
||||
PYTHONPATH=~/testsAndMisc python -m python_pkg.archwiki_rag sync --reindex
|
||||
PYTHONPATH=~/src/testsAndMisc python -m python_pkg.archwiki_rag sync
|
||||
PYTHONPATH=~/src/testsAndMisc python -m python_pkg.archwiki_rag sync --reindex
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
# "AI slop" is most detectable.
|
||||
#
|
||||
# CALIBRATION (measured, not guessed) over 250 genuine 32x32 CC0 icons from
|
||||
# ~/opengameart-downloads:
|
||||
# ~/data/opengameart-downloads:
|
||||
# p50=7 p75=10 p90=13 p95=14 p99=26 max=30
|
||||
# Rejection rate of real hand-made art by cap:
|
||||
# cap 16 -> 3.6% cap 24 -> 1.2% cap 32 -> 0.0%
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
# a lantern's glass panes all mush at 32px and read clearly at 64px.
|
||||
#
|
||||
# CALIBRATION. `max_colors` is deliberately NOT raised from pixel_icon's 32.
|
||||
# Measured over the 300 genuine 48x48 icons in ~/opengameart-downloads -- the
|
||||
# Measured over the 300 genuine 48x48 icons in ~/data/opengameart-downloads -- the
|
||||
# largest real-art sample available locally above 32px:
|
||||
# p50=9 p75=11 p90=13 p95=13 p99=14 max=14
|
||||
# Rejection rate of that real art: cap 32 -> 0.00%, and every larger cap also
|
||||
|
||||
@ -4,7 +4,7 @@ Run from a systemd user timer. Configuration comes from the environment:
|
||||
|
||||
ENDURAIN_URL base URL (default http://127.0.0.1:8085)
|
||||
ENDURAIN_API_KEY API key with activities:upload (required)
|
||||
ENDURAIN_INBOX WebDAV inbox (default ~/cloud/RunnerUp)
|
||||
ENDURAIN_INBOX WebDAV inbox (default ~/data/cloud/RunnerUp)
|
||||
ENDURAIN_STATE ledger directory (default ~/.local/state/endurain-import)
|
||||
ENDURAIN_BULK_DIR bulk_import dir for files the server rejected
|
||||
ENDURAIN_NO_ADB set to 1 to disable the phone fallback
|
||||
@ -59,7 +59,7 @@ if TYPE_CHECKING:
|
||||
_logger = logging.getLogger("endurain_import")
|
||||
|
||||
_DEFAULT_URL = "http://127.0.0.1:8085"
|
||||
_DEFAULT_INBOX = Path.home() / "cloud" / "RunnerUp"
|
||||
_DEFAULT_INBOX = Path.home() / "data/cloud" / "RunnerUp"
|
||||
_DEFAULT_STATE = Path.home() / ".local" / "state" / "endurain-import"
|
||||
_DEFAULT_USER_ID = 1
|
||||
|
||||
|
||||
@ -10,10 +10,10 @@ Type=simple
|
||||
# DISPLAY/PYTHONPATH mirror workout-locker.service so Tk can open the X server;
|
||||
# the short sleep lets the session export DISPLAY first.
|
||||
Environment=DISPLAY=:0
|
||||
Environment=PYTHONPATH=%h/testsAndMisc
|
||||
Environment=PYTHONPATH=%h/src/testsAndMisc
|
||||
ExecStartPre=/bin/sleep 1
|
||||
ExecStart=/usr/bin/python -m python_pkg.morning_routine._orchestrator --production
|
||||
WorkingDirectory=%h/testsAndMisc
|
||||
WorkingDirectory=%h/src/testsAndMisc
|
||||
Restart=on-failure
|
||||
RestartSec=2s
|
||||
RestartPreventExitStatus=0
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user