248 lines
5.7 KiB
Bash
Executable File
248 lines
5.7 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
repo_root="$(cd "$(dirname "$0")/.." && pwd)"
|
|
push_mode=0
|
|
strict_image_check=0
|
|
positional=()
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--push)
|
|
push_mode=1
|
|
shift
|
|
;;
|
|
--strict-images)
|
|
strict_image_check=1
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
# handled below via usage
|
|
positional+=("$1")
|
|
shift
|
|
;;
|
|
*)
|
|
positional+=("$1")
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [[ ${#positional[@]} -gt 2 ]]; then
|
|
echo "ERROR: too many arguments"
|
|
exit 2
|
|
fi
|
|
|
|
output_dir="${positional[0]:-$repo_root/dist}"
|
|
#zip_name="${positional[1]:-zima-appstore-$(date +%Y%m%d-%H%M%S).zip}"
|
|
zip_name="${positional[1]:-phirna-appstore.zip}"
|
|
|
|
usage() {
|
|
cat <<USAGE
|
|
Usage: $0 [output_dir] [zip_name]
|
|
$0 --push [output_dir] [zip_name]
|
|
$0 --strict-images [output_dir] [zip_name]
|
|
$0 --push --strict-images [output_dir] [zip_name]
|
|
|
|
Examples:
|
|
$0
|
|
$0 ./dist zima-apps-main.zip
|
|
$0 --push
|
|
$0 --strict-images
|
|
USAGE
|
|
}
|
|
|
|
if [[ "${positional[0]:-}" == "-h" || "${positional[0]:-}" == "--help" ]]; then
|
|
usage
|
|
exit 0
|
|
fi
|
|
|
|
if ! command -v zip >/dev/null 2>&1; then
|
|
echo "ERROR: 'zip' command not found"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "${CI:-}" == "true" || "${GITEA_ACTIONS:-}" == "true" ]]; then
|
|
strict_image_check=1
|
|
fi
|
|
|
|
required_root_files=(
|
|
"category-list.json"
|
|
"recommend-list.json"
|
|
"featured-apps.json"
|
|
)
|
|
|
|
for file in "${required_root_files[@]}"; do
|
|
if [[ ! -f "$repo_root/$file" ]]; then
|
|
echo "ERROR: missing required file: $file"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
if [[ ! -d "$repo_root/Apps" ]]; then
|
|
echo "ERROR: missing required directory: Apps"
|
|
exit 1
|
|
fi
|
|
|
|
extract_images_from_compose() {
|
|
local compose_file="$1"
|
|
awk '
|
|
/^[[:space:]]*image:[[:space:]]*/ {
|
|
line=$0
|
|
sub(/^[[:space:]]*image:[[:space:]]*/, "", line)
|
|
sub(/[[:space:]]+#.*/, "", line)
|
|
gsub(/^["'"'"']|["'"'"']$/, "", line)
|
|
if (line != "") print line
|
|
}
|
|
' "$compose_file"
|
|
}
|
|
|
|
verify_images_online() {
|
|
local -a images=()
|
|
local -a missing=()
|
|
local compose_file app_id
|
|
|
|
while IFS= read -r compose_file; do
|
|
app_id="$(basename "$(dirname "$compose_file")")"
|
|
if [[ "$app_id" == "_template" ]]; then
|
|
continue
|
|
fi
|
|
|
|
while IFS= read -r image_ref; do
|
|
[[ -z "$image_ref" ]] && continue
|
|
images+=("$image_ref")
|
|
done < <(extract_images_from_compose "$compose_file")
|
|
done < <(find "$repo_root/Apps" -type f -name 'docker-compose.yaml' | sort)
|
|
|
|
if [[ ${#images[@]} -eq 0 ]]; then
|
|
echo "WARN: no images found to verify"
|
|
return 0
|
|
fi
|
|
|
|
mapfile -t images < <(printf '%s\n' "${images[@]}" | sort -u)
|
|
|
|
if ! command -v docker >/dev/null 2>&1; then
|
|
echo "WARN: docker is not installed; skipping online image verification"
|
|
if [[ "$strict_image_check" -eq 1 ]]; then
|
|
echo "ERROR: strict image verification is enabled (CI/Gitea or --strict-images)"
|
|
return 1
|
|
fi
|
|
return 0
|
|
fi
|
|
|
|
echo "Verifying ${#images[@]} image(s) online..."
|
|
for image_ref in "${images[@]}"; do
|
|
if docker manifest inspect "$image_ref" >/dev/null 2>&1; then
|
|
echo "OK: $image_ref"
|
|
else
|
|
echo "WARN: image not found or inaccessible: $image_ref"
|
|
missing+=("$image_ref")
|
|
fi
|
|
done
|
|
|
|
if [[ ${#missing[@]} -gt 0 ]]; then
|
|
echo
|
|
echo "Image verification warnings:"
|
|
for image_ref in "${missing[@]}"; do
|
|
echo " - $image_ref"
|
|
done
|
|
|
|
if [[ "$strict_image_check" -eq 1 ]]; then
|
|
echo
|
|
echo "ERROR: strict image verification is enabled and one or more images are missing."
|
|
return 1
|
|
fi
|
|
fi
|
|
}
|
|
|
|
if ! verify_images_online; then
|
|
exit 1
|
|
fi
|
|
|
|
is_git_repo=0
|
|
if git -C "$repo_root" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
is_git_repo=1
|
|
fi
|
|
|
|
tmp_dir="$(mktemp -d)"
|
|
staging_dir="$tmp_dir/appstore"
|
|
cleanup() {
|
|
rm -rf "$tmp_dir"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
mkdir -p "$staging_dir/Apps"
|
|
|
|
copy_file() {
|
|
local rel="$1"
|
|
mkdir -p "$staging_dir/$(dirname "$rel")"
|
|
cp "$repo_root/$rel" "$staging_dir/$rel"
|
|
}
|
|
|
|
if [[ "$is_git_repo" -eq 1 ]]; then
|
|
# Build from tracked files only so accidental local files are excluded.
|
|
while IFS= read -r rel; do
|
|
[[ -z "$rel" ]] && continue
|
|
|
|
case "$rel" in
|
|
Apps/*|category-list.json|recommend-list.json|featured-apps.json)
|
|
if [[ -f "$repo_root/$rel" ]]; then
|
|
copy_file "$rel"
|
|
fi
|
|
;;
|
|
esac
|
|
done < <(git -C "$repo_root" ls-files)
|
|
else
|
|
cp -R "$repo_root/Apps" "$staging_dir/"
|
|
for file in "${required_root_files[@]}"; do
|
|
copy_file "$file"
|
|
done
|
|
fi
|
|
|
|
# Export compatibility: source uses .yaml, AppStore tooling often expects .yml
|
|
while IFS= read -r compose_yaml; do
|
|
compose_yml="${compose_yaml%.yaml}.yml"
|
|
cp "$compose_yaml" "$compose_yml"
|
|
rm -f "$compose_yaml"
|
|
done < <(find "$staging_dir/Apps" -type f -name 'docker-compose.yaml' | sort)
|
|
|
|
if ! find "$staging_dir/Apps" -type f -name 'docker-compose.yml' | grep -q .; then
|
|
echo "ERROR: no docker-compose.yml files found in exported Apps"
|
|
exit 1
|
|
fi
|
|
|
|
mkdir -p "$output_dir"
|
|
zip_path="$(cd "$output_dir" && pwd)/$zip_name"
|
|
|
|
(
|
|
cd "$staging_dir"
|
|
zip -rq "$zip_path" . -x "*.DS_Store" "__MACOSX/*"
|
|
)
|
|
|
|
if command -v shasum >/dev/null 2>&1; then
|
|
checksum="$(shasum -a 256 "$zip_path" | awk '{print $1}')"
|
|
echo "ZIP created: $zip_path"
|
|
echo "SHA256: $checksum"
|
|
else
|
|
echo "ZIP created: $zip_path"
|
|
fi
|
|
|
|
if [[ "$push_mode" -eq 1 ]]; then
|
|
if [[ "$is_git_repo" -ne 1 ]]; then
|
|
echo "ERROR: --push requires a git repository"
|
|
exit 1
|
|
fi
|
|
|
|
echo "Push mode enabled: switching to main and publishing dist/"
|
|
git -C "$repo_root" checkout main
|
|
git -C "$repo_root" add dist/
|
|
|
|
if git -C "$repo_root" diff --cached --quiet; then
|
|
echo "No staged changes in dist/. Nothing to commit."
|
|
exit 0
|
|
fi
|
|
|
|
git -C "$repo_root" commit -m "Updated appstore"
|
|
git -C "$repo_root" push origin main
|
|
fi
|