Change working directory

Sometimes you will want to change the working directory where commands are run from. At runtime, this will change the meaning of relatives paths in commands, when rendering documentation, the prompt will show a different working directory.

The working directory can changed using the chdir directive of commands. The value is a path, and is rendered as a template.

Example

Consider the following tutorial file:

configuration:
  run:
    context:
      WORKDIR: /tmp/run
  doc:
    context:
      WORKDIR: /tmp/docs
parts:
  - commands:
      - command: cd /tmp/
        chdir: /tmp/
      - command: ls
        doc:
          output: "contents of /tmp/..."
  - commands:
      - command: mkdir {{ WORKDIR }}
        run:
          cleanup:
            - command: rm -r {{ WORKDIR }}
      - command: cd {{ WORKDIR }}
        chdir: "{{ WORKDIR }}"
      - command: ls
        doc:
          output: "contents of {{ WORKDIR }}..."

The first commands part shows the user how to switch the working directory and run a command there:

user@host:~$ cd /tmp/
user@host:/tmp$ ls
contents of /tmp/...

The second commands part shows the user how to create and switch to another directory, where the path is determined by a context variable:

user@host:/tmp$ mkdir /tmp/docs
user@host:/tmp$ cd /tmp/docs
user@host:/tmp/docs$ ls
contents of /tmp/docs...

… but will run in a random directory at runtime.

chdir and alternatives

Changing the working directory is a bit more tricky with alternatives. At runtime, it is easy to follow the selected alternative and do whatever that alternative specifies. But when rendering documentation, all alternatives are rendered as tabs. Different alternatives might switch to different directories, so which directory should subsequent parts use?

That’s why a chdir directive in an alternative part is discarded after the part when rendering documentation. If all alternatives switch to the same directory, you can specify chdir also for the alternative. The following contrived example kind of shows how not to use this feature, but this better showcases how this works:

# Tutorial that has a foo and a bar alternative, but also uses chdir
parts:
  - alternatives:
      foo:
        commands:
          - command: cd /foo
            chdir: /foo  # discarded for the next part
          - command: echo foo
      bar:
        commands:
          - command: cd /bar
            chdir: /bar  # discarded for the next part
          - command: echo bar
    # Sets working directory for next part, regardless
    # of mode or selected alternative
    chdir: /alt
  - commands:
      - command: ls

The first part renders two alternatives, in this case they even switch to different directories:

user@host:~$ cd /foo
user@host:/foo$ echo foo
user@host:~$ cd /bar
user@host:/bar$ echo bar

But since the alternative specifies chdir: /alt, the next part will use that as working directory:

user@host:/alt$ ls

Switch back to the original working directory

You can also switch back to the original working directory that you started from by setting chdir: false:

parts:
  - commands:
      - command: cd /tmp
        chdir: /tmp
      - command: cd
        chdir: false
      - command: echo back to the start

As you can see, the third command is back in the home directory:

user@host:~$ cd /tmp
user@host:/tmp$ cd
user@host:~$ echo back to the start

Use a true temporary directory at runtime

Since chdir is a template value and update_context and/or output tests (that may update the context) are run before applying it, the best way is simply use mkdir only at runtime:

configuration:
  doc:
    context:
      WORKDIR: /tmp/docs # set only in docs
parts:
  - commands:
      - command: mktemp -d
        doc: false  # do not show mktemp in docs
        run:
          test:
            - regex: "(?P<WORKDIR>.*)"  # update context with output
          cleanup: # make sure dir is removed in the end
            - command: rm -r {{ WORKDIR }}
      - command: cd {{ WORKDIR }}
        chdir: "{{ WORKDIR }}"
      - command: ls
        doc:
          output: "contents of {{ WORKDIR }}..."

This will show /tmp/docs in documentation:

user@host:~$ cd /tmp/docs
user@host:/tmp/docs$ ls
contents of /tmp/docs...

Switch to a different directory in documentation and at runtime

The best way to do this is to specify a different context for documentation and runtime. The above example already does this.

Why do I have to specify chdir at runtime, even though my command already actually changes it?

Because the command you specify is launched in a subprocess, and the caller (structured-tutorials) has no idea what that subprocess does. Your command is still useful (it shows that switching directory will work), but there is no way to pass this information to the next command.