Source code for deploy_tools.deploy

import logging
import shutil
from pathlib import Path

from .layout import Layout
from .models.changes import DeploymentChanges
from .models.module import Release
from .modulefile import (
    apply_default_versions,
    deprecate_modulefile_link,
    restore_modulefile_link,
)

logger = logging.getLogger(__name__)

MODULE_VERSIONS_GLOB = "[!.]*"


[docs] def deploy_changes(changes: DeploymentChanges, layout: Layout) -> None: """Perform the Deploy step for the specified changes.""" logger.debug("Deploying changes to: %s", layout.deployment_root) release_changes = changes.release_changes logger.debug("Removing deleted modules: %d", len(release_changes.to_remove)) _remove_releases(release_changes.to_remove, layout) logger.debug("Deploying new releases: %d", len(release_changes.to_add)) _deploy_new_releases(release_changes.to_add, layout) logger.debug("Deploying updated releases: %d", len(release_changes.to_update)) _deploy_releases(release_changes.to_update, layout, exist_ok=True) logger.debug("Deprecating releases: %d", len(release_changes.to_deprecate)) _deprecate_releases(release_changes.to_deprecate, layout) logger.debug("Restoring releases %d", len(release_changes.to_restore)) _restore_releases(release_changes.to_restore, layout) logger.debug("Applying default versions") apply_default_versions(changes.default_versions, layout) logger.debug("Clearing up empty folders") _remove_name_folders( release_changes.to_deprecate, release_changes.to_restore, release_changes.to_remove, layout, )
def _remove_releases(to_remove: list[Release], layout: Layout) -> None: """Remove the given modules from the deployment area.""" for release in to_remove: name = release.module.name version = release.module.version deprecated = release.deprecated _remove_deployed_module(name, version, layout, from_deprecated=deprecated) def _deploy_new_releases(to_add: list[Release], layout: Layout) -> None: """Deploy modules from the provided list.""" _deploy_releases(to_add, layout) for release in to_add: name = release.module.name version = release.module.version deprecated = release.deprecated modulefile = layout.get_modulefile(name, version) mf_link = layout.get_modulefile_link(name, version, from_deprecated=deprecated) mf_link.parent.mkdir(parents=True, exist_ok=True) mf_link.symlink_to(modulefile) def _deploy_releases( to_deploy: list[Release], layout: Layout, exist_ok: bool = False ) -> None: build_layout = layout.build_layout for release in to_deploy: module = release.module built_module_folder = build_layout.get_module_folder( module.name, module.version ) final_module_folder = layout.get_module_folder(module.name, module.version) if exist_ok and final_module_folder.exists(): shutil.rmtree(final_module_folder) final_module_folder.parent.mkdir(parents=True, exist_ok=True) built_module_folder.rename(final_module_folder) def _deprecate_releases(to_deprecate: list[Release], layout: Layout) -> None: """Deprecate a list of modules. For each module, this will move the modulefile link to the 'deprecated' directory under the Deployment Area. If the user's MODULEPATH is set to include the 'deprecated/modulefiles' directory, all modules should be available as before. """ for release in to_deprecate: deprecate_modulefile_link(release.module.name, release.module.version, layout) def _remove_deployed_module( name: str, version: str, layout: Layout, from_deprecated: bool = False ) -> None: mf_link = layout.get_modulefile_link(name, version, from_deprecated) mf_link.unlink() _remove_module(name, version, layout) def _remove_module(name: str, version: str, layout: Layout) -> None: module_folder = layout.get_module_folder(name, version) if module_folder.exists(): shutil.rmtree(module_folder) def _restore_releases(to_restore: list[Release], layout: Layout) -> None: """Restore a previously deprecated module.""" for release in to_restore: restore_modulefile_link(release.module.name, release.module.version, layout) def _remove_name_folders( deprecated: list[Release], restored: list[Release], removed: list[Release], layout: Layout, ) -> None: """Remove module name folders where all versions have been removed. Several types of path are of the form <root>/name/version. When removing version files in operations such as deprecation, if all versions are removed the parent folder (called name folder here) will still exist. This function is designed to remove them. Note that the non-deprecated modulefile name folder will not be empty, as the default version file will still exist. """ for release in deprecated: _delete_modulefiles_name_folder(layout, release.module.name) for release in restored: _delete_modulefiles_name_folder(layout, release.module.name, True) for release in removed: _delete_modulefiles_name_folder(layout, release.module.name, release.deprecated) _delete_name_folder(release.module.name, layout.modules_root) def _delete_modulefiles_name_folder( layout: Layout, name: str, from_deprecated: bool = False ) -> None: modulefiles_name_path = layout.get_modulefiles_root(from_deprecated) / name # Handle several versions of the same module being processed together if not modulefiles_name_path.exists(): return # Ignore the default version file when checking for existing modulefile links if next(modulefiles_name_path.glob(MODULE_VERSIONS_GLOB), None) is None: shutil.rmtree(modulefiles_name_path) def _delete_name_folder(name: str, area_root: Path) -> None: name_folder = area_root / name try: name_folder.rmdir() except OSError: pass