diff --git a/AGENTS.md b/AGENTS.md index f3d14f6..0309f19 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -25,6 +25,7 @@ MUST: - Pinna images till explicit version eller digest. - Inte använda `:latest`. - Hålla volymer snäva (`/DATA/AppData/$AppID/...`). +- Vid förslag/byte av `image:` måste imagen verifieras online (manifest/tag finns i registry) innan merge. SHOULD: - `security_opt: ["no-new-privileges:true"]` diff --git a/scripts/build-appstore-zip.sh b/scripts/build-appstore-zip.sh index cce95f3..dddf634 100755 --- a/scripts/build-appstore-zip.sh +++ b/scripts/build-appstore-zip.sh @@ -3,6 +3,7 @@ set -euo pipefail repo_root="$(cd "$(dirname "$0")/.." && pwd)" push_mode=0 +strict_image_check=0 positional=() while [[ $# -gt 0 ]]; do @@ -11,6 +12,10 @@ while [[ $# -gt 0 ]]; do push_mode=1 shift ;; + --strict-images) + strict_image_check=1 + shift + ;; -h|--help) # handled below via usage positional+=("$1") @@ -36,11 +41,14 @@ usage() { cat </dev/null 2>&1; then exit 1 fi +if [[ "${CI:-}" == "true" || "${GITEA_ACTIONS:-}" == "true" ]]; then + strict_image_check=1 +fi + required_root_files=( "category-list.json" "recommend-list.json" @@ -72,6 +84,81 @@ if [[ ! -d "$repo_root/Apps" ]]; then 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