diff --git a/letsencrypt-auto-source/letsencrypt-auto b/letsencrypt-auto-source/letsencrypt-auto index 12be26e19..9973607be 100755 --- a/letsencrypt-auto-source/letsencrypt-auto +++ b/letsencrypt-auto-source/letsencrypt-auto @@ -912,6 +912,35 @@ OldVenvExists() { [ -n "$OLD_VENV_PATH" -a -f "$OLD_VENV_PATH/bin/letsencrypt" ] } +# Given python path, version 1 and version 2, check if version 1 is outdated compared to version 2. +# An unofficial version provided as version 1 (eg. 0.28.0.dev0) will be treated +# specifically by printing "UNOFFICIAL". Otherwise, print "OUTDATED" if version 1 +# is outdated, and "UP_TO_DATE" if not. +# This function relies only on installed python environment (2.x or 3.x) by certbot-auto. +CompareVersions() { + "$1" - "$2" "$3" << "UNLIKELY_EOF" +import sys +from distutils.version import StrictVersion + +try: + current = StrictVersion(sys.argv[1]) +except ValueError: + sys.stdout.write('UNOFFICIAL') + sys.exit() + +try: + remote = StrictVersion(sys.argv[2]) +except ValueError: + sys.stdout.write('UP_TO_DATE') + sys.exit() + +if current < remote: + sys.stdout.write('OUTDATED') +else: + sys.stdout.write('UP_TO_DATE') +UNLIKELY_EOF +} + if [ "$1" = "--le-auto-phase2" ]; then # Phase 2: Create venv, install LE, and run. @@ -1640,7 +1669,12 @@ UNLIKELY_EOF error "WARNING: couldn't find Python $MIN_PYTHON_VERSION+ to check for updates." elif ! REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` ; then error "WARNING: unable to check for updates." - elif [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then + fi + + LE_VERSION_STATE=`CompareVersions "$LE_PYTHON" "$LE_AUTO_VERSION" "$REMOTE_VERSION"` + if [ "$LE_VERSION_STATE" = "UNOFFICIAL" ]; then + say "Unofficial certbot-auto version detected, self-upgrade is disabled: $LE_AUTO_VERSION" + elif [ "$LE_VERSION_STATE" = "OUTDATED" ]; then say "Upgrading certbot-auto $LE_AUTO_VERSION to $REMOTE_VERSION..." # Now we drop into Python so we don't have to install even more diff --git a/letsencrypt-auto-source/letsencrypt-auto.template b/letsencrypt-auto-source/letsencrypt-auto.template index 6d2977832..f431e32e4 100755 --- a/letsencrypt-auto-source/letsencrypt-auto.template +++ b/letsencrypt-auto-source/letsencrypt-auto.template @@ -451,6 +451,35 @@ OldVenvExists() { [ -n "$OLD_VENV_PATH" -a -f "$OLD_VENV_PATH/bin/letsencrypt" ] } +# Given python path, version 1 and version 2, check if version 1 is outdated compared to version 2. +# An unofficial version provided as version 1 (eg. 0.28.0.dev0) will be treated +# specifically by printing "UNOFFICIAL". Otherwise, print "OUTDATED" if version 1 +# is outdated, and "UP_TO_DATE" if not. +# This function relies only on installed python environment (2.x or 3.x) by certbot-auto. +CompareVersions() { + "$1" - "$2" "$3" << "UNLIKELY_EOF" +import sys +from distutils.version import StrictVersion + +try: + current = StrictVersion(sys.argv[1]) +except ValueError: + sys.stdout.write('UNOFFICIAL') + sys.exit() + +try: + remote = StrictVersion(sys.argv[2]) +except ValueError: + sys.stdout.write('UP_TO_DATE') + sys.exit() + +if current < remote: + sys.stdout.write('OUTDATED') +else: + sys.stdout.write('UP_TO_DATE') +UNLIKELY_EOF +} + if [ "$1" = "--le-auto-phase2" ]; then # Phase 2: Create venv, install LE, and run. @@ -635,7 +664,12 @@ UNLIKELY_EOF error "WARNING: couldn't find Python $MIN_PYTHON_VERSION+ to check for updates." elif ! REMOTE_VERSION=`"$LE_PYTHON" "$TEMP_DIR/fetch.py" --latest-version` ; then error "WARNING: unable to check for updates." - elif [ "$LE_AUTO_VERSION" != "$REMOTE_VERSION" ]; then + fi + + LE_VERSION_STATE=`CompareVersions "$LE_PYTHON" "$LE_AUTO_VERSION" "$REMOTE_VERSION"` + if [ "$LE_VERSION_STATE" = "UNOFFICIAL" ]; then + say "Unofficial certbot-auto version detected, self-upgrade is disabled: $LE_AUTO_VERSION" + elif [ "$LE_VERSION_STATE" = "OUTDATED" ]; then say "Upgrading certbot-auto $LE_AUTO_VERSION to $REMOTE_VERSION..." # Now we drop into Python so we don't have to install even more diff --git a/letsencrypt-auto-source/tests/auto_test.py b/letsencrypt-auto-source/tests/auto_test.py index c5109e208..16c478f20 100644 --- a/letsencrypt-auto-source/tests/auto_test.py +++ b/letsencrypt-auto-source/tests/auto_test.py @@ -27,6 +27,19 @@ def tests_dir(): return dirname(abspath(__file__)) +def copy_stable(src, dst): + """ + Copy letsencrypt-auto, and replace its current version to its equivalent stable one. + This is needed to test correctly the self-upgrade functionality. + """ + copy(src, dst) + with open(dst, 'r') as file: + filedata = file.read() + filedata = re.sub(r'LE_AUTO_VERSION="(.*)\.dev0"', r'LE_AUTO_VERSION="\1"', filedata) + with open(dst, 'w') as file: + file.write(filedata) + + sys.path.insert(0, dirname(tests_dir())) from build import build as build_le_auto @@ -343,7 +356,7 @@ class AutoTests(TestCase): 'v99.9.9/letsencrypt-auto': build_le_auto(version='99.9.9'), 'v99.9.9/letsencrypt-auto.sig': signed('something else')} with serving(resources) as base_url: - copy(LE_AUTO_PATH, le_auto_path) + copy_stable(LE_AUTO_PATH, le_auto_path) try: out, err = run_le_auto(le_auto_path, venv_dir, base_url) except CalledProcessError as exc: