212 lines
6.3 KiB
Bash
Executable File
212 lines
6.3 KiB
Bash
Executable File
#!/bin/bash
|
|
# Check PKGBUILD versions against published repo versions
|
|
# Creates a state file per channel and architecture when packages need building
|
|
#
|
|
# Usage: check-versions [--pull] [--arch <arch>]
|
|
# --pull pull the repository first (the scheduled run does this)
|
|
# --arch <arch> check one architecture; default: every published one
|
|
|
|
set -e
|
|
|
|
BUILD_ROOT=$(realpath "${BASH_SOURCE[0]%/*}/..")
|
|
source "$BUILD_ROOT/helpers/message-helpers.sh"
|
|
source "$BUILD_ROOT/helpers/paths.sh"
|
|
source "$BUILD_ROOT/helpers/package-metadata.sh"
|
|
source "$BUILD_ROOT/helpers/lock-helpers.sh"
|
|
|
|
PULL=false
|
|
ARCHES=""
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case $1 in
|
|
--pull)
|
|
PULL=true
|
|
shift
|
|
;;
|
|
--arch)
|
|
require_valid_arch "$2"
|
|
ARCHES="$2"
|
|
shift 2
|
|
;;
|
|
*)
|
|
print_error "Unknown option: $1"
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
[[ -n "$ARCHES" ]] || ARCHES=$(published_arches)
|
|
|
|
mkdir -p "$STATE_DIR"
|
|
|
|
# Runs on a tight schedule, so it must never disturb a build in progress:
|
|
# comparing versions reads the PKGBUILDs a running build is reading, and
|
|
# --pull would swap them underneath it. Take the lock non-blocking and skip
|
|
# the tick when a release owns it — the queue is unchanged, so the next tick
|
|
# picks up exactly where this one left off.
|
|
if ! try_release_lock; then
|
|
exit 0
|
|
fi
|
|
|
|
print_header "Package Version Check"
|
|
|
|
# The scheduled run pulls; a manual run leaves the operator's tree alone.
|
|
if [[ "$PULL" == true ]]; then
|
|
print_info "Updating repository..."
|
|
git -C "$BUILD_ROOT" pull --ff-only || print_warning "git pull failed — checking the current tree"
|
|
fi
|
|
|
|
get_repo_version() {
|
|
local pkg="$1"
|
|
local mirror="$2"
|
|
local repo_db="$REPO_ROOT/$mirror/$ARCH/omarchy.db.tar.zst"
|
|
|
|
[[ -f "$repo_db" ]] || return 0
|
|
|
|
tar -xOf "$repo_db" --wildcards '*/desc' 2>/dev/null | awk -v pkg="$pkg" '
|
|
function reset() { name=""; base=""; version="" }
|
|
function emit() {
|
|
if (!found && version != "" && (name == pkg || base == pkg)) {
|
|
print version
|
|
found=1
|
|
}
|
|
reset()
|
|
}
|
|
$0 == "%FILENAME%" { emit(); next }
|
|
$0 == "%NAME%" { if (name != "" && version != "") emit(); getline; name=$0; next }
|
|
$0 == "%BASE%" { getline; base=$0; next }
|
|
$0 == "%VERSION%" { getline; version=$0; next }
|
|
END { emit() }
|
|
'
|
|
}
|
|
|
|
get_pkgbuild_version() {
|
|
local pkgdir="$1"
|
|
if [[ -f "$pkgdir/PKGBUILD" ]]; then
|
|
(cd "$pkgdir" && bash -c 'source PKGBUILD 2>/dev/null; if [[ -n "$epoch" ]]; then echo "${epoch}:${pkgver}-${pkgrel}"; else echo "${pkgver}-${pkgrel}"; fi')
|
|
fi
|
|
}
|
|
|
|
check_vcs_unchanged() {
|
|
local pkgdir="$1"
|
|
local repo_version="$2"
|
|
local pkgbuild="$pkgdir/PKGBUILD"
|
|
|
|
[[ -f "$pkgbuild" ]] || return 1
|
|
grep -qE '^pkgver[[:space:]]*\(\)' "$pkgbuild" || return 1
|
|
[[ -n "$repo_version" ]] || return 1
|
|
|
|
# If epoch or pkgrel changed in PKGBUILD, release even if upstream is unchanged.
|
|
local pkgbuild_epoch pkgbuild_pkgrel
|
|
pkgbuild_epoch=$(cd "$pkgdir" && bash -c 'source PKGBUILD 2>/dev/null; echo "${epoch:-}"')
|
|
pkgbuild_pkgrel=$(cd "$pkgdir" && bash -c 'source PKGBUILD 2>/dev/null; echo "${pkgrel}"')
|
|
|
|
local repo_pkgrel="${repo_version##*-}"
|
|
local repo_no_pkgrel="${repo_version%-*}"
|
|
local repo_epoch=""
|
|
if [[ "$repo_no_pkgrel" == *:* ]]; then
|
|
repo_epoch="${repo_no_pkgrel%%:*}"
|
|
fi
|
|
|
|
[[ "$pkgbuild_epoch" != "$repo_epoch" ]] && return 1
|
|
[[ "$pkgbuild_pkgrel" != "$repo_pkgrel" ]] && return 1
|
|
|
|
local repo_hash upstream_hash
|
|
repo_hash=$(package_extract_vcs_hash_from_version "$repo_version")
|
|
[[ -n "$repo_hash" ]] || return 1
|
|
|
|
upstream_hash=$(package_git_upstream_hash "$pkgdir")
|
|
[[ -n "$upstream_hash" ]] || return 1
|
|
|
|
[[ "$repo_hash" == "$upstream_hash" ]]
|
|
}
|
|
|
|
check_package() {
|
|
local pkg="$1"
|
|
local pkgdir="$2"
|
|
local mirror="$3"
|
|
|
|
local pkgbuild_version repo_version
|
|
pkgbuild_version=$(get_pkgbuild_version "$pkgdir")
|
|
repo_version=$(get_repo_version "$pkg" "$mirror")
|
|
|
|
if [[ -z "$pkgbuild_version" ]]; then
|
|
return 1
|
|
fi
|
|
|
|
if grep -qE '^pkgver[[:space:]]*\(\)' "$pkgdir/PKGBUILD"; then
|
|
if [[ -n "$repo_version" && -n "$(package_extract_vcs_hash_from_version "$repo_version")" ]]; then
|
|
if check_vcs_unchanged "$pkgdir" "$repo_version"; then
|
|
return 1
|
|
else
|
|
return 0
|
|
fi
|
|
elif [[ "$pkgbuild_version" == "$repo_version" ]]; then
|
|
return 1
|
|
else
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
if [[ "$pkgbuild_version" == "$repo_version" ]]; then
|
|
return 1
|
|
fi
|
|
|
|
# Different is not the same as newer. A checkout behind the published state
|
|
# — a stale branch, an un-pulled host — otherwise queues a REBUILD OF AN
|
|
# OLDER VERSION every cycle: the builder produces it, and promotion refuses
|
|
# it because that exact filename is already published with different bytes.
|
|
# If the artifact for this version already exists in the channel, there is
|
|
# nothing to build regardless of which direction the versions differ.
|
|
if compgen -G "$REPO_ROOT/$mirror/$ARCH/${pkg}-${pkgbuild_version}-*.pkg.tar."[!s]* >/dev/null 2>&1; then
|
|
print_warning "$pkg $pkgbuild_version is already published — this checkout is behind the channel; not queueing"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
check_mirror() {
|
|
local mirror="$1"
|
|
local state_file
|
|
state_file=$(sync_queue_file "$mirror" "$ARCH")
|
|
local needs_build=false
|
|
local packages=()
|
|
|
|
print_info "Checking $mirror packages for $ARCH..."
|
|
|
|
while IFS= read -r pkg; do
|
|
local pkgdir="$PKGBUILDS_DIR/$pkg"
|
|
if check_package "$pkg" "$pkgdir" "$mirror"; then
|
|
needs_build=true
|
|
packages+=("$pkg")
|
|
fi
|
|
done < <(packages_for_unscoped_build "$mirror" "$ARCH")
|
|
|
|
echo ""
|
|
|
|
if [[ "$needs_build" == true ]]; then
|
|
# Record WHAT is queued, not just THAT something is: the release run reads
|
|
# this to name the packages in its start report, which is the difference
|
|
# between "a build is running" and "your package is in this build".
|
|
printf '%s\n' "${packages[@]}" >"$state_file"
|
|
print_success "${mirror^} ($ARCH) needs building (${#packages[@]} packages)"
|
|
print_info "Packages: ${packages[*]}"
|
|
print_info "State file created: $state_file"
|
|
else
|
|
print_info "${mirror^} ($ARCH) is up to date"
|
|
fi
|
|
|
|
echo ""
|
|
}
|
|
|
|
# One pass per published architecture: the version comparison reads that
|
|
# architecture's channel databases, and each queue is its own file.
|
|
for ARCH in $ARCHES; do
|
|
update_arch_paths
|
|
check_mirror edge
|
|
check_mirror rc
|
|
check_mirror stable
|
|
done
|
|
|
|
print_success "Version check complete!"
|