From 0abe48cdbc18e73bf156fbb93ce3ef347bb6e967 Mon Sep 17 00:00:00 2001 From: Michael Uloth Date: Mon, 7 Jul 2025 00:14:57 -0400 Subject: [PATCH 1/3] Migrate SSH installation to bash + bats Completes Phase 1 SSH migration following the pattern established in PR #13. ## Changes - `lib/ssh-utils.bash`: SSH utilities with key detection, config management, agent operations - `bin/install/ssh.bash`: Complete bash replacement for ssh.zsh installation script - `test/install/test-ssh-utils.bats`: 14 comprehensive unit tests for utilities - `test/install/test-ssh-installation.bats`: 7 integration tests for complete workflow ## Key Improvements - shellcheck compliance with zero warnings - Better macOS version detection for ssh-add keychain flag - Comprehensive test coverage (21 total tests) - Cleaner separation of concerns with utility functions - More robust error handling This continues the bash migration effort and demonstrates the pattern can be successfully applied to different installation scripts. --- bin/install/ssh.bash | 93 ++++++++ lib/ssh-utils.bash | 104 +++++++++ test/install/test-ssh-installation.bats | 274 ++++++++++++++++++++++++ test/install/test-ssh-utils.bats | 236 ++++++++++++++++++++ 4 files changed, 707 insertions(+) create mode 100644 bin/install/ssh.bash create mode 100644 lib/ssh-utils.bash create mode 100644 test/install/test-ssh-installation.bats create mode 100644 test/install/test-ssh-utils.bats diff --git a/bin/install/ssh.bash b/bin/install/ssh.bash new file mode 100644 index 00000000..0737358d --- /dev/null +++ b/bin/install/ssh.bash @@ -0,0 +1,93 @@ +#!/usr/bin/env bash + +# SSH installation script +# Handles SSH key generation, configuration, and agent setup +# +# This script: +# 1. Generates SSH keys if not present +# 2. Creates/updates SSH config file +# 3. Adds keys to ssh-agent +# 4. Adds keys to macOS Keychain + +set -euo pipefail + +# Configuration +DOTFILES="${DOTFILES:-$HOME/Repos/ooloth/dotfiles}" + +# Load utilities +# shellcheck source=../../lib/ssh-utils.bash +source "$DOTFILES/lib/ssh-utils.bash" + +main() { + echo "🔑 Installing SSH key pair" + echo "" + + # Check for existing SSH keys + echo "🔍 Checking for existing SSH keys" + + if detect_ssh_keys; then + # Keys found, exit early + return 0 + fi + + # Generate new SSH keys + echo "" + echo "✨ Generating a new 2048-bit RSA SSH public/private key pair." + + if generate_ssh_keys; then + echo "" + echo "✅ SSH key pair generated successfully." + else + echo "" + echo "❌ Failed to generate SSH key pair." + return 1 + fi + + # Create/update SSH config file + echo "" + echo "📄 Creating SSH config file" + + local ssh_config="$HOME/.ssh/config" + + if [[ -f "$ssh_config" ]]; then + echo "" + echo "✅ SSH config file found. Checking contents..." + + if ssh_config_has_required_settings "$ssh_config"; then + echo "" + echo "✅ SSH config file contains all the expected settings." + else + echo "" + echo "❌ SSH config file does not contain all the expected settings. Updating..." + create_ssh_config "$ssh_config" + echo "" + echo "✅ SSH config file updated." + fi + else + echo "SSH config file does not exist. Creating..." + create_ssh_config "$ssh_config" + echo "" + echo "✅ SSH config file created." + fi + + # Add key to SSH agent and Keychain + echo "" + echo "🔑 Adding SSH key pair to ssh-agent and Keychain" + + if add_ssh_key_to_agent; then + echo "" + echo "✅ SSH key added successfully." + else + echo "" + echo "❌ Failed to add SSH key to agent." + return 1 + fi + + echo "" + echo "🚀 Done configuring your SSH key pair." +} + +# Run main function if script is executed directly +if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then + main "$@" +fi \ No newline at end of file diff --git a/lib/ssh-utils.bash b/lib/ssh-utils.bash new file mode 100644 index 00000000..3b97e24e --- /dev/null +++ b/lib/ssh-utils.bash @@ -0,0 +1,104 @@ +#!/usr/bin/env bash + +# SSH utility functions for installation scripts +# Provides reusable functionality for detecting and working with SSH keys + +set -euo pipefail + +# Get SSH key paths (centralized configuration) +get_ssh_key_paths() { + SSH_DIR="${HOME}/.ssh" + SSH_PRIVATE_KEY="${SSH_DIR}/id_rsa" + SSH_PUBLIC_KEY="${SSH_DIR}/id_rsa.pub" +} + +# Check if SSH keys exist on the system (no output) +# Returns: 0 if both private and public keys exist, 1 if not +ssh_keys_exist() { + get_ssh_key_paths + [[ -s "$SSH_PRIVATE_KEY" && -s "$SSH_PUBLIC_KEY" ]] +} + +# Detect if SSH keys exist on the system (with output) +# Prints informative messages and returns appropriate exit codes +# Returns: 0 if both private and public keys exist, 1 if not +detect_ssh_keys() { + if ssh_keys_exist; then + echo "✅ SSH key pair found." + return 0 + else + echo "👎 No SSH key pair found." + return 1 + fi +} + +# Create SSH config file with appropriate settings +# Arguments: $1 - SSH config file path +create_ssh_config() { + local ssh_config="$1" + local ssh_dir + ssh_dir=$(dirname "$ssh_config") + + get_ssh_key_paths + + mkdir -p "$ssh_dir" + + cat > "$ssh_config" << EOF +Host * + AddKeysToAgent yes + UseKeychain yes + IdentityFile $SSH_PRIVATE_KEY +EOF +} + +# Check if SSH config has all required settings +# Arguments: $1 - SSH config file path +# Returns: 0 if all settings present, 1 if any missing +ssh_config_has_required_settings() { + local ssh_config="$1" + + if [[ ! -f "$ssh_config" ]]; then + return 1 + fi + + get_ssh_key_paths + + # Check each required setting + grep -Fxq "Host *" "$ssh_config" && \ + grep -Fxq " AddKeysToAgent yes" "$ssh_config" && \ + grep -Fxq " UseKeychain yes" "$ssh_config" && \ + grep -Fxq " IdentityFile $SSH_PRIVATE_KEY" "$ssh_config" +} + +# Generate SSH key pair +# Returns: 0 on success, 1 on failure +generate_ssh_keys() { + get_ssh_key_paths + + # Generate a 2048-bit RSA SSH key pair + # -q makes the process quiet + # -N '' sets an empty passphrase + # -f specifies the output file + ssh-keygen -q -t rsa -b 2048 -N '' -f "$SSH_PRIVATE_KEY" <<< y >/dev/null 2>&1 +} + +# Add SSH key to ssh-agent and Keychain +# Returns: 0 on success, 1 on failure +add_ssh_key_to_agent() { + get_ssh_key_paths + + # Start ssh-agent if not running + if ! ssh-add -l >/dev/null 2>&1; then + eval "$(ssh-agent -s)" >/dev/null + fi + + # Add key to agent and keychain + # Note: -K flag is deprecated on newer macOS, use --apple-use-keychain instead + if command -v sw_vers >/dev/null 2>&1 && [[ $(sw_vers -productVersion | cut -d. -f1) -ge 12 ]]; then + # macOS 12 (Monterey) and later + ssh-add --apple-use-keychain "$SSH_PRIVATE_KEY" 2>/dev/null + else + # Older macOS versions + ssh-add -K "$SSH_PRIVATE_KEY" 2>/dev/null + fi +} \ No newline at end of file diff --git a/test/install/test-ssh-installation.bats b/test/install/test-ssh-installation.bats new file mode 100644 index 00000000..51dfc829 --- /dev/null +++ b/test/install/test-ssh-installation.bats @@ -0,0 +1,274 @@ +#!/usr/bin/env bats + +# Integration tests for SSH installation script +# Tests the complete workflow including key generation, config, and agent setup + +# Load the SSH installation script (for testing main function logic) +load "../../bin/install/ssh.bash" + +setup() { + # Create temporary directory for each test + export TEST_TEMP_DIR + TEST_TEMP_DIR="$(mktemp -d)" + + # Save original environment + export ORIGINAL_HOME="$HOME" + export ORIGINAL_DOTFILES="${DOTFILES:-}" + + # Set up test environment + export HOME="$TEST_TEMP_DIR/fake_home" + export DOTFILES="$TEST_TEMP_DIR/fake_dotfiles" + + # Create fake dotfiles structure + mkdir -p "$DOTFILES/lib" + cp "lib/ssh-utils.bash" "$DOTFILES/lib/" +} + +teardown() { + # Restore original environment + export HOME="$ORIGINAL_HOME" + if [[ -n "${ORIGINAL_DOTFILES}" ]]; then + export DOTFILES="$ORIGINAL_DOTFILES" + else + unset DOTFILES + fi + + # Clean up temporary directory + if [[ -n "${TEST_TEMP_DIR:-}" && -d "$TEST_TEMP_DIR" ]]; then + rm -rf "$TEST_TEMP_DIR" + fi +} + +@test "main exits early when SSH keys already exist" { + # Create existing SSH keys + mkdir -p "$HOME/.ssh" + echo "existing private key" > "$HOME/.ssh/id_rsa" + echo "existing public key" > "$HOME/.ssh/id_rsa.pub" + + run main + [ "$status" -eq 0 ] + [[ "$output" == *"SSH key pair found"* ]] + [[ "$output" != *"Generating a new"* ]] +} + +@test "main generates keys when none exist" { + # Ensure no keys exist + mkdir -p "$HOME/.ssh" + + # Mock ssh-keygen to create fake keys + ssh-keygen() { + local output_file="" + while [[ $# -gt 0 ]]; do + case $1 in + -f) output_file="$2"; shift 2 ;; + *) shift ;; + esac + done + + if [[ -n "$output_file" ]]; then + echo "generated private key" > "$output_file" + echo "generated public key" > "${output_file}.pub" + fi + + return 0 + } + export -f ssh-keygen + + # Mock ssh-add for agent operations + ssh-add() { + case "$1" in + -l) return 0 ;; # Agent is running + --apple-use-keychain|*) return 0 ;; # Success adding key + esac + } + export -f ssh-add + + run main + [ "$status" -eq 0 ] + [[ "$output" == *"No SSH key pair found"* ]] + [[ "$output" == *"Generating a new 2048-bit RSA"* ]] + [[ "$output" == *"SSH key pair generated successfully"* ]] + [ -f "$HOME/.ssh/id_rsa" ] + [ -f "$HOME/.ssh/id_rsa.pub" ] +} + +@test "main creates SSH config when missing" { + # No existing SSH keys - need to generate them + mkdir -p "$HOME/.ssh" + + # Mock ssh-keygen to create fake keys + ssh-keygen() { + local output_file="" + while [[ $# -gt 0 ]]; do + case $1 in + -f) output_file="$2"; shift 2 ;; + *) shift ;; + esac + done + + if [[ -n "$output_file" ]]; then + echo "generated private key" > "$output_file" + echo "generated public key" > "${output_file}.pub" + fi + + return 0 + } + export -f ssh-keygen + + # Mock ssh-add for agent operations + ssh-add() { + return 0 + } + export -f ssh-add + + run main + [ "$status" -eq 0 ] + [[ "$output" == *"SSH config file does not exist. Creating"* ]] + [[ "$output" == *"SSH config file created"* ]] + [ -f "$HOME/.ssh/config" ] + + # Verify config contents + grep -Fxq "Host *" "$HOME/.ssh/config" + grep -Fxq " AddKeysToAgent yes" "$HOME/.ssh/config" +} + +@test "main updates incomplete SSH config" { + # No existing SSH keys - need to generate them + mkdir -p "$HOME/.ssh" + + # Create incomplete config + cat > "$HOME/.ssh/config" << EOF +Host * + AddKeysToAgent yes +EOF + + # Mock ssh-keygen to create fake keys + ssh-keygen() { + local output_file="" + while [[ $# -gt 0 ]]; do + case $1 in + -f) output_file="$2"; shift 2 ;; + *) shift ;; + esac + done + + if [[ -n "$output_file" ]]; then + echo "generated private key" > "$output_file" + echo "generated public key" > "${output_file}.pub" + fi + + return 0 + } + export -f ssh-keygen + + # Mock ssh-add for agent operations + ssh-add() { + return 0 + } + export -f ssh-add + + run main + [ "$status" -eq 0 ] + [[ "$output" == *"SSH config file found. Checking contents"* ]] + [[ "$output" == *"does not contain all the expected settings"* ]] + [[ "$output" == *"SSH config file updated"* ]] + + # Verify config was updated with all settings + grep -Fxq " UseKeychain yes" "$HOME/.ssh/config" + grep -Fxq " IdentityFile $HOME/.ssh/id_rsa" "$HOME/.ssh/config" +} + +@test "main adds keys to ssh-agent" { + # No existing SSH keys - need to generate them + mkdir -p "$HOME/.ssh" + + # Mock ssh-keygen to create fake keys + ssh-keygen() { + local output_file="" + while [[ $# -gt 0 ]]; do + case $1 in + -f) output_file="$2"; shift 2 ;; + *) shift ;; + esac + done + + if [[ -n "$output_file" ]]; then + echo "generated private key" > "$output_file" + echo "generated public key" > "${output_file}.pub" + fi + + return 0 + } + export -f ssh-keygen + + # Track ssh-add calls + local agent_calls=0 + ssh-add() { + case "$1" in + -l) return 0 ;; # Agent is running + --apple-use-keychain|*) + ((agent_calls++)) + return 0 + ;; + esac + } + export -f ssh-add + export agent_calls + + run main + [ "$status" -eq 0 ] + [[ "$output" == *"Adding SSH key pair to ssh-agent and Keychain"* ]] + [[ "$output" == *"SSH key added successfully"* ]] +} + +@test "main handles key generation failure" { + # Ensure no keys exist + mkdir -p "$HOME/.ssh" + + # Mock ssh-keygen to fail + ssh-keygen() { + return 1 + } + export -f ssh-keygen + + run main + [ "$status" -eq 1 ] + [[ "$output" == *"Failed to generate SSH key pair"* ]] +} + +@test "main handles agent add failure" { + # No existing SSH keys - need to generate them + mkdir -p "$HOME/.ssh" + + # Mock ssh-keygen to create fake keys + ssh-keygen() { + local output_file="" + while [[ $# -gt 0 ]]; do + case $1 in + -f) output_file="$2"; shift 2 ;; + *) shift ;; + esac + done + + if [[ -n "$output_file" ]]; then + echo "generated private key" > "$output_file" + echo "generated public key" > "${output_file}.pub" + fi + + return 0 + } + export -f ssh-keygen + + # Mock ssh-add to fail when adding key + ssh-add() { + case "$1" in + -l) return 0 ;; # Agent is running + --apple-use-keychain|*) return 1 ;; # Fail adding key + esac + } + export -f ssh-add + + run main + [ "$status" -eq 1 ] + [[ "$output" == *"Failed to add SSH key to agent"* ]] +} \ No newline at end of file diff --git a/test/install/test-ssh-utils.bats b/test/install/test-ssh-utils.bats new file mode 100644 index 00000000..fd4e44ac --- /dev/null +++ b/test/install/test-ssh-utils.bats @@ -0,0 +1,236 @@ +#!/usr/bin/env bats + +# Test SSH utility functions +# Tests key detection, generation, config management, and agent operations + +# Load the SSH utilities +load "../../lib/ssh-utils.bash" + +setup() { + # Create temporary directory for each test + export TEST_TEMP_DIR + TEST_TEMP_DIR="$(mktemp -d)" + + # Save original HOME for restoration + export ORIGINAL_HOME="$HOME" +} + +teardown() { + # Restore original HOME + export HOME="$ORIGINAL_HOME" + + # Clean up temporary directory + if [[ -n "${TEST_TEMP_DIR:-}" && -d "$TEST_TEMP_DIR" ]]; then + rm -rf "$TEST_TEMP_DIR" + fi +} + +@test "get_ssh_key_paths sets correct paths" { + export HOME="$TEST_TEMP_DIR/fake_home" + + get_ssh_key_paths + + [ "$SSH_DIR" = "$TEST_TEMP_DIR/fake_home/.ssh" ] + [ "$SSH_PRIVATE_KEY" = "$TEST_TEMP_DIR/fake_home/.ssh/id_rsa" ] + [ "$SSH_PUBLIC_KEY" = "$TEST_TEMP_DIR/fake_home/.ssh/id_rsa.pub" ] +} + +@test "ssh_keys_exist returns 0 when both keys exist and are non-empty" { + local fake_home="$TEST_TEMP_DIR/fake_home" + mkdir -p "$fake_home/.ssh" + echo "fake private key content" > "$fake_home/.ssh/id_rsa" + echo "fake public key content" > "$fake_home/.ssh/id_rsa.pub" + + export HOME="$fake_home" + + run ssh_keys_exist + [ "$status" -eq 0 ] +} + +@test "ssh_keys_exist returns 1 when private key is missing" { + local fake_home="$TEST_TEMP_DIR/fake_home" + mkdir -p "$fake_home/.ssh" + echo "fake public key content" > "$fake_home/.ssh/id_rsa.pub" + + export HOME="$fake_home" + + run ssh_keys_exist + [ "$status" -eq 1 ] +} + +@test "ssh_keys_exist returns 1 when public key is missing" { + local fake_home="$TEST_TEMP_DIR/fake_home" + mkdir -p "$fake_home/.ssh" + echo "fake private key content" > "$fake_home/.ssh/id_rsa" + + export HOME="$fake_home" + + run ssh_keys_exist + [ "$status" -eq 1 ] +} + +@test "ssh_keys_exist returns 1 when keys are empty" { + local fake_home="$TEST_TEMP_DIR/fake_home" + mkdir -p "$fake_home/.ssh" + touch "$fake_home/.ssh/id_rsa" + touch "$fake_home/.ssh/id_rsa.pub" + + export HOME="$fake_home" + + run ssh_keys_exist + [ "$status" -eq 1 ] +} + +@test "detect_ssh_keys prints success message when keys exist" { + local fake_home="$TEST_TEMP_DIR/fake_home" + mkdir -p "$fake_home/.ssh" + echo "fake private key content" > "$fake_home/.ssh/id_rsa" + echo "fake public key content" > "$fake_home/.ssh/id_rsa.pub" + + export HOME="$fake_home" + + run detect_ssh_keys + [ "$status" -eq 0 ] + [[ "$output" == *"SSH key pair found"* ]] +} + +@test "detect_ssh_keys prints failure message when keys missing" { + local fake_home="$TEST_TEMP_DIR/fake_home" + mkdir -p "$fake_home/.ssh" + + export HOME="$fake_home" + + run detect_ssh_keys + [ "$status" -eq 1 ] + [[ "$output" == *"No SSH key pair found"* ]] +} + +@test "create_ssh_config creates config with correct settings" { + local fake_home="$TEST_TEMP_DIR/fake_home" + local ssh_config="$fake_home/.ssh/config" + + export HOME="$fake_home" + + run create_ssh_config "$ssh_config" + [ "$status" -eq 0 ] + [ -f "$ssh_config" ] + + # Verify config contents + grep -Fxq "Host *" "$ssh_config" + grep -Fxq " AddKeysToAgent yes" "$ssh_config" + grep -Fxq " UseKeychain yes" "$ssh_config" + grep -Fxq " IdentityFile $fake_home/.ssh/id_rsa" "$ssh_config" +} + +@test "create_ssh_config creates .ssh directory if missing" { + local fake_home="$TEST_TEMP_DIR/fake_home" + local ssh_config="$fake_home/.ssh/config" + + export HOME="$fake_home" + + # Ensure .ssh doesn't exist + [ ! -d "$fake_home/.ssh" ] + + run create_ssh_config "$ssh_config" + [ "$status" -eq 0 ] + [ -d "$fake_home/.ssh" ] +} + +@test "ssh_config_has_required_settings returns 0 when all settings present" { + local fake_home="$TEST_TEMP_DIR/fake_home" + local ssh_config="$fake_home/.ssh/config" + + export HOME="$fake_home" + + # Create config with all required settings + create_ssh_config "$ssh_config" + + run ssh_config_has_required_settings "$ssh_config" + [ "$status" -eq 0 ] +} + +@test "ssh_config_has_required_settings returns 1 when file missing" { + local fake_home="$TEST_TEMP_DIR/fake_home" + local ssh_config="$fake_home/.ssh/config" + + export HOME="$fake_home" + + run ssh_config_has_required_settings "$ssh_config" + [ "$status" -eq 1 ] +} + +@test "ssh_config_has_required_settings returns 1 when settings incomplete" { + local fake_home="$TEST_TEMP_DIR/fake_home" + local ssh_config="$fake_home/.ssh/config" + + export HOME="$fake_home" + + # Create config with missing settings + mkdir -p "$fake_home/.ssh" + cat > "$ssh_config" << EOF +Host * + AddKeysToAgent yes +EOF + + run ssh_config_has_required_settings "$ssh_config" + [ "$status" -eq 1 ] +} + +@test "generate_ssh_keys creates key pair files" { + local fake_home="$TEST_TEMP_DIR/fake_home" + mkdir -p "$fake_home/.ssh" + + export HOME="$fake_home" + + # Mock ssh-keygen to create fake key files + ssh-keygen() { + # Parse arguments to find output file + local output_file="" + while [[ $# -gt 0 ]]; do + case $1 in + -f) output_file="$2"; shift 2 ;; + *) shift ;; + esac + done + + if [[ -n "$output_file" ]]; then + echo "fake private key" > "$output_file" + echo "fake public key" > "${output_file}.pub" + fi + + return 0 + } + export -f ssh-keygen + + run generate_ssh_keys + [ "$status" -eq 0 ] + [ -f "$fake_home/.ssh/id_rsa" ] + [ -f "$fake_home/.ssh/id_rsa.pub" ] +} + +@test "add_ssh_key_to_agent handles missing ssh-agent gracefully" { + local fake_home="$TEST_TEMP_DIR/fake_home" + mkdir -p "$fake_home/.ssh" + echo "fake private key" > "$fake_home/.ssh/id_rsa" + + export HOME="$fake_home" + + # Mock ssh-add to simulate no agent running initially + ssh-add() { + case "$1" in + -l) return 2 ;; # No agent running + --apple-use-keychain|*) return 0 ;; # Success adding key + esac + } + export -f ssh-add + + # Mock ssh-agent + ssh-agent() { + echo "SSH_AUTH_SOCK=/tmp/fake.sock; export SSH_AUTH_SOCK;" + echo "SSH_AGENT_PID=12345; export SSH_AGENT_PID;" + } + export -f ssh-agent + + run add_ssh_key_to_agent + [ "$status" -eq 0 ] +} \ No newline at end of file From db663c0d5ccaaa1321326b2e61bbfbd7fa76b2c4 Mon Sep 17 00:00:00 2001 From: Michael Uloth Date: Mon, 7 Jul 2025 00:16:01 -0400 Subject: [PATCH 2/3] Update bash migration task documentation with SSH progress Updates the bash migration epic to reflect PR #15 creation and current Phase 1 status. ## Documentation Updates - Added PR #15 SSH Installation Testing details - Updated current status: 2 of 3 core scripts migrated - Adjusted Phase 1 title to reflect it's still in progress - Listed Homebrew as next migration target after CI update Maintains accurate project roadmap showing SSH migration is complete and ready for review. --- .claude/tasks/2025-07-07-bash-migration.md | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/.claude/tasks/2025-07-07-bash-migration.md b/.claude/tasks/2025-07-07-bash-migration.md index 31874d53..5538aa91 100644 --- a/.claude/tasks/2025-07-07-bash-migration.md +++ b/.claude/tasks/2025-07-07-bash-migration.md @@ -4,12 +4,12 @@ **Task**: Migrate dotfiles installation scripts from custom zsh test framework to industry-standard bash + shellcheck + bats **Approach**: Complete 3-phase migration for better tooling, maintainability, and reliability -**Current**: Phase 1 complete (PR #13 merged), critical setup.zsh bug fixed (PR #14 merged), ready for SSH migration +**Current**: Phase 1 continuing with SSH migration (PR #15 in draft), 2 of 3 core scripts migrated ## Migration Strategy -### Phase 1: Demonstrate Complete Migration Pattern (COMPLETED) -**Goal**: Create one complete bash + bats example to establish the migration pattern +### Phase 1: Core Script Migration (IN PROGRESS - 2/3 Complete) +**Goal**: Migrate core installation scripts (GitHub, SSH, Homebrew) to establish patterns #### ✅ PR #13: GitHub Installation Testing (MERGED) - **Files Created**: @@ -151,10 +151,22 @@ test/install/test-{component}-installation.bats # Integration tests - Feature parity verification required - Complete behavior bundle (no dead code) +## Current Work + +### 🔄 PR #15: SSH Installation Testing (Draft) +- **Files Created**: + - `lib/ssh-utils.bash` - SSH utilities with comprehensive functionality + - `bin/install/ssh.bash` - Bash replacement for ssh.zsh + - `test/install/test-ssh-utils.bats` - 14 utility function tests + - `test/install/test-ssh-installation.bats` - 7 integration tests +- **Test Results**: All 21 tests passing, shellcheck compliance verified +- **Improvements**: Better macOS version detection for ssh-add keychain flag +- **Status**: Draft PR created, ready for review + ## Next Steps -1. **Continue Phase 1**: SSH installation script migration to bash + bats -2. **Update CI Workflow**: Migrate from custom zsh runner to bats +1. **Update CI Workflow**: Migrate from custom zsh runner to bats (after PR #15) +2. **Continue Phase 1**: Homebrew installation migration to complete core scripts 3. **Plan Phase 2**: Define systematic migration order for remaining scripts 4. **Document Migration Pattern**: Create guide for future script conversions 5. **Performance Testing**: Compare bash vs zsh test execution times From d9420bded5f16c374ba709ffdffbd9c757c03db4 Mon Sep 17 00:00:00 2001 From: Michael Uloth Date: Wed, 9 Jul 2025 22:50:09 -0400 Subject: [PATCH 3/3] Fix shellcheck source path directive for ssh-utils.bash --- bin/install/ssh.bash | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/install/ssh.bash b/bin/install/ssh.bash index 0737358d..10dead31 100644 --- a/bin/install/ssh.bash +++ b/bin/install/ssh.bash @@ -15,7 +15,7 @@ set -euo pipefail DOTFILES="${DOTFILES:-$HOME/Repos/ooloth/dotfiles}" # Load utilities -# shellcheck source=../../lib/ssh-utils.bash +# shellcheck source=lib/ssh-utils.bash source "$DOTFILES/lib/ssh-utils.bash" main() {