fab-user
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Fab-user] Greetings and suggestions


From: Niklas Lindström
Subject: [Fab-user] Greetings and suggestions
Date: Sat, 18 Oct 2008 02:00:32 +0200

Hello!

First, many compliments for Fabric! I've just started using it, but it
really completes my my toolset very well.

That said, I have a couple of suggestions I'd like to discuss. Perhaps
some of this is already being addressed. If not, I'd be very happy to
provide patches for the things you'd like to incorporate.

Currently I use Fabric for deployment of a java-based, maven-built
suite of services. This has driven me to think of these suggestions --
most of them are direct needs I've felt when writing my fabfile(s). Of
course, I may have overlooked things when coming up with these
possible solutions, so don't hesitate to point me to better ways. :)

(I very much sympathise with the design of keeping Fabric compact
though, so I hope these suggestions don't stray from that (too much).)


== Core improvements ==

1. Making "_private" names in scripts accessible to the functions
(commands) you define. I was a bit surprised to see that these names
aren't kept, as I'd like to declare some instrumental functions (e.g.
for the "validate" feature of "prompt"). If I don't use a leading "_",
they are available, but of course also become commands, which "litters
the list". Working alternatives today are to either to pass them with
set/get, or bind them with keyword args in the command definitions.
But neither of those are very appealing.. ;)

2. In "load", use the optional globals and locals that execfile
supports to manage the names accessible in fabfiles. Currently,
everything in fabric.py, including imports, is available. This may be
a feature of course (not having to import re and the like), but I
believe it may become somewhat uncontrollable. (This is also a way to
go about for making fabric importable from python code and usable
without having a single, global context for it all.)

3. One useful approach may be to create Command objects for all
commands, which stores "private locals" from the file in which they
were created. This way, these "_private" names don't risk
overshadowing each other if you use "load" to combine a complex setup.
(These objects could of course also store the filename of the source,
for possible tracking/debugging of such complex setups.)


== Reasonably conventional improvements/additions ==

4. Only run "load" once per filename (by storing the loaded filenames
to prevent from loading them twice). This makes it cleaner to do load
calls in a setup made up of several co-dependent fabfiles (comprising
a tree of dependencies, where e.g. both file_b and file_c needs stuff
from file_a).

5. Add a "depend" operator. This will call a command you need,
*unless* it has already been called. This way, you can write stuff
like "compile_lib" commands, upon which "compile_component_x" and
"compile_component_y" both can depend, without having to run the
"compile_lib" twice (which may be a very lengthy process, at least in
the case of maven).

6. Extend require to take a varargs instead of just one name, so you
don't have to repeat calls to it if you have a bunch of variable names
you need to get from the "provided_by" commands.

7. Add decorator versions for require ("@requires(...)") and depend
("@depends(...)"), to be used for declaratively binding commands
together. While these would be optional features complementing the
procedural use of "require" and "depend", I think it'd look quite
nice. (It would also be very useful for some future "show dependency
tree" command or similar, since you can store the dependencies on the
commands themselves.)

8. Support for filename globs (e.g. "components/*.py") in "load". This
might be less useful if number 4 is implemented.


== Improvements related to "prompt" ==

9. Extend prompt to repeat itself until "validate" is satisfied. I
found myself writing e.g.::

    while not get('tag'):
        prompt('tag',"Tag", validate=valid_tag)

, where "valid_tag" returns None if it doesn't match the supplied value.

10. A regexp match complement to the current validate feature of
prompt. This would take a regexp (string) for the possibly common task
of simple match validation. Suggested keyword: "must_match". ;)

11. Optionally define variables on the commandline (e.g. "fab
tag_release -v tagname=release-1.0"). Perhaps only used to give values
to prompt, so you can use that non-interactively if you want to.


== Improvements related to "current working dir" ==

12. Support for supplying the fabfile to use with a named commandline
flag (e.g. "fab --use setup/vcs/tagging.py"). This would mainly be for
calling modules without forcing them to change back up in a directory
tree. While you could use "load" to read in such things, it may be
useful to call this directly as well.

13. Support for changing working dir via operator(s). A sketch of this::

    def compile():
        a_dir = directory("library_a")
        a_dir.local("mvn install")
        b_dir = directory("service"):
        b_dir.local("mvn package")

Or more to my taste, with a contexthandler combined with the "with"-statement::

    def compile():
        with directory("library_a"):
            local("mvn install")
        with directory("service"):
            local("mvn package")

Note that this feature should work with all filesystem commands
("local", "run", "sudo", "put" etc).

(Also note that this with-version would require e.g. a threadlocal for
it to work concurrently (with e.g. "rolling strategy") since it'd
alter a global state (current dir). Either that or binding the context
with a name ("with directory(...) as dir_a"), but that'd make it a lot
more like the non-with-version above.)


== Other thoughts ==

14. The name "set" shadows the built-in set type. Since these may
prove useful in some scripts, I think "something" could be done about
that. Of course, renaming "set" is the most direct option, I can
understand if you don't wish to do that. An alternative would be to
simply supply the set type under an alias, say "Set" (of course, one
can use __builtin__.set (or, while being legacy, do "import sets" and
use sets.Set) already today, so this isn't acute).

15. Possibly adding Jython support. Take a look at
<http://code.google.com/p/robotframework-sshlibrary/> for a way to go
about that. I personally don't consider this important at the moment
though, but it may be useful in the future.


That's all I can think of now. I hope it wasn't too unwieldy. :)

Keep up the good work!

Best regards,
Niklas Lindström




reply via email to

[Prev in Thread] Current Thread [Next in Thread]