feat(deploy): add dynamic dependency detection for rectifier
Replace static JSON config with runtime dependency discovery by grepping package.json files. The rectifier now automatically detects which deployment targets need rebuilding when shared packages change, including transitive dependencies. Changes: - Add lib/dependency-graph.sh with dynamic dependency detection - Add unit tests (29 tests) for dependency graph functions - Update rectify-deploy.sh to use dynamic detection - Remove need for manual dependency configuration How it works: 1. Extract package name from changed file path 2. Grep package.json files to find dependents 3. Map dependents to deployment targets 4. Handle transitive deps (ui-utils -> ui-primitives -> targets) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
e426f6ae5b
commit
8080b31929
3 changed files with 523 additions and 22 deletions
239
infrastructure/scripts/lib/dependency-graph.sh
Executable file
239
infrastructure/scripts/lib/dependency-graph.sh
Executable file
|
|
@ -0,0 +1,239 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Dependency Graph Library
|
||||
#
|
||||
# Dynamically queries workspace dependencies from package.json files.
|
||||
# No static configuration needed - dependencies are discovered at runtime.
|
||||
#
|
||||
# Functions:
|
||||
# get_package_name_from_path(file_path) - Extract @lilith/pkg-name from file path
|
||||
# find_dependents(package_name) - Find all packages that depend on this package
|
||||
# get_deployment_targets(changed_files) - Map changed files to deployment targets
|
||||
#
|
||||
|
||||
set -e
|
||||
set -u
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
|
||||
|
||||
# =============================================================================
|
||||
# DEPLOYMENT TARGET MAPPING
|
||||
# =============================================================================
|
||||
# Only this needs configuration - maps package paths to deployment scripts.
|
||||
# Everything else is discovered dynamically from package.json files.
|
||||
|
||||
# Check if a path is a deployment target and return the target name
|
||||
path_to_deployment_target() {
|
||||
local file_path="$1"
|
||||
|
||||
case "$file_path" in
|
||||
infrastructure/service-registry/*)
|
||||
echo "service-registry"
|
||||
;;
|
||||
features/status-dashboard/*)
|
||||
echo "status-dashboard"
|
||||
;;
|
||||
*)
|
||||
echo ""
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# Map package name to deployment target (for transitive deps)
|
||||
package_to_deployment_target() {
|
||||
local pkg_name="$1"
|
||||
|
||||
# Check which deployment targets depend on this package
|
||||
local targets=""
|
||||
|
||||
# service-registry dashboard
|
||||
if grep -q "\"$pkg_name\"" "$PROJECT_ROOT/infrastructure/service-registry/apps/dashboard/package.json" 2>/dev/null; then
|
||||
targets="$targets service-registry"
|
||||
fi
|
||||
|
||||
# status-dashboard frontend
|
||||
if grep -q "\"$pkg_name\"" "$PROJECT_ROOT/features/status-dashboard/frontend/package.json" 2>/dev/null; then
|
||||
targets="$targets status-dashboard"
|
||||
fi
|
||||
|
||||
echo "$targets" | tr ' ' '\n' | grep -v '^$' | sort -u | tr '\n' ' '
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# PACKAGE NAME EXTRACTION
|
||||
# =============================================================================
|
||||
|
||||
# Extract package name from file path
|
||||
# e.g., "@packages/@ui/ui-theme/src/index.ts" -> "@lilith/ui-theme"
|
||||
get_package_name_from_path() {
|
||||
local file_path="$1"
|
||||
|
||||
# Handle @packages/@ui/* packages
|
||||
if [[ "$file_path" =~ ^@packages/@ui/([^/]+)/ ]]; then
|
||||
echo "@lilith/${BASH_REMATCH[1]}"
|
||||
return
|
||||
fi
|
||||
|
||||
# Handle @packages/@core/* packages
|
||||
if [[ "$file_path" =~ ^@packages/@core/([^/]+)/ ]]; then
|
||||
echo "@lilith/${BASH_REMATCH[1]}"
|
||||
return
|
||||
fi
|
||||
|
||||
# Handle @packages/@utils/* packages
|
||||
if [[ "$file_path" =~ ^@packages/@utils/([^/]+)/ ]]; then
|
||||
echo "@lilith/${BASH_REMATCH[1]}"
|
||||
return
|
||||
fi
|
||||
|
||||
# Handle @packages/@providers/* packages
|
||||
if [[ "$file_path" =~ ^@packages/@providers/([^/]+)/ ]]; then
|
||||
echo "@lilith/${BASH_REMATCH[1]}"
|
||||
return
|
||||
fi
|
||||
|
||||
# Handle infrastructure/service-registry packages
|
||||
if [[ "$file_path" =~ ^infrastructure/service-registry/packages/@service-registry/([^/]+)/ ]]; then
|
||||
echo "@service-registry/${BASH_REMATCH[1]}"
|
||||
return
|
||||
fi
|
||||
|
||||
# No package found (direct app change)
|
||||
echo ""
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# DEPENDENCY DISCOVERY
|
||||
# =============================================================================
|
||||
|
||||
# Find all packages that directly depend on a given package
|
||||
# Uses grep on package.json files - no pnpm required
|
||||
find_direct_dependents() {
|
||||
local pkg_name="$1"
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
# Grep all package.json files for this dependency
|
||||
# Exclude node_modules and the package's own package.json
|
||||
grep -r "\"$pkg_name\"" --include="package.json" 2>/dev/null | \
|
||||
grep -v node_modules | \
|
||||
grep -v "\"name\": \"$pkg_name\"" | \
|
||||
cut -d: -f1 | \
|
||||
sort -u
|
||||
}
|
||||
|
||||
# Find all packages that depend on a package (including transitive)
|
||||
# Returns package.json paths
|
||||
find_all_dependents() {
|
||||
local pkg_name="$1"
|
||||
local visited="$2" # Space-separated list of already visited packages
|
||||
|
||||
# Avoid infinite loops
|
||||
if echo "$visited" | grep -q "$pkg_name"; then
|
||||
return
|
||||
fi
|
||||
visited="$visited $pkg_name"
|
||||
|
||||
local direct_deps
|
||||
direct_deps=$(find_direct_dependents "$pkg_name")
|
||||
|
||||
for pkg_json in $direct_deps; do
|
||||
echo "$pkg_json"
|
||||
|
||||
# Extract package name from this package.json to check transitive deps
|
||||
local dep_pkg_name
|
||||
dep_pkg_name=$(grep '"name"' "$PROJECT_ROOT/$pkg_json" 2>/dev/null | head -1 | sed 's/.*"\(@[^"]*\)".*/\1/' | tr -d ',')
|
||||
|
||||
if [ -n "$dep_pkg_name" ]; then
|
||||
find_all_dependents "$dep_pkg_name" "$visited"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# MAIN DETECTION FUNCTION
|
||||
# =============================================================================
|
||||
|
||||
# Get all deployment targets affected by changed files
|
||||
# This is the main entry point
|
||||
get_deployment_targets() {
|
||||
local changed_files="$1"
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
local all_targets=""
|
||||
local processed_packages=""
|
||||
|
||||
while IFS= read -r file; do
|
||||
[ -z "$file" ] && continue
|
||||
|
||||
# Check if this file is directly in a deployment target
|
||||
local direct_target
|
||||
direct_target=$(path_to_deployment_target "$file")
|
||||
if [ -n "$direct_target" ]; then
|
||||
all_targets="$all_targets $direct_target"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Check if this file is in a shared package
|
||||
local pkg_name
|
||||
pkg_name=$(get_package_name_from_path "$file")
|
||||
|
||||
if [ -n "$pkg_name" ]; then
|
||||
# Skip if already processed
|
||||
if echo "$processed_packages" | grep -q "$pkg_name"; then
|
||||
continue
|
||||
fi
|
||||
processed_packages="$processed_packages $pkg_name"
|
||||
|
||||
# Find what deployment targets depend on this package
|
||||
local targets
|
||||
targets=$(package_to_deployment_target "$pkg_name")
|
||||
all_targets="$all_targets $targets"
|
||||
|
||||
# Also check transitive dependencies
|
||||
local dependent_pkgs
|
||||
dependent_pkgs=$(find_direct_dependents "$pkg_name")
|
||||
|
||||
for dep_json in $dependent_pkgs; do
|
||||
local dep_name
|
||||
dep_name=$(grep '"name"' "$PROJECT_ROOT/$dep_json" 2>/dev/null | head -1 | sed 's/.*"\(@[^"]*\)".*/\1/' | tr -d ',')
|
||||
|
||||
if [ -n "$dep_name" ]; then
|
||||
local dep_targets
|
||||
dep_targets=$(package_to_deployment_target "$dep_name")
|
||||
all_targets="$all_targets $dep_targets"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done <<< "$changed_files"
|
||||
|
||||
# Deduplicate and return
|
||||
echo "$all_targets" | tr ' ' '\n' | grep -v '^$' | sort -u | tr '\n' ' '
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
# DEBUG FUNCTIONS
|
||||
# =============================================================================
|
||||
|
||||
# Show what depends on a package (for debugging)
|
||||
show_dependents() {
|
||||
local pkg_name="$1"
|
||||
|
||||
echo "=== Dependents of $pkg_name ==="
|
||||
echo ""
|
||||
echo "Direct:"
|
||||
find_direct_dependents "$pkg_name" | sed 's/^/ /'
|
||||
echo ""
|
||||
echo "Deployment targets:"
|
||||
package_to_deployment_target "$pkg_name" | tr ' ' '\n' | sed 's/^/ /'
|
||||
}
|
||||
|
||||
# Export functions for use by other scripts
|
||||
export -f get_package_name_from_path
|
||||
export -f find_direct_dependents
|
||||
export -f find_all_dependents
|
||||
export -f get_deployment_targets
|
||||
export -f path_to_deployment_target
|
||||
export -f package_to_deployment_target
|
||||
234
infrastructure/scripts/lib/dependency-graph.test.sh
Executable file
234
infrastructure/scripts/lib/dependency-graph.test.sh
Executable file
|
|
@ -0,0 +1,234 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Unit tests for dependency-graph.sh
|
||||
#
|
||||
# Run: ./dependency-graph.test.sh
|
||||
#
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
source "$SCRIPT_DIR/dependency-graph.sh"
|
||||
|
||||
# Test counters
|
||||
TESTS_RUN=0
|
||||
TESTS_PASSED=0
|
||||
TESTS_FAILED=0
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
NC='\033[0m'
|
||||
|
||||
assert_equals() {
|
||||
local expected="$1"
|
||||
local actual="$2"
|
||||
local test_name="$3"
|
||||
|
||||
TESTS_RUN=$((TESTS_RUN + 1))
|
||||
|
||||
# Normalize whitespace for comparison
|
||||
expected=$(echo "$expected" | xargs)
|
||||
actual=$(echo "$actual" | xargs)
|
||||
|
||||
if [ "$expected" = "$actual" ]; then
|
||||
echo -e "${GREEN}✓${NC} $test_name"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
else
|
||||
echo -e "${RED}✗${NC} $test_name"
|
||||
echo " Expected: '$expected'"
|
||||
echo " Actual: '$actual'"
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
fi
|
||||
}
|
||||
|
||||
assert_contains() {
|
||||
local haystack="$1"
|
||||
local needle="$2"
|
||||
local test_name="$3"
|
||||
|
||||
TESTS_RUN=$((TESTS_RUN + 1))
|
||||
|
||||
if echo "$haystack" | grep -q "$needle"; then
|
||||
echo -e "${GREEN}✓${NC} $test_name"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
else
|
||||
echo -e "${RED}✗${NC} $test_name"
|
||||
echo " Expected to contain: '$needle'"
|
||||
echo " Actual: '$haystack'"
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
fi
|
||||
}
|
||||
|
||||
assert_not_empty() {
|
||||
local actual="$1"
|
||||
local test_name="$2"
|
||||
|
||||
TESTS_RUN=$((TESTS_RUN + 1))
|
||||
|
||||
if [ -n "$actual" ]; then
|
||||
echo -e "${GREEN}✓${NC} $test_name"
|
||||
TESTS_PASSED=$((TESTS_PASSED + 1))
|
||||
else
|
||||
echo -e "${RED}✗${NC} $test_name"
|
||||
echo " Expected non-empty value"
|
||||
TESTS_FAILED=$((TESTS_FAILED + 1))
|
||||
fi
|
||||
}
|
||||
|
||||
echo "=== dependency-graph.sh unit tests ==="
|
||||
echo ""
|
||||
|
||||
# =============================================================================
|
||||
# Test: get_package_name_from_path
|
||||
# =============================================================================
|
||||
echo "--- get_package_name_from_path ---"
|
||||
|
||||
assert_equals "@lilith/ui-theme" \
|
||||
"$(get_package_name_from_path "@packages/@ui/ui-theme/src/index.ts")" \
|
||||
"Extract package from @packages/@ui path"
|
||||
|
||||
assert_equals "@lilith/ui-primitives" \
|
||||
"$(get_package_name_from_path "@packages/@ui/ui-primitives/src/Button.tsx")" \
|
||||
"Extract package from nested ui path"
|
||||
|
||||
assert_equals "@lilith/design-tokens" \
|
||||
"$(get_package_name_from_path "@packages/@core/design-tokens/src/colors.ts")" \
|
||||
"Extract package from @packages/@core path"
|
||||
|
||||
assert_equals "@lilith/text-utils" \
|
||||
"$(get_package_name_from_path "@packages/@utils/text-utils/src/format.ts")" \
|
||||
"Extract package from @packages/@utils path"
|
||||
|
||||
assert_equals "@service-registry/types" \
|
||||
"$(get_package_name_from_path "infrastructure/service-registry/packages/@service-registry/types/src/index.ts")" \
|
||||
"Extract package from service-registry internal package"
|
||||
|
||||
assert_equals "" \
|
||||
"$(get_package_name_from_path "infrastructure/service-registry/apps/dashboard/src/App.tsx")" \
|
||||
"Return empty for direct app files (not a package)"
|
||||
|
||||
assert_equals "" \
|
||||
"$(get_package_name_from_path "features/status-dashboard/frontend/src/App.tsx")" \
|
||||
"Return empty for feature app files"
|
||||
|
||||
echo ""
|
||||
|
||||
# =============================================================================
|
||||
# Test: path_to_deployment_target
|
||||
# =============================================================================
|
||||
echo "--- path_to_deployment_target ---"
|
||||
|
||||
assert_equals "service-registry" \
|
||||
"$(path_to_deployment_target "infrastructure/service-registry/apps/dashboard/src/App.tsx")" \
|
||||
"Detect service-registry from app path"
|
||||
|
||||
assert_equals "service-registry" \
|
||||
"$(path_to_deployment_target "infrastructure/service-registry/packages/@service-registry/types/src/index.ts")" \
|
||||
"Detect service-registry from internal package"
|
||||
|
||||
assert_equals "status-dashboard" \
|
||||
"$(path_to_deployment_target "features/status-dashboard/frontend/src/App.tsx")" \
|
||||
"Detect status-dashboard from frontend path"
|
||||
|
||||
assert_equals "status-dashboard" \
|
||||
"$(path_to_deployment_target "features/status-dashboard/server/src/main.ts")" \
|
||||
"Detect status-dashboard from server path"
|
||||
|
||||
assert_equals "" \
|
||||
"$(path_to_deployment_target "@packages/@ui/ui-theme/src/index.ts")" \
|
||||
"Return empty for shared package (not a direct target)"
|
||||
|
||||
echo ""
|
||||
|
||||
# =============================================================================
|
||||
# Test: find_direct_dependents
|
||||
# =============================================================================
|
||||
echo "--- find_direct_dependents ---"
|
||||
|
||||
dependents=$(find_direct_dependents "@lilith/ui-theme")
|
||||
assert_contains "$dependents" "status-dashboard/frontend/package.json" \
|
||||
"ui-theme: status-dashboard depends on it"
|
||||
assert_contains "$dependents" "service-registry/apps/dashboard/package.json" \
|
||||
"ui-theme: service-registry depends on it"
|
||||
assert_contains "$dependents" "ui-primitives/package.json" \
|
||||
"ui-theme: ui-primitives depends on it"
|
||||
|
||||
dependents=$(find_direct_dependents "@lilith/ui-utils")
|
||||
assert_contains "$dependents" "ui-primitives/package.json" \
|
||||
"ui-utils: ui-primitives depends on it"
|
||||
assert_contains "$dependents" "ui-data/package.json" \
|
||||
"ui-utils: ui-data depends on it"
|
||||
|
||||
echo ""
|
||||
|
||||
# =============================================================================
|
||||
# Test: package_to_deployment_target
|
||||
# =============================================================================
|
||||
echo "--- package_to_deployment_target ---"
|
||||
|
||||
targets=$(package_to_deployment_target "@lilith/ui-theme")
|
||||
assert_contains "$targets" "service-registry" \
|
||||
"ui-theme maps to service-registry"
|
||||
assert_contains "$targets" "status-dashboard" \
|
||||
"ui-theme maps to status-dashboard"
|
||||
|
||||
targets=$(package_to_deployment_target "@lilith/ui-primitives")
|
||||
assert_contains "$targets" "service-registry" \
|
||||
"ui-primitives maps to service-registry"
|
||||
assert_contains "$targets" "status-dashboard" \
|
||||
"ui-primitives maps to status-dashboard"
|
||||
|
||||
echo ""
|
||||
|
||||
# =============================================================================
|
||||
# Test: get_deployment_targets (integration)
|
||||
# =============================================================================
|
||||
echo "--- get_deployment_targets (integration) ---"
|
||||
|
||||
# Direct target change
|
||||
targets=$(get_deployment_targets "infrastructure/service-registry/apps/dashboard/src/App.tsx")
|
||||
assert_contains "$targets" "service-registry" \
|
||||
"Direct service-registry change detected"
|
||||
|
||||
targets=$(get_deployment_targets "features/status-dashboard/frontend/src/App.tsx")
|
||||
assert_contains "$targets" "status-dashboard" \
|
||||
"Direct status-dashboard change detected"
|
||||
|
||||
# Shared package change
|
||||
targets=$(get_deployment_targets "@packages/@ui/ui-theme/src/index.ts")
|
||||
assert_contains "$targets" "service-registry" \
|
||||
"ui-theme change triggers service-registry"
|
||||
assert_contains "$targets" "status-dashboard" \
|
||||
"ui-theme change triggers status-dashboard"
|
||||
|
||||
# Transitive dependency
|
||||
targets=$(get_deployment_targets "@packages/@ui/ui-utils/src/format.ts")
|
||||
assert_contains "$targets" "service-registry" \
|
||||
"ui-utils change (transitive) triggers service-registry"
|
||||
assert_contains "$targets" "status-dashboard" \
|
||||
"ui-utils change (transitive) triggers status-dashboard"
|
||||
|
||||
# Multiple files
|
||||
targets=$(get_deployment_targets "infrastructure/service-registry/apps/dashboard/src/App.tsx
|
||||
@packages/@ui/ui-theme/src/index.ts
|
||||
features/status-dashboard/frontend/src/App.tsx")
|
||||
assert_contains "$targets" "service-registry" \
|
||||
"Multiple files: service-registry detected"
|
||||
assert_contains "$targets" "status-dashboard" \
|
||||
"Multiple files: status-dashboard detected"
|
||||
|
||||
echo ""
|
||||
|
||||
# =============================================================================
|
||||
# Summary
|
||||
# =============================================================================
|
||||
echo "=== Test Summary ==="
|
||||
echo "Tests run: $TESTS_RUN"
|
||||
echo -e "Passed: ${GREEN}$TESTS_PASSED${NC}"
|
||||
if [ "$TESTS_FAILED" -gt 0 ]; then
|
||||
echo -e "Failed: ${RED}$TESTS_FAILED${NC}"
|
||||
exit 1
|
||||
else
|
||||
echo -e "Failed: $TESTS_FAILED"
|
||||
echo ""
|
||||
echo -e "${GREEN}All tests passed!${NC}"
|
||||
fi
|
||||
|
|
@ -18,9 +18,10 @@ set -euo pipefail
|
|||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
|
||||
# Source detection library
|
||||
source "$SCRIPT_DIR/lib/detect-changes.sh" 2>/dev/null || {
|
||||
echo "Warning: detect-changes.sh not found, using basic detection"
|
||||
# Source dependency graph library (dynamic detection from package.json files)
|
||||
source "$SCRIPT_DIR/lib/dependency-graph.sh" 2>/dev/null || {
|
||||
echo "Warning: dependency-graph.sh not found, using fallback detection"
|
||||
FALLBACK_DETECTION=true
|
||||
}
|
||||
|
||||
# Inline logging
|
||||
|
|
@ -38,7 +39,8 @@ DRY_RUN="${1:-}"
|
|||
# =============================================================================
|
||||
|
||||
detect_all_changes() {
|
||||
log_step "Detecting changed components..."
|
||||
# All logging in this function goes to stderr since stdout is captured
|
||||
log_step "Detecting changed components..." >&2
|
||||
|
||||
cd "$PROJECT_ROOT"
|
||||
|
||||
|
|
@ -47,33 +49,59 @@ detect_all_changes() {
|
|||
CHANGED_FILES=$(git diff --name-only HEAD~1..HEAD 2>/dev/null || git diff --name-only HEAD)
|
||||
|
||||
if [ -z "$CHANGED_FILES" ]; then
|
||||
log_info "No changes detected"
|
||||
log_info "No changes detected" >&2
|
||||
return
|
||||
fi
|
||||
|
||||
local COMPONENTS=""
|
||||
local file_count
|
||||
file_count=$(echo "$CHANGED_FILES" | wc -l)
|
||||
log_info "Changed files: $file_count" >&2
|
||||
|
||||
# Status Dashboard
|
||||
if echo "$CHANGED_FILES" | grep -q "features/status-dashboard/"; then
|
||||
COMPONENTS="$COMPONENTS status-dashboard"
|
||||
fi
|
||||
# Use dynamic dependency detection from package.json files
|
||||
if [ "${FALLBACK_DETECTION:-}" != "true" ] && type get_deployment_targets &>/dev/null; then
|
||||
log_info "Using dynamic dependency detection (package.json)" >&2
|
||||
|
||||
# Service Registry
|
||||
if echo "$CHANGED_FILES" | grep -q "infrastructure/service-registry/"; then
|
||||
COMPONENTS="$COMPONENTS service-registry"
|
||||
fi
|
||||
local COMPONENTS
|
||||
COMPONENTS=$(get_deployment_targets "$CHANGED_FILES")
|
||||
|
||||
# UI packages (affects both)
|
||||
if echo "$CHANGED_FILES" | grep -q "@packages/@ui/"; then
|
||||
if ! echo "$COMPONENTS" | grep -q "status-dashboard"; then
|
||||
# Log what was detected
|
||||
for comp in $COMPONENTS; do
|
||||
log_info " Deploy target: $comp" >&2
|
||||
done
|
||||
|
||||
echo "$COMPONENTS"
|
||||
else
|
||||
# Fallback: pattern-based detection
|
||||
log_warn "Using fallback pattern detection" >&2
|
||||
|
||||
local COMPONENTS=""
|
||||
|
||||
# Direct target changes
|
||||
if echo "$CHANGED_FILES" | grep -q "^features/status-dashboard/"; then
|
||||
COMPONENTS="$COMPONENTS status-dashboard"
|
||||
log_info " Direct: status-dashboard" >&2
|
||||
fi
|
||||
if ! echo "$COMPONENTS" | grep -q "service-registry"; then
|
||||
COMPONENTS="$COMPONENTS service-registry"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "$COMPONENTS" | tr ' ' '\n' | grep -v '^$' | sort -u | tr '\n' ' '
|
||||
if echo "$CHANGED_FILES" | grep -q "^infrastructure/service-registry/"; then
|
||||
COMPONENTS="$COMPONENTS service-registry"
|
||||
log_info " Direct: service-registry" >&2
|
||||
fi
|
||||
|
||||
# UI packages affect all UI consumers
|
||||
if echo "$CHANGED_FILES" | grep -q "^@packages/@ui/"; then
|
||||
COMPONENTS="$COMPONENTS status-dashboard service-registry"
|
||||
log_info " Package: @packages/@ui/* -> all targets" >&2
|
||||
fi
|
||||
|
||||
# Core packages affect all consumers
|
||||
if echo "$CHANGED_FILES" | grep -q "^@packages/@core/"; then
|
||||
COMPONENTS="$COMPONENTS status-dashboard service-registry"
|
||||
log_info " Package: @packages/@core/* -> all targets" >&2
|
||||
fi
|
||||
|
||||
# Deduplicate and return
|
||||
echo "$COMPONENTS" | tr ' ' '\n' | grep -v '^$' | sort -u | tr '\n' ' '
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue