feat(builder): merge deb-publisher + publish-docker-image into contract-driven builder skill
- skills/builder: SKILL.md, README.md, references/contract.md (make/publish contract v1), references/registry.md - scripts/check.py: executable contract checker (make dry-run probes, secret scan, push thin-wrapper and script path checks; --build verifies real .deb) - scripts/upload_deb.sh: migrated from deb-publisher, adds project .env auto-load and dirty-worktree publish gate - scripts/publish_docker.sh: migrated from publish-docker-image publish.sh, now env-first (DOCKER_REGISTRY/REPOSITORY/IMAGE_TAG/PLATFORMS), refuses floating latest and multi-platform --load - scripts/verify_deb.sh: metadata/content/sha256 verification with v-prefix normalization - orc: deb+docker stages both route to $builder; routing table, DAGs, README, config untouched stage names; tests updated - ack delivery.md + skiff source-model.md: reference builder - remove skills/deb-publisher and skills/publish-docker-image
This commit is contained in:
Executable
+111
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage:
|
||||
verify_deb.sh FILE.deb [EXPECTED_VERSION] [EXPECTED_ARCH]
|
||||
|
||||
Prints package metadata, key content listing, and SHA-256. When an expected
|
||||
version and/or architecture is given, mismatches fail with a non-zero exit.
|
||||
EOF
|
||||
}
|
||||
|
||||
if [[ $# -lt 1 || $# -gt 3 ]]; then
|
||||
usage >&2
|
||||
exit 2
|
||||
fi
|
||||
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
|
||||
usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
package=$1
|
||||
expected_version=${2:-}
|
||||
expected_arch=${3:-}
|
||||
fail=0
|
||||
|
||||
if [[ ! -f "$package" ]]; then
|
||||
echo "Error: file not found: $package" >&2
|
||||
exit 2
|
||||
fi
|
||||
if [[ ! -s "$package" ]]; then
|
||||
echo "Error: empty file: $package" >&2
|
||||
exit 2
|
||||
fi
|
||||
if ! command -v dpkg-deb >/dev/null 2>&1; then
|
||||
echo "Error: dpkg-deb is required." >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
echo "== metadata =="
|
||||
info=$(dpkg-deb --info "$package") || {
|
||||
echo "Error: dpkg-deb --info failed; not a valid Debian package." >&2
|
||||
exit 1
|
||||
}
|
||||
printf '%s\n' "$info"
|
||||
|
||||
package_name=$(dpkg-deb --field "$package" Package 2>/dev/null || true)
|
||||
package_version=$(dpkg-deb --field "$package" Version 2>/dev/null || true)
|
||||
package_arch=$(dpkg-deb --field "$package" Architecture 2>/dev/null || true)
|
||||
|
||||
# Debian versions never start with 'v'; git tags usually do. Compare normalized.
|
||||
expected_version=${expected_version#v}
|
||||
|
||||
if [[ -z "$package_name" || -z "$package_version" || -z "$package_arch" ]]; then
|
||||
echo "FAIL: missing Package/Version/Architecture field." >&2
|
||||
fail=1
|
||||
fi
|
||||
|
||||
if [[ -n "$expected_version" && "$package_version" != "$expected_version" ]]; then
|
||||
echo "FAIL: version mismatch: expected $expected_version, got $package_version" >&2
|
||||
fail=1
|
||||
fi
|
||||
if [[ -n "$expected_arch" && "$package_arch" != "$expected_arch" ]]; then
|
||||
echo "FAIL: architecture mismatch: expected $expected_arch, got $package_arch" >&2
|
||||
fail=1
|
||||
fi
|
||||
|
||||
# Filename shape per contract: <name>_<version>_<arch>.deb
|
||||
base=$(basename -- "$package")
|
||||
if [[ ! "$base" =~ ^[^_]+_[^_]+_[^_]+\.deb$ ]]; then
|
||||
echo "FAIL: filename does not match <name>_<version>_<arch>.deb: $base" >&2
|
||||
fail=1
|
||||
elif [[ -n "$package_version" && ! "$base" == *"${package_version}"* ]]; then
|
||||
echo "FAIL: filename version does not match package Version ($package_version): $base" >&2
|
||||
fail=1
|
||||
fi
|
||||
|
||||
echo "== contents (top level + binaries) =="
|
||||
dpkg-deb --contents "$package" | sed -n '1,40p'
|
||||
|
||||
echo "== maintainer scripts permissions (when present) =="
|
||||
control_dir=$(mktemp -d)
|
||||
trap 'rm -rf -- "$control_dir"' EXIT
|
||||
if dpkg-deb --control "$package" "$control_dir" 2>/dev/null; then
|
||||
found_scripts=false
|
||||
for script in preinst postinst prerm postrm; do
|
||||
if [[ -f "$control_dir/$script" ]]; then
|
||||
found_scripts=true
|
||||
mode=$(stat -c '%a' "$control_dir/$script")
|
||||
if [[ $mode =~ .*[2367]$ ]]; then
|
||||
echo "OK: $script mode $mode"
|
||||
else
|
||||
echo "FAIL: $script not executable (mode $mode)" >&2
|
||||
fail=1
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if [[ "$found_scripts" == false ]]; then
|
||||
echo "(no maintainer scripts)"
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "== sha256 =="
|
||||
sha256sum "$package"
|
||||
|
||||
if ((fail > 0)); then
|
||||
echo "VERIFY: FAILED" >&2
|
||||
exit 1
|
||||
fi
|
||||
echo "VERIFY: OK"
|
||||
Reference in New Issue
Block a user