-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathinstall-package.sh
More file actions
137 lines (119 loc) · 4.4 KB
/
Copy pathinstall-package.sh
File metadata and controls
137 lines (119 loc) · 4.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#!/usr/bin/env bash
set -euo pipefail
PACKAGE="${1:?package is required}"
SOURCE_DIR="${2:?source directory is required}"
DEST_DIR="${3:?destination directory is required}"
shift 3
FORCE=false
CLEAN=false
for arg in "$@"; do
case "$arg" in
--force) FORCE=true ;;
--clean) CLEAN=true ;;
*) echo "Unknown argument: $arg" >&2; exit 1 ;;
esac
done
if [ "$FORCE" = true ] && [ "$CLEAN" = true ]; then
echo "Error: select overlay reinstall (--force) or clean reinstall (--clean), not both." >&2
exit 1
fi
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
INVENTORY="$SCRIPT_DIR/package-inventory.tsv"
digest_file() {
if command -v sha256sum >/dev/null 2>&1; then
tr -d '\r' < "$1" | sha256sum | awk '{print $1}'
elif command -v shasum >/dev/null 2>&1; then
tr -d '\r' < "$1" | shasum -a 256 | awk '{print $1}'
else
echo "Error: sha256sum or shasum is required." >&2
return 1
fi
}
inventory_rows() {
awk -F '\t' -v package="$PACKAGE" '$0 !~ /^#/ && $1 == package { print $2 "\t" $3 "\t" $4 }' "$INVENTORY"
}
validate_package() {
local root="$1" label="$2" count=0 path expected policy actual
while IFS=$'\t' read -r path expected policy; do
[ -n "$path" ] || continue
count=$((count + 1))
if [ "$policy" != "lf" ]; then
echo "Error: unsupported newline policy: $policy" >&2; return 1
fi
if [ ! -f "$root/$path" ]; then
echo "Error: $label package is missing managed file: $path" >&2; return 1
fi
actual="$(digest_file "$root/$path")"
if [ "$actual" != "$expected" ]; then
echo "Error: $label package content mismatch: $path" >&2; return 1
fi
done < <(inventory_rows)
if [ "$count" -eq 0 ]; then
echo "Error: no inventory entry found for package: $PACKAGE" >&2; return 1
fi
}
copy_managed() {
local from="$1" to="$2" path expected policy
while IFS=$'\t' read -r path expected policy; do
[ -n "$path" ] || continue
if [ -d "$to/$path" ]; then
echo "Error: managed path is a directory but must be a file: $path" >&2; return 1
fi
mkdir -p "$(dirname "$to/$path")"
cp -f "$from/$path" "$to/$path"
done < <(inventory_rows)
}
report_extras() {
local root="$1" relative path expected policy managed
while IFS= read -r -d '' file; do
relative="${file#"$root"/}"
managed=false
while IFS=$'\t' read -r path expected policy; do
[ "$relative" = "$path" ] && managed=true && break
done < <(inventory_rows)
[ "$managed" = true ] || printf ' %s\n' "$relative"
done < <(find "$root" -type f -print0)
}
echo "Source : $SOURCE_DIR"
echo "Dest : $DEST_DIR"
# Source validation is deliberately before any destination mutation.
validate_package "$SOURCE_DIR" "Source"
if [ -e "$DEST_DIR" ] && [ "$FORCE" = false ] && [ "$CLEAN" = false ]; then
echo "Error: destination already exists: $DEST_DIR" >&2
echo "Select --force for overlay reinstall or --clean for clean reinstall." >&2
exit 1
fi
if [ "$FORCE" = true ]; then
[ -d "$DEST_DIR" ] || { echo "Error: overlay reinstall destination must be a directory: $DEST_DIR" >&2; exit 1; }
copy_managed "$SOURCE_DIR" "$DEST_DIR"
validate_package "$DEST_DIR" "Installed"
echo "Overlay reinstall complete."
extras="$(report_extras "$DEST_DIR")"
if [ -n "$extras" ]; then
echo "Extra destination files were preserved:"
printf '%s\n' "$extras"
fi
exit 0
fi
parent="$(dirname "$DEST_DIR")"
leaf="$(basename "$DEST_DIR")"
mkdir -p "$parent"
stage="$(mktemp -d "$parent/.${leaf}.stage.XXXXXX")"
backup="$parent/.${leaf}.backup.$$"
moved_existing=false
cleanup() { if [ -d "$stage" ]; then rm -rf "$stage"; fi; }
trap cleanup EXIT
copy_managed "$SOURCE_DIR" "$stage"
validate_package "$stage" "Staged"
if [ -e "$DEST_DIR" ]; then
mv "$DEST_DIR" "$backup"
moved_existing=true
fi
if ! mv "$stage" "$DEST_DIR" || ! validate_package "$DEST_DIR" "Installed"; then
[ -e "$DEST_DIR" ] && rm -rf "$DEST_DIR"
if [ "$moved_existing" = true ] && [ -e "$backup" ]; then mv "$backup" "$DEST_DIR"; fi
echo "Error: installation replacement or final validation failed." >&2
exit 1
fi
if [ "$moved_existing" = true ]; then rm -rf "$backup"; fi
if [ "$CLEAN" = true ]; then echo "Clean reinstall complete."; else echo "Installation complete."; fi