Skip to main content

This version of GitHub Enterprise Server was discontinued on 2025-04-03. No patch releases will be made, even for critical security issues. For better performance, improved security, and new features, upgrade to the latest version of GitHub Enterprise Server. For help with the upgrade, contact GitHub Enterprise support.

Workflow commands for GitHub Actions

You can use workflow commands when running shell commands in a workflow or in an action's code.

Tool navigation

Note

GitHub-hosted runners are not currently supported on GitHub Enterprise Server. You can see more information about planned future support on the GitHub public roadmap.

About workflow commands

Actions can communicate with the runner machine to set environment variables, output values used by other actions, add debug messages to the output logs, and other tasks.

Most workflow commands use the echo command in a specific format, while others are invoked by writing to a file. For more information, see Environment files.

Example of a workflow command

Bash
echo "::workflow-command parameter1={data},parameter2={data}::{command value}"
PowerShell
Write-Output "::workflow-command parameter1={data},parameter2={data}::{command value}"

Note

Workflow command and parameter names are case insensitive.

Warning

If you are using Command Prompt, omit double quote characters (") when using workflow commands.

Using workflow commands to access toolkit functions

The actions/toolkit includes a number of functions that can be executed as workflow commands. Use the :: syntax to run the workflow commands within your YAML file; these commands are then sent to the runner over stdout.

For example, instead of using code to create an error annotation, as below:

JavaScript
core.error('Missing semicolon', {file: 'app.js', startLine: 1})

Example: Creating an annotation for an error

You can use the error command in your workflow to create the same error annotation:

YAML
      - name: Create annotation for build error
        run: echo "::error file=app.js,line=1::Missing semicolon"
YAML
      - name: Create annotation for build error
        run: Write-Output "::error file=app.js,line=1::Missing semicolon"

The following table shows which toolkit functions are available within a workflow:

Toolkit functionEquivalent workflow command
core.addPathAccessible using environment file GITHUB_PATH
core.debugdebug
core.noticenotice
core.errorerror
core.endGroupendgroup
core.exportVariableAccessible using environment file GITHUB_ENV
core.getInputAccessible using environment variable INPUT_{NAME}
core.getStateAccessible using environment variable STATE_{NAME}
core.isDebugAccessible using environment variable RUNNER_DEBUG
core.summaryAccessible using environment file GITHUB_STEP_SUMMARY
core.saveStateAccessible using environment file GITHUB_STATE
core.setCommandEchoecho
core.setFailedUsed as a shortcut for ::error and exit 1
core.setOutputAccessible using environment file GITHUB_OUTPUT
core.setSecretadd-mask
core.startGroupgroup
core.warningwarning

Setting a debug message

Prints a debug message to the log. You must create a secret named ACTIONS_STEP_DEBUG with the value true to see the debug messages set by this command in the log. For more information, see Enabling debug logging.

Text
::debug::{message}

Example: Setting a debug message

Bash
echo "::debug::Set the Octocat variable"
PowerShell
Write-Output "::debug::Set the Octocat variable"

Setting a notice message

Creates a notice message and prints the message to the log. This message will create an annotation, which can associate the message with a particular file in your repository. Optionally, your message can specify a position within the file.

Text
::notice file={name},line={line},endLine={endLine},title={title}::{message}
ParameterValueRequiredDefault
titleCustom titleNoNone
fileFilenameNo.github
colColumn number, starting at 1NoNone
endColumnEnd column numberNoNone
lineLine number, starting at 1No1
endLineEnd line numberNo1

Example: Setting a notice message

Bash
echo "::notice file=app.js,line=1,col=5,endColumn=7::Missing semicolon"
PowerShell
Write-Output "::notice file=app.js,line=1,col=5,endColumn=7,title=YOUR-TITLE::Missing semicolon"

Setting a warning message

Creates a warning message and prints the message to the log. This message will create an annotation, which can associate the message with a particular file in your repository. Optionally, your message can specify a position within the file.

Text
::warning file={name},line={line},endLine={endLine},title={title}::{message}
ParameterValueRequiredDefault
titleCustom titleNoNone
fileFilenameNo.github
colColumn number, starting at 1NoNone
endColumnEnd column numberNoNone
lineLine number, starting at 1No1
endLineEnd line numberNo1

Example: Setting a warning message

Bash
echo "::warning file=app.js,line=1,col=5,endColumn=7,title=YOUR-TITLE::Missing semicolon"
PowerShell
Write-Output "::warning file=app.js,line=1,col=5,endColumn=7,title=YOUR-TITLE::Missing semicolon"

Setting an error message

Creates an error message and prints the message to the log. This message will create an annotation, which can associate the message with a particular file in your repository. Optionally, your message can specify a position within the file.

Text
::error file={name},line={line},endLine={endLine},title={title}::{message}
ParameterValueRequiredDefault
titleCustom titleNoNone
fileFilenameNo.github
colColumn number, starting at 1NoNone
endColumnEnd column numberNoNone
lineLine number, starting at 1No1
endLineEnd line numberNo1

Example: Setting an error message

Bash
echo "::error file=app.js,line=1,col=5,endColumn=7,title=YOUR-TITLE::Missing semicolon"
PowerShell
Write-Output "::error file=app.js,line=1,col=5,endColumn=7,title=YOUR-TITLE::Missing semicolon"

Grouping log lines

Creates an expandable group in the log. To create a group, use the group command and specify a title. Anything you print to the log between the group and endgroup commands is nested inside an expandable entry in the log.

Text
::group::{title}
::endgroup::

Example: Grouping log lines

YAML
jobs:
  bash-example:
    runs-on: ubuntu-latest
    steps:
      - name: Group of log lines
        run: |
            echo "::group::My title"
            echo "Inside group"
            echo "::endgroup::"
YAML
jobs:
  powershell-example:
    runs-on: windows-latest
    steps:
      - name: Group of log lines
        run: |
            Write-Output "::group::My title"
            Write-Output "Inside group"
            Write-Output "::endgroup::"

Screenshot of the log for the workflow step. The second line, "My title", is an expanded group. The next line, "Inside group", is indented below.

Masking a value in a log

Text
::add-mask::{value}

Masking a value prevents a string or variable from being printed in the log. Each masked word separated by whitespace is replaced with the * character. You can use an environment variable or string for the mask's value. When you mask a value, it is treated as a secret and will be redacted on the runner. For example, after you mask a value, you won't be able to set that value as an output.

Example: Masking a string

When you print "Mona The Octocat" in the log, you'll see "***".

Bash
echo "::add-mask::Mona The Octocat"
PowerShell
Write-Output "::add-mask::Mona The Octocat"

Warning

Make sure you register the secret with 'add-mask' before outputting it in the build logs or using it in any other workflow commands.

Example: Masking an environment variable

When you print the variable MY_NAME or the value "Mona The Octocat" in the log, you'll see "***" instead of "Mona The Octocat".

YAML
jobs:
  bash-example:
    runs-on: ubuntu-latest
    env:
      MY_NAME: "Mona The Octocat"
    steps:
      - name: bash-version
        run: echo "::add-mask::$MY_NAME"
YAML
jobs:
  powershell-example:
    runs-on: windows-latest
    env:
      MY_NAME: "Mona The Octocat"
    steps:
      - name: powershell-version
        run: Write-Output "::add-mask::$env:MY_NAME"

Example: Masking a generated output within a single job

If you do not need to pass your secret from one job to another job, you can:

  1. Generate the secret (without outputting it).
  2. Mask it with add-mask.
  3. Use GITHUB_OUTPUT to make the secret available to other steps within the job.
YAML
on: push
jobs:
  generate-a-secret-output:
    runs-on: ubuntu-latest
    steps:
      - id: sets-a-secret
        name: Generate, mask, and output a secret
        run: |
          the_secret=$((RANDOM))
          echo "::add-mask::$the_secret"
          echo "secret-number=$the_secret" >> "$GITHUB_OUTPUT"
      - name: Use that secret output (protected by a mask)
        run: |
          echo "the secret number is ${{ steps.sets-a-secret.outputs.secret-number }}"
YAML
on: push
jobs:
  generate-a-secret-output:
    runs-on: ubuntu-latest
    steps:
      - id: sets-a-secret
        name: Generate, mask, and output a secret
        shell: pwsh
        run: |
          Set-Variable -Name TheSecret -Value (Get-Random)
          Write-Output "::add-mask::$TheSecret"
          "secret-number=$TheSecret" >> $env:GITHUB_OUTPUT
      - name: Use that secret output (protected by a mask)
        shell: pwsh
        run: |
          Write-Output "the secret number is ${{ steps.sets-a-secret.outputs.secret-number }}"

Example: Masking and passing a secret between jobs or workflows

If you want to pass a masked secret between jobs or workflows, you should store the secret in a store and then retrieve it in the subsequent job or workflow.

Setup

  1. Set up a secret store to store the secret that you will generate during your workflow. For example, Vault.
  2. Generate a key for reading and writing to that secret store. Store the key as a repository secret. In the following example workflow, the secret name is SECRET_STORE_CREDENTIALS. For more information, see Using secrets in GitHub Actions.

Workflow

Note

This workflow uses an imaginary secret store, secret-store, which has imaginary commands store-secret and retrieve-secret. some/secret-store@ 27b31702a0e7fc50959f5ad993c78deac1bdfc29 is an imaginary action that installs the secret-store application and configures it to connect to an instance with credentials.

YAML
on: push

jobs:
  secret-generator:
    runs-on: ubuntu-latest
    outputs:
      handle: ${{ steps.generate-secret.outputs.handle }}
    steps:
    - uses: some/secret-store@27b31702a0e7fc50959f5ad993c78deac1bdfc29
      with:
        credentials: ${{ secrets.SECRET_STORE_CREDENTIALS }}
        instance: ${{ secrets.SECRET_STORE_INSTANCE }}
    - name: generate secret
      id: generate-secret
      shell: bash
      run: |
        GENERATED_SECRET=$((RANDOM))
        echo "::add-mask::$GENERATED_SECRET"
        SECRET_HANDLE=$(secret-store store-secret "$GENERATED_SECRET")
        echo "handle=$SECRET_HANDLE" >> "$GITHUB_OUTPUT"
  secret-consumer:
    runs-on: macos-latest
    needs: secret-generator
    steps:
    - uses: some/secret-store@27b31702a0e7fc50959f5ad993c78deac1bdfc29
      with:
        credentials: ${{ secrets.SECRET_STORE_CREDENTIALS }}
        instance: ${{ secrets.SECRET_STORE_INSTANCE }}
    - name: use secret
      shell: bash
      run: |
        SECRET_HANDLE="${{ needs.secret-generator.outputs.handle }}"
        RETRIEVED_SECRET=$(secret-store retrieve-secret "$SECRET_HANDLE")
        echo "::add-mask::$RETRIEVED_SECRET"
        echo "We retrieved our masked secret: $RETRIEVED_SECRET"
YAML
on: push

jobs:
  secret-generator:
    runs-on: ubuntu-latest
    steps:
    - uses: some/secret-store@27b31702a0e7fc50959f5ad993c78deac1bdfc29
      with:
        credentials: ${{ secrets.SECRET_STORE_CREDENTIALS }}
        instance: ${{ secrets.SECRET_STORE_INSTANCE }}
    - name: generate secret
      shell: pwsh
      run: |
        Set-Variable -Name Generated_Secret -Value (Get-Random)
        Write-Output "::add-mask::$Generated_Secret"
        Set-Variable -Name Secret_Handle -Value (Store-Secret "$Generated_Secret")
        "handle=$Secret_Handle" >> $env:GITHUB_OUTPUT
  secret-consumer:
    runs-on: macos-latest
    needs: secret-generator
    steps:
    - uses: some/secret-store@27b31702a0e7fc50959f5ad993c78deac1bdfc29
      with:
        credentials: ${{ secrets.SECRET_STORE_CREDENTIALS }}
        instance: ${{ secrets.SECRET_STORE_INSTANCE }}
    - name: use secret
      shell: pwsh
      run: |
        Set-Variable -Name Secret_Handle -Value "${{ needs.secret-generator.outputs.handle }}"
        Set-Variable -Name Retrieved_Secret -Value (Retrieve-Secret "$Secret_Handle")
        echo "::add-mask::$Retrieved_Secret"
        echo "We retrieved our masked secret: $Retrieved_Secret"

Stopping and starting workflow commands

Stops processing any workflow commands. This special command allows you to log anything without accidentally running a workflow command. For example, you could stop logging to output an entire script that has comments.

Text
::stop-commands::{endtoken}

To stop the processing of workflow commands, pass a unique token to stop-commands. To resume processing workflow commands, pass the same token that you used to stop workflow commands.

Warning

Make sure the token you're using is randomly generated and unique for each run.

Text
::{endtoken}::

Example: Stopping and starting workflow commands

YAML
jobs:
  workflow-command-job:
    runs-on: ubuntu-latest
    steps:
      - name: Disable workflow commands
        run: |
          echo '::warning:: This is a warning message, to demonstrate that commands are being processed.'
          stopMarker=$(uuidgen)
          echo "::stop-commands::$stopMarker"
          echo '::warning:: This will NOT be rendered as a warning, because stop-commands has been invoked.'
          echo "::$stopMarker::"
          echo '::warning:: This is a warning again, because stop-commands has been turned off.'
YAML
jobs:
  workflow-command-job:
    runs-on: windows-latest
    steps:
      - name: Disable workflow commands
        run: |
          Write-Output '::warning:: This is a warning message, to demonstrate that commands are being processed.'
          $stopMarker = New-Guid
          Write-Output "::stop-commands::$stopMarker"
          Write-Output '::warning:: This will NOT be rendered as a warning, because stop-commands has been invoked.'
          Write-Output "::$stopMarker::"
          Write-Output '::warning:: This is a warning again, because stop-commands has been turned off.'

Sending values to the pre and post actions

You can create environment variables for sharing with your workflow's pre: or post: actions by writing to the file located at GITHUB_STATE. For example, you can create a file with the pre: action, pass the file location to the main: action, and then use the post: action to delete the file. Alternatively, you could create a file with the main: action, pass the file location to the post: action, and also use the post: action to delete the file.

If you have multiple pre: or post: actions, you can only access the saved value in the action where it was written to GITHUB_STATE. For more information on the post: action, see Metadata syntax for GitHub Actions.

The GITHUB_STATE file is only available within an action. The saved value is stored as an environment value with the STATE_ prefix.

This example uses JavaScript to write to the GITHUB_STATE file. The resulting environment variable is named STATE_processID with the value of 12345:

JavaScript