読者です 読者をやめる 読者になる 読者になる

fabricとhostと私 2014

fabric、fabfileかかなくても

fab -H "web1, web2" -- "hostname"

みたいにできるのがかわいくて好きだ

ところで、2.x系では @task(depends="foo") のような依存関係が解決されるAPIが追加されるらしいのだけど、今versionではそれは無いので自分で書くことにした。

fabric.api.task()をwrapperした@commandというデコレータとすることにした。@taskの代わりにこれを使っておくと、envを調べてて規定の前処理がおわってないとそもそも動かない、みたいな。

デコレータ機能が追加されてからまじめにpython書いたのがひさしぶりだー。@command@command()両方をサポートしないといけなくなるとデコデコしてくるのがアレだな。

@runs_once
def check_env():
    # なんかしらべて場合によってはabort()
    pass


def command(*args, **kwargs):

    # @command(role="web", ...) or just @command
    invoked = bool(not args or kwargs)

    def deco(func):

        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            execute(check_env)  # <-- check env!
            return func(**kwargs)

        if len(kwargs) > 0:
            return task(**kwargs)(wrapper)
        else:
            return task(wrapper)

    if not invoked:
        return deco(args[0])
    else:
        return deco

とちゅうでハマったのだけど、fabricはtaskの対象hostの扱いがややカオスなので要注意だ。

env.hosts, -H, -x@hosts=...、roleなどが関係してくる。あと、Taskクラスはインスタンス化される前にその対象ホストが解決されるので、タスク自体が動き出した後はできることが限られているのが要注意。

あと、execute()documentでwarningされているように、気をつけないと同じタスクが倍々に動いてしまうことがあるので要注意。

そこだけ気をつければ思ったようにうごいてそこそこ良かった。