#!/usr/bin/env bash

# Use select-debian-mirror to write a list of nearby good Debian mirror
# sites. This list will be used by debmirror.

set -e

usage() {
    echo "Usage: $(basename "$0") DEST_FILE NUMBER_MIRRORS" >&2
    exit 2
}

if [[ $# -ne 2 ]]; then
    usage
fi

DEST_FILE=$1
NUMBER_MIRRORS=$2

if ! [[ $NUMBER_MIRRORS =~ ^[1-9][0-9]*$ ]]; then
    echo "Error: NUMBER_MIRRORS must be a positive integer (got: $NUMBER_MIRRORS)" >&2
    exit 2
fi

dest_dir=$(dirname "$DEST_FILE")
if [[ ! -d $dest_dir ]]; then
    echo "Error: directory does not exist: $dest_dir" >&2
    exit 2
fi

# Build the file in a temp location and move it into place atomically
# only on success. The EXIT trap removes the temp file on any exit path
# (signal, set -e trip, or normal exit after a successful mv where the
# file no longer exists and rm -f silently no-ops).
#
# The temp file MUST live in the same directory as DEST_FILE so that
# the final mv is a same-filesystem rename (atomic on POSIX) rather
# than a cross-filesystem copy-then-unlink. Without -p, mktemp defaults
# to /tmp, which on modern Debian is typically tmpfs -- a different
# filesystem from /var/lib/debmirror/. A cross-filesystem mv would let
# a concurrent reader of DEST_FILE observe a partially-written file
# and would also waste I/O.
tmpfile=$(mktemp -p "$dest_dir")
trap 'rm -f "$tmpfile"' EXIT

date_string=$(date "+%Y-%m-%d %H:%M:%S")
echo "### File generated by write-debian-select-list on $date_string" > "$tmpfile"

select-debian-mirror rsync --n-top-sites="$NUMBER_MIRRORS" >> "$tmpfile"

mv "$tmpfile" "$DEST_FILE"

exit 0

: <<'=cut'

=head1 NAME

write-debian-select-list - write a list of nearby good Debian mirrors to a file

=head1 SYNOPSIS

B<write-debian-select-list> I<DEST_FILE> I<NUMBER_MIRRORS>

=head1 DESCRIPTION

B<write-debian-select-list> is a thin wrapper around
B<select-debian-mirror>(1) that writes a freshly-selected list of
Debian mirror hostnames to I<DEST_FILE>, suitable for consumption by
B<debmirror>(1). The file begins with a single comment line recording
the date the list was generated, followed by I<NUMBER_MIRRORS>
hostnames, one per line.

The list is built in a temporary file and moved into place only after
B<select-debian-mirror> succeeds, so I<DEST_FILE> is never left in a
half-written state on failure. If I<DEST_FILE> already exists, it is
unconditionally replaced.

The B<rsync> protocol is requested unconditionally because B<debmirror>
uses rsync as its transport.

=head1 ARGUMENTS

=over 4

=item I<DEST_FILE>

Path to the file that will receive the mirror list. Any existing file
at this path is replaced atomically on success. Required.

=item I<NUMBER_MIRRORS>

Number of mirror hostnames to write. Must be a positive integer (1 or
greater). Passed through to B<select-debian-mirror --n-top-sites>.
Required.

=back

=head1 EXIT STATUS

=over 4

=item B<0>

The list was written successfully.

=item B<1>

B<select-debian-mirror> failed (for example: the mirror-master site
was unreachable, or no mirror passed the score cutoff).

=item B<2>

Invalid command-line arguments: wrong number of arguments,
I<NUMBER_MIRRORS> was not a positive integer, or the directory
component of I<DEST_FILE> does not exist.

=back

=head1 EXAMPLES

Write 5 mirrors to the default debmirror location:

    $ write-debian-select-list /var/lib/debmirror/good-debian-mirrors 5

=head1 SEE ALSO

B<select-debian-mirror>(1), B<debmirror>(1)

=head1 AUTHOR

Adam H. Lewenberg <adamhl@stanford.edu> with some Claude Code assistance.

=cut
