174

I have a Github action command that is really long:

name: build

on: [push]

jobs:
    build:
        runs-on: ubuntu-18.04
        steps:
            - uses: actions/checkout@v1
            - name: Install Prerequisites
              run: |
                sudo apt-get update
                sudo apt-get install -y --no-install-recommends "a very very long list of prerequisites"

May I know whether it is possible to split the long command into multiple lines for better readability? I have tried the separator '' but it does not work.

cottontail
  • 10,268
  • 18
  • 50
  • 51
Bojian Zheng
  • 2,167
  • 3
  • 13
  • 17

7 Answers7

215

I have a multi line command using backslash to separate the lines as follows:

- name: Configure functions
  run: |
    firebase functions:config:set \
      some.key1="${{ secrets.SOME_KEY_1 }}" \
      some.key2="${{ secrets.SOME_KEY_2 }}" \
    ...    

Note the preceding '|' character, and make sure the subsequent lines after the first one are indented (tabulated).

Nestor Milyaev
  • 5,845
  • 2
  • 35
  • 51
Mike De Marco
  • 2,539
  • 1
  • 12
  • 6
  • 4
    This is the correct answer and would be a better accepted answer. The backslashes work the same as a multiline command in bash. (I'd recommend a hanging indent for readability though.) – tom Jul 18 '20 at 23:16
  • Does anyone know since when this is possible? (I think it was not possible when I wrote my answer but I fully agree with that answer being accepted, now) – dan1st Jul 29 '20 at 16:46
  • @dan1st I also do not think this is possible by the time I asked this, but it seems that Github people have added the support for backslash. – Bojian Zheng Aug 03 '20 at 21:28
  • 68
    I don't know how did it work previously for anyone, but adding a backslash does not work right now with GitHub Actions. This shouldn't be the accepted answer. – mcserep Feb 01 '21 at 15:00
  • 22
    I confirm: this does not work on GitHub Actions as of now. You need to use ">" instead of "I" (see other answers) – IvanD Mar 20 '21 at 01:31
  • It seems to work but it doesn't fail on any errors, same with > also.. so it becomes kind of pointless.. – OZZIE May 05 '21 at 11:13
  • @mcserep in fact this answer works for me and solution with ">" doesn't. When I used ">", the second line was treated like another command – Sarrus Nov 29 '21 at 12:56
  • 6
    Use ">". "|" doesn't work anymore. – naimetti Feb 23 '22 at 04:19
  • This worked for me but I had to get rid of the "\" characters at the end. – okcoker May 05 '22 at 02:01
  • 5
    It works for the moment. Make sure you have a whitespace before the backslash, too. – Maxim Zubarev May 14 '22 at 12:50
  • this one works for me, the '>' one doesn't work for me – Justin Jun 09 '22 at 02:41
  • This doesn't work. Please see: https://stackoverflow.com/a/65808412/3420114 – Zoidberg Apr 11 '23 at 14:56
  • You can use `>` to split *a single command* on more lines, `|` allow to write more command in the same run, but one command each line, where you have to use `\` to split the same command on more lines aware to put lines of same command indented once to enhance readability. – Daniele Tentoni May 24 '23 at 09:57
  • This worked for me as well – Kevin Lawrence Jun 13 '23 at 04:32
  • The probable reason **why this does not work for others** is because you guys might be **combining all the commands into one**. \ is a continuation so in @Mike's example that is actually just one command. – 0xdeadbeef Jul 31 '23 at 00:37
  • `>` worked for me for a single command, but if I still want multiple commands I could get `|` to work, at least not for the default windows runner using PowerShell... may try the backtick for PowerShell itself... – Michael Hawker - MSFT Aug 18 '23 at 20:20
  • Works well for me, no clue why it doesn't work for others... – Dimitri Kopriwa Aug 28 '23 at 16:25
144

You can use the YAML folded style with > which is supported by GitHub Actions.

For example,

run: >
  xvfb-run
  ./mvnw -f my/pom.xml
  clean verify
  -DskipTests

newlines will be replaced with spaces so the above is equivalent to

run: xvfb-run ./mvnw -f my/pom.xml clean verify -DskipTests
lorenzo-bettini
  • 2,106
  • 1
  • 16
  • 11
  • 4
    Also see https://yaml-multiline.info/ to play around with the different scalar / chomping styles. – sschuberth Mar 14 '21 at 09:04
  • 8
    This works, thank you! Important note: per yml spec, white space is important here, so any extra tabs before the lines will cause problems. – Johnny Oshika Apr 23 '21 at 21:54
  • It seems to work but it doesn't fail on any errors, same with | also.. so it becomes kind of pointless.. – OZZIE May 05 '21 at 11:13
  • 1
    @JohnnyOshika Thank you for that comment! It cleared a long standing misunderstanding for me. – ruohola May 08 '21 at 18:47
  • Just a quick note about this being for the default shell. For example, my YML file had some bits running R code with `shell: Rscript {0}`, and line breaking in those cases don't need any special characters (just like in R). – Waldir Leoncio Sep 22 '21 at 11:37
  • 2
    As @JohnnyOshika points out, white space is critical here (learned it the hard way). If a line starts with extra spaces, the line will be ignored. So make sure all lines start at the same column. – Tim Van Laer Jun 02 '22 at 08:15
84

Going to share this in since it has not been mentioned.

You can use:

  • | called a Literal Block Scalar which preserves new lines and trailing spaces
  • > called a Folded Block Scalar which converts new lines into spaces
  • plain old strings, either unquoted, single-quoted or double-quoted

Additionally, you can append a block chomping indicator, -, + or none. which will alter the newlines at the end of the block.

these are the block chomping definitions

  • - is called strip removes newlines at the end
  • + is called keep keeps all newlines at the end
  • default behavior is keeping a single newline at the end

I found the site yaml-multiline.info useful for understanding how yaml strings are interpreted.

For my use case, I ended up doing the following:

run: >-
  for i in $(find . -type f -name "*.log");
  do
   echo "File: ${i} \n";
   cat $i;
   printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' -;
  done
Josue Alexander Ibarra
  • 8,269
  • 3
  • 30
  • 37
29

The above answers all had pieces, but this is what worked for me in a github composite action. It should work in a regular workflow too.

As @lorenzo-bettini said, if you want everything to be on one line, use what @Josue Alexander Ibarra called a Folded Block Scalar.

run: >
  xvfb-run
  ./mvnw -f my/pom.xml
  clean verify
  -DskipTests

newlines will be replaced with spaces so the above is equivalent to

run: xvfb-run ./mvnw -f my/pom.xml clean verify -DskipTests

If you want new lines to be preserved, use what @Josue Alexander Ibarra called a Literal Block Scalar.

run: |
  FILE=./package.json
  if test -f "$FILE"
  then
    echo "$FILE exists."
  else
    echo "File does not exist"
  fi

When you do a multi-line run, though, you have to make sure you indent correctly, otherwise step will think that shell: bash is part of the run: | string.

WRONG:

  steps:
    - run: |
      FILE=./package.json
      if test -f "$FILE"
      then
        echo "$FILE exists."
      else
        echo "File does not exist"
      fi
      shell: bash

RIGHT:

  steps:
    - run: |
        FILE=./package.json
        if test -f "$FILE"
        then
          echo "$FILE exists."
        else
          echo "File does not exist"
        fi
      shell: bash
Mixchange
  • 893
  • 1
  • 8
  • 14
11

This was not possible using backslashes, earlier. See the accepted answer on how to do it now.


As far as I know, GitHub Actions does not support that.

However, you can use environment variables for that.

For example, this script splits your command in 3 lines of code and executes it as one line.

steps:
  - name: Install Prerequisites
    run: |
      sudo apt-get update
      bash -c "$line1 $line2 $line3"
    env:
    - line1='sudo apt-get install -y --no-install-recommends '
    - line2='a very very long list'
    - line3='of prerequisites'

It creates the environment variables line1, line2 and line3 and concats and executes them in a bash session.

dan1st
  • 12,568
  • 8
  • 34
  • 67
4

Note also the join and format functions for expressions:

HELLO_WORLD = ${{ join('Hello', 'world!') }}
HELLO_WORLD = ${{ format('{{Hello {0}}}', 'World') }}

Presumably these could be combined with env variables.

Jon Peck
  • 86
  • 4
1

This is another way to do it, it will work with Github Actions:

name: build

on: [push]

jobs:
    build:
        runs-on: ubuntu-18.04
        steps:
            - name: Install Prerequisites
              uses: actions/checkout@v1
              run: >
                sudo apt-get update && 
                sudo apt-get install -y --no-install-recommends
                "a very very long 
                list of prerequisites"

alternative to the && is to split that in several steps, like:

name: build

on: [push]

jobs:
    build:
        runs-on: ubuntu-18.04
        steps:
            - name: Install Prerequisites - checkout phase
              uses: actions/checkout@v1

            - name: Install Prerequisites - update phase
              run: sudo apt-get update

            - name: Install Prerequisites - install phase
              run: >
                sudo apt-get install -y --no-install-recommends
                "a very very long
                list of prerequisites"
Andreas Lundgren
  • 12,043
  • 3
  • 22
  • 44