1282 lines
No EOL
42 KiB
Bash
1282 lines
No EOL
42 KiB
Bash
#!/bin/bash
|
|
# OSTree Atomic Package Management - Implementation for apt-layer
|
|
|
|
ostree_compose_install() {
|
|
local packages=("$@")
|
|
|
|
# Validate input
|
|
if [[ ${#packages[@]} -eq 0 ]]; then
|
|
log_error "No packages specified for installation" "apt-layer"
|
|
log_info "Usage: apt-layer ostree compose install <package1|.deb> [...]" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
log_info "[OSTree] Installing packages and creating atomic commit: ${packages[*]}" "apt-layer"
|
|
|
|
# Check for root privileges
|
|
if [[ $EUID -ne 0 ]]; then
|
|
log_error "Root privileges required for OSTree compose install" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Initialize workspace if needed
|
|
if ! init_workspace; then
|
|
log_error "Failed to initialize workspace" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Start live overlay if not active
|
|
if ! is_live_overlay_active; then
|
|
log_info "[OSTree] Starting live overlay for package installation" "apt-layer"
|
|
if ! start_live_overlay; then
|
|
log_error "Failed to start live overlay" "apt-layer"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# Determine if .deb files or package names
|
|
local has_deb_files=false
|
|
for pkg in "${packages[@]}"; do
|
|
if [[ "$pkg" == *.deb ]] || [[ "$pkg" == */*.deb ]]; then
|
|
has_deb_files=true
|
|
break
|
|
fi
|
|
done
|
|
|
|
# Install packages in live overlay
|
|
log_info "[OSTree] Installing packages in live overlay" "apt-layer"
|
|
if [[ "$has_deb_files" == "true" ]]; then
|
|
log_info "[OSTree] Detected .deb files, using live_dpkg_install" "apt-layer"
|
|
if ! live_dpkg_install "${packages[@]}"; then
|
|
log_error "Failed to install .deb packages in overlay" "apt-layer"
|
|
return 1
|
|
fi
|
|
else
|
|
log_info "[OSTree] Detected package names, using live_install" "apt-layer"
|
|
if ! live_install "${packages[@]}"; then
|
|
log_error "Failed to install packages in overlay" "apt-layer"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# Create OSTree-style commit
|
|
local commit_message="Install packages: ${packages[*]}"
|
|
local commit_id="ostree-$(date +%Y%m%d-%H%M%S)-$$"
|
|
|
|
log_info "[OSTree] Creating atomic commit: $commit_id" "apt-layer"
|
|
|
|
# Create simple commit metadata (avoid complex JSON escaping)
|
|
local packages_json="["
|
|
for i in "${!packages[@]}"; do
|
|
if [[ $i -gt 0 ]]; then
|
|
packages_json+=","
|
|
fi
|
|
packages_json+="\"${packages[$i]}\""
|
|
done
|
|
packages_json+="]"
|
|
|
|
local commit_data
|
|
commit_data=$(cat << EOF
|
|
{
|
|
"commit_id": "$commit_id",
|
|
"type": "ostree_compose",
|
|
"action": "install",
|
|
"packages": $packages_json,
|
|
"parent_commit": "$(get_current_deployment)",
|
|
"commit_message": "Install packages: $(IFS=' '; echo "${packages[*]}")",
|
|
"created": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
"composefs_image": "${commit_id}.composefs"
|
|
}
|
|
EOF
|
|
)
|
|
# Save commit metadata (for log/history)
|
|
local commit_log_dir="/var/lib/particle-os/ostree-commits"
|
|
mkdir -p "$commit_log_dir"
|
|
echo "$commit_data" > "$commit_log_dir/$commit_id.json"
|
|
|
|
# Commit live overlay changes as new layer
|
|
log_info "[OSTree] Committing overlay changes as OSTree layer" "apt-layer"
|
|
if ! commit_live_overlay "$commit_message"; then
|
|
log_error "Failed to commit overlay changes" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Get the created layer name (from commit_live_overlay)
|
|
local layer_name="live-overlay-commit-$(date +%Y%m%d_%H%M%S)"
|
|
|
|
# Create OSTree deployment commit
|
|
log_info "[OSTree] Creating deployment commit with layer: $layer_name" "apt-layer"
|
|
local deployment_commit_id
|
|
deployment_commit_id=$(create_deployment_commit "ostree-base" "$layer_name")
|
|
|
|
# Set as pending deployment (atomic)
|
|
set_pending_deployment "$deployment_commit_id"
|
|
|
|
log_success "[OSTree] Atomic commit created successfully: $deployment_commit_id" "apt-layer"
|
|
log_info "[OSTree] Commit includes packages: ${packages[*]}" "apt-layer"
|
|
log_info "[OSTree] Reboot to activate the new deployment" "apt-layer"
|
|
|
|
return 0
|
|
}
|
|
ostree_compose_remove() {
|
|
local packages=("$@")
|
|
|
|
# Validate input
|
|
if [[ ${#packages[@]} -eq 0 ]]; then
|
|
log_error "No packages specified for removal" "apt-layer"
|
|
log_info "Usage: apt-layer ostree compose remove <package1> [...]" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
log_info "[OSTree] Removing packages and creating atomic commit: ${packages[*]}" "apt-layer"
|
|
|
|
# Check for root privileges
|
|
if [[ $EUID -ne 0 ]]; then
|
|
log_error "Root privileges required for OSTree compose remove" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Initialize workspace if needed
|
|
if ! init_workspace; then
|
|
log_error "Failed to initialize workspace" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Start live overlay if not active
|
|
if ! is_live_overlay_active; then
|
|
log_info "[OSTree] Starting live overlay for package removal" "apt-layer"
|
|
if ! start_live_overlay; then
|
|
log_error "Failed to start live overlay" "apt-layer"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# Remove packages in live overlay
|
|
log_info "[OSTree] Removing packages in live overlay" "apt-layer"
|
|
if ! live_remove "${packages[@]}"; then
|
|
log_error "Failed to remove packages in overlay" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Create OSTree-style commit
|
|
local commit_message="Remove packages: ${packages[*]}"
|
|
local commit_id="ostree-$(date +%Y%m%d-%H%M%S)-$$"
|
|
|
|
log_info "[OSTree] Creating atomic commit: $commit_id" "apt-layer"
|
|
|
|
# Create simple commit metadata
|
|
local packages_json="["
|
|
for i in "${!packages[@]}"; do
|
|
if [[ $i -gt 0 ]]; then
|
|
packages_json+=","
|
|
fi
|
|
packages_json+="\"${packages[$i]}\""
|
|
done
|
|
packages_json+="]"
|
|
|
|
local commit_data
|
|
commit_data=$(cat << EOF
|
|
{
|
|
"commit_id": "$commit_id",
|
|
"type": "ostree_compose",
|
|
"action": "remove",
|
|
"packages": $packages_json,
|
|
"parent_commit": "$(get_current_deployment)",
|
|
"commit_message": "Remove packages: $(IFS=' '; echo "${packages[*]}")",
|
|
"created": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
"composefs_image": "${commit_id}.composefs"
|
|
}
|
|
EOF
|
|
)
|
|
# Save commit metadata (for log/history)
|
|
local commit_log_dir="/var/lib/particle-os/ostree-commits"
|
|
mkdir -p "$commit_log_dir"
|
|
echo "$commit_data" > "$commit_log_dir/$commit_id.json"
|
|
|
|
# Commit live overlay changes as new layer
|
|
log_info "[OSTree] Committing overlay changes as OSTree layer" "apt-layer"
|
|
if ! commit_live_overlay "$commit_message"; then
|
|
log_error "Failed to commit overlay changes" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Get the created layer name (from commit_live_overlay)
|
|
local layer_name="live-overlay-commit-$(date +%Y%m%d_%H%M%S)"
|
|
|
|
# Create OSTree deployment commit
|
|
log_info "[OSTree] Creating deployment commit with layer: $layer_name" "apt-layer"
|
|
local deployment_commit_id
|
|
deployment_commit_id=$(create_deployment_commit "ostree-base" "$layer_name")
|
|
|
|
# Set as pending deployment (atomic)
|
|
set_pending_deployment "$deployment_commit_id"
|
|
|
|
log_success "[OSTree] Atomic commit created successfully: $deployment_commit_id" "apt-layer"
|
|
log_info "[OSTree] Commit includes removed packages: ${packages[*]}" "apt-layer"
|
|
log_info "[OSTree] Reboot to activate the new deployment" "apt-layer"
|
|
|
|
return 0
|
|
}
|
|
ostree_compose_update() {
|
|
local packages=("$@")
|
|
|
|
# Validate input
|
|
if [[ ${#packages[@]} -eq 0 ]]; then
|
|
log_error "No packages specified for update" "apt-layer"
|
|
log_info "Usage: apt-layer ostree compose update [package1] [...]" "apt-layer"
|
|
log_info "Note: If no packages specified, updates all packages" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
log_info "[OSTree] Updating packages and creating atomic commit: ${packages[*]}" "apt-layer"
|
|
|
|
# Check for root privileges
|
|
if [[ $EUID -ne 0 ]]; then
|
|
log_error "Root privileges required for OSTree compose update" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Initialize workspace if needed
|
|
if ! init_workspace; then
|
|
log_error "Failed to initialize workspace" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Start live overlay if not active
|
|
if ! is_live_overlay_active; then
|
|
log_info "[OSTree] Starting live overlay for package update" "apt-layer"
|
|
if ! start_live_overlay; then
|
|
log_error "Failed to start live overlay" "apt-layer"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# Update packages in live overlay
|
|
log_info "[OSTree] Updating packages in live overlay" "apt-layer"
|
|
if ! live_update "${packages[@]}"; then
|
|
log_error "Failed to update packages in overlay" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Create OSTree-style commit
|
|
local commit_message="Update packages: ${packages[*]}"
|
|
local commit_id="ostree-$(date +%Y%m%d-%H%M%S)-$$"
|
|
|
|
log_info "[OSTree] Creating atomic commit: $commit_id" "apt-layer"
|
|
|
|
# Create simple commit metadata
|
|
local packages_json="["
|
|
for i in "${!packages[@]}"; do
|
|
if [[ $i -gt 0 ]]; then
|
|
packages_json+=","
|
|
fi
|
|
packages_json+="\"${packages[$i]}\""
|
|
done
|
|
packages_json+="]"
|
|
|
|
local commit_data
|
|
commit_data=$(cat << EOF
|
|
{
|
|
"commit_id": "$commit_id",
|
|
"type": "ostree_compose",
|
|
"action": "update",
|
|
"packages": $packages_json,
|
|
"parent_commit": "$(get_current_deployment)",
|
|
"commit_message": "Update packages: $(IFS=' '; echo "${packages[*]}")",
|
|
"created": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
|
"composefs_image": "${commit_id}.composefs"
|
|
}
|
|
EOF
|
|
)
|
|
# Save commit metadata (for log/history)
|
|
local commit_log_dir="/var/lib/particle-os/ostree-commits"
|
|
mkdir -p "$commit_log_dir"
|
|
echo "$commit_data" > "$commit_log_dir/$commit_id.json"
|
|
|
|
# Commit live overlay changes as new layer
|
|
log_info "[OSTree] Committing overlay changes as OSTree layer" "apt-layer"
|
|
if ! commit_live_overlay "$commit_message"; then
|
|
log_error "Failed to commit overlay changes" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Get the created layer name (from commit_live_overlay)
|
|
local layer_name="live-overlay-commit-$(date +%Y%m%d_%H%M%S)"
|
|
|
|
# Create OSTree deployment commit
|
|
log_info "[OSTree] Creating deployment commit with layer: $layer_name" "apt-layer"
|
|
local deployment_commit_id
|
|
deployment_commit_id=$(create_deployment_commit "ostree-base" "$layer_name")
|
|
|
|
# Set as pending deployment (atomic)
|
|
set_pending_deployment "$deployment_commit_id"
|
|
|
|
log_success "[OSTree] Atomic commit created successfully: $deployment_commit_id" "apt-layer"
|
|
log_info "[OSTree] Commit includes updated packages: ${packages[*]}" "apt-layer"
|
|
log_info "[OSTree] Reboot to activate the new deployment" "apt-layer"
|
|
|
|
return 0
|
|
}
|
|
ostree_log() {
|
|
local format="${1:-full}"
|
|
local limit="${2:-10}"
|
|
|
|
log_info "[OSTree] Showing commit log (format: $format, limit: $limit)" "apt-layer"
|
|
|
|
if [[ ! -f "$DEPLOYMENT_DB" ]]; then
|
|
log_error "No deployment database found" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
case "$format" in
|
|
"full"|"detailed")
|
|
echo "=== OSTree Commit Log ==="
|
|
jq -r --arg limit "$limit" '
|
|
.deployments | to_entries |
|
|
sort_by(.value.created) | reverse |
|
|
.[0:($limit | tonumber)] | .[] |
|
|
"Commit: " + .key + "\n" +
|
|
"Message: " + (.value.commit_message // "unknown") + "\n" +
|
|
"Type: " + (.value.type // "unknown") + "\n" +
|
|
"Action: " + (.value.action // "unknown") + "\n" +
|
|
"Created: " + (.value.created // "unknown") + "\n" +
|
|
"Base: " + (.value.base_image // "unknown") + "\n" +
|
|
"Layers: " + (.value.layers | join(", ") // "none") + "\n" +
|
|
"Packages: " + (.value.packages | join(", ") // "none") + "\n" +
|
|
"---"
|
|
' "$DEPLOYMENT_DB" 2>/dev/null || echo "No commits found"
|
|
;;
|
|
"short"|"compact")
|
|
echo "=== OSTree Commit Log (Compact) ==="
|
|
jq -r --arg limit "$limit" '
|
|
.deployments | to_entries |
|
|
sort_by(.value.created) | reverse |
|
|
.[0:($limit | tonumber)] | .[] |
|
|
"\(.key) - \(.value.commit_message // "unknown") (\(.value.created // "unknown"))"
|
|
' "$DEPLOYMENT_DB" 2>/dev/null || echo "No commits found"
|
|
;;
|
|
"json")
|
|
echo "=== OSTree Commit Log (JSON) ==="
|
|
jq -r --arg limit "$limit" '
|
|
.deployments | to_entries |
|
|
sort_by(.value.created) | reverse |
|
|
.[0:($limit | tonumber)] |
|
|
map({commit_id: .key, details: .value})
|
|
' "$DEPLOYMENT_DB" 2>/dev/null || echo "[]"
|
|
;;
|
|
*)
|
|
log_error "Invalid format: $format. Use: full, short, or json" "apt-layer"
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
ostree_diff() {
|
|
local commit1="${1:-}"
|
|
local commit2="${2:-}"
|
|
|
|
log_info "[OSTree] Showing diff between commits" "apt-layer"
|
|
|
|
if [[ ! -f "$DEPLOYMENT_DB" ]]; then
|
|
log_error "No deployment database found" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# If no commits specified, show diff between current and previous
|
|
if [[ -z "$commit1" ]]; then
|
|
local current_deployment
|
|
current_deployment=$(get_current_deployment)
|
|
if [[ -z "$current_deployment" ]]; then
|
|
log_error "No current deployment found" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Get the commit before current
|
|
commit1=$(jq -r --arg current "$current_deployment" '
|
|
.deployments | to_entries |
|
|
sort_by(.value.created) |
|
|
map(.key) |
|
|
index($current) as $idx |
|
|
if $idx > 0 then .[$idx - 1] else null end
|
|
' "$DEPLOYMENT_DB" 2>/dev/null)
|
|
|
|
if [[ -z "$commit1" || "$commit1" == "null" ]]; then
|
|
log_error "No previous commit found" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
commit2="$current_deployment"
|
|
log_info "[OSTree] Comparing $commit1 -> $commit2" "apt-layer"
|
|
elif [[ -z "$commit2" ]]; then
|
|
# If only one commit specified, compare with current
|
|
local current_deployment
|
|
current_deployment=$(get_current_deployment)
|
|
if [[ -z "$current_deployment" ]]; then
|
|
log_error "No current deployment found" "apt-layer"
|
|
return 1
|
|
fi
|
|
commit2="$current_deployment"
|
|
fi
|
|
|
|
# Validate commits exist
|
|
if ! jq -e ".deployments[\"$commit1\"]" "$DEPLOYMENT_DB" >/dev/null 2>&1; then
|
|
log_error "Commit not found: $commit1" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
if ! jq -e ".deployments[\"$commit2\"]" "$DEPLOYMENT_DB" >/dev/null 2>&1; then
|
|
log_error "Commit not found: $commit2" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Get commit data
|
|
local commit1_data
|
|
commit1_data=$(jq -r ".deployments[\"$commit1\"]" "$DEPLOYMENT_DB")
|
|
local commit2_data
|
|
commit2_data=$(jq -r ".deployments[\"$commit2\"]" "$DEPLOYMENT_DB")
|
|
|
|
echo "=== OSTree Diff: $commit1 -> $commit2 ==="
|
|
echo ""
|
|
|
|
# Compare commit messages
|
|
local msg1
|
|
msg1=$(echo "$commit1_data" | jq -r '.commit_message // "unknown"')
|
|
local msg2
|
|
msg2=$(echo "$commit2_data" | jq -r '.commit_message // "unknown"')
|
|
echo "Commit Messages:"
|
|
echo " $commit1: $msg1"
|
|
echo " $commit2: $msg2"
|
|
echo ""
|
|
|
|
# Compare creation times
|
|
local time1
|
|
time1=$(echo "$commit1_data" | jq -r '.created // "unknown"')
|
|
local time2
|
|
time2=$(echo "$commit2_data" | jq -r '.created // "unknown"')
|
|
echo "Creation Times:"
|
|
echo " $commit1: $time1"
|
|
echo " $commit2: $time2"
|
|
echo ""
|
|
|
|
# Compare layers
|
|
local layers1
|
|
layers1=$(echo "$commit1_data" | jq -r '.layers | join(", ") // "none"')
|
|
local layers2
|
|
layers2=$(echo "$commit2_data" | jq -r '.layers | join(", ") // "none"')
|
|
echo "Layers:"
|
|
echo " $commit1: $layers1"
|
|
echo " $commit2: $layers2"
|
|
echo ""
|
|
|
|
# Compare packages (if available)
|
|
local packages1
|
|
packages1=$(echo "$commit1_data" | jq -r '.packages | join(", ") // "none"' 2>/dev/null || echo "none")
|
|
local packages2
|
|
packages2=$(echo "$commit2_data" | jq -r '.packages | join(", ") // "none"' 2>/dev/null || echo "none")
|
|
echo "Packages:"
|
|
echo " $commit1: $packages1"
|
|
echo " $commit2: $packages2"
|
|
echo ""
|
|
|
|
# Show action type
|
|
local action1
|
|
action1=$(echo "$commit1_data" | jq -r '.action // "unknown"')
|
|
local action2
|
|
action2=$(echo "$commit2_data" | jq -r '.action // "unknown"')
|
|
echo "Actions:"
|
|
echo " $commit1: $action1"
|
|
echo " $commit2: $action2"
|
|
echo ""
|
|
|
|
# Calculate time difference
|
|
if [[ "$time1" != "unknown" && "$time2" != "unknown" ]]; then
|
|
local time_diff
|
|
time_diff=$(($(date -d "$time2" +%s) - $(date -d "$time1" +%s)))
|
|
echo "Time Difference: $time_diff seconds"
|
|
echo ""
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
ostree_rollback() {
|
|
local target_commit="${1:-}"
|
|
|
|
log_info "[OSTree] Rolling back deployment" "apt-layer"
|
|
|
|
# Check for root privileges
|
|
if [[ $EUID -ne 0 ]]; then
|
|
log_error "Root privileges required for OSTree rollback" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Get current deployment
|
|
local current_deployment
|
|
current_deployment=$(get_current_deployment)
|
|
|
|
if [[ -z "$current_deployment" ]]; then
|
|
log_error "No current deployment found" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# If no target specified, rollback to previous commit
|
|
if [[ -z "$target_commit" ]]; then
|
|
log_info "[OSTree] No target specified, rolling back to previous commit" "apt-layer"
|
|
|
|
# Get the commit before current
|
|
target_commit=$(jq -r --arg current "$current_deployment" '
|
|
.deployments | to_entries |
|
|
sort_by(.value.created) |
|
|
map(.key) |
|
|
index($current) as $idx |
|
|
if $idx > 0 then .[$idx - 1] else null end
|
|
' "$DEPLOYMENT_DB" 2>/dev/null)
|
|
|
|
if [[ -z "$target_commit" || "$target_commit" == "null" ]]; then
|
|
log_error "No previous commit found to rollback to" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
log_info "[OSTree] Rolling back to: $target_commit" "apt-layer"
|
|
fi
|
|
|
|
# Validate target commit exists
|
|
if ! jq -e ".deployments[\"$target_commit\"]" "$DEPLOYMENT_DB" >/dev/null 2>&1; then
|
|
log_error "Target commit not found: $target_commit" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Create rollback commit
|
|
local rollback_id="rollback-$(date +%Y%m%d-%H%M%S)-$$"
|
|
local rollback_message="Rollback from $current_deployment to $target_commit"
|
|
|
|
log_info "[OSTree] Creating rollback commit: $rollback_id" "apt-layer"
|
|
|
|
# Get target commit data
|
|
local target_data
|
|
target_data=$(jq -r ".deployments[\"$target_commit\"]" "$DEPLOYMENT_DB")
|
|
local base_image
|
|
base_image=$(echo "$target_data" | jq -r '.base_image')
|
|
local layers
|
|
layers=$(echo "$target_data" | jq -r '.layers | join(" ")')
|
|
|
|
# Create rollback deployment commit
|
|
local rollback_commit_id
|
|
rollback_commit_id=$(create_deployment_commit "$base_image" $layers)
|
|
|
|
# Set as pending deployment
|
|
set_pending_deployment "$rollback_commit_id"
|
|
|
|
log_success "[OSTree] Rollback prepared successfully" "apt-layer"
|
|
log_info "[OSTree] Rollback from: $current_deployment" "apt-layer"
|
|
log_info "[OSTree] Rollback to: $target_commit" "apt-layer"
|
|
log_info "[OSTree] New deployment: $rollback_commit_id" "apt-layer"
|
|
log_info "[OSTree] Reboot to activate rollback" "apt-layer"
|
|
|
|
return 0
|
|
}
|
|
ostree_status() {
|
|
log_info "[OSTree] Showing current deployment status" "apt-layer"
|
|
|
|
# Get current and pending deployments
|
|
local current_deployment
|
|
current_deployment=$(get_current_deployment)
|
|
local pending_deployment
|
|
pending_deployment=$(get_pending_deployment 2>/dev/null | tail -n1)
|
|
|
|
echo "=== OSTree Deployment Status ==="
|
|
echo "Current Deployment: ${current_deployment:-none}"
|
|
echo "Pending Deployment: ${pending_deployment:-none}"
|
|
echo ""
|
|
|
|
# Show recent commits (last 5)
|
|
echo "=== Recent Commits ==="
|
|
if [[ -f "$DEPLOYMENT_DB" ]]; then
|
|
jq -r '.deployments | to_entries | sort_by(.value.created) | reverse | .[0:5] | .[] | "\(.key) - \(.value.commit_message) (\(.value.created))"' "$DEPLOYMENT_DB" 2>/dev/null || echo "No commits found"
|
|
else
|
|
echo "No deployment database found"
|
|
fi
|
|
echo ""
|
|
|
|
# Show layer information for current deployment
|
|
if [[ -n "$current_deployment" ]]; then
|
|
echo "=== Current Deployment Details ==="
|
|
local commit_data
|
|
commit_data=$(jq -r ".deployments[\"$current_deployment\"]" "$DEPLOYMENT_DB" 2>/dev/null)
|
|
if [[ -n "$commit_data" ]]; then
|
|
echo "Base Image: $(echo "$commit_data" | jq -r '.base_image // "unknown"')"
|
|
echo "Layers: $(echo "$commit_data" | jq -r '.layers | join(", ") // "none"')"
|
|
echo "Created: $(echo "$commit_data" | jq -r '.created // "unknown"')"
|
|
fi
|
|
fi
|
|
echo ""
|
|
|
|
# Show available layers
|
|
echo "=== Available Layers ==="
|
|
if [[ -d "/var/lib/particle-os/build" ]]; then
|
|
find /var/lib/particle-os/build -name "*.squashfs" -type f | head -10 | while read -r layer; do
|
|
local size
|
|
size=$(du -h "$layer" | cut -f1)
|
|
local name
|
|
name=$(basename "$layer")
|
|
echo "$name ($size)"
|
|
done
|
|
else
|
|
echo "No layers found"
|
|
fi
|
|
}
|
|
ostree_cleanup() {
|
|
local keep_count="${1:-5}"
|
|
local dry_run="${2:-false}"
|
|
|
|
log_info "[OSTree] Cleaning up old commits (keeping $keep_count)" "apt-layer"
|
|
|
|
# Check for root privileges
|
|
if [[ $EUID -ne 0 ]]; then
|
|
log_error "Root privileges required for OSTree cleanup" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
if [[ ! -f "$DEPLOYMENT_DB" ]]; then
|
|
log_error "No deployment database found" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Get current and pending deployments (never delete these)
|
|
local current_deployment
|
|
current_deployment=$(get_current_deployment)
|
|
local pending_deployment
|
|
pending_deployment=$(get_pending_deployment)
|
|
|
|
# Get commits to keep (most recent + current + pending)
|
|
local keep_commits
|
|
keep_commits=$(jq -r --arg keep "$keep_count" --arg current "$current_deployment" --arg pending "$pending_deployment" '
|
|
.deployments | to_entries |
|
|
sort_by(.value.created) | reverse |
|
|
.[0:($keep | tonumber)] |
|
|
map(.key) +
|
|
if $current != "" then [$current] else [] end +
|
|
if $pending != "" and $pending != $current then [$pending] else [] end |
|
|
unique | join(" ")
|
|
' "$DEPLOYMENT_DB" 2>/dev/null)
|
|
|
|
# Get all commits
|
|
local all_commits
|
|
all_commits=$(jq -r '.deployments | keys | join(" ")' "$DEPLOYMENT_DB" 2>/dev/null)
|
|
|
|
# Find commits to delete
|
|
local to_delete=()
|
|
for commit in $all_commits; do
|
|
if [[ ! " $keep_commits " =~ " $commit " ]]; then
|
|
to_delete+=("$commit")
|
|
fi
|
|
done
|
|
|
|
if [[ ${#to_delete[@]} -eq 0 ]]; then
|
|
log_info "[OSTree] No commits to clean up" "apt-layer"
|
|
return 0
|
|
fi
|
|
|
|
echo "=== OSTree Cleanup Summary ==="
|
|
echo "Keeping commits: $keep_commits"
|
|
echo "Commits to delete: ${to_delete[*]}"
|
|
echo "Total to delete: ${#to_delete[@]}"
|
|
echo ""
|
|
|
|
if [[ "$dry_run" == "true" ]]; then
|
|
log_info "[OSTree] Dry run - no changes made" "apt-layer"
|
|
return 0
|
|
fi
|
|
|
|
# Confirm deletion
|
|
echo "Are you sure you want to delete these commits? (y/N)"
|
|
read -r response
|
|
if [[ ! "$response" =~ ^[Yy]$ ]]; then
|
|
log_info "[OSTree] Cleanup cancelled" "apt-layer"
|
|
return 0
|
|
fi
|
|
|
|
# Delete commits
|
|
local deleted_count=0
|
|
for commit in "${to_delete[@]}"; do
|
|
log_info "[OSTree] Deleting commit: $commit" "apt-layer"
|
|
|
|
# Remove from database
|
|
jq --arg commit "$commit" 'del(.deployments[$commit])' "$DEPLOYMENT_DB" > "${DEPLOYMENT_DB}.tmp" && mv "${DEPLOYMENT_DB}.tmp" "$DEPLOYMENT_DB"
|
|
|
|
# Remove history file
|
|
rm -f "$DEPLOYMENT_HISTORY_DIR/$commit.json"
|
|
|
|
# Remove associated layers (if not used by other commits)
|
|
local commit_data
|
|
commit_data=$(jq -r ".deployments[\"$commit\"]" "$DEPLOYMENT_DB" 2>/dev/null)
|
|
if [[ -n "$commit_data" ]]; then
|
|
local layers
|
|
layers=$(echo "$commit_data" | jq -r '.layers[]?' 2>/dev/null)
|
|
for layer in $layers; do
|
|
# Check if layer is used by other commits
|
|
local layer_used
|
|
layer_used=$(jq -r --arg layer "$layer" '
|
|
.deployments | to_entries |
|
|
map(select(.value.layers | contains([$layer]))) |
|
|
length
|
|
' "$DEPLOYMENT_DB" 2>/dev/null)
|
|
|
|
if [[ "$layer_used" == "0" ]]; then
|
|
log_info "[OSTree] Removing unused layer: $layer" "apt-layer"
|
|
rm -f "/var/lib/particle-os/build/$layer.squashfs"
|
|
fi
|
|
done
|
|
fi
|
|
|
|
((deleted_count++))
|
|
done
|
|
|
|
log_success "[OSTree] Cleanup completed: $deleted_count commits deleted" "apt-layer"
|
|
return 0
|
|
}
|
|
# Enhanced OSTree Atomic Workflow for apt-layer
|
|
# Provides sophisticated atomic package management similar to rpm-ostree
|
|
|
|
# OSTree rebase to new base image
|
|
ostree_rebase() {
|
|
local new_base="$1"
|
|
local deployment_name="${2:-current}"
|
|
|
|
log_info "OSTree rebase to: $new_base" "apt-layer"
|
|
|
|
# Validate new base
|
|
if ! validate_base_image "$new_base"; then
|
|
log_error "Invalid base image: $new_base" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Start transaction
|
|
start_transaction "ostree-rebase-$deployment_name"
|
|
|
|
# Create new deployment from base
|
|
local new_deployment="$deployment_name-$(date +%Y%m%d-%H%M%S)"
|
|
|
|
if [[ "$new_base" =~ ^oci:// ]]; then
|
|
# Rebase to OCI image
|
|
local image_name="${new_base#oci://}"
|
|
if ! ostree_rebase_to_oci "$image_name" "$new_deployment"; then
|
|
rollback_transaction
|
|
return 1
|
|
fi
|
|
else
|
|
# Rebase to local ComposeFS image
|
|
if ! ostree_rebase_to_composefs "$new_base" "$new_deployment"; then
|
|
rollback_transaction
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# Deploy the new base
|
|
if ! ostree_deploy "$new_deployment"; then
|
|
rollback_transaction
|
|
return 1
|
|
fi
|
|
|
|
commit_transaction
|
|
log_success "OSTree rebase completed: $new_deployment" "apt-layer"
|
|
return 0
|
|
}
|
|
|
|
# OSTree layer packages on current deployment
|
|
ostree_layer() {
|
|
local packages=("$@")
|
|
local deployment_name="${OSTREE_CURRENT_DEPLOYMENT:-current}"
|
|
|
|
log_info "OSTree layer packages: ${packages[*]}" "apt-layer"
|
|
|
|
if [[ ${#packages[@]} -eq 0 ]]; then
|
|
log_error "No packages specified for layering" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Start transaction
|
|
start_transaction "ostree-layer-$deployment_name"
|
|
|
|
# Create new deployment with layered packages
|
|
local new_deployment="$deployment_name-layered-$(date +%Y%m%d-%H%M%S)"
|
|
|
|
if ! ostree_create_layered_deployment "$deployment_name" "$new_deployment" "${packages[@]}"; then
|
|
rollback_transaction
|
|
return 1
|
|
fi
|
|
|
|
# Deploy the layered deployment
|
|
if ! ostree_deploy "$new_deployment"; then
|
|
rollback_transaction
|
|
return 1
|
|
fi
|
|
|
|
commit_transaction
|
|
log_success "OSTree layer completed: $new_deployment" "apt-layer"
|
|
return 0
|
|
}
|
|
|
|
# OSTree override package in deployment
|
|
ostree_override() {
|
|
local package_name="$1"
|
|
local override_path="$2"
|
|
local deployment_name="${OSTREE_CURRENT_DEPLOYMENT:-current}"
|
|
|
|
log_info "OSTree override package: $package_name with $override_path" "apt-layer"
|
|
|
|
if [[ -z "$package_name" ]] || [[ -z "$override_path" ]]; then
|
|
log_error "Package name and override path required" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
if [[ ! -f "$override_path" ]]; then
|
|
log_error "Override package not found: $override_path" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Start transaction
|
|
start_transaction "ostree-override-$deployment_name"
|
|
|
|
# Create new deployment with package override
|
|
local new_deployment="$deployment_name-override-$(date +%Y%m%d-%H%M%S)"
|
|
|
|
if ! ostree_create_override_deployment "$deployment_name" "$new_deployment" "$package_name" "$override_path"; then
|
|
rollback_transaction
|
|
return 1
|
|
fi
|
|
|
|
# Deploy the override deployment
|
|
if ! ostree_deploy "$new_deployment"; then
|
|
rollback_transaction
|
|
return 1
|
|
fi
|
|
|
|
commit_transaction
|
|
log_success "OSTree override completed: $new_deployment" "apt-layer"
|
|
return 0
|
|
}
|
|
|
|
# OSTree deploy deployment
|
|
ostree_deploy() {
|
|
local deployment_name="$1"
|
|
|
|
log_info "OSTree deploy: $deployment_name" "apt-layer"
|
|
|
|
if [[ -z "$deployment_name" ]]; then
|
|
log_error "Deployment name required" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Validate deployment exists
|
|
if ! ostree_deployment_exists "$deployment_name"; then
|
|
log_error "Deployment not found: $deployment_name" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Perform atomic deployment
|
|
if ! atomic_deploy_deployment "$deployment_name"; then
|
|
log_error "Failed to deploy: $deployment_name" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Update current deployment reference
|
|
OSTREE_CURRENT_DEPLOYMENT="$deployment_name"
|
|
|
|
log_success "OSTree deploy completed: $deployment_name" "apt-layer"
|
|
return 0
|
|
}
|
|
|
|
# OSTree compose tree (declarative image building)
|
|
ostree_compose_tree() {
|
|
local config_file="$1"
|
|
|
|
log_info "OSTree compose tree from: $config_file" "apt-layer"
|
|
|
|
if [[ -z "$config_file" ]] || [[ ! -f "$config_file" ]]; then
|
|
log_error "Valid configuration file required" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Parse configuration
|
|
if ! parse_compose_config "$config_file"; then
|
|
log_error "Failed to parse configuration: $config_file" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Start transaction
|
|
start_transaction "ostree-compose-tree"
|
|
|
|
# Build tree from configuration
|
|
if ! build_tree_from_config; then
|
|
rollback_transaction
|
|
return 1
|
|
fi
|
|
|
|
commit_transaction
|
|
log_success "OSTree compose tree completed" "apt-layer"
|
|
return 0
|
|
}
|
|
|
|
# Helper functions for OSTree operations
|
|
|
|
# Rebase to OCI image
|
|
ostree_rebase_to_oci() {
|
|
local image_name="$1"
|
|
local deployment_name="$2"
|
|
|
|
log_debug "Rebasing to OCI image: $image_name" "apt-layer"
|
|
|
|
# Import OCI image as ComposeFS
|
|
local composefs_image="$WORKSPACE/images/$deployment_name"
|
|
|
|
if ! import_oci_image "$image_name" "$composefs_image"; then
|
|
log_error "Failed to import OCI image: $image_name" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Create deployment from ComposeFS image
|
|
if ! create_deployment_from_composefs "$composefs_image" "$deployment_name"; then
|
|
log_error "Failed to create deployment from ComposeFS" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Rebase to ComposeFS image
|
|
ostree_rebase_to_composefs() {
|
|
local base_image="$1"
|
|
local deployment_name="$2"
|
|
|
|
log_debug "Rebasing to ComposeFS image: $base_image" "apt-layer"
|
|
|
|
# Validate base image exists
|
|
if ! composefs_image_exists "$base_image"; then
|
|
log_error "Base image not found: $base_image" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Create deployment from base image
|
|
if ! create_deployment_from_composefs "$base_image" "$deployment_name"; then
|
|
log_error "Failed to create deployment from base image" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Create layered deployment
|
|
ostree_create_layered_deployment() {
|
|
local base_deployment="$1"
|
|
local new_deployment="$2"
|
|
shift 2
|
|
local packages=("$@")
|
|
|
|
log_debug "Creating layered deployment: $base_deployment -> $new_deployment" "apt-layer"
|
|
|
|
# Get base deployment path
|
|
local base_path
|
|
base_path=$(get_deployment_path "$base_deployment")
|
|
if [[ -z "$base_path" ]]; then
|
|
log_error "Base deployment not found: $base_deployment" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Create new deployment with layered packages
|
|
if ! create_layer_on_deployment "$base_path" "$new_deployment" "${packages[@]}"; then
|
|
log_error "Failed to create layered deployment" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Create override deployment
|
|
ostree_create_override_deployment() {
|
|
local base_deployment="$1"
|
|
local new_deployment="$2"
|
|
local package_name="$3"
|
|
local override_path="$4"
|
|
|
|
log_debug "Creating override deployment: $base_deployment -> $new_deployment" "apt-layer"
|
|
|
|
# Get base deployment path
|
|
local base_path
|
|
base_path=$(get_deployment_path "$base_deployment")
|
|
if [[ -z "$base_path" ]]; then
|
|
log_error "Base deployment not found: $base_deployment" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Create new deployment with package override
|
|
if ! create_override_on_deployment "$base_path" "$new_deployment" "$package_name" "$override_path"; then
|
|
log_error "Failed to create override deployment" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Parse compose configuration
|
|
parse_compose_config() {
|
|
local config_file="$1"
|
|
|
|
log_debug "Parsing compose configuration: $config_file" "apt-layer"
|
|
|
|
# Load configuration using jq
|
|
if ! command -v jq &> /dev/null; then
|
|
log_error "jq required for configuration parsing" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Parse configuration structure
|
|
COMPOSE_CONFIG=$(jq -r '.' "$config_file")
|
|
if [[ $? -ne 0 ]]; then
|
|
log_error "Failed to parse configuration file" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Extract configuration values
|
|
COMPOSE_BASE_IMAGE=$(echo "$COMPOSE_CONFIG" | jq -r '.base-image // empty')
|
|
COMPOSE_LAYERS=$(echo "$COMPOSE_CONFIG" | jq -r '.layers[]? // empty')
|
|
COMPOSE_OVERRIDES=$(echo "$COMPOSE_CONFIG" | jq -r '.overrides[]? // empty')
|
|
|
|
log_debug "Configuration parsed: base=$COMPOSE_BASE_IMAGE, layers=${#COMPOSE_LAYERS[@]}, overrides=${#COMPOSE_OVERRIDES[@]}" "apt-layer"
|
|
return 0
|
|
}
|
|
|
|
# Build tree from configuration
|
|
build_tree_from_config() {
|
|
log_debug "Building tree from configuration" "apt-layer"
|
|
|
|
# Start with base image
|
|
if [[ -n "$COMPOSE_BASE_IMAGE" ]]; then
|
|
if ! ostree_rebase_to_oci "$COMPOSE_BASE_IMAGE" "compose-base"; then
|
|
log_error "Failed to create base from configuration" "apt-layer"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# Add layers
|
|
if [[ -n "$COMPOSE_LAYERS" ]]; then
|
|
local layer_packages=()
|
|
while IFS= read -r package; do
|
|
if [[ -n "$package" ]]; then
|
|
layer_packages+=("$package")
|
|
fi
|
|
done <<< "$COMPOSE_LAYERS"
|
|
|
|
if [[ ${#layer_packages[@]} -gt 0 ]]; then
|
|
if ! ostree_layer "${layer_packages[@]}"; then
|
|
log_error "Failed to add layers from configuration" "apt-layer"
|
|
return 1
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# Apply overrides
|
|
if [[ -n "$COMPOSE_OVERRIDES" ]]; then
|
|
while IFS= read -r override; do
|
|
if [[ -n "$override" ]]; then
|
|
local package_name
|
|
local override_path
|
|
package_name=$(echo "$override" | jq -r '.package // empty')
|
|
override_path=$(echo "$override" | jq -r '.with // empty')
|
|
|
|
if [[ -n "$package_name" ]] && [[ -n "$override_path" ]]; then
|
|
if ! ostree_override "$package_name" "$override_path"; then
|
|
log_error "Failed to apply override: $package_name" "apt-layer"
|
|
return 1
|
|
fi
|
|
fi
|
|
fi
|
|
done <<< "$COMPOSE_OVERRIDES"
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Enhanced package management with metadata handling
|
|
|
|
# Layer package with metadata preservation
|
|
ostree_layer_with_metadata() {
|
|
local package="$1"
|
|
local deployment_name="${OSTREE_CURRENT_DEPLOYMENT:-current}"
|
|
local preserve_metadata="${2:-true}"
|
|
local resolve_conflicts="${3:-keep-latest}"
|
|
|
|
log_info "OSTree layer with metadata: $package" "apt-layer"
|
|
|
|
# Start transaction
|
|
start_transaction "ostree-layer-metadata-$deployment_name"
|
|
|
|
# Create new deployment with metadata handling
|
|
local new_deployment="$deployment_name-metadata-$(date +%Y%m%d-%H%M%S)"
|
|
|
|
if ! ostree_create_metadata_aware_deployment "$deployment_name" "$new_deployment" "$package" "$preserve_metadata" "$resolve_conflicts"; then
|
|
rollback_transaction
|
|
return 1
|
|
fi
|
|
|
|
# Deploy the new deployment
|
|
if ! ostree_deploy "$new_deployment"; then
|
|
rollback_transaction
|
|
return 1
|
|
fi
|
|
|
|
commit_transaction
|
|
log_success "OSTree layer with metadata completed: $new_deployment" "apt-layer"
|
|
return 0
|
|
}
|
|
|
|
# Multi-arch aware layering
|
|
ostree_layer_multiarch() {
|
|
local package="$1"
|
|
local arch="${2:-amd64}"
|
|
local multiarch_type="${3:-same}"
|
|
local deployment_name="${OSTREE_CURRENT_DEPLOYMENT:-current}"
|
|
|
|
log_info "OSTree layer multi-arch: $package ($arch, $multiarch_type)" "apt-layer"
|
|
|
|
# Validate multi-arch parameters
|
|
case "$multiarch_type" in
|
|
same|foreign|allowed)
|
|
;;
|
|
*)
|
|
log_error "Invalid multi-arch type: $multiarch_type" "apt-layer"
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
# Start transaction
|
|
start_transaction "ostree-layer-multiarch-$deployment_name"
|
|
|
|
# Create new deployment with multi-arch support
|
|
local new_deployment="$deployment_name-multiarch-$(date +%Y%m%d-%H%M%S)"
|
|
|
|
if ! ostree_create_multiarch_deployment "$deployment_name" "$new_deployment" "$package" "$arch" "$multiarch_type"; then
|
|
rollback_transaction
|
|
return 1
|
|
fi
|
|
|
|
# Deploy the new deployment
|
|
if ! ostree_deploy "$new_deployment"; then
|
|
rollback_transaction
|
|
return 1
|
|
fi
|
|
|
|
commit_transaction
|
|
log_success "OSTree layer multi-arch completed: $new_deployment" "apt-layer"
|
|
return 0
|
|
}
|
|
|
|
# Maintainer script handling
|
|
ostree_layer_with_script_validation() {
|
|
local package="$1"
|
|
local script_context="${2:-offline}"
|
|
local deployment_name="${OSTREE_CURRENT_DEPLOYMENT:-current}"
|
|
|
|
log_info "OSTree layer with script validation: $package ($script_context)" "apt-layer"
|
|
|
|
# Validate maintainer scripts
|
|
if ! validate_maintainer_scripts "$package" "$script_context"; then
|
|
log_error "Maintainer script validation failed for: $package" "apt-layer"
|
|
return 1
|
|
fi
|
|
|
|
# Start transaction
|
|
start_transaction "ostree-layer-scripts-$deployment_name"
|
|
|
|
# Create new deployment with script handling
|
|
local new_deployment="$deployment_name-scripts-$(date +%Y%m%d-%H%M%S)"
|
|
|
|
if ! ostree_create_script_aware_deployment "$deployment_name" "$new_deployment" "$package" "$script_context"; then
|
|
rollback_transaction
|
|
return 1
|
|
fi
|
|
|
|
# Deploy the new deployment
|
|
if ! ostree_deploy "$new_deployment"; then
|
|
rollback_transaction
|
|
return 1
|
|
fi
|
|
|
|
commit_transaction
|
|
log_success "OSTree layer with script validation completed: $new_deployment" "apt-layer"
|
|
return 0
|
|
}
|
|
|
|
# Validate maintainer scripts
|
|
validate_maintainer_scripts() {
|
|
local package="$1"
|
|
local script_context="$2"
|
|
|
|
log_debug "Validating maintainer scripts for: $package ($script_context)" "apt-layer"
|
|
|
|
# Extract package and examine maintainer scripts
|
|
local temp_dir
|
|
temp_dir=$(mktemp -d)
|
|
|
|
# Download package
|
|
if ! apt-get download "$package" -o Dir::Cache="$temp_dir"; then
|
|
log_error "Failed to download package for script validation: $package" "apt-layer"
|
|
rm -rf "$temp_dir"
|
|
return 1
|
|
fi
|
|
|
|
# Extract control information
|
|
local deb_file
|
|
deb_file=$(find "$temp_dir" -name "*.deb" | head -1)
|
|
if [[ -z "$deb_file" ]]; then
|
|
log_error "No .deb file found for script validation" "apt-layer"
|
|
rm -rf "$temp_dir"
|
|
return 1
|
|
fi
|
|
|
|
# Extract control scripts
|
|
local control_dir="$temp_dir/control"
|
|
mkdir -p "$control_dir"
|
|
|
|
if ! dpkg-deb -e "$deb_file" "$control_dir"; then
|
|
log_error "Failed to extract control information" "apt-layer"
|
|
rm -rf "$temp_dir"
|
|
return 1
|
|
fi
|
|
|
|
# Check for problematic scripts
|
|
local problematic_scripts=()
|
|
|
|
# Check for service management scripts
|
|
if [[ -f "$control_dir/postinst" ]] && grep -q "systemctl" "$control_dir/postinst"; then
|
|
problematic_scripts+=("postinst:systemctl")
|
|
fi
|
|
|
|
# Check for user interaction scripts
|
|
if [[ -f "$control_dir/postinst" ]] && grep -q "debconf" "$control_dir/postinst"; then
|
|
problematic_scripts+=("postinst:debconf")
|
|
fi
|
|
|
|
# Check for live system state dependencies
|
|
if [[ -f "$control_dir/postinst" ]] && grep -q "/proc\|/sys" "$control_dir/postinst"; then
|
|
problematic_scripts+=("postinst:live-state")
|
|
fi
|
|
|
|
# Report problematic scripts
|
|
if [[ ${#problematic_scripts[@]} -gt 0 ]]; then
|
|
log_warning "Problematic maintainer scripts detected in $package:" "apt-layer"
|
|
for script in "${problematic_scripts[@]}"; do
|
|
log_warning " - $script" "apt-layer"
|
|
done
|
|
|
|
if [[ "$script_context" == "strict" ]]; then
|
|
log_error "Script validation failed in strict mode" "apt-layer"
|
|
rm -rf "$temp_dir"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# Cleanup
|
|
rm -rf "$temp_dir"
|
|
|
|
log_debug "Maintainer script validation passed for: $package" "apt-layer"
|
|
return 0
|
|
} |