#!/usr/bin/env python3 # This script is used to publish Cargo to crates.io. # # This is run automatically every 6 weeks by the Release team's automation # whose source is at https://github.com/rust-lang/simpleinfra/. # # See https://doc.crates.io/contrib/process/release.html for more about # Cargo's release process. import os import re import subprocess import urllib.request from urllib.error import HTTPError # Whenever you add a new crate to this list that does NOT start with "cargo-" # you must reach out to the infra team to add the crate to the list of crates # allowed to be published from the "cargo CI" crates.io token. TO_PUBLISH = [ 'credential/cargo-credential', 'credential/cargo-credential-libsecret', 'credential/cargo-credential-wincred', 'credential/cargo-credential-1password', 'credential/cargo-credential-macos-keychain', 'crates/rustfix', 'crates/cargo-platform', 'crates/cargo-util', 'crates/crates-io', 'crates/cargo-util-schemas', 'crates/cargo-test-macro', 'crates/cargo-test-support', '.', ] def already_published(name, version): url = f'https://static.crates.io/crates/{name}/{version}/download' try: urllib.request.urlopen(url) except HTTPError as e: # 403 and 404 are common responses to assume it is not published if 400 <= e.code < 500: return False print(f'error: failed to check if {name} {version} is already published') print(f' HTTP response error code {e.code} checking {url}') raise return True def maybe_publish(path): content = open(os.path.join(path, 'Cargo.toml')).read() name = re.search('^name = "([^"]+)"', content, re.M).group(1) version = re.search('^version = "([^"]+)"', content, re.M).group(1) if already_published(name, version): print('%s %s is already published, skipping' % (name, version)) return False subprocess.check_call(['cargo', 'publish', '--no-verify'], cwd=path) return True def main(): print('Starting publish...') for path in TO_PUBLISH: maybe_publish(path) print('Publish complete!') if __name__ == '__main__': main()