mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 02:54:26 +00:00
Replace 8 bash env modules with a single Python generator
(cli.meta.env) that materialises env/static.env + runtime context
into a gitignored .env. Each variable lives in its own handler
under utils/env/handlers/; defaults carry inline one-line `# ...`
comments.
Rename to the INFINITO_* schema:
- workflow inputs: APPS, SERVICES_DISABLED, CMD, DEBUG, WHITELIST,
BUNDLES, FULL_CYCLE, VARIANT, RUN_FLAGS, LIMIT_HOST, PARALLEL
- inventory: INVENTORY_DIR / INVENTORY_FILE / HOST_VARS_FILE,
PW_FILE -> INVENTORY_PASSWORD_FILE
- venv: VENV_BASE / VENV_FALLBACK / VENV_NAME
- new SPOT defaults: COMPILE_SILENCE, CONTAINER_NO_CA,
WAIT_HEALTH_TIMEOUT_S, PACKAGE_CACHE_FRONTEND_INIT_IMAGE,
promoted IMAGE_TAG from GHA-only to general passthrough
compose.yml hardening:
- inline \${VAR:-default} removed; strict \${VAR:?...} everywhere
- new integration lint blocks regressions
Tooling:
- scripts/install/all.sh owns the stamp-file install logic
(Makefile target delegates)
- scripts/meta/env/load.sh is the single shell entry point;
8 bash modules deleted
- new shell lint: no INFINITO_* defaults outside env/static.env
Tests:
- 93 new unit tests for utils/env/{builder,parser,runtime,writer}
- restructure tests/lint into repository/ (file-content rules) vs
filesystem/{shell,python,env}/ (filesystem-shape rules)
- drift + smoke integration tests against the generator
Docs:
- variables.md is now a pointer; per-variable docs live next to
the key in env/static.env / env/ci.env
- compose.yml.md drops the Default column
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
223 lines
5.4 KiB
Bash
Executable File
223 lines
5.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage:
|
|
system.sh [--help]
|
|
|
|
Runs the repository's full cleanup pass used by make system-purge.
|
|
Includes git state, Docker, host caches, and Windows cleanup.
|
|
|
|
The routine is best-effort:
|
|
- skips commands that are not available on the current host
|
|
- continues after individual cleanup failures
|
|
- keeps the one-time Windows Disk Cleanup profile setup optional
|
|
|
|
Optional one-time Windows setup:
|
|
PURGE_WINDOWS_CLEANMGR_SETUP=true system.sh
|
|
EOF
|
|
}
|
|
|
|
case "${1:-}" in
|
|
-h | --help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
esac
|
|
|
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd -- "${SCRIPT_DIR}/../../.." && pwd)"
|
|
|
|
cd "${REPO_ROOT}"
|
|
|
|
if [[ -f "scripts/meta/env/load.sh" ]]; then
|
|
# shellcheck source=scripts/meta/env/load.sh
|
|
source "scripts/meta/env/load.sh"
|
|
fi
|
|
|
|
log() {
|
|
echo ">>> $*"
|
|
}
|
|
|
|
warn() {
|
|
echo "!!! WARNING: $*" >&2
|
|
}
|
|
|
|
overall_rc=0
|
|
|
|
command_exists() {
|
|
local cmd="$1"
|
|
|
|
if [[ "${cmd}" == /* ]]; then
|
|
[[ -e "${cmd}" ]]
|
|
else
|
|
command -v "${cmd}" >/dev/null 2>&1
|
|
fi
|
|
}
|
|
|
|
pip_cache_supported() {
|
|
local python_bin="$1"
|
|
|
|
command_exists "${python_bin}" || return 1
|
|
"${python_bin}" -m pip cache --help >/dev/null 2>&1
|
|
}
|
|
|
|
run_step() {
|
|
local description="$1"
|
|
shift
|
|
local first="${1:-}"
|
|
|
|
if [[ -z "${first}" ]]; then
|
|
warn "${description}: missing command"
|
|
return 0
|
|
fi
|
|
|
|
if ! command_exists "${first}"; then
|
|
warn "${description}: command not found (${first}) - skipping"
|
|
return 0
|
|
fi
|
|
|
|
log "${description}"
|
|
|
|
if "$@"; then
|
|
return 0
|
|
else
|
|
local rc=$?
|
|
warn "${description} failed (rc=${rc})"
|
|
overall_rc=1
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
run_root_step() {
|
|
local description="$1"
|
|
shift
|
|
|
|
if [[ "${EUID}" -eq 0 ]]; then
|
|
run_step "${description}" "$@"
|
|
return 0
|
|
fi
|
|
|
|
if command_exists sudo; then
|
|
run_step "${description}" sudo "$@"
|
|
return 0
|
|
fi
|
|
|
|
warn "${description}: root privileges unavailable - skipping"
|
|
}
|
|
|
|
purge_git_state() {
|
|
if ! command_exists git || ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
warn "Repository cleanup skipped: not inside a git repository"
|
|
return 0
|
|
fi
|
|
|
|
run_step "Cleaning ignored Git files" git clean -fdX
|
|
}
|
|
|
|
purge_docker() {
|
|
if ! command_exists docker; then
|
|
warn "Docker not available - skipping docker cleanup"
|
|
return 0
|
|
fi
|
|
|
|
run_step "Stopping compose stack and removing volumes" docker compose down --volumes --remove-orphans
|
|
run_step "Pruning Docker system" docker system prune -a --volumes -f
|
|
run_step "Pruning Docker containers" docker container prune -f
|
|
run_step "Pruning Docker images" docker image prune -a -f
|
|
run_step "Pruning Docker volumes" docker volume prune -f
|
|
run_step "Pruning Docker networks" docker network prune -f
|
|
run_step "Pruning Docker builder cache" docker builder prune -a -f
|
|
|
|
if docker buildx version >/dev/null 2>&1; then
|
|
run_step "Pruning Docker Buildx cache" docker buildx prune -a -f
|
|
else
|
|
warn "Docker Buildx not available - skipping docker buildx prune"
|
|
fi
|
|
}
|
|
|
|
purge_host_caches() {
|
|
if command_exists journalctl; then
|
|
run_root_step "Vacuuming journald by size" journalctl --vacuum-size=100M
|
|
run_root_step "Vacuuming journald by time" journalctl --vacuum-time=3h
|
|
else
|
|
warn "journalctl not available - skipping journald cleanup"
|
|
fi
|
|
|
|
if [[ -n "${PYTHON:-}" ]] && pip_cache_supported "${PYTHON}"; then
|
|
run_step "Purging pip cache" "${PYTHON}" -m pip cache purge
|
|
elif pip_cache_supported python3; then
|
|
run_step "Purging pip cache" python3 -m pip cache purge
|
|
else
|
|
warn "pip cache purge not available - skipping pip cache cleanup"
|
|
fi
|
|
|
|
run_step "Clearing npm cache" npm cache clean --force
|
|
run_step "Clearing yarn cache" yarn cache clean
|
|
run_step "Clearing Go module cache" go clean -cache -modcache
|
|
|
|
if [[ -f "Cargo.toml" ]]; then
|
|
run_step "Cleaning Cargo build artifacts" cargo clean
|
|
else
|
|
warn "Cargo.toml not found - skipping cargo clean"
|
|
fi
|
|
|
|
if command_exists flatpak; then
|
|
run_step "Removing unused Flatpak packages" flatpak uninstall --unused -y
|
|
fi
|
|
|
|
if command_exists pacman; then
|
|
run_root_step "Cleaning Arch package cache" pacman -Scc --noconfirm
|
|
fi
|
|
|
|
if command_exists apt-get; then
|
|
run_root_step "Cleaning APT cache" apt-get clean
|
|
run_root_step "Autoremoving APT packages" apt-get autoremove --purge -y
|
|
fi
|
|
|
|
if command_exists dnf; then
|
|
run_root_step "Cleaning DNF cache" dnf clean all
|
|
fi
|
|
|
|
if command_exists yum; then
|
|
run_root_step "Cleaning YUM cache" yum clean all
|
|
fi
|
|
|
|
if command_exists brew; then
|
|
run_step "Cleaning Homebrew caches" brew cleanup --prune=all
|
|
run_step "Autoremoving Homebrew packages" brew autoremove
|
|
fi
|
|
}
|
|
|
|
purge_windows() {
|
|
[[ "${INFINITO_IS_WSL2}" == "true" ]] || return 0
|
|
|
|
local windows_system32="/mnt/c/Windows/System32"
|
|
|
|
if [[ "${PURGE_WINDOWS_CLEANMGR_SETUP:-false}" == "true" ]]; then
|
|
run_step "Configuring Windows Disk Cleanup profile 1" "${windows_system32}/cleanmgr.exe" /sageset:1
|
|
else
|
|
warn "Windows Disk Cleanup profile setup is optional; set PURGE_WINDOWS_CLEANMGR_SETUP=true to run cleanmgr /sageset:1"
|
|
fi
|
|
|
|
run_step "Running Windows Disk Cleanup profile 1" "${windows_system32}/cleanmgr.exe" /sagerun:1
|
|
run_step "Running Windows component cleanup" "${windows_system32}/DISM.exe" /Online /Cleanup-Image /StartComponentCleanup
|
|
run_step "Shutting down WSL2" "${windows_system32}/wsl.exe" --shutdown
|
|
}
|
|
|
|
log "Running low-hardware cleanup from ${REPO_ROOT}"
|
|
|
|
purge_git_state
|
|
purge_docker
|
|
purge_host_caches
|
|
purge_windows
|
|
|
|
if [[ "${overall_rc}" -eq 0 ]]; then
|
|
log "Cleanup completed successfully"
|
|
else
|
|
warn "Cleanup completed with warnings"
|
|
fi
|
|
|
|
exit "${overall_rc}"
|