From 249fbd9631f228b29b5913e582c694ceb3e4db6a Mon Sep 17 00:00:00 2001 From: "David J. Malan" Date: Fri, 25 Sep 2026 10:29:38 -0400 Subject: [PATCH 1/2] Fixed bug whereby newer Docker engine always thought remote image was newer --- cli50/__main__.py | 29 ++++++++++------------------- setup.py | 2 +- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/cli50/__main__.py b/cli50/__main__.py index 040e382..37f72b5 100644 --- a/cli50/__main__.py +++ b/cli50/__main__.py @@ -173,30 +173,22 @@ def main(): # Check Docker Hub for newer image if not args["fast"]: - # Remote manifest + # Remote digest try: - RemoteManifest = json.loads(subprocess.check_output([ - "docker", "manifest", "inspect", f"{IMAGE}:{args['tag']}", "--verbose" - ], stderr=subprocess.DEVNULL).decode("utf-8")) - except subprocess.CalledProcessError: - RemoteManifest = None + with urllib.request.urlopen(f"https://hub.docker.com/v2/repositories/{IMAGE}/tags/{args['tag']}", timeout=5) as response: + remote = json.load(response)["digest"] + except (OSError, KeyError, ValueError): + remote = None # Local digest - try: - LocalDigest = json.loads(subprocess.check_output([ - "docker", "inspect", f"{IMAGE}:{args['tag']}" - ], stderr=subprocess.DEVNULL).decode("utf-8"))[0] - except (IndexError, KeyError, subprocess.CalledProcessError): - LocalDigest = None + local = subprocess.run(["docker", "inspect", "--format", "{{.Id }} {{.RepoDigests}}", f"{IMAGE}:{args['tag']}"], capture_output=True, text=True).stdout.split() # Pull image if no local digest - if not LocalDigest: + if not local: pull(IMAGE, args["tag"]) # Ask to update image if local digest doesn't match any remote image digests - elif (LocalDigest and RemoteManifest) and \ - LocalDigest['Id'] not in [manifest['SchemaV2Manifest']['config']['digest'] for manifest in RemoteManifest]: - + elif remote not in local: try: response = input(f"A newer version of {IMAGE}:{args['tag']} is available. Pull now? [Y/n] ") except EOFError: @@ -326,7 +318,6 @@ def ports(container): def pull(image, tag): """Pull image as needed.""" - import json try: # Get the latest manifest from registry @@ -339,9 +330,9 @@ def pull(image, tag): "docker", "inspect", f"{image}:{tag}"], stderr=subprocess.DEVNULL).decode("utf-8"))[0]['Id'] # Pull latest if local image id does not match any digest in the manifest - assert localImageId in [manifest['SchemaV2Manifest']['config']['digest'] for manifest in RemoteManifest] == True + assert localImageId in [manifest['SchemaV2Manifest']['config']['digest'] for manifest in RemoteManifest] - except (AssertionError, requests.exceptions.ConnectionError, subprocess.CalledProcessError): + except (AssertionError, subprocess.CalledProcessError): # Pull image subprocess.call(["docker", "pull", f"{image}:{tag}"], stderr=subprocess.DEVNULL) diff --git a/setup.py b/setup.py index 0282d83..6651bce 100644 --- a/setup.py +++ b/setup.py @@ -24,6 +24,6 @@ "console_scripts": ["cli50=cli50.__main__:main"] }, url="https://github.com/cs50/cli50", - version="8.0.2", + version="8.0.3", include_package_data=True ) From c91b56f15f3acee595fa54c194bfe2a89106fac4 Mon Sep 17 00:00:00 2001 From: "David J. Malan" Date: Fri, 25 Sep 2026 10:30:32 -0400 Subject: [PATCH 2/2] Adjusted exception ordering --- cli50/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli50/__main__.py b/cli50/__main__.py index 37f72b5..fe19a93 100644 --- a/cli50/__main__.py +++ b/cli50/__main__.py @@ -177,7 +177,7 @@ def main(): try: with urllib.request.urlopen(f"https://hub.docker.com/v2/repositories/{IMAGE}/tags/{args['tag']}", timeout=5) as response: remote = json.load(response)["digest"] - except (OSError, KeyError, ValueError): + except (KeyError, OSError, ValueError): remote = None # Local digest