Massive improvements for upcoming updates

- All JSON definitions have been updates to become lowercase
- _Scripts/convert_vipakfile.sh now understand BasicAuth
- Improvements to the overall code
This commit is contained in:
Antranig Vartanian 2025-10-05 14:14:02 +04:00
parent 91191b6b62
commit 5bf0a1065c
No known key found for this signature in database
GPG key ID: DE3998662D59F21C
44 changed files with 896 additions and 491 deletions

218
_Scripts/convert_vipakfile.sh Executable file
View file

@ -0,0 +1,218 @@
#!/bin/sh
# convert_vipakfile.sh: Convert JSON vipakfile to plain-text .vipakfile format
PROGNAME=${0##*/}
VERSION="0.1.0"
COLOUR_SET_R="\033[0;31m"
COLOUR_SET_G="\033[0;32m"
COLOUR_SET_B="\033[0;34m"
COLOUR_END="\033[0m"
perror() {
printf "${COLOUR_SET_R}[-] ${COLOUR_END}%s\n" "$@" >&2
exit 1
}
pinfo() {
printf "${COLOUR_SET_B}[*] ${COLOUR_END}%s\n" "$@" >&2
}
paction() {
printf "${COLOUR_SET_G}[+] ${COLOUR_END}%s\n" "$@" >&2
}
usage() {
cat <<EOF
Usage: $PROGNAME [-o <output_file> | -c] [-f] [--verbose] <input_json_file>
Convert JSON vipakfile to new plain-text .vipakfile format.
-o <output_file> Write to specified file (default: stdout)
-c Write to <package>-<version>.vipakfile
-f Force overwrite of existing output file
--verbose Print debug information about extracted fields
EOF
exit 0
}
command -v jq >/dev/null 2>&1 || perror "jq is required but not found"
command -v sed >/dev/null 2>&1 || perror "sed is required but not found"
output_mode="stdout"
output_file=""
input_file=""
force_overwrite=""
verbose=""
while [ $# -gt 0 ]; do
case "$1" in
-o)
[ $# -lt 2 ] && perror "Option -o requires an output file"
output_mode="file"
output_file="$2"
shift 2
;;
-c)
output_mode="versioned"
shift
;;
-f)
force_overwrite=1
shift
;;
--verbose)
verbose=1
shift
;;
-*)
perror "Unknown option: $1"
;;
*)
[ -n "$input_file" ] && perror "Only one input file allowed"
input_file="$1"
shift
;;
esac
done
[ -z "$input_file" ] && usage
[ -f "$input_file" ] || perror "Input file '$input_file' not found"
[ -r "$input_file" ] || perror "Input file '$input_file' is not readable"
jq -e . "$input_file" >/dev/null 2>&1 || perror "Invalid JSON in $input_file"
paction "Parsing $input_file"
name=$(jq -r '.package // ""' "$input_file")
version=$(jq -r '.version // ""' "$input_file")
author=$(jq -r '.author // ""' "$input_file")
license=$(jq -r '.license // ""' "$input_file")
[ -z "$name" ] && perror "package field is required"
[ -z "$version" ] && perror "version field is required"
# Extract Remote (handle git and https)
remote_type=$(jq -r '.remote.type // ""' "$input_file")
remote=""
case "$remote_type" in
git)
remote_url=$(jq -r '.remote.url // .remote.path // ""' "$input_file")
remote_tag=$(jq -r '.remote.tag // ""' "$input_file")
if [ -z "$remote_url" ]; then
perror "remote.url or remote.path required for remote.type=git"
fi
remote="git $remote_url"
[ -n "$remote_tag" ] && remote="$remote $remote_tag"
;;
https)
# Extract remote.files as raw data
remote_files=$(jq -r '.remote.files // [] | map(
[.url, .authtype // "None", .authcredentials.user // "", .authcredentials.password // "", .md5 // ""] | join("\t")
) | join("\n")' "$input_file") || {
perror "Failed to process remote.files in $input_file"
exit 1
}
if [ -z "$remote_files" ]; then
perror "remote.files required for remote.type=https"
fi
# Process each file entry in shell
remote_files_out=""
IFS='
'
for file in $remote_files; do
url=$(echo "$file" | cut -f1)
authtype=$(echo "$file" | cut -f2)
user=$(echo "$file" | cut -f3)
password=$(echo "$file" | cut -f4)
md5=$(echo "$file" | cut -f5)
if [ "$authtype" = "BasicAuth" ]; then
if [ -z "$user" ] || [ -z "$password" ]; then
perror "Missing authcredentials.user or authcredentials.password for BasicAuth in $input_file"
fi
# Insert user:password@ after protocol
auth_url=$(echo "$url" | sed -E "s,^(https?://),\1$user:$password@,")
entry="$auth_url md5,$md5"
else
entry="$url md5,$md5"
fi
if [ -z "$remote_files_out" ]; then
remote_files_out="$entry"
else
remote_files_out="$remote_files_out;$entry"
fi
done
unset IFS
remote="https $remote_files_out"
;;
"")
: # Empty remote is valid
;;
*)
perror "Unsupported remote.type: $remote_type. Only 'git' or 'https' allowed"
;;
esac
deps=$(jq -r 'if .dependencies then .dependencies | to_entries | map("\(.key):\(.value)") | join(" ") else "" end' "$input_file")
build=$(jq -r '.build // [] | map(.command + " " + .file) | join(";")' "$input_file")
[ "$build" = "null" ] && build=""
run=""
main=""
test_run=""
test_main=""
test_cmd=""
[ -n "$verbose" ] && {
pinfo "Extracted fields:"
pinfo " NAME=$name"
pinfo " VERSION=$version"
pinfo " AUTHOR=$author"
pinfo " LICENSE=$license"
pinfo " REMOTE=$remote"
pinfo " DEPS=$deps"
pinfo " BUILD=$build"
pinfo " RUN=$run"
pinfo " MAIN=$main"
pinfo " TEST_RUN=$test_run"
pinfo " TEST_MAIN=$test_main"
pinfo " TEST=$test_cmd"
}
vipakfile_content=$(cat <<EOF
NAME = $name
VERSION = $version
AUTHOR = $author
LICENSE = $license
REMOTE = $remote
DEPS = $deps
RUN = $run
MAIN = $main
BUILD = $build
TEST_RUN = $test_run
TEST_MAIN = $test_main
TEST = $test_cmd
EOF
)
case "$output_mode" in
stdout)
paction "Writing to stdout"
printf "%s\n" "$vipakfile_content"
;;
file)
[ -z "$force_overwrite" ] && [ -f "$output_file" ] && perror "Output file '$output_file' already exists"
paction "Writing $output_file"
printf "%s\n" "$vipakfile_content" > "$output_file" 2>/dev/null || perror "Failed to write to '$output_file'"
pinfo "Conversion complete: $output_file"
;;
versioned)
output_file="$name-$version.vipakfile"
[ -z "$force_overwrite" ] && [ -f "$output_file" ] && perror "Output file '$output_file' already exists"
paction "Writing $output_file"
printf "%s\n" "$vipakfile_content" > "$output_file" 2>/dev/null || perror "Failed to write to '$output_file'"
pinfo "Conversion complete: $output_file"
;;
esac

