MCPcopy
hub / github.com/github/spec-kit / install_shared_infra

Function install_shared_infra

src/specify_cli/shared_infra.py:353–667  ·  view source on GitHub ↗

Install shared scripts and templates into *project_path*. When ``refresh_managed`` is True, files whose on-disk hash still matches the previously recorded manifest hash are overwritten with the bundled version. Files whose hash diverges are treated as user customizations and preserv

(
    project_path: Path,
    script_type: str,
    *,
    version: str,
    core_pack: Path | None,
    repo_root: Path,
    console: Any,
    force: bool = False,
    invoke_separator: str = ".",
    refresh_managed: bool = False,
    refresh_hint: str | None = None,
)

Source from the content-addressed store, hash-verified

351
352
353def install_shared_infra(
354 project_path: Path,
355 script_type: str,
356 *,
357 version: str,
358 core_pack: Path | None,
359 repo_root: Path,
360 console: Any,
361 force: bool = False,
362 invoke_separator: str = ".",
363 refresh_managed: bool = False,
364 refresh_hint: str | None = None,
365) -> bool:
366 """Install shared scripts and templates into *project_path*.
367
368 When ``refresh_managed`` is True, files whose on-disk hash still matches
369 the previously recorded manifest hash are overwritten with the bundled
370 version. Files whose hash diverges are treated as user customizations and
371 preserved with a warning. ``force=True`` overwrites every regular file
372 (symlinks and symlinked-parent destinations are always preserved with a
373 warning — the safe-destination check refuses to follow them so writes
374 cannot escape the project root). ``refresh_hint`` is shown after the
375 customization warning to tell the user which flag would overwrite their
376 customizations.
377 """
378 from .integrations.manifest import _sha256, _validate_rel_path
379
380 manifest = load_speckit_manifest(project_path, version=version, console=console)
381 prior_hashes = dict(manifest.files)
382
383 def _is_managed(rel: str, dst: Path) -> bool:
384 expected = prior_hashes.get(rel)
385 if not expected or not dst.is_file() or dst.is_symlink():
386 return False
387 if manifest.is_recovered(rel):
388 return False
389 try:
390 return _sha256(dst) == expected
391 except OSError:
392 return False
393
394 skipped_files: list[str] = []
395 preserved_user_files: list[str] = []
396 symlinked_files: list[str] = []
397 planned_copies: list[tuple[Path, str, bytes, int]] = []
398 planned_templates: list[tuple[Path, str, str]] = []
399 # Track every shared path the current bundle produces so we can detect
400 # manifest entries the core no longer ships (stale-script cleanup, #3076).
401 seen_rels: set[str] = set()
402 scripts_scanned = False
403 variant_dir = "bash" if script_type == "sh" else "powershell"
404
405 def _decide_overwrite(rel: str, dst: Path) -> tuple[bool, str | None]:
406 """Return (write, bucket) where bucket is 'skip', 'preserved', or None."""
407 if not dst.exists():
408 return True, None
409 if force:
410 return True, None

Calls 15

load_speckit_manifestFunction · 0.85
shared_scripts_sourceFunction · 0.85
_ensure_or_bucket_dirFunction · 0.85
_safe_dest_or_bucketFunction · 0.85
_decide_overwriteFunction · 0.85
shared_templates_sourceFunction · 0.85
_write_shared_bytesFunction · 0.85
_write_shared_textFunction · 0.85
_validate_rel_pathFunction · 0.85
_is_managedFunction · 0.85
record_existingMethod · 0.80