diff --git a/home/.chezmoitemplates/lib/common/install-prelude.sh b/home/.chezmoitemplates/lib/common/install-prelude.sh index 692c3c0..a51f025 100644 --- a/home/.chezmoitemplates/lib/common/install-prelude.sh +++ b/home/.chezmoitemplates/lib/common/install-prelude.sh @@ -8,8 +8,12 @@ # scripts via chezmoi template rendering, after lib/common/log.sh and before # the install library. # -# Error-handling convention: +# Error-handling conventions for install libraries: # Missing prerequisite tool: call require_command and return on failure. +# Per-item install loop: keep going on failure, then warn with a summary. +# Single critical command: let it fail the script. +# Standalone run: the self-exec guard sources log.sh and this prelude when +# they are not already defined. if [ "${DOTFILES_DEBUG:-}" ]; then set -x diff --git a/home/.chezmoitemplates/lib/install/apm.sh b/home/.chezmoitemplates/lib/install/apm.sh index fed3c3b..6e9de0e 100644 --- a/home/.chezmoitemplates/lib/install/apm.sh +++ b/home/.chezmoitemplates/lib/install/apm.sh @@ -12,6 +12,8 @@ set -Eeuo pipefail # @description Install APM dependencies from `${HOME}/.apm/apm.yml`. # function apm_install_main() { + require_command apm "Ensure run_onchange_03_install-mise-tools ran successfully." || return 1 + log_info "[apm] Installing globally from ~/.apm/apm.yml (frozen)..." cd "${HOME}/.apm" || { log_error "[apm] ~/.apm not found; run 'chezmoi apply' to materialize it." @@ -31,5 +33,11 @@ function main() { } if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + # When run directly, pull in the shared libraries that chezmoi otherwise + # concatenates ahead of this file. + # shellcheck source=/dev/null + command -v log_info >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/log.sh" + # shellcheck source=/dev/null + command -v require_command >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/install-prelude.sh" main fi diff --git a/home/.chezmoitemplates/lib/install/graphify-skills.sh b/home/.chezmoitemplates/lib/install/graphify-skills.sh index 9786f69..8c69a00 100644 --- a/home/.chezmoitemplates/lib/install/graphify-skills.sh +++ b/home/.chezmoitemplates/lib/install/graphify-skills.sh @@ -14,6 +14,7 @@ set -Eeuo pipefail function graphify_skills_install_main() { local has_platform=0 local platform + local failed=() for platform in "${GRAPHIFY_PLATFORMS[@]-}"; do [[ -z "${platform}" ]] && continue @@ -32,9 +33,17 @@ function graphify_skills_install_main() { for platform in "${GRAPHIFY_PLATFORMS[@]-}"; do [[ -z "${platform}" ]] && continue - graphify install --platform "${platform}" + + if ! graphify install --platform "${platform}"; then + failed+=("${platform}") + fi done + if ((${#failed[@]} > 0)); then + log_warn "[graphify] ${#failed[@]} platform(s) failed to install:" + printf ' - %s\n' "${failed[@]}" >&2 + fi + log_info "[graphify] Graphify agent skills installed." } @@ -46,5 +55,11 @@ function main() { } if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + # When run directly, pull in the shared libraries that chezmoi otherwise + # concatenates ahead of this file. + # shellcheck source=/dev/null + command -v log_info >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/log.sh" + # shellcheck source=/dev/null + command -v require_command >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/install-prelude.sh" main fi diff --git a/home/.chezmoitemplates/lib/install/homebrew-bundle.sh b/home/.chezmoitemplates/lib/install/homebrew-bundle.sh index bfbf39c..b086728 100644 --- a/home/.chezmoitemplates/lib/install/homebrew-bundle.sh +++ b/home/.chezmoitemplates/lib/install/homebrew-bundle.sh @@ -30,5 +30,11 @@ function main() { } if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + # When run directly, pull in the shared libraries that chezmoi otherwise + # concatenates ahead of this file. + # shellcheck source=/dev/null + command -v log_info >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/log.sh" + # shellcheck source=/dev/null + command -v require_command >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/install-prelude.sh" main fi diff --git a/home/.chezmoitemplates/lib/install/homebrew.sh b/home/.chezmoitemplates/lib/install/homebrew.sh index 184180d..164c972 100644 --- a/home/.chezmoitemplates/lib/install/homebrew.sh +++ b/home/.chezmoitemplates/lib/install/homebrew.sh @@ -53,5 +53,9 @@ function main() { } if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + # When run directly, pull in the log library that chezmoi otherwise + # concatenates ahead of this file. + # shellcheck source=/dev/null + command -v log_info >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/log.sh" main fi diff --git a/home/.chezmoitemplates/lib/install/mise.sh b/home/.chezmoitemplates/lib/install/mise.sh index e6e9618..d6354f6 100644 --- a/home/.chezmoitemplates/lib/install/mise.sh +++ b/home/.chezmoitemplates/lib/install/mise.sh @@ -27,5 +27,11 @@ function main() { } if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + # When run directly, pull in the shared libraries that chezmoi otherwise + # concatenates ahead of this file. + # shellcheck source=/dev/null + command -v log_info >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/log.sh" + # shellcheck source=/dev/null + command -v require_command >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/install-prelude.sh" main fi diff --git a/home/.chezmoitemplates/lib/install/opencode-agent-tools.sh b/home/.chezmoitemplates/lib/install/opencode-agent-tools.sh index 1a52cfa..9216563 100644 --- a/home/.chezmoitemplates/lib/install/opencode-agent-tools.sh +++ b/home/.chezmoitemplates/lib/install/opencode-agent-tools.sh @@ -106,5 +106,9 @@ function main() { } if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + # When run directly, pull in the log library that chezmoi otherwise + # concatenates ahead of this file. + # shellcheck source=/dev/null + command -v log_info >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/log.sh" main fi diff --git a/home/.chezmoitemplates/lib/install/uv-tools.sh b/home/.chezmoitemplates/lib/install/uv-tools.sh index 262f482..b1ca97f 100644 --- a/home/.chezmoitemplates/lib/install/uv-tools.sh +++ b/home/.chezmoitemplates/lib/install/uv-tools.sh @@ -14,12 +14,21 @@ set -Eeuo pipefail function uv_tools_install_main() { log_info "[uv] Installing uv tools..." + local failed=() local tool for tool in "${UV_TOOLS[@]-}"; do [[ -z "${tool}" ]] && continue - uv tool install --upgrade "${tool}" + + if ! uv tool install --upgrade "${tool}"; then + failed+=("${tool}") + fi done + if ((${#failed[@]} > 0)); then + log_warn "[uv] ${#failed[@]} tool(s) failed to install:" + printf ' - %s\n' "${failed[@]}" >&2 + fi + log_info "[uv] uv tools installed." } @@ -33,5 +42,11 @@ function main() { } if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + # When run directly, pull in the shared libraries that chezmoi otherwise + # concatenates ahead of this file. + # shellcheck source=/dev/null + command -v log_info >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/log.sh" + # shellcheck source=/dev/null + command -v require_command >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/install-prelude.sh" main fi diff --git a/home/.chezmoitemplates/lib/install/vscode.sh b/home/.chezmoitemplates/lib/install/vscode.sh index 582db2c..ec10482 100644 --- a/home/.chezmoitemplates/lib/install/vscode.sh +++ b/home/.chezmoitemplates/lib/install/vscode.sh @@ -8,21 +8,11 @@ set -Eeuo pipefail -# -# @description Check if the VS Code CLI is installed. -# -function is_vscode_cli_installed() { - command -v code >/dev/null 2>&1 -} - # # @description Install configured VS Code extensions. # function vscode_install_extensions_main() { - if ! is_vscode_cli_installed; then - log_warn "[vscode] VS Code CLI not found. Open VS Code and run: Shell Command: Install 'code' command in PATH" - return 0 - fi + require_command code "Open VS Code and run: Shell Command: Install 'code' command in PATH" || return 1 log_info "[vscode] Installing VS Code extensions..." @@ -52,5 +42,11 @@ function main() { } if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + # When run directly, pull in the shared libraries that chezmoi otherwise + # concatenates ahead of this file. + # shellcheck source=/dev/null + command -v log_info >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/log.sh" + # shellcheck source=/dev/null + command -v require_command >/dev/null 2>&1 || source "$(dirname "${BASH_SOURCE[0]}")/../common/install-prelude.sh" main fi diff --git a/tests/unit/lib/install/apm.bats b/tests/unit/lib/install/apm.bats index 7ebfa85..af90969 100644 --- a/tests/unit/lib/install/apm.bats +++ b/tests/unit/lib/install/apm.bats @@ -5,6 +5,7 @@ load '../../../test_helpers/load.bash' LOG_LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/log.sh" +PRELUDE="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/install-prelude.sh" APM_LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/install/apm.sh" setup() { @@ -24,7 +25,7 @@ APM } @test "apm_install_main: runs apm install globally from ~/.apm" { - run bash -c "source '$LOG_LIB' && source '$APM_LIB' && apm_install_main" + run bash -c "source '$LOG_LIB' && source '$PRELUDE' && source '$APM_LIB' && apm_install_main" assert_success assert_line "[apm] Installing globally from ~/.apm/apm.yml (frozen)..." @@ -36,7 +37,7 @@ APM @test "apm_install_main: warns and completes when apm install fails" { export APM_EXIT_CODE=7 - run bash -c "source '$LOG_LIB' && source '$APM_LIB' && apm_install_main" + run bash -c "source '$LOG_LIB' && source '$PRELUDE' && source '$APM_LIB' && apm_install_main" assert_success assert_line --partial "warn: [apm] apm install --frozen exited non-zero" diff --git a/tests/unit/lib/install/graphify-skills.bats b/tests/unit/lib/install/graphify-skills.bats index 57877ec..1c5ddc1 100644 --- a/tests/unit/lib/install/graphify-skills.bats +++ b/tests/unit/lib/install/graphify-skills.bats @@ -47,6 +47,17 @@ GRAPHIFY [ ! -s "$GRAPHIFY_ARGS_FILE" ] } +@test "main: warns but succeeds when a platform fails" { + export GRAPHIFY_EXIT_CODE=5 + + run bash -c "source '$LOG_LIB' && source '$PRELUDE' && source '$LIB' && GRAPHIFY_PLATFORMS=('agents') && main" + + assert_success + assert_line "warn: [graphify] 1 platform(s) failed to install:" + assert_line " - agents" + assert_line "[graphify] Graphify agent skills installed." +} + @test "main: fails when graphify is missing" { rm -f "$BATS_TEST_TMPDIR/bin/graphify" diff --git a/tests/unit/lib/install/uv-tools.bats b/tests/unit/lib/install/uv-tools.bats index 04f8650..a03ec7b 100644 --- a/tests/unit/lib/install/uv-tools.bats +++ b/tests/unit/lib/install/uv-tools.bats @@ -38,12 +38,15 @@ UV [ "$(<"$UV_ARGS_FILE")" = $'tool install --upgrade graphifyy\ntool install --upgrade tavily-cli' ] } -@test "uv_tools_install_main: fails when uv tool install fails" { +@test "uv_tools_install_main: warns but succeeds when a tool fails" { export UV_FAIL_TOOL='tavily-cli' run bash -c "source '$LOG_LIB' && source '$LIB' && UV_TOOLS=('graphifyy' 'tavily-cli') && uv_tools_install_main" - assert_failure 8 + assert_success assert_line "[uv] Installing uv tools..." + assert_line "warn: [uv] 1 tool(s) failed to install:" + assert_line " - tavily-cli" + assert_line "[uv] uv tools installed." [ "$(<"$UV_ARGS_FILE")" = $'tool install --upgrade graphifyy\ntool install --upgrade tavily-cli' ] } diff --git a/tests/unit/lib/install/vscode.bats b/tests/unit/lib/install/vscode.bats index db87e46..dffd7bc 100644 --- a/tests/unit/lib/install/vscode.bats +++ b/tests/unit/lib/install/vscode.bats @@ -5,6 +5,7 @@ load '../../../test_helpers/load.bash' LOG_LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/log.sh" +PRELUDE="$DOTFILES_ROOT/home/.chezmoitemplates/lib/common/install-prelude.sh" LIB="$DOTFILES_ROOT/home/.chezmoitemplates/lib/install/vscode.sh" setup() { @@ -23,7 +24,7 @@ CODE } @test "vscode_install_extensions_main: installs each extension with force" { - run bash -c "source '$LOG_LIB' && source '$LIB' && VSCODE_EXTENSIONS=('one.ext' 'two.ext') && vscode_install_extensions_main" + run bash -c "source '$LOG_LIB' && source '$PRELUDE' && source '$LIB' && VSCODE_EXTENSIONS=('one.ext' 'two.ext') && vscode_install_extensions_main" assert_success assert_line "[vscode] Installing VS Code extensions..." @@ -34,10 +35,20 @@ CODE @test "vscode_install_extensions_main: warns but succeeds when an extension fails" { export CODE_FAIL_EXTENSION='bad.ext' - run bash -c "source '$LOG_LIB' && source '$LIB' && VSCODE_EXTENSIONS=('good.ext' 'bad.ext') && vscode_install_extensions_main" + run bash -c "source '$LOG_LIB' && source '$PRELUDE' && source '$LIB' && VSCODE_EXTENSIONS=('good.ext' 'bad.ext') && vscode_install_extensions_main" assert_success assert_line "warn: [vscode] 1 extension(s) failed to install:" assert_line " - bad.ext" assert_line "[vscode] VS Code extensions installed." } + +@test "vscode_install_extensions_main: fails when the VS Code CLI is missing" { + rm -f "$BATS_TEST_TMPDIR/bin/code" + export PATH="$BATS_TEST_TMPDIR/bin:/usr/bin:/bin" + + run bash -c "source '$LOG_LIB' && source '$PRELUDE' && source '$LIB' && VSCODE_EXTENSIONS=('one.ext') && vscode_install_extensions_main" + + assert_failure 1 + assert_line "error: [code] not found. Open VS Code and run: Shell Command: Install 'code' command in PATH" +}