Skip to content

Commit 11a1163

Browse files
committed
Add race condition detection for uploading the PyManager index
1 parent c652cd3 commit 11a1163

File tree

1 file changed

+41
-9
lines changed

1 file changed

+41
-9
lines changed

windows-release/merge-and-upload.py

Lines changed: 41 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,17 +73,19 @@ def _run(*args):
7373
print(out.encode("ascii", "replace").decode("ascii"))
7474
if p.returncode:
7575
raise RunError(p.returncode, out)
76+
return out
7677

7778

7879
def call_ssh(*args, allow_fail=True):
7980
if not UPLOAD_HOST or NO_UPLOAD or LOCAL_INDEX:
8081
print("Skipping", args, "because UPLOAD_HOST is missing")
8182
return
8283
try:
83-
_run(*_std_args(PLINK), f"{UPLOAD_USER}@{UPLOAD_HOST}", *args)
84-
except RunError:
84+
return _run(*_std_args(PLINK), f"{UPLOAD_USER}@{UPLOAD_HOST}", *args)
85+
except RunError as ex:
8586
if not allow_fail:
8687
raise
88+
return ex.args[1]
8789

8890

8991
def upload_ssh(source, dest):
@@ -229,6 +231,13 @@ def install_sortkey(install):
229231
)
230232

231233

234+
def find_missing_from_index(url, installs):
235+
with urlopen(url) as r:
236+
x = {install_sortkey(i) for i in json.load(r)["versions"]}
237+
y = {install_sortkey(i) for i in installs} - x
238+
return [i for i in installs if install_sortkey(i) in y]
239+
240+
232241
UPLOADS = list(calculate_uploads())
233242

234243
if not UPLOADS:
@@ -241,8 +250,13 @@ def install_sortkey(install):
241250

242251
index = {"versions": []}
243252

253+
INDEX_MTIME = 0
254+
244255
if INDEX_FILE:
245256
INDEX_PATH = url2path(INDEX_URL)
257+
258+
INDEX_MTIME = int(call_ssh(["stat", "-c", "%Y", INDEX_PATH]) or 0)
259+
246260
try:
247261
if not LOCAL_INDEX:
248262
download_ssh(INDEX_PATH, INDEX_FILE)
@@ -257,6 +271,8 @@ def install_sortkey(install):
257271
except FileNotFoundError:
258272
pass
259273

274+
print(INDEX_PATH, "mtime =", INDEX_MTIME)
275+
260276

261277
new_installs = [trim_install(i) for i, *_ in UPLOADS]
262278
validate_new_installs(new_installs)
@@ -293,25 +309,41 @@ def install_sortkey(install):
293309
upload_ssh(sbom, sbom_dest)
294310

295311

296-
if not NO_UPLOAD:
297-
if INDEX_FILE:
298-
print("Uploading", INDEX_FILE, "to", INDEX_URL)
299-
upload_ssh(INDEX_FILE, INDEX_PATH)
312+
# Check that nobody else has published while we were uploading
313+
if INDEX_FILE and INDEX_MTIME:
314+
mtime = int(call_ssh(["stat", "-c", "%Y", INDEX_PATH]) or 0)
315+
if mtime > INDEX_MTIME:
316+
print("##[error]Lost a race with another publish step!")
317+
print("Expecting mtime", INDEX_MTIME, "but saw", mtime)
318+
sys.exit(1)
319+
300320

321+
if not NO_UPLOAD:
301322
if MANIFEST_FILE:
302323
print("Uploading", MANIFEST_FILE, "to", MANIFEST_URL)
303324
upload_ssh(MANIFEST_FILE, MANIFEST_PATH)
304325

326+
if INDEX_FILE:
327+
print("Uploading", INDEX_FILE, "to", INDEX_URL)
328+
upload_ssh(INDEX_FILE, INDEX_PATH)
329+
305330
print("Purging", len(UPLOADS), "uploaded files")
306331
parents = set()
307332
for i, *_ in UPLOADS:
308333
purge(i["url"])
309334
parents.add(i["url"].rpartition("/")[0] + "/")
310335
for i in parents:
311336
purge(i)
312-
if INDEX_URL:
313-
purge(INDEX_URL)
314-
purge(INDEX_URL.rpartition("/")[0] + "/")
315337
if MANIFEST_URL:
316338
purge(MANIFEST_URL)
317339
purge(MANIFEST_URL.rpartition("/")[0] + "/")
340+
if INDEX_URL:
341+
purge(INDEX_URL)
342+
purge(INDEX_URL.rpartition("/")[0] + "/")
343+
missing = find_missing_from_index(INDEX_URL, [i for i, *_ in UPLOADS])
344+
if missing:
345+
print("##[error]Lost a race with another publish step!")
346+
print("Index at", INDEX_URL, "does not contain installs:")
347+
for m in missing:
348+
print(m["id"], m["sort-version"])
349+
sys.exit(1)

0 commit comments

Comments
 (0)