Coding Stephan

GitHub workflow commands in PowerShell

As an author of GitHub Actions, you have the ability to use workflow commands to control specific things that happen when your workflow is executed. All the samples are in bash, but what if you did everything else in PowerShell? Can you do the same in PowerShell? Read along to find out how to do that.

Workflow commands introduction

Workflow commands are special commands that you can use to control the behavior of your workflow. They are prefixed with :: and can be used to set environment variables, add annotations, and more. For example, you can use the ::warning :: command to set a warning message in the GitHub Actions UI.

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

Setting messages in PowerShell

You would set a message if there is some file your want to put attention to, or display a warning/error message that gets extra attention. It will be displayed above the job summary in the GitHub Actions UI. You can even point it to a file in the repository, and the message will be clickable, so the user can go directly to the file and line number.

The above command echo ... is a bash command, the PowerShell equivalent is Write-Host. Setting a message in PowerShell would look like this:

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

Variables:

VariableSample valueDescription
fileapp.js(optional) The file name
line1(optional) The line number
col5(optional) The column number
endColumn7(optional) The end column number
titleYOUR-TITLE(optional) The title of the message
messageMissing semicolonThe message to display

And the sample command is a warning, but you can also use error, notice or debug, depending on the severity of the message you want to display.

Masking values

Since GitHub Actions are often used to deploy applications, there may be sensitive information the you don’t want to expose in the logs or in the output ever. You can use the add-mask command to mask a value in the output. This is useful for things like passwords, tokens, and other sensitive information. If you use secrets in your workflow, those are automatically masked. But if you get a token and want to make sure it is never exposed, you can use the add-mask command.

echo "::add-mask::YOUR-TOKEN"

In PowerShell, this would look like this:

Write-Host "::add-mask::YOUR-TOKEN"

Both commands would replace YOUR-TOKEN with *** in the logs and in the step summary.

Setting environment variables

You can also set environment variables in your workflows using the following command (in bash):

echo "{env_var_name}={env_var_value}" >> "$GITHUB_ENV"

Well this is a bit different in PowerShell, because it is using the >> operator, which means don’t output the value to the console, but append it to a file. This operator is not available in PowerShell! But we can use the Add-Content cmdlet to do the same thing. The PowerShell equivalent would look like this:

Add-Content -Path $env:GITHUB_ENV -Value "env_var_name=env_var_value"

Setting output variables

Sometimes it may be useful to pass information to the next step in your workflow, you can set output variables using the following command:

echo "{output_var_name}={output_var_value}" >> "$GITHUB_OUTPUT"

Again this uses the >> operator, this is the equivalent of the Add-Content cmdlet in PowerShell:

Add-Content -Path $env:GITHUB_OUTPUT -Value "output_var_name=output_var_value"

Setting job summaries

And lastly, you can set the job summary to provide a nice looking summary of the job, without the user having to dig through all the console logs. In bash this would look like:

echo "### Hello world! :rocket:" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "This is a summary of the job" >> $GITHUB_STEP_SUMMARY

In PowerShell this would look like:

Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "### Hello world! :rocket:"
Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value ""
Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value "This is a summary of the job"

My personal preference is that you write the summary to a separate file or a PowerShell variable first, and then use the Add-Content cmdlet to append it in one go to the $GITHUB_STEP_SUMMARY file. This way you can easily format the summary and add additional information if needed.

$summary = @"
### Hello world! :rocket:

This is a regular paragraph,
and this is on the same line. Because in markdown you would need two line endings to start a new paragraph.
"@
Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value $summary

Be sure to format the markdown correctly, if you fail to do so, the summary will not look great. All step summaries are isolated, so if you mess up the summary, the next step is not affected.

Check out this summary, which is generated by this code which also shows you how you can truncate the output if it is over 1MB. Truncating is important if you expect a lot of output. Your workflow will fail if the summary is over 1MB, so be sure to check the size of the summary before you write it to the file.

$filePath = "test-results/test-results.md"
if (Test-Path $filePath) {
    $maxSize = 1024KB
    $truncationMsg = "`n`n**⚠ TRUNCATED: Output exceeded GitHub's 1024 KB limit.**"

    # Check file size
    $fileSize = (Get-Item $filePath).Length
    if ($fileSize -gt $maxSize) {
        Write-Host "❌ Truncating output file to prevent failure."

        # Read the file content
        $content = Get-Content $filePath -Raw

        # Calculate the maximum content size to fit within the limit
        $maxContentSize = $maxSize - ($truncationMsg.Length * [System.Text.Encoding]::UTF8.GetByteCount("a")) - 4KB

        # Truncate the content
        $truncatedContent = $content.Substring(0, $maxContentSize / [System.Text.Encoding]::UTF8.GetByteCount("a"))

        # Write the truncated content and truncation message to the new file
        $truncatedContent | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Encoding UTF8 -Append
        Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value $truncationMsg

    } else {
        Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value $(Get-Content $filePath)
    }
}

Conclusion

Once you figure out how you can ’translate’ the bash commands to PowerShell, executing these workflow commands is not that hard. Explore all the other workflow commands and use them to spice up your workflows.