42
_Scripts/convert_vipatsar.sh Executable file
View file

@ -0,0 +1,42 @@
#!/bin/sh
# convert_vipatsar.sh: Convert old JSON vipakfiles to new plain-text format with
# proper file naming and versioning using convert_vipakfile.sh
# Program name and version
PROGNAME=${0##*/}
VERSION="0.1.0"
# Colors for output
COLOUR_SET_R="\033[0;31m"
COLOUR_SET_G="\033[0;32m"
COLOUR_SET_B="\033[0;34m"
COLOUR_END="\033[0m"
# Error, info, and action print functions
perror() {
printf "${COLOUR_SET_R}[-] ${COLOUR_END}%s\n" "$@" >&2
exit 1
}
pinfo() {
printf "${COLOUR_SET_B}[+] ${COLOUR_END}%s\n" "$@" >&2
}
paction() {
printf "${COLOUR_SET_G}[*] ${COLOUR_END}%s\n" "$@" >&2
}
# We assume that we're running inside vipatsar tree
for dir in $(find . -not -path '*/.git/*' -not -path '.' -type d) ;
do
# The port/library name is derived from the directory
port=${dir##./}
pinfo "Working on ${port}"
cd $dir
paction "Converting ${port}.json"
../_Scripts/convert_vipakfile.sh -c "../${port}.json"
pinfo "Conversion done"
cd - >/dev/null || return
done

79
_Scripts/to_lower.sh Executable file
View file

@ -0,0 +1,79 @@
#!/bin/sh
# to_lower.sh: Convert JSON keys to lowercase, preserving dependencies subkeys
PROGNAME=${0##*/}
COLOUR_SET_R="\033[0;31m"
COLOUR_SET_G="\033[0;32m"
COLOUR_SET_B="\033[0;34m"
COLOUR_END="\033[0m"
perror() {
printf "${COLOUR_SET_R}[-] ${COLOUR_END}%s\n" "$@" >&2
exit 1
}
pinfo() {
printf "${COLOUR_SET_B}[*] ${COLOUR_END}%s\n" "$@" >&2
}
paction() {
printf "${COLOUR_SET_G}[+] ${COLOUR_END}%s\n" "$@" >&2
}
usage() {
cat <<EOF
Usage: $PROGNAME [--all] [file ...]
Convert JSON keys to lowercase, preserving dependencies subkeys.
--all Process all .json files in current directory
file Process specified JSON files
EOF
exit 0
}
command -v jq >/dev/null 2>&1 || perror "jq is required but not found"
if [ $# -eq 0 ]; then
usage
fi
if [ "$1" = "--all" ]; then
files=$(ls *.json 2>/dev/null)
[ -z "$files" ] && perror "No JSON files found in current directory"
else
files="$*"
for file in $files; do
[ -f "$file" ] || perror "File $file not found"
case "$file" in
*.json) : ;;
*) perror "File $file is not a .json file" ;;
esac
done
fi
for file in $files; do
[ -r "$file" ] || perror "File $file is not readable"
paction "Converting keys to lowercase in $file"
tmpfile=$(mktemp)
jq '
def to_lower:
if type == "object" then
with_entries(
.key |= (if . == "dependencies" then . else ascii_downcase end)
) | map_values(to_lower)
elif type == "array" then
map(to_lower)
else
.
end;
to_lower
' "$file" > "$tmpfile" 2>/dev/null || {
rm -f "$tmpfile"
perror "Failed to process $file with jq"
}
mv "$tmpfile" "$file" || perror "Failed to overwrite $file"
pinfo "Processed $file"
done
pinfo "All files processed successfully"

145
_Scripts/validate_json.sh Executable file
View file

@ -0,0 +1,145 @@
#!/bin/sh
# validate_json.sh: Validate JSON vipakfiles
PROGNAME=${0##*/}
COLOUR_SET_R="\033[0;31m"
COLOUR_SET_G="\033[0;32m"
COLOUR_SET_B="\033[0;34m"
COLOUR_END="\033[0m"
perror() {
printf "${COLOUR_SET_R}[-] ${COLOUR_END}%s\n" "$@" >&2
exit 1
}
pinfo() {
printf "${COLOUR_SET_B}[*] ${COLOUR_END}%s\n" "$@" >&2
}
paction() {
printf "${COLOUR_SET_G}[+] ${COLOUR_END}%s\n" "$@" >&2
}
usage() {
cat <<EOF
Usage: $PROGNAME [--all] [file ...]
Validate JSON vipakfiles.
--all Validate all .json files in current directory
file Validate specified JSON files
EOF
exit 0
}
command -v jq >/dev/null 2>&1 || perror "jq is required but not found"
validate_json() {
json="$1"
errors=0
if ! jq -e . "$json" >/dev/null 2>/dev/null; then
perror "$json is not valid JSON"
errors=$((errors + 1))
fi
if ! jq -e '.package | type == "string" and length > 0' "$json" >/dev/null 2>/dev/null; then
perror "$json missing or invalid package field"
errors=$((errors + 1))
fi
if ! jq -e '.version | type == "string" and length > 0' "$json" >/dev/null 2>/dev/null; then
perror "$json missing or invalid version field"
errors=$((errors + 1))
fi
if ! jq -e '.author | type == "string"' "$json" >/dev/null 2>/dev/null; then
perror "$json invalid author field (must be string)"
errors=$((errors + 1))
fi
if ! jq -e '.license | type == "string"' "$json" >/dev/null 2>/dev/null; then
perror "$json invalid license field (must be string)"
errors=$((errors + 1))
fi
if jq -e '.remote | type == "object"' "$json" >/dev/null 2>/dev/null; then
if ! jq -e '.remote.type | type == "string" and (. == "" or . == "git" or . == "https")' "$json" >/dev/null 2>/dev/null; then
perror "$json invalid remote.type (must be empty, 'git', or 'https')"
errors=$((errors + 1))
fi
if jq -e '.remote.type == "git"' "$json" >/dev/null 2>/dev/null; then
if ! jq -e '.remote.url // .remote.path | type == "string" and length > 0' "$json" >/dev/null 2>/dev/null; then
perror "$json with remote.type=git missing or invalid remote.url or remote.path"
errors=$((errors + 1))
fi
if jq -e '.remote.tag | type != "string"' "$json" >/dev/null 2>/dev/null; then
perror "$json invalid remote.tag (must be string)"
errors=$((errors + 1))
fi
fi
if jq -e '.remote.type == "https"' "$json" >/dev/null 2>/dev/null; then
if ! jq -e '.remote.files | type == "array" and length > 0' "$json" >/dev/null 2>/dev/null; then
perror "$json with remote.type=https missing or empty remote.files array"
errors=$((errors + 1))
fi
if ! jq -e '.remote.files[] | select(type == "object" and (.url | test("^(http|https)://") and .md5 | test("^[0-9a-f]{32}$")) and (.authtype != "BasicAuth" or (.authcredentials.user | type == "string" and length > 0 and .authcredentials.password | type == "string" and length > 0)))' "$json" >/dev/null 2>/dev/null; then
jq -r '.remote.files[] | select(type != "object" or (.url | not or (.url | test("^(http|https)://") | not) or (.md5 | not or (.md5 | test("^[0-9a-f]{32}$") | not)) or (.authtype == "BasicAuth" and (.authcredentials.user | not or (.authcredentials.user | type != "string" or length == 0) or .authcredentials.password | not or (.authcredentials.password | type != "string" or length == 0)))) | .url // "missing"' "$json" 2>/dev/null | while read -r url; do
perror "$json has invalid remote.files entry (url: $url, must be object with valid URL, 32-char MD5, and for BasicAuth, non-empty user/password)"
errors=$((errors + 1))
done
if [ $? -ne 0 ]; then
perror "$json remote.files validation failed due to jq error"
errors=$((errors + 1))
fi
fi
fi
fi
if jq -e '.dependencies | type != "object"' "$json" >/dev/null 2>/dev/null; then
perror "$json invalid dependencies field (must be object)"
errors=$((errors + 1))
fi
if ! jq -e '.build | type == "array"' "$json" >/dev/null 2>/dev/null; then
perror "$json invalid build field (must be array)"
errors=$((errors + 1))
fi
if ! jq -e '.build[] | select(type == "object" and .command | type == "string" and length > 0 and .file | type == "string" and length > 0)' "$json" >/dev/null 2>/dev/null; then
jq -r '.build[] | select(type != "object" or (.command | not or (.command | type != "string" or length == 0) or .file | not or (.file | type != "string" or length == 0))) | .file // "missing"' "$json" 2>/dev/null | while read -r file; do
perror "$json has invalid build entry (file: $file, must be object with non-empty command and file)"
errors=$((errors + 1))
done
if [ $? -ne 0 ]; then
perror "$json build validation failed due to jq error"
errors=$((errors + 1))
fi
fi
[ $errors -eq 0 ] && pinfo "$json is valid"
return $errors
}
errors=0
if [ $# -eq 0 ]; then
usage
fi
if [ "$1" = "--all" ]; then
files=$(ls *.json 2>/dev/null)
[ -z "$files" ] && perror "No JSON files found in current directory"
else
files="$*"
for file in $files; do
[ -f "$file" ] || perror "File $file not found"
case "$file" in
*.json) : ;;
*) perror "File $file is not a .json file" ;;
esac
done
fi
for file in $files; do
paction "Validating $file"
validate_json "$file"
[ $? -ne 0 ] && errors=$((errors + 1))
done
if [ $errors -eq 0 ]; then
pinfo "All JSON files validated successfully"
exit 0
else
perror "Validation failed with $errors error(s)"
exit 1
fi