save progress
This commit is contained in:
274
devops/scripts/lib/path-utils.sh
Normal file
274
devops/scripts/lib/path-utils.sh
Normal file
@@ -0,0 +1,274 @@
|
||||
#!/usr/bin/env bash
|
||||
# Shared Path Utilities
|
||||
# Sprint: CI/CD Enhancement - Script Consolidation
|
||||
#
|
||||
# Purpose: Path manipulation and file operations for CI/CD scripts
|
||||
# Usage: source "$(dirname "${BASH_SOURCE[0]}")/lib/path-utils.sh"
|
||||
|
||||
# Prevent multiple sourcing
|
||||
if [[ -n "${__STELLAOPS_PATH_UTILS_LOADED:-}" ]]; then
|
||||
return 0
|
||||
fi
|
||||
export __STELLAOPS_PATH_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
|
||||
|
||||
# ============================================================================
|
||||
# Path Normalization
|
||||
# ============================================================================
|
||||
|
||||
# Normalize path (resolve .., ., symlinks)
|
||||
normalize_path() {
|
||||
local path="$1"
|
||||
|
||||
# Handle empty path
|
||||
if [[ -z "$path" ]]; then
|
||||
echo "."
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Try realpath first (most reliable)
|
||||
if command -v realpath >/dev/null 2>&1; then
|
||||
realpath -m "$path" 2>/dev/null && return 0
|
||||
fi
|
||||
|
||||
# Fallback to Python
|
||||
if command -v python3 >/dev/null 2>&1; then
|
||||
python3 -c "import os; print(os.path.normpath('$path'))" 2>/dev/null && return 0
|
||||
fi
|
||||
|
||||
# Manual normalization (basic)
|
||||
echo "$path" | sed 's|/\./|/|g' | sed 's|/[^/]*/\.\./|/|g' | sed 's|//|/|g'
|
||||
}
|
||||
|
||||
# Get absolute path
|
||||
absolute_path() {
|
||||
local path="$1"
|
||||
|
||||
if [[ "$path" == /* ]]; then
|
||||
normalize_path "$path"
|
||||
else
|
||||
normalize_path "$(pwd)/$path"
|
||||
fi
|
||||
}
|
||||
|
||||
# Get relative path from one path to another
|
||||
relative_path() {
|
||||
local from="$1"
|
||||
local to="$2"
|
||||
|
||||
if command -v realpath >/dev/null 2>&1; then
|
||||
realpath --relative-to="$from" "$to" 2>/dev/null && return 0
|
||||
fi
|
||||
|
||||
if command -v python3 >/dev/null 2>&1; then
|
||||
python3 -c "import os.path; print(os.path.relpath('$to', '$from'))" 2>/dev/null && return 0
|
||||
fi
|
||||
|
||||
# Fallback: just return absolute path
|
||||
absolute_path "$to"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Path Components
|
||||
# ============================================================================
|
||||
|
||||
# Get directory name
|
||||
dir_name() {
|
||||
dirname "$1"
|
||||
}
|
||||
|
||||
# Get base name
|
||||
base_name() {
|
||||
basename "$1"
|
||||
}
|
||||
|
||||
# Get file extension
|
||||
file_extension() {
|
||||
local path="$1"
|
||||
local base
|
||||
base=$(basename "$path")
|
||||
|
||||
if [[ "$base" == *.* ]]; then
|
||||
echo "${base##*.}"
|
||||
else
|
||||
echo ""
|
||||
fi
|
||||
}
|
||||
|
||||
# Get file name without extension
|
||||
file_stem() {
|
||||
local path="$1"
|
||||
local base
|
||||
base=$(basename "$path")
|
||||
|
||||
if [[ "$base" == *.* ]]; then
|
||||
echo "${base%.*}"
|
||||
else
|
||||
echo "$base"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Directory Operations
|
||||
# ============================================================================
|
||||
|
||||
# Ensure directory exists
|
||||
ensure_directory() {
|
||||
local dir="$1"
|
||||
if [[ ! -d "$dir" ]]; then
|
||||
mkdir -p "$dir"
|
||||
fi
|
||||
}
|
||||
|
||||
# Create temporary directory
|
||||
create_temp_dir() {
|
||||
local prefix="${1:-stellaops}"
|
||||
mktemp -d "${TMPDIR:-/tmp}/${prefix}.XXXXXX"
|
||||
}
|
||||
|
||||
# Create temporary file
|
||||
create_temp_file() {
|
||||
local prefix="${1:-stellaops}"
|
||||
local suffix="${2:-}"
|
||||
mktemp "${TMPDIR:-/tmp}/${prefix}.XXXXXX${suffix}"
|
||||
}
|
||||
|
||||
# Clean temporary directory
|
||||
clean_temp() {
|
||||
local path="$1"
|
||||
if [[ -d "$path" ]] && [[ "$path" == *stellaops* ]]; then
|
||||
rm -rf "$path"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# File Existence Checks
|
||||
# ============================================================================
|
||||
|
||||
# Check if file exists
|
||||
file_exists() {
|
||||
[[ -f "$1" ]]
|
||||
}
|
||||
|
||||
# Check if directory exists
|
||||
dir_exists() {
|
||||
[[ -d "$1" ]]
|
||||
}
|
||||
|
||||
# Check if path exists (file or directory)
|
||||
path_exists() {
|
||||
[[ -e "$1" ]]
|
||||
}
|
||||
|
||||
# Check if file is readable
|
||||
file_readable() {
|
||||
[[ -r "$1" ]]
|
||||
}
|
||||
|
||||
# Check if file is writable
|
||||
file_writable() {
|
||||
[[ -w "$1" ]]
|
||||
}
|
||||
|
||||
# Check if file is executable
|
||||
file_executable() {
|
||||
[[ -x "$1" ]]
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# File Discovery
|
||||
# ============================================================================
|
||||
|
||||
# Find files by pattern
|
||||
find_files() {
|
||||
local dir="${1:-.}"
|
||||
local pattern="${2:-*}"
|
||||
find "$dir" -type f -name "$pattern" 2>/dev/null
|
||||
}
|
||||
|
||||
# Find files by extension
|
||||
find_by_extension() {
|
||||
local dir="${1:-.}"
|
||||
local ext="${2:-}"
|
||||
find "$dir" -type f -name "*.${ext}" 2>/dev/null
|
||||
}
|
||||
|
||||
# Find project files (csproj, package.json, etc.)
|
||||
find_project_files() {
|
||||
local dir="${1:-.}"
|
||||
find "$dir" -type f \( \
|
||||
-name "*.csproj" -o \
|
||||
-name "*.fsproj" -o \
|
||||
-name "package.json" -o \
|
||||
-name "Cargo.toml" -o \
|
||||
-name "go.mod" -o \
|
||||
-name "pom.xml" -o \
|
||||
-name "build.gradle" \
|
||||
\) 2>/dev/null | grep -v node_modules | grep -v bin | grep -v obj
|
||||
}
|
||||
|
||||
# Find test projects
|
||||
find_test_projects() {
|
||||
local dir="${1:-.}"
|
||||
find "$dir" -type f -name "*.Tests.csproj" 2>/dev/null | grep -v bin | grep -v obj
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Path Validation
|
||||
# ============================================================================
|
||||
|
||||
# Check if path is under directory
|
||||
path_under() {
|
||||
local path="$1"
|
||||
local dir="$2"
|
||||
|
||||
local abs_path abs_dir
|
||||
abs_path=$(absolute_path "$path")
|
||||
abs_dir=$(absolute_path "$dir")
|
||||
|
||||
[[ "$abs_path" == "$abs_dir"* ]]
|
||||
}
|
||||
|
||||
# Validate path is safe (no directory traversal)
|
||||
path_is_safe() {
|
||||
local path="$1"
|
||||
local base="${2:-.}"
|
||||
|
||||
# Check for obvious traversal attempts
|
||||
if [[ "$path" == *".."* ]] || [[ "$path" == "/*" ]]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Verify resolved path is under base
|
||||
path_under "$path" "$base"
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# CI/CD Helpers
|
||||
# ============================================================================
|
||||
|
||||
# Get artifact output directory
|
||||
get_artifact_dir() {
|
||||
local name="${1:-artifacts}"
|
||||
local base="${GITHUB_WORKSPACE:-$(pwd)}"
|
||||
echo "${base}/out/${name}"
|
||||
}
|
||||
|
||||
# Get test results directory
|
||||
get_test_results_dir() {
|
||||
local base="${GITHUB_WORKSPACE:-$(pwd)}"
|
||||
echo "${base}/TestResults"
|
||||
}
|
||||
|
||||
# Ensure artifact directory exists and return path
|
||||
ensure_artifact_dir() {
|
||||
local name="${1:-artifacts}"
|
||||
local dir
|
||||
dir=$(get_artifact_dir "$name")
|
||||
ensure_directory "$dir"
|
||||
echo "$dir"
|
||||
}
|
||||
Reference in New Issue
Block a user