#!/usr/bin/env bash set -euo pipefail : "${DRY_RUN:=}" # set DRY_RUN=1 to preview deletions log() { printf '[%(%F %T)T] %s\n' -1 "$*" >&2; } # Function to create symlink for snapshots create_symlink() { local osname="$1" # e.g. almalinux local release="$2" # e.g. 9.6 local repository="$3" # e.g. appstream local basepath="$4" # e.g. /shared/apps/packagerepo local label="$5" # monthly|weekly|daily local date_value="$6" # YYYYMMDD for the snapshot to promote local snap_path="${basepath}/snap/${osname}/${release}/${repository}-${date_value}" local symlink_target="${basepath}/snap/${osname}/${release}/${repository}-${label}" if [[ -d "$snap_path" ]]; then ln -sfn "$snap_path" "$symlink_target" log "Symlink set: ${symlink_target} -> ${snap_path}" return 0 else log "Snapshot path does not exist: $snap_path" return 1 fi } # Cleanup snapshot directories older than a cutoff date (YYYYMMDD) cleanup_older_than_monthly() { local osname="$1" local release="$2" local repository="$3" local basepath="$4" local cutoff_date="$5" # newly promoted monthly date, YYYYMMDD local root="${basepath}/snap/${osname}/${release}" local pattern="${repository}-"[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9] shopt -s nullglob for dir in "${root}"/${pattern}; do # Skip symlinks and non-directories [[ -L "$dir" ]] && continue [[ -d "$dir" ]] || continue # Extract the date suffix local name base datepart name="$(basename -- "$dir")" base="${name%-????????}" # remove last 8 chars datepart="${name#${base}-}" # after "-" # Only act on valid YYYYMMDD dates if [[ "$datepart" =~ ^[0-9]{8}$ ]]; then if [[ "$datepart" -lt "$cutoff_date" ]]; then if [[ -n "${DRY_RUN}" ]]; then log "[DRY_RUN] Would remove: $dir" else log "Removing: $dir" rm -rf --one-file-system -- "$dir" fi fi fi done shopt -u nullglob } # Determine which snapshot to promote based on the passed argument case "${1:-}" in monthly) promote_date=$(date --date="$(date +%Y%m01) -1 month" +%Y%m%d) ;; weekly) promote_date=$(date --date="last Sunday" +%Y%m%d) ;; daily) promote_date=$(date --date="yesterday" +%Y%m%d) ;; *) echo "Usage: $0 {monthly|weekly|daily}" exit 1 ;; esac label="$1" # Process each repo conf for conf in /etc/reposync/conf.d/*.conf; do # shellcheck disable=SC1090 source "$conf" # Expecting these vars in the conf: OSNAME, RELEASE, REPOSITORY, BASEPATH : "${OSNAME:?missing OSNAME in $conf}" : "${RELEASE:?missing RELEASE in $conf}" : "${REPOSITORY:?missing REPOSITORY in $conf}" : "${BASEPATH:?missing BASEPATH in $conf}" # 1) Promote the symlink for this label if create_symlink "$OSNAME" "$RELEASE" "$REPOSITORY" "$BASEPATH" "$label" "$promote_date"; then # 2) If monthly, clean up older snapshot dirs for this repo if [[ "$label" == "monthly" ]]; then cleanup_older_than_monthly "$OSNAME" "$RELEASE" "$REPOSITORY" "$BASEPATH" "$promote_date" fi else log "Skipping cleanup for ${OSNAME}/${RELEASE}/${REPOSITORY} due to missing ${REPOSITORY}-${promote_date}" fi done