Files
2026-10-19 16:04:17 +02:00

135 lines
6.0 KiB
Bash

#!/bin/bash
# Published prod installer with reviewed signing keys and compatible, completed releases.
set -euo pipefail
if [[ $# != 0 ]]; then
echo "Usage: sudo bash $0" >&2
exit 1
fi
repository_url='https://persistent.oaistatic.com/codex-app-prod/linux'
package_name='chatgpt'
if [[ $EUID != 0 ]]; then
echo "Run this script with sudo: sudo bash $0" >&2
exit 1
fi
for command in pacman pacman-key pacman-conf curl gpg uname; do
command -v "$command" >/dev/null || {
echo "Missing $command; install pacman, curl, gnupg, and coreutils before continuing." >&2
exit 1
}
done
temporary=$(mktemp -d)
bootstrap_cache=''
trap 'if [[ -n $bootstrap_cache ]]; then rm -f -- "$bootstrap_cache.db" "$bootstrap_cache.db.sig"; fi; rm -rf "$temporary"' EXIT
architecture=$(uname -m)
case "$architecture" in
x86_64|aarch64) ;;
*) echo 'Unsupported architecture.' >&2; exit 1 ;;
esac
versions=(26.908.70816 26.908.61612 26.908.40834)
# The current reviewed set is authoritative for every fallback candidate.
expected_fingerprints='3BFA0E4AE8B8CC16A2D9BA684A3B4A566C4660E4'
verification_home="$temporary/verification"
mkdir -m 0700 "$verification_home"
selected=''
for version in "${versions[@]}"; do
candidate="$temporary/$version"
mkdir "$candidate"
package="$package_name-bin-$version-1-$architecture.pkg.tar.zst"
unavailable=0
for relative_path in \
"arch/$version/repository-signing-key.gpg" \
"arch/$version/$architecture/$package" \
"arch/$version/$architecture/$package.sig" \
"arch/$version/$architecture/openai-$package_name.db" \
"arch/$version/$architecture/openai-$package_name.db.sig"; do
if status=$(curl --proto '=https' --tlsv1.2 --connect-timeout 15 -fL \
--write-out '%{http_code}' -o "$candidate/${relative_path##*/}" "$repository_url/$relative_path"); then
continue
else
curl_exit=$?
fi
case "$curl_exit:$status" in
22:404|22:410|22:408|22:429|22:5??|5:*|6:*|7:*|18:*|28:*|52:*|56:*)
echo "Release $version is unavailable; trying the next published release." >&2
unavailable=1
break ;;
*) echo "Download failed for $relative_path; not falling back. No changes were made." >&2; exit 1 ;;
esac
done
if [[ $unavailable == 1 ]]; then continue; fi
if ! fingerprints=$(gpg --homedir "$verification_home" --batch --show-keys --with-colons "$candidate/repository-signing-key.gpg" |
awk -F: '$1 == "pub" { primary=1 } $1 == "sub" { primary=0 } $1 == "fpr" && primary { print $10; primary=0 }' |
LC_ALL=C sort); then
echo 'Invalid OpenAI signing-key bundle; not falling back. No keys imported.' >&2
exit 1
fi
if [[ $fingerprints != "$expected_fingerprints" ]]; then
echo 'OpenAI repository signing-key fingerprint mismatch; not falling back. No keys imported.' >&2
exit 1
fi
gpg --homedir "$verification_home" --batch --import "$candidate/repository-signing-key.gpg"
if ! gpg --homedir "$verification_home" --batch --no-auto-key-retrieve --no-auto-key-import \
--verify "$candidate/openai-$package_name.db.sig" "$candidate/openai-$package_name.db" ||
! gpg --homedir "$verification_home" --batch --no-auto-key-retrieve --no-auto-key-import \
--verify "$candidate/$package.sig" "$candidate/$package"; then
echo 'OpenAI release signature verification failed; not falling back. No keys imported or configuration changed.' >&2
exit 1
fi
selected="$candidate"
if [[ $version != "${versions[0]}" ]]; then
echo "Using previously published release $version." >&2
fi
break
done
if [[ -z $selected ]]; then
echo 'No compatible published release is available. Files may still be propagating. No changes were made; download the installer again and retry shortly.' >&2
exit 1
fi
sources_file="/etc/pacman.d/openai-$package_name.conf"
cat >"$temporary/repository.conf" <<EOF
### THIS FILE IS AUTOMATICALLY CONFIGURED ###
# Remove it and its Include line to opt out of automatic package updates.
[openai-$package_name]
SigLevel = Required DatabaseRequired TrustedOnly
Server = $repository_url/arch/\$arch
EOF
if [[ -e $sources_file ]] && ! cmp -s "$sources_file" "$temporary/repository.conf"; then
echo "Not replacing existing pacman configuration: $sources_file" >&2
exit 1
fi
pacman-key --init
pacman-key --add "$selected/repository-signing-key.gpg"
while IFS= read -r fingerprint; do
pacman-key --lsign-key "$fingerprint"
done <<<"$expected_fingerprints"
install -m 0644 "$temporary/repository.conf" "$sources_file"
include_line="Include = $sources_file"
if ! grep -qxF "$include_line" /etc/pacman.conf; then
printf '\n%s\n' "$include_line" >>/etc/pacman.conf
fi
# The first transaction must consume the files verified above, not refetch a
# newer CDN generation. Keep the installed configuration on the online repo.
# Give that snapshot its own cache identity so an older fallback cannot mask
# the online catalog's Last-Modified timestamp on the next ordinary upgrade.
bootstrap_repo="openai-$package_name-bootstrap-${temporary##*/}"
install -m 0644 "$selected/openai-$package_name.db" "$selected/$bootstrap_repo.db"
install -m 0644 "$selected/openai-$package_name.db.sig" "$selected/$bootstrap_repo.db.sig"
sed -e "s|^Server = .*|Server = file://$selected|" \
-e "s|^\\[openai-$package_name\\]$|[$bootstrap_repo]|" "$temporary/repository.conf" \
>"$temporary/repository-local.conf"
# The copied system configuration can contain private repository credentials.
install -m 0600 /dev/null "$temporary/pacman.conf"
awk -v original="$include_line" -v replacement="Include = $temporary/repository-local.conf" \
'$0 == original { $0 = replacement } { print }' /etc/pacman.conf >"$temporary/pacman.conf"
bootstrap_cache="$(pacman-conf --config "$temporary/pacman.conf" DBPath)/sync/$bootstrap_repo"
# Allow pacman's unprivileged downloader to read verified artifacts while
# keeping all files and the directory writable only by root.
chmod 0755 "$temporary" "$selected"
chmod 0644 "$selected"/*
# Keep pacman's normal confirmation prompts and perform a full system upgrade.
pacman --config "$temporary/pacman.conf" -Syu --needed "$package_name-bin"