|
| 1 | +import sys |
1 | 2 | import toml |
2 | 3 | import requests |
3 | 4 | import subprocess |
|
6 | 7 | crate_version = cargo_toml["workspace"]["package"]["version"] |
7 | 8 | print("Detected crate version " + crate_version) |
8 | 9 |
|
| 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 | +} |
9 | 17 | 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) |
11 | 19 |
|
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"] |
14 | 22 | assert (version["crate"] == "bootloader") |
15 | 23 | assert (version["num"] == crate_version) |
16 | 24 | print("Version " + crate_version + " already exists on crates.io") |
17 | 25 |
|
18 | | -else: |
| 26 | +elif response.status_code == 404: |
19 | 27 | print("Could not find version " + crate_version + |
20 | 28 | " on crates.io; creating a new release") |
21 | 29 |
|
|
33 | 41 | "-F", "draft=false", "-F", "prerelease=false", "-F", "generate_release_notes=false", |
34 | 42 | ] |
35 | 43 | 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") |
37 | 58 |
|
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