Upgrade an integration by reinstalling with diff-aware file handling. Compares manifest hashes to detect locally modified files and blocks the upgrade unless --force is used.
(
key: str | None = typer.Argument(None, help="Integration key to upgrade (default: current integration)"),
force: bool = typer.Option(False, "--force", help="Force upgrade even if files are modified"),
script: str | None = typer.Option(None, "--script", help="Script type: sh or ps (default: from init-options.json or platform default)"),
integration_options: str | None = typer.Option(None, "--integration-options", help="Options for the integration"),
)
| 334 | |
| 335 | @integration_app.command("upgrade") |
| 336 | def integration_upgrade( |
| 337 | key: str | None = typer.Argument(None, help="Integration key to upgrade (default: current integration)"), |
| 338 | force: bool = typer.Option(False, "--force", help="Force upgrade even if files are modified"), |
| 339 | script: str | None = typer.Option(None, "--script", help="Script type: sh or ps (default: from init-options.json or platform default)"), |
| 340 | integration_options: str | None = typer.Option(None, "--integration-options", help="Options for the integration"), |
| 341 | ): |
| 342 | """Upgrade an integration by reinstalling with diff-aware file handling. |
| 343 | |
| 344 | Compares manifest hashes to detect locally modified files and |
| 345 | blocks the upgrade unless --force is used. |
| 346 | """ |
| 347 | from . import get_integration |
| 348 | from .manifest import IntegrationManifest |
| 349 | from .. import _require_specify_project, _install_shared_infra_or_exit, _install_shared_infra |
| 350 | |
| 351 | project_root = _require_specify_project() |
| 352 | current = _read_integration_json(project_root) |
| 353 | installed_key = _default_integration_key(current) |
| 354 | installed_keys = _installed_integration_keys(current) |
| 355 | |
| 356 | if key is None: |
| 357 | if not installed_key: |
| 358 | console.print("[yellow]No integration is currently installed.[/yellow]") |
| 359 | raise typer.Exit(0) |
| 360 | key = installed_key |
| 361 | |
| 362 | if key not in installed_keys: |
| 363 | console.print(f"[red]Error:[/red] Integration '{key}' is not installed.") |
| 364 | raise typer.Exit(1) |
| 365 | |
| 366 | integration = get_integration(key) |
| 367 | if integration is None: |
| 368 | console.print(f"[red]Error:[/red] Unknown integration '{key}'") |
| 369 | raise typer.Exit(1) |
| 370 | |
| 371 | manifest_path = project_root / ".specify" / "integrations" / f"{key}.manifest.json" |
| 372 | if not manifest_path.exists(): |
| 373 | console.print(f"[yellow]No manifest found for integration '{key}'. Nothing to upgrade.[/yellow]") |
| 374 | console.print(f"Run [cyan]specify integration install {key}[/cyan] to perform a fresh install.") |
| 375 | raise typer.Exit(0) |
| 376 | |
| 377 | try: |
| 378 | old_manifest = IntegrationManifest.load(key, project_root) |
| 379 | except _MANIFEST_READ_ERRORS as exc: |
| 380 | console.print(f"[red]Error:[/red] Integration manifest for '{key}' is unreadable: {exc}") |
| 381 | raise typer.Exit(1) |
| 382 | |
| 383 | # Detect modified files via manifest hashes |
| 384 | modified = old_manifest.check_modified() |
| 385 | if modified and not force: |
| 386 | console.print(f"[yellow]⚠[/yellow] {len(modified)} file(s) have been modified since installation:") |
| 387 | for rel in modified: |
| 388 | console.print(f" {rel}") |
| 389 | console.print("\nUse [cyan]--force[/cyan] to overwrite modified files, or resolve manually.") |
| 390 | raise typer.Exit(1) |
| 391 | |
| 392 | selected_script = _resolve_integration_script_type(project_root, current, key, script) |
| 393 |
nothing calls this directly
no test coverage detected