4

I put together a small example of how I manage my development environment with Docker from within Emacs. The idea is, rather than having multiple files in folders that interrelate to each other and thus make it hard to track transitional changes, to have one org-file and just tangle the individual files and even execute some of the source code blocks within the org file for convenience with the use of some Emacs packages. Below I try to describe my reasoning and I am grateful for advise on how to streamline things and also alternatives to my approach. Maybe though it can also prove helpful for others who aim for similar workflows. Can I improve something, by following best practices and the likes? I am particularly concerned about the local-file variable that defines the docker-image name on a per-file basis and which I need to read out in several places in a quite cumbersome way

  # -*- image_name: "test_env"; -*-


    * Environment
      :PROPERTIES:
      :header-args: :results output :mkdirp yes
      :END:

    ** requirements.txt
        #+BEGIN_SRC conf :tangle requirements.txt
        numpy
        #+END_SRC

    ** Start docker
    #+BEGIN_SRC sh :dir "/sudo::"
    sudo service docker start
        #+END_SRC

    ** Dockerfile
    Use [ob-docker-build][1] to build the docker images upon pressing C-c C-c. The image
    name is grabbed from the file-local variable at the top of this file. This file
    can also be tangled to disk.

    #+BEGIN_SRC docker-build :dir "." :tag (symbol-value 'image_name) :tangle Dockerfile
    FROM python:3.8-slim-buster

    ENV VIRTUAL_ENV=/opt/venv
    RUN python3 -m venv $VIRTUAL_ENV
    # Make sure we use the virtualenv:
    ENV PATH="$VIRTUAL_ENV/bin:$PATH"

    COPY requirements.txt .
    RUN pip install --quiet --no-cache-dir -r requirements.txt
    WORKDIR /app
    # just for keeping the container running
    CMD tail -f /dev/null
    #+END_SRC

    ** run_docker.sh
    This script can either be tangled to disk or is executed automatically by
    org-sbe in the python application below

    #+HEADER: :var image_name=(symbol-value 'image_name)
    #+name: run_docker
    #+BEGIN_SRC bash :tangle run_docker.sh :tangle-mode (identity #o755) :results output
    #!/bin/bash

    SBX_DIR=$(realpath $(dirname $0))

    # generate some id for the container
    CONTAINER_NAME=$(uuidgen | md5sum | awk '{ print $1 }' | cut -c -12)

    if [ $# -eq 0 ]; then
        ARGS=$SHELL
    else
        ARGS=$@
    fi

    DOCKER="docker run"

    DOCKER_ARGS=(
        --name "$CONTAINER_NAME"
        -v $HOME:$HOME
        -v "$SBX_DIR":"$SBX_DIR"
        --rm
        --user $(id -u):$(id -g)
        -w $SBX_DIR
        -itd ${image_name}
        $ARGS
    )

    $DOCKER ${DOCKER_ARGS[@]}
    #+END_SRC

    ** python
    Use the [docker-tramp][1] package to attach to a running container. C-c C-c on the
    block below causes the run-docker src-block to be executed. The resulting ID of
    the container is then used here to attach to this running instance

    #+BEGIN_SRC python :dir (concat "/docker:" (org-sbe run_docker) ":") :results output
    print('hello')
    #+END_SRC

What is taking precedence here?

# -*- foo: "one"; -*-

#+PROPERTY: header-args :var foo=two
#+PROPERTY: foo=three


* first
:PROPERTIES:
:foo: four
:END:
** second
:PROPERTIES:
:foo: five
:END:
#+HEADER: foo=(symbol-value 'foo)
#+BEGIN_SRC bash :var foo=(org-macro--get-property "foo" "")
echo $foo
#+END_SRC
CD86
  • 979
  • 10
  • 27
  • Is there a specific problem you're having? (I feel like Emacs has become kind of a niche editor, for all that I personally live in it, and I wouldn't try to push this solution on my colleagues; reading through what you've written, a host-based `python3 -m venv` setup would be much simpler and wouldn't require root permissions, Emacs, TRAMP, _etc._.) – David Maze Apr 16 '20 at 11:04
  • Can't you evaluate your example to find which takes precedence? It is the last one (five), under `** second`. – mankoff Apr 17 '20 at 15:49
  • Each babel language has a specific set of header arguments that it recognizes. That defines what can be a header-arg v. a more general property that needs to be passed in as a `var`. – mankoff Apr 17 '20 at 15:50

1 Answers1

3

Re:

I am particularly concerned about the local-file variable that defines the docker-image name on a per-file basis and which I need to read out in several places in a quite cumbersome way

You can define a property and access in text and in header args:

:PROPERTIES:
:foo:  bar
:END:

You can access the foo property like this: {{{property(foo)}}}.

And you can access it in header args like this:

#+BEGIN_SRC bash :var foo=(org-macro--get-property "foo" "")
echo $foo # prints bar
#+END_SRC
mankoff
  • 2,225
  • 6
  • 25
  • 42
  • thank you, I will try that. Btw. when do I have to use :header-args: syntax and when am I free to just use whatever, like you did with foo up there? – CD86 Apr 16 '20 at 19:25
  • I edited my previous post. Been trying to understand this but failed. What is taken precedence over what? If I define foo below under the first header, it is not recognized. Only the innermost foo is valid, it looks. File-local variables dont seem to work at all... – CD86 Apr 17 '20 at 08:10
  • 1
    You use the `header arg` syntax for header args. Neither `foo` nor `bar` are header args that babel supports for `bash`. I could have used `header-arg:bash+: :var foo=bar` and then just directly accessed `$foo` in the block too. That would probably be cleaner to look at, but a bit more complicated to debug, because the connection between `$foo` in the block is not clearly connected to the babel header. – mankoff Apr 17 '20 at 15:53
  • #+PROPERTY: header-arg:bash+: :var foo=bar * Test #+BEGIN_SRC bash echo $foo #+END_SRC does not work for me – CD86 Apr 18 '20 at 11:21
  • 2
    Sorry I typed that from memory. it is actually `#+PROPERTY: header-args:bash+ :var foo="bar"`. Also, when changing a file-level `#+PROPERTY` you need to `C-c C-c` on it to activate it. – mankoff Apr 18 '20 at 14:39