test..
This commit is contained in:
@@ -65,6 +65,141 @@ jobs:
|
||||
--no-git-commit \
|
||||
--yes
|
||||
|
||||
- name: Preflight publish checks
|
||||
run: |
|
||||
python3 - <<'PY'
|
||||
import tomllib
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
root = pathlib.Path(".")
|
||||
ws = tomllib.loads((root / "Cargo.toml").read_text())
|
||||
members = ws["workspace"]["members"]
|
||||
errors = []
|
||||
workspace_members = {}
|
||||
|
||||
for member in members:
|
||||
manifest = root / member / "Cargo.toml"
|
||||
if not manifest.exists():
|
||||
continue
|
||||
|
||||
data = tomllib.loads(manifest.read_text())
|
||||
package = data.get("package")
|
||||
if not package:
|
||||
continue
|
||||
|
||||
publish = package.get("publish", True)
|
||||
publishable = publish is not False
|
||||
workspace_members[package.get("name", member)] = {
|
||||
"member": member,
|
||||
"manifest": manifest,
|
||||
"publishable": publishable,
|
||||
"data": data,
|
||||
}
|
||||
|
||||
def dependency_sections(crate_data):
|
||||
for section in ("dependencies", "dev-dependencies", "build-dependencies"):
|
||||
deps = crate_data.get(section, {})
|
||||
if isinstance(deps, dict):
|
||||
yield section, deps
|
||||
|
||||
target = crate_data.get("target", {})
|
||||
if isinstance(target, dict):
|
||||
for target_name, target_data in target.items():
|
||||
if not isinstance(target_data, dict):
|
||||
continue
|
||||
for section in ("dependencies", "dev-dependencies", "build-dependencies"):
|
||||
deps = target_data.get(section, {})
|
||||
if isinstance(deps, dict):
|
||||
yield f"target.{target_name}.{section}", deps
|
||||
|
||||
def resolve_workspace_dep_name(dep_name, spec, manifest):
|
||||
if not isinstance(spec, dict):
|
||||
return None
|
||||
|
||||
if spec.get("workspace") is True:
|
||||
return dep_name
|
||||
|
||||
candidate = spec.get("package", dep_name)
|
||||
if candidate in workspace_members:
|
||||
return candidate
|
||||
|
||||
dep_path = spec.get("path")
|
||||
if not dep_path:
|
||||
return None
|
||||
|
||||
dep_manifest = (manifest.parent / dep_path / "Cargo.toml").resolve()
|
||||
if not dep_manifest.exists():
|
||||
return None
|
||||
|
||||
dep_data = tomllib.loads(dep_manifest.read_text())
|
||||
dep_package = dep_data.get("package", {})
|
||||
resolved_name = dep_package.get("name")
|
||||
if resolved_name in workspace_members:
|
||||
return resolved_name
|
||||
return None
|
||||
|
||||
for member in members:
|
||||
manifest = root / member / "Cargo.toml"
|
||||
if not manifest.exists():
|
||||
continue
|
||||
|
||||
data = tomllib.loads(manifest.read_text())
|
||||
package = data.get("package")
|
||||
if not package:
|
||||
continue
|
||||
if package.get("publish", True) is False:
|
||||
continue
|
||||
|
||||
crate_name = package.get("name", member)
|
||||
|
||||
for field in ("description", "license", "repository"):
|
||||
value = package.get(field)
|
||||
has_value = (
|
||||
(isinstance(value, str) and bool(value.strip()))
|
||||
or (isinstance(value, dict) and value.get("workspace") is True)
|
||||
)
|
||||
if not has_value:
|
||||
errors.append(
|
||||
f"{member}: package '{crate_name}' missing required field '{field}'"
|
||||
)
|
||||
|
||||
for section, dependencies in dependency_sections(data):
|
||||
for dep_name, spec in dependencies.items():
|
||||
if isinstance(spec, dict) and "path" in spec:
|
||||
if spec.get("workspace") is True:
|
||||
continue
|
||||
if "version" not in spec:
|
||||
errors.append(
|
||||
f"{member}: {section} '{dep_name}' uses path dependency without version ({spec['path']})"
|
||||
)
|
||||
|
||||
resolved_dep = resolve_workspace_dep_name(dep_name, spec, manifest)
|
||||
if (
|
||||
resolved_dep
|
||||
and resolved_dep in workspace_members
|
||||
and not workspace_members[resolved_dep]["publishable"]
|
||||
):
|
||||
errors.append(
|
||||
f"{member}: package '{crate_name}' ({section}) depends on non-publishable workspace crate '{resolved_dep}'"
|
||||
)
|
||||
|
||||
workspace_deps = ws.get("workspace", {}).get("dependencies", {})
|
||||
for dep_name, spec in workspace_deps.items():
|
||||
if isinstance(spec, dict) and "path" in spec and "version" not in spec:
|
||||
errors.append(
|
||||
f"workspace.dependencies: '{dep_name}' has path but no version"
|
||||
)
|
||||
|
||||
if errors:
|
||||
print("Preflight checks failed:")
|
||||
for err in errors:
|
||||
print(f"- {err}")
|
||||
sys.exit(1)
|
||||
|
||||
print("Preflight checks passed.")
|
||||
PY
|
||||
|
||||
# Dry run may show cascading dependency errors because packages aren't
|
||||
# actually uploaded - these are expected and ignored. We check for real
|
||||
# errors like packaging failures, missing metadata, or invalid Cargo.toml.
|
||||
|
||||
Reference in New Issue
Block a user