diff --git a/scone/default/recipes/filesystem.py b/scone/default/recipes/filesystem.py index ee9fb93..257c04e 100644 --- a/scone/default/recipes/filesystem.py +++ b/scone/default/recipes/filesystem.py @@ -25,6 +25,7 @@ from scone.default.utensils.basic_utensils import ( Chmod, Chown, MakeDirectory, + MakeSymlink, SimpleExec, Stat, ) @@ -133,6 +134,43 @@ class EnsureDirectory(Recipe): k.get_dependency_tracker() +class EnsureSymlink(Recipe): + """ + Makes a symbolic link. + """ + + _NAME = "symlink" + + def __init__(self, recipe_context: RecipeContext, args: dict, head): + super().__init__(recipe_context, args, head) + + self.path = check_type(args.get("path"), str) + self.target = check_type(args.get("target"), str) + self.is_dir = check_type(args.get("is_dir", False), bool) + self.checked = check_type(args.get("checked", True), bool) + + # user and permissions don't make sense for symlinks + + def prepare(self, preparation: Preparation, head: "Head"): + super().prepare(preparation, head) + path = Path(self.path) + + preparation.needs("directory", str(path.parent)) + + kind = "directory" if self.is_dir else "file" + + preparation.provides(kind, str(path)) + + if self.checked: + preparation.needs(kind, str(Path(path, self.target))) + + async def cook(self, k: Kitchen): + await k.ut0(MakeSymlink(self.path, self.target)) + + # mark as tracked. + k.get_dependency_tracker() + + class ExtractTar(Recipe): """ Extracts a tar archive, expecting to get at least some files. diff --git a/scone/default/utensils/basic_utensils.py b/scone/default/utensils/basic_utensils.py index 690a7ed..5bb820e 100644 --- a/scone/default/utensils/basic_utensils.py +++ b/scone/default/utensils/basic_utensils.py @@ -63,6 +63,15 @@ class MakeDirectory(Utensil): os.umask(oldumask) +@attr.s(auto_attribs=True) +class MakeSymlink(Utensil): + path: str + target: str + + async def execute(self, channel: Channel, worktop): + os.symlink(self.target, self.path) + + @attr.s(auto_attribs=True) class Stat(Utensil): path: str