save progress
This commit is contained in:
266
devops/scripts/lib/hash-utils.sh
Normal file
266
devops/scripts/lib/hash-utils.sh
Normal file
@@ -0,0 +1,266 @@
|
||||
#!/usr/bin/env bash
|
||||
# Shared Hash/Checksum Utilities
|
||||
# Sprint: CI/CD Enhancement - Script Consolidation
|
||||
#
|
||||
# Purpose: Cryptographic hash and checksum operations for CI/CD scripts
|
||||
# Usage: source "$(dirname "${BASH_SOURCE[0]}")/lib/hash-utils.sh"
|
||||
|
||||
# Prevent multiple sourcing
|
||||
if [[ -n "${__STELLAOPS_HASH_UTILS_LOADED:-}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
export __STELLAOPS_HASH_UTILS_LOADED=1
|
||||
|
||||
# Source dependencies
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "${SCRIPT_DIR}/logging.sh" 2>/dev/null || true
|
||||
source "${SCRIPT_DIR}/exit-codes.sh" 2>/dev/null || true
|
||||
|
||||
# ============================================================================
|
||||
# Hash Computation
|
||||
# ============================================================================
|
||||
|
||||
# Compute SHA-256 hash of a file
|
||||
compute_sha256() {
|
||||
local file="$1"
|
||||
|
||||
if [[ ! -f "$file" ]]; then
|
||||
log_error "File not found: $file"
|
||||
return "${EXIT_NOT_FOUND:-4}"
|
||||
fi
|
||||
|
||||
if command -v sha256sum >/dev/null 2>&1; then
|
||||
sha256sum "$file" | awk '{print $1}'
|
||||
elif command -v shasum >/dev/null 2>&1; then
|
||||
shasum -a 256 "$file" | awk '{print $1}'
|
||||
elif command -v openssl >/dev/null 2>&1; then
|
||||
openssl dgst -sha256 "$file" | awk '{print $NF}'
|
||||
else
|
||||
log_error "No SHA-256 tool available"
|
||||
return "${EXIT_MISSING_TOOL:-10}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Compute SHA-512 hash of a file
|
||||
compute_sha512() {
|
||||
local file="$1"
|
||||
|
||||
if [[ ! -f "$file" ]]; then
|
||||
log_error "File not found: $file"
|
||||
return "${EXIT_NOT_FOUND:-4}"
|
||||
fi
|
||||
|
||||
if command -v sha512sum >/dev/null 2>&1; then
|
||||
sha512sum "$file" | awk '{print $1}'
|
||||
elif command -v shasum >/dev/null 2>&1; then
|
||||
shasum -a 512 "$file" | awk '{print $1}'
|
||||
elif command -v openssl >/dev/null 2>&1; then
|
||||
openssl dgst -sha512 "$file" | awk '{print $NF}'
|
||||
else
|
||||
log_error "No SHA-512 tool available"
|
||||
return "${EXIT_MISSING_TOOL:-10}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Compute MD5 hash of a file (for compatibility, not security)
|
||||
compute_md5() {
|
||||
local file="$1"
|
||||
|
||||
if [[ ! -f "$file" ]]; then
|
||||
log_error "File not found: $file"
|
||||
return "${EXIT_NOT_FOUND:-4}"
|
||||
fi
|
||||
|
||||
if command -v md5sum >/dev/null 2>&1; then
|
||||
md5sum "$file" | awk '{print $1}'
|
||||
elif command -v md5 >/dev/null 2>&1; then
|
||||
md5 -q "$file"
|
||||
elif command -v openssl >/dev/null 2>&1; then
|
||||
openssl dgst -md5 "$file" | awk '{print $NF}'
|
||||
else
|
||||
log_error "No MD5 tool available"
|
||||
return "${EXIT_MISSING_TOOL:-10}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Compute hash of string
|
||||
compute_string_hash() {
|
||||
local string="$1"
|
||||
local algorithm="${2:-sha256}"
|
||||
|
||||
case "$algorithm" in
|
||||
sha256)
|
||||
echo -n "$string" | sha256sum 2>/dev/null | awk '{print $1}' || \
|
||||
echo -n "$string" | shasum -a 256 2>/dev/null | awk '{print $1}'
|
||||
;;
|
||||
sha512)
|
||||
echo -n "$string" | sha512sum 2>/dev/null | awk '{print $1}' || \
|
||||
echo -n "$string" | shasum -a 512 2>/dev/null | awk '{print $1}'
|
||||
;;
|
||||
md5)
|
||||
echo -n "$string" | md5sum 2>/dev/null | awk '{print $1}' || \
|
||||
echo -n "$string" | md5 2>/dev/null
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown algorithm: $algorithm"
|
||||
return "${EXIT_USAGE:-2}"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Checksum Files
|
||||
# ============================================================================
|
||||
|
||||
# Write checksum file for a single file
|
||||
write_checksum() {
|
||||
local file="$1"
|
||||
local checksum_file="${2:-${file}.sha256}"
|
||||
local algorithm="${3:-sha256}"
|
||||
|
||||
local hash
|
||||
case "$algorithm" in
|
||||
sha256) hash=$(compute_sha256 "$file") ;;
|
||||
sha512) hash=$(compute_sha512 "$file") ;;
|
||||
md5) hash=$(compute_md5 "$file") ;;
|
||||
*)
|
||||
log_error "Unknown algorithm: $algorithm"
|
||||
return "${EXIT_USAGE:-2}"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ -z "$hash" ]]; then
|
||||
return "${EXIT_ERROR:-1}"
|
||||
fi
|
||||
|
||||
local basename
|
||||
basename=$(basename "$file")
|
||||
echo "$hash $basename" > "$checksum_file"
|
||||
log_debug "Wrote checksum to $checksum_file"
|
||||
}
|
||||
|
||||
# Write checksums for multiple files
|
||||
write_checksums() {
|
||||
local output_file="$1"
|
||||
shift
|
||||
local files=("$@")
|
||||
|
||||
: > "$output_file"
|
||||
|
||||
for file in "${files[@]}"; do
|
||||
if [[ -f "$file" ]]; then
|
||||
local hash basename
|
||||
hash=$(compute_sha256 "$file")
|
||||
basename=$(basename "$file")
|
||||
echo "$hash $basename" >> "$output_file"
|
||||
fi
|
||||
done
|
||||
|
||||
log_debug "Wrote checksums to $output_file"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Checksum Verification
|
||||
# ============================================================================
|
||||
|
||||
# Verify checksum of a file
|
||||
verify_checksum() {
|
||||
local file="$1"
|
||||
local expected_hash="$2"
|
||||
local algorithm="${3:-sha256}"
|
||||
|
||||
local actual_hash
|
||||
case "$algorithm" in
|
||||
sha256) actual_hash=$(compute_sha256 "$file") ;;
|
||||
sha512) actual_hash=$(compute_sha512 "$file") ;;
|
||||
md5) actual_hash=$(compute_md5 "$file") ;;
|
||||
*)
|
||||
log_error "Unknown algorithm: $algorithm"
|
||||
return "${EXIT_USAGE:-2}"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ "$actual_hash" == "$expected_hash" ]]; then
|
||||
log_debug "Checksum verified: $file"
|
||||
return 0
|
||||
else
|
||||
log_error "Checksum mismatch for $file"
|
||||
log_error " Expected: $expected_hash"
|
||||
log_error " Actual: $actual_hash"
|
||||
return "${EXIT_VERIFY_FAILED:-64}"
|
||||
fi
|
||||
}
|
||||
|
||||
# Verify checksums from file (sha256sum -c style)
|
||||
verify_checksums_file() {
|
||||
local checksum_file="$1"
|
||||
local base_dir="${2:-.}"
|
||||
|
||||
if [[ ! -f "$checksum_file" ]]; then
|
||||
log_error "Checksum file not found: $checksum_file"
|
||||
return "${EXIT_NOT_FOUND:-4}"
|
||||
fi
|
||||
|
||||
local failures=0
|
||||
|
||||
while IFS= read -r line; do
|
||||
# Skip empty lines and comments
|
||||
[[ -z "$line" ]] && continue
|
||||
[[ "$line" == \#* ]] && continue
|
||||
|
||||
local hash filename
|
||||
hash=$(echo "$line" | awk '{print $1}')
|
||||
filename=$(echo "$line" | awk '{print $2}')
|
||||
|
||||
local filepath="${base_dir}/${filename}"
|
||||
|
||||
if [[ ! -f "$filepath" ]]; then
|
||||
log_error "File not found: $filepath"
|
||||
((failures++))
|
||||
continue
|
||||
fi
|
||||
|
||||
if ! verify_checksum "$filepath" "$hash"; then
|
||||
((failures++))
|
||||
fi
|
||||
done < "$checksum_file"
|
||||
|
||||
if [[ $failures -gt 0 ]]; then
|
||||
log_error "$failures checksum verification(s) failed"
|
||||
return "${EXIT_VERIFY_FAILED:-64}"
|
||||
fi
|
||||
|
||||
log_info "All checksums verified"
|
||||
return 0
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Helpers
|
||||
# ============================================================================
|
||||
|
||||
# Check if two files have the same content
|
||||
files_identical() {
|
||||
local file1="$1"
|
||||
local file2="$2"
|
||||
|
||||
[[ -f "$file1" ]] && [[ -f "$file2" ]] || return 1
|
||||
|
||||
local hash1 hash2
|
||||
hash1=$(compute_sha256 "$file1")
|
||||
hash2=$(compute_sha256 "$file2")
|
||||
|
||||
[[ "$hash1" == "$hash2" ]]
|
||||
}
|
||||
|
||||
# Get short hash for display
|
||||
short_hash() {
|
||||
local hash="$1"
|
||||
local length="${2:-8}"
|
||||
echo "${hash:0:$length}"
|
||||
}
|
||||
|
||||
# Generate deterministic ID from inputs
|
||||
generate_id() {
|
||||
local inputs="$*"
|
||||
compute_string_hash "$inputs" sha256 | head -c 16
|
||||
}
|
||||
Reference in New Issue
Block a user