Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cli/bash/commands/basectl/tests/update-profile.bats
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ load ./setup_helpers.bash
run_base_command update-profile
[ "$status" -eq 0 ]

[[ "$output" != *"Updating '$TEST_HOME/.bash_profile'"* ]]
[[ "$output" != *"Updating '$TEST_HOME/.bashrc'"* ]]
[[ "$output" != *"Updating '$TEST_HOME/.zprofile'"* ]]
[[ "$output" != *"Updating '$TEST_HOME/.zshrc'"* ]]
[ "$(grep -c '# --- BEGIN base bashrc MANAGED SECTION - DO NOT EDIT ---' "$TEST_HOME/.bashrc")" -eq 1 ]
[ "$(grep -c '# --- END base bashrc MANAGED SECTION - DO NOT EDIT ---' "$TEST_HOME/.bashrc")" -eq 1 ]
[[ "$(cat "$TEST_HOME/.bashrc")" == *"user line before"* ]]
Expand Down
131 changes: 87 additions & 44 deletions lib/bash/file/lib_file.sh
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,11 @@ update_file_section() {
return 1
fi

log_info "Updating '$target_file'"
local section_exists=false
if ((beginning_marker_count > 0)); then
section_exists=true
fi

local new_content_string=""
if [[ "$remove_section" == false ]]; then
if [[ ${#new_content_array[@]} -gt 0 ]]; then
Expand All @@ -90,29 +94,73 @@ update_file_section() {
fi
fi

local temp_file new_content_file
temp_file=$(mktemp "${target_file}.XXXXXX")
if [[ ! -f "$temp_file" ]]; then
log_error "Failed to create temporary file for '$target_file'."
return 1
fi

new_content_file=$(mktemp "${target_file}.new.XXXXXX")
if [[ ! -f "$new_content_file" ]]; then
log_error "Failed to create temporary content file for '$target_file'."
rm -f "$temp_file"
return 1
if [[ "$section_exists" == false && "$remove_section" == true ]]; then
log_debug "Section not present in '$target_file'; nothing to remove."
return 0
fi

local current_content_file="" new_content_file="" temp_file
if [[ "$remove_section" == false ]]; then
new_content_file=$(mktemp "${TMPDIR:-/tmp}/base-file-section-new.XXXXXX")
if [[ ! -f "$new_content_file" ]]; then
log_error "Failed to create temporary content file for '$target_file'."
return 1
fi

if ! printf '%s' "$new_content_string" > "$new_content_file"; then
log_error "Failed to write replacement content for '$target_file'."
rm -f "$temp_file" "$new_content_file"
rm -f "$new_content_file"
return 1
fi
fi

if [[ "$section_exists" == true && "$remove_section" == false ]]; then
current_content_file=$(mktemp "${TMPDIR:-/tmp}/base-file-section-current.XXXXXX")
if [[ ! -f "$current_content_file" ]]; then
log_error "Failed to create temporary current content file for '$target_file'."
rm -f "$new_content_file"
return 1
fi

if ! awk -v START_M="$beginning_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
}
' "$target_file" > "$current_content_file"; then
log_error "Failed to read existing section in '$target_file'."
rm -f "$current_content_file" "$new_content_file"
return 1
fi

if cmp -s "$current_content_file" "$new_content_file"; then
log_debug "Section already up to date in '$target_file'."
rm -f "$current_content_file" "$new_content_file"
return 0
fi
rm -f "$current_content_file"
fi

if grep -qF -- "$beginning_marker" "$target_file" && grep -qF -- "$end_marker" "$target_file"; then
log_info "Updating '$target_file'"
temp_file=$(mktemp "${target_file}.XXXXXX")
if [[ ! -f "$temp_file" ]]; then
log_error "Failed to create temporary file for '$target_file'."
rm -f "$new_content_file"
return 1
fi

if [[ "$section_exists" == true ]]; then
if [[ "$remove_section" == true ]]; then
if awk -v START_M="$beginning_marker" -v END_M="$end_marker" '
BEGIN { in_section = 0 }
Expand Down Expand Up @@ -161,42 +209,37 @@ update_file_section() {
return 1
else
# Markers not found in the file
if [[ "$remove_section" == true ]]; then
if ! cp "$target_file" "$temp_file"; then
log_error "Failed to copy '$target_file' to '$temp_file'."
rm -f "$temp_file" "$new_content_file"
return 0
else
if ! cp "$target_file" "$temp_file"; then
log_error "Failed to copy '$target_file' to '$temp_file'."
rm -f "$temp_file" "$new_content_file"
return 1
fi

if [[ $(tail -c 1 "$temp_file" 2>/dev/null | wc -l) -eq 0 ]]; then
if ! printf '\n' >> "$temp_file"; then
log_error "Failed to add trailing newline to '$temp_file'."
rm -f "$temp_file" "$new_content_file"
return 1
fi
fi
return 1
fi

if ! {
printf '%s\n' "$beginning_marker"
printf '%s' "$new_content_string"
printf '%s\n' "$end_marker"
} >> "$temp_file"; then
log_error "Failed to add new section to '$target_file'."
if [[ $(tail -c 1 "$temp_file" 2>/dev/null | wc -l) -eq 0 ]]; then
if ! printf '\n' >> "$temp_file"; then
log_error "Failed to add trailing newline to '$temp_file'."
rm -f "$temp_file" "$new_content_file"
return 1
fi
fi

if ! mv -f "$temp_file" "$target_file"; then
log_error "Failed to replace '$target_file' with '$temp_file'."
rm -f "$temp_file" "$new_content_file"
return 1
fi
if ! {
printf '%s\n' "$beginning_marker"
printf '%s' "$new_content_string"
printf '%s\n' "$end_marker"
} >> "$temp_file"; then
log_error "Failed to add new section to '$target_file'."
rm -f "$temp_file" "$new_content_file"
return 1
fi

rm -f "$new_content_file"
return 0
if ! mv -f "$temp_file" "$target_file"; then
log_error "Failed to replace '$target_file' with '$temp_file'."
rm -f "$temp_file" "$new_content_file"
return 1
fi

rm -f "$new_content_file"
return 0
fi
}
35 changes: 35 additions & 0 deletions lib/bash/file/tests/lib_file.bats
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ setup() {
source "$BASE_BASH_DIR/file/lib_file.sh"
}

file_inode() {
if stat -c '%i' "$1" >/dev/null 2>&1; then
stat -c '%i' "$1"
else
stat -f '%i' "$1"
fi
}

@test "update_file_section appends a new marked block when markers are absent" {
local target="$TEST_TMPDIR/config.txt"
printf 'line-one' > "$target"
Expand Down Expand Up @@ -62,6 +70,33 @@ EOF
[ "$(cat "$target")" = $'before\n# BEGIN\nfirst\nsecond\nthird\n# END\nafter' ]
}

@test "update_file_section skips unchanged existing section" {
local before_inode
local target="$TEST_TMPDIR/config.txt"
cat <<'EOF' > "$target"
before
# BEGIN
same
content
# END
after
EOF
before_inode="$(file_inode "$target")"

bats_run update_file_section "$target" "# BEGIN" "# END" "same" "content"

[ "$status" -eq 0 ]
[[ "$output" != *"Updating '$target'"* ]]
[ "$(file_inode "$target")" = "$before_inode" ]
[ "$(cat "$target")" = $'before\n# BEGIN\nsame\ncontent\n# END\nafter' ]

set_log_level DEBUG
bats_run update_file_section "$target" "# BEGIN" "# END" "same" "content"

[ "$status" -eq 0 ]
[[ "$output" == *"Section already up to date in '$target'."* ]]
}

@test "update_file_section does not export replacement content to awk" {
local awk_log="$TEST_TMPDIR/awk-env.log"
local target="$TEST_TMPDIR/config.txt"
Expand Down
Loading