204 lines
5.6 KiB
Bash
204 lines
5.6 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
set -Eeuo pipefail
|
|
|
|
die() {
|
|
printf 'ERROR: %s\n' "$*" >&2
|
|
exit 1
|
|
}
|
|
|
|
show_help() {
|
|
local script_name="$1"
|
|
local description="$2"
|
|
printf '%s\n' "$description"
|
|
printf 'Usage: %s -v|--version <postgresql-version>\n' "$script_name"
|
|
printf ' %s <postgresql-version>\n' "$script_name"
|
|
printf ' %s --help | -h\n' "$script_name"
|
|
}
|
|
|
|
parse_postgresql_version() {
|
|
local version=""
|
|
local positional_seen=0
|
|
while (($#)); do
|
|
case "$1" in
|
|
-v|--version)
|
|
(($# >= 2)) || die "$1 requires a PostgreSQL version"
|
|
[[ -n "$2" ]] || die "PostgreSQL version must not be empty"
|
|
[[ -z "$version" ]] || die "PostgreSQL version was specified more than once"
|
|
version="$2"
|
|
shift 2
|
|
;;
|
|
--version=*)
|
|
[[ -n "${1#*=}" ]] || die "PostgreSQL version must not be empty"
|
|
[[ -z "$version" ]] || die "PostgreSQL version was specified more than once"
|
|
version="${1#*=}"
|
|
shift
|
|
;;
|
|
-*)
|
|
die "unknown option: $1"
|
|
;;
|
|
*)
|
|
(( positional_seen == 0 )) || die "unexpected argument: $1"
|
|
[[ -z "$version" ]] || die "PostgreSQL version was specified more than once"
|
|
version="$1"
|
|
positional_seen=1
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
[[ -n "$version" ]] || die "PostgreSQL version is required"
|
|
validate_postgresql_version "$version"
|
|
printf '%s\n' "$version"
|
|
}
|
|
|
|
require_tools() {
|
|
local tool
|
|
for tool in "$@"; do
|
|
command -v "$tool" >/dev/null || die "$tool is required"
|
|
done
|
|
}
|
|
|
|
validate_postgresql_version() {
|
|
local version="$1"
|
|
[[ "$version" =~ ^[0-9]+\.[0-9]+([.][0-9]+)?$ ]] ||
|
|
die "invalid PostgreSQL version: $version"
|
|
}
|
|
|
|
latest_tag() {
|
|
local repository="$1"
|
|
local tag
|
|
|
|
tag="$(
|
|
curl -fsSL "https://api.github.com/repos/$repository/releases/latest" |
|
|
sed -nE 's/^[[:space:]]*"tag_name":[[:space:]]*"([^"]+)".*/\1/p' |
|
|
head -n 1 || true
|
|
)"
|
|
if [[ -n "$tag" ]]; then
|
|
printf '%s\n' "$tag"
|
|
return
|
|
fi
|
|
|
|
curl -fsSL "https://api.github.com/repos/$repository/tags?per_page=1" |
|
|
sed -nE 's/^[[:space:]]*"name":[[:space:]]*"([^"]+)".*/\1/p' |
|
|
head -n 1 || true
|
|
}
|
|
|
|
latest_pgaudit_tag() {
|
|
local repository="$1"
|
|
local major="$2"
|
|
local tag
|
|
|
|
tag="$(
|
|
curl -fsSL "https://api.github.com/repos/$repository/tags?per_page=100" |
|
|
sed -nE 's/^[[:space:]]*"name":[[:space:]]*"([^"]+)".*/\1/p' |
|
|
grep -E "^${major//./\\.}\." |
|
|
head -n 1 || true
|
|
)"
|
|
printf '%s\n' "${tag:-$(latest_tag "$repository")}"
|
|
}
|
|
|
|
latest_pg_hint_plan_tag() {
|
|
local repository="$1"
|
|
local major="$2"
|
|
local tag
|
|
|
|
tag="$(
|
|
curl -fsSL "https://api.github.com/repos/$repository/tags?per_page=100" |
|
|
sed -nE 's/^[[:space:]]*"name":[[:space:]]*"([^"]+)".*/\1/p' |
|
|
grep -E "^REL${major}_" |
|
|
head -n 1 || true
|
|
)"
|
|
printf '%s\n' "${tag:-$(latest_tag "$repository")}"
|
|
}
|
|
|
|
reset_staging_dir() {
|
|
local staging_dir="$1"
|
|
[[ "$staging_dir" != "/" ]] || die "STAGING_DIR must not be the filesystem root"
|
|
rm -rf -- "$staging_dir"
|
|
mkdir -p "$staging_dir"
|
|
}
|
|
|
|
copy_install_source() {
|
|
local source_directory="$1"
|
|
local prefix="$2"
|
|
local source_name="$3"
|
|
local complete_version
|
|
complete_version="$(basename -- "$prefix")"
|
|
local sources_directory="$SCRIPT_DIR/sources/$complete_version"
|
|
local destination="$sources_directory/$source_name"
|
|
|
|
mkdir -p "$sources_directory"
|
|
rm -rf -- "$destination"
|
|
cp -a -- "$source_directory" "$destination"
|
|
printf '%s\n' "$destination"
|
|
}
|
|
|
|
write_install_recipe() {
|
|
local source_directory="$1"
|
|
local postgresql_version="$2"
|
|
local prefix="$3"
|
|
local configure_options="${4:-}"
|
|
local recipe="$source_directory/install_$postgresql_version.sh"
|
|
|
|
{
|
|
printf '#!/usr/bin/env bash\n\nset -Eeuo pipefail\n\n'
|
|
printf 'SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"\n'
|
|
printf 'readonly PREFIX=%q\n' "$prefix"
|
|
printf 'readonly POSTGRES_VERSION=%q\n' "$postgresql_version"
|
|
printf 'readonly POSTGRES_CONFIGURE_OPTIONS=%q\n' "$configure_options"
|
|
cat <<'RECIPE_HEADER'
|
|
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
|
printf 'Re-run the saved installation steps for PostgreSQL %s.\n' "$POSTGRES_VERSION"
|
|
printf 'Usage: %s\n' "$0"
|
|
printf ' %s --help | -h\n' "$0"
|
|
exit 0
|
|
fi
|
|
[[ $# -eq 0 ]] || {
|
|
printf 'Usage: %s [--help|-h]\n' "$0" >&2
|
|
exit 2
|
|
}
|
|
|
|
readonly PG_CONFIG="$PREFIX/bin/pg_config"
|
|
readonly JOBS="${JOBS:-$(nproc)}"
|
|
|
|
RECIPE_HEADER
|
|
cat
|
|
} > "$recipe"
|
|
chmod +x "$recipe"
|
|
printf 'Saved installation recipe: %s\n' "$recipe"
|
|
}
|
|
|
|
require_postgresql_installation() {
|
|
local prefix="$1"
|
|
local pg_config="$prefix/bin/pg_config"
|
|
[[ "$prefix" != "/" ]] || die "PREFIX must not be the filesystem root"
|
|
[[ -x "$pg_config" ]] || die "PostgreSQL is not installed at $prefix"
|
|
}
|
|
|
|
download_source() {
|
|
local staging_dir="$1"
|
|
local repository="$2"
|
|
local tag="$3"
|
|
local archive_name="$4"
|
|
local source_dir="$5"
|
|
local archive="$staging_dir/$archive_name"
|
|
|
|
curl -fL --retry 3 --retry-delay 2 \
|
|
-o "$archive" \
|
|
"https://github.com/$repository/archive/refs/tags/$tag.tar.gz"
|
|
tar -xzf "$archive" -C "$staging_dir"
|
|
[[ -d "$source_dir" ]] || die "source directory was not created: $source_dir"
|
|
}
|
|
|
|
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
|
|
if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
|
|
printf 'Shared helper functions used by PostgreSQL component installers.\n'
|
|
printf 'This file is sourced by the install scripts and does not install a component.\n'
|
|
exit 0
|
|
fi
|
|
printf 'This helper is not an installer; run a component installer with <postgresql-version>.\n' >&2
|
|
printf 'Usage: %s --help | -h\n' "$0" >&2
|
|
exit 2
|
|
fi
|