aboutsummaryrefslogtreecommitdiffstats
path: root/README
blob: a43ba88adce859e5a6b1b773e30d15c816a85de5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
=======
confman
=======

Description
-----------

Synchronize configuration files between machines, lazily, without root permissions,
and still be able to create powerful rules when needed.

While it is mainly targeted at managing personal configuration files (dotfiles),
it can be used to deploy files to pretty much anything.

It is designed to have almost no dependencies, and is contained in one small file.

By default, it will create symbolic links in the destination directory to the files
in the source directory.
However, some filenames can indicate special actions instead (see below).


Requirements
------------

Python 2.6 or later (for 2.5, try v0.1).
No installation needed, though you can install it.


Usage
-----
The project is usable, but has no CLI interface for now.
Examples are available in ``example.py`` and in the public-dotfiles repository.

A simple example:

::

    from confman import ConfigSource
    c = ConfigSource("~/dotfiles", "~")
    c.sync()


Special actions
---------------

* Files with names starting with ``_`` will be ignored.
  This is useful to provide data for programmable actions.
* File with names ending with ``.copy`` will be a copy.
  It will update the destination file every time.
* File with names ending with ``.copyonce`` will be a copy.
  It will not update the destination file if either file is modified.
* Files with names ending with ``.empty`` will be an empty file.
  It will not update the destination file if either file is modified.
* Files with names ending with ``.p.py`` are programmable actions.
  They contain Python code, and are provided with some special methods.
* Files with names ending with ``.F`` are normal files.
  It means ``blah.p.py.F`` will end up as ``blah.p.py``,
  and ``f.F.F`` as ``f.F``. It is really useful to make a symbolic link of
  a directory instead of all its children (effectively treating
  the directory as a file).

All the matching parts of the special actions are removed; ``myfile.empty`` will create
an empty file named ``myfile``.

Programmable actions
--------------------

Programmable actions can do everything special actions can do, and more.
They have to raise a "Forwarder", however for most uses helpers are provided to
keep the code short.

* ``empty()`` raises an empty action.
* ``ignore()`` raises an ignore action.
* ``redirect(filename)`` will create a symbolic link to the provided filename with a ``_`` added.
  ``redirect('myfile')`` will create a symbolic link to ``_myfile``.


Text / Templates
~~~~~~~~~~~~~~~~

Both ``text()`` and ``template()`` use ``string.Template`` templates.

* ``text(data)`` will put the text from the data variable in the destination file.
* ``template(filename)`` acts like ``text()`` but takes the data from a file.

They have to be used in two steps:

::

    text('hello $name').render(hello='laurentb')

A ``warning`` variable is provided, to help prevent users from modifying
automatically generated files.

::

    text('''# $warning
    aaa=$var''').render(var='123')

Will output:

::

    # WARNING: Do not edit this file, edit the template instead.
    aaa=123

Since it is Python, you can use external libraries if you want,
including advanced template libraries like ``mako``.


Options variable
~~~~~~~~~~~~~~~~

You can provide an ``options`` variable to ``confman``, which will be provided
to the programmable actions.

::

    from confman import ConfigSource
    c = ConfigSource("~/dotfiles", "~", options={'hostname': 'myhostname'})
    c.sync()

A programmable action could use it like this:

::

    if options['hostname'] == 'myhostname':
        redirect('myfile')
    else:
        ignore()


The ``options`` variable does not have to be a ``dict``, it can be anything you want.


Advanced use
------------

To debug what is happening (and why), you can do:

::

    from confman import ConfigSource
    c.sync()
    print repr(c)


Changing the default behavior
-----------------------------

All special actions are optional, and can be configured or subclassed.
For example, you could change the matching of "copy" actions:

::

    import confman, re
    # use .COPY instead of .copy
    confman.CopyAction.MATCHED = re.compile(r"\.COPY$")
    # use hg instead of git
    confman.IgnoreAction.MATCHED = re.compile(r"_|\.hg|\.hgignore")

You can also change what classes are used.
By default, it is ``confman.ConfigSource.DEFAULT_CLASSES``.

::

    import confman
    confman.ConfigSource("~/dotfiles", "~", classes=[MyClass, confman.SymlinkAction])


Development
-----------

Contributions can be sent in the form of git patches, to laurent@bachelier.name.