CI - Do some basic checks that crates are publishable (#2660)

Co-authored-by: Zeke Foppa <bfops@users.noreply.github.com>
This commit is contained in:
Zeke Foppa
2025-04-23 10:08:43 -07:00
committed by GitHub
parent 69ec80331f
commit 118e59de14
8 changed files with 106 additions and 10 deletions
+24
View File
@@ -219,6 +219,30 @@ jobs:
- name: Run bindgen tests
run: cargo test -p spacetimedb-cli
publish_checks:
name: Check that packages are publishable
runs-on: ubuntu-latest
permissions: read-all
steps:
- uses: actions/checkout@v3
- name: Set up Python env
run: |
test -d venv || python3 -m venv venv
venv/bin/pip3 install argparse toml
- name: Run checks
run: |
FAILED=0
# This definition of ROOTS and invocation of find-publish-list.py is copied from publish-crates.sh
ROOTS=(bindings sdk cli standalone)
for crate in $(venv/bin/python3 tools/find-publish-list.py --recursive --quiet "${ROOTS[@]}"); do
if ! venv/bin/python3 tools/crate-publish-checks.py "crates/$crate"; then
FAILED=$(( $FAILED + 1 ))
fi
done
if [ $FAILED -gt 0 ]; then
exit 1
fi
update:
name: Test spacetimedb-update flow
permissions: read-all
+1 -1
View File
@@ -132,7 +132,7 @@ perfmap = []
[dev-dependencies]
spacetimedb-lib = { path = "../lib", features = ["proptest"] }
spacetimedb-sats = { path = "../sats", features = ["proptest"] }
spacetimedb-commitlog = { workspace = true, features = ["test"] }
spacetimedb-commitlog = { path = "../commitlog", features = ["test"] }
criterion.workspace = true
# Also as dev-dependencies for use in _this_ crate's tests.
+2 -2
View File
@@ -20,5 +20,5 @@ spacetimedb-sql-parser.workspace = true
[dev-dependencies]
pretty_assertions.workspace = true
spacetimedb = { workspace = true, features = ["unstable"] }
spacetimedb-lib.workspace = true
spacetimedb = { path = "../bindings", features = ["unstable"] }
spacetimedb-lib = { path = "../lib" }
+1 -1
View File
@@ -48,7 +48,7 @@ proptest = { workspace = true, optional = true }
proptest-derive = { workspace = true, optional = true }
[dev-dependencies]
spacetimedb-sats = { workspace = true, features = ["test"] }
spacetimedb-sats = { path = "../sats", features = ["test"] }
bytes.workspace = true
serde_json.workspace = true
insta.workspace = true
+2 -2
View File
@@ -31,10 +31,10 @@ enum-as-inner.workspace = true
enum-map.workspace = true
[dev-dependencies]
spacetimedb-lib = { workspace = true, features = ["test"] }
spacetimedb-lib = { path = "../lib", features = ["test"] }
# these are circular dependencies, but only in tests, so it's fine
spacetimedb-testing = { path = "../testing" }
spacetimedb-cli.workspace = true
spacetimedb-cli = { path = "../cli" }
proptest.workspace = true
serial_test.workspace = true
+2 -2
View File
@@ -28,8 +28,8 @@ tokio-util = { workspace = true, features = ["io"] }
zstd-framed.workspace = true
[dev-dependencies]
spacetimedb-core = { workspace = true, features = ["test"] }
spacetimedb-schema.workspace = true
spacetimedb-core = { path = "../core", features = ["test"] }
spacetimedb-schema = { path = "../schema" }
anyhow.workspace = true
env_logger.workspace = true
+2 -2
View File
@@ -50,8 +50,8 @@ proptest = { workspace = true, optional = true }
proptest-derive = { workspace = true, optional = true }
[dev-dependencies]
spacetimedb-schema = { workspace = true, features = ["test"] }
spacetimedb-sats = { workspace = true, features = ["proptest"] }
spacetimedb-schema = { path = "../schema", features = ["test"] }
spacetimedb-sats = { path = "../sats", features = ["proptest"] }
criterion.workspace = true
proptest.workspace = true
proptest-derive.workspace = true
+72
View File
@@ -0,0 +1,72 @@
import toml
import argparse
import sys
from pathlib import Path
def find_non_path_spacetimedb_deps(dev_deps):
non_path_spacetimedb = []
for name, details in dev_deps.items():
if not name.startswith("spacetimedb"):
continue
if isinstance(details, dict):
if "path" not in details:
non_path_spacetimedb.append(name)
else:
# String dependency = version from crates.io
non_path_spacetimedb.append(name)
return non_path_spacetimedb
def check_cargo_metadata(data):
package = data.get("package", {})
missing_fields = []
# Accept either license OR license-file
if "license" not in package and "license-file" not in package:
missing_fields.append("license/license-file")
if "description" not in package:
missing_fields.append("description")
return missing_fields
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Check Cargo.toml for metadata and dev-dependencies.")
parser.add_argument("directory", help="Directory to search for Cargo.toml")
args = parser.parse_args()
cargo_toml_path = Path(args.directory) / "Cargo.toml"
try:
if not cargo_toml_path.exists():
raise FileNotFoundError(f"{cargo_toml_path} not found.")
data = toml.load(cargo_toml_path)
# Check dev-dependencies
dev_deps = data.get("dev-dependencies", {})
bad_deps = find_non_path_spacetimedb_deps(dev_deps)
# Check license/license-file and description
missing_fields = check_cargo_metadata(data)
exit_code = 0
if bad_deps:
print(f"❌ These dev-dependencies in {cargo_toml_path} must be converted to use `path` in order to not impede crate publishing:")
for dep in bad_deps:
print(f" - {dep}")
exit_code = 1
if missing_fields:
print(f"❌ Missing required fields in [package] of {cargo_toml_path}: {', '.join(missing_fields)}")
exit_code = 1
if exit_code == 0:
print(f"{cargo_toml_path} passed all checks.")
sys.exit(exit_code)
except Exception as e:
print(f"⚠️ Error: {e}")
sys.exit(2)