Skip to content

Commit 7ea340d

Browse files
authored
Merge pull request #1238 from kernelkit/gns3-fixes
Improve bump-gns3.py error handling and usage text
2 parents 543ecb3 + 9df64f1 commit 7ea340d

File tree

1 file changed

+55
-10
lines changed

1 file changed

+55
-10
lines changed

utils/bump-gns3.py

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import re
66
import shutil
77
import sys
8+
import urllib.error
89
import urllib.request
910
from datetime import datetime
1011

@@ -16,30 +17,63 @@ def check_version_format(ver):
1617
sys.exit("Error: version must be full form like 25.08.0")
1718
return ver
1819

19-
def compute_md5_and_size(url):
20-
md5 = hashlib.md5()
21-
size = 0
20+
def download_file(url):
21+
"""Download file from URL, yielding chunks.
22+
23+
Args:
24+
url: URL of the file to download
25+
26+
Yields:
27+
bytes: 1MB chunks of the file
28+
29+
Raises:
30+
urllib.error.HTTPError: If the URL returns an error (e.g., 404)
31+
urllib.error.URLError: If there's a network error
32+
"""
2233
with urllib.request.urlopen(url) as resp:
2334
while True:
2435
chunk = resp.read(1024 * 1024)
2536
if not chunk:
2637
break
27-
md5.update(chunk)
28-
size += len(chunk)
38+
yield chunk
39+
40+
def compute_md5_and_size(chunks):
41+
"""Compute MD5 hash and total size from data chunks.
42+
43+
Args:
44+
chunks: Iterable of byte chunks
45+
46+
Returns:
47+
tuple: (md5_hexdigest, size_in_bytes)
48+
"""
49+
md5 = hashlib.md5()
50+
size = 0
51+
for chunk in chunks:
52+
md5.update(chunk)
53+
size += len(chunk)
2954
return md5.hexdigest(), size
3055

3156
def main():
32-
parser = argparse.ArgumentParser(description="Add a new Infix version to a GNS3 appliance file")
57+
parser = argparse.ArgumentParser(
58+
description="Add a new Infix version to a GNS3 appliance file",
59+
epilog="The .gns3a appliance files are typically found in the appliances/ "
60+
"directory of the gns3-registry project."
61+
)
3362
parser.add_argument("version", help="Infix version (e.g. 25.08.0)")
34-
parser.add_argument("appliance", help="Path to appliance JSON (.gns3a)")
63+
parser.add_argument("appliance", help="Path to appliance JSON file (.gns3a)")
3564
args = parser.parse_args()
3665

3766
version = check_version_format(args.version)
3867
appliance_path = args.appliance
3968

4069
# Load JSON
41-
with open(appliance_path, "r", encoding="utf-8") as f:
42-
data = json.load(f)
70+
try:
71+
with open(appliance_path, "r", encoding="utf-8") as f:
72+
data = json.load(f)
73+
except FileNotFoundError:
74+
sys.exit(f"Error: Appliance file not found: {appliance_path}")
75+
except json.JSONDecodeError as e:
76+
sys.exit(f"Error: Invalid JSON in appliance file: {e}")
4377

4478
# Skip if version already exists
4579
for v in data.get("versions", []):
@@ -52,7 +86,18 @@ def main():
5286
url = f"{REPO}/releases/download/v{version}/{filename}"
5387

5488
print(f"Downloading {url} to compute MD5 and size...")
55-
md5sum, size = compute_md5_and_size(url)
89+
try:
90+
chunks = download_file(url)
91+
md5sum, size = compute_md5_and_size(chunks)
92+
except urllib.error.HTTPError as e:
93+
if e.code == 404:
94+
sys.exit(f"Error: Version {version} not found. "
95+
f"The release v{version} does not exist or the disk image is not available.\n"
96+
f"URL: {url}")
97+
else:
98+
sys.exit(f"Error: HTTP {e.code} when downloading: {e.reason}\nURL: {url}")
99+
except urllib.error.URLError as e:
100+
sys.exit(f"Error: Network error while downloading: {e.reason}\nURL: {url}")
56101

57102
# Add image entry
58103
image_entry = {

0 commit comments

Comments
 (0)