This is a dotfiles manager that can template and pull changes from rendered files. My old approach was using symlinks and the Git repository, and I am very spoiled by upstream changes (because I don’t have to track them-git does that for me). But maintaining different branches for different systems and architectures is a drag. Plus it looks like Ansible on BSD is slowing down, and I am slowly losing my testing tools, one by one (stale molecule
plugins, etc.). So I decided to switch from my Ansible dotfiles manager to something more robust, with templates and stuff, so I could merge my dotfiles branches into one. It turned out none of the currently available tools can do what I need. There are symlink-based ones and template solutions as well. But none can track changes in the rendered file.
So I wrote a new tool from the ground up—DFS
! It has a template engine that can conditionally render strings, and it tracks changes in the rendered files, translating them back into the template like it was edited there.
The source config must be a Git repository that mirrors $HOME
. You can set a custom repository destination as well as a custom deployment directory (defaults to $HOME
).
It is pretty raw; I just finished rewriting the engine after polishing the foundation (logistics and CLI), so the library still needs refactoring. Only OS conditionals are implemented right now—architecture and cool userland stuff will follow shortly.
test applyTemplate {
if (builtin.os.tag != .freebsd or builtin.cpu.arch != .x86_64) return error.SkipZigTest;
var gpa = std.testing.allocator;
const template =
\\{> if SYSTEM.os == linux <}
\\val="Foo"
\\{> elif SYSTEM.os == freebsd <}
\\val="Bar"
\\{> else <}
\\val="Else"
\\{> end <}
\\{> if SYSTEM.arch == x86_64 <}
\\val="test0"
\\{> else <}
\\val="test1"
\\{> end <}
\\
\\{> if SYSTEM.hostname == not_my_machine <}
\\val="HOST2"
\\{> else <}
\\val="HOST1"
\\{> end <}
\\
;
const rendered_expected =
\\val="Bar"
\\val="test0"
\\
\\val="HOST1"
\\
;
const rendered = try applyTemplate(gpa, template);
defer gpa.free(rendered);
try std.testing.expectEqualStrings(rendered_expected, rendered);
}
test "back-template (fbsd)" {
if (builtin.os.tag != .freebsd) return error.SkipZigTest;
var gpa = std.testing.allocator;
const template =
\\{> if SYSTEM.os == linux <}
\\val="Foo"
\\{> elif SYSTEM.os == freebsd <}
\\val="Bar"
\\{> else <}
\\val="Else"
\\{> end <}
\\
;
const rendered_user_edit =
\\val="Zoot"
\\
;
const reversed = try reverseTemplate(gpa, rendered_user_edit, template);
defer gpa.free(reversed);
const expected_template =
\\{> if SYSTEM.os == linux <}
\\val="Foo"
\\{> elif SYSTEM.os == freebsd <}
\\val="Zoot"
\\{> else <}
\\val="Else"
\\{> end <}
\\
;
try std.testing.expectEqualStrings(expected_template, reversed);
}
test "back-no_template (fbsd)" {
if (builtin.os.tag != .freebsd) return error.SkipZigTest;
var gpa = std.testing.allocator;
const template =
\\FOO
\\{> if SYSTEM.os == linux <}
\\val="Foo"
\\{> elif SYSTEM.os == freebsd <}
\\val="Bar"
\\{> else <}
\\val="Else"
\\{> end <}
\\
;
const rendered_user_edit =
\\BAR
\\val="Bar"
\\
;
const reversed = try reverseTemplate(gpa, rendered_user_edit, template);
defer gpa.free(reversed);
const expected_template =
\\BAR
\\{> if SYSTEM.os == linux <}
\\val="Foo"
\\{> elif SYSTEM.os == freebsd <}
\\val="Bar"
\\{> else <}
\\val="Else"
\\{> end <}
\\
;
try std.testing.expectEqualStrings(expected_template, reversed);
}