diff --git a/docs/narr/usage.rst b/docs/narr/usage.rst index cc0c89c..d1300fd 100644 --- a/docs/narr/usage.rst +++ b/docs/narr/usage.rst @@ -52,12 +52,15 @@ merely a personal convention. You can define tasks however you need:: """ from fabric import task - from wuttamess import apt, sync + from wuttamess import apt, sync, util # nb. this is used below, for file sync root = sync.make_root('files') + # nb. this is for global mako template context etc. + env = {'machine_is_live': False} + @task def bootstrap_all(c): @@ -74,11 +77,13 @@ merely a personal convention. You can define tasks however you need:: """ Bootstrap the base system """ + renderers = {'mako': util.mako_renderer(c, env)} + apt.dist_upgrade(c) # postfix apt.install(c, 'postfix') - if sync.check_isync(c, root, 'etc/postfix'): + if sync.check_isync(c, root, 'etc/postfix', renderers=renderers): c.run('systemctl restart postfix') diff --git a/pyproject.toml b/pyproject.toml index 65a85a3..06f8d33 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,8 @@ requires-python = ">= 3.8" dependencies = [ "fabric", "fabsync", + "mako", + "typing_extensions", ] diff --git a/src/wuttamess/util.py b/src/wuttamess/util.py index e8fb56a..075a4dd 100644 --- a/src/wuttamess/util.py +++ b/src/wuttamess/util.py @@ -24,9 +24,46 @@ Misc. Utilities """ +from pathlib import Path +from typing_extensions import Any, Mapping + +from mako.template import Template + def exists(c, path): """ Returns ``True`` if given path exists on the host, otherwise ``False``. """ return not c.run(f'test -e {path}', warn=True).failed + + +def mako_renderer(c, env={}): + """ + This returns a *function* suitable for use as a ``fabsync`` file + renderer. The function assumes the file is a Mako template. + + :param c: Fabric connection. + + :param env: Environment dictionary to be used as Mako template + context. + + Typical usage is something like:: + + from fabric import task + from wuttamess import sync, util + + root = sync.make_root('files') + env = {} + + @task + def foo(c): + + # define possible renderers for fabsync + renderers = {'mako': util.mako_renderer(c, env)} + + sync.check_isync(c, root, 'etc/postfix', renderers=renderers) + """ + def render(path: Path, vars: Mapping[str, Any], **kwargs) -> bytes: + return Template(filename=str(path)).render(**env) + + return render diff --git a/tests/files/bar/_sync.toml b/tests/files/bar/_sync.toml new file mode 100644 index 0000000..7bcceb2 --- /dev/null +++ b/tests/files/bar/_sync.toml @@ -0,0 +1,4 @@ + +[files."baz"] +renderer = 'mako' +tags = ['baz'] diff --git a/tests/files/bar/baz b/tests/files/bar/baz new file mode 100644 index 0000000..1f33ce5 --- /dev/null +++ b/tests/files/bar/baz @@ -0,0 +1 @@ +machine_is_live = ${machine_is_live} \ No newline at end of file diff --git a/tests/test_util.py b/tests/test_util.py index 177e4cc..af21f61 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -1,5 +1,6 @@ # -*- coding: utf-8; -*- +import os from unittest import TestCase from unittest.mock import MagicMock @@ -12,3 +13,14 @@ class TestExists(TestCase): c = MagicMock() mod.exists(c, '/foo') c.run.assert_called_once_with('test -e /foo', warn=True) + + +class TestMakoRenderer(TestCase): + + def test_basic(self): + c = MagicMock() + renderer = mod.mako_renderer(c, env={'machine_is_live': True}) + here = os.path.dirname(__file__) + path = os.path.join(here, 'files', 'bar', 'baz') + rendered = renderer(path, vars={}) + self.assertEqual(rendered, 'machine_is_live = True')