Add CE OS Alpine installer v0.0.1 — Tier 0/1/2 dev scaffold
Staged installer set for Alpine Linux x86-64 (tinkerpad/ThinkPad T480). Covers Base (Tier 0), Minimal (Tier 1), and Basic (Tier 2) tiers with full phased structure, per-package error capture, and re-runnable test suites. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
commit
a194c14077
32 changed files with 1748 additions and 0 deletions
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
# CE OS Alpine Installer — .gitignore
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Logs (generated at runtime on target machines)
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Temp files from 11_wiki.sh glow download
|
||||||
|
/tmp/ce-glow-*/
|
||||||
89
bootstrap.sh
Normal file
89
bootstrap.sh
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# bootstrap.sh
|
||||||
|
# Served at: http://192.168.0.138/repo/scripts/alpine-ceos-installer/bootstrap.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Run this on tinkerpad to download the full CE OS installer from workbench.
|
||||||
|
# Usage: wget http://192.168.0.138/repo/scripts/alpine-ceos-installer/bootstrap.sh
|
||||||
|
# bash bootstrap.sh
|
||||||
|
#
|
||||||
|
# TODO — Forgejo: when git.jahnet.lan is deployed, replace the wget block
|
||||||
|
# below with these three lines and delete the rest of this file:
|
||||||
|
# doas apk add git
|
||||||
|
# git clone -b dev http://git.jahnet.lan/john/ceos-alpine-installer.git
|
||||||
|
# cd ceos-alpine-installer && bash ce-install.sh
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
BASE="http://192.168.0.138/repo/scripts/alpine-ceos-installer"
|
||||||
|
DEST="/tmp/ceos-installer-$(date +%Y%m%d%H%M%S)"
|
||||||
|
|
||||||
|
echo "CE OS Alpine Installer — Bootstrap"
|
||||||
|
echo "==================================="
|
||||||
|
echo "Source: ${BASE}"
|
||||||
|
echo "Dest: ${DEST}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
mkdir -p \
|
||||||
|
"${DEST}/lib/base" \
|
||||||
|
"${DEST}/lib/minimal" \
|
||||||
|
"${DEST}/lib/basic" \
|
||||||
|
"${DEST}/tests"
|
||||||
|
|
||||||
|
fetch() {
|
||||||
|
local path="${1}"
|
||||||
|
if wget -q -O "${DEST}/${path}" "${BASE}/${path}"; then
|
||||||
|
echo " OK ${path}"
|
||||||
|
else
|
||||||
|
echo " ERR ${path}" >&2
|
||||||
|
echo "Bootstrap failed: could not fetch ${path}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Fetching orchestrators..."
|
||||||
|
fetch "ce-install.sh"
|
||||||
|
fetch "ce-base.sh"
|
||||||
|
fetch "ce-minimal.sh"
|
||||||
|
fetch "ce-basic.sh"
|
||||||
|
|
||||||
|
echo "Fetching library..."
|
||||||
|
fetch "lib/ce-common.sh"
|
||||||
|
fetch "lib/base/01_core.sh"
|
||||||
|
fetch "lib/base/02_doas.sh"
|
||||||
|
|
||||||
|
echo "Fetching Tier 1 — Minimal..."
|
||||||
|
for n in 01_shell 02_terminal 03_files 04_vcs 05_systools \
|
||||||
|
06_comms 07_journal 08_web 09_network 10_record \
|
||||||
|
11_wiki 12_access; do
|
||||||
|
fetch "lib/minimal/${n}.sh"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Fetching Tier 2 — Basic..."
|
||||||
|
for n in 01_comms 02_monitor 03_gui 04_media 05_fonts 06_utils; do
|
||||||
|
fetch "lib/basic/${n}.sh"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Fetching tests..."
|
||||||
|
for t in test_base test_minimal test_basic; do
|
||||||
|
fetch "tests/${t}.sh"
|
||||||
|
done
|
||||||
|
|
||||||
|
find "${DEST}" -name "*.sh" -exec chmod +x {} \;
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Bootstrap complete."
|
||||||
|
echo ""
|
||||||
|
echo "To install:"
|
||||||
|
echo " cd ${DEST}"
|
||||||
|
echo " bash ce-install.sh # full stack (Tier 0 + 1 + 2)"
|
||||||
|
echo " bash ce-base.sh # Tier 0 only"
|
||||||
|
echo " bash ce-minimal.sh # Tier 1 only (requires Tier 0)"
|
||||||
|
echo " bash ce-basic.sh # Tier 2 only (requires Tier 1)"
|
||||||
|
echo ""
|
||||||
|
echo "To run tests after install:"
|
||||||
|
echo " bash ${DEST}/tests/test_base.sh"
|
||||||
|
echo " bash ${DEST}/tests/test_minimal.sh"
|
||||||
|
echo " bash ${DEST}/tests/test_basic.sh"
|
||||||
76
ce-base.sh
Normal file
76
ce-base.sh
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# ce-base.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/ce-base.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Tier 0 — Base. Prerequisites common to all hardware.
|
||||||
|
# Standalone-runnable. Sources lib/ce-common.sh and lib/base/*.sh.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
SCRIPT_NAME="ce-base"
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" \
|
||||||
|
|| { echo "ERROR: lib/ce-common.sh not found"; exit 1; }
|
||||||
|
source "${SCRIPT_DIR}/lib/base/01_core.sh" \
|
||||||
|
|| { log_error "lib/base/01_core.sh not found"; exit 1; }
|
||||||
|
source "${SCRIPT_DIR}/lib/base/02_doas.sh" \
|
||||||
|
|| { log_error "lib/base/02_doas.sh not found"; exit 1; }
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
local exit_code="${?}"
|
||||||
|
log_info "Running cleanup..."
|
||||||
|
doas apk cache clean >> "${LOG_FILE}" 2>&1 || true
|
||||||
|
doas rm -rf /var/cache/apk/* >> "${LOG_FILE}" 2>&1 || true
|
||||||
|
log_info "Cleanup complete (exit code: ${exit_code})"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT INT TERM HUP
|
||||||
|
|
||||||
|
phase_preflight() {
|
||||||
|
log_info "=== TIER 0: PRE-FLIGHT ==="
|
||||||
|
check_not_root
|
||||||
|
run_test "bash >= 4" '[[ "${BASH_VERSINFO[0]}" -ge 4 ]]' fail
|
||||||
|
run_test "arch detected" '[[ -n "${ARCH}" ]]' fail
|
||||||
|
run_test "apk available" 'command -v apk >/dev/null 2>&1' fail
|
||||||
|
run_test "disk >= 500MB" '[[ $(df / --output=avail | tail -1) -ge 512000 ]]' warn
|
||||||
|
run_test "internet reachable" \
|
||||||
|
'curl --silent --max-time 10 --output /dev/null https://dl-cdn.alpinelinux.org' warn
|
||||||
|
log_info "Arch: ${ARCH}"
|
||||||
|
log_info "Pre-flight complete"
|
||||||
|
}
|
||||||
|
|
||||||
|
phase_install() {
|
||||||
|
log_info "=== TIER 0: INSTALL ==="
|
||||||
|
doas apk update >> "${LOG_FILE}" 2>&1 \
|
||||||
|
&& doas apk upgrade >> "${LOG_FILE}" 2>&1 \
|
||||||
|
|| { log_error "apk update/upgrade failed"; exit 1; }
|
||||||
|
run_test "apk functional post-update" \
|
||||||
|
'apk search bash 2>/dev/null | grep -q "^bash"' fail
|
||||||
|
install_base_core
|
||||||
|
install_base_doas
|
||||||
|
}
|
||||||
|
|
||||||
|
phase_verify() {
|
||||||
|
log_info "=== TIER 0: VERIFY ==="
|
||||||
|
run_test "bash present" 'command -v bash >/dev/null 2>&1' warn
|
||||||
|
run_test "curl present" 'command -v curl >/dev/null 2>&1' warn
|
||||||
|
run_test "ssh present" 'command -v ssh >/dev/null 2>&1' warn
|
||||||
|
run_test "doas present" 'command -v doas >/dev/null 2>&1' warn
|
||||||
|
run_test "doas wheel.conf" '[[ -f /etc/doas.d/wheel.conf ]]' warn
|
||||||
|
run_test "community repo live" 'grep -q "^[^#].*community" /etc/apk/repositories' warn
|
||||||
|
log_info "Tier 0 verify complete"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info "=== CE OS INSTALLER — TIER 0: BASE ==="
|
||||||
|
log_info "Arch: ${ARCH} | Log: ${LOG_FILE}"
|
||||||
|
|
||||||
|
phase_preflight
|
||||||
|
confirm_proceed
|
||||||
|
phase_install
|
||||||
|
phase_verify
|
||||||
|
print_summary
|
||||||
|
|
||||||
|
log_info "=== TIER 0 COMPLETE ==="
|
||||||
83
ce-basic.sh
Normal file
83
ce-basic.sh
Normal file
|
|
@ -0,0 +1,83 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# ce-basic.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/ce-basic.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Tier 2 — Basic. Daily-use additions on top of Minimal.
|
||||||
|
# Standalone-runnable. Sources lib/ce-common.sh and lib/basic/*.sh.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
SCRIPT_NAME="ce-basic"
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" \
|
||||||
|
|| { echo "ERROR: lib/ce-common.sh not found"; exit 1; }
|
||||||
|
|
||||||
|
for _sub in "${SCRIPT_DIR}"/lib/basic/*.sh; do
|
||||||
|
source "${_sub}" || { log_error "Failed to source: ${_sub}"; exit 1; }
|
||||||
|
done
|
||||||
|
unset _sub
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
local exit_code="${?}"
|
||||||
|
log_info "Running cleanup..."
|
||||||
|
doas apk cache clean >> "${LOG_FILE}" 2>&1 || true
|
||||||
|
doas rm -rf /var/cache/apk/* >> "${LOG_FILE}" 2>&1 || true
|
||||||
|
log_info "Cleanup complete (exit code: ${exit_code})"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT INT TERM HUP
|
||||||
|
|
||||||
|
phase_preflight() {
|
||||||
|
log_info "=== TIER 2: PRE-FLIGHT ==="
|
||||||
|
check_not_root
|
||||||
|
run_test "bash >= 4" '[[ "${BASH_VERSINFO[0]}" -ge 4 ]]' fail
|
||||||
|
run_test "arch detected" '[[ -n "${ARCH}" ]]' fail
|
||||||
|
run_test "doas present" 'command -v doas >/dev/null 2>&1' fail
|
||||||
|
run_test "Tier 1 complete" 'command -v tmux >/dev/null 2>&1' fail
|
||||||
|
run_test "community repo enabled" 'grep -q "^[^#].*community" /etc/apk/repositories' fail
|
||||||
|
run_test "internet reachable" \
|
||||||
|
'curl --silent --max-time 10 --output /dev/null https://dl-cdn.alpinelinux.org' warn
|
||||||
|
run_test "disk >= 2GB" '[[ $(df / --output=avail | tail -1) -ge 2097152 ]]' warn
|
||||||
|
log_info "Arch: ${ARCH}"
|
||||||
|
log_info "Pre-flight complete"
|
||||||
|
}
|
||||||
|
|
||||||
|
phase_install() {
|
||||||
|
log_info "=== TIER 2: INSTALL ==="
|
||||||
|
doas apk update >> "${LOG_FILE}" 2>&1 \
|
||||||
|
&& doas apk upgrade >> "${LOG_FILE}" 2>&1 \
|
||||||
|
|| { log_error "apk update/upgrade failed"; exit 1; }
|
||||||
|
run_test "apk functional" 'apk search jq 2>/dev/null | grep -q "^jq"' fail
|
||||||
|
|
||||||
|
install_basic_comms
|
||||||
|
install_basic_monitor
|
||||||
|
install_basic_gui
|
||||||
|
install_basic_media
|
||||||
|
install_basic_fonts
|
||||||
|
install_basic_utils
|
||||||
|
}
|
||||||
|
|
||||||
|
phase_verify() {
|
||||||
|
log_info "=== TIER 2: VERIFY ==="
|
||||||
|
local tools=(jq bc zip unzip ffmpeg mpv cmus figlet)
|
||||||
|
for tool in "${tools[@]}"; do
|
||||||
|
run_test "${tool} present" "command -v ${tool} >/dev/null 2>&1" warn
|
||||||
|
done
|
||||||
|
run_test "cagebreak present" 'command -v cagebreak >/dev/null 2>&1' warn
|
||||||
|
run_test "firefox present" 'command -v firefox >/dev/null 2>&1' warn
|
||||||
|
log_info "Tier 2 verify complete"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info "=== CE OS INSTALLER — TIER 2: BASIC ==="
|
||||||
|
log_info "Arch: ${ARCH} | Log: ${LOG_FILE}"
|
||||||
|
|
||||||
|
phase_preflight
|
||||||
|
confirm_proceed
|
||||||
|
phase_install
|
||||||
|
phase_verify
|
||||||
|
print_summary
|
||||||
|
|
||||||
|
log_info "=== TIER 2 COMPLETE ==="
|
||||||
63
ce-install.sh
Normal file
63
ce-install.sh
Normal file
|
|
@ -0,0 +1,63 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# ce-install.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/ce-install.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Top-level orchestrator. Runs Tier 0 → 1 → 2 in sequence.
|
||||||
|
# Stops if any tier exits non-zero. One confirmation prompt for all tiers.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
SCRIPT_NAME="ce-install"
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" \
|
||||||
|
|| { echo "ERROR: lib/ce-common.sh not found"; exit 1; }
|
||||||
|
|
||||||
|
check_not_root
|
||||||
|
|
||||||
|
TIERS_COMPLETED=()
|
||||||
|
TIERS_FAILED=()
|
||||||
|
|
||||||
|
print_final_summary() {
|
||||||
|
log_info "=== FULL INSTALL SUMMARY ==="
|
||||||
|
log_info "Completed: ${TIERS_COMPLETED[*]:-none}"
|
||||||
|
if [[ ${#TIERS_FAILED[@]} -gt 0 ]]; then
|
||||||
|
log_warn "Failed: ${TIERS_FAILED[*]}"
|
||||||
|
fi
|
||||||
|
log_info "Full log: ${LOG_FILE}"
|
||||||
|
log_info "Per-tier logs: ${LOG_DIR}/ce-base-*.log, ce-minimal-*.log, ce-basic-*.log"
|
||||||
|
}
|
||||||
|
|
||||||
|
run_tier() {
|
||||||
|
local name="${1}" script="${2}"
|
||||||
|
log_info "--- Starting ${name} ---"
|
||||||
|
if CE_INSTALL_NO_CONFIRM=1 bash "${script}"; then
|
||||||
|
TIERS_COMPLETED+=("${name}")
|
||||||
|
log_info "--- ${name} complete ---"
|
||||||
|
else
|
||||||
|
TIERS_FAILED+=("${name}")
|
||||||
|
log_error "--- ${name} FAILED — stopping ---"
|
||||||
|
print_final_summary
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info "=== CE OS INSTALLER — FULL STACK ==="
|
||||||
|
log_info "Arch: ${ARCH} | Log: ${LOG_FILE}"
|
||||||
|
log_info ""
|
||||||
|
log_info " Tier 0 — Base : prerequisites, SSH, doas"
|
||||||
|
log_info " Tier 1 — Minimal : full TUI workstation"
|
||||||
|
log_info " Tier 2 — Basic : daily-use additions + GUI"
|
||||||
|
log_info ""
|
||||||
|
|
||||||
|
confirm_proceed
|
||||||
|
|
||||||
|
run_tier "Tier 0 — Base" "${SCRIPT_DIR}/ce-base.sh"
|
||||||
|
run_tier "Tier 1 — Minimal" "${SCRIPT_DIR}/ce-minimal.sh"
|
||||||
|
run_tier "Tier 2 — Basic" "${SCRIPT_DIR}/ce-basic.sh"
|
||||||
|
|
||||||
|
print_final_summary
|
||||||
|
log_info "=== INSTALL COMPLETE ==="
|
||||||
89
ce-minimal.sh
Normal file
89
ce-minimal.sh
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# ce-minimal.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/ce-minimal.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Tier 1 — Minimal. Full TUI workstation on top of Base.
|
||||||
|
# Standalone-runnable. Sources lib/ce-common.sh and lib/minimal/*.sh.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
SCRIPT_NAME="ce-minimal"
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" \
|
||||||
|
|| { echo "ERROR: lib/ce-common.sh not found"; exit 1; }
|
||||||
|
|
||||||
|
for _sub in "${SCRIPT_DIR}"/lib/minimal/*.sh; do
|
||||||
|
source "${_sub}" || { log_error "Failed to source: ${_sub}"; exit 1; }
|
||||||
|
done
|
||||||
|
unset _sub
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
local exit_code="${?}"
|
||||||
|
log_info "Running cleanup..."
|
||||||
|
doas apk cache clean >> "${LOG_FILE}" 2>&1 || true
|
||||||
|
doas rm -rf /var/cache/apk/* >> "${LOG_FILE}" 2>&1 || true
|
||||||
|
log_info "Cleanup complete (exit code: ${exit_code})"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT INT TERM HUP
|
||||||
|
|
||||||
|
phase_preflight() {
|
||||||
|
log_info "=== TIER 1: PRE-FLIGHT ==="
|
||||||
|
check_not_root
|
||||||
|
run_test "bash >= 4" '[[ "${BASH_VERSINFO[0]}" -ge 4 ]]' fail
|
||||||
|
run_test "arch detected" '[[ -n "${ARCH}" ]]' fail
|
||||||
|
run_test "doas present" 'command -v doas >/dev/null 2>&1' fail
|
||||||
|
run_test "Tier 0 complete" 'command -v curl >/dev/null 2>&1' fail
|
||||||
|
run_test "community repo enabled" 'grep -q "^[^#].*community" /etc/apk/repositories' fail
|
||||||
|
run_test "internet reachable" \
|
||||||
|
'curl --silent --max-time 10 --output /dev/null https://dl-cdn.alpinelinux.org' warn
|
||||||
|
run_test "disk >= 1GB" '[[ $(df / --output=avail | tail -1) -ge 1048576 ]]' warn
|
||||||
|
log_info "Arch: ${ARCH}"
|
||||||
|
log_info "Pre-flight complete"
|
||||||
|
}
|
||||||
|
|
||||||
|
phase_install() {
|
||||||
|
log_info "=== TIER 1: INSTALL ==="
|
||||||
|
doas apk update >> "${LOG_FILE}" 2>&1 \
|
||||||
|
&& doas apk upgrade >> "${LOG_FILE}" 2>&1 \
|
||||||
|
|| { log_error "apk update/upgrade failed"; exit 1; }
|
||||||
|
run_test "apk functional" 'apk search vim 2>/dev/null | grep -q "^vim"' fail
|
||||||
|
|
||||||
|
install_minimal_shell
|
||||||
|
install_minimal_terminal
|
||||||
|
install_minimal_files
|
||||||
|
install_minimal_vcs
|
||||||
|
install_minimal_systools
|
||||||
|
install_minimal_comms
|
||||||
|
install_minimal_journal
|
||||||
|
install_minimal_web
|
||||||
|
install_minimal_network
|
||||||
|
install_minimal_record
|
||||||
|
install_minimal_wiki
|
||||||
|
install_minimal_access
|
||||||
|
}
|
||||||
|
|
||||||
|
phase_verify() {
|
||||||
|
log_info "=== TIER 1: VERIFY ==="
|
||||||
|
local tools=(tmux vim git gpg python3 lynx)
|
||||||
|
for tool in "${tools[@]}"; do
|
||||||
|
run_test "${tool} present" "command -v ${tool} >/dev/null 2>&1" warn
|
||||||
|
done
|
||||||
|
run_test "pipx present" 'command -v pipx >/dev/null 2>&1' warn
|
||||||
|
run_test "espeak-ng present" 'command -v espeak-ng >/dev/null 2>&1' warn
|
||||||
|
log_info "Tier 1 verify complete"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info "=== CE OS INSTALLER — TIER 1: MINIMAL ==="
|
||||||
|
log_info "Arch: ${ARCH} | Log: ${LOG_FILE}"
|
||||||
|
|
||||||
|
phase_preflight
|
||||||
|
confirm_proceed
|
||||||
|
phase_install
|
||||||
|
phase_verify
|
||||||
|
print_summary
|
||||||
|
|
||||||
|
log_info "=== TIER 1 COMPLETE ==="
|
||||||
45
deploy.sh
Normal file
45
deploy.sh
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# deploy.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/deploy.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: workbench (Pi 5, Alpine aarch64) — run on workbench, not tinkerpad
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Copies the script tree to /srv/repo/scripts/alpine-ceos-installer/ so
|
||||||
|
# tinkerpad can fetch files via bootstrap.sh.
|
||||||
|
#
|
||||||
|
# TODO — Forgejo: when git.jahnet.lan is deployed, replace this script with:
|
||||||
|
# git push origin dev
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
DEPLOY_TARGET="/srv/repo/scripts/alpine-ceos-installer"
|
||||||
|
|
||||||
|
echo "CE OS Alpine Installer — Deploy to serve tree"
|
||||||
|
echo "Source: ${SCRIPT_DIR}"
|
||||||
|
echo "Target: ${DEPLOY_TARGET}"
|
||||||
|
echo ""
|
||||||
|
read -r -p "Proceed with deploy? [y/N] " confirm
|
||||||
|
[[ "${confirm,,}" == "y" ]] || { echo "Aborted."; exit 0; }
|
||||||
|
|
||||||
|
doas mkdir -p \
|
||||||
|
"${DEPLOY_TARGET}/lib/base" \
|
||||||
|
"${DEPLOY_TARGET}/lib/minimal" \
|
||||||
|
"${DEPLOY_TARGET}/lib/basic" \
|
||||||
|
"${DEPLOY_TARGET}/tests" \
|
||||||
|
"${DEPLOY_TARGET}/spec"
|
||||||
|
|
||||||
|
doas cp -r "${SCRIPT_DIR}/." "${DEPLOY_TARGET}/"
|
||||||
|
|
||||||
|
doas chmod +x \
|
||||||
|
"${DEPLOY_TARGET}"/*.sh \
|
||||||
|
"${DEPLOY_TARGET}"/lib/base/*.sh \
|
||||||
|
"${DEPLOY_TARGET}"/lib/minimal/*.sh \
|
||||||
|
"${DEPLOY_TARGET}"/lib/basic/*.sh \
|
||||||
|
"${DEPLOY_TARGET}"/tests/*.sh
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Deploy complete."
|
||||||
|
echo "Pull URL: http://192.168.0.138/repo/scripts/alpine-ceos-installer/"
|
||||||
|
echo "Bootstrap: wget http://192.168.0.138/repo/scripts/alpine-ceos-installer/bootstrap.sh"
|
||||||
57
lib/base/01_core.sh
Normal file
57
lib/base/01_core.sh
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 01_core.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/lib/base/01_core.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Base Tier subscript: enable community repo, install core packages,
|
||||||
|
# run setup-utmp. Defines install_base_core().
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
install_base_core() {
|
||||||
|
log_info "--- base/01_core: enabling community repo ---"
|
||||||
|
if grep -q "^[^#].*community" /etc/apk/repositories 2>/dev/null; then
|
||||||
|
log_info "Community repo already enabled — skipping"
|
||||||
|
else
|
||||||
|
doas sed -i '/^#.*community/s/^#//' /etc/apk/repositories \
|
||||||
|
>> "${LOG_FILE}" 2>&1 \
|
||||||
|
|| log_warn "Could not uncomment community repo — check /etc/apk/repositories"
|
||||||
|
run_test "community repo enabled" \
|
||||||
|
'grep -q "^[^#].*community" /etc/apk/repositories' warn
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "--- base/01_core: verifying core packages available ---"
|
||||||
|
local missing=()
|
||||||
|
for pkg in bash bash-completion ca-certificates curl openssh tzdata utmps; do
|
||||||
|
apk search "${pkg}" 2>/dev/null | grep -q "^${pkg}" \
|
||||||
|
|| missing+=("${pkg}")
|
||||||
|
done
|
||||||
|
if [[ ${#missing[@]} -gt 0 ]]; then
|
||||||
|
log_warn "Not found in repo search: ${missing[*]} — install may fail"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "--- base/01_core: installing core packages ---"
|
||||||
|
local -a PKGS=(bash bash-completion ca-certificates curl openssh tzdata utmps)
|
||||||
|
for pkg in "${PKGS[@]}"; do
|
||||||
|
install_pkg "${pkg}"
|
||||||
|
done
|
||||||
|
|
||||||
|
log_info "--- base/01_core: running setup-utmp ---"
|
||||||
|
if doas setup-utmp >> "${LOG_FILE}" 2>&1; then
|
||||||
|
log_info "setup-utmp complete"
|
||||||
|
else
|
||||||
|
log_warn "setup-utmp returned non-zero — may already be configured"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Standalone entry point
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
SCRIPT_NAME="01_core"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
check_not_root
|
||||||
|
install_base_core
|
||||||
|
print_summary
|
||||||
|
fi
|
||||||
46
lib/base/02_doas.sh
Normal file
46
lib/base/02_doas.sh
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 02_doas.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/lib/base/02_doas.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Base Tier subscript: install doas and write /etc/doas.d/wheel.conf.
|
||||||
|
# Defines install_base_doas().
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
install_base_doas() {
|
||||||
|
log_info "--- base/02_doas: installing doas ---"
|
||||||
|
install_pkg "doas"
|
||||||
|
|
||||||
|
run_test "doas binary present" 'command -v doas >/dev/null 2>&1' warn
|
||||||
|
|
||||||
|
log_info "--- base/02_doas: configuring /etc/doas.d/wheel.conf ---"
|
||||||
|
|
||||||
|
if ! doas test -d /etc/doas.d 2>/dev/null; then
|
||||||
|
doas mkdir -p /etc/doas.d >> "${LOG_FILE}" 2>&1 \
|
||||||
|
|| { log_warn "Could not create /etc/doas.d — skipping wheel.conf"; return 1; }
|
||||||
|
fi
|
||||||
|
|
||||||
|
if doas test -f /etc/doas.d/wheel.conf 2>/dev/null; then
|
||||||
|
log_info "/etc/doas.d/wheel.conf already present — skipping write"
|
||||||
|
else
|
||||||
|
printf 'permit persist :wheel\n' \
|
||||||
|
| doas tee /etc/doas.d/wheel.conf > /dev/null 2>&1 \
|
||||||
|
|| log_warn "Failed to write wheel.conf — configure manually: permit persist :wheel"
|
||||||
|
run_test "wheel.conf written" '[[ -s /etc/doas.d/wheel.conf ]]' warn
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "doas configuration complete"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Standalone entry point
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
SCRIPT_NAME="02_doas"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
check_not_root
|
||||||
|
install_base_doas
|
||||||
|
print_summary
|
||||||
|
fi
|
||||||
30
lib/basic/01_comms.sh
Normal file
30
lib/basic/01_comms.sh
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 01_comms.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/lib/basic/01_comms.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Basic Tier subscript: communication additions — abook, task (taskwarrior).
|
||||||
|
# Defines install_basic_comms().
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
install_basic_comms() {
|
||||||
|
log_info "--- basic/01_comms: communication additions ---"
|
||||||
|
local -a PKGS=(abook task)
|
||||||
|
for pkg in "${PKGS[@]}"; do
|
||||||
|
install_pkg "${pkg}"
|
||||||
|
done
|
||||||
|
run_test "abook present" 'command -v abook >/dev/null 2>&1' warn
|
||||||
|
run_test "task present" 'command -v task >/dev/null 2>&1' warn
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
SCRIPT_NAME="basic_01_comms"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
check_not_root
|
||||||
|
install_basic_comms
|
||||||
|
print_summary
|
||||||
|
fi
|
||||||
41
lib/basic/02_monitor.sh
Normal file
41
lib/basic/02_monitor.sh
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 02_monitor.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/lib/basic/02_monitor.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Basic Tier subscript: system monitoring — nethogs, iproute2, lnav (@edge).
|
||||||
|
# lnav is an edge package; failure is logged and marked for manual follow-up.
|
||||||
|
# Defines install_basic_monitor().
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
install_basic_monitor() {
|
||||||
|
log_info "--- basic/02_monitor: system monitoring ---"
|
||||||
|
|
||||||
|
local -a PKGS=(nethogs iproute2)
|
||||||
|
for pkg in "${PKGS[@]}"; do
|
||||||
|
install_pkg "${pkg}"
|
||||||
|
done
|
||||||
|
|
||||||
|
run_test "ip present" 'command -v ip >/dev/null 2>&1' warn
|
||||||
|
|
||||||
|
# lnav — edge package, warn-on-fail
|
||||||
|
ensure_edge_repo
|
||||||
|
log_info "Installing lnav from @edge..."
|
||||||
|
if install_pkg_edge "lnav"; then
|
||||||
|
run_test "lnav present" 'command -v lnav >/dev/null 2>&1' warn
|
||||||
|
else
|
||||||
|
MANUAL_PKGS+=("lnav: edge install failed — requires manual verification")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
SCRIPT_NAME="basic_02_monitor"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
check_not_root
|
||||||
|
install_basic_monitor
|
||||||
|
print_summary
|
||||||
|
fi
|
||||||
38
lib/basic/03_gui.sh
Normal file
38
lib/basic/03_gui.sh
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 03_gui.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/lib/basic/03_gui.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Basic Tier subscript: GUI layer — cagebreak, firefox, xwayland.
|
||||||
|
# Supported on x86-64. Boot config modification (/boot/cmdline.txt) is
|
||||||
|
# RPi-only and is explicitly skipped on x86-64 hardware.
|
||||||
|
# Defines install_basic_gui().
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
install_basic_gui() {
|
||||||
|
log_info "--- basic/03_gui: GUI layer (Wayland + Firefox) ---"
|
||||||
|
log_info "Arch: ${ARCH} — GUI supported on x86-64"
|
||||||
|
|
||||||
|
local -a PKGS=(cagebreak firefox xwayland)
|
||||||
|
for pkg in "${PKGS[@]}"; do
|
||||||
|
install_pkg "${pkg}"
|
||||||
|
done
|
||||||
|
|
||||||
|
run_test "cagebreak present" 'command -v cagebreak >/dev/null 2>&1' warn
|
||||||
|
run_test "firefox present" 'command -v firefox >/dev/null 2>&1' warn
|
||||||
|
|
||||||
|
# /boot/cmdline.txt and /boot/extlinux.conf modifications are RPi-only
|
||||||
|
log_info "NOTICE: /boot/cmdline.txt modification skipped — not applicable on x86-64 (ThinkPad)"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
SCRIPT_NAME="basic_03_gui"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
check_not_root
|
||||||
|
install_basic_gui
|
||||||
|
print_summary
|
||||||
|
fi
|
||||||
34
lib/basic/04_media.sh
Normal file
34
lib/basic/04_media.sh
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 04_media.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/lib/basic/04_media.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Basic Tier subscript: media — cmus, mpv, ffmpeg, imagemagick, fbida-fbi.
|
||||||
|
# Note: package is 'fbida-fbi' (not 'fbida').
|
||||||
|
# Defines install_basic_media().
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
install_basic_media() {
|
||||||
|
log_info "--- basic/04_media: media tools ---"
|
||||||
|
local -a PKGS=(cmus mpv ffmpeg imagemagick fbida-fbi)
|
||||||
|
for pkg in "${PKGS[@]}"; do
|
||||||
|
install_pkg "${pkg}"
|
||||||
|
done
|
||||||
|
run_test "cmus present" 'command -v cmus >/dev/null 2>&1' warn
|
||||||
|
run_test "mpv present" 'command -v mpv >/dev/null 2>&1' warn
|
||||||
|
run_test "ffmpeg present" 'command -v ffmpeg >/dev/null 2>&1' warn
|
||||||
|
run_test "convert present" 'command -v convert >/dev/null 2>&1' warn
|
||||||
|
run_test "fbi present" 'command -v fbi >/dev/null 2>&1' warn
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
SCRIPT_NAME="basic_04_media"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
check_not_root
|
||||||
|
install_basic_media
|
||||||
|
print_summary
|
||||||
|
fi
|
||||||
40
lib/basic/05_fonts.sh
Normal file
40
lib/basic/05_fonts.sh
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 05_fonts.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/lib/basic/05_fonts.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Basic Tier subscript: fonts and display — figlet, nerd-fonts (@edge).
|
||||||
|
# nerd-fonts package name confirmed via pkgs.alpinelinux.org (edge/community).
|
||||||
|
# Installs with @edge tag; gracefully skips on failure.
|
||||||
|
# Defines install_basic_fonts().
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
install_basic_fonts() {
|
||||||
|
log_info "--- basic/05_fonts: fonts and display ---"
|
||||||
|
|
||||||
|
install_pkg "figlet"
|
||||||
|
run_test "figlet present" 'command -v figlet >/dev/null 2>&1' warn
|
||||||
|
|
||||||
|
# nerd-fonts — edge/community; package name verified as 'nerd-fonts'
|
||||||
|
ensure_edge_repo
|
||||||
|
|
||||||
|
log_info "Installing nerd-fonts from @edge/community..."
|
||||||
|
if install_pkg_edge "nerd-fonts"; then
|
||||||
|
log_info "nerd-fonts installed"
|
||||||
|
else
|
||||||
|
log_warn "nerd-fonts@edge not available — skipping gracefully"
|
||||||
|
MANUAL_PKGS+=("nerd-fonts: edge install failed — check pkgs.alpinelinux.org/packages?name=nerd-fonts&branch=edge")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
SCRIPT_NAME="basic_05_fonts"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
check_not_root
|
||||||
|
install_basic_fonts
|
||||||
|
print_summary
|
||||||
|
fi
|
||||||
36
lib/basic/06_utils.sh
Normal file
36
lib/basic/06_utils.sh
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 06_utils.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/lib/basic/06_utils.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Basic Tier subscript: additional utilities —
|
||||||
|
# tealdeer, restic, jq, bc, zip, unzip, xz.
|
||||||
|
# Note: package name is 'tealdeer' (not 'tldr').
|
||||||
|
# Defines install_basic_utils().
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
install_basic_utils() {
|
||||||
|
log_info "--- basic/06_utils: additional utilities ---"
|
||||||
|
local -a PKGS=(tealdeer restic jq bc zip unzip xz)
|
||||||
|
for pkg in "${PKGS[@]}"; do
|
||||||
|
install_pkg "${pkg}"
|
||||||
|
done
|
||||||
|
run_test "tldr present" 'command -v tldr >/dev/null 2>&1' warn
|
||||||
|
run_test "restic present" 'command -v restic >/dev/null 2>&1' warn
|
||||||
|
run_test "jq present" 'command -v jq >/dev/null 2>&1' warn
|
||||||
|
run_test "bc present" 'command -v bc >/dev/null 2>&1' warn
|
||||||
|
run_test "zip present" 'command -v zip >/dev/null 2>&1' warn
|
||||||
|
run_test "xz present" 'command -v xz >/dev/null 2>&1' warn
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
SCRIPT_NAME="basic_06_utils"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
check_not_root
|
||||||
|
install_basic_utils
|
||||||
|
print_summary
|
||||||
|
fi
|
||||||
151
lib/ce-common.sh
Normal file
151
lib/ce-common.sh
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# ce-common.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/lib/ce-common.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Shared library: logging, run_test, package helpers, common guards.
|
||||||
|
# Source this file; do not execute directly.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Guard against double-sourcing
|
||||||
|
[[ -n "${CE_COMMON_LOADED:-}" ]] && return 0
|
||||||
|
CE_COMMON_LOADED=1
|
||||||
|
|
||||||
|
# Architecture detection
|
||||||
|
ARCH="$(uname -m)"
|
||||||
|
|
||||||
|
# Logging setup — SCRIPT_NAME must be set before sourcing
|
||||||
|
LOG_DIR="${HOME}/.local/logs/ceos_installer"
|
||||||
|
SCRIPT_NAME="${SCRIPT_NAME:-ce-common}"
|
||||||
|
LOG_FILE="${LOG_DIR}/${SCRIPT_NAME}-$(date +%Y-%m-%d).log"
|
||||||
|
|
||||||
|
# Failed packages tracker — shared across all sourced subscripts
|
||||||
|
FAILED_PKGS=()
|
||||||
|
|
||||||
|
# Packages requiring manual follow-up (distinct from simple failures)
|
||||||
|
MANUAL_PKGS=()
|
||||||
|
|
||||||
|
mkdir -p "${LOG_DIR}"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Logging
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
_log() {
|
||||||
|
local level="${1}" msg="${2}" ts
|
||||||
|
ts="$(date '+%Y-%m-%d %H:%M:%S')"
|
||||||
|
printf '[%s] [%s] %s\n' "${ts}" "${level}" "${msg}" | tee -a "${LOG_FILE}"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info() { _log "INFO " "${1}"; }
|
||||||
|
log_warn() { _log "WARN " "${1}" >&2; }
|
||||||
|
log_error() { _log "ERROR" "${1}" >&2; }
|
||||||
|
log_debug() { [[ "${CE_DEBUG:-0}" == "1" ]] && _log "DEBUG" "${1}"; }
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# run_test <description> <test_expression> <fail|warn>
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
run_test() {
|
||||||
|
local desc="${1}" cmd="${2}" mode="${3:-fail}"
|
||||||
|
|
||||||
|
if eval "${cmd}" >/dev/null 2>&1; then
|
||||||
|
log_info " PASS: ${desc}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "${mode}" == "fail" ]]; then
|
||||||
|
log_error " FAIL: ${desc}"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
log_warn " WARN: ${desc}"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Package helpers
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# install_pkg <package> — one package, logs result
|
||||||
|
install_pkg() {
|
||||||
|
local pkg="${1}"
|
||||||
|
if doas apk add "${pkg}" >> "${LOG_FILE}" 2>&1; then
|
||||||
|
log_info "Installed: ${pkg}"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log_warn "FAILED: ${pkg} — logged, continuing"
|
||||||
|
FAILED_PKGS+=("${pkg}")
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# install_pkg_edge <package> — install with @edge tag, warn on fail
|
||||||
|
install_pkg_edge() {
|
||||||
|
local pkg="${1}"
|
||||||
|
if doas apk add "${pkg}@edge" >> "${LOG_FILE}" 2>&1; then
|
||||||
|
log_info "Installed: ${pkg} (@edge)"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
log_warn "FAILED: ${pkg}@edge — manual verification required"
|
||||||
|
FAILED_PKGS+=("${pkg} (edge, manual verification required)")
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ensure_edge_repo — add @edge community tag if not present
|
||||||
|
ensure_edge_repo() {
|
||||||
|
if ! grep -q "^@edge" /etc/apk/repositories 2>/dev/null; then
|
||||||
|
log_info "Adding @edge community tag to /etc/apk/repositories"
|
||||||
|
printf '@edge https://dl-cdn.alpinelinux.org/alpine/edge/community\n' \
|
||||||
|
| doas tee -a /etc/apk/repositories > /dev/null 2>&1 \
|
||||||
|
|| log_warn "Could not add @edge tag — edge installs may fail"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Guards and prompts
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# check_not_root — exit if accidentally run as root
|
||||||
|
check_not_root() {
|
||||||
|
if [[ "${EUID}" -eq 0 ]]; then
|
||||||
|
echo "ERROR: Do not run as root. Run as a standard user with doas configured." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# confirm_proceed — single prompt; bypassed when CE_INSTALL_NO_CONFIRM=1
|
||||||
|
confirm_proceed() {
|
||||||
|
[[ "${CE_INSTALL_NO_CONFIRM:-0}" == "1" ]] && return 0
|
||||||
|
echo ""
|
||||||
|
read -r -p "Proceed with installation? [y/N] " confirm
|
||||||
|
[[ "${confirm,,}" == "y" ]] || { log_info "Aborted by user."; exit 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Summary
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
print_summary() {
|
||||||
|
log_info "=== INSTALLATION SUMMARY ==="
|
||||||
|
if [[ ${#FAILED_PKGS[@]} -eq 0 && ${#MANUAL_PKGS[@]} -eq 0 ]]; then
|
||||||
|
log_info "All packages installed successfully."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ ${#FAILED_PKGS[@]} -gt 0 ]]; then
|
||||||
|
log_warn "Failed or skipped packages:"
|
||||||
|
for pkg in "${FAILED_PKGS[@]}"; do
|
||||||
|
log_warn " - ${pkg}"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
if [[ ${#MANUAL_PKGS[@]} -gt 0 ]]; then
|
||||||
|
log_warn "Requires manual follow-up:"
|
||||||
|
for item in "${MANUAL_PKGS[@]}"; do
|
||||||
|
log_warn " - ${item}"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
}
|
||||||
30
lib/minimal/01_shell.sh
Normal file
30
lib/minimal/01_shell.sh
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 01_shell.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/lib/minimal/01_shell.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Minimal Tier subscript: shell environment — starship, less, mandoc, ncurses.
|
||||||
|
# Defines install_minimal_shell().
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
install_minimal_shell() {
|
||||||
|
log_info "--- minimal/01_shell: shell environment ---"
|
||||||
|
local -a PKGS=(starship less mandoc ncurses)
|
||||||
|
for pkg in "${PKGS[@]}"; do
|
||||||
|
install_pkg "${pkg}"
|
||||||
|
done
|
||||||
|
run_test "less present" 'command -v less >/dev/null 2>&1' warn
|
||||||
|
run_test "starship present" 'command -v starship >/dev/null 2>&1' warn
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
SCRIPT_NAME="01_shell"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
check_not_root
|
||||||
|
install_minimal_shell
|
||||||
|
print_summary
|
||||||
|
fi
|
||||||
31
lib/minimal/02_terminal.sh
Normal file
31
lib/minimal/02_terminal.sh
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 02_terminal.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/lib/minimal/02_terminal.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Minimal Tier subscript: terminal environment — tmux, vim, nano.
|
||||||
|
# Defines install_minimal_terminal().
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
install_minimal_terminal() {
|
||||||
|
log_info "--- minimal/02_terminal: terminal environment ---"
|
||||||
|
local -a PKGS=(tmux vim nano)
|
||||||
|
for pkg in "${PKGS[@]}"; do
|
||||||
|
install_pkg "${pkg}"
|
||||||
|
done
|
||||||
|
run_test "tmux present" 'command -v tmux >/dev/null 2>&1' warn
|
||||||
|
run_test "vim present" 'command -v vim >/dev/null 2>&1' warn
|
||||||
|
run_test "nano present" 'command -v nano >/dev/null 2>&1' warn
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
SCRIPT_NAME="02_terminal"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
check_not_root
|
||||||
|
install_minimal_terminal
|
||||||
|
print_summary
|
||||||
|
fi
|
||||||
31
lib/minimal/03_files.sh
Normal file
31
lib/minimal/03_files.sh
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 03_files.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/lib/minimal/03_files.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Minimal Tier subscript: file management — ranger, mc, ncdu, fzf, zoxide, stow.
|
||||||
|
# Defines install_minimal_files().
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
install_minimal_files() {
|
||||||
|
log_info "--- minimal/03_files: file management ---"
|
||||||
|
local -a PKGS=(ranger mc ncdu fzf zoxide stow)
|
||||||
|
for pkg in "${PKGS[@]}"; do
|
||||||
|
install_pkg "${pkg}"
|
||||||
|
done
|
||||||
|
run_test "ranger present" 'command -v ranger >/dev/null 2>&1' warn
|
||||||
|
run_test "fzf present" 'command -v fzf >/dev/null 2>&1' warn
|
||||||
|
run_test "zoxide present" 'command -v zoxide >/dev/null 2>&1' warn
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
SCRIPT_NAME="03_files"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
check_not_root
|
||||||
|
install_minimal_files
|
||||||
|
print_summary
|
||||||
|
fi
|
||||||
31
lib/minimal/04_vcs.sh
Normal file
31
lib/minimal/04_vcs.sh
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 04_vcs.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/lib/minimal/04_vcs.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Minimal Tier subscript: version control and transfer — git, rsync, openssl, gnupg.
|
||||||
|
# Defines install_minimal_vcs().
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
install_minimal_vcs() {
|
||||||
|
log_info "--- minimal/04_vcs: version control and transfer ---"
|
||||||
|
local -a PKGS=(git rsync openssl gnupg)
|
||||||
|
for pkg in "${PKGS[@]}"; do
|
||||||
|
install_pkg "${pkg}"
|
||||||
|
done
|
||||||
|
run_test "git present" 'command -v git >/dev/null 2>&1' warn
|
||||||
|
run_test "rsync present" 'command -v rsync >/dev/null 2>&1' warn
|
||||||
|
run_test "gpg present" 'command -v gpg >/dev/null 2>&1' warn
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
SCRIPT_NAME="04_vcs"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
check_not_root
|
||||||
|
install_minimal_vcs
|
||||||
|
print_summary
|
||||||
|
fi
|
||||||
31
lib/minimal/05_systools.sh
Normal file
31
lib/minimal/05_systools.sh
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 05_systools.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/lib/minimal/05_systools.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Minimal Tier subscript: system tools — btop, lsof, mosh, dialog.
|
||||||
|
# Defines install_minimal_systools().
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
install_minimal_systools() {
|
||||||
|
log_info "--- minimal/05_systools: system tools ---"
|
||||||
|
local -a PKGS=(btop lsof mosh dialog)
|
||||||
|
for pkg in "${PKGS[@]}"; do
|
||||||
|
install_pkg "${pkg}"
|
||||||
|
done
|
||||||
|
run_test "btop present" 'command -v btop >/dev/null 2>&1' warn
|
||||||
|
run_test "lsof present" 'command -v lsof >/dev/null 2>&1' warn
|
||||||
|
run_test "dialog present" 'command -v dialog >/dev/null 2>&1' warn
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
SCRIPT_NAME="05_systools"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
check_not_root
|
||||||
|
install_minimal_systools
|
||||||
|
print_summary
|
||||||
|
fi
|
||||||
34
lib/minimal/06_comms.sh
Normal file
34
lib/minimal/06_comms.sh
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 06_comms.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/lib/minimal/06_comms.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Minimal Tier subscript: communication and organisation —
|
||||||
|
# neomutt, msmtp, isync (mbsync), calcurse, pass.
|
||||||
|
# Defines install_minimal_comms().
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
install_minimal_comms() {
|
||||||
|
log_info "--- minimal/06_comms: communication and organisation ---"
|
||||||
|
local -a PKGS=(neomutt msmtp isync calcurse pass)
|
||||||
|
for pkg in "${PKGS[@]}"; do
|
||||||
|
install_pkg "${pkg}"
|
||||||
|
done
|
||||||
|
run_test "neomutt present" 'command -v neomutt >/dev/null 2>&1' warn
|
||||||
|
run_test "msmtp present" 'command -v msmtp >/dev/null 2>&1' warn
|
||||||
|
run_test "mbsync present" 'command -v mbsync >/dev/null 2>&1' warn
|
||||||
|
run_test "calcurse present" 'command -v calcurse >/dev/null 2>&1' warn
|
||||||
|
run_test "pass present" 'command -v pass >/dev/null 2>&1' warn
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
SCRIPT_NAME="06_comms"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
check_not_root
|
||||||
|
install_minimal_comms
|
||||||
|
print_summary
|
||||||
|
fi
|
||||||
46
lib/minimal/07_journal.sh
Normal file
46
lib/minimal/07_journal.sh
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 07_journal.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/lib/minimal/07_journal.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Minimal Tier subscript: journal — python3, pipx (apk), jrnl (pipx install).
|
||||||
|
# Defines install_minimal_journal().
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
install_minimal_journal() {
|
||||||
|
log_info "--- minimal/07_journal: python3, pipx, jrnl ---"
|
||||||
|
|
||||||
|
install_pkg "python3"
|
||||||
|
install_pkg "pipx"
|
||||||
|
|
||||||
|
run_test "python3 present" 'command -v python3 >/dev/null 2>&1' warn
|
||||||
|
run_test "pipx present" 'command -v pipx >/dev/null 2>&1' warn
|
||||||
|
|
||||||
|
if ! command -v pipx >/dev/null 2>&1; then
|
||||||
|
log_warn "pipx not available — skipping jrnl install"
|
||||||
|
FAILED_PKGS+=("jrnl (skipped: pipx not available)")
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Installing jrnl via pipx..."
|
||||||
|
if pipx install jrnl >> "${LOG_FILE}" 2>&1; then
|
||||||
|
log_info "Installed: jrnl (via pipx)"
|
||||||
|
else
|
||||||
|
log_warn "FAILED: jrnl (pipx install) — logged, continuing"
|
||||||
|
FAILED_PKGS+=("jrnl (pipx install failed)")
|
||||||
|
fi
|
||||||
|
|
||||||
|
run_test "jrnl present" 'command -v jrnl >/dev/null 2>&1' warn
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
SCRIPT_NAME="07_journal"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
check_not_root
|
||||||
|
install_minimal_journal
|
||||||
|
print_summary
|
||||||
|
fi
|
||||||
30
lib/minimal/08_web.sh
Normal file
30
lib/minimal/08_web.sh
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 08_web.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/lib/minimal/08_web.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Minimal Tier subscript: web clients — lynx, w3m.
|
||||||
|
# Defines install_minimal_web().
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
install_minimal_web() {
|
||||||
|
log_info "--- minimal/08_web: web clients ---"
|
||||||
|
local -a PKGS=(lynx w3m)
|
||||||
|
for pkg in "${PKGS[@]}"; do
|
||||||
|
install_pkg "${pkg}"
|
||||||
|
done
|
||||||
|
run_test "lynx present" 'command -v lynx >/dev/null 2>&1' warn
|
||||||
|
run_test "w3m present" 'command -v w3m >/dev/null 2>&1' warn
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
SCRIPT_NAME="08_web"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
check_not_root
|
||||||
|
install_minimal_web
|
||||||
|
print_summary
|
||||||
|
fi
|
||||||
45
lib/minimal/09_network.sh
Normal file
45
lib/minimal/09_network.sh
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 09_network.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/lib/minimal/09_network.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Minimal Tier subscript: network services — dbus, avahi.
|
||||||
|
# Enables both services in the default OpenRC runlevel.
|
||||||
|
# Defines install_minimal_network().
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
install_minimal_network() {
|
||||||
|
log_info "--- minimal/09_network: dbus and avahi ---"
|
||||||
|
|
||||||
|
install_pkg "dbus"
|
||||||
|
install_pkg "avahi"
|
||||||
|
|
||||||
|
run_test "dbus present" 'command -v dbus-daemon >/dev/null 2>&1' warn
|
||||||
|
run_test "avahi-daemon present" 'command -v avahi-daemon >/dev/null 2>&1' warn
|
||||||
|
|
||||||
|
log_info "Enabling dbus and avahi-daemon in OpenRC default runlevel..."
|
||||||
|
|
||||||
|
if command -v rc-update >/dev/null 2>&1; then
|
||||||
|
doas rc-update add dbus default >> "${LOG_FILE}" 2>&1 \
|
||||||
|
&& log_info "rc-update: dbus added to default" \
|
||||||
|
|| log_warn "rc-update add dbus failed — enable manually"
|
||||||
|
doas rc-update add avahi-daemon default >> "${LOG_FILE}" 2>&1 \
|
||||||
|
&& log_info "rc-update: avahi-daemon added to default" \
|
||||||
|
|| log_warn "rc-update add avahi-daemon failed — enable manually"
|
||||||
|
else
|
||||||
|
log_warn "rc-update not found — skipping service enablement"
|
||||||
|
MANUAL_PKGS+=("dbus + avahi-daemon: enable with rc-update add <svc> default")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
SCRIPT_NAME="09_network"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
check_not_root
|
||||||
|
install_minimal_network
|
||||||
|
print_summary
|
||||||
|
fi
|
||||||
30
lib/minimal/10_record.sh
Normal file
30
lib/minimal/10_record.sh
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 10_record.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/lib/minimal/10_record.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Minimal Tier subscript: recording and screenshot — asciinema, fbgrab.
|
||||||
|
# Defines install_minimal_record().
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
install_minimal_record() {
|
||||||
|
log_info "--- minimal/10_record: recording and screenshot ---"
|
||||||
|
local -a PKGS=(asciinema fbgrab)
|
||||||
|
for pkg in "${PKGS[@]}"; do
|
||||||
|
install_pkg "${pkg}"
|
||||||
|
done
|
||||||
|
run_test "asciinema present" 'command -v asciinema >/dev/null 2>&1' warn
|
||||||
|
run_test "fbgrab present" 'command -v fbgrab >/dev/null 2>&1' warn
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
SCRIPT_NAME="10_record"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
check_not_root
|
||||||
|
install_minimal_record
|
||||||
|
print_summary
|
||||||
|
fi
|
||||||
87
lib/minimal/11_wiki.sh
Normal file
87
lib/minimal/11_wiki.sh
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 11_wiki.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/lib/minimal/11_wiki.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Minimal Tier subscript: wiki viewer — glow from CE repo.
|
||||||
|
# Downloads binary, verifies sha256 and GPG signature, installs to ~/.local/bin/.
|
||||||
|
# Gracefully skips if CE repo is unreachable or verification fails.
|
||||||
|
# Defines install_minimal_wiki().
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
install_minimal_wiki() {
|
||||||
|
log_info "--- minimal/11_wiki: glow (CE repo) ---"
|
||||||
|
|
||||||
|
local CE_REPO="https://cervelloelettrico.it/bin/amd64/alpine"
|
||||||
|
local INSTALL_DIR="${HOME}/.local/bin"
|
||||||
|
local TEMP_DIR
|
||||||
|
TEMP_DIR="$(mktemp -d /tmp/ce-glow-XXXXXX)"
|
||||||
|
|
||||||
|
mkdir -p "${INSTALL_DIR}"
|
||||||
|
|
||||||
|
# Abort cleanly if CE repo unreachable
|
||||||
|
if ! curl --silent --max-time 10 --output /dev/null "${CE_REPO}/glow"; then
|
||||||
|
log_warn "CE repo unreachable (${CE_REPO}) — skipping glow"
|
||||||
|
FAILED_PKGS+=("glow (CE repo unreachable)")
|
||||||
|
rm -rf "${TEMP_DIR}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log_info "Downloading glow, sha256, and sig from CE repo..."
|
||||||
|
local ok=1
|
||||||
|
curl --silent --max-time 30 -o "${TEMP_DIR}/glow" "${CE_REPO}/glow" || ok=0
|
||||||
|
curl --silent --max-time 10 -o "${TEMP_DIR}/glow.sha256" "${CE_REPO}/glow.sha256" || ok=0
|
||||||
|
curl --silent --max-time 10 -o "${TEMP_DIR}/glow.sig" "${CE_REPO}/glow.sig" || ok=0
|
||||||
|
|
||||||
|
if [[ "${ok}" -eq 0 ]]; then
|
||||||
|
log_warn "glow download incomplete — skipping install"
|
||||||
|
FAILED_PKGS+=("glow (download failed)")
|
||||||
|
rm -rf "${TEMP_DIR}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# SHA256 verification
|
||||||
|
local expected actual
|
||||||
|
expected="$(awk '{print $1}' "${TEMP_DIR}/glow.sha256")"
|
||||||
|
actual="$(sha256sum "${TEMP_DIR}/glow" | awk '{print $1}')"
|
||||||
|
if [[ "${expected}" != "${actual}" ]]; then
|
||||||
|
log_warn "glow SHA256 mismatch — aborting (expected: ${expected}, got: ${actual})"
|
||||||
|
FAILED_PKGS+=("glow (sha256 mismatch)")
|
||||||
|
rm -rf "${TEMP_DIR}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
log_info "glow SHA256 verified"
|
||||||
|
|
||||||
|
# GPG signature verification (gnupg available from 04_vcs)
|
||||||
|
if command -v gpg >/dev/null 2>&1; then
|
||||||
|
if gpg --verify "${TEMP_DIR}/glow.sig" "${TEMP_DIR}/glow" >> "${LOG_FILE}" 2>&1; then
|
||||||
|
log_info "glow GPG signature verified"
|
||||||
|
else
|
||||||
|
log_warn "glow GPG verification failed — aborting install"
|
||||||
|
FAILED_PKGS+=("glow (gpg verify failed)")
|
||||||
|
rm -rf "${TEMP_DIR}"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
log_warn "gpg not available — proceeding with sha256-only verification"
|
||||||
|
fi
|
||||||
|
|
||||||
|
chmod +x "${TEMP_DIR}/glow"
|
||||||
|
cp "${TEMP_DIR}/glow" "${INSTALL_DIR}/glow"
|
||||||
|
rm -rf "${TEMP_DIR}"
|
||||||
|
|
||||||
|
run_test "glow installed" 'command -v glow >/dev/null 2>&1' warn
|
||||||
|
log_info "glow installed to ${INSTALL_DIR}/glow"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
SCRIPT_NAME="11_wiki"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
check_not_root
|
||||||
|
install_minimal_wiki
|
||||||
|
print_summary
|
||||||
|
fi
|
||||||
56
lib/minimal/12_access.sh
Normal file
56
lib/minimal/12_access.sh
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# 12_access.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/lib/minimal/12_access.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Minimal Tier subscript: accessibility — espeak-ng, espeakup, speech-dispatcher.
|
||||||
|
# espeak conflict trap: removes espeak before installing espeak-ng.
|
||||||
|
# espeakup: repo-verified before install (package availability varies by release).
|
||||||
|
# speech-dispatcher: @edge, warn-on-fail.
|
||||||
|
# ly login manager: aarch64 CE repo only — SKIPPED on x86-64 with notice.
|
||||||
|
# Defines install_minimal_access().
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
install_minimal_access() {
|
||||||
|
log_info "--- minimal/12_access: accessibility ---"
|
||||||
|
|
||||||
|
# espeak conflict trap — must run before espeak-ng install
|
||||||
|
if doas apk info -e espeak >> "${LOG_FILE}" 2>&1; then
|
||||||
|
log_info "Removing conflicting 'espeak' package before installing espeak-ng..."
|
||||||
|
doas apk del espeak >> "${LOG_FILE}" 2>&1 \
|
||||||
|
|| log_warn "Could not remove espeak — espeak-ng may conflict"
|
||||||
|
fi
|
||||||
|
|
||||||
|
install_pkg "espeak-ng"
|
||||||
|
run_test "espeak-ng present" 'command -v espeak-ng >/dev/null 2>&1' warn
|
||||||
|
|
||||||
|
# espeakup — verify package exists in current repo before attempting install
|
||||||
|
if apk search espeakup 2>/dev/null | grep -q "^espeakup"; then
|
||||||
|
install_pkg "espeakup"
|
||||||
|
else
|
||||||
|
log_warn "espeakup not found in current Alpine repos — skipping"
|
||||||
|
MANUAL_PKGS+=("espeakup: not found in Alpine v3.23 — check https://github.com/dermotbradley/alpine-espeakup")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# speech-dispatcher — edge package
|
||||||
|
ensure_edge_repo
|
||||||
|
install_pkg_edge "speech-dispatcher"
|
||||||
|
|
||||||
|
# ly login manager — CE repo, aarch64 only — skip on x86-64
|
||||||
|
if [[ "${ARCH}" == "x86_64" ]]; then
|
||||||
|
log_info "NOTICE: ly login manager — CE repo aarch64 only, not available for x86-64 — skipped"
|
||||||
|
MANUAL_PKGS+=("ly: aarch64 CE repo only — not available for x86-64 (${ARCH})")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||||
|
SCRIPT_NAME="12_access"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
check_not_root
|
||||||
|
install_minimal_access
|
||||||
|
print_summary
|
||||||
|
fi
|
||||||
132
spec/install_spec.md
Normal file
132
spec/install_spec.md
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
# CE OS Alpine Installer — Subscript Specifications
|
||||||
|
<!-- ~/projects/ceos/alpine-installer/spec/install_spec.md -->
|
||||||
|
<!-- Version: v0.0.1 | Status: DEVELOPMENT -->
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## lib/ce-common.sh
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Name | ce-common.sh — shared library |
|
||||||
|
| Inputs | SCRIPT_NAME env var (set by caller before sourcing) |
|
||||||
|
| Outputs | LOG_FILE written, FAILED_PKGS/MANUAL_PKGS arrays populated |
|
||||||
|
| Error behaviour | run_test exits on fail mode; all others log-and-continue |
|
||||||
|
| Privilege | standard user (doas called internally by install_pkg) |
|
||||||
|
| Distro target | Alpine |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## lib/base/01_core.sh
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Name | 01_core.sh — enable community repo, core packages, setup-utmp |
|
||||||
|
| Inputs | none (sources ce-common.sh) |
|
||||||
|
| Outputs | packages installed; /etc/apk/repositories modified |
|
||||||
|
| Error behaviour | per-package log-and-continue; setup-utmp warn-only |
|
||||||
|
| Privilege | requires doas for apk and sed on /etc/apk/repositories |
|
||||||
|
| Distro target | Alpine |
|
||||||
|
|
||||||
|
## lib/base/02_doas.sh
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Name | 02_doas.sh — doas install and wheel.conf |
|
||||||
|
| Inputs | none |
|
||||||
|
| Outputs | doas installed; /etc/doas.d/wheel.conf written |
|
||||||
|
| Error behaviour | warn if wheel.conf write fails — manual action noted |
|
||||||
|
| Privilege | requires doas for mkdir/tee on /etc/doas.d |
|
||||||
|
| Distro target | Alpine |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## lib/minimal/01_shell.sh — 06_comms.sh
|
||||||
|
|
||||||
|
Standard pattern: apk community packages, per-package install_pkg, run_test warn.
|
||||||
|
|
||||||
|
| Script | Packages |
|
||||||
|
|---|---|
|
||||||
|
| 01_shell.sh | starship, less, mandoc, ncurses |
|
||||||
|
| 02_terminal.sh | tmux, vim, nano |
|
||||||
|
| 03_files.sh | ranger, mc, ncdu, fzf, zoxide, stow |
|
||||||
|
| 04_vcs.sh | git, rsync, openssl, gnupg |
|
||||||
|
| 05_systools.sh | btop, lsof, mosh, dialog |
|
||||||
|
| 06_comms.sh | neomutt, msmtp, isync, calcurse, pass |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## lib/minimal/07_journal.sh
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Name | 07_journal.sh — python3, pipx, jrnl |
|
||||||
|
| Inputs | none |
|
||||||
|
| Outputs | python3 + pipx via apk; jrnl via pipx install |
|
||||||
|
| Error behaviour | skips jrnl if pipx unavailable; logs failure |
|
||||||
|
| Privilege | standard user (pipx runs as user) |
|
||||||
|
| Distro target | Alpine |
|
||||||
|
|
||||||
|
## lib/minimal/08_web.sh
|
||||||
|
|
||||||
|
Standard pattern: lynx, w3m.
|
||||||
|
|
||||||
|
## lib/minimal/09_network.sh
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Name | 09_network.sh — dbus, avahi + rc-update |
|
||||||
|
| Inputs | none |
|
||||||
|
| Outputs | dbus + avahi installed; both added to default runlevel |
|
||||||
|
| Error behaviour | rc-update failures warn-only; logged to MANUAL_PKGS |
|
||||||
|
| Privilege | doas for apk + rc-update |
|
||||||
|
| Distro target | Alpine + OpenRC |
|
||||||
|
|
||||||
|
## lib/minimal/10_record.sh
|
||||||
|
|
||||||
|
Standard pattern: asciinema, fbgrab.
|
||||||
|
|
||||||
|
## lib/minimal/11_wiki.sh
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Name | 11_wiki.sh — glow binary from CE repo |
|
||||||
|
| Inputs | CE_REPO URL (hardcoded to cervelloelettrico.it/bin/amd64/alpine) |
|
||||||
|
| Outputs | glow installed to ~/.local/bin/glow |
|
||||||
|
| Error behaviour | graceful skip on unreachable repo, download fail, sha256 mismatch, gpg fail |
|
||||||
|
| Privilege | standard user (installs to ~/.local/bin) |
|
||||||
|
| Distro target | Alpine x86-64; CE_REPO path must change for other arches |
|
||||||
|
| Doc references | cervelloelettrico.it CE repo |
|
||||||
|
|
||||||
|
## lib/minimal/12_access.sh
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| Name | 12_access.sh — espeak-ng, espeakup, speech-dispatcher, ly notice |
|
||||||
|
| Inputs | ARCH (from ce-common.sh) |
|
||||||
|
| Outputs | espeak removed if present; espeak-ng installed; espeakup if available; speech-dispatcher@edge; ly notice on x86-64 |
|
||||||
|
| Error behaviour | espeak removal warn-only; espeakup skipped if not in repo; speech-dispatcher warn-on-fail; ly skipped with MANUAL notice |
|
||||||
|
| Privilege | doas for apk del + apk add |
|
||||||
|
| Distro target | Alpine; espeakup availability varies by release |
|
||||||
|
| Doc references | wiki.alpinelinux.org/wiki/Using_espeak_on_Alpine_Linux; github.com/dermotbradley/alpine-espeakup |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## lib/basic/01_comms.sh — 06_utils.sh
|
||||||
|
|
||||||
|
| Script | Packages | Notes |
|
||||||
|
|---|---|---|
|
||||||
|
| 01_comms.sh | abook, task | task = taskwarrior (package name is 'task') |
|
||||||
|
| 02_monitor.sh | nethogs, iproute2, lnav@edge | lnav is edge; warn-on-fail + MANUAL note |
|
||||||
|
| 03_gui.sh | cagebreak, firefox, xwayland | /boot/cmdline.txt skip noted; x86-64 only meaningful |
|
||||||
|
| 04_media.sh | cmus, mpv, ffmpeg, imagemagick, fbida-fbi | package is 'fbida-fbi' not 'fbida' |
|
||||||
|
| 05_fonts.sh | figlet, nerd-fonts@edge | nerd-fonts confirmed package name (edge/community) |
|
||||||
|
| 06_utils.sh | tealdeer, restic, jq, bc, zip, unzip, xz | tealdeer (not 'tldr') |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
Source: ce-authored
|
||||||
|
Source-type: ce-authored
|
||||||
|
Licence: Public Domain — CE FFM Policy
|
||||||
|
Verified-on: 2026-06-10
|
||||||
|
Session: ceos-alpine-installer-v0.0.1-dev
|
||||||
50
tests/test_base.sh
Normal file
50
tests/test_base.sh
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# test_base.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/tests/test_base.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Re-runnable test suite for Tier 0 (Base). Run after ce-base.sh.
|
||||||
|
# Exits with count of failures (0 = all passed).
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
SCRIPT_NAME="test_base"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
|
||||||
|
PASS=0
|
||||||
|
FAIL=0
|
||||||
|
|
||||||
|
check() {
|
||||||
|
local desc="${1}" cmd="${2}"
|
||||||
|
if eval "${cmd}" >/dev/null 2>&1; then
|
||||||
|
log_info "PASS: ${desc}"
|
||||||
|
((PASS++))
|
||||||
|
else
|
||||||
|
log_warn "FAIL: ${desc}"
|
||||||
|
((FAIL++))
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info "=== TEST SUITE: TIER 0 BASE ==="
|
||||||
|
|
||||||
|
check "bash present" 'command -v bash'
|
||||||
|
check "bash-completion installed" 'apk info -e bash-completion'
|
||||||
|
check "ca-certificates installed" 'apk info -e ca-certificates'
|
||||||
|
check "curl present" 'command -v curl'
|
||||||
|
check "openssh installed" 'apk info -e openssh'
|
||||||
|
check "sshd_config present" '[[ -f /etc/ssh/sshd_config ]]'
|
||||||
|
check "tzdata installed" 'apk info -e tzdata'
|
||||||
|
check "utmps installed" 'apk info -e utmps'
|
||||||
|
check "doas present" 'command -v doas'
|
||||||
|
check "doas wheel.conf present" '[[ -f /etc/doas.d/wheel.conf ]]'
|
||||||
|
check "wheel.conf non-empty" '[[ -s /etc/doas.d/wheel.conf ]]'
|
||||||
|
check "community repo enabled" 'grep -q "^[^#].*community" /etc/apk/repositories'
|
||||||
|
|
||||||
|
log_info "=== RESULTS: ${PASS} passed, ${FAIL} failed ==="
|
||||||
|
[[ "${FAIL}" -eq 0 ]] \
|
||||||
|
&& log_info "All Tier 0 tests passed." \
|
||||||
|
|| log_warn "${FAIL} test(s) failed — review ${LOG_FILE}"
|
||||||
|
exit "${FAIL}"
|
||||||
69
tests/test_basic.sh
Normal file
69
tests/test_basic.sh
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# test_basic.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/tests/test_basic.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Re-runnable test suite for Tier 2 (Basic). Run after ce-basic.sh.
|
||||||
|
# Exits with count of failures (0 = all passed).
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
SCRIPT_NAME="test_basic"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
|
||||||
|
PASS=0
|
||||||
|
FAIL=0
|
||||||
|
|
||||||
|
check() {
|
||||||
|
local desc="${1}" cmd="${2}"
|
||||||
|
if eval "${cmd}" >/dev/null 2>&1; then
|
||||||
|
log_info "PASS: ${desc}"
|
||||||
|
((PASS++))
|
||||||
|
else
|
||||||
|
log_warn "FAIL: ${desc}"
|
||||||
|
((FAIL++))
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info "=== TEST SUITE: TIER 2 BASIC ==="
|
||||||
|
|
||||||
|
# Comms additions
|
||||||
|
check "abook present" 'command -v abook'
|
||||||
|
check "task present" 'command -v task'
|
||||||
|
|
||||||
|
# Monitoring
|
||||||
|
check "nethogs present" 'command -v nethogs'
|
||||||
|
check "ip present" 'command -v ip'
|
||||||
|
|
||||||
|
# GUI
|
||||||
|
check "cagebreak present" 'command -v cagebreak'
|
||||||
|
check "firefox present" 'command -v firefox'
|
||||||
|
check "xwayland present" 'command -v Xwayland'
|
||||||
|
|
||||||
|
# Media
|
||||||
|
check "cmus present" 'command -v cmus'
|
||||||
|
check "mpv present" 'command -v mpv'
|
||||||
|
check "ffmpeg present" 'command -v ffmpeg'
|
||||||
|
check "convert present" 'command -v convert'
|
||||||
|
check "fbi present" 'command -v fbi'
|
||||||
|
|
||||||
|
# Fonts
|
||||||
|
check "figlet present" 'command -v figlet'
|
||||||
|
|
||||||
|
# Utilities
|
||||||
|
check "tldr present" 'command -v tldr'
|
||||||
|
check "restic present" 'command -v restic'
|
||||||
|
check "jq present" 'command -v jq'
|
||||||
|
check "bc present" 'command -v bc'
|
||||||
|
check "zip present" 'command -v zip'
|
||||||
|
check "unzip present" 'command -v unzip'
|
||||||
|
check "xz present" 'command -v xz'
|
||||||
|
|
||||||
|
log_info "=== RESULTS: ${PASS} passed, ${FAIL} failed ==="
|
||||||
|
[[ "${FAIL}" -eq 0 ]] \
|
||||||
|
&& log_info "All Tier 2 tests passed." \
|
||||||
|
|| log_warn "${FAIL} test(s) failed — review ${LOG_FILE}"
|
||||||
|
exit "${FAIL}"
|
||||||
87
tests/test_minimal.sh
Normal file
87
tests/test_minimal.sh
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Created by John A. Hoeven with the ethical assistance of Claude AI
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# test_minimal.sh
|
||||||
|
# ~/projects/ceos/alpine-installer/tests/test_minimal.sh
|
||||||
|
# Version: v0.0.1 | Status: DEVELOPMENT
|
||||||
|
# Target: Alpine Linux x86-64 (tinkerpad / ThinkPad T480)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Re-runnable test suite for Tier 1 (Minimal). Run after ce-minimal.sh.
|
||||||
|
# Exits with count of failures (0 = all passed).
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
SCRIPT_NAME="test_minimal"
|
||||||
|
source "${SCRIPT_DIR}/lib/ce-common.sh" || { echo "ERROR: ce-common.sh not found"; exit 1; }
|
||||||
|
|
||||||
|
PASS=0
|
||||||
|
FAIL=0
|
||||||
|
|
||||||
|
check() {
|
||||||
|
local desc="${1}" cmd="${2}"
|
||||||
|
if eval "${cmd}" >/dev/null 2>&1; then
|
||||||
|
log_info "PASS: ${desc}"
|
||||||
|
((PASS++))
|
||||||
|
else
|
||||||
|
log_warn "FAIL: ${desc}"
|
||||||
|
((FAIL++))
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
log_info "=== TEST SUITE: TIER 1 MINIMAL ==="
|
||||||
|
|
||||||
|
# Shell environment
|
||||||
|
check "starship present" 'command -v starship'
|
||||||
|
check "less present" 'command -v less'
|
||||||
|
check "mandoc installed" 'apk info -e mandoc'
|
||||||
|
|
||||||
|
# Terminal
|
||||||
|
check "tmux present" 'command -v tmux'
|
||||||
|
check "vim present" 'command -v vim'
|
||||||
|
check "nano present" 'command -v nano'
|
||||||
|
|
||||||
|
# Files
|
||||||
|
check "ranger present" 'command -v ranger'
|
||||||
|
check "fzf present" 'command -v fzf'
|
||||||
|
check "zoxide present" 'command -v zoxide'
|
||||||
|
check "stow present" 'command -v stow'
|
||||||
|
|
||||||
|
# VCS and transfer
|
||||||
|
check "git present" 'command -v git'
|
||||||
|
check "rsync present" 'command -v rsync'
|
||||||
|
check "gpg present" 'command -v gpg'
|
||||||
|
|
||||||
|
# System tools
|
||||||
|
check "btop present" 'command -v btop'
|
||||||
|
check "lsof present" 'command -v lsof'
|
||||||
|
check "dialog present" 'command -v dialog'
|
||||||
|
|
||||||
|
# Comms
|
||||||
|
check "neomutt present" 'command -v neomutt'
|
||||||
|
check "msmtp present" 'command -v msmtp'
|
||||||
|
check "mbsync present" 'command -v mbsync'
|
||||||
|
check "pass present" 'command -v pass'
|
||||||
|
|
||||||
|
# Journal
|
||||||
|
check "python3 present" 'command -v python3'
|
||||||
|
check "pipx present" 'command -v pipx'
|
||||||
|
|
||||||
|
# Web
|
||||||
|
check "lynx present" 'command -v lynx'
|
||||||
|
check "w3m present" 'command -v w3m'
|
||||||
|
|
||||||
|
# Network services
|
||||||
|
check "avahi-daemon present" 'command -v avahi-daemon'
|
||||||
|
check "dbus-daemon present" 'command -v dbus-daemon'
|
||||||
|
|
||||||
|
# Recording
|
||||||
|
check "asciinema present" 'command -v asciinema'
|
||||||
|
|
||||||
|
# Accessibility
|
||||||
|
check "espeak-ng present" 'command -v espeak-ng'
|
||||||
|
|
||||||
|
log_info "=== RESULTS: ${PASS} passed, ${FAIL} failed ==="
|
||||||
|
[[ "${FAIL}" -eq 0 ]] \
|
||||||
|
&& log_info "All Tier 1 tests passed." \
|
||||||
|
|| log_warn "${FAIL} test(s) failed — review ${LOG_FILE}"
|
||||||
|
exit "${FAIL}"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue