#!/bin/bash set -euo pipefail # Constants readonly REQUIRED_ENV_VARS=(BUILD_DIR REPO_DIR GIT_USER_EMAIL GIT_USER_NAME CI_FORGE_URL CI_REPO_CLONE_URL GIT_USER GIT_TOKEN) readonly REQUIRED_COMMANDS=(makepkg repo-add git pacman) # Colors for logging readonly RED='\033[0;31m' readonly GREEN='\033[0;32m' readonly YELLOW='\033[1;33m' readonly NC='\033[0m' # No Color all_packages=() trap 'echo -e "${RED}Error on line $LINENO${NC}"' ERR . .util.sh log() { local level=$1 shift local color="${NC}" case "${level}" in "ERROR") color="${RED}" ;; "WARN") color="${YELLOW}" ;; "INFO") color="${GREEN}" ;; esac echo -e "[$(date '+%Y-%m-%d %H:%M:%S')] ${color}${level}${NC}: $*" } check_dependencies() { local missing_cmds=() for cmd in "${REQUIRED_COMMANDS[@]}"; do if ! command -v "${cmd}" >/dev/null 2>&1; then missing_cmds+=("${cmd}") fi done if [[ ${#missing_cmds[@]} -gt 0 ]]; then log "ERROR" "Missing required commands: ${missing_cmds[*]}" exit 1 fi } 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() { git clean -fdx if [[ -d "${BUILD_DIR}" ]]; then rm -rf "${BUILD_DIR:?}"/* else log "INFO" "Creating build directory" mkdir -p "${BUILD_DIR}" fi } build() { local upd=${1:-false} local name ver rel epoch 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 if ! makepkg --printsrcinfo >.SRCINFO; then log "ERROR" "Failed to generate .SRCINFO" return 1 fi [[ "${upd}" == true ]] && update_pkg fi mapfile -t pkgnames < <(awk -F ' = ' '/^pkgname/ {print $2}' .SRCINFO) all_packages+=("${pkgnames[@]}") 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}: if pacman -Si "${name}" 2>/dev/null | grep -v nyyu | grep -q -E '^Repository'; then log "WARN" "Found ${name} in the Arch repo" fi if ! compgen -G "${REPO_DIR}/${name}-${epoch}${ver}-${rel}-*.pkg.tar.zst" >/dev/null; then log "INFO" "Building package ${name}-${epoch}${ver}-${rel}" if ! { makepkg --nodeps --skippgpcheck --noconfirm --sign || makepkg -C -s --skippgpcheck --noconfirm --sign || makepkg -C -s --skippgpcheck --nocheck --noconfirm --sign; }; then log "ERROR" "All build attempts failed for ${name}" return 1 fi for pkg in *.pkg.tar.zst; do if [[ -f "${pkg}" ]]; then if ! cp "${pkg}"{,.sig} "${REPO_DIR}/"; then log "ERROR" "Failed to copy ${pkg} to repo" return 1 fi if ! repo-add -R -s "${REPO_DIR}/nyyu.db.tar.zst" "${REPO_DIR}/${pkg}"; then log "ERROR" "Failed to add ${pkg} to repo database" return 1 fi fi done if ! sudo pacman -Syu --noconfirm; then log "WARN" "Failed to update system packages" fi fi return 0 } process_directory() { local dir=$1 if ! pushd "${dir}" >/dev/null; then log "ERROR" "Failed to enter directory ${dir}" return 1 fi 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 popd >/dev/null } 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() { log "INFO" "Finding removed packages" mapfile -t repo_packages < <( find "${REPO_DIR}" -type f -name "*.pkg.tar.zst" -exec basename {} \; | awk -F'-' '{ name = $1; for (i = 2; i <= NF - 3; i++) { name = name "-" $i } print name }' | sort -u ) mapfile -t removed_packages < <( comm -23 <(printf "%s\n" "${repo_packages[@]}" | sort) <(printf "%s\n" "${all_packages[@]}" | sort) ) log "INFO" "Removed packages: ${removed_packages[*]}" if [[ ${#removed_packages[@]} -gt 0 ]]; then for pkg in "${removed_packages[@]}"; 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 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}" local forge_url="${CI_FORGE_URL#https://}" cat >"${HOME}/.netrc" </dev/null; then log "ERROR" "Failed to enter directory for package ${pkg}" continue fi if ! build false; then log "ERROR" "Failed to build ${pkg}" fi popd >/dev/null rm -rf "${pkg}" done