Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GITHUB_OUTPUT variables work from workflow echo but not by core API call #1906

Open
de-jcup opened this issue Dec 16, 2024 · 0 comments
Open
Labels
bug Something isn't working

Comments

@de-jcup
Copy link

de-jcup commented Dec 16, 2024

Description

When I write a key value pair by echo inside a workflow it can be used by other steps as variables and is visible inside the context. But when I use core.setOutput('key1', 'value1') it doesn't.

Also the content inside the $GITHUB_OUTPUT files is completely different.

Steps to reproduce

Set output with echo - works

Here an example which uses the described new way of setting outputs by environment variables. This works and the output file is in an expected way:

 - name: Print output by worfklow
      id: output_test_by_worfklow
      if: always()
      run: |
        echo "scan-trafficlight=RED" >> "$GITHUB_OUTPUT"  # Test Output 1
        echo "scan-findings-count=1" >> "$GITHUB_OUTPUT"  # Test Output 2

- name: Show all github output files
      id: output_file
      if: always()
      run: |
        echo "GITHUB_OUTPUT=$GITHUB_OUTPUT"
        DIRECTORY=$(dirname -- "$GITHUB_OUTPUT")
        # Check if the directory exists
        if [ ! -d "$DIRECTORY" ]; then
          echo "Directory does not exist."
          exit 1
        fi
        # Iterate through each file in the directory
        for FILE in "$DIRECTORY"/*; do
          # Check if the file name starts with "set_output"
          if [[ $(basename "$FILE") == set_output* ]]; then
            echo "----------------------------------------------------------------------------------------------------"
            echo "Contents of $FILE:"
            echo "----------------------------------------------------------------------------------------------------"
            cat "$FILE"
            echo # Add a newline for better readability
          fi
        done
- name: Print Context Information
      if: always()
      env:
        CONTEXT: ${{ toJson(steps) }}
      run: echo "$CONTEXT"

After execution I got following:

----------------------------------------------------------------------------------------------------
Contents of /runner/_work/_temp/_runner_file_commands/set_output_8c665a1b-cb6b-4962-96ca-3075398fe305:
----------------------------------------------------------------------------------------------------
scan-trafficlight=RED
scan-findings-count=1

and the (reduced) context output looks like this

"output_test_by_worfklow": {
    "outputs": {
      "scan-trafficlight": "RED",
      "scan-findings-count": "1"
    },
    "outcome": "success",
    "conclusion": "success"
  }

Means everything is working. Fine.

Set output via API - DOES NOT WORK

But when I write a custom action and use internally there :

core.setOutput('scan-trafficlight','RED')
core.setOutput('scan-findings-count','1')

to define output, it writes the files in a different way and the context does
not contain the two keys:

 - name: Print output by custom action
      id: output_test_by_custom_action
      uses: {orgname}/{customActionWhichSetsOutput}
- name: Show all github output files
      id: output_file
      if: always()
      run: |
        echo "GITHUB_OUTPUT=$GITHUB_OUTPUT"
        DIRECTORY=$(dirname -- "$GITHUB_OUTPUT")
        # Check if the directory exists
        if [ ! -d "$DIRECTORY" ]; then
          echo "Directory does not exist."
          exit 1
        fi
        # Iterate through each file in the directory
        for FILE in "$DIRECTORY"/*; do
          # Check if the file name starts with "set_output"
          if [[ $(basename "$FILE") == set_output* ]]; then
            echo "----------------------------------------------------------------------------------------------------"
            echo "Contents of $FILE:"
            echo "----------------------------------------------------------------------------------------------------"
            cat "$FILE"
            echo # Add a newline for better readability
          fi
        done
- name: Print Context Information
      if: always()
      env:
        CONTEXT: ${{ toJson(steps) }}
      run: echo "$CONTEXT"

After execution I got following:

----------------------------------------------------------------------------------------------------
Contents of /runner/_work/_temp/_runner_file_commands/set_output_9550026e-04b7-4a5e-963b-4d7838e73e4d:
----------------------------------------------------------------------------------------------------
scan-trafficlight<<ghadelimiter_12b579a9-fb20-4e41-8b6d-53dd97e57865
RED
ghadelimiter_12b579a9-fb20-4e41-8b6d-53dd97e57865
scan-findings-count<<ghadelimiter_b1042cbc-4b13-4648-851b-83535f96876c
1
ghadelimiter_b1042cbc-4b13-4648-851b-83535f96876c

and the (reduced) context output looks like this

"output_test_by_custom_action": {
    "outputs": {

    },
    "outcome": "success",
    "conclusion": "success"
  }

Means:

  1. The output is written different to the output by echo!
  2. The context does not contain output information/there is no access to it.

Additional info

Workaround (did not help)

I tried to write the file directly {key}={value}{os.EOL} but the 2 keys were still not
visible in context output.

Analyze
// https://github.com/actions/toolkit/blob/main/packages/core/src/core.ts#L192
export function setOutput(name: string, value: any): void {
  const filePath = process.env['GITHUB_OUTPUT'] || ''
  if (filePath) {
    return issueFileCommand('OUTPUT', prepareKeyValueMessage(name, value))
  }

  process.stdout.write(os.EOL)
  issueCommand('set-output', {name}, toCommandValue(value))
}

this calls

// https://github.com/actions/toolkit/blob/main/packages/core/src/file-command.ts
export function prepareKeyValueMessage(key: string, value: any): string {
  const delimiter = `ghadelimiter_${crypto.randomUUID()}`
  const convertedValue = toCommandValue(value)

  // These should realistically never happen, but just in case someone finds a
  // way to exploit uuid generation let's not allow keys or values that contain
  // the delimiter.
  if (key.includes(delimiter)) {
    throw new Error(
      `Unexpected input: name should not contain the delimiter "${delimiter}"`
    )
  }

  if (convertedValue.includes(delimiter)) {
    throw new Error(
      `Unexpected input: value should not contain the delimiter "${delimiter}"`
    )
  }

  return `${key}<<${delimiter}${os.EOL}${convertedValue}${os.EOL}${delimiter}`
}

Hm... the usage of ${crypto.randomUUID()} looks odd: What shall here be the purpose of the crypto uuid? Was the intention to ensure keys are unqiue? It is always different. What was the real purpose for this. Sorry I did not get it.

Why does it not appear inside the context? Could it be that the read mechanism which sets the context is not able to read the new format?

Expected behavior

  1. I would expect that the output file structure would be same for echos and for API usages.
  2. using core.setOutput(...) should result in the output listed inside the context

Target

This appeared with core 1.10.1 and newer

@de-jcup de-jcup added the bug Something isn't working label Dec 16, 2024
@de-jcup de-jcup changed the title GITHUB_OUTPUT variables do work from workflow but not inside a GitHub action GITHUB_OUTPUT variables work from workflow echo but not by core API call Dec 16, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant