diff --git a/.env.example b/.env.example index e022cc36..e548bfe3 100644 --- a/.env.example +++ b/.env.example @@ -334,7 +334,7 @@ EXCALIDRAW_DB_PASSWORD=changeme # Setup: # 1. Set STATUS_PIPELINE_MINIO_ACCESS_KEY/SECRET_KEY and STATUS_PIPELINE_STATUS_SLUG # 2. Upload tailscale-acl-policy.json to Tailscale admin (adds tag:homelab-nas + Funnel) -# 3. Run: scripts/status-pipeline/setup-nas-tailscale.sh (install Tailscale on NAS) +# 3. Run: tools/status-pipeline/setup-nas-tailscale.sh (install Tailscale on NAS) # 4. Run: task status-pipeline:setup (deploys sync script + cron to NAS) # 5. Run: task status-pipeline:funnel:on (enables public Funnel endpoint) # 6. Set STATUS_PIPELINE_URL below with the Funnel URL diff --git a/.gitignore b/.gitignore index 9be4e593..1379779b 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,12 @@ eggs/ .eggs/ lib/ lib64/ +# Re-include our source dirs that collide with the Python venv-ignore above +# (lib/ here is the boilerplate venv ignore; ours is committed shared bash) +!/lib/ +!/lib/** +!/tests/unit/lib/ +!/tests/unit/lib/** parts/ sdist/ var/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1226e08f..a6d0853d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,7 +43,7 @@ repos: hooks: - id: ansible-lint name: Ansible Lint - entry: scripts/dev/run-ansible-lint.sh + entry: tools/dev/run-ansible-lint.sh language: system files: \. pass_filenames: true diff --git a/Taskfile.yml b/Taskfile.yml index 28fac17c..cd83906f 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -9,8 +9,8 @@ dotenv: [".env"] includes: ansible: ansible/Taskfile.yml status-pipeline: - taskfile: scripts/status-pipeline/Taskfile.yml - dir: scripts/status-pipeline + taskfile: tools/status-pipeline/Taskfile.yml + dir: tools/status-pipeline env: REGISTRY_NAMESPACE: $GITHUB_USERNAME @@ -69,7 +69,7 @@ tasks: - echo "๐Ÿงช Running shell unit tests with coverage..." - mkdir -p coverage - rm -rf /tmp/bats-run-* 2>/dev/null || true - - kcov --bash-method=DEBUG --include-pattern=scripts,stacks --exclude-pattern=/usr,/tmp,/var coverage/unit bats --jobs 1 stacks/apps/*/tests/*.bats tests/unit/scripts/*.bats + - kcov --bash-method=DEBUG --include-pattern=lib,stacks --exclude-pattern=/usr,/tmp,/var coverage/unit bats --jobs 1 stacks/apps/*/tests/*.bats tests/unit/lib/*.bats - echo "๐Ÿ“Š Merging coverage reports..." - kcov --merge coverage/merged coverage/unit - echo "โœ… Shell tests completed [Coverage report](coverage/merged/index.html)" @@ -96,7 +96,7 @@ tasks: desc: Run core tests with faster execution cmds: - echo "๐Ÿงช Running unit tests..." - - bats --tap stacks/apps/*/tests/*.bats tests/unit/scripts/*.bats + - bats --tap stacks/apps/*/tests/*.bats tests/unit/lib/*.bats test-local: desc: Run local-only tests (requires Docker) @@ -132,7 +132,7 @@ tasks: lint:shell: desc: Run shellcheck on shell scripts cmds: - - find scripts -name "*.sh" -type f -exec uv run shellcheck -x --source-path=scripts {} + + - find tools lib -name "*.sh" -type f -exec uv run shellcheck -x --source-path=lib --source-path=tools {} + lint:yaml: desc: Run yamllint on YAML files @@ -212,7 +212,7 @@ tasks: - sh: command -v dockumentor msg: "dockumentor not found. Install with: uv tool install dockumentor" cmds: - - ./scripts/dev/generate-stack-docs.sh + - ./tools/dev/generate-stack-docs.sh secrets:login: desc: Login and unlock the configured secret vault diff --git a/ansible/Taskfile.yml b/ansible/Taskfile.yml index 8a9317b7..9640ebf8 100644 --- a/ansible/Taskfile.yml +++ b/ansible/Taskfile.yml @@ -148,7 +148,7 @@ tasks: desc: "Check cluster health โ€” node states, degraded services, DNS crash history, gossip instability" dir: "{{.ROOT_DIR}}" cmds: - - bash scripts/cluster-health.sh + - bash tools/cluster-health.sh cluster:update-labels: desc: Update Docker Swarm node labels from inventory diff --git a/docs/services/cert-sync-nas.md b/docs/services/cert-sync-nas.md index 8ea1602d..2c770483 100644 --- a/docs/services/cert-sync-nas.md +++ b/docs/services/cert-sync-nas.md @@ -63,7 +63,7 @@ TZ=${TZ:-America/New_York} - `acme_certs:/acme.sh` - `./sync-nas-cert.sh:/scripts/sync-nas-cert.sh:ro` -- `../../../scripts/common:/scripts/common:ro` +- `./common:/scripts/common:ro` --- diff --git a/scripts/common/ssh.sh b/lib/ssh.sh similarity index 100% rename from scripts/common/ssh.sh rename to lib/ssh.sh diff --git a/llms.txt b/llms.txt index ef2096fe..9296febc 100644 --- a/llms.txt +++ b/llms.txt @@ -31,7 +31,8 @@ stacks/ monitoring/ # Prometheus + Grafana + Loki dns/ # Technitium DNS server docs/ # MkDocs documentation site -scripts/ # Utility scripts (health check, doc generation) +tools/ # Invocable tooling (ci, status-pipeline, dev helpers, cluster-health) +lib/ # Shared bash sourced by tooling (ssh.sh) ``` ## Common Operations diff --git a/stacks/apps/cert-sync-nas/README.md b/stacks/apps/cert-sync-nas/README.md index 14b1893a..be776fa3 100644 --- a/stacks/apps/cert-sync-nas/README.md +++ b/stacks/apps/cert-sync-nas/README.md @@ -12,7 +12,7 @@ On every container start, the startup script: 4. Installs a weekly crontab (Sundays at 3 AM) to renew and re-sync 5. Starts `crond` to keep the container running -`sync-nas-cert.sh` calls `omv_cert_install` from `scripts/common/nas/omv.sh`, which: +`sync-nas-cert.sh` calls `omv_cert_install` from `common/nas/omv.sh`, which: - SCPs the cert and key to `/tmp/` on the NAS - SSHes in and runs `omv-rpc CertificateMgmt set` to import the cert - Applies dirty config modules and restarts nginx diff --git a/scripts/common/cert.sh b/stacks/apps/cert-sync-nas/common/cert.sh similarity index 100% rename from scripts/common/cert.sh rename to stacks/apps/cert-sync-nas/common/cert.sh diff --git a/scripts/common/nas/install-cert-remote.sh b/stacks/apps/cert-sync-nas/common/nas/install-cert-remote.sh similarity index 100% rename from scripts/common/nas/install-cert-remote.sh rename to stacks/apps/cert-sync-nas/common/nas/install-cert-remote.sh diff --git a/scripts/common/nas/omv.sh b/stacks/apps/cert-sync-nas/common/nas/omv.sh similarity index 100% rename from scripts/common/nas/omv.sh rename to stacks/apps/cert-sync-nas/common/nas/omv.sh diff --git a/stacks/apps/cert-sync-nas/common/ssh.sh b/stacks/apps/cert-sync-nas/common/ssh.sh new file mode 100755 index 00000000..f7d8e770 --- /dev/null +++ b/stacks/apps/cert-sync-nas/common/ssh.sh @@ -0,0 +1,171 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [ -z "${SSH_KEY_FILE:-}" ]; then + SSH_KEY_FILE="$HOME/.ssh/homelab_rsa" + echo "Warning: SSH_KEY_FILE not set, defaulting to $SSH_KEY_FILE" >&2 + echo " Add SSH_KEY_FILE=~/.ssh/homelab_rsa to your .env file to suppress this warning." >&2 +fi +SSH_TIMEOUT="${SSH_TIMEOUT:-5}" +SSH_EXECUTE_TIMEOUT="${SSH_EXECUTE_TIMEOUT:-300}" + +export SSH_KEY_FILE +export SSH_TIMEOUT +export SSH_EXECUTE_TIMEOUT + +# check if ssh is installed and working (skip in test mode) +if [ -z "${TEST:-}" ]; then + if ! command -v ssh &> /dev/null; then + echo "Error: ssh could not be found" + exit 1 + fi +fi + +# SSH wrapper that uses the SSH key file. It's +# a bit more opinionated than the default ssh command. +# Args: +# $1: SSH user@hostname +# $2: Command to run +# Returns: +# None +ssh_key_auth() { + local key_file="$SSH_KEY_FILE" + local timeout_duration="${SSH_TIMEOUT:-5}" + + timeout "$timeout_duration" ssh -i "$key_file" \ + -o StrictHostKeyChecking=accept-new \ + -o PasswordAuthentication=no \ + -o PubkeyAuthentication=yes \ + -o IdentitiesOnly=yes \ + -o ConnectTimeout=5 \ + "$1" "$2" +} + +# SSH wrapper that uses ssh-copy-id to copy the SSH key to the remote machine +# Args: +# $1: SSH user@hostname +# Returns: +# None +ssh_copy_id() { + ssh-copy-id -i "$SSH_KEY_FILE" -o PasswordAuthentication=yes "$1" +} + +# Execute command on remote host using SSH key authentication +# Args: +# [--login]: optional flag โ€” run command in a login shell (loads full PATH) +# $1: user@hostname +# $2: command to execute +# Returns: +# SSH command exit code +ssh_execute() { + local login=false + if [[ "${1:-}" == "--login" ]]; then + login=true + shift + fi + local user_host="$1" + local command="$2" + if [[ "$login" == true ]]; then + command="bash -l -c '${command//\'/\'\\\'\'}'" + fi + ssh_key_auth "$user_host" "$command" +} + +# Test SSH connectivity to a host +# Args: +# $1: user@hostname +# Returns: +# 0 if connection successful, 1 otherwise +ssh_test_connection() { + local user_host="$1" + ssh_key_auth "$user_host" "exit" 2>/dev/null +} + +# Copy SSH key to remote host +# Args: +# $1: user@hostname +# Returns: +# ssh-copy-id exit code +ssh_copy_key() { + local user_host="$1" + ssh_copy_id "$user_host" +} + +# Create directory on remote host +# Args: +# $1: user@hostname +# $2: directory path +# $3: permissions (optional, e.g., 700) +# Returns: +# SSH command exit code +ssh_create_directory() { + local user_host="$1" + local dir_path="$2" + local permissions="${3:-755}" + ssh_execute "$user_host" "mkdir -p '$dir_path' && chmod '$permissions' '$dir_path'" +} + +# Check if command exists on remote host +# Args: +# $1: user@hostname +# $2: command name +# Returns: +# 0 if command exists, 1 otherwise +ssh_command_exists() { + local user_host="$1" + local command="$2" + ssh_execute "$user_host" "command -v '$command'" >/dev/null 2>&1 +} + +# Copy a file to a remote host via SCP using the SSH key file +# Args: +# $1: source file path +# $2: destination (user@host:/path) +# Returns: +# 0 on success, non-zero on failure +scp_copy_file() { + local src="$1" + local dest="$2" + local key_file="$SSH_KEY_FILE" + local timeout_duration="${SSH_TIMEOUT:-5}" + + timeout "$timeout_duration" scp -i "$key_file" \ + -o StrictHostKeyChecking=accept-new \ + -o PasswordAuthentication=no \ + -o PubkeyAuthentication=yes \ + -o IdentitiesOnly=yes \ + -o ConnectTimeout=5 \ + "$src" "$dest" +} + +# Execute a local script on a remote host via SSH +# Args: +# $1: user@hostname +# $2: path to local script file +# Returns: +# SSH command exit code +ssh_execute_script() { + local user_host="$1" + local script_file="$2" + local key_file="$SSH_KEY_FILE" + local timeout_duration="${SSH_EXECUTE_TIMEOUT:-300}" + + timeout "$timeout_duration" ssh -i "$key_file" \ + -o StrictHostKeyChecking=accept-new \ + -o PasswordAuthentication=no \ + -o PubkeyAuthentication=yes \ + -o IdentitiesOnly=yes \ + -o ConnectTimeout=5 \ + "$user_host" bash < "$script_file" +} + +# Export all SSH functions so they're available to subshells +export -f ssh_key_auth +export -f ssh_copy_id +export -f ssh_execute +export -f ssh_test_connection +export -f ssh_copy_key +export -f ssh_create_directory +export -f ssh_command_exists +export -f scp_copy_file +export -f ssh_execute_script diff --git a/stacks/apps/cert-sync-nas/docker-compose.yml b/stacks/apps/cert-sync-nas/docker-compose.yml index 9feaec26..020653fb 100644 --- a/stacks/apps/cert-sync-nas/docker-compose.yml +++ b/stacks/apps/cert-sync-nas/docker-compose.yml @@ -75,13 +75,13 @@ configs: cert_sync_script_v1: file: ./sync-nas-cert.sh cert_sync_omv_v1: - file: ../../../scripts/common/nas/omv.sh + file: ./common/nas/omv.sh cert_sync_install_cert_v3: - file: ../../../scripts/common/nas/install-cert-remote.sh + file: ./common/nas/install-cert-remote.sh cert_sync_cert_sh_v1: - file: ../../../scripts/common/cert.sh + file: ./common/cert.sh cert_sync_ssh_sh_v1: - file: ../../../scripts/common/ssh.sh + file: ./common/ssh.sh secrets: cert_sync_ssh_key: diff --git a/tests/unit/scripts/cert_test.bats b/stacks/apps/cert-sync-nas/tests/cert_test.bats similarity index 92% rename from tests/unit/scripts/cert_test.bats rename to stacks/apps/cert-sync-nas/tests/cert_test.bats index 995a62fc..e8636aca 100755 --- a/tests/unit/scripts/cert_test.bats +++ b/stacks/apps/cert-sync-nas/tests/cert_test.bats @@ -1,13 +1,13 @@ #!/usr/bin/env bats -# Tests for scripts/common/cert.sh +# Tests for the cert-sync-nas bundle's cert.sh load test_helper setup() { export TEST=true # shellcheck disable=SC1091 - source "${BATS_TEST_DIRNAME}/../../../scripts/common/cert.sh" + source "${BATS_TEST_DIRNAME}/../common/cert.sh" TEST_DIR="$(temp_make)" export TEST_DIR diff --git a/tests/unit/scripts/omv_test.bats b/stacks/apps/cert-sync-nas/tests/omv_test.bats similarity index 90% rename from tests/unit/scripts/omv_test.bats rename to stacks/apps/cert-sync-nas/tests/omv_test.bats index ebc7800f..880a042e 100755 --- a/tests/unit/scripts/omv_test.bats +++ b/stacks/apps/cert-sync-nas/tests/omv_test.bats @@ -1,18 +1,18 @@ #!/usr/bin/env bats -# Tests for scripts/common/nas/omv.sh +# Tests for the cert-sync-nas bundle's nas/omv.sh load test_helper setup() { export TEST=true - local scripts_dir="${BATS_TEST_DIRNAME}/../../../scripts" + local bundle_dir="${BATS_TEST_DIRNAME}/../common" # shellcheck disable=SC1091 - source "${scripts_dir}/common/ssh.sh" + source "${bundle_dir}/ssh.sh" # shellcheck disable=SC1091 - source "${scripts_dir}/common/cert.sh" + source "${bundle_dir}/cert.sh" # shellcheck disable=SC1091 - source "${scripts_dir}/common/nas/omv.sh" + source "${bundle_dir}/nas/omv.sh" TEST_DIR="$(temp_make)" export TEST_DIR diff --git a/stacks/apps/kiwix/setup-nas-downloads.sh b/stacks/apps/kiwix/setup-nas-downloads.sh index cc8c3e35..5a496426 100755 --- a/stacks/apps/kiwix/setup-nas-downloads.sh +++ b/stacks/apps/kiwix/setup-nas-downloads.sh @@ -79,11 +79,11 @@ load_environment() { fi # Source common ssh library - if [ -f "$PROJECT_ROOT/scripts/common/ssh.sh" ]; then - source "$PROJECT_ROOT/scripts/common/ssh.sh" + if [ -f "$PROJECT_ROOT/lib/ssh.sh" ]; then + source "$PROJECT_ROOT/lib/ssh.sh" log_info "Loaded ssh library" else - log_error "Could not find scripts/common/ssh.sh" + log_error "Could not find lib/ssh.sh" log_error "Please ensure you're running this from the project directory" exit 1 fi diff --git a/stacks/apps/kiwix/tests/setup_nas_downloads_test.bats b/stacks/apps/kiwix/tests/setup_nas_downloads_test.bats index 3e26e67b..bedc5ff4 100755 --- a/stacks/apps/kiwix/tests/setup_nas_downloads_test.bats +++ b/stacks/apps/kiwix/tests/setup_nas_downloads_test.bats @@ -27,8 +27,8 @@ BASE_DOMAIN=test.com EOF # Create mock ssh.sh - mkdir -p "$PROJECT_ROOT/scripts/common" - cat > "$PROJECT_ROOT/scripts/common/ssh.sh" <<'EOF' + mkdir -p "$PROJECT_ROOT/lib" + cat > "$PROJECT_ROOT/lib/ssh.sh" <<'EOF' #!/bin/bash ssh_test_connection() { return 0; } ssh_copy_key() { return 0; } @@ -37,7 +37,7 @@ ssh_create_directory() { return 0; } ssh_command_exists() { return 0; } export -f ssh_test_connection ssh_copy_key ssh_execute ssh_create_directory ssh_command_exists EOF - chmod +x "$PROJECT_ROOT/scripts/common/ssh.sh" + chmod +x "$PROJECT_ROOT/lib/ssh.sh" } teardown() { @@ -140,7 +140,7 @@ teardown() { [[ "$output" =~ "source" ]] && [[ "$output" =~ ".env" ]] } -@test "load_environment should source scripts/common/ssh.sh" { +@test "load_environment should source lib/ssh.sh" { local script="${BATS_TEST_DIRNAME}/../setup-nas-downloads.sh" run grep -A20 "^load_environment()" "$script" [[ "$output" =~ "source" ]] && [[ "$output" =~ "ssh.sh" ]] diff --git a/tests/unit/scripts/ssh_test.bats b/tests/unit/lib/ssh_test.bats similarity index 97% rename from tests/unit/scripts/ssh_test.bats rename to tests/unit/lib/ssh_test.bats index d8a55cc5..5777134d 100755 --- a/tests/unit/scripts/ssh_test.bats +++ b/tests/unit/lib/ssh_test.bats @@ -1,6 +1,6 @@ #!/usr/bin/env bats -# Tests for scripts/common/ssh.sh +# Tests for lib/ssh.sh load test_helper @@ -16,7 +16,7 @@ setup() { # Source after SSH_KEY_FILE is set to suppress the warning # shellcheck disable=SC1091 - source "${BATS_TEST_DIRNAME}/../../../scripts/common/ssh.sh" + source "${BATS_TEST_DIRNAME}/../../../lib/ssh.sh" } teardown() { diff --git a/tests/unit/scripts/test_helper.bash b/tests/unit/lib/test_helper.bash similarity index 100% rename from tests/unit/scripts/test_helper.bash rename to tests/unit/lib/test_helper.bash diff --git a/scripts/cluster-health.sh b/tools/cluster-health.sh similarity index 98% rename from scripts/cluster-health.sh rename to tools/cluster-health.sh index 17ea93ce..ec0ad56a 100755 --- a/scripts/cluster-health.sh +++ b/tools/cluster-health.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash -# scripts/cluster-health.sh +# tools/cluster-health.sh # Quick read-only cluster health check. Run from the manager node. -# Usage: bash scripts/cluster-health.sh +# Usage: bash tools/cluster-health.sh set -euo pipefail diff --git a/scripts/dev/generate-stack-docs.sh b/tools/dev/generate-stack-docs.sh similarity index 100% rename from scripts/dev/generate-stack-docs.sh rename to tools/dev/generate-stack-docs.sh diff --git a/scripts/dev/run-ansible-lint.sh b/tools/dev/run-ansible-lint.sh similarity index 100% rename from scripts/dev/run-ansible-lint.sh rename to tools/dev/run-ansible-lint.sh diff --git a/scripts/status-pipeline/README.md b/tools/status-pipeline/README.md similarity index 98% rename from scripts/status-pipeline/README.md rename to tools/status-pipeline/README.md index 5cbd9b52..ad3ae5a4 100644 --- a/scripts/status-pipeline/README.md +++ b/tools/status-pipeline/README.md @@ -41,7 +41,7 @@ Also requires `NAS_SERVER`, `NAS_USER`, `BASE_DOMAIN`, and `SSH_KEY_FILE` from t # https://login.tailscale.com/admin/acls # 3. Install Tailscale on the NAS (one-off) -./scripts/status-pipeline/setup-nas-tailscale.sh +./tools/status-pipeline/setup-nas-tailscale.sh # 4. Deploy sync script, create MinIO bucket, register OMV cron job task status-pipeline:setup diff --git a/scripts/status-pipeline/Taskfile.yml b/tools/status-pipeline/Taskfile.yml similarity index 89% rename from scripts/status-pipeline/Taskfile.yml rename to tools/status-pipeline/Taskfile.yml index 85d228f6..a20986d7 100644 --- a/scripts/status-pipeline/Taskfile.yml +++ b/tools/status-pipeline/Taskfile.yml @@ -20,7 +20,7 @@ tasks: desc: Enable Tailscale Funnel on NAS port 9000 cmds: - | - source ../../scripts/common/ssh.sh >/dev/null 2>&1 + source ../../lib/ssh.sh >/dev/null 2>&1 SSH_TIMEOUT=30 ssh_execute --login "{{.NAS_ROOT}}" "tailscale funnel reset" FUNNEL_BACKEND="https://{{.STATUS_PIPELINE_MINIO_HOST}}/public-status" SSH_TIMEOUT=30 ssh_execute --login "{{.NAS_ROOT}}" \ @@ -30,7 +30,7 @@ tasks: desc: Disable Tailscale Funnel on NAS (killswitch) cmds: - | - source ../../scripts/common/ssh.sh >/dev/null 2>&1 + source ../../lib/ssh.sh >/dev/null 2>&1 SSH_TIMEOUT=30 ssh_execute --login "{{.NAS_ROOT}}" "tailscale funnel off" echo "Funnel disabled" @@ -38,7 +38,7 @@ tasks: desc: Show Tailscale Funnel status on NAS cmds: - | - source ../../scripts/common/ssh.sh >/dev/null 2>&1 + source ../../lib/ssh.sh >/dev/null 2>&1 SSH_TIMEOUT=30 ssh_execute --login "{{.NAS_ROOT}}" "tailscale funnel status" cron:remove: @@ -51,7 +51,7 @@ tasks: cmds: - | echo "1. Running sync on NAS..." - source ../../scripts/common/ssh.sh >/dev/null 2>&1 + source ../../lib/ssh.sh >/dev/null 2>&1 SSH_TIMEOUT=30 ssh_execute --login "{{.NAS_ROOT}}" "/usr/local/bin/sync-status.sh" echo "2. Verifying anonymous read..." FUNNEL_URL="${STATUS_PIPELINE_URL:-}" @@ -72,7 +72,7 @@ tasks: desc: Tail the status pipeline log on NAS cmds: - | - source ../../scripts/common/ssh.sh >/dev/null 2>&1 + source ../../lib/ssh.sh >/dev/null 2>&1 SSH_TIMEOUT=30 ssh_execute --login "{{.NAS_ROOT}}" "tail -50 /var/log/status-pipeline.log" prometheus:disable: diff --git a/scripts/status-pipeline/bucket-policy.json b/tools/status-pipeline/bucket-policy.json similarity index 100% rename from scripts/status-pipeline/bucket-policy.json rename to tools/status-pipeline/bucket-policy.json diff --git a/scripts/status-pipeline/remove-cron.sh b/tools/status-pipeline/remove-cron.sh similarity index 96% rename from scripts/status-pipeline/remove-cron.sh rename to tools/status-pipeline/remove-cron.sh index d1659c5d..5c675f1c 100755 --- a/scripts/status-pipeline/remove-cron.sh +++ b/tools/status-pipeline/remove-cron.sh @@ -12,7 +12,7 @@ if [[ -f "$PROJECT_ROOT/.env" ]]; then set +a fi # shellcheck source=/dev/null -source "$PROJECT_ROOT/scripts/common/ssh.sh" +source "$PROJECT_ROOT/lib/ssh.sh" NAS_SERVER="${NAS_SERVER:?NAS_SERVER is required in .env}" NAS_ROOT="root@${NAS_SERVER}" diff --git a/scripts/status-pipeline/setup-nas-tailscale.sh b/tools/status-pipeline/setup-nas-tailscale.sh similarity index 98% rename from scripts/status-pipeline/setup-nas-tailscale.sh rename to tools/status-pipeline/setup-nas-tailscale.sh index 452e14a0..718eabfd 100755 --- a/scripts/status-pipeline/setup-nas-tailscale.sh +++ b/tools/status-pipeline/setup-nas-tailscale.sh @@ -2,7 +2,7 @@ # One-off: Install and configure Tailscale on the NAS for Funnel. # Run from the homelab project root. # -# Usage: ./scripts/status-pipeline/setup-nas-tailscale.sh +# Usage: ./tools/status-pipeline/setup-nas-tailscale.sh set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -25,7 +25,7 @@ if [[ -f "$PROJECT_ROOT/.env" ]]; then set +a fi # shellcheck source=/dev/null -source "$PROJECT_ROOT/scripts/common/ssh.sh" +source "$PROJECT_ROOT/lib/ssh.sh" NAS_SERVER="${NAS_SERVER:?NAS_SERVER is required in .env}" NAS_USER_HOST="root@${NAS_SERVER}" diff --git a/scripts/status-pipeline/setup-nas.sh b/tools/status-pipeline/setup-nas.sh similarity index 98% rename from scripts/status-pipeline/setup-nas.sh rename to tools/status-pipeline/setup-nas.sh index 3838e069..5efd66a7 100755 --- a/scripts/status-pipeline/setup-nas.sh +++ b/tools/status-pipeline/setup-nas.sh @@ -62,12 +62,12 @@ load_environment() { log_info "Loaded environment from .env" fi - if [ -f "$PROJECT_ROOT/scripts/common/ssh.sh" ]; then + if [ -f "$PROJECT_ROOT/lib/ssh.sh" ]; then # shellcheck source=/dev/null - source "$PROJECT_ROOT/scripts/common/ssh.sh" + source "$PROJECT_ROOT/lib/ssh.sh" log_info "Loaded ssh library" else - log_error "Could not find scripts/common/ssh.sh" + log_error "Could not find lib/ssh.sh" exit 1 fi } diff --git a/scripts/status-pipeline/sync-status.sh b/tools/status-pipeline/sync-status.sh similarity index 100% rename from scripts/status-pipeline/sync-status.sh rename to tools/status-pipeline/sync-status.sh