-
Notifications
You must be signed in to change notification settings - Fork 0
/
verify.sh
executable file
·291 lines (253 loc) · 7.48 KB
/
verify.sh
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
#!/usr/bin/env bash
#
# Bash script to verify file and directory structure against a predefined template.
#
# Enable error handling.
# set -eo pipefail
# Enable script debugging.
# set -x
# Define colors.
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
NC='\033[0m' # No color.
# Define a template file and folder structure
# for a reusable, custom apt repository.
declare -A template=(
## FIXME: This is wrong right now!
# Repository, release and package information.
["dists"]=""
["dists/bullseye"]=""
["dists/bullseye/main"]
["dists/bullseye/main/binary-arm64"]
["dists/bullseye/main/binary-arm64/Packages"]=""
["dists/bullseye/main/binary-arm64/Packages.gz"]=""
["dists/bullseye/main/Contents-arm64.gz"]
["dists/bullseye/Release"]=""
["dists/bullseye/Release.gpg"]=""
["dists/bullseye/InRelease"]=""
["dists/bullseye/ChangeLog"]=""
# Package binaries.
["pool"]=""
# ["pool/*.deb"]=""
# Public key.
["gpg-pubkey.asc"]=""
)
# A simple logging function.
function log {
local level="${1}"
local msg="${2}"
case "$level" in
"info")
echo -e "${GREEN}[INFO]${NC} ${msg}"
;;
"warn")
echo -e "${YELLOW}[WARN]${NC} ${msg}"
;;
"error")
echo -e "${RED}[ERROR]${NC} ${msg}"
;;
esac
}
# Function for verifying the template structure.
function verify {
local input_path="${1}"
local strict="${2}"
local path
local found
local missing_paths=""
local extra_paths=""
# set -x
for path in "${!template[@]}"; do
found=false
while IFS= read -r -d '' file; do
local relative_file_path="${file#${input_path}/}"
if [[ ${relative_file_path} == ${path} ]]; then
found=true
break
fi
done < <(find "${input_path}" \( -path "${input_path}/${path}" -type f -o -path "${input_path}/${path}" -type d \) -printf '%P\0')
if ! ${found}; then
missing_paths+="${path}"$'\n'
fi
done
# set +x
if ${strict}; then
while IFS= read -r -d '' path; do
found=false
for template_path in "${!template[@]}"; do
if [[ ${path} == ${template_path} ]]; then
found=true
break
fi
done
if ! $found; then
extra_paths+="${path}"$'\n'
fi
done < <(find "${input_path}" -mindepth 1 -printf '%P\0')
fi
echo -n -e "${missing_paths}|${extra_paths}"
}
# Function for rendering a tree structure.
function render_tree {
local root_name="${1}"
local paths="${2}"
declare -A tree
while read -r path; do
parent_node="tree"
IFS="/" read -ra path_parts <<< "${path}"
skip=false
for part in "${path_parts[@]}"; do
if [ "${skip}" = "true" ]; then
break
fi
if [[ "${part}" == *\** ]]; then
skip=true
else
if [[ -z "${tree[${parent_node}_${part}]+_}" ]]; then
tree[${parent_node}_${part}]=""
fi
parent_node="${parent_node}_${part}"
fi
done
done <<< "${paths}"
# A local function for traversing the tree structure.
function traverse_tree {
local node_prefix="${1}"
local prefix="${2-}"
local last_child="${3-false}"
local child_count=0
local child
for child in "${!tree[@]}"; do
if [[ "${child}" == "${node_prefix}_"* ]]; then
((child_count++))
fi
done
local node_name="${node_prefix##*_}"
node_name="${node_name//\*/\*}"
if [[ ! "${node_prefix}" == "tree_*" ]]; then
if [ "${last_child}" = "true" ]; then
if [[ "${node_prefix}" == *"/*" && "${node_prefix}" != */*/* ]]; then
# This is a wildcard entry, so check if there are any matching files.
local path="$(echo ${node_prefix} | sed -e 's/\*/\.\*/g')"
if find "${path}" -print -quit | grep -q .; then
# At least one matching file was found, so display the node name.
echo -e "${prefix}└── ${node_name}"
else
# No matching files were found, so display the node prefix.
echo -e "${prefix}└── ${node_prefix}"
fi
else
# This is not a wildcard entry, so display the node name.
echo -e "${prefix}└── ${node_name}"
fi
prefix+=" "
else
if [[ "${node_prefix}" == *"/*" && "${node_prefix}" != */*/* ]]; then
# This is a wildcard entry, so check if there are any matching files.
local path="$(echo ${node_prefix} | sed -e 's/\*/\.\*/g')"
if find "${path}" -print -quit | grep -q .; then
# At least one matching file was found, so display the node name.
echo -e "${prefix}├── ${node_name}"
else
# No matching files were found, so display the node prefix.
echo -e "${prefix}├── ${node_prefix}"
fi
else
# This is not a wildcard entry, so display the node name.
echo -e "${prefix}├── ${node_name}"
fi
prefix+="│ "
fi
fi
local count=1
for child in "${!tree[@]}"; do
if [[ "${child}" == "${node_prefix}_"* ]]; then
if [ ${count} -eq ${child_count} ]; then
traverse_tree "${child}" "${prefix}" "true"
else
traverse_tree "${child}" "${prefix}"
fi
((count++))
fi
done
}
# Traverse and print the tree structure.
echo "${root_name}"
for root in "${!tree[@]}"; do
if [[ "${root}" == "tree_"* ]]; then
root_name="${root#*_}"
if [[ ! "${root_name}" =~ ^\* ]]; then
traverse_tree "${root}" ""
fi
fi
done
}
# Function for printing input arguments.
function print_args {
echo
echo -e "Running with options:"
echo -e "> Input Path: ${YELLOW}${1}${NC}"
echo -e "> Strict Mode: ${YELLOW}${2}${NC}"
echo
}
# Function for printing usage information.
function usage {
echo
log "error" "Missing or invalid arguments!"
echo
echo -e "Usage: ${GREEN}${0} [-s] input_path${NC}"
echo -e " -s: Enable strict mode (do not allow extra files or directories, default: false)"
}
# Disable strict mode by default.
strict=false
# Parse command line arguments.
while getopts "s" opt; do
case "${opt}" in
s)
strict=true
shift
;;
*)
usage
exit 1
;;
esac
done
# Print usage information if no arguments are provided.
if [[ -z "${1}" ]]; then
usage
exit 1
fi
# Get input path.
input_path="${1}"
# Print input arguments.
print_args "${input_path}" "${strict}"
# Verify that the input path exists.
if [ ! -e "${input_path}" ]; then
log "error" "Input path does not exist: ${YELLOW}${input_path}${NC}"
exit 1
fi
# Verify the input path against the template.
IFS="|" read -r -d '' missing_paths extra_paths < <(verify "${input_path}" "${strict}" && printf '\0')
# Print error message if any missing files or directories are found.
if [[ ! -z "$missing_paths" ]]; then
log "error" "Missing files or directories"
echo
# echo -e "${missing_paths}" | while read -r path; do
# echo -e " ${path}"
# done
render_tree "$(basename "${input_path}")" "${missing_paths}"
fi
# Print error message if any extra files or directories are found.
if [[ ! -z "$extra_paths" ]]; then
log "error" "Extra files or directories found in strict mode"
echo
render_tree "$(basename "${input_path}")" "${extra_paths}"
fi
# Exit with error code if any missing or extra files or directories are found.
if [[ ! -z "$missing_paths" || ! -z "$extra_paths" ]]; then
exit 1
fi
# Otherwise, print success message.
log "success" "Input path matches template"