From 9c0551890f55cc8eed4228f51dcd27392b664d6c Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah Date: Mon, 6 Jul 2026 20:22:52 -0700 Subject: [PATCH] Use shared file section helpers in update-profile --- .github/workflows/tests.yml | 8 +- .../basectl/subcommands/update_profile.sh | 114 +++++------------- .../basectl/tests/update-profile.bats | 65 ++++++++++ 3 files changed, 96 insertions(+), 91 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 91ff27d7..6feebf67 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -57,7 +57,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 with: repository: basefoundry/base-bash-libs - ref: d62e8c1fec2de9148bc832977c206b9f1827dd19 + ref: 424ef9fe61bc8a462ba9980ea63b38dd202ec242 path: .dependencies/base-bash-libs - name: Install BATS @@ -91,7 +91,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 with: repository: basefoundry/base-bash-libs - ref: d62e8c1fec2de9148bc832977c206b9f1827dd19 + ref: 424ef9fe61bc8a462ba9980ea63b38dd202ec242 path: .dependencies/base-bash-libs - name: Set up Python @@ -167,7 +167,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 with: repository: basefoundry/base-bash-libs - ref: d62e8c1fec2de9148bc832977c206b9f1827dd19 + ref: 424ef9fe61bc8a462ba9980ea63b38dd202ec242 path: .dependencies/base-bash-libs - name: Set up Python @@ -210,7 +210,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 with: repository: basefoundry/base-bash-libs - ref: d62e8c1fec2de9148bc832977c206b9f1827dd19 + ref: 424ef9fe61bc8a462ba9980ea63b38dd202ec242 path: .dependencies/base-bash-libs - name: Expose reusable Bash library checkout as sibling diff --git a/cli/bash/commands/basectl/subcommands/update_profile.sh b/cli/bash/commands/basectl/subcommands/update_profile.sh index 5d4fff59..fa84089e 100644 --- a/cli/bash/commands/basectl/subcommands/update_profile.sh +++ b/cli/bash/commands/basectl/subcommands/update_profile.sh @@ -42,6 +42,7 @@ base_update_profile_usage_error() { base_update_profile_source_file_library() { import_base_lib file/lib_file.sh + assert_function_exists file_section_exists file_section_needs_update } base_update_profile_shell_double_quote() { @@ -162,102 +163,25 @@ base_update_profile_backup_existing_file() { cp -p "$target_file" "$backup_path" || fatal_error "Unable to back up '$target_file' to '$backup_path'." } -base_update_profile_write_section_content() { - local snippet_name="$1" - local output_file="$2" - local lines=() - - mapfile -t lines < <(base_update_profile_section_lines "$snippet_name") || return 1 - printf '%s\n' "${lines[@]}" > "$output_file" -} - -base_update_profile_existing_section_content() { - local target_file="$1" - local start_marker="$2" - local end_marker="$3" - local output_file="$4" - - awk -v START_M="$start_marker" -v END_M="$end_marker" ' - BEGIN { - in_section = 0 - processed = 0 - } - $0 == START_M && processed == 0 { - in_section = 1 - next - } - $0 == END_M && in_section == 1 { - processed = 1 - exit - } - in_section == 1 { - print $0 - } - END { - if (processed == 0) { - exit 1 - } - } - ' "$target_file" > "$output_file" -} - base_update_profile_update_file_needs_change() { local target_file="$1" local snippet_name="$2" local start_marker="$3" local end_marker="$4" - local current_content_file - local desired_content_file - local start_count - local end_count + local lines=() [[ -f "$target_file" ]] || return 0 - start_count="$(grep -cxF -- "$start_marker" "$target_file" || true)" - end_count="$(grep -cxF -- "$end_marker" "$target_file" || true)" - if ((start_count == 0 && end_count == 0)); then - return 0 - fi - if ((start_count != end_count)); then - return 0 - fi - - std_make_temp_file current_content_file base-profile-current || - fatal_error "Unable to create temporary section content file for '$target_file'." - std_make_temp_file desired_content_file base-profile-desired || { - fatal_error "Unable to create temporary desired content file for '$target_file'." - } - - if ! base_update_profile_existing_section_content "$target_file" "$start_marker" "$end_marker" "$current_content_file"; then - rm -f "$current_content_file" "$desired_content_file" - return 0 - fi - base_update_profile_write_section_content "$snippet_name" "$desired_content_file" || { - rm -f "$current_content_file" "$desired_content_file" - return 1 - } - - if cmp -s "$current_content_file" "$desired_content_file"; then - rm -f "$current_content_file" "$desired_content_file" - return 1 - fi - - rm -f "$current_content_file" "$desired_content_file" - return 0 + mapfile -t lines < <(base_update_profile_section_lines "$snippet_name") || return 2 + file_section_needs_update "$target_file" "$start_marker" "$end_marker" "${lines[@]}" } base_update_profile_remove_file_needs_change() { local target_file="$1" local start_marker="$2" local end_marker="$3" - local start_count - local end_count - - [[ -f "$target_file" ]] || return 1 - start_count="$(grep -cxF -- "$start_marker" "$target_file" || true)" - end_count="$(grep -cxF -- "$end_marker" "$target_file" || true)" - ((start_count > 0 || end_count > 0)) + file_section_exists "$target_file" "$start_marker" "$end_marker" } base_update_profile_update_file() { @@ -273,9 +197,17 @@ base_update_profile_update_file() { end_marker="$(base_update_profile_end_marker "$snippet_name")" || return 1 mapfile -t lines < <(base_update_profile_section_lines "$snippet_name") || return 1 - if ! base_update_profile_update_file_needs_change "$target_file" "$snippet_name" "$start_marker" "$end_marker"; then - return 0 - fi + base_update_profile_update_file_needs_change "$target_file" "$snippet_name" "$start_marker" "$end_marker" + case $? in + 0) + ;; + 1) + return 0 + ;; + *) + return 1 + ;; + esac if ((dry_run)); then base_update_profile_backup_existing_file "$target_file" "$backup_timestamp" "$dry_run" || return 1 @@ -300,9 +232,17 @@ base_update_profile_remove_file() { start_marker="$(base_update_profile_start_marker "$snippet_name")" || return 1 end_marker="$(base_update_profile_end_marker "$snippet_name")" || return 1 - if ! base_update_profile_remove_file_needs_change "$target_file" "$start_marker" "$end_marker"; then - return 0 - fi + base_update_profile_remove_file_needs_change "$target_file" "$start_marker" "$end_marker" + case $? in + 0) + ;; + 1) + return 0 + ;; + *) + return 1 + ;; + esac if ((dry_run)); then base_update_profile_backup_existing_file "$target_file" "$backup_timestamp" "$dry_run" || return 1 diff --git a/cli/bash/commands/basectl/tests/update-profile.bats b/cli/bash/commands/basectl/tests/update-profile.bats index 8fc84c40..8943ef39 100644 --- a/cli/bash/commands/basectl/tests/update-profile.bats +++ b/cli/bash/commands/basectl/tests/update-profile.bats @@ -195,6 +195,71 @@ EOF [[ "$(cat "$TEST_HOME/.zprofile"; printf marker)" == "marker" ]] } +@test "update-profile update detection delegates to shared file section helper" { + local bash_libs_dir + + bash_libs_dir="$(base_bash_libs_fixture_dir)" + touch "$TEST_HOME/.bashrc" + + run env \ + HOME="$TEST_HOME" \ + BASE_HOME="$BASE_REPO_ROOT" \ + BASE_BASH_LIBS_DIR="$bash_libs_dir" \ + bash -c ' + source "$BASE_HOME/base_init.sh" + source "$BASE_HOME/cli/bash/commands/basectl/subcommands/update_profile.sh" + base_update_profile_source_file_library + file_section_needs_update() { + printf "helper_target=%s\n" "$1" + printf "helper_start=%s\n" "$2" + printf "helper_end=%s\n" "$3" + shift 3 + printf "helper_content=%s\n" "$*" + return 1 + } + base_update_profile_update_file_needs_change "$HOME/.bashrc" bashrc "# >>> base: bashrc managed >>>" "# <<< base: bashrc managed <<<" + printf "change_status=%s\n" "$?" + ' + + [ "$status" -eq 0 ] + [[ "$output" == *"helper_target=$TEST_HOME/.bashrc"* ]] + [[ "$output" == *"helper_start=# >>> base: bashrc managed >>>"* ]] + [[ "$output" == *"helper_end=# <<< base: bashrc managed <<<"* ]] + [[ "$output" == *"source \"$BASE_REPO_ROOT/lib/shell/bashrc\""* ]] + [[ "$output" == *"change_status=1"* ]] +} + +@test "update-profile remove detection delegates to shared file section helper" { + local bash_libs_dir + + bash_libs_dir="$(base_bash_libs_fixture_dir)" + touch "$TEST_HOME/.bashrc" + + run env \ + HOME="$TEST_HOME" \ + BASE_HOME="$BASE_REPO_ROOT" \ + BASE_BASH_LIBS_DIR="$bash_libs_dir" \ + bash -c ' + source "$BASE_HOME/base_init.sh" + source "$BASE_HOME/cli/bash/commands/basectl/subcommands/update_profile.sh" + base_update_profile_source_file_library + file_section_exists() { + printf "helper_target=%s\n" "$1" + printf "helper_start=%s\n" "$2" + printf "helper_end=%s\n" "$3" + return 0 + } + base_update_profile_remove_file_needs_change "$HOME/.bashrc" "# >>> base: bashrc managed >>>" "# <<< base: bashrc managed <<<" + printf "change_status=%s\n" "$?" + ' + + [ "$status" -eq 0 ] + [[ "$output" == *"helper_target=$TEST_HOME/.bashrc"* ]] + [[ "$output" == *"helper_start=# >>> base: bashrc managed >>>"* ]] + [[ "$output" == *"helper_end=# <<< base: bashrc managed <<<"* ]] + [[ "$output" == *"change_status=0"* ]] +} + @test "basectl update-profile explains BASE_HOME mismatch recovery" { local runtime_base="$TEST_TMPDIR/runtime-base" local resolved_base="$TEST_TMPDIR/resolved-base"