5

I'm building a Makefile to automate the start of docker on osx.

At the end of the start docker requires to launch this command on the shell in order to configure the shell: eval "$(docker-machine env dev)"

The problem is that if I try to run it on the Makefile, this has no effect on the shell.
In fact, if the launch:
make start and then make status I get error from docker.

This is my Makefile:

CURRENT_DIRECTORY := $(shell pwd)

start:
    @sh run.sh -d
    @eval $(docker-machine env dev)

start-compose:
    @docker-compose up -d

clean:
    @docker-compose rm --force

stop:
    @docker-compose stop

shutdown:
    @docker-compose stop
    @docker-machine stop dev

build:
    @docker-compose build

status:
    @docker-compose ps

cli:
    @docker-compose run --rm web bash

log:
    @docker-compose logs web

logall:
    @docker-compose logs

restart:
    @docker-compose stop web
    @docker-compose start web

restartall:
    @docker-compose stop
    @docker-compose start

.PHONY: clean start start-compose stop status shutdown build cli log logall restart restartall

Is there a way to launch the eval "$(docker-machine env dev)" command on the Makefile and that will affect the shell?

Lughino
  • 151

3 Answers3

14
  1. If you don't have dependencies to track, Make is probably not the right tool.

  2. Make launches one shell per command (or per rule in some versions), if you need to configure the shell, you'll need to configure each instances.

  3. It is not possible for a process to modify its parent. So if you wanted for the Make to modify the shell in which it has been executed, that's not possible without using the same kind of trick as docker.

  4. As $ is interpreted by Make, to have it passed to the shell it needs to be escaped.

  5. Here is a structure which could be workable (note that I combine the rules into one command), but again I don't think Make is the right tool for such kind of thing and I know nothing about docker so I may miss something relevant.

    start:
        @sh run.sh -d; \
        echo $$(docker-machine env dev) > ./docker-config
    
    start-compose:
        @eval $$(cat ./docker-config); \
        docker-compose up -d
    
    clean:
        @eval $$(cat ./docker-config); \
        docker-compose rm --force
    
    stop:
        @eval $$(cat ./docker-config); \
        docker-compose stop
    
    shutdown:
        @eval $$(cat ./docker-config); \
        docker-compose stop ; \
        docker-machine stop dev
    
AProgrammer
  • 2,318
3

If I understand the question, you want make to configure a shell with some environment such that when make exits, that environment persists.

What the eval command is doing is changing the current shell, but since you are running it from make, it changes only the subshell created by make, which is then destroyed when the make target that invoke `eval is completed.

Every line make is told to run is actually invoked in a shell it creates. Once the command run from make returns, that shell is gone, along with any changes you made in it.

What you need to do is run your eval command in a shell that is used to invoke the docker command, all in the same subshell. That is, every command that a target runs that needs this environment needs to invoke the eval command first. And you have to run the commands in the same shell, instead of a separate subshell for every line.

The easiest way to do this is to string your commands together with ; so that it first sets the environment, and then runs some docker command:

status:
    eval $$(docker-machine env dev); docker-compose ps

Note the double dollar sign: $ is a special character in make, and you need to escape it as $$ so that the shell sees $.

This is a make-specific version of this answer. So, you can also invoke a script, or us && or anything else you like.

Or, if the docker environment is not configured or changed by make (for example, you don't need to tweak the environment based on make runtime variables), but is predetermined, you run make in a shell that already has the docker environment set. Then each of its subshells will also have the same environment.

There make be some way in the make you are using to collect multiple lines into the same subshell; check your documentation.

0

I solved with

@eval $$(docker-machine env dev); \
docker-compose stop; \
docker-machine stop dev

in each make command.

Lughino
  • 151