77import hashlib
88import shutil
99import re
10+ from typing import Dict , Any , Optional , cast , Tuple
11+ from common import get_image_config , read_images
1012PRECENT_PROGRESS_SIZE = 5
1113
1214class ChecksumFailException (Exception ):
1315 pass
1416
15- IMAGES_CONFIG = os .path .join (os .path .dirname (__file__ ), "images.yml" )
1617RETRY = 3
1718
1819def ensure_dir (d , chmod = 0o777 ):
@@ -26,13 +27,31 @@ def ensure_dir(d, chmod=0o777):
2627 return False
2728 return True
2829
29- def read_images ():
30- if not os .path .isfile (IMAGES_CONFIG ):
31- raise Exception (f"Error: Remotes config file not found: { IMAGES_CONFIG } " )
32- with open (IMAGES_CONFIG ,'r' ) as f :
33- output = yaml .safe_load (f )
34- return output
3530
31+ def download_webpage (url : str ) -> Optional [str ]:
32+ try :
33+ with urllib .request .urlopen (url ) as response :
34+ # Decode the response to a string
35+ webpage = response .read ().decode ('utf-8' )
36+ return webpage
37+ except Exception as e :
38+ print (str (e ))
39+ return None
40+
41+ def get_location_header (url : str ) -> str :
42+ try :
43+ with urllib .request .urlopen (url ) as response :
44+ response_url = response .url
45+
46+ if response_url is None :
47+ raise Exception ("Location header is None, can't determine latest rpi image" )
48+ return response_url
49+ except Exception as e :
50+ print (str (e ))
51+ print ("Error: Failed to determine latest rpi image" )
52+ raise e
53+
54+
3655class DownloadProgress :
3756 last_precent : float = 0
3857 def show_progress (self , block_num , block_size , total_size ):
@@ -41,8 +60,10 @@ def show_progress(self, block_num, block_size, total_size):
4160 print (f"{ new_precent } %" , end = "\r " )
4261 self .last_precent = new_precent
4362
44- def get_file_name (headers ):
45- return re .findall ("filename=(\S+)" , headers ["Content-Disposition" ])[0 ]
63+ def get_file_name (headers , url ):
64+ if "Content-Disposition" in headers .keys ():
65+ return re .findall ("filename=(\S+)" , headers ["Content-Disposition" ])[0 ]
66+ return url .split ('/' )[- 1 ]
4667
4768def get_sha256 (filename ):
4869 sha256_hash = hashlib .sha256 ()
@@ -53,10 +74,12 @@ def get_sha256(filename):
5374 return file_checksum
5475 return
5576
56- def download_image_http (board : str , dest_folder : str , redownload : bool = False ):
77+ def download_image_http (board : Dict [ str , Any ] , dest_folder : str , redownload : bool = False ):
5778 url = board ["url" ]
5879 checksum = board ["checksum" ]
80+ download_http (url , checksum )
5981
82+ def download_http (url : str , checksum_url : str , dest_folder : str , redownload : bool = False ):
6083 with tempfile .TemporaryDirectory () as tmpdirname :
6184 print ('created temporary directory' , tmpdirname )
6285 temp_file_name = os .path .join (tmpdirname , "image.xz" )
@@ -66,8 +89,8 @@ def download_image_http(board: str, dest_folder: str, redownload: bool = False):
6689 try :
6790 # Get sha and confirm its the right image
6891 download_progress = DownloadProgress ()
69- _ , headers_checksum = urllib .request .urlretrieve (checksum , temp_file_checksum , download_progress .show_progress )
70- file_name_checksum = get_file_name (headers_checksum )
92+ _ , headers_checksum = urllib .request .urlretrieve (checksum_url , temp_file_checksum , download_progress .show_progress )
93+ file_name_checksum = get_file_name (headers_checksum , checksum_url )
7194
7295 checksum_data = None
7396 with open (temp_file_checksum , 'r' ) as f :
@@ -82,13 +105,13 @@ def download_image_http(board: str, dest_folder: str, redownload: bool = False):
82105 if os .path .isfile (dest_file_name ):
83106 file_checksum = get_sha256 (dest_file_name )
84107 if file_checksum == online_checksum :
85- # We got file and checksum is right
108+ print ( " We got base image file and checksum is right" )
86109 return
87110 # Get the file
88111 download_progress = DownloadProgress ()
89112 _ , headers = urllib .request .urlretrieve (url , temp_file_name , download_progress .show_progress )
90113
91- file_name = get_file_name (headers )
114+ file_name = get_file_name (headers , url )
92115 file_checksum = get_sha256 (temp_file_name )
93116 if file_checksum != online_checksum :
94117 print (f'Failed. Attempt # { r + 1 } , checksum missmatch: { file_checksum } expected: { online_checksum } ' )
@@ -102,11 +125,31 @@ def download_image_http(board: str, dest_folder: str, redownload: bool = False):
102125 else :
103126 print ('Error encoutered at {RETRY} attempt' )
104127 print (e )
128+ exit (1 )
105129 else :
106130 print (f"Success: { temp_file_name } " )
107131 break
108132 return
109133
134+
135+ def download_image_rpi (board : Dict [str , Any ], dest_folder : str ):
136+ port = board .get ("port" , "lite_armhf" )
137+ os_name = f"raspios"
138+ distribution = board .get ("distribution" , "bookworm" )
139+ version_file = board .get ("version_file" , "latest" )
140+ version_folder = board .get ("version_folder" , "latest" )
141+
142+ latest_url = f"https://downloads.raspberrypi.org/{ os_name } _{ port } _latest"
143+
144+ download_url = f"https://downloads.raspberrypi.org/{ os_name } _{ port } /images/{ os_name } _{ port } -{ version_folder } /{ version_file } -{ os_name } -{ distribution } -{ port } .img.xz"
145+ if version_file == "latest" or version_folder == "latest" :
146+ download_url = get_location_header (latest_url )
147+
148+ checksum_url = f"{ download_url } .sha256"
149+ download_http (download_url , checksum_url , dest_folder )
150+ return
151+
152+
110153if __name__ == "__main__" :
111154 parser = argparse .ArgumentParser (add_help = True , description = 'Download images based on BASE_BOARD and BASE_O' )
112155 parser .add_argument ('WORKSPACE_SUFFIX' , nargs = '?' , default = "default" , help = "The workspace folder suffix used folder" )
@@ -118,14 +161,28 @@ def download_image_http(board: str, dest_folder: str, redownload: bool = False):
118161 base_board = os .environ .get ("BASE_BOARD" , None )
119162 base_image_path = os .environ .get ("BASE_IMAGE_PATH" , None )
120163
121- if base_board is not None and base_board in images ["images" ]:
122- if images ["images" ][base_board ]["type" ] == "http" :
123- download_image_http (images ["images" ][base_board ], base_image_path )
124- elif images ["images" ][base_board ]["type" ] == "torrent" :
164+ if base_image_path is None :
165+ print (f'Error: did not find image config file' )
166+ exit (1 )
167+ cast (str , base_image_path )
168+
169+ image_config = get_image_config ()
170+ if image_config is not None :
171+ if image_config ["type" ] == "http" :
172+ print (f"Downloading image for { base_board } " )
173+ download_image_http (image_config , base_image_path )
174+ elif image_config ["type" ] == "rpi" :
175+ print (f"Downloading Raspberry Pi image for { base_board } " )
176+ download_image_rpi (image_config , base_image_path )
177+ elif image_config ["type" ] == "torrent" :
125178 print ("Error: Torrent not implemented" )
126179 exit (1 )
127180 else :
128- print (" Error: Unsupported image download type" )
181+ print (f' Error: Unsupported image download type: { image_config [ "type" ] } ' )
129182 exit (1 )
183+ else :
184+ print (f"Error: Image config not found for: { base_board } " )
185+ exit (1 )
186+
130187
131- print ("Done" )
188+ print ("Done" )
0 commit comments