Skip to content

Commit c1ab3da

Browse files
committed
Make release script more robust and send user agent
The data access policy of crates.io requires a `user-agent` header that identifies the application. Otherwise it may block the request, which we saw in previous runs. See https://crates.io/data-access for details. This commit adds the required user-agent header and also fixes a related bug that misclassified the error response as 'release does not exist yet'. We now only create a new release if we're sure that the release does not exist yet. Errors are raised and fail the CI job so that we can investigate them.
1 parent 37a806f commit c1ab3da

1 file changed

Lines changed: 34 additions & 6 deletions

File tree

.github/workflows/trigger-release/trigger-release.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import sys
12
import toml
23
import requests
34
import subprocess
@@ -6,16 +7,23 @@
67
crate_version = cargo_toml["workspace"]["package"]["version"]
78
print("Detected crate version " + crate_version)
89

10+
# crates.io enforces a data-access policy that requires a descriptive
11+
# User-Agent with contact info; requests without one get rejected, which
12+
# previously made this script misclassify already-published versions as
13+
# unreleased. See https://crates.io/data-access.
14+
headers = {
15+
"User-Agent": "bootloader-release-ci (https://github.com/rust-osdev/bootloader)"
16+
}
917
api_url = "https://crates.io/api/v1/crates/bootloader/" + crate_version
10-
released_version = requests.get(api_url).json()
18+
response = requests.get(api_url, headers=headers)
1119

12-
if "version" in released_version:
13-
version = released_version["version"]
20+
if response.status_code == 200 and "version" in response.json():
21+
version = response.json()["version"]
1422
assert (version["crate"] == "bootloader")
1523
assert (version["num"] == crate_version)
1624
print("Version " + crate_version + " already exists on crates.io")
1725

18-
else:
26+
elif response.status_code == 404:
1927
print("Could not find version " + crate_version +
2028
" on crates.io; creating a new release")
2129

@@ -33,6 +41,26 @@
3341
"-F", "draft=false", "-F", "prerelease=false", "-F", "generate_release_notes=false",
3442
]
3543
print(" Running `" + ' '.join(command) + '`')
36-
subprocess.run(command, check=True)
44+
result = subprocess.run(command, capture_output=True, text=True)
45+
sys.stdout.write(result.stdout)
46+
sys.stderr.write(result.stderr)
47+
if result.returncode != 0:
48+
# A release for this tag may already exist (e.g. the version was
49+
# published on crates.io but the crates.io check briefly failed, or
50+
# this workflow is being re-run). Treat that as a no-op instead of a
51+
# hard failure; fail loudly on any other error.
52+
if "already_exists" in result.stdout or "already_exists" in result.stderr:
53+
print(f" Release {tag_name} already exists; nothing to do")
54+
else:
55+
raise SystemExit(result.returncode)
56+
else:
57+
print(" Done")
3758

38-
print(" Done")
59+
else:
60+
# Any other response (rate limiting, data-access policy rejection, 5xx,
61+
# etc.) is ambiguous: we can't tell whether the version is published.
62+
# Fail loudly rather than guessing and attempting to re-create a release.
63+
raise SystemExit(
64+
f"Unexpected response from crates.io (HTTP {response.status_code}): "
65+
f"{response.text}"
66+
)

0 commit comments

Comments
 (0)