62

Recently github has announced change that echo "::set-output name=x::y" command is deprecated and should be replaced by echo "x=y" >> $GITHUB_OUTPUT

The previous command was able to process multilined value of b while the new approach fails with the folllowing errors

Error: Unable to process file command 'output' successfully.
Error: Invalid format

In my script, I populate a variable message with a message text that should be sent to slack. I need output variables to pass that text to the next job step which performs the send operation.

message="Coverage: $(cat coverage.txt). Covered: $(cat covered.txt). Uncovered: $(cat uncovered.txt). Coverage required: $(cat coverageRequires.csv)"
      

The last part of message includes context of a csv file which has multiple lines

While the set-output command was able to process such multilined parameters

echo "::set-output name=text::$message"

the new version fails

echo "text=$message" >> $GITHUB_OUTPUT

What can be done to fix or avoid this error?

1
  • Note: Outputs are Unicode strings, and can be a maximum of 1 MB. The total of all outputs in a workflow run can be a maximum of 50 MB. --- Defining outputs for jobs - GitHub Docs. Commented Apr 17, 2023 at 17:42

8 Answers 8

52

The documentation describes syntax for multiline strings in a different section but it works even for the output parameters.

Syntax:

{name}<<{delimiter}
{value}
{delimiter}

This could be interpreted as:

  1. Set output with the defined name, and a delimiter that would mark the end of the data (typically it would be a plain "EOF" string but it's strongly recommended that the delimiter is random so the following examples use EOF variable set to a random value).
  2. Keep reading each line and concatenating it into one input.
  3. Once reaching the line consisting of the defined delimiter, stop processing. This means that another output could start being added.

Therefore, in your case the following should work and step's text output would consist of a multiline string that $message contains:

EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
echo "text<<$EOF" >> $GITHUB_OUTPUT
echo "$message" >> $GITHUB_OUTPUT
echo "$EOF" >> $GITHUB_OUTPUT

...and unless you need $message for something else, you could actually avoid setting it and get a more readable set of instructions to construct the output:

EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64)
echo "text<<$EOF" >> $GITHUB_OUTPUT
echo "Coverage: $(cat coverage.txt)." >> $GITHUB_OUTPUT
echo "Covered: $(cat covered.txt)." >> $GITHUB_OUTPUT
echo "Uncovered: $(cat uncovered.txt)." >> $GITHUB_OUTPUT
echo "Coverage required: $(cat coverageRequires.csv)" >> $GITHUB_OUTPUT
echo "$EOF" >> $GITHUB_OUTPUT

Note: The last example is not 100% same as yours because it would contain new lines between the sections. You could use echo -n to avoid that.

Sign up to request clarification or add additional context in comments.

9 Comments

But handling multilines is the whole point for the syntax I suggested.
@Preminster, be aware that this will also interpret escape sequences in $message. To avoid that, in bash you can use: echo "text<<EOF"$'\n'"$message"$'\n'EOF >> $GITHUB_OUTPUT
You are a lifesaver, I had read that documentation literally 5 times and it still didn't make sense to me. Your clear explanation helped me figure out my own case.
What exactly are you confused about? You said VAR in your case is text (and not message). I think that's pretty much what I wrote :-).
Thank you @tmt very well explained
|
10

The previous command was able to process multilined value of b while the new approach fails with the folllowing errors

Actually it has not been, but lately they changed the behaviour:

https://github.com/orgs/community/discussions/26288

What can be done to fix or avoid this error?

The same way as was for the GITHUB_ENV variable:

https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings

echo 'var<<EOF' >> $GITHUB_OUTPUT
echo "<multi-line-output>" >> $GITHUB_OUTPUT
echo 'EOF' >> $GITHUB_OUTPUT

Or more fancy way:

https://github.com/orgs/community/discussions/26288#discussioncomment-3876281

delimiter="$(openssl rand -hex 8)"
echo "output-name<<${delimiter}" >> "${GITHUB_OUTPUT}"
echo "Some\nMultiline\nOutput" >> "${GITHUB_OUTPUT}"
echo "${delimiter}" >> "${GITHUB_OUTPUT}"

2 Comments

I don't really see the need to generate a random delimiter every time, seems like a pointless hassle.. why not just use a random mashing of keys on the keyboard?
@JohnHunt Yes, you may generate it once.
7

I ended up having replaced all breaklines in the message variables by the command

message=$(echo $message | tr '\n' ' ')
echo "text=$message" >> $GITHUB_OUTPUT

This eliminated the error.

1 Comment

Note that this isn't useful if the following step, that uses the output variable, needs the newlines to be there. Not a need of mine but to spark your understanding: perhaps its a formatted .md file being created and the output variable holds markdown for a table (which requires newlines in the right places).
2
  steps:
     - run: | 
         some_response=$(curl -i -H "Content-Type: application/json" \
           -d "${body}" -X POST "${url}")

         echo response_output=$some_response >> $GITHUB_OUTPUT
       id: some-request
     - run: |
         echo "Response is: ${{ steps.some-request.outputs.response_output }}"

Worked for me well. Quotes (and curly brackets) are not needed in case of just setting output var

Comments

1

The cleaner solution, when using base64 is to pass -w 0 to it to stop it wrapping across multiple lines. Eg.

echo "text=$(cat path/to/file | base64 -w 0)" >> $GITHUB_OUTPUT

2 Comments

well, I don't really use base64 here
but you can encode and decode. I see your point, guess I got it from an answer..
1

I found that you can't simply cat a multiline file into the $GITHUB_OUTPUT. This code fails:

