Multiline YAML string for GitLab CI (.gitlab-ci.ym

2019-01-26 03:11发布

问题:

I'm trying to write a gitlab-ci.yml file which uses a multi-line string for the command. However, it seems like it is not being parsed. I've tried both the - | and - > with identical results.

stages:
  - mystage

Build:
  stage: mystage
  script:
    - |
        echo -e "
            echo 'hi';
            echo 'bye';
        "

When it tries to run, it only shows echo -e ' as the script to run, and not the whole multiline string. This causes issues for me.

What would be the correct syntax to write something like this?

回答1:

Since the problem seems to be related to multi-line scripts not being supported in Gitlab CI (as @Jordan indicated) and your "script" is actually a single command, you should just rewrite it to be on one line:

- echo -e "\n    echo 'hi';\n    echo 'bye';\n"

since your scalar is not quoted (i.e. it starts with echo) you don't need to do anything special in YAML for the backslashes or quotes.

The result of the script is the same (print an empty line, print echo 'hi'; on a line indented four spaces, print echo 'bye'; on a line indented four spaces.)

If you want to use the multi-line input for readability, your best option is to pre-process the input. I recommend using Python and ruamel.yaml (disclaimer: I am the author) which allows you to preserve features like quotes and comments while doing so.

Given this gitlab-ci.in.yml:

stages:
  - 'mystage'

Build:
  stage: mystage
  # the following is rewritten
  script:
    - |
        echo -e "
            echo 'hi';
            echo 'bye';
        "

and the following Python (version 3) program:

from pathlib import Path
import ruamel.yaml


def flatten_multi_line(d):
    if isinstance(d, list):
        for idx, elem in enumerate(d):
            if isinstance(elem, (list, dict)):
                flatten_multi_line(elem)
            elif isinstance(elem, ruamel.yaml.scalarstring.PreservedScalarString):
                d[idx] = ruamel.yaml.scalarstring.DoubleQuotedScalarString(elem)
    elif isinstance(d, dict):
        for key in d:
            if isinstance(d[key], (list, dict)):
                flatten_multi_line(d[key])
            elif isinstance(d[key], ruamel.yaml.scalarstring.PreservedScalarString):
                d[key] = ruamel.yaml.scalarstring.DoubleQuotedScalarString(elem)


in_file = Path('gitlab-ci.in.yml')
out_file = Path('gitlab-ci.yml')

with in_file.open() as fp:
    data = ruamel.yaml.round_trip_load(fp, preserve_quotes=True)
flatten_multi_line(data)
with out_file.open('w') as fp:
    ruamel.yaml.round_trip_dump(data, fp)

gives the following gitlab-ci.yml:

stages:
- 'mystage'

Build:
  stage: mystage
  # the following is rewritten
  script:
  - "echo -e \"\n    echo 'hi';\n    echo 'bye';\n\"\n"

(without the flatten_multi_line() the multi-line string would be preserved as well).

From the program you could also trigger the processing of the newly generated YAML file (e.g. using subprocess.check_output(), make sure to cast out_file using str() if you want it as an argument to check_output())



回答2:

I came here preemptively expecting this would be an issue but the following "multi-line" command for readability is working for me:

Gitlab Runner: Shell Runner version 1.11.0 / Gitlab version: 8.17.2

myjob:
stage: deploy
script:
  # Single line command
  - az component update --add sql

  # Multi-line command
  - az sql server create -n ${variable} -g ${variable} -l ${variable}
    --administrator-login ${variable} --administrator-login-password ${variable}


回答3:

You can use any multiline scripts/commands via yaml literal_block and anchors feature. Example:

.build: &build |
    echo -e "\n$hl