99import tempfile
1010from pathlib import Path
1111from typing import Optional
12+ from datetime import datetime
1213
1314import requests
1415
@@ -38,7 +39,7 @@ def get_with_token(*args, **kwargs):
3839 return requests .get (* args , ** kwargs )
3940
4041
41- def do_download (remote_url : str , dst_file : Path , sha256 : Optional [ str ] = None ):
42+ def do_download (remote_url : str , dst_file : Path , time : datetime , sha256 : str ):
4243 # NOTE the stream=True parameter below
4344 with get_with_token (remote_url , stream = True ) as r :
4445 r .raise_for_status ()
@@ -58,28 +59,29 @@ def do_download(remote_url: str, dst_file: Path, sha256: Optional[str] = None):
5859 downloaded_sha256 .update (chunk )
5960 # f.flush()
6061 # check for downloaded sha256
61- if sha256 and sha256 != downloaded_sha256 .hexdigest ():
62+ if sha256 != downloaded_sha256 .hexdigest ():
6263 raise Exception (
6364 f"File { dst_file .as_posix ()} sha256 mismatch: downloaded { downloaded_sha256 .hexdigest ()} , expected { sha256 } "
6465 )
6566 tmp_dst_file .chmod (0o644 )
6667 tmp_dst_file .replace (dst_file )
68+ os .utime (dst_file , (time .timestamp (), time .timestamp ())) # access and modified time
6769 finally :
6870 if tmp_dst_file is not None :
6971 if tmp_dst_file .is_file ():
7072 tmp_dst_file .unlink ()
7173
7274
7375def download_pkg_ver (
74- pkg_name : str , working_dir : Path , ver : str , url : str , sha256 : str
76+ pkg_name : str , working_dir : Path , ver : str , url : str , time : datetime , sha256 : str
7577) -> bool :
7678 # download archive file to /packages/<pkg>/versions/<version>.tar.gz
7779 dst_file = working_dir / "packages" / pkg_name / "versions" / f"{ ver } .tar.gz"
7880 if not dst_file .is_file ():
7981 dst_file .parent .mkdir (parents = True , exist_ok = True )
8082 logger .info (f"Downloading { url } to { dst_file .as_posix ()} " )
8183 try :
82- do_download (url , dst_file , sha256 = sha256 )
84+ do_download (url , dst_file , time , sha256 )
8385 return True
8486 except Exception as e :
8587 logger .error (f"Failed to download { url } to { dst_file .as_posix ()} : { e } " )
@@ -88,6 +90,11 @@ def download_pkg_ver(
8890 logger .info (f"File { dst_file .as_posix ()} already exists, skipping download" )
8991 return True
9092
93+ def _from_published_time (published : Optional [str ]) -> datetime :
94+ if published :
95+ return datetime .fromisoformat (published .replace ("Z" , "+00:00" ))
96+ else :
97+ return datetime .now ()
9198
9299# https://github.com/dart-lang/pub/blob/master/doc/repository-spec-v2.md#list-all-versions-of-a-package
93100def handle_pkg (
@@ -108,9 +115,11 @@ def handle_pkg(
108115
109116 download_tasks = []
110117 latest_ver = resp ["latest" ]["version" ]
118+ latest_time = _from_published_time (resp ["latest" ].get ("published" ))
111119
112120 for ver in resp ["versions" ]:
113121 logger .debug (f'Checking { pkg_name } =={ ver ["version" ]} ' )
122+ ver_time = _from_published_time (ver .get ("published" ))
114123 if "advisoriesUpdated" in ver :
115124 del ver ["advisoriesUpdated" ] # not supported
116125 if ver .get ("retracted" , False ):
@@ -124,6 +133,7 @@ def handle_pkg(
124133 working_dir ,
125134 ver ["version" ],
126135 ver ["archive_url" ],
136+ ver_time ,
127137 ver ["archive_sha256" ],
128138 )
129139 )
@@ -141,13 +151,13 @@ def handle_pkg(
141151 if versions_dir .is_dir ():
142152 for f in versions_dir .iterdir ():
143153 if f .is_file () and f .suffix == ".gz" :
144- ver = f .name .removesuffix (".tar.gz" )
145- if ver not in all_versions :
154+ local_ver = f .name .removesuffix (".tar.gz" )
155+ if local_ver not in all_versions :
146156 logger .info (f"Removing obsolete pkg file { f .as_posix ()} " )
147157 f .unlink (missing_ok = True )
148158
149159 # save modified metadata to api/packages/<pkg>/meta.json
150- modified_meta_str = json .dumps (ver )
160+ modified_meta_str = json .dumps (resp )
151161 meta_on_disk = working_dir / "api" / "packages" / pkg_name / "meta.json"
152162 # fast path: check if meta.json exists and has the same size
153163 if not meta_on_disk .is_file () or meta_on_disk .stat ().st_size != len (
0 commit comments