feat: use XDG directory standards

Signed-off-by: Mustafa Elbehery <melbeher@redhat.com>
This commit is contained in:
Mustafa Elbehery 2025-07-03 18:48:53 +02:00
parent 9736f096f6
commit 407c3e3bad
50 changed files with 5611 additions and 508 deletions

View file

@ -0,0 +1,168 @@
# Copyright (c) Meta Platforms, Inc. and affiliates.
# All rights reserved.
#
# This source code is licensed under the terms described in the LICENSE file in
# the root directory of this source tree.
import argparse
import shutil
import sys
from pathlib import Path
from llama_stack.distribution.utils.xdg_utils import (
get_llama_stack_config_dir,
get_llama_stack_data_dir,
get_llama_stack_state_dir,
)
from .subcommand import Subcommand
class MigrateXDG(Subcommand):
"""CLI command for migrating from legacy ~/.llama to XDG-compliant directories."""
def __init__(self, subparsers: argparse._SubParsersAction):
super().__init__()
self.parser = subparsers.add_parser(
"migrate-xdg",
prog="llama migrate-xdg",
description="Migrate from legacy ~/.llama to XDG-compliant directories",
formatter_class=argparse.RawTextHelpFormatter,
)
self.parser.add_argument(
"--dry-run", action="store_true", help="Show what would be done without actually moving files"
)
self.parser.set_defaults(func=self._run_migrate_xdg_cmd)
@staticmethod
def create(subparsers: argparse._SubParsersAction):
return MigrateXDG(subparsers)
def _run_migrate_xdg_cmd(self, args: argparse.Namespace) -> None:
"""Run the migrate-xdg command."""
if not migrate_to_xdg(dry_run=args.dry_run):
sys.exit(1)
def migrate_to_xdg(dry_run: bool = False) -> bool:
"""
Migrate from legacy ~/.llama to XDG-compliant directories.
Args:
dry_run: If True, only show what would be done without actually moving files
Returns:
bool: True if migration was successful or not needed, False otherwise
"""
legacy_path = Path.home() / ".llama"
if not legacy_path.exists():
print("No legacy ~/.llama directory found. Nothing to migrate.")
return True
# Check if we're already using XDG paths
config_dir = get_llama_stack_config_dir()
data_dir = get_llama_stack_data_dir()
state_dir = get_llama_stack_state_dir()
if str(config_dir) == str(legacy_path):
print("Already using legacy directory. No migration needed.")
return True
print(f"Found legacy directory at: {legacy_path}")
print("Will migrate to XDG-compliant directories:")
print(f" Config: {config_dir}")
print(f" Data: {data_dir}")
print(f" State: {state_dir}")
print()
# Define migration mapping
migrations = [
# (source_subdir, target_base_dir, description)
("distributions", config_dir, "Distribution configurations"),
("providers.d", config_dir, "External provider configurations"),
("checkpoints", data_dir, "Model checkpoints"),
("runtime", state_dir, "Runtime state files"),
]
# Check what needs to be migrated
items_to_migrate = []
for subdir, target_base, description in migrations:
source_path = legacy_path / subdir
if source_path.exists():
target_path = target_base / subdir
items_to_migrate.append((source_path, target_path, description))
if not items_to_migrate:
print("No items found to migrate.")
return True
print("Items to migrate:")
for source_path, target_path, description in items_to_migrate:
print(f" {description}: {source_path} -> {target_path}")
if dry_run:
print("\nDry run mode: No files will be moved.")
return True
# Ask for confirmation
response = input("\nDo you want to proceed with the migration? (y/N): ")
if response.lower() not in ["y", "yes"]:
print("Migration cancelled.")
return False
# Perform the migration
print("\nMigrating files...")
for source_path, target_path, description in items_to_migrate:
try:
# Create target directory if it doesn't exist
target_path.parent.mkdir(parents=True, exist_ok=True)
# Check if target already exists
if target_path.exists():
print(f" Warning: Target already exists: {target_path}")
print(f" Skipping {description}")
continue
# Move the directory
shutil.move(str(source_path), str(target_path))
print(f" Moved {description}: {source_path} -> {target_path}")
except Exception as e:
print(f" Error migrating {description}: {e}")
return False
# Check if legacy directory is now empty (except for hidden files)
remaining_items = [item for item in legacy_path.iterdir() if not item.name.startswith(".")]
if not remaining_items:
print(f"\nMigration complete! Legacy directory {legacy_path} is now empty.")
response = input("Remove empty legacy directory? (y/N): ")
if response.lower() in ["y", "yes"]:
try:
shutil.rmtree(legacy_path)
print(f"Removed empty legacy directory: {legacy_path}")
except Exception as e:
print(f"Could not remove legacy directory: {e}")
else:
print(f"\nMigration complete! Some items remain in legacy directory: {remaining_items}")
print("\nMigration successful!")
print("You may need to update any custom scripts or configurations that reference the old paths.")
return True
def main():
parser = argparse.ArgumentParser(description="Migrate from legacy ~/.llama to XDG-compliant directories")
parser.add_argument("--dry-run", action="store_true", help="Show what would be done without actually moving files")
args = parser.parse_args()
if not migrate_to_xdg(dry_run=args.dry_run):
sys.exit(1)
if __name__ == "__main__":
main()