#!/bin/bash set -euo pipefail trap 'echo "Error on line $LINENO"' ERR . .util.sh # Constants readonly REQUIRED_ENV_VARS=(BUILD_DIR REPO_DIR GIT_USER_EMAIL GIT_USER_NAME CI_REPO_CLONE_URL GIT_USER GIT_TOKEN) log() { local level=$1 shift echo "[$(date '+%Y-%m-%d %H:%M:%S')] ${level}: $*" } validate_env_vars() { local missing_vars=() for var in "${REQUIRED_ENV_VARS[@]}"; do if [[ -z "${!var:-}" ]]; then missing_vars+=("$var") fi done if [[ ${#missing_vars[@]} -gt 0 ]]; then log "ERROR" "Missing required environment variables: ${missing_vars[*]}" exit 1 fi } cleanup_build_dir() { if [[ -d "${BUILD_DIR}" ]]; then log "INFO" "Cleaning build directory" rm -rf "${BUILD_DIR:?}"/* else log "INFO" "Creating build directory" mkdir -p "${BUILD_DIR}" fi } build() { local upd=${1:-false} local retries=3 cleanup_build_dir if grep -q 'git+' PKGBUILD && ! grep -q '#tag=' PKGBUILD; then if ! makepkg --noprepare --nodeps --nobuild --skippgpcheck --noconfirm; then log "ERROR" "makepkg failed during preparation" return 1 fi makepkg --printsrcinfo >.SRCINFO [[ "${upd}" == true ]] && update_pkg fi local name ver rel epoch name=$(grep -m1 'pkgname' .SRCINFO | cut -d'=' -f2 | tr -d ' ') ver=$(grep -m1 'pkgver' .SRCINFO | cut -d'=' -f2 | tr -d ' ') rel=$(grep -m1 'pkgrel' .SRCINFO | cut -d'=' -f2 | tr -d ' ') epoch=$(grep -m1 'epoch' .SRCINFO | cut -d'=' -f2 | tr -d ' ') [[ -n "${epoch}" ]] && epoch=${epoch}: # Check if package exists in official repos if pacman -Si "${name}" 2>/dev/null | grep -v nyyu | grep -q -E '^Repository'; then log "WARN" "Found ${name} in arch repo" fi # Build package if it doesn't exist or needs updating if ! compgen -G "${REPO_DIR}/${name}-${epoch}${ver}-${rel}-*.pkg.tar.zst" >/dev/null; then log "INFO" "Building package ${name}-${epoch}${ver}-${rel}" while ((retries > 0)); do if makepkg --nodeps --skippgpcheck --noconfirm --sign || \ makepkg -C -s --skippgpcheck --noconfirm --sign || \ makepkg -C -s --skippgpcheck --nocheck --noconfirm --sign; then # Copy and add to repo for pkg in *.pkg.tar.zst; do if [[ -f "${pkg}" ]]; then cp "${pkg}"{,.sig} "${REPO_DIR}/" repo-add -R -s "${REPO_DIR}/nyyu.db.tar.zst" "${REPO_DIR}/${pkg}" fi done if ! sudo pacman -Syu --noconfirm; then log "WARN" "Failed to update system packages" fi return 0 fi ((retries--)) log "WARN" "Build failed, $retries retries remaining" sleep 5 done log "ERROR" "Failed to build package ${name}-${epoch}${ver}-${rel} after all retries" return 1 fi } process_directory() { local dir=$1 cd "${dir}" || { log "ERROR" "Failed to enter directory ${dir}"; return 1; } if [[ -f PKGBUILD ]]; then log "INFO" "Processing ${dir}" if [[ -f update.sh ]]; then chmod +x update.sh ./update.sh fi if ! grep -q 'pkgver()' PKGBUILD; then update_repo_tags fi update_pkg build true fi cd .. } process_directories() { local exit_status=0 local dirs=("$@") for dir in "${dirs[@]}"; do if ! process_directory "$dir"; then log "ERROR" "Failed to process directory ${dir}" exit_status=1 fi done return $exit_status } find_and_remove_deleted_packages() { local last_commit del=() if git cat-file -e "${CI_PREV_COMMIT_SHA:-}" 2>/dev/null; then last_commit=${CI_PREV_COMMIT_SHA} else last_commit=$(git --no-pager log --oneline | grep -v 'CI SKIP' | cut -d' ' -f1 | sed -n '5p') || { log "ERROR" "Failed to determine last commit" return 1 } fi log "INFO" "Finding removed packages from ${last_commit}" mapfile -t del < <( { git --no-pager diff "${last_commit}"..HEAD aur.txt | tail -n +4 | grep -E '^-' | cut -c2- git --no-pager diff --name-status "${last_commit}"..HEAD | grep -Po 'D\s+(\K.*)(?=/PKGBUILD)' } | sort -u ) if [[ ${#del[@]} -gt 0 ]]; then for pkg in "${del[@]}"; do log "INFO" "Removing package ${pkg}" if ! repo-remove -s "${REPO_DIR}/nyyu.db.tar.zst" "${pkg}"; then log "WARN" "Failed to remove ${pkg} from repo" fi if ! rm -vf "${REPO_DIR}/${pkg}"*; then log "WARN" "No files found for ${pkg} in ${REPO_DIR}/" fi done else log "INFO" "No packages to remove" fi } setup_git() { git config --global user.email "${GIT_USER_EMAIL}" git config --global user.name "${GIT_USER_NAME}" git config --global init.defaultBranch master git remote set-url origin "${CI_REPO_CLONE_URL}" # shellcheck disable=SC2016 git config credential.helper '!f() { sleep 1; echo "username=${GIT_USER}"; echo "password=${GIT_TOKEN}"; }; f' } process_aur_packages() { if [[ ! -f aur.txt ]]; then log "ERROR" "aur.txt not found" exit 1 fi while IFS= read -r pkg; do [[ -z "$pkg" ]] && continue if ! git clone https://aur.archlinux.org/"${pkg}".git --depth 1; then log "ERROR" "Failed to clone repository for package ${pkg}" continue fi (cd "${pkg}" && build false) || log "ERROR" "Failed to build ${pkg}" done < aur.txt } main() { validate_env_vars setup_git # Process local directories sequentially mapfile -t dirs < <(find . -maxdepth 1 -type d ! -name ".*" -printf "%f\n") process_directories "${dirs[@]}" || log "WARN" "Some directory processing failed" process_aur_packages find_and_remove_deleted_packages } main "$@"