トラブル防止!!Fabricのタスクをmasterブランチでのみ実行可能にする方法

Fabricの特定のタスクを、masterブランチのみで実行できるようにする方法を紹介します。これを利用すると、開発用のブランチをProduction環境に反映するミスなどの防止に役立ちます。

はじめに

開発の規模などによっても変わりますが、私はよくFabricとgitを組み合わせてサーバ上のプログラム類を更新しています。その時使用するgitのブランチは、ローカルの環境で作業中のブランチと同じになるようにしています。

この方法だと、開発環境やStaging環境にアップロードするときに便利です。ただ、うっかり本番環境にデプロイする際に、開発用のブランチを使ってしまいそうになるということがありました。

この記事ではデコレータを使って、Fabricの特定のタスクをmasterブランチのみ適用できるようにする方法を紹介します。

ブランチ名設定関数の作成

デコレータ作成の前に、まずはブランチ名を取得して変数に設定する関数を作成します。

ブランチ名の取得方法

使用中のブランチ名は以下のように取得できます。

$ git rev-parse --abbrev-ref HEAD
master

これをFabricからcapture=True付きで実行すれば、変数にブランチ名を入れることができます。

branch_name = local('git rev-parse --abbrev-ref HEAD', capture=True)

関数作成

上記ブランチ名取得をふまえて、以下のような関数を作ります。

def _set_branch():
    """ブランチ名を設定します。 """
    # 使用するブランチが決定済みかをチェックします。
    branch_name = extra_env.get('branch_name')
    if not branch_name:
        # ブランチ名を取得します。
        branch_name = local('git rev-parse --abbrev-ref HEAD', capture=True)

        # 使用するブランチを確定する前に、確認メッセージを表示します。
        if not confirm('[%s] ブランチで作業を実行します。よろしいでしょうか?'
                       % blue(branch_name, bold=True)):
            abort('使用するブランチを確認してください。')

        # ブランチ名を設定します。
        extra_env['branch_name'] = branch_name

_set_branch関数は、実行するとextra_env変数に、branch_nameというキーでブランチ名を格納します。また、ブランチ名を表示する確認メッセージを表示するようにしています。

デコレータの作成

_set_branch関数を使って、masterブランチのみをタスクを実行できるようにするデコレータ(**master_branch_only)を作成します。

def master_branch_only(func):
    """masterブランチでのみ実行できるようにします。"""
    @wraps(func)
    def wrapper(*args, **kwds):
        _set_branch()  # 使用するブランチを設定します。
        if extra_env.get('branch_name') != 'master':
            # masterブランチ出ない場合は処理を終了します。
            abort(red('masterブランチ以外では作業を実行できません。'))
        return func(*args, **kwds)
    return wrapper

このデコレータでは_set_branchを読んでブランチ名を設定し、そのブランチ名がmasterと一致するかをチェックしています。もし一致していない場合は処理を終了するようになっています。

デコレータの検証

master_branch_onlyデコレータを付けたタスクを作成し、動作検証をします。検証用タスクの動作は何でもいいので、ローカルでhostnameを実行するだけのタスクにします。

検証用タスク

@task
@master_branch_only
def hostname():
    local('hostname')

検証

masterブランチで実行

まずは、masterブランチでhostnameタスクを実行してみます。fab hostnameを打ち、確認メッセージで「y」を入力するとホスト名が表示されます。

$ fab hostname
[localhost] local: git rev-parse --abbrev-ref HEAD
[master] ブランチで作業を実行します。よろしいでしょうか? [Y/n] y
[localhost] local: hostname
ローカルPCのホスト名

Done.

testブランチで実行

次は、testという名前のブランチを作成してタスクを実行してみます。

$ git checkout -b test
Switched to a new branch 'test'

testブランチに切り替えて、fab hostnameを打ち、「y」を押すと処理が中断されることを確認できます。

$ fab hostname
[localhost] local: git rev-parse --abbrev-ref HEAD
[test] ブランチで作業を実行します。よろしいでしょうか? [Y/n] y

Fatal error: masterブランチ以外では作業を実行できません。

Aborting.
masterブランチ以外では作業を実行できません。

これで、masterブランチのみ実行できるタスクを作成することができました!!

ソース

今回使用したソースを共有します。github上にも公開してあります。https://github.com/yusukemurayama/blog-samples/blob/master/fabfile/master_branch_only.py

# coding: utf-8
from functools import wraps
from fabric.api import abort, task, local
from fabric.contrib.console import confirm
from fabric.colors import blue, red

# env以外の設定を入れる辞書型オブジェクトを定義します。
extra_env = {}


def _set_branch():
    """
    ブランチ名を設定します。 
    """
    # 使用するブランチが決定済みかをチェックします。
    branch_name = extra_env.get('branch_name')
    if not branch_name:
        # ブランチ名を取得します。
        branch_name = local('git rev-parse --abbrev-ref HEAD', capture=True)

        # 使用するブランチを確定する前に、確認メッセージを表示します。
        if not confirm('[%s] ブランチで作業を実行します。よろしいでしょうか?'
                       % blue(branch_name, bold=True)):
            abort('使用するブランチを確認してください。')

        # ブランチ名を設定します。
        extra_env['branch_name'] = branch_name


def master_branch_only(func):
    """masterブランチでのみ実行できるようにします。"""
    @wraps(func)
    def wrapper(*args, **kwds):
        _set_branch()  # 使用するブランチを設定します。
        if extra_env.get('branch_name') != 'master':
            # masterブランチ出ない場合は処理を終了します。
            abort(red('masterブランチ以外では作業を実行できません。'))
        return func(*args, **kwds)
    return wrapper


@task
@master_branch_only
def hostname():
    local('hostname')

この記事が役に立った場合、シェアしていただけると励みになります!!