- name: Source Repo Comment
  run: |
    # Form Comment for Source Repo Parse
    echo "Parsed 'source_repo_url'" > source_repo_comment.md;
    if [ -z "${{ steps.read-source-repo.outputs.source_repo_url }}" ]; then
      # Error
      echo '# source_repo_url is not parsable!' >> $GITHUB_STEP_SUMMARY;
      _error_message="Please ensure you have 1 occurrence of \`source_repo_url: 'https://github.com/org-name/repo-name'\` in your issue description. Ensure it matches this regex=\`.*source_repo_url: '(https:\/\/github\.com\/[a-z0-9_-]*\/[a-z0-9_-]*)'\`";
      echo 'FAILURE!' >> source_repo_comment.md;
      echo "$_error_message" >> source_repo_comment.md;
    else
      # Success
      echo 'SUCCESS!' >> source_repo_comment.md;
      echo "source_repo_url=${{ steps.read-source-repo.outputs.source_repo_url }}" >> source_repo_comment.md;
    fi
    cat source_repo_comment.md >> $GITHUB_STEP_SUMMARY;
    echo "************************************************************";
    cat source_repo_comment.md;
    echo "************************************************************";
    cat source_repo_comment.md >> $GITHUB_OUTPUT;
  id: source-repo-comment

With an error like: enter image description here

But when you wrap the cat in a subshell it should pass. Note: new lines seem to always be removed.

      - name: Source Repo Comment
        run: |
          # Form Comment for Source Repo Parse
          echo "Parsed 'source_repo_url'" > source_repo_comment.md;
          if [ -z "${{ steps.read-source-repo.outputs.source_repo_url }}" ]; then
            # Error
            echo '# source_repo_url is not parsable!' >> $GITHUB_STEP_SUMMARY;
            _error_message="Please ensure you have 1 occurrence of \`source_repo_url: 'https://github.com/org-name/repo-name'\` in your issue description. Ensure it matches this regex=\`.*source_repo_url: '(https:\/\/github\.com\/[a-z0-9_-]*\/[a-z0-9_-]*)'\`";
            echo 'FAILURE!' >> source_repo_comment.md;
            echo "$_error_message" >> source_repo_comment.md;
          else
            # Success
            echo 'SUCCESS!' >> source_repo_comment.md;
            echo "source_repo_url=${{ steps.read-source-repo.outputs.source_repo_url }}" >> source_repo_comment.md;
          fi
          cat source_repo_comment.md >> $GITHUB_STEP_SUMMARY;
          echo "************************************************************";
          cat source_repo_comment.md;
          echo "************************************************************";
          echo comment=$(cat source_repo_comment.md) >> $GITHUB_OUTPUT;

enter image description here

1 Comment

what is the difference between two code blocks?
0

Another option to set multilines in outputs could be using this implementation (same as for ENV variables in the $GITHUB_ENV):

      - name: Setup output var
        id: test1
        run: |
          MESSAGE=$(cat << EOF
          first line
          second line
          third line
          ...
          EOF
          )
          echo TEST=$MESSAGE >> $GITHUB_OUTPUT

      - name: Check output var
        run: |
          echo ${{steps.test1.outputs.TEST}}

I made a test here with the same behavior that for environment variables (detailed in this other thread)


EDIT 1:

This syntax also works (and looks easier to use):

      run: |
         echo "TEST1=first line \
          second line \
          third line" >> $GITHUB_OUTPUT

EDIT 2:

It's also possible to display the output as multilines (and not on a single line as the other examples above). However, the syntax would be different and you would need yo use echo -e together with \n inside the variable.

Example:

      - name: Setup output var
        id: test 
        run: echo "TEST=first line\n second line\n third line" >> $GITHUB_OUTPUT

      - name: Check output var
        run: |
          echo ${{steps.test.outputs.TEST}} #Will keep the n from the \n
          echo -e "${{steps.test.outputs.TEST}}" #Will break the line from the \n

enter image description here

8 Comments

well, how this is different from my question? How does it answer my question? I literally use $message variable and it fails in Github Actions CI context
This answer is just an alternative to the one you shared, without having to use the | tr '\n' ' ' syntax (converting the output to a single line). This will allow to keep the variable a multiline output value as the new syntax doesn't work the same way as the ::set-output expression with multiline variables.
well, this doesn't work
It worked in the test I shared here using this implementation using a multiline commit message. Feel free to share your implementation to check what can be different :)
For example, I have either a file with content or a variable I tried to use your approach x=$(cat commit.json | jq '.commit.message' -r) echo "Last commit message: $x" MESSAGE=$(cat << EOF '$x' EOF ) echo "commit=$MESSAGE" >> $GITHUB_OUTPUT and it fails with the same error Error: Unable to process file command 'output' successfully. So looks like replacing breaklines is the only option
|
0

So @oxc's answer led me to this. So to write code in the HEREDOC that does not immediately get parsed, you'll need to quote the starting delimiter. This is nothing new, but with $GITHUB_OUTPUT specifically, the way in which the environment file is parsed is not standard shell. I'm not totally sure what is parsing the file, but it's extremely clear the output file wouln't work source'd back into the runtime. In normal context, the ending delimiter should not have 'EOF' quotes around it matching the beginning, but in this context, GitHub actions will not accept it in the output file without a 1=1 match.

deployUserDataScript=$(cat << 'EOF1'
${{ env.deployUserDataScript }}
EOF1
)

echo "deployUserDataScript<<'EOF'"$'\n'"$deployUserDataScript"$'\n\'EOF\'\n' >> $GITHUB_OUTPUT

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.