|
| 1 | +#! /bin/bash |
| 2 | + |
| 3 | +# Verify a Django release: checks GPG signature, artifact checksums, and |
| 4 | +# smoke-tests installation from both the tarball and the wheel. |
| 5 | +# |
| 6 | +# Usage: VERSION=5.2 bash scripts/verify_release.sh |
| 7 | +# |
| 8 | +# Set GPG_KEY to a key fingerprint to import it before verifying, e.g.: |
| 9 | +# GPG_KEY=<fingerprint> VERSION=5.2 bash scripts/verify_release.sh |
| 10 | + |
| 11 | +set -xue |
| 12 | + |
| 13 | +if [[ -z "${VERSION:-}" ]]; then |
| 14 | + echo "Please set VERSION as env var" |
| 15 | + exit 1 |
| 16 | +fi |
| 17 | + |
| 18 | +if [[ ! "${VERSION}" =~ ^[0-9]+\.[0-9]+(\.[0-9]+|a[0-9]+|b[0-9]+|rc[0-9]+)?$ ]]; then |
| 19 | + echo "Not a valid version" |
| 20 | + exit 1 |
| 21 | +fi |
| 22 | + |
| 23 | +CHECKSUM_FILE="Django-${VERSION}.checksum.txt" |
| 24 | +MEDIA_URL_PREFIX="https://media.djangoproject.com" |
| 25 | +DOWNLOAD_PREFIX="https://www.djangoproject.com/download" |
| 26 | + |
| 27 | +WORKDIR=$(mktemp -d) |
| 28 | + |
| 29 | +function cleanup { |
| 30 | + rm -rf "${WORKDIR}" |
| 31 | +} |
| 32 | +trap cleanup EXIT |
| 33 | + |
| 34 | +cd "${WORKDIR}" |
| 35 | + |
| 36 | +echo "Downloading checksum file ..." |
| 37 | +curl --fail --output "${CHECKSUM_FILE}" "${MEDIA_URL_PREFIX}/pgp/${CHECKSUM_FILE}" |
| 38 | + |
| 39 | +echo "Verifying checksum file signature ..." |
| 40 | +if [[ -n "${GPG_KEY:-}" ]]; then |
| 41 | + gpg --recv-keys "${GPG_KEY}" |
| 42 | +fi |
| 43 | +gpg --verify "${CHECKSUM_FILE}" |
| 44 | + |
| 45 | +echo "Finding release artifacts ..." |
| 46 | +mapfile -t RELEASE_ARTIFACTS < <(grep "${DOWNLOAD_PREFIX}" "${CHECKSUM_FILE}") |
| 47 | + |
| 48 | +echo "Found these release artifacts:" |
| 49 | +for ARTIFACT_URL in "${RELEASE_ARTIFACTS[@]}"; do |
| 50 | + echo "- ${ARTIFACT_URL}" |
| 51 | +done |
| 52 | + |
| 53 | +echo "Downloading artifacts ..." |
| 54 | +for ARTIFACT_URL in "${RELEASE_ARTIFACTS[@]}"; do |
| 55 | + ARTIFACT_ACTUAL_URL=$(curl --head --write-out '%{redirect_url}' --output /dev/null --silent "${ARTIFACT_URL}") |
| 56 | + curl --location --fail --output "$(basename "${ARTIFACT_ACTUAL_URL}")" "${ARTIFACT_ACTUAL_URL}" |
| 57 | +done |
| 58 | + |
| 59 | +echo "Verifying artifact hashes ..." |
| 60 | +# The `2>/dev/null` suppresses notes like "sha256sum: WARNING: 60 lines are |
| 61 | +# improperly formatted". Return code is still set on error and a wrong |
| 62 | +# checksum will still show up as FAILED. |
| 63 | +echo "- MD5 checksums" |
| 64 | +md5sum --check "${CHECKSUM_FILE}" 2>/dev/null |
| 65 | +echo "- SHA1 checksums" |
| 66 | +sha1sum --check "${CHECKSUM_FILE}" 2>/dev/null |
| 67 | +echo "- SHA256 checksums" |
| 68 | +sha256sum --check "${CHECKSUM_FILE}" 2>/dev/null |
| 69 | + |
| 70 | +PKG_TAR=$(ls Django-*.tar.gz) |
| 71 | +PKG_WHL=$(ls Django-*.whl) |
| 72 | + |
| 73 | +echo "Testing tarball install ..." |
| 74 | +python3 -m venv django-pip |
| 75 | +. django-pip/bin/activate |
| 76 | +python -m pip install --no-cache-dir "${WORKDIR}/${PKG_TAR}" |
| 77 | +django-admin startproject test_one |
| 78 | +cd test_one |
| 79 | +./manage.py --help # Ensure executable bits |
| 80 | +python manage.py migrate |
| 81 | +python manage.py runserver 0 |
| 82 | +deactivate |
| 83 | +cd .. |
| 84 | + |
| 85 | +echo "Testing wheel install ..." |
| 86 | +python3 -m venv django-pip-wheel |
| 87 | +. django-pip-wheel/bin/activate |
| 88 | +python -m pip install --no-cache-dir "${WORKDIR}/${PKG_WHL}" |
| 89 | +django-admin startproject test_one |
| 90 | +cd test_one |
| 91 | +./manage.py --help # Ensure executable bits |
| 92 | +python manage.py migrate |
| 93 | +python manage.py runserver 0 |
| 94 | +deactivate |
| 95 | +cd .. |
0 commit comments