chore(extensions): clean up backend entrypoints and file globs (#38360)

This commit is contained in:
Ville Brofeldt
2026-03-03 09:45:35 -08:00
committed by GitHub
parent 016417f793
commit c35bf344a9
11 changed files with 426 additions and 117 deletions

View File

@@ -162,8 +162,13 @@ def build_manifest(cwd: Path, remote_entry: str | None) -> Manifest:
)
backend: ManifestBackend | None = None
if extension.backend and extension.backend.entryPoints:
backend = ManifestBackend(entryPoints=extension.backend.entryPoints)
backend_dir = cwd / "backend"
if backend_dir.exists():
# Generate conventional entry point
publisher_snake = kebab_to_snake_case(extension.publisher)
name_snake = kebab_to_snake_case(extension.name)
entrypoint = f"superset_extensions.{publisher_snake}.{name_snake}.entrypoint"
backend = ManifestBackend(entrypoint=entrypoint)
return Manifest(
id=composite_id,
@@ -217,17 +222,34 @@ def copy_frontend_dist(cwd: Path) -> str:
def copy_backend_files(cwd: Path) -> None:
"""Copy backend files based on pyproject.toml build configuration (validation already passed)."""
dist_dir = cwd / "dist"
extension = read_json(cwd / "extension.json")
if not extension:
click.secho("❌ No extension.json file found.", err=True, fg="red")
sys.exit(1)
backend_dir = cwd / "backend"
for pat in extension.get("backend", {}).get("files", []):
for f in cwd.glob(pat):
# Read build config from pyproject.toml
pyproject = read_toml(backend_dir / "pyproject.toml")
assert pyproject
build_config = (
pyproject.get("tool", {}).get("apache_superset_extensions", {}).get("build", {})
)
include_patterns = build_config.get("include", [])
exclude_patterns = build_config.get("exclude", [])
# Process include patterns
for pattern in include_patterns:
for f in backend_dir.glob(pattern):
if not f.is_file():
continue
tgt = dist_dir / f.relative_to(cwd)
# Check exclude patterns
relative_path = f.relative_to(backend_dir)
should_exclude = any(
relative_path.match(excl_pattern) for excl_pattern in exclude_patterns
)
if should_exclude:
continue
tgt = dist_dir / "backend" / relative_path
tgt.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(f, tgt)
@@ -272,6 +294,89 @@ def app() -> None:
def validate() -> None:
validate_npm()
cwd = Path.cwd()
# Validate extension.json exists and is valid
extension_data = read_json(cwd / "extension.json")
if not extension_data:
click.secho("❌ extension.json not found.", err=True, fg="red")
sys.exit(1)
try:
extension = ExtensionConfig.model_validate(extension_data)
except Exception as e:
click.secho(f"❌ Invalid extension.json: {e}", err=True, fg="red")
sys.exit(1)
# Validate conventional backend structure if backend directory exists
backend_dir = cwd / "backend"
if backend_dir.exists():
# Check for pyproject.toml
pyproject_path = backend_dir / "pyproject.toml"
if not pyproject_path.exists():
click.secho(
"❌ Backend directory exists but pyproject.toml not found",
err=True,
fg="red",
)
sys.exit(1)
# Validate pyproject.toml has build configuration
pyproject = read_toml(pyproject_path)
if not pyproject:
click.secho("❌ Failed to read backend pyproject.toml", err=True, fg="red")
sys.exit(1)
build_config = (
pyproject.get("tool", {})
.get("apache_superset_extensions", {})
.get("build", {})
)
if not build_config.get("include"):
click.secho(
"❌ Missing [tool.apache_superset_extensions.build] section with 'include' patterns in pyproject.toml",
err=True,
fg="red",
)
sys.exit(1)
# Check conventional backend entry point
publisher_snake = kebab_to_snake_case(extension.publisher)
name_snake = kebab_to_snake_case(extension.name)
expected_entry_file = (
backend_dir
/ "src"
/ "superset_extensions"
/ publisher_snake
/ name_snake
/ "entrypoint.py"
)
if not expected_entry_file.exists():
click.secho(
f"❌ Backend entry point not found at expected location: {expected_entry_file.relative_to(cwd)}",
err=True,
fg="red",
)
click.secho(
f" Convention requires: backend/src/superset_extensions/{publisher_snake}/{name_snake}/entrypoint.py",
fg="yellow",
)
sys.exit(1)
# Validate conventional frontend entry point if frontend directory exists
frontend_dir = cwd / "frontend"
if frontend_dir.exists():
expected_frontend_entry = frontend_dir / "src" / "index.tsx"
if not expected_frontend_entry.exists():
click.secho(
f"❌ Frontend entry point not found at expected location: {expected_frontend_entry.relative_to(cwd)}",
err=True,
fg="red",
)
click.secho(" Convention requires: frontend/src/index.tsx", fg="yellow")
sys.exit(1)
click.secho("✅ Validation successful", fg="green")

View File

@@ -2,3 +2,10 @@
name = "{{ backend_package }}"
version = "{{ version }}"
license = "{{ license }}"
[tool.apache_superset_extensions.build]
# Files to include in the extension build/bundle
include = [
"src/{{ backend_path|replace('.', '/') }}/**/*.py",
]
exclude = []

View File

@@ -4,11 +4,5 @@
"displayName": "{{ display_name }}",
"version": "{{ version }}",
"license": "{{ license }}",
{% if include_backend -%}
"backend": {
"entryPoints": ["{{ backend_entry }}"],
"files": ["backend/src/{{ backend_path|replace('.', '/') }}/**/*.py"]
},
{% endif -%}
"permissions": []
}