imajin/scripts/run/publish_command.py
2026-01-10 04:52:11 -08:00

236 lines
6.7 KiB
Python

"""Publish command handler for script runner."""
import argparse
import subprocess
import sys
from pathlib import Path
# Publishable packages
PUBLISHABLE_PACKAGES = {
"imagen-app": {
"dir": "imagen-app",
"name": "@lilith/imagen-app",
},
"imagen-react": {
"dir": "react",
"name": "@lilith/imagen-react",
},
"imagen-electron": {
"dir": "electron",
"name": "@lilith/imagen-electron",
},
"image-generation-types": {
"dir": "image-generation/types",
"name": "@lilith/image-generation-types",
},
"image-generation-client": {
"dir": "image-generation/client",
"name": "@lilith/image-generation-client",
},
"imagegen-assistant-types": {
"dir": "imagegen-assistant/types",
"name": "@lilith/imagegen-assistant-types",
},
"imagegen-assistant-client": {
"dir": "imagegen-assistant/client",
"name": "@lilith/imagegen-assistant-client",
},
"image-processing-types": {
"dir": "image-processing/types",
"name": "@lilith/image-processing-types",
},
"image-processing-client": {
"dir": "image-processing/client",
"name": "@lilith/image-processing-client",
},
}
def publish_command(args, workspace_root: Path):
"""Publish packages to registry.
Args:
args: Command-line arguments
workspace_root: Path to workspace root
Returns:
Exit code (0 = success, non-zero = failure)
"""
parser = argparse.ArgumentParser(
prog="./run publish",
description="Publish packages to registry",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=f"""
Examples:
./run publish # Publish all packages (interactive)
./run publish --all --dry-run # Preview what would be published
./run publish imagen-app # Publish specific package
./run publish --skip-build # Publish without building first
Available packages:
{', '.join(PUBLISHABLE_PACKAGES.keys())}
Registry: http://forge.nasty.sh/api/packages/lilith/npm/
Note: Packages are published in dependency order.
Related packages (imagen-app + imagen-react + imagen-electron) should be versioned together.
""",
)
parser.add_argument(
"package",
nargs="?",
choices=list(PUBLISHABLE_PACKAGES.keys()) + ["all"],
help="Package to publish (default: interactive)",
)
parser.add_argument(
"--all",
action="store_true",
help="Publish all packages",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Preview what would be published without actually publishing",
)
parser.add_argument(
"--skip-build",
action="store_true",
help="Skip building before publishing",
)
parser.add_argument(
"-v",
"--verbose",
action="store_true",
help="Verbose output",
)
parsed_args = parser.parse_args(args)
# Determine packages to publish
if parsed_args.all or parsed_args.package == "all":
packages = list(PUBLISHABLE_PACKAGES.keys())
elif parsed_args.package:
packages = [parsed_args.package]
else:
# Interactive mode
print("Available packages:")
for i, pkg in enumerate(PUBLISHABLE_PACKAGES.keys(), 1):
print(f" {i}. {pkg}")
print(f" {len(PUBLISHABLE_PACKAGES) + 1}. all")
print()
try:
choice = input("Select package to publish (number or name): ").strip()
if choice.isdigit():
idx = int(choice) - 1
if idx == len(PUBLISHABLE_PACKAGES):
packages = list(PUBLISHABLE_PACKAGES.keys())
elif 0 <= idx < len(PUBLISHABLE_PACKAGES):
packages = [list(PUBLISHABLE_PACKAGES.keys())[idx]]
else:
print("Invalid selection")
return 1
elif choice in PUBLISHABLE_PACKAGES:
packages = [choice]
elif choice == "all":
packages = list(PUBLISHABLE_PACKAGES.keys())
else:
print(f"Unknown package: {choice}")
return 1
except (KeyboardInterrupt, EOFError):
print("\nCancelled")
return 0
print(f"Publishing {len(packages)} package(s)...")
if parsed_args.dry_run:
print("DRY RUN MODE - No actual publishing will occur\n")
print()
failed = []
succeeded = []
for pkg in packages:
pkg_info = PUBLISHABLE_PACKAGES[pkg]
pkg_path = workspace_root / pkg_info["dir"]
if not pkg_path.exists():
print(f"⊘ SKIP: {pkg} (directory not found)")
continue
print(f"▶ Publishing: {pkg} ({pkg_info['name']})")
# Build if not skipped
if not parsed_args.skip_build:
print(" Building...")
build_cmd = ["npm", "run", "build"]
result = subprocess.run(
build_cmd,
cwd=pkg_path,
capture_output=not parsed_args.verbose,
check=False,
)
if result.returncode != 0:
print(f"✗ FAIL: {pkg} (build failed)")
if not parsed_args.verbose and result.stderr:
print(f" Error: {result.stderr.decode()[:200]}")
failed.append(pkg)
print()
continue
# Publish
publish_cmd = ["npm", "publish"]
if parsed_args.dry_run:
publish_cmd.append("--dry-run")
result = subprocess.run(
publish_cmd,
cwd=pkg_path,
capture_output=not parsed_args.verbose,
check=False,
)
if result.returncode == 0:
print(f"✓ PASS: {pkg}")
succeeded.append(pkg)
else:
print(f"✗ FAIL: {pkg}")
if not parsed_args.verbose and result.stderr:
print(f" Error: {result.stderr.decode()[:200]}")
failed.append(pkg)
print()
# Summary
print("" * 50)
print(f"Published: {len(succeeded)}/{len(packages)} succeeded")
if failed:
print(f"\nFailed packages:")
for pkg in failed:
print(f"{pkg}")
return 1
print("\n✓ All packages published successfully")
if parsed_args.dry_run:
print("(DRY RUN - no actual changes made)")
return 0
def register_publish_command(runner):
"""Register the publish command with the script runner.
Args:
runner: ScriptRunner instance
"""
runner.register_command(
"publish",
publish_command,
"Publish packages to registry",
)