44
55import logging
66import shlex
7+ import shutil
78import sys
89from argparse import ArgumentParser
910from pathlib import Path
@@ -99,6 +100,32 @@ def _lock_files(
99100 _ = check_call (options )
100101
101102
103+ # NOTE(wpk): This will not be needed once https://github.com/astral-sh/uv/issues/18155 is closed
104+ def _maybe_copy_lockfile (lock_path : Path ) -> Path | None :
105+ if not lock_path .exists ():
106+ return None
107+
108+ # copy lockfile to temp location
109+ import tempfile
110+
111+ with tempfile .TemporaryDirectory (delete = False ) as tmp_dir :
112+ new_path = Path (tmp_dir ) / lock_path .name
113+ logger .info ("backing up current uv.lock to %s" , new_path )
114+ shutil .copy2 (lock_path , new_path )
115+ return new_path
116+
117+
118+ def _only_changed_exclude_newer_time (old_path : Path , new_path : Path ) -> bool :
119+ import tomllib
120+
121+ old_data , new_data = (
122+ tomllib .loads (path .read_text (encoding = "utf-8" )) for path in (old_path , new_path )
123+ )
124+ old_data ["options" ]["exclude-newer" ] = "0"
125+ new_data ["options" ]["exclude-newer" ] = "0"
126+ return old_data == new_data
127+
128+
102129def _maybe_lock_or_sync (
103130 lock : bool ,
104131 sync : bool ,
@@ -112,17 +139,29 @@ def _maybe_lock_or_sync(
112139 else :
113140 lock = True
114141
115- if lock or sync :
116- command = [
117- "uv" ,
118- ("sync" if sync else "lock" ),
119- * (["--no-active" ] if sync else []),
120- * (["--upgrade" ] if upgrade else []),
121- * uv_options ,
122- ]
123-
124- logger .info (shlex .join (command ))
125- _ = check_call (command )
142+ if not (lock or sync ):
143+ return
144+
145+ lock_path = Path ("uv.lock" )
146+ old_lock_path = _maybe_copy_lockfile (lock_path ) if upgrade else None
147+
148+ # update lock_path
149+ command = [
150+ "uv" ,
151+ ("sync" if sync else "lock" ),
152+ * (["--no-active" ] if sync else []),
153+ * (["--upgrade" ] if upgrade else []),
154+ * uv_options ,
155+ ]
156+
157+ logger .info (shlex .join (command ))
158+ _ = check_call (command )
159+
160+ if old_lock_path is not None and _only_changed_exclude_newer_time (
161+ old_lock_path , lock_path
162+ ):
163+ logger .info ("only exclude-newer timestamp changed. Keeping old file" )
164+ shutil .move (old_lock_path , lock_path )
126165
127166
128167def main (args : Sequence [str ] | None = None ) -> int :
0 commit comments