43

I have a django app and I wrote a fabric script that installs my app on deployment server (Cent OS 5).

Now I want to run the same fabric script locally on the deployment server.

Is there a way to do it without supplying ssh user and password?

I mean just with "-H localhost"?

Thanks, Alex A.

alexarsh
  • 5,123
  • 12
  • 42
  • 51

6 Answers6

80

Yes, you can run fab locally by using method local instead of run. What I do typically is have methods for setting up the environment, and call these methods first before calling the actual task. Let me illustrate this with an example for your specific question

fabfile.py

    from fabric.operations import local as lrun, run
    from fabric.api import task
    from fabric.state import env

    @task
    def localhost():
        env.run = lrun
        env.hosts = ['localhost']

    @task
    def remote():
        env.run = run
        env.hosts = ['some.remote.host']

    @task
    def install():
        env.run('deploymentcmd')

And based on the environment, you can do the following

Install on localhost:

    fab localhost install

Install on remote machine:

    fab remote install
tester
  • 22,441
  • 25
  • 88
  • 128
Varun Katta
  • 6,446
  • 1
  • 17
  • 5
7

I am using another trick for executing remote task locally:

from fabric.api import run, sudo, local
from contextlib import contextmanager

@contextmanager
def locally():
    global run
    global sudo
    global local
    _run, _sudo = run, sudo
    run = sudo = local
    yield
    run, sudo = _run, _sudo

def local_task():
    with locally():
        run("ls -la")
xuhcc
  • 2,400
  • 24
  • 23
  • Thanks, this is very elegant, the only drawback to seems to be the fact that your sudo just becomes local and you can't do sudo locally. That can be achieved with a tiny modification: ```python sudo = lambda cmd: local('sudo ' + cmd) ``` – hoonzis Mar 22 '18 at 08:56
4

Slightly less elegant than Varun's answer, but maybe more practical by defaulting to run on the local machine unless another environment selector is given.

from fabric.api import *

# default to running on localhost
env.hosts = ["localhost"]

DEPLOYMENT_PATH = "/some/path/{}/"

def local_or_remote(*args, **kwargs):
    func = local if env.host == "localhost" else run
    return func(*args, **kwargs)


@task
def live():
    """
    Select live environment
    """
    env.hosts = ["host1", "host2"]
    env.path = DEPLOYMENT_PATH.format("live")


@task
def beta():
    """
    Select beta environment
    """
    env.hosts = ["host1", "host2"]
    env.path = DEPLOYMENT_PATH.format("beta")


@task
def host_info():
    local_or_remote("uname -a")

Then run locally as:

fab host_info

or remotely with:

fab live host_info

PS. Here is the Github issue on this topic.

Community
  • 1
  • 1
Pieter Ennes
  • 2,301
  • 19
  • 21
3

First, make sure you can ssh into your localhost without a password:

$ ssh-copy-id localhost

And then just run it as you said, with the -H localhost command line option

Aníbal Rivero
  • 302
  • 2
  • 7
2

You can run ssh server on your local machine to be able to ssh to localhost. And then just run scripts with "-H localhost". Works perfectly for me.

Eugene Leonovich
  • 884
  • 1
  • 9
  • 15
0

A modified version of Varun's answer that takes into account local not capturing stdout/stderr. Without specifying capture=True you would not be able to get results from local.

from fabric.operations import local, run
from fabric.api import task
from fabric.state import env

def local_run(*args):
    return local(args[0], capture=True)

@task
def localhost():
    env.run = local_run
    env.hosts = ['localhost']

@task
def remote():
    env.run = run
    env.hosts = ['some.remote.host']

@task
def install():
    env.run('deploymentcmd')
Community
  • 1
  • 1
dbainbridge
  • 1,190
  • 11
  • 18