scx/cargo-publish.py
Tejun Heo bc67796658 release: Add cargo-publish.py
A helper script to publish all rust crates in the repo.
2024-09-07 07:34:17 -10:00

144 lines
4.2 KiB
Python
Executable File

#!/usr/bin/python
import argparse
import os
import re
import subprocess
import sys
priority=['scx_stats', 'scx_stats_derive', 'scx_utils', 'scx_rustland_core']
skip=['scx_mitosis']
publish_args={'scx_rlfifo': ['--no-verify'],
'scx_rustland': ['--no-verify']}
verbose = False
def err(line):
raise Exception(line)
def dbg(line):
if verbose:
print('[DBG] ' + line, file=sys.stderr)
def underline(string):
return f'\033[4m{string}\033[0m'
def get_rust_paths():
result = subprocess.run(['git', 'ls-files'], stdout=subprocess.PIPE)
lines = result.stdout.decode('utf-8').splitlines()
paths = []
for line in lines:
if line.endswith('Cargo.toml'):
paths.append(line)
return paths
def cargo_path_to_crate(path):
return path.split('/')[-2]
def cargo_is_workspace(path):
with open(path, 'r') as f:
lines = f.readlines()
for lineno, line in enumerate(lines):
workspace_re = r'(^\s*)(\[\s*workspace\s*\])(.*$)'
m = re.match(workspace_re, line)
if m:
dbg(f'[{path}:{lineno}] SKIP: {m.group(1)}{underline(m.group(2))}{m.group(3)}')
return True
return False
def publish(path, extra_args, ignore_existing):
directory = os.path.dirname(path)
try:
proc = subprocess.run(['cargo', 'publish'] + extra_args, cwd=directory,
check=True, capture_output=True)
except subprocess.CalledProcessError as e:
stdout = e.stdout.decode('utf-8').splitlines()
stderr = e.stderr.decode('utf-8').splitlines()
okay = False
if ignore_existing:
already_re = r'(^.*)(crate.*already uploaded)(.*$)'
m = re.match(already_re, stderr[-1])
if m:
print(f'IGNORE: {m.group(1)}{underline(m.group(2))}{m.group(3)}')
okay = True
if verbose or not okay:
for line in stdout:
print(f'STDOUT: {line}')
for line in stderr:
print(f'STDERR: {line}')
if not okay:
raise e
def main():
parser = argparse.ArgumentParser(prog='cargo-publish.py',
description='Publish rust projects, use --dry to see what it\'s going to do')
parser.add_argument('-s', '--start',
help='skip crates which come before this crate in the publishing order')
parser.add_argument('-i', '--ignore', action='store_true',
help='ignore errors from already published crates')
parser.add_argument('-d', '--dry', action='store_true')
parser.add_argument('-v', '--verbose', action='store_true')
args = parser.parse_args()
global verbose
verbose = args.verbose
paths = get_rust_paths()
crate_path_args = {}
for path in paths:
if cargo_is_workspace(path):
continue
crate = cargo_path_to_crate(path)
pargs = []
if crate in publish_args:
pargs = publish_args[crate]
del publish_args[crate]
crate_path_args[crate] = [path, pargs]
if len(publish_args):
err(f'publish_args contains non-existent crates {publish_args}')
if args.start and args.start not in crate_path_args:
err(f'--start specified non-existent crate {args.start}')
for crate in skip:
if crate not in crate_path_args:
err(f'{crate} is in skip list but does not exist')
del crate_path_args[crate]
# Fill targets in publishing order
targets = []
for pri in priority:
if pri not in crate_path_args:
err(f'cannot find cargo path for priority crate {pri}')
path_args = crate_path_args[pri]
targets.append([pri, [path_args[0], path_args[1]]])
del crate_path_args[pri]
for crate, path_args in sorted(crate_path_args.items()):
targets.append([crate, [path_args[0], path_args[1]]])
# Publish
start_from = args.start
for target in targets:
if start_from and target[0] != start_from:
continue
start_from = None
print(f'Publishing crate {target[0]} {" ".join(target[1][1])}')
dbg(f'target: {target}')
if not args.dry:
publish(target[1][0], target[1][1], args.ignore)
main()