Skip to content

Commit eda20a0

Browse files
authored
tools/publish-crates.sh is more flexible for crate paths (#3185)
# Description of Changes I updated `tools/publish-crates.sh` and `tools/find-publish-list.py` to be more flexible to where our crates can be located (rather than hardcoding `crates/foo`). This is in preparation for #3181. Now, `find-publish-list.py` loads the output of `cargo metadata` to dynamically find the path of the crate in question. This also allows us to "properly" determine which crates are ours, instead of the `spacetimedb-` string prefix check that we were doing before. It also now supports `--directories` to output directory paths, rather than just crate names. `publish-crates.sh` now uses those output paths rather than assuming that it knows where to find crates. As a bonus, this approach is also much faster (~0.3s to find the crate list, vs ~8 before to load and process all the tomls). # API and ABI breaking changes None # Expected complexity level and risk 2 # Testing - [x] `find-publish-list.py` lists the same crates before and after: ```bash $ ( git checkout master && python3 tools/find-publish-list.py --recursive --quiet bindings sdk cli standalone > before.txt ) $ ( git checkout bfops/flexible-publish-scripts && python3 tools/find-publish-list.py --recursive --quiet spacetimedb spacetimedb-sdk spacetimedb-cli spacetimedb-standalone > after.txt ) # the new script prints out crate names rather than directory names, so we need to tweak a bit $ diff -U2 before.txt <(cat after.txt | sed 's/^spacetimedb-//' | sed 's/^spacetimedb$/bindings/') --- before.txt 2025-08-20 10:18:07.323217870 -0700 +++ /dev/fd/63 2025-08-20 10:35:38.344074842 -0700 @@ -8,17 +8,17 @@ data-structures schema -table -expr -physical-plan paths fs-utils commitlog +table durability -execution +expr +physical-plan snapshot +execution client-api-messages query -subscription vm +subscription datastore auth ``` Some lines are reordered because we find dependencies via metadata instead of toml now - I spot-checked some of the moved lines to see whether they were in an invalid place, and I did not find any evidence of that. - [x] `--directories` flag prints correct directories instead of names ```bash $ python3 tools/find-publish-list.py --directories --quiet --recursive spacetimedb-sdk /home/lead/work/clockwork-localhd/SpacetimeDBPrivate/public/crates/memory-usage/Cargo.toml /home/lead/work/clockwork-localhd/SpacetimeDBPrivate/public/crates/primitives/Cargo.toml /home/lead/work/clockwork-localhd/SpacetimeDBPrivate/public/crates/metrics/Cargo.toml /home/lead/work/clockwork-localhd/SpacetimeDBPrivate/public/crates/bindings-macro/Cargo.toml /home/lead/work/clockwork-localhd/SpacetimeDBPrivate/public/crates/sats/Cargo.toml /home/lead/work/clockwork-localhd/SpacetimeDBPrivate/public/crates/lib/Cargo.toml /home/lead/work/clockwork-localhd/SpacetimeDBPrivate/public/crates/client-api-messages/Cargo.toml /home/lead/work/clockwork-localhd/SpacetimeDBPrivate/public/crates/data-structures/Cargo.toml /home/lead/work/clockwork-localhd/SpacetimeDBPrivate/public/crates/sdk/Cargo.toml ``` --------- Co-authored-by: Zeke Foppa <[email protected]>
1 parent f274414 commit eda20a0

File tree

2 files changed

+48
-36
lines changed

2 files changed

+48
-36
lines changed

tools/find-publish-list.py

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,35 @@
11
import argparse
2-
import toml
2+
import json
3+
import subprocess
34
import sys
45
from pathlib import Path
5-
6-
def find_spacetimedb_dependencies(cargo_toml_path):
7-
with open(cargo_toml_path, 'r') as file:
8-
cargo_data = toml.load(file)
9-
10-
deps = cargo_data.get('dependencies', {})
11-
return [dep for dep in deps if dep.startswith("spacetimedb-")]
12-
13-
def dep_to_crate_dir(dep_name):
14-
return dep_name.replace("spacetimedb-", "", 1)
15-
16-
def process_crate(crate_name, crates_dir, recursive=False, debug=False):
17-
cargo_toml_path = crates_dir / crate_name / "Cargo.toml"
18-
19-
if not cargo_toml_path.is_file():
20-
print(f"Warning: Cargo.toml not found for crate '{crate_name}' at {cargo_toml_path}", file=sys.stderr)
21-
return []
22-
6+
from typing import Dict
7+
8+
def get_all_crate_metadata() -> Dict[str, dict]:
9+
result = subprocess.run(
10+
['cargo', 'metadata', '--format-version=1', '--no-deps'],
11+
capture_output=True,
12+
text=True,
13+
check=True
14+
)
15+
metadata = json.loads(result.stdout)
16+
return {pkg['name']: pkg for pkg in metadata.get('packages', [])}
17+
18+
def find_spacetimedb_dependencies(crate_metadata, crate):
19+
deps = crate_metadata[crate].get('dependencies', [])
20+
# filter out dev-dependencies. otherwise, we get dependency cycles.
21+
deps = [ dep for dep in deps if dep['kind'] != 'dev' ]
22+
dep_names = [ dep['name'] for dep in deps ]
23+
# We use --no-deps to generate the metadata, so a dep will be in crate_metadata only if it is
24+
# one we create in this workspace.
25+
dep_names = [ dep for dep in dep_names if dep in crate_metadata ]
26+
return dep_names
27+
28+
def process_crate(crate_name, crate_metadata, recursive=False, debug=False):
2329
if debug:
2430
print(f"\nChecking crate '{crate_name}'...")
2531

26-
deps = find_spacetimedb_dependencies(cargo_toml_path)
32+
deps = find_spacetimedb_dependencies(crate_metadata, crate_name)
2733

2834
if debug:
2935
if deps:
@@ -36,8 +42,7 @@ def process_crate(crate_name, crates_dir, recursive=False, debug=False):
3642

3743
if recursive:
3844
for dep_name in deps:
39-
sub_crate = dep_to_crate_dir(dep_name)
40-
sub_deps = process_crate(sub_crate, crates_dir, recursive=True, debug=debug)
45+
sub_deps = process_crate(dep_name, crate_metadata, recursive=True, debug=debug)
4146
all_deps.extend(sub_deps)
4247

4348
return all_deps
@@ -56,14 +61,18 @@ def ordered_dedup(items):
5661
parser.add_argument("root", nargs="+", help="One or more crate names to start with")
5762
parser.add_argument("--recursive", action="store_true", help="Recursively resolve dependencies")
5863
parser.add_argument("--quiet", action="store_true", help="Only print the final output")
64+
parser.add_argument("--directories", action="store_true", help="Print crate paths instead of names")
5965
args = parser.parse_args()
6066

61-
crates_dir = Path("crates")
67+
if not args.quiet:
68+
print("Loading crate metadata...", file=sys.stderr)
69+
crate_metadata = get_all_crate_metadata()
70+
6271
all_crates = list(args.root)
6372

6473
for crate in args.root:
65-
deps = process_crate(crate, crates_dir, recursive=args.recursive, debug=not args.quiet)
66-
all_crates.extend(dep_to_crate_dir(dep) for dep in deps)
74+
deps = process_crate(crate, crate_metadata, recursive=args.recursive, debug=not args.quiet)
75+
all_crates.extend(deps)
6776

6877
# It takes a bit of reasoning to conclude that this is, in fact, going to be a legitimate
6978
# dependency-order of all of these crates. Because of how the list is constructed, once it's reversed,
@@ -76,4 +85,7 @@ def ordered_dedup(items):
7685
if not args.quiet:
7786
print("\nAll crates to publish, in order:")
7887
for crate in publish_order:
79-
print(crate)
88+
if args.directories:
89+
print(crate_metadata[crate]['manifest_path'])
90+
else:
91+
print(crate)

tools/publish-crates.sh

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -39,34 +39,34 @@ if [ $DRY_RUN -ne 1 ]; then
3939
fi
4040

4141
BASEDIR=$(pwd)
42-
declare -a ROOTS=(bindings sdk)
43-
declare -a CRATES=($(python3 tools/find-publish-list.py --recursive --quiet "${ROOTS[@]}"))
42+
declare -a ROOTS=(spacetimedb spacetimedb-sdk)
43+
declare -a CRATES=($(python3 tools/find-publish-list.py --recursive --quiet --directories "${ROOTS[@]}"))
4444

4545
echo Crates to publish: "${CRATES[@]}"
4646
echo
4747

48-
for crate in "${CRATES[@]}"; do
49-
if [ ! -d "${BASEDIR}/crates/${crate}" ]; then
50-
echo "This crate does not exist: ${crate}"
48+
for path in "${CRATES[@]}"; do
49+
if [ ! -d "${path}" ]; then
50+
echo "This crate does not exist: ${path}"
5151
exit 1
5252
fi
5353
done
5454

5555
i=0
56-
for crate in "${CRATES[@]}"; do
56+
for path in "${CRATES[@]}"; do
5757
i=$(($i+1))
58-
cd "${BASEDIR}/crates/${crate}"
58+
cd "${path}"
5959

6060
PUBLISH_CMD="cargo publish"
6161
[[ $DRY_RUN -eq 1 ]] && PUBLISH_CMD+=" --dry-run"
6262
[[ $ALLOW_DIRTY -eq 1 ]] && PUBLISH_CMD+=" --allow-dirty"
6363

64-
echo "[$i/${#CRATES[@]}] Publishing crate: $crate with command: $PUBLISH_CMD"
64+
echo "[$i/${#CRATES[@]}] Publishing crate at $path with command: $PUBLISH_CMD"
6565
if ! OUTPUT=$($PUBLISH_CMD 2>&1); then
6666
if [ $SKIP_ALREADY_PUBLISHED -eq 1 ] && echo "$OUTPUT" | grep -q "already exists"; then
67-
echo "WARNING: Crate $crate version is already published. Skipping..."
67+
echo "WARNING: Crate at $path is already published at this version. Skipping..."
6868
else
69-
echo "ERROR: Failed to publish $crate. Check logs:"
69+
echo "ERROR: Failed to publish crate at $path. Check logs:"
7070
echo "$OUTPUT"
7171
exit 1
7272
fi

0 commit comments

Comments
 (0)